Browse Source

Major mojo overhaul!

Mark Sibly 9 years ago
parent
commit
4b22bcb7ed
38 changed files with 2578 additions and 1836 deletions
  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;
-}

File diff suppressed because it is too large
+ 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]
 		Return _glyphs[0]
 	End
 	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
 	#end
 	Method TextWidth:Float( text:String )
 	Method TextWidth:Float( text:String )
 		Local w:=0.0
 		Local w:=0.0
@@ -120,11 +120,11 @@ Class Font
 	'Make this ALWAYS work!	
 	'Make this ALWAYS work!	
 	#rem monkeydoc Loads a font from a ttf file.
 	#rem monkeydoc Loads a font from a ttf file.
 	#end
 	#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" )
 		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
 		Return font
 	End
 	End
@@ -141,4 +141,3 @@ Class Font
 	Global _openFonts:=New StringMap<Font>
 	Global _openFonts:=New StringMap<Font>
 
 
 End
 End
-

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

@@ -13,7 +13,7 @@ Public
 
 
 #rem monkeydoc @hidden
 #rem monkeydoc @hidden
 #end
 #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
 	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()
 	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 )
 	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 )
 	glBindTexture( GL_TEXTURE_2D,tmpi )
 End
 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
 #rem monkeydoc @hidden
 #end
 #end
 Function glPushFramebuffer:Void( framebuf:Int )
 Function glPushFramebuffer:Void( framebuf:Int )
@@ -63,7 +89,7 @@ End
 #end
 #end
 Function glCompile:Int( type:Int,source:String )
 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
 	source="precision mediump float;~n"+source
 #Endif
 #Endif
 	
 	
@@ -72,12 +98,11 @@ Function glCompile:Int( type:Int,source:String )
 	glCompileShader( shader )
 	glCompileShader( shader )
 	glGetShaderiv( shader,GL_COMPILE_STATUS,Varptr tmpi )
 	glGetShaderiv( shader,GL_COMPILE_STATUS,Varptr tmpi )
 	If Not tmpi
 	If Not tmpi
-		Print "Failed to compile fragment shader:"+glGetShaderInfoLogEx( shader )
 		Local lines:=source.Split( "~n" )
 		Local lines:=source.Split( "~n" )
 		For Local i:=0 Until lines.Length
 		For Local i:=0 Until lines.Length
 			Print (i+1)+":~t"+lines[i]
 			Print (i+1)+":~t"+lines[i]
 		Next
 		Next
-		Assert( False,"Compile fragment shader failed" )
+		RuntimeError( "Failed to compile fragment shader:"+glGetShaderInfoLogEx( shader ) )
 	Endif
 	Endif
 	Return shader
 	Return shader
 End
 End
@@ -87,5 +112,5 @@ End
 Function glLink:Void( program:Int )
 Function glLink:Void( program:Int )
 	glLinkProgram( program )
 	glLinkProgram( program )
 	glGetProgramiv( program,GL_LINK_STATUS,Varptr tmpi )
 	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
 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.
 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
 #end
 Class Image
 Class Image
 
 
-	#rem monkeydoc @hidden
+	#rem monkeydoc Invoked after image has ben discarded.
 	#end
 	#end
 	Field OnDiscarded:Void()
 	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 )
 		Local texture:=New Texture( pixmap,textureFlags )
 		
 		
-		Init( Null,texture,texture.Rect,shader )
+		Init( texture,texture.Rect,shader )
 		
 		
 		OnDiscarded+=Lambda()
 		OnDiscarded+=Lambda()
 			texture.Discard()
 			texture.Discard()
 		End
 		End
 	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()
 		OnDiscarded+=Lambda()
 			texture.Discard()
 			texture.Discard()
 		End
 		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
 	End
 	
 	
 	Method New( image:Image,rect:Recti )
 	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
 	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
 	End
 	
 	
 	#rem monkeydoc @hidden
 	#rem monkeydoc @hidden
 	#end
 	#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
 	End
 	
 	
 	#rem monkeydoc @hidden
 	#rem monkeydoc @hidden
 	#end
 	#end
