UNPKG

three-mesh-bvh

Version:

A BVH implementation to speed up raycasting against three.js meshes.

159 lines (94 loc) 3.74 kB
import { Box3 } from 'three'; import { SKIP_GENERATION, DEFAULT_OPTIONS } from './Constants.js'; import { isSharedArrayBufferSupported } from '../utils/BufferUtils.js'; import { ensureIndex, getRootPrimitiveRanges } from './build/geometryUtils.js'; import { BVH } from './BVH.js'; // construct a new buffer that points to the set of triangles represented by the given ranges export function generateIndirectBuffer( ranges, useSharedArrayBuffer ) { const lastRange = ranges[ ranges.length - 1 ]; const useUint32 = lastRange.offset + lastRange.count > 2 ** 16; // use getRootIndexRanges which excludes gaps const length = ranges.reduce( ( acc, val ) => acc + val.count, 0 ); const byteCount = useUint32 ? 4 : 2; const buffer = useSharedArrayBuffer ? new SharedArrayBuffer( length * byteCount ) : new ArrayBuffer( length * byteCount ); const indirectBuffer = useUint32 ? new Uint32Array( buffer ) : new Uint16Array( buffer ); // construct a compact form of the triangles in these ranges let index = 0; for ( let r = 0; r < ranges.length; r ++ ) { const { offset, count } = ranges[ r ]; for ( let i = 0; i < count; i ++ ) { indirectBuffer[ index + i ] = offset + i; } index += count; } return indirectBuffer; } export class GeometryBVH extends BVH { get indirect() { return ! ! this._indirectBuffer; } get primitiveStride() { return null; } get primitiveBufferStride() { return this.indirect ? 1 : this.primitiveStride; } set primitiveBufferStride( v ) {} get primitiveBuffer() { return this.indirect ? this._indirectBuffer : this.geometry.index.array; } set primitiveBuffer( v ) {} constructor( geometry, options = {} ) { if ( ! geometry.isBufferGeometry ) { throw new Error( 'BVH: Only BufferGeometries are supported.' ); } else if ( geometry.index && geometry.index.isInterleavedBufferAttribute ) { throw new Error( 'BVH: InterleavedBufferAttribute is not supported for the index attribute.' ); } if ( options.useSharedArrayBuffer && ! isSharedArrayBufferSupported() ) { throw new Error( 'BVH: SharedArrayBuffer is not available.' ); } super(); // retain references to the geometry so we can use them it without having to // take a geometry reference in every function. this.geometry = geometry; this.resolvePrimitiveIndex = options.indirect ? i => this._indirectBuffer[ i ] : i => i; this.primitiveBuffer = null; this.primitiveBufferStride = null; this._indirectBuffer = null; options = { ...DEFAULT_OPTIONS, ...options, }; // build the BVH unless we're deserializing if ( ! options[ SKIP_GENERATION ] ) { this.init( options ); } } init( options ) { const { geometry, primitiveStride } = this; if ( options.indirect ) { // construct an buffer that is indirectly sorts the triangles used for the BVH const ranges = getRootPrimitiveRanges( geometry, options.range, primitiveStride ); const indirectBuffer = generateIndirectBuffer( ranges, options.useSharedArrayBuffer ); this._indirectBuffer = indirectBuffer; } else { ensureIndex( geometry, options ); } super.init( options ); if ( ! geometry.boundingBox && options.setBoundingBox ) { geometry.boundingBox = this.getBoundingBox( new Box3() ); } } // Abstract methods to be implemented by subclasses getRootRanges( range ) { // TODO: can we avoid passing options in here if ( this.indirect ) { return [ { offset: 0, count: this._indirectBuffer.length } ]; } else { return getRootPrimitiveRanges( this.geometry, range, this.primitiveStride ); } } raycastObject3D( /* object, raycaster, intersects = [] */ ) { throw new Error( 'BVH: raycastObject3D() not implemented' ); } }