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
🔍 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:
+40717
-39869
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.
Binary file not shown.
Binary file not shown.
+213
-62
@@ -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 (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`);
|
||||
if (unclassified.children.length > 0) {
|
||||
blocking.children.push(unclassified);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface MapNode {
|
||||
}
|
||||
|
||||
export interface HierarchicalMapNode extends MapNode {
|
||||
role?: "group";
|
||||
children?: HierarchicalMapNode[];
|
||||
}
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user