Ai_Assistant/client/vr/vrPositionTracker.js

91 lines
2.3 KiB
JavaScript
Raw Normal View History

2026-05-24 13:31:30 +02:00
import * as THREE from 'three';
import { HTTP_URL } from '../config.js';
/**
* VRPositionTracker - Tracks the user's HMD position in VR world space.
* Reads camera world position each frame and periodically sends it to the server.
*/
export class VRPositionTracker {
constructor(camera, dolly, { sendInterval = 500 } = {}) {
this.camera = camera;
this.dolly = dolly;
this.sendInterval = sendInterval;
// Current position (updated every frame)
this.position = new THREE.Vector3();
this.rotation = new THREE.Euler();
// For throttled server updates
this._lastSendTime = 0;
this._enabled = false;
this._worldPos = new THREE.Vector3();
}
/** Start tracking and sending updates */
enable() {
this._enabled = true;
console.log('VR Position Tracker enabled');
}
/** Stop tracking */
disable() {
this._enabled = false;
}
/** Call every frame from the animate loop */
update() {
if (!this._enabled) return;
// Get camera world position (accounts for dolly + XR offset)
this.camera.getWorldPosition(this._worldPos);
this.position.copy(this._worldPos);
this.rotation.copy(this.camera.rotation);
// Throttled send to server
const now = performance.now();
if (now - this._lastSendTime >= this.sendInterval) {
this._lastSendTime = now;
this._sendPosition();
}
}
/** Get current position as a plain object */
getPosition() {
return {
x: this.position.x,
y: this.position.y,
z: this.position.z,
};
}
/** Get current rotation as a plain object */
getRotation() {
return {
x: this.rotation.x,
y: this.rotation.y,
z: this.rotation.z,
};
}
/** Send position to server (fire-and-forget) */
_sendPosition() {
const payload = {
x: parseFloat(this.position.x.toFixed(3)),
y: parseFloat(this.position.y.toFixed(3)),
z: parseFloat(this.position.z.toFixed(3)),
rx: parseFloat(this.rotation.x.toFixed(3)),
ry: parseFloat(this.rotation.y.toFixed(3)),
rz: parseFloat(this.rotation.z.toFixed(3)),
timestamp: Date.now(),
};
fetch(`${HTTP_URL}/vr/position`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
}).catch(() => {
// Silent fail - position updates are best-effort
});
}
}