Hi, I'm attempting to implement a first person, mouse-controlled camera for variable orientation situations. This means basically I need a regular mouse look camera to behave normally with any "up" vector. This will be used for moving around the entire surface of a spherical planet. So as you walk along, the view direction stays the same relative to the ground, so the view doesn't stay fixed on one point as you change gravity vectors. Pretty basic right?
Well I've got the necessary code down, and it works perfectly in most situations. However, there is a big problem with it. Near the south pole (basically near gravity=(0, -1, 0)), the direction vector seems to be transformed toward the south pole as the gravity/orientation changes. So if you're standing still, a few units away from the south pole, the direction is fine. Try and move in any direction, and the view direction is shifted toward the south pole. It's extremely odd.
It seems to me that the transformation matrix for transforming the world-axis-based mouse movements is somehow affecting the direction vector to shift it to point toward the south pole. Basically, I think the transformation I'm using (a rotation from the original up vector(0, 1, 0) and the current orientation) is forcing the direction to be "in line" with that, hence pointing it toward the south pole. But the transformation seems to work fine for most other orientations.
I would upload a video, but the recording really doesn't capture the problem, because it looks like the mouse is just being moved toward the south pole. Also, only the horizontal component gets messed up, the vertical component stays level.
Anyway, here's the code. I would really appreciate some guidance from someone who's got a properly working system.
Here's the implementation with just using sines and cosines to calculate direction vector from mouse angles.
glm::mat4 trans;
float factor = 1.0f;
m_horizontal += horizontal;
m_vertical += vertical;
while (m_horizontal > TWO_PI) {
m_horizontal -= TWO_PI;
}
while (m_horizontal < -TWO_PI) {
m_horizontal += TWO_PI;
}
if (m_vertical > MAX_VERTICAL) {
vertical = MAX_VERTICAL - (m_vertical - vertical);
m_vertical = MAX_VERTICAL;
}
else if (m_vertical < -MAX_VERTICAL) {
vertical = -MAX_VERTICAL - (m_vertical - vertical);
m_vertical = -MAX_VERTICAL;
glm::vec3 tmp = m_orientation;
tmp.y = fabs(tmp.y);
/* this check is to prevent glm abort on cross product of parallel vectors */
/* factor=-1.0f only extremely close to south pole. Problem occurs much outside of that region */
if (glm_sq_distance(tmp, glm::vec3(0.0f, 1.0f, 0.0f)) > 0.001f) {
glm::vec3 rot = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), m_orientation));
float angle = (acosf(m_orientation.y) * 180.0f) * PI_RECIPROCAL;
glm::quat t = glm::angleAxis(angle, rot);
trans = glm::mat4_cast(t);
}
else if (m_orientation.y < 0.0f) {
factor = -1.0f;
}
tmp = glm::vec3(cos(m_vertical) * sin(m_horizontal),
sin(m_vertical),
cos(m_vertical) * cos(m_horizontal)) * factor;
m_up = m_orientation;
m_direction = glm::vec3(trans * glm::vec4(tmp.x, tmp.y, tmp.z, 0.0f));
m_view = glm::lookAt(m_position, m_position + m_direction, m_up);
m_vp = m_perspective * m_view;
I also have a quaternion implementation, but it's a little more prone to glm aborts (anyone have an elegant solution for those, btw?). Both the quaternion and regular angle one behave identically.
glm::mat4 trans;
float factor = 1.0f;
m_horizontal += horizontal;
m_vertical += vertical;
while (m_horizontal > TWO_PI) {
m_horizontal -= TWO_PI;
}
while (m_horizontal < -TWO_PI) {
m_horizontal += TWO_PI;
}
if (m_vertical > MAX_VERTICAL) {
vertical = MAX_VERTICAL - (m_vertical - vertical);
m_vertical = MAX_VERTICAL;
}
else if (m_vertical < -MAX_VERTICAL) {
vertical = -MAX_VERTICAL - (m_vertical - vertical);
m_vertical = -MAX_VERTICAL;
}
glm::quat t, quat;
glm::vec3 tmp = m_orientation;
tmp.y = fabs(tmp.y);
if (glm_sq_distance(tmp, glm::vec3(0.0f, 1.0f, 0.0f)) > 0.001f) {
glm::vec3 axis = glm::normalize(glm::cross(glm::vec3(0.0f, 1.0f, 0.0f), m_orientation));
float angle = (acosf(m_orientation.y) * 180.0f) * PI_RECIPROCAL;
t = glm::angleAxis(angle, axis);
}
else if (m_orientation.y < 0.0f) {
factor = -1.0f;
}
glm::quat rot = glm::angleAxis(m_horizontal * ONEEIGHTY_PI, glm::vec3(0.0f, 1.0f, 0.0f));
quat = rot * quat;
rot = glm::angleAxis(m_vertical * -ONEEIGHTY_PI, glm::vec3(1.0f, 0.0f, 0.0f));
quat = quat * rot;
t = t * quat;
trans = glm::mat4_cast(t);
m_direction = glm::vec3(trans[2]);
Thanks in advance for the help.