Recalculate normals after transformation SOLVED

Hello, I’m applying 2 transformation on a ofCylinderPrimitive, getting its vertices and adding them into a mesh. I’ve some trouble recalculating the normals (i need them later in the shaders because i want to calculate the light that the material should reflects).
This is my actual code:

// .h file
    ofCylinderPrimitive root;
    ofLight light;
    ofVboMesh finalMesh;
    ofShader  shader;

// .cpp
void ofApp::setup(){
    ofSetVerticalSync(true);
    ofEnableDepthTest();
    //light
    light.setPosition(500, 500, 500);
    light.enable();
    
    //mesh
    finalMesh.setMode(OF_PRIMITIVE_TRIANGLES);
    finalMesh.enableColors();
    
    //shader, not used at the moment
    //shader.load("shaders_gl3/render.vert", "shaders_gl3/render.frag");
    
    // root
    root.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, false, OF_PRIMITIVE_TRIANGLES);
    root.setPosition(0, 0, 0);
    root.dolly(branchLenght/2);
    root.tilt(90);
    ofMatrix4x4 normalMatrix = root.getGlobalTransformMatrix().getInverse();
    for(auto v: root.getMesh().getVertices()){
        finalMesh.addVertex(v * root.getGlobalTransformMatrix());
    }
    for(auto i: root.getMesh().getIndices()){
        finalMesh.addIndex(i);
    }
    for(auto i: root.getMesh().getNormals()){
        ofVec3f normalVector = (i * normalMatrix).normalize();
        finalMesh.addNormal(normalVector);
    }
}

//--------------------------------------------------------------
void ofApp::draw(){
    cam.begin();
            drawNormals(10.00);
    cam.end();
}

void ofApp::drawNormals(float size){
    for (int i = 0; i < finalMesh.getNumVertices(); ++i){
        ofVec3f vertex = finalMesh.getVertex(i);
        ofVec3f normal = finalMesh.getNormal(i);
        ofPushStyle();
        ofSetColor(255, 0, 255);
        ofDrawLine(vertex, vertex + size * normal);
        ofPopStyle();
    }
}

As you see in this screenshot, the normals of this vertical cylinder are wrong, they are always parallel to the y axis

P.S. This post is the following of another one, but the title of the previous one has nothing to do with what is now my current issue.

my bad https://en.wikibooks.org/wiki/GLSL_Programming/Applying_Matrix_Transformations#Transforming_Normal_Vectors_with_an_Orthogonal_Matrix

vector * matrix != matrix * vector
Inverting the position from (i* normalMatrix) to normalMatrix * i fixed the problem

    for(auto i: root.getMesh().getNormals()){
        cout << i.length() << endl;
        ofVec3f normalVector = (normalMatrix * i);
        finalMesh.addNormal(normalVector);
    }

Well, I still have some problems. When adding another node, setting the previous as parent an performing some transformations, I’ve strange value in the normals of the second node(e.g. their length it is not 1, as showed in the image). I’m calculating them in the same way as in the previous mesh, the root, where it works.

    // root
    root.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, false, OF_PRIMITIVE_TRIANGLES);
    root.setPosition(0, 0, 0);
    root.dolly(branchLenght/2);
    root.tilt(90);
    ofMatrix4x4 normalMatrix = root.getGlobalTransformMatrix().getInverse();
    for(auto i: root.getMesh().getIndices()){
        finalMesh.addIndex(i);
    }
    for(auto v: root.getMesh().getVertices()){
        finalMesh.addVertex(v * root.getGlobalTransformMatrix());
    }
    for(auto i: root.getMesh().getNormals()){
        ofVec3f normalVector = (normalMatrix * i);
        // this always print 1
        //cout << normalVector.length() << endl;
        finalMesh.addNormal(normalVector);
    }

    //first branch
    ofCylinderPrimitive branch;
    branch.setParent(root);
    branch.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, false, OF_PRIMITIVE_TRIANGLES);
    branch.boom(branchLenght);
    branch.roll(theta);
    //compensation x-axis
    float mov_truck = sin(theta) * branchLenght/2;
    branch.truck(-mov_truck);
    ofMatrix4x4 branchNormalMatrix = branch.getGlobalTransformMatrix().getInverse();
    for(auto i: branch.getMesh().getIndices()){
        finalMesh.addIndex(i + finalMesh.getNumVertices());
    }
    for(auto v: branch.getMesh().getVertices()){
        finalMesh.addVertex(v * branch.getGlobalTransformMatrix());
    }
    for(auto i: branch.getMesh().getNormals()){
        ofVec3f normalVector = (branchNormalMatrix * i);
        //the length of these normal should be 1, but it is not
        //cout << normalVector.length() << endl;
        finalMesh.addNormal(normalVector);
    }
    cout << finalMesh.getNumNormals() << endl;
    cout << finalMesh.getNumVertices() << endl;

The method that I use to draw the normals is invariated. What am I doing wrong?

It’s more a monologue than a thread, but maybe this post is useful to someone :wink:

What I’ve observed now is that the order in which my transformation are applied to the solid influence the length of the normals. I’m aware of the fact that the multiplication between 2 matrices is not commutative (AB != BA), but i think the length of the normals inside a mesh should not be scaled.

That’s the code

root.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, false, OF_PRIMITIVE_TRIANGLES);
root.setPosition(0, 0, 0);
    
