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:
Start with an identity matrix
Translate the matrix by -centre of the object
Rotate the matrix by the desired amount
Translate the matrix by centre of the object
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++;
}
}
}
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.