Pārlūkot izejas kodu

WIP Mesh changes; Added abstract Renderable class.

Mark Sibly 8 gadi atpakaļ
vecāks
revīzija
7cf9f75d8a

+ 0 - 1
modules/mojo3d/graphics/gltf2loader.monkey2

@@ -172,7 +172,6 @@ Class Gltf2Loader
 					Print "Gltf mesh has no indices"
 					Continue
 				Endif
-				
 
 				If prim.POSITION.componentType<>5126 Or prim.POSITION.type<>"VEC3"
 					Print "Gltf invalid nesh POSITION data"

+ 282 - 147
modules/mojo3d/graphics/mesh.monkey2

@@ -15,27 +15,48 @@ Class Mesh Extends Resource
 	
 	#end
 	Method New()
-		
+	
 		_dirty=Null
 		_bounds=Boxf.EmptyBounds
-		_vbuffer=New VertexBuffer( Vertex3fFormat.Instance,0 )
-		_ibuffers=New Stack<IndexBuffer>
+		_vertices=New Stack<Vertex3f>
+		_materials=New Stack<MaterialID>
+'		AddMaterials( 1 )
 	End
 	
-	Method New( mesh:Mesh )
-		_dirty=mesh._dirty
-		_bounds=mesh._bounds
-		_vbuffer=New VertexBuffer( mesh._vbuffer )
-		For Local ibuffer:=Eachin mesh._ibuffers
-			_ibuffers.Push( New IndexBuffer( ibuffer ) )
+	Method New( vertices:Vertex3f[],indices:UInt[] )
+		Self.New()
+		AddVertices( vertices )
+		AddTriangles( indices )
+	End
+		
+	#rem monkeydoc Number of vertices.
+	#end
+	Property NumVertices:Int()
+		
+		Return _vertices.Length
+	End
+	
+	#rem monkeydoc Total number of indices.
+	#end
+	Property NumIndices:int()
+		
+		Local n:=0
+	
+		For Local material:=Eachin _materials
+			n+=material.indices.Length
 		Next
+		
+		Return n
 	End
+		
+	#rem monkeydoc Number of materials.
 	
-	Method New( vertices:Vertex3f[],triangles:UInt[] )
-		Self.New()
+	This will always be at least one.
+	
+	#end
+	Property NumMaterials:Int()
 		
-		AddVertices( vertices )
-		AddTriangles( triangles )
+		Return _materials.Length
 	End
 
 	#rem monkeydoc Mesh bounding box.
@@ -44,12 +65,10 @@ Class Mesh Extends Resource
 		
 		If _dirty & Dirty.Bounds
 			
-			Local vertices:=Cast<Vertex3f Ptr>( _vbuffer.Data )
-			
 			_bounds=Boxf.EmptyBounds
 			
-			For Local i:=0 Until _vbuffer.Length
-				_bounds|=vertices[i].position
+			For Local i:=0 Until _vertices.Length
+				_bounds|=_vertices[i].position
 			Next
 			
 			_dirty&=~Dirty.Bounds
@@ -58,23 +77,6 @@ Class Mesh Extends Resource
 		Return _bounds
 	End
 	
-	#rem monkeydoc Number of vertices.
-	#end
-	Property NumVertices:Int()
-		
-		Return _vbuffer.Length
-	End
-	
-	#rem monkeydoc Number of materials.
-	
-	This will always be at least one.
-	
-	#end
-	Property NumMaterials:Int()
-		
-		Return _ibuffers.Length
-	End
-	
 	#rem monkeydoc Clears the mesh.
 	
 	Removes all vertices and primitives from the mesh, and resets the number of logical materials to '1'.
@@ -82,15 +84,26 @@ Class Mesh Extends Resource
 	#end
 	Method Clear()
 		
-		_vbuffer.Clear()
+		_dirty=Null
+		_bounds=Boxf.EmptyBounds
+		_vertices.Clear()
+		_materials.Clear()
 		
-		For Local ibuffer:=Eachin _ibuffers
-			ibuffer.Clear()
-		Next
+		InvalidateVertices()
+	End
+	
+	Method ClearVertices()
 		
-		_ibuffers.Resize( 1 )
+		ResizeVertices( 0 )
 	End
+	
+	Method ResizeVertices( length:Int )
+		
+		_vertices.Resize( length )
 
+		InvalidateVertices()
+	End
+#rem
 	#rem monkeydoc @hidden
 	#end	
 	Method AddMesh( mesh:Mesh )
@@ -108,82 +121,199 @@ Class Mesh Extends Resource
 			AddTriangles( Cast<UInt Ptr>( ibuffer.Data ),ibuffer.Length )
 		Next
 	End
-	
-	#rem monkeydoc Adds vertices to the mesh.
+#end
+
+	#rem monkeydoc Sets a range of vertices.
 	#end
-	Method AddVertices( vertices:Vertex3f Ptr,count:Int )
+	Method SetVertices( vertices:Vertex3f Ptr,first:Int,count:Int )
+		
+		DebugAssert( first>=0 And count>=0 And first<=_vertices.Length And first+count<=_vertices.Length,"Invalid vertex range" )
+		
+		libc.memcpy( _vertices.Data.Data+first*Vertex3f.Pitch,vertices,count*Vertex3f.Pitch )
 
-		Local p:=_vbuffer.AddVertices( count )
+		InvalidateVertices( first,count )
+	End
+	
+	Method SetVertices( vertices:Vertex3f[] )
+		
+		_vertices.Resize( vertices.Length )
+		
+		SetVertices( vertices.Data,0,vertices.Length )
+	End
+	
+	#rem monkeydoc Sets a single vertex.
+	#end
+	Method SetVertex( index:Int,vertex:Vertex3f )
+		
+		DebugAssert( index>=0 And index<_vertices.Length,"Vertex index out of range" )
 		
-		libc.memcpy( p,vertices,count * _vbuffer.Pitch )
+		_vertices[index]=vertex
 		
-		_dirty|=Dirty.Bounds
+		InvalidateVertices( index,1 )
 	End
 	
