Physics Simulation can be framerate dependent when using native Bullet

Hello everyone,

I recently noticed that rigid bodies move at framerate dependent speeds if I frequently change their rotation via setPhysicsRotation. This is most noticable when I set the rotation from the update loop, but also happens when I set the rotation from one of the physics tick callbacks.

I’m writing this mainly because I’m unsure if this is expected behaviour along the lines of “don’t set physics location/rotation explicitly, especially from the update loop”, or this is a bug. Especially because this problem does only occur when I use native bullet, not when I use JBullet.

I included a test case below for you to try. It shows a simple solar system made out of spheres orbiting at constant speed. Use the F1 key to toggle VSync on or off to influence the framerate and see how the speed of the simulation changes. I tested this on my 64 bit Linux and Windows 7 systems with the latest stable version of the SDK.

Thanks for your time.

[java]
package test;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.PhysicsTickListener;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.bullet.objects.PhysicsRigidBody;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Sphere;
import java.util.logging.Level;
import java.util.logging.Logger;

/**

  • Native Bullet Framerate-Independence Test Case.

  • This test case shows how (when using native bullet) the physics simulation

  • can become framerate dependent under certain situations.

  • How to use this example:

  • Run this file and use F1 to toggle vsync on and off.

  • This should create a noticeable change in framerate

  • and influence the simulation.

  • @author christoph
    */
    public class Main extends SimpleApplication implements PhysicsTickListener, ActionListener {
    public static void main(String[] args) {
    Logger.getLogger("").setLevel(Level.SEVERE);
    Main app = new Main();
    app.start();
    }

    private BulletAppState bulletAppState;
    private static final Vector3f orbitalCenter = new Vector3f(0, -5, -50);
    private static final float orbitalSpeed = 10f;

    @Override
    public void simpleInitApp() {
    // Initialize Bullet.
    bulletAppState = new BulletAppState();
    bulletAppState.setThreadingType(BulletAppState.ThreadingType.SEQUENTIAL);
    bulletAppState.setBroadphaseType(PhysicsSpace.BroadphaseType.DBVT);
    stateManager.attach(bulletAppState);
    bulletAppState.getPhysicsSpace().setGravity(Vector3f.ZERO);
    bulletAppState.getPhysicsSpace().addTickListener(this);

     // Create a "solar system" of physical spheres.
     for(int i = 0; i < 10; ++i) {
         final float radius = 1f;
         Sphere s = new Sphere(10, 10, radius);
         Geometry geom = new Geometry("Sphere " + i, s);
         Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
         mat.setColor("Color", ColorRGBA.randomColor());
         geom.setMaterial(mat);
         rootNode.attachChild(geom);
         
         RigidBodyControl rigidBody = new RigidBodyControl(new SphereCollisionShape(radius), 1f);
         bulletAppState.getPhysicsSpace().add(rigidBody);
         geom.addControl(rigidBody);
         rigidBody.setPhysicsLocation(getStartPosition(i));
     }
     
     // Use F1 to toggle vsync.
     inputManager.addMapping("ToggleVsync", new KeyTrigger(KeyInput.KEY_F1));
     inputManager.addListener(this, "ToggleVsync");
    

    }

    private Vector3f getStartPosition(int index) {
    float angle = indexFastMath.PI * 0.5f;
    float radius = index
    5f;
    return new Vector3f(FastMath.cos(angle), 0, FastMath.sin(angle)).mult(radius).add(orbitalCenter);
    }

    @Override
    public void simpleUpdate(float tpf) {
    // This is the real culprit. Comment this loop out
    // to make everything framerate-independent again.
    // You can also move this into one of the tick listers,
    // which will make the effect less noticable, but not go away.
    for(PhysicsRigidBody rigidBody : stateManager.getState(BulletAppState.class).getPhysicsSpace().getRigidBodyList()) {
    Quaternion rotationToAdd = new Quaternion().fromAngleAxis(tpf*FastMath.PI, Vector3f.UNIT_Y);
    Quaternion newRotation = rigidBody.getPhysicsRotation().mult(rotationToAdd).normalizeLocal();
    rigidBody.setPhysicsRotation(newRotation);
    }
    }

    @Override
    public void simpleRender(RenderManager rm) {
    }

    public void prePhysicsTick(PhysicsSpace space, float tpf) {
    // Make the spheres orbit the center with a speed of 10 meters per second.
    for(PhysicsRigidBody rigidBody : space.getRigidBodyList()) {
    Vector3f offsetFromCenter = rigidBody.getPhysicsLocation().subtract(orbitalCenter);
    Vector3f tangent = new Vector3f(-offsetFromCenter.z, 0, offsetFromCenter.x);
    Vector3f velocity = tangent.normalizeLocal().multLocal(orbitalSpeed);
    rigidBody.setLinearVelocity(velocity);
    rigidBody.setPhysicsRotation(Quaternion.IDENTITY);
    }
    }

    public void physicsTick(PhysicsSpace space, float tpf) {
    }

    public void onAction(String name, boolean isPressed, float tpf) {
    if(name.equals(“ToggleVsync”) && !isPressed) {
    settings.setVSync(!settings.isVSync());
    setSettings(settings);
    restart();
    }
    }
    }
    [/java]

