瀏覽代碼

Rearranged mojo3d project structure and began work on scene jsonification.

Mark Sibly 7 年之前
父節點
當前提交
8684f13ba8
共有 41 個文件被更改,包括 1753 次插入116 次删除
  1. 44 34
      modules/mojo3d/mojo3d.monkey2
  2. 0 0
      modules/mojo3d/scene/bttypeconvs.monkey2
  3. 9 0
      modules/mojo3d/scene/component.monkey2
  4. 0 0
      modules/mojo3d/scene/components/animation.monkey2
  5. 0 0
      modules/mojo3d/scene/components/animator.monkey2
  6. 0 0
      modules/mojo3d/scene/components/behaviour.monkey2
  7. 0 0
      modules/mojo3d/scene/components/collider.monkey2
  8. 6 0
      modules/mojo3d/scene/components/flybehaviour.monkey2
  9. 45 0
      modules/mojo3d/scene/components/movebehaviour.monkey2
  10. 0 0
      modules/mojo3d/scene/components/native/internaledges.cpp
  11. 0 0
      modules/mojo3d/scene/components/native/internaledges.h
  12. 0 0
      modules/mojo3d/scene/components/rigidbody.monkey2
  13. 45 0
      modules/mojo3d/scene/components/rotatebehaviour.monkey2
  14. 0 0
      modules/mojo3d/scene/effects/bloomeffect.monkey2
  15. 0 0
      modules/mojo3d/scene/effects/fogeffect.monkey2
  16. 0 0
      modules/mojo3d/scene/effects/monochromeeffect.monkey2
  17. 29 4
      modules/mojo3d/scene/entities/camera.monkey2
  18. 26 30
      modules/mojo3d/scene/entities/light.monkey2
  19. 45 20
      modules/mojo3d/scene/entities/model.monkey2
  20. 0 0
      modules/mojo3d/scene/entities/particlebuffer.monkey2
  21. 0 0
      modules/mojo3d/scene/entities/particlematerial.monkey2
  22. 0 0
      modules/mojo3d/scene/entities/particlesystem.monkey2
  23. 2 2
      modules/mojo3d/scene/entities/renderable.monkey2
  24. 10 0
      modules/mojo3d/scene/entities/sprite.monkey2
  25. 44 3
      modules/mojo3d/scene/entity.monkey2
  26. 112 0
      modules/mojo3d/scene/jsonifier/comparejson.monkey2
  27. 109 0
      modules/mojo3d/scene/jsonifier/invocation.monkey2
  28. 312 0
      modules/mojo3d/scene/jsonifier/jsonifier.monkey2
  29. 164 0
      modules/mojo3d/scene/jsonifier/jsonifierexts.monkey2
  30. 425 0
      modules/mojo3d/scene/jsonifier/test.monkey2
  31. 16 2
      modules/mojo3d/scene/material.monkey2
  32. 15 1
      modules/mojo3d/scene/materials/pbrmaterial.monkey2
  33. 0 0
      modules/mojo3d/scene/materials/spritematerial.monkey2
  34. 0 0
      modules/mojo3d/scene/materials/watermaterial.monkey2
  35. 17 0
      modules/mojo3d/scene/mesh.monkey2
  36. 59 19
      modules/mojo3d/scene/meshprims.monkey2
  37. 0 0
      modules/mojo3d/scene/posteffect.monkey2
  38. 111 0
      modules/mojo3d/scene/scene.monkey2
  39. 0 0
      modules/mojo3d/scene/util3d.monkey2
  40. 8 1
      modules/mojo3d/tests/cubes.monkey2
  41. 100 0
      modules/mojo3d/tests/loadscene.monkey2

+ 44 - 34
modules/mojo3d/mojo3d.monkey2

@@ -5,43 +5,17 @@ Namespace mojo3d
 #Import "<mojo>"
 #Import "<opengl>"
 #import "<bullet>"
+#Import "<reflection>"
 
 Using std..
 Using mojo..
+Using mojo3d..
 Using opengl..
 Using bullet..
+Using reflection..
 
 #Import "assets/"
 
-#Import "components/animation"
-#Import "components/animator"
-#Import "components/rigidbody"
-#Import "components/collider"
-#Import "components/behaviour"
-#Import "components/flybehaviour"
-
-#Import "entities/camera"
-#Import "entities/light"
-#Import "entities/model"
-#Import "entities/particlebuffer"
-#Import "entities/particlematerial"
-#Import "entities/particlesystem"
-#Import "entities/renderable"
-#Import "entities/sprite"
-
-#Import "render/material"
-#Import "render/renderer"
-#Import "render/renderqueue"
-#Import "render/spritebuffer"
-#Import "render/posteffect"
-
-#Import "render/materials/pbrmaterial"
-#Import "render/materials/spritematerial"
-#Import "render/materials/watermaterial"
-
-#Import "render/effects/bloomeffect"
-#Import "render/effects/monochromeeffect"
-
 #Import "scene/raycastresult"
 #Import "scene/component"
 #Import "scene/dynamicobject"
@@ -49,16 +23,52 @@ Using bullet..
 #Import "scene/entityexts"
 #Import "scene/scene"
 #Import "scene/world"
+#Import "scene/material"
+#Import "scene/posteffect"
+
+#Import "scene/components/animation"
+#Import "scene/components/animator"
+#Import "scene/components/rigidbody"
+#Import "scene/components/collider"
+#Import "scene/components/behaviour"
+#Import "scene/components/flybehaviour"
+#Import "scene/components/movebehaviour"
+#Import "scene/components/rotatebehaviour"
+
+#Import "scene/entities/camera"
+#Import "scene/entities/light"
+#Import "scene/entities/model"
+#Import "scene/entities/particlebuffer"
+#Import "scene/entities/particlematerial"
+#Import "scene/entities/particlesystem"
+#Import "scene/entities/renderable"
+#Import "scene/entities/sprite"
+
+#Import "scene/materials/pbrmaterial"
+#Import "scene/materials/spritematerial"
+#Import "scene/materials/watermaterial"
+
+#Import "scene/effects/bloomeffect"
+#Import "scene/effects/monochromeeffect"
+
+#Import "scene/jsonifier/jsonifier"
+#Import "scene/jsonifier/invocation"
+#Import "scene/jsonifier/jsonifierexts"
+#Import "scene/jsonifier/comparejson"
+
+#Import "scene/mesh"
+#Import "scene/meshprims"
+#Import "scene/bttypeconvs"
+'#Import "scene/util3d"
+
+#Import "render/renderer"
+#Import "render/renderqueue"
+#Import "render/spritebuffer"
 
 #Import "loader/loader"
 #Import "loader/gltf2"
 #Import "loader/gltf2loader"
 
-#Import "geometry/mesh"
-#Import "geometry/meshprims"
-'#Import "geometry/util3d"
-#Import "geometry/bttypeconvs"
-
 Function Main()
 	
 #If __DESKTOP_TARGET__

+ 0 - 0
modules/mojo3d/geometry/bttypeconvs.monkey2 → modules/mojo3d/scene/bttypeconvs.monkey2


+ 9 - 0
modules/mojo3d/scene/component.monkey2

@@ -73,6 +73,15 @@ Class Component
 		_type=Null
 	End
 	
+Protected
+
+	Method AddInstance()
+		
+		Local scene:=_entity.Scene
+		
+		If scene.Editing scene.Jsonifier.AddInstance( Self,New Variant[]( _entity ) )
+	End
+	
 Internal
 
 	Method OnCopy:Component( entity:Entity ) Virtual

+ 0 - 0
modules/mojo3d/components/animation.monkey2 → modules/mojo3d/scene/components/animation.monkey2


+ 0 - 0
modules/mojo3d/components/animator.monkey2 → modules/mojo3d/scene/components/animator.monkey2


+ 0 - 0
modules/mojo3d/components/behaviour.monkey2 → modules/mojo3d/scene/components/behaviour.monkey2


+ 0 - 0
modules/mojo3d/components/collider.monkey2 → modules/mojo3d/scene/components/collider.monkey2


+ 6 - 0
modules/mojo3d/components/flybehaviour.monkey2 → modules/mojo3d/scene/components/flybehaviour.monkey2

@@ -6,8 +6,11 @@ Class FlyBehaviour Extends Behaviour
 	Method New( entity:Entity )
 		
 		Super.New( entity )
+		
+		AddInstance()
 	End
 	
+	[jsonify=1]
 	Property Speed:Float()
 		
 		Return _speed
@@ -17,6 +20,7 @@ Class FlyBehaviour Extends Behaviour
 		_speed=speed
 	End
 	
+	[jsonify=1]
 	Property RotSpeed:Float()
 		
 		Return _rspeed
@@ -52,6 +56,7 @@ Class FlyBehaviour Extends Behaviour
 			entity.MoveZ( -_speed * 60 * elapsed )
 		Endif
 		
+#If __MOBILE_TARGET__
 		If Mouse.ButtonDown( MouseButton.Left )
 			If Mouse.X<view.Width/3
 				entity.RotateY( rspeed,True )
@@ -61,6 +66,7 @@ Class FlyBehaviour Extends Behaviour
 				entity.Move( New Vec3f( 0,0,_speed * 60 * elapsed ) )
 			Endif
 		Endif
+#endif
 		
 	End
 	

+ 45 - 0
modules/mojo3d/scene/components/movebehaviour.monkey2

@@ -0,0 +1,45 @@
+Namespace mojo3d
+
+Class MoveBehaviour Extends Behaviour
+	
+	Method New( entity:Entity )
+		
+		Super.New( entity )
+		
+		AddInstance()
+	End
+	
+	[jsonify=1]
+	Property Speed:Vec3f()
+		
+		Return _speed
+	
+	Setter( speed:Vec3f )
+		
+		_speed=speed
+	End
+	
+	[jsonify=1]
+	Property LocalSpace:Bool()
+		
+		Return _localSpace
+	
+	Setter( localSpace:Bool )
+		
+		_localSpace=localSpace
+	End
+	
+	Protected
+	
+	Method OnUpdate( elapsed:Float ) Override
+		
+		Entity.Move( _speed,_localSpace )
+	End
+	
+	Private
+	
+	Field _speed:Vec3f
+	
+	Field _localSpace:Bool
+	
+End

