update Tue 07/14/2026 12:21:23.58

This commit is contained in:
2026-07-14 12:21:24 +10:00
parent b2b555ef16
commit f2bfad7ee2
14 changed files with 202 additions and 58 deletions
+3 -3
View File
@@ -41,13 +41,13 @@ export class WorldFuser {
/** estimates: [{ markerId, position(mm), quaternion, area }] */
fuse(estimates) {
const MM = 0.001;
const MM2CM = 0.1; // POS-IT translation is in mm; world units are cm
const cams = [];
for (const e of estimates) {
const entry = this.anchorMats.get(e.markerId);
if (!entry) continue;
// marker pose in camera space -> matrix (translate mm->m)
_m.compose(_p.copy(e.position).multiplyScalar(MM), e.quaternion, _s.set(1, 1, 1));
// marker pose in camera space -> matrix (translate mm->cm)
_m.compose(_p.copy(e.position).multiplyScalar(MM2CM), e.quaternion, _s.set(1, 1, 1));
_inv.copy(_m).invert(); // camera in marker space
const camWorld = new THREE.Matrix4().multiplyMatrices(entry.mat, _inv);
const pos = new THREE.Vector3();
+8 -1
View File
@@ -10,6 +10,13 @@ import * as THREE from 'three';
const _m = new THREE.Matrix4();
const _q = new THREE.Quaternion();
/* Input convention (matches the v1-validated fix): raw centered image coords are
* passed to POS-IT — NO Y pre-flip. The -t[1]/-t[2] negation below is what converts
* to the Three.js camera frame. Pre-flipping Y here double-flips the vertical axis
* and inverts pitch response (ghosts move opposite when tilting the phone).
* If tilt ever reads inverted on a device, toggle this for a quick A/B test. */
const FLIP_INPUT_Y = false;
export class PoseEstimator {
constructor(focalLength) {
this.focal = focalLength;
@@ -26,7 +33,7 @@ export class PoseEstimator {
/** corners: aruco marker corners, image-space; cx/cy: image center.
* Returns { position: THREE.Vector3 (mm, marker->camera), quaternion, error } */
estimate(markerId, corners, cx, cy, sizeMM) {
const centered = corners.map(c => ({ x: c.x - cx, y: (cy - c.y) }));
const centered = corners.map(c => ({ x: c.x - cx, y: FLIP_INPUT_Y ? (cy - c.y) : (c.y - cy) }));
const pose = this.positFor(sizeMM).pose(centered);
if (!pose) return null;
+1 -1
View File
@@ -53,7 +53,7 @@ async function start() {
renderer = new THREE.WebGLRenderer({ canvas: $('#gl'), alpha: true, antialias: true });
renderer.setPixelRatio(Math.min(devicePixelRatio, 2));
scene3 = new THREE.Scene();
camera3 = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 0.02, 50);
camera3 = new THREE.PerspectiveCamera(60, innerWidth / innerHeight, 2, 5000);
scene3.add(new THREE.AmbientLight(0xffffff, 1.2));
onResize(); addEventListener('resize', onResize);
+3 -3
View File
@@ -17,14 +17,14 @@ export function ghostTransform(rec, nowMs, out) {
if (b.type === 'wander') {
const s = b.seed || 1;
const R = b.radius ?? 0.6;
const R = b.radius ?? 60; // cm
const v = b.speed ?? 0.15;
// smooth pseudo-random orbit-drift: two incommensurate sines per axis
const ph = t * v;
out.position.set(
base.x + R * 0.9 * Math.sin(ph * 1.0 + hashNoise(s, 1) * 6.28) * 0.7
+ R * 0.4 * Math.sin(ph * 2.3 + hashNoise(s, 2) * 6.28) * 0.3,
base.y + 0.06 * Math.sin(t * 1.7 + hashNoise(s, 3) * 6.28),
base.y + 6 * Math.sin(t * 1.7 + hashNoise(s, 3) * 6.28),
base.z + R * 0.9 * Math.cos(ph * 0.8 + hashNoise(s, 4) * 6.28) * 0.7
+ R * 0.4 * Math.cos(ph * 1.9 + hashNoise(s, 5) * 6.28) * 0.3
);
@@ -37,7 +37,7 @@ export function ghostTransform(rec, nowMs, out) {
const dir = ahead(t + eps).sub(ahead(t));
out.rotationY = Math.atan2(dir.x, dir.z);
} else {
const amp = b.bobAmp ?? 0.06;
const amp = b.bobAmp ?? 6; // cm
const hz = b.bobHz ?? 0.4;
out.position.set(base.x, base.y + amp * Math.sin(t * hz * Math.PI * 2), base.z);
out.rotationY = 0.25 * Math.sin(t * 0.3); // slow idle sway
+4 -4
View File
@@ -59,12 +59,12 @@ async function loadOBJ(url) {
function proceduralWisp() {
const g = new THREE.Group();
const body = new THREE.Mesh(new THREE.SphereGeometry(0.09, 20, 16));
const body = new THREE.Mesh(new THREE.SphereGeometry(9, 20, 16));
body.scale.set(1, 1.35, 1);
body.position.y = 0.14;
const tail = new THREE.Mesh(new THREE.ConeGeometry(0.07, 0.16, 16));
body.position.y = 14;
const tail = new THREE.Mesh(new THREE.ConeGeometry(7, 16, 16));
tail.rotation.x = Math.PI;
tail.position.y = 0.0;
tail.position.y = 0;
g.add(body, tail);
return g;
}