usable camera

BlitzMax Forums/BlitzMax OpenGL Programming/usable camera

Hi,

I started ripping the basic out of my GL code last night so you could see how my camera & hierarchy works. Here's something to mess about with, I'll add in the get x/y/z pitch/yaw/roll stuff later.

Check the source for keys to use in this demo, and have a good mess abotu and see how the parenting looks & works. I've tried to keep it as close to Blitz3Ds as possible.

Which reminds me, you really have to appreciate Marks entity functions in Blitz3D, I've NEVER come across such a cool system in any sample code out there on the net.

Well done BRL! :P

Sorry for sloppy code, I'm absolutely full of flu and can't even enjoy a holiday beer because of medication, BLEH!!! :)

Oh, and I use 2 space tabs in my IDE...

Framework brl.basic
Import brl.polledinput
'Import pub.glew
Import brl.glgraphics


Const LOCALSPACE:Int		=	1
Const PARENTSPACE:Int		=	2
Const WORLDSPACE:Int		=	3

InitGL(1024,768,0,0)


Local cam1:tCamera = tCamera.Create()
Local cam2:tCamera = tCamera.Create()

cam1.name="camera1"
cam2.name="camera2"


'split screen viewports
cam1.setVP 0,0,512,768
cam2.setVP 512,0,512,768

cam2.setpos 1,3,10

Local cube1:tMesh = New tMesh
Local cube2:tMesh = New tMesh
Local cube3:tMesh = New tMesh

cube2.setPos 2,0,0
cube2.setParent cube1,True
cube3.setPos 2,2,0
cube3.setParent cube2,True

cube1.setPos 0,0,-5
cube1.turn 0,45,0
cube2.turn 0,-45,0


Local piv:tPivot = New tPivot
cam2.setParent piv,True
cam1.setPos 0,2,0


cube3.setrot 10,20,30
Print cube3.getpitch()
Print cube3.getyaw()
Print cube3.getroll()

Local coordSys:Int = 1
Local c2scale:Float = 1.0

Local sine:Float=0

While Not KeyHit(KEY_ESCAPE)


	sine:+2
	cube3.setscaling 1+Sin(sine)*.5,1+Sin(sine+90)*.5,1
	
	cube2.setScaling c2scale,c2scale,c2scale
	
	cam1.SetRot(-(400-MouseY())*.75,(200-MouseX())*.75,0)



	If KeyHit(KEY_SPACE)
		coordSys:+1
		If coordSys>3 coordsys = 1
	End If

	If KeyDown(KEY_OPENBRACKET) c2scale:- .01
	If KeyDown(KEY_CLOSEBRACKET) c2scale:+ .01
		
	If KeyDown(KEY_T) cube1.move 0,0,-.1, coordSys
	If KeyDown(KEY_G) cube1.move 0,0,.1, coordSys
	If KeyDown(KEY_F) cube1.move -.1,0,0, coordSys
	If KeyDown(KEY_H) cube1.move .1,0,0, coordSys

	If KeyDown(KEY_Q) piv.turn 0,-1,0
	If KeyDown(KEY_E) piv.turn 0,1,0

	If KeyDown(KEY_W) cam1.move 0,0,-.1', coordSys
	If KeyDown(KEY_S) cam1.move 0,0,.1', coordSys
	If KeyDown(KEY_A) cam1.move -.1,0,0', coordSys
	If KeyDown(KEY_D) cam1.move .1,0,0', coordSys
	
	If KeyHit(KEY_1) cube1.setParent cam1,True 'parent
	If KeyHit(KEY_2) cube1.setParent 0,True	'unparent

	If MouseDown(1) cube2.turn 1,0,0,coordSys
	If MouseDown(2) cube2.turn 0,1,0,coordSys
		
	piv.turn 0,.5,0
	cam2.pointat cam1
	Local sx:Float, sy:Float
	
	cam2.ScreenProject cam1,sx,sy
	
	RenderWorld()
	
	glColor3f 1,1,1
	glDisable GL_LIGHTING
	glViewport 0,0,GraphicsWidth(),GraphicsHeight()
	GLDrawText "Coordinate System: "+coordSys,0,0
	GLDrawText "cube2 world position: "+cube2.getX(3)+" , "+cube2.getY(3)+" , "+cube2.getZ(3),0,20
	GLDrawText "cam1 world rotation: "+cam1.getPitch(3)+" , "+cam1.getYaw(3)+" , "+cam1.getRoll(3),0,40
	
	GLDrawText "Cam 1",0,748
	GLDrawText "Cam 2",512,748
	
	GLDrawText "test",sx,sy
	

	GLDrawText "SPACE changes coord sys: 1=local,2=parent,3=world",0,80	
	GLDrawText "w,s,a,d,mouse controls Cam1. '1' parents cube1 to Cam1, '2' unparents it",0,100	
	
	Flip True
Wend




Function RenderWorld()
	Local camera:tCamera
	Local ent:tEntity


	For camera = EachIn tCamera.cameraList
		camera.Use()
	
		
		'--------------
		'Render Entitys
		'--------------
		For ent = EachIn tEntity.entityList
			'Only pass 'unparented' and 'undrawn' Entitiys to the RenderEntity()
			'Function, child Entitys get rendered recursively within the function
			If ent.parent = Null
				RenderEntity(ent)
			End If
		Next
		
		
	Next

End Function





