Feat/map-environment #6

Merged
math-pixel merged 116 commits from feat/map-environment into develop 2026-05-29 00:00:51 +00:00
6 changed files with 15433 additions and 75317 deletions
Showing only changes of commit cdd919c010 - Show all commits
+15262 -75310
View File
File diff suppressed because it is too large Load Diff
+124
View File
@@ -0,0 +1,124 @@
const fs = require("fs");
const path = require("path");
const INPUT_PATH = path.join(__dirname, "../public/map_raw.json");
const OUTPUT_PATH = path.join(__dirname, "../public/map.json");
const MESH_NAME_MAPPINGS = {
boitesauxlettres: "boiteauxlettres",
pyloneelectrique: "pylone",
eoliennes: "eolienne",
immeuble_1: "immeuble1",
buissons: "buisson",
panneauxquartier: "panneauaffichage",
};
const REMOVED_NODE_NAMES = new Set(["ROOT", "mc"]);
function cloneNode(node) {
return {
name: node.name,
type: node.type,
position: node.position,
rotation: node.rotation,
scale: node.scale,
};
}
function mapMeshName(node) {
if (node.type !== "Mesh") {
return cloneNode(node);
}
return {
...cloneNode(node),
name: MESH_NAME_MAPPINGS[node.name] ?? node.name,
};
}
function createGroup(node, children = []) {
return {
...cloneNode(node),
name: node.name === "Neutre" ? "blocking" : node.name,
children,
};
}
function transformMap() {
console.log("Reading map_raw.json...");
const rawData = JSON.parse(fs.readFileSync(INPUT_PATH, "utf-8"));
console.log(`Found ${rawData.length} nodes in raw file`);
let removedCount = 0;
let renamedCount = 0;
const sceneRaw = rawData.find(
(node) => node.name === "Scene" && node.type === "Object3D",
);
const terrainRaw = rawData.find(
(node) => node.name === "terrain" && node.type === "Object3D",
);
const blockingRaw = rawData.find(
(node) => node.name === "Neutre" && node.type === "Object3D",
);
if (!sceneRaw || !terrainRaw || !blockingRaw) {
throw new Error("Missing required Scene, terrain, or Neutre node");
}
const scene = createGroup(sceneRaw);
const terrain = createGroup(terrainRaw);
const blocking = createGroup(blockingRaw);
let currentGroup = null;
for (const rawNode of rawData) {
if (REMOVED_NODE_NAMES.has(rawNode.name)) {
removedCount++;
continue;
}
if (rawNode.name === "Scene" || rawNode.name === "Neutre") {
continue;
}
if (rawNode.name === "terrain" && rawNode.type === "Object3D") {
currentGroup = terrain;
continue;
}
if (rawNode.type === "Object3D") {
currentGroup = createGroup(rawNode);
blocking.children.push(currentGroup);
continue;
}
const mappedNode = mapMeshName(rawNode);
if (mappedNode.name !== rawNode.name) {
renamedCount++;
}
if (rawNode.name === "terrain" && currentGroup === terrain) {
terrain.children.push(mappedNode);
continue;
}
if (currentGroup) {
currentGroup.children.push(mappedNode);
continue;
}
blocking.children.push(mappedNode);
}
scene.children = [terrain, blocking];
console.log(`\nTransformation complete:`);
console.log(` - Removed ${removedCount} mc/ROOT nodes`);
console.log(` - Renamed ${renamedCount} mesh nodes`);
console.log(` - Output: hierarchical Scene root`);
fs.writeFileSync(OUTPUT_PATH, JSON.stringify(scene, null, 2));
console.log(`\nWritten to ${OUTPUT_PATH}`);
}
transformMap();
+4
View File
@@ -8,6 +8,10 @@ export interface MapNode {
scale: Vector3Tuple;
}
export interface HierarchicalMapNode extends MapNode {
children?: HierarchicalMapNode[];
}
export interface SceneData {
mapNodes: MapNode[];
models: Map<string, string>;
+38 -5
View File
@@ -1,4 +1,4 @@
import type { MapNode } from "../../types/editor/editor";
import type { HierarchicalMapNode, MapNode } from "../../types/editor/editor";
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null;
@@ -26,10 +26,43 @@ function isMapNode(value: unknown): value is MapNode {
);
}
export function parseMapNodes(value: unknown): MapNode[] {
if (!Array.isArray(value) || !value.every(isMapNode)) {
throw new Error("Invalid map node data");
function isHierarchicalMapNode(value: unknown): value is HierarchicalMapNode {
if (!isMapNode(value)) {
return false;
}
return value;
if (!("children" in value)) {
return true;
}
return (
value.children === undefined ||
(Array.isArray(value.children) &&
value.children.every(isHierarchicalMapNode))
);
}
function flattenMapNode(node: HierarchicalMapNode): MapNode[] {
const mapNode: MapNode = {
name: node.name,
type: node.type,
position: node.position,
rotation: node.rotation,
scale: node.scale,
};
const childNodes = node.children?.flatMap(flattenMapNode) ?? [];
return [mapNode, ...childNodes];
}
export function parseMapNodes(value: unknown): MapNode[] {
if (Array.isArray(value) && value.every(isHierarchicalMapNode)) {
return value.flatMap(flattenMapNode);
}
if (isHierarchicalMapNode(value)) {
return flattenMapNode(value);
}
throw new Error("Invalid map node data");
}
+1 -1
View File
@@ -20,7 +20,7 @@ function disposeMaterial(material: THREE.Material): void {
material.dispose();
for (const key of Object.keys(material)) {
const value = (material as Record<string, unknown>)[key];
const value = (material as unknown as Record<string, unknown>)[key];
if (value instanceof THREE.Texture) {
value.dispose();
}
+4 -1
View File
@@ -81,7 +81,10 @@ export function InstancedVegetation({
);
for (let i = 0; i < matrices.length; i++) {
instancedMesh.setMatrixAt(i, matrices[i]);
const matrix = matrices[i];
if (matrix) {
instancedMesh.setMatrixAt(i, matrix);
}
}
instancedMesh.instanceMatrix.needsUpdate = true;