faceVertexUvs for BufferGeometry

Hi there,

Due to the phaseout of the Geometry, I have a question for the BoxBufferGeometry:

In my application, I change the faceVertexUvs Box like so (to fit a custom texture)

geometry.faceVertexUvs[0][0] = [
    new Vector2(texture_data.side2[1].x, texture_data.side2[0].y),
    new Vector2(texture_data.side2[0].x, texture_data.side2[0].y),
    new Vector2(texture_data.side2[1].x, texture_data.side2[1].y),
  ];

… for each side. While doing this I understand what I am doing, however doing this for the BufferGeometry I don’t really know how to proceed. So, do you know of any documentation on how to do this or can guide me in the right direction?

Thanks!

Texture coordinates are defined as a buffer attribute. You can access it like so:

const uvAttribute = geometry.getAttribute( 'uv' );

You can iterate over the texture coordinates like so:

const uv = new THREE.Vector2();

for ( let i = 0; i < uvAttribute.count; i ++ ) {

    uv.fromBufferAttribute( uvAttribute, i );

}

If you update coordinates, you can write the data back to the buffer attribute like so:

uvAttribute.setXY( i, uv.x, uv.y );

If the buffer geometry is indexed, you have to iterate over the index instead and use the data from the index to access a single uv:

const uv = new THREE.Vector2();

for ( let i = 0; i < index.count; i ++ ) {

    const a = index.getX( i );

    uv.fromBufferAttribute( uvAttribute, a );

}

If you want to access a single face at once, increase the loop variable via i += 3 and retrieve the other vertex data by offsetting i.

3 Likes

Thanks for above! Highly appreciated.

I am a bit confused though; If I have different UV mappings for each side of the Box - will this be possible with the indexed version created by default by BoxBufferGeometry - or do I need to turn it into a NonIndexed?

Using your first example above (after converting the geometry to a NonIndexed version) by setting the uvAtttribute seems to work fine, however when using the indexed one I can’t really get it to work (or I am missing something).

Yes.

Do you mind demonstrating the uv issue with a live example?

Thanks, hard to cut the necessary parts, but for each Box I create a textureData Object:

function createBoxTexture() {
var canvasTexture = document.createElement("canvas");

// ... Creating one texture with top, side1 and side2 drawings to be mapped on the box accordingly
return {
  texture: new CanvasTexture(canvasTexture),
  top: [
    new Vector2(0, 1),
    new Vector2(itemLength / canvasWidth, 1 - itemWidth / canvasHeight),
  ],
  side1: [
    new Vector2(0, 1 - itemWidth / canvasHeight),
    new Vector2(
      itemLength / canvasWidth,
      1 - (itemWidth + itemHeight) / canvasHeight
    ),
  ],
  side2: !twoSides
    ? [
        new Vector2(0, 1 - itemWidth / canvasHeight),
        new Vector2(
          itemLength / canvasWidth,
          1 - (itemWidth + itemHeight) / canvasHeight
        ),
      ]
    : [
        new Vector2(0, 1 - (itemWidth + itemHeight) / canvasHeight),
        new Vector2(
          itemWidth / canvasWidth,
          1 - (itemWidth + 2 * itemHeight) / canvasHeight
        ),
      ],
};
}

Then I create a BufferGeometry, turn it to NonIndexed and do this:

let geometry = new BoxBufferGeometry(2, 1, 2).toNonIndexed();
const uvAttribute = geometry.getAttribute("uv");

uvAttribute.setXY(0, texture_data.side2[1].x, texture_data.side2[0].y);
uvAttribute.setXY(1, texture_data.side2[0].x, texture_data.side2[0].y);
uvAttribute.setXY(2, texture_data.side2[1].x, texture_data.side2[1].y);
uvAttribute.setXY(3, texture_data.side2[0].x, texture_data.side2[0].y);
uvAttribute.setXY(4, texture_data.side2[0].x, texture_data.side2[1].y);
uvAttribute.setXY(5, texture_data.side2[1].x, texture_data.side2[1].y);

