Reputation: 1825
I am trying to rotate a mesh by clicking on it (but NOT the traditional arcball algorithm). The idea is that when you click and drag the mouse, the point on the mesh that you clicked will absolutely remain under the mouse (the mesh will rotate about its origin). Unfortunately, the way I do it right now, the point clicked does NOT stay under the mouse when the mouse translates.
In order to cast a ray and see if it intersects the mesh, I calculate the mouse world coordinates in the following way:
//mousePos is normalized -1 .. 1
glm::vec4 mouse_clip = vec4(mousePos.x,mousePos.y,0,1);
glm::vec4 mouse_world = glm::inverse(viewMatrix) * glm::inverse(projectionMatrix) * mouse_clip;
The code that uses this to test whether or not a ray from the mouse intersects the mesh works fine. The problems start when I rotate. Here is a diagram that explains the rotation algorithm. In the following diagram imagine you're looking at the 3d scene from the top:
I store the world coordinates of the first click's intersection with the mesh and then obtain a second point by translating that point when the mouse moves. A vector from the teapot origin to the first click is v1 and to the second point is v2.
By doing a cross product v1 x v2 I obtain the rotation axis. By doing v1 dot v2 I obtain the cosine of the angle between them. I use glm::acos to find the angle (in radians?)
Here is the code that calculates the rotation as soon as the mouse has moved:
//this is a function that receives a vec3 translation.
//Translation is a vector that describes the translation of the mouse
//in world coordinates (as calculated earlier).
//pick up teapot position from the model matrix
//it will serve as origin of the 2 vectors
vec3 origin = vec3(modelMatrix[3]);
//translate the point the mesh was hit previously
//by the same amount the mouse moved (normally no Z translation)
vec3 newPoint = previousPoint + translation;
vec3 v1 = glm::normalize(previousPoint - origin); //unit vector from teapot center to first point
vec3 v2 = glm::normalize(newPoint - origin); //unit vector from teapot center to translated point
vec3 axis = glm::normalize(glm::cross(v1,v2)); //find rotation axis
float angle = glm::acos(glm::dot(v1,v2)); //find rotation angle
modelMatrix = glm::rotate(modelMatrix,angle,axis); //rotate the modelMatrix accordingly
//store point for next mouse movement
previousPoint = newPoint;
However when I move the mouse the rotation that occurs is smaller than the one that should have happened. The clicked point does not stay under the mouse. Here's a video of the problem:
How can I get the clicked point to stay under the mouse?
Upvotes: 1
Views: 629
Reputation: 48176
You should not use the ray cast for the second point (V2) but instead set it the same distance from the center of V1 while still being under the mouse.
Conceptually you make the arcball radius = dist(center, V1)
and raycast V2 onto that. (the ray sphere intersection solution is available).
Besides that I find that you can do glm::normalize(glm::fquat(1+glm::dot(v1,v2), glm::cross(v1,v2)));
to get the rotation without messing with the acos
and axis-angle (which will immediately do a cos
anyway).
Upvotes: 1