/* attempt to deal with prototype, bootstrap, jquery conflicts */ /* for dropdown menus */

$15 ### Image of the Day Submit IOTD | Top Screenshots ### The latest, straight to your Inbox. Subscribe to GameDev.net's newsletters to receive the latest updates and exclusive content. Sign up now ## Matrix rotation order in opengl 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. 4 replies to this topic ### #1Dahkex Members Posted 27 April 2011 - 03:11 PM I know this is ruby, but the calls are straightforward opengl.  require File.expand_path('../boot', __FILE__) class Window < Gosu::Window if(!(defined? PIOVER180)) PIOVER180 = 0.0174532925 end attr_accessor :yrot, :walkbias, :walkbiasangle, :lookupdown, :zworld, :xpos, :zpos def initialize super(800,600,false) @yrot = 0.0 @walkbias = 0.0 @walkbiasangle = 0.0 @lookupdown = 0.0 @zworld = 0.0 @sector = TagSECTOR.new @xpos = 0.0 @zpos = 0.0 setupworld end def setupworld @file = File.new('C:\MI\myt.txt', 'r') numtriangles = 0 readstr = readstri self.caption = readstr if(readstr) readstr.match(/([0-9])/) numtriangles =$1
@sector.numtriangles = numtriangles.to_i

(0...(numtriangles.to_i)).each{|a|
@sector.triangles << TagTRIANGLE.new

(0...3).each{|b|

@sector.triangles[a].vertex[b].x = vertarr[0]
@sector.triangles[a].vertex[b].y = vertarr[1]
@sector.triangles[a].vertex[b].z = vertarr[2]
}
}
end

@file.close
end

@file.gets.chomp
end

def update
if button_down? Gosu::Button::KbRight then
@yrot -= 1.5
end
if button_down? Gosu::Button::KbLeft then
@yrot += 1.5
end
if button_down? Gosu::Button::KbUp
@xpos += Math.sin(@yrot * (Math::PI/180.0)) * 0.05
@zpos += Math.cos(@yrot * (Math::PI/180.0)) * 0.05
end
if button_down? Gosu::Button::KbDown
@xpos -= Math.sin(@yrot * (Math::PI/180.0)) * 0.05
@zpos -= Math.cos(@yrot * (Math::PI/180.0)) * 0.05
end
if button_down? Gosu::Button::KbLeftControl
@lookupdown -= 1.0
end
if button_down? Gosu::Button::KbLeftAlt
@lookupdown += 1.0
end
if @walkbiasangle < 1.0
@walkbiasangle = 359.0
else
@walkbiasangle -= 1.0
end

@walkbias = Math.sin(walkbiasangle * (Math::PI/180.0)).to_f
end

def draw

x_m, y_m, z_m, xtrans,ztrans,ytrans,sceneroty = 0.0

gl do
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)

glMatrixMode(GL_PROJECTION) # see lesson01

gluPerspective(45.0, width / height, 0.1, 100.0) # see lesson01

glMatrixMode(GL_MODELVIEW) # see lesson01

sceneroty = 360 - @yrot

glRotatef(@lookupdown, 1,0,0)
glRotatef(sceneroty, 0,1,0)

glTranslatef(0, 0, -10) # see lesson01
glTranslatef(@xpos,0,@zpos)
glTranslatef(0,@walkbias / 20.0,0)
self.caption = @xpos
(0...@sector.numtriangles).each {|num|

glBegin(GL_TRIANGLES)
x_m = @sector.triangles[num].vertex[0].x
y_m = @sector.triangles[num].vertex[0].y
z_m = @sector.triangles[num].vertex[0].z
glColor3f(1,0,0)
glVertex3f(2.0, 0.0, 0.0)

x_m = @sector.triangles[num].vertex[1].x
y_m = @sector.triangles[num].vertex[1].y
z_m = @sector.triangles[num].vertex[1].z
glColor3f(0,1,0)
glVertex3f(4.0, 4.0, 0.0)

x_m = @sector.triangles[num].vertex[2].x
y_m = @sector.triangles[num].vertex[2].y
z_m = @sector.triangles[num].vertex[2].z
glColor3f(0,0,1)
glVertex3f(6.0,0.0,0.0)
glEnd
}
end
end

def button_down(id)
if id == Gosu::KbEscape
close
end
end
end

class TagSECTOR
attr_accessor :numtriangles, :triangles

def initialize
@numtriangles = 0
@triangles ||= []
end
end

class TagTRIANGLE
attr_accessor :vertex

def initialize
@vertex = []

(0...3).each {|x|
@vertex << Vertex.new
}
end
end

class Vertex
attr_accessor :x,:y,:z

def initialize
@x = 0.0
@y = 0.0
@z = 0.0
end
end


If I switch the order of rotation like so (so the rotation of the x-axis goes after the y-axis):



glRotatef(sceneroty, 0,1,0)
glRotatef(@lookupdown, 1,0,0)


