瀏覽代碼

WIP jsonifier loading stuff...

Mark Sibly 7 年之前
父節點
當前提交
03e5d99fa1

+ 6 - 8
modules/mojo3d/scene/entities/model.monkey2

@@ -126,7 +126,7 @@ Class Model Extends Renderable
 		
 		Local editing:=scene.Editing
 		
-		scene.Editing=False
+		If editing scene.Jsonifier.BeginLoading()
 		
 		Local model:Model
 		
@@ -134,14 +134,12 @@ Class Model Extends Renderable
 			
 			model=loader.LoadModel( path )
 			
-			If Not model Continue
-			
-			If editing scene.Jsonifier.AddInstance( model,"mojo3d.Model.Load",New Variant[]( path )  )
-				
-			Exit
+			If model Exit
 		Next
-
-		scene.Editing=editing
+		
+		If editing scene.Jsonifier.EndLoading()
+		
+		If editing scene.Jsonifier.AddInstance( model,"mojo3d.Model.Load",New Variant[]( path )  )
 		
 		Return model
 	End

+ 19 - 10
modules/mojo3d/scene/entities/sprite.monkey2

@@ -27,12 +27,13 @@ Class Sprite Extends Renderable
 		Name="Sprite"
 		
 		AddInstance()
+		
+		Visible=True
 	End
 
 	Method New( material:Material,parent:Entity=Null )
-		Self.New( parent )
 		
-		_material=material
+		Super.New( parent )
 		
 		Name="Sprite"
 		Material=material
@@ -42,6 +43,17 @@ Class Sprite Extends Renderable
 		Visible=True
 	End
 
+	Method New( sprite:Sprite,parent:Entity )
+		
+		Super.New( sprite,parent )
+		
+		_material=sprite._material
+		_handle=sprite._handle
+		_mode=sprite._mode
+		
+		AddInstance( sprite )
+	End
+	
 	#rem monkeydoc Copies the sprite.
 	#end	
 	Method Copy:Sprite( parent:Entity=Null ) Override
@@ -58,6 +70,7 @@ Class Sprite Extends Renderable
 	This must currently be an instance of a SpriteMaterial.
 	
 	#end	
+	[jsonify=1]
 	Property Material:Material()
 		
 		Return _material
@@ -70,6 +83,7 @@ Class Sprite Extends Renderable
 	#rem monkeydoc Rect within texture to draw.
 	
 	#end	
+	[jsonify=1]
 	Property TextureRect:Rectf()
 		
 		Return _textureRect
@@ -84,6 +98,7 @@ Class Sprite Extends Renderable
 	Defaults to 0.5,0.5.
 	
 	#end
+	[jsonify=1]
 	Property Handle:Vec2f()
 		
 		Return _handle
@@ -98,6 +113,7 @@ Class Sprite Extends Renderable
 	Defaults to SpriteMode.Billboard.
 	
 	#end
+	[jsonify=1]
 	Property Mode:SpriteMode()
 		
 		Return _mode
@@ -109,15 +125,8 @@ Class Sprite Extends Renderable
 	
 	Protected
 
-	Method New( sprite:Sprite,parent:Entity )
-		Super.New( sprite,parent )
-		
-		_material=sprite._material
-		_handle=sprite._handle
-		_mode=sprite._mode
-	End
-	
 	Method OnRender( rq:RenderQueue ) Override
+		
 		rq.AddSpriteOp( Self )
 	End
 	

+ 137 - 78
modules/mojo3d/scene/jsonifier/jsonifier.monkey2

@@ -2,64 +2,91 @@ Namespace mojo3d.jsonifier
 
 Class Jsonifier
 
