LWJGL 3 - Model rotating around wrong axis - opengl

My 3D models rotates around wrong axis. Everything except X-axis works. When I set angle of Y and Z to 0 then X works fine but if rotate it eg. 90 degrees around Z-axis and rotate around X after that it rotates around Z too. So when Y or Z is used X rotates around Z. So if I make my model rotate 90 degrees around Z and -90 degrees around X it stays completely still.
My function for transformation matrix. If I move matrix.translate(position); to under rotations my model rotates in huge circle (maybe around worlds origin).
public static Matrix4f createTransformationMatrix(Vector3f position, Vector3f rotation, Vector3f scale) {
Matrix4f matrix = new Matrix4f();
matrix.scale(scale);
matrix.translate(position);
matrix.rotate((float) Math.toRadians(rotation.x), new Vector3f(1, 0, 0));
matrix.rotate((float) Math.toRadians(rotation.y), new Vector3f(0, 1, 0));
matrix.rotate((float) Math.toRadians(rotation.z), new Vector3f(0, 0, 1));
return matrix;
}
Conversion of matrix to floatbuffer so I can pass it to shaders:
public static FloatBuffer toFloatBuffer(Matrix4f matrix) {
FloatBuffer buffer = BufferUtils.createFloatBuffer(16);
buffer.put(matrix.m00());
buffer.put(matrix.m01());
buffer.put(matrix.m02());
buffer.put(matrix.m03());
buffer.put(matrix.m10());
buffer.put(matrix.m11());
buffer.put(matrix.m12());
buffer.put(matrix.m13());
buffer.put(matrix.m20());
buffer.put(matrix.m21());
buffer.put(matrix.m22());
buffer.put(matrix.m23());
buffer.put(matrix.m30());
buffer.put(matrix.m31());
buffer.put(matrix.m32());
buffer.put(matrix.m33());
buffer.flip();
return buffer;
}
Usage in shader:
gl_Position = transformationMatrix * vec4(position, 1.0) * viewMatrix * projectionMatrix;
The models' zero point has been set to its origin and I am sure that all data send to those functions are correct. I have been debugging this for while now. I am using LWJGL 3.

I think you are facing the Gimbal Lock.
That means, it matters in which order the rotations are applied, and then you may get strange results such as the results you have experienced.
To solve this problem, you will have to use Quaternions.
Basically, you will have to :
Create Rotation Quaternions and multiply them with other Rotation Quaternions.
You have to create a Rotation Quaternion that represents X-Axis-Rotation, and so on for Y and Z-Axis
You multiply them
Finally, you'll have to convert the resulting Quaternion into a matrix.
Read here about how to implement Rotation Quaternions in OpenGL ?
Edit :
Read here about how to convert a Quaternion to a Matrix : Convert Quaternion rotation to rotation matrix?
Here's my Camera class(unfortunately, it uses JOGL libraries like Quaternion, but should be portable to LWJGL. It provides scaling, moving, rotation around center in Euler or Quaternion way, as well as methods to retrieve pointing direction etc.
public class Camera {
public Matrix4 matrix;
public Quaternion rotation;
public Vector3 position;
public Vector3 scale;
public Vector3 center;
public Vector2 frictionx;
public Vector2 frictiony;
public Vector2 frictionz;
public Camera() {
matrix=new Matrix4();
rotation=new Quaternion();
position=new Vector3(0f,0f,0f);
scale=new Vector3(1f,1f,1f);
center=new Vector3(0f,0f,0f);
frictionx=new Vector2(0,0);
frictiony=new Vector2(0,0);
frictionz=new Vector2(0,0);
}
public float tryFric(float a, float b) {
try {
float r=a/b;
if (r == Float.NaN) {
return 0;
}
return r;
}
catch (Exception e) {
return 0;
}
}
public void getFrictions(Vector3 pointing) {
frictionx.x=tryFric(pointing.y,pointing.x);
frictionx.y=tryFric(pointing.z,pointing.x);
frictiony.x=tryFric(pointing.x,pointing.y);
frictiony.y=tryFric(pointing.z,pointing.y);
frictionz.x=tryFric(pointing.x,pointing.z);
frictionz.y=tryFric(pointing.y,pointing.z);
}
public Vector3 getPointing(Vector3 vec) {
float[] in=new float[] {vec.x,vec.y,vec.z};
float[] out=new float[3];
rotation.conjugate();
rotation.rotateVector(out, 0, in, 0);
rotation.conjugate();
Vector3 p=new Vector3(out[0],out[1],out[2]);
getFrictions(p);
return p;
}
public void move(float x, float y, float z) {
position.x+=x;
position.y+=y;
position.z+=z;
}
public void setPosition(float x, float y, float z) {
position.x=x;
position.y=y;
position.z=z;
}
public void moveCenter(float x, float y, float z) {
center.x+=x;
center.y+=y;
center.z+=z;
}
public void setCenter(float x, float y, float z) {
center.x=x;
center.y=y;
center.z=z;
}
public void scale(float x, float y, float z) {
scale.x*=x;
scale.y*=y;
scale.z*=z;
}
public void setScale(float x, float y, float z) {
scale.x=x;
scale.y=y;
scale.z=z;
}
public void rotateQuaternion(float angle, float x, float y, float z) {
Quaternion rotationy=new Quaternion();
rotationy.rotateByAngleY(angle*y);
rotation.mult(rotationy);
Quaternion rotationx=new Quaternion();
rotationx.rotateByAngleX(angle*x);
rotation.mult(rotationx);
Quaternion rotationz=new Quaternion();
rotationz.rotateByAngleZ(angle*z);
rotation.mult(rotationz);
rotation.normalize();
}
public void rotateEuler(float angle, float x, float y, float z) {
rotation.rotateByEuler(angle*x, angle*y, angle*z);
}
public Vector3 getPointing() {
Matrix4 as_matrix=new Matrix4();
as_matrix.loadIdentity();
as_matrix.rotate(rotation);
float[] out=new float[3];
float[] in=new float[] {0,0,-1};
as_matrix.multVec(in, out);
Vector3 pointing=new Vector3(out[0],out[1],out[2]);
return pointing;
}
public Matrix4 getMatrix() {
Matrix4 rot_as_mat=new Matrix4();
rot_as_mat.loadIdentity();
rot_as_mat.translate(center.x, center.y, center.z);
rot_as_mat.rotate(rotation);
Matrix4 result=new Matrix4();
result.loadIdentity();
result.scale(scale.x, scale.y, scale.z);
result.multMatrix(rot_as_mat);
result.translate(position.x,position.y,position.z);
matrix=result;
return result;
}
}
Hope it helps !
Note :
You may have to experiment with the rotation quaternion multiplication order to achieve different results