Function RenderEntity(obj:Object)	
	'Save the Current MODELVIEW_MATRIX
	glPushMatrix()



	'--------------------------------------------
	'Multiply the current MODELVIEW_MATRIX by
	'this entitys Local space Matrix, then apply
	'any scaling. Anything drawn after this will
	'be drawn in this Entitys 'local space'
	'--------------------------------------------
	glMultMatrixf tEntity(obj).mat.m


	'-----------------------------
	'Only apply the Scaling Matrix
	'if it is non-identity
	'-----------------------------
	If tEntity(obj).ScaleMode > 0 Then glMultMatrixf tEntity(obj).smat.m



	'-----------------------------------------------
	'Vertex lighting gets affected if the modelview
	'matrix is scaled.
	'If the Scaling is Uniform (m[0] = m[5] = m[10])
	'we can use GL_RESCALE_NORMAL, which is faster
	'than using GL_NORMALIZE
	'
	'Child entitys MUST check their parent chain for
	'any possible scaling, as it will affect them
	'
	'NOTE: gEntity.ScaleMode is set during calls to
	'tEntity.SetScaling()
	'-----------------------------------------------
	Local ThisScaleMode:Int = tEntity(obj).ScaleMode
	
	Select ThisScaleMode
		Case 1
			'If all Scaling Axis values are equal
			'Use Uniform Normal Scaling for speed
			'
			'Parent scaling should be checked for
			glEnable GL_RESCALE_NORMAL

		Case 2
			'Non-Uniform Scaling, use slower
			'Normalization method
			'Parent scaling need not be checked
			glEnable GL_NORMALIZE
		
		Case 0
			'Check if any parents are scaled
			Local pa:tEntity = tEntity(obj).parent
			
			While pa And ThisScaleMode < 2
				If pa.smat.m[0] <> 1.0 Or pa.smat.m[5] <> 1.0 Or pa.smat.m[10] <> 1.0
					ThisScaleMode = 1
				End If
				
				If ThisScaleMode = 1
				
				End If
				
				pa = pa.parent
			Wend
				
			If ThisScaleMode = 1
				glEnable GL_RESCALE_NORMAL
			Else If ThisScaleMode = 2
				glEnable GL_NORMZLIZE
			End If
	End Select



	'Call the Entitys Draw Method here
	Select obj

		Case tMesh(obj)
			tMesh(obj).DrawEntity()
	
		Case tPivot(obj)
			tPivot(obj).DrawEntity()
		
		Case tCamera(obj)
			tCamera(obj).DrawEntity()
	End Select
	
	'---------------------------
	'Disable any Normalize modes
	'---------------------------
	Select ThisScaleMode
		Case 1
			glDisable GL_RESCALE_NORMAL
		Case 2
			glDisable GL_NORMALIZE
	End Select

	'-------------------------
	'Process any Child entitys
	'-------------------------
	For Local ent:tEntity = EachIn tEntity(obj).childList
		RenderEntity(ent)
	Next
	
	'----------------------------
	'Restore the MODELVIEW_MATRIX
	'----------------------------
	glPopMatrix()

End Function




Function InitGl(w,h,d,hz)
	GLGraphics w,h,d,hz
	'glewInit()
	

	glEnable GL_DEPTH_TEST							' Enable depth testing (Z sorting of pixels)
	glDepthFunc GL_LESS	' Only draw pixels if the fragment/pixel Z depth, is LESS than the Zbuffer value

	glFrontFace GL_CW									' Draw polys facing viewer Clockwise
	'glEnable GL_CULL_FACE

	glShadeModel GL_SMOOTH
	glHint GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST
	
	
	Local position:Float[] = [10.0,10.0,5.0]
		
	glEnable GL_LIGHT0
	glLightfv GL_LIGHT0, GL_POSITION, position




	
End Function





















