[PloobsEngine] Tutorial 3 – Physics and Picking


This tutorial will teach you about the PloobsEngine Physics and the Picking Systems. (tutorials series here)

This tutorial wont talk about specific physic API usage, for this check this, it will talk about how we integrate the physic API and how to use its features

Physics Engine

physics engine is a computer software that provides an approximate simulation of certain physical systems, such as rigid body dynamics (including collision detection), soft body dynamics, and fluid dynamics. It offen also offer facilities like collision detection and triggers.

The PloobsEngine uses external Physics APIs to do these tasks, internally it just sees the some interfaces like IPhysicWorld and IPhysicObject. The engine does not care about what implementations or engine you are using.

For convenience we implemented these interface for Bepu Physic API (The old engine version implemented JigLibX also), so our explanation will focus on Bepu implementation, but the information provided can be usefull for any other.

When creating a PloobsEngine Screen, you must provide the right Implementation for the IPhysicWorld interface.

The following piece of code shows how to do this setup for Bepu:

protected override void SetWorldAndRenderTechnich(out IRenderTechnic renderTech, out IWorld world)
        {
            BepuPhysicWorld bepuWorld = new BepuPhysicWorld(-0.98f,true,1,true);
            world = new IWorld(bepuWorld, new SimpleCuller());

            BepuPhysicWorld.ApplyHighStabilitySettings(bepuWorld);

            DeferredRenderTechnicInitDescription desc = DeferredRenderTechnicInitDescription.Default();
            desc.UseFloatingBufferForLightMap = true;
            desc.BackGroundColor = Color.CornflowerBlue;
            renderTech = new DeferredRenderTechnic(desc);
        }

First we create the BepuPhysicWorld, the first parameter is the gravity (in Y, if you want to change it to other axis, you need to access the BepuWorld.Space properties — see Bepu Examples for how to change the internal API parameters), second and third we pass some parameters to tell the physics how to deal with the time (use fixed or variable timestep and a correction multiplier). The last parameter tells the physic unit to use Multi-Threading.

The class BepuPhysicWorld has some static methods, like the one showed before, to control how acurate the physic simulation will be.

Physics APIs interfaces with the user in very diferent ways. It is impossible for us to create an interface that will fill all possibilities. Our option was to always give the user the possibility to access the specific API object and interact with it directely.

The following code shows how to recover (simple cast =P) the API specific object

                BepuPhysicWorld physicWorld;
                physicWorld = this.World.PhysicWorld as BepuPhysicWorld;
                System.Diagnostics.Debug.Assert(physicWorld != null);

The BepuPhysicWorld has a SPACE property, the same exposed by the Bepu guys. Remember that the engine does not knows about things you do using this object directely.

The Introduction Demos (you can download it here) provides some examples about how to access specific Bepu features.

 

Principal Physic Object Types

As you sow in previous demos, everytime we create an IObject we need to provide a IPhysicObject implementation. It is responsible for representing the IObject in the Physic World, all collision simulation, collision detection, triggers detection and raycasts wll considere these entities.

The engine provides lots implementation of IPhysicObject for Bepu (you always can make your own), the most used are:

Ghost Object

Represents an object that DOES NOT HAVE a representation in the PhysicWorld, so it does not collides, nothing detects it …. We normaly use this when we know that the object will not interact with anything at all.

                ///Create A Ghost Object (Does Not Collides)
                {
                    ///Create a Simple Model
                    SimpleModel model = new SimpleModel(factory, "..\\Content\\Model\\ball");
                    model.SetTexture(factory.CreateTexture2DColor(1,1,Color.Purple), TextureType.DIFFUSE);
                    ///Create a Physic Object
                    IPhysicObject pobj = new GhostObject(new Vector3(50, 13f, 50), Matrix.Identity, Vector3.One * 5);
                    pobj.isMotionLess = true;
                    ///Create a shader
                    IShader nd = new DeferredNormalShader();
                    ///Create a Material
                    IMaterial material = new DeferredMaterial(shader);
                    ///Create a an Object that englobs everything and add it to the world
                    IObject obj = new IObject(material, model, pobj);
                    this.World.AddObject(obj);

                }

The parameters describes where the object is (position, scale and orientation).