Related

Quaternion-based camera unwanted roll

I created a camera based on quaternions, but when I turn the camera, an unwanted roll appears. I would not like to lose my freedom of movement using, for example, Euler angles, since there is a need to add roll from time to time. If I use Euler angles, then, as far as I know, I can get a gimbal lock.
Code:
struct FreeCamera : public BaseCamera {
float pitch = 0, yaw = 0, roll = 0;
void updateView();
private:
glm::quat qCamera;
};
struct FreeCameraController: public BaseCameraController {
float sensitivityPitch = 0.0025f, sensitivityYaw = 0.0025f, sensitivityRoll = 0.0025f;
void mouseMove(const float x, const float y, const float z = 0);
inline void setMousePos(const float x, const float y, const float z = 0) {
lastMousePos = glm::vec3(x, y, z);
}
private:
glm::vec3 lastMousePos = glm::vec3(0.0f);
};
void FreeCamera::updateView() {
// temporary frame quaternion from pitch, yaw, roll
glm::quat qPYR = glm::quat(glm::vec3(pitch, yaw, roll));
// reset values
pitch = yaw = roll = 0;
// update qCamera
qCamera = qPYR * qCamera;
qCamera = glm::normalize(qCamera);
glm::mat4 rotate = glm::mat4_cast(qCamera);
glm::mat4 translate = glm::mat4(1.0f);
translate = glm::translate(translate, -pos);
view = rotate * translate;
}
void FreeCameraController::mouseMove(const float x, const float y, const float z) {
glm::vec3 dCoord = glm::vec3(x, y, z) - lastMousePos;
((FreeCamera*)camera)->yaw = dCoord.x * sensitivityYaw;
((FreeCamera*)camera)->pitch = dCoord.y * sensitivityPitch;
((FreeCamera*)camera)->roll = dCoord.z * sensitivityRoll;
lastMousePos = glm::vec3(x, y, z);
}
Is it possible to reset unwanted roll, "stabilize" the camera?
As you want to block roll (and if its a car, possibly yaw too since you will make the car fly), you must block one of the axis by concatenating the rotations. What you want to achieve is the actual Gimbal's lock (you use a single quaternion containing all the rotations when flying specifically to get rid of it). So, assuming you can detect wether the vehicle is on the ground or not:
glm::mat4 rotationMatrix;
// When you want to get rid of any axis rotation, you must lock it
if(onGround)
{
glm::quat yawQ = glm::quat(glm::vec3(0.0f, yaw, 0.0f));
yawQ = glm::normalize(yawQ);
glm::mat4 yawMat = glm::mat4_cast(yawQ);
glm::quat pitch = glm::quat(glm::vec3(pitch, 0.0f, 0.0f));
pitch = glm::normalize(pitch);
glm::mat4 pitchMat = glm::mat4_cast(pitch);
rotationMatrix = pitchMat * yawMat;
}
else
{
//Your computation
rotationMatrix = glm::mat4_cast(yourQuaternion);
}
viewMatrix = rotationMatrix * translationMatrix;
Note that it is not necessary to use quaternions to achieve the gound control effect