Type tEntity
	Global entitylist:TList = CreateList()
	
	Field parent:tEntity 'this entitys parent
	Field parentLink:TLink
	Field name:String

	Field childList:TList = CreateList() 'list of child entitys (entitys whos parent is THIS entity)
	
	Field mat:mat4 = New mat4  'stores *LOCAL* orientation & position
	Field smat:mat4 = New mat4 'scaling matrix


	'Used to tell GL if various scale modes need to be enabled
	'If scaling is used, vertex normals are scaled also, this can
	'affect the lighting of geometry. GL caters for this by having
	'2 special auto-normalizing modes.
	'if the scaling is uniform (x scale = y scale = z scale) glEnable(GL_RESCALE_NORMAL) 
	'can be used, it's faster than glEnable(GL_NORMALIZE), which should be used
	Field scaleMode:Int = 0


	Method GetX:Float(world:Int = 1)
		If world = PARENTSPACE Or parent = Null Then world = LOCALSPACE
		Select world
			Case LOCALSPACE
				Return mat.m[12]
			
			Case WORLDSPACE
				Local v:Float
				Local pa:tEntity = parent
				Local tmp:mat4 = New mat4
				tmp.Copy(mat)

				While pa
					tmp.Multiply(pa.mat)
					pa = pa.parent
				Wend
				
				Return tmp.m[12]
		End Select
	End Method

	Method GetY:Float(world:Int = 1)
		If world = PARENTSPACE Or parent = Null Then world = LOCALSPACE
		Select world
			Case LOCALSPACE
				Return mat.m[13]
			
			Case WORLDSPACE
				Local v:Float
				Local pa:tEntity = parent
				Local tmp:mat4 = New mat4
				tmp.Copy(mat)

				While pa
					tmp.Multiply(pa.mat)
					pa = pa.parent
				Wend
				
				Return tmp.m[13]
		End Select
	End Method
	
	Method GetZ:Float(world:Int = 1)
		If world = PARENTSPACE Or parent = Null Then world = LOCALSPACE
		Select world
			Case LOCALSPACE
				Return mat.m[14]
			
			Case WORLDSPACE
				Local v:Float
				Local pa:tEntity = parent
				Local tmp:mat4 = New mat4
				tmp.Copy(mat)

				While pa
					tmp.Multiply(pa.mat)
					pa = pa.parent
				Wend
				
				Return tmp.m[14]
		End Select
	End Method


	Method GetPitch:Float(world:Byte=1)
		'If this entity has no parent then it's world rotation = local rotation
		If world = LOCALSPACE Or parent = Null Return -ASin(mat.m[9])
		
		Local tmp:Mat4 = New mat4
		tmp.Copy(mat)
		Local par:tEntity

		If world = PARENTSPACE
			par = parent
			tmp.Multiply(par.mat)
			Return -ASin(tmp.m[9])

		Else 'WORLDSPACE
			par = parent
			While par
				tmp.Multiply(par.mat)
				par = par.parent
			Wend
			Return -ASin(tmp.m[9])
		End If
	End Method

	Method GetYaw:Float(world:Byte=1)
		'If this entity has no parent then it's world rotation = local rotation
		If world = LOCALSPACE Or parent = Null Return ATan2(mat.m[8], mat.m[10])

		Local tmp:Mat4 = New mat4
		tmp.Copy(mat)
		Local par:tEntity

		If world = PARENTSPACE
			par = parent
			tmp.Multiply(par.mat)
			Return ATan2(tmp.m[8], tmp.m[10])
		
		Else 'WORLDSPACE
			par = parent
			While par
				tmp.Multiply(par.mat)
				par = par.parent
			Wend
			Return ATan2(tmp.m[8], tmp.m[10])
		End If
	End Method

	Method GetRoll:Float(world:Byte=1)
		'If this entity has no parent then it's world rotation = local rotation
		If world = LOCALSPACE Or parent=Null Return ATan2(mat.m[1], mat.m[5])
		
		Local tmp:Mat4 = New mat4
		tmp.Copy(mat)
		Local par:tEntity
				
		If world = PARENTSPACE
			par = parent
			tmp.Multiply(par.mat)
			Return ATan2(tmp.m[1], tmp.m[5])		

		Else 'WORLDSPACE
			par = parent
			While par
				tmp.Multiply(par.mat)
				par = par.parent
			Wend
			Return ATan2(tmp.m[1], tmp.m[5])
		End If
	End Method




	Method SetPos(px:Float, py:Float, pz:Float, world:Int = 1)
		If world = LOCALSPACE Or world = PARENTSPACE Or parent = Null
			mat.m[12] = px
			mat.m[13] = py
			mat.m[14] = pz
		Else
			Local par:tEntity = parent
			SetParent(0, 1)
			mat.m[12] = px
			mat.m[13] = py
			mat.m[14] = pz
			SetParent(par, 1)
		End If
	End Method

	Method Move(px:Float,py:Float,pz:Float,world:Int=1)
		Local v:Vec3 = Vec3.Create()
		Local tmat:Mat4 = New mat4
		Local pa:tEntity
		

		If world = PARENTSPACE
			If Not parent Then world = WORLDSPACE
		End If
		
		Select world
			Case LOCALSPACE
				setGLMatrix()
				glTranslatef px,py,pz
				getGLMatrix()

			Case PARENTSPACE
				v.Set px,py,pz
				v.RotVec(mat.m)
				setGLMatrix()
				glTranslatef v.x,v.y,v.z
				getGLMatrix()

			Case WORLDSPACE
				v.Set px,py,pz
				tmat.Copy mat
			
				pa = parent
				While pa
					tmat.Multiply(pa.mat)
					pa = pa.parent
				Wend

				v.iRotVec(tmat.m)

				setGLMatrix()
				glTranslatef v.x,v.y,v.z
				getGLMatrix()
		End Select				
	End Method
	
	Method Turn( pitchV:Float, yawV:Float, rollV:Float, frame:Int = 1)

		If frame = PARENTSPACE And parent = Null Then frame = WORLDSPACE
		Local tmp:Mat4 = New mat4

		Select frame
		
			'Rotate about the entitys local axis
			'-----------------------------------
			Case LOCALSPACE

				setGLMatrix()

				If yawV<>0.0   glRotatef yawV  , 0.0, 1.0, 0.0
				If pitchV<>0.0 glRotatef pitchV, 1.0, 0.0, 0.0
				If rollV<>0.0  glRotatef rollV , 0.0, 0.0, 1.0
				
				'mat.ArbRot(pitchV, yawV, rollV)
				'mat.RotateYaw(yawV,0,1,0)
				'mat.RotatePitch(pitchV,1,0,0)
				'mat.RotateRoll(rollV,0,0,1)
				getGLMatrix()		

			'Rotate about the entitys parents axis
			'-------------------------------------
			Case PARENTSPACE
				
				setGLMatrix()
								
				If yawV  <> 0.0 glRotatef yawV  , mat.m[1], mat.m[5], mat.m[9]
				If pitchV<> 0.0 glRotatef pitchV, mat.m[0], mat.m[4], mat.m[8]	
				If rollV <> 0.0 glRotatef rollV , mat.m[2], mat.m[6], mat.m[10]
				
				getGLMatrix()

			'Rotate about the world axis
			'---------------------------							
			Case WORLDSPACE
				
				Local pa:tEntity

				tmp.Copy(mat)

				setGLMatrix()

				pa = parent						
				'Multiply the temp matrix up the parent chain
				While pa
					tmp.Multiply(pa.mat)
					pa = pa.parent
				Wend

				If yawV  <> 0.0 glRotatef yawV  , tmp.m[1], tmp.m[5], tmp.m[9]
				If pitchV<> 0.0 glRotatef pitchV, tmp.m[0], tmp.m[4], tmp.m[8]	
				If rollV <> 0.0 glRotatef rollV , tmp.m[2], tmp.m[6], tmp.m[10]
				
				getGLMatrix()
		End Select
	End Method


	Method SetRot(pitchV:Float=0.0, yawV:Float=0.0, rollV:Float=0.0, world:Int=1)

		Local pa:tEntity
		mat.m[0]=1.0 ; mat.m[4]=0.0 ; mat.m[8]=0.0
		mat.m[1]=0.0 ; mat.m[5]=1.0 ; mat.m[9]=0.0
		mat.m[2]=0.0 ; mat.m[6]=0.0 ; mat.m[10]=1.0
		
		If world = LOCALSPACE
			Turn(pitchV, yawV, rollV)

		Else If world = PARENTSPACE
			pa = parent
			
			If pa Then mat.Multiply(pa.mat)
			
			Turn(pitchV, yawV, rollV,2)
		Else
			pa = parent
			While pa
				'mat.postMultiply(pa.mat)
				mat.Multiply(pa.mat)
				pa = pa.parent
			Wend
			
			Turn(pitchV, yawV, rollV)

		End If
		
	End Method



	Method SetScaling(sx:Float=1.0, sy:Float=1.0, sz:Float=1.0)
		smat.m[0]  = sx
		smat.m[5]  = sy
		smat.m[10] = sz

		'-------------------------------------------------------
		'With regard to what GL normalize mode should be used...
		'check if the scaling is uniform in all 3 axis
		'-------------------------------------------------------
		If sx = 1.0 And sy = 1.0 And sz = 1.0
			ScaleMode = 0 'No Scaling needed
		Else If sx = sy And sy = sz 'Uniform Scaling
			ScaleMode = 1
		Else
			ScaleMode = 2 'Non-Uniform Scaling
			Return
		End If
		
		'If this entity has no parent, then we don't need to check
		'for any parent scaling that would affect us, so exit!
		If parent = Null Return

		'----------------------------------------------------
		'If this entity is parented, check all the way up the
		'parental chain and check them for uniform scaling
		'----------------------------------------------------
		Local pa:tEntity = parent
		Local parentScalemode:Int = 0
		If pa
			While pa And parentScalemode <> 2

				'Does this entity use scaling?
				If pa.smat.m[0] <> 1.0 Or pa.smat.m[5] <> 1.0 Or pa.smat.m[10] <> 1.0
					parentScalemode = 1
				End If
				
				'If it does, is it non uniform?
				If parentScalemode = 1
				
					'Is it using non-uniform scaling?
					If pa.smat.m[0] <> pa.smat.m[5] Or pa.smat.m[0] <> pa.smat.m[10] Or pa.smat.m[5] <> pa.smat.m[10]
						parentScalemode = 2
					End If
				End If
				
				'If the scaling was non uniform then no need to continue
				If parentScalemode = 2
					'force exit!
					pa = Null
					Exit
				End If
				
				pa = pa.parent
			Wend
		End If
		
		Select parentScalemode
			Case 1
				ScaleMode = 1
			Case 2
				ScaleMode = 2
		End Select
		
		'TODO! update scaling mode for an entity when it gets re/un-parented
	End Method

	Method SetParent(newParent:tEntity, inplace:Int = False)	
		'--------------------------------------------------------------------
		'If the specified parent entity is the same as the existing one, exit
		'--------------------------------------------------------------------
		If tEntity(newParent) = parent Then Return
		
		If Not tEntity(newParent) '----------------------------------------Unparent
		
			If inplace = False 'Unparent Localy *WORKING*
				
				'Remove Self from the old Parents list
				RemoveLink parentLink
				parent = Null
			
			Else 'Unparent Globaly *WORKING*
				Local pa:tEntity = parent
				
				'Transform matrix into world space
				While pa
					mat.Multiply(pa.mat)
					pa = pa.parent
				Wend
				
				'Remove Self from the old Parents list
				RemoveLink parentLink
				parent = Null				
			End If
			
		Else '-------------------------------------------------------- Parent
			'Remove Self from any existing parents childlist
			If parent
				RemoveLink parentLink
				parentLink = Null
				parent = Null
			End If
			
			If inplace = False 'Parent Localy *WORKING*
				' Set the parent
				parent = newParent
				
				' Add Self to the Parents list of Child Entitys
				parentLink = ListAddLast(newParent.childList, Self)

			Else 'Parent Globaly *WORKING*
				'Remove Self from any existing parents childlist
				'and multiply the matrix to leave it in its current
				'world space position and rotation
				If parent
					Local gpa:tEntity = parent
				
					'Transform matrix into world space
					While gpa
						mat.Multiply(gpa.mat)
						gpa = gpa.parent
					Wend
				
					'Remove Self from the old Parents list
					RemoveLink parentLink
					parentLink = Null
					parent = Null
				End If
				
				Local parentMat:Mat4 = New mat4

				'Build a list of child entitys from world to new parent			
				Local pchain:tEntity = newParent
				
				While pchain
					parentMat.Multiply(pchain.mat)
					pchain = pchain.parent
				Wend

				Local p:Vec3 = New Vec3
				p.x = mat.m[12] - parentmat.m[12]
				p.y = mat.m[13] - parentmat.m[13]
				p.z = mat.m[14] - parentmat.m[14]

				'transforms current matrix rotation into parent space
				mat.iMatRot(parentMat)

				'reposition
				p.iRotVec(parentMat.m)
				mat.m[12] = p.x
				mat.m[13] = p.y
				mat.m[14] = p.z
				
				' Add Self to the Parents list of Child Entitys
				parentLink = ListAddLast(newParent.childList,Self)
				
				'Set the new parent
				parent = newParent
			End If			
		End If
	End Method
	
	'Hacky
	Method PointAt(t:tEntity)
		Local d:vec3 = vec3.create(t.getX(3), t.getY(3), t.getZ(3))
		Local s:vec3 = vec3.create(getX(3), getY(3), getZ(3))
		Local s2:vec3 = New vec3


		Local bool:Int
 		If s.x<d.x bool = True

		s2.copy(s)
		s2.sub(d)
		s2.normalize()
		
		s.y=0
		d.y=0
		
		s.sub(d)
		
		s.Normalize()

		mat.LoadIdentity()
		mat.m[8] = s.x
		mat.m[9] = s.y
		mat.m[10] = s.z
		
		d.cross2(mat.m[4],mat.m[5],mat.m[6],s.x,s.y,s.z)
		d.normalize()
		
		'final X
		mat.m[0] = d.x
		mat.m[1] = d.y
		mat.m[2] = d.z
	
		
		Local a:Float = ACos(s2.Dot(s))

		If s2.y>s.y a=-a
		turn a,0,0
		
		'Cater for any parenting
		Local pa:tEntity = parent
		While pa
			mat.iMatrot(pa.mat)
			pa = pa.parent
		Wend
	
	End Method
		
		
	Method setGLMatrix()
		glLoadMatrixf mat.m
	End Method
	
	Method getGLMatrix()
		glGetFloatv GL_MODELVIEW_MATRIX,mat.m
	End Method
	
