Help needed with dynamic updates to an indexed mesh streamed from a WebWorker

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 - Screenshot%20from%202018-09-27%2001-21-55

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

Do you also have a tl;dr version of your post^^? It’s in general more promising to isolate a problem in a small live example. Right know, it’s very hard to investigate why your indexed geometry does not render as expected.

BTW: I’m not sure if sharing your github repo is a real help for the community. If the code is too complex, most users won’t invest the time for debugging. Try to provide a reduced test case which does not require an induction into your app.

Thanks for the suggestion Mugen87, I’ve gotten a little lost in this project lately so a reality check is very much appreciated. Hopefully this shows the issue - https://jsfiddle.net/qhts35a4/1/

The fiddle shows two meshes, one is indexed and the other is non-indexed. The indexed mesh doesn’t render the faces correctly so it shows the quadtree pattern. The non-indexed version (generated from the indexed one) looks perfect for what I’m after.

TL:DR, is it possible to get the indexed mesh to render like the non-indexed one without de-indexing it?

The problem is that the normal vectors of the indexed geometry are messed up. Depending on the geometry, BufferGeometry.computeVertexNormals() does not always produce acceptable results. Any chances you avoid the usage of this method and precompute the normal data?

Isn’t the problem in the topology, rather than the method?

BufferGeometry.computeVertexNormals() should always produce acceptable results for a given topology. The issue here if i understood correctly is that you’re wanting to render a box, but essentially you’re asking for a sphere. It just happens to be a very low res decimated sphere.

Essentially the thing on the right just as low res as possible:

You’d have to figure out the seams somehow, and then you could safely use computeVertexNormals() on it. But you already have all this available as THREE.BoxBufferGeometry().

I’m overall confused with your example, if this represents a quad tree, what more do you need than the cell index to render this? Why deal with it on the level of triangles and vertices at all? Why not create your geometry once (box geometry), and then manage it through the quad three structure?