/* * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jme3.bullet.objects; import com.bulletphysics.collision.dispatch.CollisionFlags; import com.bulletphysics.dynamics.RigidBody; import com.bulletphysics.dynamics.RigidBodyConstructionInfo; import com.bulletphysics.linearmath.Transform; import com.jme3.bullet.PhysicsSpace; import com.jme3.bullet.collision.PhysicsCollisionObject; import com.jme3.bullet.collision.shapes.CollisionShape; import com.jme3.bullet.collision.shapes.MeshCollisionShape; import com.jme3.bullet.joints.PhysicsJoint; import com.jme3.bullet.objects.infos.RigidBodyMotionState; import com.jme3.bullet.util.Converter; import com.jme3.export.InputCapsule; import com.jme3.export.JmeExporter; import com.jme3.export.JmeImporter; import com.jme3.export.OutputCapsule; import com.jme3.math.Matrix3f; import com.jme3.math.Quaternion; import com.jme3.math.Vector3f; import com.jme3.scene.Geometry; import com.jme3.scene.Node; import com.jme3.scene.Spatial; import com.jme3.scene.debug.Arrow; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * <p>PhysicsRigidBody - Basic physics object</p> * @author normenhansen */ public class PhysicsRigidBody extends PhysicsCollisionObject { protected RigidBodyConstructionInfo constructionInfo; protected RigidBody rBody; protected RigidBodyMotionState motionState = new RigidBodyMotionState(); protected float mass = 1.0f; protected boolean kinematic = false; protected javax.vecmath.Vector3f tempVec = new javax.vecmath.Vector3f(); protected javax.vecmath.Vector3f tempVec2 = new javax.vecmath.Vector3f(); protected Transform tempTrans = new Transform(new javax.vecmath.Matrix3f()); protected javax.vecmath.Matrix3f tempMatrix = new javax.vecmath.Matrix3f(); //TEMP VARIABLES protected javax.vecmath.Vector3f localInertia = new javax.vecmath.Vector3f(); protected ArrayList<PhysicsJoint> joints = new ArrayList<PhysicsJoint>(); public PhysicsRigidBody() { } /** * Creates a new PhysicsRigidBody with the supplied collision shape * @param shape */ public PhysicsRigidBody(CollisionShape shape) { collisionShape = shape; rebuildRigidBody(); } public PhysicsRigidBody(CollisionShape shape, float mass) { collisionShape = shape; this.mass = mass; rebuildRigidBody(); } /** * Builds/rebuilds the phyiscs body when parameters have changed */ protected void rebuildRigidBody() { boolean removed = false; if(collisionShape instanceof MeshCollisionShape && mass != 0){ throw new IllegalStateException("Dynamic rigidbody can not have mesh collision shape!"); } if (rBody != null) { if (rBody.isInWorld()) { PhysicsSpace.getPhysicsSpace().remove(this); removed = true; } rBody.destroy(); } preRebuild(); rBody = new RigidBody(constructionInfo); postRebuild(); if (removed) { PhysicsSpace.getPhysicsSpace().add(this); } } protected void preRebuild() { collisionShape.calculateLocalInertia(mass, localInertia); if (constructionInfo == null) { constructionInfo = new RigidBodyConstructionInfo(mass, motionState, collisionShape.getCShape(), localInertia); } else { constructionInfo.mass = mass; constructionInfo.collisionShape = collisionShape.getCShape(); constructionInfo.motionState = motionState; } } protected void postRebuild() { rBody.setUserPointer(this); if (mass == 0.0f) { rBody.setCollisionFlags(rBody.getCollisionFlags() | CollisionFlags.STATIC_OBJECT); } else { rBody.setCollisionFlags(rBody.getCollisionFlags() & ~CollisionFlags.STATIC_OBJECT); } } /** * @return the motionState */ public RigidBodyMotionState getMotionState() { return motionState; } /** * Sets the physics object location * @param location the location of the actual physics object */ public void setPhysicsLocation(Vector3f location) { rBody.getCenterOfMassTransform(tempTrans); Converter.convert(location, tempTrans.origin); rBody.setCenterOfMassTransform(tempTrans); motionState.setWorldTransform(tempTrans); } /** * Sets the physics object rotation * @param rotation the rotation of the actual physics object */ public void setPhysicsRotation(Matrix3f rotation) { rBody.getCenterOfMassTransform(tempTrans); Converter.convert(rotation, tempTrans.basis); rBody.setCenterOfMassTransform(tempTrans); motionState.setWorldTransform(tempTrans); } /** * Sets the physics object rotation * @param rotation the rotation of the actual physics object */ public void setPhysicsRotation(Quaternion rotation) { rBody.getCenterOfMassTransform(tempTrans); Converter.convert(rotation, tempTrans.basis); rBody.setCenterOfMassTransform(tempTrans); motionState.setWorldTransform(tempTrans); } /** * Gets the physics object location, instantiates a new Vector3f object */ public Vector3f getPhysicsLocation() { return getPhysicsLocation(null); } /** * Gets the physics object rotation */ public Matrix3f getPhysicsRotationMatrix() { return getPhysicsRotationMatrix(null); } /** * Gets the physics object location, no object instantiation * @param location the location of the actual physics object is stored in this Vector3f */ public Vector3f getPhysicsLocation(Vector3f location) { if (location == null) { location = new Vector3f(); } rBody.getCenterOfMassTransform(tempTrans); return Converter.convert(tempTrans.origin, location); } /** * Gets the physics object rotation as a matrix, no conversions and no object instantiation * @param rotation the rotation of the actual physics object is stored in this Matrix3f */ public Matrix3f getPhysicsRotationMatrix(Matrix3f rotation) { if (rotation == null) { rotation = new Matrix3f(); } rBody.getCenterOfMassTransform(tempTrans); return Converter.convert(tempTrans.basis, rotation); } /** * Gets the physics object rotation as a quaternion, converts the bullet Matrix3f value, * instantiates new object */ public Quaternion getPhysicsRotation(){ return getPhysicsRotation(null); } /** * Gets the physics object rotation as a quaternion, converts the bullet Matrix3f value * @param rotation the rotation of the actual physics object is stored in this Quaternion */ public Quaternion getPhysicsRotation(Quaternion rotation){ if (rotation == null) { rotation = new Quaternion(); } rBody.getCenterOfMassTransform(tempTrans); return Converter.convert(tempTrans.basis, rotation); } /** * Gets the physics object location * @param location the location of the actual physics object is stored in this Vector3f */ public Vector3f getInterpolatedPhysicsLocation(Vector3f location) { if (location == null) { location = new Vector3f(); } rBody.getInterpolationWorldTransform(tempTrans); return Converter.convert(tempTrans.origin, location); } /** * Gets the physics object rotation * @param rotation the rotation of the actual physics object is stored in this Matrix3f */ public Matrix3f getInterpolatedPhysicsRotation(Matrix3f rotation) { if (rotation == null) { rotation = new Matrix3f(); } rBody.getInterpolationWorldTransform(tempTrans); return Converter.convert(tempTrans.basis, rotation); } /** * Sets the node to kinematic mode. in this mode the node is not affected by physics * but affects other physics objects. Its kinetic force is calculated by the amount * of movement it is exposed to and its weight. * @param kinematic */ public void setKinematic(boolean kinematic) { this.kinematic = kinematic; if (kinematic) { rBody.setCollisionFlags(rBody.getCollisionFlags() | CollisionFlags.KINEMATIC_OBJECT); rBody.setActivationState(com.bulletphysics.collision.dispatch.CollisionObject.DISABLE_DEACTIVATION); } else { rBody.setCollisionFlags(rBody.getCollisionFlags() & ~CollisionFlags.KINEMATIC_OBJECT); rBody.setActivationState(com.bulletphysics.collision.dispatch.CollisionObject.ACTIVE_TAG); } } public boolean isKinematic() { return kinematic; } public void setCcdSweptSphereRadius(float radius) { rBody.setCcdSweptSphereRadius(radius); } /** * Sets the amount of motion that has to happen in one physics tick to trigger the continuous motion detection<br/> * This avoids the problem of fast objects moving through other objects, set to zero to disable (default) * @param threshold */ public void setCcdMotionThreshold(float threshold) { rBody.setCcdMotionThreshold(threshold); } public float getCcdSweptSphereRadius() { return rBody.getCcdSweptSphereRadius(); } public float getCcdMotionThreshold() { return rBody.getCcdMotionThreshold(); } public float getCcdSquareMotionThreshold() { return rBody.getCcdSquareMotionThreshold(); } public float getMass() { return mass; } /** * Sets the mass of this PhysicsRigidBody, objects with mass=0 are static. * @param mass */ public void setMass(float mass) { this.mass = mass; if(collisionShape instanceof MeshCollisionShape && mass != 0){ throw new IllegalStateException("Dynamic rigidbody can not have mesh collision shape!"); } if (collisionShape != null) { collisionShape.calculateLocalInertia(mass, localInertia); } if (rBody != null) { rBody.setMassProps(mass, localInertia); if (mass == 0.0f) { rBody.setCollisionFlags(rBody.getCollisionFlags() | CollisionFlags.STATIC_OBJECT); } else { rBody.setCollisionFlags(rBody.getCollisionFlags() & ~CollisionFlags.STATIC_OBJECT); } } } public Vector3f getGravity() { return getGravity(null); } public Vector3f getGravity(Vector3f gravity) { if (gravity == null) { gravity = new Vector3f(); } rBody.getGravity(tempVec); return Converter.convert(tempVec, gravity); } /** * Set the local gravity of this PhysicsRigidBody<br/> * Set this after adding the node to the PhysicsSpace, * the PhysicsSpace assigns its current gravity to the physics node when its added. * @param gravity the gravity vector to set */ public void setGravity(Vector3f gravity) { rBody.setGravity(Converter.convert(gravity, tempVec)); } public float getFriction() { return rBody.getFriction(); } /** * Sets the friction of this physics object * @param friction the friction of this physics object */ public void setFriction(float friction) { constructionInfo.friction = friction; rBody.setFriction(friction); } public void setDamping(float linearDamping, float angularDamping) { constructionInfo.linearDamping = linearDamping; constructionInfo.angularDamping = angularDamping; rBody.setDamping(linearDamping, angularDamping); } public void setLinearDamping(float linearDamping) { constructionInfo.linearDamping = linearDamping; rBody.setDamping(linearDamping, constructionInfo.angularDamping); } public void setAngularDamping(float angularDamping) { constructionInfo.angularDamping = angularDamping; rBody.setDamping(constructionInfo.linearDamping, angularDamping); } public float getLinearDamping() { return constructionInfo.linearDamping; } public float getAngularDamping() { return constructionInfo.angularDamping; } public float getRestitution() { return rBody.getRestitution(); } /** * The "bouncyness" of the PhysicsRigidBody, best performance if restitution=0 * @param restitution */ public void setRestitution(float restitution) { constructionInfo.restitution = restitution; rBody.setRestitution(restitution); } /** * Get the current angular velocity of this PhysicsRigidBody * @return the current linear velocity */ public Vector3f getAngularVelocity() { return Converter.convert(rBody.getAngularVelocity(tempVec)); } /** * Get the current angular velocity of this PhysicsRigidBody * @param vec the vector to store the velocity in */ public void getAngularVelocity(Vector3f vec) { Converter.convert(rBody.getAngularVelocity(tempVec), vec); } /** * Sets the angular velocity of this PhysicsRigidBody * @param vec the angular velocity of this PhysicsRigidBody */ public void setAngularVelocity(Vector3f vec) { rBody.setAngularVelocity(Converter.convert(vec, tempVec)); rBody.activate(); } /** * Get the current linear velocity of this PhysicsRigidBody * @return the current linear velocity */ public Vector3f getLinearVelocity() { return Converter.convert(rBody.getLinearVelocity(tempVec)); } /** * Get the current linear velocity of this PhysicsRigidBody * @param vec the vector to store the velocity in */ public void getLinearVelocity(Vector3f vec) { Converter.convert(rBody.getLinearVelocity(tempVec), vec); } /** * Sets the linear velocity of this PhysicsRigidBody * @param vec the linear velocity of this PhysicsRigidBody */ public void setLinearVelocity(Vector3f vec) { rBody.setLinearVelocity(Converter.convert(vec, tempVec)); rBody.activate(); } /** * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call * updates the physics space.<br> * To apply an impulse, use applyImpulse, use applyContinuousForce to apply continuous force. * @param force the force * @param location the location of the force */ public void applyForce(final Vector3f force, final Vector3f location) { rBody.applyForce(Converter.convert(force, tempVec), Converter.convert(location, tempVec2)); rBody.activate(); } /** * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call * updates the physics space.<br> * To apply an impulse, use applyImpulse. * * @param force the force */ public void applyCentralForce(final Vector3f force) { rBody.applyCentralForce(Converter.convert(force, tempVec)); rBody.activate(); } /** * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call * updates the physics space.<br> * To apply an impulse, use applyImpulse. * * @param torque the torque */ public void applyTorque(final Vector3f torque) { rBody.applyTorque(Converter.convert(torque, tempVec)); rBody.activate(); } /** * Apply an impulse to the PhysicsRigidBody in the next physics update. * @param impulse applied impulse * @param rel_pos location relative to object */ public void applyImpulse(final Vector3f impulse, final Vector3f rel_pos) { rBody.applyImpulse(Converter.convert(impulse, tempVec), Converter.convert(rel_pos, tempVec2)); rBody.activate(); } /** * Apply a torque impulse to the PhysicsRigidBody in the next physics update. * @param vec */ public void applyTorqueImpulse(final Vector3f vec) { rBody.applyTorqueImpulse(Converter.convert(vec, tempVec)); rBody.activate(); } /** * Clear all forces from the PhysicsRigidBody * */ public void clearForces() { rBody.clearForces(); } public void setCollisionShape(CollisionShape collisionShape) { super.setCollisionShape(collisionShape); if(collisionShape instanceof MeshCollisionShape && mass!=0){ throw new IllegalStateException("Dynamic rigidbody can not have mesh collision shape!"); } if (rBody == null) { rebuildRigidBody(); } else { collisionShape.calculateLocalInertia(mass, localInertia); constructionInfo.collisionShape = collisionShape.getCShape(); rBody.setCollisionShape(collisionShape.getCShape()); } } /** * reactivates this PhysicsRigidBody when it has been deactivated because it was not moving */ public void activate() { rBody.activate(); } public boolean isActive() { return rBody.isActive(); } /** * sets the sleeping thresholds, these define when the object gets deactivated * to save ressources. Low values keep the object active when it barely moves * @param linear the linear sleeping threshold * @param angular the angular sleeping threshold */ public void setSleepingThresholds(float linear, float angular) { constructionInfo.linearSleepingThreshold = linear; constructionInfo.angularSleepingThreshold = angular; rBody.setSleepingThresholds(linear, angular); } public void setLinearSleepingThreshold(float linearSleepingThreshold) { constructionInfo.linearSleepingThreshold = linearSleepingThreshold; rBody.setSleepingThresholds(linearSleepingThreshold, constructionInfo.angularSleepingThreshold); } public void setAngularSleepingThreshold(float angularSleepingThreshold) { constructionInfo.angularSleepingThreshold = angularSleepingThreshold; rBody.setSleepingThresholds(constructionInfo.linearSleepingThreshold, angularSleepingThreshold); } public float getLinearSleepingThreshold() { return constructionInfo.linearSleepingThreshold; } public float getAngularSleepingThreshold() { return constructionInfo.angularSleepingThreshold; } public float getAngularFactor() { return rBody.getAngularFactor(); } public void setAngularFactor(float factor) { rBody.setAngularFactor(factor); } /** * do not use manually, joints are added automatically */ public void addJoint(PhysicsJoint joint) { if (!joints.contains(joint)) { joints.add(joint); } updateDebugShape(); } /** * */ public void removeJoint(PhysicsJoint joint) { joints.remove(joint); } /** * Returns a list of connected joints. This list is only filled when * the PhysicsRigidBody is actually added to the physics space or loaded from disk. * @return list of active joints connected to this PhysicsRigidBody */ public List<PhysicsJoint> getJoints() { return joints; } /** * used internally */ public RigidBody getObjectId() { return rBody; } /** * destroys this PhysicsRigidBody and removes it from memory */ public void destroy() { rBody.destroy(); } @Override protected Spatial getDebugShape() { //add joints Spatial shape = super.getDebugShape(); Node node = null; if (shape instanceof Node) { node = (Node) shape; } else { node = new Node("DebugShapeNode"); node.attachChild(shape); } int i = 0; for (Iterator<PhysicsJoint> it = joints.iterator(); it.hasNext();) { PhysicsJoint physicsJoint = it.next(); Vector3f pivot = null; if (physicsJoint.getBodyA() == this) { pivot = physicsJoint.getPivotA(); } else { pivot = physicsJoint.getPivotB(); } Arrow arrow = new Arrow(pivot); Geometry geom = new Geometry("DebugBone" + i, arrow); geom.setMaterial(debugMaterialGreen); node.attachChild(geom); i++; } return node; } @Override public void write(JmeExporter e) throws IOException { super.write(e); OutputCapsule capsule = e.getCapsule(this); capsule.write(getMass(), "mass", 1.0f); capsule.write(getGravity(), "gravity", Vector3f.ZERO); capsule.write(getFriction(), "friction", 0.5f); capsule.write(getRestitution(), "restitution", 0); capsule.write(getAngularFactor(), "angularFactor", 1); capsule.write(kinematic, "kinematic", false); capsule.write(constructionInfo.linearDamping, "linearDamping", 0); capsule.write(constructionInfo.angularDamping, "angularDamping", 0); capsule.write(constructionInfo.linearSleepingThreshold, "linearSleepingThreshold", 0.8f); capsule.write(constructionInfo.angularSleepingThreshold, "angularSleepingThreshold", 1.0f); capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0); capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0); capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f()); capsule.write(getPhysicsRotationMatrix(new Matrix3f()), "physicsRotation", new Matrix3f()); capsule.writeSavableArrayList(joints, "joints", null); } @Override public void read(JmeImporter e) throws IOException { super.read(e); InputCapsule capsule = e.getCapsule(this); float mass = capsule.readFloat("mass", 1.0f); this.mass = mass; rebuildRigidBody(); setGravity((Vector3f) capsule.readSavable("gravity", Vector3f.ZERO.clone())); setFriction(capsule.readFloat("friction", 0.5f)); setKinematic(capsule.readBoolean("kinematic", false)); setRestitution(capsule.readFloat("restitution", 0)); setAngularFactor(capsule.readFloat("angularFactor", 1)); setDamping(capsule.readFloat("linearDamping", 0), capsule.readFloat("angularDamping", 0)); setSleepingThresholds(capsule.readFloat("linearSleepingThreshold", 0.8f), capsule.readFloat("angularSleepingThreshold", 1.0f)); setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0)); setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0)); setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f())); setPhysicsRotation((Matrix3f) capsule.readSavable("physicsRotation", new Matrix3f())); joints = capsule.readSavableArrayList("joints", null); } }