Sfoglia il codice sorgente

Animation etc updates.

Mark Sibly 8 anni fa
parent
commit
adc1bcdfb8
28 ha cambiato i file con 549 aggiunte e 302 eliminazioni
  1. 3 0
      modules/mojo/graphics/glexts/glexts.cpp
  2. 1 0
      modules/mojo/graphics/glexts/glexts.h
  3. 1 0
      modules/mojo/graphics/glexts/glexts.monkey2
  4. 2 0
      modules/mojo/graphics/glutil.monkey2
  5. 1 1
      modules/mojo/graphics/shader.monkey2
  6. 80 14
      modules/mojo3d-loaders/loaders/assimp.monkey2
  7. 9 21
      modules/mojo3d-loaders/tests/castle.monkey2
  8. 30 20
      modules/mojo3d-loaders/tests/turtle.monkey2
  9. 10 3
      modules/mojo3d/assets/shaders/lighting-deferred.glsl
  10. 4 4
      modules/mojo3d/assets/shaders/material-pbr-deferred.glsl
  11. 8 2
      modules/mojo3d/assets/shaders/material-sprite.glsl
  12. 68 5
      modules/mojo3d/components/animation.monkey2
  13. 74 34
      modules/mojo3d/components/animator.monkey2
  14. 2 10
      modules/mojo3d/entities/camera.monkey2
  15. 2 4
      modules/mojo3d/entities/light.monkey2
  16. 14 7
      modules/mojo3d/entities/model.monkey2
  17. 5 3
      modules/mojo3d/entities/particlesystem.monkey2
  18. 1 5
      modules/mojo3d/entities/renderable.monkey2
  19. 3 1
      modules/mojo3d/entities/sprite.monkey2
  20. 5 0
      modules/mojo3d/geometry/loader.monkey2
  21. 3 3
      modules/mojo3d/render/deferredrenderer.monkey2
  22. 1 1
      modules/mojo3d/render/forwardrenderer.monkey2
  23. 11 0
      modules/mojo3d/render/materials/spritematerial.monkey2
  24. 87 64
      modules/mojo3d/render/renderer.monkey2
  25. 10 8
      modules/mojo3d/scene/component.monkey2
  26. 100 89
      modules/mojo3d/scene/entity.monkey2
  27. 1 1
      modules/mojo3d/scene/entityexts.monkey2
  28. 13 2
      modules/mojo3d/tests/sprites.monkey2

+ 3 - 0
modules/mojo/graphics/glexts/glexts.cpp

