upatde: json + models
🔍 Lint / 🪄 Check lint (pull_request) Has been cancelled
🔍 Lint / 🎨 Check format (pull_request) Has been cancelled
🔍 Lint / 🔎 Typecheck (pull_request) Has been cancelled
📊 Quality / 🔒 Security Audit (pull_request) Has been cancelled
📊 Quality / 📋 Dependency Freshness (pull_request) Has been cancelled
📊 Quality / 📦 Bundle Size (pull_request) Has been cancelled
🔍 Lint / 🏗 Build (pull_request) Has been cancelled

This commit is contained in:
Tom Boullay
2026-05-15 00:18:03 +02:00
parent cdd919c010
commit 27951d13fd
12 changed files with 40939 additions and 39955 deletions
+37983 -37135
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+212 -61
View File
@@ -13,7 +13,11 @@ const MESH_NAME_MAPPINGS = {
panneauxquartier: "panneauaffichage", panneauxquartier: "panneauaffichage",
}; };
const REMOVED_NODE_NAMES = new Set(["ROOT", "mc"]); const IDENTITY_NODE = {
position: [0, 0, 0],
rotation: [0, 0, 0],
scale: [1, 1, 1],
};
function cloneNode(node) { function cloneNode(node) {
return { return {
@@ -25,32 +29,147 @@ function cloneNode(node) {
}; };
} }
function mapMeshName(node) { function createGroup(name, sourceNode) {
if (node.type !== "Mesh") { return {
return cloneNode(node); name,
type: "Object3D",
role: "group",
position: sourceNode?.position ?? IDENTITY_NODE.position,
rotation: sourceNode?.rotation ?? IDENTITY_NODE.rotation,
scale: sourceNode?.scale ?? IDENTITY_NODE.scale,
children: [],
};
} }
function mapMeshNode(node) {
return { return {
...cloneNode(node), ...cloneNode(node),
name: MESH_NAME_MAPPINGS[node.name] ?? node.name, name: MESH_NAME_MAPPINGS[node.name] ?? node.name,
}; };
} }
function createGroup(node, children = []) { function getOrCreateModelGroup(parent, modelName) {
let group = parent.children.find(
(child) => child.name === modelName && child.type === "Object3D",
);
if (!group) {
group = createGroup(modelName);
parent.children.push(group);
}
return group;
}
function createRenderableObject(objectNode, meshNode) {
const mappedMesh = mapMeshNode(meshNode);
return { return {
...cloneNode(node), ...cloneNode(objectNode ?? meshNode),
name: node.name === "Neutre" ? "blocking" : node.name, name: mappedMesh.name,
children, type: "Object3D",
children: [mappedMesh],
}; };
} }
function addRenderable(parent, objectNode, meshNode) {
const renderable = createRenderableObject(objectNode, meshNode);
getOrCreateModelGroup(parent, renderable.name).children.push(renderable);
}
function addObjectsByRange(rawData, parent, start, end, allowedNames) {
let currentObject = null;
for (let i = start; i <= end; i++) {
const node = rawData[i];
if (node?.type === "Object3D") {
currentObject = node;
continue;
}
if (node?.type !== "Mesh") continue;
if (allowedNames && !allowedNames.has(node.name)) continue;
addRenderable(parent, currentObject, node);
}
}
function getNearestGroup(groups, node) {
const [x, , z] = node.position;
return groups.reduce((nearest, group) => {
const [gx, , gz] = group.position;
const distance = Math.hypot(x - gx, z - gz);
if (!nearest || distance < nearest.distance) {
return { group, distance };
}
return nearest;
}, null).group;
}
function createResidenceZones(rawData, residence) {
const zoneSources = [rawData[830], rawData[874], rawData[892]];
const zones = zoneSources.map((sourceNode, index) => {
const zone = createGroup(`zone${index + 1}_residence`, sourceNode);
residence.children.push(zone);
return zone;
});
addObjectsByRange(rawData, zones[0], 831, 873, RESIDENCE_MESH_NAMES);
addObjectsByRange(rawData, zones[1], 875, 891, RESIDENCE_MESH_NAMES);
addObjectsByRange(rawData, zones[2], 893, 942, RESIDENCE_MESH_NAMES);
for (let i = 14; i <= 23; i++) {
const node = rawData[i];
if (node?.type === "Mesh" && node.name === "parcebike") {
addRenderable(getNearestGroup(zones, node), null, node);
}
}
for (let i = 25; i <= 58; i++) {
const node = rawData[i];
if (node?.type === "Mesh" && node.name === "boitesauxlettres") {
addRenderable(getNearestGroup(zones, node), null, node);
}
}
return zones;
}
const VEGETATION_MESH_NAMES = new Set(["arbre", "sapin", "buissons"]);
const CHAMP_MESH_NAMES = new Set([
"champdeble",
"champdesoja",
"champsdetournesol",
]);
const FERME_MESH_NAMES = new Set(["buissons", "buisson", "fermeverticale"]);
const RESIDENCE_MESH_NAMES = new Set(["immeuble_1", "immeuble_2", "maison1"]);
const ENERGIE_MESH_NAMES = new Set([
"pyloneelectrique",
"eoliennes",
"panneausolaire",
"generateur",
]);
const DIRECTION_MESH_NAMES = new Set([
"panneauxcentre",
"panneauxdomaine",
"panneaudircentre",
"panneaudirdomaine",
"panneaudirfabrik",
"panneaudirresidences1",
"panneaudirresidences2",
"panneauxquartier",
]);
const LAFABRIK_MESH_NAMES = new Set([
"lafabrik",
"immeuble_1",
"immeuble_2",
"maison1",
]);
function transformMap() { function transformMap() {
console.log("Reading map_raw.json..."); console.log("Reading map_raw.json...");
const rawData = JSON.parse(fs.readFileSync(INPUT_PATH, "utf-8")); 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( const sceneRaw = rawData.find(
(node) => node.name === "Scene" && node.type === "Object3D", (node) => node.name === "Scene" && node.type === "Object3D",
@@ -58,67 +177,99 @@ function transformMap() {
const terrainRaw = rawData.find( const terrainRaw = rawData.find(
(node) => node.name === "terrain" && node.type === "Object3D", (node) => node.name === "terrain" && node.type === "Object3D",
); );
const terrainMesh = rawData.find(
(node) => node.name === "terrain" && node.type === "Mesh",
);
const blockingRaw = rawData.find( const blockingRaw = rawData.find(
(node) => node.name === "Neutre" && node.type === "Object3D", (node) => node.name === "Neutre" && node.type === "Object3D",
); );
if (!sceneRaw || !terrainRaw || !blockingRaw) { if (!sceneRaw || !terrainRaw || !terrainMesh || !blockingRaw) {
throw new Error("Missing required Scene, terrain, or Neutre node"); throw new Error("Missing required Scene, terrain, or Neutre nodes");
} }
const scene = createGroup(sceneRaw); const scene = createGroup("Scene", sceneRaw);
const terrain = createGroup(terrainRaw); const terrain = createGroup("terrain", terrainRaw);
const blocking = createGroup(blockingRaw); const blocking = createGroup("blocking", blockingRaw);
let currentGroup = null; const vegetation = createGroup("vegetation");
const agriculture = createGroup("agriculture");
const champs = createGroup("champs");
const ferme = createGroup("ferme", rawData[4798]);
const residence = createGroup("residence");
const energie = createGroup("energie", rawData[4800]);
const direction = createGroup("direction", rawData[5]);
const lafabrik = createGroup("lafabrik", rawData[4873]);
const ecole = createGroup("ecole", rawData[4895]);
delete ecole.role;
const unclassified = createGroup("unclassified");
for (const rawNode of rawData) { terrain.children.push(createRenderableObject(terrainRaw, terrainMesh));
if (REMOVED_NODE_NAMES.has(rawNode.name)) { scene.children.push(terrain, blocking);
removedCount++; blocking.children.push(
continue; vegetation,
agriculture,
residence,
energie,
direction,
lafabrik,
ecole,
);
agriculture.children.push(champs, ferme);
addObjectsByRange(rawData, direction, 6, 12, DIRECTION_MESH_NAMES);
createResidenceZones(rawData, residence);
addObjectsByRange(rawData, energie, 61, 96, new Set(["pyloneelectrique"]));
addObjectsByRange(rawData, vegetation, 98, 829, VEGETATION_MESH_NAMES);
addObjectsByRange(rawData, agriculture, 944, 944, new Set(["tuyauxlac"]));
addObjectsByRange(rawData, champs, 946, 4594, CHAMP_MESH_NAMES);
addObjectsByRange(rawData, ferme, 4595, 4799, FERME_MESH_NAMES);
addObjectsByRange(rawData, vegetation, 4750, 4797, VEGETATION_MESH_NAMES);
addObjectsByRange(rawData, energie, 4801, 4872, ENERGIE_MESH_NAMES);
addObjectsByRange(rawData, lafabrik, 4874, 4894, LAFABRIK_MESH_NAMES);
addObjectsByRange(rawData, direction, 4896, 4897, DIRECTION_MESH_NAMES);
addObjectsByRange(rawData, vegetation, 4898, 4997, VEGETATION_MESH_NAMES);
for (let i = 0; i < rawData.length; i++) {
const node = rawData[i];
if (node.type !== "Mesh") continue;
if (node.name === "mc") continue;
if (node.name === "bati-ecole") continue;
const alreadyClassified = isMeshClassified(scene, node);
if (!alreadyClassified) {
addRenderable(unclassified, null, node);
}
} }
if (rawNode.name === "Scene" || rawNode.name === "Neutre") { if (unclassified.children.length > 0) {
continue; blocking.children.push(unclassified);
} }
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)); fs.writeFileSync(OUTPUT_PATH, JSON.stringify(scene, null, 2));
console.log(`\nWritten to ${OUTPUT_PATH}`); console.log(`Written hierarchical map to ${OUTPUT_PATH}`);
}
function isSameTransform(a, b) {
return (
a.name === (MESH_NAME_MAPPINGS[b.name] ?? b.name) &&
JSON.stringify(a.position) === JSON.stringify(b.position) &&
JSON.stringify(a.rotation) === JSON.stringify(b.rotation) &&
JSON.stringify(a.scale) === JSON.stringify(b.scale)
);
}
function isMeshClassified(root, rawMeshNode) {
const stack = [...(root.children ?? [])];
while (stack.length > 0) {
const node = stack.pop();
if (node.type === "Mesh" && isSameTransform(node, rawMeshNode)) {
return true;
}
stack.push(...(node.children ?? []));
}
return false;
} }
transformMap(); transformMap();
+1
View File
@@ -9,6 +9,7 @@ export interface MapNode {
} }
export interface HierarchicalMapNode extends MapNode { export interface HierarchicalMapNode extends MapNode {
role?: "group";
children?: HierarchicalMapNode[]; children?: HierarchicalMapNode[];
} }
+8
View File
@@ -31,6 +31,10 @@ function isHierarchicalMapNode(value: unknown): value is HierarchicalMapNode {
return false; return false;
} }
if ("role" in value && value.role !== undefined && value.role !== "group") {
return false;
}
if (!("children" in value)) { if (!("children" in value)) {
return true; return true;
} }
@@ -52,6 +56,10 @@ function flattenMapNode(node: HierarchicalMapNode): MapNode[] {
}; };
const childNodes = node.children?.flatMap(flattenMapNode) ?? []; const childNodes = node.children?.flatMap(flattenMapNode) ?? [];
if (node.role === "group") {
return childNodes;
}
return [mapNode, ...childNodes]; return [mapNode, ...childNodes];
} }