jBullet ball control

Ok, I have been trying for a while now and lost all my patience. :confused:

I am trying to make physics controllable ball with jBullet, but I am really stuck. I made input actions and I add force to the ball (based on camera direction), but the ball just starts to strangely float on my terrain and after some time falls through it. I have no clue what could be the problem, everything worked nice with JME-physics (ODE-implementation). Also, there are really no good examples in test package: "jmetest.jbullet". Ok there are TestPhysicsCharacter and TestSimplePhysicsCar but both of them use some underlying methods from jBullet native library, which doesn't really help me since I can't see actual code.

Hopefully, someone can give me some directions about this (I seriously hope that jBullet is not so complicated as it looks like).

I dont know what you are doing wrong here, maybe you show us the code you use. Are you using continuousForce or applyForce? The tests do not use any direct jbullet calls, I dont know what you mean there? In any case you should not touch the jbullet objects directly (like RigidBody etc.) but always use the PhysicsNode / PhysicsCharacter etc. objects methods.



Cheers,

Normen

Ok, I have made simple test case and found out that it actually works quite fine (I am using applyTorque for ball moving, based on input event time and some predefined constant - is there any preferred way to do this?).

Although I still get these weird problems with my level geometry. My level geometry actually extend PhysicsNode and I define its properties in its constructor. Anyway, I am getting lots of "rebuild" messages in console (don't know if this is ok, but should be moved to log or smth IMO). I also found some duplicate code in PhysicsNode which could be bug:


    public PhysicsNode(){
        motionState=createMotionState();
        collisionShape=new BoxCollisionShape(new Vector3f(0.5f,0.5f,0.5f));
        motionState=createMotionState();
        rebuildRigidBody();
    }


Following line is used two times: "motionState=createMotionState();".
Also when I try to use PhysicsDebugGameState I get strange error:


java.lang.NullPointerException
   at com.jmex.jbullet.debug.PhysicsDebugger.drawWireframes(PhysicsDebugger.java:155)
   at com.jmex.jbullet.debug.PhysicsDebugGameState.render(PhysicsDebugGameState.java:137)
   at com.jmex.game.state.GameStateNode.render(GameStateNode.java:83)
   at com.jmex.game.StandardGame.render(StandardGame.java:399)
   at com.jmex.game.StandardGame.run(StandardGame.java:251)
   at java.lang.Thread.run(Unknown Source)


Any idea will be appreciated. :)

No, the double motionstate creation should cause no issues…

You get “rebuild” messages when you change the mass of your PhysicsNode, do you do that often? There still seem to be some issues when rebuilding the PhysicsNode / changing its mass, see the googlecode issues page. For now its safest to set the mass just once in the constructor and not change it afterwards, if everything works you can try and see if it introduces problems changing the mass afterwards.

The issue with the debug state might be caused by the new buffering of wireframe objects. When the PhysicsNode is rebuilt it might be that it loses its reference (monkey_scratches_head added the debug state just recently).



Cheers,

Normen

normen wrote:
The issue with the debug state might be caused by the new buffering of wireframe objects.
It is definitely being caused by the wireframe buffering code I've added :(
normen wrote:
When the PhysicsNode is rebuilt it might be that it loses its reference (monkey_scratches_head added the debug state just recently).
Possibly.

I've looked at the line causing that NullPointerExpcetion and there are a few cases that could be the cause.
The most likely case is the bullet collision object is not convex, concave or polyhedral.

InShadow

Ok I don't have really much time today, since my GF has birthday. Will try to quickly post some code:


public final class GOPlane extends PhysicsNode
{
   private static final long serialVersionUID = 1L;
   
   private Quad quad;
   
   public GOPlane()
   {
      super();
      setMass(0);
      quad = new Quad("GOPlane", 20, 20);
      quad.setModelBound(new BoundingBox());
      quad.updateModelBound();
      
      attachChild(quad);
      
      Quaternion quat = new Quaternion();
      quat.fromAngleNormalAxis(-FastMath.HALF_PI, Vector3f.UNIT_X);

      setLocalTranslation(new Vector3f(20, 0, 20));
      setLocalRotation(quat);
      
      createCollisionShape(ShapeTypes.MESH);
   }
}


This doesn't work fine - I get strange physics behavior + rebuild messages, when I make object of this type and attach it to rootNode and PhysicsSpace.


      Quad quad = new Quad("GOPlane", 20, 20);
      quad.setModelBound(new BoundingBox());
      quad.updateModelBound();

      Quaternion quat = new Quaternion();
      quat.fromAngleNormalAxis(-FastMath.HALF_PI, Vector3f.UNIT_X);

      PhysicsNode pNode = new PhysicsNode(quad, ShapeTypes.MESH, 0);
      pNode.setLocalTranslation(new Vector3f(20, 0, 20));
      pNode.setLocalRotation(quat);

      rootNode.attachChild(pNode);
      physicsSpace.add(pNode);


