2
0
Эх сурвалжийг харах

WIP CharacterController experiments.

Mark Sibly 7 жил өмнө
parent
commit
1bf4a8b6a0

+ 280 - 35
modules/mojo3d-loaders/tests/castle.monkey2

@@ -5,9 +5,11 @@ Namespace myapp
 #Import "<mojo3d>"
 #Import "<mojo3d-loaders>"
 
-#Import "assets/"
+#Import "assets/castle/@/castle"
+#Import "../../mojo3d/tests/assets/heightmap_256.png"
+#Import "../../mojo3d/tests/assets/mossy-ground1.pbr@/mossy-ground1.pbr"
 
-#Import "../../mojo3d/tests/assets/miramar-skybox.jpg"
+'#Import "../../mojo3d/tests/assets/miramar-skybox.jpg"
 
 #Import "util"
 
@@ -15,54 +17,288 @@ Using std..
 Using mojo..
 Using mojo3d..
 
+Struct QResult
+	Field position:Vec3f
+	Field hitground:Bool
+	Field hitwall:Bool
+End
+
+Function QCollide:QResult( collider:ConvexCollider,src:Vec3f,dst:Vec3f,moving:Bool )
+	
+	Local margin:=collider.Margin
+	
+	Local world:=collider.Entity.Scene.World
+	
+	Local start:=src
+	
+	Local plane0:Planef,plane1:Planef,state:=0,casts:=0
+	
+	Local qresult:QResult
+	
+	Local debug:=""
+	
+	Repeat
+		
+		If src.Distance( dst )<.00001
+			dst=src
+			Exit
+		Endif
+
+		casts+=1
+		
+		Local cresult:=world.ConvexSweep( collider,src,dst,1 )
+		If Not cresult Exit
+	
+'		debug+=", "
+		
+		If cresult.normal.y>.7071
+'			Print "hitground "+cresult.normal
+			qresult.hitground=True
+		Endif
+		
+		If cresult.normal.y<.1
+			qresult.hitwall=True
+		Endif
+			
+		Local plane:=New Planef( cresult.point,cresult.normal )
+		plane.d-=margin
+		
+		Local d0:=plane.Distance( src ),d1:=plane.Distance( dst )
+		
+		Local tline:=New Linef( src,dst-src )
+		
+		Local t:=plane.TIntersect( tline )
+		
+		If t>0 src=tline * t
+		
+		If Not moving Or t>=1
+			dst=src
+			Exit
+		Endif
+			
+		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>.001
+				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
+				Print "QCollide OOPS2"
+'				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>3 Print debug.Slice( 2 )+"QCOLLIDE OOPS3 casts="+casts
+
+	qresult.position=dst
+	
+	Return qresult
+End
+
+
+Class CharacterController Extends Behaviour
+	
+	Method New( entity:Entity )
+		
+		Super.New( entity )
+	End
+	
+	Property OnGround:Bool()
+		
+		Return _onground
+	End
+	
+	Property StepDown:Float()
+		
+		Return _stepDown
+		
+	 Setter( stepDown:Float )
+		 
+		 _stepDown=stepDown
+	End
+	
+	Protected
+	
+	Field _jumping:Bool
+	
+	Field _stepDown:Float=.5	'50 cms
+	
+	Field _onground:Bool
+	
+	Field _vel:Vec3f
+	
+	Method OnUpdate( elapsed:Float ) Override
+		
+		If Keyboard.KeyDown( Key.Left )
+			Entity.RotateY( 2.5 )
+		Else If Keyboard.KeyDown( Key.Right )
+			Entity.RotateY( -2.5 )
+		Endif
+
+		Local src:=Entity.Position
+		
+		Local moving:=False
+		
+		If Keyboard.KeyDown( Key.A )
+			Entity.MoveZ( .15 )
+			moving=True
+		Else If Keyboard.KeyDown( Key.Z )
+			Entity.MoveZ( -.15 )
+			moving=True
+		Endif
+		
+		If _onground _vel.y=-Entity.Collider.Margin
+			
+		_vel+=Entity.Scene.World.Gravity/60.0/60.0
+		
+		If Keyboard.KeyHit( Key.Space )
+			_jumping=True
+			_vel.y=.125
+		Endif
+		
+		Entity.Move( _vel )
+		
+		Local dst:=Entity.Position
+		
+		Local qres:=QCollide( Cast<ConvexCollider>( Entity.Collider ),src,dst,moving Or Not _onground )
+		dst=qres.position
+		
+		If Not _jumping And Not qres.hitground And _onground
+			src=dst
+			dst.y-=_stepDown
+			qres=QCollide( Cast<ConvexCollider>( Entity.Collider ),src,dst,False )
+			dst=qres.position
+		Endif
+		
+		_onground=qres.hitground
+
+		If _onground _jumping=False
+		
+		_vel.y=dst.y-src.y
+		
+		Entity.Position=dst
+	End
+	
+End
+
 Class MyWindow Extends Window
 	
 	Field _scene:Scene
 	
