3d-tiles-renderer
Version:
https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification
218 lines (140 loc) • 4.96 kB
JavaScript
import { Matrix4, Box3, Vector3, Plane, Ray } from 'three';
const _worldMin = /* @__PURE__ */ new Vector3();
const _worldMax = /* @__PURE__ */ new Vector3();
const _norm = /* @__PURE__ */ new Vector3();
const _ray = /* @__PURE__ */ new Ray();
export class OBB {
constructor( box = new Box3(), transform = new Matrix4() ) {
this.box = box.clone();
this.transform = transform.clone();
this.inverseTransform = new Matrix4();
this.points = new Array( 8 ).fill().map( () => new Vector3() );
this.planes = new Array( 6 ).fill().map( () => new Plane() );
}
copy( source ) {
this.box.copy( source.box );
this.transform.copy( source.transform );
this.update();
return this;
}
clone() {
return new this.constructor().copy( this );
}
/**
* Clamps the given point within the bounds of this OBB
* @param {Vector3} point
* @param {Vector3} result
* @returns {Vector3}
*/
clampPoint( point, result ) {
return result.copy( point )
.applyMatrix4( this.inverseTransform )
.clamp( this.box.min, this.box.max )
.applyMatrix4( this.transform );
}
/**
* Returns the distance from any edge of this OBB to the specified point.
* If the point lies inside of this box, the distance will be 0.
* @param {Vector3} point
* @returns {number}
*/
distanceToPoint( point ) {
return this.clampPoint( point, _norm ).distanceTo( point );
}
containsPoint( point ) {
_norm.copy( point ).applyMatrix4( this.inverseTransform );
return this.box.containsPoint( _norm );
}
// returns boolean indicating whether the ray has intersected the obb
intersectsRay( ray ) {
_ray.copy( ray ).applyMatrix4( this.inverseTransform );
return _ray.intersectsBox( this.box );
}
// Sets "target" equal to the intersection point.
// Returns "null" if no intersection found.
intersectRay( ray, target ) {
_ray.copy( ray ).applyMatrix4( this.inverseTransform );
if ( _ray.intersectBox( this.box, target ) ) {
target.applyMatrix4( this.transform );
return target;
} else {
return null;
}
}
update() {
const { points, inverseTransform, transform, box } = this;
inverseTransform.copy( transform ).invert();
const { min, max } = box;
let index = 0;
for ( let x = - 1; x <= 1; x += 2 ) {
for ( let y = - 1; y <= 1; y += 2 ) {
for ( let z = - 1; z <= 1; z += 2 ) {
points[ index ].set(
x < 0 ? min.x : max.x,
y < 0 ? min.y : max.y,
z < 0 ? min.z : max.z,
).applyMatrix4( transform );
index ++;
}
}
}
this.updatePlanes();
}
updatePlanes() {
_worldMin.copy( this.box.min ).applyMatrix4( this.transform );
_worldMax.copy( this.box.max ).applyMatrix4( this.transform );
_norm.set( 0, 0, 1 ).transformDirection( this.transform );
this.planes[ 0 ].setFromNormalAndCoplanarPoint( _norm, _worldMin );
this.planes[ 1 ].setFromNormalAndCoplanarPoint( _norm, _worldMax ).negate();
_norm.set( 0, 1, 0 ).transformDirection( this.transform );
this.planes[ 2 ].setFromNormalAndCoplanarPoint( _norm, _worldMin );
this.planes[ 3 ].setFromNormalAndCoplanarPoint( _norm, _worldMax ).negate();
_norm.set( 1, 0, 0 ).transformDirection( this.transform );
this.planes[ 4 ].setFromNormalAndCoplanarPoint( _norm, _worldMin );
this.planes[ 5 ].setFromNormalAndCoplanarPoint( _norm, _worldMax ).negate();
}
intersectsSphere( sphere ) {
this.clampPoint( sphere.center, _norm );
return _norm.distanceToSquared( sphere.center ) <= ( sphere.radius * sphere.radius );
}
intersectsFrustum( frustum ) {
return this._intersectsPlaneShape( frustum.planes, frustum.points );
}
intersectsOBB( obb ) {
return this._intersectsPlaneShape( obb.planes, obb.points );
}
// takes a series of 6 planes that define and enclosed shape and the 8 points that lie at the corners
// of that shape to determine whether the OBB is intersected with.
_intersectsPlaneShape( otherPlanes, otherPoints ) {
const thisPoints = this.points;
const thisPlanes = this.planes;
// based on three.js' Box3 "intersects frustum" function
for ( let i = 0; i < 6; i ++ ) {
const plane = otherPlanes[ i ];
let maxDistance = - Infinity;
for ( let j = 0; j < 8; j ++ ) {
const v = thisPoints[ j ];
const dist = plane.distanceToPoint( v );
maxDistance = maxDistance < dist ? dist : maxDistance;
}
if ( maxDistance < 0 ) {
return false;
}
}
// do the opposite check using the obb planes to avoid false positives
// this check is not performed by three.js' AABB logic but helps prevent a lot incorrect intersection reports
for ( let i = 0; i < 6; i ++ ) {
const plane = thisPlanes[ i ];
let maxDistance = - Infinity;
for ( let j = 0; j < 8; j ++ ) {
const v = otherPoints[ j ];
const dist = plane.distanceToPoint( v );
maxDistance = maxDistance < dist ? dist : maxDistance;
}
if ( maxDistance < 0 ) {
return false;
}
}
return true;
}
}