End Type


Type tPivot Extends tEntity
	Method New()
		ListAddLast entityList,Self
		
		self.mat = New mat4
		self.smat = New mat4
	
	End Method


	Method DrawEntity()


		'--------------------
		'Draw some Axis Lines
		'--------------------
		Local al:Float= 0.5 ' Axis Line Length
		glDisable(GL_LIGHTING)
		glLineWidth(4.0)
		glBegin(GL_LINES)
			glColor3f (1.0,0.0,0.0)
			glVertex3f(0.0,0.0,0.0)
			glVertex3f(al,0.0,0.0)		
	
			glColor3f(0.0,1.0,0.0)
			glVertex3f(0.0,0.0,0.0)
			glVertex3f(0.0,al,0.0)
	
			glColor3f(0.0,0.0,1.0)
			glVertex3f(0.0,0.0,0.0)
			glVertex3f(0.0,0.0,al)
		glEnd()
		glEnable(GL_LIGHTING)
		glLineWidth(1.0)

	End Method
End Type


Type tMesh Extends tEntity
	Method New()
		ListAddLast entityList,Self
		
		self.mat = New mat4
		self.smat = New mat4
	
	End Method
	
	Method drawentity()
		glDisable GL_LIGHTING
		Local sz:Float = 0.5
		glBegin GL_QUADS  '									// Draw A Quad
			glColor3f 0.0,1.0,0.0  '						// Set The Color To Green
			glVertex3f  sz, sz,-sz  '					// Top Right Of The Quad  Top)
			glVertex3f -sz, sz,-sz  '					// Top Left Of The Quad  Top)
			glVertex3f -sz, sz, sz  '					// Bottom Left Of The Quad  Top)
			glVertex3f  sz, sz, sz  '					// Bottom Right Of The Quad  Top)
			
			glColor3f sz,0.5,0.0  '						// Set The Color To Orange
			glVertex3f  sz,-sz, sz  '					// Top Right Of The Quad  Bottom)
			glVertex3f -sz,-sz, sz  '					// Top Left Of The Quad  Bottom)
			glVertex3f -sz,-sz,-sz  '					// Bottom Left Of The Quad  Bottom)
			glVertex3f  sz,-sz,-sz  '					// Bottom Right Of The Quad  Bottom)
			
			glColor3f sz,0.0,0.0  '						// Set The Color To Red
			glVertex3f  sz, sz, sz  '					// Top Right Of The Quad  Front)
			glVertex3f -sz, sz, sz  '					// Top Left Of The Quad  Front)
			glVertex3f -sz,-sz, sz  '					// Bottom Left Of The Quad  Front)
			glVertex3f  sz,-sz, sz  '					// Bottom Right Of The Quad  Front)
			
			glColor3f sz,sz,0.0  '						// Set The Color To Yellow
			glVertex3f  sz,-sz,-sz  '					// Top Right Of The Quad  Back)
			glVertex3f -sz,-sz,-sz  '					// Top Left Of The Quad  Back)
			glVertex3f -sz, sz,-sz  '					// Bottom Left Of The Quad  Back)
			glVertex3f  sz, sz,-sz  '					// Bottom Right Of The Quad  Back)
			
			glColor3f 0.0,0.0,sz  '						// Set The Color To Blue
			glVertex3f -sz, sz, sz  '					// Top Right Of The Quad  Left)
			glVertex3f -sz, sz,-sz  '					// Top Left Of The Quad  Left)
			glVertex3f -sz,-sz,-sz  '					// Bottom Left Of The Quad  Left)
			glVertex3f -sz,-sz, sz  '					// Bottom Right Of The Quad  Left)
			
			glColor3f sz,0.0,sz  '						// Set The Color To Violet
			glVertex3f  sz, sz,-sz  '					// Top Right Of The Quad  Right)
			glVertex3f  sz, sz, sz  '					// Top Left Of The Quad  Right)
			glVertex3f  sz,-sz, sz  '					// Bottom Left Of The Quad  Right)
			glVertex3f  sz,-sz,-sz  '					// Bottom Right Of The Quad  Right)
		glEnd   '											// Done Drawing The Quad
	End Method
	
