File size: 3,588 Bytes
8305461
 
 
d7a80d5
8305461
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1d825fb
8305461
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1d825fb
 
8305461
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1d825fb
8305461
 
 
 
1d825fb
8305461
 
 
 
d7a80d5
8305461
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import * as THREE from "three";
import type { BakedMapGeometry, GroundPalette } from "./content/maps/mapTypes";

const TEX_SIZE = 4096;

// Lighten a hex color by mixing with white at ratio t (0=original, 1=white)
function lighten(hex: string, t: number): string {
  const n = parseInt(hex.replace("#", ""), 16);
  const r = ((n >> 16) & 0xff), g = ((n >> 8) & 0xff), b = (n & 0xff);
  const mix = (c: number) => Math.round(c + (255 - c) * t);
  return `#${[mix(r), mix(g), mix(b)].map(v => v.toString(16).padStart(2, "0")).join("")}`;
}

function fillPolygon(ctx: CanvasRenderingContext2D, ring: number[], size: number) {
  if (ring.length < 6) return;
  ctx.beginPath();
  ctx.moveTo(ring[0] * size, (1 - ring[1]) * size);
  for (let i = 2; i < ring.length; i += 2) {
    ctx.lineTo(ring[i] * size, (1 - ring[i + 1]) * size);
  }
  ctx.closePath();
  ctx.fill();
}



export function renderMapGeometry(
  geom:    BakedMapGeometry,
  palette: GroundPalette,
): THREE.CanvasTexture {
  const canvas = document.createElement("canvas");
  canvas.width  = TEX_SIZE;
  canvas.height = TEX_SIZE;
  const ctx = canvas.getContext("2d")!;
  const s   = TEX_SIZE;

  // --- Land base ---
  ctx.fillStyle = palette.base;
  ctx.fillRect(0, 0, s, s);

  // --- Land use ---
  for (const lu of geom.landUse) {
    switch (lu.kind) {
      case "forest":
        ctx.fillStyle = lighten(palette.base, -0.15);
        break;
      case "scrub":
        ctx.fillStyle = lighten(palette.base, -0.08);
        break;
      case "urban":
        ctx.fillStyle = lighten(palette.base, 0.18);
        break;
      case "farmland":
        ctx.fillStyle = lighten(palette.base, 0.1);
        break;
    }
    fillPolygon(ctx, lu.ring, s);
  }

  // --- Water ---
  ctx.fillStyle = palette.colors[0] ?? "#0369a1";
  for (const ring of geom.waterRings) {
    fillPolygon(ctx, ring, s);
  }

  // Roads are rendered as 3D ribbon geometry by ScatterRenderer; omitting them here
  // prevents duplicate road representations when geom is available.

  // --- Runways ---
  ctx.fillStyle = "#94a3b8";
  for (const rwy of geom.runways) {
    const cx = rwy.cx * s;
    const cy = (1 - rwy.cy) * s;
    const len = rwy.length * s;
    const wid = rwy.width * s;
    ctx.save();
    ctx.translate(cx, cy);
    ctx.rotate(rwy.heading * Math.PI / 180);
    ctx.fillRect(-len / 2, -wid / 2, len, wid);
    ctx.restore();
  }

  // --- Port markers ---
  const portScale = s / 512;
  ctx.fillStyle = "#e2e8f0";
  for (const port of geom.ports) {
    const px = port.x * s, py = (1 - port.y) * s;
    ctx.beginPath();
    ctx.arc(px, py, 3 * portScale, 0, Math.PI * 2);
    ctx.fill();
  }

  const tex = new THREE.CanvasTexture(canvas);
  tex.anisotropy = 16;
  return tex;
}

// Fallback palette-only texture when no geometry is available
export function renderPaletteFallback(palette: GroundPalette): THREE.CanvasTexture {
  const canvas = document.createElement("canvas");
  canvas.width = canvas.height = 256;
  const ctx = canvas.getContext("2d")!;
  ctx.fillStyle = palette.base;
  ctx.fillRect(0, 0, 256, 256);
  // Subtle noise-like variation using palette colors
  for (let i = 0; i < 400; i++) {
    const x = Math.random() * 256, y = Math.random() * 256;
    const r = 8 + Math.random() * 24;
    ctx.globalAlpha = 0.06 + Math.random() * 0.06;
    ctx.fillStyle = palette.colors[Math.floor(Math.random() * palette.colors.length)] ?? palette.base;
    ctx.beginPath();
    ctx.arc(x, y, r, 0, Math.PI * 2);
    ctx.fill();
  }
  ctx.globalAlpha = 1;
  const tex = new THREE.CanvasTexture(canvas);
  return tex;
}