im having issues updating the mass of a RigidBodyControl object from 0.0f to >0.0f.
The object stays inactive and does not respond to linear or angular impulse.
I have written a little test program to reproduce the issue using minie-5.0.0 (see below).
Things i have tried so far:
Calling activate(true) on the object, does not resolve the issue.
Calling setEnabled(false) and then setEnabled(true), does resolve the issue in my application but not with the test.
Removing and re-adding the object to the physics space, makes the object respond to linear impulse but not to angular impulse.
Observations:
Setting a new CompoundCollisionShape shape resolved the issue in my application, but not with the test.
Updating the mass from 1.0f to 10.0f does not seem to update the angular inerta.
Additional question:
Setting a margin on a CompoundCollisionShape seems to affect its angular inertia, is this intended?
Big thanks to everyone for your continuous effort here!
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.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.math.Vector3f;
public class Main extends SimpleApplication implements PhysicsTickListener {
float timer = 3.0f;
boolean isUpdate = true;
boolean isPush = true;
PhysicsSpace physicsSpace;
RigidBodyControl rigidBodyControl;
public static void main(String[] args) {
new Main().start();
}
@Override
public void simpleInitApp() {
BulletAppState bulletAppState = new BulletAppState();
bulletAppState.setDebugEnabled(true);
stateManager.attach(bulletAppState);
physicsSpace = bulletAppState.getPhysicsSpace();
physicsSpace.setGravity(Vector3f.ZERO);
physicsSpace.addTickListener(this);
rigidBodyControl = new RigidBodyControl(0.0f);
rigidBodyControl.setCollisionShape(createCollisionShape());
physicsSpace.addCollisionObject(rigidBodyControl);
Geometry box = new Geometry("boxGeometry", new Box(0.4f, 0.4f, 0.4f));
box.addControl(rigidBodyControl);
box.setMaterial(getAssetManager().loadMaterial("Common/Materials/RedColor.j3m"));
getRootNode().attachChild(box);
}
@Override
public void simpleUpdate(float tpf) {
if (timer >= 0.0f) {
timer -= tpf;
} else {
if (isUpdate) {
isUpdate = false;
System.out.println("update");
// updating the mass from 1.0f to 10.0f shows no issues with linear motion but angular inertia does not seem to get updated
rigidBodyControl.setMass(1.0f);
// doesnt work in test but works to enable linear motion in application
// rigidBodyControl.setEnabled(false);
// rigidBodyControl.setEnabled(true);
// works to re-enable torque in application but not in test
// rigidBodyControl.setCollisionShape(createCollisionShape());
// doesnt work
// rigidBodyControl.activate(true);
// works to enable linear motion but still no torque
physicsSpace.removeCollisionObject(rigidBodyControl);
physicsSpace.addCollisionObject(rigidBodyControl);
}
}
}
@Override
public void prePhysicsTick(PhysicsSpace physicsSpace, float v) {
if (!isUpdate && isPush) {
System.out.println("push");
isPush = false;
rigidBodyControl.applyCentralImpulse(Vector3f.UNIT_Y);
rigidBodyControl.applyTorqueImpulse(Vector3f.UNIT_Y);
}
}
static CollisionShape createCollisionShape(){
CompoundCollisionShape result = new CompoundCollisionShape();
result.addChildShape(new BoxCollisionShape(0.5f));
// why does this affect torque when a compound collision shape is used?
// result.setMargin(1.0f);
return result;
}
@Override
public void physicsTick(PhysicsSpace physicsSpace, float v) {
}
}
Not necessary. It seemed an roundabout way of creating the physics object, so I wanted to mention it.
Iāve run some experiments, and Iām convinced thereās a bug when a rigid body is converted from static to dynamic. Iāve written a patch, but Iād like to test it a bit before releasing it.
its a pleasure! its the least one can do for such a beautiful project like minieā¦
the initial issue is resolved with 5.0.1.
only the issue with the angular motion on updating the mass from 1.0f to 10.0f remains.
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.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
public class Main extends SimpleApplication implements PhysicsTickListener {
float timer = 3.0f;
boolean isUpdate = true;
boolean isPush = true;
PhysicsSpace physicsSpace;
RigidBodyControl updatedRigidBodyControl;
RigidBodyControl referenceRigidBodyControl;
public static void main(String[] args) {
new Main().start();
}
@Override
public void simpleInitApp() {
BulletAppState bulletAppState = new BulletAppState();
bulletAppState.setDebugEnabled(true);
stateManager.attach(bulletAppState);
physicsSpace = bulletAppState.getPhysicsSpace();
physicsSpace.setGravity(Vector3f.ZERO);
physicsSpace.addTickListener(this);
updatedRigidBodyControl = new RigidBodyControl(1.0f);
updatedRigidBodyControl.setCollisionShape(createCollisionShape());
physicsSpace.addCollisionObject(updatedRigidBodyControl);
referenceRigidBodyControl = new RigidBodyControl(10.0f);
referenceRigidBodyControl.setCollisionShape(createCollisionShape());
physicsSpace.addCollisionObject(referenceRigidBodyControl);
Geometry updatedBox = new Geometry("boxGeometry", new Box(0.4f, 0.4f, 0.4f));
updatedBox.setLocalTranslation(2.0f, 0.0f, 0.0f);
updatedBox.addControl(updatedRigidBodyControl);
updatedBox.setMaterial(getAssetManager().loadMaterial("Common/Materials/RedColor.j3m"));
getRootNode().attachChild(updatedBox);
Geometry referenceBox = new Geometry("boxGeometry", new Box(0.4f, 0.4f, 0.4f));
referenceBox.setLocalTranslation(-2.0f, 0.0f, 0.0f);
referenceBox.addControl(referenceRigidBodyControl);
referenceBox.setMaterial(getAssetManager().loadMaterial("Common/Materials/RedColor.j3m"));
getRootNode().attachChild(referenceBox);
}
@Override
public void simpleUpdate(float tpf) {
if (timer >= 0.0f) {
timer -= tpf;
} else {
if (isUpdate) {
isUpdate = false;
System.out.println("update");
// updating the mass from 1.0f to 10.0f shows no issues with linear motion but angular inertia does not seem to get updated
updatedRigidBodyControl.setMass(10.0f);
}
}
}
@Override
public void prePhysicsTick(PhysicsSpace physicsSpace, float v) {
if (!isUpdate && isPush) {
System.out.println("push");
isPush = false;
updatedRigidBodyControl.applyCentralImpulse(Vector3f.UNIT_Y);
updatedRigidBodyControl.applyTorqueImpulse(Vector3f.UNIT_Y);
referenceRigidBodyControl.applyCentralImpulse(Vector3f.UNIT_Y);
referenceRigidBodyControl.applyTorqueImpulse(Vector3f.UNIT_Y);
}
}
static CollisionShape createCollisionShape(){
CompoundCollisionShape result = new CompoundCollisionShape();
result.addChildShape(new BoxCollisionShape(0.5f));
// why does this affect torque when a compound collision shape is used?
// result.setMargin(1.0f);
return result;
}
@Override
public void physicsTick(PhysicsSpace physicsSpace, float v) {
}
}
the issue with the angular motion on updating the mass from 1.0f to 10.0f remains.
Another good catch!
This appears to be a bug in the native portion of Minie. setMass() updates the bodyās local inertia vector but not its world inertia tensor. Ordinarily this isnāt an issue because the tensor gets updated during the next simulation step. In the āMainā app, however, the torque impulse is applied before the next simulation step, so it sees the old value of the tensor.
I envision a one-line fix, but it might take a few days to release it.
the effect is persistent beyond the next simulation step i think. i updated the example with another timer to demonstrate this.
maybe its just an unusual use case for someone to update the mass of a body after creation.
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.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CollisionShape;
import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
public class Main extends SimpleApplication implements PhysicsTickListener {
float updateTimer = 3.0f;
float pushTimer = 3.0f;
boolean isUpdate = true;
boolean isPush;
boolean isDone;
PhysicsSpace physicsSpace;
RigidBodyControl updatedRigidBodyControl;
RigidBodyControl referenceRigidBodyControl;
public static void main(String[] args) {
new Main().start();
}
@Override
public void simpleInitApp() {
BulletAppState bulletAppState = new BulletAppState();
bulletAppState.setDebugEnabled(true);
stateManager.attach(bulletAppState);
physicsSpace = bulletAppState.getPhysicsSpace();
physicsSpace.setGravity(Vector3f.ZERO);
physicsSpace.addTickListener(this);
updatedRigidBodyControl = new RigidBodyControl(1.0f);
updatedRigidBodyControl.setCollisionShape(createCollisionShape());
physicsSpace.addCollisionObject(updatedRigidBodyControl);
referenceRigidBodyControl = new RigidBodyControl(10.0f);
referenceRigidBodyControl.setCollisionShape(createCollisionShape());
physicsSpace.addCollisionObject(referenceRigidBodyControl);
Geometry updatedBox = new Geometry("boxGeometry", new Box(0.4f, 0.4f, 0.4f));
updatedBox.setLocalTranslation(2.0f, 0.0f, 0.0f);
updatedBox.addControl(updatedRigidBodyControl);
updatedBox.setMaterial(getAssetManager().loadMaterial("Common/Materials/RedColor.j3m"));
getRootNode().attachChild(updatedBox);
Geometry referenceBox = new Geometry("boxGeometry", new Box(0.4f, 0.4f, 0.4f));
referenceBox.setLocalTranslation(-2.0f, 0.0f, 0.0f);
referenceBox.addControl(referenceRigidBodyControl);
referenceBox.setMaterial(getAssetManager().loadMaterial("Common/Materials/RedColor.j3m"));
getRootNode().attachChild(referenceBox);
}
@Override
public void simpleUpdate(float tpf) {
if (updateTimer >= 0.0f) {
updateTimer -= tpf;
} else {
if (isUpdate) {
isUpdate = false;
System.out.println("update");
// updating the mass from 1.0f to 10.0f shows no issues with linear motion but angular inertia does not seem to get updated
updatedRigidBodyControl.setMass(10.0f);
}
if(pushTimer >= 0.0f){
pushTimer -= tpf;
} else {
isPush = true;
}
}
}
@Override
public void prePhysicsTick(PhysicsSpace physicsSpace, float v) {
if (!isUpdate && isPush && !isDone) {
System.out.println("push");
isDone = true;
updatedRigidBodyControl.applyCentralImpulse(Vector3f.UNIT_Y);
updatedRigidBodyControl.applyTorqueImpulse(Vector3f.UNIT_Y);
referenceRigidBodyControl.applyCentralImpulse(Vector3f.UNIT_Y);
referenceRigidBodyControl.applyTorqueImpulse(Vector3f.UNIT_Y);
}
}
static CollisionShape createCollisionShape(){
CompoundCollisionShape result = new CompoundCollisionShape();
result.addChildShape(new BoxCollisionShape(0.5f));
// why does this affect torque when a compound collision shape is used?
// result.setMargin(1.0f);
return result;
}
@Override
public void physicsTick(PhysicsSpace physicsSpace, float v) {
}
}
setting a collision margin of 1.0f on the CompoundCollsisionShape also seems to influnce the angular inertia. so maybe there is a connection to the collision shape im using here.
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.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.math.Vector3f;
import com.jme3.scene.Geometry;
import com.jme3.scene.shape.Box;
public class Margin extends SimpleApplication implements PhysicsTickListener {
float pushTimer = 3.0f;
boolean isPush;
boolean isDone;
PhysicsSpace physicsSpace;
RigidBodyControl rigidBodyControl;
RigidBodyControl referenceRigidBodyControl;
public static void main(String[] args) {
new Margin().start();
}
@Override
public void simpleInitApp() {
BulletAppState bulletAppState = new BulletAppState();
bulletAppState.setDebugEnabled(true);
stateManager.attach(bulletAppState);
physicsSpace = bulletAppState.getPhysicsSpace();
physicsSpace.setGravity(Vector3f.ZERO);
physicsSpace.addTickListener(this);
rigidBodyControl = new RigidBodyControl(1.0f);
CompoundCollisionShape compoundCollisionShape = new CompoundCollisionShape();
BoxCollisionShape childShape = new BoxCollisionShape(0.5f);
compoundCollisionShape.addChildShape(childShape);
compoundCollisionShape.setMargin(1.0f);
rigidBodyControl.setCollisionShape(compoundCollisionShape);
physicsSpace.addCollisionObject(rigidBodyControl);
referenceRigidBodyControl = new RigidBodyControl(1.0f);
BoxCollisionShape referenceCollisionShape = new BoxCollisionShape(0.5f);
referenceCollisionShape.setMargin(1.0f);
referenceRigidBodyControl.setCollisionShape(referenceCollisionShape);
physicsSpace.addCollisionObject(referenceRigidBodyControl);
Geometry box = new Geometry("boxGeometry", new Box(0.4f, 0.4f, 0.4f));
box.setLocalTranslation(3.0f, 0.0f, 0.0f);
box.addControl(rigidBodyControl);
box.setMaterial(getAssetManager().loadMaterial("Common/Materials/RedColor.j3m"));
getRootNode().attachChild(box);
Geometry referenceBox = new Geometry("boxGeometry", new Box(0.4f, 0.4f, 0.4f));
referenceBox.setLocalTranslation(-3.0f, 0.0f, 0.0f);
referenceBox.addControl(referenceRigidBodyControl);
referenceBox.setMaterial(getAssetManager().loadMaterial("Common/Materials/RedColor.j3m"));
getRootNode().attachChild(referenceBox);
}
@Override
public void simpleUpdate(float tpf) {
if (pushTimer >= 0.0f) {
pushTimer -= tpf;
} else {
isPush = true;
}
}
@Override
public void prePhysicsTick(PhysicsSpace physicsSpace, float v) {
if (isPush && !isDone) {
System.out.println("push");
isDone = true;
rigidBodyControl.applyCentralImpulse(Vector3f.UNIT_Y);
rigidBodyControl.applyTorqueImpulse(Vector3f.UNIT_Y);
referenceRigidBodyControl.applyCentralImpulse(Vector3f.UNIT_Y);
referenceRigidBodyControl.applyTorqueImpulse(Vector3f.UNIT_Y);
}
}
@Override
public void physicsTick(PhysicsSpace physicsSpace, float v) {
}
}
The torque impulses are only applied once, right? At that time, the inertia was incorrect, so the angular rates got set wrong. Bullet doesnāt recalculate the rates after the inertia changes, and since there are no further forces/torques/impulses, the incorrect rates persist.
setting a collision margin of 1.0f on the CompoundCollsisionShape also seems to influnce the angular inertia
Bulletās estimate of angular inertia for a compound shape uses a crude approximation based on the shapeās axis-aligned bounding box:
It seems likely (though I havenāt checked) that the AABB includes the collision margin. If so, that would explain why margin affects the angular inertia.
(By the way, Bulletās author explicitly advises against changing collision margins from the default.)
If accuracy matters, you can override Bulletās estimate using setInverseInertiaLocal(). Browsing the code, I just realized that setInverseInertiaLocal()ālike setMass()ādoesnāt immediately update the world inertia tensor, so Iāll fix that in the next release as well.
yes, the torque impulse is only applied once.
i was just wondering as i was seeing the effect in my game as well where applyTorque() is used continously.