BRL.vec, BRL.matrix

BlitzMax Forums/BlitzMax Programming/BRL.vec, BRL.matrix

JoshK(Posted 2007) [#1]
It would be nice if BlitzMax had official vector, matrix and quaternion functions. Mine work, but I would prefer to rely on something standard.

My vector commands are like this. It's nice because you can do stuff like MulQuat( Vec4(x#,y#,z#,w#), entity.quaternion ) or entity.quaternion = entity.rotation.ToQuat()

Type TVec2
field x#,y#

Type TVec3 extends TVec2
field z#

Type TVec4 extends TVec3
field w#

Function Vec3:TVec3(x#,y#,z#)
vec:TVec3=new tvec3
vec.x=x
vec.y=y
vec.z=z
return vec
EndFunction


Chroma(Posted 2007) [#2]
Yep, that would be very nice.


JoshK(Posted 2007) [#3]
Here is my whole vector and matrix code. I just converted it to a more OO approach. The matrix code was partially based on MiniB3D mat code, with quaternion rotation added.
Rem
bbdoc:
EndRem
Type TVec2
	'expose
	
	Field x#,y#
	
	Method Magnitude:Float()
		Return Sqr(x*x+y*y)
	EndMethod
	
	Method Normalize()
		Local m#=Magnitude()
		x:/m
		y:/m
	EndMethod

	Method Flip:TVec2()
		Local v:TVec2=New TVec2
		v.x=-x
		v.y=-y
		Return v
	EndMethod

	Method Copy:TVec2()
		Local vec:TVec2=New TVec2
		vec.x=x
		vec.y=y
		Return vec
	EndMethod

	Method ToAngle:Float()
		Return ATan2(y,x)
	EndMethod

	Function FromAngle:TVec2(angle#)
		Local vec:TVec2=New tvec2
		vec.x=Cos(angle)
		vec.y=Sin(angle)
		Return vec
	EndFunction

	

	Method ToString:String()
		Return x+", "+y
	EndMethod

EndType

Rem
bbdoc:
EndRem
Type TVec3 Extends TVec2
	'expose
	
	Field z#
	
	Method Magnitude:Float()
		Return Sqr(x*x+y*y+z*z)
	EndMethod
	
	Method Normalize()
		Local m#=Magnitude()
		x:/m
		y:/m
		z:/m
	EndMethod

	Method Sub:TVec3(vec:TVec3)
		Local newvec:TVec3=New tvec3
		newvec.x=x-vec.x
		newvec.y=y-vec.y
		newvec.z=z-vec.z
		Return newvec
	EndMethod

	Method Reverse:TVec3()
		Local v:TVec3=New TVec3
		v.x=-x
		v.y=-y
		v.z=-z
		Return v
	EndMethod

	Method Invert:TVec3()
		Local v:TVec3=New TVec3
		v.x=1.0/x
		v.y=1.0/y
		v.z=1.0/z
		Return v
	EndMethod

	Method Copy:TVec3()
		Local vec:TVec3=New TVec3
		vec.x=x
		vec.y=y
		vec.z=z
		Return vec
	EndMethod

	Method ToQuat:TVec4()
		Local cr#=Cos(-z/2.0)
		Local cp#=Cos(x/2.0)
		Local cy#=Cos(y/2.0)
		Local sr#=Sin(-z/2.0)
		Local sp#=Sin(x/2.0)
		Local sy#=Sin(y/2.0)
		Local cpcy#=cp#*cy#
		Local spsy#=sp#*sy#
		Local spcy#=sp#*cy#
		Local cpsy#=cp#*sy#
		Local quat:TVec4=New TVec4
		quat.w=cr#*cpcy#+sr#*spsy#
		quat.x=sr#*cpcy#-cr#*spsy#
		quat.y=cr#*spcy#+sr#*cpsy#
		quat.z=cr#*cpsy#-sr#*spcy#
		Return quat
	EndMethod

	Method ToString$()
		Return x+", "+y+", "+z
	EndMethod

EndType

Rem
bbdoc:
EndRem
Type TVec4 Extends TVec3
	'expose
	
	Const QuatToEulerAccuracy#=0.001
	
	Field w#
	
	Method ToString$()
		Return x+", "+y+", "+z+", "+w
	EndMethod

	Method Magnitude:Float()
		Return Sqr(x*x+y*y+z*z+w*w)
	EndMethod
	
	Method Normalize()
		Local m#=Magnitude()
		x:/m
		y:/m
		z:/m
		w:/m
	EndMethod

	Method Invert:TVec4()
		Local v:TVec4=New TVec4
		v.x=1.0/x
		v.y=1.0/y
		v.z=1.0/z
		v.w=1.0/w
		Return v
	EndMethod

	Method Reverse:TVec4()
		Local v:TVec4=New TVec4
		v.x=-x
		v.y=-y
		v.z=-z
		v.w=-w
		Return v
	EndMethod

	Method Copy:TVec4()
		Local vec:TVec4=New TVec4
		vec.x=x
		vec.y=y
		vec.z=z
		vec.w=w
		Return vec
	EndMethod

	Method ToEuler:TVec3()
		Local sint#=(2.0*w*y)-(2.0*x*z)
		Local cost_temp#=1.0-(sint#*sint#)
		Local cost#,sinv#,cosv#,sinf#,cosf#
		If Abs(cost_temp#)>QuatToEulerAccuracy
			cost#=Sqr(cost_temp#)
		Else
			cost#=0.0
		EndIf
		If Abs(cost#)>QuatToEulerAccuracy
			sinv#=((2.0*y*z)+(2.0*w*x))/cost#
			cosv#=(1.0-(2.0*x*x)-(2.0*y*y))/cost#
			sinf#=((2.0*x*y)+(2.0*w*z))/cost#
			cosf#=(1.0-(2.0*y*y)-(2.0*z*z))/cost#
		Else
			sinv#=(2.0*w*x)-(2.0*y*z)
			cosv#=1.0-(2.0*x*x)-(2.0*z*z)
			sinf#=0.0
			cosf#=1.0
		EndIf
		Local euler:TVec3=New TVec3
		euler.z=-ATan2(sinv#,cosv#)
		euler.x=ATan2(sint#,cost#)
		euler.y=ATan2(sinf#,cosf#)
		Return euler
	EndMethod

EndType

Rem
bbdoc:
End Rem
Function Vec2:TVec2(x#,y#)
	Local vec:TVec2=New TVec2
	vec.x=x
	vec.y=y
	Return vec
EndFunction

Rem
bbdoc:
End Rem
Function Vec3:TVec3(x#,y#,z#)
	Local vec:TVec3=New TVec3
	vec.x=x
	vec.y=y
	vec.z=z
	Return vec
EndFunction

Rem
bbdoc:
End Rem
Function Vec4:TVec4(x#,y#,z#,w#)
	Local vec:TVec4=New TVec4
	vec.x=x
	vec.y=y
	vec.z=z
	vec.w=w
	Return vec
EndFunction

Rem
bbdoc:
End Rem
Function FlipQuat:TVec4(quat:TVec4)
	quat=quat.copy()
	quat.w=-quat.w
	Return quat
EndFunction

Rem
bbdoc:
End Rem
Function MulQuat:TVec4(quat0:TVec4,quat1:TVec4)
	Local ax#,ay#,az#,aw#,bx#,by#,bz#,bw#,a#,b#,c#,d#,e#,f#,g#,h#
	Local quat:TVec4
	ax=quat0.x
	ay=quat0.y
	az=quat0.z
	bw=quat0.w
	bx=quat1.x
	by=quat1.y
	bz=quat1.z
	bw=quat1.w	
	a#=(Aw#+Ax#)*(Bw#+Bx#)
	b#=(Az#-Ay#)*(By#-Bz#)
	c#=(Aw#-Ax#)*(By#+Bz#)
	d#=(Ay#+Az#)*(Bw#-Bx#)
	e#=(Ax#+Az#)*(Bx#+By#)
	f#=(Ax#-Az#)*(Bx#-By#)
	g#=(Aw#+Ay#)*(Bw#-Bz#)
	h#=(Aw#-Ay#)*(Bw#+Bz#)
	quat=New tvec4
	quat.w=b#+(-e#-f#+g#+h#)/2.0
	quat.x=a#-(e#+f#+g#+h#)/2.0
	quat.y=c#+(e#-f#+g#-h#)/2.0
	quat.z=d#+(e#-f#-g#+h#)/2.0
	Return quat
EndFunction

Rem
bbdoc:
End Rem
Function PointPlaneDistance:Float(point:TVec3,plane:TVec4)
	Return (plane.w-plane.x*point.x-plane.y*point.y-plane.z*point.z)
EndFunction

Rem
bbdoc:
End Rem
Function Dot:Float(vec0:TVec3,vec1:TVec3)
	Return vec0.x*vec1.x+vec0.y*vec1.y+vec0.z*vec1.z
EndFunction

Rem
bbdoc:
End Rem
Function Cross:TVec3(vec0:TVec3,vec1:TVec3)
	Local vec:TVec3=New tvec3
	vec.x=vec0.y*vec1.z-vec0.z*vec1.y
	vec.y=vec0.z*vec1.x-vec0.x*vec1.z
	vec.z=vec0.x*vec1.y-vec0.y*vec1.x
	Return vec
EndFunction


Rem
bbdoc:
End Rem
Type TMatrix

	Field grid#[4,4]

	Method LoadIdentity()
		grid[0,0]=1.0
		grid[1,0]=0.0
		grid[2,0]=0.0
		grid[3,0]=0.0
		grid[0,1]=0.0
		grid[1,1]=1.0
		grid[2,1]=0.0
		grid[3,1]=0.0
		grid[0,2]=0.0
		grid[1,2]=0.0
		grid[2,2]=1.0
		grid[3,2]=0.0
		grid[0,3]=0.0
		grid[1,3]=0.0
		grid[2,3]=0.0
		grid[3,3]=1.0
	End Method
	
	Method Copy:TMatrix()
		Local mat:TMatrix=New TMatrix
		mat.grid[0,0]=grid[0,0]
		mat.grid[1,0]=grid[1,0]
		mat.grid[2,0]=grid[2,0]
		mat.grid[3,0]=grid[3,0]
		mat.grid[0,1]=grid[0,1]
		mat.grid[1,1]=grid[1,1]
		mat.grid[2,1]=grid[2,1]
		mat.grid[3,1]=grid[3,1]
		mat.grid[0,2]=grid[0,2]
		mat.grid[1,2]=grid[1,2]
		mat.grid[2,2]=grid[2,2]
		mat.grid[3,2]=grid[3,2]
		mat.grid[0,3]=grid[0,3]
		mat.grid[1,3]=grid[1,3]
		mat.grid[2,3]=grid[2,3]
		mat.grid[3,3]=grid[3,3]
		Return mat
	EndMethod

	Method Overwrite(mat:TMatrix)
		grid[0,0]=mat.grid[0,0]
		grid[1,0]=mat.grid[1,0]
		grid[2,0]=mat.grid[2,0]
		grid[3,0]=mat.grid[3,0]
		grid[0,1]=mat.grid[0,1]
		grid[1,1]=mat.grid[1,1]
		grid[2,1]=mat.grid[2,1]
		grid[3,1]=mat.grid[3,1]
		grid[0,2]=mat.grid[0,2]
		grid[1,2]=mat.grid[1,2]
		grid[2,2]=mat.grid[2,2]
		grid[3,2]=mat.grid[3,2]
		grid[0,3]=mat.grid[0,3]
		grid[1,3]=mat.grid[1,3]
		grid[2,3]=mat.grid[2,3]
		grid[3,3]=mat.grid[3,3]
	EndMethod
	
	Method Inverse:TMatrix()
		Local mat:TMatrix=New TMatrix
		mat.loadidentity()
		Local tx#=0
		Local ty#=0
		Local tz#=0
		
	  	' The rotational part of the matrix is simply the transpose of the
	  	' original matrix.
	  	mat.grid[0,0] = grid[0,0]
	  	mat.grid[1,0] = grid[0,1]
	  	mat.grid[2,0] = grid[0,2]
	
		mat.grid[0,1] = grid[1,0]
		mat.grid[1,1] = grid[1,1]
		mat.grid[2,1] = grid[1,2]
		
		mat.grid[0,2] = grid[2,0]
		mat.grid[1,2] = grid[2,1]
		mat.grid[2,2] = grid[2,2]

		' The right column vector of the matrix should always be [ 0 0 0 1 ]
		' in most cases. . . you don't need this column at all because it'll 
		' never be used in the program, but since this code is used with GL
		' and it does consider this column, it is here.
		mat.grid[0,3] = 0 
		mat.grid[1,3] = 0
		mat.grid[2,3] = 0
		mat.grid[3,3] = 1
	
		' The translation components of the original matrix.
		tx = grid[3,0]
		ty = grid[3,1]
		tz = grid[3,2]
	
		' Result = -(Tm * Rm) To get the translation part of the inverse
		mat.grid[3,0] = -( (grid[0,0] * tx) + (grid[0,1] * ty) + (grid[0,2] * tz) )
		mat.grid[3,1] = -( (grid[1,0] * tx) + (grid[1,1] * ty) + (grid[1,2] * tz) )
		mat.grid[3,2] = -( (grid[2,0] * tx) + (grid[2,1] * ty) + (grid[2,2] * tz) )

		Return mat
	End Method
	
	' optimised
	Method Multiply(mat:TMatrix)
		Local m00# = grid#[0,0]*mat.grid#[0,0] + grid#[1,0]*mat.grid#[0,1] + grid#[2,0]*mat.grid#[0,2] + grid#[3,0]*mat.grid#[0,3]
		Local m01# = grid#[0,1]*mat.grid#[0,0] + grid#[1,1]*mat.grid#[0,1] + grid#[2,1]*mat.grid#[0,2] + grid#[3,1]*mat.grid#[0,3]
		Local m02# = grid#[0,2]*mat.grid#[0,0] + grid#[1,2]*mat.grid#[0,1] + grid#[2,2]*mat.grid#[0,2] + grid#[3,2]*mat.grid#[0,3]
		'Local m03# = grid#[0,3]*mat.grid#[0,0] + grid#[1,3]*mat.grid#[0,1] + grid#[2,3]*mat.grid#[0,2] + grid#[3,3]*mat.grid#[0,3]
		Local m10# = grid#[0,0]*mat.grid#[1,0] + grid#[1,0]*mat.grid#[1,1] + grid#[2,0]*mat.grid#[1,2] + grid#[3,0]*mat.grid#[1,3]
		Local m11# = grid#[0,1]*mat.grid#[1,0] + grid#[1,1]*mat.grid#[1,1] + grid#[2,1]*mat.grid#[1,2] + grid#[3,1]*mat.grid#[1,3]
		Local m12# = grid#[0,2]*mat.grid#[1,0] + grid#[1,2]*mat.grid#[1,1] + grid#[2,2]*mat.grid#[1,2] + grid#[3,2]*mat.grid#[1,3]
		'Local m13# = grid#[0,3]*mat.grid#[1,0] + grid#[1,3]*mat.grid#[1,1] + grid#[2,3]*mat.grid#[1,2] + grid#[3,3]*mat.grid#[1,3]
		Local m20# = grid#[0,0]*mat.grid#[2,0] + grid#[1,0]*mat.grid#[2,1] + grid#[2,0]*mat.grid#[2,2] + grid#[3,0]*mat.grid#[2,3]
		Local m21# = grid#[0,1]*mat.grid#[2,0] + grid#[1,1]*mat.grid#[2,1] + grid#[2,1]*mat.grid#[2,2] + grid#[3,1]*mat.grid#[2,3]
		Local m22# = grid#[0,2]*mat.grid#[2,0] + grid#[1,2]*mat.grid#[2,1] + grid#[2,2]*mat.grid#[2,2] + grid#[3,2]*mat.grid#[2,3]
		'Local m23# = grid#[0,3]*mat.grid#[2,0] + grid#[1,3]*mat.grid#[2,1] + grid#[2,3]*mat.grid#[2,2] + grid#[3,3]*mat.grid#[2,3]
		Local m30# = grid#[0,0]*mat.grid#[3,0] + grid#[1,0]*mat.grid#[3,1] + grid#[2,0]*mat.grid#[3,2] + grid#[3,0]*mat.grid#[3,3]
		Local m31# = grid#[0,1]*mat.grid#[3,0] + grid#[1,1]*mat.grid#[3,1] + grid#[2,1]*mat.grid#[3,2] + grid#[3,1]*mat.grid#[3,3]
		Local m32# = grid#[0,2]*mat.grid#[3,0] + grid#[1,2]*mat.grid#[3,1] + grid#[2,2]*mat.grid#[3,2] + grid#[3,2]*mat.grid#[3,3]
		'Local m33# = grid#[0,3]*mat.grid#[3,0] + grid#[1,3]*mat.grid#[3,1] + grid#[2,3]*mat.grid#[3,2] + grid#[3,3]*mat.grid#[3,3]
		grid[0,0]=m00
		grid[0,1]=m01
		grid[0,2]=m02
		'grid[0,3]=m03
		grid[1,0]=m10
		grid[1,1]=m11
		grid[1,2]=m12
		'grid[1,3]=m13
		grid[2,0]=m20
		grid[2,1]=m21
		grid[2,2]=m22
		'grid[2,3]=m23
		grid[3,0]=m30
		grid[3,1]=m31
		grid[3,2]=m32
		'grid[3,3]=m33
	EndMethod

	Method TForm4(position:TVec3,quaternion:TVec4,scale:TVec3)
		Local px#,py#,pz#,qx#,qy#,qz#,qw#,sx#,sy#,sz#
		px=position.x
		py=position.y
		pz=position.z
		qx=quaternion.x
		qy=quaternion.y
		qz=quaternion.z
		qw=quaternion.w
		sx=scale.x
		sy=scale.y
		sz=scale.z
		
		' identity
		grid[0,3]=0.0
		grid[1,3]=0.0
		grid[2,3]=0.0
		grid[3,3]=1.0
	
		' translate
		grid[3,0] = px#
		grid[3,1] = py#
		grid[3,2] = pz#
	
		' rotate + scale
		grid[0,0]=(1.0-2.0*qx*qx-2.0*qz*qz)*sx
		grid[1,0]=-(2.0*qz*qy-2.0*qw*qx)*sx
		grid[2,0]=-(2.0*qx*qy+2.0*qw*qz)*sx
		
		grid[0,1]=-(2.0*qz*qy+2.0*qw*qx)*sy
		grid[1,1]=(1.0-2.0*qy*qy-2.0*qx*qx)*sy
		grid[2,1]=-(2.0*qw*qy-2.0*qx*qz)*sy
		
		grid[0,2]=(2.0*qw*qz-2.0*qx*qy)*sz
		grid[1,2]=(2.0*qx*qz+2.0*qw*qy)*sz
		grid[2,2]=(1.0-2.0*qz*qz-2.0*qy*qy)*sz
		
	End Method
	
	Method Translate(vec:TVec3)
		grid[3,0] = grid#[0,0]*vec.x# + grid#[1,0]*vec.y# + grid#[2,0]*vec.z# + grid#[3,0]
		grid[3,1] = grid#[0,1]*vec.x# + grid#[1,1]*vec.y# + grid#[2,1]*vec.z# + grid#[3,1]
		grid[3,2] = grid#[0,2]*vec.x# + grid#[1,2]*vec.y# + grid#[2,2]*vec.z# + grid#[3,2]
	EndMethod

	Method Scale(vec:TVec3)
		grid[0,0]:*vec.x#
		grid[0,1]:*vec.x#
		grid[0,2]:*vec.x#
		grid[1,0]:*vec.y#
		grid[1,1]:*vec.y#
		grid[1,2]:*vec.y#
		grid[2,0]:*vec.z#
		grid[2,1]:*vec.z#
		grid[2,2]:*vec.z# 
	EndMethod
	
	Method ExtractPosition:TVec3()
		Local vec:TVec3
		vec=New tvec3
		vec.x=grid[3,0]
		vec.y=grid[3,1]
		vec.z=grid[3,2]
		Return vec
	EndMethod
	
	'Don't know how to extract the scale relative to the rotation.  Maybe rotate by the reversed rotation and get the scale?
	'Method ExtractScale()
		'x=Sqr(grid[0,0]*grid[0,0]+grid[0,1]*grid[0,1]+grid[0,2]*grid[0,2])
		'y=Sqr(grid[1,0]*grid[1,0]+grid[1,1]*grid[1,1]+grid[1,2]*grid[1,2])
		'z=Sqr(grid[2,0]*grid[2,0]+grid[2,1]*grid[2,1]+grid[2,2]*grid[2,2])
	'EndMethod
	
	Method ExtractQuat:TVec4()
		Local x#,y#,z#,w#
		Local fourWSquaredMinus1:Float = +grid[0,0]+grid[1,1]+grid[2,2]
		Local fourXSquaredMinus1:Float = +grid[0,0]-grid[1,1]-grid[2,2]
		Local fourYSquaredMinus1:Float = +grid[1,1]-grid[0,0]-grid[2,2]
		Local fourZSquaredMinus1:Float = +grid[2,2]-grid[0,0]-grid[1,1]
		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 mult:Float=0.25/biggestValue
		Select BiggestIndex
			Case 0
				w#=BiggestValue
				x#=(grid[1,2]-grid[2,1])*mult
				y#=-(grid[2,0]-grid[0,2])*mult
				z#=-(grid[0,1]-grid[1,0])*mult
			Case 1
				x#=-BiggestValue
				w#=-( grid[1,2] - grid[2,1] )*mult
				y#=( grid[0,1] + grid[1,0] )*mult
				z#=( grid[2,0] + grid[0,2] )*mult
			Case 2
				y#=BiggestValue
				w#=-(grid[2,0]-grid[0,2])*mult
				x#=-(grid[0,1]+grid[1,0])*mult
				z#=(grid[1,2]+grid[2,1])*mult
			Case 3
				z#=-BiggestValue
				w#=(grid[0,1]-grid[1,0])*mult
				x#=(grid[2,0]+grid[0,2])*mult
				y#=-(grid[1,2]+grid[2,1])*mult
		EndSelect
		If w<-0.0
			x=-x
			y=-y
			z=-z
			w=-w
		EndIf
		Return vec4(x,y,z,w)
	EndMethod
	
	Method ExtractEuler:TVec3()
		Local a#,b#,x#,y#,z#
		x=-ATan2( grid[2,1],Sqr( grid[2,0]*grid[2,0]+grid[2,2]*grid[2,2] ) )
		a#=grid[2,0]
		b#=grid[2,2]
		If a#<=0.0001 And a#>=-0.0001 Then a#=0
		If b#<=0.0001 And b#>=-0.0001 Then b#=0
		y=-ATan2(a#,b#)
		a#=grid[0,1]
		b#=grid[1,1]
		If a#<=0.0001 And a#>=-0.0001 Then a#=0
		If b#<=0.0001 And b#>=-0.0001 Then b#=0
		z=ATan2(a#,b#)
		Return vec3(x,y,z)
	EndMethod
	
	Method Rotate4(quat:TVec4)
		Local qx#,qy#,qz#,qw#
		
		'Scale removed for speed / doesn't work right anyways
		'sx#=1'Sqr(grid[0,0]*grid[0,0]+grid[0,1]*grid[0,1]+grid[0,2]*grid[0,2])
		'sy#=1'Sqr(grid[1,0]*grid[1,0]+grid[1,1]*grid[1,1]+grid[1,2]*grid[1,2])
		'sz#=1'Sqr(grid[2,0]*grid[2,0]+grid[2,1]*grid[2,1]+grid[2,2]*grid[2,2])
		
		quat=MulQuat(ExtractQuat(),quat)
		qx#=quat.x
		qy#=quat.y
		qz#=quat.z
		w#=quat.w
		
		grid[0,3]=0.0
		grid[1,3]=0.0
		grid[2,3]=0.0
		grid[3,3]=1.0
		grid[0,0]=(1.0-2.0*qx*qx-2.0*qz*qz)'*sx
		grid[1,0]=-(2.0*qz*qy-2.0*w*qx)'*sx
		grid[2,0]=-(2.0*qx*qy+2.0*w*qz)'*sx
		grid[0,1]=-(2.0*qz*qy+2.0*w*qx)'*sy
		grid[1,1]=(1.0-2.0*qy*qy-2.0*qx*qx)'*sy
		grid[2,1]=-(2.0*w*qy-2.0*qx*qz)'*sy
		grid[0,2]=(2.0*w*qz-2.0*qx*qy)'*sz
		grid[1,2]=(2.0*qx*qz+2.0*w*qy)'*sz
		grid[2,2]=(1.0-2.0*qz*qz-2.0*qy*qy)'*sz
	EndMethod
	
	Method Rotate(euler:TVec3)
		
		Local rx#,ry#,rz#
		Local cos_ang#,sin_ang#
	
		rx=euler.x
		ry=euler.y
		rz=euler.z
		
		' yaw
		cos_ang#=Cos(ry#)
		sin_ang#=Sin(ry#)
	
		Local m00# = grid#[0,0]*cos_ang + grid#[2,0]*-sin_ang#
		Local m01# = grid#[0,1]*cos_ang + grid#[2,1]*-sin_ang#
		Local m02# = grid#[0,2]*cos_ang + grid#[2,2]*-sin_ang#

		grid[2,0] = grid#[0,0]*sin_ang# + grid#[2,0]*cos_ang
		grid[2,1] = grid#[0,1]*sin_ang# + grid#[2,1]*cos_ang
		grid[2,2] = grid#[0,2]*sin_ang# + grid#[2,2]*cos_ang

		grid[0,0]=m00#
		grid[0,1]=m01#
		grid[0,2]=m02#
		
		' pitch
		cos_ang#=Cos(rx#)
		sin_ang#=Sin(rx#)
	
		Local m10# = grid#[1,0]*cos_ang + grid#[2,0]*sin_ang
		Local m11# = grid#[1,1]*cos_ang + grid#[2,1]*sin_ang
		Local m12# = grid#[1,2]*cos_ang + grid#[2,2]*sin_ang

		grid[2,0] = grid#[1,0]*-sin_ang + grid#[2,0]*cos_ang
		grid[2,1] = grid#[1,1]*-sin_ang + grid#[2,1]*cos_ang
		grid[2,2] = grid#[1,2]*-sin_ang + grid#[2,2]*cos_ang

		grid[1,0]=m10
		grid[1,1]=m11
		grid[1,2]=m12
		
		' roll
		cos_ang#=Cos(rz#)
		sin_ang#=Sin(rz#)

		m00# = grid#[0,0]*cos_ang# + grid#[1,0]*sin_ang#
		m01# = grid#[0,1]*cos_ang# + grid#[1,1]*sin_ang#
		m02# = grid#[0,2]*cos_ang# + grid#[1,2]*sin_ang#

		grid[1,0] = grid#[0,0]*-sin_ang# + grid#[1,0]*cos_ang#
		grid[1,1] = grid#[0,1]*-sin_ang# + grid#[1,1]*cos_ang#
		grid[1,2] = grid#[0,2]*-sin_ang# + grid#[1,2]*cos_ang#

		grid[0,0]=m00#
		grid[0,1]=m01#
		grid[0,2]=m02#
	
	EndMethod

	Method RotatePitch(ang#)
	
		Local cos_ang#=Cos(ang#)
		Local sin_ang#=Sin(ang#)
	
		Local m10# = grid#[1,0]*cos_ang + grid#[2,0]*sin_ang
		Local m11# = grid#[1,1]*cos_ang + grid#[2,1]*sin_ang
		Local m12# = grid#[1,2]*cos_ang + grid#[2,2]*sin_ang

		grid[2,0] = grid#[1,0]*-sin_ang + grid#[2,0]*cos_ang
		grid[2,1] = grid#[1,1]*-sin_ang + grid#[2,1]*cos_ang
		grid[2,2] = grid#[1,2]*-sin_ang + grid#[2,2]*cos_ang

		grid[1,0]=m10
		grid[1,1]=m11
		grid[1,2]=m12

	EndMethod
	
	Method RotateYaw(ang#)
	
		Local cos_ang#=Cos(ang#)
		Local sin_ang#=Sin(ang#)
	
		Local m00# = grid#[0,0]*cos_ang + grid#[2,0]*-sin_ang#
		Local m01# = grid#[0,1]*cos_ang + grid#[2,1]*-sin_ang#
		Local m02# = grid#[0,2]*cos_ang + grid#[2,2]*-sin_ang#

		grid[2,0] = grid#[0,0]*sin_ang# + grid#[2,0]*cos_ang
		grid[2,1] = grid#[0,1]*sin_ang# + grid#[2,1]*cos_ang
		grid[2,2] = grid#[0,2]*sin_ang# + grid#[2,2]*cos_ang

		grid[0,0]=m00#
		grid[0,1]=m01#
		grid[0,2]=m02#

	End Method
	
	Method RotateRoll(ang#)
	
		Local cos_ang#=Cos(ang#)
		Local sin_ang#=Sin(ang#)

		Local m00# = grid#[0,0]*cos_ang# + grid#[1,0]*sin_ang#
		Local m01# = grid#[0,1]*cos_ang# + grid#[1,1]*sin_ang#
		Local m02# = grid#[0,2]*cos_ang# + grid#[1,2]*sin_ang#

		grid[1,0] = grid#[0,0]*-sin_ang# + grid#[1,0]*cos_ang#
		grid[1,1] = grid#[0,1]*-sin_ang# + grid#[1,1]*cos_ang#
		grid[1,2] = grid#[0,2]*-sin_ang# + grid#[1,2]*cos_ang#

		grid[0,0]=m00#
		grid[0,1]=m01#
		grid[0,2]=m02#

	EndMethod
	
EndType



Chroma(Posted 2007) [#4]
Josh, are you using Vec4 in place of a Quaternion?

EDIT: Nm, I see that you are. That's pretty clever.

Type TVec2
   Field x:Float, y:Float
End Type

Type TVec3 Extends TVec2
   Field z:Float
End Type

Type TQuat Extends TVec3
   Field w:Float
End Type

Function Vec2:TVec2(x#=0, y#=0)
   Local v:TVec2 = New TVec2
   v.x = x
   v.y = y
   Return v
End Function

Function Vec3:TVec3(x#=0, y#=0, z#=0)
   Local v:TVec3 = New TVec3
   v.x = x
   v.y = y
   v.z = z
   Return v
End Function

Function Quat:TQuat(w#=0, x#=0, y#=0, z#=0)
   Local q:TQuat = New TQuat
   q.w = w
   q.x = x
   q.y = y
   q.z = z
   Return q
End Function


I love it man!! :)


JoshK(Posted 2007) [#5]
I also use vec4's for planes and colors. That's why I call it a vec4 and not a quat:
Type TEntity

Field color:TVec4=New TVec4

Method SetColor(r,g,b,a=255)
color.x=float(r)/255.0
color.y=float(g)/255.0
color.z=float(b)/255.0
color.w=float(a)/255.0
EndMethod

glLightfv GL_LIGHT0,GL_DIFFUSE,entity.color


All that GLSL makes me think in terms of vectors. My advice is to convert everything to OO vectors, because if you mix OO vectors with a lot of individual floats you won't get the full advantage of how much it shrinks your code.


The r0nin(Posted 2007) [#6]
One question about your code (which is veeeeery helpful!). When you create a Tform4 Matrix, how do you get the original quaternion for the original position?

In other words, I have an object at (0,0,0) that is pointing (in my arbitrary system) away from the camera (the 0 yaw) with an up-pitch of 20 degrees and no roll and at the original scale.

So I create a Tvec3 with 0,0,0 for the position to pass, and a TVec3 with 1,1,1 for the scale to pass. How do I figure out what to store in the TVec4 to pass as the quarternion? Would I just EulertoQuat (20,0,0) and pass the results?


JoshK(Posted 2007) [#7]
Store a quat for the entity and only update it when the use inputs a euler rotation.


The r0nin(Posted 2007) [#8]
Yeah... but what quat do I store? How do you "baseline" quats? What does 0,0,0,0 describe? If I knew what the baseline was for a quat, I could just update with euler as I created the object [start with a quat at 0,0,0,0 and then rotatepitch(-20)]. But does 0,0,0,0 equal pointing away from me down the Z axis? Or do I need to decide what 0,0,0,0 is and then translate whatever the result is into that frame of reference?


JoshK(Posted 2007) [#9]
0,0,0,1


Chroma(Posted 2007) [#10]
Josh, I found out you can't have the same method names for each Type this way. For example:

v2:TVec2 = TVec2.Create()
v3:TVec3 = TVec3.Create()

It throws an error "Compile Error Overriding method differs by type". That kinda sucks...unless there's something I'm missing.


H&K(Posted 2007) [#11]
I found out you can't have the same method names for each Type this way
Yes you can, post the code, cos you've made a mistake somewhere.
TVec3 IS a TVec2 so isnt a different type
Type TVec2

   Field x:Float, y:Float

	Function Create:tVec2()
		Return New tvec2
	EndFunction
	
End Type

Type TVec3 Extends TVec2

   Field z:Float

	Function Create:tVec3()
		Return New tvec3
	EndFunction

End Type

v2:TVec2 = TVec2.Create()
v3:TVec3 = TVec3.Create()
This works. What have you done differently


Chroma(Posted 2007) [#12]
Hmm...lemme check.

Ok, I have no idea. But I was getting the error above. Now I can't duplicate it. :(

EDIT: Ok I duplicated it. Posting the code in a couple minutes.


Chroma(Posted 2007) [#13]
Ok here's the problem code:

Type TVec2

   Field x:Float, y:Float

	Function Create:tVec2()
		Return New tvec2
	EndFunction
	
	Method Set(x#, y#)
		self.x = x
		self.y = y
	End Method
	
End Type

Type TVec3 Extends TVec2

   Field z:Float

	Function Create:tVec3()
		Return New tvec3
	EndFunction
	
	Method Set(x#, y#, z#)
		self.x = x
		self.y = y
		self.z = z
	End Method

End Type

v2:TVec2 = TVec2.Create()
v3:TVec3 = TVec3.Create()



degac(Posted 2007) [#14]
the problem is in the Method Set: you can not ovveride a method with different numbers of parameters
A possibile solution is
type tvec2
...
Method Set(x#, y#,z#=0)
		self.x = x
		self.y = y
	End Metho
...



Warpy(Posted 2007) [#15]
Another vote for built-in vector types. I'm tired of writing my own every time, so I am!


JoshK(Posted 2007) [#16]
I like vec3(x,y,z) better than TVec3.Create().

You could do this:
Type TVec2

   Field x:Float, y:Float

	Function Create:tVec2()
		Return New tvec2
	EndFunction
	
	Method Set(x#, y#, z#=0)
		self.x = x
		self.y = y
	End Method
	
End Type

Type TVec3 Extends TVec2

   Field z:Float

	Function Create:tVec3()
		Return New tvec3
	EndFunction
	
	Method Set(x#, y#, z#)
		self.x = x
		self.y = y
		self.z = z
	End Method

End Type


Or this:
Type TVec2

   Field x:Float, y:Float

	Function Create:tVec2()
		Return New tvec2
	EndFunction
	
	Method Set(vec#[])
		self.x = vec[0]
		self.y = vec[1]
	End Method
	
End Type

Type TVec3 Extends TVec2

   Field z:Float

	Function Create:tVec3()
		Return New tvec3
	EndFunction
	
	Method Set(vec#[])
		self.x = vec[0]
		self.y = vec[1]
		self.z = vec[2]
	End Method

End Type

vec3=NewTVec3
vec3.set [0.0,1.0,2.0]



Wiebo(Posted 2007) [#17]
Oh, this is nice. I have a lot of this already but there are some nice additions to my code in there. Thanks Josh (and others for the additional code :) )


Chroma(Posted 2007) [#18]
I think stuff that's easy to remember is the good too. Like when you say a = b + c. You're saying to yourself:

a equals b plus c

Why not name the functions like that to make em easy to remember?

a = b.plus(c)
a = b.dividedby(c)
a = b.times(c)
a = b.plus(c.minus(d))

You get the idea. I converted my vec lib to this cuz it's more intuitive.




JoshK(Posted 2007) [#19]
Here's what I would really like:

vec1:TVec3=New TVec3
vec2:TVec3=New TVec3

vec3:TVec3 = vec1 + vec2


deps(Posted 2007) [#20]
Operator overloading would be a cool feature for a future BMAX update! :)


JoshK(Posted 2007) [#21]
I also prefer this:

TVec3 vec

to this:

Local vec:TVec3=New TVec3


marksibly(Posted 2007) [#22]
Hi,

I do in fact have a set math types, but they are far from complete - there are plenty of Throw "TODO"s in there and a significant amount of untested code.

So I don't want to release them as 'official' modules just yet, but perhaps they could be released as sort of a community project where we can all contribute to making them bulletproof?


LarsG(Posted 2007) [#23]
Sounds like a nice additional feature to the BlitzMax language, Mark.

And it would be cool if you would try to do such a release.
It might just go better than expected, and could even be considered for future additions/features as well.. (here's hoping!)


JoshK(Posted 2007) [#24]
Thanks, if you just posted some unofficial code, I might learn something new from it. The MiniB3D code had a few good ideas I used to improve my matrix math.


Gabriel(Posted 2007) [#25]
Releasing them and making them into a community solution sounds like a good idea.

I also agree with the sentiment about operator overloading. Vector, Quaternion and Matrix classes just cry out for operator overloading, in my book. I have my classes as Chroma has his, but operator overloading is the ideal for me.


marksibly(Posted 2007) [#26]
Hi,

OK, here's my current geom.bmx file - all subject to change etc.

A couple of points:

* Designed to match b3d orientation.

* Objects are designed to be 'immutable' - ie: once created, it's generally expected they're not gonna be modified. If you stick to this rule, you can safely share objects around without having to copy them in case they're modified.

* I've avoided embedding objects-within-objects for speed reasons - eg: TLine could have origin:TVec3 and delta:TVec3 members, but it'd be a bit slower. Ditto arrays.

* Although TMat4 is a 4x4 matrix, many methods assume it's also affine. Used to call them AffineInverse() etc, but it turned out to be most of them.




dmaz(Posted 2007) [#27]
hmm... so when you do this
Field x#,y#,z#,dx#,dy#,dz# {attribute}

attribute is defined for each variable? dang, now I might have to start using the variable type shortcuts again!


JoshK(Posted 2007) [#28]
Thanks!


Chroma(Posted 2007) [#29]
Thanks Mark. This is lovely!


JoshK(Posted 2008) [#30]
I'm going to revamp my matrix code in Leadwerks Engine 2, since I will have a fresh start, but for right now I am not sure how to scale matrices. This is using the MiniB3D-style matrix. Is this correct? I am not sure if this is the right way to scale a matrix with rotation:

	Method Scale(vec:TVec3)
		grid[0,0]:*vec.x#
		grid[0,1]:*vec.x#
		grid[0,2]:*vec.x#
		grid[1,0]:*vec.y#
		grid[1,1]:*vec.y#
		grid[1,2]:*vec.y#
		grid[2,0]:*vec.z#
		grid[2,1]:*vec.z#
		grid[2,2]:*vec.z# 
	EndMethod



skidracer(Posted 2008) [#31]
as long as the output matches:

Method TimesScale:TMat4(v:TVec3)
  Return Times(TMat4.FromScale(v))
End Method


that's probably an ok optimiztion


JoshK(Posted 2008) [#32]
Hard to tell what is happening. If I have a complex hierarchy with any scaling, the skeleton gets totally screwed up.


JoshK(Posted 2008) [#33]
There is an error in the TQuat Times() method as written above. Replace it with this:
	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


I chose to make the TVec3 class NOT an extension of a TVec3, because I want to be able to multiply two vec4's together.

I would really like to be able to do this in BMX:

color = skycolor * 0.5 + vec3( 1.0, 0.5 ,0.5)

I have gotten so used to GLSL, and the vector and matrix syntax is really nice.


JoshK(Posted 2008) [#34]
I was having some problems, and I found that Mark's Mat4 -> Quat code is not as accurate as it could be.

I tested this by randomly rotating a mesh with a child entity all around many different ways, and orienting the child globally. It seems to be solid. I left the old code in, commented out:
	Method Rotation:TQuat()
		Rem
		Local sc:TVec3=Scale()
		Local iv:TVec3=Vec3(ix,iy,iz).Scale(1/sc.x)
		Local jv:TVec3=Vec3(jx,jy,jz).Scale(1/sc.y)
		Local kv:TVec3=Vec3(kx,ky,kz).Scale(1/sc.z)
		Local x#,y#,z#,w#
		Local t#=iv.x+jv.y+kv.z+1
		If t>0
			t=Sqr(t)*2
			x=(kv.y-jv.z)/t
			y=(iv.z-kv.x)/t
			z=(jv.x-iv.y)/t
			w=t/4
		Else If iv.x>jv.y And iv.x>kv.z
			t=Sqr(iv.x-jv.y-kv.z+1)*2.0
			x=t/4
			y=(jv.x+iv.y)/t
			z=(iv.z+kv.x)/t
			w=(kv.y-jv.z)/t
		Else If jv.y>kv.z
			t=Sqr(jv.y-kv.z-iv.x+1)*2.0
			x=(jv.x+iv.y)/t
			y=t/4
			z=(kv.y+jv.z)/t
			w=(iv.z-kv.x)/t
		Else
			t=Sqr(kv.z-jv.y-iv.x+1)*2.0
			x=(iv.z+kv.x)/t
			y=(kv.y+jv.z)/t
			z=t/4
			w=(jv.x-iv.y)/t
		EndIf
		Return Quat( x,y,z,w )
		EndRem
		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 mult:Float=0.25/biggestValue
		Local x#,y#,z#,w#
		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
		'If KeyHit(KEY_L) Notify BiggestIndex
		Return quat(-x,y,z,w)

		
		
		Rem
		'testing code:
		Global mode=0
		If KeyHit(KEY_N)
			mode:+1
			'Notify mode
		EndIf
		If KeyHit(KEY_L) Notify mode
		Select mode		
			Case 0 Return quat(x,y,z,w)
			Case 1 Return quat(x,y,z,-w)
			Case 2 Return quat(x,-y,z,w)
			Case 3 Return quat(x,y,-z,w)
			Case 4 Return quat(-x,-y,z,w)
			Case 5 Return quat(x,-y,-z,w)
			Case 6 Return quat(-x,y,z,w)
		EndSelect
		EndRem
		
	EndMethod



JoshK(Posted 2008) [#35]
Very strange. If I declare the x,y,z,w variables in the middle of the function I get errors. If I declare them at the top there are no errors.