Triangle Mesh

Triangle Meshes represents the IObject using the IModelo triangles data. So it is VERY precise, but the collision processing is a bit slower. Triangle Meshes CANT move in the scene, so it is common used for things like houses, stopped cars…

     ///Create a Triangle Mesh
       {
            IModelo sm = new SimpleModel(factory,"..\\Content\\Model\\cenario");
            ///Create a Physic Object
            IPhysicObject pi = new TriangleMeshObject(sm, Vector3.Zero,Matrix.Identity,Vector3.One,MaterialDescription.DefaultBepuMaterial());
            pi.isMotionLess = true;
            ///Create a shader
            IShader shader = new DeferredNormalShader();
            ///Create a Material
            IMaterial mat = new DeferredMaterial(shader);
            ///Create a an Object that englobs everything and add it to the world
            IObject obj4 = new IObject(mat, sm,pi);
            this.World.AddObject(obj4);
        }

To create a Triangle mesh you need to pass the IModelo, the position, scale, orientation and an object describing physic properties like static and dynamic friction.

Capsule Objects

This implementation consider the object as a capsule (like this). It is very faster than Triangle Meshes, we always recomend using Capsules, box and spheres when possible. The parameters passed are very intuitive, the visual studio intelisene will show all you need =P

private void CreateThrash(Vector3 pos)
        {
            ///Create a Simple Model
            ///thash Has a Texture Inside the .X file (not the texture itself, just the name of it)
            ///The simpleModel will find it and will attach to the Object
            IModelo model = new SimpleModel(GraphicFactory,"..\\Content\\Model\\trash");

            ///Create a Physic Object
            IPhysicObject pobj = new CapsuleObject(pos,2,1,10,Matrix.Identity,MaterialDescription.DefaultBepuMaterial());
            pobj.isMotionLess = true;
            ///Create a shader
            IShader nd = new DeferredNormalShader();

            ///Create a Material
            IMaterial material = new DeferredMaterial(nd);
            ///Create a an Object that englobs everything and add it to the world
            IObject obj = new IObject(material, model, pobj);
            this.World.AddObject(obj);
            objects.Add(obj);
        }

Box Objects

Consider the object as a box (can have diferent lenghts on each axis)

private void CreateBox(Vector3 pos)
        {
            ///Create a Simple Model
            SimpleModel model = new SimpleModel(GraphicFactory,"..\\Content\\Model\\cubo");
            model.SetTexture(GraphicFactory.CreateTexture2DColor(1,1, StaticRandom.RandomColor()), TextureType.DIFFUSE);

            ///Create a Physic Object
            IPhysicObject pobj = new BoxObject(pos,1,1,1, 10, new Vector3(2),Matrix.Identity,MaterialDescription.DefaultBepuMaterial());
            pobj.isMotionLess = true;
            ///Create a shader
            IShader nd = new DeferredNormalShader();

            ///Create a Material
            IMaterial material = new DeferredMaterial(nd);
            ///Create a an Object that englobs everything and add it to the world
            IObject obj = new IObject(material, model,pobj);
            this.World.AddObject(obj);
            objects.Add(obj);
        }

Ball Object

Consider the object as a ball.

private void CreateBall(Vector3 pos)
        {
            ///Create a Simple Model
            SimpleModel model = new SimpleModel(GraphicFactory,"..\\Content\\Model\\ball");
            model.SetTexture(GraphicFactory.CreateTexture2DColor(1, 1, StaticRandom.RandomColor()), TextureType.DIFFUSE);            

            ///Create a Physic Object
            IPhysicObject pobj = new SphereObject(pos,1, 10,1,MaterialDescription.DefaultBepuMaterial());
            pobj.isMotionLess = true;
            ///Create a shader
            IShader nd = new DeferredNormalShader();

            ///Create a Material
            IMaterial material = new DeferredMaterial(nd);
            ///Create a an Object that englobs everything and add it to the world
            IObject obj = new IObject(material, model, pobj);
            this.World.AddObject(obj);
            objects.Add(obj);
        }

Character Object

Character Object is a special implementation that handle character movimentation =P. It does not use classic physic (newton laws …) directly. (see here a little discussion about this)

