@@ -54,7 +54,8 @@ export function createDatabaseScene({
5454 contextLost = false ,
5555 frame = 0 ,
5656 last = 0 ,
57- time = 0 ;
57+ time = 0 ,
58+ renderedFrames = 0 ;
5859 let renderedZoom = 0 ,
5960 visible = document . visibilityState !== "hidden" ,
6061 loaded = false ,
@@ -91,7 +92,7 @@ export function createDatabaseScene({
9192 renderer . setClearColor ( 0x000000 , 0 ) ;
9293 renderer . shadowMap . enabled = true ;
9394 renderer . shadowMap . autoUpdate = false ;
94- renderer . shadowMap . type = THREE . PCFSoftShadowMap ;
95+ renderer . shadowMap . type = THREE . PCFShadowMap ;
9596 let canvas = renderer . domElement ;
9697 const canvasStyle =
9798 "position:absolute;left:0;top:0;width:1000px;height:1000px;transform-origin:0 0" ;
@@ -107,7 +108,8 @@ export function createDatabaseScene({
107108 samples = 0 ,
108109 slowFrames = 0 ,
109110 cpuBusyUntil = 0 ;
110- let baked , context , lastTurn ;
111+ let baked , context , lastTurn , pendingAtlas , previousCanvas ;
112+ let switchingRenderer = false ;
111113 const resetBudget = ( ) => {
112114 if ( fence ) gl . deleteSync ( fence ) ;
113115 fence = undefined ;
@@ -139,33 +141,33 @@ export function createDatabaseScene({
139141 renderer . forceContextLoss ( ) ;
140142 } ;
141143 const useBakedViews = ( ) => {
142- releaseGraphics ( ) ;
143- baked = new Image ( ) ;
144+ if ( switchingRenderer ) return ;
145+ switchingRenderer = true ;
146+ const atlas = new Image ( ) ;
147+ pendingAtlas = atlas ;
144148 const replacement = document . createElement ( "canvas" ) ;
145149 replacement . width = 400 ;
146150 replacement . height = 400 ;
147- context = replacement . getContext ( "2d" ) ;
148- if ( ! context ) {
151+ const replacementContext = replacement . getContext ( "2d" ) ;
152+ if ( ! replacementContext ) {
149153 onError ( ) ;
150154 return ;
151155 }
152- canvas . replaceWith ( replacement ) ;
153- canvas = replacement ;
154- canvas . style . cssText = canvasStyle ;
155- root . dataset . renderer = "baked" ;
156- root . dataset . scene = "loading" ;
157- loaded = readySent = false ;
158- hitArea . inert = true ;
159- if ( instructions )
160- instructions . textContent =
161- "Drag sideways or use left/right arrows to rotate. Use plus/minus to zoom and Home to reset. Swipe vertically or pinch to scroll or zoom the page." ;
162- baked . src = new URL (
156+ atlas . src = new URL (
163157 "../../assets/sculpture-turns.avif" ,
164158 import . meta. url ,
165159 ) . href ;
166- baked . decode ( ) . then (
160+ atlas . decode ( ) . then (
167161 ( ) => {
168- loaded = ! disposed ;
162+ if ( disposed ) return ;
163+ // Keep the last submitted WebGL frame visible during download/decode.
164+ // The replacement is attached only after its first atlas view is drawn.
165+ baked = atlas ;
166+ context = replacementContext ;
167+ previousCanvas = canvas ;
168+ canvas = replacement ;
169+ canvas . style . cssText = canvasStyle ;
170+ switchingRenderer = false ;
169171 needsRender = true ;
170172 } ,
171173 ( ) => {
@@ -180,25 +182,26 @@ export function createDatabaseScene({
180182 const interior = {
181183 uInteriorPosition : { value : new THREE . Vector3 ( ) } ,
182184 uInteriorRadius : { value : 1 } ,
183- uInteriorPulse : { value : 14 } ,
185+ uInteriorPulse : { value : 2.4 } ,
184186 } ;
185- const interiorLight = new THREE . PointLight ( 0x72c8de , 0.6 , 2.1 , 2 ) ;
187+ const interiorLight = new THREE . PointLight ( 0x8bb8e8 , 0.4 , 2.1 , 2 ) ;
186188 interiorLight . name = "Soft interior database light" ;
187189 const interiorScale = new THREE . Vector3 ( ) ;
188190 let glow , jewelReflection ;
189- const key = new THREE . DirectionalLight ( 0xe5edf1 , 1.7 ) ;
190- key . position . set ( - 3.5 , 5 , 5 ) ;
191+ const key = new THREE . DirectionalLight ( 0xd8e9fa , 1.2 ) ;
192+ key . position . set ( - 3 , 5 , 2. 5) ;
191193 key . castShadow = true ;
192- key . shadow . mapSize . set ( 512 , 512 ) ;
194+ key . shadow . mapSize . set ( 1024 , 1024 ) ;
193195 Object . assign ( key . shadow . camera , {
194- left : - 4 ,
195- right : 4 ,
196- top : 4 ,
197- bottom : - 4 ,
196+ left : - 3 ,
197+ right : 3 ,
198+ top : 3 ,
199+ bottom : - 3 ,
198200 near : 0.5 ,
199201 far : 18 ,
200202 } ) ;
201- key . shadow . normalBias = 0.025 ;
203+ key . shadow . normalBias = 0.012 ;
204+ key . shadow . radius = 8 ;
202205 scene . add ( key ) ;
203206 const rim = new THREE . DirectionalLight ( 0xa1bac7 , 0.8 ) ;
204207 rim . position . set ( 4 , 2 , - 2 ) ;
@@ -208,12 +211,12 @@ export function createDatabaseScene({
208211 scene . add ( fill ) ;
209212
210213 RectAreaLightUniformsLib . init ( ) ;
211- const softbox = new THREE . RectAreaLight ( 0xe5edf1 , 7 , 2 .5, 6 ) ;
212- softbox . position . set ( - 3.5 , 1.5 , 4 ) ;
214+ const softbox = new THREE . RectAreaLight ( 0xe5eff9 , 9 , 1 .5, 6 ) ;
215+ softbox . position . set ( - 3 , 2 , 4.5 ) ;
213216 softbox . lookAt ( 0 , 0 , 0 ) ;
214217 scene . add ( softbox ) ;
215- const edgeSoftbox = new THREE . RectAreaLight ( 0xa1bac7 , 4 , 1.2 , 5 ) ;
216- edgeSoftbox . position . set ( 3.5 , 1.5 , 2 ) ;
218+ const edgeSoftbox = new THREE . RectAreaLight ( 0xa9cbec , 3 , 0.7 , 4 ) ;
219+ edgeSoftbox . position . set ( 3.5 , 2 , 3.5 ) ;
217220 edgeSoftbox . lookAt ( 0 , 0 , 0 ) ;
218221 scene . add ( edgeSoftbox ) ;
219222 scene . add ( new THREE . HemisphereLight ( 0xa1bac7 , 0x18262d , 0.55 ) ) ;
@@ -242,7 +245,7 @@ export function createDatabaseScene({
242245 ) ;
243246 glow . setSize ( 1000 , 1000 , 1 ) ;
244247 jewelReflection = createFiniteJewelLight ( JEWEL_EDGES , shaders ) ;
245- studioSet = createSculptureStudio ( ) ;
248+ studioSet = createSculptureStudio ( studio , shaders ) ;
246249 scene . add ( studioSet . group ) ;
247250 performance . mark ( "sqlpage:assets-ready" ) ;
248251 const bounds = new THREE . Box3 ( ) . setFromObject ( gltf . scene ) ;
@@ -271,17 +274,24 @@ export function createDatabaseScene({
271274 if ( material . name . includes ( "Separator" ) )
272275 finishLiquid ( material , liquidClock , studio , interior , shaders ) ;
273276 if ( material . name . includes ( "Jewel Wire" ) ) {
274- material . emissiveIntensity = 1.05 ;
277+ material . color . set ( "#a9d2fa" ) ;
278+ material . emissive . set ( "#8bb8e8" ) ;
279+ material . emissiveIntensity = 1.6 ;
275280 wireMaterial = material ;
276281 }
277- if ( material . name . includes ( "Endpoint" ) )
278- material . emissiveIntensity = 1.2 ;
282+ if ( material . name . includes ( "Endpoint" ) ) {
283+ material . color . set ( "#c9e2fb" ) ;
284+ material . emissive . set ( "#a9d2fa" ) ;
285+ material . emissiveIntensity = 1.8 ;
286+ }
287+ if ( material . name . includes ( "Facets" ) ) {
288+ material . color . set ( "#8bb8e8" ) ;
289+ material . emissive . set ( "#466689" ) ;
290+ material . opacity = 0.035 ;
291+ }
279292 if ( material . transparent ) material . depthWrite = false ;
280293 }
281294 } ) ;
282- const jewelLight = new THREE . PointLight ( 0x72c8de , 0.09 , 2.4 , 2 ) ;
283- jewelLight . position . set ( 0 , 1.35 , 0 ) ;
284- gltf . scene . add ( jewelLight ) ;
285295 // Native model coordinates: stay inside the radius-1.04 database stack.
286296 interiorLight . position . set ( 0.4 , - 0.45 , 0 ) ;
287297 gltf . scene . add ( interiorLight ) ;
@@ -428,6 +438,7 @@ export function createDatabaseScene({
428438 }
429439 if (
430440 loaded &&
441+ ! switchingRenderer &&
431442 ( inView || ! readySent ) &&
432443 ( state . motion || needsRender || changing )
433444 ) {
@@ -461,6 +472,15 @@ export function createDatabaseScene({
461472 }
462473 lastTurn = turn ;
463474 }
475+ if ( previousCanvas ) {
476+ previousCanvas . replaceWith ( canvas ) ;
477+ previousCanvas = undefined ;
478+ releaseGraphics ( ) ;
479+ root . dataset . renderer = "baked" ;
480+ if ( instructions )
481+ instructions . textContent =
482+ "Drag sideways or use left/right arrows to rotate. Use plus/minus to zoom and Home to reset. Swipe vertically or pinch to scroll or zoom the page." ;
483+ }
464484 } else {
465485 // Prepare GPU-only animation only when a new live frame can be drawn.
466486 liquidClock . value = time ;
@@ -473,9 +493,9 @@ export function createDatabaseScene({
473493 - 0.45 + Math . sin ( time * ( ( Math . PI * 2 ) / 4 ) ) * 0.28 ,
474494 Math . sin ( time * ( ( Math . PI * 2 ) / 6 ) ) * 0.4 ,
475495 ) ;
476- const pulse = 14 + Math . sin ( time * ( ( Math . PI * 2 ) / 3 ) ) * 6 ;
496+ const pulse = 2.4 + Math . sin ( time * ( ( Math . PI * 2 ) / 6 ) ) * 0.3 ;
477497 interior . uInteriorPulse . value = pulse ;
478- interiorLight . intensity = ( 0.6 * pulse ) / 14 ;
498+ interiorLight . intensity = ( 0.4 * pulse ) / 2.4 ;
479499 scene . updateMatrixWorld ( true ) ;
480500 studioSet . update ( model ) ;
481501 scene . updateMatrixWorld ( true ) ;
@@ -485,15 +505,19 @@ export function createDatabaseScene({
485505 if ( diamond && wireMaterial )
486506 jewelReflection . update ( diamond , camera , wireMaterial ) ;
487507 syncHitArea ( ) ;
508+ studioSet . renderReflection ( renderer , scene , camera ) ;
488509 renderer . shadowMap . needsUpdate = true ;
489510 renderer . render ( scene , camera ) ;
490511 glow . render ( model , [ studioSet . group ] ) ;
491512 fence = gl . fenceSync ( gl . SYNC_GPU_COMMANDS_COMPLETE , 0 ) ;
492513 submittedAt = performance . now ( ) ;
514+ renderedFrames ++ ;
493515 }
494- needsRender = false ;
516+ // The reflection receiver settles on the second submitted frame. Keep
517+ // the matching preview visible and time frozen throughout initialization.
518+ needsRender = ! baked && renderedFrames < 2 ;
495519 lastOrientation . copy ( dragQuaternion ) ;
496- if ( ! readySent ) {
520+ if ( ! readySent && ( baked || renderedFrames >= 2 ) ) {
497521 readySent = true ;
498522 performance . mark ( "sqlpage:first-frame" ) ;
499523 performance . measure (
@@ -530,7 +554,9 @@ export function createDatabaseScene({
530554 timing ?. disconnect ( ) ;
531555 if ( instructions ) instructions . textContent = originalInstructions ;
532556 if ( baked ) baked . src = "" ;
533- if ( ! baked ) releaseGraphics ( ) ;
557+ if ( pendingAtlas ) pendingAtlas . src = "" ;
558+ if ( ! baked || previousCanvas ) releaseGraphics ( ) ;
559+ previousCanvas ?. remove ( ) ;
534560 canvas . remove ( ) ;
535561 } ,
536562 } ;
0 commit comments