Przeglądaj źródła

Changed Collider and RigidBody to new component system. WIP.

Mark Sibly 8 lat temu
rodzic
commit
9dd8e0cd2d

+ 1 - 0
modules/mojo3d-physics/mojo3d-physics.monkey2

@@ -13,4 +13,5 @@ Using bullet..
 #Import "physics/collider"
 #Import "physics/rigidbody"
 #Import "physics/world"
+#Import "physics/fpscollider"
 

+ 388 - 41
modules/mojo3d-physics/physics/collider.monkey2

@@ -10,138 +10,483 @@ Function CreateInternalEdgeInfo( mesh:btBvhTriangleMeshShape )="bbBullet::create
 	
 Public
 
-Class Collider
+
+Class Collider Extends Component
+	
+	Const Type:=New ComponentType( "Collider",-1,ComponentTypeFlags.Singleton )
+	
+	Method New( entity:Entity )
+		Super.New( entity,Type )
+	End
 	
 	Property Margin:Float()
 		
-		Return _btshape.getMargin()
+		Return btShape.getMargin()
 	
 	Setter( margin:Float )
 		
-		_btshape.setMargin( margin )
+		btShape.setMargin( margin )
 	End
 
 	Method CalculateLocalInertia:Vec3f( mass:Float )
 		
-		Return _btshape.calculateLocalInertia( mass )
+		Return btShape.calculateLocalInertia( mass )
 	End
 
 	Property btShape:btCollisionShape()
+		
+		If Not _btshape _btshape=OnCreate()
 	
 		Return _btshape
 	End
 
 Protected
 
-	Field _btshape:btCollisionShape
+	Method OnCreate:btCollisionShape() Abstract
 	
-	Method SetOrigin( origin:Vec3f )
+	Method Invalidate()
 		
-		If origin=Null Return
+		If Not _btshape Return
 		
-		Local shape:=New btCompoundShape( False,1 )
+		_btshape.destroy()
 		
-		shape.addChildShape( AffineMat4f.Translation( origin ),_btshape )
+		_btshape=Null
+	End
+
+	function SetOrigin:btCollisionShape( shape:btCollisionShape,origin:Vec3f )
 		
-		_btshape=shape
+		If origin=Null Return shape
+		
+		Local tshape:=New btCompoundShape( False,1 )
+		
+		tshape.addChildShape( AffineMat4f.Translation( origin ),shape )
+		
+		Return tshape
 	End
 	
+	Private
+
+	Field _btshape:btCollisionShape
+	
 End
 
 Class ConvexCollider Extends Collider
 	
+	Method New( Entity:Entity )
+		Super.New( Entity )
+	End
+	
 End
 
 Class BoxCollider Extends ConvexCollider
 	
-	Method New( box:Boxf )
+	Method New( Entity:Entity )
+		Super.New( Entity )
+		
+		Box=New Boxf( -1,1 )
+	End
 	
-		_btshape=New btBoxShape( box.Size/2 )
+	Property Box:Boxf()
 		
-		SetOrigin( box.Center )
+		Return _box
+	
+	Setter( box:Boxf )
+		
+		_box=box
+		
+		Invalidate()
 	End
 	
+	Protected
+	
+	Method OnCopy:BoxCollider( entity:Entity ) Override
+		
+		Local collider:=New BoxCollider( entity )
+		
+		collider.Box=Box
+		
+		Return collider
+	End
+	
+	Method OnCreate:btCollisionShape() Override
+		
+		Local shape:=New btBoxShape( _box.Size/2 )
+		
+		Return SetOrigin( shape,_box.Center )
+	End
+	
+	Private
+	
+	Field _box:=New Boxf( -1,1 )
 End
 
 Class SphereCollider Extends ConvexCollider
 	
-	Method New( radius:Float,origin:Vec3f=Null )
+	Method New( Entity:Entity )
+		Super.New( Entity )
+	End
+	
+	Property Radius:Float()
+		
+		Return _radius
+	
+	Setter( radius:Float )
+		
+		_radius=radius
+		
+		Invalidate()
+	End
+	
+	Property Origin:Vec3f()
+		
+		Return _origin
+		
+	Setter( origin:Vec3f )
+		
+		_origin=origin
+		
+		Invalidate()
+	End
+	
+	Protected
+	
+	Method OnCopy:SphereCollider( entity:Entity ) Override
+		
+		Local collider:=New SphereCollider( entity )
+		
+		collider.Radius=Radius
+		collider.Origin=Origin
+		
+		Return collider
+	End
+	
+	Method OnCreate:btCollisionShape() Override
 		
-		_btshape=New btSphereShape( radius )
+		Local shape:=New btSphereShape( _radius )
 		
-		SetOrigin( origin )
+		return SetOrigin( shape,_origin )
 	End
 	
+	Private
+	
+	Field _radius:Float=1
+	
+	Field _origin:Vec3f
+	
 End
 
 Class CylinderCollider Extends ConvexCollider
 	
-	Method New( radius:Float,length:Float,axis:Axis,origin:Vec3f=Null )
+	Method New( entity:Entity )
+		Super.New( entity )
+	End
+	
+	Property Radius:Float()
+		
+		Return _radius
+		
+	Setter( radius:Float )
+		
+		_radius=radius
+		
+		Invalidate()
+	End
+	
+	Property Length:Float()
+		
+		Return _length
+		
+	Setter( length:Float )
+		
+		_length=length
+		
+		Invalidate()
+	End
+	
+	Property Axis:Axis()
+		
+		Return _axis
+		
+	Setter( axis:Axis )
+		
+		_axis=axis
+		
+		Invalidate()
+	End
+	
+	Property Origin:Vec3f()
+		
+		Return _origin
+	
+	Setter( origin:Vec3f )
+		
+		_origin=origin
+		
+		Invalidate()
+	End
+
+	Protected
+
+	Method OnCopy:CylinderCollider( entity:Entity ) Override
+		
+		Local collider:=New CylinderCollider( entity )
+		
+		collider.Radius=Radius
+		collider.Length=Length
+		collider.Axis=Axis
+		collider.Origin=Origin
+		
+		Return collider
+	End
+	
+	Method OnCreate:btCollisionShape() Override
+
+		Local shape:btCollisionShape
 		