Obviously it changes if you set it explicitly and don’t account for the tpf you set them at… But as the docs say, setting the rotation explicitly doesn’t have anything to do with physics really, for the physics thats as if you “beamed” the objects around, in exactly the timing you do. So if you want to do that you a) have to account for the timing you do it at and b) have to expect weird results in the physics computations.

I understand that setting the location/rotation of a rigid body myself means I bypass the physics simulation and essientially teleport the object around. My real problem is that I don’t understand why I cannot change a rigid body’s rotation in the prePhysicsTick() callback without influenceing its percieved speed on screen. I would assume that changing the object’s rotation just before the physics update, while leaving its position untouched, would not influence the velocity integration step and the resulting change in position.

In the example, I just set the rotation in the update() call, because this makes the change in velocity more pronounced. Moving the rotation update into the prePhysicsTick() still gives me different results based on the current framerate.
I mean, I’m fine if it turns out that setting a rigid body’s rotation directly is just something you shouldn’t do, it just clashes so strongly with my intuition on how physics engines should work. :stuck_out_tongue:

Also, I really need to be able to take full control over an objects rotation for the character control I am currently writing. In my current project, the player is moving through variable gravity and I need a way to align him with the changing gravity vector, so that his feet always point “down”. And because I don’t want the player to fall over while walking, I need to set his angular factor to 0. Therefore I just have no other way of influenceing the player’s rotation without setting it directly. And for obvious reasons I just can’t have the player’s walking speed be framerate-dependent.

I would be very grateful, if someone had any recommendation on how to solve this problem.

Maybe read the manual and javadoc…? Intuition alone won’t get you far.

I read all of the documention and I do realize there is a warning to never use setPhysicsRotation() for physical objects. But I also did a lot of research before starting to implement my character control and the fact that the BetterCharacterControl itself makes heavy use of setPhysicsRotation() made me interprete this warning as “don’t use setPhysicsRotation(), unless you know what you are doing”. While I admit that this might have been a misguided idea and I might not know what I am doing, that’s exactly why I need to ask for help.

I am still unsure how to implement my character control and reliably control its rotation and would be quite grateful if anybody had some insight into this. I would really want to avoid making the player kinematic, since my game is quite physics heavy and the player should be influenced by the same forces as any other object. Also I can’t just go back to JBullet, also because my game is so physics heavy and I do need the performance of native bullet.

Maybe take a look at the BetterCharacterControl source then?

That’s the thing, I already did. In fact, I used the BetterCharacterControl as a reference when I started implementing my own character control. But this was some time ago, so I looked at it again today and it turns out the BetterCharacterControl suffers from the same problems that my control does. In case anybody wants to try it out for themselves, I have attached another test case below where I reproduce this framerate dependent behaviour on BetterCharacerControls under native Bullet. Again, JBullet works as expected.

