# ObjectBVH

class in `three-mesh-bvh`, extends `BVH`

```js
import { ObjectBVH } from 'three-mesh-bvh';
```

BVH built from a scene hierarchy rather than a single geometry. Each leaf holds
one Object3D (or one instance of an InstancedMesh/BatchedMesh), enabling
accelerated raycasting and spatial queries across many objects at once.

Example: Casting rays at a thousand objects every frame

```js
import { AmbientLight, BufferAttribute, BufferGeometry, Color, DirectionalLight, InstancedMesh, LineBasicMaterial, LineSegments, MeshStandardMaterial, Object3D, Points, PointsMaterial, Raycaster, TorusGeometry, Vector3 } from 'three';
import { ObjectBVH } from 'three-mesh-bvh';

// scene, camera and renderer are initialized here

const COUNT = 1000;
const RAYS = 200;
const UP = new Vector3( 0, 1, 0 );
const COLORS = [ 0xe91e63, 0x03a9f4, 0x4caf50, 0xffc107, 0x9c27b0 ];

scene.background = new Color( 0x131619 );
const light = new DirectionalLight( 0xffffff, 2.5 );
light.position.set( 1, 2, 1 );
scene.add( light, new AmbientLight( 0xffffff, 0.75 ) );
camera.position.set( 10, 6, 0 );

const tori = new InstancedMesh( new TorusGeometry( 0.15, 0.05, 12, 24 ), new MeshStandardMaterial(), COUNT );
const dummy = new Object3D();
const color = new Color();
for ( let i = 0; i < COUNT; i ++ ) {

	dummy.position.randomDirection().multiplyScalar( 3 * Math.cbrt( Math.random() ) );
	dummy.rotation.set( Math.random() * Math.PI, Math.random() * Math.PI, 0 );
	dummy.updateMatrix();
	tori.setMatrixAt( i, dummy.matrix );
	tori.setColorAt( i, color.set( COLORS[ i % COLORS.length ] ) );

}

scene.add( tori );

const bvh = new ObjectBVH( tori );

const origins = new Array( RAYS ).fill().map( () => new Vector3().randomDirection().multiplyScalar( 4 ) );
const rays = new BufferGeometry();
rays.setAttribute( 'position', new BufferAttribute( new Float32Array( RAYS * 6 ), 3 ) );
scene.add(
	new LineSegments( rays, new LineBasicMaterial( { color: 0xffffff, transparent: true, opacity: 0.25 } ) ),
	new Points( rays, new PointsMaterial( { color: 0xffffff, size: 0.04 } ) ),
);

const raycaster = new Raycaster();
raycaster.firstHitOnly = true;
const { ray } = raycaster;
const hits = [];
renderer.setAnimationLoop( time => {

	const position = rays.attributes.position;
	for ( let i = 0; i < RAYS; i ++ ) {

		ray.origin.copy( origins[ i ] ).applyAxisAngle( UP, time * 0.00012 );
		ray.direction.copy( ray.origin ).negate().normalize();
		hits.length = 0;
		bvh.raycast( raycaster, hits );
		const end = hits.length ? hits[ 0 ].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 );

} );
```

## Constructor

```js
new ObjectBVH( root: Object3D | Array<Object3D>, options?: Object )
```

- `root`, `Object3D | Array<Object3D>`: Root object or array of objects.
- `options`, `Object`, optional: Accepts all standard BVH options plus:
  - `precise`, `boolean`, optional, default `false`: Use vertex-level bounds instead of cached bounding boxes.
  - `includeInstances`, `boolean`, optional, default `true`: Treat each instance of InstancedMesh/BatchedMesh as a separate primitive.

## Methods

### .getObjectFromId

```js
.getObjectFromId( compositeId: number ): Object3D
```

Returns the `Object3D` associated with a composite id as provided to `intersectsObject`.

- `compositeId`, `number`

### .getInstanceFromId

```js
.getInstanceFromId( compositeId: number ): number
```

Returns the instance index associated with a composite id as provided to `intersectsObject`.

- `compositeId`, `number`

### .shapecast

```js
.shapecast( callbacks: Object ): boolean
```

Performs a spatial query against the BVH. Extends the base `shapecast` with an
`intersectsObject` callback that is called once per object primitive in leaf nodes.

- `callbacks`, `Object`
  - `intersectsBounds`, `IntersectsBoundsCallback`
  - `intersectsObject`, `IntersectsObjectCallback`, optional
  - `intersectsRange`, `IntersectsRangeCallback`, optional
  - `boundsTraverseOrder`, `BoundsTraverseOrderCallback`, optional