-		Select axis
+		Select _axis
 		case Axis.X
-			_btshape=New btCylinderShapeX( New btVector3( length/2,radius,radius ) )
+			shape=New btCylinderShapeX( New btVector3( _length/2,_radius,_radius ) )
 		Case Axis.Y
-			_btshape=New btCylinderShape ( New btVector3( radius,length/2,radius ) )
+			shape=New btCylinderShape ( New btVector3( _radius,_length/2,_radius ) )
 		case Axis.Z
-			_btshape=New btCylinderShapeZ( New btVector3( radius,radius,length/2 ) )
+			shape=New btCylinderShapeZ( New btVector3( _radius,_radius,_length/2 ) )
 		Default
 			RuntimeError( "Invalid Cylinder Axis" )
 		End
 		
-		SetOrigin( origin )
+		Return SetOrigin( shape,_origin )
 	End
+	
+	Private
+	
+	Field _radius:Float=0.5
+	Field _length:Float=1.0
+	Field _axis:Axis=geom.Axis.Y
+	Field _origin:Vec3f
 
 End
 
 Class CapsuleCollider Extends ConvexCollider
 	
-	Method New( radius:Float,length:Float,axis:Axis,origin:Vec3f=Null )
+	Method New( entity:Entity )
+		Super.New( entity )
+	End
+	
+	Property Radius:Float()
+		
+		Return _radius
+		
+	Setter( radius:Float )
+		
+		_radius=radius
+		
+		Invalidate()
+	End
+	
+	Property Length:Float()
+		
+		Return _length
+		
+	Setter( length:Float )
+		
+		_length=length
+		
+		Invalidate()
+	End
+	
+	Property Axis:Axis()
+		
+		Return _axis
+		
+	Setter( axis:Axis )
+		
+		_axis=axis
+		
+		Invalidate()
+	End
+	
+	Property Origin:Vec3f()
+		
+		Return _origin
+	
+	Setter( origin:Vec3f )
+		
+		_origin=origin
+		
+		Invalidate()
+	End
+
+	Protected
+	
+	Method OnCopy:CapsuleCollider( entity:Entity ) Override
+		
+		Local collider:=New CapsuleCollider( entity )
+		
+		collider.Radius=Radius
+		collider.Length=Length
+		collider.Axis=Axis
+		collider.Origin=Origin
+		
+		Return collider
+	End
+	
+	Method OnCreate:btCollisionShape() Override
+		
+		Local shape:btCollisionShape
 		
-		Select axis
+		Select _axis
 		Case Axis.X
-			_btshape=New btCapsuleShapeX( radius,length )
+			shape=New btCapsuleShapeX( _radius,_length )
 		Case Axis.Y
-			_btshape=New btCapsuleShape ( radius,length )
+			shape=New btCapsuleShape ( _radius,_length )
 		Case Axis.Z
-			_btshape=New btCapsuleShapeZ( radius,length )
+			shape=New btCapsuleShapeZ( _radius,_length )
 		Default
 			RuntimeError( "Invalid Capsule Axis" )
 		End
 		
-		SetOrigin( origin )
+		Return SetOrigin( shape,_origin )
 	End
 	
+	Private
+	
+	Field _radius:Float=0.5
+	Field _length:Float=1.0
+	Field _axis:Axis=geom.Axis.Y
+	Field _origin:Vec3f
+
 End
 
 Class ConeCollider Extends ConvexCollider
 	
-	Method New( radius:Float,length:Float,axis:Axis,origin:Vec3f=Null )
+	Method New( entity:Entity )
+		Super.New( entity )
+	End
+	
+	Property Radius:Float()
+		
+		Return _radius
+		
+	Setter( radius:Float )
+		
+		_radius=radius
+		
+		Invalidate()
+	End
+	
+	Property Length:Float()
+		
+		Return _length
 		
-		Select axis
+	Setter( length:Float )
+		
+		_length=length
+		
+		Invalidate()
+	End
+	
+	Property Axis:Axis()
+		
+		Return _axis
+		
+	Setter( axis:Axis )
+		
+		_axis=axis
+		
+		Invalidate()
+	End
+	
+	Property Origin:Vec3f()
+		
+		Return _origin
+	
+	Setter( origin:Vec3f )
+		
+		_origin=origin
+		
+		Invalidate()
+	End
+
+	Protected
+	
+	Method OnCopy:ConeCollider( entity:Entity ) Override
+		
+		Local collider:=New ConeCollider( entity )
+		
+		collider.Radius=Radius
+		collider.Length=Length
+		collider.Axis=Axis
+		collider.Origin=Origin
+		
+		Return collider
+	End
+	
+	Method OnCreate:btCollisionShape() Override
+		
+		Local shape:btCollisionShape
+		
+		Select _axis
 		Case Axis.X
-			_btshape=New btConeShapeX( radius,length )
+			shape=New btConeShapeX( _radius,_length )
 		Case Axis.Y
-			_btshape=New btConeShape ( radius,length )
+			shape=New btConeShape ( _radius,_length )
 		Case Axis.Z
-			_btshape=New btConeShapeZ( radius,length )
+			shape=New btConeShapeZ( _radius,_length )
 		Default
 			RuntimeError( "Invalid Cone Axis" )
 		End
-		
-		SetOrigin( origin )
-	End
 
