Adding ofMatrix4x4 transformations to a mesh and go back to the original position

Hi, I’m trying to build an L-System in 3d with OF. In order to do this, i generate a mesh, I apply a transformation to it, and i draw the mesh. The problem is that after transforming the mesh a couple of times, i want to go back to the original position. How can I achieve this?
I’ve tried ofPushMatrix and ofPopMatrix, but they are not working as expected, once i pop the matrix out, the position is not where I left it before.

That’s the code that decide which transformation to apply to the mesh:

void Transformer::generate(ofVboMesh& mesh, string _instruction, int depth) {
    instructions = _instruction;
    for (int i = 0; i < instructions.length(); i++) {
        char c = instructions[i];
        if (c == 'F') {
            addBranchIntoMesh(branch, mesh, length, "cube");
            transform.translate(0, 0, length);
            branch.setCurrentTransform(transform);
        }else if( c == 'G') {
            transform.translate(0, 0, length);
            branch.setCurrentTransform(transform);
        }else if (c == '+') {
            transform.rotate(+theta, 0, 1, 0);
            branch.setCurrentTransform(transform);
        }
        else if (c == '-') {
            transform.rotate(-theta, 0, 1, 0);
            branch.setCurrentTransform(transform);
        }
        else if (c == '[') {
            branch.setCurrentTransform(branch.getCurrentTransform());
            ofPushMatrix();
        }
        else if (c == ']') {
            ofPopMatrix();
        }
    }
    currentDepth++;
}

void Transformer::addBranchIntoMesh(Branch branch, ofVboMesh& mesh, float length, string geometry){
    if(geometry == "cube") {
        cube.generate(mesh, currentDepth, length, branch.getCurrentTransform());
    }
}

The Branch class simply store the previous transformation and get the current one

#pragma once
#include "ofMain.h"

class Branch {
public:
    Branch(
           const ofMatrix4x4& currentTransform = ofMatrix4x4()
    );
    ofMatrix4x4 currentTransform;
    ofMatrix4x4 getCurrentTransform() const { return currentTransform; }
    void setCurrentTransform(const ofMatrix4x4& transform) { this->currentTransform = transform; }
};

The cube class, take the general mesh (the mesh that will contain all the cubes), and put into it a cube, a the position specified by the transformation.

void Cube::generate(ofMesh& mesh, int depth, unsigned int length, ofMatrix4x4 transformation){
    ofColor color(255, 0, 0);
    float hue = 254.f;
    vertices = createCube(length);
    // for the sake of brevity i will not include the createCube ;)
    for (unsigned i = 0; i < vertices.size(); ++i){
        mesh.addVertex(vertices[i] * transformation);
        mesh.addNormal(normals[i]);
        mesh.addTexCoord(ofVec2f(0.f, depth));
        mesh.addColor(color);
    }
}

I’m also not sure if ofMatrix4x4 fits my needs, that is basically move a turtle back and forth on the screen and drawing a mesh where is it needed.
Any suggestion is more than appreciated!

ofPush/PopMatrix only work for the global transformations done to the model view and projection matrices not to an ofMatrix4x4 or anything else.

you can just make a copy before starting to modify the matrix and then overwrite the original with the copy to bring back the original transformation.

also the easiest way to do what you are trying to do would probably be by having an ofNode instead of a matrix for each branch. ofNodes can be parented to each other so every branch comming out of another is just it’s child and only needs to know it’s local transformation that way you don’t need to manually accumulate the transformations on every branch

Many thanks, I will definitely have a look to ofNode and setParent and try to use it with meshes.

I’ve a problem drawing the ofVboMesh containing the nodes. This is my code:

// .h file
    int branchLenght = 80;
    int branchDimension = 20;
    
    ofCylinderPrimitive root;
    ofCylinderPrimitive branch;
    
    ofLight light;
    ofPlanePrimitive plane;
    ofEasyCam cam;
    
    ofVboMesh finalMesh;	

// .cpp file
void ofApp::setup(){
    ofSetVerticalSync(true);
    ofEnableDepthTest();
    //light
    light.setPosition(500, 500, 500);
    light.enable();
    
    //camera
    cam.setPosition(0, -440, 300);
    cam.lookAt(ofVec3f(0, 0, 20));
    
    //plane
    plane.setPosition(100, 50, 0);
    plane.set(640, 480);
    plane.setResolution(20, 20);
    
    //mesh
    finalMesh.setMode(OF_PRIMITIVE_TRIANGLES);
    finalMesh.enableColors();
    
    // root
    // http://www.slideshare.net/roxlu/openframeworks-007-3d
    root.set(branchDimension, branchLenght);
    root.setPosition(0, 0, 0);
    root.dolly(branchLenght/2);
    root.tilt(90);
    //root.resetTransform();
    finalMesh.append(root.getMesh());

    //branch
    branch.setParent(root);
    branch.set(branchDimension, branchLenght);
    branch.boom(branchLenght);
    branch.roll(45.00);
    finalMesh.append(branch.getMesh());
}

//--------------------------------------------------------------
void ofApp::update(){
    root.pan(1);
}

