| This is a frozen project which was laying around in my hd, so I decided to publish the code. 
 Does the basic stuff like animations etc, has a simple particle system and shader support, loads .md2 files, shadows don't work but are partially coded in.
 
 The lib itself:
 ngl.bmx
 
 
SuperStrict
Const ShadowDistance:Float = 10.0
Include "nmatrix.bmx"
Type NPoint
	Field X:Float
	Field Y:Float
	Field Z:Float
	Method Translate(NewX:Float,NewY:Float,NewZ:Float)
		X = X+NewX
		Y = Y+NewY
		Z = Z+NewZ
	End Method
	Method Position(NewX:Float,NewY:Float,NewZ:Float)
		X = NewX
		Y = NewY
		Z = NewZ
	End Method
 	Method DotProduct:Float(P2:NPoint)
		Return X*P2.X+Y*P2.Y+Z*P2.Z
	End Method
	Method Normalize()
		Local f:Float = 1.0/Sqr(DotProduct(Self))
		X = X * f
		Y = Y * f
		Z = Z * f
	End Method
	Method RenderShadows()
		ListAddLast(NGraphics.Gfx.ShadowList,Self)
	End Method
End Type
Type NGraphics
	Field Width:Int
	Field Height:Int
	Field Depth:Int
	Field Fullscreen:Int
	Field FPS:Int,FPSCount:Int,FPSTimer:Int
	Field Lighting:Int
	Field Triangles:Int
	Field TextureEnabled:Int,CurrentTexture:Int
	Field WireframeEnabled:Int,AlphaEnabled:Int
	Field CurrentProgramObject:Int
	Field ShadowList:TList
	Global Gfx:NGraphics
	Method Render()
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)
		Triangles=0
		For Local NCam:NCamera = EachIn NCamera.NCameraList
			gluPerspective(45.0, Float(Width)/Float(Height), NCam.Near, NCam.Far)
			If NCam.Enabled Then
				glLoadIdentity()
				glClearColor(NCam.ClsColor[0],NCam.ClsColor[1],NCam.ClsColor[2], 0.0)
				If NCam.Fog Then
					glEnable(GL_FOG)
					glFogi(GL_FOG_MODE, GL_LINEAR)
					glFogfv(GL_FOG_COLOR, NCam.FogColor)
					glFogf(GL_FOG_DENSITY, NCam.FogDensity)
					glHint(GL_FOG_HINT, GL_NICEST)
					glFogf(GL_FOG_START, NCam.FogStart)
					glFogf(GL_FOG_END, NCam.FogEnd)
				Else
					glDisable(GL_FOG)
				EndIf
				'Vertex buffer objects
				'glEnableClientState(GL_VERTEX_ARRAY)
				'glEnableClientState(GL_TEXTURE_COORD_ARRAY)
			'	'Position camera
				glRotatef(NCam.Pitch,1.0,0,0)
				glRotatef(NCam.Yaw,0,1.0,0)
				glRotatef(NCam.Roll,0,0,1.0)
				glTranslatef(NCam.X,NCam.Y,NCam.Z)
	
				'Lights
				If Lighting Then
					For Local NLi:NLight = EachIn NLight.NLightList
						glLightfv(NLi.GL_LIGHT,GL_POSITION,[NLi.X,NLi.Y,NLi.Z,1.0])
					Next
				EndIf
				'Z-Order
				For Local NEnt:NEntity = EachIn NEntity.NEntityList
					NEnt.CamDistance = Sqr((NCam.X-NEnt.X)*(NCam.X-NEnt.X)+(NCam.Y-NEnt.Y)*(NCam.Y-NEnt.Y)+(NCam.Z-NEnt.Z)*(NCam.Z-NEnt.Z))
					If NEnt.RenderFirst Then NEnt.CamDistance = 0
					'Display list
					If NEnt.CreateDList Then 
						NEnt.CreateDisplayList()
						NEnt.CreateDList = 0
					EndIf
				Next
				NEntity.NEntityList.Sort(True,NEntity.CompareDistance)
				'Loop entities
				Local CAlpha:Float
				For Local NEnt:NEntity = EachIn NEntity.NEntityList
					If NEnt.Parent = Null Then RenderEntity(NEnt,NCam)
				Next
				If CurrentProgramObject <> 0 Then
					CurrentProgramObject = 0
					glUseProgramObjectARB(0)
					glDisable(GL_VERTEX_PROGRAM_ARB)
					glDisable(GL_FRAGMENT_PROGRAM_ARB)
				EndIf
				'Texture
				If TextureEnabled<>1 Then
					TextureEnabled =  1
					glEnable(GL_TEXTURE_2D)
				EndIf
				'Draw particles
				If Not AlphaEnabled Then glEnable(GL_BLEND)
				For Local NEmit:NParticleEmitter = EachIn NParticleEmitter.NParticleEmitterList
					If CurrentTexture<>NEmit.Texture Then
						CurrentTexture = NEmit.Texture
						glBindTexture (GL_TEXTURE_2D, CurrentTexture)
					EndIf
					NEmit.Update(NCam)
				Next
				If Not AlphaEnabled Then glDisable(GL_BLEND)
			'	CurrentTexture = 0
				'TextureEnabled = 0
				'glDisable(GL_TEXTURE_2D)
				'Shadows
				glDisable(GL_TEXTURE_2D)
				glEnable(GL_BLEND)
				For Local NP:NPoint = EachIn ShadowList
					glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)
					glDepthMask(GL_FALSE)
					glEnable(GL_CULL_FACE)
					glEnable(GL_STENCIL_TEST)
					glEnable(GL_POLYGON_OFFSET_FILL)
					glPolygonOffset(0.0, 100.0)
					glCullFace(GL_FRONT)
					glStencilFunc(GL_ALWAYS, 0, $ff)
					glStencilOp(GL_KEEP, GL_INCR, GL_KEEP)
					RenderShadow(NP.X,NP.Y,NP.Z,NCam)
					glCullFace(GL_BACK)
					glStencilFunc(GL_ALWAYS, 0, $ff)
					glStencilOp(GL_KEEP, GL_DECR, GL_KEEP)
					RenderShadow(NP.X,NP.Y,NP.Z,NCam)
					glDisable(GL_POLYGON_OFFSET_FILL)
					glDisable(GL_CULL_FACE)
					glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)
					glDepthMask(GL_TRUE)
					
					glStencilFunc(GL_NOTEQUAL, 0, $ff)
					glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE)
					DrawShadow()
					glDisable(GL_STENCIL_TEST)
					ListRemove(ShadowList,NP)
			'		glColor4f(1.0,1.0,1.0,0.1)
			'		glEnable(GL_CULL_FACE)
			'		glCullFace(GL_FRONT)
			'		RenderShadow(NP.X,NP.Y,NP.Z,NCam)
			'		glCullFace(GL_BACK)
				Next
				glEnable(GL_TEXTURE_2D)
				glEnable(GL_CULL_FACE)
				If Not AlphaEnabled Then glDisable(GL_BLEND)
			EndIf
		Next
		FPSCount=FPSCount+1
		If MilliSecs() > FPSTimer+999 Then
			FPS=FPSCount
			FPSTimer=MilliSecs()
			FPSCount=0
		EndIf
	End Method
	Method RenderShadow(X:Float,Y:Float,Z:Float,NCam:NCamera)
		Local FX:Float[3],FY:Float[3],FZ:Float[3],BX:Float[3],BY:Float[3],BZ:Float[3],NP:NPoint = New NPoint
		For Local NEnt:NEntity = EachIn NEntity.NEntityList
			If NEnt.Shadows Then
				For Local NTri:NTriangle = EachIn NEnt.NTriangleList[NEnt.Frame]
					FX[0] = NTri.V1.X+NEnt.X; FY[0] = NTri.V1.Y+NEnt.Y; FZ[0] = NTri.V1.Z+NEnt.Z
	 				FX[1] = NTri.V2.X+NEnt.X; FY[1] = NTri.V2.Y+NEnt.Y; FZ[1] = NTri.V2.Z+NEnt.Z
					FX[2] = NTri.V3.X+NEnt.X; FY[2] = NTri.V3.Y+NEnt.Y; FZ[2] = NTri.V3.Z+NEnt.Z
	
					NP.X = FX[0]-X; NP.Y = FY[0]-Y; NP.Z = FZ[0]-Z
					NP.Normalize()
					NP.X = NP.X * NCam.Far*ShadowDistance; NP.Y = NP.Y * NCam.Far*ShadowDistance; NP.Z = NP.Z * NCam.Far*ShadowDistance
					BX[0] = NP.X; BY[0] = NP.Y; BZ[0] = NP.Z
	
					NP.X = FX[1]-X; NP.Y = FY[1]-Y; NP.Z = FZ[1]-Z
					NP.Normalize()
					NP.X = NP.X * NCam.Far*ShadowDistance; NP.Y = NP.Y * NCam.Far*ShadowDistance; NP.Z = NP.Z * NCam.Far*ShadowDistance
					BX[1] = NP.X; BY[1] = NP.Y; BZ[1] = NP.Z
	
					NP.X = FX[2]-X; NP.Y = FY[2]-Y; NP.Z = FZ[2]-Z
					NP.Normalize()
					NP.X = NP.X * NCam.Far*ShadowDistance; NP.Y = NP.Y * NCam.Far*ShadowDistance; NP.Z = NP.Z * NCam.Far*ShadowDistance
					BX[2] = NP.X; BY[2] = NP.Y; BZ[2] = NP.Z
	
					Rem
					glBegin(GL_TRIANGLES)
						glVertex3f(FX[2],FY[2],FZ[2])
						glVertex3f(FX[1],FY[1],FZ[1])
						glVertex3f(FX[0],FY[0],FZ[0])
						glVertex3f(BX[2],BY[2],BZ[2])
						glVertex3f(BX[1],BY[1],BZ[1])
						glVertex3f(BX[0],BY[0],BZ[0])
					glEnd()
					glBegin(GL_QUADS)
						glVertex3f(BX[0],BY[0],BZ[0])
						glVertex3f(BX[1],BY[1],BZ[1])
						glVertex3f(FX[1],FY[1],FZ[1])
						glVertex3f(FX[0],FY[0],FZ[0])
						glVertex3f(BX[1],BY[1],BZ[1])
						glVertex3f(BX[2],BY[2],BZ[2])
						glVertex3f(FX[2],FY[2],FZ[2])
						glVertex3f(FX[1],FY[1],FZ[1])
						glVertex3f(BX[2],BY[2],BZ[2])
						glVertex3f(BX[0],BY[0],BZ[0])
						glVertex3f(FX[0],FY[0],FZ[0])
						glVertex3f(FX[2],FY[2],FZ[2])
					glEnd()
					End Rem
					
					glBegin(GL_TRIANGLES)
						glVertex3f(FX[0],FY[0],FZ[0])
						glVertex3f(FX[1],FY[1],FZ[1])
						glVertex3f(FX[2],FY[2],FZ[2])
					glEnd()
					glBegin(GL_TRIANGLES)
						glVertex3f(BX[0],BY[0],BZ[0])
						glVertex3f(BX[1],BY[1],BZ[1])
						glVertex3f(BX[2],BY[2],BZ[2])
					glEnd()
					glBegin(GL_QUADS)
						glVertex3f(FX[0],FY[0],FZ[0])
						glVertex3f(FX[1],FY[1],FZ[1])
						glVertex3f(BX[1],BY[1],BZ[1])
						glVertex3f(BX[0],BY[0],BZ[0])
					glEnd()
					glBegin(GL_QUADS)
						glVertex3f(FX[1],FY[1],FZ[1])
						glVertex3f(FX[2],FY[2],FZ[2])
						glVertex3f(BX[2],BY[2],BZ[2])
						glVertex3f(BX[1],BY[1],BZ[1])
					glEnd()
					glBegin(GL_QUADS)		
						glVertex3f(FX[2],FY[2],FZ[2])
						glVertex3f(FX[0],FY[0],FZ[0])
						glVertex3f(BX[0],BY[0],BZ[0])
						glVertex3f(BX[2],BY[2],BZ[2])
					glEnd()
					
				
				Next
			EndIf
		Next
	End Method
	Method DrawShadow()
			glPushMatrix()
			glLoadIdentity()
			glMatrixMode(GL_PROJECTION)
			glPushMatrix()
			glLoadIdentity()
			glOrtho(0, 1, 1, 0, 0, 1)
			glDisable(GL_DEPTH_TEST)
			
			glColor4f(0.0, 0.0, 0.0, 1.0)
			glBegin(GL_QUADS)
			glVertex2i(0, 0)
			glVertex2i(0, 1)
			glVertex2i(1, 1)
			glVertex2i(1, 0)
			glEnd()
			
			glEnable(GL_DEPTH_TEST)
			glPopMatrix()
			glMatrixMode(GL_MODELVIEW)
			glPopMatrix()
	End Method
	Method DrawTri()
			glPushMatrix()
			glLoadIdentity()
			glMatrixMode(GL_PROJECTION)
			glPushMatrix()
			glLoadIdentity()
			glOrtho(0, 1, 1, 0, 0, 1)
			glDisable(GL_DEPTH_TEST)
			
			glColor4f(0.0, 0.0, 0.0, 1.0)
			glBegin(GL_QUADS)
			glVertex2i(0, 0)
			glVertex2i(0, 0.1)
			glVertex2i(0.1, 0.1)
			glVertex2i(0.1, 0)
			glEnd()
			
			glEnable(GL_DEPTH_TEST)
			glPopMatrix()
			glMatrixMode(GL_MODELVIEW)
			glPopMatrix()
	End Method
	Method RenderEntity(NEnt:NEntity,NCam:NCamera)
		If NEnt.Alpha>0.0 And NEnt.CamDistance<NCam.Far Then
			'Shader
			If NEnt.Sha = Null Then
				If CurrentProgramObject <> 0 Then
					CurrentProgramObject = 0
					glUseProgramObjectARB(0)
					glDisable(GL_VERTEX_PROGRAM_ARB)
					glDisable(GL_FRAGMENT_PROGRAM_ARB)
				EndIf
			Else
				If CurrentProgramObject <> NEnt.Sha.ProgramObject Then
					CurrentProgramObject = NEnt.Sha.ProgramObject
					If Not NEnt.ShaderEnabled Then CurrentProgramObject = 0
					glUseProgramObjectARB(CurrentProgramObject)
					If CurrentProgramObject <> 0 Then
						glEnable(GL_VERTEX_PROGRAM_ARB)
						glEnable(GL_FRAGMENT_PROGRAM_ARB)
					Else
						glDisable(GL_VERTEX_PROGRAM_ARB)
						glDisable(GL_FRAGMENT_PROGRAM_ARB)
					EndIf
				EndIf
			EndIf
			'Texture
			If TextureEnabled<> NEnt.TextureEnabled Then
				TextureEnabled =  NEnt.TextureEnabled
				If TextureEnabled Then 
					glEnable(GL_TEXTURE_2D)
				Else
					glDisable(GL_TEXTURE_2D)
				EndIf
			EndIf
			If CurrentTexture<>NEnt.Texture And  NEnt.TextureEnabled Then
				CurrentTexture = NEnt.Texture
				glBindTexture (GL_TEXTURE_2D, CurrentTexture)
			EndIf
			If WireframeEnabled <> NEnt.WireFrame Then
				WireframeEnabled = NEnt.WireFrame
				If WireframeEnabled Then 
					glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
				Else
					glPolygonMode(GL_FRONT, GL_FILL)
				EndIf
			EndIf
			If AlphaEnabled <> NEnt.AlphaEnabled Then
				AlphaEnabled = NEnt.AlphaEnabled
				If AlphaEnabled Then
					glEnable(GL_BLEND)
				Else
					glDisable(GL_BLEND)
				EndIf
			EndIf
			If NEnt.Blend And (AlphaEnabled = 0) Then
				AlphaEnabled = 1
				glEnable(GL_BLEND)
			EndIf
			glPushMatrix()
			glTranslatef(NEnt.X,NEnt.Y,NEnt.Z)
			glRotatef(NEnt.Pitch,1.0,0,0)
			glRotatef(NEnt.Yaw,0,1.0,0)
			glRotatef(NEnt.Roll,0,0,1.0)
			If NEnt.DisplayListCreated[NEnt.Frame] Then
				'glEnableClientState(GL_VERTEX_ARRAY)
				'glEnableClientState(GL_TEXTURE_COORD_ARRAY)
				'glBindBufferARB( GL_ARRAY_BUFFER_ARB, NEnt.VertexVBO[NEnt.Frame] )
				'glVertexPointer( 3, GL_FLOAT, 0, Null)
				'	
				'glBindBufferARB( GL_ARRAY_BUFFER_ARB, NEnt.UvVBO[NEnt.Frame] )
				'glTexCoordPointer( 2, GL_FLOAT, 0, Null)
				'glDrawArrays(GL_TRIANGLES,0, NEnt.VertexArrayLength[NEnt.Frame])
				'glDisableClientState(GL_VERTEX_ARRAY)
				'glDisableClientState(GL_TEXTURE_COORD_ARRAY)
				glCallList(NEnt.DisplayList[NEnt.Frame])
			Else
				RuntimeError "Display list not created!"
			EndIf
			Triangles = Triangles+NEnt.Triangles/NEnt.Frames
			For Local Chi:NEntity = EachIn NEnt.ChildList
				RenderEntity(Chi,NCam)
			Next
			glPopMatrix()
		EndIf
	End Method
	Method Swap(VWait:Int=1)
		Flip VWait
	End Method
	Method Clear()
		glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
	End Method
	Method ClsColor(R:Int,G:Int,B:Int)
		If R>255 Then R=255
		If G>255 Then G=255
		If B>255 Then B=255
		If R<0 Then R=0
		If G<0 Then G=0
		If B<0 Then B=0
		glClearColor(R/255.0,G/255.0,B/255.0,0.0)
	End Method
	Method New()
		NGraphics.Gfx = Self
		ShadowList = CreateList()
	End Method
	Function Start:NGraphics(NewWidth:Int=800,NewHeight:Int=600,NewDepth:Int=32,NewScreen:Int=0)
		Local Gfx:NGraphics = New NGraphics
		Gfx.Width = NewWidth
		Gfx.Height = NewHeight
		Gfx.Depth = NewDepth
		Gfx.Fullscreen = NewScreen
		
		Local Flags:Int = GRAPHICS_BACKBUFFER|GRAPHICS_DEPTHBUFFER|GRAPHICS_STENCILBUFFER
		If NewScreen = 0 Then Gfx.Depth = 0
		GLGraphics(Gfx.Width,Gfx.Height,Gfx.Depth,0,Flags)
		GlewInit()
		glViewport(0,0,Gfx.Width,Gfx.Height)
		glShadeModel(GL_SMOOTH)
		glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST)
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
		glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST
		glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST
		glClearColor(0,0,0, 0.0)
		glColor3f(1.0, 1.0, 1.0)
		glClearDepth(1.0)
		glEnable(GL_CULL_FACE)
		'glEnable(GL_BLEND)
		glEnable(GL_DEPTH_TEST) 
		glDepthFunc(GL_LEQUAL) 
		glMatrixMode(GL_PROJECTION)
		glLoadIdentity()
		gluPerspective(45.0, Float(Gfx.Width)/Float(Gfx.Height), 0.1, 1000.0)
		glMatrixMode(GL_MODELVIEW)
		glLoadIdentity()
		'glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
		Gfx.FPSTimer = MilliSecs()
		Gfx.FPS = 1
		
		If NEntity.NEntityList = Null Then NEntity.NEntityList = CreateList()
		If NParticleEmitter.NParticleEmitterList = Null Then NParticleEmitter.NParticleEmitterList = CreateList()
		Return Gfx
	End Function