I guess I will have to live with this for now. But I will try to look into it and find out what is happening and also do some more research into implementing physical character controls in bullet. But from what I found so far, it seems far more common to use kinematic character controls.

[java]
package test;

import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.PhysicsTickListener;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
import com.jme3.bullet.control.BetterCharacterControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.input.KeyInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Sphere;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

/**

  • Native Bullet Framerate-Independence Test Case #2.

  • This test case shows how the BetterCharacterControl can

  • give significantly different results when using native bullet

  • and how its movement becomes framerate dependent.

  • How to use this example:

    • Run this file with JBullet. Notice how all objects
  • orbit nicely at the designated orbital speed.
    
  • Character controls are represented by red spheres,
    
  • normal rigid bodies by blue spheres.
    
  • Use F1 to toggle vsync on and off. This does
    
  • not influence the simulation.
    
    • Run this file with native Bullet. While the normal rigid
  • bodies still orbit nicely, the red character controls
    
  • move at some random speed, depending on the framerate.
    
  • Use F1 to toggle vsync on and off. This will noticeably
    
  • change the speed of the character control. Normal rigid
    
  • bodies remain unaffected.
    
  • @author christoph
    */
    public class Main extends SimpleApplication implements PhysicsTickListener, ActionListener {
    public static void main(String[] args) {
    Logger.getLogger("").setLevel(Level.SEVERE);
    Main app = new Main();
    app.start();
    }

    private static final Vector3f orbitalCenter = new Vector3f(0, -5, -50);
    private static final float orbitalSpeed = 5f;
    private static final int numObjects = 8;

    // I need a way to read the position of the character control.
    private static class EvenBetterCharacterControl extends BetterCharacterControl {
    public EvenBetterCharacterControl(float radius, float height, float mass) {
    super(radius, height, mass);
    }

      public Vector3f getPhysicsLocation() {
          return rigidBody.getPhysicsLocation();
      }
    

    }

    private ArrayList<EvenBetterCharacterControl> characters = new ArrayList<EvenBetterCharacterControl>();
    private ArrayList<RigidBodyControl> rigidBodies = new ArrayList<RigidBodyControl>();

    @Override
    public void simpleInitApp() {
    BulletAppState bulletAppState = new BulletAppState();
    bulletAppState.setThreadingType(BulletAppState.ThreadingType.SEQUENTIAL);
    bulletAppState.setBroadphaseType(PhysicsSpace.BroadphaseType.DBVT);
    stateManager.attach(bulletAppState);
    bulletAppState.getPhysicsSpace().setGravity(Vector3f.ZERO);
    bulletAppState.getPhysicsSpace().addTickListener(this);

      for(int i = 0; i &lt; numObjects; ++i) {
          final float radius = 1f;
          boolean isCharacter = i % 2 == 0;
          Sphere s = new Sphere(10, 10, radius);
          Geometry geom = new Geometry("Sphere " + i, s);
          Material mat = new Material(assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
          mat.setColor("Color", isCharacter ? ColorRGBA.Red : ColorRGBA.Blue);
          geom.setMaterial(mat);
          rootNode.attachChild(geom);
    
          if(isCharacter) {
              EvenBetterCharacterControl character = new EvenBetterCharacterControl(0.1f, 1.8f, 1f);
              bulletAppState.getPhysicsSpace().add(character);
              geom.addControl(character);
              character.warp(getStartPosition(i));
              characters.add(character);
          }
          else {
              RigidBodyControl rigidBody = new RigidBodyControl(new SphereCollisionShape(radius), 1f);
              bulletAppState.getPhysicsSpace().add(rigidBody);
              geom.addControl(rigidBody);
              rigidBody.setPhysicsLocation(getStartPosition(i));
              rigidBodies.add(rigidBody);
          }
      }
    
      // Use F1 to toggle vsync.
      inputManager.addMapping("ToggleVsync", new KeyTrigger(KeyInput.KEY_F1));
      inputManager.addListener(this, "ToggleVsync");
      
      flyCam.setMoveSpeed(10f);
    

    }

    private Vector3f getStartPosition(int index) {
    float angle = 2f * FastMath.PI * index / numObjects;
    float radius = 20f;
    return new Vector3f(FastMath.cos(angle), 0, FastMath.sin(angle)).multLocal(radius).addLocal(orbitalCenter);
    }

    private Vector3f getOffsetFromCenter(Vector3f position) {
    return position.subtract(orbitalCenter);
    }

    private Vector3f getTangent(Vector3f position) {
    return getOffsetFromCenter(position).crossLocal(Vector3f.UNIT_Y).normalizeLocal();
    }

    private Vector3f getVelocity(Vector3f position) {
    return getTangent(position).multLocal(orbitalSpeed);
    }

    // Use a small, constant gravity vector, so that the
    // character control can align itself.
    private Vector3f getGravity() {
    return new Vector3f(0, -0.0001f, 0);
    }

    @Override
    public void simpleUpdate(float tpf) {
    }

    @Override
    public void simpleRender(RenderManager rm) {
    }

    public void prePhysicsTick(PhysicsSpace space, float tpf) {
    // Put the character controls and rigid bodies into an orbit.
    for(EvenBetterCharacterControl control : characters) {
    Vector3f position = control.getPhysicsLocation();
    control.setGravity(getGravity());
    control.setViewDirection(getTangent(position));
    control.setWalkDirection(getVelocity(position));
    }
    for(RigidBodyControl rigidBody : rigidBodies) {
    rigidBody.setGravity(getGravity());
    rigidBody.setLinearVelocity(getVelocity(rigidBody.getPhysicsLocation()));
    }
    }

    public void physicsTick(PhysicsSpace space, float tpf) {
    }

    public void onAction(String name, boolean isPressed, float tpf) {
    if(name.equals(“ToggleVsync”) && !isPressed) {
    settings.setVSync(!settings.isVSync());
    setSettings(settings);
    restart();
    }
    }
    }
    [/java]

I don’t know much about bullet but I’m familiar with physics engines. Whenever I see someone set the linear velocity directly it always makes me wonder. If acceleration and velocity are integrated before the position is integrated then forcing the linear velocity in the prephysics tick (before integration) might make it frame rate dependent. Assumes there is acceleration.

At any rate, this line looks suspect for other reasons I can’t quite put my finger on:
rigidBody.setLinearVelocity(getVelocity(rigidBody.getPhysicsLocation()));

…but it will exhibit some frame rate dependent behavior because you are forcing the velocity based on position which may jump more or less depending on framerate but presumably the linear velocity you calculate is not taking this into effect. It would kind of need to aim at where it expects to be next frame, if you know what I mean. Or something to that effect. It smells badly but my brain is too sleepy to work out exactly why.

I wonder if you’d be better off simulating orbit with acceleration. Apply a force at tangent to the orbit and apply a ‘gravity’ force towards the center.

The orbit will still be frame rate dependent, though, because constraints like this are instantaneous.

I assume you are running physics in the update loop instead of on a separate thread? The real solution is to not let the physics frame rate vary. Run it in a separate thread (supported by JME’s bullet integration) and force the frame rate to a fixed amount.

The real question to me is why JBullet isn’t showing this issue. Then again, maybe I’m imagining an issue that is there in both and your issue is completely different. :slight_smile:

1 Like
@pspeed said: I don't know much about bullet but I'm familiar with physics engines. Whenever I see someone set the linear velocity directly it always makes me wonder. If acceleration and velocity are integrated before the position is integrated then forcing the linear velocity in the prephysics tick (before integration) might make it frame rate dependent. Assumes there is acceleration.

At any rate, this line looks suspect for other reasons I can’t quite put my finger on:
rigidBody.setLinearVelocity(getVelocity(rigidBody.getPhysicsLocation()));

This line is actually the one that behaves correctly.^^ In the example, I force some BetterCharacterControls and some “normal” rigid bodies into an orbit at constant speed by setting their walk direction/linear velocity directly. But the “normal” rigid bodies are just there for reference, the point of this is to show that the BetterCharacterControls behave differently and seem to move at a different speed, because setPhysicsRotation() is called on them in the character control before the physics tick.

...but it will exhibit some frame rate dependent behavior because you are forcing the velocity based on position which may jump more or less depending on framerate but presumably the linear velocity you calculate is not taking this into effect. It would kind of need to aim at where it expects to be next frame, if you know what I mean. Or something to that effect. It smells badly but my brain is too sleepy to work out exactly why.

[…]

I assume you are running physics in the update loop instead of on a separate thread? The real solution is to not let the physics frame rate vary. Run it in a separate thread (supported by JME’s bullet integration) and force the frame rate to a fixed amount.

I was under the impression that bullet is always running at a fixed frame rate, no matter whether it is run in parallel or after the update in the render thread. This is also why I would assume that setting velocity/rotation just before the physics tick should not create framerate dependent behaviour, since it is also done at a fixed framerate and at a point where no interpolation has to be done, since the simulation is actually stepping. Also I get the exact same issues no matter whether I run bullet in parallel or sequential mode.
I wonder if you'd be better off simulating orbit with acceleration. Apply a force at tangent to the orbit and apply a 'gravity' force towards the center.

The orbit will still be frame rate dependent, though, because constraints like this are instantaneous.

That's true, the orbit will surely not be stable, more or less so depending on the integration method used. When simple euler integration is used, I would assume that the rigid bodies spiral out from the center relatively quickly. I don't know much about bullet's internals, but again, this should at least give deterministic results, since the physics simulation is stepping at a fixed rate.

I guess the best (and most physically correct) way to simluate an orbit would be to set an initial tangent velocity and then let a constant gravitational pull towards the center create a stable orbit.

The real question to me is why JBullet isn't showing this issue. Then again, maybe I'm imagining an issue that is there in both and your issue is completely different. :)
Yeah, the main thing that bugs me about this issue is that I'm not sure if this is in some form expected "undefined" behaviour for messing with physics rotation and velocity directly or there is a bug somewhere.