Raycasting (Mouse Picking) while using an Perspective VS Orthographic Projection in OpenGL

I am struggling to understand how to change my algorithm to handle raycasting (utilized for MousePicking) using a Perspective projection and an Orthographic projection.
Currently I have a scene with 3D objects that have AxisAligned bounding boxes attached to them.
While rendering the scene using a perspective projection (created with glm::perspective) I can successfully use raycasting and my mouse to "pick" different objects in my scene. Here is a demonstration.
If I render the same scene, but using an Orthographic projection, and positioning the camera above the facing down (looking down the Y axis, Imagine like a level editor fora game) I am unable to correctly raycasting from the where the user clicks on the screen so I can get MousePicking working while rendering using an Orthographic projection. Here is a demonstration of it not working.
My algorithm at a high level:
auto const coords = mouse.coords();
glm::vec2 const mouse_pos{coords.x, coords.y};
glm::vec3 ray_dir, ray_start;
if (perspective) { // This "works"
auto const ar = aspect_rate;
auto const fov = field_of_view;
glm::mat4 const proj_matrix = glm::perspective(fov, ar, f.near, f.far);
auto const& target_pos = camera.target.get_position();
glm::mat4 const view_matrix = glm::lookAt(target_pos, target_pos, glm::vec3{0, -1, 0});
ray_dir = Raycast::calculate_ray_into_screen(mouse_pos, proj_matrix, view_matrix, view_rect);
ray_start = camera.world_position();
}
else if (orthographic) { // This "doesn't work"
glm::vec3 const POS = glm::vec3{50};
glm::vec3 const FORWARD = glm::vec3{0, -1, 0};
glm::vec3 const UP = glm::vec3{0, 0, -1};
// 1024, 768 with NEAR 0.001 and FAR 10000
//glm::mat4 proj_matrix = glm::ortho(0, 1024, 0, 768, 0.0001, 10000);
glm::mat4 proj_matrix = glm::ortho(0, 1024, 0, 768, 0.0001, 100);
// Look down at the scene from above
glm::mat4 view_matrix = glm::lookAt(POS, POS + FORWARD, UP);
// convert the mouse screen coordinates into world coordinates for the cube/ray test
auto const p0 = screen_to_world(mouse_pos, view_rect, proj_matrix, view_matrix, 0.0f);
auto const p1 = screen_to_world(mouse_pos, view_rect, proj_matrix, view_matrix, 1.0f);
ray_start = p0;
ray_dir = glm::normalize(p1 - p0);
}
bool const intersects = ray_intersects_cube(logger, ray_dir, ray_start,
eid, tr, cube, distances);
In perspective mode, we cast a ray into the scene and see if it intersects with the cube surrounding the object.
In orthographic mode, I'm casting two rays from the screen (one at z=0, the other at z=1) and creating a ray between those two points. I set the ray start point to where the mouse pointer is (with z=0) and use the ray direction just calculated as inputs into the same ray_cube_intersection algorithm.
My question is this
Since the MousePicking works using the Perspective projection, but not using an Orthographic projection:
Is it reasonable to assume the same ray_cube intersection algorithm can be used with a perspective/orthographic projection?
Is my thinking about setting the ray_start and ray_dir variables in the orthographic case correct?
Here is the source for the ray/cube collision algorithm in use.
glm::vec3
Raycast::calculate_ray_into_screen(glm::vec2 const& point, glm::mat4 const& proj,
glm::mat4 const& view, Rectangle const& view_rect)
{
// When doing mouse picking, we want our ray to be pointed "into" the screen
float constexpr Z = -1.0f;
return screen_to_world(point, view_rect, proj, view, Z);
}
bool
ray_cube_intersect(Ray const& r, Transform const& transform, Cube const& cube,
float& distance)
{
auto const& cubepos = transform.translation;
glm::vec3 const minpos = cube.min * transform.scale;
glm::vec3 const maxpos = cube.max * transform.scale;
std::array<glm::vec3, 2> const bounds{{minpos + cubepos, maxpos + cubepos}};
float txmin = (bounds[ r.sign[0]].x - r.orig.x) * r.invdir.x;
float txmax = (bounds[1 - r.sign[0]].x - r.orig.x) * r.invdir.x;
float tymin = (bounds[ r.sign[1]].y - r.orig.y) * r.invdir.y;
float tymax = (bounds[1 - r.sign[1]].y - r.orig.y) * r.invdir.y;
if ((txmin > tymax) || (tymin > txmax)) {
return false;
}
if (tymin > txmin) {
txmin = tymin;
}
if (tymax < txmax) {
txmax = tymax;
}
float tzmin = (bounds[ r.sign[2]].z - r.orig.z) * r.invdir.z;
float tzmax = (bounds[1 - r.sign[2]].z - r.orig.z) * r.invdir.z;
if ((txmin > tzmax) || (tzmin > txmax)) {
return false;
}
distance = tzmin;
return true;
}
edit: The math space conversions functions I'm using:
namespace boomhs::math::space_conversions
{
inline glm::vec4
clip_to_eye(glm::vec4 const& clip, glm::mat4 const& proj_matrix, float const z)
{
auto const inv_proj = glm::inverse(proj_matrix);
glm::vec4 const eye_coords = inv_proj * clip;
return glm::vec4{eye_coords.x, eye_coords.y, z, 0.0f};
}
inline glm::vec3
eye_to_world(glm::vec4 const& eye, glm::mat4 const& view_matrix)
{
glm::mat4 const inv_view = glm::inverse(view_matrix);
glm::vec4 const ray = inv_view * eye;
glm::vec3 const ray_world = glm::vec3{ray.x, ray.y, ray.z};
return glm::normalize(ray_world);
}
inline constexpr glm::vec2
screen_to_ndc(glm::vec2 const& scoords, Rectangle const& view_rect)
{
float const x = ((2.0f * scoords.x) / view_rect.right()) - 1.0f;
float const y = ((2.0f * scoords.y) / view_rect.bottom()) - 1.0f;
auto const assert_fn = [](float const v) {
assert(v <= 1.0f);
assert(v >= -1.0f);
};
assert_fn(x);
assert_fn(y);
return glm::vec2{x, -y};
}
inline glm::vec4
ndc_to_clip(glm::vec2 const& ndc, float const z)
{
return glm::vec4{ndc.x, ndc.y, z, 1.0f};
}
inline glm::vec3
screen_to_world(glm::vec2 const& scoords, Rectangle const& view_rect, glm::mat4 const& proj_matrix,
glm::mat4 const& view_matrix, float const z)
{
glm::vec2 const ndc = screen_to_ndc(scoords, view_rect);
glm::vec4 const clip = ndc_to_clip(ndc, z);
glm::vec4 const eye = clip_to_eye(clip, proj_matrix, z);
glm::vec3 const world = eye_to_world(eye, view_matrix);
return world;
}
} // namespace boomhs::math::space_conversions
I worked on this for several days because I ran into the same problem.
The unproject methods that we are used to work with are working 100% correctly here as well - even with orthographic projection. But with orthographic projection the direction vector going from the camera position into the screen is always the same. So, unprojecting the cursor in the same way dies not work as intended in this case.
What you want to do is getting the camera direction vector as it is but in order to get the ray origin you need to shift the camera position according to the current mouse position on screen.
My approach (C#, but you'll get the idea):
Vector3 worldUpDirection = new Vector3(0, 1, 0); // if your world is y-up
// Get mouse coordinates (2d) relative to window position:
Vector2 mousePosRelativeToWindow = GetMouseCoordsRelativeToWindow(); // (0,0) would be top left window corner
// get camera direction vector:
Vector3 camDirection = Vector3.Normalize(cameraTarget - cameraPosition);
// get x and y coordinates relative to frustum width and height.
// glOrthoWidth and glOrthoHeight are the sizeX and sizeY values
// you created your projection matrix with. If your frustum has a width of 100,
// x would become -50 when the mouse is left and +50 when the mouse is right.
float x = +(2.0f * mousePosRelativeToWindow .X / viewportWidth - 1) * (glOrthoWidth / 2);
float y = -(2.0f * mousePosRelativeToWindow .Y / viewPortHeight - 1) * (glOrthoHeight / 2);
// Now, you want to calculate the camera's local right and up vectors
// (depending on the camera's current view direction):
Vector3 cameraRight = Vector3.Normalize(Vector3.Cross(camDirection, worldUpDirection));
Vector3 cameraUp = Vector3.Normalize(Vector3.Cross(cameraRight, camDirection));
// Finally, calculate the ray origin:
Vector3 rayOrigin = cameraPosition + cameraRight * x + cameraUp * y;
Vector3 rayDirection = camDirection;
Now you have the ray origin and the ray direction for your orthographic projection.
With these you can run any ray-plane/volume-intersections as usual.

Glm Quaternion lookat function

I am trying to write a lookat function that uses glm::quat to represent rotations, based of off this answer. I am running into trouble getting a correct angle however. This is my lookat function:
void Camera::LookAt(float x, float y, float z) {
glm::vec3 lookVector = glm::vec3(x, y, z);
assert(lookVector != position);
glm::vec3 direction = glm::normalize(lookVector-position);
float dot = glm::dot(glm::vec3(0, 0, -1), direction);
if (fabs(dot - (-1.0f)) < 0.000001f)
rotation = glm::quat(RadiansToDegrees(M_PI), 0.0f, 1.0f, 0.0f);
if (fabs(dot - (1.0f)) < 0.000001f)
rotation = glm::quat();
float angle = RadiansToDegrees(acosf(dot));
glm::vec3 cross = (glm::cross(glm::vec3(0, 0, -1), direction));
rotation = glm::normalize(glm::angleAxis(angle, cross));
std::cout << glm::eulerAngles(rotation).x << " " << glm::eulerAngles(rotation).y << " " << glm::eulerAngles(rotation).z << "\n";
}
When I call LookAt(0.0f, 0.0f, 0.0f) when my camera is at (0.0f, 0.0f, -10.0f), this outputs a correct rotation of 0,0,0. However if I translate my camera to (0.0f, -0.01f, -10.0f) or more I get a rotation of about 124,0,0. This goes down if I continue to translate y by -0.01f. If I do not normalize the quaternion I do not get this problem. The rotation is still 124 about the x axis, but the appearance is fine. If however I normalize the quaternion later it once again appears to rotate to about 124. I can not normalize cross, because doing so throws an assert. What would cause me to get euler angles of 124 about x from my lookat function, and how can I fix it?
Since version 0.9.9.0 there is a function in <glm/gtc/quaternion.hpp> doing mostly what you want:
template<typename T, qualifier Q>
tquat<T, Q> quatLookAt(vec<3, T, Q> const& direction, vec<3, T, Q> const& up);
It was added by this pull request and has been merged into master July 24, 2017.
But:
direction has to be a normalized vector!
direction can't be parallel to up!
So you may want to write a safer wrapper around the function:
glm::quat safeQuatLookAt(
glm::vec3 const& lookFrom,
glm::vec3 const& lookTo,
glm::vec3 const& up,
glm::vec3 const& alternativeUp)
{
glm::vec3 direction = lookTo - lookFrom;
float directionLength = glm::length(direction);
// Check if the direction is valid; Also deals with NaN
if(!(directionLength > 0.0001))
return glm::quat(1, 0, 0, 0); // Just return identity
// Normalize direction
direction /= directionLength;
// Is the normal up (nearly) parallel to direction?
if(glm::abs(glm::dot(direction, up)) > .9999f) {
// Use alternative up
return glm::quatLookAt(direction, alternativeUp);
}
else {
return glm::quatLookAt(direction, up);
}
}
I have fixed the problem with the following code:
void Camera::LookAt(float x, float y, float z) {
glm::vec3 lookVector = glm::vec3(x, y, z);
assert(lookVector != position);
glm::vec3 direction = glm::normalize(lookVector-position);
float dot = glm::dot(glm::vec3(0, 0, 1), direction);
if (fabs(dot - (-1.0f)) < 0.000001f) {
rotation = glm::angleAxis(RadiansToDegrees(M_PI), glm::vec3(0, 1, 0));
return;
}
else if (fabs(dot - (1.0f)) < 0.000001f) {
rotation = glm::quat();
return;
}
float angle = -RadiansToDegrees(acosf(dot));
glm::vec3 cross = glm::normalize(glm::cross(glm::vec3(0, 0, 1), direction));
rotation = glm::normalize(glm::angleAxis(angle, cross));
}
I do not however understand the necessity of the negative on angle. It fixed the last of my problems, and an explanation of the math of why would be helpful.

Opengl Camera and multiplying matrixes

I Am currently having alot of problems with the camera I am making. The problem occurs with my matrix rotation I am doing as this website says to avoid gimble lock..
One of the first problems you will note is that the order you apply
these rotations matter. As previously stated, a rotation matrix is an
orientation transform. Each transform defines a new coordinate system,
and the next transform is based on an object in the new space. For
example, if we apply the roll first, we have now changed what the axis
for the subsequent yaw is.
And when i perform this for example if I am wanted to pitch around the current x axis the x axis also changes in my axis to rotation method which is obviously wrong. Ive look around alot and cant find any solution. I have tried alot of differenet version of the axis angle rotation matrix..
void FrustumCamera::xAxisRotation(float angle)
{
Vector3<float> x = m_orientation.getXAxis();
Matrix4<float> matrix = m_orientation.axisAngleRotation(x,angle);
m_orientation = matrix*m_orientation;
normalise(m_orientation.getXAxis());
normalise(m_orientation.getYAxis());
normalise(m_orientation.getZAxis());
}
void FrustumCamera::yAxisRotation(float angle)
{
Vector3<float> y = m_orientation.getYAxis();
Matrix4<float> matrix = m_orientation.axisAngleRotation(y,angle);
m_orientation = matrix*m_orientation;
normalise(m_orientation.getXAxis());
normalise(m_orientation.getYAxis());
normalise(m_orientation.getZAxis());
}
Matrix4<Type> Matrix4<Type>::operator*(Matrix4& matrix)
{
Matrix4<Type> temp(m_matrix);
for(int i=0;i<4;i++)
{
for(int j=0;j<4;j++)
{
Type total = 0;
for(int k=0;k<4;k++)
{
total += m_matrix[i][k]*matrix.getAt(k,j);;
}
temp.setAt(i,j,total);
}
}
return temp;
}
template <class Type>
Matrix4<Type> Matrix4<Type>::axisAngleRotation(Vector3<Type> axis, const Type angle)
{
Type radians = angle * (double)degToRad;
Matrix4<Type> temp;
float c = cosf(radians);
float s = sinf(radians);
float t = 1.0f - c;
float x = axis.x;
float y = axis.y;
float z = axis.z;
temp.setAt(0,0, c+x*x*(t));
temp.setAt(0,1, x*y*(t)-z*s);
temp.setAt(0,2, x*z*(t)+y*s);
temp.setAt(0,3, 0.0f);
temp.setAt(1,0, y*x*(t)+z*s);
temp.setAt(1,1, c+y*y*(t));
temp.setAt(1,2, y*z*(t)-x*s);
temp.setAt(1,3, 0.0f);
temp.setAt(2,0, z*x*(t)-y*s);
temp.setAt(2,1, z*y*(1-c)+x*s);
temp.setAt(2,2, c+z*z*(t));
temp.setAt(2,3, 0.0f);
temp.setAt(3,0, 0.0f);
temp.setAt(3,1, 0.0f);
temp.setAt(3,2, 0.0f);
temp.setAt(3,3, 1.0f);
return temp;
}
void OpenGLRenderer::startDraw(unsigned long mask)
{
//sortBuffer(); // sort draw queue
clearBuffers(mask); // clear buffers
loadIdentity();
glTranslatef(-1*m_frustumCamera->getViewMatrix().getTranslationAxis().x,-1*m_frustumCamera->getViewMatrix().getTranslationAxis().y,-1*m_frustumCamera->getViewMatrix().getTranslationAxis().z);// load identity
glMultMatrixf(m_frustumCamera->getViewMatrix().getMatrix());
glTranslatef(m_frustumCamera->getViewMatrix().getTranslationAxis().x,m_frustumCamera->getViewMatrix().getTranslationAxis().y,m_frustumCamera->getViewMatrix().getTranslationAxis().z);
matrixStackPush();
}
I think order of multiplication can cause the problem, instead of
m_orientation = matrix*m_orientation;
try
m_orientation = m_orientation * matrix;

Move objects to specified points in a scene?

I have been trying to throw together a chess game using a 3dsmax model. At this point, I have been able to import the model, highlight the selected game piece I am interested in moving, and choose a square I want to move to. Here is a screenshot of the current state:
http://img26.imageshack.us/img26/9555/chessk.png
The black circle represents where I clicked, and you can see where the pawn went. I haven't done specific calculations on where it should go. Whenever I click on the board with a selected piece, it always moves in the same direction. It's because just threw in this dummy code to start off:
if ( isObjectSelected && isSquareSelected && moveObject )
{
glPushMatrix();
glTranslatef(0.2f, 0.0f, 0.0f); //PLACEHOLDER-DUMMY CODE
}
glDrawElements( GL_TRIANGLES, pMaterial->triangleCount * 3, GL_UNSIGNED_INT, model.getIndexBuffer() + pMaterial->startIndex );
if ( isObjectSelected && isSquareSelected )
glPopMatrix();
What I was considering doing was after the model was done loading, is to somehow check which square on the board a game piece occupies. Then, when a piece is selected and a "move to" square is selected, find the x,y,z glTranslate3f to move to that center square.
Is this the best way? It seems as the game progresses, I will need to store the glTranslate of each piece individually. And when a piece goes from it's 2nd to 3rd spot, I should calculate the glTranslate from the original starting point to the 3rd spot, right?
But how would you figure out whether or not a game piece occupies a square, and how would you figure out glTranslate3f(X, Y, Z) between two squares? Here is an example of a square from my .OBJ file
#
# object Square58
#
v -37.1874 18.6313 80.7864
v -67.0955 18.6313 91.4436
v -56.4384 18.6313 121.3513
v -26.5306 18.6313 110.6938
# 4 vertices
vn 0.0000 1.0000 -0.0000
# 1 vertex normals
vt 0.0000 0.0000 0.0000
# 1 texture coords
I am assuming I would need to find the center of each square and say once the app knows this piece is in square1, and you clicked on sqaure4, calculate the translate & go. I am just not sure how to calculate the center of each square, and figure out what the translate coords should be from square1-->square4.
OR how I would determine which pieces occupies which square from the beginning. I can hard code this in during load, but it would help me more in understanding if there was a sound way to accomplish this.
Each square & game piece is a struct GroupObject like:
//A chess piece or square
struct GroupObject
{
std::vector<Material *> materials;
std::string objectName;
std::string groupName;
int index;
std::vector<Vector3 *> vertices;
Vector3 center;
};
And Vector3 looks like:
#ifndef VECTOR3_H
#define VECTOR3_H
#include <math.h>
class Vector3
{
public:
Vector3(float X = 0.0f, float Y = 0.0f, float Z = 0.0f)
{
x = X;
y = Y;
z = Z;
}
Vector3 operator+=(const Vector3 &vec)
{
return (*this = (*this + vec) );
}
Vector3 operator+(const Vector3 &vec)
{
return Vector3(vec.x + x, vec.y + y, vec.z + z);
}
Vector3 operator-=(const Vector3 &vec)
{
return (*this = (*this - vec) );
}
Vector3 operator-(const Vector3 &vec)
{
return Vector3(x - vec.x, y - vec.y, z - vec.z);
}
Vector3 operator*=(float num)
{
return (*this = (*this * num) );
}
Vector3 operator*(float num)
{
return Vector3(x * num, y * num, z * num);
}
Vector3 operator/=(float num)
{
return (*this = (*this / num) );
}
Vector3 operator/(float num)
{
return Vector3(x / num, y / num, z / num);
}
Vector3 operator-(void)
{
//invert direction of vector
return Vector3(-x, -y, -z);
}
float Dot(Vector3 &vec)
{
return (x * vec.x + y * vec.y + z * vec.z);
}
Vector3 operator*(const Vector3 &vec)
{
//cross product
return Vector3( y * vec.z - z * vec.y,
z * vec.x - x * vec.z,
x * vec.y - y * vec.x );
}
float Length(void)
{
//length of vector
return sqrt( x * x + y * y + z * z);
}
Vector3 Normalize(void)
{
float length = Length();
x /= length;
y /= length;
z /= length;
return *this;
}
float Distance(Vector3 &vec)
{
//find distance between two separate vectors
float disX = vec.x - x;
float disY = vec.y - y;
float disZ = vec.z - z;
return sqrt(disX * disX + disY * disY + disZ * disZ);
}
bool operator==(Vector3 &vec)
{
return (vec.x == x && vec.y == y && vec.z == z);
}
bool operator!=(Vector3 &vec)
{
return !(vec == *this);
}
public:
float x, y, z;
};
#endif
I was going to use this to store the center of each square like:
Vector3 square->center = X, Y, Z
Hopefully this would help it determining where to move, and if something occupies that space, but figured I should get some help since I haven't done this before. I am also not sure how I should calculate it, or still completely clear on how to figure out whether a square has a game piece that occupies it at the beginning, or it's empty.
Any and all help is welcome. Thank you.
UPDATE
Right now I had a buddy look in 3ds max and on a square object properties it said dimensions were x: 40.565 y: 40.565
Then I picked two squares, one in front of the other. I calculated the center of each by added all the vertices together and divided by the sum of the vertices. I got this:
#square 1
x: 111.12125
y: 18.631268
z: 78.286982
#square 2
x: 82.276817
y: 17.615297
z: 88.545845
But from my original example, the only way I could get even close to moving to the right spot was moving glTranslatef(0.2f, 0.0f, 0.0f);
The difference between the two centers listed above is much larger. I am doing something incorrect, but not seeing it.
[a-bit-offtopic]
When i was coding my univercity project, i used to draw my 'world' (which has something similar with check-desk: it is endless and have no colour distincts) using simple math formulas for determining each line cross points. When i got those points, i was able to simply determine which particular square cursor is pointing. To be honest, i used simple mathematics to determine those centers in the last ('production') version.
Here and there are my old screens.
So, what's the trouble? When you're doing glPushMatrix(), your matrix resets to its defaults - the position becomes (0, 0, 0) and rotations are reset. If you wanna put your object in the point of intersect {line, plane}, where the line is the ray from camera origin through cursor position (recall camera theory in OpenGL: actually, screen is the plane in front of camera 'position' point) and the plane is something in front of that ray. Note: if you'll not define the plane - you'll be not able to determine intersection point.
Now, when you'll got intersection point, just do glTranslatef(<intersection_point>) and then draw your pawn or whatever.
If you are interested in pure code on how to determine intersection point - just notify me in comments.
Hope this will help.
UPD: here's the intersection point code i've mentioned before. It uses sf::Vector3f structure/class - it is taken from SFML (i'm using it for all my GL projects to handle input events and forget 'bout window creation). You can define it as a simple structure with 3 floats. Function arguments are the mouse cursor' coordinates. Calling this would provide you with intersection point coordinates.
sf::Vector3f ScreenToSpace(int x, int y)
{
GLint viewport[4];
GLdouble modelview[16];
GLdouble projection[16];
GLfloat winX, winY, winZ;
GLdouble posX, posY, posZ;
glGetDoublev(GL_MODELVIEW_MATRIX, modelview);
glGetDoublev(GL_PROJECTION_MATRIX, projection);
glGetIntegerv(GL_VIEWPORT, viewport);
winX = (float) x;
winY = (float) viewport[3] - (float) y;
glReadPixels(x, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ);
gluUnProject(winX, winY, winZ, modelview, projection, viewport, &posX, &posY, &posZ);
return sf::Vector3f((float) posX, (float) posY, (float) posZ);
}
Here's the example on how you should use this (as far as i've understood your problem):
sf::Vector3f v = ScreenToSpace(App.GetInput().GetMouseX(), App.GetInput().GetMouseY()); // get global coordinates
v = SpaceToDeskCorrection(v.x, v.y, v.z); // align v to the check-desk cell center
glPushMatrix(); // reset matrix state (incl. position and rotation) and save previous one
glTranslatef(v.x, v.y, v.z); // set matrix position to the intersection point
DrawPawn(); // well, you'll understand this line, right? =)
glPopMatrix();
I have recently done a checkers game myself, 2D but the same principles apply. First of all I would suggest you keep an array with board squares, which as you defined in your struct has a centre var.
To know where each piece is, you could for instance add a variable which is a pointer to a given square, or simply a representation like 'E4' or 'A8' (square code). Having this, you would automatically have the x,y coordinates of where the piece is, and once detecting which square you want to move it to, you just substitute the pointer to the new piece and get its centre information, which is where you want to translate the piece to. This also has the added benefit that it makes it easier for you to check later when a piece takes over another one from the adversary, or simply if it can move to a certain square (go through all the pieces, if any of them is laying on the square you want to go to, don't allow the move).
To find out the centre of any square, assuming you don't know it when you create the board (don't you ?), and do an average over all the constituting vertices x and y coordinates (if the board is laying on the xy plane). For instance, Square58 centre = (v1(x)+v2(x)+v3(x)+v4(x) ; v1(y)+v2(y)+v3(y)+v4(y)).
I hope that was clear enough, if not just let me know what you didn't get and I'll try to explain.
EDIT: Sorry, I wrote something wrong. In the Square58 centre.... line, you divide the final sum by 4 (to get the average).. ie:
Square58 centre = ((v1(x)+v2(x)+v3(x)+v4(x))/4 ; (v1(y)+v2(y)+v3(y)+v4(y))/4).