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 = index5f;
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]