+	#rem monkeydoc Adds vertices.
+	#end
+	Method AddVertices( vertices:Vertex3f Ptr,count:Int )
+		
+		Local first:=_vertices.Length
+		
+		_vertices.Resize( first+count )
+		
+		libc.memcpy( _vertices.Data.Data+first*Vertex3f.Pitch,vertices,count * Vertex3f.Pitch )
+		
+		InvalidateVertices( first,count )
+	End
+
 	Method AddVertices( vertices:Vertex3f[] )
 	
 		AddVertices( vertices.Data,vertices.Length )
 	End
 	
-	#rem monkeydoc Adds a single vertex to the mesh
+	#rem monkeydoc Adds a single vertex.
 	#end
 	Method AddVertex( vertex:Vertex3f )
 		
 		AddVertices( Varptr vertex,1 )
 	End
 	
+	#rem monkeydoc Gets all vertices as an array.
+	#end	
+	Method GetVertices:Vertex3f[]()
+		
+		Return _vertices.ToArray()
+	End
+	
+	#rem monkeydoc Gets a single vertex.
+	#end
+	Method GetVertex:Vertex3f( index:Int )
+		
+		DebugAssert( index>=0 And index<_vertices.Length,"Vertex index out of range" )
+		
+		Return _vertices[index]
+	End		
+	
+	#rem monkeydoc Sets tthe triangles for a material in the mesh.
+	
+	`materialid` must be a valid material id in the range 0 to [[NumMaterials]] inclusive.
+	
+	#end
+	Method SetTriangles( indices:UInt Ptr,first:Int,count:Int,materialid:Int=0 )
+		
+		DebugAssert( first Mod 3=0,"First must be a multiple of 3" )
+
+		DebugAssert( count Mod 3=0,"Count must be a multiple of 3" )
+		
+		Local mindices:=GetMaterial( materialid ).indices
+
+		DebugAssert( first>=0 And count>=0 And first<=mindices.Length And first+count<=mindices.Length,"Invalid range" )
+		
+		libc.memcpy( mindices.Data.Data+first*4,indices,count*4 )
+	End
+	
+	Method SetTriangles( indices:UInt[],materialid:Int=0 )
+		
+		SetTriangles( indices.Data,indices.Length,materialid )
+	End
+	
+	Method SetTriangle( index:Int,i0:Int,i1:Int,i2:Int,materialid:Int=0 )
+		
+		DebugAssert( index Mod 3=0,"Index must be a multiple of 3" )
+		
+		Local mindices:=GetMaterial( materialid ).indices
+		
+		DebugAssert( index>=0 And index+3<=mindices.Length,"Triangle index out of range" )
+		
+		mindices[index]=i0;mindices[index+1]=i1;mindices[index+2]=i2
+	End
+	
 	#rem monkeydoc Adds triangles to the mesh.
+
+	`count` is the number of indices to add and must be a multiple of 3.
 	
-	The `materialid` parameter must be greater than or equal to 0 and less or equal to [[NumMaterials]].
+	`materialid` must be a valid material id in the range 0 to [[NumMaterials]] inclusive.
 	
 	If `materialid` is equal to NumMaterials, a new material is automatically added first.
 	
 	#end
 	Method AddTriangles( indices:UInt Ptr,count:Int,materialid:Int=0 )
 		
-		If materialid=_ibuffers.Length AddMaterials( 1 )
+		DebugAssert( count Mod 3=0,"Count must be a multiple of 3" )
 		
-		Local p:=_ibuffers[materialid].AddIndices( count )
+		Local mindices:=GetMaterial( materialid ).indices
 		
-		libc.memcpy( p,indices,count*4 )
+		Local first:=mindices.Length
+		
+		mindices.Resize( mindices.Length+count )
+		
+		libc.memcpy( mindices.Data.Data+first*4,indices,count*4 )
 	End
 	
 	Method AddTriangles( indices:UInt[],materialid:Int=0 )
-	
+		
 		AddTriangles( indices.Data,indices.Length,materialid )
 	End
 
-	#rem  monkeydoc Adds a single triangle the mesh.
+	#rem monkeydoc Adds a single triangle the mesh.
 	#end	
 	Method AddTriangle( i0:UInt,i1:UInt,i2:UInt,materialid:Int=0 )
 		
-		AddTriangles( New UInt[]( i0,i1,i2 ),materialid )
+		Local indices:=GetMaterial( materialid ).indices
+		
+		indices.Add( i0 )
+		indices.Add( i1 )
+		indices.Add( i2 )
+	End
+	
+	#rem monkeydoc Get indices for a material id.
+	#end
+	Method GetIndices:UInt[]( materialid:Int=0 )
+		
+		DebugAssert( materialid>=0 And materialid<_materials.Length,"Material id out of range" )
+		
+		Return _materials[materialid].indices.ToArray()
 	End
 	
+	Method GetAllIndices:Uint[]()
+		
+		Local indices:=New Uint[NumIndices],ip:=indices.Data
+		
+		For Local material:=Eachin _materials
+			Local mindices:=material.indices
+			libc.memcpy( ip,mindices.Data.Data,mindices.Length*4 )
+			ip+=mindices.Length
+		Next
+		
+		Return indices
+	End
+
 	#rem monkeydoc Adds materials to the mesh.
 	
 	Adds `count` logical materials to the mesh.
 	
-	WIP! Eventually want to be able to add lines, points etc to meshes, probably via this method...
+	Returns the first material id of the newly added materials.
 	
 	#end
-	Method AddMaterials( count:Int )
+	Method AddMaterials:Int( count:Int )
+		
+		Local first:=_materials.Length
 		
 		For Local i:=0 Until count
-			
-			_ibuffers.Push( New IndexBuffer( IndexFormat.UINT32,0 ) )
-		End
+			_materials.Push( New MaterialID )
+		Next
+		
+		Return first
 	End
 	
 	#rem monkeydoc Transforms all vertices in the mesh.
 	#end
 	Method TransformVertices( matrix:AffineMat4f )
 		
-		Local vertices:=Cast<Vertex3f Ptr>( _vbuffer.Data )
+		Local vertices:=_vertices.Data
 		
 		Local cofactor:=matrix.m.Cofactor()
 		
-		For Local i:=0 Until _vbuffer.Length
+		For Local i:=0 Until _vertices.Length
 		
 			vertices[i].position=matrix * vertices[i].position
 			
@@ -192,9 +322,7 @@ Class Mesh Extends Resource
 			vertices[i].tangent.XYZ=(cofactor * vertices[i].tangent.XYZ).Normalize()
 		Next
 		