+		Return SetOrigin( shape,_origin )
+	End
+	
+	Private
+	
+	Field _radius:Float=0.5
+	Field _length:Float=1.0
+	Field _axis:Axis=geom.Axis.Y
+	Field _origin:Vec3f
+	
 End
 
 Class ConcaveCollider Extends Collider
+
+	Method New( entity:Entity )
+		Super.New( entity )
+	End
+	
 End
 
 Class MeshCollider Extends ConcaveCollider
+	
+	Method New( entity:Entity )
+		Super.New( entity )
+	End
+	
+	Property Mesh:Mesh()
+		
+		Return _mesh
+	
+	Setter( mesh:Mesh ) 
+		
+		_mesh=mesh
+		
+		Invalidate()
+	End
 
-	Method New( mesh:Mesh )
+	Protected
 	
-		Local vertices:=mesh.GetVertices()
+	Method OnCopy:MeshCollider( entity:Entity ) Override
+		
+		Local collider:=New MeshCollider( entity )
+		
+		collider.Mesh=Mesh
+		
+		Return collider
+	End
+	
+	Method OnCreate:btCollisionShape() Override
+		
+		Local vertices:=_mesh.GetVertices()
 		_vertices=New btVector3[vertices.Length]
 		
 		For Local i:=0 Until vertices.Length
@@ -150,7 +495,7 @@ Class MeshCollider Extends ConcaveCollider
 			_vertices[i].z=vertices[i].position.z
 		Next
 		
-		Local indices:=mesh.GetAllIndices()
+		Local indices:=_mesh.GetAllIndices()
 		_indices=New Int[indices.Length]
 		For Local i:=0 Until indices.Length
 			_indices[i]=indices[i]
@@ -162,18 +507,19 @@ Class MeshCollider Extends ConcaveCollider
 		
 		'CreateInternalEdgeInfo( shape )
 		
-		_btshape=shape
+		Return shape
 	End
 	
 	Private
 	
+	Field _mesh:Mesh
 	Field _vertices:btVector3[]
 	Field _indices:Int[]
-	
 	Field _btmesh:btTriangleIndexVertexArray
 	
 End
 
+#rem
 Class TerrainCollider Extends ConcaveCollider
 
 	Method New( box:Boxf,data:Pixmap )
@@ -190,3 +536,4 @@ Class TerrainCollider Extends ConcaveCollider
 	End
 
 End
+#end

+ 212 - 0
modules/mojo3d-physics/physics/fpscollider.monkey2

@@ -0,0 +1,212 @@
+
+Namespace mojo3d.physics
+
+Private
+
+Function F:String( f:Float )
+	If f>=0 
+		Local i:Int=Floor( f*100+.5 )
+		Return String( i / 100 ) + "." + ("00"+String( i Mod 100 )).Slice( -2 )
+	Else
+		Local i:Int=Floor( -f*100+.5 )
+		Return "-"+String( i / 100 ) + "." + ("00"+String( i Mod 100 )).Slice( -2 )
+	Endif
+End
+
+Function V:String( V:Vec3f )
+	'Return String(V)
+	Return "("+F(V.x)+","+F(V.y)+","+F(V.z)+")"
+End
+
+Function P:String( P:Planef )
+	Return "("+F(P.n.x)+","+F(P.n.y)+","+F(P.n.z)+","+F(P.d)+")"
+End
+
+Public
+
+Struct QResult
+	Field position:Vec3f
+	Field onground:Bool
+End
+
+Function QCollide:QResult( collider:ConvexCollider,src:Vec3f,dst:Vec3f )
+	
+	Local start:=src
+	
+	Local plane0:Planef,plane1:Planef,state:=0,casts:=0
+	
+	Local qresult:QResult
+	
+	Local debug:=""
+	
+	Repeat
+
+		If dst.Distance( src )<.001 Exit
+		
+		casts+=1
+		
+		Local world:=World.GetCurrent()
+		
+		Local cresult:=world.ConvexSweep( collider,src,dst )
+		If Not cresult Exit
+
+'		debug+=", "
+		
+		If cresult.normal.y>.7071 qresult.onground=True
+			
+		Local plane:=New Planef( cresult.point,cresult.normal )
+
+		plane.d-=collider.Margin
+		
+		Local tline:=New Linef( src,dst-src )
+		
+		Local t:=plane.TIntersect( tline )
+		
+		If t>=1 dst=tline * t ; Exit
+		
+		If t>0 src=tline * t
+		
+		Select state
+
+		Case 0
+			
+			dst=plane.Nearest( dst )
+			
+'			debug+="A "+P( plane )
+				
+			plane0=plane
+				
+			state=1
+		
+		Case 1
+
+			Local v:=plane0.n.Cross( plane.n )
+			
+			If v.Length>.0001
+				
+				Local groove:=New Linef( src,v )
+				
+'				Local d0:=plane0.Distance( dst )
+				
+				dst=groove.Nearest( dst )
+				
+'				debug+="B "+P( plane )+" d0="+F(d0)+" sd0="+F(plane0.Distance(src))+" dd0="+F(plane0.Distance(dst))
+				
+				plane1=plane
+				
+				state=2
+			
+			Else
+				
+'				debug+="C "+P( plane )
+
+				dst=plane.Nearest( dst )
+
+				plane0=plane
+				
+				state=1
+			
+			Endif
+				
+		Case 2
+
+'			Local d0:=plane0.Distance( dst )
+'			Local d1:=plane1.Distance( dst )
+'			debug+="D "+P( plane )+" d0="+F(d0)+" d1="+F(d1)
+				
+			dst=src
+			
+			Exit
+		End
+
+	Forever
+	
+'	If casts>2 Print debug.Slice( 2 )+" casts="+casts
+
+	qresult.position=dst
+	
+	Return qresult
+
+End
+
+Public
+
+Class FPSCollider Extends CapsuleCollider
+	
+	Method New( entity:Entity )
+		Super.New( entity )
+		
+		_src=Entity.Position
+	End
+	
+	Property Gravity:Float()
+		
+		Return _gravity
+	
+	Setter( gravity:Float )
+		
+		_gravity=gravity
+	End
+	
+	Property YVelocity:Float()
+		
+		Return _yvel
+		
+	Setter( yvel:Float )
+		
+		_onground=False
+			
+		_yvel=yvel
+	End
+	
+	Property OnGround:Bool()
+		
+		Return _onground
+	End
+	
+	Method Reset()
+		_src=Entity.Position
+		_yvel=0
+		_onground=False
+	End
+	
+	Method OnBeginUpdate() Override
+		
+		_src=Entity.Position
+	End
+	
+	Method OnUpdate( elapsed:Float ) Override
+		
+		Local qresult:=QCollide( Self,_src,Entity.Position )
+
+		Entity.Position=qresult.position
+		
+		_src=Entity.Position
+		
+		If _onground _yvel=-Margin
+			
+		_yvel-=_gravity/60.0/60.0
+
+		Entity.MoveY( _yvel )
+
+		qresult=QCollide( Self,_src,Entity.Position )
+
+		Entity.Position=qresult.position
+		
+		_yvel=Entity.Position.y-_src.y
+		
+		_onground=qresult.onground
+	End
+	
+	Private
+	
+	Field _gravity:Float=30
+	
+	Field _yvel:Float
+	
+	Field _onground:Bool
+	
+	Field _src:Vec3f
+	
+End
+

