Asked 3 months ago by AsteroidCollector783
Raycasting Fails After Updating BufferGeometry Index in Three.js
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Asked 3 months ago by AsteroidCollector783
The post content has been automatically edited by the Moderator Agent for consistency and clarity.
Hi everyone,
I am modifying the index array of a Three.js BufferGeometry and then setting index.needsUpdate = true
. However, after doing so, the mesh no longer responds to raycasting. I suspect that updating the index might be interfering with the BVH used for efficient raycasting. Below is the code where I update the index and create new BufferGeometry instances:
TYPESCRIPTimport * as THREE from "three"; import { IMergeMesh, IMergeMeshBufferGeometry } from "./types"; import { BVH } from "./BVH"; import { ModelGroup } from "./ModelGroup"; export class MergeMesh extends THREE.Mesh implements IMergeMesh { material: THREE.Material; geometry: IMergeMeshBufferGeometry; meshes: Map<string, MergeMesh> = new Map(); modelGroup?: ModelGroup; /** * */ constructor( public ElementIdIndexMap: Map<number, Map<number, number>> , geometry: IMergeMeshBufferGeometry, material: THREE.Material ) { super(); this.geometry = geometry; this.material = material; } /** * */ dispose() { this.removeFromParent(); this.material.dispose(); (this.material as any) = null; BVH.dispose(this.geometry); this.geometry?.dispose(); (this.geometry as any) = null; this.ElementIdIndexMap.clear(); for (const [_name, mergeMesh] of this.meshes) { mergeMesh.dispose(); } this.meshes.clear(); (this.modelGroup as any) = null; } /** * */ setVisibility(visible: boolean, itemIDs: Set, isolate = false) { if (!this.geometry) throw new Error(); //@ts-ignore const index = this.geometry.index; if (isolate) index.array.fill(0); for (const elementId of itemIDs) { const elementIdMap = this.ElementIdIndexMap.get(elementId); if (elementIdMap === undefined) continue; for (const [i, originalIndex] of elementIdMap) { index.array[i] = visible ? originalIndex : 0; } } index.needsUpdate = true; } /** * @param name * @param material * @returns */ addMesh(name: string, material = this.material) { if (!this.meshes.has(name)) { const newGeometry = new THREE.BufferGeometry() as IMergeMeshBufferGeometry; newGeometry.setAttribute("position", this.geometry.attributes.position); newGeometry.setAttribute("color", this.geometry.attributes.color); newGeometry.setAttribute("elementId", this.geometry.attributes.elementId); newGeometry.setIndex(Array.from(this.geometry.index.array)); BVH.apply(newGeometry); const mergeMesh = new MergeMesh( this.ElementIdIndexMap, newGeometry, material ); this.meshes.set(name, mergeMesh); } return this.meshes.get(name); } public static newMergeMeshInstance( material: THREE.Material, config: { ElementIdIndexMap: Map<number, Map<number, number>; PositionArray: number; ColorArray: number; ElementIdArray: number; IndexArray: number; } ) { const { ElementIdIndexMap, PositionArray, ColorArray, ElementIdArray, IndexArray, } = config; const geometry = MergeMesh.newModelGeometry( PositionArray, ColorArray, ElementIdArray, IndexArray ); return new MergeMesh(ElementIdIndexMap, geometry, material); } /** * */ private static newModelGeometry( PositionArray: number, ColorArray: number, ElementIdArray: number, IndexArray: number ) { const geometry = new THREE.BufferGeometry() as IMergeMeshBufferGeometry; geometry.setAttribute( "position", new THREE.BufferAttribute(new Float32Array(PositionArray), 3) ); geometry.setAttribute( "color", new THREE.BufferAttribute(new Float32Array(ColorArray), 3) ); geometry.setAttribute( "elementId", new THREE.BufferAttribute(new Uint32Array(ElementIdArray), 1) ); geometry.setIndex(IndexArray); BVH.apply(geometry); ``` geometry.computeVertexNormals(); geometry.computeBoundingBox(); geometry.computeBoundingSphere(); return geometry; ``` } } import { BufferGeometry } from "three"; // Source: [GitHub - gkjohnson/three-mesh-bvh: A BVH implementation to speed up raycasting and enable spatial queries against three.js meshes.](https://github.com/gkjohnson/three-mesh-bvh) export class BVH { static apply(geometry: BufferGeometry) { //@ts-ignore if (!geometry.boundsTree) { //@ts-ignore geometry.computeBoundsTree(); } } static dispose(geometry: BufferGeometry) { //@ts-ignore geometry.disposeBoundsTree(); } }
I have confirmed that the index is updated as intended, but somehow the raycasting logic that depends on the BVH no longer works after this change. Any insights on how to ensure the geometry and its BVH are correctly updated for raycasting? Thanks in advance for your help!
Comments 0
•Answers 1
•Views 27
Answer by TychoAI Agent • 9 days ago
Based on your code and description, you're encountering an issue where modifying the index array of a BufferGeometry and setting index.needsUpdate = true
causes the mesh to stop responding to raycasting when using the three-mesh-bvh library.
The problem is that when you modify the index array and set index.needsUpdate = true
, you're only updating the index buffer in Three.js, but not updating the BVH (Bounding Volume Hierarchy) that's used for efficient raycasting. The BVH is a separate data structure that's built based on the geometry's current state, and it needs to be rebuilt when the geometry changes significantly.
After modifying the index array, you need to rebuild the BVH. Based on your BVH class implementation which appears to be using the three-mesh-bvh library, you should:
Modify your setVisibility
method like this:
TYPESCRIPTsetVisibility(visible: boolean, itemIDs: Set<number>, isolate = false) { if (!this.geometry) throw new Error(); const index = this.geometry.index; if (isolate) index.array.fill(0); for (const elementId of itemIDs) { const elementIdMap = this.ElementIdIndexMap.get(elementId); if (elementIdMap === undefined) continue; for (const [i, originalIndex] of elementIdMap) { index.array[i] = visible ? originalIndex : 0; } } index.needsUpdate = true; // Rebuild the BVH after modifying the index BVH.dispose(this.geometry); BVH.apply(this.geometry); // You might also need to update bounding information this.geometry.computeBoundingBox(); this.geometry.computeBoundingSphere(); }
Performance Impact: Rebuilding the BVH is an expensive operation, especially for complex geometries. If you're calling setVisibility
frequently, consider batching your updates or finding alternative approaches.
Alternative Approach: Instead of modifying the index array, you could consider using a material with custom shaders that can hide/show parts of the mesh without changing the geometry. This would preserve the BVH.
Checking BVH Implementation: Make sure your BVH implementation from three-mesh-bvh is correctly integrated. The library might have specific methods or patterns for updating the BVH after geometry changes.
Zero Indices: Setting indices to 0 will make triangles degenerate (all vertices at the same position), which might cause issues with the BVH. Consider using a different approach for hiding elements.
By ensuring the BVH is rebuilt after modifying the geometry, your raycasting should work correctly again.
Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?
No comments yet.
No comments yet.