-	Property Material:Material()
-	
-		Return _material
+	Method New( texture:Texture,rect:Recti,shader:Shader=Null )
+		Init( texture,rect,shader )
 	End
 	End
 	
 	
-	#rem monkeydoc @hidden
-	#end
+	#rem monkeydoc The image's primary texture.
+	#end	
 	Property Texture:Texture()
 	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
 	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
 	#end
-	Property Height:Int()
+	Property Rect:Recti()
 	
 	
-		Return _rect.Height
+		Return _rect
 	End
 	End
-
+	
 	#rem monkeydoc The image handle.
 	#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.
 	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.
 	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
 	#end
 	Property Scale:Vec2f()
 	Property Scale:Vec2f()
@@ -200,7 +165,87 @@ Class Image
 		
 		
 		UpdateVertices()
 		UpdateVertices()
 	End
 	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.
 	#rem monkeydoc The image bounds.
 	
 	
 	The bounds rect represents the actual image vertices used when the image is drawn.
 	The bounds rect represents the actual image vertices used when the image is drawn.
@@ -213,33 +258,75 @@ Class Image
 		Return _bounds
 		Return _bounds
 	End
 	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
 	#end
 	Property Radius:Float()
 	Property Radius:Float()
 	
 	
 		Return _radius
 		Return _radius
 	End
 	End
+
+	#rem monkeydoc Image shader.
+	#end
+	Property Shader:Shader()
+	
+		Return _shader
+	End
 	
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Image material.
 	#end
 	#end
+	Property Material:UniformBlock()
+	
+		Return _material
+	End
+
+	#rem monkeydoc @hidden Image vertices.
+	#end	
 	Property Vertices:Rectf()
 	Property Vertices:Rectf()
 	
 	
 		Return _vertices
 		Return _vertices
 	End
 	End
 	
 	
-	#rem monkeydoc @hidden
-	#end
+	#rem monkeydoc @hidden Image texture coorinates.
+	#end	
 	Property TexCoords:Rectf()
 	Property TexCoords:Rectf()
 	
 	
 		Return _texCoords
 		Return _texCoords
 	End
 	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
 	#end
 	Method Discard()
 	Method Discard()
 		If _discarded Return
 		If _discarded Return
@@ -247,73 +334,192 @@ Class Image
 		OnDiscarded()
 		OnDiscarded()
 	End
 	End
 	
 	
-	#rem monkeydoc Loads an image from a file.
+	#rem monkeydoc Loads an image from file.
 	#end
 	#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
 		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
 		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()
 		image.OnDiscarded+=Lambda()
-			If diffuse diffuse.Discard()
-			If specular specular.Discard()
-			If normal normal.Discard()
+			pixmap.Discard()
 		End
 		End
 		
 		
 		Return image
 		Return image
 	End
 	End
-	
+
 	Private
 	Private
 	
 	
-	Field _material:Material
-	Field _texture:Texture
-	Field _rect:Recti
+	Field _shader:Shader
+	Field _material:UniformBlock
+
 	Field _discarded:Bool
 	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 _vertices:Rectf
 	Field _texCoords:Rectf
 	Field _texCoords:Rectf
 	Field _bounds:Rectf
 	Field _bounds:Rectf
 	Field _radius:Float
 	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()
 		UpdateVertices()
 		UpdateTexCoords()
 		UpdateTexCoords()
@@ -336,10 +542,10 @@ Class Image
 	End
 	End
 	
 	
 	Method UpdateTexCoords()
 	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
 	
 	
 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
 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 name:String
-	Field id:Int
 	Field location:Int
 	Field location:Int
 	Field texunit:Int
 	Field texunit:Int
 	Field size:Int
 	Field size:Int
 	Field type:Int
 	Field type:Int
-	
+	Field uniformId:Int
+	Field blockId:Int
+
 	Method New( name:String,location:Int,texunit:Int,size:Int,type:Int )
 	Method New( name:String,location:Int,texunit:Int,size:Int,type:Int )
 		Self.name=name
 		Self.name=name
-		Self.id=ShaderParam.ParamId( Self.name )
-		Self.texunit=texunit
 		Self.location=location
 		Self.location=location