+ 111 - 231
modules/mojo3d-physics/physics/rigidbody.monkey2

@@ -3,7 +3,7 @@ Namespace mojo3d.physics
 
 #Import "native/kinematicmotionstate.h"
 
-Extern
+Extern Private
 
 Class bbKinematicMotionState Extends btMotionState
 	
@@ -11,7 +11,7 @@ Class bbKinematicMotionState Extends btMotionState
 	
 End
 
-Public
+Private
 
 Class KinematicMotionState Extends bbKinematicMotionState
 	
@@ -30,309 +30,189 @@ Private
 	Field _entity:Entity
 End
 
-Class Entity Extension
-
-	Property RigidBody:RigidBody()
-	
-		Return GetDynamicProperty<RigidBody>( "$rigidBody" )
-	End
-	
-End
+Public
 
-#rem monkeydoc The RigidBody class.
-#end
-Class RigidBody
+Class RigidBody Extends Component
 	
-	Method Init( collider:Collider,entity:Entity,mass:Float,collGroup:Int,collMask:Int,btmotion:btMotionState )
+	Const Type:=New ComponentType( "RigidBody",1,ComponentTypeFlags.Singleton )
 	
-		Local inertia:btVector3=collider ? collider.CalculateLocalInertia( mass ) Else Null
-
-		_collider=collider
-		_entity=entity
-		_mass=mass
-		_collGroup=collGroup
-		_collMask=collMask
-		_btmotion=btmotion
-
-		_btbody=New btRigidBody( mass,btmotion,collider.btShape,inertia )
-		
-		_btbody.setFriction( 1 )
-		_btbody.setRollingFriction( 1 )
-		_btbody.setRestitution( 0 )
-
-		'If Cast<MeshCollider>( _collider ) _btbody.setCollisionFlags( _btbody.getCollisionFlags() | btCollisionObject.CF_CUSTOM_MATERIAL_CALLBACK )
-			
-		If Not _entity Return
-		
-		_entity.SetDynamicProperty( "$rigidBody",Self )
-
-		Local world:=entity.Scene.World
-		
-		_entity.Shown+=Lambda()
-		
-			world.Add( Self )
-		End
-		
-		_entity.Hidden+=Lambda()
-		
-			world.Remove( Self )
-		End
+	Method New( entity:Entity )
 		
-		If _entity.Visible world.Add( Self )
+		Super.New( entity,Type )
 	End
-
-	Property Collider:Collider()
+	
+	Property World:World()
 		
-		Return _collider
+		Return World.GetWorld( Entity.Scene )
 	End
 	
-	Property Entity:Entity()
-	
-		Return _entity
-	End
+	Property Kinematic:Bool()
+		
+		Return _kinematic
 	
-	Property CollisionGroup:Short()
+	Setter( kinematic:Bool )
 		
-		Return _collGroup
+		_kinematic=kinematic
 	End
 	
-	Property CollisionMask:Short()
+	Property Mass:Float()
 		
-		Return _collMask
+		Return _mass
+		
+	Setter( mass:Float )
+		
+		_mass=mass
 	End
-	
+
 	Property Friction:Float()
-	
-		Return _btbody.getFriction()
+		
+		Return _friction
 	
 	Setter( friction:Float )
-	
-		_btbody.setFriction( friction )
+		
+		_friction=friction
 	End
 	
 	Property RollingFriction:Float()
-	
-		Return _btbody.getRollingFriction()
+		
+		Return _rfriction
 	
 	Setter( friction:Float )
-	
-		_btbody.setRollingFriction( friction )
+		
+		_rfriction=friction
 	End
 	
 	Property Restitution:Float()
 	
-		Return _btbody.getRestitution()
+		Return _restitution
 		
 	Setter( restitution:Float )
-	
-		_btbody.setRestitution( restitution )
-	End
-	
-	Property btBody:btRigidBody()
-	
-		Return _btbody
+		
+		_restitution=restitution
 	End
-	
-Protected
 
-	Field _mass:Float	
-	Field _collider:Collider
-	Field _entity:Entity
-	Field _collGroup:Int
-	Field _collMask:Int
-	
-	Field _btmotion:btMotionState
-	Field _btbody:btRigidBody
-	Field _seq:Int
-	
-	Method OnValidate() Virtual
-	End
-	
-	Method OnUpdate() Virtual
-	End
-	
-Internal
+	Property CollisionGroup:Short()
 		
