Hi All,
I’ve been working on a in-browser CAM tool (WebAssembly, Three.js and React) for 3 axis CNC machines and I’ve run into a Three.js problem I haven’t been able to find a good solution to.
The part I’m stuck on is efficiently getting an indexed mesh from a WebWorker to the main browser thread and then dynamically updating it N times per second (currently 5). This is part of the simulation feature where you simulate the tool colliding against a square bit of stock (like a bit of metal, plastic, wood etc) and seeing what’s left at the end.
For context -
- The ‘stock’ being machined is represented as a quadtree height map
- The quadtree subdivides when collided with the tool (a circle) down to a max tree depth based on the level of detail you want (6 levels at the moment)
- When an on screen update is required I convert the leaf nodes in the quadtree to polygon boxes and return them to JS land as a UInt16Array of indices and Float32Array of vertices
- The updated arrays are sent back to the main browser thread where they’re updated on screen by updating a BufferGeometry
The code for creating the model looks like -
let paddedIndices = new Uint16Array(300000);
paddedIndices.fill(0);
paddedIndices.set(indices);
let paddedVertexes = new Float32Array(300000);
paddedVertexes.fill(0);
paddedVertexes.set(vertices);
let geometry = new THREE.BufferGeometry();
geometry.setIndex( new THREE.BufferAttribute(paddedIndices, 1).setDynamic(true) );
geometry.addAttribute( 'position', new THREE.BufferAttribute(paddedVertexes, 3).setDynamic(true) );
geometry.computeVertexNormals();
let material = new THREE.MeshStandardMaterial( { ambient: 0x050505, color: 0x335cff, specular: 0x555555, shininess: 100 } );
let mesh = new THREE.Mesh( geometry, material );
mesh.rotation.x = -Math.PI / 2;
let box = new THREE.Box3().setFromObject( mesh );
let minVector = box.min;
mesh.position.set(-1 * minVector.x, -1 * minVector.y, -1 * minVector.z);
SceneManager.scene.add(mesh);
So I’m padding my buffers to hold my expected max number of values and then setting them as dynamic which I think is correct. When the model is created some triangles are rendering as black for some reason which didn’t happen when using a non-indexed mesh.
And for updates I’m doing -
let indexArray = mesh.geometry.index.array;
indexArray.fill(0)
indexArray.set(indices);
mesh.geometry.index.needsUpdate = true;
let vertexArray = mesh.geometry.attributes.position.array;
vertexArray.fill(0);
vertexArray.set(vertices);
mesh.geometry.attributes.position.needsUpdate = true;
mesh.geometry.computeVertexNormals();
This updates the mesh correctly and is working efficiently enough. The problem is that faces aren’t rendered properly, each box on the quadtree is rendered as a unique face like -
So at the moment I’m kind of stuck between something that’s efficient but looks dodgy or going back to non-indexed geometry which was really inefficient (mostly due to sending large amount of data back from the webworker I think). Instead of using one huge model I was planning on extending the above code so it splits the returned polygons based on their position in the quadtree so I can return smaller updates from the web worker but I’d need to fix the rendering issue first.
Is anyone able to suggest any improvements or might be able to see where I’ve gone wrong?
I’d also love to be able to smooth the mesh so it isn’t rendered as cubes but that’s a problem for another day.
I should hopefully have time to put the latest code up on Github tomorrow
Thanks all!
-Matt