// if the roll transformation is applied first and the boom after,
// the transformed normal have length 1, as a normal should have.
// If the boom transformation is performed before the roll, my normals are
// scaled
bool boom_first = true;
if (boom_first) {
    root.boom(branchLenght);
    root.roll(theta);
}else{
    root.roll(theta);
    root.boom(branchLenght);
}

ofMatrix4x4 normalMatrix = root.getGlobalTransformMatrix().getInverse();
ofApp::debugMatrix(normalMatrix);
for(auto i: root.getMesh().getIndices()){
    finalMesh.addIndex(i);
}
for(auto v: root.getMesh().getVertices()){
    finalMesh.addVertex(v * root.getGlobalTransformMatrix());
}
for(auto i: root.getMesh().getNormals()){
    ofVec3f normalVector = (normalMatrix * i);
    // the length is one only when boom_first is false
    cout << normalVector.length() << endl;
    finalMesh.addNormal(normalVector);
}

the whole code is available here https://github.com/edap/normals-test

The first solution that I’ve tried was simply to normalize the vector before to insert it in the the finalMesh, e.g. finalMesh.addNormal(normalVector.normalize), but not only it sounds strange, the position of the normal was still wrong.

How can i solve this problem? is there a way to preserve the normal of a of3DPrimitive after I’ve performed more transformation on it?

Ok, I’ve found the problem, you can’t do first a rotation and then a translation, the order in which matrices transformation work is: first scale, then rotation, then translation.

this is because of how ofNode works, it makes sense to do things in that order cause then the rotations are relative to the 0,0 of the object and scale doesn’t affect the translation.

in most cases that’s enough, but if you need to do things in a different order like doing a translation before the rotation then you can set another node as parent to you object and do the translation in that one

“but if you need to do things in a different order like doing a translation before the rotation then you can set another node as parent to you object and do the translation in that one”

That would mean, to create an ofNode for every transformation, and set as parent the node containing the previous transformation, avoiding to cumulate transformations, correct?

like this (pseudocode):

root.boom(200);
branch ofNode;
branch.setParent(root);
branch.rotate(15.00);

mmh, not sure waht you mean but the idea is that if you want to have a translate before the rotate for example, in your object you’ll have another more ofNode so:

class Branch: public ofNode{
    ofNode translation;

public:
    Branch(){
        ofNode::setParent(translation); // this calls the ofNode version of setParent 
                                        // avoiding a recursive call when calling our own version
    }

    void preTranslate(ofVec3f t){
        translation.move(t);
    }

    void setParent(ofNode & node){
         translation.setParent(node);
    }
}

so now if you do:

Branch branch;
Root root;

branch.setParent(root);

effectively you have a chain of nodes like root -> translation -> branch

if you want to set the same translation to several nodes then probably you want to have a different object a joint or a null (like it’s called in some 3d programs like cinema 4d i think) and then set all your branches as children of that special node that applies the pre translation:

branch1.setParent(joint);
branch2.setParent(joint);
joint.translate({0.f,10.f,0.f});
joint.setParent(root);

here joint can just be an ofNode since you don’t need to draw it

I’ve edited my code using the Branch class that you’ve posted, that creates an ofNode when a Branch object is initialized and uses that node to perform translation on it and act as joint node between the root and the branch.
That is like to something like:

ofNode root; //root
root.tilt(90);
ofNode joint; //joint, where to perform translations
joint.setParent(root);
join.truck(100);
ofNode branch; //branch, where to perform rotations
branch.setParent(join);
branch.tilt(90);

But the normals are still calculated wrong, as this picture show (the horizontal cylinder are normal with length ~ 0.2)

I think it is because branch.getGlobalTransformMatrix().getInverse() is not taking into account all the transformation, but just that one performed on the branch, and not that one in the joint node.
This is my code(the only thing that changes is that I’m using branch.preTranslate)

root.tilt(90);
root.boom(branchLenght/2);

ofMatrix4x4 normalMatrix = root.getGlobalTransformMatrix().getInverse();
ofApp::debugMatrix(normalMatrix);
for(auto i: root.getMesh().getIndices()){
    finalMesh.addIndex(i);
}
for(auto v: root.getMesh().getVertices()){
    finalMesh.addVertex(v * root.getGlobalTransformMatrix());
}
for(auto i: root.getMesh().getNormals()){
    ofVec3f normalVector = (normalMatrix * i);
    cout << normalVector.length() << endl;
    finalMesh.addNormal(normalVector);
}

//branch, first a translation and then a rotation
branch.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, false, OF_PRIMITIVE_TRIANGLES);
branch.setParent(root);
branch.preTranslate(ofVec3f(50, 50, 50));
branch.roll(90.00);

ofMatrix4x4 branchNormalMatrix = branch.getGlobalTransformMatrix().getInverse();
ofApp::debugMatrix(branchNormalMatrix);
for(auto i: branch.getMesh().getIndices()){
    finalMesh.addIndex(i);
}
for(auto v: branch.getMesh().getVertices()){
    finalMesh.addVertex(v * branch.getGlobalTransformMatrix());
}
for(auto i: branch.getMesh().getNormals()){
    ofVec3f normalVector = (branchNormalMatrix * i);
    cout << normalVector.length() << endl;
    finalMesh.addNormal(normalVector);
}

The whole code is available here https://github.com/edap/normals-test