-	Method Validate()
+		Return _collGroup
 		
-		If _entity OnValidate()
-	End
-	
-	Method Update()
+	Setter( collGroup:Short )
 		
-		If _entity OnUpdate()
+		_collGroup=collGroup
 	End
 	
-End
-
-Class StaticBody Extends RigidBody
-	
-	Method New( collider:Collider,entity:Entity,collGroup:Int=1,collMask:Int=1 )
-		
-		Init( collider,entity,0,collGroup,collMask,Null )
+	Property CollisionMask:Short()
 		
-		If entity entity.Copied+=Lambda( copy:Entity )
+		Return _collMask
 		
-			Local body:=New StaticBody( collider,copy,collGroup,collMask )
-			
-			body.Friction=Friction
-			body.RollingFriction=RollingFriction
-			body.Restitution=Restitution
-		End
+	Setter( collMask:Short )
 		
+		_collMask=collMask
 	End
 	
-Protected
+	Property btBody:btRigidBody()
 	
-	Method OnValidate() Override
-		
-		If _seq=_entity.Seq Return
-		
-		_btbody.setWorldTransform( _entity.Matrix )
+		Return _btbody
 	End
 	
-	Method OnUpdate() Override
+	Method OnCopy:RigidBody( entity:Entity ) Override
 		
-		_seq=_entity.Seq
-	End
-
-End
-
-Class KinematicBody Extends RigidBody
-	
-	Method New( collider:Collider,entity:Entity,collGroup:Int=1,collMask:Int=1 )
-
-		Init( collider,entity,0,collGroup,collMask,entity ? New KinematicMotionState( entity ) Else Null )
-
-		_btbody.setCollisionFlags( _btbody.getCollisionFlags() | btCollisionObject.CF_KINEMATIC_OBJECT )
-		_btbody.setActivationState( DISABLE_DEACTIVATION )
+		Local body:=New RigidBody( entity )
 		
-		If entity entity.Copied+=Lambda( copy:Entity )
+		body.Kinematic=Kinematic
+		body.Mass=Mass
+		body.Friction=Friction
+		body.RollingFriction=RollingFriction
+		body.Restitution=Restitution
+		body.CollisionGroup=CollisionGroup
+		body.CollisionMask=CollisionMask
 		
-			Local body:=New KinematicBody( collider,copy,collGroup,collMask )
-
-			body.Friction=Friction
-			body.RollingFriction=RollingFriction
-			body.Restitution=Restitution
-		End
-
-	End
-	
-Protected
-	
-	Method OnValidate() Override
+		Return body
 	End
-	
-	Method OnUpdate() Override
-	End
-	
-End
-
-Class DynamicBody Extends RigidBody
 
-	Method New( collider:Collider,entity:Entity,mass:Float=1,collGroup:Int=1,collMask:Int=1 )
-		
-		Init( collider,entity,mass,collGroup,collMask,entity ? New btDefaultMotionState( entity.Matrix ) Else null )
-			
-		If entity entity.Copied+=Lambda( copy:Entity )
+	Method OnBeginUpdate() Override
 		
-			Local body:=New DynamicBody( collider,copy,mass,collGroup,collMask )
-
-			body.Gravity=Gravity
-			body.Friction=Friction
-			body.RollingFriction=RollingFriction
-			body.Restitution=Restitution
+		If Not _btbody
+			Validate()
+			Return
 		End
-	End
-	
-	Property Mass:Float()
 		
-		Return _mass
-	End
-	
-	Property Gravity:Vec3f()
-		
-		Return _btbody.getGravity()
-	
-	Setter( gravity:Vec3f )
-		
-		_btbody.setGravity( gravity )
-	End
+		If _kinematic Return
 	
-	Property LinearVelocity:Vec3f()
-	
-		Return _btbody.getLinearVelocity()
-	
-	Setter( velocity:Vec3f )
-	
-		_btbody.setLinearVelocity( velocity )
+		If _seq=Entity.Seq Return
+
+		_btbody.clearForces()
+		_btbody.setLinearVelocity( New Vec3f( 0 ) )
+		_btbody.setAngularVelocity( New Vec3f( 0 ) )
+		_btbody.setWorldTransform( Entity.Matrix )
+		_btmotion.setWorldTransform( Entity.Matrix )
 	End
 	
-	Property AngularVelocity:Vec3f()
+	Method OnUpdate( elapsed:Float ) Override
 		
-		Return _btbody.getAngularVelocity()
-	
-	Setter( velocity:Vec3f )
+		If _kinematic Return
 		
-		_btbody.setAngularVelocity( velocity )
-	End
-	
-	Method ApplyForce( force:Vec3f,relativePos:Vec3f )
+		Local tform:=_btmotion.getWorldTransform()
 		
-		_btbody.applyForce( force,relativePos )
-	End
-	
-	Method ApplyCentralForce( force:Vec3f )
+		Entity.Position=tform.getOrigin()
 		
-		_btbody.applyCentralForce( force )
-	End
-	
-	Method ApplyTorque( torque:Vec3f )
+		Entity.Basis=tform.getBasis()
 		
-		_btbody.applyTorque( torque )
+		_seq=Entity.Seq
 	End
 	
-	Method ApplyImpulse( impulse:Vec3f,relativePos:Vec3f )
+	Method OnDestroy() Override
 		
-		_btbody.applyImpulse( impulse,relativePos )
-	End
-	
-	Method ApplyCentralImpulse( impulse:Vec3f )
+		If Not _btbody Return
 		
-		_btbody.applyCentralImpulse( impulse )
-	End
-	
-	Method ApplyTorqueImpulse( torque:Vec3f )
+		World.GetCurrent().Remove( Self )
 		
