So... Basically, I'm trying to create a limb system. When an object sits on top of another object, it moves, and rotates with it. My problem is the rotation.
You have an object in another object's local coordinates. So, this object in another object's local coordinates (called obj1), has position and rotation. Then, you transform the "parent" object (called obj2) to world space. Now, the transformation (dealing with row vectors) is LocalMatrix*ParentMatrix. That's all well and good, but then there's the transition from the resulting matrix, to euler angles. That's what's getting me.
So, you have a 3x3 matrix, M, and that's equal to:
[M11 M12 M13]
[M21 M22 M23]
[M31 M32 M33]
So you have 9 variables that you know the values of. That matrix, is equal to this matrix transposed:

(it's transposed because that is in column major format, where I'm working in row-major - because DBPro uses row-major.)
So, you need to find x,y, and z of that rotation matrix, given all the values of matrix M. Converting a rotation matrix to euler angles.
This is my function that converts a matrix to euler angles:
(it uses the algorithm described in
this paper)
function rotMatrixToEulerAng(C as integer)
C13#=M4Element(C,1,3)
C31#=M4Element(C,3,1)
C23#=M4Element(C,2,3)
C33#=M4Element(C,3,3)
C12#=M4Element(C,1,2)
C11#=M4Element(C,1,1)
if absolut(C13#)<>1
ret_y=-asin(C13#)
ret_x=atanfull(C23#/cos(ret_y),C33#/cos(ret_y))
ret_z=atanfull(C12#/cos(ret_y),C11#/cos(ret_y))
else
ret_z=0
if C13#=-1
ret_y=90
ret_x=atanfull(C21#,C31#)
else
ret_y=270
ret_x=atanfull(-C21#,-C31#)
endif
endif
endfunction
function absolut(n as float)
if n>0 then exitfunction n
n=-n
endfunction n
//gets an element of a 4x4 matrix.
function M4Element(m4,row,column)
n#=get matrix4 element(m4,column-1+(row-1)*4)
endfunction n#
However... there is one this glaring at me in this code - the variable y can only vary by 180 degrees! A circle is 360 degrees, 180 isn't enough. However, it seems like everywhere I look, papers about getting euler angles from rotation matrices are content with saying y=-asin(x). (like
http://www.geometrictools.com and
this paper)
So... You obviously have to overcome this problem (this way, or another way) if you want to make a parent/child 3d positioning system, like the one in DBPro. How?!
This code demonstrates how the function doesn't work.
#constant worldmat 1 `world matrix; A world matrix is responsible for transformingb an object's coordinates
`(what you would see if you placed the model at the origin) to the world's coordinates (what you would see
`after rotating/scaling/translating the object)
#constant xrotmat 2 `A matrix responsible for the rotation of a particular object about the x axis.
#constant yrotmat 3 `A matrix responsible for the rotation of a particular object about the y axis.
#constant zrotmat 4 `A matrix responsible for the rotation of a particular object about the z axis.
#constant transmat 5 `A matrix responsible for translating the object in the world.
#constant scalemat 6 `a matrix responsible for scaling the object in the world.
#constant tempmatrix1 7 `temporary matrices for storing stuff
#constant tempmatrix2 8
#constant tempmatrix3 9
#constant tempVector 10 `a value to hold a vector, used in transformations.
//three return variables, used for returning a vector from a function.
global ret_x as float
global ret_y as float
global ret_z as float
set normalization on //when you scale an object, that messes up the lighting.
//"set normalization on" fixes the lighting. never scale an object without using set normalization on!
make object cube 1,1 //the main object to be shown
make object cube 2,.9999 //the object last frame
ghost object on 2
color object 1,rgb(0,255,0)
color object 2,rgb(255,0,0)
make object cube 3,.5 //the object position relative to the first object.
color object 3,rgb(0,0,255)
position object 3,0,2,0
current_selected as integer
current_selected=0
change as float
position camera 0,5,-5
point camera 0,0,0
init_matrices()
sync on
sync rate 100
sync
sync
do
rotate object 2,object angle x(1),object angle y(1),object angle z(1)
position object 2,object position x(1),object position y(1),object position z(1)
scale object 2,object scale x(1),object scale y(1),object scale z(1)
change=(upkey()-downkey())*1.0/screen fps()
if keystate(2) then current_selected=0
if keystate(3) then current_selected=1
if keystate(4) then current_selected=2
if keystate(5) then current_selected=3
if keystate(6) then current_selected=4
if keystate(7) then current_selected=5
if keystate(8) then current_selected=6
if keystate(9) then current_selected=7
if keystate(10) then current_selected=8
if current_selected=0
text 0,0,"Selected: X Position"
position object 1,object position x(1)+change,object position y(1),object position z(1)
text 0,16,"Value: "+str$(object position x(1))
endif
if current_selected=1
text 0,0,"Selected: Y Position"
position object 1,object position x(1),object position y(1)+change,object position z(1)
text 0,16,"Value: "+str$(object position y(1))
endif
if current_selected=2
text 0,0,"Selected: Z Position"
position object 1,object position x(1),object position y(1),object position z(1)+change
text 0,16,"Value: "+str$(object position z(1))
endif
if current_selected=3
text 0,0,"Selected: X Rotation"
rotate object 1,object angle x(1)+change*90.0,object angle y(1),object angle z(1)
text 0,16,"Value: "+str$(object angle x(1))
endif
if current_selected=4
text 0,0,"Selected: Y Rotation"
rotate object 1,object angle x(1),object angle y(1)+change*90.0,object angle z(1)
text 0,16,"Value: "+str$(object angle y(1))
endif
if current_selected=5
text 0,0,"Selected: Z Rotation"
rotate object 1,object angle x(1),object angle y(1),object angle z(1)+change*90.0
text 0,16,"Value: "+str$(object angle z(1))
endif
if current_selected=6
text 0,0,"Selected: X Scale"
scale object 1,object scale x(1)+change*20.0,object scale y(1),object scale z(1)
text 0,16,"Value: "+str$(object scale x(1))
endif
if current_selected=7
text 0,0,"Selected: Y Scale"
scale object 1,object scale x(1),object scale y(1)+change*20.0,object scale z(1)
text 0,16,"Value: "+str$(object scale y(1))
endif
if current_selected=8
text 0,0,"Selected: Z Scale"
scale object 1,object scale x(1),object scale y(1),object scale z(1)+change*20.0
text 0,16,"Value: "+str$(object scale z(1))
endif
rotate object 3,object angle x(3)+mousemovey(),object angle y(3)+mousemovex(),0
//object 3 was position relative to object 2 LAST frame, but THIS
//frame, we want it relative to object 1.
lock_transformObject(2,1,3)
text 0,32,"Object 3 Position: ("+str$(object position x(3),4)+","+str$(object position y(3),4)+","+str$(object position z(3),4)+")"
text 0,48,"Object 3 Scale: ("+str$(object scale x(3),4)+","+str$(object scale y(3),4)+","+str$(object scale z(3),4)+")"
text 0,64,"Object 3 Rotation: ("+str$(object angle x(3),4)+","+str$(object angle y(3),4)+","+str$(object angle z(3),4)+")"
text 0,80,"Screen FPS: "+str$(screen fps())
sync
loop
cleanup_matrices()
end
function init_matrices()
//the matrix4 commands return a variable. I *think* that this variable is completely useless.
null=make matrix4(worldmat)
null=make matrix4(xrotmat)
null=make matrix4(zrotmat)
null=make matrix4(yrotmat)
null=make matrix4(transmat)
null=make matrix4(scalemat)
null=make matrix4(tempmatrix1)
null=make matrix4(tempmatrix2)
null=make matrix4(tempmatrix3)
null=make vector4(tempVector)
endfunction
function cleanup_matrices()
null=delete matrix4(worldmat)
null=delete matrix4(xrotmat)
null=delete matrix4(yrotmat)
null=delete matrix4(zrotmat)
null=delete matrix4(transmat)
null=delete matrix4(scalemat)
null=delete matrix4(tempmatrix1)
null=delete matrix4(tempmatrix2)
null=delete matrix4(tempmatrix3)
null=delete vector4(tempVector)
endfunction
function lock_transformObject(obj_from,obj_to,obj)
//Take object obj_from. You want to take the position and rotation of another object, "obj",
//and change that so that it's relative to obj_to. This function does that - it takes object obj's
//position relative to obj_from, and positions/rotates it so it's in the same position relative to
//obj_to.
//create some nice named variables so that the code will be clearer.
objFromInvWM=tempmatrix1 //object "obj_from"'s inverse world matrix. (short for "Object From's Inverse World Matrix")
objToWM=tempmatrix2 //object "obj_to"'s world matrix. (short for "Object To's World Matrix")
objWM=tempmatrix3 //object "obj"'s world matrix.
getObjectsWorldMatrix(obj) //get obj's world matrix,
copy matrix4 worldmat,objWM //store it in objWM
getObjectsInvWorldMatrix(obj_from) //get obj_from's inverse world matrix
copy matrix4 worldmat,objFromInvWM //store it in objFromInvWM
getObjectsWorldMatrix(obj_to) //get obj_to's world matrix
copy matrix4 worldmat,objToWM //store it in objFromInvWM
//We want objWM*ObFromInvWM*objToWM. This will give us our transformation, and when we're done,
//we want to extract translation, rotation, and scaling information.
multiply matrix4 worldmat,objWM,objFromInvWM `worldmat=objWM*objFromInvWM
multiply matrix4 worldmat,worldmat,objToWM `worldmat=worldmat*objToWM=objWM*objFromInvWM*objToWM
//now world mat is the desired transformation matrix.
scalex as float
scaley as float
scalez as float
//If you do the math, where you're using row vectors and a left handed coordinate system,
//you see that each row of the 3x3 transformation matrix (excluding the affine part)
//is multiplied by the corresponding scale value. Since the length of each row of the rotation
//matrix is one, the length of each row of this Scale and Rotation matrix, is the scaling value.
//since DBPro uses scaling values as percentages, just multiply this by 100.
scalex=sqrt(M4Element(worldmat,1,1)*M4Element(worldmat,1,1)+M4Element(worldmat,1,2)*M4Element(worldmat,1,2)+M4Element(worldmat,1,3)*M4Element(worldmat,1,3))*100
scaley=sqrt(M4Element(worldmat,2,1)*M4Element(worldmat,2,1)+M4Element(worldmat,2,2)*M4Element(worldmat,2,2)+M4Element(worldmat,2,3)*M4Element(worldmat,2,3))*100
scalez=sqrt(M4Element(worldmat,3,1)*M4Element(worldmat,3,1)+M4Element(worldmat,3,2)*M4Element(worldmat,3,2)+M4Element(worldmat,3,3)*M4Element(worldmat,3,3))*100
scale matrix4 scalemat,100.0/scalex,100.0/scaley,100.0/scalez
multiply matrix4 worldmat,scalemat,worldmat //worldmat=Scale^(-1)*worldmat=Scale^(-1)*Scale*Rot*Translation
//=Rot*Translation
//we want to isolate the rotation matrix so we can extract euler angles from it.
//the translation matrix doesn't touch the upper 3x3 part of the matrix - so we don't have to do anything with it.
//However, the scale matrix scales by everything, so we need to mutiply worldmat by the inverse of scale, to get
//worldmat=Rot*Translation.
//Now, we can extract the euler angles from the rotation matrix.
//The bottommost row of the matrix is the translation part, but it doesn't affect the rotation
//part of the matrix (the 3x3 top left values), so we can ignore it. It's still stored in worldmat,
//but that doesn't matter.
rotMatrixToEulerAng(worldmat)
scale object obj,scalex,scaley,scalez
rotate object obj,wrapvalue(ret_x),wrapvalue(ret_y),wrapvalue(ret_z)
position object obj,M4Element(worldmat,4,1),M4Element(worldmat,4,2),M4Element(worldmat,4,3)
endfunction
function transformObjectSpaceToWorldSpace(matrixnum,x as float, y as float, z as float)
`Say you have a vector that - when the object in question is positioned at the origin - lies right on top
`of the object. If you rotate, scale, and position the object, where does it go now?! This function solves
`that problem.
`Code example:
remstart
make object cube 1,1
position object 1,rnd(100),rnd(100),rnd(100)
rotate object 1,rnd(360),rnd(360),rnd(360)
getObjectsWorldMatrix(1)
transformObjectSpaceToWorldSpace(worldmat,0,1,0)
make object sphere 2,.5
position object 2,ret_x,ret_y,ret_z
remend
set vector4 tempVector,x,y,z,1
transform vector4 tempVector,tempVector,matrixnum
ret_x=x vector4(tempVector)
ret_y=y vector4(tempVector)
ret_z=z vector4(tempVector)
endfunction
function getObjectsWorldMatrix(object_number) //takes an object's position, rotation, and scaling,
`turns that data into a matrix, and then stores that matrix in matrix "worldmat".
rotate x matrix4 xrotmat,wrapvalue(object angle x(object_number))*.01745329 `calculate x, y, and z rotation matrices.
rotate y matrix4 yrotmat,wrapvalue(object angle y(object_number))*.01745329 `we need to convert angles into radians here.
rotate z matrix4 zrotmat,wrapvalue(object angle z(object_number))*.01745329
`calculate the translation matrix
translate matrix4 transmat,object position x(object_number),object position y(object_number),object position z(object_number)
`calculate the object's scaling matrix.
scale matrix4 scalemat,object scale x(object_number)/100.0,object scale y(object_number)/100.0,object scale z(object_number)/100.0
multiply matrix4 xrotmat,xrotmat,yrotmat `X'=X*Y
multiply matrix4 xrotmat,xrotmat,zrotmat `X'=X'*Z=X*Y*Z
multiply matrix4 xrotmat,scalemat,xrotmat `X'=Scale*X'=Scale*X*Y*Z
multiply matrix4 worldmat,xrotmat,transmat
`Object's World matrix=Scale*Xrot*Yrot*Zrot*Translation
`in other words:
`object's position = vertex position*Scale*Xrot*Yrot*Zrot*Translation
`in other other words:
`object = for each vertex, first scale the vertex from the origin on each axis. Then rotate it about the x axis,
`then rotate it about the y axis, then rotate it about the z axis, and then translate each vertex.
endfunction
function getObjectsInvWorldMatrix(object_number)
//this function creates the inverse of an object's world matrix.
//There's a property of matrices: A matrix multiplied by its inverse gives the identity matrix.
//this is like saying a number times its inverse is equal to one. A vector multiplied by the
//identity matrix leaves the vector unchanged. So, if you have a point transformed by a matrix,
//T (vector*T), then if you multiply by the inverse matrix of T, you get the vector.
//(vector*T*T^(-1)=vector*I=vector)
//let A' denote the inverse of matrix A, from this point on in this function.
//(normally A' denotes the transpose of matrix A. DON'T USE THIS NOTATION! IM JUST USING IT
//SO THAT I DON'T HAVE TO WRITE OUT "^(-1)" A GAZILLION TIMES. PPL WILL BE CONFUSED IF YOU USE IT
//WITHOUT SAYING IN BIG LETTERS THAT YOU'RE USING YOUR OWN NOTATION!)
//(A*B*C*...*N)'=N'*...*C'*B'*A'
//Where X(x) is a rotation of x degrees on the X axis, Y(y) is y degrees on the Y axis, etc.
//R=X(x)*Y(y)*Z(z)
//R'=Z(-z)*Y(-y)*X(-x)
rotate x matrix4 xrotmat,wrapvalue(-object angle x(object_number))*.01745329
rotate y matrix4 yrotmat,wrapvalue(-object angle y(object_number))*.01745329
rotate z matrix4 zrotmat,wrapvalue(-object angle z(object_number))*.01745329
//where T(x,y,z) is a matrix translating x units on the x axis, y units on the y axis, etc.
//T(x,y,z)'=T(-x,-y,-z)
//this makes sense, because if you translate something, and then translate it back the same amount,
//you get zero overall translation.
translate matrix4 transmat,-object position x(object_number),-object position y(object_number),-object position z(object_number)
//where S(x,y,z) is a matrix scaling x times on the x axis, y times on the y axis, etc.
//S(x,y,z)'=S(1/x,1/y,1/z)
//this makes sense, because scaling multiplies a value. If you multiply something by its inverse, you
//get 1. Something scaled by "1", isn't really scaling anything at all.
scale matrix4 scalemat,100.0/object scale x(object_number),100.0/object scale y(object_number),100.0/object scale z(object_number)
//NOTE: you cannot use this command if you
//scale an object to 0%. Scaling an object to 0% can be used to make fake shadows, or super flat objects.
//Now, last time we had:
//WorldMatrix=Scale*Xrot*Yrot*Zrot*Translation
//So, this time we have:
//WorldMatrix'=Translation'*Zrot'*Yrot'*Xrot'*Scale'
//Where A*A'=I, and A*I=A, you can see that...
//WorldMatrix*WorldMatrix'=I=Scale*Xrot*Yrot*Zrot*Translation*Translation'*Zrot'*Yrot'*Xrot'*Scale'
multiply matrix4 transmat,transmat,zrotmat //translation'*zrot'
multiply matrix4 transmat,transmat,yrotmat //translation'*zrot'*yrot'
multiply matrix4 transmat,transmat,xrotmat //translation'*zrot'*yrot'*xrot'
multiply matrix4 worldmat,transmat,scalemat //translation'*zrot'*yrot'*xrot'*scalerot'
endfunction
//uses the algorithm described on this website:
//http://www.gregslabaugh.name/publications/euler.pdf
//I've mirrored it on my site at
//http://www.neurofuzzydev.com/downloads/Papers/rotMatrixToEulerAngles.pdf
function rotMatrixToEulerAng(C as integer)
C13#=M4Element(C,1,3)
C31#=M4Element(C,3,1)
C23#=M4Element(C,2,3)
C33#=M4Element(C,3,3)
C12#=M4Element(C,1,2)
C11#=M4Element(C,1,1)
if absolut(C13#)<>1
ret_y=-asin(C13#)
ret_x=atanfull(C23#/cos(ret_y),C33#/cos(ret_y))
ret_z=atanfull(C12#/cos(ret_y),C11#/cos(ret_y))
else
ret_z=0
if C13#=-1
ret_y=90
ret_x=atanfull(C21#,C31#)
else
ret_y=270
ret_x=atanfull(-C21#,-C31#)
endif
endif
endfunction
function absolut(n as float)
if n>0 then exitfunction n
n=-n
endfunction n
//gets an element of a 4x4 matrix.
function M4Element(m4,row,column)
n#=get matrix4 element(m4,column-1+(row-1)*4)
endfunction n#
function printM4(m4, x,y,decimals) //prints a matrix: 74 units tall.
line x,y,x+10,y
line x,y,x,y+74
line x,y+74,x+10,y+74
dim s(3) as String
s(0)=""
s(1)=""
s(2)=""
s(3)=""
tw as integer
for y2=1 to 4
for x2=1 to 4
s(y2-1)=s(y2-1)+str$(M4Element(m4,y2,x2),decimals)
if x2<3 then s(y2-1)=s(y2-1)+" "
next x2
n=text width(s(y2-1))
if tw<n then tw=n
next y2
tw=tw+10
line x+tw,y,x+tw-10,y
line x+tw,y,x+tw,y+74
line x+tw,y+74,x+tw-10,y+74
for l=0 to 3
text x+5,y+5+16*l,s(l)
next l
endfunction tw
[edit]
aaaand this is obviously geared towards DBPro at the moment, but I do believe I need to know this for OpenGL/Direct3D/other stuff.