-		_vbuffer.Invalidate()
-		
-		_dirty|=Dirty.Bounds
+		InvalidateVertices()
 	End
 	
 	#rem monkeydoc Fits all vertices in the mesh to a box.
@@ -221,21 +349,18 @@ Class Mesh Extends Resource
 	#end
 	Method UpdateNormals()
 
-		Local vcount:=_vbuffer.Length
+		Local vertices:=_vertices.Data
 		
-		Local vertices:=Cast<Vertex3f Ptr>( _vbuffer.Data )
-		
-		For Local i:=0 Until vcount
+		For Local i:=0 Until _vertices.Length
 			
 			vertices[i].normal=New Vec3f(0)
 		Next
 		
-		For Local ibuffer:=Eachin _ibuffers
+		For Local material:=Eachin _materials
 			
-			Local icount:=ibuffer.Length
-			Local indices:=Cast<UInt Ptr>( ibuffer.Data )
+			Local indices:=material.indices.Data
 		
-			For Local i:=0 Until icount Step 3
+			For Local i:=0 Until material.indices.Length
 				
 				Local i1:=indices[i+0]
 				Local i2:=indices[i+1]
@@ -255,11 +380,12 @@ Class Mesh Extends Resource
 		
 		Next
 		
-		For Local i:=0 Until vcount
+		For Local i:=0 Until _vertices.Length
 			
 			vertices[i].normal=vertices[i].normal.Normalize()
 		Next
-	
+
+		InvalidateVertices()
 	End
 
 	#rem monkeydoc Updates mesh tangents.
@@ -269,19 +395,16 @@ Class Mesh Extends Resource
 	#end
 	Method UpdateTangents()
 		
-		Local vcount:=_vbuffer.Length
+		Local vertices:=_vertices.Data.Data
 		
-		Local vertices:=Cast<Vertex3f Ptr>( _vbuffer.Data )
+		Local tan1:=New Vec3f[_vertices.Length]
+		Local tan2:=New Vec3f[_vertices.Length]
 		
-		Local tan1:=New Vec3f[vcount]
-		Local tan2:=New Vec3f[vcount]
-		
-		For Local ibuffer:=Eachin _ibuffers
+		For Local material:=Eachin _materials
 			
-			Local icount:=ibuffer.Length
-			Local indices:=Cast<UInt Ptr>( ibuffer.Data )
+			Local indices:=material.indices.Data
 		
-			For Local i:=0 Until icount Step 3
+			For Local i:=0 Until material.indices.Length Step 3
 				
 				Local i1:=indices[i+0]
 				Local i2:=indices[i+1]
@@ -316,9 +439,10 @@ Class Mesh Extends Resource
 				tan2[i2]+=tdir
 				tan2[i3]+=tdir
 			Next
+			
 		Next
 	
-		For Local i:=0 Until vcount
+		For Local i:=0 Until _vertices.Length
 			
 			Local v:=vertices+i
 	
@@ -329,24 +453,24 @@ Class Mesh Extends Resource
 			v->tangent.w=n.Cross( t ).Dot( tan2[i] ) < 0 ? -1 Else 1
 		Next
 		
-		_vbuffer.Invalidate()
+		InvalidateVertices()
 	End
 	
 	#rem monkeydoc Flips all triangles.
 	#end
 	Method FlipTriangles()
 		
-		For Local ibuffer:=Eachin _ibuffers
-			
-			Local indices:=Cast<UInt Ptr>( ibuffer.Data )
+		For Local material:=Eachin _materials
 			
-			For Local i:=0 Until ibuffer.Length Step 3
+			Local indices:=material.indices.Data
+		
+			For Local i:=0 Until material.indices.Length Step 3
 				Local t:=indices[i]
 				indices[i]=indices[i+1]
 				indices[i+1]=t
 			Next
 			
-			ibuffer.Invalidate()
+			material.dirty|=Dirty.IndexBuffer
 		Next
 		
 	End
@@ -355,61 +479,16 @@ Class Mesh Extends Resource
 	#end
 	Method ScaleTexCoords( scale:Vec2f )
 		
-		Local vertices:=Cast<Vertex3f Ptr>( _vbuffer.Data )
+		Local vertices:=_vertices.Data
 		
-		For Local i:=0 Until _vbuffer.Length
+		For Local i:=0 Until _vertices.Length
 		
 			vertices[i].texCoord0*=scale
 		Next
 		
-		_vbuffer.Invalidate()
-	End
-
-	#rem monkeydoc Gets all vertices in the mesh.
-	#end	
-	Method GetVertices:Vertex3f[]()
-		
-		Local vertices:=New Vertex3f[ _vbuffer.Length ]
-		
-		libc.memcpy( vertices.Data,_vbuffer.Data,_vbuffer.Length*_vbuffer.Pitch )
-		
-		Return vertices
-	End
-	
-	#rem monkeydoc Get indices for a material id.
-	#end
-	Method GetIndices:UInt[]( materialid:Int )
-	
-		Local ibuffer:=_ibuffers[materialid]
-		
-		Local indices:=New UInt[ ibuffer.Length ]
-		
-		libc.memcpy( indices.Data,ibuffer.Data,ibuffer.Length*4 )
-		
-		Return indices
+		InvalidateVertices()
 	End
 
-	#rem monkeydoc Gets all indices in the mesh.
-	#end	
-	Method GetAllIndices:UInt[]()
-	
-		Local n:=0
-		For Local ibuffer:=Eachin _ibuffers
-			n+=ibuffer.Length
-		Next
-		
-		Local indices:=New UInt[ n ],p:=indices.Data
-		
-		For Local ibuffer:=Eachin _ibuffers
-			
-			libc.memcpy( p,ibuffer.Data,ibuffer.Length*4 )
-		
-			p+=ibuffer.Length
-		Next
-		
-		Return indices
-	End
-	
 	#rem monkeydoc Loads a mesh from a file.
 	
 	On its own, mojo3d can only load gltf2 format mesh and model files.
@@ -438,31 +517,87 @@ Class Mesh Extends Resource
 		Return Null
 	End
 	
-	'***** INTERNAL *****
+	Internal
 	
-	#rem monkeydoc @hidden
-	#end
 	Method GetVertexBuffer:VertexBuffer()
 		
