some more quat routines

BlitzMax Modules Forums/MiniB3D/some more quat routines

I notice that my old quat type has a few more utility functions

If anyone maintaining minib3d wants to use them feel free, they will need a *slight* adaptation as i was using a flat matrix [0..15] instead of [0..3,0..3]

it has the following methods

FromMatrix
ToMatrix
Normalise
FromAxisAngle
Multiply
GetEuler
Copy
FromEuler
Set
Invert

Type TQuat

	Field x!,y!,z!,w!

	Method FromMatrix(mat:Double[])

		Local T:Float= 1 + mat[0] + mat[5] + mat[10]

		Local S:Float

		If ( T > 0.00000001 ) Then

			S = Sqr(T) * 2

      		X = ( mat[6] - mat[9] ) / S

      		Y = ( mat[8] - mat[2] ) / S

      		Z = ( mat[1] - mat[4] ) / S

      		W = 0.25 * S

			Return

		EndIf

		

		If mat[0] > mat[5] And mat[0] > mat[10] Then	'Column 0: 

        	S  = Sqr( 1.0 + mat[0] - mat[5] - mat[10] ) * 2

        	X = 0.25 * S

        	Y = (mat[1] + mat[4] ) / S

        	Z = (mat[8] + mat[2] ) / S

        	W = (mat[6] - mat[9] ) / S

		ElseIf mat[5] > mat[10]							'Column 1: 

	        S  = Sqr( 1.0 + mat[5] - mat[0] - mat[10] ) * 2

	        X = (mat[1] + mat[4] ) / S

	        Y = 0.25 * S

	        Z = (mat[6] + mat[9] ) / S

	        W = (mat[8] - mat[2] ) / S

		Else											'Column 2:

	        S  = Sqr( 1.0 + mat[10] - mat[0] - mat[5] ) * 2

	        X = (mat[8] + mat[2] ) / S

	        Y = (mat[6] + mat[9] ) / S

	        Z = 0.25 * S

	        W = (mat[1] - mat[4] ) / S

    	EndIf



	EndMethod

	

	Method ToMatrix:Double[]()

		Local xx!=x*x

		Local xy!=x*y

		Local xz!=x*z

		Local xw!=x*w

		

		Local yy!=y*y

		Local yz!=y*z

		Local yw!=y*w

		

		Local zz!=z*z

		Local zw!=z*w

		

		Local m:Double[16]

		m[0]= 1-2*(yy+zz)

		m[4]=   2*(xy-zw)

		m[8]=   2*(xz+yw)

		

		m[1]=   2*(xy+zw)

		m[5]= 1-2*(xx+zz)

		m[9]=   2*(yz-xw)

		

		m[2]=   2*(xz-yw)

		m[6]=   2*(yz+xw)

		m[10]=1-2*(xx+yy)

		

		m[12]=0;m[13]=0;m[14]=0

		m[3]=0;m[7]=0;m[11]=0

		m[15]=1

		

		Return m

		

	EndMethod

	

	Method Normalise()

		Local m:Double

		m=Sqr(w*w+x*x+y*y+z*z)

		If m=1 Then Return ' is exactly 1 more often than you'd think...

		w:/m

		x:/m

		y:/m

		z:/m

	EndMethod

	

	Method FromAxisAngle(a!,axis:TVec3)

		Local sin_a! = Sin( a / 2 )

    	Local cos_a! = Cos( a / 2 )



    	X    = axis.x * sin_a

    	Y    = axis.y * sin_a

    	Z    = axis.z * sin_a

    	W    = cos_a



	    Normalise()

		Return

	EndMethod



	

	Method Multiply:TQuat(q:TQuat)

		Local a#, b#, c#, d#, e#, f#, g#, h#



		a = (w + x) * (q.w + q.x)

		b = (z - y) * (q.y - q.z)

		c = (w - x) * (q.y + q.z)

		d = (y + z) * (q.w - q.x)

		e = (x + z) * (q.x + q.y)

		f = (x - z) * (q.x - q.y)

		g = (w + y) * (q.w - q.z)

		h = (w - y) * (q.w + q.z)

		

		w = b + (-e - f + g + h) / 2

		x = a - ( e + f + g + h) / 2

		y = c + ( e - f + g - h) / 2

		z = d + ( e - f - g + h) / 2

		

	End Method

	

	

	Const QuatToEulerAccuracy# = 0.000000000001

		

	Method GetEuler:TVec3()		' using vec3 to hold eulers

		Local sint!, cost!, sinv!, cosv!, sinf!, cosf!

		Local cost_temp!

		normalise()

		sint = (2 * w * y) - (2 * x * z)

		cost_temp = 1.0 - (sint * sint)



		If Abs(cost_temp) > QuatToEulerAccuracy

			cost = Sqr(cost_temp)

		Else

			cost = 0

		EndIf



		If Abs(cost) > QuatToEulerAccuracy

			sinv = ((2 * y * z) + (2 * w * x)) / cost

			cosv = (1 - (2 * x * x) - (2 * y * y)) / cost

			sinf = ((2 * x * y) + (2 * w * z)) / cost

			cosf = (1 - (2 * y * y) - (2 * z * z)) / cost

		Else

			sinv = (2 * w * x) - (2 * y * z)

			cosv = 1 - (2 * x * x) - (2 * z * z)

			sinf = 0

			cosf = 1

		EndIf

	

		Local out:TVec3=New Tvec3		

		out.x = ATan2(sinv, cosv)

		out.y = ATan2(sint, cost)

		out.z = ATan2(sinf, cosf)

'		out.x=bound(out.x)

'		out.y=bound(out.y)

'		out.z=bound(out.z)

			

		Return out



	EndMethod

	

	Method Copy(q:TQuat)

		w=q.w

		x=q.x

		y=q.y

		z=q.z

	EndMethod

	

	

	Method FromEuler(src:TVec3)

	

		Local cr! = Cos(src.x/2)

		Local cp! = Cos(src.y/2)

		Local cy! = Cos(src.z/2)

	

		Local sr! = Sin(src.x/2)

		Local sp! = Sin(src.y/2)

		Local sy! = Sin(src.z/2)

	

		'; These variables are only here To cut down on the number of multiplications

		Local cpcy! = cp * cy

		Local spsy! = sp * sy

		Local spcy! = sp * cy

		Local cpsy! = cp * sy

	

		w = cr * cpcy + sr * spsy

		x = sr * cpcy - cr * spsy

		y = cr * spcy + sr * cpsy

		z = cr * cpsy - sr * spcy

		normalise()

	EndMethod

	

	Method Set(nw!,nx!,ny!,nz!)

		w=nw;x=nx;y=ny;z=nz

	EndMethod

	

	Method Invert()

		x=-x;y=-y;z=-z

	EndMethod 

EndType