The engine provides a Character Object (that has method for directely changing the character moviment), the user must create it CharacterControllerInput (that must handle the keyboard and controls the character, we show how to create a simple one in the Introduction Demos).

The following code is just a piece of the CharacterControllerInput:

public void Update(float dt )
        {
            if (IsActive)
            {
                KeyboardState keyboardInput = Keyboard.GetState();                

                Vector2 totalMovement = Vector2.Zero;
                Vector2 mv = VectorUtils.ToVector2(Characterobj.FaceVector);
                Vector2 lado = VectorUtils.Perpendicular2DNormalized(mv);

                ///TO SLIDE MOVEMENT USE
                //totalMovement += lado;
                //totalMovement -= lado;

                if (keyboardInput.IsKeyDown(AheadKey))
                {
                    totalMovement -= mv;
                }
                if (keyboardInput.IsKeyDown(BackKey))
                {
                    totalMovement += mv;
                }
                if (keyboardInput.IsKeyDown(RightKey))
                {                 

                    Characterobj.RotateYByAngleDegrees(-1);
                }
                if (keyboardInput.IsKeyDown(LeftKey))
                {
                    Characterobj.RotateYByAngleDegrees(1);
                }
                if (totalMovement == Vector2.Zero)
                    Characterobj.MoveToDirection(Vector2.Zero);
                else
                {
                    Characterobj.MoveToDirection(Vector2.Normalize(totalMovement));
                }

                //Jumping
                if (keyboardInput.IsKeyDown(JumpKey))
                {
                    Characterobj.Jump();
                }

            }
        }

Check the Intruduction Demos (MovementScreen.cs) for more info. In the future we will show how to integrate this with animated character (the physic side does not change)

Raycast and Collision Detection

Performing Raycast is always necessary in a game (normaly Artifical inteligence or moviment algorithm). To do this in the engine is very easy:

this.World.PhysicWorld.SegmentIntersect(Ray,PREDICATEFUNCTION,maxDistance)

where Ray is the XNA Ray object describing the start position and the direction of the raycast. PREDICATEFUNCTION is a filter function that filters what objects must be considered in the raycast (function that recieves an IPhysicObject as a parameter and returns a bool, you can use Lambda expression here). MaxDistance is the maximum distance between the ray start position and the objects.

You can detect collision with an IObject in two ways, the first is calling the IPhysicWorld method as follows:

this.World.PhysicWorld.DetectCollisions(IPHYSICOBJECT, LIST_WITH_COLLISION);

where IPHYSICOBJECT is the PhysicObject you want to know about its collision and LIST_WITH_COLLISION is the list where the IPhysicWorld will put the object.

Or use the Bepu events. (easier =P)

            {
                SimpleModel sm = new SimpleModel(factory, "..\\Content\\Model\\cubo");
                sm.SetTexture(factory.CreateTexture2DColor(1,1, Color.White), TextureType.DIFFUSE); ///set a texture generated procedurally
                BoxObject pi = new BoxObject(new Vector3(100, 40, 0), 1,1,1, 25,new Vector3(100, 10, 100),Matrix.Identity,MaterialDescription.DefaultBepuMaterial());
                ///Add a handler that will handle the colision =P
                pi.Entity.CollisionInformation.Events.InitialCollisionDetected += new Events_InitialCollisionDetected;
                DeferredNormalShader shader = new DeferredNormalShader();
                IMaterial mat = new DeferredMaterial(shader);
                IObject obj3 = new IObject(mat, sm, pi);
                this.World.AddObject(obj3);
            }

There are lots of other events, check it =P

The handler has this appearence:

void Events_InitialCollisionDetected(BEPUphysics.Collidables.MobileCollidables.EntityCollidable sender, BEPUphysics.Collidables.Collidable other, BEPUphysics.NarrowPhaseSystems.Pairs.CollidablePairHandler pair)
        {
            IObject send = BepuEntityObject.RecoverObjectFromEntity(sender.Entity);
            IObject obj = BepuEntityObject.RecoverObjectFromCollidable(other);
            ////your handler code here
        }

Look that you have to use the BepuEntityObject static method to recover the IObject from the specific Bepu Objects.

PICKING