End Type
Type NEntity Extends NPoint
	Field Name:String
	Field Pitch:Float
	Field Yaw:Float
	Field Roll:Float
	Field Alpha:Float
	Field NTriangleList:TList[512]
	Field NVertexList:TList[512]
	Field Frame:Int
	Field Frames:Int
	Field FrameTime:Int
	Field FrameTimer:Int
	Field ScaleX:Float,ScaleY:Float,ScaleZ:Float
	Field TextureEnabled:Int
	Field TextureWidth:Int,TextureHeight:Int
	Field Texture:Int
	Field WireFrame:Int
	Field CamDistance:Float
	Field Triangles:Int
	Field DisplayList:Int[512],DisplayListCreated:Int[512]
	Field AlphaEnabled:Int
	Field CreateDList:Int
	Field ColX:Float,ColY:Float,ColZ:Float
	Field ColWidth:Float,ColHeight:Float,ColDepth:Float
	Field Sha:NShader
	Field ShaderEnabled:Int
	Field Parent:NEntity
	Field ChildList:TList
	Field EntityType:Int
	Field Blend:Int
	Field RenderFirst:Int
	Field Shadows:Int
	'VBO
	Field VertexVBO:Int[512],UvVBO:Int[512]
	Field VertexArray:Float[512,0]
	Field UvArray:Float[512,0]
	Field VertexArrayLength:Int[512]
	Global NEntityList:TList
	'Partly done Vertex Buffer Objects.. My benchmark shows them to be slower than display lists when using models with
	'few thousands triangles..
	Method CreateDisplayList_()
		Print "Creating display lists"
		Local Vertices:Int = NTriangleList[Frame].Count()*9,VertexC:Int=0,UVC:Int=0
		VertexArray = New Float[1,Vertices]
		UvArray = New Float[1,Vertices]
		For Local Fr:Int = 0 To Frames-1
			DisplayListCreated[Fr] = 1 
			VertexArrayLength[Fr] = Vertices/3
			VertexC=0; UVC=0
			Triangles=0
			For Local NTri:NTriangle = EachIn NTriangleList[Fr]
				Triangles:+1
				VertexArray[Fr,VertexC] = NTri.V1.X
				VertexArray[Fr,VertexC+1] = NTri.V1.Y
				VertexArray[Fr,VertexC+2] = NTri.V1.Z
				VertexArray[Fr,VertexC+3] = NTri.V2.X
				VertexArray[Fr,VertexC+4] = NTri.V2.Y
				VertexArray[Fr,VertexC+5] = NTri.V2.Z
				VertexArray[Fr,VertexC+6] = NTri.V3.X
				VertexArray[Fr,VertexC+7] = NTri.V3.Y
				VertexArray[Fr,VertexC+8] = NTri.V3.Z
				UvArray[Fr,UVC] = NTri.V1s
				UvArray[Fr,UVC+1] = NTri.V1t
				UvArray[Fr,UVC+2] = NTri.V2s
				UvArray[Fr,UVC+3] = NTri.V2t
				UvArray[Fr,UVC+4] = NTri.V3s
				UvArray[Fr,UVC+5] = NTri.V3t
				VertexC:+9
				UVC:+6
			Next
		Next
		Local TempVertex:Float[Vertices],TempUv:Float[Vertices]
		For Local a:Int = 0 To Vertices-1
			TempVertex[a] = VertexArray[Frame,a]
			TempUv[a] = UvArray[Frame,a]
		Next
		glGenBuffersARB(1, Varptr(VertexVBO[Frame])) 'Get A Valid Name
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, VertexVBO[Frame] ) 'Bind The Buffer
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, SizeOf(TempVertex), TempVertex, GL_STATIC_DRAW_ARB )
		glGenBuffersARB( 1, Varptr(UvVBO[Frame]) )
		glBindBufferARB( GL_ARRAY_BUFFER_ARB, UvVBO[Frame] )
		glBufferDataARB( GL_ARRAY_BUFFER_ARB, SizeOf(TempUv), TempUv, GL_STATIC_DRAW_ARB )
	End Method
	Method AttachShader(NewSha:NShader)
		Sha = NewSha
		ShaderEnabled = 1
	End Method
	Method CreateDisplayList()
		AlphaEnabled = 0
		Local CAlpha:Float,OptimizeColors:Int=0,Stepper:Int=0
		For Local a:Int = 0 To Frames-1
			If DisplayListCreated[a] = 1 Then glDeleteLists(DisplayList[a],1)
			DisplayListCreated[a] = 1
			DisplayList[a] = glGenLists(1)
			glNewList(DisplayList[a], GL_COMPILE)
			glBegin(GL_TRIANGLES)
			For Local NTri:NTriangle = EachIn NTriangleList[a]
				'If Stepper=0 Then glBegin(GL_TRIANGLES)
				'Stepper:+1
				If Alpha*(NTri.V1.Alpha+NTri.V2.Alpha+NTri.V3.Alpha) < 3.0 Then AlphaEnabled = 1
				If NTri.V1.R+NTri.V1.G+NTri.V1.B+NTri.V2.R+NTri.V2.G+NTri.V2.B+NTri.V3.R+NTri.V3.G+NTri.V3.B=9.0 Then OptimizeColors=1
			'	glBegin(GL_TRIANGLES)
				If TextureEnabled Then
					glTexCoord2f (NTri.V1s,NTri.V1t)
					If NTri.V1NX<>0.0 Or NTri.V1NY<>0.0 Or NTri.V1NZ<>0.0 glNormal3f(NTri.V1NX,NTri.V1NY,NTri.V1NZ)
				EndIf
				CAlpha = Alpha*NTri.V1.Alpha
				If (CAlpha) <> 1.0 Then
					glColor4f(NTri.V1.R,NTri.V1.G,NTri.V1.B,CAlpha)
				Else
					glColor3f(NTri.V1.R,NTri.V1.G,NTri.V1.B)
				EndIf
				glVertex3f(NTri.V1.X,NTri.V1.Y,NTri.V1.Z)
				If TextureEnabled Then
					glTexCoord2f (NTri.V2s,NTri.V2t)
					If NTri.V2NX<>0.0 Or NTri.V2NY<>0.0 Or NTri.V2NZ<>0.0 glNormal3f(NTri.V2NX,NTri.V2NY,NTri.V2NZ)
				EndIf
				CAlpha = Alpha*NTri.V2.Alpha
				If (CAlpha) <> 1.0 Then
					glColor4f(NTri.V2.R,NTri.V2.G,NTri.V2.B,CAlpha)
				ElseIf Not OptimizeColors
					glColor3f(NTri.V2.R,NTri.V2.G,NTri.V2.B)
				EndIf
				glVertex3f(NTri.V2.X,NTri.V2.Y,NTri.V2.Z)
				If TextureEnabled Then
					glTexCoord2f (NTri.V3s,NTri.V3t)
					If NTri.V3NX<>0.0 Or NTri.V3NY<>0.0 Or NTri.V3NZ<>0.0 glNormal3f(NTri.V3NX,NTri.V3NY,NTri.V3NZ)
				EndIf
				CAlpha = Alpha*NTri.V3.Alpha
				If (CAlpha) <> 1.0 Then
					glColor4f(NTri.V3.R,NTri.V3.G,NTri.V3.B,CAlpha)
				ElseIf Not OptimizeColors
					glColor3f(NTri.V3.R,NTri.V3.G,NTri.V3.B)
				EndIf
				glVertex3f(NTri.V3.X,NTri.V3.Y,NTri.V3.Z)
				'If Stepper = 10 Then 
					'glEnd()
				'	Stepper=0
		'		EndIf
			Next
			glEnd()
			glEndList()
		Next
	End Method
	Method Turn(NewPitch:Float,NewYaw:Float,NewRoll:Float)
		Pitch = Pitch+NewPitch Mod 360.0
		Yaw = Yaw+NewYaw Mod 360.0
		Roll = Roll+NewRoll Mod 360.0
		If Pitch<0.0 Then Pitch=Pitch+360.0
		If Yaw<0.0 Then Yaw=Yaw+360.0
		If Roll<0.0 Then Roll=Roll+360.0
		If Pitch>360.0 Then Pitch=Pitch-360.0
		If Yaw>360.0 Then Yaw=Yaw-360.0
		If Roll>360.0 Then Roll=Roll-360.0
	End Method
	Method Rotate(NewPitch:Float,NewYaw:Float,NewRoll:Float)
		Pitch = NewPitch Mod 360.0
		Yaw = NewYaw Mod 360.0
		Roll = NewRoll Mod 360.0
		While Pitch<0.0
			Pitch=Pitch+360.0
		Wend
		While Yaw<0.0
			Yaw=Yaw+360.0
		Wend
		While Roll<0.0
			Roll=Roll+360.0
		Wend
		While Pitch>360.0
			Pitch = Pitch-360.0
		Wend
		While Yaw>360.0
			Yaw = Yaw-360.0
		Wend
		While Roll>360.0
			Roll = roll-360.0
		Wend
	End Method
	Method Move(NewX:Float,NewY:Float,NewZ:Float)
		Local MoveMat:TMatrix=New TMatrix
		MoveMat.LoadIdentity()
		MoveMat.RotateYaw(Yaw)
		MoveMat.RotatePitch(Pitch)
		MoveMat.RotateRoll(Roll)
		MoveMat.Translate(NewX,NewY,-NewZ)
		X = X+MoveMat.grid[3,0]
		Y = Y+MoveMat.grid[3,1]
		Z = Z+MoveMat.grid[3,2]
	End Method
	Method AddTriangle(V1:NVertex,V2:NVertex,V3:NVertex,V1s:Float=0,V1t:Float=0,V2s:Float=0,V2t:Float=0,V3s:Float=0,V3t:Float=0,V1n:Byte=0,V2n:Byte=0,V3n:Byte=0)
		Local NewTri:NTriangle = New NTriangle
		NewTri.V1 = V1
		NewTri.V2 = V2
		NewTri.V3 = V3
		NewTri.V1s=V1s
		NewTri.V1t=V1t
		NewTri.V2s=V2s
		NewTri.V2t=V2t
		NewTri.V3s=V3s
		NewTri.V3t=V3t
		NewTri.V1NX = Anorms[V1n*3]
		NewTri.V1NY = Anorms[V1n*3+1]
		NewTri.V1NZ = Anorms[V1n*3+2]
		NewTri.V2NX = Anorms[V2n*3]
		NewTri.V2NY = Anorms[V2n*3+1]
		NewTri.V2NZ = Anorms[V2n*3+2]
		NewTri.V3NX = Anorms[V3n*3]
		NewTri.V3NY = Anorms[V3n*3+1]
		NewTri.V3NZ = Anorms[V3n*3+2]
		ListAddLast(Self.NTriangleList[Self.Frame],NewTri)
		ListAddLast(Self.NVertexList[Self.Frame],V1)
		ListAddLast(Self.NVertexList[Self.Frame],V2)
		ListAddLast(Self.NVertexList[Self.Frame],V3)
		Triangles=Triangles+1
	End Method
	Method AddTri(V1X:Float,V1Y:Float,V1Z:Float,V2X:Float,V2Y:Float,V2Z:Float,V3X:Float,V3Y:Float,V3Z:Float,V1s:Float=0,V1t:Float=0,V2s:Float=0,V2t:Float=0,V3s:Float=0,V3t:Float=0,V1n:Byte=0,V2n:Byte=0,V3n:Byte=0)
		Local NV1:NVertex = New NVertex
		Local NV2:NVertex = New NVertex
		Local NV3:NVertex = New NVertex
		NV1.OrigX = V1X; NV1.OrigY = V1Y; NV1.OrigZ = V1Z
		NV2.OrigX = V2X; NV2.OrigY = V2Y; NV2.OrigZ = V2Z
		NV3.OrigX = V3X; NV3.OrigY = V3Y; NV3.OrigZ = V3Z
		NV1.X = V1X
		NV1.Y = V1Y
		NV1.Z = V1Z
		NV2.X = V2X
		NV2.Y = V2Y
		NV2.Z = V2Z
		NV3.X = V3X
		NV3.Y = V3Y
		NV3.Z = V3Z
		Self.AddTriangle(NV1,NV2,NV3,V1s,V1t,V2s,V2t,V3s,V3t,V1n,V2n,V3n)
	End Method
	Method LoadTexture(Filename:String)
		If TextureEnabled = False Then
			If FileType(Filename)=0 Then RuntimeError Filename+" not found!"
			Local TXMap:TPixmap = LoadPixmap(Filename)
			If Not TXMap Then RuntimeError "Could not load texture!"
			Texture = GLTexFromPixmap(TXMap)
			If Not Texture Then RuntimeError "Could not load texture to memory!"
			TextureEnabled = True
			TextureWidth = TXMap.Width
			TextureHeight = TXMap.Height
		EndIf
		CreateDList = 1
	End Method
	Method FreeTexture()
		If NEntity.TextureUseCount(Texture) = 1 Then glDeleteTextures(1,Varptr(Texture))
	End Method
	Method ScaleMesh(NewScaleX:Float,NewScaleY:Float,NewScaleZ:Float)
		For Local Fr:Int = 0 To Frames-1
			For Local NVer:NVertex = EachIn NVertexList[Fr]
				NVer.X = NVer.X*NewScaleX
				NVer.Y = NVer.Y*NewScaleY
				NVer.Z = NVer.Z*NewScaleZ
				NVer.OrigX = NVer.X
				NVer.OrigY = NVer.Y
				NVer.OrigZ = NVer.Z
			Next
		Next
		CreateDList = 1
	End Method
	Method MoveMesh(NewX:Float,NewY:Float,NewZ:Float)
		For Local Fr:Int = 0 To Frames-1
			For Local NVer:NVertex = EachIn NVertexList[Fr]
				NVer.X = NVer.X+NewX
				NVer.Y = NVer.Y+NewY
				NVer.Z = NVer.Z+NewZ
			Next
		Next
		CreateDList = 1
	End Method
	Method FreeEntity()
		For Local Fr:Int = 0 To Frames-1
			If NEntity.ListUseCount(DisplayList[Fr]) = 1 Then glDeleteLists(DisplayList[Fr],1)
		Next
		FreeTexture()
		ListRemove(NEntityList,Self)
	End Method
	Method AddMesh(NEnt:NEntity)
		Print "Adding mesh"
		For Local NTri:NTriangle = EachIn NEnt.NTriangleList[NEnt.Frame]
			Local NewTri:NTriangle = New NTriangle
			NewTri.V1s = NTri.V1s; NewTri.V1t = NTri.V1t
			NewTri.V2s = NTri.V2s; NewTri.V2t = NTri.V2t 
			NewTri.V3s = NTri.V3s; NewTri.V3t = NTri.V3t
			NewTri.V1NX = NTri.V1NX; NewTri.V1NY = NTri.V1NY; NewTri.V1NZ = NTri.V1NZ
			NewTri.V2NX = NTri.V2NX; NewTri.V2NY = NTri.V2NY; NewTri.V2NZ = NTri.V2NZ
			NewTri.V3NX = NTri.V3NX; NewTri.V3NY = NTri.V3NY; NewTri.V3NZ = NTri.V3NZ
			NewTri.V1 = New NVertex; NewTri.V2 = New NVertex; NewTri.V3 = New NVertex
			NewTri.V1.X = NTri.V1.X
			NewTri.V1.Y = NTri.V1.Y
			NewTri.V1.Z = NTri.V1.Z
			NewTri.V2.X = NTri.V2.X
 			NewTri.V2.Y = NTri.V2.Y
			NewTri.V2.Z = NTri.V2.Z
			NewTri.V3.X = NTri.V3.X; NewTri.V3.Y = NTri.V3.Y; NewTri.V3.Z = NTri.V3.Z
			NewTri.V1.OrigX = NTri.V1.OrigX; NewTri.V1.OrigY = NTri.V1.OrigY; NewTri.V1.OrigZ = NTri.V1.OrigZ
			NewTri.V2.OrigX = NTri.V2.OrigX; NewTri.V2.OrigY = NTri.V2.OrigY; NewTri.V2.OrigZ = NTri.V2.OrigZ
			NewTri.V3.OrigX = NTri.V3.OrigX; NewTri.V3.OrigY = NTri.V3.OrigY; NewTri.V3.OrigZ = NTri.V3.OrigZ
			NewTri.V1.Alpha = NTri.V1.Alpha; NewTri.V2.Alpha = NTri.V2.Alpha; NewTri.V3.Alpha = NTri.V3.Alpha
			NewTri.V1.R = NTri.V1.R; NewTri.V1.G = NTri.V1.G; NewTri.V1.B = NTri.V1.B
			NewTri.V2.R = NTri.V2.R; NewTri.V2.G = NTri.V2.G; NewTri.V2.B = NTri.V2.B
			NewTri.V3.R = NTri.V3.R; NewTri.V3.G = NTri.V3.G; NewTri.V3.B = NTri.V3.B
			ListAddLast(Self.NTriangleList[Self.Frame],NewTri)
			ListAddLast(Self.NVertexList[Self.Frame],NewTri.V1)
			ListAddLast(Self.NVertexList[Self.Frame],NewTri.V2)
			ListAddLast(Self.NVertexList[Self.Frame],NewTri.V3)
		Next
		CreateDList = 1
	End Method
	Method FlipMesh()
		Local HoldFloat:Float,HoldVertex:NVertex
		For Local Fr:Int = 0 To Frames-1
			For Local NTri:NTriangle = EachIn NTriangleList[Fr]
				HoldVertex = NTri.V1
				NTri.V1 = NTri.V3
				NTri.V3 = HoldVertex
				HoldFloat = NTri.V1s
				NTri.V1s = NTri.V3s
				NTri.v3s = HoldFloat
				HoldFloat= NTri.V1t
				NTri.V1t = NTri.V3t
				NTri.V3t = HoldFloat
				HoldFloat = NTri.V1NX
				NTri.V1NX = NTri.V3NX
				NTri.V3NX = HoldFloat
				HoldFloat = NTri.V1NY
				NTri.V1NY = NTri.V3NY
				NTri.V3NY = HoldFloat
				HoldFloat = NTri.V1NZ
				NTri.V1NZ = NTri.V3NZ
				NTri.V3NZ = HoldFloat
		
			Next
		Next
		CreateDList = 1
	End Method
	Method TurnMesh(NewPitch:Float,NewYaw:Float,NewRoll:Float)
		For Local Fr:Int = 0 To Frames-1
			For Local NVer:NVertex = EachIn NVertexList[Fr]
				Local OldX:Float = NVer.X
				Local OldY:Float = NVer.Y
				Local OldZ:Float = NVer.Z
				NVer.Y = (OldY*Cos(NewPitch)) - (OldZ*Sin(NewPitch))
				NVer.Z = (OldZ*Cos(NewPitch)) + (OldY*Sin(NewPitch))
				OldY = NVer.Y
				OldZ = NVer.Z
				NVer.Z = (OldZ*Cos(NewYaw)) - (OldX*Sin(NewYaw))
				NVer.X = (OldX*Cos(NewYaw)) + (OldZ*Sin(NewYaw))
				OldZ = NVer.Z
				OldX = NVer.X
				NVer.X = (OldX*Cos(NewRoll)) - (OldY*Sin(NewRoll))
				NVer.Y = (OldY*Cos(NewRoll)) + (OldX*Sin(NewRoll))
			Next
		Next
		CreateDList = 1
	End Method
	Method ScaleEntity(NewScaleX:Float,NewScaleY:Float,NewScaleZ:Float)
		ScaleX = ScaleX * NewScaleX
		ScaleY = ScaleY * NewScaleY
		ScaleZ = ScaleZ * NewScaleZ
		For Local Fr:Int = 0 To Frames-1
			For Local NVer:NVertex = EachIn NVertexList[Fr]
				NVer.X = NVer.OrigX*ScaleX
				NVer.Y = NVer.OrigY*ScaleY
				NVer.Z = NVer.OrigZ*ScaleZ
			Next
		Next
		CreateDList = 1
	End Method
	Method Animate()
		If MilliSecs()>FrameTimer+FrameTime Then
				FrameTimer = MilliSecs()
				Frame=Frame+1
				If Frame>=Frames Then Frame=0
		EndIf
	End Method
	Method Free()
		ListRemove(NEntity.NEntityList,Self)
	End Method
	Method EntityParent(NewParent:NEntity)
		If NewParent <> Null Then
			Parent = NewParent
			ListAddLast(Parent.ChildList,Self)
		Else
			If Parent <> Null Then
				ListRemove(Parent.ChildList,Self)
				Parent = Null
			EndIf
		EndIf
	End Method
	Method New()
		For Local a:Int = 0 To 511
			NTriangleList[a] = CreateList()
			NVertexList[a] = CreateList()
		Next
		ChildList = CreateList()
		ListAddLast(NEntity.NEntityList,Self)
		Alpha = 1.0
		Frames = 1
		ScaleX = 1.0
		ScaleY = 1.0
		ScaleZ = 1.0
		FrameTimer = MilliSecs()
		FrameTime = 1
	End Method
	Method EntityColor(NewR:Int,NewG:Int,NewB:Int)
		For Local Fr:Int = 0 To Frames-1
			For Local NVer:NVertex = EachIn NVertexList[Fr]	
				NVer.R = NewR/255.0
				NVer.G = NewG/255.0
				NVer.B = NewB/255.0
			Next
		Next
		CreateDList = 1
	End Method
	Method EntityAlpha(NewAlpha:Float)
		Alpha = NewAlpha
		CreateDList = 1
	End Method
	Method Interpolate()
		If Frames>1 Then
			For Local a:Int = 0 To Frames-1
				Local TriCount:Int=0
				For Local NTri:NTriangle = EachIn NTriangleList[a]
					TriCount:+1
					Local NTri2:NTriangle = New NTriangle
					NTri2.V1s=NTri.V1s; NTri2.V1t=NTri.V1t
					NTri2.V2s=NTri.V2s; NTri2.V2t=NTri.V2t
					NTri2.V3s=NTri.V3s; NTri2.V3t=NTri.V3t
					NTri2.V1NX = NTri.V1NX; NTri2.V1NY = NTri.V1NY; NTri2.V1NZ = NTri.V1NZ
					NTri2.V2NX = NTri.V2NX; NTri2.V2NY = NTri.V2NY; NTri2.V2NZ = NTri.V2NZ
					NTri2.V3NX = NTri.V3NX; NTri2.V3NY = NTri.V3NY; NTri2.V3NZ = NTri.V3NZ
					NTri2.V1 = New NVertex; NTri2.V2 = New NVertex; NTri2.V3 = New NVertex
					NTri2.V1.R = NTri.V1.R; NTri2.V1.G = NTri.V1.G; NTri2.V1.B = NTri.V1.B; NTri2.V1.Alpha = NTri.V1.Alpha
					NTri2.V2.R = NTri.V2.R; NTri2.V2.G = NTri.V2.G; NTri2.V2.B = NTri.V2.B; NTri2.V2.Alpha = NTri.V2.Alpha 
					NTri2.V3.R = NTri.V3.R; NTri2.V3.G = NTri.V3.G; NTri2.V3.B = NTri.V3.B; NTri2.V3.Alpha = NTri.V3.Alpha
					NTri2.V1.X = (NTri.V1.X+GetTriangle(TriCount,NextFrame(a)).V1.X)*0.5
					NTri2.V1.Y = (NTri.V1.Y+GetTriangle(TriCount,NextFrame(a)).V1.Y)*0.5
					NTri2.V1.Z = (NTri.V1.Z+GetTriangle(TriCount,NextFrame(a)).V1.Z)*0.5
					NTri2.V2.X = (NTri.V2.X+GetTriangle(TriCount,NextFrame(a)).V2.X)*0.5
					NTri2.V2.Y = (NTri.V2.Y+GetTriangle(TriCount,NextFrame(a)).V2.Y)*0.5
					NTri2.V2.Z = (NTri.V2.Z+GetTriangle(TriCount,NextFrame(a)).V2.Z)*0.5
					NTri2.V3.X = (NTri.V3.X+GetTriangle(TriCount,NextFrame(a)).V3.X)*0.5
					NTri2.V3.Y = (NTri.V3.Y+GetTriangle(TriCount,NextFrame(a)).V3.Y)*0.5
					NTri2.V3.Z = (NTri.V3.Z+GetTriangle(TriCount,NextFrame(a)).V3.Z)*0.5
					ListAddLast(NTriangleList[Frames+a],NTri2)
				Next
			Next
			For Local a:Int = 1 To Frames*2-1
				
			Next
			Frames:*2
		EndIf
	End Method
	Method NextFrame:Int(Fr:Int)
		If Fr+1 >= Frames Then Return 0
		Return Fr+1
	End Method
	Method Intersect:Int(NEnt:NEntity)
		Local linept:NPoint = New NPoint,vect:NPoint = New NPoint,V1:NPoint = New NPoint,V2:NPoint = New NPoint,V3:NPoint = New NPoint
		If NEnt = Self Then Return False
		For Local NTri:NTriangle = EachIn NTriangleList[Frame]
			For Local NTri2:NTriangle = EachIn NEnt.NTriangleList[NEnt.Frame]
				V1.X = NTri2.V1.X+NEnt.X; V1.Y = NTri2.V1.Y+NEnt.Y; V1.Z = NTri2.V1.Z+NEnt.Z
				V2.X = NTri2.V2.X+NEnt.X; V2.Y = NTri2.V2.Y+NEnt.Y; V2.Z = NTri2.V2.Z+NEnt.Z
				V3.X = NTri2.V3.X+NEnt.X; V3.Y = NTri2.V3.Y+NEnt.Y; V3.Z = NTri2.V3.Z+NEnt.Z
				linept.X = NTri.V1.X+X; linept.Y = NTri.V1.Y+Y; linept.Z = NTri.V1.Z+Z
				vect.X = NTri.V2.X-NTri.V1.X
 				vect.Y = NTri.V2.Y-NTri.V1.Y
 				vect.Z = NTri.V2.Z-NTri.V1.Z
				If LineIntersectsTriangle(V1,V2,V3,linept,vect) Then Return True
				linept.X = NTri.V2.X+X; linept.Y = NTri.V2.Y+Y; linept.Z = NTri.V2.Z+Z
				vect.X = NTri.V3.X-NTri.V2.X
				vect.Y = NTri.V3.Y-NTri.V2.Y
				vect.Z = NTri.V3.Z-NTri.V2.Z
				If LineIntersectsTriangle(V1,V2,V3,linept,vect) Then Return True
				linept.X = NTri.V3.X+X; linept.Y = NTri.V3.Y+Y; linept.Z = NTri.V3.Z+Z
				vect.X = NTri.V1.X-NTri.V3.X
				vect.Y = NTri.V1.Y-NTri.V3.Y
				vect.Z = NTri.V1.Z-NTri.V3.Z
				If LineIntersectsTriangle(V1,V2,V3,linept,vect) Then Return True
			Next
		Next
		Return False
	End Method
	Method CopyEntity:NEntity()
		CreateDisplayList(); CreateDList = 0
		Local NEnt2:NEntity = New NEntity
		NEnt2.Name = Self.Name
		NEnt2.Alpha = Self.Alpha
		NEnt2.Frame = Self.Frame
		NEnt2.Frames = Self.Frames
		NEnt2.FrameTime = Self.FrameTime
		NEnt2.FrameTimer = Self.FrameTimer
		NEnt2.ScaleX = Self.ScaleX
		NEnt2.ScaleY = Self.ScaleY
		NEnt2.ScaleZ = Self.ScaleZ
		NEnt2.TextureEnabled = Self.TextureEnabled
		NEnt2.TextureWidth = Self.TextureWidth
		NEnt2.TextureHeight = Self.TextureHeight
		NEnt2.Texture = Self.Texture
		NEnt2.AlphaEnabled = Self.AlphaEnabled
		NEnt2.VertexVBO = Self.VertexVBO
		NEnt2.UvVBO = Self.UvVBO
		NEnt2.VertexArray = Self.VertexArray
		NEnt2.VertexArrayLength = Self.VertexArrayLength
		NEnt2.UvArray = Self.UvArray
		NEnt2.CreateDList = Self.CreateDList
		NEnt2.Sha = Self.Sha
		NEnt2.ShaderEnabled = Self.ShaderEnabled
		NEnt2.EntityType = Self.EntityType
		NEnt2.Blend = Self.Blend
		NEnt2.RenderFirst = Self.RenderFirst
		NEnt2.Shadows = Self.Shadows
		For Local a:Int = 0 To Self.Frames-1
				NEnt2.DisplayListCreated[a] = Self.DisplayListCreated[a]
			NEnt2.DisplayList[a] = Self.DisplayList[a]
			For Local NTri:NTriangle = EachIn Self.NTriangleList[a]
				Local NTri2:NTriangle = New NTriangle
				NEnt2.Triangles:+1
				NTri2.V1s = NTri.V1s; NTri2.V1t = NTri.V1t
				NTri2.V2s = NTri.V2s; NTri2.V2t = NTri.V2t
				NTri2.V3s = NTri.V3s; NTri2.V3t = NTri.V3t	
				NTri2.V1NX = NTri.V1NX; NTri2.V1NY = NTri.V1NY; NTri2.V1NZ = NTri.V1NZ
				NTri2.V2NX = NTri.V2NX; NTri2.V2NY = NTri.V2NY; NTri2.V2NZ = NTri.V2NZ
				NTri2.V3NX = NTri.V3NX; NTri2.V3NY = NTri.V3NY; NTri2.V3NZ = NTri.V3NZ
				NTri2.V1 = New NVertex 
				NTri2.V2 = New NVertex 
				NTri2.V3 = New NVertex
				NTri2.V1.OrigX = NTri.V1.OrigX; NTri2.V1.OrigY = NTri.V1.OrigY; NTri2.V1.OrigZ = NTri.V1.OrigZ
				NTri2.V2.OrigX = NTri.V2.OrigX; NTri2.V2.OrigY = NTri.V2.OrigY; NTri2.V2.OrigZ = NTri.V2.OrigZ
				NTri2.V3.OrigX = NTri.V3.OrigX; NTri2.V3.OrigY = NTri.V3.OrigY; NTri2.V3.OrigZ = NTri.V3.OrigZ
				NTri2.V1.X = NTri.V1.X; NTri2.V1.Y = NTri.V1.Y; NTri2.V1.Z = NTri.V1.Z
				NTri2.V2.X = NTri.V2.X; NTri2.V2.Y = NTri.V2.Y; NTri2.V2.Z = NTri.V2.Z
				NTri2.V3.X = NTri.V3.X; NTri2.V3.Y = NTri.V3.Y; NTri2.V3.Z = NTri.V3.Z
				NTri2.V1.R = NTri.V1.R; NTri2.V1.G = NTri.V1.G; NTri2.V1.B = NTri.V1.B
				NTri2.V2.R = NTri.V2.R; NTri2.V2.G = NTri.V2.G; NTri2.V2.B = NTri.V2.B 
				NTri2.V3.R = NTri.V3.R; NTri2.V3.G = NTri.V3.G; NTri2.V3.B = NTri.V3.B
				NTri2.V1.Alpha = NTri.V1.Alpha
				NTri2.V2.Alpha = NTri.V2.Alpha
				NTri2.V3.Alpha = NTri.V3.Alpha
				ListAddLast(NEnt2.NTriangleList[NEnt2.Frame],NTri2)
				ListAddLast(NEnt2.NVertexList[NEnt2.Frame],NTri2.V1)
				ListAddLast(NEnt2.NVertexList[NEnt2.Frame],NTri2.V2)
				ListAddLast(NEnt2.NVertexList[NEnt2.Frame],NTri2.V3)
			Next
		Next
		Return NEnt2
	End End Method
	
	Method GetTriangle:NTriangle(TriCount:Int,Fr:Int)
		Local TriCounter:Int=0
		For Local NTri:NTriangle = EachIn NTriangleList[Fr]
			TriCounter:+1
			If TriCounter=TriCount Then Return NTri
		Next
	End Method
	Function CreateCube:NEntity()
		Local NEnt:NEntity = New NEntity
		NEnt.AddTri(-1,1,1,-1,-1,1,1,1,1,0,0,0,1,1,0) 'FRONT TRI 1
		NEnt.AddTri(-1,-1,1,1,-1,1,1,1,1,0,1,1,1,1,0) 'FRONT TRI2
		NEnt.AddTri(1,1,1,1,-1,1,1,-1,-1,1,0,1,1,0,1) 'RIGHT TRI1
		NEnt.AddTri(1,1,1,1,-1,-1,1,1,-1,1,0,0,1,0,0) 'RIGHT TRI2
		NEnt.AddTri(1,1,-1,1,-1,-1,-1,-1,-1,0,0,0,1,1,1) 'BACK TRI1
		NEnt.AddTri(1,1,-1,-1,-1,-1,-1,1,-1,0,0,1,1,1,0) 'BACK TRI2
		NEnt.AddTri(-1,1,-1,-1,-1,-1,-1,-1,1,1,0,1,1,0,1) 'LEFT TRI1
		NEnt.AddTri(-1,1,-1,-1,-1,1,-1,1,1,1,0,0,1,0,0) 'LEFT TRI2
		NEnt.AddTri(-1,-1,1,-1,-1,-1,1,-1,-1,0,1,0,0,1,0 ) 'BOTTOM TRI1
		NEnt.AddTri(-1,-1,1,1,-1,-1,1,-1,1,0,1,1,0,1,1) 'BOTTOM TRI2
		NEnt.AddTri(1,1,-1,-1,1,-1,-1,1,1,1,1,0,1,0,0) 'TOP TRI1
		NEnt.AddTri(1,1,1, 1,1,-1,-1,1,1,1,0,1,1,0,0 ) 'TOP TRI2
		NEnt.CreateDList = 1
		Return NEnt
	End Function
	Function CreateSprite:NEntity()
		Local NEnt:NEntity = New NEntity
		NEnt.AddTri(-1,1,1,-1,-1,1,1,1,1,0,0,0,1,1,0) 'FRONT TRI 1
		NEnt.AddTri(-1,-1,1,1,-1,1,1,1,1,0,1,1,1,1,0) 'FRONT TRI2
		NEnt.CreateDList = 1
		Return NEnt
	End Function
	Function Heightmap:NEntity(Filename:String)
		If FileType(Filename)=0 Then RuntimeError Filename+" not found!"
		Local Pixmap:TPixmap = LoadPixmap(Filename)
		If Not Pixmap Then RuntimeError "Could not load "+Filename
		Local NEnt:NEntity = New NEntity,Pixel%,R:Int,G:Int,B:Int
		Local HArray:Float[Pixmap.width,Pixmap.height]
		For Local tX:Int = 0 To Pixmap.Width-1
			For Local tY:Int = 0 To Pixmap.Height-1
				Pixel = ReadPixel(Pixmap,tX,tY)
				R = (Pixel% & $00FF0000) Shr 16
				G = (Pixel% & $0000FF00) Shr 8
				B = (Pixel% & $000000FF)
				HArray[tX,tY] = Float(R+G+B)/3.0
			Next
		Next
		For Local tX:Int = 0 To Pixmap.Width-2
			For Local tY:Int = 0 To Pixmap.Height-2
				NEnt.AddTri(tX,HArray[tX,tY+1]*0.1,tY+1,tX+1,HArray[tX+1,tY+1]*0.1,tY+1,tX,HArray[tX,tY]*0.1,tY,1,1,1,0,0,1)
				NEnt.AddTri(tX+1,HArray[tX+1,tY+1]*0.1,tY+1,tX+1,HArray[tX+1,tY]*0.1,tY,tX,HArray[tX,tY]*0.1,tY,1,0,0,0,0,1)
			Next
		Next
		NEnt.CreateDList = 1
		Return NEnt
	End Function
	Function LoadMesh:NEntity(Filename:String)
		Print "Loading "+Filename
		If FileType(Filename)=0 Then 
			RuntimeError Filename+" not found!"
			Return Null
		EndIf
		Local File:TStream = ReadFile(Filename)
		Local Ident:Int = ReadInt(File)
		If Ident <> 844121161 Then RuntimeError "MD2 identification number not found!"
		Local NEnt:NEntity = New NEntity
		Local Version:Int = ReadInt(File)
		Print "Version: "+Version
		Local SkinWidth:Int = ReadInt(File)
		Local SkinHeight:Int = ReadInt(File)
		NEnt.TextureWidth = SkinWidth
		NEnt.TextureHeight = SkinHeight
		Print NEnt.TextureWidth+"x"+Nent.TextureHeight
		Local FrameSize:Int = ReadInt(File)
		Local NumSkins:Int = ReadInt(File)
		Print NumSkins+" skins"
		Local NumVertices:Int = ReadInt(File)
		Print NumVertices+" vertices"
		Local NumSt:Int = ReadInt(File)
		Local NumTris:Int = ReadInt(File)
		Print NumTris+" triangles"
		Local NumGlcmds:Int = ReadInt(File)
		Local NumFrames:Int = ReadInt(File)
		Print NumFrames+" frames"
		NEnt.Frames = NumFrames
		
		Local OffsetSkins:Int = ReadInt(File)
		Local OffsetSt:Int = ReadInt(File)
		Local OffsetTris:Int = ReadInt(File)
		Local OffsetFrames:Int = ReadInt(File)
		Local OffsetGlcmds:Int = ReadInt(File)
		Local OffsetEnd:Int = ReadInt(File)
		'Read skins
		Local SkinName:String[32]
		SeekStream File,OffsetSkins
		For Local a:Int=1 To NumSkins
			Local StopRead:Int=False
			For Local b:Int=1 To 64
				Local c:Int = ReadByte(File)
				If c=0 Then StopRead = True
				If Not StopRead Then SkinName[a-1]=SkinName[a-1]+Chr$(c)
			Next
			Print "skinname: "+SkinName[a-1]
			Print Len(SkinName[a-1])
		Next
		If NumSkins>0 Then
			Local TXMap:TPixmap = LoadPixmap(GetFolder(Filename)+SkinName[0])
			Print GetFolder(Filename)+SkinName[0]
			If Not TXMap Then RuntimeError "Could not load texture!"
			NEnt.Texture = GLTexFromPixmap(TXMap)
			If Not NEnt.Texture Then RuntimeError "Could not load texture to memory!"
			NEnt.TextureEnabled = 1
		EndIf
		
		'Read texture coordinates
		Local s:Int[2048],t:Int[2048]
		SeekStream File,OffsetSt
		Print "NumSt"+ NumSt
		For Local a:Int=1 To NumSt
			s[a-1] = ReadShort(File)
			t[a-1] = ReadShort(File)
		Next
		'Read vertices
		Local v:Byte[512,2048,3]
		Local normalIndex:Byte[512,2048]
		Local scalex:Float[512],scaley:Float[512],scalez:Float[512]
		Local translatex:Float[512],translatey:Float[512],translatez:Float[512]
		Local frameName:String[512]
		SeekStream File,OffsetFrames
		For Local a:Int=1 To NumFrames
			scalex[a-1] = ReadFloat(File)
			scaley[a-1] = ReadFloat(File)
			scalez[a-1] = ReadFloat(File)
			translatex[a-1] = ReadFloat(File)
			translatey[a-1] = ReadFloat(File)
			translatez[a-1] = ReadFloat(File)
			Local StopRead:Int=False
			For Local b:Int=0 To 15
				Local c:Int = ReadByte(File)
				If c=0 Then StopRead=True
				If Not StopRead Then frameName[a-1] = frameName[a-1]+Chr$(c)
			Next
			'Read vertices
			For Local c:Int = 0 To NumVertices-1
				v[a-1,c,0] = ReadByte(File)
				v[a-1,c,1] = ReadByte(File)
				v[a-1,c,2] = ReadByte(File)
				normalIndex[a-1,c] = ReadByte(File)
			Next
		Next
		'Read triangles
		Local vertex:Short[4096,3]
		Local st:Short[4096,3]
		SeekStream File,OffsetTris
		For Local a:Int=1 To NumTris
			For Local b:Int=0 To 2
				vertex[a-1,b] = ReadShort(File)
			Next
			For Local b:Int=0 To 2
				st[a-1,b] = ReadShort(File)
			Next
			For Local b:Int=0 To NumFrames-1
				NEnt.Frame = b
				Local V3X:Float = (Float(v[b,vertex[a-1,0],0])*scalex[b])+translatex[b]
				Local V3Y:Float = (Float(v[b,vertex[a-1,0],1])*scaley[b])+translatey[b]
				Local V3Z:Float = (Float(v[b,vertex[a-1,0],2])*scalez[b])+translatez[b]
				Local V2X:Float = (Float(v[b,vertex[a-1,1],0])*scalex[b])+translatex[b]
				Local V2Y:Float = (Float(v[b,vertex[a-1,1],1])*scaley[b])+translatey[b]
				Local V2Z:Float = (Float(v[b,vertex[a-1,1],2])*scalez[b])+translatez[b]
				Local V1X:Float = (Float(v[b,vertex[a-1,2],0])*scalex[b])+translatex[b]
				Local V1Y:Float = (Float(v[b,vertex[a-1,2],1])*scaley[b])+translatey[b]
				Local V1Z:Float = (Float(v[b,vertex[a-1,2],2])*scalez[b])+translatez[b]
				Local V1s:Float=Float(s[st[a-1,2]])/Float(SkinWidth)
				Local V1t:Float=Float(t[st[a-1,2]])/Float(SkinHeight)
				Local V2s:Float=Float(s[st[a-1,1]])/Float(SkinWidth)
				Local V2t:Float=Float(t[st[a-1,1]])/Float(SkinHeight)
				Local V3s:Float=Float(s[st[a-1,0]])/Float(SkinWidth)
				Local V3t:Float=Float(t[st[a-1,0]])/Float(SkinHeight)
				Local V1n:Int=normalIndex[b,vertex[a-1,2]]
				Local V2n:Int=normalIndex[b,vertex[a-1,1]]
				Local V3n:Int=normalIndex[b,vertex[a-1,0]]
				NEnt.AddTri(V1X,V1Y,V1Z,V2X,V2Y,V2Z,V3X,V3Y,V3Z,V1s,V1t,V2s,V2t,V3s,V3t,V1n,V2n,V3n)
			Next
		Next
		NEnt.Frame = 0
		
		CloseFile File
	NEnt.CreateDList = 1
	Return NEnt
	End Function
	Function ListUseCount:Int(DList:Int)
		Local ListCounter:Int = 0
		For Local NEnt:NEntity = EachIn NEntityList
			For Local Fr:Int = 0 To NEnt.Frames-1
				If NEnt.DisplayList[Fr] = DList Then ListCounter:+1
			Next
		Next
		Return ListCounter
	End Function
	Function TextureUsecount:Int(Texture:Int)
		Local TextCounter:Int = 0
		For Local NEnt:NEntity = EachIn NEntityList
			If NEnt.Texture = Texture Then TextCounter:+1
		Next
	End Function
	Function CompareDistance:Int(o1:Object,o2:Object)
		If NEntity(o1).CamDistance>NEntity(o2).CamDistance Return 1
		Return 0
	End Function