uvAttribute.setXY(6, texture_data.side2[0].x, texture_data.side2[1].y);
uvAttribute.setXY(7, texture_data.side2[1].x, texture_data.side2[1].y);
uvAttribute.setXY(8, texture_data.side2[0].x, texture_data.side2[0].y);
uvAttribute.setXY(9, texture_data.side2[1].x, texture_data.side2[1].y);
uvAttribute.setXY(10, texture_data.side2[1].x, texture_data.side2[0].y);
uvAttribute.setXY(11, texture_data.side2[0].x, texture_data.side2[0].y);

uvAttribute.setXY(12, texture_data.side1[1].x, texture_data.side1[1].y);
uvAttribute.setXY(13, texture_data.side1[1].x, texture_data.side1[0].y);
uvAttribute.setXY(14, texture_data.side1[0].x, texture_data.side1[1].y);
uvAttribute.setXY(15, texture_data.side1[1].x, texture_data.side1[0].y);
uvAttribute.setXY(16, texture_data.side1[0].x, texture_data.side1[0].y);
uvAttribute.setXY(17, texture_data.side1[0].x, texture_data.side1[1].y);

uvAttribute.setXY(18, texture_data.side1[0].x, texture_data.side1[0].y);
uvAttribute.setXY(19, texture_data.side1[0].x, texture_data.side1[1].y);
uvAttribute.setXY(20, texture_data.side1[1].x, texture_data.side1[0].y);
uvAttribute.setXY(21, texture_data.side1[0].x, texture_data.side1[1].y);
uvAttribute.setXY(22, texture_data.side1[1].x, texture_data.side1[1].y);
uvAttribute.setXY(23, texture_data.side1[1].x, texture_data.side1[0].y);
//... for the rest of the sides

Is it ok with you or do you want me to generate a small live example?

Thanks!

A live example would be indeed more helpful. Use this as a basis: https://jsfiddle.net/kspwo2vm/

Please have a look here: https://jsfiddle.net/mnpaq1bz/1/
So - the aim is to use the Indexed version of BoxBufferGeometry,

Thanks again!

https://jsfiddle.net/uyn39og6/1/

@Mugen87 - Thanks a lot! I use three.js a lot and would really like to donate to the project - is buying t-shirts the only way as of now?

It is possible to sponsor the project via GitHub: GitHub - mrdoob/three.js: JavaScript 3D library.

Check out the Sponsor this project section.

Hi guys,
I have such a question, there is an example at the link three.js - texture - paint which allows you to draw on the surface a 3D model.
The principle is that when you touch the surface of the model, the coordinates of the simple triangle on the surface are determined and a fragment is drawn on the canvas texture.
In this case, faceVertexUvs is used to determine which triangle on the surface of the model was touched, and in the faceVertexUvs array there are vectors of format 0: Array (3)
0: t {x: 0, y: 1, isVector2: true}
1: t {x: 0, y: 0, isVector2: true}
2: t {x: 1, y: 1, isVector2: true}
This is using a simple rectangle var planeGeometry = new THREE.PlaneGeometry (3, 3); so it built on 3 vectors.
I want to use glb, but glb does not have a surface plotting from triangles, its surface is constructed by vertices and in the attributes array there is an array of
uv: Qe
array: Float32Array(8)
0: 0
1: 1
2: 1
3: 1
4: 0
5: 0
6: 1
7: 0
This is a simple rectangle how var planeGeometry = new THREE.PlaneGeometry (3, 3) but it loaded to loader.load (
“plane.glb”.
Thus, when loading GLB into the scene, there is no faceVertexUvs array. i would like to add the faceVertexUvs array to GLB by conversion, how to do it? or tell me other methods and options for how you can implement drawing on GLB so that the drawing is on the UV texture

Only glb? Gltf have uv.

Glb and gltf are the same

Thank you Mugen! I literally could not figure out how to add the faces back to the uv buffer. I asked on the forum but I didnt see this post until now. I could get the faces from the positions but it was not intuitive how to write them back to the buffer. It makes sense now. I’ll try and remember to link this post to the question.