Yes, here is a program demonstrating the bug. Most of the time it comes out the same, but case 8, 14, and 18 give different results:
Strict
Local vec:TVec3
Local mat:TMat4
For Local n=1 To 20
vec=vec3(Rnd(0,360),Rnd(0,360),Rnd(0,360))
mat=TMat4.FromRotation(vec.toquat())
Print n
Print mat.rotation().tostring()
Print mat.buggyrotation().tostring()
Print ""
Next
Global Infinity#=1 Shl 23
Function Vec3:TVec3( x#=0.0,y#=0.0,z#=0.0 )
Local t:TVec3=New TVec3
t.x=x
t.y=y
t.z=z
Return t
EndFunction
Function Quat:TQuat(x#,y#,z#,w#)
Local q:TQuat=New TQuat
q.x=x
q.y=y
q.z=z
q.w=w
Return q
EndFunction
Type TVec3 {value}
Field x#,y#,z# {attribute}
Method Copy:TVec3()
Return Vec3( x,y,z )
End Method
Method ToString$()
Return "Vec3("+x+","+y+","+z+")"
End Method
Method ToField$()
Return x+","+y+","+z
End Method
Method FromField:TVec3( t$ )
Local bits$[]=t.Split( "," )
If bits.length<>3 Throw "Format error"
x=bits[0].ToFloat()
y=bits[1].ToFloat()
z=bits[2].ToFloat()
Return Self
End Method
Method Pointer:Float Ptr()
Return Varptr x
EndMethod
Method Length#()
Return Sqr( x*x+y*y+z*z )
End Method
Method Dot#( v:TVec3 )
Return x*v.x+y*v.y+z*v.z
End Method
Method Inverse:TVec3()
Return Vec3( -x,-y,-z )
End Method
Method Reciprocal:TVec3()
Return Vec3( 1/x,1/y,1/z )
End Method
Method Normalize:TVec3()
Local t#=Sqr( x*x+y*y+z*z )
If t=0.0 Return vec3(0.0,0.0,0.0)
Return Vec3( x/t,y/t,z/t )
End Method
Method Scale:TVec3( scale# )
Return Vec3( x*scale,y*scale,z*scale )
End Method
Method DistanceTo#( v:TVec3 )
Local dx#=x-v.x,dy#=y-v.y,dz#=z-v.z
Return Sqr( dx*dx+dy*dy+dz*dz )
End Method
Method Plus:TVec3( v:TVec3 )
Return Vec3( x+v.x,y+v.y,z+v.z )
End Method
Method Minus:TVec3( v:TVec3 )
Return Vec3( x-v.x,y-v.y,z-v.z )
End Method
Method Times:TVec3( v:TVec3 )
Return Vec3( x*v.x,y*v.y,z*v.z )
End Method
Method DividedBy:TVec3( v:TVec3 )
Return Vec3( x/v.x,y/v.y,z/v.z )
End Method
Method Cross:TVec3( v:TVec3 )
Return Vec3( y*v.z-z*v.y,z*v.x-x*v.z,x*v.y-y*v.x )
End Method
Method Blend:TVec3( v:TVec3,Alpha# )
Local beta#=1-Alpha
Return Vec3( x*beta+v.x*Alpha,y*beta+v.y*Alpha,z*beta+v.z*Alpha )
End Method
Method ToQuat:TQuat()
Local c1#=Cos(-z/2)
Local s1#=Sin(-z/2)
Local c2#=Cos(-x/2)
Local s2#=Sin(-x/2)
Local c3#=Cos( y/2)
Local s3#=Sin( y/2)
Local c1_c2#=c1*c2
Local s1_s2#=s1*s2
Local t:TQuat=New TQuat
t.x=c1*s2*c3 - s1*c2*s3;
t.y=c1_c2*s3 + s1_s2*c3;
t.z=s1*c2*c3 + c1*s2*s3;
t.w=c1_c2*c3 - s1_s2*s3;
Return t
EndMethod
Function FromQuat:TVec3(quat:TQuat)
Return quat.toeuler()
EndFunction
EndType
Type TQuat
Field x#,y#,z#,w#=1.0 {attribute}
Method ToString$()
Return "Quat("+x+","+y+","+z+","+w+")"
End Method
Method Inverse:TQuat()
Return Quat( -x,-y,-z,w )
End Method
Method ToAngleAxis( angle# Var,axis:TVec3 Var )
Local t#=ACos(w)
angle=t*2
If angle>0
t=1/Sin(t)
axis=Vec3( x*t,y*t,z*t )
Else
axis=Vec3(0,0,0)
EndIf
End Method
Method Yaw#()
Return ATan2( 2*y*w-2*z*x,1-2*y*y-2*x*x )
End Method
Method Pitch#()
Return -ASin( 2*z*y+2*x*w )
End Method
Method Roll#()
Return -ATan2( 2*z*w-2*y*x,1-2*z*z-2*x*x )
End Method
Method ToEuler:TVec3()
Local x2#=x*x,y2#=y*y,z2#=z*z
Local euler:TVec3=New TVec3
euler.y#=ATan2( 2*y*w-2*z*x,1-2*y2-2*x2 )
euler.x#=-ASin( 2*z*y+2*x*w )
euler.z#=-ATan2( 2*z*w-2*y*x,1-2*z2-2*x2 )
Return euler
End Method
Method Times:TQuat( q:TQuat )
Local result:TQuat=New TQuat
result.x#=w*q.x + x*q.w + y*q.z - z*q.y
result.y#=w*q.y + y*q.w + z*q.x - x*q.z
result.z#=w*q.z + z*q.w + x*q.y - y*q.x
result.w#=w*q.w - x*q.x - y*q.y - z*q.z
Return result
EndMethod
Method Slerp:TQuat( q:TQuat,a# )
Local b#=1-a,f
Local d#=x*q.x+y*q.y+z*q.z+w*q.w
If d<0
d=-d
f=True
EndIf
If d<1
Local om#=acos_( d )
Local si#=sin_( om )
a=sin_( a*om )/si
b=sin_( b*om )/si
EndIf
If f a=-a
Return Quat( x*b + q.x*a , y*b + q.y*a , z*b + q.z*a , w*b + q.w*a )
EndMethod
Method Normalize:TQuat()
Local q:TQuat=New TQuat
Local m#=Sqr(x*x+y*y+z*z+w*w)
q.x=x/m
q.y=y/m
q.z=z/m
q.w=w/m
Return q
EndMethod
Function FromAngleAxis:TQuat( angle#,axis:TVec3 )
angle:/2
Local s#=Sin(angle)
Local t:TQuat=New TQuat
t.x=s*axis.x
t.y=s*axis.y
t.z=s*axis.z
t.w=Cos(angle)
Return t
EndFunction
Function FromEuler:TQuat(euler:TVec3)
Return euler.toquat()
EndFunction
EndType
Type TMat4
Field ix#,iy#,iz#,iw# {attribute}
Field jx#,jy#,jz#,jw# {attribute}
Field kx#,ky#,kz#,kw# {attribute}
Field tx#,ty#,tz#,tw# {attribute}
Method Copy:TMat4()
Local t:TMat4=New TMat4
MemCopy t,Self,SizeOf Self
Return t
End Method
Method ToString$()
Local t$="Mat4{~n"
t:+ix+","+iy+","+iz+","+iw+"~n"
t:+jx+","+jy+","+jz+","+jw+"~n"
t:+kx+","+ky+","+kz+","+kw+"~n"
t:+tx+","+ty+","+tz+","+tw+"~n"
Return t+"}~n"
End Method
Method Pointer:Float Ptr()
Return Varptr ix
EndMethod
Method Translation:TVec3()
Return Vec3(tx,ty,tz)
End Method
Method Rotation:TQuat()
Local mult:Float
Local x#,y#,z#,w#
Local fourWSquaredMinus1:Float = +ix+jy+kz
Local fourXSquaredMinus1:Float = +ix-jy-kz
Local fourYSquaredMinus1:Float = +jy-ix-kz
Local fourZSquaredMinus1:Float = +kz-ix-jy
Local biggestIndex=0
Local fourBiggestSquaredMinus1:Float=fourWSquaredMinus1
If fourXSquaredMinus1 > fourBiggestSquaredMinus1
fourBiggestSquaredMinus1 = fourXSquaredMinus1
biggestIndex=1
EndIf
If fourYSquaredMinus1 > fourBiggestSquaredMinus1
fourBiggestSquaredMinus1 = fourYSquaredMinus1
biggestIndex=2
EndIf
If fourZSquaredMinus1 > fourBiggestSquaredMinus1
fourBiggestSquaredMinus1 = fourZSquaredMinus1
biggestIndex=3
EndIf
Local biggestValue:Float=Sqr(fourBiggestSquaredMinus1+1.0)*0.5
mult=0.25/biggestValue
Select BiggestIndex
Case 0
w#=BiggestValue
x#=(jz-ky)*mult
y#=-(kx-iz)*mult
z#=-(iy-jx)*mult
Case 1
x#=-BiggestValue
w#=-( jz - ky )*mult
y#=( iy + jx )*mult
z#=( kx + iz )*mult
Case 2
y#=BiggestValue
w#=-(kx-iz)*mult
x#=-(iy+jx)*mult
z#=(jz+ky)*mult
Case 3
z#=-BiggestValue
w#=(iy-jx)*mult
x#=(kx+iz)*mult
y#=-(jz+ky)*mult
EndSelect
Return quat(-x,y,z,w)
EndMethod
Method BuggyRotation:TQuat()
Local mult:Float
Local fourWSquaredMinus1:Float = +ix+jy+kz
Local fourXSquaredMinus1:Float = +ix-jy-kz
Local fourYSquaredMinus1:Float = +jy-ix-kz
Local fourZSquaredMinus1:Float = +kz-ix-jy
Local biggestIndex=0
Local fourBiggestSquaredMinus1:Float=fourWSquaredMinus1
If fourXSquaredMinus1 > fourBiggestSquaredMinus1
fourBiggestSquaredMinus1 = fourXSquaredMinus1
biggestIndex=1
EndIf
If fourYSquaredMinus1 > fourBiggestSquaredMinus1
fourBiggestSquaredMinus1 = fourYSquaredMinus1
biggestIndex=2
EndIf
If fourZSquaredMinus1 > fourBiggestSquaredMinus1
fourBiggestSquaredMinus1 = fourZSquaredMinus1
biggestIndex=3
EndIf
Local biggestValue:Float=Sqr(fourBiggestSquaredMinus1+1.0)*0.5
Local x#,y#,z#,w#
mult=0.25/biggestValue
Select BiggestIndex
Case 0
w#=BiggestValue
x#=(jz-ky)*mult
y#=-(kx-iz)*mult
z#=-(iy-jx)*mult
Case 1
x#=-BiggestValue
w#=-( jz - ky )*mult
y#=( iy + jx )*mult
z#=( kx + iz )*mult
Case 2
y#=BiggestValue
w#=-(kx-iz)*mult
x#=-(iy+jx)*mult
z#=(jz+ky)*mult
Case 3
z#=-BiggestValue
w#=(iy-jx)*mult
x#=(kx+iz)*mult
y#=-(jz+ky)*mult
EndSelect
Return quat(-x,y,z,w)
EndMethod
Method Scale:TVec3()
Return Vec3( Vec3(ix,iy,iz).Length(),Vec3(jx,jy,jz).Length(),Vec3(kx,ky,kz).Length() )
End Method
Method Times:TMat4( m:TMat4 )
Local t:TMat4=New TMat4
t.ix= ix*m.ix + jx*m.iy + kx*m.iz + tx*m.iw
t.iy= iy*m.ix + jy*m.iy + ky*m.iz + ty*m.iw
t.iz= iz*m.ix + jz*m.iy + kz*m.iz + tz*m.iw
t.iw= iw*m.ix + jw*m.iy + kw*m.iz + tw*m.iw
t.jx= ix*m.jx + jx*m.jy + kx*m.jz + tx*m.jw
t.jy= iy*m.jx + jy*m.jy + ky*m.jz + ty*m.jw
t.jz= iz*m.jx + jz*m.jy + kz*m.jz + tz*m.jw
t.jw= iw*m.jx + jw*m.jy + kw*m.jz + tw*m.jw
t.kx= ix*m.kx + jx*m.ky + kx*m.kz + tx*m.kw
t.ky= iy*m.kx + jy*m.ky + ky*m.kz + ty*m.kw
t.kz= iz*m.kx + jz*m.ky + kz*m.kz + tz*m.kw
t.kw= iw*m.kx + jw*m.ky + kw*m.kz + tw*m.kw
t.tx= ix*m.tx + jx*m.ty + kx*m.tz + tx*m.tw
t.ty= iy*m.tx + jy*m.ty + ky*m.tz + ty*m.tw
t.tz= iz*m.tx + jz*m.ty + kz*m.tz + tz*m.tw
t.tw= iw*m.tx + jw*m.ty + kw*m.tz + tw*m.tw
Return t
End Method
Method TimesPoint:TVec3( v:TVec3 )
Return Vec3(..
ix*v.x + jx*v.y + kx*v.z + tx,..
iy*v.x + jy*v.y + ky*v.z + ty,..
iz*v.x + jz*v.y + kz*v.z + tz )
End Method
Method TimesVector:TVec3( v:TVec3 )
Return Vec3(..
ix*v.x + jx*v.y + kx*v.z,..
iy*v.x + jy*v.y + ky*v.z,..
iz*v.x + jz*v.y + kz*v.z )
End Method
Method TimesNormal:TVec3( v:TVec3 )
Local m:TMat4=Inverse()
Return Vec3(..
m.ix*v.x + m.iy*v.y + m.iz*v.z,..
m.jx*v.x + m.jy*v.y + m.jz*v.z,..
m.kx*v.x + m.ky*v.y + m.kz*v.z )
End Method
Method Determinant#()
Assert Abs(iw)<=.001 And Abs(jw)<=.001 And Abs(kw<=.001) And tw>=1-.001
Return ix*(jy*kz-jz*ky) - iy*(jx*kz-jz*kx) + iz*(jx*ky-jy*kx)
End Method
Method I:TVec3()
Return Vec3(ix,iy,iz)
End Method
Method J:TVec3()
Return Vec3(jx,jy,jz)
End Method
Method K:TVec3()
Return Vec3(kx,ky,kz)
End Method
Method Cofactor:TMat4()
Local t:TMat4=New TMat4
t.ix= (jy*kz-jz*ky) ; t.iy=-(jx*kz-jz*kx) ; t.iz= (jx*ky-jy*kx)
t.jx=-(iy*kz-iz*ky) ; t.jy= (ix*kz-iz*kx) ; t.jz=-(ix*ky-iy*kx)
t.kx= (iy*jz-iz*jy) ; t.ky=-(ix*jz-iz*jx) ; t.kz= (ix*jy-iy*jx)
Return t
End Method
Method Inverse:TMat4()
Local c#=1.0/Determinant()
Local t:TMat4=New TMat4
t.ix= c * ( jy*kz - jz*ky )
t.iy=-c * ( iy*kz - iz*ky )
t.iz= c * ( iy*jz - iz*jy )
t.jx=-c * ( jx*kz - jz*kx )
t.jy= c * ( ix*kz - iz*kx )
t.jz=-c * ( ix*jz - iz*jx )
t.kx= c * ( jx*ky - jy*kx )
t.ky=-c * ( ix*ky - iy*kx )
t.kz= c * ( ix*jy - iy*jx )
t.tx=-( tx*t.ix + ty*t.jx + tz*t.kx )
t.ty=-( tx*t.iy + ty*t.jy + tz*t.ky )
t.tz=-( tx*t.iz + ty*t.jz + tz*t.kz )
t.tw=1
Return t
End Method
Method Transpose:TMat4()
Local t:TMat4=New TMat4
t.ix=ix ; t.iy=jx ; t.iz=kx ; t.iw=tx
t.jx=iy ; t.jy=jy ; t.jz=ky ; t.jw=ty
t.kx=iz ; t.ky=jz ; t.kz=kz ; t.kw=tz
t.tx=iw ; t.ty=jw ; t.tz=kw ; t.tw=tw
Return t
End Method
Function FromDirection:TMat4( dir:TVec3,up:TVec3=Null )
If Not up up=Vec3(0,1,0)
Local j:TVec3=up.Normalize()
Local k:TVec3=dir.Normalize()
Local i:TVec3=j.Cross(k).Normalize()
j=k.Cross(i).Normalize()
Local t:TMat4=New TMat4
t.ix=i.x ; t.iy=i.y ; t.iz=i.z
t.jx=j.x ; t.jy=j.y ; t.jz=j.z
t.kx=k.x ; t.ky=k.y ; t.kz=k.z
t.tw=1
Return t
EndFunction
Function FromTranslation:TMat4( v:TVec3 )
Local t:TMat4=New TMat4
t.ix=1 ; t.jy=1 ; t.kz=1
t.tx=v.x ; t.ty=v.y ; t.tz=v.z
t.tw=1
Return t
EndFunction
Function FromRotation:TMat4( q:TQuat )
Local t:TMat4=New TMat4
Local xx#=q.x*q.x,yy#=q.y*q.y,zz#=q.z*q.z
Local xy#=q.x*q.y,xz#=q.x*q.z,yz#=q.y*q.z
Local wx#=q.w*q.x,wy#=q.w*q.y,wz#=q.w*q.z
t.ix=1-2*(yy+zz) ; t.iy= 2*(xy-wz) ; t.iz= 2*(xz+wy)
t.jx= 2*(xy+wz) ; t.jy=1.0-2*(xx+zz) ; t.jz= 2*(yz-wx)
t.kx= 2*(xz-wy) ; t.ky= 2*(yz+wx) ; t.kz=1.0-2*(xx+yy)
t.tw=1
Return t
EndFunction
Function FromScale:TMat4( v:TVec3 )
Local t:TMat4=New TMat4
t.ix=v.x
t.jy=v.y
t.kz=v.z
t.tw=1
Return t
EndFunction
Function FromUniformScale:TMat4( sc# )
Local t:TMat4=New TMat4
t.ix=sc
t.jy=sc
t.kz=sc
t.tw=1
Return t
EndFunction
Function FromTransRotScale:TMat4( trans:TVec3,rot:TQuat,scale:TVec3 )
Local t:TMat4
If trans
t=FromTranslation(trans)
If rot t=t.Times(FromRotation(rot))
If scale t=t.Times(FromScale(scale))
Else If rot
t=FromRotation(rot)
If scale t=t.Times(FromScale(scale))
Else If scale
t=FromScale(scale)
Else
t=Identity()
EndIf
Return t
EndFunction
Function FromYaw:TMat4( yaw# ) 'untested!
Local t:TMat4=New TMat4
Local s#=Sin(yaw),c#=Cos(yaw)
t.ix=c ; t.iz=s ; t.jy=1
t.kx=-s ; t.kz=c ; t.tw=1
Return t
EndFunction
Function FromPitch:TMat4( pitch# ) 'untested!
Local t:TMat4=New TMat4
Local s#=Sin(pitch),c#=Cos(pitch)
t.ix=1 ; t.jy=c ; t.jz=s
t.ky=-s ; t.kz=c ; t.tw=1
Return t
EndFunction
Function FromRoll:TMat4( roll# ) 'untested!
Local t:TMat4=New TMat4
Local s#=Sin(roll),c#=Cos(roll)
t.ix=c ; t.iy=s ; t.jx=-s
t.jy=c ; t.kz=1 ; t.tw=1
Return t
EndFunction
Function FromYawPitchRoll:TMat4( yaw#,pitch#,roll# )
Local t:TMat4
If yaw
t=FromYaw(yaw)
If pitch t=t.Times(FromPitch(pitch))
If roll t=t.Times(FromRoll(roll))
Else If pitch
t=FromPitch(pitch)
If roll t=t.Times(FromRoll(roll))
Else If roll
t=FromRoll(roll)
Else
t=Identity()
EndIf
Return t
EndFunction
Function FromOrtho:TMat4( l#,r#,b#,t#,n#,f# )
Local q:TMat4=New TMat4
q.ix=2/(r-l)
q.tx=-(r+l)/(r-l)
q.jy=2/(t-b)
q.ty=-(t+b)/(t-b)
q.kz=2/(f-n)
q.tz=-(f+n)/(f-n)
q.tw=1
Return q
EndFunction
Function FromFrustum:TMat4( near_left#,near_right#,near_bottom#,near_top#,near#,far# )
Local t:TMat4=New TMat4
Local near2#=near*2
Local w#=near_right-near_left
Local h#=near_top-near_bottom
Local d#=far-near
t.ix=near2/w
t.jy=near2/h
t.kx=(near_right+near_left)/w
t.ky=(near_top+near_bottom)/h
t.kz=(far+near)/d
t.kw=1
t.tz=-(far*near2)/d
Return t
EndFunction
Function FromInfiniteFrustum:TMat4( near_left#,near_right#,near_bottom#,near_top#,near# )
Local t:TMat4=New TMat4
Local near2#=near*2
Local w#=near_right-near_left
Local h#=near_top-near_bottom
Local e#=.001
t.ix=near2/w
t.jy=near2/w
t.kx=(near_right+near_left)/w
t.ky=(near_top+near_bottom)/h
t.kz=1-e
t.kw=1
t.tz=near*(e-1)
Return t
EndFunction
Function Identity:TMat4()
Local t:TMat4=New TMat4
t.ix=1 ; t.jy=1 ; t.kz=1 ; t.tw=1
Return t
EndFunction
Method Normalize:TMat4()
Local mat:TMat4=New TMat4
Local vec:TVec3
vec=i().normalize()
mat.ix=vec.x
mat.iy=vec.y
mat.iz=vec.z
vec=j().normalize()
mat.jx=vec.x
mat.jy=vec.y
mat.jz=vec.z
vec=k().normalize()
mat.kx=vec.x
mat.ky=vec.y
mat.kz=vec.z
mat.tx=tx
mat.ty=ty
mat.tz=tz
mat.tw=1.0
Return mat
EndMethod
EndTypeHere is my output log:
Building bug
Compiling:bug.bmx
flat assembler version 1.66
3 passes, 24505 bytes.
Linking:bug.exe
Executing:bug.exe
1
Quat(0.505490303,-0.533333719,-0.307793617,0.604398549)
Quat(0.505490303,-0.533333719,-0.307793617,0.604398549)
2
Quat(0.479863316,0.697153628,-0.357858539,-0.394519120)
Quat(0.479863316,0.697153628,-0.357858539,-0.394519120)
3
Quat(-0.381797940,-0.183105320,-0.623658419,0.657079101)
Quat(-0.381797940,-0.183105320,-0.623658419,0.657079101)
4
Quat(0.260478765,0.870145679,0.391556114,0.147245243)
Quat(0.260478765,0.870145679,0.391556114,0.147245243)
5
Quat(0.0375171192,0.707903206,0.382147193,-0.592814445)
Quat(0.0375171192,0.707903206,0.382147193,-0.592814445)
6
Quat(0.789529085,0.0312671997,0.198185980,0.579990089)
Quat(0.789529085,0.0312671997,0.198185980,0.579990089)
7
Quat(0.742616892,-0.284792781,-0.244056195,0.554842114)
Quat(0.742616892,-0.284792781,-0.244056195,0.554842114)
8
Quat(-0.194731668,-0.612208784,-0.730031371,-0.233096883)
Quat(0.730031371,-0.233096883,0.194731668,-0.612208784)
9
Quat(-0.529806376,0.429777414,-0.405184805,0.608622909)
Quat(-0.529806376,0.429777414,-0.405184805,0.608622909)
10
Quat(0.557507277,0.593058288,0.458249658,0.357036322)
Quat(0.557507277,0.593058288,0.458249658,0.357036322)
11
Quat(0.743423522,0.224995196,0.133408666,0.615549266)
Quat(0.743423522,0.224995196,0.133408666,0.615549266)
12
Quat(0.638532400,0.211647302,-0.413198650,0.613798618)
Quat(0.638532400,0.211647302,-0.413198650,0.613798618)
13
Quat(0.661161602,0.473749906,0.578552246,-0.0608576164)
Quat(0.661161602,0.473749906,0.578552246,-0.0608576164)
14
Quat(0.432375759,-0.635354221,-0.639105976,0.0303268787)
Quat(0.639105976,0.0303268787,-0.432375759,-0.635354221)
15
Quat(0.633128643,-0.0820736513,0.297141016,0.710013568)
Quat(0.633128643,-0.0820736513,0.297141016,0.710013568)
16
Quat(0.385254771,0.691897929,0.574016333,0.208233491)
Quat(0.385254771,0.691897929,0.574016333,0.208233491)
17
Quat(0.616475344,-0.275921464,0.142099261,0.723625183)
Quat(0.616475344,-0.275921464,0.142099261,0.723625183)
18
Quat(-0.100223772,0.0979351997,-0.818663001,-0.556915581)
Quat(0.818663001,-0.556915581,0.100223772,0.0979351997)
19
Quat(-0.582106888,0.392956883,-0.268982202,0.659078956)
Quat(-0.582106888,0.392956883,-0.268982202,0.659078956)
20
Quat(-0.206659749,0.683476806,0.674812436,-0.186492458)
Quat(-0.206659749,0.683476806,0.674812436,-0.186492458)
Process complete