+		If _dirty & Dirty.VertexBuffer
+			
+			_vbuffer=New VertexBuffer( Vertex3f.Format,_vertices.Length )
+			
+			_vbuffer.SetVertices( _vertices.Data.Data,0,_vertices.Length )
+			
+			_dirty&=~Dirty.VertexBuffer
+		End
+		
 		Return _vbuffer
 	End
 	
-	#rem monkeydoc @hidden
-	#end
-	Method GetIndexBuffers:Stack<IndexBuffer>()
+	Method GetIndexBuffer:IndexBuffer( materialid:Int )
 		
-		Return _ibuffers
+		Local material:=_materials[materialid]
+		
+		If material.dirty & Dirty.IndexBuffer
+			
+			Local indices:=material.indices
+			
+			material.ibuffer=New IndexBuffer( IndexFormat.UINT32,indices.Length )
+			
+			material.ibuffer.SetIndices( indices.Data.Data,0,indices.Length )
+			
+			material.dirty&=~Dirty.IndexBuffer
+		End
+		
+		Return material.ibuffer
 	End
 	
 	Private
 	
 	Enum Dirty
 		Bounds=1
+		VertexBuffer=2
+		IndexBuffer=4
+	End
+	
+	class MaterialID
+		Field indices:=New Stack<UInt>
+		Field dirty:Dirty=Dirty.IndexBuffer
+		Field ibuffer:IndexBuffer
 	End
 	
-	Field _dirty:Dirty
-	Field _bounds:Boxf
+	Field _vertices:=New Stack<Vertex3f>
+	Field _materials:=New Stack<MaterialID>
+	
+	Field _dirty:Dirty=Null
+	Field _bounds:Boxf=Boxf.EmptyBounds
 	Field _vbuffer:VertexBuffer
-	Field _ibuffers:Stack<IndexBuffer>
+	Field _minDirty:Int
+	Field _maxDirty:Int
+	
+	Method InvalidateVertices( first:Int,count:Int )
+		
+		_minDirty=Min( _minDirty,first )
+		_maxDirty=Max( _maxDirty,first+count )
+		
+		_dirty|=Dirty.Bounds|Dirty.VertexBuffer
+	End
+	
+	Method InvalidateVertices()
+		
+		InvalidateVertices( 0,_vertices.Length )
+	End
+	
+	Method GetMaterial:MaterialID( materialid:Int,dirty:Dirty=Dirty.IndexBuffer )
+		
+		DebugAssert( materialid>=0 And materialid<=_materials.Length,"Materialid out of range" )
+		
+		If materialid=_materials.Length AddMaterials( 1 )
+			
+		Local material:=_materials[materialid]
+		
+		material.dirty|=dirty
+
+		Return material
+	End
 	
 End

+ 25 - 58
modules/mojo3d/graphics/model.monkey2

@@ -3,7 +3,7 @@ Namespace mojo3d.graphics
 
 #rem monkeydoc The Model class.
 #end
-Class Model Extends Entity
+Class Model Extends Renderable
 	
 	#rem monkeydoc @hidden
 	#end
@@ -83,15 +83,6 @@ Class Model Extends Entity
 		_bones=bones
 	End
 	
-	Property CastsShadow:Bool()
-		
-		Return _castsShadow
-		
-	Setter( castsShadow:Bool )
-	
-		_castsShadow=castsShadow
-	End
-	
 	#rem monkeydoc Loads a model from a file path.
 	
 	On its own, mojo3d can only load gltf2 format mesh and model files.
@@ -148,19 +139,27 @@ Class Model Extends Entity
 		
 		Return Null
 	End
+	
+	Protected
 
-	#rem monkeydoc @hidden
-	#end	
-	Method OnRender( rq:RenderQueue )
+	Method New( model:Model,parent:Entity )
+		Super.New( model,parent )
 		
-		If Not _mesh Return
+		_mesh=model._mesh
+		
+		_material=model._material
 		
-		Local instance:=Self
+		_materials=model._materials.Slice( 0 )
+	End
+	
+	Internal
+
+	Method OnRender( rq:RenderQueue ) Override
+		
+		If Not _mesh Return
 		
 		If _bones
 
-			instance=Null
-		
 			If _boneMatrices.Length<>_bones.Length _boneMatrices=New Mat4f[ _bones.Length ]
 			
 			For Local i:=0 Until _bones.Length
@@ -171,51 +170,21 @@ Class Model Extends Entity
 		
 		Local vbuffer:=_mesh.GetVertexBuffer()
 		
-		Local ibuffers:=_mesh.GetIndexBuffers()
-		
-		For Local i:=0 Until ibuffers.Length
-			
+		For Local i:=0 Until _mesh.NumMaterials
+
+			Local ibuffer:=_mesh.GetIndexBuffer( i )
+
 			Local material:=i<_materials.Length And _materials[i] ? _materials[i] Else _material
 			
-			Local ibuffer:=ibuffers[i]
-			
-			rq.AddRenderOp( material,vbuffer,ibuffer,instance,_boneMatrices,3,ibuffer.Length/3,0 )
+			If _bones
+				rq.AddRenderOp( material,_boneMatrices,vbuffer,ibuffer,3,ibuffer.Length/3,0 )
+			Else
+				rq.AddRenderOp( material,Self,vbuffer,ibuffer,3,ibuffer.Length/3,0 )
+			Endif
 			
 		Next
 	End
 	
-	Protected
-
-	#rem monkeydoc @hidden
-	#end
-	Method New( model:Model,parent:Entity )
-		Super.New( model,parent )
-		
-		_mesh=model._mesh
-		
-		_material=model._material
-		
-		_materials=model._materials.Slice( 0 )
-		
-		_castsShadow=model._castsShadow
-		
-		Show()
-	End
-
-	#rem monkeydoc @hidden
-	#end	
-	Method OnShow() Override
-		
-		Scene.Models.Add( Self )
-	End
-	
-	#rem monkeydoc @hidden
-	#end	
-	Method OnHide() Override
-		
-		Scene.Models.Remove( Self )
-	End
-	
 	Private
 	
 	Field _mesh:Mesh
@@ -225,6 +194,4 @@ Class Model Extends Entity
 	Field _bones:Bone[]
 	Field _boneMatrices:Mat4f[]
 	
-	Field _castsShadow:Bool=true
-	
 End

