Search code examples
c++openglglsltransformationglm-math

OpenGL Rotating an object (set of cubes) around itself rather than the origin (0,0,0)


I'm practicing OpenGL and transformations and have run into a problem. I have a four cubes translated together to form the skeleton of a car. I want to be able to drive and turn the car without the car disassembling itself. Basically, I want the trunk and the front of the car to rotate around the body of the car instead of the origin (0,0,0). I've read that to accomplish this, I must follow the following steps:

  1. Start with an identity matrix

  2. Translate the matrix by -centre of the object

  3. Rotate the matrix by the desired amount

  4. Translate the matrix by centre of the object

  5. Use the resulting matrix to transform the object that you desire to rotate

and have implemented it into my code, but it doesn't work as I expected. Please tell me what I'm doing wrong and the correct way to implement this. I have pasted my code below, and trimmed it so only the important parts are here.

result:

float xMove = 0.0f;  //xMove and zMove keeps track of the center of the main body while driving
float zMove = 0.0f;
float carRotate = 0.0f;  //degrees of rotation incremented by pressing E

int main()
{
/*some code to initialize the program...*/

    while (!glfwWindowShouldClose(window))
    {
    /*Draw main car body*/

        mat4 carMatrix = translate(mat4(1.0f), vec3(0.0f + xMove + x, 0.2f, 0.0f + zMove + z))
            * rotate(mat4(1.0f), radians(carRotate), vec3(0.0f, 1.0f, 0.0f))
            * scale(mat4(1.0f), vec3(0.5f, 0.4f, 1.0f))
            * translate(mat4(1.0f), vec3(0.0f, -0.2f,0.0f));
        shaderProgram.setMat4("model", carMatrix);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        //Bonnet
        carMatrix = translate(mat4(1.0f), vec3(0.0f + xMove + x, 0.1f, 0.65f + zMove + z))
            * rotate(mat4(1.0f), radians(carRotate), vec3(0.0f, 1.0f, 0.0f))
            * scale(mat4(1.0f), vec3(0.3f, 0.2f, 0.3f))
            * translate(mat4(1.0f), vec3(xMove, 0.0f, -zMove));
        shaderProgram.setMat4("model", carMatrix);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        //Trunk
        carMatrix = translate(mat4(1.0f), vec3(0.0f + xMove + x, 0.1f, -0.65f + zMove + z))
            * rotate(mat4(1.0f), radians(carRotate), vec3(0.0f, 1.0f, 0.0f))
            * scale(mat4(1.0f), vec3(0.3f, 0.2f, 0.3f))
            * translate(mat4(1.0f), vec3(0.0f, 0.0f, 0.0f));
        shaderProgram.setMat4("model", carMatrix);
        glDrawArrays(GL_TRIANGLES, 0, 36);

        //Roof
        carMatrix = translate(mat4(1.0f), vec3(0.0f + xMove + x, 0.45f, 0.0f + zMove + z))
            * rotate(mat4(1.0f), radians(carRotate), vec3(0.0f, 1.0f, 0.0f))
            * scale(mat4(1.0f), vec3(0.3f, 0.1f, 0.6f))
            * translate(mat4(1.0f), vec3(0.0f, 0.0f, 0.0f));
        shaderProgram.setMat4("model", carMatrix);
        glDrawArrays(GL_TRIANGLES, 0, 36);


    if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS)
    {
        zMove++;
    }
    if (glfwGetKey(window, GLFW_KEY_E) == GLFW_PRESS)
    {
        carRotate++;
    }
}
}

Solution

  • Do not "Translate the matrix by -centre of the object".

    Place then entire car in that way, that its center is at (0, 0, 0). Thant means translate each object to its place relative to (0, 0, 0). Then scale and rotate. Finally move the care to the position in the world.

    If the relative position of a part of the car to (0, 0, 0) is (relX, relY, relZ), then:

    mat4 carMatrix = 
        translate(mat4(1.0f), vec3(xMove + x, 0.0f, zMove + z) * 
        rotate(mat4(1.0f), radians(carRotate), vec3(0.0f, 1.0f, 0.0f));
    
    //Bonnet
    mat4 partMatrix_1 = 
        translate(mat4(1.0f), vec3(relX, relY, relZ)) * 
        scale(mat4(1.0f), vec3(0.5f, 0.4f, 1.0f));
    
    shaderProgram.setMat4("model", carMatrix * partMatrix_1);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    
    //Trunk
    mat4 partMatrix_2 = 
        translate(mat4(1.0f), vec3(relX2, relY2, relZ2)) * 
        scale(mat4(1.0f), vec3(0.3f, 0.2f, 0.3f));
    
    shaderProgram.setMat4("model", carMatrix * partMatrix_2);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    
    // [...]
    

    Note, I recommend to scale each part first. After that put it to its place.

    relX, relY, relZ are constant and individual coordinates, for each component of the car.