End Type
Type NCamera Extends NPoint
	Field Enabled:Int
	Field Pitch:Float
	Field Yaw:Float
	Field Roll:Float
	Field Near:Float,Far:Float
	Field ClsColor:Float[4]
	Field Fog:Int,FogStart:Float,FogEnd:Float,FogColor:Float[3],FogDensity:Float
	Global NCameraList:TList
	Method EnableFog(NewFogStart:Float,NewFogEnd:Float,NewFogR:Int=128,NewFogG:Int=128,NewFogB:Int=128,NewFogDensity:Float=0.35)
		Fog = 1
		FogStart = NewFogStart; FogEnd = NewFogEnd
		FogColor[0] = NewFogR/255.0; FogColor[1] = NewFogG/255.0; FogColor[2] = NewFogB/255.0
		FogDensity = NewFogDensity
	End Method
	Method DisableFog()
		Fog = 0
	End Method
	
	Method Turn(NewPitch:Float,NewYaw:Float,NewRoll:Float)
		Pitch = Pitch+NewPitch Mod 360.0
		Yaw = Yaw+NewYaw Mod 360.0
		Roll = Roll+NewRoll Mod 360.0
		If Pitch<0.0 Then Pitch=Pitch+360.0
		If Yaw<0.0 Then Yaw=Yaw+360.0
		If Roll<0.0 Then Roll=Roll+360.0
		If Pitch>360.0 Then Pitch=Pitch-360.0
		If Yaw>360.0 Then Yaw=Yaw-360.0
		If Roll>360.0 Then Roll=Roll-360.0
	End Method
	Method SetClearColor(NewR:Int,NewG:Int,NewB:Int)
		ClsColor[0] = NewR/255.0; ClsColor[1] = NewG/255.0; ClsColor[2] = NewB/255.0; 
	End Method
	Method Rotate(NewPitch:Float,NewYaw:Float,NewRoll:Float)
		Pitch = NewPitch Mod 360.0
		Yaw = NewYaw Mod 360.0
		Roll = NewRoll Mod 360.0
		While Pitch<0.0
			Pitch=Pitch+360.0
		Wend
		While Yaw<0.0
			Yaw=Yaw+360.0
		Wend
		While Roll<0.0
			Roll=Roll+360.0
		Wend
		While Pitch>360.0
			Pitch = Pitch-360.0
		Wend
		While Yaw>360.0
			Yaw = Yaw-360.0
		Wend
		While Roll>360.0
			Roll = roll-360.0
		Wend
	End Method
	
	Method Move(NewX:Float,NewY:Float,NewZ:Float)
		Local MoveMat:TMatrix=New TMatrix
		MoveMat.LoadIdentity()
		MoveMat.RotateYaw(-Yaw)
		MoveMat.RotatePitch(-Pitch)
		MoveMat.RotateRoll(-Roll)
		MoveMat.Translate(-NewX,-NewY,NewZ)
		X = X+MoveMat.grid[3,0]
		Y = Y+MoveMat.grid[3,1]
		Z = Z+MoveMat.grid[3,2]
	End Method
	Method Translate(NewX:Float,NewY:Float,NewZ:Float)
		X = X-NewX
		Y = Y-NewY
		Z = Z+NewZ
	End Method
	Method Position(NewX:Float,NewY:Float,NewZ:Float)
		X = -NewX
		Y = -NewY
		Z = NewZ
	End Method
	Method New()
		If NCamera.NCameraList = Null Then NCameraList = CreateList()
		ListAddLast(NCamera.NCameraList,Self)
		Enabled = True
		Near = 0.1
		Far = 1000.0
		'Yaw = -180
	End Method
	Function CreateCamera:NCamera()
		Local NCam:NCamera = New NCamera
		Return NCam
	End Function