+Private
+	
+	Class Instance
+		Field id:Int
+		Field obj:Variant
+		Field ctor:Invocation
+		Field initialState:JsonObject
+		
+		Operator To:String()
+			Return "Instance(type="+obj.Type+" dynamic type="+obj.DynamicType+")"
+		End
+	End
+	
+	Field _loading:Int
+	Field _instLoading:Int
+	Field _instsByObj:=New Map<Object,Instance>
+	Field _insts:=New Stack<Instance>
+	
+Public
+
+	Method BeginLoading()
+		
+		_loading+=1
+
+		If _loading>1 Return
+
+		_instLoading=_insts.Length
+	End
+	
+	Method EndLoading()
+		
+		_loading-=1
+		
+		If _loading Return
+		
+		For Local i:=_instLoading Until _insts.Length
+			Local inst:=_insts[i]
+			Local tobj:=Cast<Object>( inst.obj )
+			inst.initialState=JsonifyState( tobj )
+		next
+	End
+	
 	Method AddInstance( obj:Variant,ctor:Invocation )
 		
 		Local tobj:=Cast<Object>( obj )
 		
-		Assert( Not _instsByObj.Contains( tobj ) )
-		
-		Local inst:=New Instance
+		Local inst:=_instsByObj[tobj]
+		If inst
+			If inst.ctor Print "Warning: overwriting instance ctor for "+inst
+			inst.ctor=ctor
+			Return
+		Endif
+			
+		inst=New Instance
+		inst.id=_insts.Length
 		inst.obj=obj
-		inst.id="@"+String(_insts.Length)
-		inst.ctor=ctor
-		inst.initialState=JsonifyState( tobj )
 		
-		_instsByObj[tobj]=inst
-		_instsById[inst.id]=inst
-
 		_insts.Add( inst )
-	End
-
-	'ctor via ctor
-	Method AddInstance( obj:Variant,args:Variant[] )
-		
-		AddInstance( obj,New Invocation( obj.DynamicType,"New",Null,args ) )
-	end
-
-	'ctor via method call
-	Method AddInstance( obj:Variant,decl:String,inst:Variant,args:Variant[] )
+		_instsByObj[tobj]=inst
 		
-		AddInstance( obj,New Invocation( decl,inst,args ) )
-	End
+		If _loading Return
 
-	'ctor via function call
-	Method AddInstance( obj:Variant,decl:String,args:Variant[] )
-		
-		AddInstance( obj,New Invocation( decl,Null,args ) )
+		inst.ctor=ctor
+		inst.initialState=JsonifyState( tobj )
 	End
-
+	
 	Method JsonifyInstances:JsonObject()
 		
 		Local jobj:=New JsonObject
 		
 		jobj["assetsDir"]=New JsonString( AssetsDir() )
 		
-		Local jinsts:=New JsonArray( _insts.Length )
+		Local jinsts:=New Stack<JsonValue>
 		
 		For Local i:=0 Until _insts.Length
 			
-			Local jobj:=New JsonObject
+'			Print "Jsonifying "+i
 			
 			Local inst:=_insts[i]
 			Local tobj:=Cast<Object>( inst.obj )
+
+			'compute delta state
+			Local state:=JsonifyState( tobj )
 			
-			jobj["id"]=New JsonString( inst.id )
-			
-			jobj["type"]=New JsonString( tobj.DynamicType.Name )
-			
-			jobj["ctor"]=Jsonify( inst.ctor )
-			
-			Local state:=JsonifyState( tobj ),dstate:=New JsonObject
+			Local dstate:=New JsonObject
 			
 			For Local it:=Eachin state.All()
 				
@@ -69,12 +96,19 @@ Class Jsonifier
 				If CompareJson( x,y )<>0 dstate[it.Key]=x
 			Next
 			
-			jobj["state"]=dstate
+			If Not inst.ctor And dstate.Empty Continue
+			
+			Local jobj:=New JsonObject
+			jobj["type"]=New JsonString( tobj.DynamicType.Name )	'not actually used, cool for debug
+			
+			jobj["id"]=New JsonNumber( inst.id )
+			If inst.ctor jobj["ctor"]=Jsonify( inst.ctor )
+			If Not dstate.Empty jobj["state"]=dstate
 				
-			jinsts[i]=jobj
+			jinsts.Add( jobj )
 		Next
 
-		jobj["instances"]=jinsts
+		jobj["instances"]=New JsonArray( jinsts )
 		
 		Return jobj
 	End
@@ -86,44 +120,73 @@ Class Jsonifier
 		If jobj.Contains( "assetsDir" ) SetAssetsDir( jobj.GetString( "assetsDir" ) )
 		
 		Local jinsts:=jobj.GetArray( "instances" )