Picking is the act of selecting an 3D object in the screen using the mouse (clicking in an object =P). This operation requires some unprojection and raytest calculation. People often use this in games, so we decided to add this in the engine.

Before telling how picking works, we need to introduce you a new engine feature calle Screen Updateable. It is a way to create an object that will habe its Update method called everytime every frame (until the attached screen is removed).

To create an object like this, you need to extend from IScreenUpdateable (very simple abstract class). Then you have to add it to the screen using the method Screen.AddScreenUpdateable(picking). You can stop or start a IScreenUpdateable using its protected method Stop and Start. Check the code for more info.

The Picking is a IScreenUpdateable, every frame it checks if the mouse is clicked, and if so, he tries to find the object (if any) that you reached.

The following sample shows how to use it:

  • Create and add the Picking to the IScreen
  • Attach an event (can be right mouse click, left mouse click or just mouse position (without clicking anything))
  • When picking founds an object, it call the event OnPick passing an object called SegmentInfo, where you can find information about the reached object
 public class PickingScreen : IScene
    {        

        protected override void SetWorldAndRenderTechnich(out IRenderTechnic renderTech, out IWorld world)
        {
            world = new IWorld(new BepuPhysicWorld(-9.8f, true), new SimpleCuller());

            DeferredRenderTechnicInitDescription desc = DeferredRenderTechnicInitDescription.Default();
            desc.UseFloatingBufferForLightMap = true;
            desc.BackGroundColor = Color.CornflowerBlue;
            renderTech = new DeferredRenderTechnic(desc);
        }

        protected override void InitScreen(PloobsEngine.Engine.GraphicInfo GraphicInfo, PloobsEngine.Engine.EngineStuff engine)
        {
            base.InitScreen(GraphicInfo, engine);
            engine.IsMouseVisible = true;
        }

        protected override void LoadContent(PloobsEngine.Engine.GraphicInfo GraphicInfo, PloobsEngine.Engine.GraphicFactory factory, IContentManager contentManager)
        {
            base.LoadContent(GraphicInfo, factory, contentManager);

            Picking picking = new Picking(this);
            this.AddScreenUpdateable(picking);
            picking.OnPickedLeftButton += new OnPicked(onPick);
            /// .... cameras, object, lights .... usual stuffs

        }

        SegmentInterceptInfo ri = null;
        string objName;

        ///
        /// Called when picking happens
        ///
        ///
The segment intercept info.
        void onPick(SegmentInterceptInfo SegmentInterceptInfo)
        {

            IObject obj = SegmentInterceptInfo.PhysicObject.ObjectOwner; ///recovering the picked object !!!!!
            if(obj != null)
            {
                objName = obj.Name;
                shouldDraw = true;
                this.ri = SegmentInterceptInfo;
            }
        }

    }

 

The code for all showed demos can be found in our Introduction Demos package, you can download it here
 Any doubts, critics, suggestions, pls go to our forum or leave a comment here.

See you guys =P

Links

 

, , , , , , , , ,

  1. #1 by http://www.gtech-chauffage.com/clshoes.asp on 28 de maio de 2017 - 9:32 am

    it comes within a beautiful container appears awesome still it is slightly mini consistent to my parents wrist but it seems ideal just want this ended up being further

  2. #2 by amazon reviewer on 28 de maio de 2017 - 9:35 am

    Thanks to my father who told me concerning this blog, this web site is in fact amazing.

  3. #3 by Jesse on 28 de maio de 2017 - 9:49 am

    Most calorie and nutrition trackers only track a
    handful of nutrients, with no vitamin and minerals.

  4. #4 by chauffeur on 28 de maio de 2017 - 10:12 am

    Fine way of explaining, and nice post to get information about my
    presentation focus, which i am going to deliver in college.

  5. #5 by John on 28 de maio de 2017 - 10:30 am

    Hello! Do you use Twitter? I’d like to follow you if that would
    be ok. I’m absolutely enjoying your blog and look forward to new updates.

  6. #6 by donate on 28 de maio de 2017 - 10:39 am

    Hi to all, how is the whole thing, I think every one is getting more from this site, and your views are
    pleasant designed for new users.

1 1.245 1.246 1.247
(não será publicado)