billsak

10-28-2016, 04:34 AM

Hello,

I am building an obj viewer from scratch and have decided to implement an orbit camera system, allowing common functionality such as panning (along the camera XY axes), zooming and rotations. Rotations can happen around arbitrary pivot points.

All these are generated based on mouse events. Panning is right click, zooming is mouse wheel and rotations are left click.

The implementation is simple:

Panning is based on the camera's right and up vector, zooming is based on the camera's direction vector, and rotations rotate the camera's up (horizontal mouse direction) and right vectors (vertical mouse direction).

At the end of these events, I am creating the camera's orthonormal system and the view matrix, using glm.

I am posting a simple code example:

// variables used

glm::vec3 eye;

glm::vec3 lookat;

glm::vec3 direction_vector;

glm::vec3 right_vector;

glm::vec3 up_vector;

glm::mat4x4 view_matrix;

// Zoom function (in the direction of the camera)

void zoom(float zoom_factor) {

glm::vec3 zoom = direction_vector * zoom_factor;

eye += zoom;

lookat += zoom;

// create orthonormal camera system and view matrix

create_view_matrix();

}

// Pan function (translate both camera eye and lookat point)

void pan(float pan_factorX, float pan_factorY) {

glm::vec3 panX = right_vector * pan_factorX;

glm::vec3 panY = up_vector * pan_factorY;

eye += panX + panY;

lookat += panX + panY;

// create orthonormal camera system and view matrix

create_view_matrix();

}

// Rotate function

// rotate around a particular center (this does not have to be the lookat point or the origin)

void rotate(glm::vec3 rotation_center, float angle_X_inc, float angle_Y_inc) {

// rotate up and right vector based on the increments retrieved from the mouse events

// rotation 1: based on the mouse horizontal axis

glm::mat4x4 rotation_matrixX = glm::rotate(angle_X_inc, glm::normalize(up_vector));

// rotation 2: based on the mouse vertical axis

glm::mat4x4 rotation_matrixY = glm::rotate(angle_Y_inc, glm::normalize(right_vector));

// translate back to the origin, rotate and translate back to the pivot location

glm::mat4x4 transformation = glm::translate(rotation_center) * rotation_matrixY * rotation_matrixX * glm::translate(-rotation_center);

// apply the transformations

eye = glm::vec3(transformation * glm::vec4(eye, 1));

lookat = glm::vec3(transformation * glm::vec4(lookat, 1));

// create orthonormal camera system and view matrix

create_view_matrix();

}

// create orthonormal camera system

void create_view_matrix() {

// create new direction vector

direction_vector = glm::normalize(lookat - eye);

// new right vector (orthogonal to direction, up)

right_vector = glm::normalize(glm::cross(direction_vector, up_vector));

// new up vector (orthogonal to right, direction)

up_vector = glm::normalize(glm::cross(right_vector, direction_vector));

// generate view matrix

view_matrix = glm::lookAt(eye, lookat, up_vector);

}

The above code seems to work fine, as long as i perform one rotation only (the other one is set to 0).

If i apply both rotations: Pressing left click and rotating the mouse in, for example, a circular clockwise motion, the object appears to essentially roll counter-clockwise.

The expected behaviour would be that a 360 circular motion of the mouse would result in the object returning to its original position (as in an arcball camera).

I am not exactly sure what the problem is as the implementation is rather trivial. I am probably missing something. If you have any ideas, I would be grateful.

I am building an obj viewer from scratch and have decided to implement an orbit camera system, allowing common functionality such as panning (along the camera XY axes), zooming and rotations. Rotations can happen around arbitrary pivot points.

All these are generated based on mouse events. Panning is right click, zooming is mouse wheel and rotations are left click.

The implementation is simple:

Panning is based on the camera's right and up vector, zooming is based on the camera's direction vector, and rotations rotate the camera's up (horizontal mouse direction) and right vectors (vertical mouse direction).

At the end of these events, I am creating the camera's orthonormal system and the view matrix, using glm.

I am posting a simple code example:

// variables used

glm::vec3 eye;

glm::vec3 lookat;

glm::vec3 direction_vector;

glm::vec3 right_vector;

glm::vec3 up_vector;

glm::mat4x4 view_matrix;

// Zoom function (in the direction of the camera)

void zoom(float zoom_factor) {

glm::vec3 zoom = direction_vector * zoom_factor;

eye += zoom;

lookat += zoom;

// create orthonormal camera system and view matrix

create_view_matrix();

}

// Pan function (translate both camera eye and lookat point)

void pan(float pan_factorX, float pan_factorY) {

glm::vec3 panX = right_vector * pan_factorX;

glm::vec3 panY = up_vector * pan_factorY;

eye += panX + panY;

lookat += panX + panY;

// create orthonormal camera system and view matrix

create_view_matrix();

}

// Rotate function

// rotate around a particular center (this does not have to be the lookat point or the origin)

void rotate(glm::vec3 rotation_center, float angle_X_inc, float angle_Y_inc) {

// rotate up and right vector based on the increments retrieved from the mouse events

// rotation 1: based on the mouse horizontal axis

glm::mat4x4 rotation_matrixX = glm::rotate(angle_X_inc, glm::normalize(up_vector));

// rotation 2: based on the mouse vertical axis

glm::mat4x4 rotation_matrixY = glm::rotate(angle_Y_inc, glm::normalize(right_vector));

// translate back to the origin, rotate and translate back to the pivot location

glm::mat4x4 transformation = glm::translate(rotation_center) * rotation_matrixY * rotation_matrixX * glm::translate(-rotation_center);

// apply the transformations

eye = glm::vec3(transformation * glm::vec4(eye, 1));

lookat = glm::vec3(transformation * glm::vec4(lookat, 1));

// create orthonormal camera system and view matrix

create_view_matrix();

}

// create orthonormal camera system

void create_view_matrix() {

// create new direction vector

direction_vector = glm::normalize(lookat - eye);

// new right vector (orthogonal to direction, up)

right_vector = glm::normalize(glm::cross(direction_vector, up_vector));

// new up vector (orthogonal to right, direction)

up_vector = glm::normalize(glm::cross(right_vector, direction_vector));

// generate view matrix

view_matrix = glm::lookAt(eye, lookat, up_vector);

}

The above code seems to work fine, as long as i perform one rotation only (the other one is set to 0).

If i apply both rotations: Pressing left click and rotating the mouse in, for example, a circular clockwise motion, the object appears to essentially roll counter-clockwise.

The expected behaviour would be that a 360 circular motion of the mouse would result in the object returning to its original position (as in an arcball camera).

I am not exactly sure what the problem is as the implementation is rather trivial. I am probably missing something. If you have any ideas, I would be grateful.