Preview: apply ghost height, keep name labels world-scale above scaled ghosts

This commit is contained in:
2026-07-24 14:12:15 +10:00
parent 774822fb6a
commit d51e441b65
+13 -6
View File
@@ -129,29 +129,34 @@ function buildStatics(scene) {
// ---------- live ghosts ---------- // ---------- live ghosts ----------
const active = new Map(); // uid -> { rec, group, label } const active = new Map(); // uid -> { rec, group, label }
let followUid = null; let followUid = null;
let ghostHeight = 4; // cm, from scene.ghostHeightCm
const clockSamples = []; let clockOffset = 0; const clockSamples = []; let clockOffset = 0;
async function addGhost(rec) { async function addGhost(rec) {
if (active.has(rec.uid)) return; if (active.has(rec.uid)) return;
const group = await buildGhost(rec, gradients, modelManifest); const group = await buildGhost(rec, gradients, modelManifest);
group.userData.setHeight(ghostHeight);
const label = textSprite(rec.name, COLORS[rec.color] || '#fff'); const label = textSprite(rec.name, COLORS[rec.color] || '#fff');
label.position.y = 28; label.scale.set(20, 5, 1);
group.add(label); scn.add(group); scn.add(label); // label stays world-scale, outside the scaled group
scn.add(group);
active.set(rec.uid, { rec, group, label }); active.set(rec.uid, { rec, group, label });
renderList(); renderList();
} }
function removeGhost(uid) { function removeGhost(uid) {
const e = active.get(uid); const e = active.get(uid);
if (!e) return; if (!e) return;
scn.remove(e.group); scn.remove(e.group); scn.remove(e.label);
active.delete(uid); active.delete(uid);
if (followUid === uid) followUid = null; if (followUid === uid) followUid = null;
renderList(); renderList();
} }
const net = new ExhibitNet(); const net = new ExhibitNet();
net.on('scene', m => buildStatics(m.scene)) net.on('scene', m => {
buildStatics(m.scene);
ghostHeight = m.scene.ghostHeightCm || 4;
for (const e of active.values()) e.group.userData.setHeight(ghostHeight);
})
.on('active', async m => { .on('active', async m => {
for (const uid of [...active.keys()]) removeGhost(uid); for (const uid of [...active.keys()]) removeGhost(uid);
for (const rec of m.ghosts) await addGhost(rec); for (const rec of m.ghosts) await addGhost(rec);
@@ -208,12 +213,14 @@ fit();
(function loop(t) { (function loop(t) {
requestAnimationFrame(loop); requestAnimationFrame(loop);
const now = Date.now() + clockOffset; const now = Date.now() + clockOffset;
for (const { rec, group } of active.values()) { for (const { rec, group, label } of active.values()) {
ghostTransform(rec, now, _tmp); ghostTransform(rec, now, _tmp);
group.position.copy(_tmp.position); group.position.copy(_tmp.position);
group.rotation.y = _tmp.rotationY; group.rotation.y = _tmp.rotationY;
group.userData.setOpacity(_tmp.opacity * 0.92); group.userData.setOpacity(_tmp.opacity * 0.92);
group.userData.tick(t / 1000); group.userData.tick(t / 1000);
label.position.set(_tmp.position.x, _tmp.position.y + ghostHeight + 3, _tmp.position.z);
label.material.opacity = _tmp.opacity;
} }
if (followUid && active.has(followUid)) orbit.target.lerp(active.get(followUid).group.position, 0.08); if (followUid && active.has(followUid)) orbit.target.lerp(active.get(followUid).group.position, 0.08);
orbit.update(); orbit.update();