+	Field _player:Model
+	
 	Field _camera:Camera
 	
-	Field _light:Light
+	Method CreateTerrain:Model()
+		
+		Local box:=New Boxf( -256,-32,-256,256,0,256 )
+		
+		Local hmap:=Pixmap.Load( "asset::heightmap_256.png",PixelFormat.I8 )
+
+		Local material:=PbrMaterial.Load( "asset::mossy-ground1.pbr" )
+		material.ScaleTextureMatrix( 64,64 )
+		
+		'model+mesh
+		Local model:=Model.CreateTerrain( hmap,box,material )
+		model.CastsShadow=False
+		
+		Local collider:=model.AddComponent<TerrainCollider>()
+		collider.Heightmap=hmap
+		collider.Bounds=box
+		
+		Local body:=model.AddComponent<RigidBody>()
+		body.Mass=0
+		
+		Return model
+	End
 	
-	Field _ground:Model
+	Method CreateCastle:Model()
+		
+		Local box:=New Boxf( -10000,0,-10000,10000,30,10000 )
+		
+		Local model:=Model.Load( "asset::castle/CASTLE1.X" )
+		model.Mesh.FitVertices( box,True )
+		
+		Local collider:=model.AddComponent<MeshCollider>()
+		collider.Mesh=model.Mesh
+		
+		Local body:=model.AddComponent<RigidBody>()
+		body.Mass=0
+		
+		Return model
+	End
 	
-	Field _model:Model
+	Method CreatePlayer:Model()
+		
+		Local radius:=.25,length:=1.25,segs:=12
+		Local material:=New PbrMaterial( Color.Green )
+		
+		Local model:=Model.CreateCapsule( radius,length,Axis.Y,segs,material )
+		model.Move( 0,10,0 )
+		
+		Local collider:=model.AddComponent<CapsuleCollider>()
+		collider.Radius=radius
+		collider.Length=length
+		collider.Axis=Axis.Y
+		
+'		Local body:=_player.AddComponent<RigidBody>()
+'		body.Kinematic=True
+		
+		Local controller:=model.AddComponent<CharacterController>()
+		
+		Return model
+	End
 	
-	Method New( title:String="Simple mojo app",width:Int=640,height:Int=480,flags:WindowFlags=WindowFlags.Resizable )
-
-		Super.New( title,width,height,flags )
+	Method CreateScene:Scene()
 		
-		'create scene
-		'		
-		_scene=Scene.GetCurrent()
-		_scene.SkyTexture=Texture.Load( "asset::miramar-skybox.jpg",TextureFlags.FilterMipmap|TextureFlags.Cubemap )
+		Local scene:=New Scene
 		
-		'create camera
-		'
-		_camera=New Camera( Self )
-		_camera.Near=.1
-		_camera.Far=100
-		_camera.Move( 0,10,-10 )
+		Local light:=New Light
+		light.Rotate( 60,30,0 )	'aim directional light 'down' - Pi/2=90 degrees.
+		light.CastsShadow=True
 		
-		New FlyBehaviour( _camera )
+		Local terrain:=CreateTerrain()
 		
-		'create light
-		'
-		_light=New Light
-		_light.Rotate( 60,30,0 )	'aim directional light 'down' - Pi/2=90 degrees.
-		_light.CastsShadow=True
+		Local castle:=CreateCastle()
 		