End Type
Type NLight Extends NPoint
	Field Pitch:Float
	Field Yaw:Float
	Field Roll:Float
	Global NLightList:TList
	Field AR:Int,AG:Int,AB:Int
	Field DR:Int,DG:Int,DB:Int
	Field GL_LIGHT:Int
	Global Lights:Int
	Method Ambient(NewAR:Int,NewAG:Int,NewAB:Int)
		AR=NewAR
		AG=NewAG
		AB=NewAB
		glLightfv(GL_LIGHT,GL_AMBIENT,[AR/255.0,AG/255.0,AB/255.0,1.0])
	End Method
	Method Diffuse(NewDR:Int,NewDG:Int,NewDB:Int)
		DR=NewDR
		DG=NewDG
		DB=NewDB
		glLightfv(GL_LIGHT,GL_DIFFUSE,[DR/255.0,DG/255.0,DB/255.0,1.0])
	End Method
	Method Turn(NewPitch:Float,NewYaw:Float,NewRoll:Float)
		Pitch = Pitch+NewPitch Mod 360.0
		Yaw = Yaw+NewYaw Mod 360.0
		Roll = Roll+NewRoll Mod 360.0
		If Pitch<0.0 Then Pitch=Pitch+360.0
		If Yaw<0.0 Then Yaw=Yaw+360.0
		If Roll<0.0 Then Roll=Roll+360.0
		If Pitch>360.0 Then Pitch=Pitch-360.0
		If Yaw>360.0 Then Yaw=Yaw-360.0
		If Roll>360.0 Then Roll=Roll-360.0
	End Method
	Method Rotate(NewPitch:Float,NewYaw:Float,NewRoll:Float)
		Pitch = NewPitch Mod 360.0
		Yaw = NewYaw Mod 360.0
		Roll = NewRoll Mod 360.0
		While Pitch<0.0
			Pitch=Pitch+360.0
		Wend
		While Yaw<0.0
			Yaw=Yaw+360.0
		Wend
		While Roll<0.0
			Roll=Roll+360.0
		Wend
		While Pitch>360.0
			Pitch = Pitch-360.0
		Wend
		While Yaw>360.0
			Yaw = Yaw-360.0
		Wend
		While Roll>360.0
			Roll = roll-360.0
		Wend
	End Method
	Method Move(NewX:Float,NewY:Float,NewZ:Float)
		Local MoveMat:TMatrix=New TMatrix
		MoveMat.LoadIdentity()
		MoveMat.RotateYaw(Yaw)
		MoveMat.RotatePitch(Pitch)
		MoveMat.RotateRoll(Roll)
		MoveMat.Translate(NewX,NewY,NewZ)
		X = X+MoveMat.grid[3,0]
		Y = Y+MoveMat.grid[3,1]
		Z = Z+MoveMat.grid[3,2]
	End Method
	Method New()
		NLight.Lights:+1
		If NLight.Lights>GL_MAX_LIGHTS Then RuntimeError "Too many lights created!"
		If NLight.Lights=1 Then GL_LIGHT = GL_LIGHT0
		If NLight.Lights=2 Then GL_LIGHT = GL_LIGHT1
		If NLight.Lights=3 Then GL_LIGHT = GL_LIGHT2
		If NLight.Lights=4 Then GL_LIGHT = GL_LIGHT3
		If NLight.Lights=5 Then GL_LIGHT = GL_LIGHT4
		If NLight.Lights=6 Then GL_LIGHT = GL_LIGHT5
		If NLight.Lights=7 Then GL_LIGHT = GL_LIGHT6
		If NLight.Lights=8 Then GL_LIGHT = GL_LIGHT7
		If NLight.NLightList = Null Then NLightList = CreateList()
		ListAddLast(NLight.NLightList,Self)
		If Not NGraphics.Gfx.Lighting Then 
			glEnable(GL_LIGHTING)
			NGRaphics.Gfx.Lighting = 1
		EndIf
		glEnable(GL_LIGHT)
	End Method
	Function CreateLight:NLight()
		Local NLi:NLight = New NLight
		Return NLi
	End Function
