3d-tiles-renderer
Version:
https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification
313 lines (191 loc) • 5.84 kB
JavaScript
import { Vector3, Sphere } from 'three';
import { OBB } from './OBB.js';
import { EllipsoidRegion } from './EllipsoidRegion.js';
const _vecX = /* @__PURE__ */ new Vector3();
const _vecY = /* @__PURE__ */ new Vector3();
const _vecZ = /* @__PURE__ */ new Vector3();
const _sphereVec = /* @__PURE__ */ new Vector3();
const _obbVec = /* @__PURE__ */ new Vector3();
// TODO: check region more precisely in all functions
export class TileBoundingVolume {
constructor() {
this.sphere = null;
this.obb = null;
this.region = null;
this.regionObb = null;
}
intersectsRay( ray ) {
const sphere = this.sphere;
const obb = this.obb || this.regionObb;
// Early out if we don't hit this tile sphere
if ( sphere && ! ray.intersectsSphere( sphere ) ) {
return false;
}
// Early out if we don't this this tile box
if ( obb && ! obb.intersectsRay( ray ) ) {
return false;
}
return true;
}
intersectRay( ray, target = null ) {
const sphere = this.sphere;
const obb = this.obb || this.regionObb;
let sphereDistSq = - Infinity;
let obbDistSq = - Infinity;
if ( sphere ) {
if ( ray.intersectSphere( sphere, _sphereVec ) ) {
sphereDistSq = sphere.containsPoint( ray.origin ) ? 0 : ray.origin.distanceToSquared( _sphereVec );
}
}
if ( obb ) {
if ( obb.intersectRay( ray, _obbVec ) ) {
obbDistSq = obb.containsPoint( ray.origin ) ? 0 : ray.origin.distanceToSquared( _obbVec );
}
}
// if we didn't hit anything then exit
const furthestDist = Math.max( sphereDistSq, obbDistSq );
if ( furthestDist === - Infinity ) {
return null;
}
// get the furthest hit point if needed
ray.at( Math.sqrt( furthestDist ), target );
return target;
}
distanceToPoint( point ) {
const sphere = this.sphere;
const obb = this.obb || this.regionObb;
let sphereDistance = - Infinity;
let obbDistance = - Infinity;
if ( sphere ) {
// Sphere#distanceToPoint is negative inside the sphere, whereas Box3#distanceToPoint is
// zero inside the box. Clipping the distance to a minimum of zero ensures that both
// types of bounding volume behave the same way.
sphereDistance = Math.max( sphere.distanceToPoint( point ), 0 );
}
if ( obb ) {
obbDistance = obb.distanceToPoint( point );
}
// return the further distance of the two volumes
return sphereDistance > obbDistance ? sphereDistance : obbDistance;
}
intersectsFrustum( frustum ) {
const obb = this.obb || this.regionObb;
const sphere = this.sphere;
if ( sphere && ! frustum.intersectsSphere( sphere ) ) {
return false;
}
if ( obb && ! obb.intersectsFrustum( frustum ) ) {
return false;
}
// if we don't have a sphere or obb then just say we did intersect
return Boolean( sphere || obb );
}
intersectsSphere( otherSphere ) {
const obb = this.obb || this.regionObb;
const sphere = this.sphere;
if ( sphere && ! sphere.intersectsSphere( otherSphere ) ) {
return false;
}
if ( obb && ! obb.intersectsSphere( otherSphere ) ) {
return false;
}
return Boolean( sphere || obb );
}
intersectsOBB( otherObb ) {
const obb = this.obb || this.regionObb;
const sphere = this.sphere;
if ( sphere && ! otherObb.intersectsSphere( sphere ) ) {
return false;
}
if ( obb && ! obb.intersectsOBB( otherObb ) ) {
return false;
}
return Boolean( sphere || obb );
}
getOBB( targetBox, targetMatrix ) {
const obb = this.obb || this.regionObb;
if ( obb ) {
targetBox.copy( obb.box );
targetMatrix.copy( obb.transform );
} else {
this.getAABB( targetBox );
targetMatrix.identity();
}
}
getAABB( target ) {
if ( this.sphere ) {
this.sphere.getBoundingBox( target );
} else {
const obb = this.obb || this.regionObb;
target.copy( obb.box ).applyMatrix4( obb.transform );
}
}
getSphere( target ) {
if ( this.sphere ) {
target.copy( this.sphere );
} else if ( this.region ) {
this.region.getBoundingSphere( target );
} else {
const obb = this.obb || this.regionObb;
obb.box.getBoundingSphere( target );
target.applyMatrix4( obb.transform );
}
}
setObbData( data, transform ) {
const obb = new OBB();
// 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
obb.transform
.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
)
.premultiply( transform );
// scale the box by the extents
obb.box.min.set( - scaleX, - scaleY, - scaleZ );
obb.box.max.set( scaleX, scaleY, scaleZ );
obb.update();
this.obb = obb;
}
setSphereData( x, y, z, radius, transform ) {
const sphere = new Sphere();
sphere.center.set( x, y, z );
sphere.radius = radius;
sphere.applyMatrix4( transform );
this.sphere = sphere;
}
setRegionData( ellipsoid, west, south, east, north, minHeight, maxHeight ) {
const region = new EllipsoidRegion(
...ellipsoid.radius,
south, north,
west, east,
minHeight, maxHeight,
);
const obb = new OBB();
region.getBoundingBox( obb.box, obb.transform );
obb.update();
this.region = region;
this.regionObb = obb;
}
}