fix(scene): land the opening crane on scroll or stutter and prewarm cut shaders
This commit is contained in:
@@ -187,6 +187,9 @@ export async function createProceduralSceneGeneration(
|
|||||||
// Opening shot: a crane down from the sun to the chase position. It plays
|
// Opening shot: a crane down from the sun to the chase position. It plays
|
||||||
// once, only when the visit starts at the top, and a scroll hurries it.
|
// once, only when the visit starts at the top, and a scroll hurries it.
|
||||||
let introProgress: number | null = null;
|
let introProgress: number | null = null;
|
||||||
|
let introHurried = false;
|
||||||
|
let introFrames = 0;
|
||||||
|
let introSlowFrames = 0;
|
||||||
|
|
||||||
const resize = (): void => {
|
const resize = (): void => {
|
||||||
const nextWidth = Math.max(1, canvas.clientWidth || canvas.width || 1);
|
const nextWidth = Math.max(1, canvas.clientWidth || canvas.width || 1);
|
||||||
@@ -261,10 +264,8 @@ export async function createProceduralSceneGeneration(
|
|||||||
throw new Error('Injected post-first-frame exception');
|
throw new Error('Injected post-first-frame exception');
|
||||||
}
|
}
|
||||||
resize();
|
resize();
|
||||||
const deltaSeconds =
|
const wallSeconds = lastNow === null ? 0 : Math.max(0, (now - lastNow) / 1000);
|
||||||
lastNow === null
|
const deltaSeconds = Math.min(0.1, wallSeconds);
|
||||||
? 0
|
|
||||||
: Math.min(0.1, Math.max(0, (now - lastNow) / 1000));
|
|
||||||
lastNow = now;
|
lastNow = now;
|
||||||
dynamics.update(snapshot.acceleration, snapshot.direction, deltaSeconds);
|
dynamics.update(snapshot.acceleration, snapshot.direction, deltaSeconds);
|
||||||
const response = dynamics.snapshot();
|
const response = dynamics.snapshot();
|
||||||
@@ -514,10 +515,25 @@ export async function createProceduralSceneGeneration(
|
|||||||
if (introProgress === null) {
|
if (introProgress === null) {
|
||||||
introProgress =
|
introProgress =
|
||||||
snapshot.activeSectionId === 'start' && snapshot.scrollY < 40 ? 0 : 1;
|
snapshot.activeSectionId === 'start' && snapshot.scrollY < 40 ? 0 : 1;
|
||||||
|
// The crane opens on the sky, so the road, car and roadside would
|
||||||
|
// otherwise compile their shaders mid-drive when they first enter the
|
||||||
|
// frame. Compile every visible object now, as the chase framing would.
|
||||||
|
if (introProgress === 0) renderer.compile(scene, camera);
|
||||||
}
|
}
|
||||||
if (introProgress < 1) {
|
if (introProgress < 1) {
|
||||||
const hurry = snapshot.scrollY > 40 ? 5 : 1;
|
// The shot belongs to a visitor who waits at the top. Once they scroll
|
||||||
introProgress = Math.min(1, introProgress + (deltaSeconds * hurry) / INTRO_SECONDS);
|
// or navigate it lands within about 0.4 s of wall time, so the chase
|
||||||
|
// framing (and every sign placed against it) is back before arrival.
|
||||||
|
if (snapshot.scrollY > 40 || snapshot.activeSectionId !== 'start') introHurried = true;
|
||||||
|
// A crane that stutters is worse than none: on a device that cannot
|
||||||
|
// hold the shot smoothly, land it straight away.
|
||||||
|
// The first frames carry shader compilation and a lone hitch (a late
|
||||||
|
// texture upload) is forgiven; three slow frames end the shot.
|
||||||
|
introFrames += 1;
|
||||||
|
if (introFrames > 3 && wallSeconds > 0.1) introSlowFrames += 1;
|
||||||
|
if (introSlowFrames >= 3) introHurried = true;
|
||||||
|
const step = introHurried ? Math.min(0.5, wallSeconds) / 0.4 : deltaSeconds / INTRO_SECONDS;
|
||||||
|
introProgress = Math.min(1, introProgress + step);
|
||||||
}
|
}
|
||||||
const crane = easeInOutCubic(
|
const crane = easeInOutCubic(
|
||||||
THREE.MathUtils.clamp((introProgress - 0.06) / 0.94, 0, 1),
|
THREE.MathUtils.clamp((introProgress - 0.06) / 0.94, 0, 1),
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ export function createExitGates(resources: SceneResourceBag) {
|
|||||||
|
|
||||||
let progress = 1;
|
let progress = 1;
|
||||||
let previous: SectionId | null = null;
|
let previous: SectionId | null = null;
|
||||||
|
let warmFrames = 2;
|
||||||
|
|
||||||
const place = (index: number, x: number, y: number, z: number, sx: number, sy: number) => {
|
const place = (index: number, x: number, y: number, z: number, sx: number, sy: number) => {
|
||||||
dummy.position.set(x, y, z);
|
dummy.position.set(x, y, z);
|
||||||
@@ -94,6 +95,14 @@ export function createExitGates(resources: SceneResourceBag) {
|
|||||||
if (progress >= 0.7) progress = 0;
|
if (progress >= 0.7) progress = 0;
|
||||||
}
|
}
|
||||||
previous = section;
|
previous = section;
|
||||||
|
if (warmFrames > 0) {
|
||||||
|
// Draw the gates unlit for the first frames so the shader program
|
||||||
|
// compiles at the on-ramp, not as a stall on the first exit.
|
||||||
|
warmFrames -= 1;
|
||||||
|
uniforms.uIntensity.value = 0;
|
||||||
|
group.visible = true;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
if (progress >= 1) {
|
if (progress >= 1) {
|
||||||
group.visible = false;
|
group.visible = false;
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user