Hello everybody!
I wonder what the physical meaning of the Vector3f parameter in the function com.jme3.bullet.objects.PhysicsRigidBody.applyTorqueImpulse(Vector3f vec) might be.
I thought the direction of this vector should represent the direction of the rotation axis (through the center of mass of the body) and its length should represent the absolute value of the torque impulse.
But now in my tests I see, that a RigidBody’s rotation axis is NOT the same in the following two cases:
vehicle.setAngularVelocity(Vector3f.UNIT_Z)
vehicle.applyTorqueImpulse(Vector3f.UNIT_Z)
After I got suspicious about that point, I tried it out with a box with attached local coordinate-arrows (Gravity switched off):
First I give the Box a translation and rotation by using setPhysicsLocation and setPhysicsRotation to have a difference between local and global coordinates.
In some very simple Positions (eg. 90° turn around UNIT_Y) of the Box everything behaves like I would expect. But when the box is placed in a more complex way, the following problem occurs:
SetAngularVelocity results in a Rotation around the center of mass and and with the given vector as direction of the rotation axis (as I assumed).
But applyTorqueImpulse results in a Rotation around a different axis. The box seams to rotate neither around the UNIT_Z in world coordinates nor in local coordinates.
By the way: With applyTorque() its the same axis as whith applyTorqueImpulse.
So what is the physical meaning of the vector parameter in applyTorque and applyTorqueImpulse? It’s not described in JavaDoc. Could anybody explain it, please?
Thanks a lot for helping!
Here’s my test code:
import com.jme3.app.SimpleApplication;
import com.jme3.bullet.BulletAppState;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
import com.jme3.bullet.collision.shapes.CompoundCollisionShape;
import com.jme3.bullet.control.VehicleControl;
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.scene.Geometry;
import com.jme3.scene.Node;
import com.jme3.scene.debug.Arrow;
import com.jme3.scene.shape.Box;
import com.jme3.scene.shape.Cylinder;
public class TestPhysicsCar extends SimpleApplication implements ActionListener {
private BulletAppState bulletAppState;
private VehicleControl vehicle;
Material matRed;
Material matGreen;
Material matBlue;
Material matBlack;
boolean capital = false;
Vector3f rotationAxis;
public static void main(String[] args) {
TestPhysicsCar app = new TestPhysicsCar();
app.start();
}
@Override
public void simpleInitApp() {
bulletAppState = new BulletAppState();
stateManager.attach(bulletAppState);
bulletAppState.setDebugEnabled(true);
PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
matRed = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
matRed.getAdditionalRenderState().setWireframe(true);
matRed.setColor("Color", ColorRGBA.Red);
matGreen = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
matGreen.getAdditionalRenderState().setWireframe(true);
matGreen.setColor("Color", ColorRGBA.Green);
matBlue = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
matBlue.getAdditionalRenderState().setWireframe(true);
matBlue.setColor("Color", ColorRGBA.Blue);
matBlack = new Material(getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
matBlack.getAdditionalRenderState().setWireframe(true);
matBlack.setColor("Color", ColorRGBA.Black);
setupKeys();
buildPlayer();
buildGlobalCoordSys();
cam.setLocation(new Vector3f(5, 10, 20));
}
private PhysicsSpace getPhysicsSpace(){
return bulletAppState.getPhysicsSpace();
}
private void setupKeys() {
inputManager.addMapping("LeftTorque", new KeyTrigger(KeyInput.KEY_LEFT));
inputManager.addMapping("RightTorque", new KeyTrigger(KeyInput.KEY_RIGHT));
inputManager.addMapping("LeftAngularV", new KeyTrigger(KeyInput.KEY_A));
inputManager.addMapping("RightAngularV", new KeyTrigger(KeyInput.KEY_D));
inputManager.addMapping("Stop", new KeyTrigger(KeyInput.KEY_SPACE));
inputManager.addMapping("Reset", new KeyTrigger(KeyInput.KEY_RETURN));
inputManager.addListener(this, "LeftTorque");
inputManager.addListener(this, "LeftAngularV");
inputManager.addListener(this, "RightTorque");
inputManager.addListener(this, "RightAngularV");
inputManager.addListener(this, "Stop");
inputManager.addListener(this, "Reset");
}
private void buildPlayer() {
//create a compound shape and attach the BoxCollisionShape for the car body
CompoundCollisionShape compoundShape = new CompoundCollisionShape();
BoxCollisionShape box = new BoxCollisionShape(new Vector3f(1, 0.5f, 3f));
compoundShape.addChildShape(box, new Vector3f(0, 0, 0));
//create vehicle node
Node vehicleNode=new Node("vehicleNode");
vehicle = new VehicleControl(compoundShape, 1);
vehicleNode.addControl(vehicle);
getPhysicsSpace().add(vehicle);
vehicle.setGravity(new Vector3f(0,0,0));
//give it a mesh
Box vehicleMesh = new Box(1, 0.5f, 3);
Geometry vehicleGeo = new Geometry("vehicle", vehicleMesh);
vehicleGeo.setMaterial(matBlack);
vehicleNode.attachChild(vehicleGeo);
//show a local CoordSys
//X
Arrow arrowXMesh = new Arrow(Vector3f.UNIT_X.mult(4));
Geometry arrowXGeo = new Geometry("arrowXLocal", arrowXMesh);
arrowXGeo.setMaterial(matRed);
Node arrowXNode = new Node ("arrowX");
arrowXNode.attachChild(arrowXGeo);
vehicleNode.attachChild(arrowXNode);
//Y
Arrow arrowYMesh = new Arrow(Vector3f.UNIT_Y.mult(4));
Geometry arrowYGeo = new Geometry("arrowYLocal", arrowYMesh);
arrowYGeo.setMaterial(matGreen);
Node arrowYNode = new Node ("arrowY");
arrowYNode.attachChild(arrowYGeo);
vehicleNode.attachChild(arrowYNode);
//Z
Arrow arrowZMesh = new Arrow(Vector3f.UNIT_Z.mult(4));
Geometry arrowZGeo = new Geometry("arrowZLocal", arrowZMesh);
arrowZGeo.setMaterial(matBlue);
Node arrowZNode = new Node ("arrowZ");
arrowZNode.attachChild(arrowZGeo);
vehicleNode.attachChild(arrowZNode);
//place vehicle node
placeVehicle();
//set Rotation Axis
rotationAxis = Vector3f.UNIT_Z;
rootNode.attachChild(vehicleNode);
}
private void placeVehicle() {
vehicle.setPhysicsLocation(new Vector3f(0, 5, 0));
Quaternion quat1 = new Quaternion();
quat1.fromAngleAxis((float)Math.PI/2, new Vector3f(0,1,1));
vehicle.setPhysicsRotation(quat1);
}
private void buildGlobalCoordSys() {
//show a global CoordSys
//X
Arrow arrowXMesh = new Arrow(Vector3f.UNIT_X.mult(6));
Geometry arrowXGeo = new Geometry("arrowXGlobal", arrowXMesh);
arrowXGeo.setMaterial(matRed);
Node arrowXNode = new Node ("arrowX");
arrowXNode.attachChild(arrowXGeo);
rootNode.attachChild(arrowXNode);
//Y
Arrow arrowYMesh = new Arrow(Vector3f.UNIT_Y.mult(6));
Geometry arrowYGeo = new Geometry("arrowYGlobal", arrowYMesh);
arrowYGeo.setMaterial(matGreen);
Node arrowYNode = new Node ("arrowY");
arrowYNode.attachChild(arrowYGeo);
rootNode.attachChild(arrowYNode);
//Z
Arrow arrowZMesh = new Arrow(Vector3f.UNIT_Z.mult(6));
Geometry arrowZGeo = new Geometry("arrowZGlobal", arrowZMesh);
arrowZGeo.setMaterial(matBlue);
Node arrowZNode = new Node ("arrowZ");
arrowZNode.attachChild(arrowZGeo);
rootNode.attachChild(arrowZNode);
}
@Override
public void simpleUpdate(float tpf) {
cam.setLocation(new Vector3f(5, 10, 20));
cam.lookAt(vehicle.getPhysicsLocation(), Vector3f.UNIT_Y);
}
@Override
public void onAction(String binding, boolean value, float tpf) {
if (binding.equals("Stop")) {
if (value) {
vehicle.setAngularVelocity(Vector3f.ZERO);
} else {
}
} else if (binding.equals("LeftTorque")) {
if (value) {
vehicle.applyTorqueImpulse(rotationAxis.normalize());
} else {
}
} else if (binding.equals("LeftAngularV")) {
if (value) {
vehicle.setAngularVelocity(rotationAxis.normalize());
} else {
}
} else if (binding.equals("RightTorque")) {
if (value) {
vehicle.applyTorqueImpulse(rotationAxis.normalize().negate());
} else {
}
} else if (binding.equals("RightAngularV")) {
if (value) {
vehicle.setAngularVelocity(rotationAxis.normalize().negate());
} else {
}
} else if (binding.equals("Reset")) {
if (value) {
vehicle.setAngularVelocity(Vector3f.ZERO);
placeVehicle();
}
}
}
}