End Type
Type NVertex Extends NPoint
	Field OrigX:Float,OrigY:Float,OrigZ:Float
	'Field Tri:NTriangle
	Field R:Float,G:Float,B:Float
	Field Alpha:Float
	Method New()
		Alpha = 1.0
		R = 1.0
		G = 1.0
		B = 1.0
	End Method
End Type
Type NTriangle
	Field V1s:Float,V1t:Float 
	Field V2s:Float,V2t:Float
	Field V3s:Float,V3t:Float
	Field V1NX:Float,V1NY:Float,V1NZ:Float
	Field V2NX:Float,V2NY:Float,V2NZ:Float
	Field V3NX:Float,V3NY:Float,V3NZ:Float
	Field V1:NVertex
	Field V2:NVertex
	Field V3:NVertex
End Type
Type NParticle Extends NPoint
	Field XSpeed:Float,YSpeed:Float,ZSpeed:Float
	Field R:Int,G:Int,B:Int
	Field Alpha:Float,AlphaSpeed:Float
	Field Starttime:Int
	Field CamDistance:Float
End Type
Type NParticleEmitter Extends NPoint
	Field Pitch:Float,Yaw:Float,Roll:Float
	Field StartR:Int,StartG:Int,StartB:Int
	Field RSpeed:Int,GSpeed:Int,BSpeed:Int
	Field StartRRnd:Int,StartGRnd:Int,StartBRnd:Int
	Field XAcceleration:Float,YAcceleration:Float,ZAcceleration:Float
	Field StartXRnd:Float,StartYRnd:Float,StartZRnd:Float
	Field StartXSpeedRnd:Float,StartYSpeedRnd:Float,StartZSpeedRnd:Float
	Field StartXSpeed:Float,StartYSpeed:Float,StartZSpeed:Float
	Field AlphaAcceleration:Float,StartAlphaRnd:Float,StartAlpha:Float,StartAlphaSpeed:Float
	Field NParticleList:TList	
	Field Rate:Int
	Field ParticleTimer:Int,Particles:Int
	Field Texture:Int,TextureWidth:Int,TextureHeight:Int
	Field Lifetime:Int
	Field Enabled:Int,RenderEnabled:Int
	Global NParticleEmitterList:TList
	Method Update(NCam:NCamera)
		Local UpdateTime:Int = MilliSecs() - ParticleTimer
		ParticleTimer = MilliSecs()
		'Loop particles
		For Local NPar:NParticle = EachIn NParticleList
			NPar.XSpeed = NPar.XSpeed+XAcceleration
			NPar.YSpeed = NPar.YSpeed+YAcceleration
			NPar.ZSpeed = NPar.ZSpeed+ZAcceleration
			NPar.X=NPar.X+NPar.XSpeed
			NPar.Y=NPar.Y+NPar.YSpeed
			NPar.Z=NPar.Z+NPar.ZSpeed
			NPar.AlphaSpeed = NPar.AlphaSpeed+AlphaAcceleration
			NPar.Alpha = NPar.Alpha+NPar.AlphaSpeed
			If NPar.Alpha>1.0 Then NPar.Alpha=1.0
			NPar.R = NPar.R+RSpeed; NPar.G = NPar.G+GSpeed; NPar.B = NPar.B+BSpeed; 
			If NPar.Alpha<=0.0 Or (MilliSecs()-NPar.Starttime>Lifetime) Then 
				ListRemove(NParticleList,NPar)
				Particles:-1
			EndIf
		Next
		'Create particles
		If Enabled
			For Local a:Int=1 To Rate
				Particles:+1
				Local NPar:NParticle = New NParticle
				ListAddLast(NParticleList,NPar)
				NPar.X = X+Rnd(-StartXRnd,StartXRnd)
				NPar.Y = Y+Rnd(-StartYRnd,StartYRnd)
				NPar.Z = Z+Rnd(-StartZRnd,StartZRnd)
				NPar.XSpeed = StartXSpeed+Rnd(-StartXSpeedRnd,StartXSpeedRnd)
				NPar.YSpeed = StartYSpeed+Rnd(-StartYSpeedRnd,StartYSpeedRnd)
				NPar.ZSpeed = StartZSpeed+Rnd(-StartZSpeedRnd,StartZSpeedRnd)
				NPar.R = StartR + Rand(-StartRRnd,StartRRnd)
				NPar.G = StartG + Rand(-StartGRnd,StartGRnd)
				NPar.B = StartB + Rand(-StartBRnd,StartBRnd)
				NPar.AlphaSpeed = StartAlphaSpeed
				NPar.Alpha = StartAlpha+Rnd(-StartAlphaRnd,StartAlphaRnd)
				NPar.Starttime=MilliSecs()
			Next
		EndIf
		If RenderEnabled Then
			SortParticles(NCam:NCamera)
			'MergeSortList(NParticleList)
			Render(NCam)
		EndIf
	End Method
	Method Render(NCam:NCamera)
		'glEnable(GL_TEXTURE_2D)
		'glBlendFunc(GL_SRC_ALPHA, GL_ONE)
		glDepthMask(GL_FALSE)
	'	glBindTexture (GL_TEXTURE_2D, Texture)
		'glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST
		'glTexParameteri GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST
		NGraphics.Gfx.Triangles:+Particles*2
		Local maxsize:Float,quad:Float[]=[1.0,0.0,0.01]
		glPointParameterfvARB(GL_POINT_DISTANCE_ATTENUATION_ARB,quad)
		glGetFloatv(GL_POINT_SIZE_MAX_ARB,Varptr(maxsize))
		glPointSize(maxsize)
		glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB,80.0)
   	 	glPointParameterfARB(GL_POINT_SIZE_MIN_ARB,1.0)
    		glPointParameterfARB(GL_POINT_SIZE_MAX_ARB,maxsize)
		glTexEnvf(GL_POINT_SPRITE_ARB,GL_COORD_REPLACE_ARB,GL_TRUE)
		glEnable( GL_POINT_SPRITE_ARB)
		glBegin(GL_POINTS)
		For Local NPar:NParticle = EachIn NParticleList
			glColor4f(NPar.R/255.0,NPar.G/255.0,NPar.B/255.0,NPar.Alpha)
			glVertex3f(NPar.X,NPar.Y,NPar.Z)
		Next
		glEnd()
		glDisable(GL_POINT_SPRITE_ARB)
		'glDisable(GL_TEXTURE_2D)
		glDepthMask(GL_TRUE)
		'glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
	End Method
	Method Turn(NewPitch:Float,NewYaw:Float,NewRoll:Float)
		Pitch = Pitch+NewPitch Mod 360.0
		Yaw = Yaw+NewYaw Mod 360.0
		Roll = Roll+NewRoll Mod 360.0
		If Pitch<0.0 Then Pitch=Pitch+360.0
		If Yaw<0.0 Then Yaw=Yaw+360.0
		If Roll<0.0 Then Roll=Roll+360.0
		If Pitch>360.0 Then Pitch=Pitch-360.0
		If Yaw>360.0 Then Yaw=Yaw-360.0
		If Roll>360.0 Then Roll=Roll-360.0
	End Method
	Method Rotate(NewPitch:Float,NewYaw:Float,NewRoll:Float)
		Pitch = NewPitch Mod 360.0
		Yaw = NewYaw Mod 360.0
		Roll = NewRoll Mod 360.0
		While Pitch<0.0
			Pitch=Pitch+360.0
		Wend
		While Yaw<0.0
			Yaw=Yaw+360.0
		Wend
		While Roll<0.0
			Roll=Roll+360.0
		Wend
		While Pitch>360.0
			Pitch = Pitch-360.0
		Wend
		While Yaw>360.0
			Yaw = Yaw-360.0
		Wend
		While Roll>360.0
			Roll = roll-360.0
		Wend
	End Method
	Method Move(NewX:Float,NewY:Float,NewZ:Float)
		Local MoveMat:TMatrix=New TMatrix
		MoveMat.LoadIdentity()
		MoveMat.RotateYaw(Yaw)
		MoveMat.RotatePitch(Pitch)
		MoveMat.RotateRoll(Roll)
		MoveMat.Translate(NewX,NewY,-NewZ)
		X = X+MoveMat.grid[3,0]
		Y = Y+MoveMat.grid[3,1]
		Z = Z+MoveMat.grid[3,2]
	End Method
	Method SortParticles(NCam:NCamera)
		For Local NPar:NParticle = EachIn NParticleList
			NPar.CamDistance = (NCam.X-NPar.X)*(NCam.X-NPar.X)+(NCam.Y-NPar.Y)*(NCam.Y-NPar.Y)+(NCam.Z-NPar.Z)*(NCam.Z-NPar.Z)
		Next
		MergeSortList(NParticleList)
	'	NParticleList.Sort(True,NParticleEmitter.CompareDistance)
	End Method
	Function CompareDistance:Int(o1:Object,o2:Object)
		If NParticle(o1).CamDistance>NParticle(o2).CamDistance Return 1
		Return 0
	End Function
	Function CreateEmitter:NParticleEmitter(Filename:String)
		Local NEmit:NParticleEmitter = New NParticleEmitter
		NEmit.NParticleList = CreateList()
		ListAddLast(NParticleEmitter.NParticleEmitterList,NEmit)
		If FileType(Filename)=0 Then RuntimeError Filename+" not found!"
		Local TXMap:TPixmap = LoadPixmap(Filename)
		If Not TXMap Then RuntimeError "Could not load texture!"
		NEmit.Texture = GLTexFromPixmap(TXMap)
		If Not NEmit.Texture Then RuntimeError "Could not load texture to memory!"
		NEmit.TextureWidth = TXMap.Width
		NEmit.TextureHeight = TXMap.Height
		NEmit.Enabled = 1
		NEmit.RenderEnabled = 1
		NEmit.ParticleTimer = MilliSecs()
		Return NEmit
	End Function