then it's no longer looks too great. Instead, if I go rotate around the x-axis, the triangle drawn looks like it's being pulled to one direction or the other, it's almost as if it's on a path of a slanted ring. When I visualize this, they do the exact same thing and are in the exact same positions.

Does the order matter here for some reason or is the fact that my sceneroty's default value being 360.0 degrees messing things up?

### #2Anthony Serrano  Members

Posted 27 April 2011 - 06:04 PM

Order matters.

Matrix multiplication is non-commutative, i.e., when dealing with matrices, A * B is not the same as B * A.

### #3Dahkex  Members

Posted 27 April 2011 - 07:18 PM

Order matters.

Matrix multiplication is non-commutative, i.e., when dealing with matrices, A * B is not the same as B * A.

So how would I know which one goes first in my example? Visually, they should be in the same spot.

### #4Brother Bob  Moderators

Posted 28 April 2011 - 01:30 AM

You are simply visualizing it wrong. For example, the second rotation is affected by the first rotation. Transformations happens, when read from top to bottom, in the objects local coordinate system. So if you first rotate about the Y-axis, as in your example, the object's X-axis is rotated, so the axis of rotation for the second rotation is also rotated. In this case, rotations are not about the global axes, but about the local axes, and the local axes transforms along with the object.

Say your first rotation is 90 degrees around the Y-axis (sceneroty=90). Since this is your first transform, the object's local Y-axis is the same as the global Y-axis. The local X-axis, however, is also rotated, and is now equal to the global Z-axis, so the second rotation is about the local X-axis, or equivalently about the global Z-axis. This is probably where your visualization is going wrong. If you track the local axes and visualize the rotations around them instead, you will see that order does matter.

Which order is correct is entirely dependent on what you want to achieve. Only you can answer that.

### #5haegarr  Members

Posted 28 April 2011 - 02:43 AM

You've have to consider several things when dealing with transformations. First, OpenGL uses column vectors, what means that a matrix-vector product looks like
M * v
where M is a transformation matrix (e.g. build on one of OpenGL's matrix stacks) and v a vertex position. Notice that v is on the right of M, what is a necessity for column vectors. The transformation matrix will typically be composed. Let's say by a rotation R and a translation T like in
T * R * v
In OpenGL, you would have to write
glTranslatef(...);
glRotatef(...);
glVertex3f(...);
to get the transformation above. Notice that the OpenGL commands from top to bottom correspond with the formula terms from left to right!

Next you have to consider that all of the glTranslate, glRotate, glScale, and glMultMatrix commands are multiplicative, i.e. the corresponding matrix is ever multiplied (on the right, you know) of what is already on the active matrix stack. The only possibility to overcome this is to use glLoadIdentity which overwrites the current matrix with the identity matrix (that is a matrix equivalent to the scalar 1, i.e. multiplying a matrix with the identity matrix gives the matrix itself). So in certain situations yo may need to write
glTranslatef(...);
glRotatef(...);
to get in fact something like
I * T * R == T * R

When you're doing
glTranslatef(...);
glRotatef(...);
foreach vertex do glVertex3f(...);
then OpenGL computes the matrix product and applies the composed matrix to each vertex, like so
( T * R ) * v
However, these parentheses are just for clarity, because the matrix product is associative. When you are interpreting what happens to the vertex, it is IMHO better to think about it as
T * ( R * v )
instead: The vertex position is rotated, and the rotated position is translated. Notice please that terms that are closer to the vertex appear to be applied "earlier", and terms that are more to the left are applied on a vertex position that is already transformed by all the matrices to the right of it!

Next, whenever a particular transformation is applied, it is done relative to a reference co-ordinate system. This system is, at the moment of application, the global co-ordionate system. For example, assume 2 consecutive rotations
Rx * Ry * v
This means that the original vertex position is rotated around the cardinal y axis. The y axis is identical to the global y axis. The so rotated vertex will be rotated around the cardinal x axis. This x axis is again identical to those of the global co-ordinate system. Notice that this is not the same x axis as it was before Ry, because Ry has changed the space.

If you desire to rotate around local axes instead, you have to transform into a space where the local axis is coincident with the global one, apply the transformation, and undo the former space transformation. For our example of 2 consecutive rotations, the first rotation Ry is applied in a space where local and global co-ordinates are the same, so the first step is still
Ry * v
Next we would rotate around the global x axis, but we want to rotate around the local x axis. So we need to map the local x axis onto the global one; this means to "undo" the former rotation
Ry-1 * Ry * v
then to apply the x rotation
Rx * Ry-1 * Ry * v
and then to "redo" the former rotation
Ry * Rx * Ry-1 * Ry * v
When simplifying this, the fact that Ry-1 * Ry == I (similar to s / s == 1 for a scalar s != 0) gives us
Ry * Rx * v
Err, well, exactly changing the order of rotations gives us the effect of using local axes for these rotations!

I known that this is damn much stuff to think about. But it is both sufficient and necessary to determine the correct order of transformation matrices.

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.