From 150184fbe808b403448411a5ba069f04bc92009a Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 15 Mar 2025 11:29:46 +0900 Subject: [PATCH 01/22] Add "setTileActive" plugin support --- src/base/TilesRendererBase.js | 2 +- src/base/traverseFunctions.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/base/TilesRendererBase.js b/src/base/TilesRendererBase.js index df6cefce0..0a933a321 100644 --- a/src/base/TilesRendererBase.js +++ b/src/base/TilesRendererBase.js @@ -438,7 +438,7 @@ export class TilesRendererBase { if ( tile.__active ) { - this.setTileActive( tile, false ); + this.invokeOnePlugin( plugin => plugin.setTileActive && plugin.setTileActive( tile, false ) ); tile.__active = false; } diff --git a/src/base/traverseFunctions.js b/src/base/traverseFunctions.js index c235b330f..d189b854a 100644 --- a/src/base/traverseFunctions.js +++ b/src/base/traverseFunctions.js @@ -463,7 +463,7 @@ export function toggleTiles( tile, renderer ) { if ( tile.__wasSetActive !== setActive ) { - renderer.setTileActive( tile, setActive ); + renderer.invokeOnePlugin( plugin => plugin.setTileActive && plugin.setTileActive( tile, setActive ) ); } From 9fca291d30ea3428914e1502121bda0843ff6c99 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 15 Mar 2025 11:30:42 +0900 Subject: [PATCH 02/22] Add initial tile flattening plugin --- src/plugins/three/TileFlatteningPlugin.js | 98 +++++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 src/plugins/three/TileFlatteningPlugin.js diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js new file mode 100644 index 000000000..8cd362424 --- /dev/null +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -0,0 +1,98 @@ +import { Vector3 } from 'three'; + +export class TileFlatteningPlugin { + + constructor() { + + this.name = 'TILE_FLATTENING_PLUGIN'; + this.priority = - 100; + + this.tiles = null; + this.shapes = new Map(); + this.positionsMap = new Map(); + this.positionsUpdated = new Set(); + this.needsUpdate = false; + + } + + init( tiles ) { + + this.tiles = tiles; + + tiles.addEventListener( 'update-before', () => { + + if ( this.needsUpdate ) { + + this._updateTiles(); + + } + + } ); + + } + + setTileActive( tile, active ) { + + // update tiles if not visible yet + if ( active && ! this.positionsUpdated.has( tile ) ) { + + this._updateTile( tile ); + + } + + } + + _updateTile( tile ) { + + this.positionsUpdated.add( tile ); + + // TODO: iterate over every vertex and update the flattening + + } + + _updateTiles() { + + this.positionsUpdated.clear(); + + // TODO: iterate over every tile and update the flattening + + } + + addShape( mesh, direction = new Vector3( 0, - 1, 0 ) ) { + + this.needsUpdate = true; + + mesh.updateMatrix(); + this.shapes.set( mesh, { + shape: mesh, + direction: direction.clone(), + matrix: mesh.matrix.clone(), + } ); + + } + + updateShape( mesh ) { + + this.needsUpdate = true; + + mesh.updateMatrix(); + this.shapes.get( mesh ).matrix.copy( mesh.matrix ); + + } + + deleteShape( mesh ) { + + this.needsUpdate = true; + + return this.shapes.delete( mesh ); + + } + + dispose() { + + // TODO: reset all the geometry positions + // TODO: remove event listener + + } + +} From fd7f22b01861658ab4a364dbeb941e103be26dbe Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 15 Mar 2025 11:42:42 +0900 Subject: [PATCH 03/22] Updates --- src/plugins/three/TileFlatteningPlugin.js | 65 +++++++++++++++++++++-- 1 file changed, 62 insertions(+), 3 deletions(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index 8cd362424..97de0431e 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -1,5 +1,8 @@ import { Vector3 } from 'three'; +// Limitations: +// - No support for BatchedTilesPlugin +// - Sharing geometry between models may result in incorrect flattening export class TileFlatteningPlugin { constructor() { @@ -18,6 +21,7 @@ export class TileFlatteningPlugin { init( tiles ) { this.tiles = tiles; + this.needsUpdate = true; tiles.addEventListener( 'update-before', () => { @@ -44,17 +48,72 @@ export class TileFlatteningPlugin { _updateTile( tile ) { - this.positionsUpdated.add( tile ); + const { positionsUpdated, positionsMap, shapes } = this; + positionsUpdated.add( tile ); + + const scene = tile.cached.scene; + if ( ! positionsMap.has( tile ) ) { + + const geomMap = new Map(); + positionsMap.set( tile, geomMap ); + scene.traverse( c => { + + if ( c.geometry ) { + + geomMap.set( c.geometry, c.geometry.attributes.position.array.clone() ); + + } + + } ); + + } else { + + const geomMap = positionsMap.get( tile ); + scene.traverse( c => { + + if ( c.geometry ) { + + const buffer = geomMap.get( c.geometry ); + if ( buffer ) { + + c.geometry.attributes.position.array.set( buffer ); + + } + + } - // TODO: iterate over every vertex and update the flattening + } ); + + } + shapes.forEach( ( { shape, matrix, direction } ) => { + + // TODO: check tile intersection with shape + + scene.traverse( c => { + + // TODO + // iterate over every vertex and update the flattening + + if ( c.geometry ) { + + c.geometry.attributes.position.needsUpdate = true; + + } + + } ); + + } ); } _updateTiles() { this.positionsUpdated.clear(); + this.tiles.activeTiles.forEach( tile => { - // TODO: iterate over every tile and update the flattening + this._updateTile( tile ); + + } ); } From e35fc5c19c8adfe3db833a866119b8c79df8dd45 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 15 Mar 2025 11:46:33 +0900 Subject: [PATCH 04/22] Add dispose function --- src/plugins/three/TileFlatteningPlugin.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index 97de0431e..dae777314 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -23,7 +23,7 @@ export class TileFlatteningPlugin { this.tiles = tiles; this.needsUpdate = true; - tiles.addEventListener( 'update-before', () => { + this._updateBeforeCallback = () => { if ( this.needsUpdate ) { @@ -31,7 +31,8 @@ export class TileFlatteningPlugin { } - } ); + }; + tiles.addEventListener( 'update-before', this._updateBeforeCallback ); } @@ -149,8 +150,18 @@ export class TileFlatteningPlugin { dispose() { - // TODO: reset all the geometry positions - // TODO: remove event listener + this.tiles.removeEventListener( 'before-update', this._updateBeforeCallback ); + this.positionsMap.forEach( geomMap => { + + geomMap.forEach( ( geometry, buffer ) => { + + const { position } = geometry.attributes; + position.array.set( buffer ); + position.needsUpdate = true; + + } ); + + } ); } From ef86d0962b06a34cfc08442c6e4c6fae565bf57f Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 15 Mar 2025 11:58:50 +0900 Subject: [PATCH 05/22] Add vertex iteration --- src/plugins/three/TileFlatteningPlugin.js | 40 +++++++++++++++++++---- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index dae777314..430302c35 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -1,8 +1,11 @@ -import { Vector3 } from 'three'; +import { Matrix4, Raycaster, Vector3 } from 'three'; // Limitations: // - No support for BatchedTilesPlugin // - Sharing geometry between models may result in incorrect flattening + +const _invMatrix = /* @__PURE__ */ new Matrix4(); +const _raycaster = /* @__PURE__ */ new Raycaster(); export class TileFlatteningPlugin { constructor() { @@ -36,9 +39,9 @@ export class TileFlatteningPlugin { } + // update tile flattening state if it has not been made visible, yet setTileActive( tile, active ) { - // update tiles if not visible yet if ( active && ! this.positionsUpdated.has( tile ) ) { this._updateTile( tile ); @@ -49,12 +52,13 @@ export class TileFlatteningPlugin { _updateTile( tile ) { - const { positionsUpdated, positionsMap, shapes } = this; + const { positionsUpdated, positionsMap, shapes, tiles } = this; positionsUpdated.add( tile ); const scene = tile.cached.scene; if ( ! positionsMap.has( tile ) ) { + // save the geometry positions for resetting after const geomMap = new Map(); positionsMap.set( tile, geomMap ); scene.traverse( c => { @@ -69,6 +73,7 @@ export class TileFlatteningPlugin { } else { + // reset the geometry state before re-flattening tiles const geomMap = positionsMap.get( tile ); scene.traverse( c => { @@ -86,18 +91,37 @@ export class TileFlatteningPlugin { } ); } + + const ray = _raycaster.ray; shapes.forEach( ( { shape, matrix, direction } ) => { // TODO: check tile intersection with shape + shape.matrixWorld.copy( matrix ).premultiply( tiles.group.matrixWorld ); + ray.direction.copy( direction ); + // iterate over every geometry scene.traverse( c => { - // TODO - // iterate over every vertex and update the flattening - if ( c.geometry ) { - c.geometry.attributes.position.needsUpdate = true; + const { position } = c.geometry.attributes; + position.needsUpdate = true; + _invMatrix.copy( c.matrixWorld ).invert(); + + // iterate over every vertex position + for ( let i = 0, l = position.count; i < l; i ++ ) { + + ray.origin.fromBufferAttribute( position, i ).applyMatrix4( c.matrixWorld ); + + const hit = _raycaster.intersectObject( shape )[ 0 ]; + if ( hit ) { + + hit.point.applyMatrix4( _invMatrix ); + position.setXYZ( i, ...hit.point ); + + } + + } } @@ -118,6 +142,7 @@ export class TileFlatteningPlugin { } + // API for updating and shapes to flatten the vertices addShape( mesh, direction = new Vector3( 0, - 1, 0 ) ) { this.needsUpdate = true; @@ -148,6 +173,7 @@ export class TileFlatteningPlugin { } + // reset the vertex positions and remove the update callback dispose() { this.tiles.removeEventListener( 'before-update', this._updateBeforeCallback ); From 15f7941b412868ef74c5f8885272ba5f0d918357 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sat, 15 Mar 2025 14:22:43 +0900 Subject: [PATCH 06/22] comment --- src/plugins/three/TileFlatteningPlugin.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index 430302c35..7111873d1 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -96,7 +96,10 @@ export class TileFlatteningPlugin { shapes.forEach( ( { shape, matrix, direction } ) => { // TODO: check tile intersection with shape - shape.matrixWorld.copy( matrix ).premultiply( tiles.group.matrixWorld ); + // TODO: must perform this in a 2d projected way + // prepare the shape and ray + shape.matrix.copy( obb.matrix ).premultiply( tiles.group.matrixWorld ); + shape.matrixWorld.copy( shape.matrix ); ray.direction.copy( direction ); // iterate over every geometry From f8bd37c2eda74a302791114f377f6327cd2f0cf8 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sun, 16 Mar 2025 14:53:11 +0900 Subject: [PATCH 07/22] Add obb --- src/plugins/three/TileFlatteningPlugin.js | 37 +++++++++++++++++++---- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index 7111873d1..c0ffc5d85 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -1,9 +1,12 @@ -import { Matrix4, Raycaster, Vector3 } from 'three'; +import { Box3, Matrix4, Raycaster, Sphere, Vector3 } from 'three'; +import { OBB } from '../../three/math/OBB'; // Limitations: -// - No support for BatchedTilesPlugin +// - No support for BatchedTilesPlugin when resetting or modifying geometry // - Sharing geometry between models may result in incorrect flattening +const _sphere = /* @__PURE__ */ new Sphere(); +const _box = /* @__PURE__ */ new Box3(); const _invMatrix = /* @__PURE__ */ new Matrix4(); const _raycaster = /* @__PURE__ */ new Raycaster(); export class TileFlatteningPlugin { @@ -93,10 +96,16 @@ export class TileFlatteningPlugin { } const ray = _raycaster.ray; - shapes.forEach( ( { shape, matrix, direction } ) => { + shapes.forEach( ( { shape, obb, direction } ) => { // TODO: check tile intersection with shape - // TODO: must perform this in a 2d projected way + // TODO: must perform this in a 2d projected way (circles are easiest) + if ( ! tile.cached.boundingVolume.intersectOBB( obb ) ) { + + return; + + } + // prepare the shape and ray shape.matrix.copy( obb.matrix ).premultiply( tiles.group.matrixWorld ); shape.matrixWorld.copy( shape.matrix ); @@ -150,11 +159,21 @@ export class TileFlatteningPlugin { this.needsUpdate = true; + // TODO: generate frame to generate projected bounds in to + + const obb = new OBB(); + + mesh.matrix.identity(); + mesh.matrixWorld.identity(); + obb.box.setFromObject( mesh, true ); + mesh.updateMatrix(); + obb.matrix.copy( mesh.matrix ); + this.shapes.set( mesh, { shape: mesh, direction: direction.clone(), - matrix: mesh.matrix.clone(), + obb: obb, } ); } @@ -163,8 +182,14 @@ export class TileFlatteningPlugin { this.needsUpdate = true; + const info = this.shapes.get( mesh ); + + mesh.matrix.identity(); + mesh.matrixWorld.identity(); + info.obb.box.setFromObject( mesh, true ); + mesh.updateMatrix(); - this.shapes.get( mesh ).matrix.copy( mesh.matrix ); + info.obb.matrix.copy( mesh.matrix ); } From 4e347862ed37a458f22d67cadb82d6528d399045 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sun, 16 Mar 2025 17:15:33 +0900 Subject: [PATCH 08/22] Use circle bounding volume --- src/plugins/three/TileFlatteningPlugin.js | 94 +++++++++++++++-------- 1 file changed, 63 insertions(+), 31 deletions(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index c0ffc5d85..bf9444f61 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -1,14 +1,43 @@ -import { Box3, Matrix4, Raycaster, Sphere, Vector3 } from 'three'; -import { OBB } from '../../three/math/OBB'; +import { Matrix4, Raycaster, Sphere, Vector3 } from 'three'; +import { OBB } from '../../three/math/OBB.js'; // Limitations: // - No support for BatchedTilesPlugin when resetting or modifying geometry // - Sharing geometry between models may result in incorrect flattening const _sphere = /* @__PURE__ */ new Sphere(); -const _box = /* @__PURE__ */ new Box3(); +const _obb = /* @__PURE__ */ new OBB(); +const _vec = /* @__PURE__ */ new Vector3(); const _invMatrix = /* @__PURE__ */ new Matrix4(); const _raycaster = /* @__PURE__ */ new Raycaster(); + +function calculateCirclePoint( object, direction, target ) { + + if ( object instanceof OBB ) { + + _obb.copy( object ); + + } else { + + // construct obb + object.matrix.identity(); + object.matrixWorld.identity(); + _obb.box.setFromObject( object, true ); + + object.updateMatrix(); + _obb.matrix.copy( object.matrix ); + + } + + // get sphere + _obb.box.getBoundingSphere( _sphere ).applyMatrix4( _obb.matrix ); + + // get projected point + target.copy( _sphere.center ).addScaledVector( direction, - direction.dot( _sphere.center ) ); + return _sphere.radius; + +} + export class TileFlatteningPlugin { constructor() { @@ -34,6 +63,7 @@ export class TileFlatteningPlugin { if ( this.needsUpdate ) { this._updateTiles(); + this.needsUpdate = false; } @@ -96,18 +126,32 @@ export class TileFlatteningPlugin { } const ray = _raycaster.ray; - shapes.forEach( ( { shape, obb, direction } ) => { - - // TODO: check tile intersection with shape - // TODO: must perform this in a 2d projected way (circles are easiest) - if ( ! tile.cached.boundingVolume.intersectOBB( obb ) ) { + shapes.forEach( ( { + shape, + direction, + matrix, + circleCenter, + circleRadius, + } ) => { + + // TODO: if we save the sphere of the original mesh we can check the height to limit the tiles checked + // TODO: we should use the tile bounding volume sphere if present + + // calculate the bounding circle for the tile + const { boundingVolume } = tile.cached; + const tileCircleCenter = _vec; + const tileCircleRadius = calculateCirclePoint( boundingVolume.obb || boundingVolume.regionObb, direction, tileCircleCenter ); + + // check if we intersect + const r2 = ( circleRadius + tileCircleRadius ) ** 2; + if ( tileCircleCenter.distanceToSquared( circleCenter ) > r2 ) { return; } // prepare the shape and ray - shape.matrix.copy( obb.matrix ).premultiply( tiles.group.matrixWorld ); + shape.matrix.copy( matrix ).premultiply( tiles.group.matrixWorld ); shape.matrixWorld.copy( shape.matrix ); ray.direction.copy( direction ); @@ -146,11 +190,7 @@ export class TileFlatteningPlugin { _updateTiles() { this.positionsUpdated.clear(); - this.tiles.activeTiles.forEach( tile => { - - this._updateTile( tile ); - - } ); + this.tiles.activeTiles.forEach( tile => this._updateTile( tile ) ); } @@ -159,21 +199,16 @@ export class TileFlatteningPlugin { this.needsUpdate = true; - // TODO: generate frame to generate projected bounds in to - - const obb = new OBB(); - - mesh.matrix.identity(); - mesh.matrixWorld.identity(); - obb.box.setFromObject( mesh, true ); - mesh.updateMatrix(); - obb.matrix.copy( mesh.matrix ); + const circleCenter = new Vector3(); + const circleRadius = calculateCirclePoint( mesh, direction, circleCenter ); this.shapes.set( mesh, { shape: mesh, direction: direction.clone(), - obb: obb, + matrix: mesh.matrix.clone(), + circleCenter: circleCenter, + circleRadius: circleRadius, } ); } @@ -182,14 +217,11 @@ export class TileFlatteningPlugin { this.needsUpdate = true; - const info = this.shapes.get( mesh ); - - mesh.matrix.identity(); - mesh.matrixWorld.identity(); - info.obb.box.setFromObject( mesh, true ); - mesh.updateMatrix(); - info.obb.matrix.copy( mesh.matrix ); + + const info = this.shapes.get( mesh ); + info.matrix.copy( mesh.matrix ); + info.circleRadius = calculateCirclePoint( mesh, info.direction, info.circleCenter ); } From 4dd31638543f414bfe24681cfd34a82a6c828207 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sun, 16 Mar 2025 18:29:17 +0900 Subject: [PATCH 09/22] fixes --- src/plugins/three/TileFlatteningPlugin.js | 56 ++++++++++++++++++++--- 1 file changed, 49 insertions(+), 7 deletions(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index bf9444f61..226ce7df5 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -8,6 +8,7 @@ import { OBB } from '../../three/math/OBB.js'; const _sphere = /* @__PURE__ */ new Sphere(); const _obb = /* @__PURE__ */ new OBB(); const _vec = /* @__PURE__ */ new Vector3(); +const _matrix = /* @__PURE__ */ new Matrix4(); const _invMatrix = /* @__PURE__ */ new Matrix4(); const _raycaster = /* @__PURE__ */ new Raycaster(); @@ -25,12 +26,12 @@ function calculateCirclePoint( object, direction, target ) { _obb.box.setFromObject( object, true ); object.updateMatrix(); - _obb.matrix.copy( object.matrix ); + _obb.transform.copy( object.matrix ); } // get sphere - _obb.box.getBoundingSphere( _sphere ).applyMatrix4( _obb.matrix ); + _obb.box.getBoundingSphere( _sphere ).applyMatrix4( _obb.transform ); // get projected point target.copy( _sphere.center ).addScaledVector( direction, - direction.dot( _sphere.center ) ); @@ -98,7 +99,7 @@ export class TileFlatteningPlugin { if ( c.geometry ) { - geomMap.set( c.geometry, c.geometry.attributes.position.array.clone() ); + geomMap.set( c.geometry, c.geometry.attributes.position.array.slice() ); } @@ -116,6 +117,7 @@ export class TileFlatteningPlugin { if ( buffer ) { c.geometry.attributes.position.array.set( buffer ); + c.geometry.attributes.position.needsUpdate = true; } @@ -151,10 +153,12 @@ export class TileFlatteningPlugin { } // prepare the shape and ray - shape.matrix.copy( matrix ).premultiply( tiles.group.matrixWorld ); + shape.matrix.copy( matrix ); shape.matrixWorld.copy( shape.matrix ); ray.direction.copy( direction ); + scene.updateMatrixWorld( true ); + // iterate over every geometry scene.traverse( c => { @@ -162,12 +166,20 @@ export class TileFlatteningPlugin { const { position } = c.geometry.attributes; position.needsUpdate = true; - _invMatrix.copy( c.matrixWorld ).invert(); + + _matrix.copy( c.matrixWorld ); + if ( scene.parent !== null ) { + + _matrix.premultiply( tiles.group.matrixWorldInverse ); + + } + + _invMatrix.copy( _matrix ).invert(); // iterate over every vertex position for ( let i = 0, l = position.count; i < l; i ++ ) { - ray.origin.fromBufferAttribute( position, i ).applyMatrix4( c.matrixWorld ); + ray.origin.fromBufferAttribute( position, i ).applyMatrix4( _matrix ); const hit = _raycaster.intersectObject( shape )[ 0 ]; if ( hit ) { @@ -195,8 +207,20 @@ export class TileFlatteningPlugin { } // API for updating and shapes to flatten the vertices + hasShape( mesh ) { + + return this.shapes.has( mesh ); + + } + addShape( mesh, direction = new Vector3( 0, - 1, 0 ) ) { + if ( this.hasShape( mesh ) ) { + + throw new Error( 'TileFlatteningPlugin: Shape is already used.' ); + + } + this.needsUpdate = true; mesh.updateMatrix(); @@ -215,6 +239,12 @@ export class TileFlatteningPlugin { updateShape( mesh ) { + if ( ! this.hasShape( mesh ) ) { + + throw new Error( 'TileFlatteningPlugin: Shape is not present.' ); + + } + this.needsUpdate = true; mesh.updateMatrix(); @@ -228,11 +258,23 @@ export class TileFlatteningPlugin { deleteShape( mesh ) { this.needsUpdate = true; - return this.shapes.delete( mesh ); } + clearShapes() { + + if ( this.shapes.size === 0 ) { + + return; + + } + + this.needsUpdate = true; + this.shapes.clear(); + + } + // reset the vertex positions and remove the update callback dispose() { From f3ae4422de3774036c72b01c2e737de9fc52ad60 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sun, 16 Mar 2025 18:35:51 +0900 Subject: [PATCH 10/22] Update docs --- src/plugins/README.md | 44 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/plugins/README.md b/src/plugins/README.md index b7a8d0d44..c8135d514 100644 --- a/src/plugins/README.md +++ b/src/plugins/README.md @@ -723,3 +723,47 @@ clearRegions(): void ``` Remove all regions. + +## TileFlatteningPlugin + +A plugin that takes a shape as a mesh and direction along which to "flatten" vertices to the surface of the shape. Useful for shifting tile geometry to make room for new assets. Not compatible with other plugins that modify geometry such as `BatchedTilesPlugin`. + +### hasShape + +```js +hasShape( shape: Object3D ): boolean +``` + +Returns whether the given object has been passed in as a shape. + +### addShape + +```js +addShape( shape: Object3D, direction: Vector3 ): void +``` + +Adds the given object as a shape to flatten to in addition to the direction to flatten. + +### updateShape + +```js +updateShape( shape: Object3D ): void +``` + +Notifies the plugin that the given shape (geometry, position) has been changed and "tile flattening" needs to be regenerated. + +### deleteShape + +```js +deleteShape( shape: Object3D ): void +``` + +Deletes the given shape and regenerates teh tile flattening. + +### clearShapes + +```js +clearShapes(): void +``` + +Deletes all shapes and resets the tiles. From ffdff057ac3f107dc158f5c05f9e5a03886ae454 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sun, 16 Mar 2025 18:47:52 +0900 Subject: [PATCH 11/22] docs update --- src/plugins/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/README.md b/src/plugins/README.md index c8135d514..e3b871a5c 100644 --- a/src/plugins/README.md +++ b/src/plugins/README.md @@ -758,7 +758,7 @@ Notifies the plugin that the given shape (geometry, position) has been changed a deleteShape( shape: Object3D ): void ``` -Deletes the given shape and regenerates teh tile flattening. +Deletes the given shape and regenerates the tile flattening. ### clearShapes From 6a7ff6784d2a2e02501b0de6d2d99a62f099bb66 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Sun, 16 Mar 2025 18:58:17 +0900 Subject: [PATCH 12/22] Clone the geometry --- src/plugins/three/TileFlatteningPlugin.js | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index 226ce7df5..5936f350b 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -1,4 +1,4 @@ -import { Matrix4, Raycaster, Sphere, Vector3 } from 'three'; +import { DoubleSide, Matrix4, MeshBasicMaterial, Raycaster, Sphere, Vector3 } from 'three'; import { OBB } from '../../three/math/OBB.js'; // Limitations: @@ -11,6 +11,7 @@ const _vec = /* @__PURE__ */ new Vector3(); const _matrix = /* @__PURE__ */ new Matrix4(); const _invMatrix = /* @__PURE__ */ new Matrix4(); const _raycaster = /* @__PURE__ */ new Raycaster(); +const _doubleSidedMaterial = /* @__PURE__ */ new MeshBasicMaterial( { side: DoubleSide } ); function calculateCirclePoint( object, direction, target ) { @@ -227,6 +228,17 @@ export class TileFlatteningPlugin { const circleCenter = new Vector3(); const circleRadius = calculateCirclePoint( mesh, direction, circleCenter ); + const shape = mesh.clone(); + shape.traverse( c => { + + if ( c.material ) { + + c.material = _doubleSidedMaterial; + + } + + } ); + this.shapes.set( mesh, { shape: mesh, direction: direction.clone(), @@ -252,6 +264,16 @@ export class TileFlatteningPlugin { const info = this.shapes.get( mesh ); info.matrix.copy( mesh.matrix ); info.circleRadius = calculateCirclePoint( mesh, info.direction, info.circleCenter ); + info.shape = mesh.clone(); + info.shape.traverse( c => { + + if ( c.material ) { + + c.material = _doubleSidedMaterial; + + } + + } ); } From 08522ed23581507edf8a4b066ccc4ee8ba54b2cf Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 17 Mar 2025 20:02:49 +0900 Subject: [PATCH 13/22] Use spheres rather than projected bounding spheres --- src/plugins/three/TileFlatteningPlugin.js | 33 ++++++++++------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index 5936f350b..3ec2f3ef1 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -13,9 +13,9 @@ const _invMatrix = /* @__PURE__ */ new Matrix4(); const _raycaster = /* @__PURE__ */ new Raycaster(); const _doubleSidedMaterial = /* @__PURE__ */ new MeshBasicMaterial( { side: DoubleSide } ); -function calculateCirclePoint( object, direction, target ) { +function calculateSphere( object, target ) { - if ( object instanceof OBB ) { + if ( object.transform ) { _obb.copy( object ); @@ -32,11 +32,9 @@ function calculateCirclePoint( object, direction, target ) { } // get sphere - _obb.box.getBoundingSphere( _sphere ).applyMatrix4( _obb.transform ); + _obb.box.getBoundingSphere( target ).applyMatrix4( _obb.transform ); - // get projected point - target.copy( _sphere.center ).addScaledVector( direction, - direction.dot( _sphere.center ) ); - return _sphere.radius; + return target; } @@ -133,21 +131,20 @@ export class TileFlatteningPlugin { shape, direction, matrix, - circleCenter, - circleRadius, + sphere, } ) => { // TODO: if we save the sphere of the original mesh we can check the height to limit the tiles checked // TODO: we should use the tile bounding volume sphere if present - // calculate the bounding circle for the tile + // calculate the project distance between circles const { boundingVolume } = tile.cached; - const tileCircleCenter = _vec; - const tileCircleRadius = calculateCirclePoint( boundingVolume.obb || boundingVolume.regionObb, direction, tileCircleCenter ); + calculateSphere( boundingVolume.obb || boundingVolume.regionObb, _sphere ); + _vec.subVectors( _sphere.center, sphere.center ); + _vec.addScaledVector( direction, - direction.dot( _vec ) ); - // check if we intersect - const r2 = ( circleRadius + tileCircleRadius ) ** 2; - if ( tileCircleCenter.distanceToSquared( circleCenter ) > r2 ) { + const r2 = ( _sphere.radius+ sphere.radius ) ** 2; + if ( _vec.lengthSq() > r2 ) { return; @@ -226,8 +223,7 @@ export class TileFlatteningPlugin { mesh.updateMatrix(); - const circleCenter = new Vector3(); - const circleRadius = calculateCirclePoint( mesh, direction, circleCenter ); + const sphere = calculateSphere( mesh, new Sphere() ); const shape = mesh.clone(); shape.traverse( c => { @@ -243,8 +239,7 @@ export class TileFlatteningPlugin { shape: mesh, direction: direction.clone(), matrix: mesh.matrix.clone(), - circleCenter: circleCenter, - circleRadius: circleRadius, + sphere: sphere, } ); } @@ -263,7 +258,7 @@ export class TileFlatteningPlugin { const info = this.shapes.get( mesh ); info.matrix.copy( mesh.matrix ); - info.circleRadius = calculateCirclePoint( mesh, info.direction, info.circleCenter ); + calculateSphere( mesh, info.sphere ); info.shape = mesh.clone(); info.shape.traverse( c => { From 09288a5541549eaeac4791c681e59be2edc63b76 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 17 Mar 2025 20:33:58 +0900 Subject: [PATCH 14/22] Fix AABB construction --- src/plugins/three/TileFlatteningPlugin.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index 3ec2f3ef1..17ef0dd50 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -21,12 +21,15 @@ function calculateSphere( object, target ) { } else { - // construct obb - object.matrix.identity(); - object.matrixWorld.identity(); - _obb.box.setFromObject( object, true ); + // clone the object so we can calculate the root bounding box + const clone = object.clone(); + clone.position.set( 0, 0, 0 ); + clone.quaternion.identity(); + clone.scale.setScalar( 1 ); - object.updateMatrix(); + // construct obb + _obb.box.setFromObject( clone, true ); + _obb.box.getSize( _vec ); _obb.transform.copy( object.matrix ); } From 75bcb94709447b7eb0c5e4fc1e09b1d5e699e3e2 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 17 Mar 2025 20:51:55 +0900 Subject: [PATCH 15/22] Update flattening plugin --- src/plugins/three/TileFlatteningPlugin.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index 17ef0dd50..f480fb99d 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -71,7 +71,16 @@ export class TileFlatteningPlugin { } }; + + this._disposeModelCallback = tile => { + + this.positionsMap.delete( tile ); + this.positionsUpdated.delete( tile ); + + }; + tiles.addEventListener( 'update-before', this._updateBeforeCallback ); + tiles.addEventListener( 'dispose-model', this._disposeModelCallback ); } @@ -299,6 +308,8 @@ export class TileFlatteningPlugin { dispose() { this.tiles.removeEventListener( 'before-update', this._updateBeforeCallback ); + this.tiles.removeEventListener( 'dispose-model', this._disposeModelCallback ); + this.positionsMap.forEach( geomMap => { geomMap.forEach( ( geometry, buffer ) => { From ff5a78d76ada1de3978532643268b2e5fb353314 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 17 Mar 2025 20:53:46 +0900 Subject: [PATCH 16/22] One comment --- src/plugins/three/TileFlatteningPlugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index f480fb99d..c5bed2e24 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -15,7 +15,7 @@ const _doubleSidedMaterial = /* @__PURE__ */ new MeshBasicMaterial( { side: Doub function calculateSphere( object, target ) { - if ( object.transform ) { + if ( object instanceof OBB ) { _obb.copy( object ); From a5a3458e6d232513edf74ec1db4c13f0e7685938 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 17 Mar 2025 20:56:27 +0900 Subject: [PATCH 17/22] lint fix --- src/plugins/three/TileFlatteningPlugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index c5bed2e24..6d65b6f16 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -29,7 +29,7 @@ function calculateSphere( object, target ) { // construct obb _obb.box.setFromObject( clone, true ); - _obb.box.getSize( _vec ); + _obb.box.getSize( _vec ); _obb.transform.copy( object.matrix ); } From a1f76d876cda13a6b33cbabe9bdb6122e58b5c88 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 17 Mar 2025 20:58:08 +0900 Subject: [PATCH 18/22] lint fix --- src/plugins/three/TileFlatteningPlugin.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index 6d65b6f16..f3e66b85e 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -155,7 +155,7 @@ export class TileFlatteningPlugin { _vec.subVectors( _sphere.center, sphere.center ); _vec.addScaledVector( direction, - direction.dot( _vec ) ); - const r2 = ( _sphere.radius+ sphere.radius ) ** 2; + const r2 = ( _sphere.radius + sphere.radius ) ** 2; if ( _vec.lengthSq() > r2 ) { return; From b9ae5412cc459927438fa2567423d5780d46e12d Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Mon, 17 Mar 2025 20:59:19 +0900 Subject: [PATCH 19/22] Add force rerender --- src/plugins/three/TileFlatteningPlugin.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index f3e66b85e..f66f9a6e7 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -207,6 +207,8 @@ export class TileFlatteningPlugin { } ); + this.tiles.dispatchEvent( { type: 'force-rerender' } ); + } _updateTiles() { From afff17efd013f0d0b0314f6cf323c0dc42e4d7b5 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Tue, 18 Mar 2025 16:37:00 +0900 Subject: [PATCH 20/22] Fixes --- src/plugins/three/TileFlatteningPlugin.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index f66f9a6e7..1ca089cbc 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -72,7 +72,7 @@ export class TileFlatteningPlugin { }; - this._disposeModelCallback = tile => { + this._disposeModelCallback = ( { tile } ) => { this.positionsMap.delete( tile ); this.positionsUpdated.delete( tile ); @@ -165,7 +165,7 @@ export class TileFlatteningPlugin { // prepare the shape and ray shape.matrix.copy( matrix ); shape.matrixWorld.copy( shape.matrix ); - ray.direction.copy( direction ); + ray.direction.copy( direction ).multiplyScalar( - 1 ); scene.updateMatrixWorld( true ); @@ -189,7 +189,11 @@ export class TileFlatteningPlugin { // iterate over every vertex position for ( let i = 0, l = position.count; i < l; i ++ ) { - ray.origin.fromBufferAttribute( position, i ).applyMatrix4( _matrix ); + ray.origin + .fromBufferAttribute( position, i ) + .applyMatrix4( _matrix ) + .addScaledVector( direction, 1e5 ); + _raycaster.far = 1e5; const hit = _raycaster.intersectObject( shape )[ 0 ]; if ( hit ) { From b6ff8448113d23ac5b845879d1b5a5da86018a18 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 20 Mar 2025 11:24:02 +0900 Subject: [PATCH 21/22] Add d ts --- src/plugins/three/TileFlatteningPlugin.d.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/plugins/three/TileFlatteningPlugin.d.ts diff --git a/src/plugins/three/TileFlatteningPlugin.d.ts b/src/plugins/three/TileFlatteningPlugin.d.ts new file mode 100644 index 000000000..fea9212e6 --- /dev/null +++ b/src/plugins/three/TileFlatteningPlugin.d.ts @@ -0,0 +1,11 @@ +import { Mesh } from 'three'; + +export class TileFatteningPlugin { + + hasShape( mesh: Mesh ): boolean; + addShape( mesh: Mesh, direction: Vector3 ): void; + updateShape( mesh: Mesh ): void; + deleteShape( mesh ): boolean; + clearShapes(): void; + +} From ed9edc69b189c5f69301b567a77e44b59d68b513 Mon Sep 17 00:00:00 2001 From: Garrett Johnson Date: Thu, 20 Mar 2025 11:34:25 +0900 Subject: [PATCH 22/22] Fixes --- src/plugins/three/TileFlatteningPlugin.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/plugins/three/TileFlatteningPlugin.js b/src/plugins/three/TileFlatteningPlugin.js index 1ca089cbc..15f3da840 100644 --- a/src/plugins/three/TileFlatteningPlugin.js +++ b/src/plugins/three/TileFlatteningPlugin.js @@ -142,7 +142,6 @@ export class TileFlatteningPlugin { shapes.forEach( ( { shape, direction, - matrix, sphere, } ) => { @@ -163,8 +162,6 @@ export class TileFlatteningPlugin { } // prepare the shape and ray - shape.matrix.copy( matrix ); - shape.matrixWorld.copy( shape.matrix ); ray.direction.copy( direction ).multiplyScalar( - 1 ); scene.updateMatrixWorld( true ); @@ -243,6 +240,7 @@ export class TileFlatteningPlugin { const sphere = calculateSphere( mesh, new Sphere() ); const shape = mesh.clone(); + shape.matrixWorld.copy( shape.matrix ); shape.traverse( c => { if ( c.material ) { @@ -254,9 +252,8 @@ export class TileFlatteningPlugin { } ); this.shapes.set( mesh, { - shape: mesh, + shape: shape, direction: direction.clone(), - matrix: mesh.matrix.clone(), sphere: sphere, } ); @@ -275,7 +272,6 @@ export class TileFlatteningPlugin { mesh.updateMatrix(); const info = this.shapes.get( mesh ); - info.matrix.copy( mesh.matrix ); calculateSphere( mesh, info.sphere ); info.shape = mesh.clone(); info.shape.traverse( c => {