Updating index, position , uv attributes of a BufferGeometry

I am working on a custom Geometry that extends the BufferGeometry.

I am just wondering what the best way to update the attributes is ?

When setting up the attributes I have this

this.addAttribute( 'position', new BufferAttribute( positions, 2 ));
this.addAttribute( 'uv', new BufferAttribute( uvs, 2 ));
this.setIndex( new BufferAttribute( indices, 1 ) );

Where they are of type Float32Array and indices is of type Uint16Array.

How do I deal with updating them, dealing with changing index sizes. Do I completely reset the attributes somehow ? I believe the lengths of the typed arrays can’t change.

Trying to update positions for instance

positions[i++] = x

Keeps the original data there if not all updated because of different lengths and I believe causes problems.

Does calling this again reset the attribute properly and efficient enough ?

this.addAttribute( 'position', new BufferAttribute( positions, 2 ));

Hopefully this is clear.

Update values in its elements or change its length?

My tests , updating the index like this.index[0] = …

It causes problems with the texture rendering because the length keeps changing per update. So some of the previous texture is still there because of the original index data.

The updates is not per animation frame. But I have implemented a subtitle feature that will change the text quite regularly per subtitle text track cue for instance.

To be specific I am trying to optimise and modify a current library for efficient msdf text rendering as its not efficient enough. Its very bloaty and runs multiple loops for no reason. Which is not part of three.js but uses it. I have initial mods up on my github.

So each text content change the length of the glyph textures change and therefore the size of the type array does too.

I guess I have to reset all the attributes rather than reuse them ?

The index has to be reset to a new set of indices definitely and this seems to work with a new typed array

this.setIndex( new BufferAttribute( indices, 1 ) );
this.index.needsUpdate = true;

However how about uv and position ?

I am getting this error trying to update the current position when the length of the buffer is too big

“[.Offscreen-For-WebGL-00000242A19EF050]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 0”

Sorry for the dumb questions and stating the obvious.

A new generated set of positions and uv and updating like so seems to work. Is this the right way to go then ?

this.attributes.position = new BufferAttribute( newPos, 2 );
  	this.attributes.uv = new BufferAttribute( newUvs, 2 );

  	this.index.needsUpdate = true;

  	this.attributes.position.needsUpdate = true;
  	this.attributes.uv.needsUpdate = true;

Why position’s itemSize is 2?

Its flat text rendering not 3D. there is just X/Y.

Is that above the best way to update in this circumstance. It’s not a manipulation of the current positions as such it seems but a whole new set of positions.

The documentation says:

position (itemSize: 3)
Stores the x, y, and z coordinates of each vertex in this geometry.

There is no z data generated in it. It will complain if I set it to 3. I had to set it to 2. ??

It’s not my original code I am improving it before I can use it properly. The generated positions look like

 positions[i++] = x
positions[i++] = y
// TL
positions[i++] = x
positions[i++] = y + h
// TR
positions[i++] = x + w
positions[i++] = y + h
// BR
positions[i++] = x + w
positions[i++] = y

I may be wrong, but itemSize of position in THREE.BufferGeometry() is always 3.

Even in THREE.ShapeBufferGeometry(), which produces a flat shaped geometry, this parameter equals 3. Look at this. The third (z) coordinate is just always 0 there.

So, if you have no z data, then use 0 for it.

Flat text example

Understand. I can improve it there then. However where does 0 get added.

Here is a fully combined example so both typed arrays are updated at once from the updated glyphs length. I’m trying to do the index here too.

The feature is very similar to that example but it uses a generated texture with the font data using msdf rendering. Its very smooth and scales well.

// BL
	    positions[i] = x;
	    uvs[i] = u0;

	    positions[i+1] = y;
	    uvs[i+1] = v1;

	    // TL
	    positions[i+2] = x;
	    uvs[i+2] = u0;

	    positions[i+3] = heightPos;
	    uvs[i+3] = v0;

	    // TR
	    positions[i+4] = widthPos;
	    uvs[i+4] = u1;

	    positions[i+5] = heightPos;
	    uvs[i+5] = v0;

	    // BR
	    positions[i+6] = widthPos;
	    uvs[i+6] = u1;

	    positions[i+7] = y;
	    uvs[i+7] = v1;

	    i+= 8;

Where is the 0 value for z supposed to go here ?

 positions[i+8] = 0; ?

This article (especially the Geometries section) might answers some of your questions: https://threejs.org/docs/#manual/introduction/How-to-update-things

1 Like

yes I understand that. but there was the issue of updating old data and the lengths being different. so its rendering portions of the old data ?

This is an attempt to add a z position but it wont render now. The index is added at the bottom also. The example is very basic with just 3 coordinate. This has about 8 added ?