Edit: I guess that character controls are just not regular physical objects and have to break some rules. Therefore I assume there is no way around creating some weird behaviour when implementing a character control. But I would really like to create reliable, predictable weirdness, not this annoying framerate dependent weirdness. :smiley:

If physics ran always at a constant framerate then you’d have little use for a ‘tpf’ passed all over the place. Certainly in the case where it is not threaded the physics ‘step’ will be normally the same as the update loop frequency.

I’m quite sure there has to be ways to set a fixed step size (even if framerate varies) and I know with certainty that it’s possible to run physics on a separate thread.

@pspeed said: If physics ran always at a constant framerate then you'd have little use for a 'tpf' passed all over the place. Certainly in the case where it is not threaded the physics 'step' will be normally the same as the update loop frequency.

I’m quite sure there has to be ways to set a fixed step size (even if framerate varies) and I know with certainty that it’s possible to run physics on a separate thread.

Bullet runs at a fixed frame rate, how the update method is called has few to do with that.

@normen said: Bullet runs at a fixed frame rate, how the update method is called has few to do with that.

Ah, I see… it (potentially) does make up frames to make up the difference.

Well, visually, this might look like physics varies with framerate even if the physics simulation is still accurate with respect to its own concept of time, I guess.

1 Like
@pspeed said: Ah, I see... it (potentially) does make up frames to make up the difference.

Well, visually, this might look like physics varies with framerate even if the physics simulation is still accurate with respect to its own concept of time, I guess.

The way the premade controls get their data they in fact simply get interpolated values according to the tpf, thats a function built into bullet. The debug display actually uses the fixed positions according to the actual framerate of the physics space. The method that updates the physics space either does nothing in most calls when the framerate its called with is above its own or does multiple steps when its lower, see setMaxSubSteps.

And no, the issue is simply that he places the objects absolutely at some time that he doesn’t define because he doesn’t track tpf.