-		'create ground
-		'
-		_ground=Model.CreateBox( New Boxf( -50,-1,-50,50,0,50 ),8,1,8,New PbrMaterial( Color.Green*.1,0,1 ) )
-		_ground.CastsShadow=False
+		_player=CreatePlayer()
 		
-		'create model
-		'		
-		_model=Model.Load( "asset::castle/CASTLE1.X" )
+		Local camera:=New Camera( _player )
+		camera.View=Self
+		camera.LocalPosition=New Vec3f( 0,.5,0 )
+		camera.RotateX( 45 )
+		camera.Move( 0,0,-2 )
 		
-		Const cheight:=30.0
+		_camera=camera
 		
-		_model.Mesh.FitVertices( New Boxf( -10000,0,-10000,10000,cheight,10000 ),True )
+		Return scene
+	End
+	
+	Method New( title:String="Simple mojo app",width:Int=640,height:Int=480,flags:WindowFlags=WindowFlags.Resizable )
+
+		Super.New( title,width,height,flags )
+		
+		_scene=CreateScene()
 	End
 		
 	Method OnRender( canvas:Canvas ) Override
@@ -71,9 +307,18 @@ Class MyWindow Extends Window
 		
 		_scene.Update()
 		
-		_camera.Render( canvas )
+		If Keyboard.KeyDown( Key.Up )
+			_camera.RotateX( 1,True )
+		Else If Keyboard.KeyDown( Key.Down )
+			_camera.RotateX( -1,True )
+		Endif
+		
+		_scene.Render( canvas )
+		
+		Local controller:=_player.GetComponent<CharacterController>()
 
-		canvas.DrawText( "FPS="+App.FPS,1,0,Width,0 )
+'		canvas.DrawText( "y="+_player.Position.y+" onground="+controller.OnGround+" FPS="+App.FPS,0,0 )
+		canvas.DrawText( " onground="+controller.OnGround+" FPS="+App.FPS,0,0 )
 	End
 	
 End

+ 5 - 5
modules/mojo3d/scene/scene.monkey2

@@ -34,6 +34,11 @@ Class Scene
 		
 	End
 	
+	Property World:World()
+		
+		Return _world
+	End
+	
 	#rem monkeydoc The sky texture.
 	
 	The sky texture is used to clear the scene. 
@@ -399,11 +404,6 @@ Class Scene
 		Return _renderables
 	End
 	
-	Property World:World()
-		
-		Return _world
-	End
-	
 	Private
 	
 	Global _current:Scene

+ 6 - 3
modules/mojo3d/scene/world.monkey2

@@ -68,9 +68,12 @@ Class World
 		Return New RayCastResult( Varptr btresult )
 	End
 	
-	Method ConvexSweep:RayCastResult( collider:ConvexCollider,castFrom:AffineMat4f,castTo:AffineMat4f )
+	Method ConvexSweep:RayCastResult( collider:ConvexCollider,castFrom:AffineMat4f,castTo:AffineMat4f,collisionMask:Int )
 		
 		Local btresult:=New btCollisionWorld.ClosestConvexResultCallback( castFrom.t,castTo.t )
+
+		btresult.m_collisionFilterGroup=collisionMask
+		btresult.m_collisionFilterMask=collisionMask
 		
 		_btworld.convexSweepTest( Cast<btConvexShape>( collider.Validate() ),castFrom,castTo,Cast<btCollisionWorld.ConvexResultCallback Ptr>( Varptr btresult ),0 )
 		
@@ -79,9 +82,9 @@ Class World
 		Return New RayCastResult( Varptr btresult )
 	End
 	
-	Method ConvexSweep:RayCastResult( collider:ConvexCollider,castFrom:Vec3f,castTo:Vec3f )
+	Method ConvexSweep:RayCastResult( collider:ConvexCollider,castFrom:Vec3f,castTo:Vec3f,collisionMask:Int )
 		
-		Return ConvexSweep( collider,AffineMat4f.Translation( castFrom ),AffineMat4f.Translation( castTo ) )
+		Return ConvexSweep( collider,AffineMat4f.Translation( castFrom ),AffineMat4f.Translation( castTo ),collisionMask )
 	End
 
 	Method Update( elapsed:Float )