Normalize ghost models to exact cm height (setHeight API, feet at origin, manifest scale multiplier)

This commit is contained in:
2026-07-24 11:34:29 +10:00
parent 2f0599c462
commit 386d094d44
+16 -5
View File
@@ -83,7 +83,6 @@ export async function buildGhost(ghost, gradients, manifest) {
if (!url) continue;
group.add(await loadOBJ(url));
}
if (model.scale) group.scale.setScalar(model.scale);
if (!group.children.length) group = proceduralWisp();
} catch (e) {
console.warn('ghost model load failed, using wisp fallback', e);
@@ -103,8 +102,20 @@ export async function buildGhost(ghost, gradients, manifest) {
}
});
group.userData.material = mat;
group.userData.setOpacity = (v) => { mat.uniforms.opacity.value = v; };
group.userData.tick = (t) => { mat.uniforms.uTime.value = t; };
return group;
// Normalize: wrap so feet sit at the group origin, then expose setHeight(cm) that
// scales the whole ghost (any source units) to an exact world height. Minifigure ≈ 4 cm.
const outer = new THREE.Group();
const inner = new THREE.Group();
inner.position.y = -box.min.y; // feet -> origin
inner.add(group);
outer.add(inner);
const rawH = Math.max(box.max.y - box.min.y, 1e-6);
const modelScale = model?.scale || 1; // per-model fine-tune multiplier from the manifest
outer.userData.setHeight = (cm) => outer.scale.setScalar((cm / rawH) * modelScale);
outer.userData.setHeight(4);
outer.userData.material = mat;
outer.userData.setOpacity = (v) => { mat.uniforms.opacity.value = v; };
outer.userData.tick = (t) => { mat.uniforms.uTime.value = t; };
return outer;
}