+ 15 - 8
modules/mojo3d/graphics/particlebuffer.monkey2

@@ -7,13 +7,14 @@ Class ParticleBuffer
 	
 		_length=length
 
-		_vbuffer=New VertexBuffer( Vertex3fFormat.Instance,_length )
+		_vbuffer=New VertexBuffer( Vertex3f.Format,_length )
 		
 		_uniforms=New UniformBlock( 4,true )
 		
-		Local vertices:=_vbuffer.AddVertices( _length )
-		
-		libc.memset( vertices,0,_vbuffer.Pitch*_length )
+		Local vertices:=_vbuffer.Lock()
+		libc.memset( vertices,0,_vbuffer.Length*_vbuffer.Pitch)
+		_vbuffer.Invalidate()
+		_vbuffer.Unlock()
 
 		Gravity=New Vec3f( 0,-9.81,0 )
 		Duration=5.0
@@ -137,17 +138,23 @@ Class ParticleBuffer
 			
 			local timeStep:=frames * (1.0/60.0) / n
 			
+			Local vertices:=Cast<Vertex3f Ptr>( _vbuffer.Lock() )
+			
 			For Local i:=0 Until n
 			
-				AddParticle( _time )
+				AddParticle( vertices,_time )
 				
 				_time+=timeStep
 				
 			Next
+			
+			_vbuffer.Unlock()
+			
+			_vbuffer.Invalidate()
 
 		Endif
 		
-		rq.AddRenderOp( material,_uniforms,instance,_vbuffer,Null,1,_length,0 )
+		rq.AddSpriteOp( material,_uniforms,instance,_vbuffer,Null,1,_length,0 )
 	End		
 	
 	Private
@@ -167,7 +174,7 @@ Class ParticleBuffer
 	Field _time:Float
 	Field _index:Int
 
-	Method AddParticle( time:Float )
+	Method AddParticle( vertices:Vertex3f Ptr,time:Float )
 	
 		Local r:=Rnd( -Pi,Pi )
 		Local d:=Rnd( 0,Tan( _coneAngle*.5*TwoPi/360.0 ) )
@@ -188,7 +195,7 @@ Class ParticleBuffer
 		Local velocity:=New Vec3f( vx,vy,1.0 ) * sp '.Normalize() * sp
 		#end
 			
-		Local vertex:=Cast<Vertex3f Ptr>( _vbuffer.Data )+_index
+		Local vertex:=vertices+_index
 		
 		_index+=1 
 		

+ 2 - 22
modules/mojo3d/graphics/particlesystem.monkey2

@@ -1,7 +1,7 @@
 
 Namespace mojo3d.graphics
 
-Class ParticleSystem Extends Entity
+Class ParticleSystem Extends Renderable
 
 	#rem monkeydoc Creates a new particle system.
 	#end
@@ -11,8 +11,6 @@ Class ParticleSystem Extends Entity
 		_pbuffer=New ParticleBuffer( particleCount )
 		
 		_material=New ParticleMaterial
-
-		Show()
 	End
 
 	Method New( particleBuffer:ParticleBuffer,material:ParticleMaterial,parent:Entity=Null )
@@ -21,8 +19,6 @@ Class ParticleSystem Extends Entity
 		_pbuffer=particleBuffer
 		
 		_material=material
-		
-		Show()
 	End
 
 	#rem monkeydoc Copies the particle system.
@@ -69,27 +65,11 @@ Class ParticleSystem Extends Entity
 		_pbuffer=psystem._pbuffer
 		
 		_material=psystem._material
-		
-		Show()
-	End
-	
-	#rem monkeydoc @hidden
-	#End		
-	Method OnShow() Override
-		
-		Scene.ParticleSystems.Add( Self )
-	End
-	
-	#rem monkeydoc @hidden
-	#End		
-	Method OnHide() Override
-		
-		Scene.ParticleSystems.Remove( Self )
 	End
 	
 	Internal
 	
-	Method OnRender( rq:RenderQueue )
+	Method OnRender( rq:RenderQueue ) override
 	
 		_pbuffer.OnRender( rq,_material,Self )
 	End

+ 35 - 2
modules/mojo3d/graphics/renderable.monkey2

@@ -4,15 +4,48 @@ Namespace mojo3d.graphics
 Class Renderable Extends Entity Abstract
 
 	Method New( parent:Entity=Null )
+	
 		Super.New( parent )
+		
+		Show()
 	End
 	
+	Property CastsShadow:bool()
+	
+		Return _castsShadow
+	
+	Setter( castsShadow:Bool )
+	
+		_castsShadow=castsShadow
+	End
+	
+	Protected
+	
 	Method New( renderable:Renderable,parent:Entity )
+	
 		Super.New( renderable,parent )
+		
+		_castsShadow=renderable.CastsShadow
+		
+		Show()
+	End
+	
+	Method OnShow() Override
+		
+		Scene.Renderables.Add( Self )
 	End
 	
-	'***** INTERNAL *****
+	Method OnHide() Override
+		
+		Scene.Renderables.Remove( Self )
+	End
+	
+	Internal
+	
+	Method OnRender( rq:RenderQueue ) Abstract
+	
+	Private
 	
-	Method OnRender( device:GraphicsDevice ) Abstract
+	Field _castsShadow:bool
 	
 End

+ 8 - 18
modules/mojo3d/graphics/renderer.monkey2

@@ -140,22 +140,13 @@ Class Renderer
 		_renderQueue.Time=time
 		
 		_renderQueue.AddShadowOps=False
-			
-		For Local model:=Eachin _renderScene.Models
-			
-			_renderQueue.AddShadowOps=model.CastsShadow
-			
-			model.OnRender( _renderQueue )
-		Next
-		
-		_spriteQueue.Clear()
-		
-		_spriteQueue.Time=time
 		
-		For Local psystem:=Eachin _renderScene.ParticleSystems
+		For Local r:=Eachin _renderScene.Renderables
 		
-			psystem.OnRender( _spriteQueue )
-		End
+			_renderQueue.AddShadowOps=r.CastsShadow
+			
+			r.OnRender( _renderQueue )
+		Next
 		
 		'***** Set render camera *****
 
@@ -165,7 +156,6 @@ Class Renderer
 		Local viewMat:=_renderCamera.InverseMatrix
 		Local projMat:=_renderCamera.ProjectionMatrix
 		Local invProjMat:=-projMat
