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.
+213 -62
View File
@@ -13,7 +13,11 @@ const MESH_NAME_MAPPINGS = {
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) {
return {
@@ -25,32 +29,147 @@ function cloneNode(node) {
};
}
function mapMeshName(node) {
if (node.type !== "Mesh") {
return cloneNode(node);
}
function createGroup(name, sourceNode) {
return {
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 {
...cloneNode(node),
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 {
...cloneNode(node),
name: node.name === "Neutre" ? "blocking" : node.name,
children,
...cloneNode(objectNode ?? meshNode),
name: mappedMesh.name,
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() {
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",
@@ -58,67 +177,99 @@ function transformMap() {
const terrainRaw = rawData.find(
(node) => node.name === "terrain" && node.type === "Object3D",
);
const terrainMesh = rawData.find(
(node) => node.name === "terrain" && node.type === "Mesh",
);
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");
if (!sceneRaw || !terrainRaw || !terrainMesh || !blockingRaw) {
throw new Error("Missing required Scene, terrain, or Neutre nodes");
}
const scene = createGroup(sceneRaw);
const terrain = createGroup(terrainRaw);
const blocking = createGroup(blockingRaw);
let currentGroup = null;
const scene = createGroup("Scene", sceneRaw);
const terrain = createGroup("terrain", terrainRaw);
const blocking = createGroup("blocking", blockingRaw);
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) {
if (REMOVED_NODE_NAMES.has(rawNode.name)) {
removedCount++;
continue;
terrain.children.push(createRenderableObject(terrainRaw, terrainMesh));
scene.children.push(terrain, blocking);
blocking.children.push(
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") {
continue;
if (unclassified.children.length > 0) {
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));
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();
+1
View File
@@ -9,6 +9,7 @@ export interface MapNode {
}
export interface HierarchicalMapNode extends MapNode {
role?: "group";
children?: HierarchicalMapNode[];
}
+8
View File
@@ -31,6 +31,10 @@ function isHierarchicalMapNode(value: unknown): value is HierarchicalMapNode {
return false;
}
if ("role" in value && value.role !== undefined && value.role !== "group") {
return false;
}
if (!("children" in value)) {
return true;
}
@@ -52,6 +56,10 @@ function flattenMapNode(node: HierarchicalMapNode): MapNode[] {
};
const childNodes = node.children?.flatMap(flattenMapNode) ?? [];
if (node.role === "group") {
return childNodes;
}
return [mapNode, ...childNodes];
}