Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/tile utils #5

Open
wants to merge 4 commits into
base: mp_main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).

## Unreleased
### Added
- `TilesRenderer.onTileVisibilityChange` callback.

### Changed
- Improved type definitions.

Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,14 @@ onDisposeModel = null : ( scene : Object3D, tile : Tile ) => void

Callback that is called every time a model is disposed of. This should be used in conjunction with [.onLoadModel](#onLoadModel) to dispose of any custom materials created for a tile. Note that the textures, materials, and geometries that a tile loaded in with are all automatically disposed of even if they have been removed from the tile meshes.

### .onTileVisibilityChange

```js
onTileVisibilityChange = null : ( scene : Object3D, tile : Tile, visible : boolean ) => void
```

Callback that is called when a tile's visibility changed. The parameter `visible` is `true` when the tile is visible

### .dispose

```js
Expand Down
2 changes: 2 additions & 0 deletions src/base/TileBase.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,6 @@ export interface TileBase {

refine?: 'REPLACE' | 'ADD';

transform?: number[];

}
3 changes: 3 additions & 0 deletions src/base/traverseFunctions.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Tile } from './Tile';

export function isTileDownloadFinished( tile: Tile ): boolean;
10 changes: 5 additions & 5 deletions src/base/traverseFunctions.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { LOADED, FAILED } from './constants.js';

function isDownloadFinished( value ) {
export function isTileDownloadFinished( tile ) {

return value === LOADED || value === FAILED;
return tile.__loadingState === LOADED || tile.__loadingState === FAILED;

}

Expand Down Expand Up @@ -59,7 +59,7 @@ function recursivelyLoadTiles( tile, depthFromRenderedParent, renderer ) {
const doTraverse =
tile.__contentEmpty && (
! tile.__externalTileSet ||
isDownloadFinished( tile.__loadingState )
isTileDownloadFinished( tile )
);
if ( doTraverse ) {

Expand Down Expand Up @@ -238,7 +238,7 @@ export function markUsedSetLeaves( tile, renderer ) {

const childLoaded =
c.__allChildrenLoaded ||
( ! c.__contentEmpty && isDownloadFinished( c.__loadingState ) ) ||
( ! c.__contentEmpty && isTileDownloadFinished( c ) ) ||
( c.__externalTileSet && c.__loadingState === FAILED );
allChildrenLoaded = allChildrenLoaded && childLoaded;

Expand Down Expand Up @@ -300,7 +300,7 @@ export function skipTraversal( tile, renderer ) {
const includeTile = meetsSSE || tile.refine === 'ADD';
const hasModel = ! tile.__contentEmpty;
const hasContent = hasModel || tile.__externalTileSet;
const loadedContent = isDownloadFinished( tile.__loadingState ) && hasContent;
const loadedContent = isTileDownloadFinished( tile ) && hasContent;
const childrenWereVisible = tile.__childrenWereVisible;
const children = tile.children;
let allChildrenHaveContent = tile.__allChildrenLoaded;
Expand Down
3 changes: 3 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ export { I3DMLoaderBase } from './base/I3DMLoaderBase';
export { PNTSLoaderBase } from './base/PNTSLoaderBase';
export { CMPTLoaderBase } from './base/CMPTLoaderBase';
export { LoaderBase } from './base/LoaderBase';
export { isTileDownloadFinished } from './base/traverseFunctions';

export * as ThreeTileUtils from './three/ThreeTileUtils';

export { LRUCache } from './utilities/LRUCache';
export { PriorityQueue } from './utilities/PriorityQueue';
5 changes: 5 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ import { PNTSLoader } from './three/PNTSLoader.js';
import { I3DMLoader } from './three/I3DMLoader.js';
import { CMPTLoader } from './three/CMPTLoader.js';
import { GLTFExtensionLoader } from './three/GLTFExtensionLoader.js';
import * as ThreeTileUtils from './three/ThreeTileUtils.js';

import { TilesRendererBase } from './base/TilesRendererBase.js';
import { LoaderBase } from './base/LoaderBase.js';
import { B3DMLoaderBase } from './base/B3DMLoaderBase.js';
import { I3DMLoaderBase } from './base/I3DMLoaderBase.js';
import { PNTSLoaderBase } from './base/PNTSLoaderBase.js';
import { CMPTLoaderBase } from './base/CMPTLoaderBase.js';
import { isTileDownloadFinished } from './base/traverseFunctions.js';

import { LRUCache } from './utilities/LRUCache.js';
import { PriorityQueue } from './utilities/PriorityQueue.js';
Expand All @@ -47,6 +49,9 @@ export {
LRUCache,
PriorityQueue,

isTileDownloadFinished,
ThreeTileUtils,

NONE,
SCREEN_ERROR,
GEOMETRIC_ERROR,
Expand Down
19 changes: 19 additions & 0 deletions src/three/ThreeTileUtils.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import type { Box3, Matrix4, Sphere } from 'three';
import type { TileBase } from '../base/TileBase';

// Convert optional 3d-tiles transform object into a THREE.Matrix4
export function convertTileTransform( transform: TileBase['transform'], parentMatrix: Matrix4 ): Matrix4;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe tileTransformToMatrix4 is a bit clearer?


type BoundingVolumeDescriptor = {
box: Box3|null,
boxTransform: Matrix4|null,
boxTransformInverse: Matrix4|null,
sphere: Sphere,
region: null,
}

// Convert 3d-tiles boundingVolume optional definitions into concrete THREE box/sphere/matrix description
// - ex: used in 'tile.cached'
export function convertTileBoundingVolume( boundingVolume: TileBase['boundingVolume'], transform: Matrix4 ): BoundingVolumeDescriptor;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this should be convertBoundingVolumeToBox3 to match the function below? Or maybe just boundingVolumeToBox3 to be shorter?


export function convertBox3ToBoundingVolume( localBox: Box3, boxTransform: Matrix4 ): number[];
173 changes: 173 additions & 0 deletions src/three/ThreeTileUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { Box3, Matrix4, Vector3, Sphere } from 'three';

const vecX = new Vector3();
const vecY = new Vector3();
const vecZ = new Vector3();

// Convert optional 3d-tiles transform object into a THREE.Matrix4
export function convertTileTransform( transform, parentMatrix ) {

const result = new Matrix4();
if ( transform ) {

const transformArr = transform;
for ( let i = 0; i < 16; i ++ ) {

result.elements[ i ] = transformArr[ i ];

}

} else {

result.identity();

}

if ( parentMatrix ) {

result.premultiply( parentMatrix );

}

return result;

}

// Convert 3d-tiles boundingVolume optional definitions into concrete THREE box/sphere/matrix description
// - ex: used in 'tile.cached'
export function convertTileBoundingVolume( boundingVolume, transform ) {

let box = null;
let boxTransform = null;
let boxTransformInverse = null;
if ( 'box' in boundingVolume ) {

const data = boundingVolume.box;
box = new Box3();
boxTransform = new Matrix4();
boxTransformInverse = new Matrix4();

// get the extents of the bounds in each axis
vecX.set( data[ 3 ], data[ 4 ], data[ 5 ] );
vecY.set( data[ 6 ], data[ 7 ], data[ 8 ] );
vecZ.set( data[ 9 ], data[ 10 ], data[ 11 ] );

const scaleX = vecX.length();
const scaleY = vecY.length();
const scaleZ = vecZ.length();

vecX.normalize();
vecY.normalize();
vecZ.normalize();

// handle the case where the box has a dimension of 0 in one axis
if ( scaleX === 0 ) {

vecX.crossVectors( vecY, vecZ );

}

if ( scaleY === 0 ) {

vecY.crossVectors( vecX, vecZ );

}

if ( scaleZ === 0 ) {

vecZ.crossVectors( vecX, vecY );

}

// create the oriented frame that the box exists in
boxTransform.set(
vecX.x, vecY.x, vecZ.x, data[ 0 ],
vecX.y, vecY.y, vecZ.y, data[ 1 ],
vecX.z, vecY.z, vecZ.z, data[ 2 ],
0, 0, 0, 1
);
boxTransform.premultiply( transform );
boxTransformInverse.copy( boxTransform ).invert();

// scale the box by the extents
box.min.set( - scaleX, - scaleY, - scaleZ );
box.max.set( scaleX, scaleY, scaleZ );

}

let sphere = null;
if ( 'sphere' in boundingVolume ) {

const data = boundingVolume.sphere;
sphere = new Sphere();
sphere.center.set( data[ 0 ], data[ 1 ], data[ 2 ] );
sphere.radius = data[ 3 ];
sphere.applyMatrix4( transform );

} else if ( 'box' in boundingVolume ) {

const data = boundingVolume.box;
sphere = new Sphere();
box.getBoundingSphere( sphere );
sphere.center.set( data[ 0 ], data[ 1 ], data[ 2 ] );
sphere.applyMatrix4( transform );

}

let region = null;
if ( 'region' in boundingVolume ) {

console.warn( 'ThreeTilesRenderer: region bounding volume not supported.' );

}

return {

box,
boxTransform,
boxTransformInverse,
sphere,
region,

};

}

// Convert a three box3 + transform into a 3d-tiles boundingVolume.box array
export function convertBox3ToBoundingVolume( box, boxTransform ) {

const worldBox = new Box3().copy( box ).applyMatrix4( boxTransform );
const min = [ worldBox.min.x, worldBox.min.y, worldBox.min.z ];
const max = [ worldBox.max.x, worldBox.max.y, worldBox.max.z ];

const center = [
( max[ 0 ] - min[ 0 ] ) / 2 + min[ 0 ],
( max[ 1 ] - min[ 1 ] ) / 2 + min[ 1 ],
( max[ 2 ] - min[ 2 ] ) / 2 + min[ 2 ],
];

const halfX = ( max[ 0 ] - min[ 0 ] ) / 2.0;
const halfY = ( max[ 1 ] - min[ 1 ] ) / 2.0;
const halfZ = ( max[ 2 ] - min[ 2 ] ) / 2.0;

// oriented bounding box
// a right-handed 3-axis (x, y, z) Cartesian coordinate system where the z-axis is up.
const boundingVolumeBox = [
// The first three elements define the x, y, and z values for the center of the box.
// center
center[ 0 ], center[ 1 ], center[ 2 ],

// The next three elements (with indices 3, 4, and 5) define the x-axis direction and half-length.
halfX, 0.0, 0.0,

// The next three elements (indices 6, 7, and 8) define the y-axis direction and half-length.
// TODO: verify handedness swap and whether we really need to flip the Y axis direction, or if something
// else is wrong internally?
0.0, - halfY, 0.0,

// The last three elements (indices 9, 10, and 11) define the z-axis direction and half-length.
0.0, 0.0, halfZ,
];
return boundingVolumeBox;

}
1 change: 1 addition & 0 deletions src/three/TilesRenderer.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ export class TilesRenderer extends TilesRendererBase {
onLoadTileSet : ( ( tileSet : Tileset ) => void ) | null;
onLoadModel : ( ( scene : Object3D, tile : Tile ) => void ) | null;
onDisposeModel : ( ( scene : Object3D, tile : Tile ) => void ) | null;
onTileVisibilityChange : ( ( scene : Object3D, tile : Tile, visible : boolean ) => void ) | null;

}
Loading