@@ -15,11 +15,9 @@ import { Color } from '../../core/math/color.js';
1515/**
1616 * @import { GraphicsDevice } from '../../platform/graphics/graphics-device.js'
1717 * @import { GSplatPlacement } from './gsplat-placement.js'
18+ * @import { Scene } from '../scene.js'
1819 */
1920
20- // render aabb's for debugging
21- const _debugAabbs = false ;
22-
2321const cameraPosition = new Vec3 ( ) ;
2422const cameraDirection = new Vec3 ( ) ;
2523const translation = new Vec3 ( ) ;
@@ -29,6 +27,23 @@ const tempOctreePlacements = new Set();
2927const _updatedSplats = [ ] ;
3028const tempOctreesTicked = new Set ( ) ;
3129
30+ const _lodColorsRaw = [
31+ [ 1 , 0 , 0 ] , // red
32+ [ 0 , 1 , 0 ] , // green
33+ [ 0 , 0 , 1 ] , // blue
34+ [ 1 , 1 , 0 ] , // yellow
35+ [ 1 , 0 , 1 ] // magenta
36+ ] ;
37+
38+ // Color instances used by debug wireframe rendering
39+ const _lodColors = [
40+ new Color ( 1 , 0 , 0 ) ,
41+ new Color ( 0 , 1 , 0 ) ,
42+ new Color ( 0 , 0 , 1 ) ,
43+ new Color ( 1 , 1 , 0 ) ,
44+ new Color ( 1 , 0 , 1 )
45+ ] ;
46+
3247/**
3348 * GSplatManager manages the rendering of splats using a work buffer, where all active splats are
3449 * stored and rendered from.
@@ -77,6 +92,9 @@ class GSplatManager {
7792 /** @type {GraphNode } */
7893 cameraNode ;
7994
95+ /** @type {Scene } */
96+ scene ;
97+
8098 /**
8199 * Layer placements, only non-octree placements are included.
82100 *
@@ -100,6 +118,7 @@ class GSplatManager {
100118
101119 constructor ( device , director , layer , cameraNode ) {
102120 this . device = device ;
121+ this . scene = director . scene ;
103122 this . director = director ;
104123 this . cameraNode = cameraNode ;
105124 this . workBuffer = new GSplatWorkBuffer ( device ) ;
@@ -204,7 +223,9 @@ class GSplatManager {
204223 // add octree splats
205224 for ( const [ , inst ] of this . octreeInstances ) {
206225 inst . activePlacements . forEach ( ( p ) => {
207- splats . push ( new GSplatInfo ( this . device , p . resource , p ) ) ;
226+ if ( p . resource ) {
227+ splats . push ( new GSplatInfo ( this . device , p . resource , p ) ) ;
228+ }
208229 } ) ;
209230 }
210231
@@ -276,8 +297,9 @@ class GSplatManager {
276297 this . renderer . setMaxNumSplats ( textureSize * textureSize ) ;
277298 }
278299
279- // render all splats to work buffer
280- this . workBuffer . render ( worldState . splats , this . cameraNode ) ;
300+ // render all splats to work buffer with LOD color palette
301+ const colorize = this . scene . gsplat . colorizeLod ;
302+ this . workBuffer . render ( worldState . splats , this . cameraNode , colorize ? _lodColorsRaw : undefined ) ;
281303
282304 // apply pending file-release requests
283305 if ( worldState . pendingReleases && worldState . pendingReleases . length ) {
@@ -298,22 +320,28 @@ class GSplatManager {
298320 }
299321 }
300322
301- update ( scene ) {
323+ update ( ) {
302324
303325 // check if any octree instances have moved enough to require LOD update
304326 let anyOctreeMoved = false ;
327+ const threshold = this . scene . gsplat . lodUpdateThreshold ;
305328 for ( const [ , inst ] of this . octreeInstances ) {
306- anyOctreeMoved ||= inst . testMoved ( ) ;
329+ anyOctreeMoved ||= inst . testMoved ( threshold ) ;
307330 }
308331
309332 // check if camera has moved enough to require LOD update
310333 const currentCameraPos = this . cameraNode . getPosition ( ) ;
311334 const distance = this . lastCameraPos . distance ( currentCameraPos ) ;
312- const cameraMoved = distance > 1.0 ;
335+ const cameraMoved = distance > threshold ;
313336
314337 // when camera of octree need LOD evaluated
315338 if ( cameraMoved || anyOctreeMoved ) {
316339
340+ // update the previous position where LOD was evaluated
341+ for ( const [ , inst ] of this . octreeInstances ) {
342+ inst . updateMoved ( ) ;
343+ }
344+
317345 this . lastCameraPos . copy ( currentCameraPos ) ;
318346
319347 // update LOD for all octree instances
@@ -338,11 +366,12 @@ class GSplatManager {
338366
339367 // debug render world space bounds for all splats
340368 Debug . call ( ( ) => {
341- if ( _debugAabbs ) {
369+ if ( this . scene . gsplat . debugAabbs ) {
342370 const tempAabb = new BoundingBox ( ) ;
371+ const scene = this . scene ;
343372 lastState . splats . forEach ( ( splat ) => {
344373 tempAabb . setFromTransformedAabb ( splat . aabb , splat . node . getWorldTransform ( ) ) ;
345- scene . immediate . drawWireAlignedBox ( tempAabb . getMin ( ) , tempAabb . getMax ( ) , Color . WHITE , true , scene . defaultDrawLayer ) ;
374+ scene . immediate . drawWireAlignedBox ( tempAabb . getMin ( ) , tempAabb . getMax ( ) , _lodColors [ splat . lodIndex ] , true , scene . defaultDrawLayer ) ;
346375 } ) ;
347376 }
348377 } ) ;
@@ -364,7 +393,8 @@ class GSplatManager {
364393
365394 // Batch render all updated splats in a single render pass
366395 if ( _updatedSplats . length > 0 ) {
367- this . workBuffer . render ( _updatedSplats , this . cameraNode ) ;
396+ const colorize = this . scene . gsplat . colorizeLod ;
397+ this . workBuffer . render ( _updatedSplats , this . cameraNode , colorize ? _lodColorsRaw : undefined ) ;
368398 _updatedSplats . length = 0 ;
369399 }
370400 }
0 commit comments