Added bullet physics to jme3

I just uploaded the first version of jme3 physics into the jme3 branch.

Many things have changed integration-wise compared to jbullet-jme but for the user most things stay the same. The integration changes are not quite done yet and some things are still done a bit quick-n-dirty but I will continue to work on it anyway, just thought people might want to have a look :slight_smile:



For the user, most notably the automatic collision shape generation via the PhysicsNode constructor is currently gone. I will replace the functionality with static helper methods in CollisionShape that can create CollisionShapes from various types of jme3 objects. The constructor thing seemingly confused some people.



On the system side, I can happily announce that the physics will be able to run on another thread (already working somehow) and that it will be possible to have multiple physics spaces (not working yet due to queue sharing, PhysicsSpaces already get registered on a ThreadLocal variable).



Now I'm going to rename the GDE packages and clean that sucker up, lets see when I get to commit that :slight_smile: Hurrah, final package name!



Cheers,

Normen



Edit: btw its the /src/jme3_jbullet folder you have to add to your source path :slight_smile:

Excellent! I love you very much lol.

This means a lot… I will check the code now  :smiley:

Just awesome!

Great work, Normen!  This is now three huge feature additions for jME3.  (Android, GDE, Physics)

sbook said:

Great work, Normen!  This is now three huge feature additions for jME3.  (Android, GDE, Physics)

You forgot beautiful graphics!

Great work !!!

I've seen there's a PhisicalsVehicleNode implementation that seems to be useful for my project.

Do you think to add a test class to show the usage of this?

Bye

truman said:

Great work !!!
I've seen there's a PhisicalsVehicleNode implementation that seems to be useful for my project.
Do you think to add a test class to show the usage of this?
Bye

Yes I do, but the vehicle implementation was not completely done when I first uploaded it. You can look at the example in jbullet-jme to see how its done, its basically the same (if you come to port it to jme3, please post it here!). Vehicles *should* work now, did not check though.
Cheers,
Normen
normen said:

truman said:

Great work !!!
I've seen there's a PhisicalsVehicleNode implementation that seems to be useful for my project.
Do you think to add a test class to show the usage of this?
Bye

Yes I do, but the vehicle implementation was not completely done when I first uploaded it. You can look at the example in jbullet-jme to see how its done, its basically the same (if you come to port it to jme3, please post it here!). Vehicles *should* work now, did not check though.
Cheers,
Normen

I'll try to do it !!!

I tried to do it, but probably I'm not able  :D



here's my code:


import java.util.logging.Logger;

import com.jme3.app.SimplePhysicsApplication;
import com.jme3.asset.TextureKey;
import com.jme3.bounding.BoundingBox;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.MeshCollisionShape;
import com.jme3.bullet.nodes.PhysicsNode;
import com.jme3.bullet.nodes.PhysicsVehicleNode;
import com.jme3.light.DirectionalLight;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.renderer.queue.RenderQueue.ShadowMode;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Sphere;
import com.jme3.texture.Texture;
import com.jme3.texture.Texture.WrapMode;





public class TestPhysicsCar extends SimplePhysicsApplication {
    private static final Logger logger = Logger.getLogger(TestPhysicsCar.class
            .getName());
    
    //the new player object
    private PhysicsVehicleNode player;
    //protected InputHandler input;
    

    // Our camera object for viewing the scene
    //The chase camera, this will follow our player as he zooms around the level
    //private ChaseCamera chaser;


   private PhysicsNode tb;

    /**
     * Main entry point of the application
     */
    public static void main(String[] args) {
        TestPhysicsCar app = new TestPhysicsCar();
        // We will load our own "fantastic" Flag Rush logo. Yes, I'm an artist.
      app.start();
    }
   @Override
   public void simpleInitApp() {
        // initialize the camera
        cam.setFrustumPerspective(45.0f, 640 / 480, 1,
                5000);
        cam.setLocation(new Vector3f(0,20,0));
        
        /** Signal that we've changed our camera's location/frustum. */
        cam.update();      
        
        //Time for a little optimization. We don't need to render back face triangles, so lets
        //not. This will give us a performance boost for very little effort.
        //rootNode.setCullHint(CullHint.Never);
        
        //Add terrain to the scene
        setupFloor();
        //Light the world
        buildLighting();
        //add the force field fence
        //buildEnvironment();
        //Add the skybox
       // buildSkyBox();
        //Build the player
        buildPlayer();
        //build the chase cam
        //buildChaseCamera();
        //build the player input
        //buildInput();

        // update the scene graph for rendering
       // rootNode.updateGeometricState();

   }


