2
votes

I'm trying to export and load a model from Maya into a very simple iOS OpenGL ES setup. For that I wrote a ruby obj parser that basically takes the vertices and normals and computes them into a C header which I simply include. The output of a simply triangulated unit cube is the following structure:

Vertex3D cubeVertices[] = {
   {{-0.500000f, -0.500000f, 0.500000f},{0.000000f, 0.000000f, 1.000000f},{0.5f, 0.5f, 0.5f, 1},{{0,1,2},{0,6,7},{0,7,1},{0,6,4},{0,4,2}},5},
   {{0.500000f, -0.500000f, 0.500000f},{0.000000f, 0.000000f, 1.000000f},{0.5f, 0.5f, 0.5f, 1},{{1,0,2},{1,2,3},{1,0,7},{1,7,3}},4},
   {{-0.500000f, 0.500000f, 0.500000f},{0.000000f, 0.000000f, 1.000000f},{0.5f, 0.5f, 0.5f, 1},{{2,0,1},{2,1,3},{2,3,4},{2,4,0}},4},
   {{0.500000f, 0.500000f, 0.500000f},{0.000000f, 0.000000f, 1.000000f},{0.5f, 0.5f, 0.5f, 1},{{3,2,1},{3,2,4},{3,4,5},{3,1,7},{3,7,5}},5},
   {{-0.500000f, 0.500000f, -0.500000f},{0.000000f, 1.000000f, 0.000000f},{0.5f, 0.5f, 0.5f, 1},{{4,2,3},{4,3,5},{4,5,6},{4,6,0},{4,0,2}},5},
   {{0.500000f, 0.500000f, -0.500000f},{0.000000f, 1.000000f, 0.000000f},{0.5f, 0.5f, 0.5f, 1},{{5,4,3},{5,4,6},{5,6,7},{5,3,7}},4},
   {{-0.500000f, -0.500000f, -0.500000f},{0.000000f, 1.000000f, 0.000000f},{0.5f, 0.5f, 0.5f, 1},{{6,4,5},{6,5,7},{6,7,0},{6,0,4}},4},
   {{0.500000f, -0.500000f, -0.500000f},{0.000000f, 1.000000f, 0.000000f},{0.5f, 0.5f, 0.5f, 1},{{7,6,5},{7,6,0},{7,0,1},{7,1,3},{7,3,5}},5}
};

GLubyte cubeFaceIndices[] = {
   0, 1, 2,
   2, 1, 3,
   2, 3, 4,
   4, 3, 5,
   4, 5, 6,
   6, 5, 7,
   6, 7, 0,
   0, 7, 1,
   1, 7, 3,
   3, 7, 5,
   6, 0, 4,
   4, 0, 2
};

The definition for the Vertex3D is

struct Vertex3D {
   vec3 position;
   vec3 normal;
   vec4 color;
   Index3D connected_faces[100];
   int connectedFaceCount;
};
typedef struct Vertex3D Vertex3D;

Now I need to recalculate my vertex normals because I want to animate the movement of some vertices. For this I simply added all connected vertex indices to every vertex, that's what the connected_faces index array is for.

For the computation I simply calculate all the face normals with the cross-product. For that I only have to use the 3 vertices stored in a Index3D, load the positions, subtract the first vertex so I have the vectors and calculate the cross-product. Then I add up all cross-products that belong to this vertex, and normalize the vector, that's my final vertex normal.

The problem I am running into is, that it happens that 2 faces that would have the same face normal, e.g. a triangulated cube always has 2 triangles that split the quad, have that face normal in the opposite direction. If I add them, to compute later, of course their sum is the Null-Vector. I know that this happens because in some cases the 2 vertices don't have the right order, this means A should be replaced with B, but I have no idea which one I have to flip.

Is there any mathematic way to estimate in which direction the normal goes, and whether I calculate AxB or BxA? I double checked the normals in Maya, they are just perfect there.

EDIT: I ordered the connected faces now, which works just fine. I'm computing the normals with the following:

// every vertex
GLsizei vertexCount = sizeof(cubeVertices) / sizeof(Vertex3D);
for (int i = 0; i < vertexCount; i++) {
    vec3 newNormal = {0.0f, 0.0f, 0.0f};

    // every vertex connected to the current vertex
    GLsizei faceCount = cubeVertices[i].connectedFaceCount;
    for(int j = 0; j < faceCount; j++){

        Index3D index = cubeVertices[i].connected_faces[j];

        vec3 vectorA = cubeVertices[index.a].position;
        vec3 vectorB = cubeVertices[index.b].position;
        vec3 vectorC = cubeVertices[index.c].position;
        vec3 vectorNormal = {0.0f, 0.0f, 0.0f};

        substractVectors(&vectorB, &vectorA);
        substractVectors(&vectorC, &vectorA);

        makeNormal(&vectorB, &vectorC, &vectorNormal);
        addVectors(&newNormal, &vectorNormal);
    }

    // set normal for current vertex
    normalize(&newNormal);
    cubeVertices[i].normal = newNormal;
}

But now I have the problem that, through triangulation I sometimes have 2 normals pointing in the exact same direction, which doesn't lead to my expected { 0.33f, 0.33f, 0.33f } vertex normals. Is that a correct behavior, or is there any other way to calculate that?

Thank you very much for your help!

1
Maybe posting the normal computation code would help to solve your problem.Christian Rau
I added the normal calculation now, still having some little problems. Thanks your for your second advice, to do all the calculating in 2 round-trips, I'm going to try that next.Thomas Fankhauser
Ok, updated my answer. Btw, it's actually three roundtrips, as you need to initialize the vertex normals to zero, of course (but the simplicity is still worth it).Christian Rau

1 Answers

1
votes

It depends on the vertex ordering of the faces. If they are ordered counter-clockwise (when looking from the outside onto the face), then you compute the face normal as AxB, with A = (v1-v0) and B = (v2-v0), if ordered clockwise you have to switch A and B.

But as long as you do this consistently for every face, it should work. Othwerwise the faces in the cube are not oriented consistently, maybe somebody (the exporter?) messed up the orientation when converting quads to triangles or something like this.

And by the way, you can compute them without the connected_faces array, by just traversing the face/index array once, computing the face normal for each face and adding this to the respective three vertices' normals, followed by a single vertex array traversal for normalizing the normals.

EDIT: You can try to render them in OpenGL with culling either the front faces or the back faces (using glCullFace(GL_BACK) and glEnable(GL_CULL_FACE)). If some triangles of the inside (or outside, depending on the culling mode) are culled away and some are not, then the triangles are not oriented consistently and your cube data is broken.

EDIT: From your updated question I assume, that at least you don't have 0-normals anymore. The code also looks fine. The fact, that not every normal has three similar coordinates (btw it's not 0.33 but 1/sqrt(3), but I know what you mean) is natural for a cube built from triangles. In extreme cases you may get vertices connected to 6 or only 3 triangles. In all other cases (and this should happen) you won't get a perfect corner normal.

This is due to the trianglulation of the cube. You don't see a cube anymore (built from 6 faces and 8 vertices connected to exactly 3 faces each), but a triangular mesh with vertices connected to any number between 3 and 6 triangles. Your program logic does not know that some of the triangles belong together and form a quad. That is also a common problem with general meshes. For example doing any subdivision algorithm on a quadrangular mesh (built from quads) results in a different geometry than doing the same subdivision on a triangulation of that mesh (with two triangles for every quad).

If you really need to work on quads, you need some higher level of abstraction to represent the quad layer (maybe another datastructure on top or just a special ordering of the triangles e.g. every consecutive pair of tris forms a quad). Otherwise you can also work completely with quads, as OpenGL can render quads directly (although I'm not sure if non-deprecated GL or at least ES has dropped support for quads).

EDIT: In your case you can just order it, so every two triangles form a quad. Then in the normal computation you always process two triangles, compute the normal of one of them and then only update the vertex normals of the four distinct vertices.