diff --git a/public/js/exhibit.js b/public/js/exhibit.js index 280cd82..d6c2b00 100644 --- a/public/js/exhibit.js +++ b/public/js/exhibit.js @@ -28,6 +28,12 @@ const CAM_CORR = { const CAM_MODES = ['none', 'y180', 'x180', 'z180']; let camMode = 'none'; // default: none — rely on pose.js conjugation alone +/* Diagnostic: freeze all ghost motion (bob/sway/wander/path) so ghosts sit at their + * static spawn position. Lets you tell tracking jitter apart from the built-in float + * animation — if a frozen ghost still moves, that's the camera pose, not the ghost. + * Defaults ON in dev; tap the bottom third of the HUD tracking line to toggle. */ +let freezeGhosts = false; + let info = { mode: 'dev' }; let scene3, camera3, renderer, video, canvas2d, ctx2d; let detector, poseEst, fuser; @@ -55,7 +61,7 @@ function updateDbg(fusedQuat, markerCount, spread) { const v3 = v => `(${v.x.toFixed(2)},${v.y.toFixed(2)},${v.z.toFixed(2)})`; const spreadLine = markerCount > 1 ? ` spread ${spread.toFixed(1)}cm` : ''; dbgEl.textContent = - `rot ${getRotMode()} cam ${camMode} (tap to cycle)\n` + + `rot ${getRotMode()} cam ${camMode} ${freezeGhosts ? 'FROZEN' : 'moving'}\n` + `markers ${markerCount}${spreadLine}\n${rawLine}\nview ${v3(view)}\nup ${v3(up)}\n` + `pos ${v3(camera3.position)}`; } @@ -139,6 +145,7 @@ async function start() { // dev-mode pose readout + tap-to-cycle correction modes if (info.mode === 'dev') { + freezeGhosts = true; // start static so tracking jitter is isolated from ghost float dbgEl = $('#dbg'); if (dbgEl) { dbgEl.style.display = 'block'; @@ -155,6 +162,12 @@ async function start() { } }); } + // tap the tracking HUD chip to toggle ghost motion freeze + const trk = $('#trk'); + if (trk) { + trk.style.pointerEvents = 'auto'; + trk.addEventListener('click', () => { freezeGhosts = !freezeGhosts; }); + } } // network @@ -278,10 +291,16 @@ function loop(t) { const now = Date.now() + clockOffset; for (const { rec, group } of activeGhosts.values()) { ghostTransform(rec, now, _tmp); - group.position.copy(_tmp.position); - group.rotation.y = _tmp.rotationY; + if (freezeGhosts) { + // hold at the static spawn position; keep only the fade opacity from _tmp + group.position.set(rec.pos[0], rec.pos[1], rec.pos[2]); + group.rotation.y = 0; + } else { + group.position.copy(_tmp.position); + group.rotation.y = _tmp.rotationY; + } group.userData.setOpacity(_tmp.opacity * 0.92); - group.userData.tick(t / 1000); + group.userData.tick(freezeGhosts ? 0 : t / 1000); } // HUD