Three.js BufferGeometry Vertices Not Updating

1.8k views Asked by At

I am looking to connect plane buffer geometry grid tiles which have real elevation data from IndexedDB. My issue is the data resolution on the STRM elevation is not perfect so the edges between the tiles are not the same. I need to essentially average out all the grid edges between the touching vertices to create a seamless terrain.

When I copy paste the code into the console in the scene it works. However just in the code it doesn't. The sceneRef that is passed is valid and the rest of the codebase using the sceneRef correctly.

The tiles are a 3 x 3 with the current grid tile being the center at 1,1 from range 0,0 - 2,2.

function connectTiles(currGridKey, sceneRef){
console.log("connectTiles");
console.log("currGridKey");
// Current Tile Connection
for (var lat = 0; lat < currGridKey[0]+2; lat++) {
    for (var long = 0; long < currGridKey[1]+2; long++) {
        const currentTile = sceneRef.getObjectByName(`${lat}-${long}`); 
        // Current Grid Tile Per Loop
        if (currentTile) {
            const currentTileVerts = currentTile.geometry.attributes.position.array,
                  latPlusTile = sceneRef.getObjectByName(`${lat}-${long+1}`),
                  longPlusTile = sceneRef.getObjectByName(`${lat+1}-${long}`);

            // Connect Latitudinally
            if (latPlusTile) {
                const latPlusTileVerts = latPlusTile.geometry.attributes.position.array;
                for (var z = 0; z < currentTileVerts.length; z+=27) {
                    const newVertHeight = (currentTileVerts[z] + latPlusTileVerts[z]) / 2;
                    latPlusTileVerts[z] = newVertHeight;
                    currentTileVerts[z] = newVertHeight;
                }
                latPlusTile.geometry.attributes.position.needsUpdate = true;
                currentTile.geometry.attributes.position.needsUpdate = true;
            }
            // Connection Longitudinally
            if (longPlusTile) {
                const longPlusTileVerts = longPlusTile.geometry.attributes.position.array;
                for (var x = 0; x < currentTileVerts.length; x+=3) {
                    const newVertHeight = (currentTileVerts[x] + longPlusTileVerts[x]) / 2;
                    longPlusTileVerts[x] = newVertHeight;
                    currentTileVerts[x] = newVertHeight;
                }
                longPlusTile.geometry.attributes.position.needsUpdate = true;
                currentTile.geometry.attributes.position.needsUpdate = true;
            }       
        }
    }
}
2

There are 2 answers

1
M - On

If all values inside the array are in fact being updated, maybe they're just not getting uploaded to the GPU. Instead of changing the value inside geometry.attributes.position directly, try using the .setAttribute() method. The docs state that using .setAttribute() and .getAttribute() is preferrable than accessing it directly because it has its own internal storage mechanism.

const latPlusTileVerts = latPlusTile.geometry.getAttribute("position").array;
// ... Loops
latPlusTile.geometry.getAttribute("position").needsUpdate = true;

// Or an alternative is to generate a new attribute...
// in case updating the old one fails 
const posAttrib = new THREE.BufferAttribute(latPlusTileVerts, 3);
latPlusTile.geometry.setAttribute("position", posAttrib);
0
Edward On

Kind of late, but posting here to help others. I also ran into this issue. As mentioned in the Three.js docs, I found out that these lines are needed:

geometry.computeBoundingBox();
geometry.computeBoundingSphere();

In my case, I believe frustum culling was culling my new geometry.