Switchable camera-frame correction + tap-to-cycle rot/cam modes on debug overlay
This commit is contained in:
+37
-14
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
import { createDetector, detectMarkers, quadArea } from './ar/detect.js';
|
import { createDetector, detectMarkers, quadArea } from './ar/detect.js';
|
||||||
import { PoseEstimator } from './ar/pose.js';
|
import { PoseEstimator, ROT_MODES, setRotMode, getRotMode } from './ar/pose.js';
|
||||||
import { WorldFuser } from './ar/fuse.js';
|
import { WorldFuser } from './ar/fuse.js';
|
||||||
import { buildGhost } from './ghosts/loader.js';
|
import { buildGhost } from './ghosts/loader.js';
|
||||||
import { ghostTransform } from './ghosts/behavior.js';
|
import { ghostTransform } from './ghosts/behavior.js';
|
||||||
@@ -14,15 +14,19 @@ import { renderMarkdown } from './md.js';
|
|||||||
|
|
||||||
const $ = (s) => document.querySelector(s);
|
const $ = (s) => document.querySelector(s);
|
||||||
|
|
||||||
/* The fused pose is a camera-in-world transform in OpenCV convention
|
/* Camera-frame correction applied to the fused quaternion (see pose.js for the
|
||||||
* (+X right, +Y DOWN, +Z FORWARD along the view axis). A Three.js camera uses
|
* rotation-frame conjugation that happens upstream). Switchable for on-device A/B:
|
||||||
* OpenGL convention (+X right, +Y UP, looks down LOCAL -Z). The correct change of
|
* once pose.js conjugates the rotation correctly, the right camera correction here
|
||||||
* basis between them is a 180° rotation about the camera's local Y axis: it flips
|
* may be identity, Y180, X180, or Z180 depending on the net convention. The debug
|
||||||
* the forward axis (+Z_cv view -> three.js -Z) AND the up axis (+Y_cv down ->
|
* overlay cycles CAM_MODES. */
|
||||||
* three.js +Y up) together, leaving roll correct. (A 180° about X fixes the view
|
const CAM_CORR = {
|
||||||
* axis too but leaves roll inverted — ghosts render upside-down when the phone is
|
none: new THREE.Quaternion(),
|
||||||
* upright.) Applied after copying the fused quaternion each frame. */
|
y180: new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI),
|
||||||
const CV_TO_GL = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), Math.PI);
|
x180: new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.PI),
|
||||||
|
z180: new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI),
|
||||||
|
};
|
||||||
|
const CAM_MODES = ['none', 'y180', 'x180', 'z180'];
|
||||||
|
let camMode = 'none'; // default: none — rely on pose.js conjugation alone
|
||||||
|
|
||||||
let info = { mode: 'dev' };
|
let info = { mode: 'dev' };
|
||||||
let scene3, camera3, renderer, video, canvas2d, ctx2d;
|
let scene3, camera3, renderer, video, canvas2d, ctx2d;
|
||||||
@@ -50,6 +54,7 @@ function updateDbg(fusedQuat, markerCount) {
|
|||||||
const up = _dbgV.set(0, 1, 0).applyQuaternion(camera3.quaternion).clone();
|
const up = _dbgV.set(0, 1, 0).applyQuaternion(camera3.quaternion).clone();
|
||||||
const v3 = v => `(${v.x.toFixed(2)},${v.y.toFixed(2)},${v.z.toFixed(2)})`;
|
const v3 = v => `(${v.x.toFixed(2)},${v.y.toFixed(2)},${v.z.toFixed(2)})`;
|
||||||
dbgEl.textContent =
|
dbgEl.textContent =
|
||||||
|
`rot ${getRotMode()} cam ${camMode} (tap to cycle)\n` +
|
||||||
`markers ${markerCount}\n${rawLine}\nview ${v3(view)}\nup ${v3(up)}\n` +
|
`markers ${markerCount}\n${rawLine}\nview ${v3(view)}\nup ${v3(up)}\n` +
|
||||||
`pos ${v3(camera3.position)}`;
|
`pos ${v3(camera3.position)}`;
|
||||||
}
|
}
|
||||||
@@ -131,8 +136,25 @@ async function start() {
|
|||||||
detector = createDetector();
|
detector = createDetector();
|
||||||
fuser = new WorldFuser();
|
fuser = new WorldFuser();
|
||||||
|
|
||||||
// dev-mode pose readout
|
// dev-mode pose readout + tap-to-cycle correction modes
|
||||||
if (info.mode === 'dev') { dbgEl = $('#dbg'); if (dbgEl) dbgEl.style.display = 'block'; }
|
if (info.mode === 'dev') {
|
||||||
|
dbgEl = $('#dbg');
|
||||||
|
if (dbgEl) {
|
||||||
|
dbgEl.style.display = 'block';
|
||||||
|
dbgEl.style.pointerEvents = 'auto';
|
||||||
|
// tap top ~60% cycles rotation mode; tap bottom cycles camera mode
|
||||||
|
dbgEl.addEventListener('click', (ev) => {
|
||||||
|
const r = dbgEl.getBoundingClientRect();
|
||||||
|
if ((ev.clientY - r.top) < r.height * 0.5) {
|
||||||
|
const i = ROT_MODES.indexOf(getRotMode());
|
||||||
|
setRotMode(ROT_MODES[(i + 1) % ROT_MODES.length]);
|
||||||
|
} else {
|
||||||
|
const i = CAM_MODES.indexOf(camMode);
|
||||||
|
camMode = CAM_MODES[(i + 1) % CAM_MODES.length];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// network
|
// network
|
||||||
const net = new ExhibitNet();
|
const net = new ExhibitNet();
|
||||||
@@ -241,8 +263,9 @@ function loop(t) {
|
|||||||
const fused = fuser.fuse(estimates);
|
const fused = fuser.fuse(estimates);
|
||||||
if (fused) {
|
if (fused) {
|
||||||
camera3.position.copy(fused.position);
|
camera3.position.copy(fused.position);
|
||||||
// convert OpenCV camera frame -> Three.js (OpenGL) camera frame
|
// pose.js already conjugated rotation into the three.js frame; apply any
|
||||||
camera3.quaternion.copy(fused.quaternion).multiply(CV_TO_GL);
|
// residual camera-frame correction (switchable via overlay)
|
||||||
|
camera3.quaternion.copy(fused.quaternion).multiply(CAM_CORR[camMode]);
|
||||||
updateDbg(fused.quaternion, fused.markerCount);
|
updateDbg(fused.quaternion, fused.markerCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user