+
+		Local jobjsById:=New IntMap<JsonObject>
 		
 		For Local i:=0 Until jinsts.Length
 			
 			Local jobj:=jinsts.GetObject( i )
+			Assert( jobj.Contains( "id" ) )
 			
-			Local obj:Variant
-						
-			If i<_insts.Length
-				
-				obj=_insts[i].obj
-			Else
-				Local ctor:=Cast<Invocation>( Dejsonify( jobj["ctor"],Typeof<Invocation> ) )
+			Local id:=jobj.GetNumber( "id" )
+			Assert( Not jobjsById.Contains( id ) )
 			
-				obj=ctor.Execute()
-			Endif
-
-			_dejsonified.Add( obj )
+			jobjsById[id]=jobj
+		Next
+		
+		'copy of insts alreday created (ie: initial Scene)
+		Local tinsts:=_insts.ToArray(),id:=0
+		
+		For Local i:=0 Until jinsts.Length
 			
-			Local tobj:=Cast<Object>( obj )
+			Local jobj:=jinsts.GetObject( i )
+			If Not jobj.Contains( "ctor" ) Continue
 			
-			'set value type state only on this pass.
-			If jobj.Contains( "state" ) DejsonifyState( tobj,jobj.GetObject( "state" ),tobj.DynamicType,False )
-		Next
+			Local ctor:=Cast<Invocation>( Dejsonify( jobj["ctor"],Typeof<Invocation> ) )
+			ctor.Execute()
+			
+			For Local j:=id Until _insts.Length
+				
+				If Not jobjsById.Contains( j ) Continue
 
-		'set reference type state - do this on a second pass 'coz of forward refs. Probably wont always work?
-		For Local i:=0 Until _dejsonified.Length
+				Local jobj:=jobjsById[j]
+				Local tobj:=Cast<Object>( _insts[j].obj )
+				If jobj.Contains( "state" ) DejsonifyState( tobj,jobj.GetObject( "state" ),tobj.DynamicType,False )
+			Next
 			
-			Local jobj:=jinsts.GetObject( i )
+			id=_insts.Length
 			
-			Local obj:=_dejsonified[i]
+		Next
+		
+		'set reference type state - do this on a second pass 'coz of forward refs. Probably wont always work?
+		For Local i:=0 Until _insts.Length
 			
-			Local tobj:=Cast<Object>( obj )
+			If Not jobjsById.Contains( i ) Continue
 			
+			Local jobj:=jobjsById[i]
+			Local tobj:=Cast<Object>( _insts[i].obj )
 			If jobj.Contains( "state" ) DejsonifyState( tobj,jobj.GetObject( "state" ),tobj.DynamicType,True )
 		Next
 		
 		SetAssetsDir( assetsDir )
 	End
+	'ctor via ctor
+	Method AddInstance( obj:Variant,args:Variant[] )
+		
+		AddInstance( obj,New Invocation( obj.DynamicType,"New",Null,args ) )
+	end
+
+	'ctor via method call
+	Method AddInstance( obj:Variant,decl:String,inst:Variant,args:Variant[] )
+		
+		AddInstance( obj,New Invocation( decl,inst,args ) )
+	End
+
+	'ctor via function call
+	Method AddInstance( obj:Variant,decl:String,args:Variant[] )
+		
+		AddInstance( obj,New Invocation( decl,Null,args ) )
+	End
 	
 	Method Jsonify:JsonValue( value:Variant )
 		
@@ -131,7 +194,7 @@ Class Jsonifier
 		
 		Local type:=value.Type
 		Assert( type )
-		
+
 		'handle primitive types
 		Select type
 		Case Typeof<Bool>
@@ -152,7 +215,7 @@ Class Jsonifier
 			Local obj:=Cast<Object>( value )
 			If Not obj Return JsonValue.NullValue
 			Local inst:=_instsByObj[obj]
-			If inst Return New JsonString( inst.id )
+			If inst Return New JsonString( "@"+inst.id )
 		Case "Enum"
 			Return New JsonNumber( value.EnumValue )
 		End
@@ -164,6 +227,11 @@ Class Jsonifier
 		Next
 
 		Select type.Kind
+		Case "Unknown"
+			Local obj:=Cast<Object>( value )
+			If Not obj Return JsonValue.NullValue
+			Local inst:=_instsByObj[obj]
+			If inst Return New JsonString( "@"+inst.id )
 		Case "Class"
 			Return JsonValue.NullValue
 		Case "Array"
@@ -207,9 +275,8 @@ Class Jsonifier
 				Return type.NullValue
 			Elseif jvalue.IsString
 				Local id:=Int( jvalue.ToString().Slice( 1 ) )
-				Assert( id>=0 And id<_dejsonified.Length,"Dejsonify error" )
-				Local inst:=_dejsonified[id]
-				Return inst
+				Assert( id>=0 And id<_insts.Length,"Dejsonify error" )
+				Return _insts[id].obj
 			Endif
 		Case "Enum"
 			Return type.MakeEnum( jvalue.ToNumber() )
@@ -222,6 +289,12 @@ Class Jsonifier
 		Next
 
 		Select type.Kind
+		Case "Unknown"
+			If jvalue.IsString
+				Local id:=Int( jvalue.ToString().Slice( 1 ) )
+				Assert( id>=0 And id<_insts.Length,"Dejsonify error" )
+				Return _insts[id].obj
+			Endif
 		Case "Class"
 			Return type.NullValue
 		Case "Array"
@@ -242,20 +315,6 @@ Class Jsonifier
 	
 	Private
 	
-	Class Instance
-		Field obj:Variant
-		Field id:String
-		Field ctor:Invocation
-		Field initialState:JsonObject
-	End
-	
-	Field _insts:=New Stack<Instance>
-	
-	Field _instsByObj:=New Map<Object,Instance>
-	Field _instsById:=New StringMap<Instance>
-	
-	Field _dejsonified:=New Stack<Variant>
-	
 	Method JsonifyState:JsonObject( obj:Object )
 		
 		Local jobj:=New JsonObject
@@ -300,7 +359,7 @@ Class Jsonifier
 			
 			Local type:=d.Type
 			
-			Local isinst:=type.Kind="Class"
+			Local isinst:=type.Kind="Class" Or type.Kind="Unknown"
 			
 			If Not isinst And type.Kind="Array" And type.ElementType.Kind="Class" isinst=True
 				

+ 26 - 0
modules/mojo3d/scene/jsonifier/jsonifierexts.monkey2

@@ -1,5 +1,31 @@
 Namespace mojo3d.jsonifier
 
+Class MojoJsonifierExt Extends JsonifierExt
+	
+	Const Instance:=New MojoJsonifierExt
+
+	Method Jsonify:JsonValue( value:Variant,jsonifier:Jsonifier ) Override
+		
+		Select value.Type
+		Case Typeof<TextureFlags>
+			Return New JsonNumber( Int( Cast<TextureFlags>( value ) ) )
+		End
+		
+		Return Null
+	End
+
+	Method Dejsonify:Variant( jvalue:JsonValue,type:TypeInfo,jsonifier:Jsonifier ) Override
+		
+		Select type
+		Case Typeof<TextureFlags>
+			Return Cast<TextureFlags>( Int( jvalue.ToNumber() ) )
+		End
+		
+		Return Null
+	End
+	
+End
+
 Class StdJsonifierExt Extends JsonifierExt
 	
 	Const Instance:=New StdJsonifierExt

+ 25 - 0
modules/mojo3d/scene/material.monkey2

@@ -122,6 +122,24 @@ Class Material Extends Resource
 		Return GetOpaqueShader()
 	End
 	
+	Function LoadTexture:Texture( path:String,textureFlags:TextureFlags,flipy:Bool=False )
+		
+		Local scene:=Scene.GetCurrent()
+		
+		Return scene.LoadTexture( path,textureFlags,flipy )
+	End
+	
+	Function LoadTexture:Texture( path:String,name:String,textureFlags:TextureFlags,flipy:Bool=False )
+		
+		Local scene:=Scene.GetCurrent()
+		
+		Local texture:=scene.LoadTexture( path+"/"+name+".png",textureFlags,flipy )
+		
+		If Not texture texture=scene.LoadTexture( path+"/"+name+".jpg",textureFlags,flipy )
+			
+		Return texture
+	End
+	
 	Protected
 	
 	Method New()
@@ -143,6 +161,13 @@ Class Material Extends Resource
 		TextureMatrix=material.TextureMatrix
 	End
 	
+	Method AddInstance()
+
+		Local scene:=Scene.GetCurrent()
+		
+		If scene.Editing scene.Jsonifier.AddInstance( Self,New Variant[0] )
+	End
+	
 	Method AddInstance( args:Variant[] )
 		
 		Local scene:=Scene.GetCurrent()

+ 37 - 28
modules/mojo3d/scene/materials/pbrmaterial.monkey2

@@ -30,6 +30,30 @@ Public
 #end
 Class PbrMaterial Extends Material
 	
+	Private
+	
+	Method Init( boned:Bool )
+		
+		_boned=boned
+
+		Uniforms.DefaultTexture=Texture.ColorTexture( Color.White )
+		
+		ColorTexture=Null
+		EmissiveTexture=Null
+		MetalnessTexture=Null
+		RoughnessTexture=Null
+		OcclusionTexture=Null
+		NormalTexture=Null
+		
+		ColorFactor=Color.White
+		EmissiveFactor=Color.Black
+		MetalnessFactor=1.0
+		RoughnessFactor=1.0
+	End
+	
+	Public
+	
+	
 	#rem monkeydoc Creates a new pbr material.
 	
 	All properties default to white or '1' except for emissive factor which defaults to black. 
@@ -47,26 +71,14 @@ Class PbrMaterial Extends Material
 	#end
 	Method New( boned:Bool=False )
 		
-		_boned=boned
-		
-		Uniforms.DefaultTexture=Texture.ColorTexture( Color.White )
+		Init( boned )
 		
-		ColorTexture=Null
-		EmissiveTexture=Null
-		MetalnessTexture=Null
-		RoughnessTexture=Null
-		OcclusionTexture=Null
-		NormalTexture=Null
-		
-		ColorFactor=Color.White
-		EmissiveFactor=Color.Black
-		MetalnessFactor=1.0
-		RoughnessFactor=1.0
+		AddInstance( New Variant[]( boned ) )
 	End
 	
 	Method New( color:Color,metalness:Float=1.0,roughness:Float=1.0,boned:Bool=False )
 		
-		Self.New( boned )
+		Init( boned )
 		
 		ColorFactor=color
 		MetalnessFactor=metalness
@@ -230,12 +242,13 @@ Class PbrMaterial Extends Material
 	#end
 	Function Load:PbrMaterial( path:String,textureFlags:TextureFlags=TextureFlags.WrapST|TextureFlags.FilterMipmap )
 		
-		Local material:=New PbrMaterial
+		Local scene:=Scene.GetCurrent(),editing:=scene.Editing
 		
-		Local scene:=Scene.GetCurrent()
-		If scene.Editing 
-			scene.Jsonifier.AddInstance( material,"mojo3d.PbrMaterial.Load",New Variant[]( path,textureFlags ) )
+		If editing 
+			scene.Jsonifier.BeginLoading()
 		Endif
+
+		Local material:=New PbrMaterial
 		
 		Local texture:=LoadTexture( path,"color",textureFlags )
 		If texture
@@ -277,6 +290,11 @@ Class PbrMaterial Extends Material
 			If jobj.Contains( "roughnessFactor" ) material.RoughnessFactor=jobj.GetNumber( "roughnessFactor" )
 		Endif
 		
+		If editing 
+			scene.Jsonifier.EndLoading()
+			scene.Jsonifier.AddInstance( material,"mojo3d.PbrMaterial.Load",New Variant[]( path,textureFlags ) )
+		Endif
+		
 		Return material
 	End
 	
@@ -292,15 +310,6 @@ Class PbrMaterial Extends Material
 	
 	Field _dirty:=True
 	
-	Function LoadTexture:Texture( path:String,name:String,flags:TextureFlags,flipy:Bool=False )
-		
-		Local texture:=Texture.Load( path+"/"+name+".png",flags,flipy )
-		
-		If Not texture texture=Texture.Load( path+"/"+name+".jpg",flags,flipy )
-			
-		Return texture
-	End
-	
 	Method ValidateShaders()
 		
 		If Not _dirty Return

+ 18 - 3
modules/mojo3d/scene/materials/spritematerial.monkey2

@@ -16,11 +16,15 @@ Class SpriteMaterial Extends Material
 		ColorFactor=Color.White
 		
 		AlphaDiscard=.5
+		
+		AddInstance()
 	End
 	
 	Method New( material:SpriteMaterial )
 	
 		Super.New( material )
+		
+		AddInstance( material )
 	End
 	
 	#rem monkeydoc Creates a copy of the sprite material.
@@ -61,6 +65,7 @@ Class SpriteMaterial Extends Material
 		Return GetOpaqueShader()
 	End
 	
+	[jsonify=1]
 	Property ColorTexture:Texture()
 		
 		Return Uniforms.GetTexture( "ColorTexture" )
@@ -70,6 +75,7 @@ Class SpriteMaterial Extends Material
 		Uniforms.SetTexture( "ColorTexture",texture )
 	End
 	
+	[jsonify=1]
 	Property ColorFactor:Color()
 	
 		Return Uniforms.GetColor( "ColorFactor" )
@@ -79,6 +85,7 @@ Class SpriteMaterial Extends Material
 		Uniforms.SetColor( "ColorFactor",color )
 	End
 	
+	[jsonify=1]
 	Property AlphaDiscard:Float()
 		
 		Return Uniforms.GetFloat( "AlphaDiscard" )
@@ -92,14 +99,22 @@ Class SpriteMaterial Extends Material
 	#end	
 	Function Load:SpriteMaterial( path:String,textureFlags:TextureFlags=TextureFlags.FilterMipmap )
 		
-		Local texture:=Texture.Load( path,textureFlags )
-		If Not texture texture=Texture.ColorTexture( Color.Magenta )
+		Local scene:=Scene.GetCurrent(),editing:=scene.Editing
+		
+		If editing scene.Jsonifier.BeginLoading()
+		
+		Local texture:=LoadTexture( path,textureFlags )
+		'If Not texture texture=Texture.ColorTexture( Color.Magenta )
 		
 		Local material:=New SpriteMaterial
 		material.ColorTexture=texture
 		
+		If editing
+			scene.Jsonifier.EndLoading()
+			scene.Jsonifier.AddInstance( material,"mojo3d.SpriteMaterial.Load",New Variant[]( path,textureFlags ) )
+		Endif
+		
 		Return material
 	End
 	
 End
-

+ 63 - 67
modules/mojo3d/scene/scene.monkey2

@@ -10,9 +10,11 @@ Class Scene
 	If there is no current scene when a new scene is created, the new scene becomes the current scene.
 		
 	#end
-	Method New()
+	Method New( editable:Bool=False )
 		
 		If Not _current _current=Self
+			
+		_editable=editable
 		
 		_clearColor=Color.Sky
 
@@ -22,13 +24,14 @@ Class Scene
 		
 		_world=New World( Self )
 		
-		Local type:=TypeInfo.GetType( "mojo3d.Scene" )
-		If type And type.Kind="Class"
+		If _editable
+			Local type:=TypeInfo.GetType( "mojo3d.Scene" )
+			Assert( type And type.Kind="Class","mojo3d reflection must be enabled for editable scenes" )
 			_jsonifier=New Jsonifier
-			_jsonifier.AddInstance( Self,New Variant[0] )
+			_jsonifier.AddInstance( Self,New Variant[]( true ) )
+			_editing=True
 		Endif
 		
-		_editing=False
 	End
 	
 	#rem monkeydoc The sky texture.
@@ -40,7 +43,7 @@ Class Scene
 	This must currently be a valid cubemap texture.
 	
 	#end
-'	[jsonify=1]
+	[jsonify=1]
 	Property SkyTexture:Texture()
 		
 		Return _skyTexture
@@ -61,7 +64,7 @@ Class Scene
 	This must currently be a valid cubemap texture.
 	
 	#end
-'	[jsonify=1]
+	[jsonify=1]
 	Property EnvTexture:Texture()
 		
 		Return _envTexture
@@ -195,44 +198,6 @@ Class Scene
 		_csmSplits=splits.Slice( 0 )
 	End
 	
-	Property Editing:Bool()
-		
-		Return _editing
-	
-	Setter( editing:Bool )
-		
-		If editing And Not _jsonifier RuntimeError( "Scene is not editable" )
-		
-		_editing=editing
-	End
-	
-	Property Jsonifier:Jsonifier()
-		
-		Return _jsonifier
-	End
-	
-	Method PauseEditing()
-		
-		_editingPaused+=1
-	End
-	
-	Method ResumeEditing:Bool()
-		
-		_editingPaused-=1
-		
-		Return Editing
-	End
-	
-	Method LoadTexture:Texture( path:String,flags:TextureFlags,flipNormalY:Bool=False )
-		
-		Local texture:=Texture.Load( path,flags,flipNormalY )
-		If Not texture Return Null
-		
-		If Editing Jsonifier.AddInstance( texture,"mojo3d.Scene.LoadTexture",Self,New Variant[]( path,flags,flipNormalY ) )
-			
-		Return texture
-	End
-
 	#rem monkeydoc Finds an entity in the scene.
 	
 	Finds an entity in the scene with the given name.
@@ -320,6 +285,39 @@ Class Scene
 		Return _rootEntities.ToArray()
 	End
 	
+	'***** serialization stuff *****
+	
+	Property Editable:Bool()
+		
+		Return _editable
+	End
+	
+	Property Editing:Bool()
+		
+		Return _editing
+	
+	Setter( editing:Bool )
+		
+		If editing And Not _editable RuntimeError( "Scene is not editable" )
+		
+		_editing=editing
+	End
+	
+	Property Jsonifier:Jsonifier()
+		
+		Return _jsonifier
+	End
+	
+	Method LoadTexture:Texture( path:String,flags:TextureFlags,flipNormalY:Bool=False )
+		
+		Local texture:=Texture.Load( path,flags,flipNormalY )
+		If Not texture Return Null
+		
+		If Editing Jsonifier.AddInstance( texture,"mojo3d.Scene.LoadTexture",Self,New Variant[]( path,flags,flipNormalY ) )
+			
+		Return texture
+	End
+
 	#rem monkeydoc Saves the scene to a mojo3d scene file
 	#end
 	Method Save( path:String )
@@ -332,6 +330,25 @@ Class Scene
 		
 		SaveString( json,path )
 	End
+
+	#rem monkeydoc Loads a mojo3d scene file and makes it current
+	#end
+	Function Load:Scene( path:String )
+		
+		Local json:=LoadString( path )
+		If Not json Return Null
+		
+		Local jobj:=JsonObject.Parse( json )
+		If Not jobj Return Null
+		
+		Local scene:=New Scene( True )
+		
+		SetCurrent( scene )
+		
+		scene.Jsonifier.DejsonifyInstances( jobj )
+		
+		Return scene
+	End
 	
 	#rem monkeydoc Sets the current scene.
 	
@@ -355,25 +372,6 @@ Class Scene
 		Return _current
 	End
 	
-	#rem monkeydoc Loads a mojo3d scene file and makes it current
-	#end
-	Function Load:Scene( path:String )
-
-		Local json:=LoadString( path )
-		If Not json Return Null
-		
-		Local jobj:=JsonObject.Parse( json )
-		If Not jobj Return Null
-		
-		Local scene:=New Scene
-		
-		SetCurrent( scene )
-		
-		scene.Jsonifier.DejsonifyInstances( jobj )
-		
-		Return scene
-	End
-	
 	Internal
 
 	Property PostEffects:Stack<PostEffect>()
@@ -438,11 +436,9 @@ Class Scene
 	Field _world:World
 	
 	Field _jsonifier:Jsonifier
-	
+	Field _editable:Bool
 	Field _editing:Bool
 	
-	Field _editingPaused:=0
-	
 	Method Update( elapsed:Float )
 		
 		For Local e:=Eachin _rootEntities