@@ -18,6 +18,7 @@ namespace bbGLexts{
 	bool GL_depth_texture;
 	bool GL_seamless_cube_map;
 	bool GL_texture_filter_anisotropic;
+	bool GL_standard_derivatives;
 	
 	PFNGLDRAWBUFFERSPROC glDrawBuffers;
 	
@@ -69,6 +70,8 @@ namespace bbGLexts{
 		GL_seamless_cube_map=SDL_GL_ExtensionSupported( "GL_ARB_seamless_cube_map" );
 			
 		GL_texture_filter_anisotropic=SDL_GL_ExtensionSupported( "GL_EXT_texture_filter_anisotropic" );
+		
+		GL_standard_derivatives=SDL_GL_ExtensionSupported( "GL_OES_standard_derivatives" );
 	
 //		bb_printf( "GL_draw_buffers=%i\n",int( GL_draw_buffers ) );
 //		bb_printf( "GL_texture_float=%i\n",int( GL_texture_float ) );

+ 1 - 0
modules/mojo/graphics/glexts/glexts.h

@@ -16,6 +16,7 @@ namespace bbGLexts{
 	extern bool GL_depth_texture;
 	extern bool GL_seamless_cube_map;
 	extern bool GL_texture_filter_anisotropic;
+	extern bool GL_standard_derivatives;
 	
 	typedef void (GL_APIENTRY *PFNGLDRAWBUFFERSPROC)( GLsizei n,const GLenum *bufs );
 

+ 1 - 0
modules/mojo/graphics/glexts/glexts.monkey2

@@ -56,6 +56,7 @@ Const GL_depth_texture:bool="bbGLexts::GL_depth_texture"
 Const GL_texture_float:Bool="bbGLexts::GL_texture_float"
 Const GL_texture_half_float:bool="bbGLexts::GL_texture_half_float"
 Const GL_texture_filter_anisotropic:Bool="bbGLexts::GL_texture_filter_anisotropic"
+Const GL_standard_derivatives:Bool="bbGLexts::GL_standard_derivatives"
 
 Function glDrawBuffers( n:Int,bufs:GLenum Ptr )="bbGLexts::glDrawBuffers"
 

+ 2 - 0
modules/mojo/graphics/glutil.monkey2

@@ -151,6 +151,8 @@ precision mediump float;
 		
 		If glexts.GL_draw_buffers source="#extension GL_EXT_draw_buffers : require~n"+source
 			
+		If glexts.GL_standard_derivatives source="#extension GL_OES_standard_derivatives : require~n"+source
+			
 #ElseIf __TARGET__="macos" or __TARGET__="linux"
 	
 		Const prefix:="

+ 1 - 1
modules/mojo/graphics/shader.monkey2

@@ -160,7 +160,7 @@ Class GLProgram
 					
 					If size>1 size=ublock.GetMat4fArray( u.uniformId ).Length
 					
-					glUniformMatrix4fv( u.location,u.size,False,ublock.GetMat4fv( u.uniformId ) )
+					glUniformMatrix4fv( u.location,size,False,ublock.GetMat4fv( u.uniformId ) )
 					
 				Case GL_SAMPLER_2D,GL_SAMPLER_CUBE
 				

+ 80 - 14
modules/mojo3d-loaders/loaders/assimp.monkey2

@@ -96,12 +96,37 @@ Class AssimpLoader
 	
 	Method LoadBonedModel:Model()
 		
-		Local model:=LoadNode( _scene.mRootNode,Null,True )
+'		#rem
+		Local model:=New Model
+
+'		_nodes[""]=model
+'		_entityIds[""]=_entities.Length
+'		_entities.Add( model )
+		
+		LoadNode( _scene.mRootNode,model )
+'		#end
+		
+'		Local model:=LoadNode( _scene.mRootNode,Null )
 		
 		LoadAnimator( model )
 		
 		Return model
 	End
+
+	Method LoadAnimation:Animation()
+		
+		If Not _scene.mNumAnimations Return Null
+		
+		_nodes["<null>"]=Null
+		_entityIds["<null>"]=_entities.Length
+		_entities.Add( Null )
+		
+		EnumEntityIds( _scene.mRootNode )
+		
+		Local animation:=LoadAnimation( _scene.mAnimations[0] )
+		
+		Return animation
+	End
 	
 	Private
 	
@@ -155,7 +180,7 @@ Class AssimpLoader
 	End
 	
 	Method LoadMesh( aimesh:aiMesh,mesh:Mesh,model:Model,boned:bool )
-	
+		
 		Local vertices:=New Vertex3f[ aimesh.mNumVertices ]
 		
 		Local vp:=aimesh.mVertices
@@ -195,7 +220,7 @@ Class AssimpLoader
 		If index<_materials.Length And _materials[index] Return _materials[index]
 
 		If index>=_materials.Length _materials.Resize( index+1 )
-		
+			
 		_materials[index]=LoadMaterial( _scene.mMaterials[index],boned )
 		
 		Return _materials[index]
@@ -229,7 +254,17 @@ Class AssimpLoader
 		Return material
 	End
 	
-	Method LoadNode:Model( node:aiNode,parent:Model,boned:bool )
+	Method EnumEntityIds( node:aiNode )
+		
+		_entityIds[ node.mName.data ]=_entityIds.Count()
+	
+		For Local i:=0 Until node.mNumChildren
+			
+			EnumEntityIds( node.mChildren[i] )
+		Next
+	End
+	
+	Method LoadNode:Model( node:aiNode,parent:Model )
 		
 		Local model:=New Model( parent )
 		
@@ -251,7 +286,7 @@ Class AssimpLoader
 		
 		For Local i:=0 Until node.mNumChildren
 			
-			LoadNode( node.mChildren[i],model,boned )
+			LoadNode( node.mChildren[i],model )
 		Next
 		
 		Local mesh:=New Mesh
@@ -264,9 +299,9 @@ Class AssimpLoader
 			
 			mesh.AddMaterials( 1 )
 			
-			LoadMesh( aimesh,mesh,model,boned )
+			LoadMesh( aimesh,mesh,model,aimesh.mNumBones>0 )
 			
-			materials.Push( LoadMaterial( aimesh,boned ) )
+			materials.Push( LoadMaterial( aimesh,aimesh.mNumBones>0 ) )
 		Next
 		
 		If materials.Length
@@ -312,9 +347,11 @@ Class AssimpLoader
 	
 	Method LoadAnimation:Animation( aianim:aiAnimation )
 		
-		Local channels:=New AnimationChannel[ _entities.Length ]
-		
-'		Print "Num anim channels="+aianim.mNumChannels
+'		Print "_entities.Length="+_entities.Length
+'		Print "_entityIds.Count="+_entityIds.Count()
+'		Print "mNumChannels="+aianim.mNumChannels
+
+		Local channels:=New AnimationChannel[ _entityIds.Count() ]
 		
 		For Local i:=0 Until aianim.mNumChannels
 			
@@ -322,7 +359,11 @@ Class AssimpLoader
 			
 			Local id:=_entityIds[ aichan.mNodeName.data ]
 			
-			channels[id]=LoadAnimationChannel( aichan )
+			Local channel:=LoadAnimationChannel( aichan )
+			
+			channels[id]=channel
+			
+'			Print "channel "+id+", numposkeys="+channel.PositionKeys.Length+", numrotkeys="+channel.RotationKeys.Length+", numsclkeys="+channel.ScaleKeys.Length
 		
 		Next
 		
@@ -340,11 +381,11 @@ Class AssimpLoader
 			animations[i]=LoadAnimation( _scene.mAnimations[i] )
 		Next
 		
-		Local animator:=entity.AddComponent<Animator>()
+		Local animator:=New Animator( entity )
 		
-		animator.Animations=animations
+		animator.Skeleton=_entities.ToArray()
 		
-		animator.Entities=_entities.ToArray()
+		animator.Animations.AddAll( animations )
 		
 		Return animator
 	End
@@ -429,6 +470,31 @@ Class AssimpMojo3dLoader Extends Mojo3dLoader
 		Return model
 	End
 	
+	Method LoadAnimation:Animation( path:String ) Override
+
+		Local flags:UInt=0
+		
+		flags|=aiProcess_MakeLeftHanded | aiProcess_FlipWindingOrder | aiProcess_FlipUVs
+		'flags|=aiProcess_JoinIdenticalVertices | aiProcess_RemoveRedundantMaterials | aiProcess_FindDegenerates | aiProcess_SortByPType
+		flags|=aiProcess_JoinIdenticalVertices | aiProcess_RemoveRedundantMaterials | aiProcess_SortByPType
+'		flags|=aiProcess_GenSmoothNormals | aiProcess_FixInfacingNormals | aiProcess_Triangulate
+		flags|=aiProcess_GenSmoothNormals |aiProcess_Triangulate
+'		flags|=aiProcess_SplitByBoneCount
+		flags|=aiProcess_LimitBoneWeights
+		flags|=aiProcess_FindInvalidData
+		flags|=aiProcess_OptimizeMeshes
+'		flags|=aiProcess_OptimizeGraph	'fails quite spectacularly!
+		
+		Local scene:=LoadScene( path,flags )
+		If Not scene Return Null
+
+		Local loader:=New AssimpLoader( scene,ExtractDir( path ) )
+		
+		Local animation:=loader.LoadAnimation()
+		
+		Return animation
+	End
+		
 	Private
 
 	Function LoadScene:aiScene( path:String,flags:UInt )

+ 9 - 21
modules/mojo3d-loaders/tests/castle.monkey2

@@ -5,8 +5,6 @@ Namespace myapp
 #Import "<mojo3d>"
 #Import "<mojo3d-loaders>"
 
-#Import "../mojo3d"
-
 #Import "assets/"
 
 #Import "../../mojo3d/tests/assets/miramar-skybox.jpg"
@@ -43,46 +41,36 @@ Class MyWindow Extends Window
 		_camera=New Camera
 		_camera.Near=.1
 		_camera.Far=100
-		_camera.Move( 0,10,-20 )
+		_camera.Move( 0,10,-10 )
+		
+		New FlyBehaviour( _camera )
 		
 		'create light
 		'
 		_light=New Light
-		_light.Rotate( 60,80,0 )	'aim directional light 'down' - Pi/2=90 degrees.
+		_light.Rotate( 60,30,0 )	'aim directional light 'down' - Pi/2=90 degrees.
+		_light.CastsShadow=True
 		
 		'create ground
 		'
 		_ground=Model.CreateBox( New Boxf( -50,-1,-50,50,0,50 ),8,1,8,New PbrMaterial( Color.Green*.1,0,1 ) )
+		_ground.CastsShadow=False
 		
 		'create model
 		'		
 		_model=Model.Load( "asset::castle/CASTLE1.X" )
-'		_model=Model.Load( "desktop::Temple.3DS" )
-'		_model=Model.Load( "desktop::FairyHouse/FairyHouse.3DS" )
-		
-		For Local material:=Eachin _model.Materials
-			material.CullMode=CullMode.None
-		Next
 		
-		Const sz:=30
+		Const cheight:=30.0
 		
-		_model.Mesh.FitVertices( New Boxf( -10000,0,-10000,10000,sz,10000 ),True )
-		
-		_model.Position=Null
-		
-		_camera.Position=New Vec3f( 0,10,-10 )
+		_model.Mesh.FitVertices( New Boxf( -10000,0,-10000,10000,cheight,10000 ),True )
 	End
 		
 	Method OnRender( canvas:Canvas ) Override
 		
-		Global time:=0.0
-		
 		RequestRender()
 		
-		util.Fly( _camera,Self )
+		_scene.Update()
 		
-		If Keyboard.KeyDown( Key.Space ) time+=12.0/60.0
-			
 		_scene.Render( canvas,_camera )
 
 		canvas.Scale( Width/640.0,Height/480.0 )

+ 30 - 20
modules/mojo3d-loaders/tests/turtle.monkey2

@@ -6,9 +6,8 @@ Namespace myapp
 #Import "<mojo3d>"
 #Import "<mojo3d-loaders>"
 
-#Import "../mojo3d"
-
-#Import "assets/"
+#Import "assets/turtle1.b3d"
+#Import "assets/turtle1.png"
 
 #Import "util"
 
@@ -44,10 +43,18 @@ Class MyWindow Extends Window
 		_camera.Far=100
 		_camera.Move( 0,10,-20 )
 		
+		New FlyBehaviour( _camera )
+		
 		'create light
 		'
 		_light=New Light
-		_light.Rotate( Pi/2,0,0 )	'aim directional light 'down' - Pi/2=90 degrees.
+		_light.Rotate( 75,15,0 )	'aim directional light 'down' - Pi/2=90 degrees.
+		_light.CastsShadow=True
+		
+		'create ground
+		'
+		_ground=Model.CreateBox( New Boxf( -50,-5,-50,50,0,50 ),1,1,1,New PbrMaterial( Color.Green ) )
+		_ground.CastsShadow=False
 		
 		'create ground
 		'
@@ -56,28 +63,31 @@ Class MyWindow Extends Window
 		'create turtle
 		'		
 		_turtle=Model.LoadBoned( "asset::turtle1.b3d" )
-		
-		Local animator:=_turtle.GetComponent<Animator>()
-		
-		animator.Paused=False
-		
+		_turtle.Scale=New Vec3f( .125 )
+
+		Local walk:=_turtle.Animator.Animations[0].Slice( 1,11 )
+		_turtle.Animator.Animations.Add( walk )
+		
+		For Local i:=0 Until 360 Step 15
+			
+			Local copy:=_turtle.Copy()
+			
+			copy.RotateY( i )
+			copy.MoveZ( 12 )
+			
+			copy.Animator.Animate( 0,Rnd(.5,1.0) )
+			
+		Next
+		
+		_turtle.Animator.Animate( 1,1 )
 		
 	End
 		
 	Method OnRender( canvas:Canvas ) Override
 		
-		Global time:=0.0
-		
 		RequestRender()
-		
-		util.Fly( _camera,Self )
-		
-		If Keyboard.KeyHit( Key.Space )
 
-			Local animator:=_turtle.GetComponent<Animator>()
-		
-			animator.Paused=Not animator.Paused
-		Endif
+		If Keyboard.KeyDown( Key.Space ) _turtle.Animator.Time+=.01
 		
 		_scene.Update()
 		
@@ -85,7 +95,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+", anim time="+_turtle.Animator.Time+", FPS="+App.FPS,0,0 )
 	End
 	
 End

+ 10 - 3
modules/mojo3d/assets/shaders/lighting-deferred.glsl

@@ -82,9 +82,16 @@ float viewDepth( float depth ){
 
 float shadowColor(){
 
-//	vec4 vpos=vec4( v_Position + v_Normal * .05,1.0 );
-	vec4 vpos=vec4( v_Position + v_Normal * .06,1.0 );
-	vec4 lpos;
+//	vec3 dx=dFdx( v_Position );
+//	vec3 dy=dFdy( v_Position );
+//	vec3 vn=vec4( cross( dx,dy ),0.0 );
+//	vec4 vpos=vec4( v_Position + v_Normal*0.01,1.0 );
+
+	vec4 vpos=vec4( v_Position,1.0 );
+
+	if( vpos.z>=r_ShadowCSMSplits.w ) return 0.5;
+	
+	vec4 llpos,lpos;
 	vec2 off;
 	
 	if( vpos.z<r_ShadowCSMSplits.x ){

+ 4 - 4
modules/mojo3d/assets/shaders/material-pbr-deferred.glsl

@@ -123,8 +123,8 @@ void main(){
 	v_TexCoord0=(m_TextureMatrix * vec3(a_TexCoord0,1.0)).st;
 #ifdef MX2_BUMPMAPPED
 	// viewspace tangent matrix
-	v_TanMatrix[2]=v_Normal;
-	v_TanMatrix[0]=i_ModelViewNormalMatrix * b_Tangent.xyz;
+	v_TanMatrix[2]=normalize( v_Normal );
+	v_TanMatrix[0]=normalize( i_ModelViewNormalMatrix * b_Tangent.xyz );
 	v_TanMatrix[1]=cross( v_TanMatrix[0],v_TanMatrix[2] ) * b_Tangent.a;
 #endif
 #endif
@@ -151,8 +151,8 @@ void main(){
 	v_TexCoord0=(m_TextureMatrix * vec3(a_TexCoord0,1.0)).st;
 #ifdef MX2_BUMPMAPPED
 	// viewspace tangent matrix
-	v_TanMatrix[2]=v_Normal;
-	v_TanMatrix[0]=i_ModelViewNormalMatrix * a_Tangent.xyz;
+	v_TanMatrix[2]=normalize( v_Normal );
+	v_TanMatrix[0]=normalize( i_ModelViewNormalMatrix * a_Tangent.xyz );
 	v_TanMatrix[1]=cross( v_TanMatrix[0],v_TanMatrix[2] ) * a_Tangent.a;
 #endif
 #endif

+ 8 - 2
modules/mojo3d/assets/shaders/material-sprite.glsl

@@ -31,11 +31,17 @@ uniform sampler2D m_ColorTexture;
 
 uniform vec4 m_ColorFactor;
 
+uniform float m_AlphaDiscard;
+
 void main(){
 
-	vec3 color=pow( texture2D( m_ColorTexture,v_TexCoord0 ).rgb,vec3( 2.2 ) ) * m_ColorFactor.rgb;
+	vec4 tcolor=texture2D( m_ColorTexture,v_TexCoord0 );
+
+	float alpha=tcolor.a * m_ColorFactor.a;
 	
-	float alpha=texture2D( m_ColorTexture,v_TexCoord0 ).a * m_ColorFactor.a;
+	if( alpha<m_AlphaDiscard ) discard;
+
+	vec3 color=pow( tcolor.rgb,vec3( 2.2 ) ) * m_ColorFactor.rgb;
 	
 #if defined( MX2_SRGBOUTPUT )
 	gl_FragColor=vec4( pow( color,vec3( 1.0/2.2 ) ),alpha );

+ 68 - 5
modules/mojo3d/components/animation.monkey2

@@ -18,35 +18,98 @@ Alias ScaleKey:AnimationKey<Vec3f>
 Class Animation
 	
 	Method New( channels:AnimationChannel[],duration:Float,hertz:Float )
-		
 		_channels=channels
-		
 		_duration=duration
-		
-		_hertz=hertz
+		_hertz=hertz ?Else 24
 	End
 	
+	#rem monkeydoc Animation channels. 
+	
+	There is a channel for each bone in the animation's skeleton.
+	
+	#end
 	Property Channels:AnimationChannel[]()
 		
 		Return _channels
 	End
 	
+	#rem monkeydoc Duration.
+	
+	The duration of the animation in seconds
+	
+	#end
 	Property Duration:Float()
 		
 		Return _duration
 	End
 	
+	#rem monkeydoc Hertz.
+	
+	The frequency of the animation in seconds
+	
+	#end
 	Property Hertz:Float()
 		
 		Return _hertz
 	End
 	
+	Method Slice:Animation( begin:Float,term:Float )
+		
+		Local newchannels:=New Stack<AnimationChannel>
+		
+		For Local channel:=Eachin _channels
+			
+			If Not channel
+				newchannels.Add( Null )
+				Continue
+			End
+
+			Local posKeys:=New Stack<PositionKey>
+			For Local key:=Eachin channel.PositionKeys
+				If key.Time>term Exit
+				If key.Time>=begin posKeys.Add( New PositionKey( key.Time-begin,key.Value ) )
+			Next
+			
+			Local rotKeys:=New Stack<RotationKey>
+			For Local key:=Eachin channel.RotationKeys
+				If key.Time>term Exit
+				If key.Time>=begin rotKeys.Add( New RotationKey( key.Time-begin,key.Value ) )
+			Next
+			
+			Local sclKeys:=New Stack<ScaleKey>
+			For Local key:=Eachin channel.ScaleKeys
+				If key.Time>term Exit
+				If key.Time>=begin sclKeys.Add( New ScaleKey( key.Time-begin,key.Value ) )
+			Next
+			
+			Local newchannel:=New AnimationChannel( posKeys.ToArray(),rotKeys.ToArray(),sclKeys.ToArray() )
+			newchannels.Add( newchannel )
+		
+		Next
+		
+		Local animation:=New Animation( newchannels.ToArray(),term-begin,_hertz )
+		
+		Return animation
+	
+	End
+	
+	Function Load:Animation( path:String )
+		
+		For Local loader:=Eachin Mojo3dLoader.Instances
+		
+			Local animation:=loader.LoadAnimation( path )
+			
+			If animation Return animation
+		Next
+		
+		Return Null
+	End
+	
 	Private
 	
 	Field _channels:AnimationChannel[]
 	Field _duration:Float
 	Field _hertz:Float
-	
 End
 
 #rem monkeydoc @hidden

+ 74 - 34
modules/mojo3d/components/animator.monkey2

@@ -1,6 +1,15 @@
 
 Namespace mojo3d
 
+Class Entity Extension
+	
+	Property Animator:Animator()
+		
+		Return GetComponent<Animator>()
+	End
+	
+End
+
 #rem monkeydoc The Animator class.
 #end
 Class Animator Extends Component
@@ -8,26 +17,44 @@ Class Animator Extends Component
 	Const Type:=New ComponentType( "Animator",0,ComponentTypeFlags.Singleton )
 	
 	Method New( entity:Entity )
-		
 		Super.New( entity,Type )
 	End
 	
-	Property Animations:Animation[]()
+	Method New( entity:Entity,animator:Animator )
+		Self.New( entity )
+
+		_skeleton=animator._skeleton.Slice( 0 )
+		For Local i:=0 Until _skeleton.Length
+			_skeleton[i]=_skeleton[i].LastCopy
+		End
+		_animations=animator._animations
+		_playing=animator._playing
+		_paused=animator._paused
+		_speed=animator._speed
+		_time=animator._time
+	End
+	
+	Property Skeleton:Entity[]()
+		
+		Return _skeleton
+		
+	Setter( skeleton:Entity[] )
+		
+		_skeleton=skeleton
+	End
+	
+	Property Animations:Stack<Animation>()
 		
 		Return _animations
 		
-	Setter( animations:Animation[] )
+	Setter( animations:Stack<Animation> )
 		
 		_animations=animations
 	End
 	
-	Property Entities:Entity[]()
-	
-		Return _entities
-	
-	Setter( entities:Entity[] )
+	Property Playing:Bool()
 		
-		_entities=entities
+		Return _playing<>Null
 	End
 	
 	Property Paused:Bool()
@@ -57,45 +84,58 @@ Class Animator Extends Component
 		_time=time
 	End
 	
-	Method Animate( animationId:Int,time:Float )
+	Method Animate( animationId:Int,speed:Float=1.0 )
 		
-		Local animation:=_animations[animationId]
-
-		For Local i:=0 Until animation.Channels.Length
-			
-			Local channel:=animation.Channels[i]
-			If Not channel continue
-			
-			_entities[i].LocalPosition=channel.GetPosition( time )
-			_entities[i].LocalBasis=New Mat3f( channel.GetRotation( time ) )
-			_entities[i].LocalScale=channel.GetScale( time )
-		End
+		DebugAssert( animationId>=0 And animationId<_animations.Length,"Animation id out of range" )
+		
+		_playing=_animations[animationId]
+		
+		_speed=speed
 		
+		_time=0
 	End
 	
-	Method OnUpdate( elapsed:Float ) Override
+	Method Stop()
 		
-		If _paused  or not _animations Return
+		_playing=Null
+	End
+	
+	Protected
+	
+	Method OnCopy:Animator( entity:Entity ) Override
 		
-		Local anim:=_animations[0]
+		Return New Animator( entity,Self )
+	End
+	
+	Method OnUpdate( elapsed:Float ) Override
 		
-		_time+=anim.Hertz*elapsed
+		If _paused  Or Not _playing Return
 		
-		If _time>=anim.Duration _time-=anim.Duration Else If _time<0 _time+=anim.Duration
+		Local hertz:=_playing.Hertz
+		Local timeScale:=1.0/hertz
+
+		_time+=elapsed * _speed
+		
+		If _time>=_playing.Duration * timeScale _time-=_playing.Duration * timeScale Else If _time<0 _time+=_playing.Duration * timeScale
+			
+		For Local i:=0 Until _playing.Channels.Length
+			
+			Local channel:=_playing.Channels[i]
+			If Not channel continue
 			
-		Animate( 0,_time )
+			_skeleton[i].LocalPosition=channel.GetPosition( _time * hertz )
+			_skeleton[i].LocalBasis=New Mat3f( channel.GetRotation( _time * hertz ) )
+			_skeleton[i].LocalScale=channel.GetScale( _time * hertz )
+		End
 	End
 	
 	Private
-
-	Field _animations:Animation[]
-
-	Field _entities:Entity[]
-	
-	Field _paused:Bool=True
 	
+	Field _skeleton:Entity[]
+	Field _animations:=New Stack<Animation>
+	Field _playing:Animation
+	Field _paused:Bool=False
 	Field _speed:Float=1
-	
 	Field _time:Float=0
 	
 End

+ 2 - 10
modules/mojo3d/entities/camera.monkey2

@@ -15,7 +15,7 @@ Class Camera Extends Entity
 		Far=1000
 		FOV=90
 		
-		Show()
+		Visible=True
 	End
 	
 	#rem monkeydoc Copies the camera.
@@ -24,7 +24,7 @@ Class Camera Extends Entity
 		
 		Local copy:=New Camera( Self,parent )
 		
-		CopyComplete( copy )
+		CopyTo( copy )
 		
 		Return copy
 	End
@@ -144,8 +144,6 @@ Class Camera Extends Entity
 	
 	Protected
 
-	#rem monkeydoc @hidden
-	#end	
 	Method New( camera:Camera,parent:Entity )
 		Super.New( camera,parent )
 		
@@ -153,19 +151,13 @@ Class Camera Extends Entity
 		Near=camera.Near
 		Far=camera.Far
 		FOV=camera.FOV
-		
-		Show()
 	End
 	
-	#rem monkeydoc @hidden
-	#end	
 	Method OnShow() Override
 		
 		Scene.Cameras.Add( Self )
 	End
 	
-	#rem monkeydoc @hidden
-	#end	
 	Method OnHide() Override
 		
 		Scene.Cameras.Remove( Self )

+ 2 - 4
modules/mojo3d/entities/light.monkey2

@@ -31,7 +31,7 @@ Class Light Extends Entity
 		Range=10
 		CastsShadow=False
 		
-		Show()
+		Visible=True
 	End
 	
 	#rem monkeydoc Copies the light.
@@ -40,7 +40,7 @@ Class Light Extends Entity
 		
 		Local copy:=New Light( Self,parent )
 		
-		CopyComplete( copy )
+		CopyTo( copy )
 		
 		Return copy
 	End
@@ -99,8 +99,6 @@ Class Light Extends Entity
 		Type=light.Type
 		Color=light.Color
 		Range=light.Range
-		
-		Show()
 	End
 	
 	#rem monkeydoc @hidden

+ 14 - 7
modules/mojo3d/entities/model.monkey2

@@ -17,15 +17,16 @@ Class Model Extends Renderable
 	Method New( parent:Entity=Null )
 		Super.New( parent )
 		
-		Show()
+		Visible=True
 	End
 	
 	Method New( mesh:Mesh,material:Material,parent:Entity=Null )
-		Self.New( parent )
+		super.New( parent )
 		
 		_mesh=mesh
+		_materials=New Material[]( material )
 		
-		_material=material
+		Visible=True
 	End
 	
 	#rem monkeydoc Copies the model.
@@ -34,7 +35,14 @@ Class Model Extends Renderable
 		
 		Local copy:=New Model( Self,parent )
 		
-		CopyComplete( copy )
+		CopyTo( copy )
+		
+		copy._bones=_bones.Slice( 0 )
+		
+		For Local i:=0 Until _bones.Length
+			
+			copy._bones[i].entity=_bones[i].entity.LastCopy
+		Next
 		
 		Return copy
 	End
@@ -109,9 +117,8 @@ Class Model Extends Renderable
 		Next
 		
 		Return Null
-	
 	End
-
+	
 	#rem monkeydoc Loads a boned model from a file path.
 	
 	On its own, mojo3d can only load gltf2 format mesh and model files.
@@ -159,7 +166,7 @@ Class Model Extends Renderable
 		If Not _mesh Return
 		
 		If _bones
-
+			
 			If _boneMatrices.Length<>_bones.Length _boneMatrices=New Mat4f[ _bones.Length ]
 			
 			For Local i:=0 Until _bones.Length

+ 5 - 3
modules/mojo3d/entities/particlesystem.monkey2

@@ -9,16 +9,18 @@ Class ParticleSystem Extends Renderable
 		Super.New( parent )
 		
 		_pbuffer=New ParticleBuffer( particleCount )
-		
 		_material=New ParticleMaterial
+		
+		Visible=True
 	End
 
 	Method New( particleBuffer:ParticleBuffer,material:ParticleMaterial,parent:Entity=Null )
 		Super.New( parent )
 		
 		_pbuffer=particleBuffer
-		
 		_material=material
+		
+		Visible=True
 	End
 
 	#rem monkeydoc Copies the particle system.
@@ -27,7 +29,7 @@ Class ParticleSystem Extends Renderable
 		
 		Local copy:=New ParticleSystem( Self,parent )
 		
-		CopyComplete( copy )
+		CopyTo( copy )
 		
 		Return copy
 	End

+ 1 - 5
modules/mojo3d/entities/renderable.monkey2

@@ -4,10 +4,8 @@ Namespace mojo3d
 Class Renderable Extends Entity Abstract
 
 	Method New( parent:Entity=Null )
-	
 		Super.New( parent )
-		
-		Show()
+
 	End
 	
 	Property CastsShadow:bool()
@@ -26,8 +24,6 @@ Class Renderable Extends Entity Abstract
 		Super.New( renderable,parent )
 		
 		_castsShadow=renderable.CastsShadow
-		
-		Show()
 	End
 	
 	Method OnShow() Override

+ 3 - 1
modules/mojo3d/entities/sprite.monkey2

@@ -28,6 +28,8 @@ Class Sprite Extends Renderable
 		Self.New( parent )
 		
 		_material=material
+		
+		Visible=True
 	End
 
 	#rem monkeydoc Copies the sprite.
@@ -36,7 +38,7 @@ Class Sprite Extends Renderable
 		
 		Local copy:=New Sprite( Self,parent )
 		
-		CopyComplete( copy )
+		CopyTo( copy )
 		
 		Return copy
 	End

+ 5 - 0
modules/mojo3d/geometry/loader.monkey2

@@ -26,5 +26,10 @@ Class Mojo3dLoader
 		
 		Return Null
 	End
+	
+	Method LoadAnimation:Animation( path:String ) Virtual
+		
+		Return Null
+	End
 
 End

+ 3 - 3
modules/mojo3d/render/deferredrenderer.monkey2

@@ -165,7 +165,7 @@ Class DeferredRenderer Extends Renderer
 	Method RenderTransparent()
 		
 		_device.ColorMask=ColorMask.All
-		_device.DepthMask=false
+		_device.DepthMask=True'false
 		_device.DepthFunc=DepthFunc.LessEqual
 		_device.RenderPass=0
 
@@ -173,9 +173,9 @@ Class DeferredRenderer Extends Renderer
 	End
 	
 	Method RenderSprites()
-
+		
 		_device.ColorMask=ColorMask.All
-		_device.DepthMask=False
+		_device.DepthMask=True'False
 		_device.DepthFunc=DepthFunc.LessEqual
 		_device.RenderPass=0
 		

+ 1 - 1
modules/mojo3d/render/forwardrenderer.monkey2

@@ -186,7 +186,7 @@ Class ForwardRenderer Extends Renderer
 	Method RenderSprites()
 
 		_device.ColorMask=ColorMask.All
-		_device.DepthMask=False
+		_device.DepthMask=True'False
 		_device.DepthFunc=DepthFunc.LessEqual
 		_device.RenderPass=0
 		

+ 11 - 0
modules/mojo3d/render/materials/spritematerial.monkey2

@@ -19,6 +19,8 @@ Class SpriteMaterial Extends Material
 		
 		ColorTexture=Texture.ColorTexture( Color.White )
 		ColorFactor=Color.White
+		
+		AlphaDiscard=.5
 	End
 	
 	Method New( material:SpriteMaterial )
@@ -50,6 +52,15 @@ Class SpriteMaterial Extends Material
 	
 		Uniforms.SetColor( "ColorFactor",color )
 	End
+	
+	Property AlphaDiscard:Float()
+		
+		Return Uniforms.GetFloat( "AlphaDiscard" )
+	
+	Setter( discard:Float )
+		
+		Uniforms.SetFloat( "AlphaDiscard",discard )
+	End
 
 	#rem monkeydoc Loads a sprite material from an image file.
 	#end	

+ 87 - 64
modules/mojo3d/render/renderer.monkey2

@@ -22,7 +22,7 @@ Class Renderer
 	
 	#rem monkeydoc Array containing the cascaded shadow map frustum splits for directional light shadows.
 	
-	Defaults to Float[]( 1.0/64.0,1.0/16.0,1.0/4.0 )
+	Defaults to Float[]( 8.0,16.0,64.0,256.0 )
 	
 	Must have length 3.
 		
@@ -32,7 +32,7 @@ Class Renderer
 		Return _csmSplits
 		
 	Setter( splits:Float[] )
-		Assert( splits.Length=3,"CSMSplits array must have 3 elements" )
+		Assert( splits.Length=4,"CSMSplits array must have 4 elements" )
 		
 		_csmSplits=splits.Slice( 0 )
 	End
@@ -99,10 +99,9 @@ Class Renderer
 		ValidateShadowMaps()
 		
 		_csmSplitDepths[0]=camera.Near
-		For Local i:=1 Until 4
-			_csmSplitDepths[i]=camera.Near+_csmSplits[i-1]*(camera.Far-camera.Near)
+		For Local i:=1 Until 5
+			_csmSplitDepths[i]=_csmSplitDepths[i-1]+_csmSplits[i-1]
 		Next
-		_csmSplitDepths[4]=camera.Far
 		
 		Local time:=Float( Now() )
 
@@ -146,6 +145,20 @@ Class Renderer
 			r.OnRender( _renderQueue )
 		Next
 		
+		Local ops:=_renderQueue.OpaqueOps
+
+#rem		
+		ops.Sort( Lambda:Int( x:RenderOp,y:RenderOp )
+			If x.instance<y.instance Return -1
+			If x.instance>y.instance Return 1
+			If x.material<y.material Return -1
+			If x.material>y.material Return 1
+			If x<y Return -1
+			If x>y Return 1
+			Return 0
+		End )
+#end
+		
 		'***** Set render camera *****
 
 		_renderCamera=camera
@@ -211,19 +224,6 @@ Class Renderer
 			New Mat3f( +1,0, 0, 0,0,-1,  0,-1,0 ),	'-Y
 			New Mat3f( +1,0, 0, 0,-1,0,  0,0,+1 ),	'+Z
 			New Mat3f( -1,0, 0, 0,-1,0,  0,0,-1 ) )	'-Z
-			
-		#rem
-		Matxf tforms[]={
-		{ {0,0,+1},{0,-1,0},{-1,0,0},{0,0,0} },	//+X
-		{ {0,0,-1},{0,-1,0},{+1,0,0},{0,0,0} },	//-X
-		{ {+1,0,0},{0,0,+1},{0,+1,0},{0,0,0} },	//+Y test me!
-		{ {+1,0,0},{0,0,-1},{0,-1,0},{0,0,0} },	//-Y
-		{ {+1,0,0},{0,-1,0},{0,0,+1},{0,0,0} },	//+Z
-		{ {-1,0,0},{0,-1,0},{0,0,-1},{0,0,0} }	//-Z
-	};		
-		
-		#end
-	
 	
 	End
 
@@ -304,29 +304,6 @@ Class Renderer
 		RenderRenderOps( _renderQueue.SpriteOps,_renderCamera.InverseMatrix,_renderCamera.ProjectionMatrix )
 	End
 	
-	#rem
-	Method RenderAmbient()
-		
-		_device.ColorMask=ColorMask.All
-		_device.DepthMask=True
-		_device.DepthFunc=DepthFunc.LessEqual
-		_device.BlendMode=BlendMode.Opaque
-		_device.RenderPass=1
-
-		RenderRenderOps( _renderQueue.OpaqueOps,_renderCamera.InverseMatrix,_renderCamera.ProjectionMatrix )
-	End
-	
-	Method RenderSprites()
-
-		_device.ColorMask=ColorMask.All
-		_device.DepthMask=False
-		_device.DepthFunc=DepthFunc.LessEqual
-		_device.RenderPass=0
-
-		RenderRenderOps( _spriteQueue.TransparentOps,_renderCamera.InverseMatrix,_renderCamera.ProjectionMatrix )
-	End
-	#end
-	
 	Method RenderCSMShadows( light:Light )
 	
 		'Perhaps use a different device for CSM...?
@@ -341,13 +318,10 @@ Class Renderer
 		_device.ColorMask=ColorMask.All
 		_device.DepthMask=True
 		_device.Clear( Color.White,1.0 )
-'		_device.ColorMask=ColorMask.None
-'		_device.DepthMask=True
-'		_device.Clear( Null,1.0 )
 		
 		_device.DepthFunc=DepthFunc.LessEqual
 		_device.BlendMode=BlendMode.Opaque
-		_device.CullMode=CullMode.Back
+		_device.CullMode=CullMode.Front	'CullMode.Back
 		_device.RenderPass=16
 
 		Local invLightMatrix:=light.InverseMatrix
@@ -394,7 +368,7 @@ Class Renderer
 			
 			_device.Scissor=_device.Viewport
 				
-			RenderRenderOps( _renderQueue.ShadowOps,invLightMatrix,lightProj )
+			RenderShadowOps( _renderQueue.ShadowOps,invLightMatrix,lightProj )
 			
 		Next
 		
@@ -415,10 +389,10 @@ Class Renderer
 		_device.Scissor=_device.Viewport
 		_device.ColorMask=ColorMask.All
 		_device.DepthMask=True
+		
 		_device.DepthFunc=DepthFunc.LessEqual
-		'
 		_device.BlendMode=BlendMode.Opaque
-		_device.CullMode=CullMode.Back
+		_device.CullMode=CullMode.Front
 		_device.RenderPass=17
 		
 		Local lightProj:=Mat4f.Frustum( -1,+1,-1,+1,1,light.Range )
@@ -437,7 +411,7 @@ Class Renderer
 			
 			Local viewMatrix:=New AffineMat4f( _psmFaceTransforms[i] ) * invLightMatrix
 
-			RenderRenderOps( _renderQueue.ShadowOps,viewMatrix,lightProj )
+			RenderShadowOps( _renderQueue.ShadowOps,viewMatrix,lightProj )
 			
 		Next
 
@@ -475,8 +449,8 @@ Class Renderer
 	
 	Field _psmFaceTransforms:Mat3f[]
 	
-	Field _csmSize:=2048
-	Field _csmSplits:=New Float[]( 1.0/64.0,1.0/16.0,1.0/4.0 )
+	Field _csmSize:=4096
+	Field _csmSplits:=New Float[]( 8.0,16.0,64.0,256.0 )
 	Field _csmSplitDepths:=New Float[5]
 	Field _csmTexture:Texture
 	Field _csmDepth:Texture
@@ -547,49 +521,97 @@ Class Renderer
 
 	Method RenderRenderOps( ops:Stack<RenderOp>,viewMatrix:AffineMat4f,projMatrix:Mat4f )
 		
+		Local viewProjMatrix:=projMatrix * viewMatrix
+		
 		_runiforms.SetMat4f( "ViewMatrix",viewMatrix )
 		_runiforms.SetMat4f( "ProjectionMatrix",projMatrix )
-		_runiforms.SetMat4f( "ViewProjectionMatrix",projMatrix * viewMatrix )
+		_runiforms.SetMat4f( "ViewProjectionMatrix",viewProjMatrix )
 		_runiforms.SetMat4f( "InverseProjectionMatrix",-projMatrix )
 		
+		'_iuniforms.SetMat4fArray( "ModelBoneMatrices",Null )
+		
 		Local instance:Entity=_renderCamera
+		Local bones:Mat4f[]
 		Local material:Material
 		
 		For Local op:=Eachin ops
 			
 			If op.instance<>instance
-				
 				instance=op.instance
-				
-				Local modelMat:= instance ? instance.Matrix Else New AffineMat4f
+				Local modelMat:=instance ? instance.Matrix Else New AffineMat4f
 				Local modelViewMat:=viewMatrix * modelMat
+				Local modelViewNormMat:=modelViewMat.m.Cofactor()
 				Local modelViewProjMat:=projMatrix * modelViewMat
-				Local modelViewNormMat:=~-modelViewMat.m
-					
 				_iuniforms.SetMat4f( "ModelMatrix",modelMat )
 				_iuniforms.SetMat4f( "ModelViewMatrix",modelViewMat )
-				_iuniforms.SetMat4f( "ModelViewProjectionMatrix",modelViewProjMat )
 				_iuniforms.SetMat3f( "ModelViewNormalMatrix",modelViewNormMat )
+				_iuniforms.SetMat4f( "ModelViewProjectionMatrix",modelViewProjMat )
+			Endif
 				
-				_iuniforms.SetMat4fArray( "ModelBoneMatrices",op.bones )
+			If op.bones _iuniforms.SetMat4fArray( "ModelBoneMatrices",op.bones )
 				
-			Endif
-			
+			If op.uniforms _device.BindUniformBlock( op.uniforms )
+						
 			If op.material<>material
-				
 				material=op.material
-				
 				_device.Shader=material.ValidateShader()
 				_device.BindUniformBlock( material.Uniforms )
 				If material.BlendMode<>BlendMode.Opaque
 					_device.BlendMode=material.BlendMode
 				Endif
 				_device.CullMode=material.CullMode
-				
 			Endif
 			
+			_device.VertexBuffer=op.vbuffer
+			If op.ibuffer
+				_device.IndexBuffer=op.ibuffer
+				_device.RenderIndexed( op.order,op.count,op.first )
+			Else
+				_device.Render( op.order,op.count,op.first )
+			Endif
+			
+		Next
+	End
+
+	Method RenderShadowOps( ops:Stack<RenderOp>,viewMatrix:AffineMat4f,projMatrix:Mat4f )
+		
+		Local viewProjMatrix:=projMatrix * viewMatrix
+		
+		_runiforms.SetMat4f( "ViewMatrix",viewMatrix )
+		_runiforms.SetMat4f( "ProjectionMatrix",projMatrix )
+		_runiforms.SetMat4f( "ViewProjectionMatrix",viewProjMatrix )
+		_runiforms.SetMat4f( "InverseProjectionMatrix",-projMatrix )
+		
+		'_iuniforms.SetMat4fArray( "ModelBoneMatrices",Null )
+		
+		Local instance:Entity=_renderCamera
+		Local bones:Mat4f[]
+		Local material:Material
+		
+		For Local op:=Eachin ops
+			
+			If op.instance<>instance
+				instance=op.instance
+				Local modelMat:=instance ? instance.Matrix Else New AffineMat4f
+				Local modelViewMat:=viewMatrix * modelMat
+				Local modelViewNormMat:=modelViewMat.m.Cofactor()
+				Local modelViewProjMat:=projMatrix * modelViewMat
+				_iuniforms.SetMat4f( "ModelMatrix",modelMat )
+				_iuniforms.SetMat4f( "ModelViewMatrix",modelViewMat )
+				_iuniforms.SetMat3f( "ModelViewNormalMatrix",modelViewNormMat )
+				_iuniforms.SetMat4f( "ModelViewProjectionMatrix",modelViewProjMat )
+			Endif
+				
+			If op.bones _iuniforms.SetMat4fArray( "ModelBoneMatrices",op.bones )
+			
 			If op.uniforms _device.BindUniformBlock( op.uniforms )
 						
+			If op.material<>material
+				material=op.material
+				_device.Shader=material.ValidateShader()
+				_device.BindUniformBlock( material.Uniforms )
+			Endif
+			
 			_device.VertexBuffer=op.vbuffer
 			If op.ibuffer
 				_device.IndexBuffer=op.ibuffer
@@ -599,6 +621,7 @@ Class Renderer
 			Endif
 			
 		Next
+
 	End
 
 End

+ 10 - 8
modules/mojo3d/scene/component.monkey2

@@ -61,17 +61,21 @@ Class Component
 		
 		Return _type
 	End
-	
-	Method Copy:Component( entity:Entity )
-		
-		Return OnCopy( entity )
-	End
 		
+	Internal
+
 	Method OnCopy:Component( entity:Entity ) Virtual
+
+		RuntimeError( "Don't know how to copy component of type "+Type.Name )
+		
 		Return Null
 	End
 	
-	Internal
+	Method OnShow() virtual
+	End
+	
+	Method OnHide() Virtual
+	End
 		
 	Method OnBeginUpdate() Virtual
 	End
@@ -86,8 +90,6 @@ Class Component
 	
 	Field _entity:Entity
 	
-	Field _priority:Int
-	
 	Field _type:ComponentType
 End
 

+ 100 - 89
modules/mojo3d/scene/entity.monkey2

@@ -41,11 +41,9 @@ Class Entity Extends DynamicObject
 		
 		If _parent 
 			_scene=_parent._scene
-			
 			_parent._children.Add( Self )
 		Else
 			_scene=Scene.GetCurrent()
-			
 			_scene.RootEntities.Add( Self )
 		Endif
 			
@@ -57,13 +55,16 @@ Class Entity Extends DynamicObject
 	Method Copy:Entity( parent:Entity=Null ) Virtual
 		
 		Local copy:=New Entity( Self,parent )
-
-		CopyComplete( copy )
 		
-		Return copy
+		CopyTo( copy )
+		
+		return copy
 	End
 	
-	#rem monkeydoc @hidden
+	#rem monkeydoc Sequence id.
+	
+	The sequence id is an integer that is incremented whenever the entity's matrix is modified.
+	
 	#end
 	Property Seq:Int()
 		
@@ -111,6 +112,8 @@ Class Entity Extends DynamicObject
 		Else
 			_scene.RootEntities.Add( Self )
 		Endif
+		
+		ValidateVisibility()
 			
 		Invalidate()
 	End
@@ -137,20 +140,27 @@ Class Entity Extends DynamicObject
 	
 	Setter( visible:Bool )
 		
-		If visible Show() Else Hide()
-	End
-
-	#rem monkeydoc Entity animator.
-	#end	
-	Property Animator:Animator()
+		If visible=_visible Return
 		
-		Return _animator
-	
-	Setter( animator:Animator )
+		_visible=visible
 		
-		_animator=animator
+		ValidateVisibility()
 	End
 	
+	#rem monkeydoc True if entity and all parents are visible.
+	#end
+	Property ReallyVisible:Bool()
+		
+		Return _rvisible
+	End
+
+	#rem monkeydoc Last copy.
+	#end
+	Property LastCopy:Entity()
+		
+		Return _lastCopy
+	End
+
 	'***** World space properties *****
 	
 	#rem monkeydoc World space transformation matrix.
@@ -285,34 +295,6 @@ Class Entity Extends DynamicObject
 		Invalidate()
 	End
 	
-	#rem monkeydoc Hides the entity and all of its children
-	#end
-	Method Hide()
-		
-		If _visible
-			_visible=False
-			OnHide()
-		Endif
-		
-		For Local child:=Eachin _children
-			child.Hide()
-		Next
-	End
-	
-	#rem monkeydoc Shows the entity and all of its children
-	#end
-	Method Show()
-		
-		If Not _visible
-			_visible=True
-			OnShow()
-		Endif
-
-		For Local child:=Eachin _children
-			child.Show()
-		Next
-	End
-	
 	#rem monkeydoc Destroys the entity and all of its children.
 	#end
 	Method Destroy()
@@ -361,30 +343,7 @@ Class Entity Extends DynamicObject
 		Next
 		Return n
 	End
-	
-	Internal
-	
-	Method AddComponent( c:Component )
-
-		If c.Type.Flags & ComponentTypeFlags.Singleton And NumComponents( c.Type ) RuntimeError( "Duplicate component" )
-			
-		For Local i:=0 Until _components.Length
-			
-			If c.Type.Priority>_components[i].Type.Priority
-				
-				_components.Insert( i,c )
-				
-				Return
-			Endif
-		Next
 
-		_components.Add( c )
-		
-		Return
-	End
-	
-	Public
-	
 	Method AddComponent<T>:T() Where T Extends Component
 		
 		Local c:=New T( Self )
@@ -417,12 +376,9 @@ Class Entity Extends DynamicObject
 			index-=1
 		Next
 	End
-		
 	
-Protected
+	Protected
 
-	#rem monkeydoc @hidden
-	#end
 	Method New( entity:Entity,parent:Entity )
 		Self.New( parent )
 		
@@ -433,43 +389,67 @@ Protected
 		Invalidate()
 	End
 	
-	#rem monkeydoc @hidden
-	#end
 	Method OnShow() Virtual
 	End
 	
-	#rem monkeydoc @hidden
-	#end
 	Method OnHide() Virtual
 	End
-		
-	#rem monkeydoc @hidden
+	
+	#rem monkeydoc OnCopy
+	
+	1) Recursively copies all child entities.
+	
+	2) Invokes OnCopy for each component.
+	
+	3) Copies visibility.
+	
+	4) Invokes Copied signal.
+	
 	#end
-	Method CopyComplete( copy:Entity )
+	Method CopyTo( copy:Entity )
+		
+		_lastCopy=copy
 		
 		For Local child:=Eachin _children
 			child.Copy( copy )
 		Next
-
+		
+		'should really be different pass...ie: ALL entities should be copied before ANY components.
 		For Local c:=Eachin _components
-			c.Copy( copy )
+			c.OnCopy( copy )
 		Next
-
+		
+		copy.Visible=Visible
+		
 		Copied( copy )
 	End
+
+	Internal
 	
-Internal
+	Method AddComponent( c:Component )
+
+		If c.Type.Flags & ComponentTypeFlags.Singleton 
+			If NumComponents( c.Type ) RuntimeError( "Duplicate component" )
+		Endif
+			
+		For Local i:=0 Until _components.Length
+			If c.Type.Priority>_components[i].Type.Priority
+				_components.Insert( i,c )
+				Return
+			Endif
+		Next
+
+		_components.Add( c )
+	End
 
 	'bottom up
 	Method BeginUpdate()
 
 		For Local e:=Eachin _children
-			
 			e.BeginUpdate()
 		Next
 		
 		For Local c:=Eachin _components
-			
 			c.OnBeginUpdate()
 		Next
 		
@@ -479,12 +459,10 @@ Internal
 	Method Update( elapsed:Float )
 		
 		For Local c:=Eachin _components
-			
 			c.OnUpdate( elapsed )
 		End
 		
 		For Local e:=Eachin _children
-			
 			e.Update( elapsed )
 		Next
 	End
@@ -502,8 +480,10 @@ Private
 	Field _scene:Scene
 	Field _parent:Entity
 	Field _children:=New Stack<Entity>
+	Field _components:=New Stack<Component>
+	Field _lastCopy:Entity
+	Field _rvisible:Bool
 	Field _visible:Bool
-	Field _animator:Animator
 	
 	Field _t:Vec3f=New Vec3f
 	Field _r:Mat3f=New Mat3f
@@ -515,8 +495,6 @@ Private
 	Field _W:AffineMat4f
 	Field _IW:AffineMat4f
 	
-	Field _components:=New Stack<Component>
-	
 	Method InvalidateWorld()
 		
 		If _dirty & _dirty.W Return
@@ -537,5 +515,38 @@ Private
 		
 		InvalidateWorld()
 	End
+	
+	Method ValidateVisibility()
+		
+		If _visible And (Not _parent Or _parent._rvisible)
+			
+			If _rvisible Return
+			
+			_rvisible=True
 
+			OnShow()
+			
+			For Local c:=Eachin _components
+				c.OnShow()
+			Next
+		
+		Else
+			
+			If Not _rvisible Return
+			
+			_rvisible=False
+			
+			OnHide()
+			
+			For Local c:=Eachin _components
+				c.OnHide()
+			Next
+		Endif
+		
+		For Local child:=Eachin _children
+			
+			child.ValidateVisibility()
+		Next
+	
+	End
 End

+ 1 - 1
modules/mojo3d/scene/entityexts.monkey2

@@ -13,7 +13,7 @@ Public
 #end
 Class Entity Extension
 
-	#rem monkeydoc Local space rotation in degrees.
+	#rem monkeydoc World space rotation in degrees.
 	#end
 	Property Rotation:Vec3f()
 		

+ 13 - 2
modules/mojo3d/tests/sprites.monkey2

@@ -17,6 +17,8 @@ Class MyWindow Extends Window
 	
 	Field _scene:Scene
 	
+	Field _fog:FogEffect
+	
 	Field _camera:Camera
 	
 	Field _light:Light
@@ -33,6 +35,13 @@ Class MyWindow Extends Window
 		
 		_scene.SkyTexture=Texture.Load( "asset::miramar-skybox.jpg",TextureFlags.FilterMipmap|TextureFlags.Cubemap )
 		
+		'create fog
+		'
+		_fog=New FogEffect
+		_fog.Near=0
+		_fog.Far=50
+		_scene.AddPostEffect( _fog )
+		
 		'create camera
 		'
 		_camera=New Camera
@@ -53,6 +62,8 @@ Class MyWindow Extends Window
 		'
 		Local material:=SpriteMaterial.Load( "asset::Acadia-Tree-Sprite.png" )
 		
+		material.AlphaDiscard=1.0/255.0
+		
 		For Local i:=0 Until 1000
 			
 			Local sprite:=New Sprite( material )
@@ -70,9 +81,9 @@ Class MyWindow Extends Window
 		
 		For Local i:=0 Until 100
 			
-			Local box:=Model.CreateBox( New Boxf( -5,0,-5,5,Rnd(2,10),5 ),1,1,1,New PbrMaterial( New Color( Rnd(),Rnd(),Rnd() ) ) )
+'			Local box:=Model.CreateBox( New Boxf( -5,0,-5,5,Rnd(2,10),5 ),1,1,1,New PbrMaterial( New Color( Rnd(),Rnd(),Rnd() ) ) )
 			
-			box.Move( Rnd(-50,50),0,Rnd(-50,50) )
+'			box.Move( Rnd(-50,50),0,Rnd(-50,50) )
 
 		next