-		_btbody.applyTorqueImpulse( torque )
+		_btbody=Null
 	End
 	
-Protected
+	Internal
 	
-	Method OnValidate() Override
+	Method Validate:btRigidBody()
 		
-		If _seq=_entity.Seq Return
-		
-		_btbody.clearForces()
-		
-		_btbody.setLinearVelocity( New Vec3f( 0 ) )
-		
-		_btbody.setAngularVelocity( New Vec3f( 0 ) )
+		If _btbody Return _btbody
+
+		If _kinematic		
+			_btmotion=New KinematicMotionState( Entity )
+		Else
+			_btmotion=New btDefaultMotionState( Entity.Matrix )
+		Endif
 		
-		_btbody.setWorldTransform( _entity.Matrix )
+		Local collider:=Entity.GetComponent<Collider>()
 		
-		_btmotion.setWorldTransform( _entity.Matrix )
-	End
-	
-	Method OnUpdate() Override
+		Local inertia:btVector3=collider ? collider.CalculateLocalInertia( _mass ) Else Null
 		
-		Local tform:=_btmotion.getWorldTransform()
+		_btbody=New btRigidBody( _mass,_btmotion,collider.btShape,inertia )
+
+		If _kinematic
+			_btbody.setCollisionFlags( _btbody.getCollisionFlags() | btCollisionObject.CF_KINEMATIC_OBJECT )
+			_btbody.setActivationState( DISABLE_DEACTIVATION )
+		Endif
 		
-		_entity.Position=tform.getOrigin()
+		_btbody.setFriction( _friction )
+		_btbody.setRollingFriction( _rfriction )
+		_btbody.setRestitution( _restitution )
 		
-		_entity.Basis=tform.getBasis()
+		World.Add( Self )
 		
-		_seq=_entity.Seq
+		Return _btbody
 	End
+	
+	Private
+
+	Field _kinematic:Bool=False
+	Field _mass:Float=1
+	Field _friction:Float=1
+	Field _rfriction:Float=1
+	Field _restitution:Float=0
+	Field _collGroup:Short=1
+	Field _collMask:Short=1
+	
+	Field _btmotion:btMotionState
+	Field _btbody:btRigidBody
+	Field _seq:Int
 
 End

+ 35 - 36
modules/mojo3d-physics/physics/world.monkey2

@@ -1,20 +1,6 @@
 
 Namespace mojo3d.physics
 
-Class Scene Extension
-
-	Property World:World()
-	
-		Local world:=GetDynamicProperty<World>( "$world" )
-		If Not world
-			world=New World( Self )
-			SetDynamicProperty( "$world",world )
-		Endif
-		Return world
-	End
-
-End
-
 Class RaycastResult
 
 	Field time:Float
@@ -48,9 +34,13 @@ End
 Class World
 	
 	Method New( scene:Scene )
+		
+		Assert( scene.GetDynamicProperty<World>( "$world" )=Null,"World already exists" )
 	
 		_scene=scene
 		
+		_scene.SetDynamicProperty( "$world",Self )
+		
 		Local broadphase:=New btDbvtBroadphase()
 		
 		Local config:=New btDefaultCollisionConfiguration()
@@ -63,6 +53,7 @@ Class World
 
 		Gravity=New Vec3f( 0,-9.81,0 )
 		
+		_scene.Updating+=OnUpdate
 	End
 	
 	Property Scene:Scene()
@@ -79,22 +70,6 @@ Class World
 		_btworld.setGravity( gravity )
 	End
 	
-	Method Update()
-
-		For Local body:=Eachin _bodies
-		
-			body.Validate()
-		Next
-		
-		_btworld.stepSimulation( 1.0/60.0 )
-		
-		For Local body:=Eachin _bodies
-		
-			body.Update()
-		Next
-		
-	End
-	
 	Method RayCast:RaycastResult( rayFrom:Vec3f,rayTo:Vec3f )
 		
 		Local btresult:=New btCollisionWorld.ClosestRayResultCallback( rayFrom,rayTo )
@@ -122,24 +97,42 @@ Class World
 		Return ConvexSweep( collider,AffineMat4f.Translation( castFrom ),AffineMat4f.Translation( castTo ) )
 	End
 	
+	Function GetCurrent:World()
+		
+		Return GetWorld( mojo3d.Scene.GetCurrent() )
+	End
+	
 	Internal
 	
+	Function GetWorld:World( scene:Scene )
+		
+		Local world:=scene.GetDynamicProperty<World>( "$world" )
+		
+		If Not world world=New World( scene )
+			
+		Return world
+	End
+	
 	Method Add( body:RigidBody )
 		
 		_bodies.Add( body )
 		
-		_btworld.addRigidBody( body.btBody,body.CollisionGroup,body.CollisionMask )
+		Local btbody:=body.Validate()
+		
+		btbody.setUserPointer( Cast<Void Ptr>( body ) )
 		
-		body.btBody.setUserPointer( Cast<Void Ptr>( body ) )
+		_btworld.addRigidBody( btbody,body.CollisionGroup,body.CollisionMask )
 	End
 	
 	Method Remove( body:RigidBody )
 		
-		_bodies.Remove( body )
-
-		_btworld.removeRigidBody( body.btBody )
-
+		Local btbody:=body.Validate()
+		
+		_btworld.removeRigidBody( btbody )
+		
 		body.btBody.setUserPointer( Null )
+
+		_bodies.Remove( body )
 	End
 	
 	Private
@@ -151,5 +144,11 @@ Class World
 	Field _newBodies:=New Stack<RigidBody>
 	
 	Field _bodies:=New Stack<RigidBody>
+
+	Method OnUpdate( elapsed:Float )
+		
+		_btworld.stepSimulation( 1.0/60.0 )
+		
+	End
 	
 End

+ 53 - 102
modules/mojo3d-physics/tests/fpsthing.monkey2