+ 0 - 0
modules/mojo3d/components/native/internaledges.cpp → modules/mojo3d/scene/components/native/internaledges.cpp


+ 0 - 0
modules/mojo3d/components/native/internaledges.h → modules/mojo3d/scene/components/native/internaledges.h


+ 0 - 0
modules/mojo3d/components/rigidbody.monkey2 → modules/mojo3d/scene/components/rigidbody.monkey2


+ 45 - 0
modules/mojo3d/scene/components/rotatebehaviour.monkey2

@@ -0,0 +1,45 @@
+Namespace mojo3d
+
+Class RotateBehaviour Extends Behaviour
+	
+	Method New( entity:Entity )
+		
+		Super.New( entity )
+		
+		AddInstance()
+	End
+	
+	[jsonify=1]
+	Property Speed:Vec3f()
+		
+		Return _speed
+	
+	Setter( speed:Vec3f )
+		
+		_speed=speed
+	End
+	
+	[jsonify=1]
+	Property LocalSpace:Bool()
+		
+		Return _localSpace
+	
+	Setter( localSpace:Bool )
+		
+		_localSpace=localSpace
+	End
+	
+	Protected
+	
+	Method OnUpdate( elapsed:Float ) Override
+		
+		Entity.Rotate( _speed,_localSpace )
+	End
+	
+	Private
+	
+	Field _speed:Vec3f
+	
+	Field _localSpace:Bool
+	
+End

+ 0 - 0
modules/mojo3d/render/effects/bloomeffect.monkey2 → modules/mojo3d/scene/effects/bloomeffect.monkey2


+ 0 - 0
modules/mojo3d/render/effects/fogeffect.monkey2 → modules/mojo3d/scene/effects/fogeffect.monkey2


+ 0 - 0
modules/mojo3d/render/effects/monochromeeffect.monkey2 → modules/mojo3d/scene/effects/monochromeeffect.monkey2


+ 29 - 4
modules/mojo3d/entities/camera.monkey2 → modules/mojo3d/scene/entities/camera.monkey2

@@ -8,24 +8,32 @@ Class Camera Extends Entity
 	#rem monkeydoc Creates a new camera.
 	#end	
 	Method New( parent:Entity=Null )
+		
 		Super.New( parent )
 		
+		Name="Camera"
 		Viewport=New Recti( 0,0,640,480 )
 		Near=.1
 		Far=100
 		FOV=90
 		
+		AddInstance()
+		
 		Visible=True
 	End
-	
+
 	Method New( view:View,parent:Entity=Null )
-		Super.New( parent )
 		
+		Super.New( parent )
+
+		Name="Camera"		
 		View=view
 		Near=.1
 		Far=100
 		FOV=90
 		
+		AddInstance()
+		
 		Visible=True
 	End
 	
@@ -33,7 +41,7 @@ Class Camera Extends Entity
 	#end
 	Method Copy:Camera( parent:Entity=Null ) Override
 		
-		Local copy:=New Camera( Self,parent )
+		Local copy:=OnCopy( parent )
 		
 		CopyTo( copy )
 		
@@ -87,6 +95,7 @@ Class Camera Extends Entity
 	Defaults to 90.0.
 	
 	#end
+	[jsonify=1]
 	Property FOV:Float()
 	
 		Return _fov
@@ -105,6 +114,7 @@ Class Camera Extends Entity
 	The ratio of Far/Near clip planes should be kept as low as possible to reduce numerical precision errors.
 	
 	#end
+	[jsonify=1]
 	Property Near:Float()
 	
 		Return _near
@@ -123,6 +133,7 @@ Class Camera Extends Entity
 	The ratio of Far/Near clip planes should be kept as low as possible to reduce numerical precision errors.
 	
 	#end
+	[jsonify=1]
 	Property Far:Float()
 	
 		Return _far
@@ -158,7 +169,11 @@ Class Camera Extends Entity
 	#end
 	Method Render( canvas:Canvas )
 		
-		If _view SetViewport( _view.Rect )
+		If _view
+			SetViewport( _view.Rect )
+		Else
+			SetViewport( New Recti( 0,0,canvas.RenderBounds.Size ) )
+		Endif
 		
 		Local target:=canvas.GraphicsDevice.RenderTarget
 		
@@ -167,6 +182,8 @@ Class Camera Extends Entity
 '		Local viewport:=canvas.RenderMatrix * Viewport
 		Local viewport:=canvas.RenderBounds
 		
+'		Print "camera viewport="+viewport
+		
 		canvas.Flush()
 		
 		Local renderer:=Renderer.GetCurrent()
@@ -238,12 +255,20 @@ Class Camera Extends Entity
 	Protected
 
 	Method New( camera:Camera,parent:Entity )
+		
 		Super.New( camera,parent )
 		
 		Viewport=camera.Viewport
 		Near=camera.Near
 		Far=camera.Far
 		FOV=camera.FOV
+		
+		AddInstance( camera )
+	End
+	
+	Method OnCopy:Camera( parent:Entity ) Override
+		
+		Return New Camera( Self,parent )
 	End
 	
 	Method OnShow() Override

+ 26 - 30
modules/mojo3d/entities/light.monkey2 → modules/mojo3d/scene/entities/light.monkey2

@@ -24,12 +24,15 @@ Class Light Extends Entity
 	#rem monkeydoc Creates a new light.
 	#end
 	Method New( parent:Entity=Null )
+		
 		Super.New( parent )
 		
+		Name="Light"
 		Type=LightType.Directional
-		Color=Color.White
-		Range=10
 		CastsShadow=False
+		Range=10
+		
+		AddInstance()
 		
 		Visible=True
 	End
@@ -38,24 +41,13 @@ Class Light Extends Entity
 	#end
 	Method Copy:Light( parent:Entity=Null ) Override
 		
-		Local copy:=New Light( Self,parent )
+		Local copy:=OnCopy( parent )
 		
 		CopyTo( copy )
 		
 		Return copy
 	End
 	
-	#rem monkeydoc Light shadows enabled flag.
-	#end
-	Property CastsShadow:Bool()
-		
-		Return _castsShadow
-		
-	Setter( shadows:Bool )
-		
-		_castsShadow=shadows
-	End
-	
 	#rem monkeydoc The light type.
 	#end
 	Property Type:LightType()
@@ -67,21 +59,21 @@ Class Light Extends Entity
 		_type=type
 	End
 	
-	#rem
-	#rem monkeydoc The light color.
+	#rem monkeydoc Light shadows enabled flag.
 	#end
-	Property Color:Color()
-	
-		Return _color
-	
-	Setter( color:Color )
-	
-		_color=color
+	[jsonify=1]
+	Property CastsShadow:Bool()
+		
+		Return _castsShadow
+		
+	Setter( shadows:Bool )
+		
+		_castsShadow=shadows
 	End
-	#end
 	
 	#rem monkeydoc The light range.
 	#end
+	[jsonify=1]
 	Property Range:Float()
 	
 		Return _range
@@ -93,25 +85,29 @@ Class Light Extends Entity
 	
 	Protected
 
-	#rem monkeydoc @hidden
-	#end	
 	Method New( light:Light,parent:Entity )
+		
 		Super.New( light,parent )
 		
 		Type=light.Type
 		Color=light.Color
 		Range=light.Range
+		
+		AddInstance( light )
+	End
+	
+	Method OnCopy:Light( parent:Entity ) Override
+		
+		Return New Light( Self,parent )
 	End
 	
-	#rem monkeydoc @hidden
-	#end	
 	Method OnShow() Override
+		
 		Scene.Lights.Add( Self )
 	End
 	
-	#rem monkeydoc @hidden
-	#end	
 	Method OnHide() Override
+		
 		Scene.Lights.Remove( Self )
 	End
 

+ 45 - 20
modules/mojo3d/entities/model.monkey2 → modules/mojo3d/scene/entities/model.monkey2

@@ -15,16 +15,27 @@ Class Model Extends Renderable
 	#rem monkeydoc Creates a new model.
 	#end
 	Method New( parent:Entity=Null )
+		
 		Super.New( parent )
 		
+		Name="Model"
+		
+		AddInstance()
+		
 		Visible=True
 	End
 	
 	Method New( mesh:Mesh,material:Material,parent:Entity=Null )
-		super.New( parent )
 		
-		_mesh=mesh
-		_materials=New Material[]( material )
+		Super.New( parent )
+		
+		Name="Model"
+		
+		AddInstance( New Variant[]( mesh,material,parent ) )
+		
+		Mesh=mesh
+		Materials=New Material[]( material )
+		Material=material
 		
 		Visible=True
 	End
@@ -33,7 +44,7 @@ Class Model Extends Renderable
 	#end	
 	Method Copy:Model( parent:Entity=Null ) Override
 		
-		Local copy:=New Model( Self,parent )
+		Local copy:=OnCopy( parent )
 		
 		CopyTo( copy )
 		
@@ -48,7 +59,8 @@ Class Model Extends Renderable
 	End
 
 	#rem monkeydoc The mesh rendered by the model.
-	#end	
+	#end
+	[jsonify=1]
 	Property Mesh:Mesh()
 		
 		Return _mesh
@@ -58,8 +70,20 @@ Class Model Extends Renderable
 		_mesh=mesh
 	End
 	
+	#rem monkeydoc The materials to use for rendering.
+	#end
+	Property Materials:Material[]()
+		
+		Return _materials
+		
+	Setter( materials:Material[] )
+		
+		_materials=materials
+	End
+	
 	#rem monkeydoc The default material to use for rendering.
 	#end
+	[jsonify=1]
 	Property Material:Material()
 		
 		Return _material
@@ -69,17 +93,6 @@ Class Model Extends Renderable
 		_material=material
 	End
 
