feat: dashcam photo mode with telemetry frame, lens drift and postcard capture

This commit is contained in:
2026-10-11 07:49:56 +03:00
parent d71b0ecfd4
commit 568b0802b4
29 changed files with 1472 additions and 7 deletions

View File

@@ -43,6 +43,8 @@ import { isMilestoneSection, sectionLocalProgress } from '@/journey/route-log';
import { dawnStep, type DawnStep } from '@/journey/dawn';
import { dawnLightUniforms } from '@/scene/world/dawn-light';
import { isTestFault, recordTestFaultEvent } from '@/testing/fault-hooks';
import { readDashcam, takeDashcamCapture } from '@/journey/dashcam-feed';
import { createDashcamLens } from '@/scene/world/dashcam-lens';
declare const __PORTFOLIO_TEST_BUILD__: boolean;
const TEST_BUILD =
@@ -244,6 +246,8 @@ export async function createProceduralSceneGeneration(
let riseUntil = 0;
let gantryState = '';
let dawnState: DawnStep | '' = '';
// DASHCAM: the pointer drifts the lens and the letterbox closes while open.
const dashcamLens = createDashcamLens();
const resize = (): void => {
const nextWidth = Math.max(1, canvas.clientWidth || canvas.width || 1);
@@ -651,6 +655,7 @@ export async function createProceduralSceneGeneration(
THREE.MathUtils.clamp((introProgress - 0.06) / 0.94, 0, 1),
) * (1 - outro);
const craneShift = cameraState.shift * crane;
const dashcam = dashcamLens.update(readDashcam(), deltaSeconds, now);
camera.setViewOffset(
width,
height,
@@ -660,13 +665,13 @@ export async function createProceduralSceneGeneration(
height,
);
camera.position.set(
THREE.MathUtils.lerp(0, cameraState.x, crane),
THREE.MathUtils.lerp(0, cameraState.x, crane) + dashcam.x,
THREE.MathUtils.lerp(46, cameraState.y, crane),
THREE.MathUtils.lerp(92, cameraState.z, crane),
);
camera.lookAt(
THREE.MathUtils.lerp(0, cameraState.targetX, crane),
THREE.MathUtils.lerp(64, cameraState.targetY, crane),
THREE.MathUtils.lerp(64, cameraState.targetY, crane) + dashcam.targetY,
THREE.MathUtils.lerp(-720, cameraState.targetZ, crane),
);
camera.rotation.z += cameraState.roll * crane;
@@ -737,7 +742,10 @@ export async function createProceduralSceneGeneration(
postProcessor.setDynamics(response.bloomEnergy, dawnRims ? dawn : 0);
postProcessor.setLens({
time: elapsedSeconds,
letterbox: 0.115 * (1 - THREE.MathUtils.smoothstep(introProgress, 0.5, 1)),
letterbox: Math.max(
0.115 * (1 - THREE.MathUtils.smoothstep(introProgress, 0.5, 1)),
dashcam.letterbox,
),
energy: overdrive,
rain: service.rain,
rainTime: elapsedSeconds,
@@ -747,6 +755,15 @@ export async function createProceduralSceneGeneration(
} else {
renderer.render(scene, camera);
}
// DASHCAM: the drawing buffer is only valid now, inside this task.
const capture = takeDashcamCapture();
if (capture !== null) {
try {
capture(canvas);
} catch {
// A failed postcard never takes the scene down.
}
}
renderedFrames += 1;
},
dispose() {