   public void setupFloor() {
      Material mat = manager.loadMaterial("rockwall.j3m");
      mat.selectTechnique("OldGpu");
      mat.getTextureParam("m_DiffuseMap").getValue().setWrap(WrapMode.Repeat);
      mat.getTextureParam("m_NormalMap").getValue().setWrap(WrapMode.Repeat);
      mat.getTextureParam("m_ParallaxMap").getValue()
            .setWrap(WrapMode.Repeat);
      Box floor = new Box(Vector3f.ZERO, 3200, 1f, 3200);
      floor.scaleTextureCoordinates(new Vector2f(320, 320));
      Geometry floorGeom = new Geometry("Floor", floor);
      floorGeom.setMaterial(mat);
      floorGeom.updateModelBound();
      floorGeom.setShadowMode(ShadowMode.Off);
        tb=new PhysicsNode(floorGeom,new MeshCollisionShape(floorGeom.getMesh()),0);
        rootNode.attachChild(tb);
        tb.setLocalTranslation(new Vector3f(0f,-6,0f));
        tb.updateModelBound();
        tb.updateGeometricState();
        getPhysicsSpace().addQueued(tb);
   }




    

    private void buildPlayer() {
        float r=2f;
       float x=8,z=10,y=2;
        Material matBox = new Material(manager, "plain_texture.j3md");
        TextureKey keyBox = new TextureKey("signpost_color.jpg", true);
        keyBox.setGenerateMips(true);
        Texture texBox = manager.loadTexture(keyBox);
        texBox.setMinFilter(Texture.MinFilter.Trilinear);
        matBox.setTexture("m_ColorMap", texBox);
        Material mat = new Material(manager, "plain_texture.j3md");
        TextureKey key = new TextureKey("Monkey.jpg", true);
        key.setGenerateMips(true);
        Texture tex = manager.loadTexture(key);
        tex.setMinFilter(Texture.MinFilter.Trilinear);
        mat.setTexture("m_ColorMap", tex);
        //box stand in
        Box b = new Box( new Vector3f(0,r*2+2, 0),x-4.5f,y,z);
        
        Geometry g= new Geometry("Box",b);
        g.setMaterial(matBox);

        g.getMesh().setBound(new BoundingBox());
      
        //set the vehicles attributes (these numbers can be thought
        //of as Unit/Second).
        player = new PhysicsVehicleNode(g, new BoxCollisionShape(new Vector3f( x,y,z)));
        float stiffness=200.0f;//200=f1 car
        float compValue=1.0f; //(lower than damp!)
        float dampValue=2.0f;

        player.setMass(1);
        player.updatePhysicsState();
        player.setSuspensionStiffness(stiffness);
        player.setSuspensionCompression(compValue);
       player.setDamping(dampValue,compValue);
       // player.setDirty(true);
        Vector3f wheelDirection=new Vector3f(0,-1,0);
        Vector3f wheelAxle=new Vector3f(-1,0,0);
//         Create four wheels and add them at their locations
        Sphere wheelSphere=new Sphere(8,8,r);
        Geometry wheels1=new Geometry("wheels1",wheelSphere);
        wheels1.setMaterial(mat);
        wheels1.setModelBound(new BoundingBox());
        wheels1.updateModelBound();
        player.addWheel(wheels1, new Vector3f(-x/2,r,-z/2),
                        wheelDirection, wheelAxle, 0.2f, r, true);
       // ((DesktopAssetManager) manager).clearCache();
        Geometry wheels2=new Geometry("wheels1",wheelSphere);
        wheels2.setMaterial(mat);
        wheels2.setModelBound(new BoundingBox());
        wheels2.updateModelBound();
        player.addWheel(wheels2, new Vector3f(x/2,r,-z/2),
                        wheelDirection, wheelAxle, 0.2f, r, true);
        //((DesktopAssetManager) manager).clearCache();
        Geometry wheels3=new Geometry("wheels1",wheelSphere);
        wheels3.setMaterial(matBox);
        wheels3.setModelBound(new BoundingBox());
        wheels3.updateModelBound();
        player.addWheel(wheels3, new Vector3f(x/2,r,z/2),
                        wheelDirection, wheelAxle, 0.2f, r, false);
        //((DesktopAssetManager) manager).clearCache();
        Geometry wheels4=new Geometry("wheels1",wheelSphere);
        wheels4.setMaterial(matBox);
        wheels4.setModelBound(new BoundingBox());
        wheels4.updateModelBound();
        player.addWheel(wheels4, new Vector3f(-x/2,r,z/2),
                        wheelDirection, wheelAxle, 0.2f, r, false);
        player.updatePhysicsState();
       rootNode.attachChild(player);
        //player.setLocalTranslation(new Vector3f(0,10f, 0));
       getPhysicsSpace().addQueued(player);
       player.activate();
       player.accelerate(100);

    }
    

