UNPKG

three-mesh-bvh

Version:

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

215 lines (151 loc) 5.34 kB
import { Box3 } from 'three'; import { CONTAINED, UINT32_PER_NODE } from '../Constants.js'; import { arrayToBox } from '../../utils/ArrayBoxUtilities.js'; import { PrimitivePool } from '../../utils/PrimitivePool.js'; import { COUNT, OFFSET, LEFT_NODE, RIGHT_NODE, IS_LEAF, BOUNDING_DATA_INDEX } from '../utils/nodeBufferUtils.js'; import { BufferStack } from '../utils/BufferStack.js'; let _box1, _box2; const boxStack = []; const boxPool = /* @__PURE__ */ new PrimitivePool( () => new Box3() ); export function shapecast( bvh, root, intersectsBounds, intersectsRange, boundsTraverseOrder, nodeOffset ) { // setup _box1 = boxPool.getPrimitive(); _box2 = boxPool.getPrimitive(); boxStack.push( _box1, _box2 ); BufferStack.setBuffer( bvh._roots[ root ] ); const result = shapecastTraverse( 0, bvh.geometry, intersectsBounds, intersectsRange, boundsTraverseOrder, nodeOffset ); // cleanup BufferStack.clearBuffer(); boxPool.releasePrimitive( _box1 ); boxPool.releasePrimitive( _box2 ); boxStack.pop(); boxStack.pop(); const length = boxStack.length; if ( length > 0 ) { _box2 = boxStack[ length - 1 ]; _box1 = boxStack[ length - 2 ]; } return result; } function shapecastTraverse( nodeIndex32, geometry, intersectsBoundsFunc, intersectsRangeFunc, nodeScoreFunc = null, nodeIndexOffset = 0, // offset for unique node identifier depth = 0 ) { const { float32Array, uint16Array, uint32Array } = BufferStack; let nodeIndex16 = nodeIndex32 * 2; const isLeaf = IS_LEAF( nodeIndex16, uint16Array ); if ( isLeaf ) { const offset = OFFSET( nodeIndex32, uint32Array ); const count = COUNT( nodeIndex16, uint16Array ); arrayToBox( BOUNDING_DATA_INDEX( nodeIndex32 ), float32Array, _box1 ); return intersectsRangeFunc( offset, count, false, depth, nodeIndexOffset + nodeIndex32 / UINT32_PER_NODE, _box1 ); } else { const left = LEFT_NODE( nodeIndex32 ); const right = RIGHT_NODE( nodeIndex32, uint32Array ); let c1 = left; let c2 = right; let score1, score2; let box1, box2; if ( nodeScoreFunc ) { box1 = _box1; box2 = _box2; // bounding data is not offset arrayToBox( BOUNDING_DATA_INDEX( c1 ), float32Array, box1 ); arrayToBox( BOUNDING_DATA_INDEX( c2 ), float32Array, box2 ); score1 = nodeScoreFunc( box1 ); score2 = nodeScoreFunc( box2 ); if ( score2 < score1 ) { c1 = right; c2 = left; const temp = score1; score1 = score2; score2 = temp; box1 = box2; // box2 is always set before use below } } // Check box 1 intersection if ( ! box1 ) { box1 = _box1; arrayToBox( BOUNDING_DATA_INDEX( c1 ), float32Array, box1 ); } const isC1Leaf = IS_LEAF( c1 * 2, uint16Array ); const c1Intersection = intersectsBoundsFunc( box1, isC1Leaf, score1, depth + 1, nodeIndexOffset + c1 / UINT32_PER_NODE ); let c1StopTraversal; if ( c1Intersection === CONTAINED ) { const offset = getLeftOffset( c1 ); const end = getRightEndOffset( c1 ); const count = end - offset; c1StopTraversal = intersectsRangeFunc( offset, count, true, depth + 1, nodeIndexOffset + c1 / UINT32_PER_NODE, box1 ); } else { c1StopTraversal = c1Intersection && shapecastTraverse( c1, geometry, intersectsBoundsFunc, intersectsRangeFunc, nodeScoreFunc, nodeIndexOffset, depth + 1 ); } if ( c1StopTraversal ) return true; // Check box 2 intersection // cached box2 will have been overwritten by previous traversal box2 = _box2; arrayToBox( BOUNDING_DATA_INDEX( c2 ), float32Array, box2 ); const isC2Leaf = IS_LEAF( c2 * 2, uint16Array ); const c2Intersection = intersectsBoundsFunc( box2, isC2Leaf, score2, depth + 1, nodeIndexOffset + c2 / UINT32_PER_NODE ); let c2StopTraversal; if ( c2Intersection === CONTAINED ) { const offset = getLeftOffset( c2 ); const end = getRightEndOffset( c2 ); const count = end - offset; c2StopTraversal = intersectsRangeFunc( offset, count, true, depth + 1, nodeIndexOffset + c2 / UINT32_PER_NODE, box2 ); } else { c2StopTraversal = c2Intersection && shapecastTraverse( c2, geometry, intersectsBoundsFunc, intersectsRangeFunc, nodeScoreFunc, nodeIndexOffset, depth + 1 ); } if ( c2StopTraversal ) return true; return false; // Define these inside the function so it has access to the local variables needed // when converting to the buffer equivalents function getLeftOffset( nodeIndex32 ) { const { uint16Array, uint32Array } = BufferStack; let nodeIndex16 = nodeIndex32 * 2; // traverse until we find a leaf while ( ! IS_LEAF( nodeIndex16, uint16Array ) ) { nodeIndex32 = LEFT_NODE( nodeIndex32 ); nodeIndex16 = nodeIndex32 * 2; } return OFFSET( nodeIndex32, uint32Array ); } function getRightEndOffset( nodeIndex32 ) { const { uint16Array, uint32Array } = BufferStack; let nodeIndex16 = nodeIndex32 * 2; // traverse until we find a leaf while ( ! IS_LEAF( nodeIndex16, uint16Array ) ) { // adjust offset to point to the right node nodeIndex32 = RIGHT_NODE( nodeIndex32, uint32Array ); nodeIndex16 = nodeIndex32 * 2; } // return the end offset of the triangle range return OFFSET( nodeIndex32, uint32Array ) + COUNT( nodeIndex16, uint16Array ); } } }