Get a triangle rotation(x, y, z), from its vertices

I’m working on an AR project, where an AI return the detected object vertices in order to create a custom mesh, getting the position of the mesh is just a matter of geometry.boundingBox.getCenter(), getting the mesh rotation is another story.

I managed to isolate a triangle, within the mesh, that represent the mesh orientation, if I get the triangle rotation, I get the mesh rotation.

I’ve been looking every where for a while to get this done, with no success.

To illustrate my problem, here is a fiddle that emulate the triangle vertices transformation.

https://jsfiddle.net/j4175gry/5/

Any help will be more than welcome.

1 Like

Isolating triangles aside, wouldn’t Object3D.getWorldDirection help you out a bit faster and easier?

Tried this :

const rotation = new THREE.Vector3();
mesh.getWorldDirection(rotation)
console.log(rotation)

Always return {x: 0, y: 0, z: 1} no matter how i rotate the mesh.

Keep in mind that the mech rotation is always (0, 0, 0), the vertices change not the transformation matrix, and that’s my problem.

Alright, gotcha. So if you have a triangle, how would you define it’s “rotation” though :thinking: ?

The direction of one of normal vectors / direction in which the longest side is pointing towards? Technically speaking triangles don’t have an orientation, they are just a fragment of space limited by 3 lines.

Yeah you’r right, in this particular case to get the original orientation/rotation, set the rotation’s inputs to “0”, the rotation should be deducted from this resting position as a reference.

What you probably want is to construct a THREE.Triangle then compute its normal, which represents the direction the triangle is facing.

Depending on what kind of rotation value you want from that, you could compute the angle between the normal and the vertical, for example.

4 Likes

Yes, I think you’re after the normal. You could get this from the normals data in the geometry.

However, you didn’t say if the mesh was animated? It might also be possible to read the orientation from one of the bones in the skeleton.

ent.traverse( (submesh) => {
if( ! submesh.isMesh && submesh.name === ‘RootBoneName’) {
submesh.updateWorldMatrix();
console.log( submesh.rotation );
}
});

1 Like

Thanks for your answers, both solution seems plausible.

@donmccurdy, computing the normal seems clear and straightforward, thanks I’ll give it a try.

@becky_rose, I’ve never worked with bones before, and before I start investigating this solution, the generated mesh doesn’t have any bones, if I attache a bone to it, will I get the bone rotation? regrading the fact that the mesh rotation matrix is always (0, 0, 0), thanks.

Thanks to @donmccurdy advice, I ended up making this function :

function computeNormals(vertices) {
    const triangle = new THREE.Triangle(
        new THREE.Vector3(...vertices.slice(0, 3)),
        new THREE.Vector3(...vertices.slice(3, 6)),
        new THREE.Vector3(...vertices.slice(6, 9))
    );

    const normal = new THREE.Vector3();
    triangle.getNormal(normal);

     const matrix = new THREE.Matrix4().lookAt(
        normal,
        new THREE.Vector3(0, 0, 0),
        new THREE.Vector3(0, 1, 0)
    );
    const euler = new THREE.Euler().setFromRotationMatrix(matrix);

    console.log('Computed rotation from the normal : ', euler.toArray());
}

And it partially worked, it return with accuracy the X and Y axes :slight_smile: , but the Z axis is completely off :upside_down_face:, I tried many configuration without success, it’s almost there, but I’m messing something.

Here is the updated fiddle, change the triangle rotation in the inputs the resulting computed rotation will be displayed in the console.

Thanks again.

That sounds like its probably a gimbal lock issue, which can be avoided with Quaternions

const q = new THREE.Quaternion().setFromRotationMatrix(matrix);
console.log( '📐', q );

What cannot be avoided with quaternions is the brain melting goo that comes from trying to understand them, as they’re not a 4D rotational vector; they’re an axis and a scalar value, except when they aren’t. Like I dunno where to even begin… However. If you are just hoping to apply the rotational vector to something else, you could just copy across the quaternion.

If you want to do pitch/yaw/roll maths with it, well that gets a bit more complex I’m sorry to say.

They are just a little abstract like some other things in mathematics.

But at least you can look at them, which is not so easy with all mathematical abstractions.

For this purpose I have created the visualization.
:eye:
Quaternion - Axis, Angle Visualization

1 Like

Finally, made it.

After almost give up, I stumbled upon this great article that explain angles from a matrix point of view, “Matrix Basics. How to step away from storing an orientation as 3 angles”. And The solution was to get the triangle Orthonormal rotation matrix from its vertices.

The Z normal is the triangle normal

const zNormal = new THREE.Vector3();
triangle.getNormal(zAxis);

Get the Y normal from the subVectors of the triangle tip and the midPoint

const yNormal = new THREE.Vector3().subVectors( vec1, midPoint ).normalize();

Get The X normal from subVectors of the triangle base.

const xNormal = new THREE.Vector3().subVectors( vec3, vec2 ).normalize();

Create the orthonormal rotation matrix of the triangle

  const rotationMatrix = new THREE.Matrix4().set(...[
    ...xNormal.toArray(), 0,
    ...yNormal.toArray(), 0,
    ...zNormal.toArray(), 0,
    0, 0, 0, 1
  ]);

Finlay set the Quaternion from the rotation matrix and conjugate it

const q = new THREE.Quaternion()
    .setFromRotationMatrix(rotationMatrix)
    .conjugate(); 

Et voila, the triangle orientation : :slight_smile:

const { x, y, z } = new THREE.Euler().setFromQuaternion(q);

Here is a working fiddle, thanks a lot to all of you, for your patience and pointing me in the right direction.

2 Likes

I am trying to solve the very similar task and tried to use the code you posted, however, for some reason, xNormal is always off for me.

For example, when I use the following code:

  const vec1 = new THREE.Vector3(0.3306500315666199,0.6106522083282471,1.0107344294851828e-8);
  const vec2 = new THREE.Vector3(0.35890841484069824,0.38110581040382385,0.005071461200714111);
  const vec3 = new THREE.Vector3(0.3967372477054596,0.4337632656097412,-0.06387970596551895);
  const triangle = new THREE.Triangle(vec1, vec2, vec3);

  const midPoint = new THREE.Vector3();
  triangle.getMidpoint(midPoint);

  const xNormal = new THREE.Vector3().subVectors( vec3, vec2 ).normalize();  
  const yNormal = new THREE.Vector3().subVectors( vec1, midPoint ).normalize();
  const zNormal = new THREE.Vector3();
  triangle.getNormal(zNormal);

  console.log(THREE.MathUtils.radToDeg(xNormal.angleTo(yNormal)));
  console.log(THREE.MathUtils.radToDeg(xNormal.angleTo(zNormal)));
  console.log(THREE.MathUtils.radToDeg(yNormal.angleTo(zNormal)));

I get the following angles between normal vectors:

69.78913496474597
90
90

xNormal is going sideways all the time and I am not sure how to fix it. The data I used is a result of ML model that recognizes the object in world coordinates. My goal is to get the position of this object, so I can rotate and translate its geometrical siblings to the same position.

1 Like

yes, it was always just two random vectors not at 90 degrees at all. try xnormal = + or - cross product of ynormal and znormal.

1 Like

@makc3d thanks a lot! I guess the main reason was the fact that midPoint is calculated based on Median that doesn’t form 90 degree with the triangle’s base.