fix(virtual): cyclorama extent for east/west coves + never frustum-cull the cove

east/west coves run only the cyclorama section (extent from the north wall) instead
of the full L-shaped depth; big static swept surface gets frustumCulled=false.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
type-two 2026-07-02 23:07:28 +10:00
parent d0c91faf79
commit d46025e887

View File

@ -126,7 +126,7 @@ function slopedSide(x, ch, cd, hf, th, m) {
// Cyclorama / infinity cove — the floor curves up into the named wall(s) via a fillet of radius R,
// with a white floor lead-in, so there's no hard floor↔wall seam (the seamless studio look). Each
// wall gets one swept profile: [wall down to R] → [quarter-circle fillet] → [flat floor lead-in].
function buildCyclorama(walls, R, lead, W, D, H, mat) {
function buildCyclorama(walls, R, lead, W, D, H, mat, extent) {
const prof = [];
for (let i = 0; i <= 6; i++) prof.push([0, H - (H - R) * i / 6]); // wall: u=0, v H→R
for (let i = 1; i <= 12; i++) { const t = Math.PI + (Math.PI / 2) * i / 12; prof.push([R + R * Math.cos(t), R + R * Math.sin(t)]); } // fillet R→0
@ -141,14 +141,18 @@ function buildCyclorama(walls, R, lead, W, D, H, mat) {
};
walls.forEach(wall => {
const mp = maps[wall]; if (!mp) return;
// east/west coves only run the cyclorama section (extent from the north wall), not the full L-shaped depth
let s0 = -mp.len / 2, s1 = mp.len / 2;
if ((wall === 'east' || wall === 'west') && extent > 0) { s0 = -D / 2; s1 = -D / 2 + extent; }
const pos = [];
[-mp.len / 2, mp.len / 2].forEach(s => prof.forEach(([u, v]) => { const p = mp.f(s, u, v); pos.push(p[0], p[1], p[2]); }));
[s0, s1].forEach(s => prof.forEach(([u, v]) => { const p = mp.f(s, u, v); pos.push(p[0], p[1], p[2]); }));
const idx = [];
for (let p = 0; p < P - 1; p++) idx.push(p, P + p, p + 1, p + 1, P + p, P + p + 1);
const geo = new THREE.BufferGeometry();
geo.setAttribute('position', new THREE.BufferAttribute(new Float32Array(pos), 3));
geo.setIndex(idx); geo.computeVertexNormals();
grp.add(new THREE.Mesh(geo, mat));
geo.setIndex(idx); geo.computeVertexNormals(); geo.computeBoundingSphere();
const me = new THREE.Mesh(geo, mat); me.frustumCulled = false; // big static surface — never cull it
grp.add(me);
});
return grp;
}
@ -255,7 +259,7 @@ function buildScene(data) {
const R = num(sp.cyclorama_radius, 0.9);
const cmat = material(sp.cyclorama_color || '#ffffff', '#ffffff');
cmat.side = THREE.DoubleSide; cmat.roughness = 0.92; cmat.metalness = 0;
roomGroup.add(buildCyclorama(cyc, R, Math.max(R + 0.3, 1.2), W, D, H, cmat));
roomGroup.add(buildCyclorama(cyc, R, Math.max(R + 0.3, 1.2), W, D, H, cmat, num(sp.cyclorama_extent, 0)));
}
// lights (data-driven + a soft ambient so it's never pitch black)