@@ -12,112 +12,65 @@ Namespace myapp
 
 #Import "../../mojo3d-loaders/tests/assets/castle/@/castle"
 
-#Import "util"
-
-#Import "qcollide"
-
 Using std..
 Using mojo..
 Using mojo3d..
 
-Const GRAVITY:=30	'coz reality sux!
-
-Class Player
-
-	Field _scene:Scene
-	
-	Field _model:Model
-	
-	Field _collider:ConvexCollider
+Class FPSPlayer Extends FPSCollider
 	
-	Field _paused:Bool
-	
-	Field _onground:Bool
-	
-	Field _yvel:Float
-	
-	Method New( radius:Float=.5,height:Float=1 )
-		
-		_model=Model.CreateCapsule( radius,height,Axis.Y,12,New PbrMaterial( Color.Sky ) )
-
-'		_model.Move( 0,0,0 )
-		
-		_collider=New CapsuleCollider( radius,height,Axis.Y )
-		
-		_collider.Margin=.01
+	Method New( entity:Entity )
+		Super.New( entity )
 	End
 	
-	Method Update()
+	Method OnUpdate( elapsed:Float ) Override
 		
-		Local src:=_model.Position
-		
-		Move()
-		
-		If _paused Return
-		
-		Local qresult:=qcollide.QCollide( _collider,src,_model.Position )
-
-		_model.Position=qresult.position
-		
-		src=_model.Position
-		
-		If _onground _yvel=-_collider.Margin
-			
-		_yvel-=GRAVITY/60.0/60.0
-
-		_model.MoveY( _yvel )
-
-		qresult=qcollide.QCollide( _collider,src,_model.Position )
-
-		_model.Position=qresult.position
-		
-		_yvel=_model.Position.y-src.y
-		
-		_onground=qresult.onground
-	End
-	
-	Method Move()
+		Local c:=Entity.GetComponent<GameController>()
+		DebugAssert( c )
 		
-		If Keyboard.KeyDown( Key.Left )
+		If c.ButtonDown( Button.Left )
 			
-			_model.RotateY( 2.5 )
+			Entity.RotateY( 2.5 )
 			
-		Else If Keyboard.KeyDown( Key.Right )
+		Else If c.ButtonDown( Button.Right )
 			
-			_model.RotateY( -2.5 )
+			Entity.RotateY( -2.5 )
 		Endif
 
-		If _paused
+		If c.ButtonDown( Button.Up )
+			
+			Entity.MoveY( .25 )
+			
+		Else If c.ButtonDown( Button.Down )
+			
+			Entity.MoveY( -.25 )
 			
-			If Keyboard.KeyDown( Key.Up )
-				
-				_model.MoveY( .25 )
-				
-			Else If Keyboard.KeyDown( Key.Down )
-				
-				_model.MoveY( -.25 )
-				
-			Endif
 		Endif
 		
-		If Keyboard.KeyDown( Key.A )
+		If c.ButtonDown( Button.Forward )
 			
-			_model.MoveZ( .15 )
+			Entity.MoveZ( .15 )
 			
-		Else If Keyboard.KeyDown( Key.Z )
+		Else If c.ButtonDown( Button.Backward )
 			
-			_model.MoveZ( -.15 )
+			Entity.MoveZ( -.15 )
 		Endif
 		
-		If Keyboard.KeyHit( Key.Space )
+		If c.ButtonDown( Button.Fire )
 			
-			_onground=False
+			YVelocity=.25
 			
-			_yvel=.25
 		Endif
+
+		'Update actual collider.
+		'
+		'Extending FPSCollider is probably not a great idea, this lot should really be in its own
+		'component, but haven't thought much about how to access YVelocity etc yet so this'll do for now.
+		'
+		Super.OnUpdate( elapsed )
 		
 	End
 	
+	
 End
 
 Class MyWindow Extends Window
@@ -130,7 +83,7 @@ Class MyWindow Extends Window
 	
 	Field _castle:Model
 	
-	Field _player:Player
+	Field _player:FPSPlayer
 	
 	Field _sphere:SphereCollider
 	
@@ -144,11 +97,16 @@ Class MyWindow Extends Window
 		
 		_scene.SkyTexture=Texture.Load( "asset::miramar-skybox.jpg",TextureFlags.FilterMipmap|TextureFlags.Cubemap )
 		
+		'create camera
+		'
+		_camera=New Camera
+		_camera.Near=.1
+		_camera.Far=200
+		
 		'create light
 		'
 		_light=New Light
 		_light.Rotate( 60,60,0 )	'aim directional light 'down' - Pi/2=90 degrees.
-		
 		_light.CastsShadow=True
 		
 		'Load castle
@@ -157,45 +115,38 @@ Class MyWindow Extends Window
 		
 '		_castle=Model.Load( "asset::E1M1_clean.obj" )	'On the off chance you've got this...have no idea of license issues though.
 		_castle=Model.Load( "asset::castle/CASTLE1.X" )
-
 		_castle.Mesh.FitVertices( New Boxf( -sz,sz ),True )
 		
-		Local collider:=New MeshCollider( _castle.Mesh )
+		Local collider:=New MeshCollider( _castle )
+		collider.Mesh=_castle.Mesh
 		
-		Local body:=New StaticBody( collider,_castle,1,1 )
+		Local body:=New RigidBody( _castle )
+		body.Mass=0
 		
-	
-		'create player
-		'
-		_player=New Player( .75,.5 )
+		Local model:=Model.CreateCapsule( .5,.75,Axis.Y,24,New PbrMaterial( Color.Sky ) )
 		
-		'create camera
-		'
-		_camera=New Camera
-		_camera.Near=.1
-		_camera.Far=200
+		model.AddComponent<KeyboardController>()
+		
+		_player=model.AddComponent<FPSPlayer>()
+		_player.Margin=.01
+		_player.Radius=.5
+		_player.Length=.75
 		
