fix: load scene over HTTP so tracking no longer depends on the WebSocket; HUD shows scene state and detected-vs-usable crests
This commit is contained in:
+36
-9
@@ -52,8 +52,10 @@ let detector, poseEst, fuser, cvSolver;
|
||||
let gradients = {}, modelManifest = null, characters = { defaults: {}, byId: {} };
|
||||
let ghostHeight = 4; // cm, from scene.ghostHeightCm
|
||||
const activeGhosts = new Map(); // uid -> { rec, group }
|
||||
let tracking = { markers: 0, lastSeen: 0 };
|
||||
let tracking = { markers: 0, raw: 0, rawIds: [], lastSeen: 0 };
|
||||
let netRef = null;
|
||||
let sceneLoaded = false;
|
||||
let anchorCount = 0;
|
||||
const rxLog = []; // last few WS message types, newest last
|
||||
function rxTrace(t) { rxLog.push(t); if (rxLog.length > 6) rxLog.shift(); }
|
||||
const clockOffsetSamples = [];
|
||||
@@ -71,7 +73,7 @@ let dbgPose = '';
|
||||
function dbgStatus() {
|
||||
const ws = netRef && netRef.ws ? ['conn','open','closing','closed'][netRef.ws.readyState] : 'none';
|
||||
return `ws ${ws} rx ${rxLog.join(',') || '-'}\n` +
|
||||
`ghost recs ${activeGhosts.size}\n`;
|
||||
`scene ${sceneLoaded ? anchorCount + ' anchors' : 'NOT LOADED'} ghost recs ${activeGhosts.size}\n`;
|
||||
}
|
||||
function paintDbg() { if (dbgEl) dbgEl.textContent = dbgStatus() + dbgPose + dbgAim; }
|
||||
|
||||
@@ -123,7 +125,7 @@ function updateDbg(fusedQuat, markerCount, extra) {
|
||||
dbgPose =
|
||||
`eng ${engine} ${freezeGhosts ? 'FROZEN' : 'moving'}\n` +
|
||||
`${modeLine}\n` +
|
||||
`markers ${markerCount}${extra}\n${rawLine}\nview ${v3(view)}\nup ${v3(up)}\n` +
|
||||
`markers ${markerCount}/${tracking.raw} seen [${tracking.rawIds.join(',')}]${extra}\n${rawLine}\nview ${v3(view)}\nup ${v3(up)}\n` +
|
||||
`pos ${v3(camera3.position)}`;
|
||||
paintDbg();
|
||||
}
|
||||
@@ -222,6 +224,13 @@ async function start() {
|
||||
cvSolver = new CvBoardSolver();
|
||||
ensureOpenCV();
|
||||
|
||||
// scene over HTTP so tracking works even if the WebSocket is down
|
||||
try {
|
||||
applyScene(await (await fetch('/api/scene')).json());
|
||||
} catch (e) {
|
||||
reportError({ kind: 'scene', msg: `scene fetch failed: ${(e && e.message) || e}` });
|
||||
}
|
||||
|
||||
// dev-mode pose readout + tap-to-cycle modes
|
||||
if (info.mode === 'dev') {
|
||||
freezeGhosts = true; // start static so tracking jitter is isolated from ghost float
|
||||
@@ -266,11 +275,7 @@ async function start() {
|
||||
const net = new ExhibitNet();
|
||||
netRef = net;
|
||||
const on = (t, fn) => net.on(t, (m) => { rxTrace(t); return fn(m); });
|
||||
on('scene', (m) => {
|
||||
fuser.setScene(m.scene); cvSolver.setScene(m.scene); buildOcclusion(m.scene);
|
||||
ghostHeight = m.scene.ghostHeightCm || 4;
|
||||
for (const e of activeGhosts.values()) e.group.userData.setHeight(ghostHeight);
|
||||
});
|
||||
on('scene', (m) => applyScene(m.scene));
|
||||
on('characters', async (m) => {
|
||||
characters = m.characters || characters;
|
||||
const recs = [...activeGhosts.values()].map(e => e.rec);
|
||||
@@ -301,6 +306,22 @@ function onResize() {
|
||||
camera3.updateProjectionMatrix();
|
||||
}
|
||||
|
||||
/* Scene application. Tracking must NOT depend on the WebSocket: detection filters
|
||||
* against the scene's marker IDs, so if the socket is slow or flapping the crest
|
||||
* is detected and then silently discarded ("Point at a Newbury Crest" forever).
|
||||
* We therefore load the scene over plain HTTP at start; the WS message just
|
||||
* refreshes it when an operator edits the layout. */
|
||||
function applyScene(scene) {
|
||||
if (!scene) return;
|
||||
sceneLoaded = true;
|
||||
anchorCount = (scene.anchors || []).filter(a => a.enabled !== false).length;
|
||||
fuser.setScene(scene);
|
||||
cvSolver.setScene(scene);
|
||||
buildOcclusion(scene);
|
||||
ghostHeight = scene.ghostHeightCm || 4;
|
||||
for (const e of activeGhosts.values()) e.group.userData.setHeight(ghostHeight);
|
||||
}
|
||||
|
||||
// ---------- occlusion: invisible depth-only building volumes ----------
|
||||
const occlusionGroup = new THREE.Group();
|
||||
function buildOcclusion(scene) {
|
||||
@@ -374,6 +395,8 @@ function loop(t) {
|
||||
|
||||
const markers = detectMarkers(detector, img, fuser.knownIds());
|
||||
tracking.markers = markers.length;
|
||||
tracking.raw = markers.rawCount || 0;
|
||||
tracking.rawIds = markers.rawIds || [];
|
||||
if (dbgEl) updateAim(markers, W, H);
|
||||
if (markers.length) {
|
||||
tracking.lastSeen = performance.now();
|
||||
@@ -421,7 +444,11 @@ function loop(t) {
|
||||
if ((frameCount++ & 15) === 0) {
|
||||
paintDbg();
|
||||
const stale = performance.now() - tracking.lastSeen > 1500;
|
||||
$('#trk').textContent = stale ? 'Point at a Newbury Crest' : `Tracking ${tracking.markers} crest${tracking.markers === 1 ? '' : 's'}`;
|
||||
$('#trk').textContent = !stale
|
||||
? `Tracking ${tracking.markers} crest${tracking.markers === 1 ? '' : 's'}`
|
||||
: (tracking.raw > 0
|
||||
? `Crest ${tracking.rawIds.join(',')} not in this layout` // detected, but no anchor for it
|
||||
: 'Point at a Newbury Crest');
|
||||
$('#trk').classList.toggle('warn', stale || tracking.markers < 2);
|
||||
$('#cnt').textContent = `${activeGhosts.size} ghost${activeGhosts.size === 1 ? '' : 's'} nearby`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user