-	#rem monkeydoc The materials to use for rendering.
-	#end	
-	Property Materials:Material[]()
-		
-		Return _materials
-		
-	Setter( materials:Material[] )
-		
-		_materials=materials
-	End
-	
 	#rem monkeydoc @hidden
 	#end
 	Property Bones:Bone[]()
@@ -113,7 +126,11 @@ Class Model Extends Renderable
 		
 			Local model:=loader.LoadModel( path )
 			
-			If model Return model
+			If Not model Continue
+			
+			If model.Scene.Editing model.Scene.Jsonifier.AddInstance( model,"mojo3d.Model.Load",New Variant[]( path )  )
+				
+			Return model
 		Next
 		
 		Return Null
@@ -150,13 +167,21 @@ Class Model Extends Renderable
 	Protected
 
 	Method New( model:Model,parent:Entity )
+		
 		Super.New( model,parent )
 		
-		_mesh=model._mesh
+		Mesh=model.Mesh
 		
-		_material=model._material
+		Materials=model.Materials
+		
+		Material=model.Material
+		
+		AddInstance( model )
+	End
+	
+	Method OnCopy:Model( parent:Entity ) Override
 		
-		_materials=model._materials.Slice( 0 )
+		Return New Model( Self,parent )
 	End
 	
 	Internal

+ 0 - 0
modules/mojo3d/entities/particlebuffer.monkey2 → modules/mojo3d/scene/entities/particlebuffer.monkey2


+ 0 - 0
modules/mojo3d/entities/particlematerial.monkey2 → modules/mojo3d/scene/entities/particlematerial.monkey2


+ 0 - 0
modules/mojo3d/entities/particlesystem.monkey2 → modules/mojo3d/scene/entities/particlesystem.monkey2


+ 2 - 2
modules/mojo3d/entities/renderable.monkey2 → modules/mojo3d/scene/entities/renderable.monkey2

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

+ 10 - 0
modules/mojo3d/entities/sprite.monkey2 → modules/mojo3d/scene/entities/sprite.monkey2

@@ -21,7 +21,12 @@ Class Sprite Extends Renderable
 	#rem monkeydoc Creates a new sprite.
 	#end
 	Method New( parent:Entity=Null )
+		
 		Super.New( parent )
+		
+		Name="Sprite"
+		
+		AddInstance()
 	End
 
 	Method New( material:Material,parent:Entity=Null )
@@ -29,6 +34,11 @@ Class Sprite Extends Renderable
 		
 		_material=material
 		
+		Name="Sprite"
+		Material=material
+		
+		AddInstance( New Variant[]( material,parent ) )
+		
 		Visible=True
 	End
 

+ 44 - 3
modules/mojo3d/scene/entity.monkey2

@@ -61,11 +61,11 @@ Class Entity Extends DynamicObject
 	#end
 	Method Copy:Entity( parent:Entity=Null ) Virtual
 		
-		Local copy:=New Entity( Self,parent )
+		Local copy:=OnCopy( parent )
 		
 		CopyTo( copy )
 		
-		return copy
+		Return copy
 	End
 	
 	#rem monkeydoc Sequence id.
@@ -80,6 +80,7 @@ Class Entity Extends DynamicObject
 	
 	#rem monkeydoc Entity name.
 	#end
+	[jsonify=1]
 	Property Name:String()
 		
 		Return _name
@@ -160,6 +161,7 @@ Class Entity Extends DynamicObject
 
 	#rem monkeydoc Visibility flag.
 	#end
+	[jsonify=1]
 	Property Visible:Bool()
 		
 		Return _visible
@@ -189,6 +191,7 @@ Class Entity Extends DynamicObject
 	
 	#rem monkeydoc Master color.
 	#end
+	[jsonify=1]
 	Property Color:Color()
 		
 		Return _color
@@ -200,6 +203,7 @@ Class Entity Extends DynamicObject
 	
 	#rem monkeydoc Master alpha.
 	#end
+	[jsonify=1]
 	Property Alpha:Float()
 		
 		Return _alpha
@@ -297,6 +301,7 @@ Class Entity Extends DynamicObject
 	The local matrix combines the local position, orientation and scale of the entity into a single affine 4x4 matrix.
 	
 	#end
+	[jsonify=1]
 	Property LocalMatrix:AffineMat4f()
 		
 		If _dirty & Dirty.M
@@ -361,6 +366,20 @@ Class Entity Extends DynamicObject
 		Invalidate()
 	End
 
+	#rem monkeydoc Finds an entity with the given name.
+	#end
+	Method Find:Entity( name:String )
+		
+		If _name=name Return Self
+		
+		For Local child:=Eachin _children
+			Local found:=child.Find( name )
+			If found Return found
+		Next
+		
+		Return Null
+	End
+	
 	#rem monkeydoc Destroys the entity and all of its children.
 	#end
 	Method Destroy()
@@ -442,8 +461,10 @@ Class Entity Extends DynamicObject
 	#rem monkeydoc Copy constructor
 	#end
 	Method New( entity:Entity,parent:Entity )
+		
 		Self.New( parent )
 		
+		_name="Copy of "+entity._name
 		_t=entity._t
 		_r=entity._r
 		_s=entity._s
@@ -451,6 +472,11 @@ Class Entity Extends DynamicObject
 		Invalidate()
 	End
 	
+	Method OnCopy:Entity( parent:Entity ) Virtual
+		
+		Return New Entity( Self,parent )
+	End
+		
 	#rem monkeydoc Invoked when entity transitions from hidden->visible.
 	#end
 	Method OnShow() Virtual
@@ -477,7 +503,7 @@ Class Entity Extends DynamicObject
 		_lastCopy=copy
 		
 		For Local child:=Eachin _children
-			child.Copy( copy )
+			child.CopyTo( child.OnCopy( copy ) )
 		Next
 		
 		'should really be different pass...ie: ALL entities should be copied before ANY components?
@@ -494,6 +520,21 @@ Class Entity Extends DynamicObject
 	
 	Internal
 	
+	Method AddInstance()
+		
+		If _scene.Editing _scene.Jsonifier.AddInstance( Self,New Variant[]( _parent ) )
+	End
+	
+	Method AddInstance( entity:Entity )
+		
+		If _scene.Editing _scene.Jsonifier.AddInstance( Self,New Variant[]( entity,_parent ) )
+	End
+	
+	Method AddInstance( args:Variant[] )
+		
+		If _scene.Editing _scene.Jsonifier.AddInstance( Self,args )
+	End
+	
 	Method AddComponent( c:Component )
 		
 		Local type:=c.Type

+ 112 - 0
modules/mojo3d/scene/jsonifier/comparejson.monkey2

@@ -0,0 +1,112 @@
+Namespace mojo3d.jsonifier
+
+Function CompareJson:Int( x:JsonObject,y:JsonValue )
+	If y=JsonValue.NullValue Return 1
+	Local r:=Cast<JsonObject>( y )
+	If Not r Return 1
+	Local xit:=x.All(),rit:=r.All()
+	While Not xit.AtEnd And Not rit.AtEnd
+		Local cmp:=xit.Current.Key<=>rit.Current.Key
+		If cmp Return cmp
+		cmp=CompareJson( xit.Current.Value,rit.Current.Value )
+		If cmp Return cmp
+		xit.Bump()
+		rit.Bump()
+	Wend
+	Return x.Count()<=>r.Count()
+End
+
+Function CompareJson:Int( x:JsonArray,y:JsonValue )
+	If y=JsonValue.NullValue Or y.IsBool Or y.IsNumber Or y.IsString Return 1
+	Local r:=Cast<JsonArray>( y )
+	If Not r Return -1
+	For Local i:=0 Until Min( x.Length,r.Length )
+		Local cmp:=CompareJson( x[i],r[i] )
+		If cmp Return cmp
+	Next
+	Return x.Length<=>r.Length
+End
+
+Function CompareJson:Int( x:JsonString,y:JsonValue )
+	If y=JsonValue.NullValue Or y.IsBool Or y.IsNumber Return 1
+	Local r:=Cast<JsonString>( y )
+	Return r ? x.Data<=>r.Data Else -1
+End
+
+Function CompareJson:Int( x:JsonNumber,y:JsonValue )
+	If y=JsonValue.NullValue Or y.IsBool Return 1
+	Local r:=Cast<JsonNumber>( y )
+	Return r ? x.Data<=>r.Data Else -1
+End
+
+Function CompareJson:Int( x:JsonBool,y:JsonValue )
+	If y=JsonValue.NullValue Return 1
+	Local r:=Cast<JsonBool>( y )
+	Return r ? x.Data<=>r.Data Else -1
+End
+
+Function CompareJson:Int( x:JsonValue,y:JsonValue )
+	If x=JsonValue.NullValue Return y=JsonValue.NullValue ? 0 Else -1
+	Local jbool:=Cast<JsonBool>( x )
+	If jbool Return CompareJson( jbool,y )
+	Local jnumber:=Cast<JsonNumber>( x )
+	If jnumber Return CompareJson( jnumber,y )
+	Local jstring:=Cast<JsonString>( x )
+	If jstring Return CompareJson( jstring,y )
+	local jarray:=Cast<JsonArray>( x )
+	If jarray Return CompareJson( jarray,y )
+	Local jobj:=Cast<JsonObject>( x )
+	If jobj Return CompareJson( jobj,y )
+	RuntimeError( "TODO" )
+	Return 0
+End
+
+#rem
+Function RndJson:JsonValue( range:Int=6 )
+	
+	Select Int( Rnd( range ) )
+	Case 0 
+		Return JsonValue.NullValue
+	Case 1 
+		Return Rnd()>.5 ? JsonBool.TrueValue Else JsonBool.FalseValue
+	Case 2 
+		Return New JsonNumber( Rnd(10000) )
+	Case 3 
+		Return New JsonString( Rnd(10000) )
+	Case 4
+		Local data:=New Stack<JsonValue>( Rnd( 10,20 ) )
+		For Local i:=0 Until data.Length
+			data[i]=RndJson( 4 )
+		Next
+		Return New JsonArray( data )
+	Case 5
+		Local data:=New StringMap<JsonValue>
+		For Local i:=0 Until Rnd( 10,20 )
+			data[ Rnd(100) ]=RndJson( 4 )
+		Next
+		Return New JsonObject( data )
+	End
+	Return Null
+End
+
+Function Main()
+	
+	Print "Start..."
+	
+	For Local i:=1 To 100000
+		
+		Local x:=RndJson()
+		Local y:=RndJson()
+		
+		Local cmp1:=CompareJson( x,y )
+		Local cmp2:=CompareJson( y,x )
+		
+		If cmp1<0 Assert( cmp2>=0 )
+		If cmp1=0 Assert( cmp2 =0 )
+		If cmp1>0 Assert( cmp2<=0 )
+	End
+	
+	Print "Success!"
+		
+End
+#end

+ 109 - 0
modules/mojo3d/scene/jsonifier/invocation.monkey2

@@ -0,0 +1,109 @@
+Namespace mojo3d.jsonifier
+
+Class Invocation
+	
+	Method New( scope:TypeInfo,decl:DeclInfo,inst:Variant,args:Variant[] )
+		_scope=scope
+		_decl=decl
+		_inst=inst
+		_args=args
+	End
+	
+	Method New( scope:TypeInfo,decl:String,inst:Variant,args:Variant[] )
+		Init( scope,decl,inst,args )
+	End
+	
+	Method New( name:String,inst:Variant,args:Variant[] )
+		Local i:=name.FindLast( "." )
+		Init( TypeInfo.GetType( name.Slice( 0,i ) ),name.Slice( i+1 ),inst,args )
+	End
+	
+	Property Scope:TypeInfo()
+		
+		Return _scope
+	End
+	
+	Property Decl:DeclInfo()
+		
+		Return _decl
+	End
+	
+	Property Inst:Variant()
+		
+		Return _inst
+	End
+	
+	Property Args:Variant[]()
+		
+		Return _args
+	End
+	
+	Method Execute:Variant()
+		
+		Return _decl.Invoke( _inst,_args )
+	End
+	
+	Private
+	
+	Field _scope:TypeInfo
+	Field _decl:DeclInfo
+	Field _inst:Variant
+	Field _args:Variant[]
+	
+	Function FindDecl:DeclInfo( scope:TypeInfo,name:String,args:Variant[] )
+	
+		For Local decl:=Eachin scope.GetDecls( name )
+			
+			Local ftype:=decl.Type
+			If ftype.ParamTypes.Length<>args.Length Continue
+			
+			Local match:=True
+			For Local i:=0 Until args.Length
+				
+				If args[i]
+					
+					If args[i].Type.ExtendsType( ftype.ParamTypes[i] ) Continue
+					
+					If args[i].Type.Kind="Class" And ftype.ParamTypes[i].Kind="Class"
+						
+						Local obj:=Cast<Object>( args[i] )
+						
+						If Not obj Or obj.DynamicType.ExtendsType( ftype.ParamTypes[i] ) Continue
+						
+					Endif
+					
+				Else
+					If ftype.ParamTypes[i].Kind="Class" Continue
+				Endif
+				match=False
+				Exit
+			Next
+			
+			If match Return decl
+			
+		Next
+		
+		Return Null
+	End
+
+	Method Init( scope:TypeInfo,name:String,inst:Variant,args:Variant[] )
+		
+		_scope=scope
+		_inst=inst
+		_args=args
+		
+		_decl=FindDecl( _scope,name,args )
+		
+		If Not _decl
+			For Local type:=Eachin GetTypeExtensions( _scope )
+				_decl=FindDecl( type,name,args )
+				If Not _decl Continue
+				_scope=type
+				Exit
+			Next
+			Assert( _decl )
+		Endif
+	End
+	
+End
+

+ 312 - 0
modules/mojo3d/scene/jsonifier/jsonifier.monkey2

@@ -0,0 +1,312 @@
+Namespace mojo3d.jsonifier
+
+Class Jsonifier
+	
+	Method AddInstance( obj:Object,ctor:Invocation )
+		
+		Assert( Not _instsByObj.Contains( obj ) )
+		
+		Local inst:=New Instance
+		inst.obj=obj
+		inst.id="@"+String(_insts.Length)
+		inst.ctor=ctor
+		inst.initialState=JsonifyState( obj )
+		
+		_instsByObj[inst.obj]=inst
+		_instsById[inst.id]=inst
+
+		_insts.Add( inst )
+	End
+
+	'ctor via ctor
+	Method AddInstance( obj:Object,args:Variant[] )
+		
+		AddInstance( obj,New Invocation( obj.DynamicType,"New",Null,args ) )
+	end
+
+	'ctor via method call
+	Method AddInstance( obj:Object,decl:String,inst:Variant,args:Variant[] )
+		
+		AddInstance( obj,New Invocation( decl,inst,args ) )
+	End
+
+	'ctor via function call
+	Method AddInstance( obj:Object,decl:String,args:Variant[] )
+		
+		AddInstance( obj,New Invocation( decl,Null,args ) )
+	End
+
+	#rem
+	'function/method call	
+	Method AddInstance( obj:Object,name:String,args:Variant[] )
+		
+		AddInstance( obj,New Invocation( name,args ) )
+	End
+	
+	#end
+	
+	Method JsonifyInstances:JsonObject()
+		
+		Local jobj:=New JsonObject
+		
+		Local jinsts:=New JsonArray( _insts.Length )
+		
+		For Local i:=0 Until jinsts.Length
+			
+			Local inst:=_insts[i]
+			Local jobj:=New JsonObject
+			
+			jobj["id"]=New JsonString( inst.id )
+			jobj["type"]=New JsonString( inst.obj.DynamicType.Name )
+			jobj["ctor"]=Jsonify( inst.ctor )
+			
+			Local state:=JsonifyState( inst.obj ),dstate:=New JsonObject
+			
+			For Local it:=Eachin state.All()
+				
+				Local x:=it.Value
+				Local y:=inst.initialState.GetValue( it.Key )
+				
+				If CompareJson( x,y )<>0 dstate[it.Key]=x
+			Next
+			
+			jobj["state"]=dstate
+			
+			jinsts[i]=jobj
+		Next
+
+		jobj["instances"]=jinsts
+		
+		Return jobj
+	End
+	
+	Method DejsonifyInstances( jobj:JsonObject )
+		
+		Local jinsts:=jobj.GetArray( "instances" )
+		
+		For Local i:=0 Until jinsts.Length
+			
+			Local obj:Object
+						
+			If i<_insts.Length
+				
+				obj=_insts[i].obj
+				
+			Else
+				Local jobj:=jinsts.GetObject( i )
+				
+				Local ctor:=Cast<Invocation>( Dejsonify( jobj["ctor"],Typeof<Invocation> ) )
+			
+				obj=Cast<Object>( ctor.Execute() )
+			Endif
+			
+			_dejsonified.Add( obj )
+		Next
+		
+		For Local i:=0 Until _dejsonified.Length
+			
+			Local jobj:=jinsts.GetObject( i )
+			
+			Local obj:=_dejsonified[i]
+			
+			DejsonifyState( obj,jobj.GetObject( "state" ),obj.DynamicType )
+		Next
+	End
+	
+	Method Jsonify:JsonValue( value:Variant )
+		
+		If Not value Return JsonValue.NullValue
+		
+		Local type:=value.Type
+		Assert( type )
+		
+		'handle primitive types
+		Select type
+		Case Typeof<Bool>
+			Return New JsonBool( Cast<Bool>( value ) )
+		Case Typeof<Int>
+			Return New JsonNumber( Cast<Int>( value ) )
+		Case Typeof<Float>
+			Return New JsonNumber( Cast<Float>( value ) )
+		Case Typeof<String>
+			Return New JsonString( Cast<String>( value ) )
+		Case Typeof<Bool[]>
+			Return JsonifyArray( Cast<Bool[]>( value ) )
+		Case Typeof<Int[]>
+			Return JsonifyArray( Cast<Int[]>( value ) )
+		Case Typeof<Float[]>
+			Return JsonifyArray( Cast<Float[]>( value ) )
+		Case Typeof<String[]>
+			Return JsonifyArray( Cast<String[]>( value ) )
+		End
+		
+		'handle enums+references
+		Select type.Kind
+		Case "Class"
+			Local obj:=Cast<Object>( value )
+			If Not obj Return JsonValue.NullValue
+			Local inst:=_instsByObj[obj]
+			If inst Return New JsonString( inst.id )
+		Case "Enum"
+			Return New JsonNumber( value.EnumValue )
+		End
+		
+		'try custom jsonifiers
+		For Local jext:=Eachin JsonifierExt.All
+			Local jvalue:=jext.Jsonify( value,Self )
+			If jvalue Return jvalue
+		Next
+		
+		RuntimeError( "TODO: No jsonifier found for type '"+type+"'" )
+		Return Null
+	End
+	
+	Method Dejsonify:Variant( jvalue:JsonValue,type:TypeInfo )
+		
+		'handle primitive types
+		Select type
+		Case Typeof<Bool>
+			Return jvalue.ToBool()
+		Case Typeof<Int>
+			Return Int( jvalue.ToNumber() )
+		Case Typeof<Float>
+			Return Float( jvalue.ToNumber() )
+		Case Typeof<String>
+			Return jvalue.ToString()
+		Case Typeof<Bool[]>
+			Return DejsonifyArray<Bool>( jvalue )
+		Case Typeof<Int[]>
+			Return DejsonifyArray<Int>( jvalue )
+		Case Typeof<Float[]>
+			Return DejsonifyArray<Float>( jvalue )
+		Case Typeof<String[]>
+			Return DejsonifyArray<String>( jvalue )
+		End
+		
+		'handle references
+		Select type.Kind
+		Case "Class"
+			If jvalue.IsNull
+				Return type.NullValue
+			Elseif jvalue.IsString
+				Local id:=Int( jvalue.ToString().Slice( 1 ) )
+				Assert( id>=0 And id<_dejsonified.Length,"Dejsonify error" )
+				Local obj:=_dejsonified[id]
+				Return obj
+			Endif
+		Case "Enum"
+			Return type.MakeEnum( jvalue.ToNumber() )
+		End
+		
+		'try custom jsonifiers
+		For Local jext:=Eachin JsonifierExt.All
+			Local value:=jext.Dejsonify( jvalue,type,Self )
+			If value Return value
+		Next
+		
+		RuntimeError( "No dejsonifier found for type '"+type+"'" )
+		Return Null
+	End
+	
+	Method JsonifyArray<C>:JsonArray( values:C[] )
+		
+		Local jvalues:=New JsonArray( values.Length )
+		
+		For Local i:=0 Until jvalues.Length
+			jvalues[i]=Jsonify( values[i] )
+		Next
+		
+		Return jvalues
+	End
+	
+	Method DejsonifyArray<C>:C[]( jvalue:JsonValue )
+		
+		Local jvalues:=jvalue.ToArray()
+		Local values:=New C[jvalues.Length]
+		
+		For Local i:=0 Until values.Length
+			values[i]=Cast<C>( Dejsonify( jvalues[i],Typeof<C> ) )
+		Next
+		
+		Return values
+	End
+	
+	Private
+	
+	Class Instance
+		Field obj:Object
+		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<Object>
+	
+	Method JsonifyState:JsonObject( obj:Object )
+		
+		Local jobj:=New JsonObject
+		
+		JsonifyState( obj,jobj,obj.DynamicType )
+		
+		Return jobj
+	End
+	
+	Method JsonifyState( obj:Object,jobj:JsonObject,type:TypeInfo )
+		
+		If type.Kind<>"Class" Return
+		
+		If type.SuperType JsonifyState( obj,jobj,type.SuperType )
+		
+		For Local d:=Eachin type.GetDecls()
+			
+			If d.Kind<>"Property" Continue
+			'Note: Add DeclInfo.Access property so we can do public fields only?
+			If Not d.Gettable Or Not d.Settable Continue
+			
+			If Not Int( d.GetMetaValue( "jsonify" ) ) Continue
+			
+			jobj.SetValue( d.Name,Jsonify( d.Get( obj ) ) )
+		Next
+		
+	End
+	
+	Method DejsonifyState( obj:Object,jobj:JsonObject,type:TypeInfo  )
+		
+		If type.Kind<>"Class" Return
+		
+		If type.SuperType DejsonifyState( obj,jobj,type.SuperType )
+
+		For Local d:=Eachin type.GetDecls()
+			
+			If d.Kind<>"Property" Continue
+			'Note: Add DeclInfo.Access property so we can do public fields only?
+			If Not d.Gettable Or Not d.Settable Or Not jobj.Contains( d.Name ) Continue
+			
+			If Not Int( d.GetMetaValue( "jsonify" ) ) Continue
+			
+			d.Set( obj,Dejsonify( jobj.GetValue( d.Name ),d.Type ) )
+		Next
+	
+	End
+	
+End
+
+
+Class JsonifierExt
+	
+	Const All:=New Stack<JsonifierExt>
+	
+	Method New()
+		All.Add( Self )
+	End
+	
+	Method Jsonify:JsonValue( value:Variant,jsonifier:Jsonifier ) Abstract
+	
+	Method Dejsonify:Variant( jvalue:JsonValue,type:TypeInfo,jsonifier:Jsonifier ) Abstract
+End
+

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

@@ -0,0 +1,164 @@
+Namespace mojo3d.jsonifier
+
+Class StdJsonifierExt Extends JsonifierExt
+	
+	Const Instance:=New StdJsonifierExt
+
+	Method Jsonify:JsonValue( value:Variant,jsonifier:Jsonifier ) Override
+		
+		Select value.Type
+		Case Typeof<Vec2i>
+			Local v:=Cast<Vec2i>( value )
+			Return jsonifier.JsonifyArray( New Int[]( v.x,v.y ) )
+		Case Typeof<Vec2f>
+			Local v:=Cast<Vec2f>( value )
+			Return jsonifier.JsonifyArray( New Float[]( v.x,v.y ) )
+		Case Typeof<Recti>
+			Local v:=Cast<Recti>( value )
+			Return jsonifier.JsonifyArray( new Int[]( v.min.x,v.min.y,v.max.x,v.max.y ) )
+		Case Typeof<Rectf>
+			Local v:=Cast<Rectf>( value )
+			Return jsonifier.JsonifyArray( New Float[]( v.min.x,v.min.y,v.max.x,v.max.y ) )
+		Case Typeof<Vec3f>
+			Local v:=Cast<Vec3f>( value )
+			Return jsonifier.JsonifyArray( New Float[]( v.x,v.y,v.z ) )
+		Case Typeof<Boxf>
+			Local v:=Cast<Boxf>( value )
+			Return jsonifier.JsonifyArray( New Float[]( v.min.x,v.min.y,v.min.z,v.max.x,v.max.y,v.max.z ) )
+		Case Typeof<AffineMat4f>
+			Local v:=Cast<AffineMat4f>( value )
+			Return jsonifier.JsonifyArray( New Float[]( v.m.i.x,v.m.i.y,v.m.i.z, v.m.j.x,v.m.j.y,v.m.j.z, v.m.k.x,v.m.k.y,v.m.k.z, v.t.x,v.t.y,v.t.z ) )
+		Case Typeof<Color>
+			Local v:=Cast<Color>( value )
+			Return jsonifier.JsonifyArray( New Float[]( v.r,v.g,v.b,v.a ) )
+		End
+		
+		Return Null
+	End
+
+	Method Dejsonify:Variant( jvalue:JsonValue,type:TypeInfo,jsonifier:Jsonifier ) Override
+		
+		Select type
+		Case Typeof<Vec2i>
+			Local v:=jsonifier.DejsonifyArray<Int>( jvalue )
+			Return New Vec2i( v[0],v[1] )
+		Case Typeof<Vec2f>
+			Local v:=jsonifier.DejsonifyArray<Float>( jvalue )
+			Return New Vec2f( v[0],v[1] )
+		Case Typeof<Recti>
+			Local v:=jsonifier.DejsonifyArray<Float>( jvalue )
+			Return New Recti( v[0],v[1],v[2],v[3] )
+		Case Typeof<Rectf>
+			Local v:=jsonifier.DejsonifyArray<Float>( jvalue )
+			Return New Rectf( v[0],v[1],v[2],v[3] )
+		Case Typeof<Vec3f>
+			Local v:=jsonifier.DejsonifyArray<Float>( jvalue )
+			Return New Vec3f( v[0],v[1],v[2] )
+		Case Typeof<Boxf>
+			Local v:=jsonifier.DejsonifyArray<Float>( jvalue )
+			Return New Boxf( v[0],v[1],v[2],v[3],v[4],v[5] )
+		Case Typeof<AffineMat4f>
+			Local v:=jsonifier.DejsonifyArray<Float>( jvalue )
+			Return New AffineMat4f( v[0],v[1],v[2], v[3],v[4],v[5], v[6],v[7],v[8], v[9],v[10],v[11] )
+		Case Typeof<Color>
+			Local v:=jsonifier.DejsonifyArray<Float>( jvalue )
+			Return New Color( v[0],v[1],v[2],v[3] )
+		End
+		
+		Return Null
+	End
+	
+End
+
+Class InvocationJsonifierExt Extends JsonifierExt
+	
+	Const Instance:=New InvocationJsonifierExt
+	
+	Method Jsonify:JsonValue( value:Variant,jsonifier:Jsonifier ) Override
+		
+		Select value.Type
+		Case Typeof<Invocation>
+			
+			Local v:=Cast<Invocation>( value )
+			
+			Local jobj:=New JsonObject
+			
+'			jobj.SetString( "scope",v.Scope.Name )
+'			jobj.SetString( "decl",v.Decl.Name )
+
+			Local decl:=v.Scope.Name
+			If decl.EndsWith( " Extension" ) decl=decl.Slice( 0,-10 )
+			decl+="."+v.Decl.Name
+			
+			jobj.SetString( "decl",decl )
+			jobj.SetString( "type",v.Decl.Type )
+			If v.Inst jobj.SetValue( "inst",jsonifier.Jsonify( v.Inst ) )
+			jobj.SetValue( "args",jsonifier.JsonifyArray( v.Args ) )
+			
+			Return jobj
+		End
+		
+		Return Null
+	End
+	
+	Method Dejsonify:Variant( jvalue:JsonValue,type:TypeInfo,jsonifier:Jsonifier ) Override
+		
+		Select type
+		Case Typeof<Invocation>
+			
+			Local jobj:=Cast<JsonObject>( jvalue )
+			
+'			Local scope:=TypeInfo.GetType( jobj.GetString( "scope" ) )
+'			Local dname:=jobj.GetString( "decl" )
+
+			Local dname:=jobj.GetString( "decl" )
+			Local dtype:=jobj.GetString( "type" )
+			Local jinst:=jobj.Contains( "inst" ) ? jobj.GetValue( "inst" ) Else JsonValue.NullValue
+			Local jargs:=jobj.GetArray( "args" )
+
+			Local i:=dname.FindLast( "." )
+			Local dscope:=dname.Slice( 0,i )
+			dname=dname.Slice( i+1 )
+			
+			Local scope:=TypeInfo.GetType( dscope )
+			Local decl:DeclInfo
+			
+			For Local tdecl:=Eachin scope.GetDecls( dname )
+				If String(tdecl.Type)<>dtype Continue
+				decl=tdecl
+				Exit
+			Next
+			
+			If Not decl
+				For Local type:=Eachin reflection.GetTypeExtensions( scope )
+					For Local tdecl:=Eachin type.GetDecls( dname )
+						If String(tdecl.Type)<>dtype Continue
+						scope=type
+						decl=tdecl
+						Exit
+					Next
+					If decl Exit
+				Next
+				Assert( decl )
+			Endif
+			
+			Local type:=dname="New" ? scope Else decl.Type.ReturnType
+			
+			Local inst:=jsonifier.Dejsonify( jinst,type )'Typeof<Object> )
+			
+			Local args:=New Variant[jargs.Length]
+			For Local i:=0 Until args.Length
+				args[i]=jsonifier.Dejsonify( jargs[i],decl.Type.ParamTypes[i] )
+			Next
+			
+			Local ctor:=New Invocation( scope,decl,inst,args )
+			
+			Return ctor
+		End
+		
+		Return Null
+	End
+	
+End
+
+

+ 425 - 0
modules/mojo3d/scene/jsonifier/test.monkey2

@@ -0,0 +1,425 @@
+Namespace test
+
+#Reflect test
+
+#Import "<std>"
+
+#Import "jsonifier"
+#Import "invocation"
+#Import "jsonifierexts"
+#Import "comparejson"
+
+Using std..
+Using jsonifier..
+
+Global editing:=True
+Global jsonifier:=New Jsonifier
+
+Class Component
+	
+	'simple contructor
+	Method New( entity:Entity )
+		
+		_entity=entity
+		
+		_entity.AddComponent( Self )
+	End
+
+	'copy contructor
+	Method New( component:Component,entity:Entity )
+		
+		Self.New( entity )
+	End
+	
+	Property Entity:Entity()
+		
+		Return _entity
+	End
+	
+	Protected
+	
+	Method OnCopy:Component( entity:Entity ) Abstract
+	
+	Method SaveInitialState()
+		
+		If editing jsonifier.AddInstance( Self,New Variant[]( _entity ) )
+	End
+	
+	Method SaveInitialState( component:Component )
+		
+		If editing jsonifier.AddInstance( Self,New Variant[]( component,_entity ) )
+	End
+	
+	Private
+	
+	Field _entity:Entity
+	
+End
+
+Class Behaviour Extends Component
+	
+	'simple contructor
+	Method New( entity:Entity )
+		
+		Super.New( entity )
+		
+		Color=graphics.Color.White
+			
+		SaveInitialState()
+	End
+	
+	'copy constructor
+	Method New( behaviour:Behaviour,entity:Entity )
+		
+		Super.New( entity )
+		
+		Color=behaviour.Color
+		
+		SaveInitialState( behaviour )
+	End
+	
+	Property Color:Color()
+		
+		Return _color
+		
+	Setter( color:Color )
+		
+		_color=color
+	End
+	
+	Internal
+	
+	Method OnCopy:Behaviour( entity:Entity ) Override
+		
+		Return New Behaviour( Self,entity )
+	End
+	
+	Private
+	
+	Field _color:Color
+End
+
+Class Entity
+	
+	'simple ctor
+	Method New( parent:Entity )
+		
+		_parent=parent
+		
+		If _parent _parent._children.Add( Self )
+	End
+	
+	'copy ctor
+	Method New( entity:Entity,parent:Entity )
+		
+		Self.New( parent )
+	End
+	
+	Property Visible:Bool()
+		
+		Return _visible
+	
+	Setter( visible:Bool )
+		
+		_visible=visible
+	End
+	
+	Method Copy:Entity( parent:Entity ) Virtual
+		
+		Local copy:=OnCopy( parent )
+		
+		CopyTo( copy )
+		
+		Return copy
+	End
+	
+	Method AddComponent<T>:T()
+		
+		Local component:=New T( Self )
+		
+		Return component
+	End
+	
+	Protected
+	
+	Method OnCopy:Entity( parent:Entity ) Virtual
+		
+		Return New Entity( Self,parent )
+	End
+	
+	Method CopyTo( copy:Entity )
+		
+		For Local child:=Eachin _children
+			
+			child.CopyTo( child.OnCopy( copy ) )
+		Next
+		
+		For Local c:=Eachin _components
+			
+			c.OnCopy( copy )
+		Next
+	End
+	
+	Method SaveInitialState()
+		
+		If editing jsonifier.AddInstance( Self,New Variant[]( _parent ) )
+	End
+
+	Method SaveInitialState( entity:Entity )
+		
+		If editing jsonifier.AddInstance( Self,New Variant[]( entity,_parent ) )
+	End
+	
+	Private
+	
+	Field _parent:Entity
+	
+	Field _children:=New Stack<Entity>
+	
+	Field _visible:Bool
+	
+	Field _components:=New Stack<Component>
+	
+	Method AddComponent( component:Component )
+		
+		_components.Add( component )
+	End
+	
+End
+
+Class Camera Extends Entity
+	
+	Method New( parent:Entity )
+
+		Super.New( parent )
+		
+		FOV=90
+		
+		SaveInitialState()
+		
+		Visible=True
+	End
+	
+	Method New( camera:Camera,parent:Entity )
+		
+		Super.New( camera,parent )
+		
+		FOV=camera.FOV
+		
+		SaveInitialState( camera )
+		
+		Visible=True
+	End
+	
+	Property FOV:Float()
+	
+		Return _fov
+	
+	Setter( fov:Float )
+		
+		_fov=fov
+	End
+	
+	Method Copy:Camera( parent:Entity ) Override
+		
+		Local camera:=OnCopy( parent )
+		
+		CopyTo( camera )
+		
+		Return camera
+	End
+
+	Protected
+		
+	Method OnCopy:Camera( parent:Entity ) Override
+		
+		Return New Camera( Self,parent )
+	End
+	
+	Private
+	
+	Field _fov:Float
+	
+End
+
+Class Light Extends Entity
+	
+	Method New( parent:Entity )
+		
+		Super.New( parent )
+		
+		SaveInitialState()
+		
+		Visible=True
+	End
+
+	Method New( light:Light,parent:Entity )
+		
+		Super.New( parent )
+		
+		SaveInitialState( light )
+		
+		Visible=True
+	End
+
+	Method Copy:Light( parent:Entity ) Override
+		
+		Local light:=OnCopy( parent )
+		
+		CopyTo( light )
+		
+		Return light
+	End
+
+	Protected
+		
+	Method OnCopy:Light( parent:Entity ) Override
+		
+		Return New Light( Self,parent )
+	End
+	
+End
+
+Class Model Extends Entity
+	
+	Method New( parent:Entity )
+		
+		Super.New( parent )
+		
+		Mesh=""
+		
+		SaveInitialState()
+		
+		Visible=True
+	End
+	
+	Method New( model:Model,parent:Entity )
+		
+		Super.New( parent )
+		
+		Mesh=model.Mesh
+		
+		SaveInitialState( model )
+		
+		Visible=True
+	End
+	
+	Property Mesh:String()
+		
+		Return _mesh
+		
+	Setter( mesh:String )
+		
+		_mesh=mesh
+	End
+	
+	Function Load:Model( path:String,parent:Entity )
+		
+		Local model:=New Model( parent,True )
+		
+		model.Mesh="<"+path+">"
+		
+		If editing jsonifier.AddInstance( model,"Load",New Variant[]( path,parent ) )
+		
+		Return model
+	End
+	
+	Method Copy:Model( parent:Entity ) Override
+		
+		Local model:=OnCopy( parent )
+		
+		CopyTo( model )
+		
+		Return model
+	End
+
+	Protected
+		
+	Method OnCopy:Model( parent:Entity ) Override
+		
+		Return New Model( Self,parent )
+	End
+	
+	Private
+	
+	Field _mesh:String
+	
+	Method New( parent:Entity,loading:Bool )
+		
+		Super.New( parent )
+	End
+
+End
+
+Function CreateScene()
+	
+	Print "CreateScene"
+
+	jsonifier=New Jsonifier
+	
+	Local model1:=Model.Load( "model1.png",Null )
+	
+	Local behavour1:=New Behaviour( model1 )
+	
+	Local model2:=model1.Copy( Null )
+	
+End
+
+Function CreateScene2()
+	
+	Print "CreateScene"
+
+	jsonifier=New Jsonifier
+	
+	Local camera:=New Camera( Null )
+	
+	Local light:=New Light( Null )
+	
+	Local root:=Model.Load( "blah.txt",Null )
+	
+	For Local i:=0 Until 3
+		
+		Local model:=New Model( root )
+		
+		Local component:=New Behaviour( model )
+	Next
+	
+	Local copy:=root.Copy( Null )
+	
+End
+
+Function SaveScene:JsonObject()
+	
+	Print "SaveScene"
+
+	Local jobj:=jsonifier.JsonifyInstances()
+	
+	Return jobj
+End
+
+Function LoadScene( jobj:JsonObject )
+	
+	Print "LoadScene"
+	
+	jsonifier=New Jsonifier
+	
+	jsonifier.DejsonifyInstances( jobj )
+End
+
+Function Main()
+	
+	CreateScene()
+	
+	Local saved1:=SaveScene()
+	
+	LoadScene( saved1 )
+	
+	Local saved2:=SaveScene()
+	
+	If CompareJson( saved1,saved2 )=0
+		Print saved1.ToJson()+"~nSuccess!"
+	Else
+		Print "saved1:~n"+saved1.ToJson()+"~nsaved2:~n"+saved2.ToJson()+"~nError!"
+	Endif
+	
+End

+ 16 - 2
modules/mojo3d/render/material.monkey2 → modules/mojo3d/scene/material.monkey2

@@ -125,7 +125,7 @@ Class Material Extends Resource
 	Protected
 	
 	Method New()
-
+		
 		_uniforms=New UniformBlock( 3,True )
 		_blendMode=BlendMode.Opaque
 		_cullMode=CullMode.Back
@@ -134,7 +134,7 @@ Class Material Extends Resource
 	End		
 	
 	Method New( material:Material )
-
+		
 		_uniforms=New UniformBlock( material._uniforms )
 
 		_blendMode=material._blendMode
@@ -143,6 +143,20 @@ Class Material Extends Resource
 		TextureMatrix=material.TextureMatrix
 	End
 	
+	Method AddInstance( args:Variant[] )
+		
+		Local scene:=Scene.GetCurrent()
+		
+		If scene.Editing scene.Jsonifier.AddInstance( Self,args )
+	End
+	
+	Method AddInstance( material:Material )
+		
+		Local scene:=Scene.GetCurrent()
+		
+		If scene.Editing scene.Jsonifier.AddInstance( Self,New Variant[]( material ) )
+	End
+	
 	Private
 	
 	Field _name:String

+ 15 - 1
modules/mojo3d/render/materials/pbrmaterial.monkey2 → modules/mojo3d/scene/materials/pbrmaterial.monkey2

@@ -65,19 +65,25 @@ Class PbrMaterial Extends Material
 	End
 	
 	Method New( color:Color,metalness:Float=1.0,roughness:Float=1.0,boned:Bool=False )
+		
 		Self.New( boned )
 		
 		ColorFactor=color
 		MetalnessFactor=metalness
 		RoughnessFactor=roughness
+		
+		AddInstance( New Variant[]( color,metalness,roughness,boned ) )
 	End
 	
 	Method New( material:PbrMaterial )
+		
 		Super.New( material )
 		
 		_textured=material._textured
 		_bumpmapped=material._bumpmapped
 		_boned=material._boned
+		
+		AddInstance( material )
 	End
 	
 	#rem monkeydoc Creates a copy of the pbr material.
@@ -170,7 +176,7 @@ Class PbrMaterial Extends Material
 	End
 	
 	'***** factors *****
-
+	[jsonify=1]
 	Property ColorFactor:Color()
 	
 		Return Uniforms.GetColor( "ColorFactor" )
@@ -180,6 +186,7 @@ Class PbrMaterial Extends Material
 		Uniforms.SetColor( "ColorFactor",color )
 	End
 	
+	[jsonify=1]
 	Property EmissiveFactor:Color()
 	
 		Return Uniforms.GetColor( "EmissiveFactor" )
@@ -189,6 +196,7 @@ Class PbrMaterial Extends Material
 		Uniforms.SetColor( "EmissiveFactor",color )
 	End
 	
+	[jsonify=1]
 	Property MetalnessFactor:Float()
 	
 		Return Uniforms.GetFloat( "MetalnessFactor" )
@@ -198,6 +206,7 @@ Class PbrMaterial Extends Material
 		Uniforms.SetFloat( "MetalnessFactor",factor )
 	End
 	
+	[jsonify=1]
 	Property RoughnessFactor:Float()
 	
 		Return Uniforms.GetFloat( "RoughnessFactor" )
@@ -223,6 +232,11 @@ Class PbrMaterial Extends Material
 		
 		Local material:=New PbrMaterial
 		
+		Local scene:=Scene.GetCurrent()
+		If scene.Editing 
+			scene.Jsonifier.AddInstance( material,"mojo3d.PbrMaterial.Load",New Variant[]( path,textureFlags ) )
+		Endif
+		
 		Local texture:=LoadTexture( path,"color",textureFlags )
 		If texture
 			material.ColorTexture=texture

+ 0 - 0
modules/mojo3d/render/materials/spritematerial.monkey2 → modules/mojo3d/scene/materials/spritematerial.monkey2


+ 0 - 0
modules/mojo3d/render/materials/watermaterial.monkey2 → modules/mojo3d/scene/materials/watermaterial.monkey2


+ 17 - 0
modules/mojo3d/geometry/mesh.monkey2 → modules/mojo3d/scene/mesh.monkey2

@@ -75,6 +75,21 @@ Class Mesh Extends Resource
 		Return _bounds
 	End
 	
+	#rem monkeydoc Compacts the mesh.
+	
+	Compacts all internal data used by the mesh so they occupy as little memory as possible.
+	
+	#end
+	Method Compact()
+		
+		_vertices.Compact()
+		_materials.Compact()
+		
+		For Local material:=Eachin _materials
+			material.indices.Compact()
+		Next
+	End
+	
 	#rem monkeydoc Clears the mesh.
 	
 	Removes all vertices and primitives from the mesh, and resets the number of logical materials to '1'.
@@ -90,6 +105,8 @@ Class Mesh Extends Resource
 		InvalidateVertices()
 	End
 	
+	#rem monkeydoc Clear the mesh vertices.
+	#end
 	Method ClearVertices()
 		
 		ResizeVertices( 0 )

+ 59 - 19
modules/mojo3d/geometry/meshprims.monkey2 → modules/mojo3d/scene/meshprims.monkey2

@@ -3,6 +3,18 @@ Namespace mojo3d
 
 Private
 
+Function Editing:Bool()
+	
+	Return Scene.GetCurrent().Editing
+End
+
+Function AddInstance( instance:Mesh,ctor:String,args:Variant[] )
+	
+	Local scene:=Scene.GetCurrent()
+	
+	If scene?.Editing scene.Jsonifier.AddInstance( instance,ctor,args )
+End
+
 Struct TerrainData
 	
 	Field heightMap:Pixmap
@@ -58,7 +70,7 @@ Class Mesh Extension
 	
 	Function CreateRect:Mesh( rect:Rectf )
 