End Type
Type NShader
	Field Shader:String
	Field VertexShader:Byte,FragmentShader:Byte
	Field ProgramObject:Int
	Function LoadShader:NShader(VertexFilename:String,FragmentFilename:String)
		If FileType(VertexFilename)<>1 Then RuntimeError VertexFilename+" not found!"
		If FileType(FragmentFilename)<>1 Then RuntimeError FragmentFilename+" not found!"
		Local VertexFile:TStream = OpenStream(VertexFilename)
		Local FragmentFile:TStream = OpenStream(FragmentFilename)
		Local Sha:NShader = New NShader
		Local VertexArray:Byte[FileSize(VertexFilename)+1]
		Local FragmentArray:Byte[FileSize(FragmentFilename)+1]
		Local i:Int=0,j:Int=0,VertexSize:Int,FragmentSize:Int
		VertexSize = FileSize(VertexFilename)
		FragmentSize = FileSize(FragmentFilename)
		While Not Eof(VertexFile)
			VertexArray[i] = ReadByte(VertexFile)
			i=i+1
			If i>VertexSize Then i=VertexSize
		Wend
		While Not Eof(FragmentFile)
			FragmentArray[j] = ReadByte(FragmentFile)
			j=j+1
			If j>FragmentSize Then j=FragmentSize
		Wend
		VertexArray[i] = 0
		FragmentArray[j] = 0
		Local VertexPointer:Byte Ptr[1]
		Local FragmentPointer:Byte Ptr[1]
		VertexPointer[0] = Varptr(VertexArray[0])
		FragmentPointer[0] = Varptr(FragmentArray[0])
		Sha.VertexShader = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB)
		Sha.FragmentShader = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB)
		glShaderSourceARB(Sha.VertexShader, 1, VertexPointer, Null)
		glShaderSourceARB(Sha.FragmentShader, 1, FragmentPointer, Null)
		glCompileShaderARB(Sha.VertexShader)
		glCompileShaderARB(Sha.FragmentShader)
		Sha.ProgramObject = glCreateProgramObjectARB()
		If Not Sha.ProgramObject Then RuntimeError "Could not create shader program object."
		glAttachObjectARB(Sha.ProgramObject,Sha.VertexShader)
		glAttachObjectARB(Sha.ProgramObject,Sha.FragmentShader)
		glLinkProgramARB(Sha.ProgramObject)
		Return Sha:NShader
	End Function