+		Self.texunit=texunit
 		Self.size=size
 		Self.size=size
 		Self.type=type
 		Self.type=type
+		
+		Self.uniformId=UniformBlock.GetUniformId( name )
+		Self.blockId=UniformBlock.GetUniformBlockId( name )
 	End
 	End
 	
 	
 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
 		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
 		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
 		Local size:Int,type:UInt,length:Int,nameBuf:=New Byte[256],texunit:=0
-		
+			
 		For Local i:=0 Until n
 		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...
 			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
 			Select type
 			Case GL_SAMPLER_2D
 			Case GL_SAMPLER_2D
+				textures[uniform.blockId].Push( uniform )
 				texunit+=1
 				texunit+=1
 			End
 			End
+			
 		Next
 		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
 	
 	
-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
 End
 
 
-#rem monkeydoc @hidden
+Public
+
+#rem monkeydoc The Shader class.
 #end
 #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
 	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
 	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
 	End
 	
 	
+	'***** INTERNAL *****
+	
 	#rem monkeydoc @hidden
 	#rem monkeydoc @hidden
 	#end
 	#end
-	Method Bind( env:ShaderEnv )
+	Method Bind( renderPass:Int )
 	
 	
 		If _seq<>glGraphicsSeq
 		If _seq<>glGraphicsSeq
 			_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
 		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
 	End
 	
 	
 	#rem monkeydoc @hidden
 	#rem monkeydoc @hidden
 	#end
 	#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
 	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
 	End
 
 
 	Private
 	Private
 	
 	
+	Global _shaders:=New StringMap<Shader>
+	
 	Field _source:String
 	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
 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
 | 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.
 | 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
 #end
 Enum TextureFlags
 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
 	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
 End
 
 
 #rem monkeydoc @hidden
 #rem monkeydoc @hidden
@@ -28,20 +47,18 @@ Class Texture
 	#end
 	#end
 	Field OnDiscarded:Void()
 	Field OnDiscarded:Void()
 
 
-	Method New( pixmap:Pixmap,flags:TextureFlags=TextureFlags.Filter|TextureFlags.Mipmap )
-		
+	Method New( pixmap:Pixmap,flags:TextureFlags )
+	
 #If Not __DESKTOP_TARGET__
 #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
 #Endif
-
 		_rect=New Recti( 0,0,pixmap.Width,pixmap.Height )
 		_rect=New Recti( 0,0,pixmap.Width,pixmap.Height )
 		_format=pixmap.Format
 		_format=pixmap.Format
 		_flags=flags
 		_flags=flags
+		_filter=Null
 
 
-		If flags & TextureFlags.Dynamic
+		If _flags & TextureFlags.Unmanaged
 			PastePixmap( pixmap,0,0 )
 			PastePixmap( pixmap,0,0 )
 		Else
 		Else
 			_managed=pixmap
 			_managed=pixmap
@@ -49,13 +66,18 @@ Class Texture
 		
 		
 	End
 	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 )
 		_rect=New Recti( 0,0,width,height )
 		_format=format
 		_format=format
 		_flags=flags
 		_flags=flags
+		_filter=Null
 		
 		
-		If Not (_flags & TextureFlags.Dynamic)
+		If Not (_flags & TextureFlags.Unmanaged)
 			_managed=New Pixmap( width,height,format )
 			_managed=New Pixmap( width,height,format )
 			_managed.Clear( Color.Magenta )
 			_managed.Clear( Color.Magenta )
 			OnDiscarded+=Lambda()
 			OnDiscarded+=Lambda()
@@ -63,32 +85,35 @@ Class Texture
 				_managed=Null
 				_managed=Null
 			End
 			End
 		Endif
 		Endif
+		
 	End
 	End
 	
 	
 	Property Rect:Recti()
 	Property Rect:Recti()
+	
 		Return _rect
 		Return _rect
 	End
 	End
 	
 	
 	Property Width:Int()
 	Property Width:Int()
+	
 		Return _rect.Width
 		Return _rect.Width
 	End
 	End
 	
 	
 	Property Height:Int()
 	Property Height:Int()
