woollybah 9 rokov pred
rodič
commit
f95baddb1b
1 zmenil súbory, kde vykonal 3152 pridanie a 3141 odobranie
  1. 3152 3141
      mojo2.mod/graphics.bmx

+ 3152 - 3141
mojo2.mod/graphics.bmx

@@ -1,3141 +1,3152 @@
-'
-' BlitzMax port, 2015 Bruce A Henderson
-' 
-' Copyright (c) 2015 Mark Sibly
-' 
-' This software is provided 'as-is', without any express or implied
-' warranty. In no event will the authors be held liable for any damages
-' arising from the use of this software.
-' 
-' Permission is granted to anyone to use this software for any purpose,
-' including commercial applications, and to alter it and redistribute it
-' freely, subject to the following restrictions:
-' 
-' 1. The origin of this software must not be misrepresented; you must not
-'    claim that you wrote the original software. If you use this software
-'    in a product, an acknowledgement in the product documentation would be
-'    appreciated but is not required.
-' 2. Altered source versions must be plainly marked as such, and must not be
-'    misrepresented as being the original software.
-' 3. This notice may not be removed or altered from any source distribution.
-' 
-SuperStrict
-
-
-'Framework brl.standardio
-
-Import brl.bank
-Import brl.map
-Import brl.ramstream
-Import brl.pixmap
-Import brl.filesystem
-Import brl.system
-Import brl.Graphics
-Import "math3d.bmx"
-Import "glutil.bmx"
-Import "glslparser.bmx"
-Import "maps.bmx"
-
-
-Incbin "data/mojo2_font.png"
-Incbin "data/mojo2_program.glsl"
-Incbin "data/mojo2_fastshader.glsl"
-Incbin "data/mojo2_bumpshader.glsl"
-Incbin "data/mojo2_matteshader.glsl"
-Incbin "data/mojo2_shadowshader.glsl"
-Incbin "data/mojo2_lightmapshader.glsl"
-
-Private
-
-Global _inited:Int
-
-Global mainShader:String
-
-Global _fastShader:TShader
-Global _bumpShader:TShader
-Global _matteShader:TShader
-Global _shadowShader:TShader
-Global _lightMapShader:TShader
-
-Global defaultFont:TFont
-Global _defaultShader:TShader
-
-Global freeOps:TDrawOpStack=New TDrawOpStack
-Global nullOp:TDrawOp=New TDrawOp
-
-Global tmpi:Int[16]
-Global tmpf:Float[16]
-Global defaultFbo:Int
-
-Global tmpMat2d:Float[6]
-Global tmpMat3d:Float[16]
-Global tmpMat3d2:Float[16]
-
-Global flipYMatrix:Float[]=Mat4New()
-Global graphicsSeq:Int=1
-Global vbosSeq:Int
-
-'shader params
-Global rs_projMatrix:Float[]=Mat4New()
-Global rs_modelViewMatrix:Float[]=Mat4New()
-Global rs_modelViewProjMatrix:Float[]=Mat4New()
-Global rs_clipPosScale:Float[]=[1.0,1.0,1.0,1.0]
-Global rs_globalColor:Float[]=[1.0,1.0,1.0,1.0]
-Global rs_numLights:Int
-Global rs_fogColor:Float[]=[0.0,0.0,0.0,0.0]
-Global rs_ambientLight:Float[]=[0.0,0.0,0.0,1.0]
-Global rs_lightColors:Float[MAX_LIGHTS*4]
-Global rs_lightVectors:Float[MAX_LIGHTS*4]
-Global rs_shadowTexture:TTexture
-Global rs_program:TGLProgram
-Global rs_material:TMaterial
-Global rs_blend:Int=-1
-Global rs_vbo:Int
-Global rs_ibo:Int
-
-Const VBO_USAGE:Int=GL_STREAM_DRAW
-Const VBO_ORPHANING_ENABLED:Int=False
-
-Const MAX_LIGHTS:Int=4
-Const BYTES_PER_VERTEX:Int=28
-
-'can really be anything <64K (due to 16bit indices) but this keeps total VBO size<64K, and making it bigger doesn't seem to improve performance much.
-Const MAX_VERTICES:Int=65536/BYTES_PER_VERTEX	
-
-Const MAX_QUADS:Int=MAX_VERTICES/4
-Const MAX_QUAD_INDICES:Int=MAX_QUADS*6
-Const PRIM_VBO_SIZE:Int=MAX_VERTICES*BYTES_PER_VERTEX
-
-Function IsPow2:Int( sz:Int )
-	Return (sz & (sz-1))=0
-End Function
-
-Public
-
-Type TLightData
-	Field kind:Int=0
-	Field color:Float[]=[1.0,1.0,1.0,1.0]
-	Field position:Float[]=[0.0,0.0,-10.0]
-	Field Range:Float=10
-	'
-	Field vector:Float[]=[0.0,0.0,-10.0,1.0]
-	Field tvector:Float[4]
-End Type
-
-Private
-
-Function InitVbos()
-	If vbosSeq=graphicsSeq Return
-	vbosSeq=graphicsSeq
-
-	glGenBuffers 1, Varptr rs_vbo
-	glBindBuffer GL_ARRAY_BUFFER,rs_vbo
-	glBufferData GL_ARRAY_BUFFER,PRIM_VBO_SIZE,Null,VBO_USAGE
-	
-	glEnableVertexAttribArray 0
-	glVertexAttribPointer 0,2,GL_FLOAT,False,BYTES_PER_VERTEX,0
-	
-	glEnableVertexAttribArray 1
-	glVertexAttribPointer 1,2,GL_FLOAT,False,BYTES_PER_VERTEX,8
-	
-	glEnableVertexAttribArray 2
-	glVertexAttribPointer 2,2,GL_FLOAT,False,BYTES_PER_VERTEX,16
-	
-	glEnableVertexAttribArray 3
-	glVertexAttribPointer 3,4,GL_UNSIGNED_BYTE,True,BYTES_PER_VERTEX,24
-	
-	glGenBuffers(1, Varptr rs_ibo)
-	glBindBuffer GL_ELEMENT_ARRAY_BUFFER,rs_ibo
-	Local idxs:TBank=New TBank.Create( MAX_QUAD_INDICES*4*2 )
-	For Local j:Int = 0 Until 4
-		Local k:Int = j*MAX_QUAD_INDICES*2
-		For Local i:Int = 0 Until MAX_QUADS
-			idxs.PokeShort i*12+k+0,i*4+j+0
-			idxs.PokeShort i*12+k+2,i*4+j+1
-			idxs.PokeShort i*12+k+4,i*4+j+2
-			idxs.PokeShort i*12+k+6,i*4+j+0
-			idxs.PokeShort i*12+k+8,i*4+j+2
-			idxs.PokeShort i*12+k+10,i*4+j+3
-		Next
-	Next
-	glBufferData GL_ELEMENT_ARRAY_BUFFER,idxs.Size(),idxs._buf,GL_STATIC_DRAW
-	'idxs.Discard
-End Function
-
-Global inited:Int
-
-Function InitMojo2()
-	If inited Return
-	inited=True
-
-?Not opengles
-	glewInit()
-?
-	
-	InitVbos
-	
-	glGetIntegerv GL_FRAMEBUFFER_BINDING,tmpi
-	defaultFbo=tmpi[0]
-	
-	mainShader=LoadString( "incbin::data/mojo2_program.glsl" )
-	
-	_fastShader=New TShader.Create( LoadString( "incbin::data/mojo2_fastshader.glsl" ) )
-	_bumpShader=New TBumpShader.Create( LoadString( "incbin::data/mojo2_bumpshader.glsl" ) )
-	_matteShader=New TMatteShader.Create( LoadString( "incbin::data/mojo2_matteshader.glsl" ) )
-	_shadowShader=New TShader.Create( LoadString( "incbin::data/mojo2_shadowshader.glsl" ) )
-	_lightMapShader=New TShader.Create( LoadString( "incbin::data/mojo2_lightmapshader.glsl" ) )
-	_defaultShader=_bumpShader
-
-	defaultFont=TFont.Load( "incbin::data/mojo2_font.png",32,96,True )'9,13,1,0,7,13,32,96 )
-	If Not defaultFont Throw "Can't load default font"
-	
-	flipYMatrix[5]=-1
-End Function
-
-Public
-
-Type TRefCounted
-
-	Method Retain()
-		If _refs<=0 Throw "Internal error"
-		_refs:+1
-	End Method
-	
-	Method Free()
-		If _refs<=0 Throw "Internal error"
-		_refs:-1
-		If _refs Return
-		_refs=-1
-		Destroy
-	End Method
-	
-	Method Destroy() Abstract
-
-	Field _refs:Int=1
-End Type
-
-'***** Texture *****
-
-rem
-bbdoc: Textures contains image data for use by shaders when rendering.
-about: For more information, please see the #TShader type.
-end rem
-Type TTexture Extends TRefCounted
-
-	'flags
-	Const Filter:Int=1
-	Const Mipmap:Int=2
-	Const ClampS:Int=4
-	Const ClampT:Int=8
-	Const ClampST:Int=12
-	Const RenderTarget:Int=16
-	Const Managed:Int=256
-
-	Rem
-	bbdoc: Creates a new texture.
-	about: The @width and @height are parameters are the size of the new texture.
-	The @format parameter must be 4.
-	The @flags parameter can be a bitwise combination of:
-	| @ Flags				| @Description
-	| Texture.Filter			| The texture is filtered when magnified
-	| Texture.Mipmap			| The texture is mipmapped when minified
-	| Texture.ClampS			| Texture S coordinate is clamped
-	| Texture.ClampT			| Texture T coordinate is clamped
-	| Texture.ClampST		| Texture S and T coordinates are clamped.
-	| Texture.RenderTarget	| The texture can rendered to using a #Canvas.
-	| Texture.Managed		| Texture contents are preserved when graphics are lost
-	End Rem
-	Method Create:TTexture( width:Int,height:Int,format:Int,flags:Int, data:TPixmap = Null )
-
-		If format<>PF_RGBA8888 Then
-			Throw "Invalid texture format: "+format
-		End If
-
-		'can't mipmap NPOT textures on gles20
-		If Not IsPow2( width ) Or Not IsPow2( height ) flags:&~Mipmap
-		
-		_width=width
-		_height=height
-		_format=format
-		_flags=flags
-		_data=data
-
-		If _flags & Managed
-			_managed=New TPixmap.Create( width,height,PF_RGBA8888 )
-			If _data
-				_managed.Paste( _data,0,0 )
-				_data=Null
-			Else
-				_managed.ClearPixels( $ffff00ff )
-			EndIf
-		EndIf
-		
-		Validate()
-
-		Return Self
-	End Method
-	
-	Method Destroy()
-		If _seq=graphicsSeq glDeleteTextures 1, Varptr _glTexture
-		_glTexture=0
-		_glFramebuffer=0
-	End Method
-	
-	rem
-	bbdoc: Gets texture width.
-	end rem
-	Method Width:Int()
-		Return _width
-	End Method
-	
-	rem
-	bbdoc: Gets texture height.
-	end rem
-	Method Height:Int()
-		Return _height
-	End Method
-	
-	rem
-	bbdoc: Gets texture format.
-	end rem
-	Method Format:Int()
-		Return _format
-	End Method
-	
-	rem
-	bbdoc: Gets texture flags.
-	end rem
-	Method Flags:Int()
-		Return _flags
-	End Method
-
-	rem
-	bbdoc: Writes pixel data to texture.
-	about: Pixels should be in premultiplied alpha format.
-	end rem
-	Method WritePixels( x:Int,y:Int,width:Int,height:Int,data:TPixmap,dataOffset:Int=0,dataPitch:Int=0 )
-
-		glPushTexture2d GLTexture()
-	
-		If Not dataPitch Or dataPitch=width*4
-		
-			glTexSubImage2D GL_TEXTURE_2D,0,x,y,width,height,GL_RGBA,GL_UNSIGNED_BYTE,data.pixels + dataOffset
-			
-		Else
-			For Local iy:Int=0 Until height
-				glTexSubImage2D GL_TEXTURE_2D,0,x,y+iy,width,1,GL_RGBA,GL_UNSIGNED_BYTE,data.pixels + dataOffset+iy*dataPitch
-			Next
-		EndIf
-		
-		glPopTexture2d
-		
-		If _flags & Managed
-		
-			Local texPitch:Int=_width*4
-			If Not dataPitch dataPitch=width*4
-			
-			For Local iy:Int=0 Until height
-				MemCopy _data.pixels + (y+iy)*texPitch+x*4, data.pixels + dataOffset+iy*dataPitch, width*4
-			Next
-			
-		EndIf
-
-	End Method
-
-	Method SetData( x:Int,y:Int,pixmap:TPixmap )
-		
-		If _managed
-			If pixmap<>_managed _managed.Paste( pixmap,x,y )
-		Else If _data
-			If pixmap<>_data Throw "Texture is read only" 
-		EndIf
-		
-		glPushTexture2d( GLTexture() )
-		
-		Local width:Int=pixmap.Width
-		Local height:Int=pixmap.Height
-		
-		If pixmap.Pitch=_width*4
-		
-			glTexSubImage2D( GL_TEXTURE_2D,0,x,y,width,height,GL_RGBA,GL_UNSIGNED_BYTE,pixmap.pixels )
-			
-		Else
-		
-			For Local iy:Int=0 Until height
-				glTexSubImage2D( GL_TEXTURE_2D,0,x,y+iy,width,1,GL_RGBA,GL_UNSIGNED_BYTE,pixmap.PixelPtr( 0,iy ) )
-			Next
-			
-		EndIf
-		
-		glPopTexture2d
-		
-	End Method
-		
-	Method UpdateMipmaps()
-		If Not (_flags & Mipmap) Return
-			
-		If _seq<>graphicsSeq
-			Validate()
-			Return
-		EndIf
-
-		glPushTexture2d GLTexture()
-
-		glGenerateMipmap GL_TEXTURE_2D
-		
-		glPopTexture2d
-	End Method
-	
-	Method Loading:Int()
-			Return False
-	End Method
-	
-	Method GLTexture:Int()
-		Validate
-		Return _glTexture
-	End Method
-	
-	Method GLFramebuffer:Int()
-		Validate
-		Return _glFramebuffer
-	End Method
-	
-	Function TexturesLoading:Int()
-			Return 0
-	End Function
-	
-	rem
-	bbdoc: Loads a texture from a url.
-	end rem
-	Function Load:TTexture( url:Object,format:Int=PF_RGBA8888,flags:Int=Filter|Mipmap|ClampST )
-	
-		Local info:Int[2]
-		
-		Local data:TPixmap=LoadPixmap(url)
-		If Not data Return Null
-		
-		' convert to RGBA
-		If data.format <> format Then
-			data = data.Convert(format)
-		End If
-		
-		PremultiplyAlpha(data)
-			
-		Local tex:TTexture=New TTexture.Create( data.width,data.height,format,flags,data )
-		
-		Return tex
-	End Function
-
-	Function PremultiplyAlpha(pix:TPixmap)
-		For Local y:Int=0 Until pix.height
-			For Local x:Int=0 Until pix.width
-				Local pixel:Int=pix.ReadPixel( x,y )
-				Local a:Int=pixel Shr 24 & 255
-				Local b:Int=(pixel Shr 16 & 255)*a/255
-				Local g:Int=(pixel Shr 8 & 255)*a/255
-				Local r:Int=(pixel & 255)*a/255
-				pixel=a Shl 24 | b Shl 16 | g Shl 8 | r
-				pix.WritePixel( x,y,pixel )
-			Next
-		Next
-	End Function
-	
-	Function Color:TTexture( color:Int )
-		Local tex:TTexture=TTexture(_colors.ValueForKey( color ))
-		If tex Return tex
-
-		Local pixmap:TPixmap=New TPixmap.Create( 1,1,PF_RGBA8888 )
-		pixmap.ClearPixels( color )
-
-		tex=New TTexture.Create( 1,1,PF_RGBA8888,ClampST,pixmap )
-		_colors.Insert color,tex
-		Return tex
-	End Function
-	
-	rem
-	bbdoc: Returns a stock single texel black texture.
-	end rem
-	Function Black:TTexture()
-		If Not _black _black=Color( $ff000000 )
-		Return _black
-	End Function
-	
-	rem
-	bbdoc: Returns a stock single texel white texture.
-	end rem
-	Function White:TTexture()
-		If Not _white _white=Color( $ffffffff )
-		Return _white
-	End Function
-	
-	rem
-	bbdoc: Returnss a stock single texel magenta texture.
-	end rem
-	Function Magenta:TTexture()
-		If Not _magenta _magenta=Color( $ffff00ff )
-		Return _magenta
-	End Function
-	
-	rem
-	bbdoc: Returns a stock single texel 'flat' texture for normal mapping.
-	end rem
-	Function Flat:TTexture()
-		If Not _flat _flat=Color( $ff888888 )
-		Return _flat
-	End Function
-	
-	Private
-	
-	Field _seq:Int
-	Field _width:Int
-	Field _height:Int
-	Field _format:Int
-	Field _flags:Int
-	Field _data:TPixmap
-	Field _managed:TPixmap
-	
-	Field _glTexture:Int
-	Field _glFramebuffer:Int
-
-	Global _colors:TIntMap=New TIntMap'<TTexture>
-	Global _black:TTexture
-	Global _white:TTexture
-	Global _magenta:TTexture
-	Global _flat:TTexture
-
-	Method Validate()
-		
-		If _seq=graphicsSeq Return
-		
-		InitMojo2()
-	
-		_seq=graphicsSeq
-	
-		glGenTextures(1, Varptr _glTexture)
-		
-		glPushTexture2d _glTexture
-		
-		If _flags & Filter
-			glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR
-		Else
-			glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST
-		EndIf
-		If (_flags & Mipmap) And (_flags & Filter)
-			glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR
-		Else If _flags & Mipmap
-			glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_NEAREST
-		Else If _flags & Filter
-			glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR
-		Else
-			glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST
-		EndIf
-
-		If _flags & ClampS glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE
-		If _flags & ClampT glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE
-
-		glTexImage2D GL_TEXTURE_2D,0,GL_RGBA,_width,_height,0,GL_RGBA,GL_UNSIGNED_BYTE,Null
-
-		glPopTexture2d
-		
-		If _flags & RenderTarget
-		
-			glGenFramebuffers(1, Varptr _glFramebuffer)
-			
-			glPushFramebuffer _glFramebuffer
-			
-			glBindFramebuffer GL_FRAMEBUFFER,_glFramebuffer
-			glFramebufferTexture2D GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,_glTexture,0
-			
-			If glCheckFramebufferStatus( GL_FRAMEBUFFER )<>GL_FRAMEBUFFER_COMPLETE Throw "Incomplete framebuffer"
-			
-			glPopFramebuffer
-			
-		EndIf
-		
-		If _managed Then
-
-			SetData( 0,0,_managed )
-			UpdateMipmaps()
-			
-		Else If _data
-		
-			SetData( 0,0,_data )
-			UpdateMipmaps()
-		
-		EndIf
-		
-	End Method
-	
-	Method LoadData( data:TPixmap )
-		glPushTexture2d GLTexture()
-		
-		glTexImage2D GL_TEXTURE_2D,0,GL_RGBA,_width,_height,0,GL_RGBA,GL_UNSIGNED_BYTE,data.pixels
-
-		glPopTexture2d
-		
-		UpdateMipmaps
-	End Method
-	
-End Type
-
-'***** Shader ****
-
-Public
-
-Type TGLUniform
-	Field name:String
-	Field location:Int
-	Field size:Int
-	Field kind:Int
-	
-	Method Create:TGLUniform( name:String,location:Int,size:Int,kind:Int )
-		Self.name=name
-		Self.location=location
-		Self.size=size
-		Self.kind=kind
-		Return Self
-	End Method
-	
-End Type
-
-Type TGLProgram
-	Field program:Int
-	'material uniforms
-	Field matuniforms:TGLUniform[]
-	'hard coded uniform locations
-	Field mvpMatrix:Int
-	Field mvMatrix:Int
-	Field clipPosScale:Int
-	Field globalColor:Int
-	Field AmbientLight:Int
-	Field fogColor:Int
-	Field lightColors:Int
-	Field lightVectors:Int
-	Field shadowTexture:Int
-	
-	Method Create:TGLProgram( program:Int,matuniforms:TGLUniform[] )
-		Self.program=program
-		Self.matuniforms=matuniforms
-		mvpMatrix=glGetUniformLocation( program,"ModelViewProjectionMatrix" )
-		mvMatrix=glGetUniformLocation( program,"ModelViewMatrix" )
-		clipPosScale=glGetUniformLocation( program,"ClipPosScale" )
-		globalColor=glGetUniformLocation( program,"GlobalColor" )
-		fogColor=glGetUniformLocation( program,"FogColor" )
-		AmbientLight=glGetUniformLocation( program,"AmbientLight" )
-		lightColors=glGetUniformLocation( program,"LightColors" )
-		lightVectors=glGetUniformLocation( program,"LightVectors" )
-		shadowTexture=glGetUniformLocation( program,"ShadowTexture" )
-		Return Self
-	End Method
-	
-	Method Bind()
-	
-		glUseProgram program
-		
-		If mvpMatrix<>-1 glUniformMatrix4fv mvpMatrix,1,False,rs_modelViewProjMatrix
-		If mvMatrix<>-1 glUniformMatrix4fv mvMatrix,1,False,rs_modelViewMatrix
-		If clipPosScale<>-1 glUniform4fv clipPosScale,1,rs_clipPosScale
-		If globalColor<>-1 glUniform4fv globalColor,1,rs_globalColor
-		If fogColor<>-1 glUniform4fv fogColor,1,rs_fogColor
-		If AmbientLight<>-1 glUniform4fv AmbientLight,1,rs_ambientLight
-		If lightColors<>-1 glUniform4fv lightColors,rs_numLights,rs_lightColors
-		If lightVectors<>-1 glUniform4fv lightVectors,rs_numLights,rs_lightVectors
-		glActiveTexture GL_TEXTURE0+7
-		If shadowTexture<>-1 And rs_shadowTexture
-			glBindTexture GL_TEXTURE_2D,rs_shadowTexture.GLTexture()
-			glUniform1i shadowTexture,7
-		Else
-			glBindTexture GL_TEXTURE_2D,TTexture.White().GLTexture()
-		End If
-		glActiveTexture GL_TEXTURE0
-	End Method
-	
-End Type
-
-Public
-
-Type TShader
-
-	Method Create:TShader( source:String )
-		Build source
-		Return Self
-	End Method
-	
-	Method DefaultMaterial:TMaterial()
-		If Not _defaultMaterial _defaultMaterial=New TMaterial.Create( Self )
-		Return _defaultMaterial
-	End Method
-	
-	Function FastShader:TShader()
-		Return _fastShader
-	End Function
-	
-	Rem
-	bbdoc: Returns a stock bump shader for drawing lit sprites with specular and normal maps.
-	about: 
-The following material properties are supported:
-
-| @Property			| @Type		| @Default
-| ColorTexture		| Texture	| White
-| SpecularTexture	| Texture	| Black
-| NormalTexture		| Texture	| Flat
-| AmbientColor		| Float[4]	| [0.0,0.0,0.0,1.0]
-| Roughness			| Float		| 0.5
-
-The shader b3d_Ambient value is computed by multiplying ColorTexture by AmbientColor.
-
-The shader b3d_Diffuse value is computed by multiplying ColorTexture by 1-AmbientColor.
-
-When loading materials that use the bump shader, diffuse, specular and normal maps can be given the following files names:
-
-| @Texture map		| @Valid paths
-| Diffuse			| (FILE).(EXT) ; (FILE)_d.(EXT) ; (FILE)_diff.(EXT) ; (FILE)_diffuse.(EXT)
-| Specular			| (FILE)_s.(EXT) ; (FILE)_spec.(EXT) ; (FILE)_specular.(EXT) ;(FILE)_SPECUALR.(EXT)
-| Normal			| (FILE)_n.(EXT) ; (FILE)_norm.(EXT) ; (FILE)_normal.(EXT) ; (FILE)_NORMALS.(EXT)
-
-Where (FILE) is the filename component of the path provided to Material.Load or Image.Load, and (EXT) is the file extension, eg: png, jpg.
-	end rem
-	Function BumpShader:TShader()
-		Return _bumpShader
-	End Function
-	
-	Rem
-	bbdoc: Returns a stock matte shader for drawing lit sprites with no specular or normal maps.
-	about: 
-The following material properties are supported:
-
-| @Property			| @Type		| @Default
-| ColorTexture		| Texture	| White
-| AmbientColor		| Float[4]	| [0.0,0.0,0.0,1.0]
-| Roughness			| Float		| 0.5
-	end rem
-	Function MatteShader:TShader()
-		Return _matteShader
-	End Function
-	
-	Rem
-	bbdoc: Returns a stock shadow shader for drawing shadows.
-	about: This shader simply writes 'black' to b3d_FragColor.
-	end rem
-	Function ShadowShader:TShader()
-		Return _shadowShader
-	End Function
-	
-	Rem
-	bbdoc: Returns a stock shader for drawing light textures and light mask effects.
-	about: 
-This shader performs a texture lookup, and writes the red component to b3d_FragColor.
-	
-The following material properties are supported:
-
-| @Property			| @Type		| @Default
-| ColorTexture		| Texture	| White
-	end rem	
-	Function LightMapShader:TShader()
-		Return _lightMapShader
-	End Function
-	
-	Rem
-	bbdoc: Returns the default shader used when a material is created with a 'Null' shader.
-	about: This is initially the #BumpShader, but can be modified using #SetDefaultShader.
-	end rem
-	Function DefaultShader:TShader()
-		Return _defaultShader
-	End Function
-	
-	Rem
-	bbdoc: Sets the default shader used when a material is created with a 'Null' shader.
-	end rem
-	Function SetDefaultShader( shader:TShader )
-		If Not shader shader=_bumpShader
-		_defaultShader=shader
-	End Function
-	
-	'Protected
-	
-	Rem
-	bbdoc: Compiles and links the shader.
-	about: Types that extend Shader must call this method at some point. This is usually done in the subclasses constructor.
-	end rem
-	Method Build( source:String )
-		_source=source
-		BuildInit
-	End Method
-	
-	Rem
-	bbdoc: Types that extend Shader must set defalut values for all valid shader parameters in this method.
-	end rem
-	Method OnInitMaterial( material:TMaterial )
-		material.SetTexture "ColorTexture",TTexture.White()
-	End Method
-	
-	Rem
-	bbdoc: Classes that extend Shader should load textures and other valid shader parameters from @path into @material in this method.
-	about: The interpretation of @path is completely up to the shader. The @texFlags parameter contains texture flag values that should be used for any textures loaded.
-	The @material parameter is an already initialized material.
-	This method should return @material if successful, or null on failure.
-	end rem
-	Method OnLoadMaterial:TMaterial( material:TMaterial,path:String,texFlags:Int )
-		Local texture:TTexture=TTexture.Load( path,4,texFlags )
-		If Not texture Return Null
-		material.SetTexture "ColorTexture",texture
-		If texture texture.Free
-		Return material
-	End Method
-	
-	'Private
-	
-	Const MAX_FLAGS:Int=8
-	
-	Field _seq:Int
-	Field _source:String
-	
-	Field _vsource:String
-	Field _fsource:String
-	Field _uniforms:TStringMap=New TStringMap
-	
-	Field _glPrograms:TGLProgram[MAX_LIGHTS+1]
-	
-	Field _defaultMaterial:TMaterial
-	
-	Method Bind()	
-		Local program:TGLProgram=GLProgram()
-		
-		If program=rs_program Return
-
-		rs_program=program
-		rs_material=Null
-		
-		program.Bind
-	End Method
-	
-	Method GLProgram:TGLProgram()
-	
-		If _seq<>graphicsSeq 
-			_seq=graphicsSeq
-			rs_program=Null
-			BuildInit
-		EndIf
-		
-		Return _glPrograms[rs_numLights]
-	End Method
-	
-	Method BuildProgram:TGLProgram( numLights:Int )
-
-		Local defs:String=""
-		defs:+"#define NUM_LIGHTS "+numLights+"~n"
-
-		Local vshader:Int=glCompile( GL_VERTEX_SHADER,defs+_vsource )
-		Local fshader:Int=glCompile( GL_FRAGMENT_SHADER,defs+_fsource )
-		
-		Local program:Int=glCreateProgram()
-		glAttachShader program,vshader
-		glAttachShader program,fshader
-		glDeleteShader vshader
-		glDeleteShader fshader
-		
-		glBindAttribLocation program,0,"Position"
-		glBindAttribLocation program,1,"Texcoord0"
-		glBindAttribLocation program,2,"Tangent"
-		glBindAttribLocation program,3,"Color"
-		
-		glLink program
-		
-		'enumerate program uniforms	
-		Local matuniforms:TGLUniform[] = New TGLUniform[0]
-		Local size:Int
-		Local kind:Int
-		Local buf:Byte[1024]
-		Local l:Int
-		glGetProgramiv program,GL_ACTIVE_UNIFORMS,tmpi
-		For Local i:Int=0 Until tmpi[0]
-			glGetActiveUniform program,i,1024,Varptr l,Varptr size, Varptr kind, buf
-			Local name:String = String.FromBytes(buf, l)
-			If _uniforms.Contains( name )
-				Local location:Int=glGetUniformLocation( program,name )
-				If location=-1 Continue  'IE fix...
-				matuniforms :+ [New TGLUniform.Create( name,location,size,kind )]
-'				Print name[0]+"->"+location
-			EndIf
-		Next
-		
-		Return New TGLProgram.Create( program,matuniforms )
-	
-	End Method
-	
-	Method BuildInit()
-		InitMojo2
-
-		Local p:TGlslParser=TGlslParser(New TGlslParser.Create( _source ))
-
-		Local vars:TMap=New TMap
-		
-		While p.Toke
-		
-			If p.CParse( "uniform" )
-				'uniform decl
-				Local ty:String=p.ParseType()
-				Local id:String=p.ParseIdent()
-				p.ParseToke ";"
-				_uniforms.Insert id, id
-'				Print "uniform "+ty+" "+id+";"
-				Continue
-			EndIf
-			
-			Local id:String=p.CParseIdent()
-			If id
-				If id.StartsWith( "gl_" )
-					vars.Insert "B3D_"+id.ToUpper(), ""
-				Else If id.StartsWith( "b3d_" ) 
-					vars.Insert id.ToUpper(), ""
-				EndIf
-				Continue
-			EndIf
-			
-			p.Bump
-		Wend
-		
-		Local vardefs:String=""
-		For Local v:String=EachIn vars.Keys()
-			vardefs:+"#define "+v+" 1~n"
-		Next
-		
-'		Print "Vardefs:";Print vardefs
-		
-		Local source:String=mainShader
-		Local i0:Int=source.Find( "//@vertex" )
-		If i0=-1 Throw "Can't find //@vertex chunk"
-		Local i1:Int=source.Find( "//@fragment" )
-		If i1=-1 Throw "Can't find //@fragment chunk"
-		
-		Local header:String=vardefs+source[..i0]
-		_vsource=header+source[i0..i1]
-		_fsource=header+source[i1..].Replace( "${SHADER}",_source )
-		
-		For Local numLights:Int=0 To MAX_LIGHTS
-		
-			_glPrograms[numLights]=BuildProgram( numLights )
-
-			If numLights Or vars.Contains( "B3D_DIFFUSE" ) Or vars.Contains( "B3D_SPECULAR" ) Continue
-			
-			For Local i:Int=1 To MAX_LIGHTS
-				_glPrograms[i]=_glPrograms[0]
-			Next
-			
-			Exit
-			
-		Next
-		
-		
-	End Method
-	
-End Type
-
-Type TBumpShader Extends TShader
-
-'	Method New( source:String )
-'		Super.New( source )
-'	End
-
-	'Protected
-	
-	Method OnInitMaterial( material:TMaterial )
-		material.SetTexture "ColorTexture",TTexture.White()
-		material.SetTexture "SpecularTexture",TTexture.Black()
-		material.SetTexture "NormalTexture",TTexture.Flat()
-		material.SetVector "AmbientColor",[1.0,1.0,1.0,1.0]
-		material.SetScalar "Roughness",1.0
-	End Method
-	
-	Method OnLoadMaterial:TMaterial( material:TMaterial,path:String,texFlags:Int )
-
-		Local format:Int = PF_RGBA8888
-	
-		Local ext:String = ExtractExt( path )
-		If ext path=StripExt( path ) Else ext="png"
-		
-		Local colorTex:TTexture=TTexture.Load( path+"."+ext,format,texFlags )
-		If Not colorTex colorTex=TTexture.Load( path+"_d."+ext,format,texFlags )
-		If Not colorTex colorTex=TTexture.Load( path+"_diff."+ext,format,texFlags )
-		If Not colorTex colorTex=TTexture.Load( path+"_diffuse."+ext,format,texFlags )
-		
-		Local specularTex:TTexture=TTexture.Load( path+"_s."+ext,format,texFlags )
-		If Not specularTex specularTex=TTexture.Load( path+"_spec."+ext,format,texFlags )
-		If Not specularTex specularTex=TTexture.Load( path+"_specular."+ext,format,texFlags )
-		If Not specularTex specularTex=TTexture.Load( path+"_SPECULAR."+ext,format,texFlags )
-		
-		Local normalTex:TTexture=TTexture.Load( path+"_n."+ext,format,texFlags )
-		If Not normalTex normalTex=TTexture.Load( path+"_norm."+ext,format,texFlags )
-		If Not normalTex normalTex=TTexture.Load( path+"_normal."+ext,format,texFlags )
-		If Not normalTex normalTex=TTexture.Load( path+"_NORMALS."+ext,format,texFlags )
-		
-		If Not colorTex And Not specularTex And Not normalTex Return Null
-
-		material.SetTexture "ColorTexture",colorTex
-		material.SetTexture "SpecularTexture",specularTex
-		material.SetTexture "NormalTexture",normalTex
-		
-		If specularTex Or normalTex
-			material.SetVector "AmbientColor",[0.0,0.0,0.0,1.0]
-			material.SetScalar "Roughness",.5
-		EndIf
-		
-		If colorTex colorTex.Free
-		If specularTex specularTex.Free
-		If normalTex normalTex.Free
-		
-		Return material
-	End Method
-	
-End	Type
-
-Type TMatteShader Extends TShader
-
-'	Method Create( source:String )
-'		Super.New( source )
-'	End
-	
-'	Protected
-	
-	Method OnInitMaterial( material:TMaterial )
-		material.SetTexture "ColorTexture",TTexture.White()
-		material.SetVector "AmbientColor",[0.0,0.0,0.0,1.0]
-		material.SetScalar "Roughness",1.0
-	End Method
-	
-End Type
-
-'***** Material *****
-
-Rem
-bbdoc: Materials contain shader parameters that map to shader uniforms variables when rendering. 
-End Rem
-Type TMaterial Extends TRefCounted
-
-	Rem
-	bbdoc: Creates a new material.
-	End Rem
-	Method Create:TMaterial( shader:TShader=Null )
-		InitMojo2
-		
-		If Not shader shader=_defaultShader
-		_shader=shader
-		_shader.OnInitMaterial( Self )
-		_inited=True
-		
-		Return Self
-	End Method
-	
-	Method Destroy()
-		For Local tex:TTexture=EachIn _textures
-			tex.Free()
-		Next
-	End Method
-	
-	Rem
-	bbdoc: Gets material shader.
-	End Rem
-	Method Shader:TShader() 
-		Return _shader
-	End Method
-	
-	Method ColorTexture:TTexture()
-		Return _colorTexture
-	End Method
-	
-	Method Width:Int()
-		If _colorTexture Return _colorTexture._width
-		Return 0
-	End Method
-	
-	Method Height:Int()
-		If _colorTexture Return _colorTexture._height
-		Return 0
-	End Method
-	
-	Rem
-	bbdoc: Sets float shader parameter.
-	End Rem
-	Method SetScalar( param:String,scalar:Float )
-		If _inited And Not _scalars.Contains( param ) Return
-		_scalars.Insert param,scalar
-	End Method
-	
-	Rem
-	bbdoc: Gets float shader parameter.
-	End Rem
-	Method GetScalar:Float( param:String,defValue:Float=1.0 )
-		If Not _scalars.Contains( param ) Return defValue
-		Return _scalars.ValueForKey( param )
-	End Method
-	
-	Rem
-	bbdoc: Sets vector shader parameter.
-	End Rem
-	Method SetVector( param:String,vector:Float[] )
-		If _inited And Not _vectors.Contains( param ) Return
-		_vectors.Insert param,vector
-	End Method
-	
-	Rem
-	bbdoc: Gets vector shader parameter.
-	End Rem
-	Method GetVector:Float[]( param:String,defValue:Float[]=[1.0,1.0,1.0,1.0] )
-		If Not _vectors.Contains( param ) Return defValue
-		Return _vectors.ValueForKey( param )
-	End Method
-	
-	Rem
-	bbdoc: Sets texture shader parameter.
-	End Rem
-	Method SetTexture( param:String,texture:TTexture )
-		If Not texture Return
-		If _inited And Not _textures.Contains( param ) Return
-		
-		Local old:TTexture=TTexture(_textures.ValueForKey( param ))
-		texture.Retain
-		_textures.Insert param,texture
-		If old old.Free
-		
-		If param="ColorTexture" _colorTexture=texture
-		
-	End Method
-	
-	Rem
-	bbdoc: Gets texture shader parameter.
-	End Rem
-	Method GetTexture:TTexture( param:String,defValue:TTexture=Null )
-		If Not _textures.Contains( param ) Return defValue
-		Return TTexture(_textures.ValueForKey( param ))
-	End Method
-	
-	Method Loading:Int()
-		Return False
-	End Method
-	
-	Rem
-	bbdoc: Loads a material.
-	about: If @shader is null, the TShader.DefaultShader is used.
-	End Rem
-	Function Load:TMaterial( path:String,texFlags:Int,shader:TShader )
-	
-		Local material:TMaterial=New TMaterial.Create( shader )
-
-		material=material.Shader.OnLoadMaterial( material,path,texFlags )
-		
-		Return material
-	End Function
-	
-	'Private
-	
-	Field _shader:TShader
-	Field _colorTexture:TTexture
-	Field _scalars:TStringFloatMap=New TStringFloatMap
-	Field _vectors:TStringMap=New TStringMap
-	Field _textures:TStringMap=New TStringMap
-	Field _inited:Int
-	
-	Method Bind:Int()
-	
-		_shader.Bind
-		
-		If rs_material=Self Return True
-		
-		rs_material=Self
-	
-		Local texid:Int=0
-		
-		For Local u:TGLUniform=EachIn rs_program.matuniforms
-			Select u.kind
-			Case GL_FLOAT
-				glUniform1f u.location,GetScalar( u.name )
-			Case GL_FLOAT_VEC4
-				glUniform4fv u.location,1,GetVector( u.name )
-			Case GL_SAMPLER_2D
-				Local tex:TTexture=GetTexture( u.name )
-'				If tex.Loading
-'					rs_material=Null 
-'					Exit
-'				Endif
-				glActiveTexture GL_TEXTURE0+texid
-				glBindTexture GL_TEXTURE_2D,tex.GLTexture()
-				glUniform1i u.location,texid
-				texid:+1
-			Default
-				Throw "Unsupported uniform type:"+u.kind 
-			End Select
-		Next
-
-		If texid glActiveTexture GL_TEXTURE0
-		
-		Return rs_material=Self
-	End Method
-	
-End Type
-
-'***** ShaderCaster *****
-
-Rem
-bbdoc: The ShadowCaster class provides support for simple 2d shadow rendering.
-about: Shadow casters are used by #Renderer objects when rendering layers. To render shadows, you will need to add
-shadow casters to the drawlists returned by ILayer.OnRenderLayer.
-A shadow caster can either be added to a drawlist using [[DrawList.AddShadowCaster]], or attached to images using [[Image.SetShadowCaster]]. Shadow casters attached to images are automatically added to drawlists when an image is drawn.
-A shadow caster contains a set of 2d vertices which describe the geometric shape of the object that casts a shadow. The vertices should describe a convex polygon.
-End Rem
-Type TShadowCaster
-
-	Method Create:TShadowCaster( verts:Float[] = Null,kind:Int = -1 )
-		If verts Then
-			_verts=verts
-		End If
-		If kind >= 0 Then
-			_kind=kind
-		End If
-		Return Self
-	End Method
-	
-	Rem
-	bbdoc: Set shadow caster vertices.
-	end rem
-	Method SetVertices( vertices:Float[] )
-		_verts=vertices
-	End Method
-	
-	Rem
-	bbdoc: Get shadow caster vertices.
-	end rem
-	Method Vertices:Float[]()
-		Return _verts
-	End Method
-
-	Method SetKind( kind:Int )
-		_kind=kind
-	End Method
-	
-	Method Kind:Int()
-		Return _kind
-	End Method
-	
-	'Private
-	
-	Field _verts:Float[]
-	Field _kind:Int
-	
-End Type
-
-'***** Image *****
-
-Rem
-bbdoc: An image is a rectangular area of pixels within a material, that can be drawn using one of the [[DrawList.DrawImage]] methods.
-about: You can create a new image using one of the [[Image.Create]] methods, or load an image from file using [[Image.Load|Image.Load]].
-An image also has a handle, an offset within the image that represents it's origin whan it is drawn. Image handles are specified in fractional values, where 0,0 is the top-left of an image, 1,1 is the bottom-right and .5,.5 is the center.
-end rem
-Type TImage
-
-	Const Filter:Int=TTexture.Filter
-	Const Mipmap:Int=TTexture.Mipmap
-	Const Managed:Int=TTexture.Managed
-	
-	Method Create:TImage( width:Int,height:Int,xhandle:Float=.5,yhandle:Float=.5,flags:Int=TImage.Filter )
-		flags:&_flagsMask
-		Local texture:TTexture=New TTexture.Create( width,height,PF_RGBA8888,flags|TTexture.ClampST|TTexture.RenderTarget )
-		_material=New TMaterial.Create( _fastShader )
-		_material.SetTexture "ColorTexture",texture
-		_width=width
-		_height=height
-		SetHandle xhandle,yhandle
-		Return Self
-	End Method
-	
-	Method CreateImage:TImage( image:TImage,x:Int,y:Int,width:Int,height:Int,xhandle:Float=.5,yhandle:Float=.5 )
-		_material=image._material
-		_material.Retain
-		_x=image._x+x
-		_y=image._y+y
-		_width=width
-		_height=height
-		SetHandle xhandle,yhandle
-		Return Self
-	End Method
-	
-	Method CreateMaterial:TImage( material:TMaterial,xhandle:Float=.5,yhandle:Float=.5 )
-		Local texture:TTexture=material.ColorTexture()
-		If Not texture Throw "Material has no ColorTexture"
-		_material=material
-		_material.Retain
-		_width=_material.Width
-		_height=_material.Height
-		SetHandle xhandle,yhandle
-		Return Self
-	End Method
-
-	Method CreateMaterialSize:TImage( material:TMaterial,x:Int,y:Int,width:Int,height:Int,xhandle:Float=.5,yhandle:Float=.5 )
-		Local texture:TTexture=material.ColorTexture()
-		If Not texture Throw "Material has no ColorTexture"
-		_material=material
-		_material.Retain
-		_x=x
-		_y=y
-		_width=width
-		_height=height
-		SetHandle xhandle,yhandle
-		Return Self
-	End Method
-
-	Method Discard()
-		If _material _material.Free
-		_material=Null
-	End Method
-	
-	Method Material:TMaterial()
-		Return _material
-	End Method
-	
-	Method X0:Float()
-		Return _x0
-	End Method
-	
-	Method Y0:Float()
-		Return _y0
-	End Method
-	
-	Method X1:Float()
-		Return _x1
-	End Method
-	
-	Method Y1:Float()
-		Return _y1
-	End Method
-	
-	Method Width:Int()
-		Return _width
-	End Method
-	
-	Method Height:Int()
-		Return _height
-	End Method
-	
-	Method HandleX:Float()
-		Return -_x0/(_x1-_x0)
-	End Method
-	
-	Method HandleY:Float()
-		Return -_y0/(_y1-_y0)
-	End Method
-	
-	Method WritePixels( x:Int,y:Int,width:Int,height:Int,data:TPixmap,dataOffset:Int=0,dataPitch:Int=0 )
-		_material.ColorTexture().WritePixels( x+_x,y+_y,width,height,data,dataOffset,dataPitch )
-	End Method
-	
-	Method SetHandle( xhandle:Float,yhandle:Float )
-		_x0=Float(_width)*-xhandle
-		_x1=Float(_width)*(1-xhandle)
-		_y0=Float(_height)*-yhandle
-		_y1=Float(_height)*(1-yhandle)
-		_s0=Float(_x)/Float(_material.Width)
-		_t0=Float(_y)/Float(_material.Height)
-		_s1=Float(_x+_width)/Float(_material.Width)
-		_t1=Float(_y+_height)/Float(_material.Height)
-	End Method
-	
-	Method SetShadowCaster( shadowCaster:TShadowCaster )
-		_caster=shadowCaster
-	End Method
-	
-	Method ShadowCaster:TShadowCaster()
-		Return _caster
-	End Method
-	
-	Method Loading:Int()
-		Return _material.Loading()
-	End Method
-	
-	Function ImagesLoading:Int()
-		Return TTexture.TexturesLoading>0
-	End Function
-	
-	Function SetFlagsMask( mask:Int )
-		_flagsMask=mask
-	End Function
-	
-	Function FlagsMask:Int()
-		Return _flagsMask
-	End Function
-	
-	Function Load:TImage( path:String,xhandle:Float=.5,yhandle:Float=.5,flags:Int=TImage.Filter|TImage.Mipmap,shader:TShader=Null )
-		flags:&_flagsMask
-	
-		Local material:TMaterial=TMaterial.Load( path,flags|TTexture.ClampST,shader )
-		If Not material Return Null
-
-		Return New TImage.CreateMaterial( material,xhandle,yhandle )
-	End Function
-	
-	Function LoadFrames:TImage[]( path:String,numFrames:Int,padded:Int=False,xhandle:Float=.5,yhandle:Float=.5,flags:Int=TImage.Filter|TImage.Mipmap,shader:TShader=Null )
-		flags:&_flagsMask
-	
-		Local material:TMaterial=TMaterial.Load( path,flags|TTexture.ClampST,shader )
-		If Not material Return Null
-		
-		Local cellWidth:Int=material.Width/numFrames
-		Local cellHeight:Int=material.Height
-		
-		Local x:Int=0
-		Local width:Int=cellWidth
-		If padded Then
-			x:+1
-			width:-2
-		End If
-		
-		Local frames:TImage[]=New TImage[numFrames]
-		
-		For Local i:Int=0 Until numFrames
-			frames[i]=New TImage.CreateMaterialSize( material,i*cellWidth+x,0,width,cellHeight,xhandle,yhandle )
-		Next
-		
-		Return frames
-	End Function
-	
-	'Private
-	
-	Global _flagsMask:Int=Filter|Mipmap|Managed
-	
-	Field _material:TMaterial
-	Field _x:Int,_y:Int,_width:Int,_height:Int
-	Field _x0:Float=-1,_y0:Float=-1,_x1:Float=1,_y1:Float=1
-	Field _s0:Float=0 ,_t0:Float=0 ,_s1:Float=1,_t1:Float=1
-
-	Field _caster:TShadowCaster
-	
-'	Method SetFrame( x0:Float,y0:Float,x1:Float,y1:Float,s0:Float,t0:Float,s1:Float,t1:Float )
-'		_x0=x0;_y0=y0;_x1=x1;_y1=y1
-'		_s0=s0;_t0=t0;_s1=s1;_t1=t1
-'	End
-	
-End Type
-
-'***** Font *****
-
-Type TGlyph
-	Field image:TImage
-	Field char:Int
-	Field x:Int
-	Field y:Int
-	Field width:Int
-	Field height:Int
-	Field advance:Float
-	
-	Method Create:TGlyph( image:TImage,char:Int,x:Int,y:Int,width:Int,height:Int,advance:Float )
-		Self.image=image
-		Self.char=char
-		Self.x=x
-		Self.y=y
-		Self.width=width
-		Self.height=height
-		Self.advance=advance
-		Return Self
-	End Method
-End Type
-
-Rem
-bbdoc: Provides support for simple fixed width bitmap fonts.
-End Rem
-Type TFont
-
-	Method Create:TFont( glyphs:TGlyph[],firstChar:Int,height:Float )
-		_glyphs=glyphs
-		_firstChar=firstChar
-		_height=height
-		Return Self
-	End Method
-
-	Method GetGlyph:TGlyph( char:Int )
-		Local i:Int=char-_firstChar
-		If i>=0 And i<_glyphs.Length Return _glyphs[i]
-		Return Null
-	End Method
-	
-	Rem
-	bbdoc: Gets width of @text drawn in this font.
-	End Rem
-	Method TextWidth:Float( Text:String )
-		Local w:Float=0.0
-		For Local char:Int=EachIn Text
-			Local glyph:TGlyph=GetGlyph( char )
-			If Not glyph Continue
-			w:+glyph.advance
-		Next
-		Return w
-	End Method
-
-	Rem
-	bbdoc: Gets height of @text drawn in this font.
-	End Rem
-	Method TextHeight:Float( Text:String )
-		Return _height
-	End Method
-	
-	Rem
-	bbdoc: Loads a fixed width font from @path.
-	about: Glyphs should be laid out horizontally within the source image.
-	If @padded is true, then each glyph is assumed to have a transparent one pixel padding border around it.
-	End Rem
-	Function Load:TFont( path:String,firstChar:Int,numChars:Int,padded:Int )
-
-		Local image:TImage=TImage.Load( path )
-		If Not image Return Null
-		
-		Local cellWidth:Int=image.Width/numChars
-		Local cellHeight:Int=image.Height
-		Local glyphX:Int=0,glyphY:Int=0,glyphWidth:Int=cellWidth,glyphHeight:Int=cellHeight
-		If padded glyphX:+1;glyphY:+1;glyphWidth:-2;glyphHeight:-2
-
-		Local w:Int=image.Width/cellWidth
-		Local h:Int=image.Height/cellHeight
-
-		Local glyphs:TGlyph[]=New TGlyph[numChars]
-		
-		For Local i:Int=0 Until numChars
-			Local y:Int=i / w
-			Local x:Int=i Mod w
-			Local glyph:TGlyph=New TGlyph.Create( image,firstChar+i,x*cellWidth+glyphX,y*cellHeight+glyphY,glyphWidth,glyphHeight,glyphWidth )
-			glyphs[i]=glyph
-		Next
-		
-		Return New TFont.Create( glyphs,firstChar,glyphHeight )
-	
-	End Function
-	
-	Function LoadSize:TFont( path:String,cellWidth:Int,cellHeight:Int,glyphX:Int,glyphY:Int,glyphWidth:Int,glyphHeight:Int,firstChar:Int,numChars:Int )
-
-		Local image:TImage=TImage.Load( path )
-		If Not image Return Null
-
-		Local w:Int=image.Width/cellWidth
-		Local h:Int=image.Height/cellHeight
-
-		Local glyphs:TGlyph[]=New TGlyph[numChars]
-		
-		For Local i:Int=0 Until numChars
-			Local y:Int=i / w
-			Local x:Int=i Mod w
-			Local glyph:TGlyph=New TGlyph.Create( image,firstChar+i,x*cellWidth+glyphX,y*cellHeight+glyphY,glyphWidth,glyphHeight,glyphWidth )
-			glyphs[i]=glyph
-		Next
-		
-		Return New TFont.Create( glyphs,firstChar,glyphHeight )
-	End Function
-	
-	'Private
-	
-	Field _glyphs:TGlyph[]
-	Field _firstChar:Int
-	Field _height:Float
-	
-End Type
-
-'***** DrawList *****
-
-Type TDrawOp
-'	Field shader:Shader
-	Field material:TMaterial
-	Field blend:Int
-	Field order:Int
-	Field count:Int
-End Type
-
-Type TBlendMode
-	Const Opaque:Int=0
-	Const Alpha:Int=1
-	Const Additive:Int=2
-	Const Multiply:Int=3
-	Const Multiply2:Int=4
-End Type
-
-Rem
-bbdoc: A drawlist contains drawing state and a sequence of 2d drawing operations.
-about:
-You add drawing operations to a drawlist using any of the Draw methods. When a drawing operation is added, the current drawing state is captured by the drawing operation. Further changes to the drawing state will not affect drawing operations already in the drawlist.
-A [[Canvas]] extends [[DrawList]], and can be used to draw directly to the app window or an image. A drawlist can also be rendered to a canvas using [[Canvas.RenderDrawList]].
-A drawlist's drawing state consists of:
-| @Drawing state			| @Description
-| Current color			| [[SetColor]]
-| Current 2d matrix		| [[Translate]], [[Rotate]], [[Scale]], [[PushMatrix]], [[PopMatrix]]
-| Current blend mode		| [[SetBlendMode]]
-| Current font			| [[DrawText]]
-End Rem
-Type TDrawList
-
-	Method New()
-		InitMojo2
-		
-		_color = __colorArray
-		
-		SetFont Null
-		SetDefaultMaterial _fastShader.DefaultMaterial
-	End Method
-	
-	Method SetBlendMode( blend:Int )
-		_blend=blend
-	End Method
-	
-	Method BlendMode:Int()
-		Return _blend
-	End Method
-	
-	Method SetColor( r:Float,g:Float,b:Float,a:Float = -1 )
-		Local c:Float Ptr = _color
-		c[0]=r
-		c[1]=g
-		c[2]=b
-		If a >= 0
-			c[3]=a
-			_alpha=a*255
-		End If
-		_pmcolor=Int(_alpha) Shl 24 | Int(c[2]*_alpha) Shl 16 | Int(c[1]*_alpha) Shl 8 | Int(c[0]*_alpha)
-	End Method
-	
-	Method SetAlpha( a:Float )
-		_color[3]=a
-		_alpha=a*255
-		_pmcolor=Int(_alpha) Shl 24 | Int(_color[2]*_alpha) Shl 16 | Int(_color[1]*_alpha) Shl 8 | Int(_color[0]*_alpha)
-	End Method
-	
-	Method Color:Float[]()
-		Return [_color[0],_color[1],_color[2],_color[3]]
-	End Method
-	
-	Method GetColor( color:Float[] )
-		color[0]=_color[0]
-		color[1]=_color[1]
-		color[2]=_color[2]
-		If color.Length>3 color[3]=_color[3]
-	End Method
-	
-	Method Alpha:Float()
-		Return _color[3]
-	End Method
-	
-	rem
-	bbdoc: Sets the current 2d matrix to the identity matrix.
-	about: Same as SetMatrix( 1,0,0,1,0,0 ).
-	end rem
-	Method ResetMatrix()
-		_ix=1;_iy=0
-		_jx=0;_jy=1
-		_tx=0;_ty=0
-	End Method
-	
-	rem
-	bbdoc: Sets the current 2d matrix to the given matrix.
-	end rem
-	Method SetMatrix( ix:Float,iy:Float,jx:Float,jy:Float,tx:Float,ty:Float )
-		_ix=ix;_iy=iy
-		_jx=jx;_jy=jy
-		_tx=tx;_ty=ty
-	End Method
-	
-	rem
-	bbdoc: Gets the current 2d matrix.
-	end rem
-	Method GetMatrix( matrix:Float[] )
-		matrix[0]=_ix
-		matrix[1]=_iy
-		matrix[2]=_jx
-		matrix[3]=_jy
-		matrix[4]=_tx
-		matrix[5]=_ty
-	End Method
-	
-	rem
-	bbdoc: Multiplies the current 2d matrix by the given matrix.
-	end rem
-	Method Transform( ix:Float,iy:Float,jx:Float,jy:Float,tx:Float,ty:Float )
-		Local ix2:Float=ix*_ix+iy*_jx
-		Local iy2:Float=ix*_iy+iy*_jy
-		Local jx2:Float=jx*_ix+jy*_jx
-		Local jy2:Float=jx*_iy+jy*_jy
-		Local tx2:Float=tx*_ix+ty*_jx+_tx
-		Local ty2:Float=tx*_iy+ty*_jy+_ty
-		SetMatrix ix2,iy2,jx2,jy2,tx2,ty2
-	End Method
-
-	rem
-	bbdoc: Translates the current 2d matrix.
-	end rem
-	Method Translate( tx:Float,ty:Float )
-		Transform 1,0,0,1,tx,ty
-	End Method
-	
-	rem
-	bbdoc: Rotates the current 2d matrix.
-	end rem
-	Method Rotate( rz:Float )
-		Transform Cos( rz ),-Sin( rz ),Sin( rz ),Cos( rz ),0,0
-	End Method
-	
-	rem
-	bbdoc: Scales the current 2d matrix.
-	end rem
-	Method Scale( sx:Float,sy:Float )
-		Transform sx,0,0,sy,0,0
-	End Method
-	
-	rem
-	bbdoc: Translates and rotates (in that order) the current 2d matrix.
-	end rem
-	Method TranslateRotate( tx:Float,ty:Float,rz:Float )
-		Translate tx,ty
-		Rotate rz
-	End Method
-	
-	rem
-	bbdoc: Rotates and scales (in that order) the current 2d matrix.
-	end rem
-	Method RotateScale( rz:Float,sx:Float,sy:Float )
-		Rotate rz
-		Scale sx,sy
-	End Method
-	
-	Method TranslateScale( tx:Float,ty:Float,sx:Float,sy:Float )
-		Translate tx,ty
-		Scale sx,sy
-	End Method
-	
-	rem
-	bbdoc: Translates, rotates and scales (in that order) the current 2d matrix.
-	end rem
-	Method TranslateRotateScale( tx:Float,ty:Float,rz:Float,sx:Float,sy:Float )
-		Translate tx,ty
-		Rotate rz
-		Scale sx,sy
-	End Method
-	
-	rem
-	bbdoc: Sets the maximum number of 2d matrices that can be pushed onto the matrix stack using @PushMatrix.
-	end rem
-	Method SetMatrixStackCapacity( capacity:Int )
-		'_matStack=_matStack.Resize( capacity*6 )
-		_matStack = _matStack[..capacity*6]
-		_matSp=0
-	End Method
-	
-	rem
-	bbdoc: Gets the maximum number of 2d matrices that can be pushed onto the matrix stack using @PushMatrix.
-	end rem
-	Method MatrixStackCapacity:Int()
-		Return _matStack.Length/6
-	End Method
-	
-	rem
-	bbdoc: Pushes the current 2d matrix on the 2d matrix stack.
-	end rem
-	Method PushMatrix()
-		_matStack[_matSp+0]=_ix;_matStack[_matSp+1]=_iy
-		_matStack[_matSp+2]=_jx;_matStack[_matSp+3]=_jy
-		_matStack[_matSp+4]=_tx;_matStack[_matSp+5]=_ty
-		_matSp:+6
-		If _matSp>=_matStack.Length _matSp:-_matStack.Length
-	End Method
-	
-	rem
-	bbdoc: Pops the current 2d matrix from the 2d matrix stack.
-	end rem
-	Method PopMatrix()
-		_matSp:-6
-		If _matSp<0 _matSp:+_matStack.Length
-		_ix=_matStack[_matSp+0]
-		_iy=_matStack[_matSp+1]
-		_jx=_matStack[_matSp+2]
-		_jy=_matStack[_matSp+3]
-		_tx=_matStack[_matSp+4]
-		_ty=_matStack[_matSp+5]
-	End Method
-	
-	rem
-	bbdoc: Sets current font for use with #DrawText.
-	about: 	If @font is null, a default font is used.
-	end rem
-	Method SetFont( font:TFont )
-		If Not font font=defaultFont
-		_font=font
-	End Method
-	
-	rem
-	bbdoc: Gets the current font.
-	end rem
-	Method Font:TFont()
-		Return _font
-	End Method
-	
-	rem
-	bbdoc: Sets the default material used for drawing operations that use a null material.
-	end rem
-	Method SetDefaultMaterial( material:TMaterial )
-		_defaultMaterial=material
-	End Method
-	
-	rem
-	bbdoc: Returns the current default material.
-	end rem
-	Method DefaultMaterial:TMaterial()
-		Return _defaultMaterial
-	End Method
-	
-	rem
-	bbdoc: Draws a point at @x0,@y0.
-	about: If @material is null, the current default material is used.
-	end rem
-	Method DrawPoint( x0:Float,y0:Float,material:TMaterial=Null,s0:Float=0,t0:Float=0 )
-		BeginPrim material,1
-		PrimVert x0+.5,y0+.5,s0,t0
-	End Method
-	
-	rem
-	bbdoc: Draws a line from @x0,@y0 to @x1,@y1.
-	about: If @material is null, the current default material is used.
-	end rem
-	Method DrawLine( x0:Float,y0:Float,x1:Float,y1:Float,material:TMaterial=Null,s0:Float=0,t0:Float=0,s1:Float=1,t1:Float=0 )
-		BeginPrim material,2
-		PrimVert x0+.5,y0+.5,s0,t0
-		PrimVert x1+.5,y1+.5,s1,t1
-	End Method
-	
-	Rem
-	bbdoc: Draw a triangle.
-	about: If @material is null, the current default material is used.
-	End Rem
-	Method DrawTriangle( x0:Float,y0:Float,x1:Float,y1:Float,x2:Float,y2:Float,material:TMaterial=Null,s0:Float=.5,t0:Float=0,s1:Float=1,t1:Float=1,s2:Float=0,t2:Float=1 )
-		BeginPrim material,3
-		PrimVert x0,y0,s0,t0
-		PrimVert x1,y1,s1,t1
-		PrimVert x2,y2,s2,t2
-	End Method
-	
-	Rem
-	bbdoc: Draw a quad.
-	about: If @material is null, the current default material is used.
-	end rem
-	Method DrawQuad( x0:Float,y0:Float,x1:Float,y1:Float,x2:Float,y2:Float,x3:Float,y3:Float,material:TMaterial=Null,s0:Float=.5,t0:Float=0,s1:Float=1,t1:Float=1,s2:Float=0,t2:Float=1 )
-		BeginPrim material,4
-		PrimVert x0,y0,s0,t0
-		PrimVert x1,y1,s1,t1
-		PrimVert x2,y2,s2,t2
-		PrimVert x3,y3,s2,t2
-	End Method
-	
-	Rem
-	bbdoc: Draw an oval in the given rectangle.
-	about: If @material is null, the current default material is used.
-	end rem
-	Method DrawOval( x:Float,y:Float,width:Float,height:Float,material:TMaterial=Null )
-		Local xr:Float=width/2.0
-		Local yr:Float=height/2.0
-		
-		Local dx_x:Float=xr*_ix
-		Local dx_y:Float=xr*_iy
-		Local dy_x:Float=yr*_jx
-		Local dy_y:Float=yr*_jy
-		Local dx:Float=Sqr( dx_x*dx_x+dx_y*dx_y )
-		Local dy:Float=Sqr( dy_x*dy_x+dy_y*dy_y )
-
-		Local n:Int=Int( dx+dy )
-		If n<12 
-			n=12 
-		Else If n>MAX_VERTICES
-			n=MAX_VERTICES
-		Else
-			n:&~3
-		EndIf
-		
-		Local x0:Float=x+xr
-		Local y0:Float=y+yr
-		
-		BeginPrim material,n
-		
-		For Local i:Int=0 Until n
-			Local th:Float=i*360.0/n
-			Local px:Float=x0+Cos( th ) * xr
-			Local py:Float=y0+Sin( th ) * yr
-			PrimVert px,py,0,0
-		Next
-	End Method
-	
-	Rem
-	bbdoc: Draw an ellipse at @x, @y with radii @xRadius, @yRadius.
-	about: If @material is null, the current default material is used.
-	end rem
-	Method DrawEllipse( x:Float,y:Float,xr:Float,yr:Float,material:TMaterial=Null )
-		DrawOval x-xr,y-yr,xr*2,yr*2,material
-	End Method
-	
-	Rem
-	bbdoc: Draw a circle at @x, @y with radius @radius.
-	about: If @material is null, the current default material is used.
-	end rem
-	Method DrawCircle( x:Float,y:Float,r:Float,material:TMaterial=Null )
-		DrawOval x-r,y-r,r*2,r*2,material
-	End Method
-	
-	Method DrawPoly( vertices:Float[],material:TMaterial=Null )
-	
-		Local n:Int=vertices.Length/2
-		If n<3 Or n>MAX_VERTICES Return
-	
-		BeginPrim material,n
-
-		For Local i:Int=0 Until n
-			PrimVert vertices[i*2],vertices[i*2+1],0,0
-		Next
-	End Method
-	
-	Rem
-	bbdoc: Draw a batch of primtives.
-	about:
-	@order is the number of vertices for each primitive, eg: 1 for points, 2 for lines, 3 for triangles etc.
-	@count is the number of primitives to draw.
-	The @vertices array contains x,y vertex data, and must be at least @count \* @order \* 2 long.
-	If @material is null, the current default material is used.
-	end rem
-	Method DrawPrimitives( order:Int,count:Int,vertices:Float[],material:TMaterial=Null )
-	
-		BeginPrims material,order,count
-		Local p:Int=0
-		For Local i:Int=0 Until count
-			For Local j:Int=0 Until order
-				PrimVert vertices[p],vertices[p+1],0,0
-				p:+2
-			Next
-		Next
-	End Method
-	
-	Rem
-	bbdoc: Draw a batch of primtives.
-	about: 
-	@order is the number of vertices for each primitive, eg: 1 for points, 2 for lines, 3 for triangles etc.
-	@count is the number of primitives to draw.
-	The @vertices array contains x,y vertex data, and must be at least @count \* @order \* 2 long.
-	The @texcoords array contains s,t texture coordinate data, and must be at least @count \* @order \* 2 long.
-	If @material is null, the current default material is used.
-	end rem
-	Method DrawPrimitivesCoords( order:Int,count:Int,vertices:Float[],texcoords:Float[],material:TMaterial=Null )
-	
-		BeginPrims material,order,count
-		Local p:Int=0
-		For Local i:Int=0 Until count
-			For Local j:Int=0 Until order
-				PrimVert vertices[p],vertices[p+1],texcoords[p],texcoords[p+1]
-				p:+2
-			Next
-		Next
-	End Method
-	
-	Rem
-	bbdoc: Draw a batch of indexed primtives.
-	about:
-	@order is the number of vertices for each primitive, eg: 1 for points, 2 for lines, 3 for triangles etc.
-	@count is the number of primitives to draw.
-	The @vertices array contains x,y vertex data.
-	The @indices array contains vertex indices, and must be at least @count \* @order long.
-	If @material is null, the current default material is used.
-	end rem
-	Method DrawIndexedPrimitives( order:Int,count:Int,vertices:Float[],indices:Int[],material:TMaterial=Null )
-	
-		BeginPrims material,order,count
-		Local p:Int=0
-		For Local i:Int=0 Until count
-			For Local j:Int=0 Until order
-				Local k:Int=indices[p+j]*2
-				PrimVert vertices[k],vertices[k+1],0,0
-			Next
-			p:+order
-		Next
-	
-	End Method
-	
-	Method DrawIndexedPrimitivesCoords( order:Int,count:Int,vertices:Float[],texcoords:Float[],indices:Int[],material:TMaterial=Null )
-	
-		BeginPrims material,order,count
-		Local p:Int=0
-		For Local i:Int=0 Until count
-			For Local j:Int=0 Until order
-				Local k:Int=indices[p+j]*2
-				PrimVert vertices[k],vertices[k+1],texcoords[k],texcoords[k+1]
-			Next
-			p:+order
-		Next
-	
-	End Method
-	
-	rem
-	bbdoc: Draws a rect from @x,@y to @x+@width,@y+@height.
-	about: If @material is null, the current default material is used.
-	end rem
-	Method DrawRect( x0:Float,y0:Float,width:Float,height:Float,material:TMaterial=Null,s0:Float=0,t0:Float=0,s1:Float=1,t1:Float=1 )
-		Local x1:Float=x0+width
-		Local y1:Float=y0+height
-		BeginPrim material,4
-		PrimVert x0,y0,s0,t0
-		PrimVert x1,y0,s1,t0
-		PrimVert x1,y1,s1,t1
-		PrimVert x0,y1,s0,t1
-	End Method
-	
-	rem
-	bbdoc: Draws a rect from @x,@y to @x+@width,@y+@height filled with @image.
-	about: The image's handle is ignored.
-	end rem
-	Method DrawRectImage( x0:Float,y0:Float,width:Float,height:Float,image:TImage )
-		DrawRect x0,y0,width,height,image._material,image._s0,image._t0,image._s1,image._t1
-	End Method
-	
-	rem
-	bbdoc: Draws a rect at @x,@y filled with the given subrect of @image.
-	about: The image's handle is ignored.
-	end rem
-	Method DrawRectImageSource( x:Float,y:Float,image:TImage,sourceX:Int,sourceY:Int,sourceWidth:Int,sourceHeight:Int )
-		DrawRectImageSourceSize( x,y,sourceWidth,sourceHeight,image,sourceX,sourceY,sourceWidth,sourceHeight )
-	End Method
-
-	rem
-	bbdoc: Draws a rect from @x,@y to @x+@width,@y+@height filled with the given subrect of @image.
-	about: The image's handle is ignored.
-	end rem
-	Method DrawRectImageSourceSize( x0:Float,y0:Float,width:Float,height:Float,image:TImage,sourceX:Int,sourceY:Int,sourceWidth:Int,sourceHeight:Int )
-		Local material:TMaterial=image._material
-		Local s0:Float=Float(image._x+sourceX)/Float(material.Width)
-		Local t0:Float=Float(image._y+sourceY)/Float(material.Height)
-		Local s1:Float=Float(image._x+sourceX+sourceWidth)/Float(material.Width)
-		Local t1:Float=Float(image._y+sourceY+sourceHeight)/Float(material.Height)
-		DrawRect x0,y0,width,height,material,s0,t0,s1,t1
-	End Method
-	
-	'gradient rect - kinda hacky, but doesn't slow anything else down
-	Method DrawGradientRect( x0:Float,y0:Float,width:Float,height:Float,r0:Float,g0:Float,b0:Float,a0:Float,r1:Float,g1:Float,b1:Float,a1:Float,axis:Int )
-	
-		r0:*_color[0];g0:*_color[1];b0:*_color[2];a0:*_alpha
-		r1:*_color[0];g1:*_color[1];b1:*_color[2];a1:*_alpha
-		
-		Local pm0:Int=Int( a0 ) Shl 24 | Int( b0*a0 ) Shl 16 | Int( g0*a0 ) Shl 8 | Int( r0*a0 )
-		Local pm1:Int=Int( a1 ) Shl 24 | Int( b1*a0 ) Shl 16 | Int( g1*a0 ) Shl 8 | Int( r1*a0 )
-		
-		Local x1:Float=x0+width
-		Local y1:Float=y0+height
-		Local s0:Float=0.0
-		Local t0:Float=0.0
-		Local s1:Float=1.0
-		Local t1:Float=1.0
-		
-		BeginPrim Null,4
-
-		Local pmcolor:Int=_pmcolor
-		
-		BeginPrim Null,4
-		
-		Select axis
-		Case 0	'left->right
-			_pmcolor=pm0
-			PrimVert x0,y0,s0,t0
-			_pmcolor=pm1
-			PrimVert x1,y0,s1,t0
-			PrimVert x1,y1,s1,t1
-			_pmcolor=pm0
-			PrimVert x0,y1,s0,t1
-		Default	'top->bottom
-			_pmcolor=pm0
-			PrimVert x0,y0,s0,t0
-			PrimVert x1,y0,s1,t0
-			_pmcolor=pm1
-			PrimVert x1,y1,s1,t1
-			PrimVert x0,y1,s0,t1
-		End Select
-		
-		_pmcolor=pmcolor
-	End Method
-	
-	Method DrawImageImage( image:TImage )
-		BeginPrim image._material,4
-		PrimVert image._x0,image._y0,image._s0,image._t0
-		PrimVert image._x1,image._y0,image._s1,image._t0
-		PrimVert image._x1,image._y1,image._s1,image._t1
-		PrimVert image._x0,image._y1,image._s0,image._t1
-		If image._caster AddShadowCaster image._caster
-	End Method
-	
-	Method DrawImage( image:TImage,tx:Float,ty:Float )
-		PushMatrix
-		Translate tx,ty
-		DrawImageImage image
-		PopMatrix
-	End Method
-
-	Method DrawImageXYZ( image:TImage,tx:Float,ty:Float,rz:Float )
-		PushMatrix
-		TranslateRotate tx,ty,rz
-		DrawImageImage image
-		PopMatrix
-	End Method
-	
-	Method DrawImageXYZS( image:TImage,tx:Float,ty:Float,rz:Float,sx:Float,sy:Float )
-		PushMatrix
-		TranslateRotateScale tx,ty,rz,sx,sy
-		DrawImageImage image
-		PopMatrix
-	End Method
-	
-	rem
-	bbdoc: Draws @text at @x,@y in the current font.
-	end rem
-	Method DrawText( Text:String,x:Float,y:Float,xhandle:Float=0,yhandle:Float=0 )
-		x:-_font.TextWidth( Text )*xhandle
-		y:-_font.TextHeight( Text )*yhandle
-		For Local char:Int=EachIn Text
-			Local glyph:TGlyph=_font.GetGlyph( char )
-			If Not glyph Continue
-			DrawRectImageSource x,y,glyph.image,glyph.x,glyph.y,glyph.width,glyph.height
-			x:+glyph.advance
-		Next
-	End Method
-	
-	rem
-	bbdoc: Draws a shadow volume.
-	end rem
-	Method DrawShadow:Int( lx:Float,ly:Float,x0:Float,y0:Float,x1:Float,y1:Float )
-	
-		Local ext:Int=1024
-	
-		Local dx:Float=x1-x0
-		Local dy:Float=y1-y0
-		Local d0:Float=Sqr( dx*dx+dy*dy )
-		Local nx:Float=-dy/d0
-		Local ny:Float=dx/d0
-		Local pd:Float=-(x0*nx+y0*ny)
-		
-		Local d:Float=lx*nx+ly*ny+pd
-		If d<0 Return False
-
-		Local x2:Float=x1-lx
-		Local y2:Float=y1-ly
-		'Local d2:Float=ext/Sqr( x2*x2+y2*y2 )
-		x2=lx+x2*ext;y2=ly+y2*ext
-		
-		Local x3:Float=x0-lx
-		Local y3:Float=y0-ly
-		'Local d3:Float=ext/Sqr( x3*x3+y3*y3 )
-		x3=lx+x3*ext;y3=ly+y3*ext
-		
-		Local x4:Float=(x2+x3)/2-lx
-		Local y4:Float=(y2+y3)/2-ly
-		'Local d4:Float=ext/Sqr( x4*x4+y4*y4 )
-		x4=lx+x4*ext;y4=ly+y4*ext
-		
-		DrawTriangle x0,y0,x4,y4,x3,y3
-		DrawTriangle x0,y0,x1,y1,x4,y4
-		DrawTriangle x1,y1,x2,y2,x4,y4
-		
-		Return True
-	End Method
-	
-	rem
-	bbdoc: Draws multiple shadow volumes.
-	end rem
-	Method DrawShadows( x0:Float,y0:Float,drawList:TDrawList )
-	
-		Local lx:Float= x0 * _ix + y0 * _jx + _tx
-		Local ly:Float= x0 * _iy + y0 * _jy + _ty
-
-		Local verts:Float[]=drawList._casterVerts.Data
-		Local v0:Int=0
-		
-		For Local i:Int=0 Until drawList._casters.Length
-		
-			Local caster:TShadowCaster=drawList._casters.Get( i )
-			Local n:Int=caster._verts.Length
-			
-			Select caster._kind
-			Case 0	'closed loop
-				Local x0:Float=verts[v0+n-2]
-				Local y0:Float=verts[v0+n-1]
-				For Local i:Int=0 Until n-1 Step 2
-					Local x1:Float=verts[v0+i]
-					Local y1:Float=verts[v0+i+1]
-					DrawShadow( lx,ly,x0,y0,x1,y1 )
-					x0=x1
-					y0=y1
-				Next
-			Case 1	'open loop
-			Case 2	'edge soup
-			End Select
-			
-			v0:+n
-		Next
-		
-	End Method
-	
-	rem
-	bbdoc: Adds a shadow caster to the drawlist.
-	end rem
-	Method AddShadowCaster( caster:TShadowCaster )
-		_casters.Push caster
-		Local verts:Float[]=caster._verts
-		For Local i:Int=0 Until verts.Length-1 Step 2
-			Local x0:Float=verts[i]
-			Local y0:Float=verts[i+1]
-			_casterVerts.Push x0*_ix+y0*_jx+_tx
-			_casterVerts.Push x0*_iy+y0*_jy+_ty
-		Next
-	End Method
-	
-	rem
-	bbdoc: Adds a shadow caster to the drawlist at @tx,@ty.
-	end rem
-	Method AddShadowCasterXY( caster:TShadowCaster,tx:Float,ty:Float )
-		PushMatrix
-		Translate tx,ty
-		AddShadowCaster caster
-		PopMatrix
-	End Method
-	
-	Method AddShadowCasterXYZ( caster:TShadowCaster,tx:Float,ty:Float,rz:Float )
-		PushMatrix
-		TranslateRotate tx,ty,rz
-		AddShadowCaster caster
-		PopMatrix
-	End Method
-	
-	Method AddShadowCasterXYZS( caster:TShadowCaster,tx:Float,ty:Float,rz:Float,sx:Float,sy:Float )
-		PushMatrix
-		TranslateRotateScale tx,ty,rz,sx,sy
-		AddShadowCaster caster
-		PopMatrix
-	End Method
-	
-	Method IsEmpty:Int()
-		Return _next=0
-	End Method
-	
-	Method Compact()
-		If _data.Size()=_next Return
-		Local data:TBank=New TBank.Create( _next )
-		'_data.CopyBytes 0,data,0,_next
-		'_data.Discard
-		MemCopy(data._buf,_data._buf,_next)
-		_data=data
-	End Method
-	
-	Method RenderOp( op:TDrawOp,index:Int,count:Int )
-	
-		If Not op.material.Bind() Return
-		
-		If op.blend<>rs_blend
-			rs_blend=op.blend
-			Select rs_blend
-			Case TBlendMode.Opaque
-				glDisable GL_BLEND
-			Case TBlendMode.Alpha
-				glEnable GL_BLEND
-				glBlendFunc GL_ONE,GL_ONE_MINUS_SRC_ALPHA
-			Case TBlendMode.Additive
-				glEnable GL_BLEND
-				glBlendFunc GL_ONE,GL_ONE
-			Case TBlendMode.Multiply
-				glEnable GL_BLEND
-				glBlendFunc GL_DST_COLOR,GL_ONE_MINUS_SRC_ALPHA
-			Case TBlendMode.Multiply2
-				glEnable GL_BLEND
-				glBlendFunc GL_DST_COLOR,GL_ZERO
-			End Select
-		End If
-		
-		Select op.order
-		Case 1
-			glDrawArrays GL_POINTS,index,count
-		Case 2
-			glDrawArrays GL_LINES,index,count
-		Case 3
-			glDrawArrays GL_TRIANGLES,index,count
-		Case 4
-			glDrawElements GL_TRIANGLES,count/4*6,GL_UNSIGNED_SHORT,(index/4*6 + (index&3)*MAX_QUAD_INDICES)*2
-		Default
-			Local j:Int=0
-			While j<count
-				glDrawArrays GL_TRIANGLE_FAN,index+j,op.order
-				j:+op.order
-			Wend
-		End Select
-
-	End Method
-	
-	Method Render()
-		If Not _next Return
-		
-		Local offset:Int=0
-		Local opid:Int=0
-		Local ops:Object[]=_ops.Data
-		Local length:Int=_ops.length
-				
-		While offset<_next
-		
-			Local size:Int=_next-offset
-			Local lastop:Int=length
-			
-			If size>PRIM_VBO_SIZE
-			
-				size=0
-				lastop=opid
-				While lastop<length
-					Local op:TDrawOp=TDrawOp(ops[lastop])
-					Local n:Int=op.count*BYTES_PER_VERTEX
-					If size+n>PRIM_VBO_SIZE Exit
-					size:+n
-					lastop:+1
-				Wend
-				
-				If Not size
-					Local op:TDrawOp=TDrawOp(ops[opid])
-					Local count:Int=op.count
-					While count
-						Local n:Int=count
-						If n>MAX_VERTICES n=MAX_VERTICES/op.order*op.order
-						Local size:Int=n*BYTES_PER_VERTEX
-						
-						If VBO_ORPHANING_ENABLED glBufferData GL_ARRAY_BUFFER,PRIM_VBO_SIZE,Null,VBO_USAGE
-						glBufferSubData GL_ARRAY_BUFFER,0,size,_data._buf + offset
-						
-						RenderOp op,0,n
-						
-						offset:+size
-						count:-n
-					Wend
-					opid:+1
-					Continue
-				EndIf
-				
-			EndIf
-			
-			If VBO_ORPHANING_ENABLED glBufferData GL_ARRAY_BUFFER,PRIM_VBO_SIZE,Null,VBO_USAGE
-			glBufferSubData GL_ARRAY_BUFFER,0,size,_data._buf + offset
-			
-			Local index:Int=0
-			While opid<lastop
-				Local op:TDrawOp=TDrawOp(ops[opid])
-				RenderOp op,index,op.count
-				index:+op.count
-				opid:+1
-			Wend
-			offset:+size
-			
-		Wend
-		
-		glGetError
-		
-	End Method
-	
-	Method Reset()
-		_next=0
-		
-		Local data:TDrawOp[]=_ops.Data
-		For Local i:Int=0 Until _ops.Length
-			data[i].material=Null
-			freeOps.Push data[i]
-		Next
-		_ops.Clear()
-		_op=nullOp
-		
-		_casters.Clear
-		_casterVerts.Clear
-	End Method
-	
-	Method Flush()
-		Render
-		Reset
-	End Method
-	
-'	Protected
-
-	Field _blend:Float=1
-	Field _alpha:Float=255.0
-	Field _color:Float Ptr
-	Field __colorArray:Float[]=[1.0,1.0,1.0,1.0]
-	Field _pmcolor:Int=$ffffffff
-	Field _ix:Float=1,_iy:Float
-	Field _jx:Float,_jy:Float=1
-	Field _tx:Float,_ty:Float
-	Field _matStack:Float[64*6]
-	Field _matSp:Int
-	Field _font:TFont
-	Field _defaultMaterial:TMaterial
-	
-'	Private
-	
-	Field _data:TBank=New TBank.Create( 4096 )
-	Field _next:Int=0
-	
-	Field _op:TDrawOp=nullOp
-	Field _ops:TDrawOpStack=New TDrawOpStack'<DrawOp>
-	Field _casters:TShadowCasterStack=New TShadowCasterStack'<ShadowCaster>
-	Field _casterVerts:TFloatStack=New TFloatStack
-
-	Method BeginPrim( material:TMaterial,order:Int ) Final
-	
-		If Not material material=_defaultMaterial
-		
-		If _next+order*BYTES_PER_VERTEX>_data.Size()
-			Local newsize:Int=Max( _data.Size()+_data.Size()/2,_next+order*BYTES_PER_VERTEX )
-			Local data:TBank=New TBank.Create( newsize )
-			MemCopy(data._buf, _data._buf, _next)
-			'_data.CopyBytes 0,data,0,_next
-			'_data.Discard
-			_data=data
-		EndIf
-	
-		If material=_op.material And _blend=_op.blend And order=_op.order
-			_op.count:+order
-			Return
-		EndIf
-		
-		If freeOps.Length _op=freeOps.Pop() Else _op=New TDrawOp
-		
-		_ops.Push _op
-		_op.material=material
-		_op.blend=_blend
-		_op.order=order
-		_op.count=order
-	End Method
-	
-	Method BeginPrims( material:TMaterial,order:Int,count:Int ) Final
-	
-		If Not material material=_defaultMaterial
-		
-		count:*order
-		
-		If _next+count*BYTES_PER_VERTEX>_data.Size()
-			Local newsize:Int=Max( _data.Size()+_data.Size()/2,_next+count*BYTES_PER_VERTEX )
-			Local data:TBank=New TBank.Create( newsize )
-			MemCopy data._buf, _data._buf, _next
-			_data=data
-		EndIf
-	
-		If material=_op.material And _blend=_op.blend And order=_op.order
-			_op.count:+count
-			Return
-		EndIf
-		
-		If freeOps.Length _op=freeOps.Pop() Else _op=New TDrawOp
-		
-		_ops.Push _op
-		_op.material=material
-		_op.blend=_blend
-		_op.order=order
-		_op.count=count
-	End Method
-	
-	Method PrimVert( x0:Float,y0:Float,s0:Float,t0:Float ) Final
-		Local df:Float Ptr = (_data._buf + _next)
-		Local di:Int Ptr = (_data._buf + _next)
-		df[0] = x0 * _ix + y0 * _jx + _tx
-		df[1] = x0 * _iy + y0 * _jy + _ty
-		df[2] = s0
-		df[3] = t0
-		df[4] = _ix
-		df[5] = _iy
-		di[6] = _pmcolor
-		_next:+BYTES_PER_VERTEX
-	End Method
-	
-End Type
-
-
-'***** Canvas *****
-
-Type TCanvas Extends TDrawList
-
-	Const MaxLights:Int=MAX_LIGHTS
-
-	Method CreateCanvas:TCanvas( target:Object=Null )
-'		Super.Create()
-		Init
-		SetRenderTarget target
-		SetViewport 0,0,_width,_height
-		SetProjection2d 0,_width,0,_height
-		Return Self
-	End Method
-	
-	Method Discard()
-	End Method
-
-	Method SetRenderTarget( target:Object )
-
-		FlushPrims
-		
-		If Not target
-		
-			_image=Null
-			_texture=Null
-			_width=GraphicsWidth()
-			_height=GraphicsHeight()
-			_twidth=_width
-			_theight=_height
-		
-		Else If TImage( target )
-		
-			_image=TImage( target )
-			_texture=_image.Material.ColorTexture
-			If Not (_texture.Flags & TTexture.RenderTarget) Throw "Texture is not a render target texture"
-			_width=_image.Width
-			_height=_image.Height
-			_twidth=_texture.Width
-			_theight=_texture.Height
-			
-		Else If TTexture( target )
-		
-			_image=Null
-			_texture=TTexture( target )
-
-			If Not (_texture.Flags & TTexture.RenderTarget) Throw "Texture is not a render target texture"
-			_width=_texture.Width
-			_height=_texture.Height
-			_twidth=_texture.Width
-			_theight=_texture.Height
-			
-		Else
-		
-			Throw "RenderTarget object must a TImage, a TTexture or Null"
-			
-		EndIf
-		
-		_dirty=-1
-		
-	End Method
-
-	Method RenderTarget:Object()
-		If _image Return _image Else Return _texture
-	End Method
-	
-	Method Width:Int()
-		Return _width
-	End Method
-	
-	Method Height:Int()
-		Return _height
-	End Method
-	
-	Method SetColorMask( r:Int,g:Int,b:Int,a:Int )
-		FlushPrims
-		_colorMask[0]=r
-		_colorMask[1]=g
-		_colorMask[2]=b
-		_colorMask[3]=a
-		_dirty:|DIRTY_COLORMASK
-	End Method
-	
-	Method ColorMask:Int[]()
-		Return _colorMask
-	End Method
-	
-	Method SetViewport( x:Int,y:Int,w:Int,h:Int )
-		FlushPrims
-		_viewport[0]=x
-		_viewport[1]=y
-		_viewport[2]=w
-		_viewport[3]=h
-		_dirty:|DIRTY_VIEWPORT
-	End Method
-	
-	Method Viewport:Int[]()
-		Return _viewport
-	End Method
-	
-	Method SetScissor( x:Int,y:Int,w:Int,h:Int )
-		FlushPrims
-		_scissor[0]=x
-		_scissor[1]=y
-		_scissor[2]=w
-		_scissor[3]=h
-		_dirty:|DIRTY_VIEWPORT
-	End Method
-	
-	Method Scissor:Int[]()
-		Return _scissor
-	End Method
-	
-	Method SetProjectionMatrix( projMatrix:Float[] )
-		FlushPrims
-		If projMatrix
-			Mat4Copy projMatrix,_projMatrix
-		Else
-			Mat4InitArray _projMatrix
-		EndIf
-		_dirty:|DIRTY_SHADER
-	End Method
-	
-	Method SetProjection2d( Left:Float,Right:Float,top:Float,bottom:Float,znear:Float=-1,zfar:Float=1 )
-		FlushPrims
-		Mat4Ortho Left,Right,top,bottom,znear,zfar,_projMatrix
-		_dirty:|DIRTY_SHADER
-	End Method
-	
-	Method ProjectionMatrix:Float[]()
-		Return _projMatrix
-	End Method
-	
-	Method SetViewMatrix( viewMatrix:Float[] )
-		FlushPrims
-		If viewMatrix
-			Mat4Copy viewMatrix,_viewMatrix
-		Else
-			Mat4InitArray _viewMatrix
-		End If
-		_dirty:|DIRTY_SHADER
-	End Method
-	
-	Method ViewMatrix:Float[]()
-		Return _viewMatrix
-	End Method
-	
-	Method SetModelMatrix( modelMatrix:Float[] )
-		FlushPrims
-		If modelMatrix
-			Mat4Copy modelMatrix,_modelMatrix
-		Else
-			Mat4InitArray _modelMatrix
-		EndIf
-		_dirty:|DIRTY_SHADER
-	End Method
-	
-	Method ModelMatrix:Float[]()
-		Return _modelMatrix
-	End Method
-
-	Method SetAmbientLight( r:Float,g:Float,b:Float,a:Float=1 )
-		FlushPrims
-		_ambientLight[0]=r
-		_ambientLight[1]=g
-		_ambientLight[2]=b
-		_ambientLight[3]=a
-		_dirty:|DIRTY_SHADER
-	End Method
-	
-	Method AmbientLight:Float[]()
-		Return _ambientLight
-	End Method
-	
-	Method SetFogColor( r:Float,g:Float,b:Float,a:Float )
-		FlushPrims
-		_fogColor[0]=r
-		_fogColor[1]=g
-		_fogColor[2]=b
-		_fogColor[3]=a
-		_dirty:|DIRTY_SHADER
-	End Method
-	
-	Method FogColor:Float[]()
-		Return _fogColor
-	End Method
-	
-	Method SetLightType( index:Int,kind:Int )
-		FlushPrims
-		Local light:TLightData=_lights[index]
-		light.kind=kind
-		_dirty:|DIRTY_SHADER
-	End Method
-	
-	Method GetLightType:Int( index:Int )
-		Return _lights[index].kind
-	End Method
-	
-	Method SetLightColor( index:Int,r:Float,g:Float,b:Float,a:Float=1 )
-		FlushPrims
-		Local light:TLightData=_lights[index]
-		light.color[0]=r
-		light.color[1]=g
-		light.color[2]=b
-		light.color[3]=a
-		_dirty:|DIRTY_SHADER
-	End Method
-	
-	Method GetLightColor:Float[]( index:Int )
-		Return _lights[index].color
-	End Method
-	
-	Method SetLightPosition( index:Int,x:Float,y:Float,z:Float )
-		FlushPrims
-		Local light:TLightData=_lights[index]
-		light.position[0]=x
-		light.position[1]=y
-		light.position[2]=z
-		light.vector[0]=x
-		light.vector[1]=y
-		light.vector[2]=z
-		_dirty:|DIRTY_SHADER
-	End Method
-	
-	Method GetLightPosition:Float[]( index:Int )
-		Return _lights[index].position
-	End Method
-	
-	Method SetLightRange( index:Int,Range:Float )
-		FlushPrims
-		Local light:TLightData=_lights[index]
-		light.Range=Range
-		_dirty:|DIRTY_SHADER
-	End Method
-	
-	Method GetLightRange:Float( index:Int )
-		Return _lights[index].Range
-	End Method
-	
-	Method SetShadowMap( image:TImage )
-		FlushPrims
-		_shadowMap=image
-		_dirty:|DIRTY_SHADER
-	End Method
-	
-	Method ShadowMap:TImage()
-		Return _shadowMap
-	End Method
-	
-	Method SetLineWidth( lineWidth:Float )
-		FlushPrims
-		_lineWidth=lineWidth
-		_dirty:|DIRTY_LINEWIDTH
-	End Method
-	
-	Method LineWidth:Float()
-		Return _lineWidth
-	End Method
-	
-	Method Clear( r:Float=0,g:Float=0,b:Float=0,a:Float=1 )
-		FlushPrims
-		Validate
-		If _clsScissor
-			glEnable GL_SCISSOR_TEST
-			glScissor _vpx,_vpy,_vpw,_vph
-		EndIf
-		glClearColor r,g,b,a
-		glClear GL_COLOR_BUFFER_BIT
-		If _clsScissor glDisable GL_SCISSOR_TEST
-	End Method
-	
-	Method ReadPixels( x:Int,y:Int,width:Int,height:Int,data:TBank,dataOffset:Int=0,dataPitch:Int=0 )
-	
-		FlushPrims
-		
-		If Not dataPitch Or dataPitch=width*4
-			glReadPixels x,y,width,height,GL_RGBA,GL_UNSIGNED_BYTE,data._buf + dataOffset
-		Else
-			For Local iy:Int=0 Until height
-				glReadPixels x,y+iy,width,1,GL_RGBA,GL_UNSIGNED_BYTE,data._buf + dataOffset+dataPitch*iy
-			Next
-		EndIf
-
-	End Method
-
-	Method RenderDrawList( drawbuf:TDrawList )
-
-		Local fast:Int=_ix=1 And _iy=0 And _jx=0 And _jy=1 And _tx=0 And _ty=0 And _color[0]=1 And _color[1]=1 And _color[2]=1 And _color[3]=1
-		
-		If fast
-			FlushPrims
-			Validate
-			drawbuf.Render
-			Return
-		EndIf
-		
-		tmpMat3d[0]=_ix
-		tmpMat3d[1]=_iy
-		tmpMat3d[4]=_jx
-		tmpMat3d[5]=_jy
-		tmpMat3d[12]=_tx
-		tmpMat3d[13]=_ty
-		tmpMat3d[10]=1
-		tmpMat3d[15]=1
-		
-		Mat4Multiply _modelMatrix,tmpMat3d,tmpMat3d2
-		
-		FlushPrims
-		
-		Local tmp:Float[]=_modelMatrix
-		_modelMatrix=tmpMat3d2
-		rs_globalColor[0]=_color[0]*_color[3]
-		rs_globalColor[1]=_color[1]*_color[3]
-		rs_globalColor[2]=_color[2]*_color[3]
-		rs_globalColor[3]=_color[3]
-		_dirty:|DIRTY_SHADER
-		
-		Validate
-		drawbuf.Render
-		
-		_modelMatrix=tmp
-		rs_globalColor[0]=1
-		rs_globalColor[1]=1
-		rs_globalColor[2]=1
-		rs_globalColor[3]=1
-		_dirty:|DIRTY_SHADER
-	End Method
-	
-	Method RenderDrawListXYZ( drawList:TDrawList,tx:Float,ty:Float,rz:Float=0,sx:Float=1,sy:Float=1 )
-		Super.PushMatrix
-		Super.TranslateRotateScale tx,ty,rz,sx,sy
-		RenderDrawList( drawList )
-		Super.PopMatrix
-	End Method
-
-	Method Flush()
-		FlushPrims
-		
-		If Not _texture Return
-		
-		If _texture._flags & TTexture.Managed
-			Validate
-
-			glDisable GL_SCISSOR_TEST
-			glViewport 0,0,_twidth,_theight
-			
-			If _width=_twidth And _height=_theight
-				glReadPixels 0,0,_twidth,_theight,GL_RGBA,GL_UNSIGNED_BYTE, _texture._data.pixels
-			Else
-				For Local y:Int=0 Until _height
-					glReadPixels _image._x,_image._y+y,_width,1,GL_RGBA,GL_UNSIGNED_BYTE,_texture._data.pixels + (_image._y+y) * (_twidth*4) + (_image._x*4)
-				Next
-			EndIf
-
-			_dirty:|DIRTY_VIEWPORT
-		EndIf
-
-		_texture.UpdateMipmaps
-	End Method
-	
-	Global _tformInvProj:Float[16]
-	Global _tformT:Float[]=[0.0,0.0,-1.0,1.0]
-	Global _tformP:Float[4]
-	
-	Method TransformCoords( coords_in:Float[],coords_out:Float[],Mode:Int=0 )
-	
-		Mat4Inverse _projMatrix,_tformInvProj
-
-		Select Mode
-		Case 0
-			_tformT[0]=(coords_in[0]-_viewport[0])/_viewport[2]*2-1
-			_tformT[1]=(coords_in[1]-_viewport[1])/_viewport[3]*2-1
-			Mat4Transform _tformInvProj,_tformT,_tformP
-			_tformP[0]:/_tformP[3]
-			_tformP[1]:/_tformP[3]
-			_tformP[2]:/_tformP[3]
-			_tformP[3]=1
-			coords_out[0]=_tformP[0]
-			coords_out[1]=_tformP[1]
-			If coords_out.Length>2 coords_out[2]=_tformP[2]
-		Default
-			Throw "Invalid TransformCoords mode"
-		End Select
-	End Method
-	
-	'Private
-
-	Const DIRTY_RENDERTARGET:Int=1
-	Const DIRTY_VIEWPORT:Int=2
-	Const DIRTY_SHADER:Int=4
-	Const DIRTY_LINEWIDTH:Int=8
-	Const DIRTY_COLORMASK:Int=16
-		
-	Field _seq:Int
-	Field _dirty:Int=-1
-	Field _image:TImage
-	Field _texture:TTexture	
-	Field _width:Int
-	Field _height:Int
-	Field _twidth:Int
-	Field _theight:Int
-	Field _shadowMap:TImage
-	Field _colorMask:Int[]=[True,True,True,True]
-	Field _viewport:Int[]=[0,0,640,480]
-	Field _scissor:Int[]=[0,0,100000,100000]
-	Field _vpx:Int,_vpy:Int,_vpw:Int,_vph:Int
-	Field _scx:Int,_scy:Int,_scw:Int,_sch:Int
-	Field _clsScissor:Int
-	Field _projMatrix:Float[]=Mat4New()
-	Field _invProjMatrix:Float[]=Mat4New()
-	Field _viewMatrix:Float[]=Mat4New()
-	Field _modelMatrix:Float[]=Mat4New()
-	Field _ambientLight:Float[]=[0.0,0.0,0.0,1.0]
-	Field _fogColor:Float[]=[0.0,0.0,0.0,0.0]
-	Field _lights:TLightData[4]
-	Field _lineWidth:Float=1
-
-	Global _active:TCanvas
-	
-	Method Init()
-		For Local i:Int=0 Until 4
-			_lights[i]=New TLightData
-		Next
-		_dirty=-1
-	End Method
-
-	Method FlushPrims()
-		If Super.IsEmpty Return
-		Validate
-		Super.Flush
-	End Method
-	
-	Method Validate()
-		If _seq<>graphicsSeq	
-			_seq=graphicsSeq
-			InitVbos
-			_dirty=-1
-		EndIf
-	
-		If _active=Self
-			If Not _dirty Return
-		Else
-			If _active _active.Flush
-			_active=Self
-			_dirty=-1
-		EndIf
-
-'		_dirty=-1
-		
-		If _dirty & DIRTY_RENDERTARGET
-
-			If _texture
-				glBindFramebuffer GL_FRAMEBUFFER,_texture.GLFramebuffer()
-			Else
-				glBindFramebuffer GL_FRAMEBUFFER,defaultFbo
-			EndIf
-		End If
-		
-		If _dirty & DIRTY_VIEWPORT
-		
-			_vpx=_viewport[0];_vpy=_viewport[1];_vpw=_viewport[2];_vph=_viewport[3]
-			If _image
-				_vpx:+_image._x
-				_vpy:+_image._y
-			EndIf
-			
-			_scx=_scissor[0];_scy=_scissor[1];_scw=_scissor[2];_sch=_scissor[3]
-			
-			If _scx<0 _scx=0 Else If _scx>_vpw _scx=_vpw
-			If _scw<0 _scw=0 Else If _scx+_scw>_vpw _scw=_vpw-_scx
-			
-			If _scy<0 _scy=0 Else If _scy>_vph _scy=_vph
-			If _sch<0 _sch=0 Else If _scy+_sch>_vph _sch=_vph-_scy
-			
-			_scx:+_vpx;_scy:+_vpy
-		
-			If Not _texture
-				_vpy=_theight-_vpy-_vph
-				_scy=_theight-_scy-_sch
-			EndIf
-			
-			glViewport _vpx,_vpy,_vpw,_vph
-			
-			If _scx<>_vpx Or _scy<>_vpy Or _scw<>_vpw Or _sch<>_vph
-				glEnable GL_SCISSOR_TEST
-				glScissor _scx,_scy,_scw,_sch
-				_clsScissor=False
-			Else
-				glDisable GL_SCISSOR_TEST
-				_clsScissor=(_scx<>0 Or _scy<>0 Or _vpw<>_twidth Or _vph<>_theight)
-			EndIf
-			
-		EndIf
-		
-		If _dirty & DIRTY_SHADER
-		
-			rs_program=Null
-			
-			If _texture
-				rs_clipPosScale[1]=1
-				Mat4Copy _projMatrix,rs_projMatrix
-			Else
-				rs_clipPosScale[1]=-1
-				Mat4Multiply flipYMatrix,_projMatrix,rs_projMatrix
-			EndIf
-			
-			Mat4Multiply _viewMatrix,_modelMatrix,rs_modelViewMatrix
-			Mat4Multiply rs_projMatrix,rs_modelViewMatrix,rs_modelViewProjMatrix
-			Vec4Copy _ambientLight,rs_ambientLight
-			Vec4Copy _fogColor,rs_fogColor
-			
-			rs_numLights=0
-			For Local i:Int=0 Until MAX_LIGHTS
-
-				Local light:TLightData=_lights[i]
-				If Not light.kind Continue
-				
-				Mat4Transform _viewMatrix,light.vector,light.tvector
-				
-				rs_lightColors[rs_numLights*4+0]=light.color[0]
-				rs_lightColors[rs_numLights*4+1]=light.color[1]
-				rs_lightColors[rs_numLights*4+2]=light.color[2]
-				rs_lightColors[rs_numLights*4+3]=light.color[3]
-				
-				rs_lightVectors[rs_numLights*4+0]=light.tvector[0]
-				rs_lightVectors[rs_numLights*4+1]=light.tvector[1]
-				rs_lightVectors[rs_numLights*4+2]=light.tvector[2]
-				rs_lightVectors[rs_numLights*4+3]=light.Range
-
-				rs_numLights:+1
-			Next
-			
-			If _shadowMap
-				rs_shadowTexture=_shadowMap._material._colorTexture
-			Else 
-				rs_shadowTexture=Null
-			EndIf
-			
-			rs_blend=-1
-
-		End If
-		
-		If _dirty & DIRTY_LINEWIDTH
-			glLineWidth _lineWidth
-		EndIf
-		
-		If _dirty & DIRTY_COLORMASK
-			glColorMask _colorMask[0],_colorMask[1],_colorMask[2],_colorMask[3]
-		End If
-		
-		_dirty=0
-	End Method
-	
-End Type
-
-' stacks
-
-Type TFloatStack
-	Field data:Float[]
-	Field length:Int
-
-	Method Push( value:Float )
-		If length=data.Length
-			data=data[..length*2+10]
-		EndIf
-		data[length]=value
-		length:+1
-	End Method
-
-	Method Pop:Float()
-		length:-1
-		Local v:Float=data[length]
-		data[length]=Null
-		Return v
-	End Method
-
-	Method Top:Float()
-		Return data[length-1]
-	End Method
-
-	Method Clear()
-		For Local i:Int=0 Until length
-			data[i]=Null
-		Next
-		length=0
-	End Method
-
-End Type
-
-Type TDrawOpStack
-	Field data:TDrawOp[]
-	Field length:Int
-
-	Method Push( value:TDrawOp )
-		If length=data.Length
-			data=data[..length*2+10]
-		EndIf
-		data[length]=value
-		length:+1
-	End Method
-
-	Method Pop:TDrawOp()
-		length:-1
-		Local v:TDrawOp=data[length]
-		data[length]=Null
-		Return v
-	End Method
-
-	Method Top:TDrawOp()
-		Return data[length-1]
-	End Method
-
-	Method Clear()
-		For Local i:Int=0 Until length
-			data[i]=Null
-		Next
-		length=0
-	End Method
-
-End Type
-
-
-Type TShadowCasterStack
-	Field data:TShadowCaster[]
-	Field length:Int
-
-	Method Push( value:TShadowCaster )
-		If length=data.Length
-			data=data[..length*2+10]
-		EndIf
-		data[length]=value
-		length:+1
-	End Method
-
-	Method Pop:TShadowCaster()
-		length:-1
-		Local v:TShadowCaster=data[length]
-		data[length]=Null
-		Return v
-	End Method
-
-	Method Top:TShadowCaster()
-		Return data[length-1]
-	End Method
-
-	Method Get:TShadowCaster(index:Int)
-		Return data[index]
-	End Method
-	
-	Method Clear()
-		For Local i:Int=0 Until length
-			data[i]=Null
-		Next
-		length=0
-	End Method
-
-End Type
-
-Type TDrawListStack
-	Field data:TDrawList[]
-	Field length:Int
-
-	Method Push( value:TDrawList )
-		If length=data.Length
-			data=data[..length*2+10]
-		EndIf
-		data[length]=value
-		length:+1
-	End Method
-
-	Method Pop:TDrawList()
-		length:-1
-		Local v:TDrawList=data[length]
-		data[length]=Null
-		Return v
-	End Method
-
-	Method Top:TDrawList()
-		Return data[length-1]
-	End Method
-	
-	Method Get:TDrawList(index:Int)
-		Return data[index]
-	End Method
-
-	Method Clear()
-		For Local i:Int=0 Until length
-			data[i]=Null
-		Next
-		length=0
-	End Method
-
-End Type
+'
+' BlitzMax port, 2015 Bruce A Henderson
+' 
+' Copyright (c) 2015 Mark Sibly
+' 
+' This software is provided 'as-is', without any express or implied
+' warranty. In no event will the authors be held liable for any damages
+' arising from the use of this software.
+' 
+' Permission is granted to anyone to use this software for any purpose,
+' including commercial applications, and to alter it and redistribute it
+' freely, subject to the following restrictions:
+' 
+' 1. The origin of this software must not be misrepresented; you must not
+'    claim that you wrote the original software. If you use this software
+'    in a product, an acknowledgement in the product documentation would be
+'    appreciated but is not required.
+' 2. Altered source versions must be plainly marked as such, and must not be
+'    misrepresented as being the original software.
+' 3. This notice may not be removed or altered from any source distribution.
+' 
+SuperStrict
+
+
+'Framework brl.standardio
+
+Import brl.bank
+Import brl.map
+Import brl.ramstream
+Import brl.pixmap
+Import brl.filesystem
+Import brl.system
+Import brl.Graphics
+Import "math3d.bmx"
+Import "glutil.bmx"
+Import "glslparser.bmx"
+Import "maps.bmx"
+
+
+Incbin "data/mojo2_font.png"
+Incbin "data/mojo2_program.glsl"
+Incbin "data/mojo2_fastshader.glsl"
+Incbin "data/mojo2_bumpshader.glsl"
+Incbin "data/mojo2_matteshader.glsl"
+Incbin "data/mojo2_shadowshader.glsl"
+Incbin "data/mojo2_lightmapshader.glsl"
+
+Private
+
+Global _inited:Int
+
+Global mainShader:String
+
+Global _fastShader:TShader
+Global _bumpShader:TShader
+Global _matteShader:TShader
+Global _shadowShader:TShader
+Global _lightMapShader:TShader
+
+Global defaultFont:TFont
+Global _defaultShader:TShader
+
+Global freeOps:TDrawOpStack=New TDrawOpStack
+Global nullOp:TDrawOp=New TDrawOp
+
+Global defaultFbo:Int
+
+Global tmpMat2d:Float[6]
+Global tmpMat3d:Float[16]
+Global tmpMat3d2:Float[16]
+
+Global flipYMatrix:Float[]=Mat4New()
+Global graphicsSeq:Int=1
+Global vbosSeq:Int
+
+'shader params
+Global rs_projMatrix:Float[]=Mat4New()
+Global rs_modelViewMatrix:Float[]=Mat4New()
+Global rs_modelViewProjMatrix:Float[]=Mat4New()
+Global rs_clipPosScale:Float[]=[1.0,1.0,1.0,1.0]
+Global rs_globalColor:Float[]=[1.0,1.0,1.0,1.0]
+Global rs_numLights:Int
+Global rs_fogColor:Float[]=[0.0,0.0,0.0,0.0]
+Global rs_ambientLight:Float[]=[0.0,0.0,0.0,1.0]
+Global rs_lightColors:Float[MAX_LIGHTS*4]
+Global rs_lightVectors:Float[MAX_LIGHTS*4]
+Global rs_shadowTexture:TTexture
+Global rs_program:TGLProgram
+Global rs_material:TMaterial
+Global rs_blend:Int=-1
+Global rs_vbo:Int
+Global rs_ibo:Int
+
+Const VBO_USAGE:Int=GL_STREAM_DRAW
+Const VBO_ORPHANING_ENABLED:Int=False
+
+Const MAX_LIGHTS:Int=4
+Const BYTES_PER_VERTEX:Int=28
+
+'can really be anything <64K (due to 16bit indices) but this keeps total VBO size<64K, and making it bigger doesn't seem to improve performance much.
+Const MAX_VERTICES:Int=65536/BYTES_PER_VERTEX	
+
+Const MAX_QUADS:Int=MAX_VERTICES/4
+Const MAX_QUAD_INDICES:Int=MAX_QUADS*6
+Const PRIM_VBO_SIZE:Int=MAX_VERTICES*BYTES_PER_VERTEX
+
+Function IsPow2:Int( sz:Int )
+	Return (sz & (sz-1))=0
+End Function
+
+Public
+
+Type TLightData
+	Field kind:Int=0
+	Field color:Float[]=[1.0,1.0,1.0,1.0]
+	Field position:Float[]=[0.0,0.0,-10.0]
+	Field Range:Float=10
+	'
+	Field vector:Float[]=[0.0,0.0,-10.0,1.0]
+	Field tvector:Float[4]
+End Type
+
+Private
+
+Function InitVbos()
+	If vbosSeq=graphicsSeq 
+		BindVbos()
+		Return
+	EndIf
+	vbosSeq=graphicsSeq
+
+	glGenBuffers 1, Varptr rs_vbo
+	glBindBuffer GL_ARRAY_BUFFER,rs_vbo
+	glBufferData GL_ARRAY_BUFFER,PRIM_VBO_SIZE,Null,VBO_USAGE
+	
+	glEnableVertexAttribArray 0
+	glVertexAttribPointer 0,2,GL_FLOAT,False,BYTES_PER_VERTEX,0
+	
+	glEnableVertexAttribArray 1
+	glVertexAttribPointer 1,2,GL_FLOAT,False,BYTES_PER_VERTEX,8
+	
+	glEnableVertexAttribArray 2
+	glVertexAttribPointer 2,2,GL_FLOAT,False,BYTES_PER_VERTEX,16
+	
+	glEnableVertexAttribArray 3
+	glVertexAttribPointer 3,4,GL_UNSIGNED_BYTE,True,BYTES_PER_VERTEX,24
+	
+	glGenBuffers(1, Varptr rs_ibo)
+	glBindBuffer GL_ELEMENT_ARRAY_BUFFER,rs_ibo
+	Local idxs:TBank=New TBank.Create( MAX_QUAD_INDICES*4*2 )
+	For Local j:Int = 0 Until 4
+		Local k:Int = j*MAX_QUAD_INDICES*2
+		For Local i:Int = 0 Until MAX_QUADS
+			idxs.PokeShort i*12+k+0,i*4+j+0
+			idxs.PokeShort i*12+k+2,i*4+j+1
+			idxs.PokeShort i*12+k+4,i*4+j+2
+			idxs.PokeShort i*12+k+6,i*4+j+0
+			idxs.PokeShort i*12+k+8,i*4+j+2
+			idxs.PokeShort i*12+k+10,i*4+j+3
+		Next
+	Next
+	glBufferData GL_ELEMENT_ARRAY_BUFFER,idxs.Size(),idxs._buf,GL_STATIC_DRAW
+	'idxs.Discard
+End Function
+
+Function BindVbos()
+	glBindBuffer( GL_ARRAY_BUFFER,rs_vbo )
+	glEnableVertexAttribArray( 0 ) ; glVertexAttribPointer 0,2,GL_FLOAT,False,BYTES_PER_VERTEX, 0
+	glEnableVertexAttribArray( 1 ) ; glVertexAttribPointer 1,2,GL_FLOAT,False,BYTES_PER_VERTEX, 8
+	glEnableVertexAttribArray( 2 ) ; glVertexAttribPointer 2,2,GL_FLOAT,False,BYTES_PER_VERTEX, 16
+	glEnableVertexAttribArray( 3 ) ; glVertexAttribPointer 3,4,GL_UNSIGNED_BYTE,True,BYTES_PER_VERTEX, 24
+
+	glBindBuffer( GL_ELEMENT_ARRAY_BUFFER,rs_ibo )
+End Function
+
+Global inited:Int
+
+Function InitMojo2()
+	If inited Return
+	inited=True
+
+?Not opengles
+	glewInit()
+?
+	
+	InitVbos
+	
+	glGetIntegerv GL_FRAMEBUFFER_BINDING, Varptr defaultFbo
+	
+	mainShader=LoadString( "incbin::data/mojo2_program.glsl" )
+	
+	_fastShader=New TShader.Create( LoadString( "incbin::data/mojo2_fastshader.glsl" ) )
+	_bumpShader=New TBumpShader.Create( LoadString( "incbin::data/mojo2_bumpshader.glsl" ) )
+	_matteShader=New TMatteShader.Create( LoadString( "incbin::data/mojo2_matteshader.glsl" ) )
+	_shadowShader=New TShader.Create( LoadString( "incbin::data/mojo2_shadowshader.glsl" ) )
+	_lightMapShader=New TShader.Create( LoadString( "incbin::data/mojo2_lightmapshader.glsl" ) )
+	_defaultShader=_bumpShader
+
+	defaultFont=TFont.Load( "incbin::data/mojo2_font.png",32,96,True )'9,13,1,0,7,13,32,96 )
+	If Not defaultFont Throw "Can't load default font"
+	
+	flipYMatrix[5]=-1
+End Function
+
+Public
+
+Type TRefCounted
+
+	Method Retain()
+		If _refs<=0 Throw "Internal error"
+		_refs:+1
+	End Method
+	
+	Method Free()
+		If _refs<=0 Throw "Internal error"
+		_refs:-1
+		If _refs Return
+		_refs=-1
+		Destroy
+	End Method
+	
+	Method Destroy() Abstract
+
+	Field _refs:Int=1
+End Type
+
+'***** Texture *****
+
+Rem
+bbdoc: Textures contains image data for use by shaders when rendering.
+about: For more information, please see the #TShader type.
+end rem
+Type TTexture Extends TRefCounted
+
+	'flags
+	Const Filter:Int=1
+	Const Mipmap:Int=2
+	Const ClampS:Int=4
+	Const ClampT:Int=8
+	Const ClampST:Int=12
+	Const RenderTarget:Int=16
+	Const Managed:Int=256
+
+	Rem
+	bbdoc: Creates a new texture.
+	about: The @width and @height are parameters are the size of the new texture.
+	The @format parameter must be 4.
+	The @flags parameter can be a bitwise combination of:
+	| @ Flags				| @Description
+	| Texture.Filter			| The texture is filtered when magnified
+	| Texture.Mipmap			| The texture is mipmapped when minified
+	| Texture.ClampS			| Texture S coordinate is clamped
+	| Texture.ClampT			| Texture T coordinate is clamped
+	| Texture.ClampST		| Texture S and T coordinates are clamped.
+	| Texture.RenderTarget	| The texture can rendered to using a #Canvas.
+	| Texture.Managed		| Texture contents are preserved when graphics are lost
+	End Rem
+	Method Create:TTexture( width:Int,height:Int,format:Int,flags:Int, data:TPixmap = Null )
+
+		If format<>PF_RGBA8888 Then
+			Throw "Invalid texture format: "+format
+		End If
+
+		'can't mipmap NPOT textures on gles20
+		If Not IsPow2( width ) Or Not IsPow2( height ) flags:&~Mipmap
+		
+		_width=width
+		_height=height
+		_format=format
+		_flags=flags
+		_data=data
+
+		If _flags & Managed
+			_managed=New TPixmap.Create( width,height,PF_RGBA8888 )
+			If _data
+				_managed.Paste( _data,0,0 )
+				_data=Null
+			Else
+				_managed.ClearPixels( $ffff00ff )
+			EndIf
+		EndIf
+		
+		Validate()
+
+		Return Self
+	End Method
+	
+	Method Destroy()
+		If _seq=graphicsSeq glDeleteTextures 1, Varptr _glTexture
+		_glTexture=0
+		_glFramebuffer=0
+	End Method
+	
+	Rem
+	bbdoc: Gets texture width.
+	end rem
+	Method Width:Int()
+		Return _width
+	End Method
+	
+	Rem
+	bbdoc: Gets texture height.
+	end rem
+	Method Height:Int()
+		Return _height
+	End Method
+	
+	Rem
+	bbdoc: Gets texture format.
+	end rem
+	Method Format:Int()
+		Return _format
+	End Method
+	
+	Rem
+	bbdoc: Gets texture flags.
+	end rem
+	Method Flags:Int()
+		Return _flags
+	End Method
+
+	Rem
+	bbdoc: Writes pixel data to texture.
+	about: Pixels should be in premultiplied alpha format.
+	end rem
+	Method WritePixels( x:Int,y:Int,width:Int,height:Int,data:TPixmap,dataOffset:Int=0,dataPitch:Int=0 )
+
+		glPushTexture2d GLTexture()
+	
+		If Not dataPitch Or dataPitch=width*4
+		
+			glTexSubImage2D GL_TEXTURE_2D,0,x,y,width,height,GL_RGBA,GL_UNSIGNED_BYTE,data.pixels + dataOffset
+			
+		Else
+			For Local iy:Int=0 Until height
+				glTexSubImage2D GL_TEXTURE_2D,0,x,y+iy,width,1,GL_RGBA,GL_UNSIGNED_BYTE,data.pixels + dataOffset+iy*dataPitch
+			Next
+		EndIf
+		
+		glPopTexture2d
+		
+		If _flags & Managed
+		
+			Local texPitch:Int=_width*4
+			If Not dataPitch dataPitch=width*4
+			
+			For Local iy:Int=0 Until height
+				MemCopy _data.pixels + (y+iy)*texPitch+x*4, data.pixels + dataOffset+iy*dataPitch, width*4
+			Next
+			
+		EndIf
+
+	End Method
+
+	Method SetData( x:Int,y:Int,pixmap:TPixmap )
+		
+		If _managed
+			If pixmap<>_managed _managed.Paste( pixmap,x,y )
+		Else If _data
+			If pixmap<>_data Throw "Texture is read only" 
+		EndIf
+		
+		glPushTexture2d( GLTexture() )
+		
+		Local width:Int=pixmap.Width
+		Local height:Int=pixmap.Height
+		
+		If pixmap.Pitch=_width*4
+		
+			glTexSubImage2D( GL_TEXTURE_2D,0,x,y,width,height,GL_RGBA,GL_UNSIGNED_BYTE,pixmap.pixels )
+			
+		Else
+		
+			For Local iy:Int=0 Until height
+				glTexSubImage2D( GL_TEXTURE_2D,0,x,y+iy,width,1,GL_RGBA,GL_UNSIGNED_BYTE,pixmap.PixelPtr( 0,iy ) )
+			Next
+			
+		EndIf
+		
+		glPopTexture2d
+		
+	End Method
+		
+	Method UpdateMipmaps()
+		If Not (_flags & Mipmap) Return
+			
+		If _seq<>graphicsSeq
+			Validate()
+			Return
+		EndIf
+
+		glPushTexture2d GLTexture()
+
+		glGenerateMipmap GL_TEXTURE_2D
+		
+		glPopTexture2d
+	End Method
+	
+	Method Loading:Int()
+			Return False
+	End Method
+	
+	Method GLTexture:Int()
+		Validate
+		Return _glTexture
+	End Method
+	
+	Method GLFramebuffer:Int()
+		Validate
+		Return _glFramebuffer
+	End Method
+	
+	Function TexturesLoading:Int()
+			Return 0
+	End Function
+	
+	Rem
+	bbdoc: Loads a texture from a url.
+	end rem
+	Function Load:TTexture( url:Object,format:Int=PF_RGBA8888,flags:Int=Filter|Mipmap|ClampST )
+	
+		Local info:Int[2]
+		
+		Local data:TPixmap=LoadPixmap(url)
+		If Not data Return Null
+		
+		' convert to RGBA
+		If data.format <> format Then
+			data = data.Convert(format)
+		End If
+		
+		PremultiplyAlpha(data)
+			
+		Local tex:TTexture=New TTexture.Create( data.width,data.height,format,flags,data )
+		
+		Return tex
+	End Function
+
+	Function PremultiplyAlpha(pix:TPixmap)
+		For Local y:Int=0 Until pix.height
+			For Local x:Int=0 Until pix.width
+				Local pixel:Int=pix.ReadPixel( x,y )
+				Local a:Int=pixel Shr 24 & 255
+				Local b:Int=(pixel Shr 16 & 255)*a/255
+				Local g:Int=(pixel Shr 8 & 255)*a/255
+				Local r:Int=(pixel & 255)*a/255
+				pixel=a Shl 24 | b Shl 16 | g Shl 8 | r
+				pix.WritePixel( x,y,pixel )
+			Next
+		Next
+	End Function
+	
+	Function Color:TTexture( color:Int )
+		Local tex:TTexture=TTexture(_colors.ValueForKey( color ))
+		If tex Return tex
+
+		Local pixmap:TPixmap=New TPixmap.Create( 1,1,PF_RGBA8888 )
+		pixmap.ClearPixels( color )
+
+		tex=New TTexture.Create( 1,1,PF_RGBA8888,ClampST,pixmap )
+		_colors.Insert color,tex
+		Return tex
+	End Function
+	
+	Rem
+	bbdoc: Returns a stock single texel black texture.
+	end rem
+	Function Black:TTexture()
+		If Not _black _black=Color( $ff000000 )
+		Return _black
+	End Function
+	
+	Rem
+	bbdoc: Returns a stock single texel white texture.
+	end rem
+	Function White:TTexture()
+		If Not _white _white=Color( $ffffffff )
+		Return _white
+	End Function
+	
+	Rem
+	bbdoc: Returnss a stock single texel magenta texture.
+	end rem
+	Function Magenta:TTexture()
+		If Not _magenta _magenta=Color( $ffff00ff )
+		Return _magenta
+	End Function
+	
+	Rem
+	bbdoc: Returns a stock single texel 'flat' texture for normal mapping.
+	end rem
+	Function Flat:TTexture()
+		If Not _flat _flat=Color( $ff888888 )
+		Return _flat
+	End Function
+	
+	Private
+	
+	Field _seq:Int
+	Field _width:Int
+	Field _height:Int
+	Field _format:Int
+	Field _flags:Int
+	Field _data:TPixmap
+	Field _managed:TPixmap
+	
+	Field _glTexture:Int
+	Field _glFramebuffer:Int
+
+	Global _colors:TIntMap=New TIntMap'<TTexture>
+	Global _black:TTexture
+	Global _white:TTexture
+	Global _magenta:TTexture
+	Global _flat:TTexture
+
+	Method Validate()
+		
+		If _seq=graphicsSeq Return
+		
+		InitMojo2()
+	
+		_seq=graphicsSeq
+	
+		glGenTextures(1, Varptr _glTexture)
+		
+		glPushTexture2d _glTexture
+		
+		If _flags & Filter
+			glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR
+		Else
+			glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST
+		EndIf
+		If (_flags & Mipmap) And (_flags & Filter)
+			glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR
+		Else If _flags & Mipmap
+			glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_NEAREST
+		Else If _flags & Filter
+			glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR
+		Else
+			glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST
+		EndIf
+
+		If _flags & ClampS glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE
+		If _flags & ClampT glTexParameteri GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE
+
+		glTexImage2D GL_TEXTURE_2D,0,GL_RGBA,_width,_height,0,GL_RGBA,GL_UNSIGNED_BYTE,Null
+
+		glPopTexture2d
+		
+		If _flags & RenderTarget
+		
+			glGenFramebuffers(1, Varptr _glFramebuffer)
+			
+			glPushFramebuffer _glFramebuffer
+			
+			glBindFramebuffer GL_FRAMEBUFFER,_glFramebuffer
+			glFramebufferTexture2D GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,_glTexture,0
+			
+			If glCheckFramebufferStatus( GL_FRAMEBUFFER )<>GL_FRAMEBUFFER_COMPLETE Throw "Incomplete framebuffer"
+			
+			glPopFramebuffer
+			
+		EndIf
+		
+		If _managed Then
+
+			SetData( 0,0,_managed )
+			UpdateMipmaps()
+			
+		Else If _data
+		
+			SetData( 0,0,_data )
+			UpdateMipmaps()
+		
+		EndIf
+		
+	End Method
+	
+	Method LoadData( data:TPixmap )
+		glPushTexture2d GLTexture()
+		
+		glTexImage2D GL_TEXTURE_2D,0,GL_RGBA,_width,_height,0,GL_RGBA,GL_UNSIGNED_BYTE,data.pixels
+
+		glPopTexture2d
+		
+		UpdateMipmaps
+	End Method
+	
+End Type
+
+'***** Shader ****
+
+Public
+
+Type TGLUniform
+	Field name:String
+	Field location:Int
+	Field size:Int
+	Field kind:Int
+	
+	Method Create:TGLUniform( name:String,location:Int,size:Int,kind:Int )
+		Self.name=name
+		Self.location=location
+		Self.size=size
+		Self.kind=kind
+		Return Self
+	End Method
+	
+End Type
+
+Type TGLProgram
+	Field program:Int
+	'material uniforms
+	Field matuniforms:TGLUniform[]
+	'hard coded uniform locations
+	Field mvpMatrix:Int
+	Field mvMatrix:Int
+	Field clipPosScale:Int
+	Field globalColor:Int
+	Field AmbientLight:Int
+	Field fogColor:Int
+	Field lightColors:Int
+	Field lightVectors:Int
+	Field shadowTexture:Int
+	
+	Method Create:TGLProgram( program:Int,matuniforms:TGLUniform[] )
+		Self.program=program
+		Self.matuniforms=matuniforms
+		mvpMatrix=glGetUniformLocation( program,"ModelViewProjectionMatrix" )
+		mvMatrix=glGetUniformLocation( program,"ModelViewMatrix" )
+		clipPosScale=glGetUniformLocation( program,"ClipPosScale" )
+		globalColor=glGetUniformLocation( program,"GlobalColor" )
+		fogColor=glGetUniformLocation( program,"FogColor" )
+		AmbientLight=glGetUniformLocation( program,"AmbientLight" )
+		lightColors=glGetUniformLocation( program,"LightColors" )
+		lightVectors=glGetUniformLocation( program,"LightVectors" )
+		shadowTexture=glGetUniformLocation( program,"ShadowTexture" )
+		Return Self
+	End Method
+	
+	Method Bind()
+	
+		glUseProgram program
+		
+		If mvpMatrix<>-1 glUniformMatrix4fv mvpMatrix,1,False,rs_modelViewProjMatrix
+		If mvMatrix<>-1 glUniformMatrix4fv mvMatrix,1,False,rs_modelViewMatrix
+		If clipPosScale<>-1 glUniform4fv clipPosScale,1,rs_clipPosScale
+		If globalColor<>-1 glUniform4fv globalColor,1,rs_globalColor
+		If fogColor<>-1 glUniform4fv fogColor,1,rs_fogColor
+		If AmbientLight<>-1 glUniform4fv AmbientLight,1,rs_ambientLight
+		If lightColors<>-1 glUniform4fv lightColors,rs_numLights,rs_lightColors
+		If lightVectors<>-1 glUniform4fv lightVectors,rs_numLights,rs_lightVectors
+		glActiveTexture GL_TEXTURE0+7
+		If shadowTexture<>-1 And rs_shadowTexture
+			glBindTexture GL_TEXTURE_2D,rs_shadowTexture.GLTexture()
+			glUniform1i shadowTexture,7
+		Else
+			glBindTexture GL_TEXTURE_2D,TTexture.White().GLTexture()
+		End If
+		glActiveTexture GL_TEXTURE0
+	End Method
+	
+End Type
+
+Public
+
+Type TShader
+
+	Method Create:TShader( source:String )
+		Build source
+		Return Self
+	End Method
+	
+	Method DefaultMaterial:TMaterial()
+		If Not _defaultMaterial _defaultMaterial=New TMaterial.Create( Self )
+		Return _defaultMaterial
+	End Method
+	
+	Function FastShader:TShader()
+		Return _fastShader
+	End Function
+	
+	Rem
+	bbdoc: Returns a stock bump shader for drawing lit sprites with specular and normal maps.
+	about: 
+The following material properties are supported:
+
+| @Property			| @Type		| @Default
+| ColorTexture		| Texture	| White
+| SpecularTexture	| Texture	| Black
+| NormalTexture		| Texture	| Flat
+| AmbientColor		| Float[4]	| [0.0,0.0,0.0,1.0]
+| Roughness			| Float		| 0.5
+
+The shader b3d_Ambient value is computed by multiplying ColorTexture by AmbientColor.
+
+The shader b3d_Diffuse value is computed by multiplying ColorTexture by 1-AmbientColor.
+
+When loading materials that use the bump shader, diffuse, specular and normal maps can be given the following files names:
+
+| @Texture map		| @Valid paths
+| Diffuse			| (FILE).(EXT) ; (FILE)_d.(EXT) ; (FILE)_diff.(EXT) ; (FILE)_diffuse.(EXT)
+| Specular			| (FILE)_s.(EXT) ; (FILE)_spec.(EXT) ; (FILE)_specular.(EXT) ;(FILE)_SPECUALR.(EXT)
+| Normal			| (FILE)_n.(EXT) ; (FILE)_norm.(EXT) ; (FILE)_normal.(EXT) ; (FILE)_NORMALS.(EXT)
+
+Where (FILE) is the filename component of the path provided to Material.Load or Image.Load, and (EXT) is the file extension, eg: png, jpg.
+	end rem
+	Function BumpShader:TShader()
+		Return _bumpShader
+	End Function
+	
+	Rem
+	bbdoc: Returns a stock matte shader for drawing lit sprites with no specular or normal maps.
+	about: 
+The following material properties are supported:
+
+| @Property			| @Type		| @Default
+| ColorTexture		| Texture	| White
+| AmbientColor		| Float[4]	| [0.0,0.0,0.0,1.0]
+| Roughness			| Float		| 0.5
+	end rem
+	Function MatteShader:TShader()
+		Return _matteShader
+	End Function
+	
+	Rem
+	bbdoc: Returns a stock shadow shader for drawing shadows.
+	about: This shader simply writes 'black' to b3d_FragColor.
+	end rem
+	Function ShadowShader:TShader()
+		Return _shadowShader
+	End Function
+	
+	Rem
+	bbdoc: Returns a stock shader for drawing light textures and light mask effects.
+	about: 
+This shader performs a texture lookup, and writes the red component to b3d_FragColor.
+	
+The following material properties are supported:
+
+| @Property			| @Type		| @Default
+| ColorTexture		| Texture	| White
+	end rem	
+	Function LightMapShader:TShader()
+		Return _lightMapShader
+	End Function
+	
+	Rem
+	bbdoc: Returns the default shader used when a material is created with a 'Null' shader.
+	about: This is initially the #BumpShader, but can be modified using #SetDefaultShader.
+	end rem
+	Function DefaultShader:TShader()
+		Return _defaultShader
+	End Function
+	
+	Rem
+	bbdoc: Sets the default shader used when a material is created with a 'Null' shader.
+	end rem
+	Function SetDefaultShader( shader:TShader )
+		If Not shader shader=_bumpShader
+		_defaultShader=shader
+	End Function
+	
+	'Protected
+	
+	Rem
+	bbdoc: Compiles and links the shader.
+	about: Types that extend Shader must call this method at some point. This is usually done in the subclasses constructor.
+	end rem
+	Method Build( source:String )
+		_source=source
+		BuildInit
+	End Method
+	
+	Rem
+	bbdoc: Types that extend Shader must set defalut values for all valid shader parameters in this method.
+	end rem
+	Method OnInitMaterial( material:TMaterial )
+		material.SetTexture "ColorTexture",TTexture.White()
+	End Method
+	
+	Rem
+	bbdoc: Classes that extend Shader should load textures and other valid shader parameters from @path into @material in this method.
+	about: The interpretation of @path is completely up to the shader. The @texFlags parameter contains texture flag values that should be used for any textures loaded.
+	The @material parameter is an already initialized material.
+	This method should return @material if successful, or null on failure.
+	end rem
+	Method OnLoadMaterial:TMaterial( material:TMaterial,path:String,texFlags:Int )
+		Local texture:TTexture=TTexture.Load( path,4,texFlags )
+		If Not texture Return Null
+		material.SetTexture "ColorTexture",texture
+		If texture texture.Free
+		Return material
+	End Method
+	
+	'Private
+	
+	Const MAX_FLAGS:Int=8
+	
+	Field _seq:Int
+	Field _source:String
+	
+	Field _vsource:String
+	Field _fsource:String
+	Field _uniforms:TStringMap=New TStringMap
+	
+	Field _glPrograms:TGLProgram[MAX_LIGHTS+1]
+	
+	Field _defaultMaterial:TMaterial
+	
+	Method Bind()	
+		Local program:TGLProgram=GLProgram()
+		
+		If program=rs_program Return
+
+		rs_program=program
+		rs_material=Null
+		
+		program.Bind
+	End Method
+	
+	Method GLProgram:TGLProgram()
+	
+		If _seq<>graphicsSeq 
+			_seq=graphicsSeq
+			rs_program=Null
+			BuildInit
+		EndIf
+		
+		Return _glPrograms[rs_numLights]
+	End Method
+	
+	Method BuildProgram:TGLProgram( numLights:Int )
+
+		Local defs:String=""
+		defs:+"#define NUM_LIGHTS "+numLights+"~n"
+
+		Local vshader:Int=glCompile( GL_VERTEX_SHADER,defs+_vsource )
+		Local fshader:Int=glCompile( GL_FRAGMENT_SHADER,defs+_fsource )
+		
+		Local program:Int=glCreateProgram()
+		glAttachShader program,vshader
+		glAttachShader program,fshader
+		glDeleteShader vshader
+		glDeleteShader fshader
+		
+		glBindAttribLocation program,0,"Position"
+		glBindAttribLocation program,1,"Texcoord0"
+		glBindAttribLocation program,2,"Tangent"
+		glBindAttribLocation program,3,"Color"
+		
+		glLink program
+		
+		'enumerate program uniforms	
+		Local matuniforms:TGLUniform[] = New TGLUniform[0]
+		Local size:Int
+		Local kind:Int
+		Local buf:Byte[1024]
+		Local l:Int
+		Local n:Int
+		glGetProgramiv program,GL_ACTIVE_UNIFORMS, Varptr n
+		For Local i:Int=0 Until n
+			glGetActiveUniform program,i,1024,Varptr l,Varptr size, Varptr kind, buf
+			Local name:String = String.FromBytes(buf, l)
+			If _uniforms.Contains( name )
+				Local location:Int=glGetUniformLocation( program,name )
+				If location=-1 Continue  'IE fix...
+				matuniforms :+ [New TGLUniform.Create( name,location,size,kind )]
+'				Print name[0]+"->"+location
+			EndIf
+		Next
+		
+		Return New TGLProgram.Create( program,matuniforms )
+	
+	End Method
+	
+	Method BuildInit()
+		InitMojo2
+
+		Local p:TGlslParser=TGlslParser(New TGlslParser.Create( _source ))
+
+		Local vars:TMap=New TMap
+		
+		While p.Toke
+		
+			If p.CParse( "uniform" )
+				'uniform decl
+				Local ty:String=p.ParseType()
+				Local id:String=p.ParseIdent()
+				p.ParseToke ";"
+				_uniforms.Insert id, id
+'				Print "uniform "+ty+" "+id+";"
+				Continue
+			EndIf
+			
+			Local id:String=p.CParseIdent()
+			If id
+				If id.StartsWith( "gl_" )
+					vars.Insert "B3D_"+id.ToUpper(), ""
+				Else If id.StartsWith( "b3d_" ) 
+					vars.Insert id.ToUpper(), ""
+				EndIf
+				Continue
+			EndIf
+			
+			p.Bump
+		Wend
+		
+		Local vardefs:String=""
+		For Local v:String=EachIn vars.Keys()
+			vardefs:+"#define "+v+" 1~n"
+		Next
+		
+'		Print "Vardefs:";Print vardefs
+		
+		Local source:String=mainShader
+		Local i0:Int=source.Find( "//@vertex" )
+		If i0=-1 Throw "Can't find //@vertex chunk"
+		Local i1:Int=source.Find( "//@fragment" )
+		If i1=-1 Throw "Can't find //@fragment chunk"
+		
+		Local header:String=vardefs+source[..i0]
+		_vsource=header+source[i0..i1]
+		_fsource=header+source[i1..].Replace( "${SHADER}",_source )
+		
+		For Local numLights:Int=0 To MAX_LIGHTS
+		
+			_glPrograms[numLights]=BuildProgram( numLights )
+
+			If numLights Or vars.Contains( "B3D_DIFFUSE" ) Or vars.Contains( "B3D_SPECULAR" ) Continue
+			
+			For Local i:Int=1 To MAX_LIGHTS
+				_glPrograms[i]=_glPrograms[0]
+			Next
+			
+			Exit
+			
+		Next
+		
+		
+	End Method
+	
+End Type
+
+Type TBumpShader Extends TShader
+
+'	Method New( source:String )
+'		Super.New( source )
+'	End
+
+	'Protected
+	
+	Method OnInitMaterial( material:TMaterial )
+		material.SetTexture "ColorTexture",TTexture.White()
+		material.SetTexture "SpecularTexture",TTexture.Black()
+		material.SetTexture "NormalTexture",TTexture.Flat()
+		material.SetVector "AmbientColor",[1.0,1.0,1.0,1.0]
+		material.SetScalar "Roughness",1.0
+	End Method
+	
+	Method OnLoadMaterial:TMaterial( material:TMaterial,path:String,texFlags:Int )
+
+		Local format:Int = PF_RGBA8888
+	
+		Local ext:String = ExtractExt( path )
+		If ext path=StripExt( path ) Else ext="png"
+		
+		Local colorTex:TTexture=TTexture.Load( path+"."+ext,format,texFlags )
+		If Not colorTex colorTex=TTexture.Load( path+"_d."+ext,format,texFlags )
+		If Not colorTex colorTex=TTexture.Load( path+"_diff."+ext,format,texFlags )
+		If Not colorTex colorTex=TTexture.Load( path+"_diffuse."+ext,format,texFlags )
+		
+		Local specularTex:TTexture=TTexture.Load( path+"_s."+ext,format,texFlags )
+		If Not specularTex specularTex=TTexture.Load( path+"_spec."+ext,format,texFlags )
+		If Not specularTex specularTex=TTexture.Load( path+"_specular."+ext,format,texFlags )
+		If Not specularTex specularTex=TTexture.Load( path+"_SPECULAR."+ext,format,texFlags )
+		
+		Local normalTex:TTexture=TTexture.Load( path+"_n."+ext,format,texFlags )
+		If Not normalTex normalTex=TTexture.Load( path+"_norm."+ext,format,texFlags )
+		If Not normalTex normalTex=TTexture.Load( path+"_normal."+ext,format,texFlags )
+		If Not normalTex normalTex=TTexture.Load( path+"_NORMALS."+ext,format,texFlags )
+		
+		If Not colorTex And Not specularTex And Not normalTex Return Null
+
+		material.SetTexture "ColorTexture",colorTex
+		material.SetTexture "SpecularTexture",specularTex
+		material.SetTexture "NormalTexture",normalTex
+		
+		If specularTex Or normalTex
+			material.SetVector "AmbientColor",[0.0,0.0,0.0,1.0]
+			material.SetScalar "Roughness",.5
+		EndIf
+		
+		If colorTex colorTex.Free
+		If specularTex specularTex.Free
+		If normalTex normalTex.Free
+		
+		Return material
+	End Method
+	
+End	Type
+
+Type TMatteShader Extends TShader
+
+'	Method Create( source:String )
+'		Super.New( source )
+'	End
+	
+'	Protected
+	
+	Method OnInitMaterial( material:TMaterial )
+		material.SetTexture "ColorTexture",TTexture.White()
+		material.SetVector "AmbientColor",[0.0,0.0,0.0,1.0]
+		material.SetScalar "Roughness",1.0
+	End Method
+	
+End Type
+
+'***** Material *****
+
+Rem
+bbdoc: Materials contain shader parameters that map to shader uniforms variables when rendering. 
+End Rem
+Type TMaterial Extends TRefCounted
+
+	Rem
+	bbdoc: Creates a new material.
+	End Rem
+	Method Create:TMaterial( shader:TShader=Null )
+		InitMojo2
+		
+		If Not shader shader=_defaultShader
+		_shader=shader
+		_shader.OnInitMaterial( Self )
+		_inited=True
+		
+		Return Self
+	End Method
+	
+	Method Destroy()
+		For Local tex:TTexture=EachIn _textures
+			tex.Free()
+		Next
+	End Method
+	
+	Rem
+	bbdoc: Gets material shader.
+	End Rem
+	Method Shader:TShader() 
+		Return _shader
+	End Method
+	
+	Method ColorTexture:TTexture()
+		Return _colorTexture
+	End Method
+	
+	Method Width:Int()
+		If _colorTexture Return _colorTexture._width
+		Return 0
+	End Method
+	
+	Method Height:Int()
+		If _colorTexture Return _colorTexture._height
+		Return 0
+	End Method
+	
+	Rem
+	bbdoc: Sets float shader parameter.
+	End Rem
+	Method SetScalar( param:String,scalar:Float )
+		If _inited And Not _scalars.Contains( param ) Return
+		_scalars.Insert param,scalar
+	End Method
+	
+	Rem
+	bbdoc: Gets float shader parameter.
+	End Rem
+	Method GetScalar:Float( param:String,defValue:Float=1.0 )
+		If Not _scalars.Contains( param ) Return defValue
+		Return _scalars.ValueForKey( param )
+	End Method
+	
+	Rem
+	bbdoc: Sets vector shader parameter.
+	End Rem
+	Method SetVector( param:String,vector:Float[] )
+		If _inited And Not _vectors.Contains( param ) Return
+		_vectors.Insert param,vector
+	End Method
+	
+	Rem
+	bbdoc: Gets vector shader parameter.
+	End Rem
+	Method GetVector:Float[]( param:String,defValue:Float[]=[1.0,1.0,1.0,1.0] )
+		If Not _vectors.Contains( param ) Return defValue
+		Return _vectors.ValueForKey( param )
+	End Method
+	
+	Rem
+	bbdoc: Sets texture shader parameter.
+	End Rem
+	Method SetTexture( param:String,texture:TTexture )
+		If Not texture Return
+		If _inited And Not _textures.Contains( param ) Return
+		
+		Local old:TTexture=TTexture(_textures.ValueForKey( param ))
+		texture.Retain
+		_textures.Insert param,texture
+		If old old.Free
+		
+		If param="ColorTexture" _colorTexture=texture
+		
+	End Method
+	
+	Rem
+	bbdoc: Gets texture shader parameter.
+	End Rem
+	Method GetTexture:TTexture( param:String,defValue:TTexture=Null )
+		If Not _textures.Contains( param ) Return defValue
+		Return TTexture(_textures.ValueForKey( param ))
+	End Method
+	
+	Method Loading:Int()
+		Return False
+	End Method
+	
+	Rem
+	bbdoc: Loads a material.
+	about: If @shader is null, the TShader.DefaultShader is used.
+	End Rem
+	Function Load:TMaterial( path:String,texFlags:Int,shader:TShader )
+	
+		Local material:TMaterial=New TMaterial.Create( shader )
+
+		material=material.Shader.OnLoadMaterial( material,path,texFlags )
+		
+		Return material
+	End Function
+	
+	'Private
+	
+	Field _shader:TShader
+	Field _colorTexture:TTexture
+	Field _scalars:TStringFloatMap=New TStringFloatMap
+	Field _vectors:TStringMap=New TStringMap
+	Field _textures:TStringMap=New TStringMap
+	Field _inited:Int
+	
+	Method Bind:Int()
+	
+		_shader.Bind
+		
+		If rs_material=Self Return True
+		
+		rs_material=Self
+	
+		Local texid:Int=0
+		
+		For Local u:TGLUniform=EachIn rs_program.matuniforms
+			Select u.kind
+			Case GL_FLOAT
+				glUniform1f u.location,GetScalar( u.name )
+			Case GL_FLOAT_VEC4
+				glUniform4fv u.location,1,GetVector( u.name )
+			Case GL_SAMPLER_2D
+				Local tex:TTexture=GetTexture( u.name )
+'				If tex.Loading
+'					rs_material=Null 
+'					Exit
+'				Endif
+				glActiveTexture GL_TEXTURE0+texid
+				glBindTexture GL_TEXTURE_2D,tex.GLTexture()
+				glUniform1i u.location,texid
+				texid:+1
+			Default
+				Throw "Unsupported uniform type:"+u.kind 
+			End Select
+		Next
+
+		If texid glActiveTexture GL_TEXTURE0
+		
+		Return rs_material=Self
+	End Method
+	
+End Type
+
+'***** ShaderCaster *****
+
+Rem
+bbdoc: The ShadowCaster class provides support for simple 2d shadow rendering.
+about: Shadow casters are used by #Renderer objects when rendering layers. To render shadows, you will need to add
+shadow casters to the drawlists returned by ILayer.OnRenderLayer.
+A shadow caster can either be added to a drawlist using [[DrawList.AddShadowCaster]], or attached to images using [[Image.SetShadowCaster]]. Shadow casters attached to images are automatically added to drawlists when an image is drawn.
+A shadow caster contains a set of 2d vertices which describe the geometric shape of the object that casts a shadow. The vertices should describe a convex polygon.
+End Rem
+Type TShadowCaster
+
+	Method Create:TShadowCaster( verts:Float[] = Null,kind:Int = -1 )
+		If verts Then
+			_verts=verts
+		End If
+		If kind >= 0 Then
+			_kind=kind
+		End If
+		Return Self
+	End Method
+	
+	Rem
+	bbdoc: Set shadow caster vertices.
+	end rem
+	Method SetVertices( vertices:Float[] )
+		_verts=vertices
+	End Method
+	
+	Rem
+	bbdoc: Get shadow caster vertices.
+	end rem
+	Method Vertices:Float[]()
+		Return _verts
+	End Method
+
+	Method SetKind( kind:Int )
+		_kind=kind
+	End Method
+	
+	Method Kind:Int()
+		Return _kind
+	End Method
+	
+	'Private
+	
+	Field _verts:Float[]
+	Field _kind:Int
+	
+End Type
+
+'***** Image *****
+
+Rem
+bbdoc: An image is a rectangular area of pixels within a material, that can be drawn using one of the [[DrawList.DrawImage]] methods.
+about: You can create a new image using one of the [[Image.Create]] methods, or load an image from file using [[Image.Load|Image.Load]].
+An image also has a handle, an offset within the image that represents it's origin whan it is drawn. Image handles are specified in fractional values, where 0,0 is the top-left of an image, 1,1 is the bottom-right and .5,.5 is the center.
+end rem
+Type TImage
+
+	Const Filter:Int=TTexture.Filter
+	Const Mipmap:Int=TTexture.Mipmap
+	Const Managed:Int=TTexture.Managed
+	
+	Method Create:TImage( width:Int,height:Int,xhandle:Float=.5,yhandle:Float=.5,flags:Int=TImage.Filter )
+		flags:&_flagsMask
+		Local texture:TTexture=New TTexture.Create( width,height,PF_RGBA8888,flags|TTexture.ClampST|TTexture.RenderTarget )
+		_material=New TMaterial.Create( _fastShader )
+		_material.SetTexture "ColorTexture",texture
+		_width=width
+		_height=height
+		SetHandle xhandle,yhandle
+		Return Self
+	End Method
+	
+	Method CreateImage:TImage( image:TImage,x:Int,y:Int,width:Int,height:Int,xhandle:Float=.5,yhandle:Float=.5 )
+		_material=image._material
+		_material.Retain
+		_x=image._x+x
+		_y=image._y+y
+		_width=width
+		_height=height
+		SetHandle xhandle,yhandle
+		Return Self
+	End Method
+	
+	Method CreateMaterial:TImage( material:TMaterial,xhandle:Float=.5,yhandle:Float=.5 )
+		Local texture:TTexture=material.ColorTexture()
+		If Not texture Throw "Material has no ColorTexture"
+		_material=material
+		_material.Retain
+		_width=_material.Width
+		_height=_material.Height
+		SetHandle xhandle,yhandle
+		Return Self
+	End Method
+
+	Method CreateMaterialSize:TImage( material:TMaterial,x:Int,y:Int,width:Int,height:Int,xhandle:Float=.5,yhandle:Float=.5 )
+		Local texture:TTexture=material.ColorTexture()
+		If Not texture Throw "Material has no ColorTexture"
+		_material=material
+		_material.Retain
+		_x=x
+		_y=y
+		_width=width
+		_height=height
+		SetHandle xhandle,yhandle
+		Return Self
+	End Method
+
+	Method Discard()
+		If _material _material.Free
+		_material=Null
+	End Method
+	
+	Method Material:TMaterial()
+		Return _material
+	End Method
+	
+	Method X0:Float()
+		Return _x0
+	End Method
+	
+	Method Y0:Float()
+		Return _y0
+	End Method
+	
+	Method X1:Float()
+		Return _x1
+	End Method
+	
+	Method Y1:Float()
+		Return _y1
+	End Method
+	
+	Method Width:Int()
+		Return _width
+	End Method
+	
+	Method Height:Int()
+		Return _height
+	End Method
+	
+	Method HandleX:Float()
+		Return -_x0/(_x1-_x0)
+	End Method
+	
+	Method HandleY:Float()
+		Return -_y0/(_y1-_y0)
+	End Method
+	
+	Method WritePixels( x:Int,y:Int,width:Int,height:Int,data:TPixmap,dataOffset:Int=0,dataPitch:Int=0 )
+		_material.ColorTexture().WritePixels( x+_x,y+_y,width,height,data,dataOffset,dataPitch )
+	End Method
+	
+	Method SetHandle( xhandle:Float,yhandle:Float )
+		_x0=Float(_width)*-xhandle
+		_x1=Float(_width)*(1-xhandle)
+		_y0=Float(_height)*-yhandle
+		_y1=Float(_height)*(1-yhandle)
+		_s0=Float(_x)/Float(_material.Width)
+		_t0=Float(_y)/Float(_material.Height)
+		_s1=Float(_x+_width)/Float(_material.Width)
+		_t1=Float(_y+_height)/Float(_material.Height)
+	End Method
+	
+	Method SetShadowCaster( shadowCaster:TShadowCaster )
+		_caster=shadowCaster
+	End Method
+	
+	Method ShadowCaster:TShadowCaster()
+		Return _caster
+	End Method
+	
+	Method Loading:Int()
+		Return _material.Loading()
+	End Method
+	
+	Function ImagesLoading:Int()
+		Return TTexture.TexturesLoading>0
+	End Function
+	
+	Function SetFlagsMask( mask:Int )
+		_flagsMask=mask
+	End Function
+	
+	Function FlagsMask:Int()
+		Return _flagsMask
+	End Function
+	
+	Function Load:TImage( path:String,xhandle:Float=.5,yhandle:Float=.5,flags:Int=TImage.Filter|TImage.Mipmap,shader:TShader=Null )
+		flags:&_flagsMask
+	
+		Local material:TMaterial=TMaterial.Load( path,flags|TTexture.ClampST,shader )
+		If Not material Return Null
+
+		Return New TImage.CreateMaterial( material,xhandle,yhandle )
+	End Function
+	
+	Function LoadFrames:TImage[]( path:String,numFrames:Int,padded:Int=False,xhandle:Float=.5,yhandle:Float=.5,flags:Int=TImage.Filter|TImage.Mipmap,shader:TShader=Null )
+		flags:&_flagsMask
+	
+		Local material:TMaterial=TMaterial.Load( path,flags|TTexture.ClampST,shader )
+		If Not material Return Null
+		
+		Local cellWidth:Int=material.Width/numFrames
+		Local cellHeight:Int=material.Height
+		
+		Local x:Int=0
+		Local width:Int=cellWidth
+		If padded Then
+			x:+1
+			width:-2
+		End If
+		
+		Local frames:TImage[]=New TImage[numFrames]
+		
+		For Local i:Int=0 Until numFrames
+			frames[i]=New TImage.CreateMaterialSize( material,i*cellWidth+x,0,width,cellHeight,xhandle,yhandle )
+		Next
+		
+		Return frames
+	End Function
+	
+	'Private
+	
+	Global _flagsMask:Int=Filter|Mipmap|Managed
+	
+	Field _material:TMaterial
+	Field _x:Int,_y:Int,_width:Int,_height:Int
+	Field _x0:Float=-1,_y0:Float=-1,_x1:Float=1,_y1:Float=1
+	Field _s0:Float=0 ,_t0:Float=0 ,_s1:Float=1,_t1:Float=1
+
+	Field _caster:TShadowCaster
+	
+'	Method SetFrame( x0:Float,y0:Float,x1:Float,y1:Float,s0:Float,t0:Float,s1:Float,t1:Float )
+'		_x0=x0;_y0=y0;_x1=x1;_y1=y1
+'		_s0=s0;_t0=t0;_s1=s1;_t1=t1
+'	End
+	
+End Type
+
+'***** Font *****
+
+Type TGlyph
+	Field image:TImage
+	Field char:Int
+	Field x:Int
+	Field y:Int
+	Field width:Int
+	Field height:Int
+	Field advance:Float
+	
+	Method Create:TGlyph( image:TImage,char:Int,x:Int,y:Int,width:Int,height:Int,advance:Float )
+		Self.image=image
+		Self.char=char
+		Self.x=x
+		Self.y=y
+		Self.width=width
+		Self.height=height
+		Self.advance=advance
+		Return Self
+	End Method
+End Type
+
+Rem
+bbdoc: Provides support for simple fixed width bitmap fonts.
+End Rem
+Type TFont
+
+	Method Create:TFont( glyphs:TGlyph[],firstChar:Int,height:Float )
+		_glyphs=glyphs
+		_firstChar=firstChar
+		_height=height
+		Return Self
+	End Method
+
+	Method GetGlyph:TGlyph( char:Int )
+		Local i:Int=char-_firstChar
+		If i>=0 And i<_glyphs.Length Return _glyphs[i]
+		Return Null
+	End Method
+	
+	Rem
+	bbdoc: Gets width of @text drawn in this font.
+	End Rem
+	Method TextWidth:Float( Text:String )
+		Local w:Float=0.0
+		For Local char:Int=EachIn Text
+			Local glyph:TGlyph=GetGlyph( char )
+			If Not glyph Continue
+			w:+glyph.advance
+		Next
+		Return w
+	End Method
+
+	Rem
+	bbdoc: Gets height of @text drawn in this font.
+	End Rem
+	Method TextHeight:Float( Text:String )
+		Return _height
+	End Method
+	
+	Rem
+	bbdoc: Loads a fixed width font from @path.
+	about: Glyphs should be laid out horizontally within the source image.
+	If @padded is true, then each glyph is assumed to have a transparent one pixel padding border around it.
+	End Rem
+	Function Load:TFont( path:String,firstChar:Int,numChars:Int,padded:Int )
+
+		Local image:TImage=TImage.Load( path )
+		If Not image Return Null
+		
+		Local cellWidth:Int=image.Width/numChars
+		Local cellHeight:Int=image.Height
+		Local glyphX:Int=0,glyphY:Int=0,glyphWidth:Int=cellWidth,glyphHeight:Int=cellHeight
+		If padded glyphX:+1;glyphY:+1;glyphWidth:-2;glyphHeight:-2
+
+		Local w:Int=image.Width/cellWidth
+		Local h:Int=image.Height/cellHeight
+
+		Local glyphs:TGlyph[]=New TGlyph[numChars]
+		
+		For Local i:Int=0 Until numChars
+			Local y:Int=i / w
+			Local x:Int=i Mod w
+			Local glyph:TGlyph=New TGlyph.Create( image,firstChar+i,x*cellWidth+glyphX,y*cellHeight+glyphY,glyphWidth,glyphHeight,glyphWidth )
+			glyphs[i]=glyph
+		Next
+		
+		Return New TFont.Create( glyphs,firstChar,glyphHeight )
+	
+	End Function
+	
+	Function LoadSize:TFont( path:String,cellWidth:Int,cellHeight:Int,glyphX:Int,glyphY:Int,glyphWidth:Int,glyphHeight:Int,firstChar:Int,numChars:Int )
+
+		Local image:TImage=TImage.Load( path )
+		If Not image Return Null
+
+		Local w:Int=image.Width/cellWidth
+		Local h:Int=image.Height/cellHeight
+
+		Local glyphs:TGlyph[]=New TGlyph[numChars]
+		
+		For Local i:Int=0 Until numChars
+			Local y:Int=i / w
+			Local x:Int=i Mod w
+			Local glyph:TGlyph=New TGlyph.Create( image,firstChar+i,x*cellWidth+glyphX,y*cellHeight+glyphY,glyphWidth,glyphHeight,glyphWidth )
+			glyphs[i]=glyph
+		Next
+		
+		Return New TFont.Create( glyphs,firstChar,glyphHeight )
+	End Function
+	
+	'Private
+	
+	Field _glyphs:TGlyph[]
+	Field _firstChar:Int
+	Field _height:Float
+	
+End Type
+
+'***** DrawList *****
+
+Type TDrawOp
+'	Field shader:Shader
+	Field material:TMaterial
+	Field blend:Int
+	Field order:Int
+	Field count:Int
+End Type
+
+Type TBlendMode
+	Const Opaque:Int=0
+	Const Alpha:Int=1
+	Const Additive:Int=2
+	Const Multiply:Int=3
+	Const Multiply2:Int=4
+End Type
+
+Rem
+bbdoc: A drawlist contains drawing state and a sequence of 2d drawing operations.
+about:
+You add drawing operations to a drawlist using any of the Draw methods. When a drawing operation is added, the current drawing state is captured by the drawing operation. Further changes to the drawing state will not affect drawing operations already in the drawlist.
+A [[Canvas]] extends [[DrawList]], and can be used to draw directly to the app window or an image. A drawlist can also be rendered to a canvas using [[Canvas.RenderDrawList]].
+A drawlist's drawing state consists of:
+| @Drawing state			| @Description
+| Current color			| [[SetColor]]
+| Current 2d matrix		| [[Translate]], [[Rotate]], [[Scale]], [[PushMatrix]], [[PopMatrix]]
+| Current blend mode		| [[SetBlendMode]]
+| Current font			| [[DrawText]]
+End Rem
+Type TDrawList
+
+	Method New()
+		InitMojo2
+		
+		_color = __colorArray
+		
+		SetFont Null
+		SetDefaultMaterial _fastShader.DefaultMaterial
+	End Method
+	
+	Method SetBlendMode( blend:Int )
+		_blend=blend
+	End Method
+	
+	Method BlendMode:Int()
+		Return _blend
+	End Method
+	
+	Method SetColor( r:Float,g:Float,b:Float,a:Float = -1 )
+		Local c:Float Ptr = _color
+		c[0]=r
+		c[1]=g
+		c[2]=b
+		If a >= 0
+			c[3]=a
+			_alpha=a*255
+		End If
+		_pmcolor=Int(_alpha) Shl 24 | Int(c[2]*_alpha) Shl 16 | Int(c[1]*_alpha) Shl 8 | Int(c[0]*_alpha)
+	End Method
+	
+	Method SetAlpha( a:Float )
+		_color[3]=a
+		_alpha=a*255
+		_pmcolor=Int(_alpha) Shl 24 | Int(_color[2]*_alpha) Shl 16 | Int(_color[1]*_alpha) Shl 8 | Int(_color[0]*_alpha)
+	End Method
+	
+	Method Color:Float[]()
+		Return [_color[0],_color[1],_color[2],_color[3]]
+	End Method
+	
+	Method GetColor( color:Float[] )
+		color[0]=_color[0]
+		color[1]=_color[1]
+		color[2]=_color[2]
+		If color.Length>3 color[3]=_color[3]
+	End Method
+	
+	Method Alpha:Float()
+		Return _color[3]
+	End Method
+	
+	Rem
+	bbdoc: Sets the current 2d matrix to the identity matrix.
+	about: Same as SetMatrix( 1,0,0,1,0,0 ).
+	end rem
+	Method ResetMatrix()
+		_ix=1;_iy=0
+		_jx=0;_jy=1
+		_tx=0;_ty=0
+	End Method
+	
+	Rem
+	bbdoc: Sets the current 2d matrix to the given matrix.
+	end rem
+	Method SetMatrix( ix:Float,iy:Float,jx:Float,jy:Float,tx:Float,ty:Float )
+		_ix=ix;_iy=iy
+		_jx=jx;_jy=jy
+		_tx=tx;_ty=ty
+	End Method
+	
+	Rem
+	bbdoc: Gets the current 2d matrix.
+	end rem
+	Method GetMatrix( matrix:Float[] )
+		matrix[0]=_ix
+		matrix[1]=_iy
+		matrix[2]=_jx
+		matrix[3]=_jy
+		matrix[4]=_tx
+		matrix[5]=_ty
+	End Method
+	
+	Rem
+	bbdoc: Multiplies the current 2d matrix by the given matrix.
+	end rem
+	Method Transform( ix:Float,iy:Float,jx:Float,jy:Float,tx:Float,ty:Float )
+		Local ix2:Float=ix*_ix+iy*_jx
+		Local iy2:Float=ix*_iy+iy*_jy
+		Local jx2:Float=jx*_ix+jy*_jx
+		Local jy2:Float=jx*_iy+jy*_jy
+		Local tx2:Float=tx*_ix+ty*_jx+_tx
+		Local ty2:Float=tx*_iy+ty*_jy+_ty
+		SetMatrix ix2,iy2,jx2,jy2,tx2,ty2
+	End Method
+
+	Rem
+	bbdoc: Translates the current 2d matrix.
+	end rem
+	Method Translate( tx:Float,ty:Float )
+		Transform 1,0,0,1,tx,ty
+	End Method
+	
+	Rem
+	bbdoc: Rotates the current 2d matrix.
+	end rem
+	Method Rotate( rz:Float )
+		Transform Cos( rz ),-Sin( rz ),Sin( rz ),Cos( rz ),0,0
+	End Method
+	
+	Rem
+	bbdoc: Scales the current 2d matrix.
+	end rem
+	Method Scale( sx:Float,sy:Float )
+		Transform sx,0,0,sy,0,0
+	End Method
+	
+	Rem
+	bbdoc: Translates and rotates (in that order) the current 2d matrix.
+	end rem
+	Method TranslateRotate( tx:Float,ty:Float,rz:Float )
+		Translate tx,ty
+		Rotate rz
+	End Method
+	
+	Rem
+	bbdoc: Rotates and scales (in that order) the current 2d matrix.
+	end rem
+	Method RotateScale( rz:Float,sx:Float,sy:Float )
+		Rotate rz
+		Scale sx,sy
+	End Method
+	
+	Method TranslateScale( tx:Float,ty:Float,sx:Float,sy:Float )
+		Translate tx,ty
+		Scale sx,sy
+	End Method
+	
+	Rem
+	bbdoc: Translates, rotates and scales (in that order) the current 2d matrix.
+	end rem
+	Method TranslateRotateScale( tx:Float,ty:Float,rz:Float,sx:Float,sy:Float )
+		Translate tx,ty
+		Rotate rz
+		Scale sx,sy
+	End Method
+	
+	Rem
+	bbdoc: Sets the maximum number of 2d matrices that can be pushed onto the matrix stack using @PushMatrix.
+	end rem
+	Method SetMatrixStackCapacity( capacity:Int )
+		'_matStack=_matStack.Resize( capacity*6 )
+		_matStack = _matStack[..capacity*6]
+		_matSp=0
+	End Method
+	
+	Rem
+	bbdoc: Gets the maximum number of 2d matrices that can be pushed onto the matrix stack using @PushMatrix.
+	end rem
+	Method MatrixStackCapacity:Int()
+		Return _matStack.Length/6
+	End Method
+	
+	Rem
+	bbdoc: Pushes the current 2d matrix on the 2d matrix stack.
+	end rem
+	Method PushMatrix()
+		_matStack[_matSp+0]=_ix;_matStack[_matSp+1]=_iy
+		_matStack[_matSp+2]=_jx;_matStack[_matSp+3]=_jy
+		_matStack[_matSp+4]=_tx;_matStack[_matSp+5]=_ty
+		_matSp:+6
+		If _matSp>=_matStack.Length _matSp:-_matStack.Length
+	End Method
+	
+	Rem
+	bbdoc: Pops the current 2d matrix from the 2d matrix stack.
+	end rem
+	Method PopMatrix()
+		_matSp:-6
+		If _matSp<0 _matSp:+_matStack.Length
+		_ix=_matStack[_matSp+0]
+		_iy=_matStack[_matSp+1]
+		_jx=_matStack[_matSp+2]
+		_jy=_matStack[_matSp+3]
+		_tx=_matStack[_matSp+4]
+		_ty=_matStack[_matSp+5]
+	End Method
+	
+	Rem
+	bbdoc: Sets current font for use with #DrawText.
+	about: 	If @font is null, a default font is used.
+	end rem
+	Method SetFont( font:TFont )
+		If Not font font=defaultFont
+		_font=font
+	End Method
+	
+	Rem
+	bbdoc: Gets the current font.
+	end rem
+	Method Font:TFont()
+		Return _font
+	End Method
+	
+	Rem
+	bbdoc: Sets the default material used for drawing operations that use a null material.
+	end rem
+	Method SetDefaultMaterial( material:TMaterial )
+		_defaultMaterial=material
+	End Method
+	
+	Rem
+	bbdoc: Returns the current default material.
+	end rem
+	Method DefaultMaterial:TMaterial()
+		Return _defaultMaterial
+	End Method
+	
+	Rem
+	bbdoc: Draws a point at @x0,@y0.
+	about: If @material is null, the current default material is used.
+	end rem
+	Method DrawPoint( x0:Float,y0:Float,material:TMaterial=Null,s0:Float=0,t0:Float=0 )
+		BeginPrim material,1
+		PrimVert x0+.5,y0+.5,s0,t0
+	End Method
+	
+	Rem
+	bbdoc: Draws a line from @x0,@y0 to @x1,@y1.
+	about: If @material is null, the current default material is used.
+	end rem
+	Method DrawLine( x0:Float,y0:Float,x1:Float,y1:Float,material:TMaterial=Null,s0:Float=0,t0:Float=0,s1:Float=1,t1:Float=0 )
+		BeginPrim material,2
+		PrimVert x0+.5,y0+.5,s0,t0
+		PrimVert x1+.5,y1+.5,s1,t1
+	End Method
+	
+	Rem
+	bbdoc: Draw a triangle.
+	about: If @material is null, the current default material is used.
+	End Rem
+	Method DrawTriangle( x0:Float,y0:Float,x1:Float,y1:Float,x2:Float,y2:Float,material:TMaterial=Null,s0:Float=.5,t0:Float=0,s1:Float=1,t1:Float=1,s2:Float=0,t2:Float=1 )
+		BeginPrim material,3
+		PrimVert x0,y0,s0,t0
+		PrimVert x1,y1,s1,t1
+		PrimVert x2,y2,s2,t2
+	End Method
+	
+	Rem
+	bbdoc: Draw a quad.
+	about: If @material is null, the current default material is used.
+	end rem
+	Method DrawQuad( x0:Float,y0:Float,x1:Float,y1:Float,x2:Float,y2:Float,x3:Float,y3:Float,material:TMaterial=Null,s0:Float=.5,t0:Float=0,s1:Float=1,t1:Float=1,s2:Float=0,t2:Float=1 )
+		BeginPrim material,4
+		PrimVert x0,y0,s0,t0
+		PrimVert x1,y1,s1,t1
+		PrimVert x2,y2,s2,t2
+		PrimVert x3,y3,s2,t2
+	End Method
+	
+	Rem
+	bbdoc: Draw an oval in the given rectangle.
+	about: If @material is null, the current default material is used.
+	end rem
+	Method DrawOval( x:Float,y:Float,width:Float,height:Float,material:TMaterial=Null )
+		Local xr:Float=width/2.0
+		Local yr:Float=height/2.0
+		
+		Local dx_x:Float=xr*_ix
+		Local dx_y:Float=xr*_iy
+		Local dy_x:Float=yr*_jx
+		Local dy_y:Float=yr*_jy
+		Local dx:Float=Sqr( dx_x*dx_x+dx_y*dx_y )
+		Local dy:Float=Sqr( dy_x*dy_x+dy_y*dy_y )
+
+		Local n:Int=Int( dx+dy )
+		If n<12 
+			n=12 
+		Else If n>MAX_VERTICES
+			n=MAX_VERTICES
+		Else
+			n:&~3
+		EndIf
+		
+		Local x0:Float=x+xr
+		Local y0:Float=y+yr
+		
+		BeginPrim material,n
+		
+		For Local i:Int=0 Until n
+			Local th:Float=i*360.0/n
+			Local px:Float=x0+Cos( th ) * xr
+			Local py:Float=y0+Sin( th ) * yr
+			PrimVert px,py,0,0
+		Next
+	End Method
+	
+	Rem
+	bbdoc: Draw an ellipse at @x, @y with radii @xRadius, @yRadius.
+	about: If @material is null, the current default material is used.
+	end rem
+	Method DrawEllipse( x:Float,y:Float,xr:Float,yr:Float,material:TMaterial=Null )
+		DrawOval x-xr,y-yr,xr*2,yr*2,material
+	End Method
+	
+	Rem
+	bbdoc: Draw a circle at @x, @y with radius @radius.
+	about: If @material is null, the current default material is used.
+	end rem
+	Method DrawCircle( x:Float,y:Float,r:Float,material:TMaterial=Null )
+		DrawOval x-r,y-r,r*2,r*2,material
+	End Method
+	
+	Method DrawPoly( vertices:Float[],material:TMaterial=Null )
+	
+		Local n:Int=vertices.Length/2
+		If n<3 Or n>MAX_VERTICES Return
+	
+		BeginPrim material,n
+
+		For Local i:Int=0 Until n
+			PrimVert vertices[i*2],vertices[i*2+1],0,0
+		Next
+	End Method
+	
+	Rem
+	bbdoc: Draw a batch of primtives.
+	about:
+	@order is the number of vertices for each primitive, eg: 1 for points, 2 for lines, 3 for triangles etc.
+	@count is the number of primitives to draw.
+	The @vertices array contains x,y vertex data, and must be at least @count \* @order \* 2 long.
+	If @material is null, the current default material is used.
+	end rem
+	Method DrawPrimitives( order:Int,count:Int,vertices:Float[],material:TMaterial=Null )
+	
+		BeginPrims material,order,count
+		Local p:Int=0
+		For Local i:Int=0 Until count
+			For Local j:Int=0 Until order
+				PrimVert vertices[p],vertices[p+1],0,0
+				p:+2
+			Next
+		Next
+	End Method
+	
+	Rem
+	bbdoc: Draw a batch of primtives.
+	about: 
+	@order is the number of vertices for each primitive, eg: 1 for points, 2 for lines, 3 for triangles etc.
+	@count is the number of primitives to draw.
+	The @vertices array contains x,y vertex data, and must be at least @count \* @order \* 2 long.
+	The @texcoords array contains s,t texture coordinate data, and must be at least @count \* @order \* 2 long.
+	If @material is null, the current default material is used.
+	end rem
+	Method DrawPrimitivesCoords( order:Int,count:Int,vertices:Float[],texcoords:Float[],material:TMaterial=Null )
+	
+		BeginPrims material,order,count
+		Local p:Int=0
+		For Local i:Int=0 Until count
+			For Local j:Int=0 Until order
+				PrimVert vertices[p],vertices[p+1],texcoords[p],texcoords[p+1]
+				p:+2
+			Next
+		Next
+	End Method
+	
+	Rem
+	bbdoc: Draw a batch of indexed primtives.
+	about:
+	@order is the number of vertices for each primitive, eg: 1 for points, 2 for lines, 3 for triangles etc.
+	@count is the number of primitives to draw.
+	The @vertices array contains x,y vertex data.
+	The @indices array contains vertex indices, and must be at least @count \* @order long.
+	If @material is null, the current default material is used.
+	end rem
+	Method DrawIndexedPrimitives( order:Int,count:Int,vertices:Float[],indices:Int[],material:TMaterial=Null )
+	
+		BeginPrims material,order,count
+		Local p:Int=0
+		For Local i:Int=0 Until count
+			For Local j:Int=0 Until order
+				Local k:Int=indices[p+j]*2
+				PrimVert vertices[k],vertices[k+1],0,0
+			Next
+			p:+order
+		Next
+	
+	End Method
+	
+	Method DrawIndexedPrimitivesCoords( order:Int,count:Int,vertices:Float[],texcoords:Float[],indices:Int[],material:TMaterial=Null )
+	
+		BeginPrims material,order,count
+		Local p:Int=0
+		For Local i:Int=0 Until count
+			For Local j:Int=0 Until order
+				Local k:Int=indices[p+j]*2
+				PrimVert vertices[k],vertices[k+1],texcoords[k],texcoords[k+1]
+			Next
+			p:+order
+		Next
+	
+	End Method
+	
+	Rem
+	bbdoc: Draws a rect from @x,@y to @x+@width,@y+@height.
+	about: If @material is null, the current default material is used.
+	end rem
+	Method DrawRect( x0:Float,y0:Float,width:Float,height:Float,material:TMaterial=Null,s0:Float=0,t0:Float=0,s1:Float=1,t1:Float=1 )
+		Local x1:Float=x0+width
+		Local y1:Float=y0+height
+		BeginPrim material,4
+		PrimVert x0,y0,s0,t0
+		PrimVert x1,y0,s1,t0
+		PrimVert x1,y1,s1,t1
+		PrimVert x0,y1,s0,t1
+	End Method
+	
+	Rem
+	bbdoc: Draws a rect from @x,@y to @x+@width,@y+@height filled with @image.
+	about: The image's handle is ignored.
+	end rem
+	Method DrawRectImage( x0:Float,y0:Float,width:Float,height:Float,image:TImage )
+		DrawRect x0,y0,width,height,image._material,image._s0,image._t0,image._s1,image._t1
+	End Method
+	
+	Rem
+	bbdoc: Draws a rect at @x,@y filled with the given subrect of @image.
+	about: The image's handle is ignored.
+	end rem
+	Method DrawRectImageSource( x:Float,y:Float,image:TImage,sourceX:Int,sourceY:Int,sourceWidth:Int,sourceHeight:Int )
+		DrawRectImageSourceSize( x,y,sourceWidth,sourceHeight,image,sourceX,sourceY,sourceWidth,sourceHeight )
+	End Method
+
+	Rem
+	bbdoc: Draws a rect from @x,@y to @x+@width,@y+@height filled with the given subrect of @image.
+	about: The image's handle is ignored.
+	end rem
+	Method DrawRectImageSourceSize( x0:Float,y0:Float,width:Float,height:Float,image:TImage,sourceX:Int,sourceY:Int,sourceWidth:Int,sourceHeight:Int )
+		Local material:TMaterial=image._material
+		Local s0:Float=Float(image._x+sourceX)/Float(material.Width)
+		Local t0:Float=Float(image._y+sourceY)/Float(material.Height)
+		Local s1:Float=Float(image._x+sourceX+sourceWidth)/Float(material.Width)
+		Local t1:Float=Float(image._y+sourceY+sourceHeight)/Float(material.Height)
+		DrawRect x0,y0,width,height,material,s0,t0,s1,t1
+	End Method
+	
+	'gradient rect - kinda hacky, but doesn't slow anything else down
+	Method DrawGradientRect( x0:Float,y0:Float,width:Float,height:Float,r0:Float,g0:Float,b0:Float,a0:Float,r1:Float,g1:Float,b1:Float,a1:Float,axis:Int )
+	
+		r0:*_color[0];g0:*_color[1];b0:*_color[2];a0:*_alpha
+		r1:*_color[0];g1:*_color[1];b1:*_color[2];a1:*_alpha
+		
+		Local pm0:Int=Int( a0 ) Shl 24 | Int( b0*a0 ) Shl 16 | Int( g0*a0 ) Shl 8 | Int( r0*a0 )
+		Local pm1:Int=Int( a1 ) Shl 24 | Int( b1*a0 ) Shl 16 | Int( g1*a0 ) Shl 8 | Int( r1*a0 )
+		
+		Local x1:Float=x0+width
+		Local y1:Float=y0+height
+		Local s0:Float=0.0
+		Local t0:Float=0.0
+		Local s1:Float=1.0
+		Local t1:Float=1.0
+		
+		BeginPrim Null,4
+
+		Local pmcolor:Int=_pmcolor
+		
+		BeginPrim Null,4
+		
+		Select axis
+		Case 0	'left->right
+			_pmcolor=pm0
+			PrimVert x0,y0,s0,t0
+			_pmcolor=pm1
+			PrimVert x1,y0,s1,t0
+			PrimVert x1,y1,s1,t1
+			_pmcolor=pm0
+			PrimVert x0,y1,s0,t1
+		Default	'top->bottom
+			_pmcolor=pm0
+			PrimVert x0,y0,s0,t0
+			PrimVert x1,y0,s1,t0
+			_pmcolor=pm1
+			PrimVert x1,y1,s1,t1
+			PrimVert x0,y1,s0,t1
+		End Select
+		
+		_pmcolor=pmcolor
+	End Method
+	
+	Method DrawImageImage( image:TImage )
+		BeginPrim image._material,4
+		PrimVert image._x0,image._y0,image._s0,image._t0
+		PrimVert image._x1,image._y0,image._s1,image._t0
+		PrimVert image._x1,image._y1,image._s1,image._t1
+		PrimVert image._x0,image._y1,image._s0,image._t1
+		If image._caster AddShadowCaster image._caster
+	End Method
+	
+	Method DrawImage( image:TImage,tx:Float,ty:Float )
+		PushMatrix
+		Translate tx,ty
+		DrawImageImage image
+		PopMatrix
+	End Method
+
+	Method DrawImageXYZ( image:TImage,tx:Float,ty:Float,rz:Float )
+		PushMatrix
+		TranslateRotate tx,ty,rz
+		DrawImageImage image
+		PopMatrix
+	End Method
+	
+	Method DrawImageXYZS( image:TImage,tx:Float,ty:Float,rz:Float,sx:Float,sy:Float )
+		PushMatrix
+		TranslateRotateScale tx,ty,rz,sx,sy
+		DrawImageImage image
+		PopMatrix
+	End Method
+	
+	Rem
+	bbdoc: Draws @text at @x,@y in the current font.
+	end rem
+	Method DrawText( Text:String,x:Float,y:Float,xhandle:Float=0,yhandle:Float=0 )
+		x:-_font.TextWidth( Text )*xhandle
+		y:-_font.TextHeight( Text )*yhandle
+		For Local char:Int=EachIn Text
+			Local glyph:TGlyph=_font.GetGlyph( char )
+			If Not glyph Continue
+			DrawRectImageSource x,y,glyph.image,glyph.x,glyph.y,glyph.width,glyph.height
+			x:+glyph.advance
+		Next
+	End Method
+	
+	Rem
+	bbdoc: Draws a shadow volume.
+	end rem
+	Method DrawShadow:Int( lx:Float,ly:Float,x0:Float,y0:Float,x1:Float,y1:Float )
+	
+		Local ext:Int=1024
+	
+		Local dx:Float=x1-x0
+		Local dy:Float=y1-y0
+		Local d0:Float=Sqr( dx*dx+dy*dy )
+		Local nx:Float=-dy/d0
+		Local ny:Float=dx/d0
+		Local pd:Float=-(x0*nx+y0*ny)
+		
+		Local d:Float=lx*nx+ly*ny+pd
+		If d<0 Return False
+
+		Local x2:Float=x1-lx
+		Local y2:Float=y1-ly
+		'Local d2:Float=ext/Sqr( x2*x2+y2*y2 )
+		x2=lx+x2*ext;y2=ly+y2*ext
+		
+		Local x3:Float=x0-lx
+		Local y3:Float=y0-ly
+		'Local d3:Float=ext/Sqr( x3*x3+y3*y3 )
+		x3=lx+x3*ext;y3=ly+y3*ext
+		
+		Local x4:Float=(x2+x3)/2-lx
+		Local y4:Float=(y2+y3)/2-ly
+		'Local d4:Float=ext/Sqr( x4*x4+y4*y4 )
+		x4=lx+x4*ext;y4=ly+y4*ext
+		
+		DrawTriangle x0,y0,x4,y4,x3,y3
+		DrawTriangle x0,y0,x1,y1,x4,y4
+		DrawTriangle x1,y1,x2,y2,x4,y4
+		
+		Return True
+	End Method
+	
+	Rem
+	bbdoc: Draws multiple shadow volumes.
+	end rem
+	Method DrawShadows( x0:Float,y0:Float,drawList:TDrawList )
+	
+		Local lx:Float= x0 * _ix + y0 * _jx + _tx
+		Local ly:Float= x0 * _iy + y0 * _jy + _ty
+
+		Local verts:Float[]=drawList._casterVerts.Data
+		Local v0:Int=0
+		
+		For Local i:Int=0 Until drawList._casters.Length
+		
+			Local caster:TShadowCaster=drawList._casters.Get( i )
+			Local n:Int=caster._verts.Length
+			
+			Select caster._kind
+			Case 0	'closed loop
+				Local x0:Float=verts[v0+n-2]
+				Local y0:Float=verts[v0+n-1]
+				For Local i:Int=0 Until n-1 Step 2
+					Local x1:Float=verts[v0+i]
+					Local y1:Float=verts[v0+i+1]
+					DrawShadow( lx,ly,x0,y0,x1,y1 )
+					x0=x1
+					y0=y1
+				Next
+			Case 1	'open loop
+			Case 2	'edge soup
+			End Select
+			
+			v0:+n
+		Next
+		
+	End Method
+	
+	Rem
+	bbdoc: Adds a shadow caster to the drawlist.
+	end rem
+	Method AddShadowCaster( caster:TShadowCaster )
+		_casters.Push caster
+		Local verts:Float[]=caster._verts
+		For Local i:Int=0 Until verts.Length-1 Step 2
+			Local x0:Float=verts[i]
+			Local y0:Float=verts[i+1]
+			_casterVerts.Push x0*_ix+y0*_jx+_tx
+			_casterVerts.Push x0*_iy+y0*_jy+_ty
+		Next
+	End Method
+	
+	Rem
+	bbdoc: Adds a shadow caster to the drawlist at @tx,@ty.
+	end rem
+	Method AddShadowCasterXY( caster:TShadowCaster,tx:Float,ty:Float )
+		PushMatrix
+		Translate tx,ty
+		AddShadowCaster caster
+		PopMatrix
+	End Method
+	
+	Method AddShadowCasterXYZ( caster:TShadowCaster,tx:Float,ty:Float,rz:Float )
+		PushMatrix
+		TranslateRotate tx,ty,rz
+		AddShadowCaster caster
+		PopMatrix
+	End Method
+	
+	Method AddShadowCasterXYZS( caster:TShadowCaster,tx:Float,ty:Float,rz:Float,sx:Float,sy:Float )
+		PushMatrix
+		TranslateRotateScale tx,ty,rz,sx,sy
+		AddShadowCaster caster
+		PopMatrix
+	End Method
+	
+	Method IsEmpty:Int()
+		Return _next=0
+	End Method
+	
+	Method Compact()
+		If _data.Size()=_next Return
+		Local data:TBank=New TBank.Create( _next )
+		'_data.CopyBytes 0,data,0,_next
+		'_data.Discard
+		MemCopy(data._buf,_data._buf,_next)
+		_data=data
+	End Method
+	
+	Method RenderOp( op:TDrawOp,index:Int,count:Int )
+	
+		If Not op.material.Bind() Return
+		
+		If op.blend<>rs_blend
+			rs_blend=op.blend
+			Select rs_blend
+			Case TBlendMode.Opaque
+				glDisable GL_BLEND
+			Case TBlendMode.Alpha
+				glEnable GL_BLEND
+				glBlendFunc GL_ONE,GL_ONE_MINUS_SRC_ALPHA
+			Case TBlendMode.Additive
+				glEnable GL_BLEND
+				glBlendFunc GL_ONE,GL_ONE
+			Case TBlendMode.Multiply
+				glEnable GL_BLEND
+				glBlendFunc GL_DST_COLOR,GL_ONE_MINUS_SRC_ALPHA
+			Case TBlendMode.Multiply2
+				glEnable GL_BLEND
+				glBlendFunc GL_DST_COLOR,GL_ZERO
+			End Select
+		End If
+		
+		Select op.order
+		Case 1
+			glDrawArrays GL_POINTS,index,count
+		Case 2
+			glDrawArrays GL_LINES,index,count
+		Case 3
+			glDrawArrays GL_TRIANGLES,index,count
+		Case 4
+			glDrawElements GL_TRIANGLES,count/4*6,GL_UNSIGNED_SHORT,(index/4*6 + (index&3)*MAX_QUAD_INDICES)*2
+		Default
+			Local j:Int=0
+			While j<count
+				glDrawArrays GL_TRIANGLE_FAN,index+j,op.order
+				j:+op.order
+			Wend
+		End Select
+
+	End Method
+	
+	Method Render()
+		If Not _next Return
+		
+		Local offset:Int=0
+		Local opid:Int=0
+		Local ops:Object[]=_ops.Data
+		Local length:Int=_ops.length
+				
+		While offset<_next
+		
+			Local size:Int=_next-offset
+			Local lastop:Int=length
+			
+			If size>PRIM_VBO_SIZE
+			
+				size=0
+				lastop=opid
+				While lastop<length
+					Local op:TDrawOp=TDrawOp(ops[lastop])
+					Local n:Int=op.count*BYTES_PER_VERTEX
+					If size+n>PRIM_VBO_SIZE Exit
+					size:+n
+					lastop:+1
+				Wend
+				
+				If Not size
+					Local op:TDrawOp=TDrawOp(ops[opid])
+					Local count:Int=op.count
+					While count
+						Local n:Int=count
+						If n>MAX_VERTICES n=MAX_VERTICES/op.order*op.order
+						Local size:Int=n*BYTES_PER_VERTEX
+						
+						If VBO_ORPHANING_ENABLED glBufferData GL_ARRAY_BUFFER,PRIM_VBO_SIZE,Null,VBO_USAGE
+						glBufferSubData GL_ARRAY_BUFFER,0,size,_data._buf + offset
+						
+						RenderOp op,0,n
+						
+						offset:+size
+						count:-n
+					Wend
+					opid:+1
+					Continue
+				EndIf
+				
+			EndIf
+			
+			If VBO_ORPHANING_ENABLED glBufferData GL_ARRAY_BUFFER,PRIM_VBO_SIZE,Null,VBO_USAGE
+			glBufferSubData GL_ARRAY_BUFFER,0,size,_data._buf + offset
+			
+			Local index:Int=0
+			While opid<lastop
+				Local op:TDrawOp=TDrawOp(ops[opid])
+				RenderOp op,index,op.count
+				index:+op.count
+				opid:+1
+			Wend
+			offset:+size
+			
+		Wend
+		
+		glGetError
+		
+	End Method
+	
+	Method Reset()
+		_next=0
+		
+		Local data:TDrawOp[]=_ops.Data
+		For Local i:Int=0 Until _ops.Length
+			data[i].material=Null
+			freeOps.Push data[i]
+		Next
+		_ops.Clear()
+		_op=nullOp
+		
+		_casters.Clear
+		_casterVerts.Clear
+	End Method
+	
+	Method Flush()
+		Render
+		Reset
+	End Method
+	
+'	Protected
+
+	Field _blend:Float=1
+	Field _alpha:Float=255.0
+	Field _color:Float Ptr
+	Field __colorArray:Float[]=[1.0,1.0,1.0,1.0]
+	Field _pmcolor:Int=$ffffffff
+	Field _ix:Float=1,_iy:Float
+	Field _jx:Float,_jy:Float=1
+	Field _tx:Float,_ty:Float
+	Field _matStack:Float[64*6]
+	Field _matSp:Int
+	Field _font:TFont
+	Field _defaultMaterial:TMaterial
+	
+'	Private
+	
+	Field _data:TBank=New TBank.Create( 4096 )
+	Field _next:Int=0
+	
+	Field _op:TDrawOp=nullOp
+	Field _ops:TDrawOpStack=New TDrawOpStack'<DrawOp>
+	Field _casters:TShadowCasterStack=New TShadowCasterStack'<ShadowCaster>
+	Field _casterVerts:TFloatStack=New TFloatStack
+
+	Method BeginPrim( material:TMaterial,order:Int ) Final
+	
+		If Not material material=_defaultMaterial
+		
+		If _next+order*BYTES_PER_VERTEX>_data.Size()
+			Local newsize:Int=Max( _data.Size()+_data.Size()/2,_next+order*BYTES_PER_VERTEX )
+			Local data:TBank=New TBank.Create( newsize )
+			MemCopy(data._buf, _data._buf, _next)
+			'_data.CopyBytes 0,data,0,_next
+			'_data.Discard
+			_data=data
+		EndIf
+	
+		If material=_op.material And _blend=_op.blend And order=_op.order
+			_op.count:+order
+			Return
+		EndIf
+		
+		If freeOps.Length _op=freeOps.Pop() Else _op=New TDrawOp
+		
+		_ops.Push _op
+		_op.material=material
+		_op.blend=_blend
+		_op.order=order
+		_op.count=order
+	End Method
+	
+	Method BeginPrims( material:TMaterial,order:Int,count:Int ) Final
+	
+		If Not material material=_defaultMaterial
+		
+		count:*order
+		
+		If _next+count*BYTES_PER_VERTEX>_data.Size()
+			Local newsize:Int=Max( _data.Size()+_data.Size()/2,_next+count*BYTES_PER_VERTEX )
+			Local data:TBank=New TBank.Create( newsize )
+			MemCopy data._buf, _data._buf, _next
+			_data=data
+		EndIf
+	
+		If material=_op.material And _blend=_op.blend And order=_op.order
+			_op.count:+count
+			Return
+		EndIf
+		
+		If freeOps.Length _op=freeOps.Pop() Else _op=New TDrawOp
+		
+		_ops.Push _op
+		_op.material=material
+		_op.blend=_blend
+		_op.order=order
+		_op.count=count
+	End Method
+	
+	Method PrimVert( x0:Float,y0:Float,s0:Float,t0:Float ) Final
+		Local df:Float Ptr = (_data._buf + _next)
+		Local di:Int Ptr = (_data._buf + _next)
+		df[0] = x0 * _ix + y0 * _jx + _tx
+		df[1] = x0 * _iy + y0 * _jy + _ty
+		df[2] = s0
+		df[3] = t0
+		df[4] = _ix
+		df[5] = _iy
+		di[6] = _pmcolor
+		_next:+BYTES_PER_VERTEX
+	End Method
+	
+End Type
+
+
+'***** Canvas *****
+
+Type TCanvas Extends TDrawList
+
+	Const MaxLights:Int=MAX_LIGHTS
+
+	Method CreateCanvas:TCanvas( target:Object=Null )
+'		Super.Create()
+		Init
+		SetRenderTarget target
+		SetViewport 0,0,_width,_height
+		SetProjection2d 0,_width,0,_height
+		Return Self
+	End Method
+	
+	Method Discard()
+	End Method
+
+	Method SetRenderTarget( target:Object )
+
+		FlushPrims
+		
+		If Not target
+		
+			_image=Null
+			_texture=Null
+			_width=GraphicsWidth()
+			_height=GraphicsHeight()
+			_twidth=_width
+			_theight=_height
+		
+		Else If TImage( target )
+		
+			_image=TImage( target )
+			_texture=_image.Material.ColorTexture
+			If Not (_texture.Flags & TTexture.RenderTarget) Throw "Texture is not a render target texture"
+			_width=_image.Width
+			_height=_image.Height
+			_twidth=_texture.Width
+			_theight=_texture.Height
+			
+		Else If TTexture( target )
+		
+			_image=Null
+			_texture=TTexture( target )
+
+			If Not (_texture.Flags & TTexture.RenderTarget) Throw "Texture is not a render target texture"
+			_width=_texture.Width
+			_height=_texture.Height
+			_twidth=_texture.Width
+			_theight=_texture.Height
+			
+		Else
+		
+			Throw "RenderTarget object must a TImage, a TTexture or Null"
+			
+		EndIf
+		
+		_dirty=-1
+		
+	End Method
+
+	Method RenderTarget:Object()
+		If _image Return _image Else Return _texture
+	End Method
+	
+	Method Width:Int()
+		Return _width
+	End Method
+	
+	Method Height:Int()
+		Return _height
+	End Method
+	
+	Method SetColorMask( r:Int,g:Int,b:Int,a:Int )
+		FlushPrims
+		_colorMask[0]=r
+		_colorMask[1]=g
+		_colorMask[2]=b
+		_colorMask[3]=a
+		_dirty:|DIRTY_COLORMASK
+	End Method
+	
+	Method ColorMask:Int[]()
+		Return _colorMask
+	End Method
+	
+	Method SetViewport( x:Int,y:Int,w:Int,h:Int )
+		FlushPrims
+		_viewport[0]=x
+		_viewport[1]=y
+		_viewport[2]=w
+		_viewport[3]=h
+		_dirty:|DIRTY_VIEWPORT
+	End Method
+	
+	Method Viewport:Int[]()
+		Return _viewport
+	End Method
+	
+	Method SetScissor( x:Int,y:Int,w:Int,h:Int )
+		FlushPrims
+		_scissor[0]=x
+		_scissor[1]=y
+		_scissor[2]=w
+		_scissor[3]=h
+		_dirty:|DIRTY_VIEWPORT
+	End Method
+	
+	Method Scissor:Int[]()
+		Return _scissor
+	End Method
+	
+	Method SetProjectionMatrix( projMatrix:Float[] )
+		FlushPrims
+		If projMatrix
+			Mat4Copy projMatrix,_projMatrix
+		Else
+			Mat4InitArray _projMatrix
+		EndIf
+		_dirty:|DIRTY_SHADER
+	End Method
+	
+	Method SetProjection2d( Left:Float,Right:Float,top:Float,bottom:Float,znear:Float=-1,zfar:Float=1 )
+		FlushPrims
+		Mat4Ortho Left,Right,top,bottom,znear,zfar,_projMatrix
+		_dirty:|DIRTY_SHADER
+	End Method
+	
+	Method ProjectionMatrix:Float[]()
+		Return _projMatrix
+	End Method
+	
+	Method SetViewMatrix( viewMatrix:Float[] )
+		FlushPrims
+		If viewMatrix
+			Mat4Copy viewMatrix,_viewMatrix
+		Else
+			Mat4InitArray _viewMatrix
+		End If
+		_dirty:|DIRTY_SHADER
+	End Method
+	
+	Method ViewMatrix:Float[]()
+		Return _viewMatrix
+	End Method
+	
+	Method SetModelMatrix( modelMatrix:Float[] )
+		FlushPrims
+		If modelMatrix
+			Mat4Copy modelMatrix,_modelMatrix
+		Else
+			Mat4InitArray _modelMatrix
+		EndIf
+		_dirty:|DIRTY_SHADER
+	End Method
+	
+	Method ModelMatrix:Float[]()
+		Return _modelMatrix
+	End Method
+
+	Method SetAmbientLight( r:Float,g:Float,b:Float,a:Float=1 )
+		FlushPrims
+		_ambientLight[0]=r
+		_ambientLight[1]=g
+		_ambientLight[2]=b
+		_ambientLight[3]=a
+		_dirty:|DIRTY_SHADER
+	End Method
+	
+	Method AmbientLight:Float[]()
+		Return _ambientLight
+	End Method
+	
+	Method SetFogColor( r:Float,g:Float,b:Float,a:Float )
+		FlushPrims
+		_fogColor[0]=r
+		_fogColor[1]=g
+		_fogColor[2]=b
+		_fogColor[3]=a
+		_dirty:|DIRTY_SHADER
+	End Method
+	
+	Method FogColor:Float[]()
+		Return _fogColor
+	End Method
+	
+	Method SetLightType( index:Int,kind:Int )
+		FlushPrims
+		Local light:TLightData=_lights[index]
+		light.kind=kind
+		_dirty:|DIRTY_SHADER
+	End Method
+	
+	Method GetLightType:Int( index:Int )
+		Return _lights[index].kind
+	End Method
+	
+	Method SetLightColor( index:Int,r:Float,g:Float,b:Float,a:Float=1 )
+		FlushPrims
+		Local light:TLightData=_lights[index]
+		light.color[0]=r
+		light.color[1]=g
+		light.color[2]=b
+		light.color[3]=a
+		_dirty:|DIRTY_SHADER
+	End Method
+	
+	Method GetLightColor:Float[]( index:Int )
+		Return _lights[index].color
+	End Method
+	
+	Method SetLightPosition( index:Int,x:Float,y:Float,z:Float )
+		FlushPrims
+		Local light:TLightData=_lights[index]
+		light.position[0]=x
+		light.position[1]=y
+		light.position[2]=z
+		light.vector[0]=x
+		light.vector[1]=y
+		light.vector[2]=z
+		_dirty:|DIRTY_SHADER
+	End Method
+	
+	Method GetLightPosition:Float[]( index:Int )
+		Return _lights[index].position
+	End Method
+	
+	Method SetLightRange( index:Int,Range:Float )
+		FlushPrims
+		Local light:TLightData=_lights[index]
+		light.Range=Range
+		_dirty:|DIRTY_SHADER
+	End Method
+	
+	Method GetLightRange:Float( index:Int )
+		Return _lights[index].Range
+	End Method
+	
+	Method SetShadowMap( image:TImage )
+		FlushPrims
+		_shadowMap=image
+		_dirty:|DIRTY_SHADER
+	End Method
+	
+	Method ShadowMap:TImage()
+		Return _shadowMap
+	End Method
+	
+	Method SetLineWidth( lineWidth:Float )
+		FlushPrims
+		_lineWidth=lineWidth
+		_dirty:|DIRTY_LINEWIDTH
+	End Method
+	
+	Method LineWidth:Float()
+		Return _lineWidth
+	End Method
+	
+	Method Clear( r:Float=0,g:Float=0,b:Float=0,a:Float=1 )
+		FlushPrims
+		Validate
+		If _clsScissor
+			glEnable GL_SCISSOR_TEST
+			glScissor _vpx,_vpy,_vpw,_vph
+		EndIf
+		glClearColor r,g,b,a
+		glClear GL_COLOR_BUFFER_BIT
+		If _clsScissor glDisable GL_SCISSOR_TEST
+	End Method
+	
+	Method ReadPixels( x:Int,y:Int,width:Int,height:Int,data:TBank,dataOffset:Int=0,dataPitch:Int=0 )
+	
+		FlushPrims
+		
+		If Not dataPitch Or dataPitch=width*4
+			glReadPixels x,y,width,height,GL_RGBA,GL_UNSIGNED_BYTE,data._buf + dataOffset
+		Else
+			For Local iy:Int=0 Until height
+				glReadPixels x,y+iy,width,1,GL_RGBA,GL_UNSIGNED_BYTE,data._buf + dataOffset+dataPitch*iy
+			Next
+		EndIf
+
+	End Method
+
+	Method RenderDrawList( drawbuf:TDrawList )
+
+		Local fast:Int=_ix=1 And _iy=0 And _jx=0 And _jy=1 And _tx=0 And _ty=0 And _color[0]=1 And _color[1]=1 And _color[2]=1 And _color[3]=1
+		
+		If fast
+			FlushPrims
+			Validate
+			drawbuf.Render
+			Return
+		EndIf
+		
+		tmpMat3d[0]=_ix
+		tmpMat3d[1]=_iy
+		tmpMat3d[4]=_jx
+		tmpMat3d[5]=_jy
+		tmpMat3d[12]=_tx
+		tmpMat3d[13]=_ty
+		tmpMat3d[10]=1
+		tmpMat3d[15]=1
+		
+		Mat4Multiply _modelMatrix,tmpMat3d,tmpMat3d2
+		
+		FlushPrims
+		
+		Local tmp:Float[]=_modelMatrix
+		_modelMatrix=tmpMat3d2
+		rs_globalColor[0]=_color[0]*_color[3]
+		rs_globalColor[1]=_color[1]*_color[3]
+		rs_globalColor[2]=_color[2]*_color[3]
+		rs_globalColor[3]=_color[3]
+		_dirty:|DIRTY_SHADER
+		
+		Validate
+		drawbuf.Render
+		
+		_modelMatrix=tmp
+		rs_globalColor[0]=1
+		rs_globalColor[1]=1
+		rs_globalColor[2]=1
+		rs_globalColor[3]=1
+		_dirty:|DIRTY_SHADER
+	End Method
+	
+	Method RenderDrawListXYZ( drawList:TDrawList,tx:Float,ty:Float,rz:Float=0,sx:Float=1,sy:Float=1 )
+		Super.PushMatrix
+		Super.TranslateRotateScale tx,ty,rz,sx,sy
+		RenderDrawList( drawList )
+		Super.PopMatrix
+	End Method
+
+	Method Flush()
+		FlushPrims
+		
+		If Not _texture Return
+		
+		If _texture._flags & TTexture.Managed
+			Validate
+
+			glDisable GL_SCISSOR_TEST
+			glViewport 0,0,_twidth,_theight
+			
+			If _width=_twidth And _height=_theight
+				glReadPixels 0,0,_twidth,_theight,GL_RGBA,GL_UNSIGNED_BYTE, _texture._data.pixels
+			Else
+				For Local y:Int=0 Until _height
+					glReadPixels _image._x,_image._y+y,_width,1,GL_RGBA,GL_UNSIGNED_BYTE,_texture._data.pixels + (_image._y+y) * (_twidth*4) + (_image._x*4)
+				Next
+			EndIf
+
+			_dirty:|DIRTY_VIEWPORT
+		EndIf
+
+		_texture.UpdateMipmaps
+	End Method
+	
+	Global _tformInvProj:Float[16]
+	Global _tformT:Float[]=[0.0,0.0,-1.0,1.0]
+	Global _tformP:Float[4]
+	
+	Method TransformCoords( coords_in:Float[],coords_out:Float[],Mode:Int=0 )
+	
+		Mat4Inverse _projMatrix,_tformInvProj
+
+		Select Mode
+		Case 0
+			_tformT[0]=(coords_in[0]-_viewport[0])/_viewport[2]*2-1
+			_tformT[1]=(coords_in[1]-_viewport[1])/_viewport[3]*2-1
+			Mat4Transform _tformInvProj,_tformT,_tformP
+			_tformP[0]:/_tformP[3]
+			_tformP[1]:/_tformP[3]
+			_tformP[2]:/_tformP[3]
+			_tformP[3]=1
+			coords_out[0]=_tformP[0]
+			coords_out[1]=_tformP[1]
+			If coords_out.Length>2 coords_out[2]=_tformP[2]
+		Default
+			Throw "Invalid TransformCoords mode"
+		End Select
+	End Method
+	
+	'Private
+
+	Const DIRTY_RENDERTARGET:Int=1
+	Const DIRTY_VIEWPORT:Int=2
+	Const DIRTY_SHADER:Int=4
+	Const DIRTY_LINEWIDTH:Int=8
+	Const DIRTY_COLORMASK:Int=16
+		
+	Field _seq:Int
+	Field _dirty:Int=-1
+	Field _image:TImage
+	Field _texture:TTexture	
+	Field _width:Int
+	Field _height:Int
+	Field _twidth:Int
+	Field _theight:Int
+	Field _shadowMap:TImage
+	Field _colorMask:Int[]=[True,True,True,True]
+	Field _viewport:Int[]=[0,0,640,480]
+	Field _scissor:Int[]=[0,0,100000,100000]
+	Field _vpx:Int,_vpy:Int,_vpw:Int,_vph:Int
+	Field _scx:Int,_scy:Int,_scw:Int,_sch:Int
+	Field _clsScissor:Int
+	Field _projMatrix:Float[]=Mat4New()
+	Field _invProjMatrix:Float[]=Mat4New()
+	Field _viewMatrix:Float[]=Mat4New()
+	Field _modelMatrix:Float[]=Mat4New()
+	Field _ambientLight:Float[]=[0.0,0.0,0.0,1.0]
+	Field _fogColor:Float[]=[0.0,0.0,0.0,0.0]
+	Field _lights:TLightData[4]
+	Field _lineWidth:Float=1
+
+	Global _active:TCanvas
+	
+	Method Init()
+		For Local i:Int=0 Until 4
+			_lights[i]=New TLightData
+		Next
+		_dirty=-1
+	End Method
+
+	Method FlushPrims()
+		If Super.IsEmpty Return
+		Validate
+		Super.Flush
+	End Method
+	
+	Method Validate()
+		If _seq<>graphicsSeq	
+			_seq=graphicsSeq
+			InitVbos
+			_dirty=-1
+		EndIf
+	
+		If _active=Self
+			If Not _dirty Return
+		Else
+			If _active _active.Flush
+			_active=Self
+			_dirty=-1
+		EndIf
+
+'		_dirty=-1
+		
+		If _dirty & DIRTY_RENDERTARGET
+
+			If _texture
+				glBindFramebuffer GL_FRAMEBUFFER,_texture.GLFramebuffer()
+			Else
+				glBindFramebuffer GL_FRAMEBUFFER,defaultFbo
+			EndIf
+		End If
+		
+		If _dirty & DIRTY_VIEWPORT
+		
+			_vpx=_viewport[0];_vpy=_viewport[1];_vpw=_viewport[2];_vph=_viewport[3]
+			If _image
+				_vpx:+_image._x
+				_vpy:+_image._y
+			EndIf
+			
+			_scx=_scissor[0];_scy=_scissor[1];_scw=_scissor[2];_sch=_scissor[3]
+			
+			If _scx<0 _scx=0 Else If _scx>_vpw _scx=_vpw
+			If _scw<0 _scw=0 Else If _scx+_scw>_vpw _scw=_vpw-_scx
+			
+			If _scy<0 _scy=0 Else If _scy>_vph _scy=_vph
+			If _sch<0 _sch=0 Else If _scy+_sch>_vph _sch=_vph-_scy
+			
+			_scx:+_vpx;_scy:+_vpy
+		
+			If Not _texture
+				_vpy=_theight-_vpy-_vph
+				_scy=_theight-_scy-_sch
+			EndIf
+			
+			glViewport _vpx,_vpy,_vpw,_vph
+			
+			If _scx<>_vpx Or _scy<>_vpy Or _scw<>_vpw Or _sch<>_vph
+				glEnable GL_SCISSOR_TEST
+				glScissor _scx,_scy,_scw,_sch
+				_clsScissor=False
+			Else
+				glDisable GL_SCISSOR_TEST
+				_clsScissor=(_scx<>0 Or _scy<>0 Or _vpw<>_twidth Or _vph<>_theight)
+			EndIf
+			
+		EndIf
+		
+		If _dirty & DIRTY_SHADER
+		
+			rs_program=Null
+			
+			If _texture
+				rs_clipPosScale[1]=1
+				Mat4Copy _projMatrix,rs_projMatrix
+			Else
+				rs_clipPosScale[1]=-1
+				Mat4Multiply flipYMatrix,_projMatrix,rs_projMatrix
+			EndIf
+			
+			Mat4Multiply _viewMatrix,_modelMatrix,rs_modelViewMatrix
+			Mat4Multiply rs_projMatrix,rs_modelViewMatrix,rs_modelViewProjMatrix
+			Vec4Copy _ambientLight,rs_ambientLight
+			Vec4Copy _fogColor,rs_fogColor
+			
+			rs_numLights=0
+			For Local i:Int=0 Until MAX_LIGHTS
+
+				Local light:TLightData=_lights[i]
+				If Not light.kind Continue
+				
+				Mat4Transform _viewMatrix,light.vector,light.tvector
+				
+				rs_lightColors[rs_numLights*4+0]=light.color[0]
+				rs_lightColors[rs_numLights*4+1]=light.color[1]
+				rs_lightColors[rs_numLights*4+2]=light.color[2]
+				rs_lightColors[rs_numLights*4+3]=light.color[3]
+				
+				rs_lightVectors[rs_numLights*4+0]=light.tvector[0]
+				rs_lightVectors[rs_numLights*4+1]=light.tvector[1]
+				rs_lightVectors[rs_numLights*4+2]=light.tvector[2]
+				rs_lightVectors[rs_numLights*4+3]=light.Range
+
+				rs_numLights:+1
+			Next
+			
+			If _shadowMap
+				rs_shadowTexture=_shadowMap._material._colorTexture
+			Else 
+				rs_shadowTexture=Null
+			EndIf
+			
+			rs_blend=-1
+
+		End If
+		
+		If _dirty & DIRTY_LINEWIDTH
+			glLineWidth _lineWidth
+		EndIf
+		
+		If _dirty & DIRTY_COLORMASK
+			glColorMask _colorMask[0],_colorMask[1],_colorMask[2],_colorMask[3]
+		End If
+		
+		_dirty=0
+	End Method
+	
+End Type
+
+' stacks
+
+Type TFloatStack
+	Field data:Float[]
+	Field length:Int
+
+	Method Push( value:Float )
+		If length=data.Length
+			data=data[..length*2+10]
+		EndIf
+		data[length]=value
+		length:+1
+	End Method
+
+	Method Pop:Float()
+		length:-1
+		Local v:Float=data[length]
+		data[length]=Null
+		Return v
+	End Method
+
+	Method Top:Float()
+		Return data[length-1]
+	End Method
+
+	Method Clear()
+		For Local i:Int=0 Until length
+			data[i]=Null
+		Next
+		length=0
+	End Method
+
+End Type
+
+Type TDrawOpStack
+	Field data:TDrawOp[]
+	Field length:Int
+
+	Method Push( value:TDrawOp )
+		If length=data.Length
+			data=data[..length*2+10]
+		EndIf
+		data[length]=value
+		length:+1
+	End Method
+
+	Method Pop:TDrawOp()
+		length:-1
+		Local v:TDrawOp=data[length]
+		data[length]=Null
+		Return v
+	End Method
+
+	Method Top:TDrawOp()
+		Return data[length-1]
+	End Method
+
+	Method Clear()
+		For Local i:Int=0 Until length
+			data[i]=Null
+		Next
+		length=0
+	End Method
+
+End Type
+
+
+Type TShadowCasterStack
+	Field data:TShadowCaster[]
+	Field length:Int
+
+	Method Push( value:TShadowCaster )
+		If length=data.Length
+			data=data[..length*2+10]
+		EndIf
+		data[length]=value
+		length:+1
+	End Method
+
+	Method Pop:TShadowCaster()
+		length:-1
+		Local v:TShadowCaster=data[length]
+		data[length]=Null
+		Return v
+	End Method
+
+	Method Top:TShadowCaster()
+		Return data[length-1]
+	End Method
+
+	Method Get:TShadowCaster(index:Int)
+		Return data[index]
+	End Method
+	
+	Method Clear()
+		For Local i:Int=0 Until length
+			data[i]=Null
+		Next
+		length=0
+	End Method
+
+End Type
+
+Type TDrawListStack
+	Field data:TDrawList[]
+	Field length:Int
+
+	Method Push( value:TDrawList )
+		If length=data.Length
+			data=data[..length*2+10]
+		EndIf
+		data[length]=value
+		length:+1
+	End Method
+
+	Method Pop:TDrawList()
+		length:-1
+		Local v:TDrawList=data[length]
+		data[length]=Null
+		Return v
+	End Method
+
+	Method Top:TDrawList()
+		Return data[length-1]
+	End Method
+	
+	Method Get:TDrawList(index:Int)
+		Return data[index]
+	End Method
+
+	Method Clear()
+		For Local i:Int=0 Until length
+			data[i]=Null
+		Next
+		length=0
+	End Method
+
+End Type