End Type


Type tCamera Extends tEntity
	Global cameraList:TList = CreateList()
	Global activeCamera:tCamera

	Field fov:Float 'field of view
	Field aspect:Float 'usualy screenwidth divided by screenheight
	Field zNear:Float 'near clip distance
	Field zFar:Float 'far clip distance

	Field vPortX:Int
	Field vPortY:Int
	Field vPortW:Int
	Field vPortH:Int

	Field clearColor:Float[] = [0.5,0.5,0.5,1.0]
	Field clearDepth:Float = 1.0

	Field gridSize:Float = 20.0
	Field gridElementSize:Int = 1

	Function Create:tCamera()
		Local c:tCamera = New tCamera
		
		c.parent = Null
	
		c.vPortX = 0
		c.vPortY = 0
		c.vPortW = GraphicsWidth()
		c.vPortH = GraphicsHeight()
		
		c.fov    = 60
		c.aspect = Float(c.vPortW)/Float(c.vPortH)
		c.zNear  = 0.25
		c.zFar   = 500.0

		c.mat = New mat4
		c.smat = New mat4

		c.scaleMode = 0

		ListAddLast entityList,c
		ListAddLast cameraList,c

		Return c
	End Function


	Method Use()

		glViewport vPortX,vPortY,vPortW,vPortH
		glEnable GL_SCISSOR_TEST
		glScissor vPortX,vPortY,vPortW,vPortH
		glClear GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
	
		activeCamera = Self

		'Setup Projection Matrix
		'
		'glhPerspective() applies FOV horizontally
		'glvPerspective() applies FOV vertically, idnetical to gluPerspective()
		glMatrixMode GL_PROJECTION
		glLoadIdentity()
		glhPerspective()

		
		'Apply camera translation/rotation
		'
		'GL doesn't really know a Camera exists, what we are actualy doing here
		'moving & rotating the WORLD opposite to our cameras settings :)
		glMatrixMode GL_MODELVIEW
		glLoadIdentity()
		
		'copy the cameras position/rotation matrix
		Local tmp:mat4 = New mat4
		tmp.Copy(mat)

		'Remember we store entitys position/rotation matrix in LOCAL space, so if
		'the camera has a parent, we must transform its matrix into WORLD space
		'this is done by multiplying it up the parent chain!
		Local pa:tEntity = parent
		While pa
			tmp.Multiply(pa.mat)
			pa = pa.parent
		Wend

		'Now we apply the matrix to the current OpenGL MODELVIEW matrix
		'GL provides a handy function to do this called 'gluLookAt()'
		'Here is an alternative that works exactly the same
		Local view:mat4 = New mat4

    Local vRight:Vec3	= Vec3.Create( tmp.m[0],  tmp.m[1],  tmp.m[2]  )
		Local vUp:Vec3		= Vec3.Create( tmp.m[4],  tmp.m[5],  tmp.m[6]  )
		Local vLook:Vec3	= Vec3.Create( tmp.m[8],  tmp.m[9],  tmp.m[10] )
		Local vEye:Vec3		= Vec3.Create( tmp.m[12], tmp.m[13], tmp.m[14] )

    vRight.Normalize()
    vUp.Normalize() 
    vLook.Normalize()
 	
    view.m[0] = vRight.x
    view.m[1] = vUp.x
    view.m[2] = vLook.x
    view.m[3] =  0.0

    view.m[4] = vRight.y
    view.m[5] = vUp.y
    view.m[6] = vLook.y
    view.m[7] =  0.0

    view.m[8]  = vRight.z
    view.m[9]  = vUp.z
    view.m[10] = vLook.z
    view.m[11] =  0.0

    view.m[12] = -vRight.Dot(vEye)
    view.m[13] = -vUp.Dot(vEye)
    view.m[14] = -vLook.Dot(vEye)
    view.m[15] =  1.0

		'Finaly apply the transform to the GL_MODELVIEW matrix
		glMultMatrixf view.m
		
		glClearDepth 1.0
		glClearColor clearColor[0],clearColor[1],clearColor[2],clearColor[3]
		glClear GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT
		glDisable GL_SCISSOR_TEST


		'Draw a debug Grid?
		If gridSize > 1.0 Then DrawGrid()


		'Camera is all setup now!
	End Method

	Method ScreenProject(t:tEntity, sx:Float Var, sy:Float Var)
		Local tx:Float, ty:Float, tz:Float
		Local v:vec3 = New vec3
		
		v.x = t.getX(3)
		v.y = t.getY(3)
		v.z = t.getZ(3)
		
		Local pm:Float[16]
		Local mv:Float[16]
		
		glGetFloatv GL_PROJECTION_MATRIX,pm
		glGetFloatv GL_MODELVIEW_MATRIX,mv
		
			v.multiply pm
			v.multiply mv	
	
	
	End Method

	Method SetVP(x:Float, y:Float, w:Float, h:Float)
		vPortX = x
		vPortY = y
		vPortW = w
		vPortH = h	
		aspect = Float(vPortW)/Float(vPortH)
	End Method
	
	Method DrawGrid()
		glDisable GL_LIGHTING
		
		Local i:Float = -gridSize
		glColor3f 1,1,1
		While i <= gridSize
			glBegin GL_LINES
				glVertex3f(i, 0.0, gridSize)
				glVertex3f(i, 0.0, -gridSize)
			glEnd
			
			glBegin GL_LINES
				glVertex3f(-gridSize, 0.0, i)
				glVertex3f(gridSize, 0.0, i)		
			glEnd
			i:+ gridElementSize
		Wend
	End Method

	Method glhPerspective()

		Local xmin:Double, xmax:Double, ymin:Double, ymax:Double
		Local m:Double[16]

		xmax = zNear * Tan(fov*.5)
		xmin = -xmax

		ymin = xmin / aspect
		ymax = xmax / aspect

		m[0] = (2.0 * zNear) / (xmax - xmin)
		m[5] = (2.0 * zNear) / (ymax - ymin)
		m[10]= -(zFar + zNear) / (zFar - zNear)

		m[2] = (xmax + xmin) / (xmax - xmin)
		m[6] = (ymax + ymin) / (ymax - ymin)
		m[11] = -1

		m[14] = -(2.0 * zFar * zNear) / (zFar - zNear)

		glMultMatrixd m
	End Method
	
	
	Method DrawEntity()
	
		glDisable GL_LIGHTING
		'--------------------
		'Draw some Axis Lines
		'--------------------
		Local al:Float= 0.2 ' Axis Line Length
		glDisable(GL_LIGHTING)
		glLineWidth(2.0)
		glBegin(GL_LINES)
			glColor3f (1.0,0.0,0.0)
			glVertex3f(0.0,0.0,0.0)
			glVertex3f(al,0.0,0.0)		
	
			glColor3f(0.0,1.0,0.0)
			glVertex3f(0.0,0.0,0.0)
			glVertex3f(0.0,al,0.0)
	
			glColor3f(0.0,0.0,1.0)
			glVertex3f(0.0,0.0,0.0)
			glVertex3f(0.0,0.0,al)
		glEnd()

		
		glLineWidth(1.0)
		glColor3f 1.0,1.0,0.0
		glBegin(GL_LINES)
			glVertex3f -.05, .1, .1
			glVertex3f  .05, .1, .1

			glVertex3f  .05, .1, .1
			glVertex3f  .05, .1, .4

			glVertex3f  .05, .1, .4
			glVertex3f -.05, .1, .4

			glVertex3f -.05, .1, .4
			glVertex3f -.05, .1, .1

			glVertex3f -.05, -.1, .1
			glVertex3f  .05, -.1, .1

			glVertex3f  .05, -.1, .1
			glVertex3f  .05, -.1, .4

			glVertex3f  .05, -.1, .4
			glVertex3f -.05, -.1, .4

			glVertex3f -.05, -.1, .4
			glVertex3f -.05, -.1, .1

			glVertex3f -.05, .1, .1
			glVertex3f -.05, -.1, .1
			glVertex3f  .05, .1, .1
			glVertex3f  .05, -.1, .1
			glVertex3f  .05, .1, .4
			glVertex3f  .05, -.1, .4
			glVertex3f -.05, .1, .4
			glVertex3f -.05, -.1, .4

			glVertex3f 0, .1, .4
			glVertex3f 0, .15, .45

			glVertex3f 0, .15, .45
			glVertex3f 0, .25, .45

			glVertex3f 0, .25, .45
			glVertex3f 0, .3, .4

			glVertex3f 0, .3, .4
			glVertex3f 0, .3, .3

			glVertex3f 0, .3, .3
			glVertex3f 0, .25, .25

			glVertex3f 0, .25, .25
			glVertex3f 0, .3, .2

			glVertex3f 0, .3, .2
			glVertex3f 0, .3, .1

			glVertex3f 0, .3, .1
			glVertex3f 0, .25, .05

			glVertex3f 0, .25, .05
			glVertex3f 0, .15, .05

			glVertex3f 0, .15, .05
			glVertex3f 0, .1, .1

			glVertex3f -.07, .05, 0
			glVertex3f .07, .05, 0

			glVertex3f .07, .05, 0
			glVertex3f .07, -.05, 0

			glVertex3f .07, -.05, 0
			glVertex3f -.07, -.05, 0

			glVertex3f -.07, -.05, 0
			glVertex3f -.07, .05, 0

			glVertex3f -.07, .05, 0
			glVertex3f -.03, .025, .1

			glVertex3f .07, .05, 0
			glVertex3f .03, .025, .1

			glVertex3f .07, -.05, 0
			glVertex3f .03, -.025, .1

			glVertex3f -.07, -.05, 0
			glVertex3f -.03, -.025, .1

		glEnd()
		glEnable GL_LIGHTING
		glLineWidth 1.0		

	End Method

