About Monkey 2 › Forums › Monkey 2 Code Library › mojo3d Physics Samples
This topic contains 11 replies, has 3 voices, and was last updated by
DruggedBunny
1 year, 2 months ago.
-
AuthorPosts
-
January 16, 2018 at 1:45 am #12875
Just posting some simple mojo3d samples using Bullet physics. Feel free to add more!
My first two samples just implement a basic wrapper called PhysBox, specific to cube bodies.
Example 1: Simple box with vertical boost and spin
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309' Simple box with vertical boost...#Import "<std>"#Import "<mojo>"#Import "<mojo3d>"#Import "<bullet>"Using std..Using mojo..Using mojo3d..Const WALL_WIDTH:Int = 10Const WALL_HEIGHT:Int = 5Class PhysBoxPrivate' PhysParams is used internally to allow creation of box prior to manual placement.' Entities should not be manually positioned after being attached to a RigidBody.' Create PhysBox, manually position PhysBox.model, then call PhysBox.Start ().Class InitParamsField mass:FloatField group:ShortField mask:ShortField box:BoxfEnd' InitParams are only needed for setup, and freed afterwards, hence not added as separate fields.Field init:InitParamsPublic' Public references...Field model:Model ' mojo3d entityField collider:BoxCollider ' Bullet physics colliderField body:RigidBody ' Bullet physics bodyMethod New (width:Float = 1, height:Float = 1, depth:Float = 1, mass:Float = 1, material:Material = Null, group:Int = 1, mask:Int = 1)' Store setup params for PhysBox.Start ()init = New InitParamsinit.mass = massinit.group = groupinit.mask = maskinit.box = New Boxf (-width * 0.5, -height * 0.5, -depth * 0.5, width * 0.5, height * 0.5, depth * 0.5)If Not materialmaterial = New PbrMaterial (Color.Red)Endifmodel = Model.CreateBox (init.box, 1, 1, 1, material)EndMethod Start (sleeps:Bool = True)collider = New BoxCollider (model)body = New RigidBody (model)collider.Box = init.boxbody.Mass = init.massbody.CollisionGroup = init.groupbody.CollisionMask = init.mask' Disables sleeping -- NOT generally desirable outside of demos, as most bodies should settle...' Recommended only for player-controlled bodies in general!If Not sleepsbody.btBody.setActivationState (bullet.DISABLE_DEACTIVATION)Endifinit = NullEndMethod Move (x:Float, y:Float, z:Float)model.Move (x, y, z)EndMethod Rotate (pitch:Float, roll:Float, yaw:Float, localspace:Bool = False)model.Rotate (pitch, roll, yaw, localspace)EndEnd' Main Game class...Class Game Extends Window' 3D scene references...Field scene:SceneField cam:CameraField light:Light' Physics box representing ground...Field ground:PhysBox' Player box...Field player:PhysBox' Changeable ground and player sizes...Field ground_size:Float = 7.5Field player_size:Float = 1.0' App init/scene creation...Method New (title:String, width:Int, height:Int, flags:WindowFlags)Super.New (title, width, height, flags) ' Create windowSeedRnd (Millisecs ()) ' Init random seedSwapInterval = 1 ' V-sync onCreateScene (player_size, ground_size) ' WAAAAAAAHHH! (Parameters = player size, ground size.)EndMethod CreateScene:Void (player_size:Float, ground_size:Float)' Set up 3D scene stuff...scene = Scene.GetCurrent ()cam = New Cameralight = New Lightground = New PhysBox (ground_size, 1, ground_size, 0, New PbrMaterial (Color.Green * 0.25))player = New PhysBox (player_size, player_size, player_size, 1, New PbrMaterial (Color.Silver))scene.AmbientLight = Color.White * 0.75scene.ClearColor = Color.Sky * 0.75' Camera setup...cam.FOV = 100cam.Move (0, 5, -12.5)cam.Near = 0.01cam.Far = 1000' Light setup...light.CastsShadow = Truelight.Range = 1000light.Move (0, 20, 10)' Point things at ground centre...cam.PointAt (player.model)light.PointAt (ground.model)' Position player (see method StartPos () below for explanation)...player.Move (0, StartPos (), 0)' Start physics...ground.Start () ' Sleep by defaultplayer.Start (False) ' Never sleepEnd' Helper, sets player start position...Method StartPos:Float ()' Returns start position: half player size plus half ground height...Return (player_size * 0.5) + 0.5EndMethod Boost (pb:PhysBox, force:Float = 0.25)' Apply force to all boxes in list...Local vec:Vec3f = New Vec3f (0.0, force, 0.0)pb.body.ApplyImpulse (vec)EndMethod Spin (pb:PhysBox, torque:Float = 0.01)Local vec:Vec3f = New Vec3f (torque, torque, torque)pb.body.ApplyTorqueImpulse (vec)End' Checks if out of range (Y lower than -20); if so, scales down, and when box is' eventually small enough, deletes it. Increase Scale multiplier (0.75) to remove more' slowly (0.0 to 0.999)...Method BoxLost:Bool (pb:PhysBox)If pb.model.Y < -10pb.model.Scale = pb.model.Scale * 0.75' Return True when too small...If pb.model.Scale.x < 0.01Return TrueEndifEndifReturn FalseEndMethod UpdateGame ()cam.PointAt (player.model)' Check box position/reset if too far down or R key hit...If Keyboard.KeyHit (Key.R) Or BoxLost (player)player.model.Destroy ()player = New PhysBox (player_size, player_size, player_size, 1, New PbrMaterial (Color.Silver))player.Move (0, StartPos (), 0)player.Start (False)EndifIf Keyboard.KeyDown (Key.Space)Boost (player)EndifIf Keyboard.KeyDown (Key.Enter)Spin (player, -0.01) ' Give bias towards camera!Endif' Quit...If Keyboard.KeyHit (Key.Escape)App.Terminate ()Endif' Shadows on/off...If Keyboard.KeyHit (Key.S)light.CastsShadow = Not light.CastsShadowEndifEnd' Helper...Method ShadowText:Void (canvas:Canvas, s:String, x:Float, y:Float)canvas.Color = Color.Blackcanvas.DrawText (s, x + 1, y + 1)canvas.Color = Color.Whitecanvas.DrawText (s, x, y)EndMethod RenderText (canvas:Canvas)ShadowText (canvas, "FPS: " + App.FPS, 20.0, 20.0)ShadowText (canvas, "S to toggle shadows", 20.0, 40.0)ShadowText (canvas, "SPACE to boost player", 20.0, 80.0)ShadowText (canvas, "ENTER to spin player while in air", 20.0, 100.0)ShadowText (canvas, "R to reset player", 20.0, 120.0)EndMethod OnRender (canvas:Canvas) OverrideUpdateGame () ' Process controls/game worldRequestRender () ' Ask window to render itselfscene.Update () ' Update scene AND PHYSICS; IMPORTANT!scene.Render (canvas) ' Render sceneRenderText (canvas) ' Add textEndEndFunction Run3D (title:String, width:Int, height:Int, flags:WindowFlags = WindowFlags.Center)New AppInstanceNew Game (title, width, height, flags)App.Run ()EndFunction Main ()Run3D ("3D Scene", 960, 540, WindowFlags.Center) ' 1/4 HD!' Run3D ("3D Scene", 1920, 1080, WindowFlags.Fullscreen)EndJanuary 16, 2018 at 1:46 am #12876Example 2: Wall of boxes
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398' Wall of boxes...#Import "<std>"#Import "<mojo>"#Import "<mojo3d>"#Import "<bullet>"Using std..Using mojo..Using mojo3d..Const WALL_WIDTH:Int = 10Const WALL_HEIGHT:Int = 5Class PhysBoxPrivate' PhysParams is used internally to allow creation of box prior to manual placement.' Entities should not be manually positioned after being attached to a RigidBody.' Create PhysBox, manually position PhysBox.model, then call PhysBox.Start ().Class InitParamsField mass:FloatField group:ShortField mask:ShortField box:BoxfEnd' InitParams are only needed for setup, and freed afterwards, hence not added as separate fields.Field init:InitParamsPublic' Public references...Field model:Model ' mojo3d entityField collider:BoxCollider ' Bullet physics colliderField body:RigidBody ' Bullet physics bodyMethod New (width:Float = 1, height:Float = 1, depth:Float = 1, mass:Float = 1, material:Material = Null, group:Int = 1, mask:Int = 1)' Store setup params for PhysBox.Start ()init = New InitParamsinit.mass = massinit.group = groupinit.mask = maskinit.box = New Boxf (-width * 0.5, -height * 0.5, -depth * 0.5, width * 0.5, height * 0.5, depth * 0.5)If Not materialmaterial = New PbrMaterial (Color.Red)Endifmodel = Model.CreateBox (init.box, 1, 1, 1, material)EndMethod Start (sleeps:Bool = True)collider = New BoxCollider (model)body = New RigidBody (model)collider.Box = init.boxbody.Mass = init.massbody.CollisionGroup = init.groupbody.CollisionMask = init.mask' Disables sleeping -- NOT generally desirable outside of demos, as most bodies should settle...' Recommended only for player-controlled bodies in general!If Not sleepsbody.btBody.setActivationState (bullet.DISABLE_DEACTIVATION)Endifinit = NullEndMethod Move (x:Float, y:Float, z:Float)model.Move (x, y, z)EndMethod Rotate (pitch:Float, roll:Float, yaw:Float, localspace:Bool = False)model.Rotate (pitch, roll, yaw, localspace)EndEnd' Main Game class...Class Game Extends Window' Er, camera speed logic is a bit weird/unnecessary...Const CAMERA_SPEED_NORMAL:Float = 0.05 ' Normal camera movementConst CAMERA_SPEED_BOOST:Float = 4.0 ' Camera boost with Shift keyField cam_speed:Float = 1.0 ' Camera forward speed' 3D scene references...Field scene:SceneField cam:CameraField light:Light' Physics box representing ground...Field ground:PhysBox' List of PhysBoxes...Field boxes:List <PhysBox>' App init/scene creation...Method New (title:String, width:Int, height:Int, flags:WindowFlags)Super.New (title, width, height, flags) ' Create windowSeedRnd (Millisecs ()) ' Init random seedSwapInterval = 1 ' V-sync onCreateScene (15) ' WAAAAAAAHHH! (Parameter = ground size.)EndMethod CreateScene:Void (ground_size:Float = 100)' Set up 3D scene stuff...scene = Scene.GetCurrent ()ground = New PhysBox (ground_size, 1, ground_size, 0, New PbrMaterial (Color.Green * 0.25))cam = New Cameralight = New Lightscene.AmbientLight = Color.White * 0.75scene.ClearColor = Color.Sky * 0.75' Camera setup...cam.FOV = 100cam.Move (0, 5, -15)cam.Near = 0.01cam.Far = 1000' Light setup...light.CastsShadow = Truelight.Range = 1000light.Move (0, 20, 10)' Point things at ground centre...cam.PointAt (ground.model)light.PointAt (ground.model)' Create list of PhysBoxes...boxes = New List <PhysBox>' Build wall of PhysBoxes...BuildWall (WALL_WIDTH, WALL_HEIGHT)' Start ground physics...ground.Start ()EndMethod BuildWall (width:Int, height:Int)' Creates bunch of PhysBoxes and adds to internal list (boxes)...For Local y:Int = 0 Until heightFor Local x:Int = 0 Until widthLocal color:Color = New Color (Rnd (0.4, 1.0), Rnd (0.4, 1.0), Rnd (0.4, 1.0))' Create new PhysBox...Local pb:PhysBox = New PhysBox (1, 1, 1, 1, New PbrMaterial (color))' Position PhysBox...pb.Move (x - (width / 2.0), y + 1, 0)' Add to list...boxes.Add (pb)' Start its physics...pb.Start (False) ' False = RigidBody doesn't sleep at restNextNextEndMethod DropBox ()' Drops red boxes into scene...Local pb:PhysBox = New PhysBox (1, 1, 1, 1, New PbrMaterial (Color.Red))pb.Move (0, 50, 0)boxes.Add (pb)pb.Start ()EndMethod BumpWall (force:Float = 0.25)' Apply force to all boxes in list...Local vec:Vec3f = New Vec3f (0.0, force, 0.0)For Local pb:PhysBox = Eachin boxespb.body.ApplyImpulse (vec)NextEnd' Checks if out of range (Y lower than -20); if so, scales down, and when box is' eventually small enough, deletes it. Increase Scale multiplier (0.75) to remove more' slowly (0.0 to 0.999)...Method UpdateBoxes ()For Local pb:PhysBox = Eachin boxesIf pb.model.Y < -20pb.model.Scale = pb.model.Scale * 0.75' Remove when too small...If pb.model.Scale.x < 0.01pb.model.Destroy ()boxes.Remove (pb)EndifEndifNextEndMethod UpdateGame ()' Check box position/remove if too far down...UpdateBoxes ()' Drop red boxes into scene...If Keyboard.KeyDown (Key.D)DropBox ()Endif' Give wall of boxes a bump!If Keyboard.KeyDown (Key.Space)BumpWall ()Endif' Quit...If Keyboard.KeyHit (Key.Escape)App.Terminate ()Endif' Shadows on/off...If Keyboard.KeyHit (Key.S)light.CastsShadow = Not light.CastsShadowEndif' Rebuild wall...If Keyboard.KeyHit (Key.R)For Local pb:PhysBox = Eachin boxespb.model.Destroy ()boxes.Remove (pb)NextBuildWall (WALL_WIDTH, WALL_HEIGHT)Endif' Speed booster...If Keyboard.KeyDown (Key.LeftShift)cam_speed = CAMERA_SPEED_BOOSTElsecam_speed = 1.0Endif' Forward...If Keyboard.KeyDown (Key.A)cam.Move (0.0, 0.0, CAMERA_SPEED_NORMAL * cam_speed)Endif' Back...If Keyboard.KeyDown (Key.Z)cam.Move (0.0, 0.0, -CAMERA_SPEED_NORMAL * cam_speed)Endif' Rotation...If Keyboard.KeyDown (Key.Left)cam.Rotate (0.0, 1.0, 0.0)EndifIf Keyboard.KeyDown (Key.Right)cam.Rotate (0.0, -1.0, 0.0)EndifIf Keyboard.KeyDown (Key.Up)cam.Rotate (1.0, 0.0, 0.0, True)EndifIf Keyboard.KeyDown (Key.Down)cam.Rotate (-1.0, 0.0, 0.0, True)EndifEnd' Helper...Method ShadowText:Void (canvas:Canvas, s:String, x:Float, y:Float)canvas.Color = Color.Blackcanvas.DrawText (s, x + 1, y + 1)canvas.Color = Color.Whitecanvas.DrawText (s, x, y)EndMethod RenderText (canvas:Canvas)ShadowText (canvas, "FPS: " + App.FPS, 20.0, 20.0)ShadowText (canvas, "A/Z + Cursors to move camera", 20.0, 40.0)ShadowText (canvas, "SHIFT to boost", 20.0, 60.0)ShadowText (canvas, "R to rebuild wall", 20.0, 100.0)ShadowText (canvas, "S to toggle shadows", 20.0, 120.0)ShadowText (canvas, "SPACE to boost boxes", 20.0, 140.0)ShadowText (canvas, "D to drop more boxes into scene", 20.0, 160.0)ShadowText (canvas, "Boxes: " + boxes.Count (), 20.0, 200.0)EndMethod OnRender (canvas:Canvas) OverrideUpdateGame () ' Process controls/game worldRequestRender () ' Ask window to render itselfscene.Update () ' Update scene AND PHYSICS; IMPORTANT!scene.Render (canvas) ' Render sceneRenderText (canvas) ' Add textEndEndFunction Run3D (title:String, width:Int, height:Int, flags:WindowFlags = WindowFlags.Center)New AppInstanceNew Game (title, width, height, flags)App.Run ()EndFunction Main ()Run3D ("3D Scene", 960, 540, WindowFlags.Center) ' 1/4 HD!' Run3D ("3D Scene", 1920, 1080, WindowFlags.Fullscreen)EndJanuary 16, 2018 at 1:55 am #12877Try turning off shadows in second demo if lots of boxes cause slowdown on your system!
January 16, 2018 at 3:20 am #12878Make boxes bouncy by adding this in PhysBox.Start:
Monkey123456' Try 0.25 to 0.95. Lower may have no effect, while anything > 0.5 just keeps bouncing around. Over 1.0 is... interesting (try 1.1 and wait a few seconds!)' Effects are likely dependent on mass, too, so play with that in New PhysBox, defaults to 1.0 above...body.Restitution = 0.5January 16, 2018 at 3:34 am #12879Also, note that red boxes dropped via “D” in wall sample may stop responding when Space is pressed — this is intentional, and is the default behaviour of RigidBody — bodies well away from others will auto-sleep until hit again to reduce processing.
Pass False to Start () call in DropBoxes to disable sleeping!
January 16, 2018 at 10:20 am #12882Cool samples!
January 16, 2018 at 1:16 pm #12891Very cool stuff, thanks for adding!
January 17, 2018 at 3:43 am #12898Thanks, hopefully it might help others start playing with this stuff!
January 18, 2018 at 9:00 pm #12999This sample adds a lintel or “top bit” to the wall, and finally adds shooting via Space bar!
Demonstrates how to orientate a bullet from the camera (or gun) and apply impulse force.
Code’s getting a bit messy, but hey-ho.
Example 3: Wall of boxes and SHOOTING ACTION!
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482'Wall of boxes and SHOOTING ACTION!#Import "<std>"#Import "<mojo>"#Import "<mojo3d>"#Import "<bullet>"Using std..Using mojo..Using mojo3d..Const WALL_WIDTH:Int = 15Const WALL_HEIGHT:Int = 10Const BULLET_FORCE:Float = 250.0 ' Too high will go through without colliding!Class PhysBoxPrivate' PhysParams is used internally to allow creation of box prior to manual placement.' Entities should not be manually positioned after being attached to a RigidBody.' Create PhysBox, manually position PhysBox.model, then call PhysBox.Start ().Class InitParamsField mass:FloatField group:ShortField mask:ShortField box:BoxfEnd' InitParams are only needed for setup, and freed afterwards, hence not added as separate fields.Field init:InitParamsPublic' Public references...Field model:Model ' mojo3d entityField collider:BoxCollider ' Bullet physics colliderField body:RigidBody ' Bullet physics bodyMethod New (width:Float = 1, height:Float = 1, depth:Float = 1, mass:Float = 1, material:Material = Null, parent:Entity = Null, group:Int = 1, mask:Int = 1)' Store setup params for PhysBox.Start ()init = New InitParamsinit.mass = massinit.group = groupinit.mask = maskinit.box = New Boxf (-width * 0.5, -height * 0.5, -depth * 0.5, width * 0.5, height * 0.5, depth * 0.5)If Not materialmaterial = New PbrMaterial (Color.Red)Endifmodel = Model.CreateBox (init.box, 1, 1, 1, material, parent)EndMethod Start (sleeps:Bool = True)collider = New BoxCollider (model)body = New RigidBody (model)collider.Box = init.boxbody.Mass = init.massbody.CollisionGroup = init.groupbody.CollisionMask = init.maskbody.Restitution = 0.1 ' Slight bounciness' Disables sleeping -- NOT generally desirable outside of demos, as most bodies should settle...' Recommended only for player-controlled bodies in general!If Not sleepsbody.btBody.setActivationState (bullet.DISABLE_DEACTIVATION)Endifinit = NullEndMethod Move (x:Float, y:Float, z:Float)model.Move (x, y, z)EndMethod Rotate (pitch:Float, roll:Float, yaw:Float, localspace:Bool = False)model.Rotate (pitch, roll, yaw, localspace)EndEndClass Entity Extension' Temp workaround for child-entity.PointAt by DoctorWhoof!' https://github.com/blitz-research/monkey2/issues/324Method WorldPointAt( target:Vec3f,up:Vec3f=New Vec3f( 0,1,0 ) )Local k:=(target-Position).Normalize()Local i:=up.Cross( k ).Normalize()Local j:=k.Cross( i )Basis=New Mat3f( i,j,k )End' Temp workaround for removing parent entities, by Mark:' http://monkeycoder.co.nz/forums/topic/mojo3d-set-to-default-parent/#post-12911Method RemoveParent ()Local matrix:AffineMat4f = MatrixParent = NullMatrix = matrixEndEnd' Main Game class...Class Game Extends Window' Er, camera speed logic is a bit weird/unnecessary...Const CAMERA_SPEED_NORMAL:Float = 0.05 ' Normal camera movementConst CAMERA_SPEED_BOOST:Float = 5.0 ' Camera boost with Shift keyField camera_speed:Float = 1.0 ' Camera forward speed' 3D scene references...Field scene:SceneField camera:CameraField light:LightField cam_piv:ModelField camera_collider:SphereColliderField camera_body:RigidBody' Physics box representing ground...Field ground:PhysBox' List of PhysBoxes...Field boxes:List <PhysBox>' App init/scene creation...Method New (title:String, width:Int, height:Int, flags:WindowFlags)Super.New (title, width, height, flags) ' Create windowSeedRnd (Millisecs ()) ' Init random seedSwapInterval = 1 ' V-sync onCreateScene (50) ' WAAAAAAAHHH! (Parameter = ground size.)EndMethod CreateScene:Void (ground_size:Float = 100)' Set up 3D scene stuff...scene = Scene.GetCurrent ()ground = New PhysBox (ground_size, 1, ground_size, 0, New PbrMaterial (Color.Green * 0.25))' Camera pivot...cam_piv = New Modelcamera = New Camera (cam_piv)light = New Lightscene.AmbientLight = Color.White * 0.75scene.ClearColor = Color.Sky * 0.75' Camera setup...camera.FOV = 100cam_piv.Move (0, 5, -10)camera.Near = 0.01camera.Far = 1000' Camera collision with boxes. Not with ground? Both masses = 0, possibly relevant...' NB. camera radius is half width of default block (1.0), minus a little to allow passing between columns of blocks...camera_collider = New SphereCollider (cam_piv)camera_collider.Radius = 0.45camera_body = New RigidBody (cam_piv)camera_body.Kinematic = Truecamera_body.Mass = 0' Light setup...light.CastsShadow = Truelight.Range = 1000light.Move (-10, 50, -10)' Point things at ground centre...' camera.WorldPointAt (ground.model.Position)light.PointAt (ground.model)' Create list of PhysBoxes...boxes = New List <PhysBox>' Build wall of PhysBoxes...BuildWall (WALL_WIDTH, WALL_HEIGHT)' Start ground physics...ground.Start ()EndMethod BuildWall (width:Int, height:Int, block_mass:Float = 5.0)Local pb:PhysBox' Creates bunch of PhysBoxes and adds to internal list (boxes)...For Local y:Int = 0 Until heightFor Local x:Int = 0 Until widthLocal color:Color = New Color (Rnd (0.4, 1.0), Rnd (0.4, 1.0), Rnd (0.4, 1.0))' Create new PhysBox...pb = New PhysBox (1, 1, 1, block_mass, New PbrMaterial (color))' Position PhysBox...pb.Move (x - (width * 0.5) + 0.5, y + 1, 0)' Add to list...boxes.Add (pb)' Start its physics...pb.Start (False) ' False = RigidBody doesn't sleep at restNextNext' Lintel, or "top bit"...pb = New PhysBox (width, 1, 1, width * height * 1.0, New PbrMaterial (Color.Chromium))pb.Move (0, height + 1, 0)boxes.Add (pb)pb.Start ()EndMethod DropBox ()' Drops pink boxes into scene...Local pb:PhysBox = New PhysBox (1, 1, 1, 1, New PbrMaterial (Color.HotPink))pb.Move (0, 30, 0)boxes.Add (pb)pb.Start ()End' Fire bullets!Method ShootBox (force:Float = BULLET_FORCE)' Create bullet -- mass 5.0 -- as child of' camera, to pick up location/rotation...Local pb:PhysBox = New PhysBox (0.75, 0.75, 0.75, 5, New PbrMaterial (Color.Red), camera)' Move forward in local (child) space by 2 units...pb.model.Move (0.0, 0.0, 2.0)' Get rid of parent/child relationship so bullet isn't' attached to camera any more...pb.model.RemoveParent ()' Add to list of PhysBoxes...boxes.Add (pb)' Start physics...pb.Start ()' Apply force (as impulse, ie. instantly)...' Method carefully derived from typing properties and methods until it worked!' World vector multiplied by 1 in all axes other than z, which is multiplied by force...pb.body.ApplyImpulse (pb.model.Basis * New Vec3f (1.0, 1.0, force))EndMethod UpdateBoxes ()For Local pb:PhysBox = Eachin boxesIf pb.model.Y < -20pb.model.Scale = pb.model.Scale * 0.75' Remove when too small...If pb.model.Scale.x < 0.01pb.model.Destroy ()boxes.Remove (pb)EndifEndifNextEndMethod UpdateGame ()' Check box position/remove if too far down...UpdateBoxes ()If Keyboard.KeyHit (Key.Space)ShootBox ()Endif' Drop boxes...If Keyboard.KeyDown (Key.D)DropBox ()Endif' Quit...If Keyboard.KeyHit (Key.Escape)App.Terminate ()Endif' Shadows on/off...If Keyboard.KeyHit (Key.S)light.CastsShadow = Not light.CastsShadowEndif' Rebuild wall...If Keyboard.KeyHit (Key.R)For Local pb:PhysBox = Eachin boxespb.model.Destroy ()boxes.Remove (pb)NextBuildWall (WALL_WIDTH, WALL_HEIGHT)Endif' Speed booster...If Keyboard.KeyDown (Key.LeftShift)camera_speed = CAMERA_SPEED_BOOSTElsecamera_speed = 1.0Endif' Forward...If Keyboard.KeyDown (Key.A)cam_piv.Move (0.0, 0.0, CAMERA_SPEED_NORMAL * camera_speed)Endif' Back...If Keyboard.KeyDown (Key.Z)cam_piv.Move (0.0, 0.0, -CAMERA_SPEED_NORMAL * camera_speed)Endif' Rotation...If Keyboard.KeyDown (Key.Left)cam_piv.Rotate (0.0, 1.0, 0.0)EndifIf Keyboard.KeyDown (Key.Right)cam_piv.Rotate (0.0, -1.0, 0.0)EndifIf Keyboard.KeyDown (Key.Up)cam_piv.Rotate (1.0, 0.0, 0.0, True)EndifIf Keyboard.KeyDown (Key.Down)cam_piv.Rotate (-1.0, 0.0, 0.0, True)EndifEnd' Helper...Method ShadowText:Void (canvas:Canvas, s:String, x:Float, y:Float, warning:Bool = False)canvas.Color = Color.Blackcanvas.DrawText (s, x + 1, y + 1)If warningcanvas.Color = Color.RedElsecanvas.Color = Color.WhiteEndifcanvas.DrawText (s, x, y)EndMethod RenderText (canvas:Canvas)ShadowText (canvas, "FPS: " + App.FPS, 20.0, 20.0)ShadowText (canvas, "A/Z + Cursors to move camera", 20.0, 40.0)ShadowText (canvas, "SHIFT to boost", 20.0, 60.0)ShadowText (canvas, "S to toggle shadows", 20.0, 80.0)ShadowText (canvas, "R to rebuild wall", 20.0, 120.0)ShadowText (canvas, "D to drop more boxes", 20.0, 140.0)ShadowText (canvas, "SPACE to fire", 20.0, 160.0)ShadowText (canvas, "Boxes: " + boxes.Count (), 20.0, 200.0)EndMethod OnRender (canvas:Canvas) OverrideUpdateGame () ' Process controls/game worldRequestRender () ' Ask window to render itselfscene.Update () ' Update scene AND PHYSICS; IMPORTANT!scene.Render (canvas) ' Render sceneRenderText (canvas) ' Add textEndEndFunction Run3D (title:String, width:Int, height:Int, flags:WindowFlags = WindowFlags.Center)New AppInstanceNew Game (title, width, height, flags)App.Run ()EndFunction Main ()Run3D ("3D Scene", 960, 540, WindowFlags.Center) ' 1/4 HD!' Run3D ("3D Scene", 1920, 1080, WindowFlags.Fullscreen)EndJanuary 21, 2018 at 2:33 pm #13147This is an update to Sample 2, wall of boxes. (No shooting in this one!)
I found that the original gradually slides apart over around 3-4 minutes (faster if no vsync), due to all boxes being forced to remain active, rather than using the default sleep timer to deactivate them.
I’d done this because if they fell asleep, per the default behaviour, pressing Space would not wake them up again, so instead of rising up they would just sit still, unless hit by another box.
This version uses the default sleep timeout, but now when you hit Space, it wakes all boxes so they can rise up, yet it allows the wall to be much more stable by default.
I’ve also added colour-coding, so that you can see the current physics state. If you do nothing, all boxes will turn orange after a few seconds.
Use Space to ‘boost’ all boxes, or D to drop boxes into the scene. (DropBox now uses a timer to limit number of boxes per second.)
Fun challenge: boost the wall off the top of the screen and try land it again without it all collapsing!
Note for non-native English speakers: “nodding off” means falling asleep! This is the state where Bullet Physics is sampling an object’s movement to see if it should be deactivated.
Example 4: Wall of boxes showing sleep states
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480' Wall of boxes with physics-state colour-coding...#Import "<std>"#Import "<mojo>"#Import "<mojo3d>"#Import "<bullet>"Using std..Using mojo..Using mojo3d..Const WALL_WIDTH:Int = 12Const WALL_HEIGHT:Int = 10Class PhysBoxPrivate' PhysParams is used internally to allow creation of box prior to manual placement.' Entities should not be manually positioned after being attached to a RigidBody.' Create PhysBox, manually position PhysBox.model, then call PhysBox.Start ().Class InitParamsField mass:FloatField group:ShortField mask:ShortField box:BoxfEnd' InitParams are only needed for setup, and freed afterwards, hence not added as separate fields.Field init:InitParamsPublic' --------------------------------------------------------' Hacked-in for demo purposes...Const PHYS_SLEEPING:Color = Color.OrangeConst PHYS_NODDING_OFF:Color = Color.YellowConst PHYS_DISABLED:Color = Color.GreyConst PHYS_NEVER_SLEEPS:Color = Color.GreenField color:ColorMethod ChangeColor (color:Color)model.Materials [0] = New PbrMaterial (color)End' --------------------------------------------------------' Public references...Field model:Model ' mojo3d entityField collider:BoxCollider ' Bullet physics colliderField body:RigidBody ' Bullet physics bodyMethod New (width:Float = 1, height:Float = 1, depth:Float = 1, mass:Float = 1, material:Material = Null, group:Int = 1, mask:Int = 1)' Store setup params for PhysBox.Start ()init = New InitParamsinit.mass = massinit.group = groupinit.mask = maskinit.box = New Boxf (-width * 0.5, -height * 0.5, -depth * 0.5, width * 0.5, height * 0.5, depth * 0.5)If Not materialLocal materials:Material[] = model.Materials.Resize (model.Materials.Length + 1)materials[materials.Length - 1] = New PbrMaterial (Color.Red)model.Materials = materialscolor = Color.RedEndifmodel = Model.CreateBox (init.box, 1, 1, 1, material)EndMethod Start (sleeps:Bool = True)collider = New BoxCollider (model)body = New RigidBody (model)collider.Box = init.boxbody.Mass = init.massbody.CollisionGroup = init.groupbody.CollisionMask = init.mask' Disables sleeping -- NOT generally desirable outside of demos, as most bodies should settle...' Recommended only for player-controlled bodies in general!If Not sleepsbody.btBody.setActivationState (bullet.DISABLE_DEACTIVATION)Endif' Don't need init params any more...init = NullEndMethod Move (x:Float, y:Float, z:Float)model.Move (x, y, z)EndMethod Rotate (pitch:Float, roll:Float, yaw:Float, localspace:Bool = False)model.Rotate (pitch, roll, yaw, localspace)EndEnd' Main Game class...Class Game Extends Window' Er, camera speed logic is a bit weird/unnecessary...Const CAMERA_SPEED_NORMAL:Float = 0.05 ' Normal camera movementConst CAMERA_SPEED_BOOST:Float = 4.0 ' Camera boost with Shift keyField cam_speed:Float = 1.0 ' Camera forward speed' 3D scene references...Field scene:SceneField cam:CameraField light:Light' Physics box representing ground...Field ground:PhysBox' List of PhysBoxes...Field boxes:List <PhysBox>' Time between DropBoxes...Field drop_timer:Int' App init/scene creation...Method New (title:String, width:Int, height:Int, flags:WindowFlags)Super.New (title, width, height, flags) ' Create windowSeedRnd (Millisecs ()) ' Init random seedSwapInterval = 1 ' V-sync onCreateScene (15) ' WAAAAAAAHHH! (Parameter = ground size.)drop_timer = Millisecs ()EndMethod CreateScene:Void (ground_size:Float = 100)' Set up 3D scene stuff...scene = Scene.GetCurrent ()ground = New PhysBox (ground_size, 1, ground_size, 0, New PbrMaterial (Color.Green * 0.25))cam = New Cameralight = New Lightscene.AmbientLight = Color.White * 0.75scene.ClearColor = Color.Sky * 0.75' Camera setup...cam.FOV = 100cam.Move (0, 5, -15)cam.Near = 0.01cam.Far = 1000' Light setup...light.CastsShadow = Truelight.Range = 1000light.Move (0, 20, 10)' Point things at ground centre...' cam.PointAt (ground.model)light.PointAt (ground.model)' Create list of PhysBoxes...boxes = New List <PhysBox>' Build wall of PhysBoxes...BuildWall (WALL_WIDTH, WALL_HEIGHT)' Start ground physics...ground.Start ()EndMethod BuildWall (width:Int, height:Int)' Creates bunch of PhysBoxes and adds to internal list (boxes)...For Local y:Int = 0 Until heightFor Local x:Int = 0 Until widthLocal color:Color = New Color (Rnd (0.4, 1.0), Rnd (0.4, 1.0), Rnd (0.4, 1.0))' Create new PhysBox...Local pb:PhysBox = New PhysBox (1, 1, 1, 1, New PbrMaterial (color))pb.color = color' Position PhysBox...pb.Move (x - (width * 0.5) + 0.5, y + 1, 0)' Add to list...boxes.Add (pb)' Start its physics...pb.Start () ' False = RigidBody doesn't sleep at restNextNextEndMethod DropBox (time_between:Int = 200)If Millisecs () - drop_timer < time_betweenReturnElsedrop_timer = Millisecs ()Endif' Drops red boxes into scene...Local pb:PhysBox = New PhysBox (1, 1, 1, 1, New PbrMaterial (Color.Red))pb.color = Color.Redpb.Move (0, 25, 0)boxes.Add (pb)pb.Start ()EndMethod BumpBoxes (force:Float = 0.25)' Wakey-wakey! Boxes may have gone to sleep if left untouched for a few seconds...' Timing test shows If check to be trivially quicker -- 45 ms with check, 65 ms without,' for *10,000* checks! (AMD FX 6350 @ 4.2 Ghz)' For a single iteration, there is therefore no measurable impact in reactivating all' boxes as needed...' Local ticks:Int = Millisecs ()' For Local loop:Int = 1 To 10000For Local pb:PhysBox = Eachin boxesIf pb.body.btBody.getActivationState () <> bullet.ACTIVE_TAGpb.body.btBody.setActivationState (bullet.ACTIVE_TAG)EndifNext' Next' Print Millisecs () - ticks' Apply force to all boxes in list...Local vec:Vec3f = New Vec3f (0.0, force, 0.0)For Local pb:PhysBox = Eachin boxespb.body.ApplyImpulse (vec)NextEndMethod UpdateBoxes ()For Local pb:PhysBox = Eachin boxes' Set colour, dependent on current physics state...Select pb.body.btBody.getActivationState ()Case bullet.ACTIVE_TAGpb.ChangeColor (pb.color)Case bullet.ISLAND_SLEEPINGpb.ChangeColor (PhysBox.PHYS_SLEEPING)Case bullet.WANTS_DEACTIVATIONpb.ChangeColor (PhysBox.PHYS_NODDING_OFF)Case bullet.DISABLE_DEACTIVATIONpb.ChangeColor (PhysBox.PHYS_NEVER_SLEEPS)Case bullet.DISABLE_SIMULATIONpb.ChangeColor (PhysBox.PHYS_DISABLED)End' Checks if out of range (Y lower than -20); if so, scales down, and when box is' eventually small enough, deletes it. Increase Scale multiplier (0.75) to remove more' slowly (0.0 to 0.999)...If pb.model.Y < -20pb.model.Scale = pb.model.Scale * 0.75' Remove when too small...If pb.model.Scale.x < 0.01pb.model.Destroy ()boxes.Remove (pb)EndifEndifNextEndMethod UpdateGame ()' Check box position/remove if too far down...UpdateBoxes ()' Drop red boxes into scene...If Keyboard.KeyDown (Key.D)DropBox ()Endif' Give all boxes a boost!If Keyboard.KeyDown (Key.Space)BumpBoxes ()Endif' Quit...If Keyboard.KeyHit (Key.Escape)App.Terminate ()Endif' Shadows on/off...If Keyboard.KeyHit (Key.S)light.CastsShadow = Not light.CastsShadowEndif' Rebuild wall...If Keyboard.KeyHit (Key.R)For Local pb:PhysBox = Eachin boxespb.model.Destroy ()boxes.Remove (pb)NextBuildWall (WALL_WIDTH, WALL_HEIGHT)Endif' Speed booster...If Keyboard.KeyDown (Key.LeftShift)cam_speed = CAMERA_SPEED_BOOSTElsecam_speed = 1.0Endif' Forward...If Keyboard.KeyDown (Key.A)cam.Move (0.0, 0.0, CAMERA_SPEED_NORMAL * cam_speed)Endif' Back...If Keyboard.KeyDown (Key.Z)cam.Move (0.0, 0.0, -CAMERA_SPEED_NORMAL * cam_speed)Endif' Rotation...If Keyboard.KeyDown (Key.Left)cam.Rotate (0.0, 1.0, 0.0)EndifIf Keyboard.KeyDown (Key.Right)cam.Rotate (0.0, -1.0, 0.0)EndifIf Keyboard.KeyDown (Key.Up)cam.Rotate (1.0, 0.0, 0.0, True)EndifIf Keyboard.KeyDown (Key.Down)cam.Rotate (-1.0, 0.0, 0.0, True)EndifEnd' Helper...Method ShadowText:Void (canvas:Canvas, s:String, x:Float, y:Float, main_color:Color = Color.White)canvas.Color = Color.Blackcanvas.DrawText (s, x + 1, y + 1)canvas.Color = main_colorcanvas.DrawText (s, x, y)EndMethod RenderGUI (canvas:Canvas)ShadowText (canvas, "FPS: " + App.FPS, 20.0, 20.0)ShadowText (canvas, "A/Z + Cursors to move camera", 20.0, 60.0)ShadowText (canvas, "SHIFT to boost camera speed", 20.0, 80.0)ShadowText (canvas, "R to rebuild wall", 20.0, 120.0)ShadowText (canvas, "S to toggle shadows", 20.0, 140.0)ShadowText (canvas, "SPACE to boost boxes", 20.0, 160.0)ShadowText (canvas, "D to drop more boxes into scene", 20.0, 180.0)ShadowText (canvas, "Boxes: " + boxes.Count (), 20.0, 220.0)ShadowText (canvas, "KEY:", Window.Width - 120, 20)ShadowText (canvas, "Nodding off...", Window.Width - 120, 40, PhysBox.PHYS_NODDING_OFF)ShadowText (canvas, "Sleeping", Window.Width - 120, 60, PhysBox.PHYS_SLEEPING)ShadowText (canvas, "Never sleeps", Window.Width - 120, 80, PhysBox.PHYS_NEVER_SLEEPS)ShadowText (canvas, "Disabled", Window.Width - 120, 100, PhysBox.PHYS_DISABLED)ShadowText (canvas, "Other: Active", Window.Width - 120, 120)EndMethod OnRender (canvas:Canvas) OverrideUpdateGame () ' Process controls/game worldRequestRender () ' Ask window to render itselfscene.Update () ' Update scene AND PHYSICS; IMPORTANT!scene.Render (canvas) ' Render sceneRenderGUI (canvas) ' Add textEndEndFunction Run3D (title:String, width:Int, height:Int, flags:WindowFlags = WindowFlags.Center)New AppInstanceNew Game (title, width, height, flags)App.Run ()EndFunction Main ()Run3D ("3D Scene", 960, 540, WindowFlags.Center) ' 1/4 HD!' Run3D ("3D Scene", 1920, 1080, WindowFlags.Fullscreen)EndJanuary 25, 2018 at 3:20 pm #13222These won’t work with latest Github ‘develop’ branch — however, the only change needed should be in OnRender:
From…
scene.Render (canvas, cam)
To…
cam.Render (canvas)
Optionally, in Example 3, you can delete the whole Class Entity Extension and replace…
pb.model.RemoveParent ()
… with:
pb.model.Parent = Null
I’ll update the samples here once the itch.io release includes these changes.
February 10, 2018 at 2:16 am #13595Updated all samples to work with latest Patreon subscriber release/latest Github develop branch.
-
AuthorPosts
You must be logged in to reply to this topic.