mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
SCENES > VCAM pairs a phone through a QR code and turns it into a handheld camera, after Unreal's Live Link VCAM and Blender's VirtuCamera: - Android Chrome tracks 6DoF through WebXR immersive-ar; iPhone Safari, which has no WebXR AR, tracks rotation through DeviceOrientation, and thumb sticks dolly, truck, crane and pan. - A motion scale (1:1 walk to 1:10000 orbit), smoothing, height and horizon locks, hold/clutch and recenter, none of which make the camera jump. Focal length and scale-aware depth of field with tap-to-focus. - The phone shows the app's render as a monitor (newest-frame JPEG pull). - REC records a take as a version-7 track shot in the selected scene. - The rig claims the camera through the navigation seam (Cockpit refuses it) and holds on any other navigation, scene run or globe input. Pairing never exposes the app: a loopback-only session request starts a separate HTTPS listener (VCAM_PORT, default 4174) that serves only the static phone page and a token-paired relay, and stops after the last session. Its certificate comes from a local CA built with node:crypto, which an iPhone can trust once; without it the phone falls back from WebSocket to HTTPS requests, which iOS allows past a certificate warning. scripts/qa-vcam.mjs drives it end to end: the real phone page over the HTTPS fallback, a scripted 6DoF phone, a recorded and replayed take, depth of field and camera handoff. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_015H1iLusTzWpVKitGic2AW8
46 lines
1.5 KiB
JavaScript
46 lines
1.5 KiB
JavaScript
/**
|
|
* Device orientation to the virtual camera's Y-up pose frame (+x right,
|
|
* +y up, -z forward), shared by the phone page and its tests.
|
|
*/
|
|
export const quat = {
|
|
multiply(a, b) {
|
|
const [ax, ay, az, aw] = a;
|
|
const [bx, by, bz, bw] = b;
|
|
return [
|
|
aw * bx + ax * bw + ay * bz - az * by,
|
|
aw * by - ax * bz + ay * bw + az * bx,
|
|
aw * bz + ax * by - ay * bx + az * bw,
|
|
aw * bw - ax * bx - ay * by - az * bz,
|
|
];
|
|
},
|
|
axisAngle(axis, angle) {
|
|
const s = Math.sin(angle / 2);
|
|
return [axis[0] * s, axis[1] * s, axis[2] * s, Math.cos(angle / 2)];
|
|
},
|
|
};
|
|
const DEG = Math.PI / 180;
|
|
// Device orientation is device→earth (x east, y north, z up). Rotating -90°
|
|
// about x maps that earth frame to the Y-up frame (north becomes -z).
|
|
const EARTH_TO_Y_UP = quat.axisAngle([1, 0, 0], -Math.PI / 2);
|
|
|
|
/** W3C DeviceOrientation (Z-X'-Y'') to a Y-up quaternion for the current screen orientation. */
|
|
export function orientationQuaternion(alpha, beta, gamma, screenAngle) {
|
|
const x = (beta || 0) * DEG;
|
|
const y = (gamma || 0) * DEG;
|
|
const z = (alpha || 0) * DEG;
|
|
const cX = Math.cos(x / 2);
|
|
const cY = Math.cos(y / 2);
|
|
const cZ = Math.cos(z / 2);
|
|
const sX = Math.sin(x / 2);
|
|
const sY = Math.sin(y / 2);
|
|
const sZ = Math.sin(z / 2);
|
|
const device = [
|
|
sX * cY * cZ - cX * sY * sZ,
|
|
cX * sY * cZ + sX * cY * sZ,
|
|
cX * cY * sZ + sX * sY * cZ,
|
|
cX * cY * cZ - sX * sY * sZ,
|
|
];
|
|
const screen = quat.axisAngle([0, 0, 1], -(screenAngle || 0) * DEG);
|
|
return quat.multiply(EARTH_TO_Y_UP, quat.multiply(device, screen));
|
|
}
|