Explorar el Código

Major mojo overhaul!

Mark Sibly hace 9 años
padre
commit
4b22bcb7ed
Se han modificado 38 ficheros con 2578 adiciones y 1836 borrados
  1. 0 127
      modules/mojo/assets/mojo/shader_env.glsl
  2. 0 24
      modules/mojo/assets/mojo/shader_font.glsl
  3. 0 28
      modules/mojo/assets/mojo/shader_matte.glsl
  4. 0 20
      modules/mojo/assets/mojo/shader_null.glsl
  5. 0 30
      modules/mojo/assets/mojo/shader_phong.glsl
  6. 0 24
      modules/mojo/assets/mojo/shader_sprite.glsl
  7. BIN
      modules/mojo/graphics/assets/RobotoMono-Regular.ttf
  8. BIN
      modules/mojo/graphics/assets/monkey2-logo-63.png
  9. 0 127
      modules/mojo/graphics/assets/shader_env.glsl
  10. 0 24
      modules/mojo/graphics/assets/shader_font.glsl
  11. 0 28
      modules/mojo/graphics/assets/shader_matte.glsl
  12. 0 20
      modules/mojo/graphics/assets/shader_null.glsl
  13. 0 30
      modules/mojo/graphics/assets/shader_phong.glsl
  14. 0 24
      modules/mojo/graphics/assets/shader_sprite.glsl
  15. 313 336
      modules/mojo/graphics/canvas.monkey2
  16. 0 347
      modules/mojo/graphics/device.monkey2
  17. 3 4
      modules/mojo/graphics/font.monkey2
  18. 4 2
      modules/mojo/graphics/fontloader.monkey2
  19. 0 57
      modules/mojo/graphics/fontloader_stb.monkey2
  20. 29 4
      modules/mojo/graphics/glutil.monkey2
  21. 446 0
      modules/mojo/graphics/graphicsdevice.monkey2
  22. 362 156
      modules/mojo/graphics/image.monkey2
  23. 91 0
      modules/mojo/graphics/indexbuffer.monkey2
  24. 0 50
      modules/mojo/graphics/material.monkey2
  25. 248 254
      modules/mojo/graphics/shader.monkey2
  26. 65 0
      modules/mojo/graphics/shaders/bump.glsl
  27. 44 0
      modules/mojo/graphics/shaders/font.glsl
  28. 92 0
      modules/mojo/graphics/shaders/light.glsl
  29. 27 0
      modules/mojo/graphics/shaders/null.glsl
  30. 20 0
      modules/mojo/graphics/shaders/shadow.glsl
  31. 44 0
      modules/mojo/graphics/shaders/sprite.glsl
  32. 49 0
      modules/mojo/graphics/shadowcaster.monkey2
  33. 314 0
      modules/mojo/graphics/test.monkey2
  34. 138 68
      modules/mojo/graphics/texture.monkey2
  35. 182 0
      modules/mojo/graphics/uniformblock.monkey2
  36. 6 49
      modules/mojo/graphics/vertex.monkey2
  37. 95 0
      modules/mojo/graphics/vertexbuffer.monkey2
  38. 6 3
      modules/mojo/mojo.monkey2

+ 0 - 127
modules/mojo/assets/mojo/shader_env.glsl

@@ -1,127 +0,0 @@
-
-// One of these defined!
-
-//#define RENDERPASS_AMBIENT
-
-//#define RENDERPASS_NORMAL
-
-//#define RENDERPASS_LIGHT
-
-uniform mat4 mx2_ModelViewMatrix;
-uniform mat4 mx2_ProjectionMatrix;
-uniform vec4 mx2_AmbientLight;
-uniform vec4 mx2_RenderColor;
-
-varying vec4 mx2_ViewPosition;
-varying vec4 mx2_Color;
-
-#if defined( RENDERPASS_LIGHT )
-varying vec2 mx2_FragPos;
-#endif
-
-//@vertex
-
-attribute vec4 mx2_VertexPosition;
-attribute vec2 mx2_VertexTexCoord0;
-attribute vec2 mx2_VertexTangent;
-attribute vec4 mx2_VertexColor;
-
-void transform( out vec4 viewpos );
-
-void main(){
-
-	vec4 position;
-	
-	transform( position );
-	
-	mx2_ViewPosition=position;
-	mx2_Color=mx2_RenderColor * mx2_VertexColor;
-	
-	gl_Position=mx2_ProjectionMatrix * position;
-	
-#if defined( RENDERPASS_LIGHT )
-	mx2_FragPos=gl_Position;
-#endif
-}
-
-//@fragment
-
-#if defined( RENDERPASS_LIGHT )
-
-uniform vec4 mx2_LightVector;
-uniform vec4 mx2_LightColor;
-uniform sampler2D mx2_NormalTexture;
-
-#endif
-
-void lighting( out vec4 diffuse,out vec4 specular,out vec4 emissive,out vec3 normal,out float alpha );
-
-void main(){
-
-	vec4 diffuse,specular,emissive;
-	vec3 normal;
-	float alpha;
-	
-	lighting( diffuse,specular,emissive,normal,alpha );
-
-#if defined( RENDERPASS_AMBIENT )
-
-	vec4 color=diffuse * mx2_AmbientLight + emissive;
-	
-	gl_FragColor=vec4( color.rgb,alpha ) * mx2_Color;
-	
-#elif defined( RENDERPASS_NORMAL )
-
-	//write out normal + specular component
-	//
-	gl_FragColor=vec4( normal * alpha,specular.w );
-	
-#elif defined( RENDERPASS_LIGHT )
-
-	vec4 npass=texture2D( mx2_PassTexture,mx2_FragPos );
-	
-	//grab normal
-	vec3 normal=npass.xyz;
-
-	//specular power
-	float gloss=npass.w;
-	float spow=pow( 2.0,gloss*12.0 );
-	float fnorm=(spow+8.0)/25.1327412287;		//	(n+8)/8*pi apparently for blinn-phong...
-
-	//normalized vector to eye	
-	
-	vec3 v=mx2_LightVector.xyz-mx2_ViewPosition.xyz;
-	float falloff=max( 1.0-length( v )/mx2_LightVector.w,0.0 );
-	
-	vec3 lvec=normalize( v );
-	vec3 evec=normalize( -mx2_ViewPosition.xyz );
-	vec3 hvec=normalize( lvec + evec );
-	
-	float ndotv=max( dot( normal,evec ),0.0 );
-	float ndotl=max( dot( normal,lvec ),0.0 );
-	float ndoth=max( dot( normal,hvec ),0.0 );
-	
-	float i=ndtol * falloff;
-	
-	vec3 diff=mx2_LightColor.rgb * i;
-	float spec=mx2_LightColor.w * i * pow( ndoth,spow ) * fnorm;
-	
-	//frenel-ish?
-	spec+=(1.0-spec) * pow( 1.0-ndotv,5.0 ) * gloss;
-	
-	//write diffuse + specular
-	gl_FragColor=vec4( diff,spec );
-	
-#elif defined( RENDERPASS_LIGHT2 )
-
-	vec4 intex=texture2D( mx2_PassTexture,mx2_FragPos );
-	
-	diffuse=diffuse * mx2_AmbientLight + (diffuse * intex.rgb) + emissive;
-	
-	specular=specular * intex.w;
-	
-	gl_FragColor=vec4( (diffuse+specular).rgb,alpha );
-	
-#endif
-
-}

+ 0 - 24
modules/mojo/assets/mojo/shader_font.glsl

@@ -1,24 +0,0 @@
-
-// ***** alpha surface shader *****
-
-varying vec2 TexCoord0;
-
-//@vertex
-
-void transform( out vec4 viewpos ){
-
-	viewpos=mx2_ModelViewMatrix * mx2_VertexPosition;
-	
-	TexCoord0=mx2_VertexTexCoord0;
-}
-
-//@fragment
-
-uniform sampler2D DiffuseTexture;
-
-void lighting( out vec4 diffuse,out vec4 specular,out vec4 emissive,out vec3 normal,out float alpha ){
-
-	emissive=vec4( texture2D( DiffuseTexture,TexCoord0 ).a );
-	
-	alpha=emissive.a;
-}

+ 0 - 28
modules/mojo/assets/mojo/shader_matte.glsl

@@ -1,28 +0,0 @@
-
-// ***** matte surface shader *****
-
-varying vec2 texCoord0;
-
-//@vertex
-
-void transform( out vec4 viewpos ){
-
-	viewpos=mx2_ModelViewMatrix * mx2_Vertex;
-
-	texCoord0=mx2_TexCoord0;
-}
-
-//@fragment
-
-uniform sampler2D ColorTexture;		//default=white
-uniform sampler2D SpecularTexture;	//default=black
-uniform sampler2D NormalTexture;	//default=black
-
-void ambientPass( out vec4 ambient ){
-	
-	ambient=texture2D( ColorTexture,texCoord0 ) * mx2_AmbientLight;
-}
-
-void lightingPass( out vec4 diffuse,out vec4 specular,out vec4 normal ){
-
-}

+ 0 - 20
modules/mojo/assets/mojo/shader_null.glsl

@@ -1,20 +0,0 @@
-
-// ***** alpha surface shader *****
-
-//@vertex
-
-void transform( out vec4 viewpos ){
-
-	viewpos=mx2_ModelViewMatrix * mx2_VertexPosition;
-
-	gl_PointSize=1.0;
-}
-
-//@fragment
-
-void lighting( out vec4 diffuse,out vec4 specular,out vec4 emissive,out vec3 normal,out float alpha ){
-
-	emissive=vec4( 1.0 );
-	
-	alpha=1.0;
-}

+ 0 - 30
modules/mojo/assets/mojo/shader_phong.glsl

@@ -1,30 +0,0 @@
-
-// ***** phong surface shader *****
-
-varying vec2 TexCoord0;
-
-//@vertex
-
-void transform( out vec4 viewpos ){
-
-	viewpos=mx2_ModelViewMatrix * mx2_VertexPosition;
-	
-	TexCoord0=mx2_VertexTexCoord0;
-}
-
-//@fragment
-
-uniform sampler2D DiffuseTexture;
-uniform sampler2D SpecularTexture;
-uniform sampler2D NormalTexture;
-
-void lighting( out vec4 diffuse,out vec4 specular,out vec4 emissive,out vec3 normal,out float alpha ){
-
-	diffuse=texture2D( DiffuseTexture,TexCoord0 );
-	
-	specular=texture2D( SpecularTexture,TexCoord0 );
-	
-	normal=normalize( mat3( mx2_ModelViewMatrix ) * texture2D( NormalTexture,TexCoord0 ).xyz );
-	
-	alpha=diffuse.a;
-}

+ 0 - 24
modules/mojo/assets/mojo/shader_sprite.glsl

@@ -1,24 +0,0 @@
-
-// ***** sprite surface shader *****
-
-varying vec2 TexCoord0;
-
-//@vertex
-
-void transform( out vec4 viewpos ){
-
-	viewpos=mx2_ModelViewMatrix * mx2_VertexPosition;
-	
-	TexCoord0=mx2_VertexTexCoord0;
-}
-
-//@fragment
-
-uniform sampler2D DiffuseTexture;
-
-void lighting( out vec4 diffuse,out vec4 specular,out vec4 emissive,out vec3 normal,out float alpha ){
-
-	emissive=texture2D( DiffuseTexture,TexCoord0 );
-	
-	alpha=emissive.a;
-}

BIN
modules/mojo/graphics/assets/RobotoMono-Regular.ttf


BIN
modules/mojo/graphics/assets/monkey2-logo-63.png


+ 0 - 127
modules/mojo/graphics/assets/shader_env.glsl

@@ -1,127 +0,0 @@
-
-// One of these defined!
-
-//#define RENDERPASS_AMBIENT
-
-//#define RENDERPASS_NORMAL
-
-//#define RENDERPASS_LIGHT
-
-uniform mat4 mx2_ModelViewMatrix;
-uniform mat4 mx2_ProjectionMatrix;
-uniform vec4 mx2_AmbientLight;
-uniform vec4 mx2_RenderColor;
-
-varying vec4 mx2_ViewPosition;
-varying vec4 mx2_Color;
-
-#if defined( RENDERPASS_LIGHT )
-varying vec2 mx2_FragPos;
-#endif
-
-//@vertex
-
-attribute vec4 mx2_VertexPosition;
-attribute vec2 mx2_VertexTexCoord0;
-attribute vec2 mx2_VertexTangent;
-attribute vec4 mx2_VertexColor;
-
-void transform( out vec4 viewpos );
-
-void main(){
-
-	vec4 position;
-	
-	transform( position );
-	
-	mx2_ViewPosition=position;
-	mx2_Color=mx2_RenderColor * mx2_VertexColor;
-	
-	gl_Position=mx2_ProjectionMatrix * position;
-	
-#if defined( RENDERPASS_LIGHT )
-	mx2_FragPos=gl_Position;
-#endif
-}
-
-//@fragment
-
-#if defined( RENDERPASS_LIGHT )
-
-uniform vec4 mx2_LightVector;
-uniform vec4 mx2_LightColor;
-uniform sampler2D mx2_NormalTexture;
-
-#endif
-
-void lighting( out vec4 diffuse,out vec4 specular,out vec4 emissive,out vec3 normal,out float alpha );
-
-void main(){
-
-	vec4 diffuse,specular,emissive;
-	vec3 normal;
-	float alpha;
-	
-	lighting( diffuse,specular,emissive,normal,alpha );
-
-#if defined( RENDERPASS_AMBIENT )
-
-	vec4 color=diffuse * mx2_AmbientLight + emissive;
-	
-	gl_FragColor=vec4( color.rgb,alpha ) * mx2_Color;
-	
-#elif defined( RENDERPASS_NORMAL )
-
-	//write out normal + specular component
-	//
-	gl_FragColor=vec4( normal * alpha,specular.w );
-	
-#elif defined( RENDERPASS_LIGHT )
-
-	vec4 npass=texture2D( mx2_PassTexture,mx2_FragPos );
-	
-	//grab normal
-	vec3 normal=npass.xyz;
-
-	//specular power
-	float gloss=npass.w;
-	float spow=pow( 2.0,gloss*12.0 );
-	float fnorm=(spow+8.0)/25.1327412287;		//	(n+8)/8*pi apparently for blinn-phong...
-
-	//normalized vector to eye	
-	
-	vec3 v=mx2_LightVector.xyz-mx2_ViewPosition.xyz;
-	float falloff=max( 1.0-length( v )/mx2_LightVector.w,0.0 );
-	
-	vec3 lvec=normalize( v );
-	vec3 evec=normalize( -mx2_ViewPosition.xyz );
-	vec3 hvec=normalize( lvec + evec );
-	
-	float ndotv=max( dot( normal,evec ),0.0 );
-	float ndotl=max( dot( normal,lvec ),0.0 );
-	float ndoth=max( dot( normal,hvec ),0.0 );
-	
-	float i=ndtol * falloff;
-	
-	vec3 diff=mx2_LightColor.rgb * i;
-	float spec=mx2_LightColor.w * i * pow( ndoth,spow ) * fnorm;
-	
-	//frenel-ish?
-	spec+=(1.0-spec) * pow( 1.0-ndotv,5.0 ) * gloss;
-	
-	//write diffuse + specular
-	gl_FragColor=vec4( diff,spec );
-	
-#elif defined( RENDERPASS_LIGHT2 )
-
-	vec4 intex=texture2D( mx2_PassTexture,mx2_FragPos );
-	
-	diffuse=diffuse * mx2_AmbientLight + (diffuse * intex.rgb) + emissive;
-	
-	specular=specular * intex.w;
-	
-	gl_FragColor=vec4( (diffuse+specular).rgb,alpha );
-	
-#endif
-
-}

+ 0 - 24
modules/mojo/graphics/assets/shader_font.glsl

@@ -1,24 +0,0 @@
-
-// ***** alpha surface shader *****
-
-varying vec2 TexCoord0;
-
-//@vertex
-
-void transform( out vec4 viewpos ){
-
-	viewpos=mx2_ModelViewMatrix * mx2_VertexPosition;
-	
-	TexCoord0=mx2_VertexTexCoord0;
-}
-
-//@fragment
-
-uniform sampler2D DiffuseTexture;
-
-void lighting( out vec4 diffuse,out vec4 specular,out vec4 emissive,out vec3 normal,out float alpha ){
-
-	emissive=vec4( texture2D( DiffuseTexture,TexCoord0 ).a );
-	
-	alpha=emissive.a;
-}

+ 0 - 28
modules/mojo/graphics/assets/shader_matte.glsl

@@ -1,28 +0,0 @@
-
-// ***** matte surface shader *****
-
-varying vec2 texCoord0;
-
-//@vertex
-
-void transform( out vec4 viewpos ){
-
-	viewpos=mx2_ModelViewMatrix * mx2_Vertex;
-
-	texCoord0=mx2_TexCoord0;
-}
-
-//@fragment
-
-uniform sampler2D ColorTexture;		//default=white
-uniform sampler2D SpecularTexture;	//default=black
-uniform sampler2D NormalTexture;	//default=black
-
-void ambientPass( out vec4 ambient ){
-	
-	ambient=texture2D( ColorTexture,texCoord0 ) * mx2_AmbientLight;
-}
-
-void lightingPass( out vec4 diffuse,out vec4 specular,out vec4 normal ){
-
-}

