UNPKG

three-mesh-bvh

Version:

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

166 lines (115 loc) 4.16 kB
/** @import { BufferGeometry } from 'three' */ import { Box3, BufferAttribute } from 'three'; import { MeshBVH } from '../core/MeshBVH.js'; import { WorkerBase } from './utils/WorkerBase.js'; /** * Helper class for generating a MeshBVH for a given geometry in asynchronously in a worker. The * geometry position and index buffer attribute `ArrayBuffers` are transferred to the Worker while * the BVH is being generated meaning the geometry will be unavailable to use while the BVH is * being processed unless `SharedArrayBuffers` are used. They will be automatically replaced when * the MeshBVH is finished generating. * * _NOTE It's best to reuse a single instance of this class to avoid the overhead of instantiating * a new Worker._ * * @extends WorkerBase */ export class GenerateMeshBVHWorker extends WorkerBase { constructor() { const worker = new Worker( new URL( './generateMeshBVH.worker.js', import.meta.url ), { type: 'module' } ); super( worker ); this.name = 'GenerateMeshBVHWorker'; } /** * Flag indicating whether or not a BVH is already being generated in the worker. * @name running * @memberof GenerateMeshBVHWorker * @instance * @type {boolean} */ /** * Generates a `MeshBVH` instance for the given geometry with the given options in a WebWorker. * Returns a Promise that resolves with the generated `MeshBVH`. Throws if already running. * * @name generate * @memberof GenerateMeshBVHWorker * @instance * @function * @param {BufferGeometry} geometry * @param {Object} [options] - Same options accepted by the `MeshBVH` constructor. * @param {function(number): void} [options.onProgress] - Callback invoked with a `[0, 1]` * progress value as the BVH is built. * @returns {Promise<MeshBVH>} */ /** * Terminates the worker. * * @name dispose * @memberof GenerateMeshBVHWorker * @instance * @function */ runTask( worker, geometry, options = {} ) { return new Promise( ( resolve, reject ) => { if ( geometry.getAttribute( 'position' ).isInterleavedBufferAttribute || geometry.index && geometry.index.isInterleavedBufferAttribute ) { throw new Error( 'GenerateMeshBVHWorker: InterleavedBufferAttribute are not supported for the geometry attributes.' ); } worker.onerror = e => { reject( new Error( `GenerateMeshBVHWorker: ${ e.message }` ) ); }; worker.onmessage = e => { const { data } = e; if ( data.error ) { reject( new Error( data.error ) ); worker.onmessage = null; } else if ( data.serialized ) { const { serialized, position } = data; const bvh = MeshBVH.deserialize( serialized, geometry, { setIndex: false } ); const boundsOptions = Object.assign( { setBoundingBox: true, }, options ); // we need to replace the arrays because they're neutered entirely by the // webworker transfer. geometry.attributes.position.array = position; if ( serialized.index ) { if ( geometry.index ) { geometry.index.array = serialized.index; } else { const newIndex = new BufferAttribute( serialized.index, 1, false ); geometry.setIndex( newIndex ); } } if ( boundsOptions.setBoundingBox ) { geometry.boundingBox = bvh.getBoundingBox( new Box3() ); } if ( options.onProgress ) { options.onProgress( data.progress ); } resolve( bvh ); worker.onmessage = null; } else if ( options.onProgress ) { options.onProgress( data.progress ); } }; const index = geometry.index ? geometry.index.array : null; const position = geometry.attributes.position.array; const transferable = [ position ]; if ( index ) { transferable.push( index ); } worker.postMessage( { index, position, options: { ...options, onProgress: null, includedProgressCallback: Boolean( options.onProgress ), groups: [ ... geometry.groups ], }, }, transferable.map( arr => arr.buffer ).filter( v => ( typeof SharedArrayBuffer === 'undefined' ) || ! ( v instanceof SharedArrayBuffer ) ) ); } ); } }