Rotate an object in 3d

I would like to rotate an object by ax around its x axis, ay around its y axis, and az around its z axis, in this order. However if i apply this transforms in this order with the rotate functions below, i get a different behavior: the object rotates around z axis as desired, but it rotates around the screen x and y axis (not around its own x and y axis). Does anyone know how i should proceed to get the control i want?

    rotate(self.ax,1,0,0)
    rotate(self.ay,0,1,0)
    rotate(self.az,0,0,1)

Try reversing the order. A transformation says “Apply this transformation to all that follows”.

(Why do you need or want three rotations?)

Try `translate(self.x, self.y)‘

.@Jordan ?? Won’t help, i want to rotate the object, not translate.

@Andrew_Stacey reversing the order simply shifts the problem: now the rotation around x axis is correct, but the rotation around y and z axis are wrong. My goal is to built a workbench with clear control of a 3d object rendering, because i ran into some difficulties in my sphere project, and i cannot find out the bug cause, because i loosely implemented object position control. With this workbench i’ll be completly aware of what i am doing (angles around which axis, sign of the rotation, …) and i will be able to inspect my objects on every side by rotating them.

Then you are not being clear about what you want. What I said does exactly what you asked for: “rotate an object by ax around its x axis, ay around its y axis, and az around its z axis, in this order

If what happens is not what you expected to happen then you haven’t specified the problem correctly.

Ok, i found how to do it. You’ll get it in my next code post. The principle is to 1/ Remember the position of the object via a matrix self.m, 2/ apply rotations by rotating this matrix, 3/ find the x,y and z axis current directions (around which i want to apply rotations) in the rows of modelMatrix(). 4/set the object position by modelMatrix(self.m). This part of the code below. Thanks for your help Andrew.

-- m0 = self.m

function ObjectControl:rotX(m0,angle)
    local m = modelMatrix()
    local xx,xy,xz = m[1],m[2],m[3]
    self.m = m0:rotate(angle,xx,xy,xz)
end
function ObjectControl:rotY(m0,angle)
    local m = modelMatrix()
    local yx,yy,yz = m[5],m[6],m[7]
    self.m = m0:rotate(angle,yx,yy,yz)
end
function ObjectControl:rotZ(m0,angle)
    local m = modelMatrix()
    local zx,zy,zz = m[9],m[10],m[11]
    self.m = m0:rotate(angle,zx,zy,zz)
end

function ObjectControl:move()
    modelMatrix(self.m)
end

This does not look right - or I really haven’t gotten what you are trying to do. I’d have to see the whole code to understand what’s going on.

What this looks like is that you are modifying the model matrix by a rotation about where the axis will end up. Which is the wrong way around. You should modify the model matrix by a rotation about where the axis is now.

If I do:

m = matrix() -- identity matrix
m = m:rotate(45,1,0,0) -- these are just to jumble up the matrix a little
m = m:rotate(45,0,1,0)
m = m:rotate(45,0,0,1)
m = m:scale(.5,1,2) -- and make sure it's not orthogonal any more

m = m:rotate(xa,m[1],m[2],m[3])
modelMatrix(m)
cube:draw()

where cube draws the unit cube and xa is some parameter I can vary, then this rotates the cube about some seemingly random axis (not actually random, but may as well be).

However, if I put m = m:rotate(xa,1,0,0) then the cube rotates about its natural x-axis.

Now where things get a bit confusing is if I do something like:

m = m:rotate(xa,1,0,0)
m = m:rotate(ya,0,1,0)
m = m:rotate(za,0,0,1)

because this rotates first about the z axis, then the y axis, and finally the x axis. If I vary xa, then ya, then za then it appears as though the cube rotates first about its natural x-axis, then about its natural y-axis, and finally about its natural z-axis. So this looks right. Until, that is, I go back and vary xa again. Now it looks as though it is no longer rotating about its natural x-axis but about the world’s x-axis. But that’s because I’m still applying the transformations in the same order. What I probably wanted to do was something more complicated which involves remembering the order in which I specify the operations at runtime not at start-up. So if I vary xa, then ya, and then vary xa again, I want it to apply the second variation of xa taking into account the ya rotation. The code snippet above can’t cope with that: it does all the xa’s then all the ya’s, and then all the za’s (actually, it does them in the opposite order … but that’s not the point).

So as you do, you need to keep track of the model matrix and accumulate the transformations. But each new transformation is with respect to the natural coordinate and so doesn’t need to know about the accumulated transformation up to that point.

Here’s some code that does that (there are better ways):

in setup:

m = matrix()
iparameter(xa,-180,180,0)
iparameter(ya,-180,180,0)
iparameter(za,-180,180,0)
ix,iy,iz = 0,0,0

in draw:

if xa =~ ix then
    m = m:rotate(xa - ix,1,0,0)
    ix = xa
end
if ya =~ iy then
    m = m:rotate(ya - iy,0,1,0)
    iy = ya
end
if za =~ iz then
    m = m:rotate(za - iz,0,0,1)
    iz = za
end
modelMatrix(m)
cube:draw()

This will always rotate the object about its natural axes.

Its correct what @jvm38 does in his last post. his problem is that the rotation behaves different depending on the current rotation. this also leads to the gimbal lock → when you rotate about 90 degrees in one angle, then only one of the two others can be rotated, the others are locked. this is called the gimbal lock.

You can find a comment about this here:

http://www.twolivesleft.com/Codea/Talk/discussion/comment/14324#Comment_14324

and also a link to some 3d sample in that post with the same solution: saving a rotation matrix and only adding up the delta rotations.

Hello. I think my code is doing what i wanted. You can check it in http://db.tt/CxRAsVlH its the project ‘‘copy’’ in a txt file. Paste it in a new project it should work. I didnt intend to post the code as it is now, but have no time to clean it now. Here is what the program does:
http://www.youtube.com/watch?v=biPnOvtU04s
Don’t believe the 23 fps, it is really 60fps (i have modified Nat’s controllers so that they work in real time and many of them can be put in the screen. I’ll explain that in the ‘‘controllers’’ post)

That’s why you should always post code. Your code isn’t doing what you think it is doing. It’s doing what I said it should do (well, there’s a coding improvement on what I posted above - this is one of the “better ways” I hinted at).

How do I know? I changed your code. Here’s what I changed:

function ObjectControl:rotX(m0,angle)
    local m = modelMatrix()
    local xx,xy,xz = m[1],m[2],m[3]
    self.m = m0:rotate(angle,1,0,0)
end
function ObjectControl:rotY(m0,angle)
    local m = modelMatrix()
    local yx,yy,yz = m[5],m[6],m[7]
    self.m = m0:rotate(angle,0,1,0)
end
function ObjectControl:rotZ(m0,angle)
    local m = modelMatrix()
    local zx,zy,zz = m[9],m[10],m[11]
    self.m = m0:rotate(angle,0,0,1)
end

What happened? Absolutely nothing changed. Your program ran the same after these changes as it had before. I also put matrixMat = m in one of the functions to see that it was the identity matrix.

The reason being that when this is called, modelMatrix() returns the identity matrix. They are called in the touched function which is handled after the draw function. By the end of the draw function you’ve set modelMatrix() back to the identity matrix. If you want to have the controlling matrix in force, you need to put controls:move() at the very end of the draw function. I recommend that you do that as you’ll see that I’m right: the rotation is very strange indeed.

Thanks for the advice. I checked that quicky: you are right. Then the programming is simpler, so i like it. I still don’t understand everything there, but the bottom line is that i know how to control my object orientation: with a self.m matrix, to which apply the std rotation and translation.That will be enough for today (time to go to bed…). Regards.

I’m pleased you’ve got a way to do it now. And I’m sorry if I came across as a bit peeved in the above.