How to orientate the top face of a cylinder to another point

I’ve to build a tubular structure out of branches. Each branch has a starting position, an end position, a starting direction and an end direction. With this 4 elements I want to draw cylinders connected to each other. Unfortunately, I can not use ofNode like I did here How to draw a cylinder starting from 2 ofNode because it turned out to be too slow.

My current problem is to draw a single cylinder, what I would like to have is this:

Where A is startingPos and B is endingPos. B-A is the startingDir and C - B is the the endDir. C is the lookingAt point that the top face should look.

What I’m obtaining now, is this:

As you see, the rotation of the top face happens on an axis that is not what it should be.

To rotate the top face, I use:
the formula:
angle = acos(v1 dot v2)
axis = norm(v1 cross v2)

Therefore:

auto angle = acos(glm::dot(glm::normalize(startDir), glm::normalize(endDir)));
auto axis = glm::normalize(glm::cross(startDir, endDir));
glm::mat4x4 rotMat = glm::axisAngleMatrix(axis, angle);

This is the whole code that I’m using to draw this scene:

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    //light.enable();
    glm::vec3 startPos = glm::vec3(0.0f,0.0f, 0.0f);
    glm::vec3 endPos = glm::vec3(0.0f, 40.0f, 0.0f);
    lookingAt = glm::vec3(40.0f, 75.0f, 0.0f);
    glm::vec3 startDir = glm::normalize(endPos - startPos);
    glm::vec3 endDir = glm::normalize(lookingAt - endPos);

    createTube(startPos, endPos, startDir, endDir, this->mesh);
}

//--------------------------------------------------------------
void ofApp::update(){

}

//--------------------------------------------------------------
void ofApp::draw(){
    ofEnableDepthTest();
    cam.begin();
    ofSetColor(0,0,0,100);
    mesh.drawWireframe();
    auto n = mesh.getNormals();
    auto v = mesh.getVertices();
    float normalLength = 20.;

    ofSetColor(255,0,0,70);
    for(unsigned int i=0; i < n.size() ;i++){
        ofDrawLine(.98*v[i].x,.98*v[i].y,.98*v[i].z,
                   .98*v[i].x+n[i].x*normalLength*.2,.98*v[i].y+n[i].y*normalLength*.2,.98*v[i].z+n[i].z*normalLength*.2);

    }

    ofDrawSphere(lookingAt.x, lookingAt.y, lookingAt.z, 3);

    cam.end();
    ofDisableDepthTest();
}

void ofApp::createTube(glm::vec3 startPos, glm::vec3 endPos, glm::vec3 startDir, glm::vec3 endDir, ofMesh& mesh){
    bool cap = false;
    int resolution = 6;
    int textureRepeat = 1;
    float length = glm::distance(startPos, endPos);
    const int radius = 8;
    const int scaledRadius = 8;//for now, do not scale the branches;

    auto angle = acos(glm::dot(glm::normalize(startDir), glm::normalize(endDir)));
    auto axis = glm::normalize(glm::cross(startDir, endDir));
    glm::mat4x4 rotMat = glm::axisAngleMatrix(axis, angle);

    // Cylinder body
    int first = mesh.getNumVertices();
    for (int i = 0; i <= resolution; i++) {
        // if it is the last face, close it where the first face
        // was started
        if (i == resolution) {
            mesh.addIndex(first+(i*2));
            mesh.addIndex(first);
            mesh.addIndex(first+1);

            mesh.addIndex(first+1);
            mesh.addIndex(first+(i*2)+1);
            mesh.addIndex(first+(i*2));
        } else {
            mesh.addIndex(first+(i*2));
            mesh.addIndex(first+(i*2)+2);
            mesh.addIndex(first+(i*2)+3);

            mesh.addIndex(first+(i*2)+3);
            mesh.addIndex(first+(i*2)+1);
            mesh.addIndex(first+(i*2));
        }
    }

    for (int i = 0; i <= resolution; i++) {
        //calculate x and y component
        float theta = 2.0f * 3.1415926f * float(i) / float(resolution);
        float x = radius * cosf(theta);
        float z = radius * sinf(theta);

        glm::vec3 offset = glm::vec3(x, startPos.y, z);
        glm::vec3 circleBottom = startPos + offset;
        glm::vec3 direction = glm::normalize(circleBottom);

        glm::vec3 circleTop =  glm::vec3(rotMat * glm::vec4((endPos + offset),0.0));
        glm::vec3 directionTop = endDir;

        // bottom
        //botton and top vertices share the same normal
        glm::vec3 normal = glm::normalize(circleBottom-startPos);
        mesh.addVertex(circleBottom);
        mesh.addNormal(normal);

        //top
        mesh.addVertex(circleTop);
        mesh.addNormal(normal);
    }
}

Does anyone have some suggestion?

It was a stupid error in matrix order multiplication:

I leave the code here, maybe it will be useful for someone. The math about the angle between 2 vectors it is well explained here http://www.euclideanspace.com/maths/algebra/vectors/angleBetween/index.htm

    for (int i = 0; i <= resolution; i++) {
        //the circle, this is the element that will be moved
        float theta = 2.0f * 3.1415926f * float(i) / float(resolution);
        float x = radius * cosf(theta);
        float z = radius * sinf(theta);
        glm::vec4 circle = glm::vec4(x, 0.0f, z, 1.0f); // it is a vec4 because
        // it will be used with the matrices, and w is 1.0 because it is a postion

        //let's keep for later the adjustement on the circle bottom,
        // rotation and translation has to be applied here too, but for now
        // I keep it to 0,0,0
        glm::vec3 circleBottom = glm::vec3(circle);

        // first rotate the plane on the axis, than move it up
        glm::mat4 tranMat = glm::translate(endPos);
        glm::vec4 circleTop =  glm::vec4(rotMat * circle);
        glm::vec4 circleTopRotated = tranMat * circleTop;

        glm::vec2 tcoord;
        tcoord.x = ofMap(i, 0.f, resolution, 0.f, xWrapLimit);

        // bottom
        //botton and top vertices share the same normal
        glm::vec3 normal = glm::normalize(circleBottom-startPos);
        tcoord.y = 0;
        mesh.addVertex(circleBottom);
        mesh.addNormal(normal);
        mesh.addTexCoord(tcoord);

        //top
        tcoord.y = textureRepeat;
        mesh.addVertex(glm::vec3(circleTopRotated));
        mesh.addNormal(normal);
        mesh.addTexCoord(tcoord);

I hope this will work when chaining together more branches

1 Like