Download node_modules/three-mesh-bvh/src/objects/BVHHelper.js from SimWorld-AI/industrial-harbor-viewer: direct link, hf CLI and curl.
- Browser
- Download file 10.3 kB
-
https://huggingface.co/spaces/SimWorld-AI/industrial-harbor-viewer/resolve/main/node_modules/three-mesh-bvh/src/objects/BVHHelper.js
- Command line
-
hf download hf://spaces/SimWorld-AI/industrial-harbor-viewer/node_modules/three-mesh-bvh/src/objects/BVHHelper.js
-
curl -L -o BVHHelper.js https://huggingface.co/spaces/SimWorld-AI/industrial-harbor-viewer/resolve/main/node_modules/three-mesh-bvh/src/objects/BVHHelper.js
10.3 kB
| /** @import { Color } from 'three' */ | |
| /** @import { GeometryBVH } from '../core/GeometryBVH.js' */ | |
| import { LineBasicMaterial, BufferAttribute, Box3, Group, MeshBasicMaterial, Object3D, BufferGeometry, Mesh, Matrix4, Vector3 } from 'three'; | |
| import { arrayToBox } from '../utils/ArrayBoxUtilities.js'; | |
| import { MeshBVH } from '../core/MeshBVH.js'; | |
| const boundingBox = /* @__PURE__ */ new Box3(); | |
| const matrix = /* @__PURE__ */ new Matrix4(); | |
| const vec = /* @__PURE__ */ new Vector3(); | |
| class BVHRootHelper extends Object3D { | |
| get isMesh() { | |
| return ! this.displayEdges; | |
| } | |
| get isLineSegments() { | |
| return this.displayEdges; | |
| } | |
| get isLine() { | |
| return this.displayEdges; | |
| } | |
| getVertexPosition( ...args ) { | |
| // implement this function so it works with Box3.setFromObject | |
| return Mesh.prototype.getVertexPosition.call( this, ...args ); | |
| } | |
| constructor( bvh, material, depth = 10, group = 0 ) { | |
| super(); | |
| this.material = material; | |
| this.geometry = new BufferGeometry(); | |
| this.name = 'BVHRootHelper'; | |
| this.depth = depth; | |
| this.displayParents = false; | |
| this.bvh = bvh; | |
| this.displayEdges = true; | |
| this._group = group; | |
| } | |
| raycast() {} | |
| update() { | |
| const boundsTree = this.bvh; | |
| this.geometry.dispose(); | |
| this.visible = false; | |
| if ( boundsTree ) { | |
| this.geometry = this.getGeometry( boundsTree ); | |
| this.visible = true; | |
| } | |
| } | |
| getGeometry( boundsTree ) { | |
| const group = this._group; | |
| // fill in the position buffer with the bounds corners | |
| let positionArray = null; | |
| if ( group !== - 1 ) { | |
| positionArray = this.getBVHBoundPositions( boundsTree, group ); | |
| } else { | |
| const positionArrays = boundsTree._roots.map( ( r, i ) => this.getBVHBoundPositions( boundsTree, i ) ); | |
| const total = positionArrays.reduce( ( v, arr ) => v + arr.length, 0 ); | |
| positionArray = new Float32Array( total ); | |
| let offset = 0; | |
| positionArrays.forEach( arr => { | |
| positionArray.set( arr, offset ); | |
| offset += arr.length; | |
| } ); | |
| } | |
| const indexArray = this.getBVHBoundIndices( positionArray ); | |
| // update the geometry | |
| const geometry = new BufferGeometry(); | |
| geometry.setIndex( new BufferAttribute( indexArray, 1, false ) ); | |
| geometry.setAttribute( 'position', new BufferAttribute( positionArray, 3, false ) ); | |
| return geometry; | |
| } | |
| getBVHBoundIndices( positionArray ) { | |
| const boundsCount = positionArray.length / ( 8 * 3 ); | |
| let indexArray; | |
| let indices; | |
| if ( this.displayEdges ) { | |
| // fill in the index buffer to point to the corner points | |
| indices = new Uint8Array( [ | |
| // x axis | |
| 0, 4, | |
| 1, 5, | |
| 2, 6, | |
| 3, 7, | |
| // y axis | |
| 0, 2, | |
| 1, 3, | |
| 4, 6, | |
| 5, 7, | |
| // z axis | |
| 0, 1, | |
| 2, 3, | |
| 4, 5, | |
| 6, 7, | |
| ] ); | |
| } else { | |
| indices = new Uint8Array( [ | |
| // X-, X+ | |
| 0, 1, 2, | |
| 2, 1, 3, | |
| 4, 6, 5, | |
| 6, 7, 5, | |
| // Y-, Y+ | |
| 1, 4, 5, | |
| 0, 4, 1, | |
| 2, 3, 6, | |
| 3, 7, 6, | |
| // Z-, Z+ | |
| 0, 2, 4, | |
| 2, 6, 4, | |
| 1, 5, 3, | |
| 3, 5, 7, | |
| ] ); | |
| } | |
| if ( positionArray.length > 65535 ) { | |
| indexArray = new Uint32Array( indices.length * boundsCount ); | |
| } else { | |
| indexArray = new Uint16Array( indices.length * boundsCount ); | |
| } | |
| const indexLength = indices.length; | |
| for ( let i = 0; i < boundsCount; i ++ ) { | |
| const posOffset = i * 8; | |
| const indexOffset = i * indexLength; | |
| for ( let j = 0; j < indexLength; j ++ ) { | |
| indexArray[ indexOffset + j ] = posOffset + indices[ j ]; | |
| } | |
| } | |
| return indexArray; | |
| } | |
| getBVHBoundPositions( bvh, group = 0, matrix = null ) { | |
| // count the number of bounds required | |
| const targetDepth = this.depth - 1; | |
| const displayParents = this.displayParents; | |
| let boundsCount = 0; | |
| bvh.traverse( ( depth, isLeaf ) => { | |
| if ( depth >= targetDepth || isLeaf ) { | |
| boundsCount ++; | |
| return true; | |
| } else if ( displayParents ) { | |
| boundsCount ++; | |
| } | |
| }, group ); | |
| // fill in the position buffer with the bounds corners | |
| let posIndex = 0; | |
| const positionArray = new Float32Array( 8 * 3 * boundsCount ); | |
| bvh.traverse( ( depth, isLeaf, boundingData ) => { | |
| const terminate = depth >= targetDepth || isLeaf; | |
| if ( terminate || displayParents ) { | |
| arrayToBox( 0, boundingData, boundingBox ); | |
| const { min, max } = boundingBox; | |
| for ( let x = - 1; x <= 1; x += 2 ) { | |
| const xVal = x < 0 ? min.x : max.x; | |
| for ( let y = - 1; y <= 1; y += 2 ) { | |
| const yVal = y < 0 ? min.y : max.y; | |
| for ( let z = - 1; z <= 1; z += 2 ) { | |
| const zVal = z < 0 ? min.z : max.z; | |
| vec.set( xVal, yVal, zVal ); | |
| if ( matrix ) { | |
| vec.applyMatrix4( matrix ); | |
| } | |
| vec.toArray( positionArray, posIndex ); | |
| posIndex += 3; | |
| } | |
| } | |
| } | |
| return terminate; | |
| } | |
| }, group ); | |
| return positionArray; | |
| } | |
| } | |
| /** | |
| * A `THREE.Group` that visualizes a BVH as wireframe bounding boxes or solid | |
| * face overlays. Attach it as a sibling of the mesh in the scene graph and | |
| * call `update()` whenever the mesh's BVH or world transform changes. | |
| * | |
| * @param {Object3D | GeometryBVH | null} [mesh=null] - The mesh whose `geometry.boundsTree` | |
| * should be displayed, or a `GeometryBVH` to display directly. | |
| * @param {GeometryBVH | number | null} [bvh=null] - The BVH to visualize. When the first | |
| * argument is a `GeometryBVH`, this argument is interpreted as `depth`. | |
| * @param {number} [depth=10] - Maximum tree depth to display. | |
| * @extends Group | |
| */ | |
| export class BVHHelper extends Group { | |
| /** | |
| * Shortcut to `edgeMaterial.color`. | |
| * @type {Color} | |
| * @readonly | |
| */ | |
| get color() { | |
| return this.edgeMaterial.color; | |
| } | |
| /** | |
| * Opacity applied to both edge and mesh materials. | |
| * @type {number} | |
| */ | |
| get opacity() { | |
| return this.edgeMaterial.opacity; | |
| } | |
| set opacity( v ) { | |
| this.edgeMaterial.opacity = v; | |
| this.meshMaterial.opacity = v; | |
| } | |
| get objectIndex() { | |
| console.warn( 'BVHHelper: "objectIndex" has been renamed "instanceId".' ); | |
| return this.instanceId; | |
| } | |
| set objectIndex( v ) { | |
| console.warn( 'BVHHelper: "objectIndex" has been renamed "instanceId".' ); | |
| this.instanceId = v; | |
| } | |
| constructor( mesh = null, bvh = null, depth = 10 ) { | |
| // handle bvh, depth signature | |
| if ( mesh instanceof MeshBVH ) { | |
| depth = bvh || 10; | |
| bvh = mesh; | |
| mesh = null; | |
| } | |
| // handle mesh, depth signature | |
| if ( typeof bvh === 'number' ) { | |
| depth = bvh; | |
| bvh = null; | |
| } | |
| super(); | |
| this.name = 'BVHHelper'; | |
| /** @type {number} */ | |
| this.depth = depth; | |
| /** @type {Object3D | null} */ | |
| this.mesh = mesh; | |
| /** @type {GeometryBVH | null} */ | |
| this.bvh = bvh; | |
| /** @type {boolean} */ | |
| this.displayParents = false; | |
| /** @type {boolean} */ | |
| this.displayEdges = true; | |
| /** @type {number} */ | |
| this.instanceId = 0; | |
| this._roots = []; | |
| const edgeMaterial = new LineBasicMaterial( { | |
| color: 0x00FF88, | |
| transparent: true, | |
| opacity: 0.3, | |
| depthWrite: false, | |
| } ); | |
| const meshMaterial = new MeshBasicMaterial( { | |
| color: 0x00FF88, | |
| transparent: true, | |
| opacity: 0.3, | |
| depthWrite: false, | |
| } ); | |
| meshMaterial.color = edgeMaterial.color; | |
| /** | |
| * Material used when rendering in wireframe edge mode. | |
| * @type {LineBasicMaterial} | |
| */ | |
| this.edgeMaterial = edgeMaterial; | |
| /** | |
| * Material used when rendering in solid face mode. | |
| * @type {MeshBasicMaterial} | |
| */ | |
| this.meshMaterial = meshMaterial; | |
| this.update(); | |
| } | |
| /** | |
| * Rebuilds the helper's display geometry from the current BVH state. Must | |
| * be called after changes to the BVH, `depth`, `displayParents`, or | |
| * `displayEdges`. | |
| */ | |
| update() { | |
| const mesh = this.mesh; | |
| const instanceId = this.instanceId; | |
| let bvh = this.bvh || mesh.boundsTree || mesh.geometry && mesh.geometry.boundsTree || null; | |
| if ( mesh && mesh.isBatchedMesh && mesh.boundsTrees && ! bvh && instanceId >= 0 ) { | |
| // get the bvh from a batchedMesh if not provided | |
| // TODO: we should have an official way to get the geometry index cleanly | |
| const drawInfo = mesh._drawInfo[ instanceId ]; | |
| if ( drawInfo ) { | |
| bvh = mesh.boundsTrees[ drawInfo.geometryIndex ] || bvh; | |
| } | |
| } | |
| const totalRoots = bvh ? bvh._roots.length : 0; | |
| while ( this._roots.length > totalRoots ) { | |
| const root = this._roots.pop(); | |
| root.geometry.dispose(); | |
| this.remove( root ); | |
| } | |
| for ( let i = 0; i < totalRoots; i ++ ) { | |
| const { depth, edgeMaterial, meshMaterial, displayParents, displayEdges } = this; | |
| if ( i >= this._roots.length ) { | |
| const root = new BVHRootHelper( bvh, edgeMaterial, depth, i ); | |
| this.add( root ); | |
| this._roots.push( root ); | |
| } | |
| const root = this._roots[ i ]; | |
| root.bvh = bvh; | |
| root.depth = depth; | |
| root.displayParents = displayParents; | |
| root.displayEdges = displayEdges; | |
| root.material = displayEdges ? edgeMaterial : meshMaterial; | |
| root.update(); | |
| } | |
| } | |
| updateMatrixWorld( ...args ) { | |
| const mesh = this.mesh; | |
| const parent = this.parent; | |
| const instanceId = this.instanceId; | |
| if ( mesh !== null ) { | |
| mesh.updateWorldMatrix( true, false ); | |
| if ( parent ) { | |
| this.matrix | |
| .copy( parent.matrixWorld ) | |
| .invert() | |
| .multiply( mesh.matrixWorld ); | |
| } else { | |
| this.matrix | |
| .copy( mesh.matrixWorld ); | |
| } | |
| // handle batched and instanced mesh bvhs | |
| if ( ( mesh.isInstancedMesh || mesh.isBatchedMesh ) && instanceId >= 0 ) { | |
| mesh.getMatrixAt( instanceId, matrix ); | |
| this.matrix.multiply( matrix ); | |
| } | |
| this.matrix.decompose( | |
| this.position, | |
| this.quaternion, | |
| this.scale, | |
| ); | |
| } | |
| super.updateMatrixWorld( ...args ); | |
| } | |
| copy( source ) { | |
| this.depth = source.depth; | |
| this.mesh = source.mesh; | |
| this.bvh = source.bvh; | |
| this.opacity = source.opacity; | |
| this.color.copy( source.color ); | |
| } | |
| clone() { | |
| return new BVHHelper().copy( this ); | |
| } | |
| /** | |
| * Disposes of the materials and geometries used by the helper. | |
| */ | |
| dispose() { | |
| this.edgeMaterial.dispose(); | |
| this.meshMaterial.dispose(); | |
| const children = this.children; | |
| for ( let i = 0, l = children.length; i < l; i ++ ) { | |
| children[ i ].geometry.dispose(); | |
| } | |
| } | |
| } | |
| export class MeshBVHHelper extends BVHHelper { | |
| constructor( ...args ) { | |
| console.warn( 'MeshBVHHelper: Class has been deprecated. Use BVHHelper instead.' ); | |
| super( ...args ); | |
| } | |
| } | |