    /**
     * creates a light for the terrain.
     */
    private void buildLighting() {
        /** Set up a basic, default light. */
        DirectionalLight light = new DirectionalLight();
        light.setColor(new ColorRGBA(1.0f, 1.0f, 1.0f, 1.0f));
        light.setDirection(new Vector3f(1,-1,0));
        rootNode.addLight(light);

      
    }

  /*  private void buildChaseCamera() {
        Vector3f targetOffset = new Vector3f();
        targetOffset.y = ((BoundingBox)((Geometry) player.getChild(0)).getMesh().getBound()) .getYExtent()* 1.5f;

        chaser = new ChaseCamera(cam, player);
      chaser.registerWithDispatcher(inputManager);

       // chaser.setMaxDistance(8);
       // chaser.setMinDistance(2);
    }*/


    public void simpleUpdate(float tpf) {
        //update the chase camera to handle the player moving around.
       //chaser.updateLogicaState(tpf);
       Vector3f location=new Vector3f(player.getLocalTranslation());
       location.y+=100;
       location.x+=10;
       location.z+=10;
       System.out.println(player.getLocalTranslation());
       cam.setLocation(location);
       cam.lookAt(player.getLocalTranslation(), Vector3f.UNIT_Y);
      //player.updatePhysicsState();
        //player.apply(1);
      // player.accelerate(10);
       //player.applyCentralForce( new Vector3f(10,0,0));
      // player.updateLogicalState(tpf);
      // player.setLinearVelocity(new Vector3f(10,0,0));
        //We don't want the chase camera to go below the world, so always keep
        //it 2 units above the level.
        if(cam.getLocation().y < 2) {
            cam.getLocation().y +=  2;
        }

    }

   
}



vehicle doesn't want to move. Could you give me any hint?
thanks
bye

Umm, try to add the vehicle with add() instead of addQueued()? Does it at least fall down like it used to in jme2? :slight_smile:

normen said:

Umm, try to add the vehicle with add() instead of addQueued()? Does it at least fall down like it used to in jme2? :)


tried without results, it doesn't fall down on groud, it remains at 3 pixels on Y axe

I added your code to my jme3 and it fell down and the suspension seemed to work as well, still the vehicele did not move… I will try around with it more, but now I have got to sleep :slight_smile:

normen said:

I added your code to my jme3 and it fell down and the suspension seemed to work as well, still the vehicele did not move.. I will try around with it more, but now I have got to sleep :)


I don't if it's normal, but if you call addWheel method without calling an updatePhysics on object you'll receive a NullPointerException (vehicle is null)
bye
truman said:

I don't if it's normal, but if you call addWheel method without calling an updatePhysics on object you'll receive a NullPointerException (vehicle is null)
bye

Only when you use addQueued, doesn't it?

