NgtsBVH
import { ChangeDetectionStrategy, Component } from '@angular/core';import { SobaWrapper } from '@soba/wrapper.ts';import { NgtCanvas, provideNgtRenderer } from 'angular-three/dom';import { SceneGraph } from './scene-graph';
@Component({ selector: 'app-bvh', template: ` <ngt-canvas [camera]="{ position: [0, 0, 6], fov: 50 }"> <app-soba-wrapper *canvasContent [controls]="null"> <app-scene-graph /> </app-soba-wrapper> </ngt-canvas> `, changeDetection: ChangeDetectionStrategy.OnPush, host: { class: 'bvh-demo relative block h-full' }, imports: [NgtCanvas, SobaWrapper, SceneGraph],})export default class Bvh { static clientProviders = [provideNgtRenderer()];}import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, signal } from '@angular/core';import { NgtArgs } from 'angular-three';import { NgtsOrbitControls } from 'angular-three-soba/controls';import { NgtsBVH } from 'angular-three-soba/performances';
@Component({ selector: 'app-scene-graph', template: ` <!-- BVH-accelerated meshes for fast raycasting --> <ngts-bvh [options]="{ firstHitOnly: true }"> <!-- Complex geometry - torus knot --> <ngt-mesh [position]="[-2, 0, 0]" (pointerover)="color1.set('#ffff00')" (pointerout)="color1.set('#ff6b6b')" > <ngt-torus-knot-geometry *args="[0.6, 0.2, 128, 32]" /> <ngt-mesh-standard-material [color]="color1()" /> </ngt-mesh>
<!-- Complex geometry - icosahedron with high detail --> <ngt-mesh [position]="[0, 0, 0]" (pointerover)="color2.set('#ffff00')" (pointerout)="color2.set('#4ecdc4')"> <ngt-icosahedron-geometry *args="[0.8, 4]" /> <ngt-mesh-standard-material [color]="color2()" /> </ngt-mesh>
<!-- Complex geometry - dodecahedron --> <ngt-mesh [position]="[2, 0, 0]" (pointerover)="color3.set('#ffff00')" (pointerout)="color3.set('#a29bfe')"> <ngt-dodecahedron-geometry *args="[0.8, 2]" /> <ngt-mesh-standard-material [color]="color3()" /> </ngt-mesh> </ngts-bvh>
<ngts-orbit-controls [options]="{ enablePan: false }" /> `, schemas: [CUSTOM_ELEMENTS_SCHEMA], changeDetection: ChangeDetectionStrategy.OnPush, imports: [NgtArgs, NgtsBVH, NgtsOrbitControls],})export class SceneGraph { color1 = signal('#ff6b6b'); color2 = signal('#4ecdc4'); color3 = signal('#a29bfe');}NgtsBVH applies Bounding Volume Hierarchy (BVH) acceleration to child meshes for significantly faster raycasting performance. It uses three-mesh-bvh under the hood.
BVH is essential for complex geometries where standard raycasting would be too slow.
Dependencies
npm install three-mesh-bvhUsage
import { NgtsBVH } from 'angular-three-soba/performances';<ngts-bvh [options]="{ firstHitOnly: true }"> <ngt-mesh> <ngt-buffer-geometry /> <ngt-mesh-standard-material /> </ngt-mesh></ngts-bvh>When to Use
- Complex meshes with thousands of triangles
- Scenes with many interactive objects
- When raycasting performance is a bottleneck
Split Strategies
SAH(default): Surface Area Heuristic, best for most casesCENTER: Split at center of bounding boxAVERAGE: Split at average of triangle centroids
<ngts-bvh [options]="{ strategy: CENTER, maxDepth: 30 }"> <!-- meshes --></ngts-bvh>Options
Properties
| name | type | description |
|---|---|---|
| enabled | boolean | Whether BVH acceleration is enabled, default to true |
| firstHitOnly | boolean | Use raycastFirst for faster single-hit detection, default to false |
| strategy | SplitStrategy | Split strategy for BVH construction (SAH recommended), default to SAH |
| verbose | boolean | Print warnings during tree construction, default to false |
| setBoundingBox | boolean | Set geometry bounding box after BVH construction, default to true |
| maxDepth | number | Maximum tree depth, default to 40 |
| maxLeafTris | number | Target number of triangles per leaf node, default to 10 |
| indirect | boolean | Use separate buffer for BVH structure (experimental), default to false |