//--------------------------------------------------------------
void ofApp::draw(){
    cam.begin();
    plane.drawWireframe();
        root.draw();
        branch.draw();
        //finalMesh.draw();
    cam.end();
}

calling root.draw() and branch.draw() in the draw method works as expected, as in this picture:

but if I comment out these 2 methods and i draw using finalMesh.draw(), the transformations are not considered, and the cylinders are rendered in a funny way.

I would like to avoid to draw each element separately, because i would like to add thousands of nodes, is there a way to draw the mesh preserving the transformation of the nodes?

what you are doing when creating the finalMesh, is appending the meshes to one another but not their transformations, in order to do so you would need to multiply each vertex of each mesh by it’s node’s matrix. something like:

for(auto v: root.getMesh().getVertices()){
    finalMesh.addVertex(v * root.getGlobalTransformationMatrix());
}

for(auto i: root.getMesh().getIndices()){
    finalMesh.addIndex(i);
}

another problem is that unless the mesh is created as triangles (by default they are created as triangle strip) they can’t be appended like that since the order of the vertices and indices is more complex then just putting the vertices of one after the other. that’s why you see the glitchy final mesh. you can set the type of the 3d primitives when calling set like:

root.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, true, OF_PRIMITIVE_TRIANGLES);

to start with i think it would be easier to just have separate meshes and then try to join them in one if you have performance issues

Ok, so basically with getGlobalTransformationMatrix() it is possible to obtain the ofMatrix4x4 relative to the world, and multiplying it for the vertices in the primitive mesh of the cylinder would perform the transformation on the vertices, right?

The problem with the rendering is fixed using OF_PRIMITIVE_TRIANGLES, and also the mesh of the root is added correctly to the finalMesh, transformation included. I still can not add the branch in to the finalMesh. Better said, the vertices are added, and the indexes too, but when i call finalMesh.draw only the root is displayed

    // root
    // http://www.slideshare.net/roxlu/openframeworks-007-3d
    root.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, true, OF_PRIMITIVE_TRIANGLES);
    root.setPosition(0, 0, 0);
    root.dolly(branchLenght/2);
    root.tilt(90);
    for(auto v: root.getMesh().getVertices()){
        finalMesh.addVertex(v * root.getGlobalTransformMatrix());
    }
    for(auto i: root.getMesh().getIndices()){
        finalMesh.addIndex(i);
    }
    cout << finalMesh.getNumVertices() << endl;

    //branch
    branch.setParent(root);
    branch.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, true, OF_PRIMITIVE_TRIANGLES);
    branch.boom(branchLenght);
    branch.roll(45.00);
    for(auto v: branch.getMesh().getVertices()){
        finalMesh.addVertex(v * branch.getGlobalTransformMatrix());
    }
    for(auto i: branch.getMesh().getIndices()){
        finalMesh.addIndex(i + finalMesh.getNumVertices());
    }
    cout << finalMesh.getNumVertices() << endl;

P.S.
I’m trying to use a single ofVboMesh because the sketch that i’ve done previously was drawing ~1000 ofCylinder calling ofDrawCylinder for each cylinder and it was lagging a lot. That’s why I’ve thought that generating all the geometries in the setup method, loading them into the GPU using the ofVboMesh would speed up the sketch.

the problem is that the indices you are adding for the second mesh are not correct, the idea is good, you need to add the indices starting from the last vertex number but the way you’ve implemented it is wrong, the number of vertices you are getting is considering that of the current mesh too and so it’s adding indices pointing to vertices that don’t exist yet. the last 2 for loops should look somethign like:

        auto prevNumVertices = finalMesh.getNumVertices();
        for(auto v: branch.getMesh().getVertices()){
            finalMesh.addVertex(v * branch.getGlobalTransformMatrix());
        }
        for(auto i: branch.getMesh().getIndices()){
            finalMesh.addIndex(i + prevNumVertices);
        }

or just invert the order so you add the indices first like:

        for(auto i: branch.getMesh().getIndices()){
            finalMesh.addIndex(i + finalMesh.getNumVertices());
        }
        for(auto v: branch.getMesh().getVertices()){
            finalMesh.addVertex(v * branch.getGlobalTransformMatrix());
        }

and yes getGlobalTransformation gives you the 4x4 amtrix for the recursive transformations of a node combined with all it’s parents

Yes, definitely ofNode is what i was looking for, thank you @arturo for the suggestions . The cool thing is that when i set the parent, the transformations are relative to that parent.
I post here the code in the case someone will need it as reference. I’ve calculate the movement over the x-axis to make the junctions look a bit nicer.

// .h file
    int branchLenght = 80;
    int branchDimension = 20;
    int radiusSegments = 10;
    int heightSegments = 10;
    int capSegments = 10;
    float theta = 45;
    
    ofCylinderPrimitive root;
    ofVboMesh finalMesh;
