• Create Account

## opengl object world coordinates

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

8 replies to this topic

### #1Misantes  GDNet+

2092
Like
0Likes
Like

Posted 19 June 2014 - 08:52 PM

I thought I had this figured out, but apparently I was mistaken. I'm failing to understand a small but crucial step here.

Anyhow, in order to implement collision detection, I need to know the coordinates in world space of the objects. Previously, I would simply track their position by how much they've been rotated and translated. But, since implementing rotation by quaternions, I seem to have lost the ability to do that (or, more likely, I simply just don't know how). I'm rotating and translating the Model Matrix by a translation matrix and rotation matrix.

a simple example:

ModelMatrix = TranslationMatrix * RotationMatrix * ModelMatrix;


where the translation and rotation matrices are created by

TranslationMatrix = glm::translate(movement);

RotationMatrix = glm::mat4_cast(quaternion); 

, respectively.

Right now, I'm trying to retrieve the world coordinates by just using the ModelMatrix[3][0],ModelMatrix[3][1], and ModelMatrix[3][2] values. However, I quickly found that this is insufficient as the values slowly become offset (they're roughly correct at first, but as I rotate and move things around, the values constantly increase by small increments over time). I would guess this is because of the way I'm creating the ModelMatrix by multiplying it by itself, but I'm unsure. For example, if I take the distance between the model position(as defined above) and "camera" and output it to the console, if I just spin in place without translating, the output distance very slowly grows. Nothing moves in-game(well, it spins, but ). Again, I would guess this is because it's multiplying the previous rotation by the new one, but I've yet to be able to pin this down.

Most advice I've found online suggests to simply track the position separately. If this is the case, I suppose my question would be how would I go about keeping track of the coordinates after rotating and translating them (I understand how to do this with simple trigonometry rotations and translations, but I don't know quite how to do so using quaternions). I realize it ought to be relatively simple, since I've told openGL where to  put the object:P However, I'm finding it rather difficult with the matrix multiplication and quaternion involved (essentially, in my limited knowledge, I'm waving my hand at openGL and saying "put it over there somewhere." It works to put it there, but I'm failing to retrieve where exactly "there" is.)

Otherwise, is there a better way to retrieve the world coordinates of an object? Are the first three values of the bottom column of the matrix sufficient, but I'm somehow messing up their values by multiplying it by itself?

Anyway, thanks in advance for any help. Please let me know if you need more of the code, I genuinely appreciate any assistance .

Edited by Misantes, 19 June 2014 - 09:10 PM.

Beginner here <- please take any opinions with grain of salt

### #2SeanMiddleditch  Members

16752
Like
1Likes
Like

Posted 19 June 2014 - 10:06 PM

