/* * 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.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.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 java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; /** * <p>PhysicsRigidBody - Basic physics object</p> * @author normenhansen */ public class PhysicsRigidBody extends PhysicsCollisionObject { protected RigidBodyMotionState motionState = new RigidBodyMotionState(); protected float mass = 1.0f; protected boolean kinematic = false; protected ArrayList<PhysicsJoint> joints = new ArrayList<PhysicsJoint>(); public PhysicsRigidBody() { } /** * Creates a new PhysicsRigidBody with the supplied collision shape * @param child * @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 (objectId != 0) { if (isInWorld(objectId)) { PhysicsSpace.getPhysicsSpace().remove(this); removed = true; } Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Clearing RigidBody {0}", Long.toHexString(objectId)); finalizeNative(objectId); } preRebuild(); objectId = createRigidBody(mass, motionState.getObjectId(), collisionShape.getObjectId()); Logger.getLogger(this.getClass().getName()).log(Level.FINE, "Created RigidBody {0}", Long.toHexString(objectId)); postRebuild(); if (removed) { PhysicsSpace.getPhysicsSpace().add(this); } } protected void preRebuild() { } private native long createRigidBody(float mass, long motionStateId, long collisionShapeId); protected void postRebuild() { if (mass == 0.0f) { setStatic(objectId, true); } else { setStatic(objectId, false); } initUserPointer(); } /** * @return the motionState */ public RigidBodyMotionState getMotionState() { return motionState; } public boolean isInWorld() { return isInWorld(objectId); } private native boolean isInWorld(long objectId); /** * Sets the physics object location * @param location the location of the actual physics object */ public void setPhysicsLocation(Vector3f location) { setPhysicsLocation(objectId, location); } private native void setPhysicsLocation(long objectId, Vector3f location); /** * Sets the physics object rotation * @param rotation the rotation of the actual physics object */ public void setPhysicsRotation(Matrix3f rotation) { setPhysicsRotation(objectId, rotation); } private native void setPhysicsRotation(long objectId, Matrix3f rotation); /** * Sets the physics object rotation * @param rotation the rotation of the actual physics object */ public void setPhysicsRotation(Quaternion rotation) { setPhysicsRotation(objectId, rotation); } private native void setPhysicsRotation(long objectId, Quaternion rotation); /** * @return the physicsLocation */ public Vector3f getPhysicsLocation(Vector3f trans) { if (trans == null) { trans = new Vector3f(); } getPhysicsLocation(objectId, trans); return trans; } private native void getPhysicsLocation(long objectId, Vector3f vector); /** * @return the physicsLocation */ public Quaternion getPhysicsRotation(Quaternion rot) { if (rot == null) { rot = new Quaternion(); } getPhysicsRotation(objectId, rot); return rot; } private native void getPhysicsRotation(long objectId, Quaternion rot); /** * @return the physicsLocation */ public Matrix3f getPhysicsRotationMatrix(Matrix3f rot) { if (rot == null) { rot = new Matrix3f(); } getPhysicsRotationMatrix(objectId, rot); return rot; } private native void getPhysicsRotationMatrix(long objectId, Matrix3f rot); /** * @return the physicsLocation */ public Vector3f getPhysicsLocation() { Vector3f vec = new Vector3f(); getPhysicsLocation(objectId, vec); return vec; } /** * @return the physicsLocation */ public Quaternion getPhysicsRotation() { Quaternion quat = new Quaternion(); getPhysicsRotation(objectId, quat); return quat; } public Matrix3f getPhysicsRotationMatrix() { Matrix3f mtx = new Matrix3f(); getPhysicsRotationMatrix(objectId, mtx); return mtx; } // /** // * 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. Iits 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; setKinematic(objectId, kinematic); } private native void setKinematic(long objectId, boolean kinematic); public boolean isKinematic() { return kinematic; } public void setCcdSweptSphereRadius(float radius) { setCcdSweptSphereRadius(objectId, radius); } private native void setCcdSweptSphereRadius(long objectId, float 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) { setCcdMotionThreshold(objectId, threshold); } private native void setCcdMotionThreshold(long objectId, float threshold); public float getCcdSweptSphereRadius() { return getCcdSweptSphereRadius(objectId); } private native float getCcdSweptSphereRadius(long objectId); public float getCcdMotionThreshold() { return getCcdMotionThreshold(objectId); } private native float getCcdMotionThreshold(long objectId); public float getCcdSquareMotionThreshold() { return getCcdSquareMotionThreshold(objectId); } private native float getCcdSquareMotionThreshold(long objectId); 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 (objectId != 0) { if (collisionShape != null) { updateMassProps(objectId, collisionShape.getObjectId(), mass); } if (mass == 0.0f) { setStatic(objectId, true); } else { setStatic(objectId, false); } } } private native void setStatic(long objectId, boolean state); private native long updateMassProps(long objectId, long collisionShapeId, float mass); public Vector3f getGravity() { return getGravity(null); } public Vector3f getGravity(Vector3f gravity) { if (gravity == null) { gravity = new Vector3f(); } getGravity(objectId, gravity); return gravity; } private native void getGravity(long objectId, Vector3f 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) { setGravity(objectId, gravity); } private native void setGravity(long objectId, Vector3f gravity); public float getFriction() { return getFriction(objectId); } private native float getFriction(long objectId); /** * Sets the friction of this physics object * @param friction the friction of this physics object */ public void setFriction(float friction) { setFriction(objectId, friction); } private native void setFriction(long objectId, float friction); public void setDamping(float linearDamping, float angularDamping) { setDamping(objectId, linearDamping, angularDamping); } private native void setDamping(long objectId, float linearDamping, float angularDamping); // private native void setRestitution(long objectId, float factor); // // public void setLinearDamping(float linearDamping) { // constructionInfo.linearDamping = linearDamping; // rBody.setDamping(linearDamping, constructionInfo.angularDamping); // } // // private native void setRestitution(long objectId, float factor); // public void setLinearDamping(float linearDamping) { setDamping(objectId, linearDamping, getAngularDamping()); } public void setAngularDamping(float angularDamping) { setAngularDamping(objectId, angularDamping); } private native void setAngularDamping(long objectId, float factor); public float getLinearDamping() { return getLinearDamping(objectId); } private native float getLinearDamping(long objectId); public float getAngularDamping() { return getAngularDamping(objectId); } private native float getAngularDamping(long objectId); public float getRestitution() { return getRestitution(objectId); } private native float getRestitution(long objectId); /** * The "bouncyness" of the PhysicsRigidBody, best performance if restitution=0 * @param restitution */ public void setRestitution(float restitution) { setRestitution(objectId, restitution); } private native void setRestitution(long objectId, float factor); /** * Get the current angular velocity of this PhysicsRigidBody * @return the current linear velocity */ public Vector3f getAngularVelocity() { Vector3f vec = new Vector3f(); getAngularVelocity(objectId, vec); return vec; } private native void getAngularVelocity(long objectId, Vector3f vec); /** * Get the current angular velocity of this PhysicsRigidBody * @param vec the vector to store the velocity in */ public void getAngularVelocity(Vector3f vec) { getAngularVelocity(objectId, vec); } /** * Sets the angular velocity of this PhysicsRigidBody * @param vec the angular velocity of this PhysicsRigidBody */ public void setAngularVelocity(Vector3f vec) { setAngularVelocity(objectId, vec); activate(); } private native void setAngularVelocity(long objectId, Vector3f vec); /** * Get the current linear velocity of this PhysicsRigidBody * @return the current linear velocity */ public Vector3f getLinearVelocity() { Vector3f vec = new Vector3f(); getLinearVelocity(objectId, vec); return vec; } private native void getLinearVelocity(long objectId, Vector3f vec); /** * Get the current linear velocity of this PhysicsRigidBody * @param vec the vector to store the velocity in */ public void getLinearVelocity(Vector3f vec) { getLinearVelocity(objectId, vec); } /** * Sets the linear velocity of this PhysicsRigidBody * @param vec the linear velocity of this PhysicsRigidBody */ public void setLinearVelocity(Vector3f vec) { setLinearVelocity(objectId, vec); activate(); } private native void setLinearVelocity(long objectId, Vector3f vec); /** * 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(Vector3f force, Vector3f location) { applyForce(objectId, force, location); activate(); } private native void applyForce(long objectId, Vector3f force, Vector3f location); /** * 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(Vector3f force) { applyCentralForce(objectId, force); activate(); } private native void applyCentralForce(long objectId, Vector3f force); /** * 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(Vector3f torque) { applyTorque(objectId, torque); activate(); } private native void applyTorque(long objectId, Vector3f vec); /** * 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(Vector3f impulse, Vector3f rel_pos) { applyImpulse(objectId, impulse, rel_pos); activate(); } private native void applyImpulse(long objectId, Vector3f impulse, Vector3f rel_pos); /** * Apply a torque impulse to the PhysicsRigidBody in the next physics update. * @param vec */ public void applyTorqueImpulse(Vector3f vec) { applyTorqueImpulse(objectId, vec); activate(); } private native void applyTorqueImpulse(long objectId, Vector3f vec); /** * Clear all forces from the PhysicsRigidBody * */ public void clearForces() { clearForces(objectId); } private native void clearForces(long objectId); 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 (objectId == 0) { rebuildRigidBody(); } else { setCollisionShape(objectId, collisionShape.getObjectId()); updateMassProps(objectId, collisionShape.getObjectId(), mass); } } private native void setCollisionShape(long objectId, long collisionShapeId); /** * reactivates this PhysicsRigidBody when it has been deactivated because it was not moving */ public void activate() { activate(objectId); } private native void activate(long objectId); public boolean isActive() { return isActive(objectId); } private native boolean isActive(long objectId); /** * 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) { setSleepingThresholds(objectId, linear, angular); } private native void setSleepingThresholds(long objectId, float linear, float angular); public void setLinearSleepingThreshold(float linearSleepingThreshold) { setLinearSleepingThreshold(objectId, linearSleepingThreshold); } private native void setLinearSleepingThreshold(long objectId, float linearSleepingThreshold); public void setAngularSleepingThreshold(float angularSleepingThreshold) { setAngularSleepingThreshold(objectId, angularSleepingThreshold); } private native void setAngularSleepingThreshold(long objectId, float angularSleepingThreshold); public float getLinearSleepingThreshold() { return getLinearSleepingThreshold(objectId); } private native float getLinearSleepingThreshold(long objectId); public float getAngularSleepingThreshold() { return getAngularSleepingThreshold(objectId); } private native float getAngularSleepingThreshold(long objectId); public float getAngularFactor() { return getAngularFactor(null).getX(); } public Vector3f getAngularFactor(Vector3f store) { // doing like this prevent from breaking the API if (store == null) { store = new Vector3f(); } getAngularFactor(objectId, store); return store; } private native void getAngularFactor(long objectId, Vector3f vec); public void setAngularFactor(float factor) { setAngularFactor(objectId, new Vector3f(factor, factor, factor)); } public void setAngularFactor(Vector3f factor) { setAngularFactor(objectId, factor); } private native void setAngularFactor(long objectId, Vector3f factor); public Vector3f getLinearFactor() { Vector3f vec = new Vector3f(); getLinearFactor(objectId, vec); return vec; } private native void getLinearFactor(long objectId, Vector3f vec); public void setLinearFactor(Vector3f factor) { setLinearFactor(objectId, factor); } private native void setLinearFactor(long objectId, Vector3f factor); /** * do not use manually, joints are added automatically */ public void addJoint(PhysicsJoint joint) { if (!joints.contains(joint)) { joints.add(joint); } } /** * */ 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; } @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); Vector3f angularFactor = getAngularFactor(null); if (angularFactor.x == angularFactor.y && angularFactor.y == angularFactor.z) { capsule.write(getAngularFactor(), "angularFactor", 1); } else { capsule.write(getAngularFactor(null), "angularFactor", Vector3f.UNIT_XYZ); capsule.write(getLinearFactor(), "linearFactor", Vector3f.UNIT_XYZ); } capsule.write(kinematic, "kinematic", false); capsule.write(getLinearDamping(), "linearDamping", 0); capsule.write(getAngularDamping(), "angularDamping", 0); capsule.write(getLinearSleepingThreshold(), "linearSleepingThreshold", 0.8f); capsule.write(getAngularSleepingThreshold(), "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)); Vector3f angularFactor = (Vector3f) capsule.readSavable("angularFactor", null); if(angularFactor == null) { setAngularFactor(capsule.readFloat("angularFactor", 1)); } else { setAngularFactor(angularFactor); setLinearFactor((Vector3f) capsule.readSavable("linearFactor", Vector3f.UNIT_XYZ.clone())); } 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); } }