End Type









Type mat4
	'OpenGL column major 4x4 Matrix
	
	'0  4  8  12
	'1  5  9  13
	'2  6  10 14
	'3  7  11 15
	
	'When used to store orientation & rotation
	'm[0],m[1],m[2] = X axis
	'm[4],m[5],m[6] = Y axis
	'm[8],m[9],m[10] = Z axis
	'm[12],m[13],m[14] = Position	X,Y,Z
	
	'when used to store Scaling
	'm[0] = X scale
	'm[5] = Y scale
	'm[10] = Z scale
	
	Field m:Float[16]
	
	Method New()
		Local i:Int
		For i = 0 Until 16
			m[i] = 0.0
		Next
		
		'Make Identity matrix
		m[0] = 1.0
		m[5] = 1.0
		m[10] = 1.0
		m[15] = 1.0
	End Method

	Method LoadIdentity()
		For Local i=0 To 11
			m[i]=0.0
		Next
		m[0]=1.0
		m[5]=1.0
		m[10]=1.0
		m[15]=1.0
	End Method
	
	Method Copy(s:mat4)
		For Local i:Int = 0 To 15
			m[i] = s.m[i]
		Next
	End Method

	Method Multiply(mat:Mat4)
		Local Tempmat:Float[16]
		Tempmat[0]  = m[0] * mat.m[0] + m[1] * mat.m[4] + m[2] * mat.m[8]  + m[3] * mat.m[12]
		Tempmat[1]  = m[0] * mat.m[1] + m[1] * mat.m[5] + m[2] * mat.m[9]  + m[3] * mat.m[13]
		Tempmat[2]  = m[0] * mat.m[2] + m[1] * mat.m[6] + m[2] * mat.m[10] + m[3] * mat.m[14]
		Tempmat[3]  = m[0] * mat.m[3] + m[1] * mat.m[7] + m[2] * mat.m[11] + m[3] * mat.m[15]
	
		Tempmat[4]  = m[4] * mat.m[0] + m[5] * mat.m[4] + m[6] * mat.m[8]  + m[7] * mat.m[12]
		Tempmat[5]  = m[4] * mat.m[1] + m[5] * mat.m[5] + m[6] * mat.m[9]  + m[7] * mat.m[13]
		Tempmat[6]  = m[4] * mat.m[2] + m[5] * mat.m[6] + m[6] * mat.m[10] + m[7] * mat.m[14]
		Tempmat[7]  = m[4] * mat.m[3] + m[5] * mat.m[7] + m[6] * mat.m[11] + m[7] * mat.m[15]
	
		Tempmat[8]  = m[8] * mat.m[0] + m[9] * mat.m[4] + m[10] * mat.m[8]  + m[11] * mat.m[12]
		Tempmat[9]  = m[8] * mat.m[1] + m[9] * mat.m[5] + m[10] * mat.m[9]  + m[11] * mat.m[13]
		Tempmat[10] = m[8] * mat.m[2] + m[9] * mat.m[6] + m[10] * mat.m[10] + m[11] * mat.m[14]
		Tempmat[11] = m[8] * mat.m[3] + m[9] * mat.m[7] + m[10] * mat.m[11] + m[11] * mat.m[15]
	
		Tempmat[12] = m[12] * mat.m[0] + m[13] * mat.m[4] + m[14] * mat.m[8]  + m[15] * mat.m[12]
		Tempmat[13] = m[12] * mat.m[1] + m[13] * mat.m[5] + m[14] * mat.m[9]  + m[15] * mat.m[13]
		Tempmat[14] = m[12] * mat.m[2] + m[13] * mat.m[6] + m[14] * mat.m[10] + m[15] * mat.m[14]
		Tempmat[15] = m[12] * mat.m[3] + m[13] * mat.m[7] + m[14] * mat.m[11] + m[15] * mat.m[15]
		
		For Local i:Int = 0 To 15
			m[i]=Tempmat[i]
		Next
	End Method

	Method postMultiply(mat:Mat4)
		Local Tempmat:Float[16]
		
		Tempmat[0] = m[0] * mat.m[0] + m[4] * mat.m[1] + m[8]  * mat.m[2] + m[12] * mat.m[3]
		Tempmat[1] = m[1] * mat.m[0] + m[5] * mat.m[1] + m[9]  * mat.m[2] + m[13] * mat.m[3]
		Tempmat[2] = m[2] * mat.m[0] + m[6] * mat.m[1] + m[10] * mat.m[2] + m[14] * mat.m[3]
		Tempmat[3] = m[3] * mat.m[0] + m[7] * mat.m[1] + m[11] * mat.m[2] + m[15] * mat.m[3]

		Tempmat[4] = m[0] * mat.m[4] + m[4] * mat.m[5] + m[8]  * mat.m[6] + m[12] * mat.m[7]
		Tempmat[5] = m[1] * mat.m[4] + m[5] * mat.m[5] + m[9]  * mat.m[6] + m[13] * mat.m[7]
		Tempmat[6] = m[2] * mat.m[4] + m[6] * mat.m[5] + m[10] * mat.m[6] + m[14] * mat.m[7]
		Tempmat[7] = m[3] * mat.m[4] + m[7] * mat.m[5] + m[11] * mat.m[6] + m[15] * mat.m[7]

		Tempmat[8] = m[0] * mat.m[8] + m[4] * mat.m[9]  + m[8]  * mat.m[10] + m[12] * mat.m[11]
		Tempmat[9] = m[1] * mat.m[8] + m[5] * mat.m[9]  + m[9]  * mat.m[10] + m[13] * mat.m[11]
		Tempmat[10] = m[2] * mat.m[8] + m[6] * mat.m[9] + m[10] * mat.m[10] + m[14] * mat.m[11]
		Tempmat[11] = m[3] * mat.m[8] + m[7] * mat.m[9] + m[11] * mat.m[10] + m[15] * mat.m[11]

		Tempmat[12] = m[0] * mat.m[12] + m[4] * mat.m[13] + m[8]  * mat.m[14] + m[12]
		Tempmat[13] = m[1] * mat.m[12] + m[5] * mat.m[13] + m[9]  * mat.m[14] + m[13]
		Tempmat[14] = m[2] * mat.m[12] + m[6] * mat.m[13] + m[10] * mat.m[14] + m[14]
		Tempmat[15] = 1

		For Local i:Byte=0 To 15
			m[i]=Tempmat[i]
		Next
	End Method

	Method iMatRot(mat:Mat4)
		Local Tempmat:Float[16]
		Tempmat[0] = m[0] * mat.m[0] + m[1] * mat.m[1] + m[2] * mat.m[2]
		Tempmat[1] = m[0] * mat.m[4] + m[1] * mat.m[5] + m[2] * mat.m[6]
		Tempmat[2] = m[0] * mat.m[8] + m[1] * mat.m[9] + m[2] * mat.m[10]
	
		Tempmat[4] = m[4] * mat.m[0] + m[5] * mat.m[1] + m[6] * mat.m[2]
		Tempmat[5] = m[4] * mat.m[4] + m[5] * mat.m[5] + m[6] * mat.m[6]
		Tempmat[6] = m[4] * mat.m[8] + m[5] * mat.m[9] + m[6] * mat.m[10]
	
		Tempmat[8] = m[8] * mat.m[0] + m[9] * mat.m[1] + m[10] * mat.m[2]
		Tempmat[9] = m[8] * mat.m[4] + m[9] * mat.m[5] + m[10] * mat.m[6]
		Tempmat[10] = m[8] * mat.m[8] + m[9] * mat.m[9] + m[10] * mat.m[10]
	
		For Local i:Byte=0 To 10
			If i<>3 And i<>7 Then m[i] = Tempmat[i]
		Next
	End Method


	

	Method ArbRot(x:Float, y:Float, z:Float)
		Local a:Float = Cos(x)
		Local b:Float = Sin(x)
		Local c:Float = Cos(y)
		Local d:Float = Sin(y)
		Local e:Float = Cos(z)
		Local f:Float = Sin(z)

		Local ad:Float = a * d
		Local bd:Float = b * d

    m[0]  =   c * e
    m[1]  =  -c * f
    m[2]  =  -d
    m[4]  = -bd * e + a * f
    m[5]  =  bd * f + a * e
    m[6]  =  -b * c
    m[8]  =  ad * e + b * f
    m[9]  = -ad * f + b * e
    m[10] =   a * c;

    'm[3]  =  mat[7] = mat[11] = mat[12] = mat[13] = mat[14] = 0;
    m[15] = 1

	End Method

