**0**

# Vertex Rotation Calculations - Work one at a time, but fail in groups?

###
#1
Members - Reputation: **152**

Posted 28 April 2012 - 03:00 PM

Specifically, when I apply a apply a rotation in only the x, y, or z coord, it will calculate correctly. But if I apply rotation in two directions, it does not calculate correct coords. I'm not sure why they would fail running with multiple rotations, since if they each work individually. Here is my code:

Any thoughts? Thanks in advance.

[source lang="cpp"] /////////// Z ///////////// // atan2( 0, 0 ) will throw an error if( !(point.x == 0 && point.y == 0) ) { //get initial 'rotation' of the vert from (0,0,0) rot = atan2( -point.x, -point.y ); //convert rotation to radians and add it to the existing rotation rot += CMath::DegreesToRadians( rotations.z ); length = Vector3( point.x, point.y, 0 ).Length(); point.x = -sin( rot ) * length; point.y = cos( rot ) * length; } /////////// Y ///////////// if( !(point.x == 0 && point.z == 0) ) { rot = atan2( -point.x, -point.z ); rot += CMath::DegreesToRadians( rotations.y ); length = Vector3( point.x, point.z, 0 ).Length(); point.x = sin( rot ) * length; point.z = -cos( rot ) * length; } /////////// X ///////////// if( !(point.z == 0 && point.y == 0) ) { rot = atan2( -point.z, -point.y ); rot += CMath::DegreesToRadians( rotations.x ); length = Vector3( point.z, point.y, 0 ).Length(); point.z = sin( rot ) * length; //adj point.y = -cos( rot ) * length; //opp }[/source]

###
#2
Crossbones+ - Reputation: **15566**

Posted 28 April 2012 - 05:47 PM

So here's the better way:

new_x = old_x * cos(angle) - old_y * sin(angle) new_y = old_x * sin(angle) + old_y * cos(angle)

With that out of the way, what your code is doing is composing three rotations around axes. You haven't explained why you think the results are incorrect. Chances are your intuitions about rotations are a bit off and the code is working just fine.

So what's an example of inputs into this function that produce output that you consider incorrect? What result did you expect instead?

**Edited by alvaro, 28 April 2012 - 05:49 PM.**

###
#3
Members - Reputation: **152**

Posted 29 April 2012 - 02:41 AM

Let me go into a bit more detail. I'm working on a simple 3D engine with openGL. I am using this function for collision calculation between a model, made up of triangles, and a ray. In order to run the collision test, I need to convert the ray (which has coordinates relative to global space) to the local space of the model (by applying the rotation, translation, and scale to the origin and end point of the ray)