+	
 		Return _rect.Height
 		Return _rect.Height
 	End
 	End
 	
 	
 	Property Format:PixelFormat()
 	Property Format:PixelFormat()
-		Return _format
-	End
 	
 	
-	Property Flags:TextureFlags()
-		Return _flags
+		Return _format
 	End
 	End
 	
 	
 	Method Discard()
 	Method Discard()
 		If _discarded Return
 		If _discarded Return
 		If _texSeq=glGraphicsSeq glDeleteTextures( 1,Varptr _glTexture )
 		If _texSeq=glGraphicsSeq glDeleteTextures( 1,Varptr _glTexture )
 		If _fbSeq=glGraphicsSeq glDeleteFramebuffers( 1,Varptr _glFramebuffer )
 		If _fbSeq=glGraphicsSeq glDeleteFramebuffers( 1,Varptr _glFramebuffer )
+		_glTexture=0
+		_glFramebuffer=0
 		_discarded=True
 		_discarded=True
 		OnDiscarded()
 		OnDiscarded()
 	End
 	End
@@ -99,8 +124,7 @@ Class Texture
 
 
 			_managed.Paste( pixmap,x,y )
 			_managed.Paste( pixmap,x,y )
 			
 			
-			_texDirty=True
-			
+			_dirty|=Dirty.TexImage|Dirty.Mipmaps
 		Else
 		Else
 		
 		
 			glPushTexture2d( GLTexture )
 			glPushTexture2d( GLTexture )
@@ -117,13 +141,13 @@ Class Texture
 			
 			
 			glPopTexture2d()
 			glPopTexture2d()
 			
 			
-			_mipsDirty=True
-		
+			_dirty|=Dirty.Mipmaps
+			
 		Endif
 		Endif
 	
 	
 	End
 	End
 	
 	
-	Function Load:Texture( path:String,flags:TextureFlags=TextureFlags.Filter|TextureFlags.Mipmap )
+	Function Load:Texture( path:String,flags:TextureFlags )
 	
 	
 		Local pixmap:=Pixmap.Load( path )
 		Local pixmap:=Pixmap.Load( path )
 		If Not pixmap Return Null
 		If Not pixmap Return Null
@@ -153,36 +177,23 @@ Class Texture
 	#rem monkeydoc @hidden
 	#rem monkeydoc @hidden
 	#end	
 	#end	
 	Property GLTexture:GLuint()
 	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
 		If _texSeq=glGraphicsSeq
 		
 		
 			glPushTexture2d( _glTexture )
 			glPushTexture2d( _glTexture )
 		
 		
 		Else
 		Else
+
+			_texSeq=glGraphicsSeq
+			_dirty=Dirty.All
 		
 		
 			glGenTextures( 1,Varptr _glTexture )
 			glGenTextures( 1,Varptr _glTexture )
 
 
 			glPushTexture2d( _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
 			If _flags & TextureFlags.WrapS
 				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT )
 				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 )
 				glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE )
 			Endif
 			Endif
 			
 			
-			glTexImage2D( GL_TEXTURE_2D,0,glFormat( _format ),Width,Height,0,glFormat( _format ),GL_UNSIGNED_BYTE,Null )
-			
-			_texSeq=glGraphicsSeq
-			_texDirty=True
 		Endif
 		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 )
 				glPixelStorei( GL_UNPACK_ALIGNMENT,1 )
 			
 			
 				If _managed.Pitch=_managed.Width*_managed.Depth
 				If _managed.Pitch=_managed.Width*_managed.Depth
@@ -219,26 +247,31 @@ Class Texture
 				glFlush()	'macos nvidia bug!
 				glFlush()	'macos nvidia bug!
 				
 				
 			Else
 			Else
-			
 				Local tmp:=New Pixmap( Width,1,Format )
 				Local tmp:=New Pixmap( Width,1,Format )
 				tmp.Clear( Color.Red )
 				tmp.Clear( Color.Red )
+				
 				For Local iy:=0 Until Height
 				For Local iy:=0 Until Height
 					glTexSubImage2D( GL_TEXTURE_2D,0,0,iy,Width,1,glFormat( _format ),GL_UNSIGNED_BYTE,tmp.Data )
 					glTexSubImage2D( GL_TEXTURE_2D,0,0,iy,Width,1,glFormat( _format ),GL_UNSIGNED_BYTE,tmp.Data )
 				Next
 				Next
