@@ -827,7 +827,7 @@ export default function DevWebSurf3D({
827827 > ( )
828828 const thumbnailPrefetchTimers = new Set < number > ( )
829829 const MAX_RESIDENT_COVERS = 32
830- const SHELF_ATLAS_PANELS_PER_FLOOR = 6
830+ const SHELF_ATLAS_STRIPS_PER_SHELF = 2
831831 const COVERS_PER_SHELF_ATLAS = 3
832832 const MAX_ACTIVE_BOOK_DETAILS = 14
833833 const COVER_LOAD_DISTANCE = 10.5
@@ -3725,81 +3725,73 @@ export default function DevWebSurf3D({
37253725 )
37263726 if ( ! candidates . length ) continue
37273727
3728- const floorEntries : ShelfCoverAtlasLod [ ] = [ ]
37293728 const floorBase = floor * LIBRARY_FLOOR_HEIGHT
3729+ // One small atlas is shared by every strip on the floor. This keeps
3730+ // remote image work bounded (three cached requests per floor) while
3731+ // letting every synthetic stack carry a cover-detail layer on both
3732+ // faces instead of a handful of floating cards.
3733+ const urls = candidates
3734+ . slice ( 0 , COVERS_PER_SHELF_ATLAS )
3735+ . map ( articleCoverUrl )
3736+ . filter ( ( url ) : url is string => Boolean ( url ) )
3737+ if ( ! urls . length ) continue
3738+
3739+ const atlas = createShelfCoverAtlas (
3740+ urls ,
3741+ floor ,
3742+ ( floor - 1 ) * 320 ,
3743+ )
3744+ const material = new THREE . MeshBasicMaterial ( {
3745+ map : atlas ,
3746+ color : 0xffffff ,
3747+ transparent : true ,
3748+ opacity : .48 ,
3749+ toneMapped : false ,
3750+ depthWrite : true ,
3751+ polygonOffset : true ,
3752+ polygonOffsetFactor : - 1 ,
3753+ polygonOffsetUnits : - 1 ,
3754+ } )
3755+ architecturalMaterials . push ( material )
37303756
3731- for (
3732- let panelIndex = 0 ;
3733- panelIndex < SHELF_ATLAS_PANELS_PER_FLOOR ;
3734- panelIndex += 1
3735- ) {
3736- const rowIndex = Math . round (
3737- panelIndex *
3738- ( ( placeholderRows . length - 1 ) /
3739- Math . max ( 1 , SHELF_ATLAS_PANELS_PER_FLOOR - 1 ) ) ,
3740- )
3741- const columnIndex =
3742- ( panelIndex * 3 + floor ) % placeholderColumns . length
3743- const rotationY = rowIndex % 2 === 0 ? 0 : Math . PI
3744- const level = panelIndex % 3
3745- const urls : string [ ] = [ ]
3746-
3747- for (
3748- let coverIndex = 0 ;
3749- coverIndex < COVERS_PER_SHELF_ATLAS ;
3750- coverIndex += 1
3751- ) {
3752- const sampleIndex =
3753- ( panelIndex * COVERS_PER_SHELF_ATLAS + coverIndex ) %
3754- candidates . length
3755- const url = articleCoverUrl ( candidates [ sampleIndex ] )
3756- if ( url ) urls . push ( url )
3757- }
3758- if ( ! urls . length ) continue
3759-
3760- const atlas = createShelfCoverAtlas (
3761- urls ,
3762- floor ,
3763- ( floor - 1 ) * 260 + panelIndex * 70 ,
3764- )
3765- const material = new THREE . MeshBasicMaterial ( {
3766- map : atlas ,
3767- color : 0xffffff ,
3768- transparent : true ,
3769- opacity : .48 ,
3770- toneMapped : false ,
3771- depthWrite : true ,
3772- side : THREE . DoubleSide ,
3773- polygonOffset : true ,
3774- polygonOffsetFactor : - 1 ,
3775- polygonOffsetUnits : - 1 ,
3776- } )
3777- architecturalMaterials . push ( material )
3778-
3779- const mesh = new THREE . Mesh (
3780- shelfCoverAtlasGeometry ,
3781- material ,
3782- )
3783- const x = placeholderColumns [ columnIndex ]
3784- const z = placeholderRows [ rowIndex ]
3785- const front = .095
3786- mesh . position . set (
3787- x + Math . sin ( rotationY ) * front ,
3788- floorBase + .64 + level * 1.08 ,
3789- z + Math . cos ( rotationY ) * front ,
3790- )
3791- mesh . rotation . y = rotationY
3792- mesh . renderOrder = 2
3793- scene . add ( mesh )
3794-
3795- floorEntries . push ( {
3796- mesh,
3797- material,
3798- floorIndex : floor ,
3757+ const stripCount =
3758+ placeholderRows . length *
3759+ placeholderColumns . length *
3760+ SHELF_ATLAS_STRIPS_PER_SHELF
3761+ const mesh = new THREE . InstancedMesh (
3762+ shelfCoverAtlasGeometry ,
3763+ material ,
3764+ stripCount ,
3765+ )
3766+ const stripMatrix = new THREE . Matrix4 ( )
3767+ const stripPosition = new THREE . Vector3 ( )
3768+ const stripScale = new THREE . Vector3 ( 1 , 1 , 1 )
3769+ const stripQuaternion = new THREE . Quaternion ( )
3770+ let stripIndex = 0
3771+ placeholderRows . forEach ( ( z , rowIndex ) => {
3772+ placeholderColumns . forEach ( ( x , columnIndex ) => {
3773+ const baseRotation = rowIndex % 2 === 0 ? 0 : Math . PI
3774+ for ( let face = - 1 ; face <= 1 ; face += 2 ) {
3775+ const rotationY = baseRotation + ( face < 0 ? Math . PI : 0 )
3776+ const front = face * .692
3777+ const level =
3778+ ( floor + rowIndex + columnIndex + ( face < 0 ? 1 : 0 ) ) % 3
3779+ stripPosition . set (
3780+ x + Math . sin ( baseRotation ) * front ,
3781+ floorBase + .64 + level * 1.08 ,
3782+ z + Math . cos ( baseRotation ) * front ,
3783+ )
3784+ stripQuaternion . setFromAxisAngle ( placeholderUp , rotationY )
3785+ stripMatrix . compose ( stripPosition , stripQuaternion , stripScale )
3786+ mesh . setMatrixAt ( stripIndex , stripMatrix )
3787+ stripIndex += 1
3788+ }
37993789 } )
3800- }
3801-
3802- shelfCoverAtlasesByFloor . set ( floor , floorEntries )
3790+ } )
3791+ mesh . instanceMatrix . needsUpdate = true
3792+ mesh . renderOrder = 2
3793+ scene . add ( mesh )
3794+ shelfCoverAtlasesByFloor . set ( floor , [ { mesh, material, floorIndex : floor } ] )
38033795 }
38043796
38053797 // Article LOD: every real article has a tiny instanced stand-in. Distant
0 commit comments