The MeshBVH generation process modifies the geometry's index bufferAttribute in place to save memory. The BVH construction will use the geometry's boundingBox if it exists or set it if it does not. The BVH will no longer work correctly if the index buffer is modified.
Only triangles within the geometry's draw range (or provided range option) are included in the
BVH. When a geometry has multiple groups, only triangles within the defined group ranges are
included. Triangles in gaps between groups are excluded.
Note that all query functions expect arguments in local space of the BVH and return results in
local space, as well. If world space results are needed they must be transformed into world space
using object.matrixWorld.
import { BufferAttribute, BufferGeometry, HemisphereLight, LineBasicMaterial, LineSegments, Points, PointsMaterial, Ray, Vector3 } from 'three';
import { FBXLoader } from 'three/addons/loaders/FBXLoader.js';
import { MeshBVH } from 'three-mesh-bvh';
// scene, camera and renderer are initialized here
const URL = 'https://raw.githubusercontent.com/mrdoob/three.js/dev/examples/models/fbx/stanford-bunny.fbx';
const RAYS = 1000;
const UP = new Vector3( 0, 1, 0 );
scene.add( new HemisphereLight( 0xffffff, 0x999999, 3 ) );
camera.position.set( 5, 3, 8 );
const { children: [ bunny ] } = await new FBXLoader().loadAsync( URL );
bunny.scale.setScalar( 1 );
bunny.geometry.scale( 0.0075, 0.0075, 0.0075 );
scene.add( bunny );
const bvh = new MeshBVH( bunny.geometry );
const origins = new Array( RAYS ).fill().map( () => new Vector3().randomDirection().multiplyScalar( 3.75 ) );
const rays = new BufferGeometry();
rays.setAttribute( 'position', new BufferAttribute( new Float32Array( RAYS * 6 ), 3 ) );
scene.add(
new LineSegments( rays, new LineBasicMaterial( { color: 0xe91e63, transparent: true, opacity: 0.25 } ) ),
new Points( rays, new PointsMaterial( { color: 0xe91e63, size: 0.04 } ) ),
);
const ray = new Ray();
renderer.setAnimationLoop( time => {
const position = rays.attributes.position;
for ( let i = 0; i < RAYS; i ++ ) {
ray.origin.copy( origins[ i ] ).applyAxisAngle( UP, time * 0.0001 );
ray.direction.copy( ray.origin ).negate().normalize();
const hit = bvh.raycastFirst( ray );
const end = hit ? hit.point : ray.origin;
position.setXYZ( 2 * i, ray.origin.x, ray.origin.y, ray.origin.z );
position.setXYZ( 2 * i + 1, end.x, end.y, end.z );
}
position.needsUpdate = true;
renderer.render( scene, camera );
} );On this page
Properties
Helper function for use when indirect is set to true. This function takes a triangle
index in the BVH layout and returns the associated triangle index in the geometry index
buffer or position attribute.
Methods
new MeshBVH(
geometry: BufferGeometry,
// Same options as GeometryBVH.
options?: Object
)static serialize(
// The BVH to serialize.
bvh: MeshBVH,
options?: {
// If `true`, the index and BVH root buffers are cloned so the serialized data is independent of the live BVH.
cloneBuffers: boolean = true
}
): SerializedBVHGenerates a representation of the complete bounds tree and the geometry index buffer which
can be used to recreate a bounds tree using the deserialize function. The serialize and
deserialize functions can be used to generate a MeshBVH asynchronously in a background web
worker to prevent the main thread from stuttering. The BVH roots buffer stored in the
serialized representation are the same as the ones used by the original BVH so they should
not be modified. If SharedArrayBuffers are used then the same BVH memory can be used for
multiple BVH in multiple WebWorkers.
static deserialize(
// Serialized BVH data.
data: SerializedBVH,
// The geometry the BVH was originally built from.
geometry: BufferGeometry,
options?: {
// If `true`, sets `geometry.index` from the serialized index buffer (creating one if none exists).
setIndex: boolean = true
}
): MeshBVHReturns a new MeshBVH instance from the serialized data. geometry is the geometry used
to generate the original BVH data was derived from. The root buffers stored in data
are set directly on the new BVH so the memory is shared.
shiftTriangleOffsets( offset: number ): voidAdjusts all triangle offsets stored in the BVH by the given offset. This is useful when the
triangle data has been compacted or shifted in the geometry buffers (e.g. in BatchedMesh
when geometries are compacted using the 'optimize' function or constructing a 'merged' BVH).
This function only adjusts the BVH to point to different triangles in the geometry. The
geometry's index buffer and/or position attributes must be updated separately to match.
raycastObject3D(
object: Object3D,
raycaster: Raycaster,
intersects: Array<Intersection> = []
): Array<Intersection>A convenience function for performing a raycast based on a mesh. Results are formed like three.js raycast results in world frame.
refit( nodeIndices: Set<number> | Array<number> | null = null ): voidRefit the node bounds to the current triangle positions. This is quicker than regenerating
a new BVH but will not be optimal after significant changes to the vertices. nodeIndices
is a set of node indices (provided by the shapecast function) that need to be refit
including all internal nodes.
raycast(
ray: Ray,
materialOrSide: number | Material | Array<Material> = FrontSide,
near: number = 0,
far: number = Infinity
): Array<Intersection>Returns all raycast triangle hits in unsorted order. It is expected that ray is in the
frame of the BVH already. Likewise the returned results are also provided in the local
frame of the BVH. The side identifier is used to determine the side to check when
raycasting or a material with the given side field can be passed. If an array of materials
is provided then it is expected that the geometry has groups and the appropriate material
side is used per group.
Note that unlike three.js' Raycaster results the points and distances in the intersections
returned from this function are relative to the local frame of the MeshBVH. When using the
acceleratedRaycast function as an override for Mesh.raycast they are transformed into
world space to be consistent with three's results.
raycastFirst(
ray: Ray,
materialOrSide: number | Material | Array<Material> = FrontSide,
near: number = 0,
far: number = Infinity
): Intersection | nullReturns the first raycast hit in the model. This is typically much faster than returning
all hits. See raycast for information on the side and material options as well as the
frame of the returned intersections.
intersectsGeometry(
otherGeometry: BufferGeometry,
// Transform of `otherGeometry` into the local space of this BVH.
geometryToBvh: Matrix4
): booleanReturns whether or not the mesh intersects the given geometry.
The geometryToBvh parameter is the transform of the geometry in the BVH's local frame.
Performance improves considerably if the provided geometry also has a boundsTree.
shapecast(
callbacks: {
intersectsBounds: ( box: Box3, isLeaf: boolean, score: number | undefined, depth: number, nodeIndex: number ) => number,
intersectsTriangle?: ( triangle: ExtendedTriangle, triangleIndex: number, contained: boolean, depth: number ) => boolean,
intersectsRange?: ( offset: number, count: number, contained: boolean, depth: number, nodeIndex: number, box: Box3 ) => boolean,
boundsTraverseOrder?: ( box: Box3 ) => number
}
): booleanA generalized cast function that can be used to implement intersection logic for custom
shapes. This is used internally for intersectsBox, intersectsSphere, and more. The
function returns as soon as a triangle has been reported as intersected and returns true
if a triangle has been intersected.
bvhcast(
otherBvh: MeshBVH,
// Transforms `otherBvh` into the local space of this BVH.
matrixToLocal: Matrix4,
callbacks: {
intersectsRanges?: ( offset1: number, count1: number, offset2: number, count2: number, depth1: number, nodeIndex1: number, depth2: number, nodeIndex2: number ) => boolean,
intersectsTriangles?: ( triangle1: ExtendedTriangle, triangle2: ExtendedTriangle, triangleIndex1: number, triangleIndex2: number, depth1: number, nodeIndex1: number, depth2: number, nodeIndex2: number ) => boolean
}
): booleanA generalized cast function that traverses two BVH structures simultaneously to perform
intersection tests between them. This is used internally by intersectsGeometry. The
function returns true as soon as a triangle pair has been reported as intersected by
the callbacks.
matrixToLocal is a Matrix4 that transforms otherBvh into the local space of this BVH.
The other BVH's triangles are transformed by this matrix before intersection tests.
intersectsBox(
box: Box3,
// Transform of the box in the local space of this BVH.
boxToBvh: Matrix4
): booleanReturns whether or not the mesh intersects the given box.
The boxToBvh parameter is the transform of the box in the meshes frame.
intersectsSphere( sphere: Sphere ): booleanReturns whether or not the mesh intersects the given sphere.
closestPointToGeometry(
otherGeometry: BufferGeometry,
// Transform of `otherGeometry` into the local space of this BVH.
geometryToBvh: Matrix4,
target1: HitPointInfo = {},
target2: HitPointInfo = {},
minThreshold: number = 0,
maxThreshold: number = Infinity
): HitPointInfo | nullComputes the closest distance from the geometry to the mesh and puts the closest point on
the mesh in target1 (in the frame of the BVH) and the closest point on the other
geometry in target2 (in the geometry frame). If target1 is not provided a new Object
is created and returned from the function.
The geometryToBvh parameter is the transform of the geometry in the BVH's local frame.
If a point is found that is closer than minThreshold then the function will return that
result early. Any triangles or points outside of maxThreshold are ignored. If no point
is found within the min / max thresholds then null is returned and the target objects
are not modified.
The returned faceIndex in target1 and target2 can be used with the standalone function
getTriangleHitPointInfo to obtain more information like UV coordinates, triangle normal
and materialIndex.
Note that this function can be very slow if geometry does not have a
geometry.boundsTree computed.
closestPointToPoint(
point: Vector3,
target: HitPointInfo = {},
minThreshold: number = 0,
maxThreshold: number = Infinity
): HitPointInfo | nullComputes the closest distance from the point to the mesh and gives additional information
in target. The target can be left undefined to default to a new object which is
ultimately returned by the function.
If a point is found that is closer than minThreshold then the function will return that
result early. Any triangles or points outside of maxThreshold are ignored. If no point
is found within the min / max thresholds then null is returned and the target object
is not modified.
The returned faceIndex can be used with the standalone function getTriangleHitPointInfo
to obtain more information like UV coordinates, triangle normal and materialIndex.
import { Mesh, TorusKnotGeometry, MeshNormalMaterial, MeshBasicMaterial, SphereGeometry } from 'three';
import { MeshBVH } from 'three-mesh-bvh';
// scene, camera and renderer are initialized here
const geometry = new TorusKnotGeometry( 1, 0.3, 200, 30 );
const bvh = new MeshBVH( geometry );
scene.add( new Mesh( geometry, new MeshNormalMaterial() ) );
// a point circles the knot; the red marker is the closest surface point to it
const point = new Mesh( new SphereGeometry( 0.05 ), new MeshNormalMaterial() );
const closest = new Mesh( new SphereGeometry( 0.05 ), new MeshBasicMaterial( { color: 'red' } ) );
scene.add( point, closest );
// the query writes its result straight into the marker's position
const target = { point: closest.position };
renderer.setAnimationLoop( time => {
point.position.set( 2 * Math.cos( time / 1000 ), Math.sin( time / 700 ), 2 * Math.sin( time / 1000 ) );
bvh.closestPointToPoint( point.position, target );
renderer.render( scene, camera );
} );