File size: 3,780 Bytes
b29710c
 
 
e4d85e6
b29710c
 
 
 
 
 
 
e4d85e6
 
 
 
 
 
 
 
 
 
 
 
b29710c
 
 
 
 
 
 
 
 
 
 
e4d85e6
 
b29710c
e4d85e6
 
 
 
 
 
 
 
 
 
 
e51a3e6
e4d85e6
 
 
 
 
 
 
 
 
 
 
 
e51a3e6
e4d85e6
 
 
 
 
 
 
e51a3e6
e4d85e6
 
 
 
 
 
 
 
 
 
 
 
 
 
e51a3e6
 
b29710c
 
e4d85e6
b29710c
e4d85e6
 
 
 
 
 
b29710c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e51a3e6
 
 
 
 
 
 
 
 
 
 
b29710c
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
120
121
122
123
124
125
126
127
128
import * as THREE from "three";
import {
  PATH_POINTS,
  PATHS,
  ROAD_HALF_WIDTH,
  GRID_CELL_SIZE,
} from "../config/gameConfig.js";

export class PathBuilder {
  constructor(scene) {
    this.scene = scene;
    // Prepare one or many paths, snapping points to grid
    const snap = (v) => {
      const out = v.clone ? v.clone() : new THREE.Vector3(v.x, v.y ?? 0, v.z);
      out.x = Math.round(out.x / GRID_CELL_SIZE) * GRID_CELL_SIZE;
      out.z = Math.round(out.z / GRID_CELL_SIZE) * GRID_CELL_SIZE;
      out.y = 0;
      return out;
    };
    const hasBranches = Array.isArray(PATHS) && PATHS.length > 0;
    this.paths = hasBranches
      ? PATHS.map((path) => path.map(snap))
      : [PATH_POINTS.map(snap)];
    this.roadMeshes = [];

    // Materials
    this.roadMat = new THREE.MeshStandardMaterial({
      color: 0x393c41,
      metalness: 0.1,
      roughness: 0.9,
    });
  }

  buildPath() {
    // Visualize path lines
    this.createPathLines();

    // Deduplicate segments across paths
    const segKey = (a, b) => {
      // direction-agnostic key
      const ax = a.x.toFixed(4);
      const az = a.z.toFixed(4);
      const bx = b.x.toFixed(4);
      const bz = b.z.toFixed(4);
      return ax < bx || (ax === bx && az <= bz)
        ? `${ax},${az}|${bx},${bz}`
        : `${bx},${bz}|${ax},${az}`;
    };

    const seenSegs = new Set();
    for (const pts of this.paths) {
      for (let i = 0; i < pts.length - 1; i++) {
        const a = pts[i];
        const b = pts[i + 1];
        const key = segKey(a, b);
        if (!seenSegs.has(key)) {
          seenSegs.add(key);
          this.addSegment(a, b);
        }
      }
    }

    // Corner tiles at turns; deduplicate by center
    const seenCorners = new Set();
    for (const pts of this.paths) {
      for (let i = 1; i < pts.length - 1; i++) {
        const prev = pts[i - 1];
        const cur = pts[i];
        const next = pts[i + 1];

        const v1 = new THREE.Vector3().subVectors(cur, prev);
        const v2 = new THREE.Vector3().subVectors(next, cur);
        v1.y = 0;
        v2.y = 0;
        const isV1H = Math.abs(v1.x) > Math.abs(v1.z);
        const isV2H = Math.abs(v2.x) > Math.abs(v2.z);
        const isTurn = (isV1H && !isV2H) || (!isV1H && isV2H);
        if (isTurn) {
          const key = `${cur.x.toFixed(4)},${cur.z.toFixed(4)}`;
          if (!seenCorners.has(key)) {
            seenCorners.add(key);
            this.addCornerTile(cur);
          }
        }
      }
    }
  }

  createPathLines() {
    const pathLineMat = new THREE.LineBasicMaterial({ color: 0xffff00 });
    for (const pts of this.paths) {
      const pathLineGeo = new THREE.BufferGeometry().setFromPoints(pts);
      const pathLine = new THREE.Line(pathLineGeo, pathLineMat);
      pathLine.position.y = 0.01;
      this.scene.add(pathLine);
    }
  }

  addSegment(a, b) {
    const seg = new THREE.Vector3().subVectors(b, a);
    const len = seg.length();
    if (len <= 0.0001) return;

    const mid = new THREE.Vector3().addVectors(a, b).multiplyScalar(0.5);

    const roadGeo = new THREE.BoxGeometry(len, 0.1, ROAD_HALF_WIDTH * 2);
    const road = new THREE.Mesh(roadGeo, this.roadMat);
    road.castShadow = false;
    road.receiveShadow = true;
    road.position.set(mid.x, 0.05, mid.z);
    const angle = Math.atan2(seg.z, seg.x);
    road.rotation.y = -angle;
    this.scene.add(road);
    this.roadMeshes.push(road);
  }

  addCornerTile(center) {
    const size = ROAD_HALF_WIDTH * 2;
    const geo = new THREE.BoxGeometry(size, 0.101, size);
    const tile = new THREE.Mesh(geo, this.roadMat);
    tile.castShadow = false;
    tile.receiveShadow = true;
    tile.position.set(center.x, 0.0505, center.z);
    this.scene.add(tile);
    this.roadMeshes.push(tile);
  }
}