End Type

Type Vec3
	Field x:Float
	Field y:Float
	Field z:Float
	
	Method New()
		x=0.0
		y=0.0
		z=0.0
	End Method
	
	Method Set(xx:Float, yy:Float, zz:Float)
		x = xx
		y = yy
		z = zz
	End Method
	
	Function Create:Vec3(xx:Float=0.0, yy:Float=0.0, zz:Float=0.0)
		Local v:Vec3=New Vec3
		v.x=xx
		v.y=yy
		v.z=zz
		Return v
	End Function

	Method Copy(v:Vec3)
		x=v.x ; y=v.y ; z=v.z
	End Method
	
	Method Sub(v:Vec3)
		x:-v.x
		y:-v.y
		z:-v.z
	End Method	
	
	Method Normalize()
		Local mag:Float=1.0/Sqr(x*x+y*y+z*z)
		'If mag=1.0 Return
		x:*mag
		y:*mag
		z:*mag
	End Method

	Method Dot:Float(v:Vec3) 'dot product (angle between 2 vectors)
		Return x*v.x + y*v.y + z*v.z
	End Method

	Method Cross(v1:Vec3, v2:Vec3)
		x = (v1.y * v2.z)  -  (v2.y * v1.z) 
		y = (v1.z * v2.x)  -  (v2.z * v1.x) 
		z = (v1.x * v2.y)  -  (v2.x * v1.y)
	End Method

	Method Cross2(x1#,y1#,z1,x2#,y2#,z2#)
		x = (y1 * z2)  -  (y2 * z1) 
		y = (z1 * x2)  -  (z2 * x1) 
		z = (x1 * y2)  -  (x2 * y1)
	End Method

	Method RotVec(m:Float[])
		Local tmp:Float[3]
		tmp[0] = x*m[0] + y*m[4] + z*m[8]
		tmp[1] = x*m[1] + y*m[5] + z*m[9]
		tmp[2] = x*m[2] + y*m[6] + z*m[10]
				
		x = tmp[0]
		y = tmp[1]
		z = tmp[2]
	End Method
	
	Method iRotVec(m:Float[])
		Local tmp:Float[3]
		tmp[0] = x*m[0] + y*m[1] + z*m[2]
		tmp[1] = x*m[4] + y*m[5] + z*m[6]
		tmp[2] = x*m[8] + y*m[9] + z*m[10]

		x = tmp[0]
		y = tmp[1]
		z = tmp[2]
	End Method

	'Transform a Point by a Matrix
	Method Multiply(m:Float[])
		Local tmp:Float[3]

		tmp[0] = x*m[0] + y*m[4] + z*m[8] + m[12]
		tmp[1] = x*m[1] + y*m[5] + z*m[9] + m[13]
		tmp[2] = x*m[2] + y*m[6] + z*m[10]+ m[14]
		
		x = tmp[0]
		y = tmp[1]
		z = tmp[2]
	End Method
	
End Type



That's very good. Bravo. Now if I can get the Nehe tutorial 13 to load
meshes into this, I might get a jump-start on finally making games in 3d.

Splendid work, my friend.

Updated with entity.getX() Y & Z, entity.getPitch() Yaw & Roll

Tom - good work on the camera sample.

Drew - I am Just curious how you are going to implement collision detection. Are you going use bounding boxes (or other primitives) only, or some type of polygon to polygon collision test also?

I am almost certain I will go with the most basic aproach I
can get away with. That would be points and planes. I have
most of the mechanics worked out and I'm testing different
methods like mad. This seems to be the easiest and most
reliable solution for me.

do a search for loadbsp I did theres some ray to poly collision code, didnt work with the bsp sometimes (maybe odd bsp winding)

If its useful I could tidy it up a bit

@tom
The light dawns!
If yawV <> 0.0 glRotatef yawV , mat.m[1], mat.m[5], mat.m[9]
If pitchV<> 0.0 glRotatef pitchV, mat.m[0], mat.m[4], mat.m[8]
If rollV <> 0.0 glRotatef rollV , mat.m[2], mat.m[6], mat.m[10]

neat work tom!

Updated, scaling is in. I just realised I was doing it wrong too, sorted! :)