-		_sphere=New SphereCollider( .2 )
-
 	End
 	
 	Method OnRender( canvas:Canvas ) Override
 		
 		RequestRender()
 		
-		If Keyboard.KeyHit( Key.Enter ) _player._paused=Not _player._paused
-		
-		_player.Update()
-
-		_scene.World.Update()
+		_scene.Update()
 		
-		Local src:=_player._model.Matrix * New Vec3f( 0,1,0 )
+		Local src:=_player.Entity.Matrix * New Vec3f( 0,1,0 )
 		
-		Local dst:=_player._model.Matrix * New Vec3f( 0,1.5,-2.5 )
+		Local dst:=_player.Entity.Matrix * New Vec3f( 0,1.5,-2.5 )
 		
 		_camera.Position=dst
 		
-		_camera.PointAt( _player._model.Position+New Vec3f( 0,1.5,0 ) )
+		_camera.PointAt( _player.Entity.Position+New Vec3f( 0,1.5,0 ) )
 		
 		_scene.Render( canvas,_camera )
 		

+ 43 - 24
modules/mojo3d-physics/tests/shapes.monkey2

@@ -41,7 +41,11 @@ Class MyWindow Extends Window
 		_camera.Far=60
 		_camera.Move( 0,10,-10 )
 		
-		New KinematicBody( New SphereCollider( 1 ),_camera )
+		Local camBody:=New RigidBody( _camera )
+		camBody.Kinematic=True
+		camBody.Mass=1
+		
+		Local camCollider:=New SphereCollider( _camera )
 		
 		'create fog
 		'		
@@ -61,29 +65,44 @@ Class MyWindow Extends Window
 		
 		_ground=Model.CreateBox( groundBox,16,16,16,New PbrMaterial( Color.Green ) )
 		
-		Local collider:Collider=New BoxCollider( groundBox )
+		Local collider:=New BoxCollider( _ground )
+		collider.Box=groundBox
 		
-		Local body:=New StaticBody( collider,_ground )
-
-		'create some meshes/colliders
+		Local body:=New RigidBody( _ground )
+		body.Mass=0
 		
-		Local meshes:=New Mesh[5]
-		Local colliders:=New Collider[5]
+		'create some meshes/colliders
 		
-		meshes[0]=Mesh.CreateBox( New Boxf( -1,-5,-1,1,5,1 ),1,1,1 )
-		colliders[0]=New BoxCollider(  New Boxf( -1,-5,-1,1,5,1 ) )
+		Local material:=New PbrMaterial( Color.White )
 		
-		meshes[1]=Mesh.CreateSphere( 1,32,16 )
-		colliders[1]=New SphereCollider( 1 )
+		Local model0:=Model.CreateBox( New Boxf( -1,-5,-1,1,5,1 ),1,1,1,material )
+		model0.AddComponent<RigidBody>()
+		Local collider0:=model0.AddComponent<BoxCollider>()
+		collider0.Box=New Boxf( -1,-5,-1,1,5,1 )
+
+		Local model1:=Model.CreateSphere( 1,32,16,material )
+		model1.AddComponent<RigidBody>()
+		Local collider1:=model1.AddComponent<SphereCollider>()
 
-		meshes[2]=Mesh.CreateCylinder( 1,8,Axis.Y,32 )
-		colliders[2]=New CylinderCollider( 1,8,Axis.Y )
+		Local model2:=Model.CreateCylinder( 1,8,Axis.Y,32,material )
+		model2.AddComponent<RigidBody>()
+		Local collider2:=model2.AddComponent<CylinderCollider>()
+		collider2.Radius=1
+		collider2.Length=8
 
-		meshes[3]=Mesh.CreateCapsule( 1,10,Axis.Y,32 )
-		colliders[3]=New CapsuleCollider( 1,10,Axis.Y )
+		Local model3:=Model.CreateCapsule( 1,10,Axis.Y,32,material )
+		model3.AddComponent<RigidBody>()
+		Local collider3:=model3.AddComponent<CapsuleCollider>()
+		collider3.Radius=1
+		collider3.Length=10
 
-		meshes[4]=Mesh.CreateCone( 2.5,5,Axis.Y,32 )
-		colliders[4]=New ConeCollider( 2.5,5,Axis.Y )
+		Local model4:=Model.CreateCone( 2.5,5,Axis.Y,32,material )
+		model4.AddComponent<RigidBody>()
+		Local collider4:=model4.AddComponent<ConeCollider>()
+		collider4.Radius=2.5
+		collider4.Length=5
+		
+		Local models:=New Model[]( model0,model1,model2,model3,model4 )
 		
 		For Local x:=-40 To 40 Step 8
 			
@@ -91,18 +110,18 @@ Class MyWindow Extends Window
 				
 				Local i:=Int( Rnd(5) )
 				
-				Local mesh:=meshes[i]
-				Local material:=New PbrMaterial( New Color( Rnd(),Rnd(),Rnd() ) )
-				
-				Local model:=New Model( mesh,material )
-				model.Move( x,10,z )
+				Local model:=models[i].Copy()
 				
-				Local body:=New DynamicBody( colliders[i],model )
+				model.Materials=New Material[]( New PbrMaterial( New Color( Rnd(),Rnd(),Rnd() ) ) )
 				
+				model.Move( x,10,z )
 			Next
 		
 		Next
 		
+		For Local model:=Eachin models
+			model.Destroy()
+		Next
 	End
 	
 	Method OnRender( canvas:Canvas ) Override
@@ -111,7 +130,7 @@ Class MyWindow Extends Window
 		
 		util.Fly( _camera,Self )
 			
-		_scene.World.Update()
+		_scene.Update()
 		
 		_scene.Render( canvas,_camera )