For now, the ray I'm testing with is a pick ray, generated by a mouse click. That ray's coords are then moved into object space, and run a collision test against each face in the model. The collisions register perfectly one at a time (I'm not outputting any fixed values, rather simply clicking around the edges of my object, to make sure the collisions are accurate) As I mentioned, this functions perfectly with only rotation applied, but becomes inaccurate when a second or 3rd rotation direction is put in place. I've traced the collision area with my mouse, and it looks like, it is testing collisions against the model, with a different rotation. But again, it works perfectly when rotated on only one angle.

I know the pick ray generation is working properly, as is the collision test. (They've both been tested pretty thoroughly in several previous instances) I'm not sure if this helps, but here is the rest of the code for the globalToLocal function. Btw, I've been learning this on my own, so if you happen to catch any other poorly written bits of code, please let me know.

[source lang="cpp"] /* Hierarchy is the set of transformations to apply. In this case, only one element is in the list.*/ void Mesh::globalToLocal( vector<Vector3 *> &in_vertices, list<Transform *> hierarchy ){ for ( int i = 0; i < (int)in_vertices.size(); i++ ) { list<Transform *>::iterator it; for ( it = hierarchy.begin(); it != hierarchy.end(); it ++ ) { applyVertexTranslation ( (*in_vertices[i]), (*it)->translate ); applyVertexRotation ( (*in_vertices[i]), (*it)->rotation ); //scale is not currently added to the heirarchy, since it is never changed/used } }}void Mesh::applyVertexRotation ( Vector3 &point, Vector3 &rotations ){ float rot, oldX, oldY, oldZ; /////////// Z ///////////// if( !(point.x == 0 && point.y == 0) ) { rot = -CMath::DegreesToRadians( rotations.z ); oldX = point.x; oldY = point.y; point.x = oldX * cos(rot) - oldY * sin(rot); point.y = oldX * sin(rot) + oldY * cos(rot); } /////////// Y ///////////// if( !(point.x == 0 && point.z == 0) ) { rot = CMath::DegreesToRadians( rotations.y ); oldX = point.x; oldZ = point.z; point.x = oldX * cos(rot) - oldZ * sin(rot); point.z = oldX * sin(rot) + oldZ * cos(rot); } /////////// X ///////////// if( !(point.z == 0 && point.y == 0) ) { rot = CMath::DegreesToRadians( rotations.x ); oldZ = point.z; oldY = point.y; point.z = oldZ * cos(rot) - oldY * sin(rot); point.y = oldZ * sin(rot) + oldY * cos(rot); }}void Mesh::applyVertexScale ( Vector3 &point, Vector3 &scale ){ point.x /= scale.x; point.y /= scale.y; point.z /= scale.z;}void Mesh::applyVertexTranslation ( Vector3 &point, Vector3 &translations ){ point.x -= translations.x; point.y -= translations.y; point.z -= translations.z;}[/source]

###
#4
Crossbones+ - Reputation: **5582**

Posted 29 April 2012 - 03:43 AM

**v**' =

**T**

_{2}* (

**R**

_{2}* (

**T**

_{1}* (

**R**

_{1}*

**v**) ) )

for all vertices

**v**, assuming 2 transformations in your list, where the usual way would look like

**v**' = (

**T**

_{2}*

**R**

_{2}*

**T**

_{1}*

**R**

_{1}) *

**v**=

**M***

**v**with

**M**:=

**T**

_{2}*

**R**

_{2}*

**T**

_{1}*

**R**

_{1}

instead. The inefficiency becomes more and more relevant the more transformations you have and the more vertices you transform, of course.

Now, when looking at your given problem, you want to transform from global to local space. In other words, you want to compute

**v**from

**v**' when still using the above naming scheme. Hence you require to apply the

*inverse*transformation, because

**M**

^{-1}*

**v**' =

**M**

^{-1}* (

**M***

**v**) =

**v**

When using a composed transformation then inverting it is all you have to do. However, for the sake of clarity and perhaps you insist of using the particular transformations, the effect of inversion is

**M**

^{-1}= (

**T**

_{2}*

**R**

_{2}*

**T**

_{1}*

**R**

_{1})

^{-1}=

**R**

_{1}

^{-1 }*

**T**

_{1}

^{-1}*

**R**

_{2}

^{-1}*

**T**

_{2}

^{-1}

so that not only each each transformation is inverted but also the order is reversed. Do you have considered this?

Now let us assuming that

**M**is the the local-to-global transformation of your model. You can transform the model into global space using

**M**on the model's vertices, followed by doing the hit test in global space. This would be inefficient, so you want to go the other way: Transform the ray into model's local space and apply the hit test therein. Hence you have to do

**r**

_{1}' =

**M**

^{-1}*

**r**

_{1}

**r**

_{2}' =

**M**

^{-1}*

**r**

_{2}

onto the both points

**r**

_{1}and

**r**

_{2}defining the ray, and do the hit test with

**r**

_{1}' and

**r**

_{2}'.

**Edited by haegarr, 30 April 2012 - 12:40 AM.**

###
#5
Members - Reputation: **152**

Posted 29 April 2012 - 04:07 AM

That being said, I was unaware a method of calculating all transformations at once, and applying it existed. Shows where my math skills are at. In any case, that looks like it would solve my problem, so I'm going ot give it a shot.

I'll let you know how it goes.

###
#6
Crossbones+ - Reputation: **5582**

Posted 30 April 2012 - 12:56 AM

To be exact: You have toI had considered that the order would need reversed, in addition to negating the transformations. I actually was previously using a working local to global function. However, as you said, trying to convert all the verts in a 13000 poly model uses absurd amounts of processing power, made worse by my method of calculation. I had reversed the order, and inverted the calculations. Transformation and scale worked out fine, only rotation was behaving strangly.

That being said, I was unaware a method of calculating all transformations at once, and applying it existed. Shows where my math skills are at. In any case, that looks like it would solve my problem, so I'm going ot give it a shot.

*invert*the transformation. Negation is the inversion for addition, but we have a multiplication here. Due to the fact that we deal with matrix math (where an equivalent of a division as the inverse of a normal scalar multiplication isn't defined) we speak of the matrix inverse. I mention this because

*negation*is also a defined matrix operation but it will not result in what you want here.

W.r.t. the underlying math a rotation isn't different from a translation or even scaling. If you have considered the reverse order (notice please that this is also necessary within the 3 particular rotations if you deal with Euler angles) and inversions, then you probably just had a implementation bug. However, using the composed transformation makes things easier and will probably erase the bug anyway.

**Edited by haegarr, 30 April 2012 - 12:56 AM.**

###
#7
Members - Reputation: **152**

Posted 30 April 2012 - 10:20 AM

http://msdn.microsoft.com/en-us/library/ms536397%28v=vs.85%29.aspx

The page is about microsoft GDI, but it explains the math pretty well, at least imho.

###
#9
Members - Reputation: **152**

Posted 04 May 2012 - 10:39 PM

Is there a different set of rotation matrices for this, or simply a different way to apply them?

Please let me know if I am not being clear enough.

EDIT:

Never mind, aswered my own question. I had the rotations correct, but I failed to apply them in the correct order to match my glRotate calls. All is well now.

**Edited by gbMike, 05 May 2012 - 01:19 AM.**