+ 0 - 20
modules/mojo/graphics/assets/shader_null.glsl

@@ -1,20 +0,0 @@
-
-// ***** alpha surface shader *****
-
-//@vertex
-
-void transform( out vec4 viewpos ){
-
-	viewpos=mx2_ModelViewMatrix * mx2_VertexPosition;
-
-	gl_PointSize=1.0;
-}
-
-//@fragment
-
-void lighting( out vec4 diffuse,out vec4 specular,out vec4 emissive,out vec3 normal,out float alpha ){
-
-	emissive=vec4( 1.0 );
-	
-	alpha=1.0;
-}

+ 0 - 30
modules/mojo/graphics/assets/shader_phong.glsl

@@ -1,30 +0,0 @@
-
-// ***** phong surface shader *****
-
-varying vec2 TexCoord0;
-
-//@vertex
-
-void transform( out vec4 viewpos ){
-
-	viewpos=mx2_ModelViewMatrix * mx2_VertexPosition;
-	
-	TexCoord0=mx2_VertexTexCoord0;
-}
-
-//@fragment
-
-uniform sampler2D DiffuseTexture;
-uniform sampler2D SpecularTexture;
-uniform sampler2D NormalTexture;
-
-void lighting( out vec4 diffuse,out vec4 specular,out vec4 emissive,out vec3 normal,out float alpha ){
-
-	diffuse=texture2D( DiffuseTexture,TexCoord0 );
-	
-	specular=texture2D( SpecularTexture,TexCoord0 );
-	
-	normal=normalize( mat3( mx2_ModelViewMatrix ) * texture2D( NormalTexture,TexCoord0 ).xyz );
-	
-	alpha=diffuse.a;
-}

+ 0 - 24
modules/mojo/graphics/assets/shader_sprite.glsl

@@ -1,24 +0,0 @@
-
-// ***** sprite surface shader *****
-
-varying vec2 TexCoord0;
-
-//@vertex
-
-void transform( out vec4 viewpos ){
-
-	viewpos=mx2_ModelViewMatrix * mx2_VertexPosition;
-	
-	TexCoord0=mx2_VertexTexCoord0;
-}
-
-//@fragment
-
-uniform sampler2D DiffuseTexture;
-
-void lighting( out vec4 diffuse,out vec4 specular,out vec4 emissive,out vec3 normal,out float alpha ){
-
-	emissive=texture2D( DiffuseTexture,TexCoord0 );
-	
-	alpha=emissive.a;
-}

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 313 - 336
modules/mojo/graphics/canvas.monkey2


+ 0 - 347
modules/mojo/graphics/device.monkey2

@@ -1,347 +0,0 @@
-
-Namespace mojo.graphics
-
-#rem monkeydoc Blend modes.
-
-Blend modes are used with the [[Canvas.BlendMode]] property.
-
-| BlendMode	| Description
-|:----------|:-----------
-| Opaque	| Blending disabled.
-| Alpha		| Alpha blending.
-| Multiply	| Multiply blending.
-| Additive	| Additive blending.
-
-#end
-Enum BlendMode
-	Opaque=0
-	Alpha=1
-	Additive=2
-	Multiply=3
-End
-
-#rem monkeydoc @hidden
-#end
-Class GraphicsDevice
-
-	Method New()
-		RenderTarget=Null
-		Viewport=New Recti( 0,0,640,480 )
-		Scissor=New Recti( 0,0,16384,16384 )
-		BlendMode=BlendMode.Alpha
-	End
-	
-	Property RenderTarget:Texture()
-	
-		Return _target
-	
-	Setter( renderTarget:Texture )
-	
-		FlushTarget()
-		
-		_target=renderTarget
-		
-		_dirty|=Dirty.Target
-	End
-	
-	Property Viewport:Recti()
-	
-		Return _viewport
-	
-	Setter( viewport:Recti )
-	
-		FlushTarget()
-	
-		_viewport=viewport
-		
-		_dirty|=Dirty.Viewport|Dirty.Scissor
-	End
-	
-	Property Scissor:Recti()
-	
-		Return _scissor
-	
-	Setter( scissor:Recti )
-	
-		FlushTarget()
-	
-		_scissor=scissor
-		
-		_dirty|=Dirty.Scissor
-	End
-	
-	Property BlendMode:BlendMode()
-	
-		Return _blendMode
-	
-	Setter( blendMode:BlendMode )
-	
-		_blendMode=blendMode
-		
-		_dirty|=Dirty.BlendMode
-	End
-	
-	Property ShaderEnv:ShaderEnv()
-	
-		Return _shaderEnv
-	
-	Setter( shaderEnv:ShaderEnv )
-	
-		_shaderEnv=shaderEnv
-		
-		_dirty|=Dirty.Shader|Dirty.EnvParams|Dirty.Params
-	End
-	
-	Property EnvParams:ParamBuffer()
-	
-		Return _envParams
-	
-	Setter( envParams:ParamBuffer )
-	
-		_envParams=envParams
-		
-		_dirty|=Dirty.EnvParams
-	End
-	
-	Property Shader:Shader()
-	
-		Return _shader
-	
-	Setter( shader:Shader )
-	
-		_shader=shader
-		
-		_dirty|=Dirty.Shader|Dirty.EnvParams|Dirty.Params
-	End
-	
-	Property Params:ParamBuffer()
-	
-		Return _params
-	
-	Setter( params:ParamBuffer )
-	
-		_params=params
-		
-		_dirty|=Dirty.Params
-	End
-	
-	Property FilteringEnabled:Bool()
-	
-		Return _filter
-	
-	Setter( filteringEnabled:Bool )
-	
-		If filteringEnabled=_filter Return
-		
-		_filter=filteringEnabled
-
-		_dirty|=Dirty.Params
-	End
-	
-	Method Clear( color:Color )
-	
-		Validate()
-		
-		If _rscissor<>_windowRect
-			glEnable( GL_SCISSOR_TEST )
-			glScissor( _rscissor.X,_rscissor.Y,_rscissor.Width,_rscissor.Height )
-		Else
-			glDisable( GL_SCISSOR_TEST )
-		Endif
-		
-		glClearColor( color.r,color.g,color.b,color.a )
-
-		glClear( GL_COLOR_BUFFER_BIT )
-		
-		If _rscissor<>_viewport
-			glEnable( GL_SCISSOR_TEST )
-			glScissor( _rscissor.X,_rscissor.Y,_rscissor.Width,_rscissor.Height )
-		Else
-			glDisable( GL_SCISSOR_TEST )
-		Endif
-		
-		_modified=True
-	End
-	
-	Method Render( vertices:Vertex2f Ptr,order:Int,count:Int )
-	
-		Validate()
-		
-		Local n:=order*count
-		
-		If n>_vertices.Length 
-			_vertices=New Vertex2f[n]
-			Local p:=Cast<UByte Ptr>( _vertices.Data )
-			glEnableVertexAttribArray( 0 ) ; glVertexAttribPointer( 0,2,GL_FLOAT,False,BYTES_PER_VERTEX,p )
-			glEnableVertexAttribArray( 1 ) ; glVertexAttribPointer( 1,2,GL_FLOAT,False,BYTES_PER_VERTEX,p+8 )
-			glEnableVertexAttribArray( 2 ) ; glVertexAttribPointer( 2,2,GL_FLOAT,False,BYTES_PER_VERTEX,p+16 )
-			glEnableVertexAttribArray( 3 ) ; glVertexAttribPointer( 3,4,GL_UNSIGNED_BYTE,True,BYTES_PER_VERTEX,p+24 )
-		Endif
-
-		libc.memcpy( _vertices.Data,vertices,n*BYTES_PER_VERTEX )
-		
-		Select order
-		Case 1
-			glDrawArrays( GL_POINTS,0,n )
-		Case 2
-			glDrawArrays( GL_LINES,0,n )
-		Case 3
-			glDrawArrays( GL_TRIANGLES,0,n )
-		Case 4
-			Local n:=count*6
-			If n>_qindices.Length
-				_qindices=New UShort[n]
-				For Local i:=0 Until count
-					_qindices[i*6+0]=i*4
-					_qindices[i*6+1]=i*4+1
-					_qindices[i*6+2]=i*4+2
-					_qindices[i*6+3]=i*4
-					_qindices[i*6+4]=i*4+2
-					_qindices[i*6+5]=i*4+3
-				Next
-			Endif
-			glDrawElements( GL_TRIANGLES,n,GL_UNSIGNED_SHORT,_qindices.Data )
-		Default
-			For Local i:=0 Until count
-				glDrawArrays( GL_TRIANGLE_FAN,i*order,order )
-			Next
-		End
-		
-		_modified=True
-	End
-	
-	Method CopyPixmap:Pixmap( rect:Recti )
-	
-		Validate()
-
-		Local pixmap:=New Pixmap( rect.Width,rect.Height,PixelFormat.RGBA32 )
-		
-		glReadPixels( rect.X,rect.Y,rect.Width,rect.Height,GL_RGBA,GL_UNSIGNED_BYTE,pixmap.Data )
-		
-		Return pixmap
-	End
-
-	Private
-	
-	Enum Dirty
-		Target=			$0001
-		Viewport=		$0002
-		Scissor=		$0004
-		BlendMode=		$0008
-		Shader=			$0010
-		EnvParams=		$0020
-		Params=			$0040
-		All=			$007f
-	End
-	
-	Field _dirty:Dirty=Dirty.All
-	Field _modified:Bool
-	Field _target:Texture
-	Field _windowRect:Recti
-	Field _viewport:Recti
-	Field _scissor:Recti
-	Field _blendMode:BlendMode
-	Field _shaderEnv:ShaderEnv
-	Field _envParams:ParamBuffer
-	Field _shader:Shader
-	Field _params:ParamBuffer
-	Field _filter:Bool=True
-	
-	Field _rscissor:Recti
-
-	Global _seq:Int
-	Global _current:GraphicsDevice
-	Global _defaultFbo:GLint
-	
-	Global _vertices:Vertex2f[]
-	Global _qindices:UShort[]
-	
-	Const BYTES_PER_VERTEX:=28
-	
-	Function InitGLState()
-		glDisable( GL_CULL_FACE )
-		glDisable( GL_DEPTH_TEST )
-		glGetIntegerv( GL_FRAMEBUFFER_BINDING,Varptr _defaultFbo )
-	End
-	
-	Method FlushTarget()
-		If Not _modified Return
-		If _target _target.Modified( Self )
-		_modified=False
-	End
-	
-	Method Validate()
-	
-		If _seq<>glGraphicsSeq
-			_seq=glGraphicsSeq
-			_current=Null
-			InitGLState()
-		Endif
-		
-		If _current=Self 
-			If Not _dirty Return
-		Else
-			If _current _current.FlushTarget()
-			_current=Self
-			_dirty=Dirty.All
-		Endif
-		
-		If _dirty & Dirty.Target
-			If _target
-				glBindFramebuffer( GL_FRAMEBUFFER,_target.GLFramebuffer )
-			Else
-				glBindFramebuffer( GL_FRAMEBUFFER,_defaultFbo )
-			Endif
-		Endif
-		
-		If _dirty & Dirty.Viewport
-			glViewport( _viewport.X,_viewport.Y,_viewport.Width,_viewport.Height )
-		Endif
-		
-		If _dirty & Dirty.Scissor
-			_rscissor=_scissor & _viewport
-			If _rscissor<>_viewport
-				glEnable( GL_SCISSOR_TEST )
-				glScissor( _rscissor.X,_rscissor.Y,_rscissor.Width,_rscissor.Height )
-			Else
-				glDisable( GL_SCISSOR_TEST )
-			Endif
-		Endif
-
-		If _dirty & Dirty.BlendMode		
-			Select _blendMode
-			Case BlendMode.Opaque
-				glDisable( GL_BLEND )
-			Case BlendMode.Alpha
-				glEnable( GL_BLEND )
-				glBlendFunc( GL_ONE,GL_ONE_MINUS_SRC_ALPHA )
-			Case BlendMode.Additive
-				glEnable( GL_BLEND )
-				glBlendFunc( GL_ONE,GL_ONE )
-			Case BlendMode.Multiply
-				glEnable( GL_BLEND )
-				glBlendFunc( GL_DST_COLOR,GL_ONE_MINUS_SRC_ALPHA )
-			End
-		Endif
-		
-		If _shader And _shaderEnv And _envParams And _params
-		
-			If _dirty & Dirty.Shader
-				_shader.Bind( _shaderEnv )
-			Endif
-			
-			If _dirty & Dirty.EnvParams
-				_shader.BindEnvParams( _envParams )
-			Endif
-			
-			If _dirty & Dirty.Params
-				_shader.BindParams( _params,_filter )
-			End
-			
-		Endif
-		
-		_dirty=Null
-		
-	End
-
-End

+ 3 - 4
modules/mojo/graphics/font.monkey2

@@ -92,7 +92,7 @@ Class Font
 		Return _glyphs[0]
 	End
 	
-	#rem monkedoc Measures the width of some text when rendered by the font.
+	#rem monkeydoc Measures the width of some text when rendered by the font.
 	#end
 	Method TextWidth:Float( text:String )
 		Local w:=0.0
@@ -120,11 +120,11 @@ Class Font
 	'Make this ALWAYS work!	
 	#rem monkeydoc Loads a font from a ttf file.
 	#end
-	Function Load:Font( path:String,height:Float,textureFlags:TextureFlags=Null,shader:Shader=Null )
+	Function Load:Font( path:String,height:Float,shader:Shader=Null )
 	
 		If Not shader shader=Shader.GetShader( "font" )
 		
-		Local font:=fontloader.LoadFont( path,height,textureFlags,shader )
+		Local font:=mojo.graphics.fontloader.LoadFont( path,height,shader )
 		
 		Return font
 	End
@@ -141,4 +141,3 @@ Class Font
 	Global _openFonts:=New StringMap<Font>
 
 End
-

+ 4 - 2
modules/mojo/graphics/fontloader_freetype.monkey2 → modules/mojo/graphics/fontloader.monkey2

@@ -13,7 +13,7 @@ Public
 
 #rem monkeydoc @hidden
 #end
-Function LoadFont:Font( path:String,fheight:Float,textureFlags:TextureFlags,shader:Shader )
+Function LoadFont:Font( path:String,fheight:Float,shader:Shader )
 
 	If Not FreeType And FT_Init_FreeType( Varptr FreeType ) Return Null
 	