-		
 			
 		_runiforms.SetMat3f( "EnvMatrix",envMat )
 		_runiforms.SetMat4f( "ProjectionMatrix",projMat )
@@ -175,7 +165,7 @@ Class Renderer
 		_runiforms.SetFloat( "DepthNear",_renderCamera.Near )
 		_runiforms.SetFloat( "DepthFar",_renderCamera.Far )
 		
-		_spriteBuffer.AddSprites( _spriteQueue,_renderScene.Sprites,_renderCamera )
+		_spriteBuffer.AddSprites( _renderQueue,_renderCamera )
 		
 		OnRender( scene,camera,device )
 		
@@ -252,7 +242,7 @@ Class Renderer
 	Method RenderQuad()
 
 		Global _vertices:VertexBuffer
-	
+		
 		If Not _vertices
 			_vertices=New VertexBuffer( New Vertex3f[](
 			New Vertex3f( 0,1,0 ),
@@ -313,7 +303,7 @@ Class Renderer
 
 	Method RenderSpriteOps()
 
-		RenderRenderOps( _spriteQueue.TransparentOps,_renderCamera.InverseMatrix,_renderCamera.ProjectionMatrix )
+		RenderRenderOps( _renderQueue.SpriteOps,_renderCamera.InverseMatrix,_renderCamera.ProjectionMatrix )
 	End
 	
 	#rem

+ 41 - 9
modules/mojo3d/graphics/renderqueue.monkey2

@@ -6,10 +6,10 @@ Namespace mojo3d.graphics
 Class RenderOp
 	Field material:Material
 	Field uniforms:UniformBlock
-	Field vbuffer:VertexBuffer
-	Field ibuffer:IndexBuffer
 	Field instance:Entity
 	Field bones:Mat4f[]
+	Field vbuffer:VertexBuffer
+	Field ibuffer:IndexBuffer
 	Field order:Int
 	Field count:Int
 	Field first:Int
@@ -38,10 +38,21 @@ Class RenderQueue
 		Return _transparentOps
 	End
 	
+	Property SpriteOps:Stack<RenderOp>()
+	
+		Return _spriteOps
+	End
+	
 	Property ShadowOps:Stack<RenderOp>()
 		
 		Return _shadowOps
 	End
+
+	'bit of a hack for now, but renderqueue should be able to handle sprites...	
+	Property Sprites:Stack<Sprite>()
+	
+		Return _sprites
+	End
 	
 	Property AddShadowOps:Bool()
 		
@@ -53,7 +64,8 @@ Class RenderQueue
 	End
 	
 	Method Clear()
-		
+	
+		_sprites.Clear()	
 		_opaqueOps.Clear()
 		_shadowOps.Clear()
 		_transparentOps.Clear()
@@ -88,9 +100,10 @@ Class RenderQueue
 		AddRenderOp( op )
 	End
 	
-	Method AddRenderOp( material:Material,vbuffer:VertexBuffer,ibuffer:IndexBuffer,order:Int,count:Int,first:Int )
+	Method AddRenderOp( material:Material,instance:Entity,vbuffer:VertexBuffer,ibuffer:IndexBuffer,order:Int,count:Int,first:Int )
 		Local op:=New RenderOp
 		op.material=material
+		op.instance=instance
 		op.vbuffer=vbuffer
 		op.ibuffer=ibuffer
 		op.order=order
@@ -99,37 +112,56 @@ Class RenderQueue
 		AddRenderOp( op )
 	End
 	
-	Method AddRenderOp( material:Material,vbuffer:VertexBuffer,ibuffer:IndexBuffer,instance:Entity,order:Int,count:Int,first:Int )
+	Method AddRenderOp( material:Material,bones:Mat4f[],vbuffer:VertexBuffer,ibuffer:IndexBuffer,order:Int,count:Int,first:Int )
 		Local op:=New RenderOp
 		op.material=material
+		op.bones=bones
 		op.vbuffer=vbuffer
 		op.ibuffer=ibuffer
-		op.instance=instance
 		op.order=order
 		op.count=count
 		op.first=first
 		AddRenderOp( op )
 	End
 	
-	Method AddRenderOp( material:Material,vbuffer:VertexBuffer,ibuffer:IndexBuffer,instance:Entity,bones:Mat4f[],order:Int,count:Int,first:Int )
+	Method AddRenderOp( material:Material,vbuffer:VertexBuffer,ibuffer:IndexBuffer,order:Int,count:Int,first:Int )
 		Local op:=New RenderOp
 		op.material=material
 		op.vbuffer=vbuffer
 		op.ibuffer=ibuffer
-		op.instance=instance
-		op.bones=bones
 		op.order=order
 		op.count=count
 		op.first=first
 		AddRenderOp( op )
 	End
 	
+	Method AddSpriteOp( material:Material,uniforms:UniformBlock,instance:Entity,vbuffer:VertexBuffer,ibuffer:IndexBuffer,order:Int,count:Int,first:Int )
+		Local op:=New RenderOp
+		op.material=material
+		op.uniforms=uniforms
+		op.instance=instance
+		op.vbuffer=vbuffer
+		op.ibuffer=ibuffer
+		op.order=order
+		op.count=count
+		op.first=first
+		_spriteOps.Add( op )
+	End
+	
+	Method AddSprite( sprite:Sprite )
+	
+		_sprites.Add( sprite )
+	End
+	
 	Private
 
 	Field _time:float	
 	Field _opaqueOps:=New Stack<RenderOp>
 	Field _transparentOps:=New Stack<RenderOp>
+	Field _spriteOps:=New Stack<RenderOp>
 	Field _shadowOps:=New Stack<RenderOp>
 	
+	Field _sprites:=New Stack<Sprite>
+	
 	Field _addShadowOps:Bool
 End

+ 3 - 15
modules/mojo3d/graphics/scene.monkey2

@@ -180,19 +180,9 @@ Class Scene Extends DynamicObject
 		Return _lights
 	End
 	
-	Property Models:Stack<Model>()
-		
-		Return _models
-	End
-	
-	Property Sprites:Stack<Sprite>()
-		
-		Return _sprites
-	End
-	
-	Property ParticleSystems:Stack<ParticleSystem>()
+	Property Renderables:Stack<Renderable>()
 	
-		Return _psystems
+		Return _renderables
 	End
 	
 	Private
@@ -214,8 +204,6 @@ Class Scene Extends DynamicObject
 	
 	Field _cameras:=New Stack<Camera>
 	Field _lights:=New Stack<Light>
-	Field _models:=New Stack<Model>
-	Field _sprites:=New Stack<Sprite>
-	Field _psystems:=New Stack<ParticleSystem>
+	Field _renderables:=New Stack<Renderable>()
 			
 End

+ 3 - 18
modules/mojo3d/graphics/sprite.monkey2

@@ -16,14 +16,12 @@ End
 
 #rem monkeydoc The Sprite class.
 #end
-Class Sprite Extends Entity
+Class Sprite Extends Renderable
 	
 	#rem monkeydoc Creates a new sprite.
 	#end
 	Method New( parent:Entity=Null )
 		Super.New( parent )
-
-		Show()
 	End
 
 	Method New( material:Material,parent:Entity=Null )
@@ -99,30 +97,17 @@ Class Sprite Extends Entity
 	
 	Protected
 
-	#rem monkeydoc @hidden
-	#End		
 	Method New( sprite:Sprite,parent:Entity )
 		Super.New( sprite,parent )
 		
 		_material=sprite._material
 		_handle=sprite._handle
 		_mode=sprite._mode
-		
-		Show()
 	End
 	
-	#rem monkeydoc @hidden
-	#End		
-	Method OnShow() Override
-		
-		Scene.Sprites.Add( Self )
-	End
+	Method OnRender( rq:RenderQueue ) Override
 	
-	#rem monkeydoc @hidden
-	#End		
-	Method OnHide() Override
-		
-		Scene.Sprites.Remove( Self )
+		rq.AddSprite( Self )
 	End
 	
 	Private

+ 30 - 22
modules/mojo3d/graphics/spritebuffer.monkey2

@@ -6,37 +6,42 @@ Namespace mojo3d.graphics
 Class SpriteBuffer
 	
 	Method New()
+		_vbuffer=New VertexBuffer( Vertex3f.Format,0 )
 		
-		If _spriteVertices Return
-		
-		_spriteVertices=New VertexBuffer( Vertex3fFormat.Instance,0 )
-		
-		_spriteIndices=New IndexBuffer( IndexFormat.UINT32,0 )
+		_ibuffer=New IndexBuffer( IndexFormat.UINT32,0 )
 	End
 	
-	Method AddSprites( rq:RenderQueue,sprites:Stack<Sprite>,camera:Camera )
+	Method AddSprites( rq:RenderQueue,camera:Camera )
+	
+		Local sprites:=rq.Sprites
 		
 		If sprites.Empty Return
 		
 		Local n:=sprites.Length
 		
-		_spriteVertices.Clear()
-		Local vp:=Cast<Vertex3f Ptr>( _spriteVertices.AddVertices( n*4 ) )
+		If n*4>_vbuffer.Length
+			_vbuffer.Resize( Max( _vbuffer.Length*3/2,n*4 ) )
+		Endif
 		
-		If n>_spriteIndices.Length/6
-			Local i:=_spriteIndices.Length/6
-			Local ip:=Cast<UInt Ptr>( _spriteIndices.AddIndices( (n-i)*6 ) )
-			For Local j:=i Until n
-				ip[0]=j*4
-				ip[1]=j*4+1
-				ip[2]=j*4+2
-				ip[3]=j*4
-				ip[4]=j*4+2
-				ip[5]=j*4+3
+		If n*6>_ibuffer.Length
+			Local i0:=_ibuffer.Length/6
+			_ibuffer.Resize( Max( _ibuffer.Length*3/2,n*6 ) )
+			Local ip:=Cast<UInt Ptr>( _ibuffer.Lock() )+i0*6
+			For Local i:=i0 Until n
+				ip[0]=i*4
+				ip[1]=i*4+1
+				ip[2]=i*4+2
+				ip[3]=i*4
+				ip[4]=i*4+2
+				ip[5]=i*4+3
 				ip+=6
 			Next
+			_ibuffer.Invalidate( i0*6,(n-i0)*6 )
+			_ibuffer.Unlock()
 		Endif
 		
+		Local vp:=Cast<Vertex3f Ptr>( _vbuffer.Lock() )
+		
 		'Sort sprites by distance from camera. Only really need to do this for transparent sprites, but meh...
 		'
 		sprites.Sort( Lambda:Int( x:Sprite,y:Sprite )
@@ -50,7 +55,7 @@ Class SpriteBuffer
 			
 			Local material:=sprite.Material
 			If material<>cmaterial
-				rq.AddRenderOp( cmaterial,_spriteVertices,_spriteIndices,3,(i-i0)*2,i0*6 )
+				rq.AddSpriteOp( cmaterial,Null,Null,_vbuffer,_ibuffer,3,(i-i0)*2,i0*6 )
 				cmaterial=material
 				i0=i
 			Endif
@@ -83,14 +88,17 @@ Class SpriteBuffer
 			i+=1
 		Next
 		
-		rq.AddRenderOp( cmaterial,_spriteVertices,_spriteIndices,3,(i-i0)*2,i0*6 )
+		rq.AddSpriteOp( cmaterial,Null,Null,_vbuffer,_ibuffer,3,(i-i0)*2,i0*6 )
+		
+		_vbuffer.Invalidate()
 		
+		_vbuffer.Unlock()
 	End
 
 	Private
 	
-	Field _spriteVertices:VertexBuffer
+	Field _vbuffer:VertexBuffer
 	
-	Field _spriteIndices:IndexBuffer
+	Field _ibuffer:IndexBuffer
 	
 End

+ 1 - 0
modules/mojo3d/mojo3d.monkey2

@@ -42,6 +42,7 @@ Using gles20..
 #Import "graphics/entityexts"
 
 #Import "graphics/renderqueue"
+#Import "graphics/renderable"
 #Import "graphics/renderer"
 #Import "graphics/deferredrenderer"
 #Import "graphics/forwardrenderer"

+ 5 - 4
modules/mojo3d/tests/ducks.monkey2

@@ -38,8 +38,9 @@ Class MyWindow Extends Window
 		'create scene
 		'		
 		_scene=Scene.GetCurrent()
+		
 		_scene.ClearColor=Color.Sky
-
+		
 		'add fog effect to scene
 		'		
 '		_fog=New FogEffect
@@ -66,7 +67,7 @@ Class MyWindow Extends Window
 
 		'create ground
 		'
-		_ground=Model.CreateBox( New Boxf( -50,-1,-50,50,0,50 ),10,10,10,New PbrMaterial( Color.Green,0,1 ) )
+		_ground=Model.CreateBox( New Boxf( -50,-1,-50,50,0,50 ),1,1,1,New PbrMaterial( Color.Green,0,1 ) )
 		_ground.CastsShadow=False
 		
 		'create ducks
@@ -126,7 +127,7 @@ Class MyWindow Extends Window
 
 		canvas.Scale( Width/640.0,Height/480.0 )
 		
-		canvas.DrawText( "Width="+Width+", Height="+Height+", FPS="+App.FPS,0,0 )
+		'canvas.DrawText( "Width="+Width+", Height="+Height+", FPS="+App.FPS,0,0 )
 	End
 	
 End
@@ -140,7 +141,7 @@ Function Main()
 '	config["mojo3d_renderer"]="forward-direct"	'default on mobile targets. depth buffer must be enabled too.
 '	config["GL_depth_buffer_enabled"]=1
 
-	config["mojo3d_renderer"]="forward"
+'	config["mojo3d_renderer"]="forward"
 		
 	New AppInstance( config )
 	

+ 165 - 0
modules/mojo3d/tests/morpher.monkey2

@@ -0,0 +1,165 @@
+
+Namespace myapp
+
+#Import "<std>"
+#Import "<mojo>"
+#Import "<mojo3d>"
+
+#Import "assets/"
+
+#Import "util"
+
+Using std..
+Using mojo..
+Using mojo3d..
+
+Class Morpher Extends Renderable
+
+	Method New( mesh0:Mesh,mesh1:Mesh,material:Material,parent:Entity=null )
+		Super.New( parent )
+
+		'get mesh vertices - don't need entire meshes	
+		_vertices0=mesh0.GetVertices()
+		_vertices1=mesh1.GetVertices()
+		_material=material
+		
+		'create vertexbuffer
+		'
+		_vbuffer=New VertexBuffer( Vertex3f.Format,_vertices0.Length )
+		
+		'create and initilize indexbuffer
+		'	
+		Local indices:=mesh0.GetIndices( 0 )
+		_ibuffer=New IndexBuffer( IndexFormat.UINT32,indices.Length )
+		_ibuffer.SetIndices( indices.Data,0,indices.Length )
+	End
+	
+	Protected
+	
+	Method OnRender( rq:RenderQueue ) override
+	
+		Local alpha:=Sin( Now() )*.5+.5
+		
+		'lock vertex buffer
+		Local vp:=Cast<Vertex3f Ptr>( _vbuffer.Lock() )
+		
+		For Local i:=0 Until _vbuffer.Length
+		
+			Local v:=_vertices0[i]
+
+			'interpolate position			
+			v.position=_vertices0[i].position.Blend( _vertices1[i].position,alpha )
+			
+			'interpolate normal
+			v.normal=_vertices0[i].normal.Blend( _vertices1[i].normal,alpha ).Normalize()
+			
+			vp[i]=v
+		Next
+		
+		'invalidate all vertices
+		_vbuffer.Invalidate()
+		
+		'unlock vertices
+		_vbuffer.Unlock()
+		
+		'add renderop
+		rq.AddRenderOp( _material,Self,_vbuffer,_ibuffer,3,_ibuffer.Length/3,0 )
+	End
+	
+	Private
+	
+	Field _vertices0:Vertex3f[]
+	Field _vertices1:Vertex3f[]
+	Field _material:Material
+	
+	Field _vbuffer:VertexBuffer
+	Field _ibuffer:IndexBuffer
+	
+	Field _nvertices:Int
+End
+
+Class MyWindow Extends Window
+	
+	Field _scene:Scene
+	
+	Field _camera:Camera
+	
+	Field _light:Light
+	
+	Field _morpher:Morpher
+	
+	Method New( title:String="Simple mojo app",width:Int=640,height:Int=480,flags:WindowFlags=WindowFlags.Resizable )
+
+		Super.New( title,width,height,flags )
+		
+		Print gles20.glGetString( gles20.GL_EXTENSIONS ).Replace( " ","~n" )
+		
+		'create scene
+		'		
+		_scene=Scene.GetCurrent()
+		
+		_scene.ClearColor=Color.Sky
+		
+		'create camera
+		'
+		_camera=New Camera
+		_camera.Move( 0,0,-5 )
+		
+		'create light
+		'
+		_light=New Light
+		_light.RotateX( 90 )
+		_light.CastsShadow=True
+		
+		Local mesh1:=Mesh.CreateBox( New Boxf( -1,1 ),8,8,8 )
+		
+		Local mesh2:=Mesh.CreateBox( New Boxf( -1,1 ),8,8,8 )
+		
+		'sphericalize mesh2
+		For Local i:=0 Until mesh2.NumVertices
+			Local v:=mesh2.GetVertex( i )
+			v.position=v.position.Normalize()
+			v.normal=v.position.Normalize()
+			mesh2.SetVertex( i,v )
+		Next
+		
+		Local material:=New PbrMaterial( Color.Green )
+		
+		_morpher=New Morpher( mesh1,mesh2,material )
+	End
+	
+	Method OnRender( canvas:Canvas ) Override
+
+		RequestRender()
+
+		util.Fly( _camera,Self )
+		
+		_scene.Render( canvas,_camera )
+
+		canvas.Scale( Width/640.0,Height/480.0 )
+		
+		canvas.DrawText( "Width="+Width+", Height="+Height+", FPS="+App.FPS,0,0 )
+	End
+	
+End
+
+Function Main()
+	
+	Local config:=New StringMap<String>
+
+'	config["mojo3d_renderer"]="deferred"		'defeault on non-mobile targets.
+
+'	config["mojo3d_renderer"]="forward-direct"	'default on mobile targets. depth buffer must be enabled too.
+'	config["GL_depth_buffer_enabled"]=1
+
+'	config["mojo3d_renderer"]="forward"
+		
+	New AppInstance( config )
+	
+	New MyWindow
+	
+	App.Run()
+End
+		
+		
+