End Type
Function SameDirection:Int(Pt1:NPoint,Pt2:NPoint,Pt3:NPoint,Norm:NPoint)
	Local Testi:Float,Testj:Float,Testk:Float
	Local DotProduct:Float
	Testi = (((Pt2.y - Pt1.y)*(Pt3.z - Pt1.z)) - ((Pt3.y - Pt1.y)*(Pt2.z - Pt1.z)))
   	Testj = (((Pt2.z - Pt1.z)*(Pt3.x - Pt1.x)) - ((Pt3.z - Pt1.z)*(Pt2.x - Pt1.x)))
   	Testk = (((Pt2.x - Pt1.x)*(Pt3.y - Pt1.y)) - ((Pt3.x - Pt1.x)*(Pt2.y - Pt1.y)))
	DotProduct = Testi*Norm.x + Testj*Norm.y + Testk*Norm.z
	If DotProduct<0.0 Then
		Return False
	Else
		Return True
	EndIf
End Function
Function LineIntersectsTriangle:Int(pt1:NPoint,pt2:NPoint,pt3:NPoint,linept:NPoint,vect:NPoint)
	Local V1x:Float, V1y:Float, V1z:Float
   	Local V2x:Float, V2y:Float, V2z:Float
 	Local norm:NPoint = New NPoint,pt_int:NPoint = New NPoint
  	Local dotprod:Float,t:Float
   	V1x = pt2.x - pt1.x
   	V1y = pt2.y - pt1.y
   	V1z = pt2.z - pt1.z
   	V2x = pt3.x - pt2.x
   	V2y = pt3.y - pt2.y
   	V2z = pt3.z - pt2.z
   	norm.x = V1y*V2z-V1z*V2y
   	norm.y = V1z*V2x-V1x*V2z
   	norm.z = V1x*V2y-V1y*V2x
	dotprod = norm.x*vect.x + norm.y*vect.y + norm.z*vect.z
	If dotprod<0 Then
		t = -(norm.x*(linept.x-pt1.x)+norm.y*(linept.y-pt1.y)+norm.z*(linept.z-pt1.z))/(norm.x*vect.x+norm.y*vect.y+norm.z*vect.z)
		If t < 0 Then Return False
		pt_int.x = linept.x + vect.x*t
      	pt_int.y = linept.y + vect.y*t
      	pt_int.z = linept.z + vect.z*t
		
		If SameDirection(pt1, pt2, pt_int, norm)
        		If SameDirection(pt2, pt3, pt_int, norm)
            		If SameDirection(pt3, pt1, pt_int, norm)
            			Return True
				EndIf
			EndIf
		EndIf
	EndIf
	Return False