@@ -97,7 +97,9 @@ Function LoadFont:Font( path:String,fheight:Float,textureFlags:TextureFlags,shad
 	
 	data.Discard()
 	
-	Local image:=New Image( pixmap,textureFlags,shader )
+	Local texture:=New Texture( pixmap,Null )
+	
+	Local image:=New Image( texture,shader )
 	
 	Local font:=New Font( image,height,firstChar,glyphs )
 	

+ 0 - 57
modules/mojo/graphics/fontloader_stb.monkey2

@@ -1,57 +0,0 @@
-
-Namespace mojo.graphics.fontloader
-
-#Import "<stb-truetype>"
-
-Using stb.truetype
-
-#rem monkeydoc @hidden
-#end
-Function LoadFont:Font( path:String,height:Float )
-
-	Local firstChar:=32
-	Local numChars:=96
-
-	Local data:=DataBuffer.Load( path )
-	If Not data Return Null
-	
-	'Get font info
-	Local f:stbtt_fontinfo,fp:=Varptr f
-	If Not stbtt_InitFont( fp,data.Data,0 ) Return Null
-
-	Local ascenti:Int,descenti:Int,linegapi:Int
-	stbtt_GetFontVMetrics( fp,Varptr ascenti,Varptr descenti,Varptr linegapi )
-	
-	Local scale:=stbtt_ScaleForPixelHeight( fp,height )
-
-	Local ascent:=ascenti*scale,descent:=descenti*scale,linegap:=linegapi*scale
-	
-	Local fheight:=(ascenti-descenti+linegapi)*scale
-	
-	'Bake the chars		
-	Local bakedChars:=New stbtt_bakedchar[numChars]
-	Local pixmap:=New Pixmap( 512,512,PixelFormat.A8 )
-	stbtt_BakeFontBitmap( data.Data,0,height,pixmap.Data,512,512,firstChar,numChars,bakedChars.Data )
-
-	Local image:=New Image( pixmap,Shader.GetShader( "font" ) )
-	
-	Local glyphs:=New Glyph[numChars]
-	
-	For Local i:=0 Until numChars
-	
-		Local x:=bakedChars[i].x0
-		Local y:=bakedChars[i].y0
-		Local w:=bakedChars[i].x1-x
-		Local h:=bakedChars[i].y1-y
-		Local xoffset:=bakedChars[i].xoff
-		Local yoffset:=bakedChars[i].yoff+ascent
-		Local advance:=bakedChars[i].xadvance
-		
-		glyphs[i]=New Glyph( New Recti( x,y,x+w,y+h ),New Vec2f( xoffset,yoffset ),advance )
-		
-	Next
-	
-	Local font:=New Font( image,fheight,firstChar,glyphs )
-	
-	Return font
-End

+ 29 - 4
modules/mojo/graphics/glutil.monkey2

@@ -46,6 +46,32 @@ Function glPopTexture2d:Void()
 	glBindTexture( GL_TEXTURE_2D,tmpi )
 End
 
+#rem monkeydoc @hidden
+#end
+Function glPushArrayBuffer( buf:Int )
+	glGetIntegerv( GL_ARRAY_BUFFER_BINDING,Varptr tmpi )
+	glBindBuffer( GL_ARRAY_BUFFER,buf )
+End
+
+#rem monkeydoc @hidden
+#end
+Function glPopArrayBuffer()
+	glBindBuffer( GL_ARRAY_BUFFER,tmpi )
+End
+
+#rem monkeydoc @hidden
+#end
+Function glPushElementArrayBuffer( buf:Int )
+	glGetIntegerv( GL_ELEMENT_ARRAY_BUFFER_BINDING,Varptr tmpi )
+	glBindBuffer( GL_ELEMENT_ARRAY_BUFFER,buf )
+End
+
+#rem monkeydoc @hidden
+#end
+Function glPopElementArrayBuffer()
+	glBindBuffer( GL_ELEMENT_ARRAY_BUFFER,tmpi )
+End
+
 #rem monkeydoc @hidden
 #end
 Function glPushFramebuffer:Void( framebuf:Int )
@@ -63,7 +89,7 @@ End
 #end
 Function glCompile:Int( type:Int,source:String )
 
-#If __TARGET__="windows" Or __TARGET__="raspbian" Or Not __DESKTOP_TARGET__
+#If __TARGET__="windows" Or Not __DESKTOP_TARGET__
 	source="precision mediump float;~n"+source
 #Endif
 	
@@ -72,12 +98,11 @@ Function glCompile:Int( type:Int,source:String )
 	glCompileShader( shader )
 	glGetShaderiv( shader,GL_COMPILE_STATUS,Varptr tmpi )
 	If Not tmpi
-		Print "Failed to compile fragment shader:"+glGetShaderInfoLogEx( shader )
 		Local lines:=source.Split( "~n" )
 		For Local i:=0 Until lines.Length
 			Print (i+1)+":~t"+lines[i]
 		Next
-		Assert( False,"Compile fragment shader failed" )
+		RuntimeError( "Failed to compile fragment shader:"+glGetShaderInfoLogEx( shader ) )
 	Endif
 	Return shader
 End
@@ -87,5 +112,5 @@ End
 Function glLink:Void( program:Int )
 	glLinkProgram( program )
 	glGetProgramiv( program,GL_LINK_STATUS,Varptr tmpi )
-	If Not tmpi Assert( False,"Failed to link program:"+glGetProgramInfoLogEx( program ) )
+	If Not tmpi RuntimeError( "Failed to link program:"+glGetProgramInfoLogEx( program ) )
 End

+ 446 - 0
modules/mojo/graphics/graphicsdevice.monkey2

@@ -0,0 +1,446 @@
+
+Namespace mojo.graphics
+
+#rem monkeydoc Blend modes.
+
+Blend modes are used with the [[Canvas.BlendMode]] property.
+
+| BlendMode	| Description
+|:----------|:-----------
+| Opaque	| Blending disabled.
+| Alpha		| Alpha blending.
+| Multiply	| Multiply blending.
+| Additive	| Additive blending.
+
+#end
+Enum BlendMode
+	None=0
+	Opaque
+	Alpha
+	Additive
+	Multiply
+End
+
+#rem monkeydoc @hidden Color mask values.
+
+Color masks are used with the [[Canvas.ColorMask]] property.
+
+| ColorMask	| Descripten
+|:----------|:----------
+| Red		| Red color mask.
+| Green		| Green color mask.
+| Blue		| Blue color mask.
+| Alpha		| Alpha color mask.
+#end
+Enum ColorMask
+	None=0
+	Red=1
+	Green=2
+	Blue=4
+	Alpha=8
+	All=15
+End
+
+#rem monkeydoc @hidden
+#end
+Class GraphicsDevice
+
+	Method New()
+		Init()
+	End
+
+	Method New( width:Int,height:Int )
+		Init()
+		
+		_deviceSize=New Vec2i( width,height )
+		
+		_rtargetSize=_deviceSize
+	End
+	
+	Method Resize( size:Vec2i )
+	
+		_deviceSize=size
+	
+		If Not _rtarget _rtargetSize=size
+	End
+	
+	Property RenderTargetSize:Vec2i()
+	
+		Return _rtargetSize
+	End
+	
+	'***** PUBLIC *****
+	
+	Property RenderTarget:Texture()
+
+		Return _rtarget
+	
+	Setter( renderTarget:Texture )
+
+		FlushTarget()
+	
+		_rtarget=renderTarget
+		
+		_rtargetSize=_rtarget ? _rtarget.Rect.Size Else _deviceSize
+		
+		_dirty|=Dirty.RenderTarget|Dirty.Viewport|Dirty.Scissor
+	End
+	
+	Property Viewport:Recti()
+	
+		Return _viewport
+	
+	Setter( viewport:Recti )
+	
+		FlushTarget()
+	
+		_viewport=viewport
+		
+		_dirty|=Dirty.Viewport|Dirty.Scissor
+	End
+	
+	Property Scissor:Recti()
+	
+		Return _scissor
+	
+	Setter( scissor:Recti )
+	
+		FlushTarget()
+	
+		_scissor=scissor
+		
+		_dirty|=Dirty.Scissor
+	End
+	
+	Property ColorMask:ColorMask()
+	
+		Return _colorMask
+		
+	Setter( colorMask:ColorMask )
+	
+		_colorMask=colorMask
+		
+		_dirty|=Dirty.ColorMask
+	End
+	
+	Property BlendMode:BlendMode()
+	
+		Return _blendMode
+	
+	Setter( blendMode:BlendMode )
+	
+		_blendMode=blendMode
+		
+		_dirty2|=Dirty.BlendMode
+	End
+	
+	Property TextureFilter:TextureFilter()
+	
+		return _textureFilter
+	
+	Setter( filter:TextureFilter )
+	
+		_textureFilter=filter
+		
+		_dirty2|=Dirty.TextureFilter
+	End
+	
+	Property VertexBuffer:VertexBuffer()
+	
+		Return _vertexBuffer
+		
+	Setter( vbuffer:VertexBuffer )
+	
+		_vertexBuffer=vbuffer
+		
+		_dirty2|=Dirty.VertexBuffer
+	End
+	
+	Property IndexBuffer:IndexBuffer()
+	
+		Return _indexBuffer
+		
+	Setter( ibuffer:IndexBuffer )
+	
+		_indexBuffer=ibuffer
+		
+		_dirty2|=Dirty.IndexBuffer
+	End
+	
+	Property RenderPass:Int()
+	
+		Return _rpass
+		
+	Setter( rpass:Int )
+	
+		_rpass=rpass
+		
+		_dirty2|=Dirty.Shader
+	End
+	
+	Property Shader:Shader()
+	
+		Return _shader
+
+	Setter( shader:Shader )
+	
+		_shader=shader
+		
+		_dirty2|=Dirty.Shader
+	End
+	
+	Method SetUniformBlock( id:Int,ublock:UniformBlock )
+	
+		_ublocks[id]=ublock
+	End
+	
+	Method GetUniformBlock:UniformBlock( id:Int )
+	
+		Return _ublocks[id]
+	End
+	
+	Method CopyPixmap:Pixmap( rect:Recti )
+	
+		Validate()
+
+		Local pixmap:=New Pixmap( rect.Width,rect.Height,PixelFormat.RGBA32 )
+		
+		glReadPixels( rect.X,rect.Y,rect.Width,rect.Height,GL_RGBA,GL_UNSIGNED_BYTE,pixmap.Data )
+		
+		If Not _rtarget pixmap.FlipY()
+		
+		Return pixmap
+	End
+
+	Method Clear( color:Color )
+	
+		Validate()
+		
+		glClearColor( color.r,color.g,color.b,color.a )
+		
+		If Not _scissorTest glEnable( GL_SCISSOR_TEST )
+		
+		glClear( GL_COLOR_BUFFER_BIT )
+		
+		If Not _scissorTest glDisable( GL_SCISSOR_TEST )
+		
+		_modified=true
+	End
+	
+	Method Render( order:Int,count:Int,offset:Int=0 )
+	
+		Validate2()
+	
+		Local n:=order*count
+	
+		Select order
+		Case 1 glDrawArrays( GL_POINTS,offset,n )
+		Case 2 glDrawArrays( GL_LINES,offset,n )
+		Case 3 glDrawArrays( GL_TRIANGLES,offset,n )
+		Default
+			For Local i:=0 Until count
+				glDrawArrays( GL_TRIANGLE_FAN,offset+i*order,order )
+			Next
+		End
+		
+		_modified=true
+	End
+	
+	Method RenderIndexed( order:Int,count:Int,offset:Int=0 )
+	
+		Validate2()
+
+		Local n:=order*count
+
+		Local p:=Cast<UShort Ptr>( offset*2 )
+		
+		Select order
+		Case 1 glDrawElements( GL_POINTS,n,GL_UNSIGNED_SHORT,p )
+		Case 2 glDrawElements( GL_LINES,n,GL_UNSIGNED_SHORT,p )
+		Case 3 glDrawElements( GL_TRIANGLES,n,GL_UNSIGNED_SHORT,p )
+		Default
+			For Local i:=0 Until count
+				glDrawElements( GL_TRIANGLE_FAN,order,GL_UNSIGNED_SHORT,p+i*order )
+			Next
+		End
+		
+		_modified=true
+	End
+	
+	Private
+	
+	Enum Dirty
+		'
+		RenderTarget=		$0001
+		Viewport=			$0002
+		Scissor=			$0004
+		ColorMask=			$0008
+		'
+		BlendMode=			$0010
+		VertexBuffer=		$0020
+		IndexBuffer=		$0040
+		Shader=				$0080
+		TextureFilter=		$0100
+		All=				$01ff
+		'
+	End
+	
+	Field _dirty:Dirty
+	Field _dirty2:Dirty
+	Field _modified:Bool
+	
+	Field _rtarget:Texture
+	Field _rtargetSize:Vec2i
+	Field _deviceSize:Vec2i
+	Field _viewport:Recti
+	Field _scissor:Recti
+	Field _scissorTest:Bool
+	Field _colorMask:ColorMask
+	Field _blendMode:BlendMode
+	Field _textureFilter:TextureFilter
+	Field _vertexBuffer:VertexBuffer
+	Field _indexBuffer:IndexBuffer
+	Field _ublocks:=New UniformBlock[4]
+	Field _shader:Shader
+	Field _rpass:Int
+	
+	Global _seq:Int
+	Global _current:GraphicsDevice
+	Global _defaultFbo:GLint
+	
+	Method Init()
+		_colorMask=ColorMask.All
+	End
+	
+	Function InitGLState()
+		glDisable( GL_CULL_FACE )
+		glDisable( GL_DEPTH_TEST )
+		glGetIntegerv( GL_FRAMEBUFFER_BINDING,Varptr _defaultFbo )
+	End
+	
+	Method FlushTarget()
+		If Not _modified Return
+		_modified=False
+		If _rtarget
+			Validate()
+			_rtarget.Modified( _viewport & _scissor )
+		Endif
+	End
+	
+	Method Validate()
+
+		If _seq<>glGraphicsSeq
+			_seq=glGraphicsSeq
+			_current=Null
+			InitGLState()
+		Endif
+		
+		If _current=Self 
+			If Not _dirty Return
+		Else
+			If _current _current.FlushTarget()
+			_current=Self
+			_dirty=Dirty.All
+		Endif
+		
+		If _dirty & Dirty.RenderTarget
+		
+			If _rtarget
+				glBindFramebuffer( GL_FRAMEBUFFER,_rtarget.GLFramebuffer )
+			Else
+				glBindFramebuffer( GL_FRAMEBUFFER,_defaultFbo )
+			Endif
+
+		Endif
+	
+		If _dirty & Dirty.Viewport
+		
+			If _rtarget
+				glViewport( _viewport.X,_viewport.Y,_viewport.Width,_viewport.Height )
+			Else
+				glViewport( _viewport.X,_rtargetSize.y-_viewport.Bottom,_viewport.Width,_viewport.Height )
+			Endif
+			
+		Endif
+		
+		If _dirty & Dirty.Scissor
+		
+			Local scissor:=_scissor & _viewport
+			
+			_scissorTest=scissor<>_viewport
+			If _scissorTest glEnable( GL_SCISSOR_TEST ) Else glDisable( GL_SCISSOR_TEST )
+			
+			If _rtarget
+				glScissor( scissor.X,scissor.Y,scissor.Width,scissor.Height )
+			Else
+				glScissor( scissor.X,_rtargetSize.y-scissor.Bottom,scissor.Width,scissor.Height )
+			Endif
+		
+		Endif
+		
+		If _dirty & Dirty.ColorMask
+			
+			Local r:=Bool( _colorMask & ColorMask.Red )
+			Local g:=Bool( _colorMask & ColorMask.Green )
+			Local b:=Bool( _colorMask & ColorMask.Blue )
+			Local a:=Bool( _colorMask & ColorMask.Alpha )
+			
+			glColorMask( r,g,b,a )
+		
+		Endif
+		
+		_dirty=Null
+	End
+	
+	Method Validate2()
+	
+		Validate()
+		
+		If _dirty2 & Dirty.BlendMode
+
+			Select _blendMode
+			Case BlendMode.Opaque
+				glDisable( GL_BLEND )
+			Case BlendMode.Alpha
+				glEnable( GL_BLEND )
+				glBlendFunc( GL_ONE,GL_ONE_MINUS_SRC_ALPHA )
+			Case BlendMode.Additive
+				glEnable( GL_BLEND )
+				glBlendFunc( GL_ONE,GL_ONE )
+			Case BlendMode.Multiply
+				glEnable( GL_BLEND )
+				glBlendFunc( GL_DST_COLOR,GL_ONE_MINUS_SRC_ALPHA )
+			Default
+				glDisable( GL_BLEND )
+			End
+
+		Endif
+		
+		If _dirty2 & Dirty.VertexBuffer
+		
+			 _vertexBuffer.Bind()
+			
+		Endif
+
+		If _dirty2 & Dirty.IndexBuffer
+		
+			If _indexBuffer _indexBuffer.Bind()
+			
+		Endif
+		
+		If _dirty2 & Dirty.Shader
+		
+			_shader.Bind( _rpass )
+
+		Endif
+		
+		_vertexBuffer.Validate()
+		
+		If _indexBuffer _indexBuffer.Validate()
+
+		_shader.ValidateUniforms( _rpass,_ublocks,_textureFilter )
+		
+		_dirty2=Null
+	End
+	
+End

+ 362 - 156
modules/mojo/graphics/image.monkey2

@@ -5,168 +5,133 @@ Namespace mojo.graphics
 
 An image is a rectangular array of pixels that can be drawn using one of the [[Canvas.DrawImage]] methods.
 
-You can load an image from a file using the [[Load]].
+You can load an image from a file using one of the [[Load]], [[LoadBump]] or [[LoadLight]] functions.
 
 #end
 Class Image
 
-	#rem monkeydoc @hidden
+	#rem monkeydoc Invoked after image has ben discarded.
 	#end
 	Field OnDiscarded:Void()
-
-	#rem monkeydoc Creates a new image.
 	
-	New( pixmap,... ) allows you to create a new image from an existing pixmap.
+	#rem monkeydoc Creates a new Image.
 	
-	New( width,height,... ) allows you to create a new image that can be rendered to using a canvas. For images that will be frequently updated, use `TextureFlags.Filter|TextureFlags.Dynamic' for the best performance.
+	New( pixmap,... ) Creates an image from an existing pixmap.
 	
-	New( image,rect,... ) allows you to create an image from within an 'atlas' image.
+	New( width,height,... ) Creates an image that can be rendered to using a canvas.
 	
-	@example
+	New( image,... ) Creates an image from within an 'atlas' image.
 	
-	Namespace myapp
+	Note: `textureFlags` should be null for static images or TextureFlags.Dynamic for dynamic images.
+
+	@param pixmap Source image.
 	
-	#Import "<std>"
-	#Import "<mojo>"
+	@param textureFlags Image texture flags. 
 	
-	Using std..
-	Using mojo..
+	@param shader Image shader.
 	
-	Class MyWindow Extends Window
+	@param image Source pixmap.
 	
-		Field image1:Image
-		Field image2:Image
-		Field image3:Image
-			
-		Method New()
+	@param rect Source rect.
 	
-			'Create an image from a pixmap
-			Local pixmap:=New Pixmap( 16,16 )
-			pixmap.Clear( Color.Red )
-			image1=New Image( pixmap )
-			
-			'Create an image and render something to it
-			image2=New Image( 16,16 )
-			Local icanvas:=New Canvas( image2 )
-			icanvas.Color=Color.Yellow
-			icanvas.DrawRect( 0,0,8,8 )
-			icanvas.DrawRect( 8,8,8,8 )
-			icanvas.Color=Color.LightGrey
-			icanvas.DrawRect( 8,0,8,8 )
-			icanvas.DrawRect( 0,8,8,8 )
-			icanvas.Flush() 'Important!
-			
-			'Create a image from an atlas image
-			image3=New Image( image2,New Recti( 4,4,12,12 ) )
-			
-		End
-	
-		Method OnRender( canvas:Canvas ) Override
-		
-			canvas.DrawText( "Image1",0,0 )
-			canvas.DrawImage( image1,0,16 )
-			
-			canvas.DrawText( "Image2",0,40 )
-			canvas.DrawImage( image2,0,56 )
-	
-			canvas.DrawText( "Image3",0,80 )
-			canvas.DrawImage( image3,0,96 )
-			
-		End
-		
-	End
-	
-	Function Main()
-	
-		New AppInstance
-		
-		New MyWindow
-		
-		App.Run()
-	End
+	@param x,y,width,height Source rect
 	
-	@end
+	@param width,height Image size.
 	
-	#end
-	Method New( pixmap:Pixmap,textureFlags:TextureFlags=TextureFlags.Filter|TextureFlags.Mipmap,shader:Shader=Null )
+	#end	
+	Method New( pixmap:Pixmap,textureFlags:TextureFlags=Null,shader:Shader=Null )
 	
 		Local texture:=New Texture( pixmap,textureFlags )
 		
-		Init( Null,texture,texture.Rect,shader )
+		Init( texture,texture.Rect,shader )
 		
 		OnDiscarded+=Lambda()
 			texture.Discard()
 		End
 	End
+
+	Method New( width:Int,height:Int,textureFlags:TextureFlags=Null,shader:Shader=Null )
 	
-	Method New( width:Int,height:Int,textureFlags:TextureFlags=TextureFlags.Filter|TextureFlags.Mipmap,shader:Shader=Null )
-	
-		Local textureFormat:PixelFormat=PixelFormat.RGBA32
+		Local texture:=New Texture( width,height,PixelFormat.RGBA32,textureFlags )
 		
-		Local texture:=New Texture( width,height,textureFormat,textureFlags )
+		Init( texture,texture.Rect,shader )
 		
-		Init( Null,texture,texture.Rect,shader )
-
 		OnDiscarded+=Lambda()
 			texture.Discard()
 		End
+	End
 
+	Method New( image:Image )
+	
+		Init( image._textures[0],image._rect,image._shader )
+		
+		For Local i:=1 Until 4
+			SetTexture( i,image.GetTexture( i ) )
+		Next
+		
+		BlendMode=image.BlendMode
+		TextureFilter=image.TextureFilter
+		LightDepth=image.LightDepth
+		Handle=image.Handle
+		Scale=image.Scale
+		Color=image.Color
 	End
 	
 	Method New( image:Image,rect:Recti )
 	
-		Init( image._material,image._texture,rect,Null )
+		Init( image._textures[0],rect+image._rect.Origin,image._shader )
+		
+		For Local i:=1 Until 4
+			SetTexture( i,image.GetTexture( i ) )
+		Next
+		
+		BlendMode=image.BlendMode
+		TextureFilter=image.TextureFilter
+		LightDepth=image.LightDepth
+		Handle=image.Handle
+		Scale=image.Scale
+		Color=image.Color
 	End
-
-	#rem monkeydoc @hidden
-	#end
-	Method New( texture:Texture,shader:Shader=Null )
 	
-		Init( Null,texture,texture.Rect,shader )
+	Method New( image:Image,x:Int,y:Int,width:Int,height:Int )
+	
+		Self.New( image,New Recti( x,y,x+width,y+height ) )
 	End
 	
 	#rem monkeydoc @hidden
 	#end
-	Method New( material:Material,texture:Texture,rect:Recti )
-	
-		Init( material,texture,rect,Null )
+	Method New( texture:Texture,shader:Shader=Null )
+
+		Init( texture,texture.Rect,shader )
 	End
 	
 	#rem monkeydoc @hidden
 	#end
-	Property Material:Material()
-	
-		Return _material
+	Method New( texture:Texture,rect:Recti,shader:Shader=Null )
+		Init( texture,rect,shader )
 	End
 	
-	#rem monkeydoc @hidden
-	#end
+	#rem monkeydoc The image's primary texture.
+	#end	
 	Property Texture:Texture()
 	
-		Return _texture
-	End
+		Return _textures[0]
 	
-	#rem monkeydoc @hidden The image's rect within its texture.
-	#end
-	Property Rect:Recti()
+	Setter( texture:Texture )
 	
-		Return _rect
+		SetTexture( 0,texture )
 	End
+
+	#rem monkeydoc The image's texture rect.
 	
-	#rem monkeydoc The width of the image's rect within its texture.
-	#end
-	Property Width:Int()
-	
-		Return _rect.Width
-	End
+	Describes the rect the image occupies within its primary texture.
 	
-	#rem monkeydoc The height of the image's rect within its texture.
 	#end
-	Property Height:Int()
+	Property Rect:Recti()
 	
-		Return _rect.Height
+		Return _rect
 	End
-
+	
 	#rem monkeydoc The image handle.
 	
 	Image handle values are fractional, where 0,0 is the top-left of the image and 1,1 is the bottom-right.
@@ -187,7 +152,7 @@ Class Image
 	
 	The scale property provides a simple way to 'pre-scale' an image.
 	
-	Scaling an image this way is faster than using one of the 'scale' parameters of [[Canvas.DrawImage]].
+	For images with a constant scale, Scaling an image this way is faster than using one of the 'scale' parameters of [[Canvas.DrawImage]].
 	
 	#end
 	Property Scale:Vec2f()
@@ -200,7 +165,87 @@ Class Image
 		
 		UpdateVertices()
 	End
+
+	#rem monkeydoc The image blend mode.
+	
+	The blend mode used to draw the image.
+	
+	If set to BlendMode.None, the canvas blend mode is used instead.
+	
+	Defaults to BlendMode.None.
+	
+	#end	
+	Property BlendMode:BlendMode()
+	
+		Return _blendMode
+		
+	Setter( blendMode:BlendMode )
+	
+		_blendMode=blendMode
+	End
+	
+	#rem monkeydoc The image texture filter.
+	
+	The texture flags used to draw the image.
+	
+	If set to TextureFilter.None, the canvas texture filter is used instead.
+	
+	Defaults to TextureFilter.None
+	
+	#end	
+	Property TextureFilter:TextureFilter()
+	
+		Return _textureFilter
+		
+	Setter( filter:TextureFilter )
+	
+		_textureFilter=filter
+	End
+	
+	#rem monkeydoc The image color.
 	
+	The color used to draw the image.
+	
+	Image color is multiplied by canvas color to achieve the final rendering color.
+	
+	Defaults to white.
+	
+	#end	
+	Property Color:Color()
+	
+		Return _color
+	
+	Setter( color:Color )
+	
+		_color=color
+		
+		_material.SetVector( "mx2_ImageColor",_color )
+	End
+
+	#rem monkeydoc The image light depth.
+	#end
+	Property LightDepth:Float()
+	
+		Return _lightDepth
+	
+	Setter( depth:Float )
+	
+		_lightDepth=depth
+		
+		_material.SetScalar( "mx2_LightDepth",_lightDepth )
+	End
+
+	#rem monkeydoc Shadow caster attached to image.
+	#end	
+	Property ShadowCaster:ShadowCaster()
+	
+		Return _shadowCaster
+		
+	Setter( shadowCaster:ShadowCaster )
+	
+		_shadowCaster=shadowCaster
+	End
+
 	#rem monkeydoc The image bounds.
 	
 	The bounds rect represents the actual image vertices used when the image is drawn.
@@ -213,33 +258,75 @@ Class Image
 		Return _bounds
 	End
 
-	#rem monkeydoc Image radius.
+	#rem monkeydoc Image bounds width.
+	#end	
+	Property Width:Float()
 	
-	The radius property returns the radius of the [[Bounds]] rect.
+		Return _bounds.Width
+	End
 	
-	Image bounds are affected by [[Scale]] and [[Handle]], and can be used for simple collision detection.
+	#rem monkeydoc Image bounds height.
+	#end	
+	Property Height:Float()
 	
+		Return _bounds.Height
+	End
+
+	#rem monkeydoc Image bounds radius.
 	#end
 	Property Radius:Float()
 	
 		Return _radius
 	End
+
+	#rem monkeydoc Image shader.
+	#end
+	Property Shader:Shader()
+	
+		Return _shader
+	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Image material.
 	#end
+	Property Material:UniformBlock()
+	
+		Return _material
+	End
+
+	#rem monkeydoc @hidden Image vertices.
+	#end	
 	Property Vertices:Rectf()
 	
 		Return _vertices
 	End
 	
-	#rem monkeydoc @hidden
-	#end
+	#rem monkeydoc @hidden Image texture coorinates.
+	#end	
 	Property TexCoords:Rectf()
 	
 		Return _texCoords
 	End
+
+	#rem monkeydoc @hidden Sets an image texture.
+	#end	
+	Method SetTexture( index:Int,texture:Texture )
+	
+		_textures[index]=texture
+		
+		_material.SetTexture( "mx2_ImageTexture"+index,texture )
+	End
+	
+	#rem monkeydoc @hidden gets an image texture.
+	#end	
+	Method GetTexture:Texture( index:Int )
+	
+		Return _textures[index]
+	End
+	
+	#rem monkeydoc Discards the image.
+	
+	Discards the image and releases any resources held by the image.
 	
-	#rem monkeydoc Releases the image and any resource it uses.
 	#end
 	Method Discard()
 		If _discarded Return
@@ -247,73 +334,192 @@ Class Image
 		OnDiscarded()
 	End
 	
-	#rem monkeydoc Loads an image from a file.
+	#rem monkeydoc Loads an image from file.
 	#end
-	Function Load:Image( path:String,textureFlags:TextureFlags=TextureFlags.Filter|TextureFlags.Mipmap,shader:Shader=Null )
+	Function Load:Image( path:String,shader:Shader=Null )
 	
-		Local diffuse:=mojo.graphics.Texture.Load( path,textureFlags )
-		If Not diffuse Return Null
-		
-		Local file:=StripExt( path )
-		Local ext:=ExtractExt( path )
+		If Not shader shader=mojo.graphics.Shader.GetShader( "sprite" )
+	
+		Local texture:=mojo.graphics.Texture.Load( path,Null )
+		If Not texture Return Null
 		
-		Local specular:=mojo.graphics.Texture.Load( file+"_SPECULAR"+ext,textureFlags )
-		Local normal:=mojo.graphics.Texture.Load( file+"_NORMALS"+ext,textureFlags )
+		Return New Image( texture,shader )
+	End
+	
+	#rem monkeydoc Loads a bump image from file(s).
+	
+	`diffuse`, `normal` and `specular` are filepaths of the diffuse, normal and specular image files respectively.
+	
+	`specular` can be null, in which case `specularScale` is used for the specular component. Otherwise, `specularScale` is used to modulate the specular components of the 
+	specular texture.
+	
+	#end
+	Function LoadBump:Image( diffuse:String,normal:String,specular:String,specularScale:Float=1,flipNormalY:Bool=True,shader:Shader=Null )
+	
+		If Not shader shader=mojo.graphics.Shader.GetShader( "bump" )
+
+		Local pdiff:=Pixmap.Load( diffuse )
+		Local pnorm:=Pixmap.Load( normal )
+		Local pspec:=Pixmap.Load( specular )
 		
-		If specular Or normal
-			If Not specular specular=mojo.graphics.Texture.ColorTexture( Color.Black )
-			If Not normal normal=mojo.graphics.Texture.ColorTexture( New Color( .5,.5,.5 ) )
+		If pdiff
+			pdiff.PremultiplyAlpha()
+		Else
+			pdiff=New Pixmap( pnorm.Width,pnorm.Height,PixelFormat.I8 )
+			pdiff.Clear( std.graphics.Color.White )
 		Endif
 		
-		If Not shader
-			If specular Or normal
-				shader=Shader.GetShader( "phong" )
-			Else
-				shader=Shader.GetShader( "sprite" )
-			Endif
+		Local yxor:=flipNormalY ? $ff00 Else 0
+		
+		If pspec And pspec.Width=pnorm.Width And pspec.Height=pnorm.Height
+			For Local y:=0 Until pnorm.Height
+				For Local x:=0 Until pnorm.Width
+					Local n:=pnorm.GetPixelARGB( x,y ) ~ yxor
+					Local s:=(pspec.GetPixelARGB( x,y ) Shr 16) & $ff
+					n=n & $ffffff00 | Clamp( Int( specularScale * s ),1,255 )
+					pnorm.SetPixelARGB( x,y,n )
+				Next
+			Next
+			pspec.Discard()
+		Else
+			Local g:=Clamp( Int( specularScale * 255.0 ),1,255 )
+			For Local y:=0 Until pnorm.Height
+				For Local x:=0 Until pnorm.Width
+					Local n:=pnorm.GetPixelARGB( x,y ) ~ yxor
+					n=n & $ffffff00 | g
+					pnorm.SetPixelARGB( x,y,n )
+				Next
+			Next
+			If pspec pspec.Discard()
 		Endif
 		
-		Local material:=New Material( shader )
+		Local texture0:=New Texture( pdiff,Null )
+		Local texture1:=New Texture( pnorm,Null )
 		
-		If diffuse material.SetTexture( "DiffuseTexture",diffuse )
-		If specular material.SetTexture( "SpecularTexture",specular )
-		If normal material.SetTexture( "NormalTexture",normal )
+		Local image:=New Image( texture0,texture0.Rect,shader )
+		image.SetTexture( 1,texture1 )
 		
-		Local image:=New Image( material,diffuse,diffuse.Rect )
+		image.OnDiscarded+=Lambda()
+			texture0.Discard()
+			texture1.Discard()
+			pdiff.Discard()
+			pnorm.Discard()
+		End
+	
+		Return image
+	End
+	
+	#rem monkeydoc Loads a light image from file.
+	#end
+	Function LoadLight:Image( path:String,shader:Shader=Null )
+	
+		Local pixmap:=Pixmap.Load( path )
+		If Not pixmap Return Null
+		
+		If Not shader shader=mojo.graphics.Shader.GetShader( "light" )
+	
+		Select pixmap.Format
+		Case PixelFormat.IA16,PixelFormat.RGBA32
+		
+			pixmap.PremultiplyAlpha()
+			
+		Case PixelFormat.A8
+
+			Local tpixmap:=pixmap
+			pixmap=pixmap.Convert( PixelFormat.IA16 )
+			tpixmap.Discard()
+
+			'Copy A->I
+			For Local y:=0 Until pixmap.Height
+				Local p:=pixmap.PixelPtr( 0,y )
+				For Local x:=0 Until pixmap.Width
+					p[0]=p[1]
+					p+=2
+				Next
+			Next
+
+		Case PixelFormat.I8
+		
+			Local tpixmap:=pixmap
+			pixmap=pixmap.Convert( PixelFormat.IA16 )
+			tpixmap.Discard()
+			
+			'Copy I->A
+			For Local y:=0 Until pixmap.Height
+				Local p:=pixmap.PixelPtr( 0,y )
+				For Local x:=0 Until pixmap.Width
+					p[1]=p[0]
+					p+=2
+				Next
+			Next
+
+		Case PixelFormat.RGB24
+		
+			Local tpixmap:=pixmap
+			pixmap=pixmap.Convert( PixelFormat.RGBA32 )
+			tpixmap.Discard()
+			
+			'Copy R->A
+			For Local y:=0 Until pixmap.Height
+				Local p:=pixmap.PixelPtr( 0,y )
+				For Local x:=0 Until pixmap.Width
+					p[3]=p[0]
+					p+=4
+				Next
+			Next
+		
+		End
+		
+		Local texture:=New Texture( pixmap,Null )
+		
+		Local image:=New Image( texture,shader )
 		
 		image.OnDiscarded+=Lambda()
-			If diffuse diffuse.Discard()
-			If specular specular.Discard()
-			If normal normal.Discard()
+			pixmap.Discard()
 		End
 		
 		Return image
 	End
-	
+
 	Private
 	
-	Field _material:Material
-	Field _texture:Texture
-	Field _rect:Recti
+	Field _shader:Shader
+	Field _material:UniformBlock
+
 	Field _discarded:Bool
-	Field _handle:=New Vec2f( 0,0 )
-	Field _scale:=New Vec2f( 1,1 )
+	
+	Field _textures:=New Texture[4]
+	Field _blendMode:BlendMode
+	Field _textureFilter:TextureFilter
+	Field _color:Color
+	Field _lightDepth:Float
+	Field _shadowCaster:ShadowCaster
+	
+	Field _rect:Recti
+	Field _handle:Vec2f
+	Field _scale:Vec2f
+	
 	Field _vertices:Rectf
 	Field _texCoords:Rectf
 	Field _bounds:Rectf
 	Field _radius:Float
 	
-	Method Init( material:Material,texture:Texture,rect:Recti,shader:Shader )
+	Method Init( texture:Texture,rect:Recti,shader:Shader )
+	
+		If Not shader shader=Shader.GetShader( "sprite" )
+	
+		_rect=rect
+		_shader=shader
+		_material=New UniformBlock
 		
-		If Not material
-			If Not shader shader=Shader.GetShader( "sprite" )
-			material=New Material( shader )
-			material.SetTexture( "DiffuseTexture",texture )
-		Endif
+		SetTexture( 0,texture )
 		
-		_material=material
-		_texture=texture
-		_rect=rect
+		BlendMode=BlendMode.None
+		TextureFilter=TextureFilter.None
+		Color=Color.White
+		LightDepth=100
+		Handle=New Vec2f( 0 )
+		Scale=New Vec2f( 1 )
 		
 		UpdateVertices()
 		UpdateTexCoords()
@@ -336,10 +542,10 @@ Class Image
 	End
 	
 	Method UpdateTexCoords()
-		_texCoords.min.x=Float(_rect.min.x)/_texture.Width
-		_texCoords.min.y=Float(_rect.min.y)/_texture.Height
-		_texCoords.max.x=Float(_rect.max.x)/_texture.Width
-		_texCoords.max.y=Float(_rect.max.y)/_texture.Height
+		_texCoords.min.x=Float(_rect.min.x)/_textures[0].Width
+		_texCoords.min.y=Float(_rect.min.y)/_textures[0].Height
+		_texCoords.max.x=Float(_rect.max.x)/_textures[0].Width
+		_texCoords.max.y=Float(_rect.max.y)/_textures[0].Height
 	End
 	
 End

+ 91 - 0
modules/mojo/graphics/indexbuffer.monkey2

@@ -0,0 +1,91 @@
+
+Namespace mojo.graphics
+
+
+#rem monkeydoc @hidden
+#end	
+Class IndexBuffer
+
+	Method New( capacity:Int )
+	
+		_capacity=capacity
+		
+		_data=New UShort[_capacity]
+	End
+	
+	Property Capacity:Int()
+	
+		Return _capacity
+	End
+
+	Property Length:Int()
+	
+		Return _length
+	End
+	
+	Property Pitch:Int()
+	
+		Return 2
+	End
+	
+	Method Clear()
+	
+		_length=0
+		_clean=0
+	End
+	
+	Method AddIndices:UShort Ptr( count:Int )
+		If _length+count>_capacity Return Null
+		
+		Local p:=_data.Data+_length
+		_length+=count
+		
+		Return p
+	End
+	
+	'***** INTERNAL *****
+	
+	Method Bind()
+	
+		If _seq<>glGraphicsSeq
+			_seq=glGraphicsSeq
+			_clean=0
+			glGenBuffers( 1,Varptr _glvbo )
+			glBindBuffer( GL_ELEMENT_ARRAY_BUFFER,_glvbo )
+			glBufferData( GL_ELEMENT_ARRAY_BUFFER,_capacity * Pitch,Null,GL_STATIC_DRAW )
+		Else
+			glBindBuffer( GL_ELEMENT_ARRAY_BUFFER,_glvbo )
+		Endif
+
+	End
+	
+	Method Validate()
+	
+		If _clean=_length Return
+		
+		_clean=_length
+		
+		'mythical 'orphaning'...
+'		glBufferData( GL_ELEMENT_ARRAY_BUFFER,_capacity * Pitch,Null,GL_STATIC_DRAW )
+
+'		glBufferSubData( GL_ELEMENT_ARRAY_BUFFER,0,_length*Pitch,_data.Data )
+
+		'lazy - but fastest?
+		glBufferData( GL_ELEMENT_ARRAY_BUFFER,_length*Pitch,_data.Data,GL_STATIC_DRAW )
+	End
+		
+	Private
+	
+	Field _capacity:Int
+	
+	Field _length:Int
+	
+	Field _clean:Int
+	
+	Field _data:UShort[]
+	
+	Field _glvbo:GLuint
+	
+	Field _seq:Int
+
+End

+ 0 - 50
modules/mojo/graphics/material.monkey2

@@ -1,50 +0,0 @@
-
-Namespace mojo.graphics
-
-#rem monkeydoc @hidden
-#end
-Class Material
-
-	Method New( shader:Shader )
-
-		_shader=shader
-	End
-	
-	Property Shader:Shader()
-	
-		Return _shader
-	End
-	
-	#rem monkeydoc @hidden
-	#end
-	Property Params:ParamBuffer()
-	
-		Return _params
-	End
-	
-	Method SetVector( name:String,value:Vec4f )
-	
-		_params.SetVector( name,value )
-	End
-
-	Method SetMatrix( name:String,value:Mat4f )
-	
-		_params.SetMatrix( name,value )
-	End
-
-	Method SetTexture( name:String,value:Texture )
-	
-		_params.SetTexture( name,value )
-	End
-
-	Method SetColor( name:String,value:Color )
-	
-		_params.SetColor( name,value )
-	End
-	
-	Private
-	
-	Field _shader:Shader
-	Field _params:=New ParamBuffer
-	
-End

+ 248 - 254
modules/mojo/graphics/shader.monkey2

@@ -1,338 +1,332 @@
 
 Namespace mojo.graphics
 
-Private
-
-Function BindUniforms( uniforms:Uniform[],params:ParamBuffer,filter:Bool )
+#Import "shaders/@/shaders"
 
-	For Local u:=Eachin uniforms
-	
-		Local p:=params._params[u.id]
-		
-		Select u.type
-		Case GL_FLOAT
-			glUniform1f( u.location,p.scalar )
-		Case GL_FLOAT_VEC4
-			glUniform4fv( u.location,1,Varptr p.vector.x )
-		Case GL_FLOAT_MAT4
-			glUniformMatrix4fv( u.location,1,False,Varptr p.matrix.i.x )
-		Case GL_SAMPLER_2D
-			Local tex:=p.texture
-			DebugAssert( tex,"Can't bind shader texture uniform '"+u.name+"' - no texture!" )
-			glActiveTexture( GL_TEXTURE0+u.texunit )
-			Local gltex:=tex.GLTexture
-			If gltex
-				glBindTexture( GL_TEXTURE_2D,tex.GLTexture )
-				If (tex.Flags & TextureFlags.Filter) And filter
-					glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR )
-				Else
-					glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST )
-				Endif
-				glUniform1i( u.location,u.texunit )
-			Endif
-		Default
-			Assert( False,"Unsupported uniform type for param:"+u.name )
-		End
-
-	Next
-	
-	glActiveTexture( GL_TEXTURE0 )
-End
+Private
 
-Class Uniform
+Class GLUniform
 
 	Field name:String
-	Field id:Int
 	Field location:Int
 	Field texunit:Int
 	Field size:Int
 	Field type:Int
-	
+	Field uniformId:Int
+	Field blockId:Int
+
 	Method New( name:String,location:Int,texunit:Int,size:Int,type:Int )
 		Self.name=name
-		Self.id=ShaderParam.ParamId( Self.name )
-		Self.texunit=texunit
 		Self.location=location
+		Self.texunit=texunit
 		Self.size=size
 		Self.type=type
+		
+		Self.uniformId=UniformBlock.GetUniformId( name )
+		Self.blockId=UniformBlock.GetUniformBlockId( name )
 	End
 	
 End
 
-Class ShaderProgram
+Class GLProgram
 
-	Method New( sources:String[] )
+	Field _glprogram:GLuint
+	Field _uniforms:=New GLUniform[4][]
+	Field _textures:=New GLUniform[4][]
+	Field _ublockSeqs:=New Int[4]
 
-		_sources=sources
-	End
-	
-	Property Sources:String[]()
+	Method New( glprogram:GLuint )
 
-		Return _sources
-	End
-	
-	Property EnvUniforms:Uniform[]()
-	
-		Return _envUniforms
-	End
-	
-	Property Uniforms:Uniform[]()
-
-		Return _uniforms
-	End
-	
-	Property GLProgram:GLuint()
-	
-		If _seq=glGraphicsSeq
-			Return _glProgram
-		Endif
+		_glprogram=glprogram
 		
-		BuildProgram()
-
-		EnumUniforms()
-		
-		_seq=glGraphicsSeq
-		
-		Return _glProgram
-	End
-	
-	Private
-	
-	Field _sources:String[]
-	
-	Field _seq:Int
-	Field _glProgram:GLuint
-	Field _envUniforms:Uniform[]
-	Field _uniforms:Uniform[]
-		
-	Method BuildProgram()
-
-		Local csource:=""
-		Local vsource:=""
-		Local fsource:=""
-		
-		For Local source:=Eachin _sources
-			Local i0:=source.Find( "//@vertex" )
-			If i0=-1 
-				Print "Shader source:~n"+source
-				Assert( False,"Can't find //@vertex chunk" )
-			Endif
-			Local i1:=source.Find( "//@fragment" )
-			If i1=-1
-				Print "Shader source:~n"+source
-				Assert( False,"Can't find //@fragment chunk" )
-			Endif
-			
-			csource+=source.Slice( 0,i0 )+"~n"
-			vsource+=source.Slice( i0,i1 )+"~n"
-			fsource+=source.Slice( i1 )+"~n"
+		Local uniforms:=New Stack<GLUniform>[4]
+		Local textures:=New Stack<GLUniform>[4]
+		For Local i:=0 Until 4
+			uniforms[i]=New Stack<GLUniform>
+			textures[i]=New Stack<GLUniform>
 		Next
-		
-		vsource=csource+vsource
-		fsource=csource+fsource
-		
-		Local vshader:=glCompile( GL_VERTEX_SHADER,vsource )
-		Local fshader:=glCompile( GL_FRAGMENT_SHADER,fsource )
-		
-		_glProgram=glCreateProgram()
-	
-		glAttachShader( _glProgram,vshader )
-		glAttachShader( _glProgram,fshader )
-'		glDeleteShader( vshader )
-'		glDeleteShader( fshader )
-		
-		glBindAttribLocation( _glProgram,0,"mx2_VertexPosition" )
-		glBindAttribLocation( _glProgram,1,"mx2_VertexTexCoord0" )
-		glBindAttribLocation( _glProgram,2,"mx2_VertexTangent" )
-		glBindAttribLocation( _glProgram,3,"mx2_VertexColor" )
-		
-		glLink( _glProgram )
-	End
-	
-	Method EnumUniforms()
-
-		Local envUniforms:=New Stack<Uniform>
-		Local uniforms:=New Stack<Uniform>
-		
+			
 		Local n:Int
-		glGetProgramiv( _glProgram,GL_ACTIVE_UNIFORMS,Varptr n )
-		
+		glGetProgramiv( _glprogram,GL_ACTIVE_UNIFORMS,Varptr n )
+			
 		Local size:Int,type:UInt,length:Int,nameBuf:=New Byte[256],texunit:=0
-		
+			
 		For Local i:=0 Until n
-		
-			glGetActiveUniform( _glProgram,i,nameBuf.Length,Varptr length,Varptr size,Varptr type,Cast<GLchar Ptr>( nameBuf.Data ) )
-
-			Local name:=String.FromCString( nameBuf.Data )
 			
-			Local location:=glGetUniformLocation( _glProgram,name )
+			glGetActiveUniform( _glprogram,i,nameBuf.Length,Varptr length,Varptr size,Varptr type,Cast<GLchar Ptr>( nameBuf.Data ) )
+	
+			Local name:=String.FromCString( nameBuf.Data )
+				
+			Local location:=glGetUniformLocation( _glprogram,name )
 			If location=-1 Continue  'IE fix...
 			
-			Local u:=New Uniform( name,location,texunit,size,type )
-			If name.StartsWith( "mx2_" )
-				envUniforms.Push( u )
-			Else
-				uniforms.Push( u )
-			Endif
+			Local uniform:=New GLUniform( name,location,texunit,size,type )
+			
+			uniforms[uniform.blockId].Push( uniform )
+			
 			Select type
 			Case GL_SAMPLER_2D
+				textures[uniform.blockId].Push( uniform )
 				texunit+=1
 			End
+			
 		Next
 		
-		_envUniforms=envUniforms.ToArray()
-		_uniforms=uniforms.ToArray()
+		For Local i:=0 Until 4
+			_uniforms[i]=uniforms[i].ToArray()
+			_textures[i]=textures[i].ToArray()
+			_ublockSeqs[i]=-1
+		Next
 	End
 	
-End
+	Property GLProgram:GLuint()
+	
+		Return _glprogram
+	End
 
-Public
+	Method ValidateUniforms( ublocks:UniformBlock[],textureFilter:TextureFilter )
 
-#rem monkeydoc @hidden
-#end
-Struct ShaderParam
+		For Local i:=0 Until 4
 
-	Field scalar:Float
-	Field vector:Vec4f
-	Field matrix:Mat4f
-	Field texture:Texture
+			Local ublock:=ublocks[ i ]
+			
+			If Not ublock Or ublock.Seq=_ublockSeqs[i] Continue
+			
+			_ublockSeqs[i]=ublock.Seq
+			
+			For Local u:=Eachin _uniforms[i]
+			
+				Select u.type
+				Case GL_FLOAT
+				
+					glUniform1f( u.location,ublock.GetScalar( u.uniformId ) )
+					
+				Case GL_FLOAT_VEC2
+				
+					glUniform2fv( u.location,1,ublock.GetVector4fv( u.uniformId ) )
+					
+				Case GL_FLOAT_VEC3
+				
+					glUniform3fv( u.location,1,ublock.GetVector4fv( u.uniformId ) )
+					
+				Case GL_FLOAT_VEC4
+				
+					glUniform4fv( u.location,1,ublock.GetVector4fv( u.uniformId ) )
+					
+				Case GL_FLOAT_MAT4
+				
+					glUniformMatrix4fv( u.location,1,False,ublock.GetMatrix4fv( u.uniformId ) )
+					
+				Case GL_SAMPLER_2D
+				
+					glUniform1i( u.location,u.texunit )
+				
+				End
+			
+			Next
+		
+		Next
+		
+		For Local i:=0 Until 4
+		
+			If Not _textures[i] Continue
+			
+			For Local u:=Eachin _textures[i]
 
-	Function ParamId:Int( name:String )
-		Local id:=_ids[name]
-		If id Return id
-		_nextId+=1
-		_ids[name]=_nextId
-'		Print "Shader param "+name+"="+_nextId
-		Return _nextId
-	End
-	
-	Private
+				Local tex:=ublocks[i].GetTexture( u.uniformId )
+				If tex
+					tex.Bind( u.texunit,textureFilter )
+				Else
+					Print( "Can't bind shader texture uniform '"+u.name+"' - no texture!" )
+				Endif
+			
+			Next
+		
+		Next
+		
+		glActiveTexture( GL_TEXTURE0 )
 	
-	Global _nextId:Int
-	Global _ids:=New StringMap<Int>
+	End
+
 End
 
-#rem monkeydoc @hidden
+Public
+
+#rem monkeydoc The Shader class.
 #end
-Class ShaderEnv
+Class Shader
 
-	Method New( sourceCode:String )
-		_source=sourceCode
-		_id=_nextId
-		_nextId+=1
-	End
+	#rem monkeydoc Creates a new shader.
+	#end
+	Method New( name:String,source:String )
 	
-	Property SourceCode:String()
-		Return _source
-	End
+		Assert( Not _shaders.Contains( name ),"Shader with name '"+name+"' already exists" )
+		
+		_shaders[name]=Self
 	
-	Property Id:Int()
-		Return _id
+		_source=source
+		
+		EnumPasses()
 	End
 	
-	Private
+	#rem monkeydoc The shader source code.
+	#end
+	Property Source:String()
 	
-	Field _source:String
-	Field _id:Int
+		Return _source
+	End
 	
-	Global _nextId:=0
+	#rem monkeydoc @hidden The renderpasses the shader is involved in.
+	#end
+	Property RenderPasses:Int[]()
 	
-End
-
-#rem monkeydoc @hidden
-#end
-Class ParamBuffer
-
-	Method SetVector( name:String,value:Vec4f )
-		_params[ ShaderParam.ParamId( name ) ].vector=value
-	End
-
-	Method SetMatrix( name:String,value:Mat4f )
-		_params[ ShaderParam.ParamId( name ) ].matrix=value
-	End
-
-	Method SetTexture( name:String,value:Texture )
-		_params[ ShaderParam.ParamId( name ) ].texture=value
-	End
-
-	Method SetColor( name:String,value:Color )
-		_params[ ShaderParam.ParamId( name ) ].vector=New Vec4f( value.r,value.g,value.b,value.a )
+		Return _rpasses
 	End
-
-	Private
 	
-	Field _params:=New ShaderParam[32]
-End
-
-#rem monkeydoc @hidden
-#end
-Class Shader
-
-	Method New( sourceCode:String )
-		_source=sourceCode
-	End
+	#rem monkeydoc @hidden Renderpass bitmask.
+	#end
+	Property RenderPassMask:Int()
 	
-	Property SourceCode:String()
-		Return _source
+		Return _rpassMask
 	End
 	
+	'***** INTERNAL *****
+	
 	#rem monkeydoc @hidden
 	#end
-	Method Bind( env:ShaderEnv )
+	Method Bind( renderPass:Int )
 	
 		If _seq<>glGraphicsSeq
 			_seq=glGraphicsSeq
-			_bound=Null
-		Endif
-		
-		Local p:=_programs[env.Id]
-		If Not p
-			p=New ShaderProgram( New String[]( env._source,_source ) )
-			_programs[env.Id]=p
+			Rebuild()
 		Endif
-		
-		If _bound=p Return
-		glUseProgram( p.GLProgram )
-		_bound=p
-	End
 	
-	#rem monkeydoc @hidden
-	#end
-	Method BindEnvParams( params:ParamBuffer )
-	
-		BindUniforms( _bound._envUniforms,params,True )
+		glUseProgram( _programs[renderPass].GLProgram )
 	End
 	
 	#rem monkeydoc @hidden
 	#end
-	Method BindParams( params:ParamBuffer,filter:Bool )
+	Method ValidateUniforms( renderPass:Int,ublocks:UniformBlock[],textureFilter:TextureFilter )
 	
-		BindUniforms( _bound._uniforms,params,filter )
+		_programs[renderPass].ValidateUniforms( ublocks,textureFilter )
 	End
-	
-	#rem monkeydoc @hidden
-	#end
-	Function GetShader:Shader( name:String )
 
-		If Not _shaders
-			_shaders=New StringMap<Shader>
-			_shaders["sprite"]=New Shader( stringio.LoadString( "asset::mojo/shader_sprite.glsl" ) )
-			_shaders["phong"]=New Shader( stringio.LoadString( "asset::mojo/shader_phong.glsl" ) )
-			_shaders["font"]=New Shader( stringio.LoadString( "asset::mojo/shader_font.glsl" ) )
-			_shaders["null"]=New Shader( stringio.LoadString( "asset::mojo/shader_null.glsl" ) )
-		Endif
+	#rem monkeydoc Gets a shader with a given name.
+	#end	
+	Function GetShader:Shader( name:String )
+	
+		Local shader:=_shaders[name]
+		If shader Return shader
+		
+		Local source:=LoadString( "asset::shaders/"+name+".glsl" )
+		If Not source Return Null
 		
-		Return _shaders[name]
+		Return New Shader( name,source )
 	End
 
 	Private
 	
+	Global _shaders:=New StringMap<Shader>
+	
 	Field _source:String
-	Field _programs:=New ShaderProgram[16]
+	Field _rpasses:Int[]
+	Field _rpassMask:Int
+	Field _programs:=New GLProgram[8]
+	Field _seq:Int
+	
+	Method EnumPasses()
 
-	Global _seq:Int
-	Global _bound:ShaderProgram
+		Local tag:="//@renderpasses"
+		Local tagi:=_source.Find( tag )
+		If tagi=-1
+			Print "Shader source:~n"+_source
+			RuntimeError( "Can't find '"+tag+"' tag" )
+		Endif
+		tagi+=tag.Length
+		Local tage:=_source.Find( "~n",tagi )
+		If tage=-1 tage=_source.Length
+		Local tagv:=_source.Slice( tagi,tage )
+		Local rpasses:=tagv.Split( "," )
+		If Not rpasses
+			Print "Shader source:~n"+_source
+			RuntimeError( "Invalid renderpasses value: '"+tagv+"'" )
+		Endif
+		_rpasses=New Int[rpasses.Length]
+		For Local i:=0 Until rpasses.Length
+			_rpasses[i]=Int( rpasses[i] )
+			_rpassMask|=(1 Shl _rpasses[i])
+		Next
+		
+	End
 	
-	Global _shaders:StringMap<Shader>
+	Method Rebuild()
+	
+		'Get renderpasses
+		'
+		Local tag:="//@renderpasses"
+		Local tagi:=_source.Find( tag )
+		If tagi=-1
+			Print "Shader source:~n"+_source
+			RuntimeError( "Can't find '"+tag+"' tag" )
+		Endif
+		tagi+=tag.Length
+		Local tage:=_source.Find( "~n",tagi )
+		If tage=-1 tage=_source.Length
+		Local tagv:=_source.Slice( tagi,tage )
+		Local rpasses:=tagv.Split( "," )
+		If Not rpasses
+			Print "Shader source:~n"+_source
+			RuntimeError( "Invalid renderpasses value: '"+tagv+"'" )
+		Endif
+		_rpasses=New Int[rpasses.Length]
+		For Local i:=0 Until rpasses.Length
+			_rpasses[i]=Int( rpasses[i] )
+			_rpassMask|=(1 Shl _rpasses[i])
+		Next
+		
+		'Find vertex/fragment chunks
+		'
+		Local i0:=_source.Find( "//@vertex" )
+		If i0=-1 
+			Print "Shader source:~n"+_source
+			Assert( False,"Can't find //@vertex chunk" )
+		Endif
+		Local i1:=_source.Find( "//@fragment" )
+		If i1=-1
+			Print "Shader source:~n"+_source
+			Assert( False,"Can't find //@fragment chunk" )
+		Endif
+			
+		Local cs:=_source.Slice( 0,i0 )+"~n"
+		Local vs:=cs+_source.Slice( i0,i1 )+"~n"
+		Local fs:=cs+_source.Slice( i1 )+"~n"
+		
+		For Local rpass:=Eachin _rpasses
+		
+			Local defs:="#define MX2_RENDERPASS "+rpass+"~n"
+			
+			Local vshader:=glCompile( GL_VERTEX_SHADER,defs+vs )
+			Local fshader:=glCompile( GL_FRAGMENT_SHADER,defs+fs )
+				
+			Local glprogram:=glCreateProgram()
+			
+			glAttachShader( glprogram,vshader )
+			glAttachShader( glprogram,fshader )
+			glDeleteShader( vshader )
+			glDeleteShader( fshader )
+				
+			glBindAttribLocation( glprogram,0,"mx2_Vertex" )
+			glBindAttribLocation( glprogram,1,"mx2_TexCoord0" )
+			glBindAttribLocation( glprogram,2,"mx2_TexCoord1" )
+			glBindAttribLocation( glprogram,3,"mx2_Color" )
+			
+			glLink( glprogram )
+			
+			Local program:=New GLProgram( glprogram )
+			
+			_programs[rpass]=program
+		Next
+
+	End
 
 End

+ 65 - 0
modules/mojo/graphics/shaders/bump.glsl

@@ -0,0 +1,65 @@
+
+//@renderpasses 0,1,2
+
+varying vec2 texCoord0;
+varying mat2 tanMatrix;
+varying vec4 color;
+
+//@vertex
+
+attribute vec4 mx2_Vertex;
+attribute vec2 mx2_TexCoord0;
+attribute vec2 mx2_TexCoord1;
+attribute vec4 mx2_Color;
+
+uniform mat4 mx2_ModelViewProjectionMatrix;
+
+uniform vec4 mx2_AmbientLight;
+
+uniform vec4 mx2_ImageColor;
+
+void main(){
+
+	texCoord0=mx2_TexCoord0;
+
+	tanMatrix=mat2( mx2_TexCoord1.x,mx2_TexCoord1.y,-mx2_TexCoord1.y,mx2_TexCoord1.x );
+
+#if MX2_RENDERPASS==0
+	color=mx2_AmbientLight * mx2_ImageColor * mx2_Color;
+#else
+	color=mx2_ImageColor * mx2_Color;
+#endif
+	
+	gl_Position=mx2_ModelViewProjectionMatrix * mx2_Vertex;
+}
+
+//@fragment
+
+uniform sampler2D mx2_ImageTexture0;
+
+uniform sampler2D mx2_ImageTexture1;
+
+void main(){
+
+	vec4 diffuse=texture2D( mx2_ImageTexture0,texCoord0 ) * color;
+
+#if MX2_RENDERPASS==0		//ambient
+
+	gl_FragColor=diffuse;
+
+#elif MX2_RENDERPASS==1		//diffuse
+
+	gl_FragColor=diffuse;
+	
+#elif MX2_RENDERPASS==2		//normal
+
+	vec3 normal=texture2D( mx2_ImageTexture1,texCoord0 ).xyz;
+	
+	normal.xy=tanMatrix * (normal.xy * 2.0 - 1.0) * diffuse.a * 0.5 + 0.5;
+	normal.z*=diffuse.a;
+	
+	gl_FragColor=vec4( normal,diffuse.a );
+
+#endif
+
+}

+ 44 - 0
modules/mojo/graphics/shaders/font.glsl

@@ -0,0 +1,44 @@
+
+//@renderpasses 0,1,2
+
+varying vec2 texCoord0;
+varying vec4 color;
+
+//@vertex
+
+attribute vec4 mx2_Vertex;
+attribute vec2 mx2_TexCoord0;
+attribute vec4 mx2_Color;
+
+uniform mat4 mx2_ModelViewProjectionMatrix;
+
+uniform vec4 mx2_ImageColor;
+
+void main(){
+
+	texCoord0=mx2_TexCoord0;
+
+	color=mx2_ImageColor * mx2_Color;
+	
+	gl_Position=mx2_ModelViewProjectionMatrix * mx2_Vertex;
+}
+
+//@fragment
+
+uniform sampler2D mx2_ImageTexture0;
+
+void main(){
+
+	float alpha=texture2D( mx2_ImageTexture0,texCoord0 ).a;
+
+#if MX2_RENDERPASS==0
+
+	gl_FragColor=vec4( alpha ) * color;
+
+#else
+
+	gl_FragColor=vec4( 0.0,0.0,0.0,alpha * color.a );
+	
+#endif
+
+}

+ 92 - 0
modules/mojo/graphics/shaders/light.glsl

@@ -0,0 +1,92 @@
+
+//@renderpasses 4,5
+
+varying vec2 texCoord0;
+varying vec2 lightPos;
+varying vec4 color;
+
+varying vec2 gbufferCoords;
+varying vec2 fragPos;
+
+//@vertex
+
+attribute vec4 mx2_Vertex;
+attribute vec2 mx2_TexCoord0;
+attribute vec2 mx2_TexCoord1;
+attribute vec4 mx2_Color;
+
+uniform mat4 mx2_ModelViewProjectionMatrix;
+
+uniform vec2 mx2_ViewportOrigin;
+uniform vec2 mx2_ViewportSize;
+uniform vec2 mx2_ViewportClip;
+
+uniform vec2 mx2_GBufferScale;
+
+uniform vec4 mx2_ImageColor;
+
+void main(){
+
+	texCoord0=mx2_TexCoord0;
+	lightPos=mx2_TexCoord1;
+	color=mx2_ImageColor * mx2_Color;
+
+	gl_Position=mx2_ModelViewProjectionMatrix * mx2_Vertex;
+	
+	vec2 vpcoords=(gl_Position.xy * 0.5 + 0.5) * mx2_ViewportSize;
+	
+	gbufferCoords=(vpcoords + mx2_ViewportOrigin) * mx2_GBufferScale;
+
+	fragPos=vpcoords;
+	fragPos.y=mx2_ViewportSize.y-fragPos.y;
+	fragPos-=mx2_ViewportClip;
+}
+
+//@fragment
+
+uniform sampler2D mx2_ImageTexture0;			//image texture
+uniform float mx2_LightDepth;
+
+uniform sampler2D mx2_GBuffer0;					//gbuffer diffuse
+uniform sampler2D mx2_GBuffer1;					//gbuffer normal
+
+void main(){
+
+	vec3 normal=texture2D( mx2_GBuffer1,gbufferCoords ).xyz;
+	
+	float gloss=normal.z;
+	
+	normal.xy=normal.xy * 2.0 - 1.0;
+	
+	normal.z=sqrt( 1.0-dot( normal.xy,normal.xy ) );
+	
+	//diffuse...
+	//	
+	vec3 lvec=normalize( vec3( lightPos-fragPos,mx2_LightDepth ) );
+	
+	float ndotl=max( dot( normal,lvec ),0.0 );
+	
+	vec4 tcolor=texture2D( mx2_ImageTexture0,texCoord0 ) * color;
+
+	vec4 diffuse=texture2D( mx2_GBuffer0,gbufferCoords ) * tcolor * ndotl;
+	
+	//specular...
+	//
+	vec3 hvec=normalize( lvec+vec3( 0.0,0.0,1.0 ) );
+	
+	float ndoth=max( dot( normal,hvec ),0.0 );
+	
+	vec4 specular=tcolor * pow( ndoth,128.0 ) * gloss;
+	
+#if MX2_RENDERPASS==5
+
+	float shadow=texture2D( mx2_GBuffer0,gbufferCoords ).a;
+	diffuse*=shadow;
+	specular*=shadow;
+
+#endif
+	
+	//tada!
+	//
+	gl_FragColor=vec4( diffuse.rgb+vec3( specular.a ),0.0 );
+}

+ 27 - 0
modules/mojo/graphics/shaders/null.glsl

@@ -0,0 +1,27 @@
+
+//@renderpasses 0
+
+varying vec4 color;
+
+//@vertex
+
+attribute vec4 mx2_Vertex;
+attribute vec4 mx2_Color;
+
+uniform mat4 mx2_ModelViewProjectionMatrix;
+
+void main(){
+
+	color=mx2_Color;
+	
+	gl_Position=mx2_ModelViewProjectionMatrix * mx2_Vertex;
+	
+	gl_PointSize=1.0;
+}
+
+//@fragment
+
+void main(){
+
+	gl_FragColor=color;
+}

+ 20 - 0
modules/mojo/graphics/shaders/shadow.glsl

@@ -0,0 +1,20 @@
+
+//@renderpasses 4
+
+//@vertex
+
+attribute vec4 mx2_Vertex;
+
+uniform mat4 mx2_ModelViewProjectionMatrix;
+
+void main(){
+
+	gl_Position=mx2_ModelViewProjectionMatrix * mx2_Vertex;
+}
+
+//@fragment
+
+void main(){
+
+	gl_FragColor=vec4( 1.0,1.0,1.0,0.0 );
+}

+ 44 - 0
modules/mojo/graphics/shaders/sprite.glsl

@@ -0,0 +1,44 @@
+
+//@renderpasses 0,1,2
+
+varying vec2 texCoord0;
+varying vec4 color;
+
+//@vertex
+
+attribute vec4 mx2_Vertex;
+attribute vec2 mx2_TexCoord0;
+attribute vec4 mx2_Color;
+
+uniform mat4 mx2_ModelViewProjectionMatrix;
+
+uniform vec4 mx2_ImageColor;
+
+void main(){
+
+	texCoord0=mx2_TexCoord0;
+
+	color=mx2_ImageColor * mx2_Color;
+	
+	gl_Position=mx2_ModelViewProjectionMatrix * mx2_Vertex;
+}
+
+//@fragment
+
+uniform sampler2D mx2_ImageTexture0;
+
+void main(){
+
+#if MX2_RENDERPASS==0
+
+	gl_FragColor=texture2D( mx2_ImageTexture0,texCoord0 ) * color;
+	
+#else
+
+	float alpha=texture2D( mx2_ImageTexture0,texCoord0 ).a * color.a;
+
+	gl_FragColor=vec4( 0.0,0.0,0.0,alpha );
+
+#endif
+
+}

+ 49 - 0
modules/mojo/graphics/shadowcaster.monkey2

@@ -0,0 +1,49 @@
+
+Namespace mojo.graphics
+
+#rem monkeydoc The ShadowCaster class.
+#end
+Class ShadowCaster
+
+	#rem monkeydoc Creates a new shadow caster.
+	#end
+	Method New()
+	End
+	
+	Method New( rect:Rectf )
+	
+		_vertices=New Vec2f[]( rect.TopLeft,rect.TopRight,rect.BottomRight,rect.BottomLeft )
+'		_vertices=New Vec2f[]( rect.TopLeft,rect.BottomLeft,rect.BottomRight,rect.TopRight )
+	End
+	
+	Method New( radius:Float,segments:Int )
+	
+		_vertices=New Vec2f[segments]
+		
+		For Local i:=0 Until segments
+			_vertices[i]=New Vec2f( Cos( i * TwoPi /segments )*radius,Sin( i * TwoPi / segments )*radius )
+		Next
+	End
+	
+	Method New( vertices:Vec2f[] )
+	
+		_vertices=vertices
+	End
+
+	#rem monkeydoc Shadow caster vertices.
+	#end	
+	Property Vertices:Vec2f[]()
+	
+		Return _vertices
+	
+	Setter( vertices:Vec2f[] )
+	
+		_vertices=vertices
+	End
+	
+	Private
+	
+	Field _vertices:Vec2f[]
+	
+End
+

+ 314 - 0
modules/mojo/graphics/test.monkey2

@@ -0,0 +1,314 @@
+
+Namespace mojo3d
+
+#Import "<std>"
+#Import "<gles20>"
+#Import "<mojo>"
+#Import "<mojox>"
+
+Using std..
+Using gles20..
+
+Using mojo.app
+Using mojo.input
+Using mojox
+
+#Import "glutil"
+#Import "texture"
+#Import "uniformblock"
+#Import "shader"
+#Import "vertex"
+#Import "vertexbuffer"
+#Import "indexbuffer"
+#Import "graphicsdevice"
+#Import "shadowcaster"
+#Import "image"
+#Import "font"
+#import "fontloader"
+#Import "canvas"
+
+#Import "Monkey2-logo-48.png"
+#Import "trans_heart.png"
+#Import "assets/"
+
+Class SettingsView Extends TableView
+
+	Method New()
+		AddColumn( "Setting" )
+		AddColumn( "Value" )
+	End
+
+	Method AddRow( label:String,view:View )
+		Local row:=NumRows
+		AddRows( 1 )
+		Self[0,row]=New Label( label )
+		Self[1,row]=view
+	End
+	
+	Method AddText:TextField( label:String,text:String,changed:Void( text:String ) )
+	
+		Local view:=New TextField( text )
+		view.Entered+=Lambda()
+			changed( view.Text )
+		End
+		
+		AddRow( label,view )
+		Return view
+	End
+	
+	Method AddCheck:CheckButton( label:String,value:String,checked:Bool,changed:Void( checked:Bool ) )
+	
+		Local view:=New CheckButton( value )
+		view.Checked=checked
+		view.Clicked+=Lambda()
+			changed( view.Checked )
+		End
+		
+		AddRow( label,view )
+		Return view
+	End
+	
+	Method AddFilePath:FilePathField( label:String,path:String,changed:Void( path:String ) )
+	
+		Local view:=New FilePathField( path )
+		view.FilePathChanged+=Lambda()
+			changed( view.FilePath )
+		End
+		
+		AddRow( label,view )
+		Return view
+	End
+	
+	Method AddOptions:OptionsField( label:String,options:String[],current:Int,changed:Void( current:Int ) )
+	
+		Local view:=New OptionsField( options )
+		view.CurrentChanged+=Lambda()
+			changed( view.Current )
+		End
+		
+		AddRow( label,view )
+		Return view
+	End
+
+End
+
+Class MyWindow Extends GLWindow
+
+	Field _canvas:Canvas
+	
+	Field _logoImage:Image
+	Field _logoCaster:ShadowCaster
+	
+	Field _heartImage:Image
+		
+	Field _bumpImage:Image
+	Field _light:Image
+	
+	Field _imageSize:=512.0
+	Field _imageRepeat:=1
+	
+	Field _diffPath:=""	'"Slate Tiles II_D.png"
+	Field _normPath:="scale_normal.png"	'"Slate Tiles II_N.png"
+	Field _specPath:=""	'"Slate Tiles II_S.png"
+	Field _imageGloss:=0.5
+	
+	Field _lightPath:="pointlight2_light.png"
+	Field _lightColor:=0
+	Field _lightIntensity:=1.0
+	Field _lightSize:=512.0
+	Field _lightDepth:=100.0
+	
+	Field _ambient:Float=0.1
+	
+	Method UpdateImage()
+	
+		_bumpImage=Image.LoadBump( _diffPath,_normPath,_specPath,_imageGloss,True )
+		
+		Local size:=_imageSize/_imageRepeat
+		
+		_bumpImage.Scale=New Vec2f( size/_bumpImage.Width,size/_bumpImage.Height )
+	End
+	
+	Method UpdateLight()
+	
+		Const _colors:=New Color[]( Color.White,Color.Red,Color.Green,Color.Blue )
+		
+		_light=Image.LoadLight( _lightPath )
+	
+		_light.Handle=New Vec2f( .5,.5 )
+		_light.Scale=New Vec2f( _lightSize/_light.Width,_lightSize/_light.Height )
+		_light.Color=_colors[_lightColor]*_lightIntensity
+		_light.Depth=_lightDepth
+		
+	End
+
+	Method New()
+		Super.New( "Window",1024,768 )
+	
+		ChangeDir( AssetsDir() )
+	
+		Local _settings:=New SettingsView
+		
+		_settings.AddText( "Image size",_imageSize,Lambda( text:String )
+			_imageSize=Float( text )
+			UpdateImage()
+		End )
+		
+		_settings.AddText( "Image repeat",_imageRepeat,Lambda( text:String )
+			_imageRepeat=Int( text )
+			UpdateImage()
+		End )
+		
+		_settings.AddFilePath( "Image diffuse",_diffPath,Lambda( path:String )
+			_diffPath=path
+			UpdateImage()
+		End )
+		
+		_settings.AddFilePath( "Image normal",_normPath,Lambda( path:String )
+			_normPath=path
+			UpdateImage()
+		End )
+		
+		_settings.AddFilePath( "Image specular",_specPath,Lambda( path:String )
+			_specPath=path
+			UpdateImage()
+		End )
+		
+		_settings.AddText( "Image gloss",_imageGloss,Lambda( text:String )
+			_imageGloss=Float( text )
+			UpdateImage()
+		End )
+		
+		_settings.AddFilePath( "Light texture",_lightPath,Lambda( path:String )
+			_lightPath=path
+			UpdateLight()
+		End )
+		
+		_settings.AddOptions( "Light color",New String[]( "White","Red","Green","Blue" ),_lightColor,Lambda( index:Int )
+			_lightColor=index
+			UpdateLight()
+		End )
+		
+		_settings.AddText( "Light intensity",_lightIntensity,Lambda( text:String )
+			_lightIntensity=Float( text )
+			UpdateLight()
+		End )
+		
+		_settings.AddText( "Light size",_lightSize,Lambda( text:String )
+			_lightSize=Float( text )
+			UpdateLight()
+		End )
+		
+		_settings.AddText( "Light depth",_lightDepth,Lambda( text:String )
+			_lightDepth=Float( text )
+			UpdateLight()
+		End )
+		
+		_settings.AddText( "Ambient light",_ambient,Lambda( text:String )
+			_ambient=Float( text )
+		End )
+		
+		Local dialog:=New Dialog( "Settings" )
+		dialog.ContentView=_settings
+		dialog.Open()
+	
+		BeginGL()
+	
+		_canvas=New Canvas
+		
+		_logoImage=Image.Load( "asset::monkey2-logo-48.png" )
+		_logoImage.BlendMode=BlendMode.Alpha
+		_logoImage.Handle=New Vec2f( .5,.5 )
+		
+		_logoCaster=New ShadowCaster( _logoImage.Width/2,24 )'_logoImage.Bounds )
+		
+		_heartImage=Image.Load( "asset::trans_heart.png" )
+		_heartImage.BlendMode=BlendMode.Alpha'Additive
+		_heartImage.Scale=New Vec2f( .25,.25 )
+		_heartImage.Handle=New Vec2f( .5,.5 )
+		
+		UpdateImage()
+		
+		UpdateLight()
+		
+		EndGL()
+	End
+	
+	Method OnRender( canvas:mojo.graphics.Canvas ) Override
+	
+		App.RequestRender()
+	
+		Super.OnRender( canvas )
+		
+		canvas.DrawText( "Hello World!",Width/2,0,.5,0 )
+	End
+	
+	Method OnRenderGL() Override
+
+		Global _rotz:=0.0
+		
+		Global _viewport:=New Recti( 32,32,1024-32,768-32 )
+		
+		If Keyboard.KeyDown( Key.Up )
+			_viewport+=New Vec2i( 0,-3 )
+		Else If Keyboard.KeyDown( Key.Down )
+			_viewport+=New Vec2i( 0, 3 )
+		Else If  Keyboard.KeyDown( Key.Left )
+			_viewport+=New Vec2i( -3,0 )
+		Else If  Keyboard.KeyDown( Key.Right )
+			_viewport+=New Vec2i(  3,0 )
+		Endif
+
+		_canvas.AmbientLight=New Color( _ambient,_ambient,_ambient,1 )
+		
+		_canvas.Viewport=_viewport
+		
+		_canvas.Clear( Color.Blue )
+
+		_canvas.PushMatrix()
+		
+		_canvas.Translate( Width/2,Height/2 )
+		
+		_canvas.Rotate( _rotz )
+
+		_canvas.Translate( -Width/2,-Height/2 )
+		
+		For Local x:=0 Until _imageRepeat
+			For Local y:=0 Until _imageRepeat
+				_canvas.DrawImage( 
+				_bumpImage,
+				(Width-_imageRepeat*_bumpImage.Width)/2+x*_bumpImage.Width,
+				(Height-_imageRepeat*_bumpImage.Height)/2+y*_bumpImage.Height )
+			Next
+		Next
+		
+		_canvas.PopMatrix()
+		
+		_canvas.DrawImage( _heartImage,Width/2,Height/3 )
+		
+		_canvas.DrawImage( _logoImage,Width/2,Height/2 )
+		
+		_canvas.Color=Color.Yellow
+		
+		_canvas.DrawText( "Hello World!",0,0 )
+		
+		_canvas.Color=Color.White
+		
+		_canvas.AddShadowCaster( _logoCaster,Width/2,Height/2 )
+
+		_canvas.AddLight( _light,Mouse.X-_viewport.Origin.x,Mouse.Y-_viewport.Origin.y )
+		
+		_canvas.Flush()
+	End
+
+End
+
+Function Main()
+
+	New AppInstance
+	
+	New MyWindow
+	
+	App.Run()
+	
+End

+ 138 - 68
modules/mojo/graphics/texture.monkey2

@@ -5,19 +5,38 @@ Namespace mojo.graphics
 
 | TextureFlags	| Description
 |:--------------|:-----------
-| Filter		| Enable filtering. When the texture is magnified, texel colors are interpolated giving a smooth/blurred result.
-| Mipmap		| Enable mipmapping. When the texture is minified, automatically pre-generated 'mipmaps' are used to give a smoother result.
 | Dynamic		| Texture is frequently updated. This flag should be set if the texture contents are regularly updated and don't need to be preserved.
 
 #end
 Enum TextureFlags
 
-	Filter=			$0001
-	Mipmap=			$0002
-	WrapS=			$0004			'wrap works, but hidden for now...
-	WrapT=			$0008
+	WrapS=			$0001			'wrap works, but hidden for now...
+	WrapT=			$0002
+	Unmanaged=		$0004
+	DisableMipmap=	$0008
+	RenderTarget=	$0010
+	
 	WrapST=			WrapS|WrapT
-	Dynamic=		$1000
+	Dynamic=		Unmanaged|RenderTarget|DisableMipmap
+	
+End
+
+#rem monkeydoc Texture filters.
+
+| TextureFlags	| Description
+|:--------------|:-----------
+| Nearest		| Textures are not filtered.
+| Linear		| Textures are filtered when magnified.
+| Mipmap		| Textures are filtered when magnified and minified.
+
+#end
+Enum TextureFilter
+
+	None=0
+	Nearest=1
+	Linear
+	Mipmap
+	
 End
 
 #rem monkeydoc @hidden
@@ -28,20 +47,18 @@ Class Texture
 	#end
 	Field OnDiscarded:Void()
 
-	Method New( pixmap:Pixmap,flags:TextureFlags=TextureFlags.Filter|TextureFlags.Mipmap )
-		
+	Method New( pixmap:Pixmap,flags:TextureFlags )
+	
 #If Not __DESKTOP_TARGET__
-		If flags & TextureFlags.Mipmap
-			Local tw:=Log2( pixmap.Width ),th:=Log2( pixmap.Height )
-			If tw<>Round( tw ) Or th<>Round( th ) flags&=~TextureFlags.Mipmap
-		Endif
+		Local tw:=Log2( pixmap.Width ),th:=Log2( pixmap.Height )
+		If tw<>Round( tw ) Or th<>Round( th ) flags|=TextureFlags.DisableMipmap
 #Endif
-
 		_rect=New Recti( 0,0,pixmap.Width,pixmap.Height )
 		_format=pixmap.Format
 		_flags=flags
+		_filter=Null
 
-		If flags & TextureFlags.Dynamic
+		If _flags & TextureFlags.Unmanaged
 			PastePixmap( pixmap,0,0 )
 		Else
 			_managed=pixmap
@@ -49,13 +66,18 @@ Class Texture
 		
 	End
 	
-	Method New( width:Int,height:Int,format:PixelFormat=PixelFormat.RGBA32,flags:TextureFlags=TextureFlags.Filter|TextureFlags.Mipmap )
+	Method New( width:Int,height:Int,format:PixelFormat,flags:TextureFlags )
 	
+#If Not __DESKTOP_TARGET__
+		Local tw:=Log2( Width ),th:=Log2( Height )
+		If tw<>Round( tw ) Or th<>Round( th ) flags|=TextureFlags.DisableMipmap
+#Endif
 		_rect=New Recti( 0,0,width,height )
 		_format=format
 		_flags=flags
+		_filter=Null
 		
-		If Not (_flags & TextureFlags.Dynamic)
+		If Not (_flags & TextureFlags.Unmanaged)
 			_managed=New Pixmap( width,height,format )
 			_managed.Clear( Color.Magenta )
 			OnDiscarded+=Lambda()
@@ -63,32 +85,35 @@ Class Texture
 				_managed=Null
 			End
 		Endif
+		
 	End
 	
 	Property Rect:Recti()
+	
 		Return _rect
 	End
 	
 	Property Width:Int()
+	
 		Return _rect.Width
 	End
 	
 	Property Height:Int()
+	
 		Return _rect.Height
 	End
 	
 	Property Format:PixelFormat()
-		Return _format
-	End
 	
-	Property Flags:TextureFlags()
-		Return _flags
+		Return _format
 	End
 	
 	Method Discard()
 		If _discarded Return
 		If _texSeq=glGraphicsSeq glDeleteTextures( 1,Varptr _glTexture )
 		If _fbSeq=glGraphicsSeq glDeleteFramebuffers( 1,Varptr _glFramebuffer )
+		_glTexture=0
+		_glFramebuffer=0
 		_discarded=True
 		OnDiscarded()
 	End
@@ -99,8 +124,7 @@ Class Texture
 
 			_managed.Paste( pixmap,x,y )
 			
-			_texDirty=True
-			
+			_dirty|=Dirty.TexImage|Dirty.Mipmaps
 		Else
 		
 			glPushTexture2d( GLTexture )
@@ -117,13 +141,13 @@ Class Texture
 			
 			glPopTexture2d()
 			
-			_mipsDirty=True
-		
+			_dirty|=Dirty.Mipmaps
+			
 		Endif
 	
 	End
 	
-	Function Load:Texture( path:String,flags:TextureFlags=TextureFlags.Filter|TextureFlags.Mipmap )
+	Function Load:Texture( path:String,flags:TextureFlags )
 	
 		Local pixmap:=Pixmap.Load( path )
 		If Not pixmap Return Null
@@ -153,36 +177,23 @@ Class Texture
 	#rem monkeydoc @hidden
 	#end	
 	Property GLTexture:GLuint()
-		If _discarded Return 0
-'		DebugAssert( Not _discarded,"texture has been discarded" )
 	
-		If _texSeq=glGraphicsSeq And Not _texDirty And Not _mipsDirty Return _glTexture
+		If _texSeq=glGraphicsSeq And Not _dirty Return _glTexture
+		
+		If _discarded Return 0
 		
 		If _texSeq=glGraphicsSeq
 		
 			glPushTexture2d( _glTexture )
 		
 		Else
+
+			_texSeq=glGraphicsSeq
+			_dirty=Dirty.All
 		
 			glGenTextures( 1,Varptr _glTexture )
 
 			glPushTexture2d( _glTexture )
-		
-			If _flags & TextureFlags.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 & TextureFlags.Mipmap) And (_flags & TextureFlags.Filter)
-				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR )
-			Else If _flags & TextureFlags.Mipmap
-				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST_MIPMAP_NEAREST )
-			Else If _flags & TextureFlags.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 & TextureFlags.WrapS
 				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT )
@@ -196,16 +207,33 @@ Class Texture
 				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE )
 			Endif
 			
-			glTexImage2D( GL_TEXTURE_2D,0,glFormat( _format ),Width,Height,0,glFormat( _format ),GL_UNSIGNED_BYTE,Null )
-			
-			_texSeq=glGraphicsSeq
-			_texDirty=True
 		Endif
 		
-		If _texDirty
+		If _dirty & Dirty.Filter
+
+			'mag filter		
+			If _filter=TextureFilter.Mipmap Or _filter=TextureFilter.Linear
+				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR )
+			Else
+				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST )
+			Endif
+
+			'min filter			
+			If _filter=TextureFilter.Mipmap
+				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR )
+			Else If _filter=TextureFilter.Linear
+				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR )
+			Else
+				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST )
+			Endif
 		
-			If _managed
+		Endif
+		
+		If _dirty & Dirty.TexImage
 		
+			glTexImage2D( GL_TEXTURE_2D,0,glFormat( _format ),Width,Height,0,glFormat( _format ),GL_UNSIGNED_BYTE,Null )
+			
+			If _managed
 				glPixelStorei( GL_UNPACK_ALIGNMENT,1 )
 			
 				If _managed.Pitch=_managed.Width*_managed.Depth
@@ -219,26 +247,31 @@ Class Texture
 				glFlush()	'macos nvidia bug!
 				
 			Else
-			
 				Local tmp:=New Pixmap( Width,1,Format )
 				tmp.Clear( Color.Red )
+				
 				For Local iy:=0 Until Height
 					glTexSubImage2D( GL_TEXTURE_2D,0,0,iy,Width,1,glFormat( _format ),GL_UNSIGNED_BYTE,tmp.Data )
 				Next
+				
+				glFlush()	'macos nvidia bug!
+				
 				tmp.Discard()
-			
 			Endif
-			
-			_texDirty=False
-			_mipsDirty=True
+
 		Endif
 		
-		If _mipsDirty
-		
-			If _flags & TextureFlags.Mipmap glGenerateMipmap( GL_TEXTURE_2D )
-			_mipsDirty=False
+		If _dirty & Dirty.Mipmaps
+			If _filter=TextureFilter.Mipmap
+				glGenerateMipmap( GL_TEXTURE_2D )
+				_mipsDirty&=~Dirty.Mipmaps
+			Else
+				_mipsDirty|=Dirty.Mipmaps	'mipmap still dirty!
+			Endif
 		End
-			
+		
+		_dirty=Null
+		
 		glPopTexture2d()
 		
 		Return _glTexture
@@ -258,7 +291,7 @@ Class Texture
 		glBindFramebuffer( GL_FRAMEBUFFER,_glFramebuffer )
 		glFramebufferTexture2D( GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,GLTexture,0 )
 			
-		If glCheckFramebufferStatus( GL_FRAMEBUFFER )<>GL_FRAMEBUFFER_COMPLETE Assert( False,"Incomplete framebuffer" )
+		If glCheckFramebufferStatus( GL_FRAMEBUFFER )<>GL_FRAMEBUFFER_COMPLETE RuntimeError( "Incomplete framebuffer" )
 			
 		glPopFramebuffer()
 		
@@ -266,24 +299,60 @@ Class Texture
 
 		Return _glFramebuffer
 	End
+
+	#rem monkeydoc @hidden
+	#end	
+	Method Bind( unit:Int,filter:TextureFilter )
 	
-	'***** INTERNAL *****
+		If _boundSeq<>glGraphicsSeq
+			_boundSeq=glGraphicsSeq
+			For Local i:=0 Until 8
+				_bound[i]=0
+			Next
+		Endif
+		
+		If filter<>_filter
+			If filter=TextureFilter.Mipmap And (_flags & TextureFlags.DisableMipmap) filter=TextureFilter.Linear
+			If filter<>_filter
+				If filter=TextureFilter.Mipmap _dirty|=_mipsDirty
+				_dirty|=Dirty.Filter
+				_filter=filter
+			Endif
+		Endif
+		
+		Local gltex:=GLTexture
+		If gltex=_bound[unit] Return
+		
+		_bound[unit]=gltex
+		
+		glActiveTexture( GL_TEXTURE0+unit )
+		glBindTexture( GL_TEXTURE_2D,gltex )
+	End
 	
 	#rem monkeydoc @hidden
 	#end
-	Method Modified( device:GraphicsDevice )
+	Method Modified( r:Recti )
 	
 		If _managed
-			Local r:=device.Viewport & device.Scissor
 			glPixelStorei( GL_PACK_ALIGNMENT,1 )
 			glReadPixels( r.X,r.Y,r.Width,r.Height,GL_RGBA,GL_UNSIGNED_BYTE,_managed.PixelPtr( r.X,r.Y ) )
 		Endif
 		
-		If _flags & TextureFlags.Mipmap _mipsDirty=True
+		_dirty|=Dirty.Mipmaps
 	End
 	
 	Private
 	
+	Enum Dirty
+		Filter=		1
+		TexImage=	2
+		Mipmaps=	4
+		All=		7
+	End
+	
+	Global _boundSeq:Int
+	Global _bound:=New GLuint[8]
+	
 	Field _rect:Recti
 	Field _format:PixelFormat
 	Field _flags:TextureFlags
@@ -291,8 +360,9 @@ Class Texture
 	Field _discarded:Bool
 	
 	Field _texSeq:Int
-	Field _texDirty:Bool
-	Field _mipsDirty:Bool
+	Field _dirty:Dirty
+	Field _mipsDirty:Dirty
+	Field _filter:TextureFilter
 	Field _glTexture:GLuint
 	
 	Field _fbSeq:Int

+ 182 - 0
modules/mojo/graphics/uniformblock.monkey2

@@ -0,0 +1,182 @@
+
+Namespace mojo.graphics
+
+#rem monkeydoc The UniformBlock class.
+#end
+Class UniformBlock
+
+	#rem monkeydoc Sets a scalar uniform.
+	#end
+	Method SetScalar( uniform:String,scalar:Float )
+		Local id:=GetUniformId( uniform )
+		_scalars[id]=scalar
+		_seq=_gseq
+		_gseq+=1
+	End
+	
+	#rem monkeydoc Gets a scalar uniform.
+	#end
+	Method GetScalar:Float( uniform:String )
+		Local id:=GetUniformId( uniform )
+		Return _scalars[id]
+	End
+
+	Method GetScalar:Float( id:Int )
+		Return _scalars[id]
+	End
+	
+	#rem monkeydoc Sets a vector uniform.
+	#end
+	Method SetVector( uniform:String,color:Color )
+		Local id:=GetUniformId( uniform )
+		_vectors[id]=New Vec4f( color.r,color.g,color.b,color.a )
+		_seq=_gseq
+		_gseq+=1
+	End
+
+	Method SetVector( uniform:String,vector:Vec2f )
+		Local id:=GetUniformId( uniform )
+		_vectors[id]=New Vec4f( vector.x,vector.y,0,0 )
+		_seq=_gseq
+		_gseq+=1
+	End
+
+	Method SetVector( uniform:String,vector:Vec3f )
+		Local id:=GetUniformId( uniform )
+		_vectors[id]=New Vec4f( vector.x,vector.y,vector.z,0 )
+		_seq=_gseq
+		_gseq+=1
+	End
+	
+	Method SetVector( uniform:String,vector:Vec4f )
+		Local id:=GetUniformId( uniform )
+		_vectors[id]=vector
+		_seq=_gseq
+		_gseq+=1
+	End
+	
+	#rem monkeydoc Gets a vector uniform.
+	#end
+	Method GetVector:Vec4f( uniform:String )
+		Local id:=GetUniformId( uniform )
+		Return _vectors[id]
+	End
+	
+	Method GetVector:Vec4f( id:Int )
+		Return _vectors[id]
+	End
+	
+	#rem monkeydoc @hidden
+	#end	
+	Method GetVector4fv:Float Ptr( id:Int )
+		Return Varptr _vectors[id].x
+	End
+	
+	#rem monkeydoc Sets a matrix uniform.
+	#end
+	Method SetMatrix( uniform:String,matrix:Mat4f )
+		Local id:=GetUniformId( uniform )
+		_matrices[id]=matrix
+		_seq=_gseq
+		_gseq+=1
+	End
+	
+	#rem monkeydoc Gets a matrix uniform.
+	#end
+	Method GetMatrix:Mat4f( uniform:String )
+		Local id:=GetUniformId( uniform )
+		Return _matrices[id]
+	End
+	
+	Method GetMatrix:Mat4f( id:Int )
+		Return _matrices[id]
+	End
+
+	#rem monkeydoc @hidden
+	#end	
+	Method GetMatrix4fv:Float Ptr( id:Int )
+		Return Varptr _matrices[id].i.x
+	End
+
+	#rem monkeydoc Set a texture uniform.
+	#end	
+	Method SetTexture( uniform:String,texture:Texture )
+		Local id:=GetUniformId( uniform )
+		_textures[id]=texture
+		_seq=_gseq
+		_gseq+=1
+	End
+
+	#rem monkeydoc Gets a texture uniform.
+	#end	
+	Method GetTexture:Texture( uniform:String )
+		Local id:=GetUniformId( uniform )
+		Return _textures[id]
+	End
+	
+	Method GetTexture:Texture( id:Int )
+		Return _textures[id]
+	End
+
+	#rem monkeydoc Gets the id of a uniform name.
+	#end	
+	Function GetUniformId:Int( uniform:String )
+		Init()
+		Local id:=_uniformIds[uniform]
+		If Not id
+			id=_uniformIds.Count()+1
+			_uniformIds[uniform]=id
+		Endif
+		Return id
+	End
+
+	#rem monkeydoc @hidden
+	#end	
+	Property Seq:Int()
+		Return _seq
+	End
+	
+	#rem monkeydoc @hidden
+	#end	
+	Function BindUniformsToBlockId( uniforms:String[],index:Int )
+		For Local uniform:=Eachin uniforms
+			_blockIds[ GetUniformId( uniform ) ]=index
+		Next
+	End
+	
+	#rem monkeydoc @hidden
+	#end	
+	Function GetUniformBlockId:Int( uniform:String )
+		Return _blockIds[ GetUniformId( uniform ) ]
+	End
+	
+	Private
+	
+	Field _seq:Int
+	Global _gseq:Int
+
+	Field _scalars:=New Float[MaxUniforms]
+	Field _vectors:=New Vec4f[MaxUniforms]
+	Field _matrices:=New Mat4f[MaxUniforms]
+	Field _textures:=New Texture[MaxUniforms]
+	
+	Global _uniformIds:=New StringMap<Int>
+	Global _blockIds:=New Int[MaxUniforms]
+
+	Const MaxUniforms:=32
+
+	Function Init()
+		Global inited:=False
+		If inited Return	
+		inited=True
+	
+		UniformBlock.BindUniformsToBlockId( New String[]( "mx2_ViewportSize","mx2_ViewportOrigin","mx2_ViewportClip" ),0 )
+		UniformBlock.BindUniformsToBlockId( New String[]( "mx2_AmbientLight" ),0 )
+		UniformBlock.BindUniformsToBlockId( New String[]( "mx2_ModelViewProjectionMatrix" ),0 )
+		UniformBlock.BindUniformsToBlockId( New String[]( "mx2_GBuffer0","GBuffer1","GBufferScale" ),0 )
+
+		UniformBlock.BindUniformsToBlockId( New String[]( "mx2_ImageTexture0","mx2_ImageTexture1" ),1 )
+		UniformBlock.BindUniformsToBlockId( New String[]( "mx2_ImageColor","mx2_LightDepth" ),1 )
+	End
+
+End

+ 6 - 49
modules/mojo/graphics/vertex.monkey2

@@ -2,56 +2,13 @@
 Namespace mojo.graphics
 
 #rem monkeydoc @hidden
-#end	
+#end
 Struct Vertex2f
 
-	Field x:Float,y:Float	'mx2_Vertex
-	Field s0:Float,t0:Float	'mx2_TexCoord0
-	Field ix:Float,iy:Float	'mx2_Tangent
-	Field color:UInt		'mx2_Color
-	
-	Method New()
-	End
-	
-	Method New( position:Vec2f,texCoord0:Vec2f=New Vec2f,tangent:Vec2f=New Vec2f,colorARGB:UInt=$ffffffff )
-		Position=position
-		TexCoord0=texCoord0
-		Tangent=tangent
-		ColorARGB=colorARGB
-	End
-	
-	Method New( x:Float,y:Float,s0:Float=0,t0:Float=0,ix:Float=0,iy:Float=0,colorARGB:UInt=$ffffffff )
-		Self.x=x;Self.y=y
-		Self.s0=s0;Self.t0=t0
-		Self.ix=ix;Self.iy=iy
-		Self.color=colorARGB
-	End
-	
-	Property Position:Vec2f()
-		Return New Vec2f( x,y )
-	Setter( position:Vec2f )
-		x=position.x
-		y=position.y
-	End
-	
-	Property TexCoord0:Vec2f()
-		Return New Vec2f( s0,t0 )
-	Setter( texCoord0:Vec2f )
-		s0=texCoord0.x
-		t0=texCoord0.y
-	End
-	
-	Property Tangent:Vec2f()
-		Return New Vec2f( ix,iy )
-	Setter( tangent:Vec2f )
-		ix=tangent.x
-		iy=tangent.y
-	End
-	
-	Property ColorARGB:UInt()
-		Return color
-	Setter( colorARGB:UInt )
-		color=colorARGB
-	End
+	Field position:Vec2f
+	Field texCoord0:Vec2f
+	Field texCoord1:Vec2f
+	Field color:UInt
 	
 End
+

+ 95 - 0
modules/mojo/graphics/vertexbuffer.monkey2

@@ -0,0 +1,95 @@
+
+Namespace mojo.graphics
+
+#rem monkeydoc @hidden
+#end	
+Class VertexBuffer
+
+	Method New( capacity:Int )
+	
+		_capacity=capacity
+		
+		_data=New Vertex2f[_capacity]
+	End
+	
+	Property Capacity:Int()
+	
+		Return _capacity
+	End
+
+	Property Length:Int()
+	
+		Return _length
+	End
+	
+	Property Pitch:Int()
+	
+		Return 28
+	End
+	
+	Method Clear()
+	
+		_length=0
+		_clean=0
+	End
+	
+	Method AddVertices:Vertex2f Ptr( count:Int )
+		If _length+count>_capacity Return Null
+		
+		Local p:=_data.Data+_length
+		_length+=count
+		
+		Return p
+	End
+	
+	'***** INTERNAL *****
+	
+	Method Bind()
+	
+		If _seq<>glGraphicsSeq
+			_seq=glGraphicsSeq
+			_clean=0
+			glGenBuffers( 1,Varptr _glvbo )
+			glBindBuffer( GL_ARRAY_BUFFER,_glvbo )
+			glBufferData( GL_ARRAY_BUFFER,_capacity * Pitch,Null,GL_DYNAMIC_DRAW )
+		Else
+			glBindBuffer( GL_ARRAY_BUFFER,_glvbo )
+		Endif
+		
+		glEnableVertexAttribArray( 0 ) ; glVertexAttribPointer( 0,2,GL_FLOAT,False,Pitch,Cast<Void Ptr>( 0 ) )
+		glEnableVertexAttribArray( 1 ) ; glVertexAttribPointer( 1,2,GL_FLOAT,False,Pitch,Cast<Void Ptr>( 8 ) )
+		glEnableVertexAttribArray( 2 ) ; glVertexAttribPointer( 2,2,GL_FLOAT,False,Pitch,Cast<Void Ptr>( 16 ) )
+		glEnableVertexAttribArray( 3 ) ; glVertexAttribPointer( 3,4,GL_UNSIGNED_BYTE,True,Pitch,Cast<Void Ptr>( 24 ) )
+
+	End
+	
+	Method Validate()
+	
+		If _clean=_length Return
+		
+		_clean=_length
+		
+		'mythical 'orphaning'...
+'		glBufferData( GL_ARRAY_BUFFER,_capacity*Pitch,Null,GL_DYNAMIC_DRAW )	
+
+'		glBufferSubData( GL_ARRAY_BUFFER,0,_length*Pitch,_data.Data )
+
+		'lazy - but fastest?
+		glBufferData( GL_ARRAY_BUFFER,_length*Pitch,_data.Data,GL_DYNAMIC_DRAW )
+	End
+		
+	Private
+	
+	Field _capacity:Int
+	
+	Field _length:Int
+	
+	Field _clean:Int
+	
+	Field _data:Vertex2f[]
+	
+	Field _glvbo:GLuint
+	
+	Field _seq:Int
+
+End

+ 6 - 3
modules/mojo/mojo.monkey2

@@ -22,15 +22,18 @@ Namespace mojo
 #Import "app/sdl_rwstream.monkey2"
 
 #Import "graphics/canvas"
-#Import "graphics/device"
 #Import "graphics/font"
-#Import "graphics/fontloader_freetype"
+#Import "graphics/fontloader"
 #Import "graphics/glutil"
+#Import "graphics/graphicsdevice"
 #Import "graphics/image"
-#Import "graphics/material"
+#Import "graphics/indexbuffer"
 #Import "graphics/shader"
+#Import "graphics/shadowcaster"
 #Import "graphics/texture"
+#Import "graphics/uniformblock"
 #Import "graphics/vertex"
+#Import "graphics/vertexbuffer"
 
 #Import "input/device"
 #Import "input/keyboard"

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio