Thanks for this.

Have wondered about this for ages, and trying it out has been on my to do list for as long. I feared it was complicated, but actually it's fairly simple and understandable, the DLL is very small, very easy to install (you just copy and paste the file into a folder) and it does what it does very well.

Would recommend it to anyone. Makes it easy to make, say, a rolling marble. Also, I have been for a while using a method to extract the rotation matrix from objects rotated with free flight commands, by using 3 dummy limbs per object. Pretty wasteful. With this you can do it easily without the limbs!

I did some speed tests, and it's not quite as quick at extracting Euler angles from matrices as doing it manually in DBP (when i'm passing it the matrix values, rather than using ezR for all my rotation operations), but obviously it saves on a lot of coding, and can do more than that.

Speed test code:

Rem Project: test ezrotate - compare with own matrix euler conversion
Rem Created: 11/03/2010 23:31:17
Rem ***** Main Source File *****
`have own code to get from rotation matrix to euler angles.
`ezrotate can do the same.
`want to see which of the two is fastest.
`to start basically rip quaternion interpolation project....
Rem Project: quaternion interpolation
Rem Created: 09/03/2010 02:03:26
Rem ***** Main Source File *****
dim Euler#(3)
dim quats(100,3) as float
dim matrices(100,3,3) as float
sync on:sync rate 100
`make an object that i can rotate
`make object cube 1,1
make object box 1,0.2,0.5,1.0
autocam off
p#=0.0
`create random rotation quaternion
rand_quat_b(1)
do
inc p#,0.005 `power..
if spacekey() then p#=0.0
if returnkey():rand_quat_b(1):p#=0.0:endif
text 0,0,str$(screen fps())
text 0,10,str$(p#,3)
`test
`theta#=atanfull(r#,quats(q,0))
`text 0,20,str$(atanfull(0.1,1.0))
_quaternion_power(2,1,p#) `quat 2 = quat 1 to the power p#
_convert_quat_to_matrix(2,0) `convert quat 2 to matrix 0
`change a bit- put in alternate matrix to euler things
if leftkey() then ezr=1
if rightkey() then ezr=0
if ezr
text 0,30,"ezrotate (rightkey for other)"
`EZro_SetMatRowX MatrixXX#, MatrixXY#, MatrixXZ#
`EZro_SetMatRowY MatrixYX#, MatrixYY#, MatrixYZ#
`EZro_SetMatRowZ MatrixZX#, MatrixZY#, MatrixZZ#
t=0
EZro_SetMatRowX matrices(t, 1, 1),matrices(t, 2, 1),matrices(t, 3, 1)
EZro_SetMatRowY matrices(t, 1, 2),matrices(t, 2, 2),matrices(t, 3, 2)
EZro_SetMatRowZ matrices(t, 1, 3),matrices(t, 2, 3),matrices(t, 3, 3)
`EZro_Set EulerX#, EulerY#, EulerZ# `Sets up EZrotate with Euler X, Y, Z angles
Rotate object 1,EZro_GetX(),EZro_GetY(),EZro_GetZ()
else
text 0,30,"own code (leftkey for other)"
`from hypersphere proj.
`should swap mat4t for rotation matrix.
t=0
Euler#(1) = atanfull(matrices(t, 2, 1),matrices(t, 1, 1))
Euler#(2) = atanfull(matrices(t, 3, 2),matrices(t, 3, 3))
temp#=sqrt(matrices(t, 3, 2)*matrices(t, 3, 2) + matrices(t, 3, 3)*matrices(t, 3, 3) )
Euler#(3) = atanfull(-matrices(t, 3, 1),temp#)
rotate object 1, Euler#(2),Euler#(3),Euler#(1)
endif
`speed test
a=timer()
for n=1 to 30000
Rotate object 1,0.0,0.0,0.0
next n
b=timer()
t0=a-b
a=timer()
for n=1 to 30000
EZro_SetMatRowX matrices(t, 1, 1),matrices(t, 2, 1),matrices(t, 3, 1)
EZro_SetMatRowY matrices(t, 1, 2),matrices(t, 2, 2),matrices(t, 3, 2)
EZro_SetMatRowZ matrices(t, 1, 3),matrices(t, 2, 3),matrices(t, 3, 3)
`EZro_Set EulerX#, EulerY#, EulerZ# `Sets up EZrotate with Euler X, Y, Z angles
Rotate object 1,EZro_GetX(),EZro_GetY(),EZro_GetZ()
next n
b=timer()
t1=a-b
a=timer()
for n=1 to 30000
`from hypersphere proj.
`should swap mat4t for rotation matrix.
t=0
Euler#(1) = atanfull(matrices(t, 2, 1),matrices(t, 1, 1))
Euler#(2) = atanfull(matrices(t, 3, 2),matrices(t, 3, 3))
temp#=sqrt(matrices(t, 3, 2)*matrices(t, 3, 2) + matrices(t, 3, 3)*matrices(t, 3, 3) )
Euler#(3) = atanfull(-matrices(t, 3, 1),temp#)
rotate object 1, Euler#(2),Euler#(3),Euler#(1)
next n
b=timer()
t2=a-b
`old own code- with ifs
a=timer()
for n=1 to 30000
done=0
`SUBBED IN fw=2:ri=3:up=1:x=3:y=1:z=2
if matrices(t, 3, 1) > 0.99999
` text 0,0,"******"
` text object screen x(n),object screen y(n),"HERE B"
Euler#(2) = atanfull(-matrices(t, 2, 3),matrices(t, 2, 2))
Euler#(3) = -90.0
Euler#(1) = 0.0
done=1
endif
if matrices(t, 3, 1) < -0.99999
` text 0,0,"#######"
` text object screen x(n),object screen y(n),"HERE A editing"
Euler#(2) = atanfull(-matrices(t, 2, 3),matrices(t, 2, 2))
`Euler#(2) = atanfull(matrices(t, 2, 2),0.0-matrices(t, 2, 3))
Euler#(3) = 90.0
Euler#(1) = 0.0
done=1
endif
if done=0
Euler#(1) = atanfull(matrices(t, 2, 1),matrices(t, 1, 1))
Euler#(2) = atanfull(matrices(t, 3, 2),matrices(t, 3, 3))
Euler#(3) = asin(-matrices(t, 3, 1))
endif
rotate object 1, Euler#(2),Euler#(3),Euler#(1)
next n
b=timer()
t3=a-b
text 0,40,str$(t0)
text 0,50,str$(t1)
text 0,60,str$(t2)
text 0,70,str$(t3)
sync
loop
`_quaternion_power(2,1,p#) `quat 2 = quat 1 to the power p#
function _quaternion_power(result,q,power as float)
r#=sqrt( quats(q,1)*quats(q,1) + quats(q,2)*quats(q,2) + quats(q,3)*quats(q,3) )
`w#=quats(q,0) `not really required but use for transparency.
`theta#=atanfull(r#,w#) `not sure what correct order is. - test out atanfull!
theta#=atanfull(r#,quats(q,0))
thetap#=theta#*power
r2#=sin(thetap#)
`w2#=cos(thetap#)
`quats(result,0)=w2#
quats(result,0)=cos(thetap#)
if r#>0.0
f#=r2#/r#
for c=1 to 3
quats(result,c)=quats(q,c)*f#
next f
else
for c=1 to 3
quats(result,c)=0.0
next f
endif
endfunction
`don't need all these funcs for this proj
`also old random quat func superceded.
`from random quaternion generator project
function rand_quat_b(q)
ang1#=0.1*rnd(3599)
ang2#=0.1*rnd(3599)
unif=rnd(1000)
`unif#=rnd(1000)*0.001
`t#=sqrt(0) `test root 0 works
`a#=sqrt(unif#)
`b#=sqrt(1.0-unif#)
a#=sqrt(unif*0.001)
b#=sqrt((1000-unif)*0.001)
quats(q,0)=a#*sin(ang1#)
quats(q,1)=a#*cos(ang1#)
quats(q,2)=b#*sin(ang2#)
quats(q,3)=b#*cos(ang2#)
endfunction
`functions from "quaternions test" project. don't need all these just yet, but may as well copy all across.
function _make_random_quaternion(q) `superceded by above. this is a poor random function (makes in a hypercube)
w#=rnd(1001)-500.5
x#=rnd(1001)-500.5
y#=rnd(1001)-500.5
z#=rnd(1001)-500.5
invsize#=1.0/sqrt(w#*w#+x#*x#+y#*y#+z#*z#)
w#=w#*invsize#
x#=x#*invsize#
y#=y#*invsize#
z#=z#*invsize#
quats(q,0)=w#
quats(q,1)=x#
quats(q,2)=y#
quats(q,3)=z#
endfunction
function normalise_quat(q)
w#=quats(q,0)
x#=quats(q,1)
y#=quats(q,2)
z#=quats(q,3)
invsize#=1.0/sqrt(w#*w#+x#*x#+y#*y#+z#*z#)
w#=w#*invsize#
x#=x#*invsize#
y#=y#*invsize#
z#=z#*invsize#
quats(q,0)=w#
quats(q,1)=x#
quats(q,2)=y#
quats(q,3)=z#
endfunction
function _convert_quat_to_matrix(q,m)
a as float
b as float
c as float
d as float
asq as float
bsq as float
csq as float
dsq as float
a=quats(q,0)
b=quats(q,1)
c=quats(q,2)
d=quats(q,3)
asq=a*a
bsq=b*b
csq=c*c
dsq=d*d
matrices(m,1,1)=asq+bsq-csq-dsq :matrices(m,1,2)=2.0*(b*c-a*d) :matrices(m,1,3)=2.0*(b*d+a*c)
matrices(m,2,1)=2.0*(b*c+a*d) :matrices(m,2,2)=asq-bsq+csq-dsq :matrices(m,2,3)=2.0*(c*d-a*b)
matrices(m,3,1)=2.0*(b*d-a*c) :matrices(m,3,2)=2.0*(c*d+a*b) :matrices(m,3,3)=asq-bsq-csq+dsq
endfunction
function _convert_quats_to_4matrix(q1,q2,m)
a1 as float
b1 as float
c1 as float
d1 as float
a2 as float
b2 as float
c2 as float
d2 as float
a1=quats(q1,0)
b1=quats(q1,1)
c1=quats(q1,2)
d1=quats(q1,3)
a2=quats(q2,0)
b2=quats(q2,1)
c2=quats(q2,2)
d2=quats(q2,3)
` aa as float
` bb as float
` cc as float
` dd as float
a1a2#=a1*a2
a1b2#=a1*b2
a1c2#=a1*c2
a1d2#=a1*d2
b1a2#=b1*a2
b1b2#=b1*b2
b1c2#=b1*c2
b1d2#=b1*d2
c1a2#=c1*a2
c1b2#=c1*b2
c1c2#=c1*c2
c1d2#=c1*d2
d1a2#=d1*a2
d1b2#=d1*b2
d1c2#=d1*c2
d1d2#=d1*d2
matrices(m,0,0)=a1a2#-b1b2#-c1c2#-d1d2# :matrices(m,0,1)=-a1b2#-b1a2#+c1d2#-d1c2# :matrices(m,0,2)=-a1c2#-b1d2#-c1a2#+d1b2# :matrices(m,0,3)=-a1d2#+b1c2#-c1b2#-d1a2#
matrices(m,1,0)=b1a2#+a1b2#-d1c2#+c1d2# :matrices(m,1,1)=-b1b2#+a1a2#+d1d2#+c1c2# :matrices(m,1,2)=-b1c2#+a1d2#-d1a2#-c1b2# :matrices(m,1,3)=-b1d2#-a1c2#-d1b2#+c1a2#
matrices(m,2,0)=c1a2#+d1b2#+a1c2#-b1d2# :matrices(m,2,1)=-c1b2#+d1a2#-a1d2#-b1c2# :matrices(m,2,2)=-c1c2#+d1d2#+a1a2#+b1b2# :matrices(m,2,3)=-c1d2#-d1c2#+a1b2#-b1a2#
matrices(m,3,0)=d1a2#-c1b2#+b1c2#+a1d2# :matrices(m,3,1)=-d1b2#-c1a2#-b1d2#+a1c2# :matrices(m,3,2)=-d1c2#-c1d2#+b1a2#-a1b2# :matrices(m,3,3)=-d1d2#+c1c2#+b1b2#+a1a2#
` matrices(m,1,1)=asq+bsq-csq-dsq :matrices(m,1,2)=2.0*(b*c-a*d) :matrices(m,1,3)=2.0*(b*d+a*c)
` matrices(m,2,1)=2.0*(b*c+a*d) :matrices(m,2,2)=asq-bsq+csq-dsq :matrices(m,2,3)=2.0*(c*d-a*b)
` matrices(m,3,1)=2.0*(b*d-a*c) :matrices(m,3,2)=2.0*(c*d+a*b) :matrices(m,3,3)=asq-bsq-csq+dsq
endfunction
function _multiply_quaternions(prod,a,b)
REMSTART
quats(prod,0)=quats(a,0)*quats(b,0)-(quats(a,1)*quats(b,1)+quats(a,2)*quats(b,2)+quats(a,3)*quats(b,3)) `s1s2 - v1.v2
`v ector part of product= s1v2+s2v1+s3v3 +v1 x v2
quats(prod,1)=quats(a,0)*quats(b,1)+quats(b,0)*quats(a,1)+ quats(a,2)*quats(b,3)-quats(a,3)*quats(b,2)
quats(prod,2)=quats(a,0)*quats(b,2)+quats(b,0)*quats(a,2)+ quats(a,3)*quats(b,1)-quats(a,1)*quats(b,3)
quats(prod,3)=quats(a,0)*quats(b,3)+quats(b,0)*quats(a,3)+ quats(a,1)*quats(b,2)-quats(a,2)*quats(b,1)
REMEND
`this verion less efficient, but allows use of prod=a or b
`can use clever code to never need this, but speed not critical.
w#=quats(a,0)*quats(b,0)-(quats(a,1)*quats(b,1)+quats(a,2)*quats(b,2)+quats(a,3)*quats(b,3))
x#=quats(a,0)*quats(b,1)+quats(b,0)*quats(a,1)+ quats(a,2)*quats(b,3)-quats(a,3)*quats(b,2)
y#=quats(a,0)*quats(b,2)+quats(b,0)*quats(a,2)+ quats(a,3)*quats(b,1)-quats(a,1)*quats(b,3)
z#=quats(a,0)*quats(b,3)+quats(b,0)*quats(a,3)+ quats(a,1)*quats(b,2)-quats(a,2)*quats(b,1)
quats(prod,0)=w#
quats(prod,1)=x#
quats(prod,2)=y#
quats(prod,3)=z#
endfunction
function _multiply_matrices(prod,a,b)
for col=1 to 3:for row=1 to 3
matrices(prod,row,col)=0
for i=1 to 3
matrices(prod,row,col)=matrices(prod,row,col)+matrices(a,row,i)*matrices(b,i,col)
next i
next row:next col
endfunction
function _multiply_4matrices(prod,a,b)
for col=0 to 3:for row=0 to 3
matrices(prod,row,col)=0
for i=0 to 3
matrices(prod,row,col)=matrices(prod,row,col)+matrices(a,row,i)*matrices(b,i,col)
next i
next row:next col
endfunction
function _copy_matrix(m_from,m_to)
for i=1 to 3:for j=1 to 3 `only 3x3 copy. use another func for full 4x4 copy
matrices(m_to,i,j)=matrices(m_from,i,j)
next j:next i
endfunction
function _copy_4matrix(m_from,m_to)
for i=0 to 3:for j=0 to 3 `full 4x4 copy
matrices(m_to,i,j)=matrices(m_from,i,j)
next j:next i
endfunction
function _copy_quaternion(q_from,q_to)
for i=0 to 3
quats(q_to,i)=quats(q_from,i)
next i
endfunction
function _init_quaternion(q,w#,x#,y#,z#)
quats(q,0)=w#:quats(q,1)=x#:quats(q,2)=y#:quats(q,3)=z#
endfunction

I'm not sure whether there is any way to pass a rotation matrix to an object in DBP. As far as I know there are no standard commands for it, but maybe it's possible with a DLL. Would be great to have an ezRotate command

EZro_SetObjectRotation objectnumber

and the reverse

EZro_GetObjectRotation objectnumber

or similar, to bypass Euler angles and give/get from DBPro the matrix, or quaternion or whatever it uses internally. I imagine it would be significantly faster.

Anyway this is free and it's really good. Don't put off trying it out.