EmboDi / src /client /ui /creation-layout.mjs
xlt99's picture
Upload 180 files
970fc3e verified
Raw History Blame Contribute Delete
4 kB
import * as THREE from "three";
import { isFloor } from "../../shared/entry-points.mjs";
function scanPresentationBounds(definition) {
const bounds = new THREE.Box3();
for (const o of (definition?.objects || []).filter(isFloor)) {
const c = Math.cos(o.rotation), s = Math.sin(o.rotation);
for (const x of [-o.size[0] / 2, o.size[0] / 2]) for (const z of [-o.size[2] / 2, o.size[2] / 2]) bounds.expandByPoint(new THREE.Vector3(o.position[0] + c * x + s * z, 0, o.position[2] - s * x + c * z));
}
return { center: bounds.getCenter(new THREE.Vector3()).toArray(), extent: Math.max(...bounds.getSize(new THREE.Vector3()).toArray()) };
}
function createCreationLayout(panel, world) {
let yaw = 0, hasYaw = false, stableSince = null, start = null, previous = null;
const up = new THREE.Vector3(0, 1, 0);
function sample(view) {
const position = view.getWorldPosition(new THREE.Vector3()), q = view.getWorldQuaternion(new THREE.Quaternion());
const forward = new THREE.Vector3(0, 0, -1).applyQuaternion(q);
forward.y = 0;
const horizontal = forward.lengthSq() > 0.04;
if (horizontal) {
yaw = Math.atan2(-forward.x, -forward.z);
hasYaw = true;
}
return { position, yaw, horizontal };
}
return {
reset() {
stableSince = start = null;
previous = null;
hasYaw = false;
yaw = 0;
},
ready(view, time, valid = true) {
if (!valid) {
stableSince = start = null;
previous = null;
return false;
}
const pose = sample(view);
start ??= time;
if (!hasYaw) return false;
const angle = previous ? Math.atan2(Math.sin(pose.yaw - previous.yaw), Math.cos(pose.yaw - previous.yaw)) : 0;
if (!previous || Math.abs(angle) > 0.04 || pose.position.distanceTo(previous.position) > 0.04) {
stableSince = time;
previous = pose;
}
return time - stableSince >= 250 || time - start >= 1500;
},
place(view, { miniature = false, center = [0, 0, 0], rotationOffset = 0 } = {}) {
const { position, yaw: yaw2 } = sample(view), forward = new THREE.Vector3(-Math.sin(yaw2), 0, -Math.cos(yaw2));
panel.position.copy(position).addScaledVector(forward, miniature ? 1.25 : 1.5);
panel.position.y -= miniature ? 0.64 : 0.18;
panel.quaternion.setFromAxisAngle(up, yaw2);
panel.visible = true;
panel.updateMatrixWorld(true);
if (miniature) {
world.quaternion.setFromAxisAngle(up, yaw2 + rotationOffset);
const offset = new THREE.Vector3().fromArray(center).multiply(world.scale).applyQuaternion(world.quaternion);
world.position.copy(position).addScaledVector(forward, 1.9).sub(offset);
world.position.y -= 0.48;
world.updateMatrixWorld(true);
}
}
};
}
function alignedRoomPose(candidate, ground) {
const yaw = candidate.yaw - (ground?.rotation || 0), q = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), yaw);
const local = new THREE.Vector3(ground?.position[0] || 0, ground ? ground.position[1] + ground.size[1] / 2 : 0, ground?.position[2] || 0).applyQuaternion(q);
return { position: new THREE.Vector3().fromArray(candidate.origin).sub(local).toArray(), yaw };
}
function captureCreationPose(world, rig, camera, alignedMode) {
const copy = (o) => ({ position: o.position.toArray(), quaternion: o.quaternion.toArray(), scale: o.scale.toArray() });
return { world: copy(world), rig: copy(rig), camera: copy(camera), alignedMode };
}
function restoreCreationPose(saved, world, rig, camera, { xr = false } = {}) {
const restore = (o, p) => {
o.position.fromArray(p.position);
o.quaternion.fromArray(p.quaternion);
o.scale.fromArray(p.scale);
o.updateMatrixWorld(true);
};
restore(world, saved.world);
restore(rig, saved.rig);
if (!xr) restore(camera, saved.camera);
}
export {
alignedRoomPose,
captureCreationPose,
createCreationLayout,
restoreCreationPose,
scanPresentationBounds
};