fix(repair): drill explosion to natural group + apply mission rotation

- ExplodedModel.createParts now descends recursively through single
  mesh-bearing wrapper nodes (e.g. Scene > Moto > Eclatement) until
  reaching a node with multiple mesh-bearing children. Previously the
  first wrapper was used as root, so models with extra Empty/group
  parents fell back to flat leaf meshes lerping in local space.
- Add optional modelRotation field on RepairMissionConfig so fragmented
  + repairing models can match the world-space rotation of the source
  inspection model (parked Ebike).
- Ebike mission now uses EBIKE_WORLD_ROTATION_Y/EBIKE_WORLD_SCALE
  directly so the fragmented bike lines up with the parked bike.
This commit is contained in:
Tom Boullay
2026-06-02 22:51:35 +02:00
parent 89050331df
commit d9a92e336c
4 changed files with 31 additions and 7 deletions
+16 -6
View File
@@ -53,13 +53,23 @@ export class ExplodedModel {
}
private createParts(model: THREE.Object3D): ExplodedPart[] {
const root =
model.children.length === 1 && model.children[0]
? model.children[0]
: model;
const directChildren = root.children.filter((child) => hasMesh(child));
// Drill down through single-mesh-bearing branches until we find a node
// with multiple mesh-bearing children (the natural "explosion group" the
// modeler authored). Falls back to flat mesh list only if no such group
// exists. This avoids exploding leaves in local space when wrapper nodes
// (e.g. "Empty" + "Moto" > "Eclatement") sit above the actual group.
let current = model;
while (true) {
const meshChildren = current.children.filter((child) => hasMesh(child));
if (meshChildren.length === 1 && meshChildren[0]) {
current = meshChildren[0];
continue;
}
break;
}
const directChildren = current.children.filter((child) => hasMesh(child));
const sourceObjects =
directChildren.length > 1 ? directChildren : getMeshes(root);
directChildren.length > 1 ? directChildren : getMeshes(current);
if (sourceObjects.length === 0) return [];