Can't understand why result is a triangle instead of rectangle when creating 3d polygon

I am relatively new to Three.JS and tried to implement something to build polygons in 3D space. I tried using earcut library to triangulate points - did not help that much, tried to use Hull.js, which did work, but only with convex shapes.
After a while I found solution that was provided by @prisoner849 and for some sets of points it worked perfectly, but for some not.
Live code example
As far as I understand this solution, there should be a rectangle here, not a triangle.
For this set of points it build rectangle as expected:

[
    {
        "x": 2.6443464756011963,
        "y": 1.637024998664856,
        "z": -3.753018856048584
    },
    {
        "x": -2.3420379161834717,
        "y": 1.637024998664856,
        "z": -3.2846970558166504
    },
    {
        "x": -3.70695424079895,
        "y": 1.637024998664856,
        "z": -6.309837818145752
    },
    {
        "x": 0.8760173916816711,
        "y": 1.637024998664856,
        "z": -8.244649887084961
    }
]

How to change the code so example in the fiddle would build a rectangle?

Try this in your code:

  const vertices = new Float32Array([
    -3.70695424079895, -1.5, -6.309837818145752,  // A
    -3.70695424079895,  1.637024998664856, -6.309837818145752,  // B
    0.8760173916816711, -1.5, -8.244649887084961,  // C
    0.8760173916816711,  1.637024998664856, -8.244649887084961   // D
  ]);

  const indices = new Uint16Array([
    0, 1, 2,  // Triangle 1: A -> B -> C
    2, 1, 3   // Triangle 2: C -> B -> D
  ]);

  // UV-Coordinates
  const uvs = new Float32Array([
    0, 0,  // UV A
    0, 1,  // UV B
    1, 0,  // UV C
    1, 1   // UV D
  ]);

  //BufferGeometry
  const geometry = new THREE.BufferGeometry();
  geometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));
  geometry.setAttribute('uv', new THREE.BufferAttribute(uvs, 2));
  geometry.setIndex(new THREE.BufferAttribute(indices, 1));

  var texture = new THREE.TextureLoader().load("https://threejs.org/examples/textures/uv_grid_opengl.jpg");
  const material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide });

  const mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

I kept the example as short as possible, so no normal ones.
A polygon consists of 2 triangles. Therefore you have to set the indices for your geometry. geometry.setIndex(new THREE.BufferAttribute(indices, 1));

With 0, 1, 2, 2, 1, 3 the two triangles are set in a clockwise direction. You can set the order counterclockwise. This decides which side is outside and inside.

That’s how I started back then too :smile:
I hope it helps. I’ll leave it up to you to add the normal ones

2 Likes

Or maybe just try changing the raw points in that live example to this:

var rawPoints = [
    {
        "x": -3.70695424079895,
        "y": -1.5,
        "z": -6.309837818145752
    },
    {
        "x": -3.70695424079895,
        "y": 1.637024998664856,
        "z": -6.309837818145752
    },
    {
        "x": 0.8760173916816711,
        "y": 1.637024998664856,
        "z": -6.309837818145752
    },
    {
        "x": 0.8760173916816711,
        "y": -1.5,
        "z": -6.309837818145752
    }
]
2 Likes

GitHubDragonFlys way is the better option if the original code is preferred.

More generally… in threejs there aren’t quads. There are only triangles. To make a quad, you make 2 triangles, but share 2 of the verts between the triangles via the .index as in @Attila_Schroeder s example.

Or instead output 6 vertices if you don’t want to use an index.

So order do matter in that case. I was looking at the Shape class, and read somewhere about automatic triangulation it performs when created. But how to sort it then? I have a camera position at (0,0,0), I assume it is clockwise order, so I sort like this:

const copy = points.map(p => p.clone());
return copy.sort((a, b) => Math.atan2(a.y, a.x) - Math.atan2(b.y, b.x));

But it’s incorrect anyway, because there is no such thing as clockwise order in 3d space.
And I think you unintentially set all z-indexes to the same value, I tried to simply reorder points at index 2 and 3, and it worked:

var rawPoints = [
    {
        "x": -3.70695424079895,
        "y": -1.5,
        "z": -6.309837818145752
    },
    {
        "x": -3.70695424079895,
        "y": 1.637024998664856,
        "z": -6.309837818145752
    },
    {
        "x": 0.8760173916816711,
        "y": 1.637024998664856,
        "z": -8.244649887084961
    },
    {
        "x": 0.8760173916816711,
        "y": -1.5,
        "z": -8.244649887084961
    }
]

So I guess I just need to came up with some sophisticated method to sort points?

 0---1---2---3
 | \ | \ | \ |
 4---5---6---7
 | \ | \ | \ |
 8---9--10--11
 | \ | \ | \ |
12--13--14--15


5, 4, 0, 0, 1, 5, 6, 5, 1, 1, 2, 6, 7, 6, 2, 2, 3, 7 ... is clockwise
0, 4, 5, 5, 1, 0, 1, 5, 6, 6, 2, 1, 2, 6, 7, 7, 3, 2 ... is counter clockwise

The clockwise and counterclockwise topic refers to the order of your vertices in your vertex array. Since you want to create an area with your vertices, you automatically define a local orientation within the area. And now how you define the triangles in it with the index order cw or ccw defines inside and outside. It doesn’t matter where you start a triangle 0, 5, 4 or 5, 4, 0 or 4, 0, 5 for the first triangle cw makes the same triangle

1 Like

Here you can see the arrangement of the vertices and triangles for the three.js base geometries.

NumberingHelperExamples

see also
BufferGeometryIndexed
BufferGeometryNonIndexed

(from the Collection of examples from discourse.threejs.org)

1 Like

Clockwise exists in 3d. If you’re facing the triangle from it’s front side, the vertices are declared clockwise. If they aren’t clockwise, you’re facing the back side. :smiley:

2 Likes