End Function
Function GetFolder:String(Filename:String)
		If Filename.Find("/")=-1 And Filename.Find("\")=-1 Then Return ""
		If Filename.Find("/") > 0 Then
			Return Left(Filename,Filename.FindLast("/")+1)
		Else
			Return Left(Filename,Filename.FindLast("\")+1)
		EndIf
End Function
Function MergeSortList( slist:TList)
	Local p:TLink, q:TLink, e:TLink, tail:TLink, oldhead:TLink
	Local insize:Int, nmerges:Int, psize:Int, qsize:Int, i:Int
	Local list:TLink
	
	If slist = Null Then Return
	If slist._head._succ <> slist._head Then list = slist._head._succ
	If list = Null Then Return
	
	insize = 1
	Repeat
		p = list
		oldhead = list
		list = Null
		tail = Null
		
		nmerges = 0
		
		While p <> Null
			nmerges :+ 1
			q = p
			psize = 0
			For i = 0 Until insize
				psize :+ 1
				If q._succ = oldhead Then
					q = Null
				Else
					q = q._succ
				EndIf
				If q = Null Then Exit
			Next
			
			qsize = insize
			While (psize > 0) Or ((qsize > 0) And (q <> Null))
				If psize = 0 Then
					e = q
					q = q._succ
					qsize :- 1
					If q = oldhead Then q = Null
				ElseIf (qsize = 0) Or (q = Null) Then
					e = p
					p = p._succ
					psize :- 1
					If p = oldhead Then p = Null
				ElseIf NParticle(p._value).CamDistance<NParticle(q._value).CamDistance Then
					e = p
					p = p._succ
					psize :- 1
					If p = oldhead Then p = Null
				Else
					e = q
					q = q._succ
					qsize :- 1
					If q = oldhead Then q = Null
				EndIf
								
				If tail <> Null Then
					tail._succ = e
				Else
					list = e
				EndIf
				e._pred = tail				
				tail = e					
			Wend
			p = q
		Wend
		tail._succ = list
		list._pred = tail
		If nmerges <= 1 Then Return
		insize :* 2
	Forever	
EndFunction
 A simple test file that shows some of the commands:
 ngltest.bmx
 
 
Strict
Import "ngl.bmx"
SeedRnd MilliSecs()
Local Gfx:NGraphics = NGraphics.Start(800,600)
Local Cam:NCamera = NCamera.CreateCamera()
Cam.Move(30,10,-60)
Cam.SetClearColor(163,192,255)
'Cam.EnableFog(150,520,163,192,255)
HideMouse
Local Cube:NEntity = NEntity.CreateCube()
Cube.ScaleMesh(0.1,0.1,0.1)
Cube.Position(15,6,4)
'Cube.LoadTexture("crate.jpg")
'Cube.EntityAlpha 0.3
'Local Cube2:NEntity = NEntity.CreateCube()
'Cube2.Translate(0,0,-5)
'Cube2.Shadows = 1
'Cube2.EntityColor(255,0,0)
Local Room:NEntity = NEntity.CreateCube()
Room.ScaleMesh(40,25,40)
Room.Move(0,20,0)
Room.LoadTexture("wall.jpg")
Room.FlipMesh()
'Local Mesh:NEntity = NEntity.LoadMesh("md2/fish.md2")
'Mesh.ScaleMesh(0.3,0.3,0.3)
'Mesh.Shadows = 0
'Local S:NEntity = NEntity.CreateSprite()
'S.LoadTexture("crate.jpg")
'S.Translate(0,0,15)
'S.Shadows = 1
'Local Mesh2:NEntity = NEntity.LoadMesh("md2/turret.md2")
'Mesh2.ScaleMesh(0.1,0.1,0.1)
'Mesh2.Move(6,3,0.4)
'Mesh2.Shadows = 0
'Mesh.FrameTime = 40
''Mesh.EntityAlpha 0.3
'Mesh.Move(15,-2,0)
'Mesh.MoveMesh(3,0,0)
'Mesh.Turn(-90,0,135)
'Mesh.FrameTime = 40
'Mesh.Interpolate()
'Mesh.Move -10000,0,0
'Local testshader:NShader = NShader.LoadShader("shader/water.vertex","shader/water.frag")
'Cube2.EntityParent Mesh
'Local tmesh:NEntity[100],counter
'For Local a=0 To 9
'	For Local b=0 To 9
'		tmesh:NEntity[counter]= Mesh.CopyEntity()
'		tmesh[counter] .Move(b*10,0,a)
'		counter:+1
'	Next
'Next
'tmesh.FreeEntity
'Mesh.FreeEntity
'Mesh.Move(20,0,-50)
'Local Light:NLight = NLight.CreateLight()
'Light.Translate(20,40,0)
'Light.Ambient(255,255,255)
'Light.Diffuse(255,255,255)
'sprite.AttachShader(testshader)
'Local Smoke:NParticleEmitter = NParticleEmitter.CreateEmitter("smoke4.png")
'Smoke.StartR=205; Smoke.StartG=205; Smoke.StartB=105; Smoke.Translate(0,0,6)
'Smoke.RSpeed = -1; Smoke.GSpeed = -1; Smoke.BSPeed = -1
'Smoke.StartRRnd = 50; Smoke.StartGRnd = 50; Smoke.StartBRnd = 50
'Smoke.StartXSpeedRnd = 0.06; Smoke.StartYSpeedRnd = 0.06; Smoke.StartZSpeedRnd = 0
'Smoke.StartYSpeed = 0.1; Smoke.YAcceleration = -0.005; Smoke.StartZSpeed = 0.05
'Smoke.StartXRnd = 0.2; Smoke.StartYRnd = 0.2; Smoke.StartZRnd = 0.2; 
'Smoke.Rate =30; Smoke.Lifetime=1000; Smoke.StartAlpha = 0.9; Smoke.StartAlphaRnd = 0.2
'Smoke.StartAlphaSpeed = -0.005
'Local Map:NEntity = NEntity.Heightmap("hmap.bmp")
'Map.LoadTexture("grass.png")
'Local Tree:NEntity = NEntity.CreateSprite()
'Tree.LoadTexture("tree.png")
'Tree.Blend = 1
'Tree.X = Map.X; Tree.Y = Map.Y; Tree.Z = Map.Z
'Local NewTree:NEntity
'Local Counter:Int = 0
'For Local NTri:NTriangle = EachIn Map.NTriangleList[Map.Frame]
'	If Counter=Rand(1,30) Or Counter>30 And NTri.V1.Y>12.1 Then
	'	Tree.MoveMesh(NTri.V1.X,NTri.V1.Y+0.5,NTri.V1.Z)
		'NewTree.AddMesh(Tree)
''		Tree.MoveMesh(-NTri.V1.X,-NTri.V1.Y-0.5,-NTri.V1.Z)
'		NewTree:NEntity = Tree.CopyEntity()
'		NewTree.Position(NTri.V1.X+Rnd(0.5),NTri.V1.Y+0.5,NTri.V1.Z+Rnd(0.5))
'		Counter=0
'	EndIf
'	Counter:+1
'Next
'Tree.FreeEntity()
'Local Water:NEntity = NEntity.CreateSprite()
'Water.Turn(-90,0,0)
''Water.EntityColor(50,50,255)
'Water.LoadTexture("water.jpg")
'Water.ScaleMesh(1000,1000,1)
'Water.Translate(0,10,0)
'Water.RenderFirst = 1
Repeat
	'Camera control
	If KeyDown(KEY_LEFT) Then Cam.Move(-0.3,0,0)
	If KeyDown(KEY_RIGHT) Then Cam.Move(0.3,0,0)
	If KeyDown(KEY_UP) Then Cam.Move(0,0,0.3)
	If KeyDown(KEY_DOWN) Then Cam.Move(0,0,-0.3)
	If KeyDown(KEY_A) Then Cam.Move(0,0.1,0)
	If KeyDown(KEY_Z) Then Cam.Move(0,-0.1,0)
	If KeyDown(KEY_W) Then Cam.Turn(0,2,0)
	If KeyDown(KEY_Q) Then Cam.Turn(0,-2,0)
	If KeyDown(KEY_Y) Then Cube.Move(-0.3,0,0)
	If KeyDown(KEY_U) Then Cube.Move(0.3,0,0)
	'If Cube.Intersect(Mesh) Then Print "collision"
	'Mesh.Animate()
'	Mesh.Turn(0.3,1,2)
'	Mesh.Move(0,0.1,0)
'	Cube2.Turn(2,2,2)
	'Mesh.Turn(0,0,2)
	'Mesh.Move(0,0.05,0)
	'Cube.Turn(2,2,2)
	'Cube2.Turn(0,0.5,0)
	Cube.RenderShadows()
	Gfx.Render
	Gfx.Swap(1)
	Print Gfx.FPS+" fps "+Gfx.Triangles+" tris"
	'Print GCMemAlloced()
	
Until KeyHit(KEY_ESCAPE)
 You need extra file called nmatrix.bmx for the lib to work, copy it from the post below.
 
 
 |