Edit: Ah, no, I know what's the problem. Anyway, I will have to put some more work into it, CharacterNodes do not work properly yet as well, and as said vehicles were not checked yet.. But thanks for your file, will help me a lot.
normen said:

truman said:

I don't if it's normal, but if you call addWheel method without calling an updatePhysics on object you'll receive a NullPointerException (vehicle is null)
bye

Only when you use addQueued, doesn't it?

Edit: Ah, no, I know what's the problem. Anyway, I will have to put some more work into it, CharacterNodes do not work properly yet as well, and as said vehicles were not checked yet.. But thanks for your file, will help me a lot.

thanks for your great work:-D
bye

w00t… physics supported in another thread!!!



I think I love you.



Of all the issues jme has, the monolithic threading model bothers me the most… I have the jme3 branch checked out, I'll implement my wrapper classes and see how much still works



(or doesn't…)

ebola said:

w00t... physics supported in another thread!!!

I think I love you.

Lol, dont love me before you know about the implications :P
It basically means that you will have to care about what values are set from what thread, does not exactly make things easier ;)

Threaded programming isnt a difficult proposition in itself.



I read a book once…





Mostly, volatile solves many problems, synchronized{} ISN'T a performance killer (any more), double checked locking does work (in 1.5+), and doesn't save enough time to be worth it any more.



The issues I had (jme 1 & 2) were usually caused from within a native call handles (more ODE than GL, from memory).



The code between 2 & 3 seems to be quite different though… It'll take a while to sort it out and get back to a really simple drive sim (flag rush physics, I'm afraid, with a slightly more detailed physics model - switchable, of course).

Multithreading is misunderstood by many programmers nowadays. The secret is not to have separate logical loops going on in parallel and have them communicate about their state (Like one thread AI, one Sound, one Graphics) but to have one big loop for logic and then just multithread within that loop when its appropriate. Each for loop in the main loop might be a chance for MT’ing, this scales way better to multiple cores and does not break up code logic. See this thread where this was discussed before.



The way bullet is MT’ed in jme3 right now is exactly the opposite of that sadly, since the bullet implementation allows me only two ways to multithread properly currently:



Solution 1:

Like most people/users of bullet do: they just let the physics update loop run completely separate on another thread and then atomize the location/rotation of the single physics objects (via the MotionState). This way the scenegraph loop can safely get and set these values. However all physics parameters have still to be changed from the physics thread and adding/removing of objects has to happen via the physics thread.

This is how it is in jme3 now when you multithread physics, the updatePhysics() is called by a separate thread and you have to care for yourself that you access the physics parameters of a PhysicsNode from the physics thread and the “rest” of the parameters via the opengl thread.



Good about this way:

  • Because only the single objects are atomized, the scenegraph and physics update loops will seldom block each other as they're probably working in different "areas" of the scenegraph most of the time. This means maximum thread efficiency.



    Bad about this way:
  • Because only the single objects are atomized, the state of the physics objects that is applied to the jme objects is not assured to be from the same physics step for all objects. But as said most people handle it this way and in effect its not noticeable.
  • User has to care about what to modify from what thread





    Solution 2:

    The other solution comes closer to the "one loop /w parallel" way I described before. Here the physics update would happen in parallel to rendering, after the users changes have been made in the update() call. This way the loop logic is still maintained: the user can set and change values in physics and scenegraph objects before render() and physicsUpdate() are called in parallel.

    I want to put this into jme3 and set it as the "standard" form of multithreading since it will provoke less problems for users.



    Good about this way:
  • Seamless for user, no syncing problems



    Bad about this way:
  • render and physics thread will have to wait for each other, lowering the multithreading efficiency
  • user update() takes away cpu cycles for rendering and physics, not just render time



    In the future, I hope that when I have integrated the native bullet version with OpenCL support etc. the second solution or even the "no multithreading" solution becomes the standard way of doing things as the boost in performance from the graphics card should beat the boost from using another CPU by far.



    Cheers,

    Normen

So now that I just elaborated on it, I created a new base class for physics Applications that allows selecting between the three different types (sequential, detached, parallel). I will commit it as soon as googlecode works again -.-