Use square brackets to scale cube2

Added PointAt()

nice work tom, thanks for sharing, at this rate no ones gonna want to buy the 3d module ;)

Hell, I will! :)

I hope it comes out sooner rather than later though. Might go bug Mark for an update on it actualy.

had troubles with view ports that overlap, ie say 1 full screen with 1 smaller window over the top of it, how do you clear the smaller viewport so it cant be seen through...

Haven't tried overlapping yet, will see what happens.

There seems to be a problem with getPitch it will sometimes return NAN

I notice its calculated in a different way to getYaw and getRoll, which two matrix elements you I need to use for pitch?

This is a very nice piece of work. Thanks for sharing!

I was reading through the code and noticed some inconsistencies though. May be just because the code needs some cleaning up.

For instance, in the tEntity.SetParent() method, there is a code block that is never executed.

...
Else 'Parent Globaly *WORKING*
	'Remove Self from any existing parents childlist
	'and multiply the matrix to leave it in its current
	'world space position and rotation
	If parent
		Local gpa:tEntity = parent
	
		'Transform matrix into world space
		While gpa
			mat.Multiply(gpa.mat)
			gpa = gpa.parent
		Wend
	
		'Remove Self from the old Parents list
		RemoveLink parentLink
		parentLink = Null
		parent = Null
	End If



The part between 'If parent..' and 'end if' will never be called, since a few lines up you are performing:

If parent
	RemoveLink parentLink
	parentLink = Null
	parent = Null
End If


So if it reaches the first posted snippet, parent will always be Null.

Nothing Important, but may be worth cleaning up sometime to make it easier to read.

Oh, and the tCamera.ScreenProject() method isn't finished :)

And another one: the RenderEntity() method has a type-o
			If ThisScaleMode = 1
				glEnable GL_RESCALE_NORMAL
			Else If ThisScaleMode = 2
				glEnable GL_NORMZLIZE
			End If
	End Select


'glEnable GL_NORMZLIZE' should be 'glEnable GL_NORMALIZE'.

Well spotted, prize is in the post :P

glEnable GL_NORMZLIZE

thats why we have Strict :)

I like the wireframe cameras... very cool! :)

Great stuff Tom

Dabz