This other example works completely fine (code should be put in some GameState init). I test it with ball which I make with:


   private void initPlayer()
   {
      Sphere playerSphere = new Sphere("PlayerSphere", 20, 20, 5);
      
      player = new PhysicsNode(playerSphere, ShapeTypes.SPHERE, 10);
      player.setLocalTranslation(20, 20, 20);
      physicsSpace.add(player);
      rootNode.attachChild(player);
      
      player.setRestitution(0);
      player.setFriction(0.1f);
   }


I skipped all the material and other render state definitions...
Error can be seen when the ball is moved with WASD (it starts floating strangely and finally falls through quad).
Controls code:


   private void initInput()
   {
      physicsInput = new InputHandler();
      physicsInput.addAction(new ForwardAction(), InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_W, InputHandler.AXIS_NONE, true);
      physicsInput.addAction(new BackwardAction(), InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_S, InputHandler.AXIS_NONE, true);
      physicsInput.addAction(new LeftAction(), InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_A, InputHandler.AXIS_NONE, true);
      physicsInput.addAction(new RightAction(), InputHandler.DEVICE_KEYBOARD, KeyInput.KEY_D, InputHandler.AXIS_NONE, true);
      
      cam = DisplaySystem.getDisplaySystem().getRenderer().getCamera();
        cam.setFrustumNear(1f);
        cam.setFrustumFar(5000f);
        cam.update();
       
      chaseCam = new ChaseCamera(cam, player);
      chaseCam.setEnableSpring(true);
      //chaseCam.setDampingK(100);
      //chaseCam.setSpringK(100);
      chaseCam.setStayBehindTarget(true);
      //chaseCam.getMouseLook().setMinAscent(0.1f);
      //chaseCam.getMouseLook().setMaxAscent(0.8f);
      chaseCam.setMinDistance(10);
      chaseCam.setMaxDistance(40);
      chaseCam.getMouseLook().setMinRollOut(10);
      chaseCam.getMouseLook().setMaxRollOut(40);
      chaseCam.getMouseLook().setMouseRollMultiplier(2);
      chaseCam.getMouseLook().setSpeed(0.5f);
      //chaseCam.setTargetOffset(new Vector3f(0, 3, 0));
   }

   private class ForwardAction extends InputAction
   {
      public void performAction(InputActionEvent evt)
      {
         addLinearMoveForce(cam.getDirection(), evt, 10000);
      }
   }
   
   private class BackwardAction extends InputAction
   {
      public void performAction(InputActionEvent evt)
      {
         addLinearMoveForce(cam.getDirection(), evt, -10000);
      }
   }
   
   private class LeftAction extends InputAction
   {
      public void performAction(InputActionEvent evt)
      {
         addLinearMoveForce(cam.getLeft(), evt, 10000);
      }
   }
   
   private class RightAction extends InputAction
   {
      public void performAction(InputActionEvent evt)
      {
         addLinearMoveForce(cam.getLeft(), evt, -10000);
      }
   }

   private void addLinearMoveForce(Vector3f direction, InputActionEvent evt, float forceConst)
   {
      Vector3f force = new Vector3f(direction);
      float multVar = evt.getTime() * forceConst;
      force.multLocal(multVar);
      
      if (evt.getTriggerPressed())
      {
         player.applyTorque(force);
      }
   }


If some variables are not declared, you can suspect they are declared as private in GameState head.
Hopefully this can help you find the problem...
About NullPointerException, as I said, I don't have so much time today, so I will try to research this tomorrow... :)

Try taking out the call to super() in the constructor and change setMass(0); to mass=0;, then it should work w/o the rebuilds.

Currently, when you call super() the RigidBody is first built, then when you call setMass() it is rebuilt and finally when you call createCollisionShape() it is rebuilt again.



Cheers,

Normen



Edit:

Uh, and you apply torque to the Node, not linear force, no wonder its spinning :wink:

Ok, call to super() can be omitted, but mass can't be set, since its declared as private and not protected. :confused:

Also, I want the ball to spin, but this is not the problem. :slight_smile: Problem is that is falls through physics object after spinning it around a little. :confused:

Then dont use createCollisionShape() but build it by hand in the constructor and call setMass() as the last thing, then it will rebuild the rigidbody only once.

Again impossible. :slight_smile:

Variable collisionShape is declared as private and setCollisionShape() also calls rebuildRigidBody(). :slight_smile:

Uh, collisionShape is protected? Hmm, gotta change that…



Edit: change is in svn

Not to change the subject entirely, but you also experiencing a problem with a NullPointerException caused by the debugger, yes?

InShadow wrote:
About NullPointerException, as I said, I don't have so much time today, so I will try to research this tomorrow... Smiley

Any luck with a test case / code snippet that will recreate the problem for me?
monkey_scratches_head said:

Any luck with a test case / code snippet that will recreate the problem for me?

Maybe try using some test with debug view on and call setMass() on a PhysicsNode when you press a key.