positions[i] = x;
	    uvs[i] = u0;

	    positions[i+1] = y;
	    uvs[i+1] = v1;

	    positions[i+2] = 0;

	    // TL
	    positions[i+3] = x;
	    uvs[i+2] = u0;

	    positions[i+4] = heightPos;
	    uvs[i+3] = v0;

	    positions[i+5] = 0;

	    // TR
	    positions[i+4] = widthPos;
	    uvs[i+4] = u1;

	    positions[i+5] = heightPos;
	    uvs[i+5] = v0;

	    positions[i+6] = 0;

	    // BR
	    positions[i+6] = widthPos;
	    uvs[i+6] = u1;

	    positions[i+7] = y;
	    uvs[i+7] = v1;

	    positions[i+8] = 0;

		// BL
	   /* positions[i] = x;
	    uvs[i] = u0;

	    positions[i+1] = y;
	    uvs[i+1] = v1;

	    // TL
	    positions[i+2] = x;
	    uvs[i+2] = u0;

	    positions[i+3] = heightPos;
	    uvs[i+3] = v0;

	    // TR
	    positions[i+4] = widthPos;
	    uvs[i+4] = u1;

	    positions[i+5] = heightPos;
	    uvs[i+5] = v0;

	    // BR
	    positions[i+6] = widthPos;
	    uvs[i+6] = u1;

	    positions[i+7] = y;
	    uvs[i+7] = v1;*/

	    indices[indicesIndex + 0] = indicesValueIndex + 0;
        indices[indicesIndex + 1] = indicesValueIndex + 1;
        indices[indicesIndex + 2] = indicesValueIndex + 2;
        indices[indicesIndex + 3] = indicesValueIndex + 0;
        indices[indicesIndex + 4] = indicesValueIndex + 2;
        indices[indicesIndex + 5] = indicesValueIndex + 3;

	    i += 8;
	    indicesIndex += 6;
	    indicesValueIndex += 4;

just found that https://github.com/Jam3/three-bmfont-text/blob/master/docs/sdf.md

Yes. Its a bit of a mess and fixing it up for my own purpose. its been converted to es6 also. Its getting there but needs cleaning up more after.

Where its all being generated for now but hoping to generate it further in the one loop. There is two loops for now. Before there was many.

Keen to figure out how to properly inject the z cordinate then to also work with the uv and index data.

I’ve had another crack adding z value to the positions. It won’t render properly. Is this relevant to the shader at all ?

The uv is 4 sets of 2 values. The positions is four sets of 3 values. I am assuming this is a quad mesh or something.

It will only render properly if the positions is x/y.

So again out of curiosity. If I attempt to update the current uv / position with new values. Do I set this new “drawRange” with the current length like “drawRange(0,length);” .

And that will work with changing of lengths and values ? If I just update the values its rendering the old data. Hence my questions above.

positions[verticesOffset] = x;
	    uvs[uvOffset] = u0;


	    positions[verticesOffset + 1] = y;
	    uvs[uvOffset + 1] = v1;

	    positions[verticesOffset + 2] = 0;
	    // TL
	    positions[verticesOffset + 3] = x;
	    uvs[uvOffset + 2] = u0;

	    positions[verticesOffset + 4] = heightPos;
	    uvs[uvOffset + 3] = v0;

	    positions[verticesOffset + 5] = 0;

	    // TR
	    positions[verticesOffset + 6] = widthPos;
	    uvs[uvOffset + 4] = u1;

	    positions[verticesOffset + 7] = heightPos;
	    uvs[uvOffset + 5] = v0;

	    positions[verticesOffset + 8] = 0;

	    // BR
	    positions[verticesOffset + 9] = widthPos;
	    uvs[uvOffset + 6] = u1;

	    positions[verticesOffset + 10] = y;
	    uvs[uvOffset + 7] = v1;

	    positions[verticesOffset + 11] = 0;

	
	    indices[indicesOffset] = indicesValueIndex + 0;
        indices[indicesOffset + 1] = indicesValueIndex + 1;
        indices[indicesOffset + 2] = indicesValueIndex + 2;
        indices[indicesOffset + 3] = indicesValueIndex + 0;
        indices[indicesOffset + 4] = indicesValueIndex + 2;
        indices[indicesOffset + 5] = indicesValueIndex + 3;

	    verticesOffset += 12;
	    uvOffset += 8;
	    indicesOffset += 6;
	    indicesValueIndex += 4;

This is the cleaned up positions builder. It cant be updated efficiently it seems and new buffers and lengths have to match the amount of glyphs being used. In my case I am detecting text length. And now using a draw range of glyphs being used.

Hopefully this is efficient enough.