// .cpp file 

    // root
    // http://www.slideshare.net/roxlu/openframeworks-007-3d
    root.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, false, OF_PRIMITIVE_TRIANGLES);
    root.setPosition(0, 0, 0);
    root.dolly(branchLenght/2);
    root.tilt(90);
    for(auto v: root.getMesh().getVertices()){
        finalMesh.addVertex(v * root.getGlobalTransformMatrix());
    }
    for(auto i: root.getMesh().getIndices()){
        finalMesh.addIndex(i);
    }

    //first branch
    ofCylinderPrimitive branch;
    branch.setParent(root);
    branch.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, false, OF_PRIMITIVE_TRIANGLES);
    branch.boom(branchLenght);
    branch.roll(theta);
    float mov_truck = sin(theta) * branchLenght/2;
    branch.truck(-mov_truck);
    
    for(auto i: branch.getMesh().getIndices()){
        finalMesh.addIndex(i + finalMesh.getNumVertices());
    }
    for(auto v: branch.getMesh().getVertices()){
        finalMesh.addVertex(v * branch.getGlobalTransformMatrix());
    }
    
    //second branch;
    ofCylinderPrimitive branch2;
    branch2.setParent(root);
    branch2.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, false, OF_PRIMITIVE_TRIANGLES);
    branch2.boom(branchLenght);
    branch2.roll(-theta);
    branch2.truck( (sin(theta) * branchLenght/2) );
    for(auto i: branch2.getMesh().getIndices()){
        finalMesh.addIndex(i + finalMesh.getNumVertices());
    }
    for(auto v: branch2.getMesh().getVertices()){
        finalMesh.addVertex(v * branch2.getGlobalTransformMatrix());
    }
    
    //third branch;
    ofCylinderPrimitive branch3;
    branch3.setParent(branch2);
    branch3.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, false, OF_PRIMITIVE_TRIANGLES);
    branch3.boom(branchLenght);
    for(auto i: branch3.getMesh().getIndices()){
        finalMesh.addIndex(i + finalMesh.getNumVertices());
    }
    for(auto v: branch3.getMesh().getVertices()){
        finalMesh.addVertex(v * branch3.getGlobalTransformMatrix());
    }

this is the result.

@arturo, is there any documentation about how to use ofNode? i can not find it in the ofBook or in the tutorials, only the forum contains some posts. Do you think that a draft of a page about how to use it with meshes could be useful? Are there other people already working on it?

I’m trying to understand shaders and I want to apply a simple light to this scene, (as it is in the default OF implementation) using shaders.
In order to do like this, i think that I’ve to multiply the normals with the transformation, as I’ve done with the vertices, am I correct?

    // root
    root.set(branchDimension, branchLenght, radiusSegments, heightSegments, capSegments, false, OF_PRIMITIVE_TRIANGLES);
    root.setPosition(0, 0, 0);
    root.dolly(branchLenght/2);
    root.tilt(90);
    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()){
        finalMesh.addNormal(i.normalize());
    }
    cout << finalMesh.getNumVertices() << endl;

    //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);
    
    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: root.getMesh().getNormals()){
        ofVec3f normalVector = (i * branch.getGlobalTransformMatrix());
        finalMesh.addNormal(normalVector.normalize());
    }

of course I’ve changed the main.cpp file as follow:

int main( ){
    ofGLWindowSettings settings;
    settings.setGLVersion(3, 2);  // Programmable pipeline
    settings.width = 1024;
    settings.height = 768;
    ofCreateWindow(settings);
    ofRunApp(new ofApp());
}

Multiplying a vertex with a ofMatrix4x4 does not seem to work anymore in OF 0.11
I get this error:
Use of overloaded operator '*' is ambiguous (with operand types 'ofMatrix4x4' and 'glm::vec<3, float, glm::packed_highp>')
I am guessing it has to do with the switch to glm::mat ?
How would I apply a transformation to a vertex these days?
Thanks for any advice.

@stephanschulz
Right, the problem there is that now the ofMesh uses glm::vec3 as its default vector class, thus multiplying by an ofMatrix4x4 does not work.
Converting between these two matrices types is straight forwards, as their memory layout is the same

ofMatrix4x4 ofM;
glm::mat4 m = ofM;

but be aware that the multiplication order is reversed as arturo explained here
this means that the following

should be

for(auto v: root.getMesh().getVertices()){
    finalMesh.addVertex(root.getGlobalTransformationMatrix() * v);
}

Hope this helps

1 Like

Thanks for the tip.
I still get a compile error.

/Applications/of_v0.11.0_osx_release/apps/movingHead/3DModelLoaderExample2/src/ofApp.cpp:147:33: Invalid operands to binary expression ('glm::mat4' (aka 'mat<4, 4, float, defaultp>') and 'glm::vec<3, float, glm::packed_highp>')

You can’t multiply a vec3 for a mat4, the vec3 needs to be converted to a vec4.

1 Like

as @edapx points out you can not multiply a vec3 by a mat4, it has to be a vec4.

glm::vec3 v;
glm::mat4 m;

auto v2 = m * glm::vec4(v, 1.0);

There is something important to note about the second argument you pass to the glm::vec4 in the code above. This has to be 1 if the vector represents a point in space (which is the case for vertices of an ofMesh), but it has to be 0 if it represents a direction, like a normal.

1 Like