Ok about this NullPointerException, here is some test case:


      Quad quad = new Quad("GOPlane", 20, 20);
      quad.setModelBound(new BoundingBox());
      quad.updateModelBound();
      
      Quaternion quat = new Quaternion();
      quat.fromAngleNormalAxis(-FastMath.HALF_PI, Vector3f.UNIT_X);
      
      PhysicsNode pNode = new PhysicsNode(quad, ShapeTypes.MESH, 0);
      pNode.setLocalRotation(quat);
      
      CompoundCollisionShape ccShape = new CompoundCollisionShape();
      ccShape.addChildShape(new BoxCollisionShape(pNode), Vector3f.ZERO);
      pNode.setCollisionShape(ccShape);
      
      rootNode.attachChild(pNode);
      physicsSpace.add(pNode);


Seems to me like error happens when object is using CompoundCollisionShape. :)

EDIT:
I have one more suggestion about PhysicsDebugGameState: bind please DRAW_STATE_SWITCH_COMMAND to some other button than backslash (because some of us do our programming on laptops... :)).
InShadow wrote:
Seems to me like error happens when object is using CompoundCollisionShape.
Ha, ha ...yes, that is because the compound and infinite shapes are not yet supported by the debugger.  :-o
Those two cases I was intending to look into (as well as bullet's collision shape hierarchy) after performance trials of converting the code to use the JME wireframe state.  
Short term - I'll put a temporary fix to solve that NullPointer, and include a one time a log line.
InShadow wrote:
I have one more suggestion about PhysicsDebugGameState: bind please DRAW_STATE_SWITCH_COMMAND to some other button than backslash
Sure, I'm open to suggestions for the key binding, it just needs to be a key not already in use.

EDIT:
Sumitted hack to avoid NullPointerException to svn - no logging as there is no easy way to achieve a once only logging

Hey, about DRAW_STATE_SWITCH_COMMAND, it could be anything from F5 to F12 for example. Also, P is for pause, so maybe also physics updates should be paused if P is pressed…

About my collision problems, I found out that if I change empty constructor of PhysicsNode from


    public PhysicsNode(){
        motionState=createMotionState();
        collisionShape=new BoxCollisionShape(new Vector3f(0.5f,0.5f,0.5f));
        motionState=createMotionState();
        rebuildRigidBody();
    }


to


    public PhysicsNode(){
        motionState=createMotionState();
    }


it works really OK. There are no rebuild messages and there is no strange collision behavior, so obviously the problem is when calling rebuildRigidBody() more than once. Could it be possible to change this empty constructor like I have written? Because it is useful only if PhysicsNode is subclassed (there is no practical use of calling it and not supplying any object to it), in any other situation all other constructors are used.
InShadow wrote:
Hey, about DRAW_STATE_SWITCH_COMMAND, it could be anything from F5 to F12 for example.
I'd be happy with F5, does anybody else mind if F5 is used?
InShadow wrote:
Also, P is for pause, so maybe also physics updates should be paused if P is pressed...
The physics simulation is not updated by the PhysicsDebugGameState, it is updated by you.
The update occurs when in your code there is a line similar to:

pSpace.update( tpf );


If you want the behaviour you are describing, when using the PhysicsDebugGameState or DebugGameState replace the previous code with:


if ( pause == false )
{
    pSpace.update( tpf );
}


pause is a field from DebugGameState, a flag of whether the game is paused  :-o

Hmmm... about the default constructor, that I'm sure norman will an idea about that one.

Yeah, the default… I actually just put it in so one can override the PhysicsNode w/o a super() so I guess it would be ok removing almost everything from it, I just shy away from such an easy way to build a non-working, exception throwing object… I'd rather make more things protected to allow easy overriding/replacing of the constructor. Just tell me what you need to access :slight_smile:



I work differently in most projects, not extending but wrapping the object in another object so I can easily replace it and keep my object and methods. For example the switch from jmephysics to jbullet-jme in my private project was quite easy because of that. Thats why I did not think so much about extending PhysicsNode but again, just tell me what you need.



Cheers,

Normen


I'd be happy with F5, does anybody else mind if F5 is used?

F5 is cool IMO.

About PhysicsNode, mass and collisionShape are IMO enough to be declared protected (since their setters all call rebuildRigidBody()). Variable motionState would also be needed as protected, but since it is probably not intended to be used heavily by normal JME programmer, would be cool to maybe change

motionState=createMotionState();

to only

createMotionState();

and make createMotionState() method set motionState variable and leave its access to private.
Or at least, keep setting motionState in empty constructor as I suggested in previous post, and make some comment above the constructor about manual setting of collisionShape and rebuilding rigid body. :)
Actually I would prefer the second one (possible to subclass without super(x, y, z)), but the decision is yours. :)

Oh, and another question: any suggestion about friction and restitution values on colliding objects for some normal rolling and jumping? :)
InShadow said:

Oh, and another question: any suggestion about friction and restitution values on colliding objects for some normal rolling and jumping? :)

Yes, try what works best for you ;)