-		Local data:=New Mesh( 
+		Local mesh:=New Mesh( 
 			New Vertex3f[](
 				New Vertex3f( rect.min.x,rect.max.y,0 ),
 				New Vertex3f( rect.max.x,rect.max.y,0 ),
@@ -67,10 +79,12 @@ Class Mesh Extension
 			New UInt[](
 				0,1,2,
 				0,2,3 ) )
+				
+		If Editing() AddInstance( mesh,"mojo3d.Mesh.CreateRect",New Variant[]( rect ) )
 		
-		data.UpdateTangents()
+		mesh.UpdateTangents()
 		
-		Return data
+		Return mesh
 	End
 
 	Function CreateBox:Mesh( box:Boxf,xsegs:Int=1,ysegs:Int=1,zsegs:Int=1 )
@@ -139,11 +153,13 @@ Class Mesh Extension
 			v0+=zsegs+1
 		Next
 		
-		Local data:=New Mesh( vertices,indices )
+		Local mesh:=New Mesh( vertices,indices )
+		
+		If Editing() AddInstance( mesh,"mojo3d.Mesh.CreateBox",New Variant[]( box,xsegs,ysegs,zsegs ) )
 		
-		data.UpdateTangents()
+		mesh.UpdateTangents()
 		
-		Return data
+		Return mesh
 	End
 
 	Function CreateSphere:Mesh( radius:float,hsegs:Int=24,vsegs:Int=12 )
@@ -191,11 +207,13 @@ Class Mesh Extension
 			vdata[i].normal=vdata[i].position.Normalize()
 		Next
 		
-		Local data:=New Mesh( vertices.ToArray(),indices.ToArray() )
+		Local mesh:=New Mesh( vertices.ToArray(),indices.ToArray() )
 		
-		data.UpdateTangents()
+		If Editing() AddInstance( mesh,"mojo3d.Mesh.CreateSphere",New Variant[]( radius,hsegs,vsegs ) )
+		
+		mesh.UpdateTangents()
 		
-		Return data
+		Return mesh
 	End
 	
 	Function CreateTorus:Mesh( outerRadius:Float,innerRadius:Float,outerSegs:Int=24,innerSegs:Int=12 )
@@ -246,11 +264,13 @@ Class Mesh Extension
 			Next
 		Next
 		
-		Local data:=New Mesh( vertices,indices )
+		Local mesh:=New Mesh( vertices,indices )
+		
+		If Editing() AddInstance( mesh,"mojo3d.Mesh.CreateTorus",New Variant[]( outerRadius,innerRadius,outerSegs,innerSegs ) )
 		
-		data.UpdateTangents()
+		mesh.UpdateTangents()
 		
-		Return data
+		Return mesh
 	End
 	
 	Function CreateCylinder:Mesh( radius:Float,length:Float,axis:Axis,segs:Int )
@@ -295,6 +315,8 @@ Class Mesh Extension
 		
 		Local mesh:=New Mesh( vertices.ToArray(),triangles.ToArray() )
 		
+		If Editing() AddInstance( mesh,"mojo3d.Mesh.CreateCylinder",New Variant[]( radius,length,axis,segs ) )
+		
 		Select axis
 		Case Axis.X
 			mesh.TransformVertices( New AffineMat4f( 0,1,0, 1,0,0, 0,0,1, 0,0,0 ) )
@@ -383,6 +405,8 @@ Class Mesh Extension
 		
 		Local mesh:=New Mesh( vertices.ToArray(),triangles.ToArray() )
 		
+		If Editing() AddInstance( mesh,"mojo3d.Mesh.CreateCapsule",New Variant[]( radius,length,axis,segs ) )
+		
 		Select axis
 		Case Axis.X
 			mesh.TransformVertices( New AffineMat4f( 0,1,0, 1,0,0, 0,0,1, 0,0,0 ) )
@@ -431,6 +455,8 @@ Class Mesh Extension
 		
 		Local mesh:=New Mesh( vertices.ToArray(),triangles.ToArray() )
 		
+		If Editing() AddInstance( mesh,"mojo3d.Mesh.CreateCone",New Variant[]( radius,length,axis,segs ) )
+		
 		Select axis
 		Case Axis.X
 			mesh.TransformVertices( New AffineMat4f( 0,1,0, 1,0,0, 0,0,1, 0,0,0 ) )
@@ -497,49 +523,63 @@ Class Model Extension
 		
 		Local mesh:=mojo3d.Mesh.CreateBox( box,xsegs,ysegs,zsegs )
 		
-		Return New Model( mesh,material,parent )
+		Local model:=New Model( mesh,material,parent )
+		
+		Return model
 	End
 	
 	Function CreateSphere:Model( radius:Float,hsegs:Int,vsegs:Int,material:Material,parent:Entity=Null )
 		
 		Local mesh:=mojo3d.Mesh.CreateSphere( radius,hsegs,vsegs )
 		
-		Return New Model( mesh,material,parent )
+		Local model:=New Model( mesh,material,parent )
+
+		Return model
 	End
 	
 	Function CreateTorus:Model( outerRadius:Float,innerRadius:Float,outerSegs:Int,innerSegs:Int,material:Material,parent:Entity=Null )
 		
 		Local mesh:=mojo3d.Mesh.CreateTorus( outerRadius,innerRadius,outerSegs,innerSegs )
 		
-		Return New Model( mesh,material,parent )
+		Local model:=New Model( mesh,material,parent )
+		
+		Return model
 	End
 	
 	Function CreateCylinder:Model( radius:Float,length:Float,axis:Axis,segs:Int,material:Material,parent:Entity=null )
 		
 		Local mesh:=mojo3d.Mesh.CreateCylinder( radius,length,axis,segs )
 		
-		Return New Model( mesh,material,parent )
+		Local model:=New Model( mesh,material,parent )
+
+		Return model
 	End
 	
 	Function CreateCapsule:Model( radius:Float,length:Float,axis:Axis,segs:Int,material:Material,parent:Entity=null )
 		
 		Local mesh:=mojo3d.Mesh.CreateCapsule( radius,length,axis,segs )
 		
-		Return New Model( mesh,material,parent )
+		Local model:=New Model( mesh,material,parent )
+
+		Return model
 	End
 	
 	Function CreateCone:Model( radius:Float,length:Float,axis:Axis,segs:Int,material:Material,parent:Entity=null )
 		
 		Local mesh:=mojo3d.Mesh.CreateCone( radius,length,axis,segs )
 		
-		Return New Model( mesh,material,parent )
+		Local model:=New Model( mesh,material,parent )
+
+		Return model
 	End
 	
 	Function CreateTerrain:Model( heightMap:Pixmap,bounds:Boxf,material:Material,parent:Entity=Null )
 		
 		Local mesh:=mojo3d.Mesh.CreateTerrain( heightMap,bounds )
 		
-		Return New Model( mesh,material,parent )
+		Local model:=New Model( mesh,material,parent )
+
+		Return model
 	End
 	
 End

+ 0 - 0
modules/mojo3d/render/posteffect.monkey2 → modules/mojo3d/scene/posteffect.monkey2


+ 111 - 0
modules/mojo3d/scene/scene.monkey2

@@ -21,6 +21,14 @@ Class Scene
 		_envColor=Color.White
 		
 		_world=New World( Self )
+		
+		Local type:=TypeInfo.GetType( "mojo3d.Scene" )
+		If type And type.Kind="Class"
+			_jsonifier=New Jsonifier
+			_jsonifier.AddInstance( Self,New Variant[0] )
+		Endif
+		
+		_editing=False
 	End
 	
 	#rem monkeydoc The sky texture.
@@ -32,6 +40,7 @@ Class Scene
 	This must currently be a valid cubemap texture.
 	
 	#end
+'	[jsonify=1]
 	Property SkyTexture:Texture()
 		
 		Return _skyTexture
@@ -52,6 +61,7 @@ Class Scene
 	This must currently be a valid cubemap texture.
 	
 	#end
+'	[jsonify=1]
 	Property EnvTexture:Texture()
 		
 		Return _envTexture
@@ -64,6 +74,7 @@ Class Scene
 	#rem monkey The environment color.
 	
 	#end
+	[jsonify=1]
 	Property EnvColor:Color()
 		
 		Return _envColor
@@ -80,6 +91,7 @@ Class Scene
 	The clear color is only used if there is no sky texture.
 	
 	#end
+	[jsonify=1]
 	Property ClearColor:Color()
 		
 		Return _clearColor
@@ -89,6 +101,7 @@ Class Scene
 		_clearColor=color
 	End
 	
+	[jsonify=1]
 	Property FogColor:Color()
 		
 		Return _fogColor
@@ -98,6 +111,7 @@ Class Scene
 		_fogColor=color
 	End
 	
+	[jsonify=1]
 	Property FogNear:Float()
 		
 		Return _fogNear
@@ -107,6 +121,7 @@ Class Scene
 		_fogNear=near
 	End
 	
+	[jsonify=1]
 	Property FogFar:Float()
 		
 		Return _fogFar
@@ -116,6 +131,7 @@ Class Scene
 		_fogFar=far
 	End
 	
+	[jsonify=1]
 	Property ShadowAlpha:Float()
 		
 		Return _shadowAlpha
@@ -127,6 +143,7 @@ Class Scene
 	
 	#rem monkeydoc Scene update rate.
 	#end
+	[jsonify=1]
 	Property UpdateRate:Float()
 		
 		Return _updateRate
@@ -138,6 +155,7 @@ Class Scene
 	
 	#rem monkeydoc Ambient diffuse lighting.
 	#end
+	[jsonify=1]
 	Property AmbientLight:Color()
 		
 		Return _ambientDiffuse
@@ -154,6 +172,7 @@ Class Scene
 	Must have length 4.
 		
 	#end
+	[jsonify=1]
 	Property CSMSplits:Float[]()
 		
 		Return _csmSplits
@@ -164,6 +183,60 @@ 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.
+	
+	#end
+	Method FindEntity:Entity( name:String )
+		
+		For Local entity:=Eachin _rootEntities
+			
+			Local found:=entity.Find( name )
+			If found Return found
+		Next
+		
+		Return Null
+	End
+	
 	#rem monkeydoc Adds a post effect to the scene.
 	#end
 	Method AddPostEffect( postEffect:PostEffect )
@@ -235,6 +308,19 @@ Class Scene
 		Return _rootEntities.ToArray()
 	End
 	
+	#rem monkeydoc Saves the scene to a mojo3d scene file
+	#end
+	Method Save( path:String )
+		
+		Assert( _jsonifier,"Scene is not editable" )
+		
+		Local jobj:=_jsonifier.JsonifyInstances()
+		
+		Local json:=jobj.ToJson()
+		
+		SaveString( json,path )
+	End
+	
 	#rem monkeydoc Sets the current scene.
 	
 	All newly created entities (including entites created using Entity.Copy]]) are automatically added to the current scene.
@@ -257,6 +343,25 @@ 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>()
@@ -319,6 +424,12 @@ Class Scene
 	
 	Field _world:World
 	
+	Field _jsonifier:Jsonifier
+	
+	Field _editing:Bool
+	
+	Field _editingPaused:=0
+	
 	Method Update( elapsed:Float )
 		
 		For Local e:=Eachin _rootEntities

+ 0 - 0
modules/mojo3d/geometry/util3d.monkey2 → modules/mojo3d/scene/util3d.monkey2


+ 8 - 1
modules/mojo3d/tests/cubes.monkey2

@@ -1,5 +1,7 @@
 Namespace myapp
 
+#Reflect mojo3d
+
 #Import "<std>"
 #Import "<mojo>"
 #Import "<mojo3d>"
@@ -24,13 +26,16 @@ Class MyWindow Extends Window
 		
 		Print opengl.glGetString( opengl.GL_VERSION )
 		
-		_scene=Scene.GetCurrent()
+		_scene=New Scene
+		
+		_scene.Editing=True
 		
 		_scene.ClearColor=Color.Sky
 		
 		'create camera
 		'
 		_camera=New Camera( Self )
+		_camera.Name="Camera"
 		_camera.Near=.1
 		_camera.Far=1000
 		_camera.Move( 0,10,-10 )
@@ -56,6 +61,8 @@ Class MyWindow Extends Window
 		Next
 		
 		cube.Destroy()
+		
+		_scene.Save( "cubes-scene.mojo3d" )
 	End
 	
 	Method OnRender( canvas:Canvas ) Override

+ 100 - 0
modules/mojo3d/tests/loadscene.monkey2

@@ -0,0 +1,100 @@
+Namespace myapp3d
+
+#Reflect mojo3d
+
+#Import "<std>"
+#Import "<mojo>"
+#Import "<mojo3d>"
+
+Using std..
+Using mojo..
+Using mojo3d..
+
+Class MyWindow Extends Window
+	
+	Field _scene:Scene
+	Field _camera:Camera
+	Field _light:Light
+	Field _ground:Model
+	Field _donut:Model
+	
+	Method New( title:String="Simple mojo3d app",width:Int=640,height:Int=480,flags:WindowFlags=WindowFlags.Resizable )
+		
+		Super.New( title,width,height,flags )
+	End
+	
+	Method CreateScene()
+		
+		_scene.AmbientLight = _scene.ClearColor * 0.25
+		_scene.FogColor = _scene.ClearColor
+		_scene.FogFar = 1.0
+		_scene.FogFar = 200.0
+
+		'create camera
+		_camera=New Camera
+		_camera.AddComponent<FlyBehaviour>()
+		_camera.Move( 0,2.5,-5 )
+		
+		'create light
+		_light=New Light
+		_light.CastsShadow=True
+		_light.Rotate( 45, 45, 0 )
+		
+		'create ground
+		Local groundBox:=New Boxf( -100,-1,-100,100,0,100 )
+		Local groundMaterial:=New PbrMaterial( Color.Lime )
+		_ground=Model.CreateBox( groundBox,1,1,1,groundMaterial )
+		_ground.CastsShadow=False
+		
+		'create donut
+		Local donutMaterial:=New PbrMaterial( Color.Red, 0.05, 0.2 )
+		_donut=Model.CreateTorus( 2,.5,48,24,donutMaterial )
+		_donut.Move( 0,2.5,0 )
+		Local rb:=_donut.AddComponent<RotateBehaviour>()
+		rb.Speed=New Vec3f( .2,.4,.6 )
+	End
+	
+	Method OnCreateWindow() Override
+		
+		Local path:=CurrentDir()+"test-scene.mojo3d"
+		
+		_scene=New Scene
+		
+		_scene.Editing=True
+		
+		Print "Creating scene"
+		
+		CreateScene()
+		
+		Print "Saving scene to "+path
+			
+		_scene.Save( path )
+		
+		Print "Loading scene from "+path
+			
+		_scene=Scene.Load( path )
+			
+		_camera=Cast<Camera>( _scene.FindEntity( "Camera" ) )
+	End
+	
+	Method OnRender( canvas:Canvas ) Override
+		
+		RequestRender()
+		
+		_scene.Update()
+		
+		_camera.Render( canvas )
+		
+		canvas.DrawText( "FPS="+App.FPS,0,0 )
+	End
+	
+End
+
+Function Main()
+
+	New AppInstance
+	
+	New MyWindow
+	
+	App.Run()
+End