However, I quickly found that this is insufficient as the values slowly become offset (they're roughly correct at first, but as I rotate and move things around, the values constantly increase by small increments over time).

Yup. Don't actually operate on your matrix. Store the rotation, translation, and scale separately, and then recalculate the matrix when needed. Avoid letting errors compound.

Game Developer, C++ Geek, Dragon Slayer - http://seanmiddleditch.com

C++ SG14 "Games & Low Latency" - Co-chair - public forums

Wargaming Seattle - Lead Server Engineer - We're hiring!

### #3Misantes  GDNet+

2092
Like
0Likes
Like

Posted 19 June 2014 - 10:41 PM

However, I quickly found that this is insufficient as the values slowly become offset (they're roughly correct at first, but as I rotate and move things around, the values constantly increase by small increments over time).

Yup. Don't actually operate on your matrix. Store the rotation, translation, and scale separately, and then recalculate the matrix when needed. Avoid letting errors compound.

Hi.

Would you mind elaborating on this a little? I'm not sure I'm 100% following. If I understand what you're saying, instead of now, where I recalculate the rotation matrix and translation matrix every turn and then multiply it by the existing model matrix, I ought to recalculate the model matrix every turn, but store the rotation and translation as separate matrices?, so along the lines of:

TranslationMatrix = glm::translate(movement);//stored translation, movement vector is updated separately, but not reset.
RotationMatrix = glm::mat4_cast(quaternion) * RotationMatrix; //stored rotation,

ModelMatrix = TranslationMatrix * RotationMatrix;//combine the two



I'll try adapting things along these lines and let you know how it goes. If I'm misunderstanding you, please feel free to correct me

And, thank you for the help!

Edit* in hindsight, I'm applying  your advice totally wrong, above. I'll work on that a bit, and see how to properly store the rotations and update here.

edit* changed the code above, this seems to work a little better. Things actually rotate in game now (had some weird rotation problems in trying to store the rotation, maybe fixed now). I'll hopefully get some time later tonight to test to see if the position is properly saved.

edit* hm. Now, if saving the rotations and translations, it rotates around the world coordinates 0,0,0. I'll adapt the code to make sure it's rotating around the "camera" position and see what happens.

Edited by Misantes, 19 June 2014 - 11:34 PM.

Beginner here <- please take any opinions with grain of salt

### #4SeanMiddleditch  Members

16752
Like
1Likes
Like

Posted 20 June 2014 - 01:57 AM

Would you mind elaborating on this a little? I'm not sure I'm 100% following. If I understand what you're saying, instead of now, where I recalculate the rotation matrix and translation matrix every turn and then multiply it by the existing model matrix, I ought to recalculate the model matrix every turn, but store the rotation and translation as separate matrices?, so along the lines of:

Just store translation as a vector. Construct a matrix with it when and if needed. You can cache the object's transformation matrix to support objects that only infrequently change their transforms if you wish.

The gist being that if all you're doing is changing rotation, change nothing but rotation. If all you're doing is moving, do nothing by modify the translation. A matrix modified everything - mainly when applying rotations - and you can end up with a little drift over time.

Matrices are tools, use them where appropriate, and don't use them where they're not. They're handy for composing a series of transformations. If you're not doing that, reconsider the use of a matrix to see if it's really the best tool for the job.

Game Developer, C++ Geek, Dragon Slayer - http://seanmiddleditch.com

C++ SG14 "Games & Low Latency" - Co-chair - public forums

Wargaming Seattle - Lead Server Engineer - We're hiring!

### #5Misantes  GDNet+

2092
Like
0Likes
Like

Posted 20 June 2014 - 03:56 AM

I understand what you're saying now, I think. But, I guess I'm struggling with the implementation. When I first started implementing quaternions, I got the opposite advice. I was told to never store the rotations and just apply them incrementally(unless I misunderstood the advice). When I use the matrices the way I am here, it stores the rotation with the translation, which works really well(minus this problem of course ). When I move, I'm always translating along the z axis. So, when, lets say, the forward button is pressed, the value added to the translation is always just vec3(0,0,10). So, regardless of whether I'm turning left, right, down etc, the translation vector is always vec3(0,0,some#). Coupled with the rotation quaternion, this keeps everything rotating around the player, and keeps the player always moving forward (from the camera's perspective). But, it seems like the translation vector on its own is relatively meaningless. I can never just apply the translation vector by itself.

It's what I meant when I said previously, when using just trig functions, it found it far simpler to translate the position by whatever values, and keep that translation separate. But now, since I'm translating(always along z axis) by the rotated direction, if I keep them separate, the rotation is a little screwy. When I try to store the rotation and translation separate, everything rotates around the "center of the world," as it now is translating by a large value on the z axis against the rotation. I suppose I can try to figure out how to retrieve the vector that represents the translation vector with the quaternion rotation, but that has been eluding me for a bit now.

Or, perhaps, I'm now getting the odd rotation around the for an entirely different reason, or the way I'm storing my translation. As a workaround, I can probably figure out how to get the quaternion to rotate around the new translated point.

And, my apologies, I'm probably explaining this terribly. But, you've answered my initial question, so no worries if you don't have the patience for this follow-up one I'll chew on this for awhile and see what I can figure out. You're definitely right on why I'm getting the weird drift. If you have the time and patience, I'd of course love any additional assistance. But, either way, I appreciate the help

Edited by Misantes, 20 June 2014 - 04:06 AM.

Beginner here <- please take any opinions with grain of salt

### #6haegarr  Members

7187
Like
1Likes
Like

Posted 20 June 2014 - 05:27 AM

one has several things to consider here.

1.) Numerical limitations of representations of numbers in computers will ever introduce some inaccuracy if only enough (non-identity) computations are made. This is the case with quaternions, too. The advantage of (unit-)quaternions is that it uses 4 numbers for 3 degrees of freedom, while a rotation matrix uses 9 numbers for the same degrees of freedom. That means that rotation matrices require more constraints, or in other words, that rotation matrices need a re-configuration more often than a quaternion does. However, this lowers inaccuracies only, but do not remove them totally.

So, if one accumulates rotations in a quaternion, it has to be re-normalized from time to time. If not, then the length of the quaternion will differ from 1 more and more, and that will disturb its use as orientation because only unit-quaternions give you a pure orientation / rotation. If one uses matrices, then they have to be re-orthonormliazed from time to time, which means their columns / rows have to be normalized and, by using the cross-product, to be made pairwise orthogonal. Doing such things is common when dealing with accumulation of transformations.

2.) You need to think about spatial spaces, and what transformation should happen in which space. If you define a computation like

Mn+1 := Tn+1 * Rn+1 * Mn

you perform a rotation R onto an formerly translated and rotated model, since Mn contains both the current position and orientation. This means that a rotation R, which ever has (0,0,0) in its axis of rotation, will cause the model to rotate around an axis which is distant to the model's own local origin.

Instead, if you undo the current position first, so that

Mn+1 := Tn+1 Tn * Rn+1 * Tn-1Mn = ( Tn+1 * Tn * … * T0 ) * ( Rn+1 * Rn * … * R0 )

you get the model rotate around its own origin ever. Here you accumulate the rotations for themselves, and so you do with the translations. This can be obtained by storing position and orientation in distinct variables, and applying translation on the position only and rotation on the orientation only.

Notice that the latter way does not keep you away from using the current forward vector for translation.

### #7Misantes  GDNet+

2092
Like
0Likes
Like

Posted 20 June 2014 - 12:02 PM

Ah, this seems like it may solve things. I'm at work now, but I"ll try to implement this when I get home and let you know how it goes. Thank you

Beginner here <- please take any opinions with grain of salt

### #8SeanMiddleditch  Members

16752
Like
2Likes
Like

Posted 20 June 2014 - 12:09 PM

When I first started implementing quaternions, I got the opposite advice. I was told to never store the rotations and just apply them incrementally(unless I misunderstood the advice)

They might have just meant not to store Euler angles but instead to store a quaternion or 3x3 rotation matrix.

A typical unoptimized transform object might look like:

struct Transform
{
mat33 rotation;
vec3 position;
vec3 scale;
};

void UpdateTransformation(const Transform& transform, mat44& out_matrix)
{
out_matrix = scale_mat44(transform.scale) * transform.rotation * translate_mat44(transform.position);
}
If you cache the resulting transform data, you acn keep around the 4x4 matrix for reuse and also cache the post-rotated axis (forward, up, right) which is handy for movement. e.g., when the player moves, it's usually some linear combination of forward and right, so being able to do something like:

input_accel = cached.forward * get_forward_input() * player.move_speed + cached.right * get_right_input() * player.move_speed;
Where get_forward_input returns a value in the range [-1,+1] (+1 is forward at full speed, -1 is backward at full speed) and get_right_input is the same [-1,+1] for left-to-right. You can make WASD or joystick controls to these axis really easily.

The input_accel ultimately only affects the position, not rotation or scale, of course.

Game Developer, C++ Geek, Dragon Slayer - http://seanmiddleditch.com

C++ SG14 "Games & Low Latency" - Co-chair - public forums

Wargaming Seattle - Lead Server Engineer - We're hiring!

### #9Misantes  GDNet+

2092
Like
0Likes
Like

Posted 20 June 2014 - 05:39 PM

@haegarr

Is it enough to normalize the quaternion each time, or should the entire rotation matrix need to be re-orthonormalized? The latter seems a slightly more involved process, at least for a 4x4 matrix.

edit* I'm going to guess the latter, as a test of normalizing the quaternion  didn't seem to fix it  The drift is much, much smaller now (it slowly changes by .001 now), but it still definitely drifting as I rotate. Just to reiterate, this is a drift in the distance between ModelMatrix[3][0], ModelMatrix[3][1], ModelMatrix[3][2](minus the object's starting position), and the camera at 0,0,0. and distance is calculated by the typical pythagorean method.

2.) You need to think about spatial spaces, and what transformation should happen in which space. If you define a computation like
Mn+1 := Tn+1 * Rn+1 * Mn
you perform a rotation R onto an formerly translated and rotated model, since Mn contains both the current position and orientation. This means that a rotation R, which ever has (0,0,0) in its axis of rotation, will cause the model to rotate around an axis which is distant to the model's own local origin.

This is actually what I'm going for(i think), as I'd like the objects to rotate around the "camera". The rotation Matrix is recalculated every time by a new quaternion, so just rotates in small pieces, same with the translation vector. Basically, take it's stored location and rotation, and then rotate and translate that by another small rotation and translation.Everything works right as the code stands now,visually, at least, just the problem with the values drifting a little lingers. It really isn't much, just enough to cause problems. It's possible I'm misunderstanding you here, though. Is the fact that it's rotating around an axis distant to the model's local origin causing the drifting to occur?

To really clarify my problem(as I'm experiencing it), lets say I calculate the distance between the object as represented by ModelMatrix[3][0],ModelMatrix[3][1], ModelMatrix[3][2]  and the "camera" at 0,0,0. and, let's say that distance is 10. if I rotate around (without translating at all) a whole bunch, eventually that distance will increase to 10.001 and then 10.002, etc. is calculating the matrix as "Mn+1 := Tn+1 * Rn+1 * Mn" causing that drift?

Instead, if you undo the current position first, so that
Mn+1 := Tn+1 * Tn * Rn+1 * Tn-1 * Mn = ( Tn+1 * Tn * … * T0 ) * ( Rn+1 * Rn * … * R0 )

I'm going to admit my mathematical skills are a little too poor to parse this(simple as it may be). If I'm reading it correctly, for each iteration, I undo the translation, then rotate, then apply the translation again with whatever added translation for that pass. Doesn't this result in the rotation always rotating about the original point, making it so regardless of where the player-camera is, the world will seem to rotate around the original starting position(like, imagine a space-ship(the camera) starts near the sun, but no matter where the ship goes in the solar system, when it rotates it looks like the whole solar system is rotating around the sun still, rather than everything around the ship)?

Back, to the normalization thing, a quick search online has recommended Gramm-Schmidt orthogonalization. I'm using the glm library. Would it just be easier to use the glm/gtx orthogonalization function that's in the library and fanagle it into the rotation matrix(the library is for 3x3 matrices, but perhaps I can write a function to adapt it),or, do you recommend a specific way to go about doing this? I'm happy to do my own research into this, but thought I may as well ask while I have your attention.

@Sean

I think I may have inadvertently led us off on an irrelevant tangent. In hindsight, this does appear to be a problem more along the lines of what haegarr is referring to. Nothing is way off, everything appears to be mostly correct, just over time everything "drifts" just a little. Fractions of a value, even (about .001 every other second, if I'm holding the rotate key down). But, when implementing collision, those fractions start adding up and become noticeable. I'm finding if I try to store the translation and rotation separately, and not multiply the matrix against itself, the rotations tend to be wrong. They'll either do what was reported above, where they rotate around a central point, instead of the camera, or weirder, they'll rotate with the camera view. I'll spend some time tomorrow working with your idea as well, to see if I can suss out what I'm doing wrong there.

Edit* @Sean

the issue I'm having with implementing your method, wherein I save the rotation and translation is that the translation has no direction in it. It's merely forward. Multiplying the matrix as you have it results in odd translation as it's translating before rotating.

void UpdateTransformation(const Transform& transform, mat44& out_matrix)
{
out_matrix = scale_mat44(transform.scale) * transform.rotation * translate_mat44(transform.position);//translates before rotating, but translation has no direction. always moves along world z-axis, not camera z-axis
}

but, if I change that to:

void UpdateTransformation(const Transform& transform, mat44& out_matrix)
{
out_matrix = scale_mat44(transform.scale)* translate_mat44(transform.position) * transform.rotation;//rotates then translates, but origin is always the starting point, not the current position of the player camera
}

This is a little better, but results in the objects rotating around the starting position of the camera, not its current one.

Perhaps there is an easy solution to this I'm overlooking. Normally, I would translate to the current position before rotating, then translate the new movement however, since the translation has no direction, it results in odd behavior(like always translating along the world z-axis, and not the camera z-axis). Again, when I say the translation has no direction, I mean it's just a forward position, say vec3(0,0,1). It only makes sense when coupled with the rotation to end up moving in the desired direction. This makes it so I'm unable to translate to the current position before rotating, as I only know what it is after multiplying the rotation by the translation. Perhaps I'm totally going about this the wrong way and there is a simple solution, but I thought I would lay out what I'm struggling with (even if it makes me look a little dense). Your method definitely gets rid of the drifting matrix points however, so if I can figure out how to make this work, it would solve my problem

bleh. Is there just a way to get to the object's position coordinates after translating it in a rotated direction? That alone would just really solve most of the problems here. After multiplying the model matrix by a quaternion based matrix, i just lose any sort of knowledge of the actual world coordinates of the object. Trying to calculate it by:

positionVector += quaternion * movement * conjugateOfQuaternion;


is giving me funny results.

Alright, last edit, hopefully.

Got everything working, kinda.

Taking Sean's advice. Though, I had to find a way to translate with a rotated quaternion as simply translating and rotating would move along worldspace z-axis instead of camera space. So:

combinedQuaternion = quaternion * combinedQuaternion;//cached quaternion
position += vec3Move * sumQuaternion;//position to be translated. current movement + total rotation.

RotationMatrix = mat4_cast(combinedQuaternion);
ModelMatrix = RotationMatrix * glm::translate(position);//translate with the direction, then rotate.


Seems to work. I get the correct movement and don't have to multiply the ModelMatrix by itself. I can retrieve the position of the object by the "position" vector, and use that for collision without dealing with parsing the ModelMatrix (which wasn't really working out anyhow).

My problem was I didn't realize you could translate a vector by a quaternion. I was mistakenly under the impression you could only multiply a quaternion once it was converted to a matrix. So, once that was resolved, the rest worked out pretty well.

My old problem of the translation coming to a halt while rotating has reared its ugly head again, but I'l spend some time trying to figure that out on my own.

Thank you all for your help I learned a lot, and got this sorted out (I think, there's always something else, isn't there? )

Cheers,

Edited by Misantes, 22 June 2014 - 12:56 PM.

Beginner here <- please take any opinions with grain of salt

Old topic!

Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.