I've recently implemented the arcball guide here: https://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Arcball in a project of mine and I'm having some problems with scaling and translation of objects.
Problem: When scaling and translation is involved, rotation gives incorrect output, such as bending inwards or enlarging parts of the object at certain angles. However when I apply the scaling and translation directly to the vertices of my object, the rotation works. That is, in the working state, my model matrix for this object is an identity matrix. Anything else, and this fails. So I know for a fact it's the order these get applied. I'm just unsure what order. Applying translation and scaling along with rotation in TRS order also didn't help and produced incorrect result.
EDIT: Fixed translation and scaling problems, but the object doesn't rotate with respect to the changed center. It rotates with respect to the old center.
Relevant code:
Part from the object render code:
if (R.mouseMoved) {
glm::vec3 va = get_arcball_vector(R.click_mx, R.click_my);
glm::vec3 vb = get_arcball_vector(R.cur_mx, R.cur_my);
float ang = glm::degrees(acos(min(1.0f, glm::dot(va, vb)))) * MMOVE_FACTOR * R.deltaTime;
glm::vec3 axis_in_camera_coord = glm::cross(va, vb);
glm::mat3 camera2object = glm::inverse(glm::mat3(R.view) * glm::mat3(model));
glm::vec3 rotaxis = camera2object * axis_in_camera_coord;
model = glm::rotate(model, ang, rotaxis);
}
// assuming this is what I needed to do here for view matrix, TRT, this doesn't work
glm::mat4 mv = /*glm::translate(glm::mat4(1.0f), glm::vec3(0, 0, -WINDOW_X * .5)) * */R.view * model/* * glm::translate(glm::mat4(1.0f), -center)*/;
glm::mat4 pvm = Renderer::proj * mv;
pvm = glm::translate(pvm, translation);
pvm = glm::scale(pvm, scale);
The pvm matrix is sent to shader right after and the object is rendered.
The vector calculation function:
glm::vec3 get_arcball_vector(int x, int y) {
glm::vec3 P = glm::vec3(1.0*x / WINDOW_X * 2 - 1.0,
1.0*y / WINDOW_Y * 2 - 1.0,
0);
P.y = -P.y;
float OP_squared = P.x * P.x + P.y * P.y;
if (OP_squared <= 1 * 1)
P.z = sqrt(1 * 1 - OP_squared); // Pythagoras
else
P = glm::normalize(P); // nearest point
return P;
}
MMOVE_FACTOR is 0.05 and R.deltaTime is to regulate render time and mouse movement to make it feel smoother. The object in question can have arbitrary scaling and translation to make it appear on different places of the scene. No matter what I tried it doesn't produce the result I wanted. For example, I've tried the following:
model = glm::translate(model, -center);
model = glm::rotate(model, glm::degrees(ang), rotaxis);
model = glm::translate(model, center);
But this also doesn't provide good results, and the rotation still is incorrect.
EDIT 2: As per httpdigest's suggestion, here's the most recent code:
glm::mat4 tr = glm::translate(glm::mat4(1.0f), -R.camera.getPos());
glm::mat4 trc = glm::translate(glm::mat4(1.0f), -center);
glm::mat4 tmc = glm::translate(glm::mat4(1.0f), center);
glm::mat4 rx = glm::rotate(glm::mat4(1.0f), -(float)R.camera.getPitch(), glm::vec3(1.0, 0, 0));
glm::mat4 ry = glm::rotate(glm::mat4(1.0f), (float)R.camera.getYaw(), glm::vec3(0, 1.0, 0));
glm::mat4 pvm = Renderer::proj * tr * rx * ry * trc * tmc;
pvm = glm::translate(pvm, translation);
pvm = glm::scale(pvm, scale);
Center is a glm::vec3 with the sum of vertices divided by vertex count.
Problem with the above: It doesn't work right with translations around X-axis. When I rotate it enough that I think it sits on the same distance as the translation value, it rotates properly on Y-axis. Another problem is that this doesn't behave like an arcball but I guess that's expected from how the rotations are handled. I could be wrong though.