@@ -22,6 +22,7 @@ const _normal = /* @__PURE__ */ new Vector3();
2222const _box = /* @__PURE__ */ new Box3 ( ) ;
2323const SPLIT_TILE_DATA = Symbol ( 'SPLIT_TILE_DATA' ) ;
2424const SPLIT_HASH = Symbol ( 'SPLIT_HASH' ) ;
25+ const ORIGINAL_REFINE = Symbol ( 'ORIGINAL_REFINE' ) ;
2526
2627// Plugin for overlaying tiled image data on top of 3d tiles geometry.
2728export class ImageOverlayPlugin {
@@ -221,12 +222,46 @@ export class ImageOverlayPlugin {
221222
222223 }
223224
225+ _removeVirtualChildren ( tile ) {
226+
227+ if ( ! ( ORIGINAL_REFINE in tile ) ) {
228+
229+ return ;
230+
231+ }
232+
233+ // remove the virtual children associated with the given tile
234+ const { tiles } = this ;
235+ const { virtualChildCount } = tile . internal ;
236+ const len = tile . children . length ;
237+ const start = len - virtualChildCount ;
238+ for ( let i = start ; i < len ; i ++ ) {
239+
240+ const child = tile . children [ i ] ;
241+ tiles . processNodeQueue . remove ( child ) ;
242+ tiles . lruCache . remove ( child ) ;
243+ child . parent = null ;
244+
245+ }
246+
247+ tile . children . length -= virtualChildCount ;
248+ tile . internal . virtualChildCount = 0 ;
249+ tile . refine = tile [ ORIGINAL_REFINE ] ;
250+ delete tile [ ORIGINAL_REFINE ] ;
251+ delete tile [ SPLIT_HASH ] ;
252+
253+ }
254+
224255 disposeTile ( tile ) {
225256
226257 const { overlayInfo, tileControllers, processQueue, pendingTiles, processedTiles } = this ;
227258
228259 processedTiles . delete ( tile ) ;
229260
261+ // remove any virtual children since they depend on this tile being loaded for regeneration.
262+ // they will be recreated with fresh split configuration when the tile is reloaded.
263+ this . _removeVirtualChildren ( tile ) ;
264+
230265 // Cancel any ongoing tasks. If a tile is cancelled while downloading
231266 // this will not have been created, yet.
232267 if ( tileControllers . has ( tile ) ) {
@@ -354,8 +389,6 @@ export class ImageOverlayPlugin {
354389 this . _updateLayers ( tile ) ;
355390 this . disposeTile ( tile ) ;
356391
357- delete tile [ SPLIT_HASH ] ;
358-
359392 } ) ;
360393
361394 tiles . removeEventListener ( 'update-after' , this . _onUpdateAfter ) ;
@@ -403,49 +436,37 @@ export class ImageOverlayPlugin {
403436
404437 }
405438
406- // collect the tiles split into virtual tiles
439+ // collect the tiles split into virtual tiles, sorted deepest-first so nested virtual tiles
440+ // are cleaned up before their parents when iterating
407441 const { tiles } = this ;
408- const parents = new Set ( ) ;
442+ const splitTiles = [ ] ;
409443 this . processedTiles . forEach ( tile => {
410444
411445 if ( SPLIT_HASH in tile ) {
412446
413- parents . add ( tile ) ;
447+ splitTiles . push ( tile ) ;
414448
415449 }
416450
417451 } ) ;
418452
419- // dispose of the virtual children if this tile would not be split or the spilt could change
420- // under the current overlays used.
421- parents . forEach ( parent => {
422-
423- if ( parent . parent === null ) {
424-
425- return ;
453+ // ensure we clean depth first
454+ splitTiles . sort ( ( a , b ) => b . internal . depth - a . internal . depth ) ;
426455
427- }
456+ // dispose of the virtual children if this tile would not be split or the split could change
457+ // under the current overlays used.
458+ splitTiles . forEach ( tile => {
428459
429- const clone = parent . engineData . scene . clone ( ) ;
460+ const clone = tile . engineData . scene . clone ( ) ;
430461 clone . updateMatrixWorld ( ) ;
431462
432- if ( fullDispose || parent [ SPLIT_HASH ] !== this . _getSplitVectors ( clone , parent ) . hash ) {
433-
434- // TODO: if are parent tile is forcibly remove then we should make sure that all the children are, too?
435- const children = collectChildren ( parent ) ;
436- children . sort ( ( a , b ) => ( b . internal . depth || 0 ) - ( a . internal . depth || 0 ) ) ;
463+ if ( fullDispose || tile [ SPLIT_HASH ] !== this . _getSplitVectors ( clone , tile ) . hash ) {
437464
438465 // note that we need to remove children from the processing queue in this case
439- // because we are forcibly evicting them from the cache.
440- children . forEach ( child => {
441-
442- tiles . processNodeQueue . remove ( child ) ;
443- tiles . lruCache . remove ( child ) ;
444- child . parent = null ;
445-
446- } ) ;
447-
448- parent . children . length = 0 ;
466+ // because we are forcibly evicting them from the cache. Since parents is sorted
467+ // deepest-first, nested virtual tiles are already cleaned up before we reach
468+ // their parent here.
469+ this . _removeVirtualChildren ( tile ) ;
449470
450471 }
451472
@@ -462,18 +483,6 @@ export class ImageOverlayPlugin {
462483
463484 }
464485
465- function collectChildren ( root , target = [ ] ) {
466-
467- root . children . forEach ( child => {
468-
469- target . push ( child ) ;
470- collectChildren ( child , target ) ;
471-
472- } ) ;
473- return target ;
474-
475- }
476-
477486 }
478487
479488 _getSplitVectors ( scene , tile , centerTarget = _center ) {
@@ -567,7 +576,16 @@ export class ImageOverlayPlugin {
567576
568577 async expandVirtualChildren ( scene , tile ) {
569578
570- if ( tile . children . length !== 0 || this . enableTileSplitting === false ) {
579+ const { refine } = tile ;
580+
581+ // Only split tiles that would benefit from it:
582+ // - REPLACE tiles with no children are leaf tiles where splitting improves overlay UV projection quality. REPLACE tiles
583+ // that already have children are already refined by their children so splitting is unnecessary.
584+ // - ADD tiles always need splitting since their content is rendered alongside children at all levels.
585+ // Also skip any tiles that already have virtual children to avoid interfering with other plugins.
586+ const shouldSplit = ( refine === 'REPLACE' && tile . children . length === 0 ) || refine === 'ADD' ;
587+ const alreadySplit = tile . internal . virtualChildCount !== 0 ;
588+ if ( this . enableTileSplitting === false || ! shouldSplit || alreadySplit ) {
571589
572590 return ;
573591
@@ -577,17 +595,16 @@ export class ImageOverlayPlugin {
577595 const clone = scene . clone ( ) ;
578596 clone . updateMatrixWorld ( ) ;
579597
580- // get the directions to split on
598+ // get the directions to split on & if there are no directions to split on then exit early
581599 const { directions, hash } = this . _getSplitVectors ( clone , tile , _center ) ;
582- tile [ SPLIT_HASH ] = hash ;
583-
584- // if there are no directions to split on then exit early
585600 if ( directions . length === 0 ) {
586601
587602 return ;
588603
589604 }
590605
606+ tile [ SPLIT_HASH ] = hash ;
607+
591608 // set up the splitter to ignore overlay uvs
592609 const clipper = new GeometryClipper ( ) ;
593610 clipper . attributeList = key => ! / ^ l a y e r _ u v _ \d + / . test ( key ) ;
@@ -604,7 +621,7 @@ export class ImageOverlayPlugin {
604621
605622 // run the clipping operations by performing every permutation of sides
606623 // defined by the split directions
607- const children = [ ] ;
624+ const splitChildren = [ ] ;
608625 clipper . forEachSplitPermutation ( ( ) => {
609626
610627 // clip the object itself
@@ -720,7 +737,8 @@ export class ImageOverlayPlugin {
720737
721738 }
722739
723- children . push ( {
740+ splitChildren . push ( {
741+ internal : { isVirtual : true } ,
724742 refine : 'REPLACE' ,
725743 geometricError : tile . geometricError * 0.5 ,
726744 boundingVolume : boundingVolume ,
@@ -731,12 +749,13 @@ export class ImageOverlayPlugin {
731749
732750 } ) ;
733751
734- // force the tile "refine" mode to be set to "REPLACE" if we're splitting tiles
735- // TODO: If a tile is of type "ADD" refine and it has children then it will not be split
736- // as expected since only geometry tiles with no children are split. Instead we'd want
737- // to split this tiles geometry in addition to adding the child tiles.
752+ // force the tile "refine" mode to be set to "REPLACE" so that the virtual children
753+ // replace this tile's geometry display. Save the original mode so it can be restored
754+ // if virtual children are later removed.
755+ tile [ ORIGINAL_REFINE ] = tile . refine ;
738756 tile . refine = 'REPLACE' ;
739- tile . children . push ( ...children ) ;
757+ tile . children . push ( ...splitChildren ) ;
758+ tile . internal . virtualChildCount += splitChildren . length ;
740759
741760 }
742761
0 commit comments