+				
+				glFlush()	'macos nvidia bug!
+				
 				tmp.Discard()
 				tmp.Discard()
-			
 			Endif
 			Endif
-			
-			_texDirty=False
-			_mipsDirty=True
+
 		Endif
 		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
 		End
-			
+		
+		_dirty=Null
+		
 		glPopTexture2d()
 		glPopTexture2d()
 		
 		
 		Return _glTexture
 		Return _glTexture
@@ -258,7 +291,7 @@ Class Texture
 		glBindFramebuffer( GL_FRAMEBUFFER,_glFramebuffer )
 		glBindFramebuffer( GL_FRAMEBUFFER,_glFramebuffer )
 		glFramebufferTexture2D( GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,GLTexture,0 )
 		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()
 		glPopFramebuffer()
 		
 		
@@ -266,24 +299,60 @@ Class Texture
 
 
 		Return _glFramebuffer
 		Return _glFramebuffer
 	End
 	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
 	#rem monkeydoc @hidden
 	#end
 	#end
-	Method Modified( device:GraphicsDevice )
+	Method Modified( r:Recti )
 	
 	
 		If _managed
 		If _managed
-			Local r:=device.Viewport & device.Scissor
 			glPixelStorei( GL_PACK_ALIGNMENT,1 )
 			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 ) )
 			glReadPixels( r.X,r.Y,r.Width,r.Height,GL_RGBA,GL_UNSIGNED_BYTE,_managed.PixelPtr( r.X,r.Y ) )
 		Endif
 		Endif
 		
 		
-		If _flags & TextureFlags.Mipmap _mipsDirty=True
+		_dirty|=Dirty.Mipmaps
 	End
 	End
 	
 	
 	Private
 	Private
 	
 	
+	Enum Dirty
+		Filter=		1
+		TexImage=	2
+		Mipmaps=	4
+		All=		7
+	End
+	
+	Global _boundSeq:Int
+	Global _bound:=New GLuint[8]
+	
 	Field _rect:Recti
 	Field _rect:Recti
 	Field _format:PixelFormat
 	Field _format:PixelFormat
 	Field _flags:TextureFlags
 	Field _flags:TextureFlags
@@ -291,8 +360,9 @@ Class Texture
 	Field _discarded:Bool
 	Field _discarded:Bool
 	
 	
 	Field _texSeq:Int
 	Field _texSeq:Int
-	Field _texDirty:Bool
-	Field _mipsDirty:Bool
+	Field _dirty:Dirty
+	Field _mipsDirty:Dirty
+	Field _filter:TextureFilter
 	Field _glTexture:GLuint
 	Field _glTexture:GLuint
 	
 	
 	Field _fbSeq:Int
 	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
 Namespace mojo.graphics
 
 
 #rem monkeydoc @hidden
 #rem monkeydoc @hidden
-#end	
+#end
 Struct Vertex2f
 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
 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 "app/sdl_rwstream.monkey2"
 
 
 #Import "graphics/canvas"
 #Import "graphics/canvas"
-#Import "graphics/device"
 #Import "graphics/font"
 #Import "graphics/font"
-#Import "graphics/fontloader_freetype"
+#Import "graphics/fontloader"
 #Import "graphics/glutil"
 #Import "graphics/glutil"
+#Import "graphics/graphicsdevice"
 #Import "graphics/image"
 #Import "graphics/image"
-#Import "graphics/material"
+#Import "graphics/indexbuffer"
 #Import "graphics/shader"
 #Import "graphics/shader"
+#Import "graphics/shadowcaster"
 #Import "graphics/texture"
 #Import "graphics/texture"
+#Import "graphics/uniformblock"
 #Import "graphics/vertex"
 #Import "graphics/vertex"
+#Import "graphics/vertexbuffer"
 
 
 #Import "input/device"
 #Import "input/device"
 #Import "input/keyboard"
 #Import "input/keyboard"

Some files were not shown because too many files changed in this diff