1

I've seen a lot of similar questions to this on StackOverflow and elsewhere, but none directly addressed my problem. I'm generating n-sided polyhedrons using a convex hull generator in THREE.js. I want to map a square texture onto each face of each polyhedron such that it is not distorted, and gets drawn correctly with perspective (ie. not counteracting perspective or anything like that).

My first attempt, and most of what I've seen elsewhere, looked like this:

function faceUv( v1, v2, v3 ) {
  return [
    new THREE.Vector2(0,0),
    new THREE.Vector2(1,0),
    new THREE.Vector2(1,1)
  ];
}

This changes the aspect ratio of the texture on faces that are not XY aligned right triangles. That is, all of them ;)

So, I thought I would try rotating the face to be aligned with the XY plane, then compute the UVs as if a 1x1 square were drawn over them. So a triangle like this--after XY alignment--would get the following uv coords:

0,0                          1,0
                             * v1(uv=1.0,0.0)


  v3(uv=0.1,0.5)
   *


               * v2(uv=0.5,0.8)

0,1                          1,1

This would be good enough for my purposes.

Here's my current solution:

function faceUv(v1, v2, v3) {
    var z = new THREE.Vector3(0, 0, 1),
        n = new THREE.Triangle(v1, v2, v3).normal(),
        // from http://stackoverflow.com/questions/13199126/find-opengl-rotation-matrix-for-a-plane-given-the-normal-vector-after-the-rotat
        // RotationAxis = cross(N, N')
        // RotationAngle = arccos(dot(N, N') / (|N| * |N'|))
        axis = new n.clone().cross(z),
        angle = Math.acos(n.clone().dot(z) / (n.length() * z.length()));

    var mtx = new THREE.Matrix4().makeRotationAxis(axis, angle);
    var inv = new THREE.Matrix4().getInverse(mtx);
    var v1r = v1.clone().applyMatrix4(inv),
        v2r = v2.clone().applyMatrix4(inv),
        v3r = v3.clone().applyMatrix4(inv),
        ul = v1r.clone().min(v2r).min(v3r),
        lr = v1r.clone().max(v2r).max(v3r),
        scale = new THREE.Vector2(1.0 / (lr.x - ul.x), 1.0 / (lr.y - ul.y));

    v1r.sub(ul);
    v2r.sub(ul);
    v3r.sub(ul);

    return [
        new THREE.Vector2(v1r.x * scale.x, v1r.y * scale.y),
        new THREE.Vector2(v2r.x * scale.x, v2r.y * scale.y),
        new THREE.Vector2(v3r.x * scale.x, v3r.y * scale.y)
    ];
}

It seems to work pretty well, with the exception of faces that are aligned with the YZ plane (ie. with a normal of 0,-1,0). Here's a screenshot, notice the bottoms of the polyhedrons.

screenshot

So my questions are: a) is there a better/simpler way to do this? b) how can I fix it for the bottom faces?, c) is there a way to at least simplify the code by using more existing functionality from THREE.js?

Thanks!

Ian
  • 2,078
  • 1
  • 17
  • 27
  • pssssh! Weird that always setting the axis to `(0,0,0)` only broke it on certain faces. Thanks for the help. If you make an answer, I can accept it as such and give you points or something. – Ian Nov 07 '13 at 01:20

1 Answers1

1

Do you mean this?

axis = n.clone().cross( z );

Also, UVs are normally ( 0, 0 ) in the lower left.

Regarding questions (a) and (c): Given what you are doing, 'No'.

Regarding question (b): I expect you can track that down yourself. :-)

WestLangley
  • 102,557
  • 10
  • 276
  • 276