1
0
Files
VR-Web-Player/tests/projection.test.mjs
Aiden c28386ccdd
All checks were successful
Test / test (push) Successful in 9m33s
New screen behaviour
2026-06-10 14:58:26 +10:00

162 lines
4.8 KiB
JavaScript

import assert from 'node:assert/strict';
import test from 'node:test';
import {
applyHeadPositionLock,
applySbsTextureWindow,
hideContentMeshes,
isLeftEyeCamera,
positionPlaneForPresentation,
resetHeadPositionLockedContent,
shouldLockContentToHeadPosition,
showActiveContentMesh
} from '../vr180player/rendering/projection.js';
function createMaterial() {
return {
map: {
offset: { x: 99, y: 99 },
repeat: { x: 99, y: 99 }
}
};
}
function createRenderer({ isPresenting = false, xrCamera = null } = {}) {
return {
xr: {
getCamera: () => xrCamera,
isPresenting
}
};
}
function createCamera(x, projectionOffset = 0) {
return {
matrixWorldInverse: { elements: new Array(16).fill(0).with(12, x) },
matrixWorld: { elements: new Array(16).fill(0).with(12, x).with(13, 1.7).with(14, 0.25) },
projectionMatrix: { elements: new Array(16).fill(0).with(8, projectionOffset) }
};
}
function createPositionedMesh() {
const calls = [];
return {
calls,
position: {
set: (...args) => calls.push(args)
}
};
}
test('applySbsTextureWindow uses left eye only in non-XR 2D fallback', () => {
const material = createMaterial();
applySbsTextureWindow(createRenderer(), createCamera(0), material, true);
assert.equal(material.map.offset.x, 0);
assert.equal(material.map.repeat.x, 0.5);
assert.equal(material.map.offset.y, 0);
assert.equal(material.map.repeat.y, 1);
});
test('applySbsTextureWindow keeps the full texture outside XR when not in 2D fallback', () => {
const material = createMaterial();
applySbsTextureWindow(createRenderer(), createCamera(0), material, false);
assert.equal(material.map.offset.x, 0);
assert.equal(material.map.repeat.x, 1);
assert.equal(material.map.offset.y, 0);
assert.equal(material.map.repeat.y, 1);
});
test('applySbsTextureWindow selects the right SBS half for the right XR eye', () => {
const leftCamera = createCamera(-0.03);
const rightCamera = createCamera(0.03);
const renderer = createRenderer({
isPresenting: true,
xrCamera: { cameras: [leftCamera, rightCamera] }
});
const material = createMaterial();
applySbsTextureWindow(renderer, rightCamera, material, false);
assert.equal(material.map.offset.x, 0.5);
assert.equal(material.map.repeat.x, 0.5);
});
test('isLeftEyeCamera falls back to projection matrix offset when XR cameras are unavailable', () => {
assert.equal(isLeftEyeCamera(createRenderer({ isPresenting: true }), createCamera(0, -0.1)), true);
assert.equal(isLeftEyeCamera(createRenderer({ isPresenting: true }), createCamera(0, 0.1)), false);
});
test('showActiveContentMesh hides inactive meshes before showing the active mesh', () => {
const vr180Mesh = { visible: true };
const planeMesh = { visible: true };
showActiveContentMesh(vr180Mesh, planeMesh, planeMesh);
assert.equal(vr180Mesh.visible, false);
assert.equal(planeMesh.visible, true);
hideContentMeshes(vr180Mesh, planeMesh);
assert.equal(vr180Mesh.visible, false);
assert.equal(planeMesh.visible, false);
});
test('positionPlaneForPresentation uses the fallback camera depth in 2D plane mode', () => {
const calls = [];
const planeMesh = {
position: {
set: (...args) => calls.push(args)
}
};
positionPlaneForPresentation(planeMesh, { position: { z: 0.1 } }, true, 3, 1.2);
positionPlaneForPresentation(planeMesh, { position: { z: 0.1 } }, false, 3, 1.2);
assert.deepEqual(calls[0], [0, 1.6, -1.0999999999999999]);
assert.deepEqual(calls[1], [0, 1.6, -3]);
});
test('shouldLockContentToHeadPosition defaults to VR180 only in auto mode', () => {
assert.equal(shouldLockContentToHeadPosition('auto', 'vr180'), true);
assert.equal(shouldLockContentToHeadPosition('auto', 'plane'), false);
assert.equal(shouldLockContentToHeadPosition('position', 'plane'), true);
assert.equal(shouldLockContentToHeadPosition('none', 'vr180'), false);
});
test('applyHeadPositionLock centers VR180 content on the XR camera position', () => {
const mesh = createPositionedMesh();
applyHeadPositionLock(mesh, createCamera(0.4), 'vr180', true, 3);
assert.deepEqual(mesh.calls[0], [0.4, 1.7, 0.25]);
});
test('applyHeadPositionLock keeps opt-in plane content in front of the XR camera position', () => {
const mesh = createPositionedMesh();
applyHeadPositionLock(mesh, createCamera(-0.25), 'plane', true, 3);
assert.deepEqual(mesh.calls[0], [-0.25, 1.7, -2.75]);
});
test('applyHeadPositionLock leaves content untouched when disabled', () => {
const mesh = createPositionedMesh();
applyHeadPositionLock(mesh, createCamera(0.4), 'vr180', false, 3);
assert.deepEqual(mesh.calls, []);
});
test('resetHeadPositionLockedContent restores default mesh positions', () => {
const vr180Mesh = createPositionedMesh();
const planeMesh = createPositionedMesh();
resetHeadPositionLockedContent(vr180Mesh, planeMesh, 3);
assert.deepEqual(vr180Mesh.calls[0], [0, 0, 0]);
assert.deepEqual(planeMesh.calls[0], [0, 1.6, -3]);
});