diff options
Diffstat (limited to 'engine/src/bullet/com/jme3/bullet/objects')
7 files changed, 2587 insertions, 0 deletions
diff --git a/engine/src/bullet/com/jme3/bullet/objects/PhysicsCharacter.java b/engine/src/bullet/com/jme3/bullet/objects/PhysicsCharacter.java new file mode 100644 index 0000000..fb98fec --- /dev/null +++ b/engine/src/bullet/com/jme3/bullet/objects/PhysicsCharacter.java @@ -0,0 +1,319 @@ +/* + * Copyright (c) 2009-2010 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.collision.PhysicsCollisionObject; +import com.jme3.bullet.collision.shapes.CollisionShape; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Basic Bullet Character + * @author normenhansen + */ +public class PhysicsCharacter extends PhysicsCollisionObject { + + protected long characterId = 0; + protected float stepHeight; + protected Vector3f walkDirection = new Vector3f(); + protected float fallSpeed = 55.0f; + protected float jumpSpeed = 10.0f; + protected int upAxis = 1; + protected boolean locationDirty = false; + //TEMP VARIABLES + protected final Quaternion tmp_inverseWorldRotation = new Quaternion(); + + public PhysicsCharacter() { + } + + /** + * @param shape The CollisionShape (no Mesh or CompoundCollisionShapes) + * @param stepHeight The quantization size for vertical movement + */ + public PhysicsCharacter(CollisionShape shape, float stepHeight) { + this.collisionShape = shape; +// if (shape instanceof MeshCollisionShape || shape instanceof CompoundCollisionShape) { +// throw (new UnsupportedOperationException("Kinematic character nodes cannot have mesh or compound collision shapes")); +// } + this.stepHeight = stepHeight; + buildObject(); + } + + protected void buildObject() { + if (objectId == 0) { + objectId = createGhostObject(); + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Creating GhostObject {0}", Long.toHexString(objectId)); + initUserPointer(); + } + setCharacterFlags(objectId); + attachCollisionShape(objectId, collisionShape.getObjectId()); + if (characterId != 0) { + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Clearing Character {0}", Long.toHexString(objectId)); + finalizeNativeCharacter(characterId); + } + characterId = createCharacterObject(objectId, collisionShape.getObjectId(), stepHeight); + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Creating Character {0}", Long.toHexString(characterId)); + } + + private native long createGhostObject(); + + private native void setCharacterFlags(long objectId); + + private native long createCharacterObject(long objectId, long shapeId, float stepHeight); + + /** + * Sets the location of this physics character + * @param location + */ + public void warp(Vector3f location) { + warp(characterId, location); + } + + private native void warp(long characterId, Vector3f location); + + /** + * Set the walk direction, works continuously. + * This should probably be called setPositionIncrementPerSimulatorStep. + * This is neither a direction nor a velocity, but the amount to + * increment the position each physics tick. So vector length = accuracy*speed in m/s + * @param vec the walk direction to set + */ + public void setWalkDirection(Vector3f vec) { + walkDirection.set(vec); + setWalkDirection(characterId, vec); + } + + private native void setWalkDirection(long characterId, Vector3f vec); + + /** + * @return the currently set walkDirection + */ + public Vector3f getWalkDirection() { + return walkDirection; + } + + public void setUpAxis(int axis) { + upAxis = axis; + setUpAxis(characterId, axis); + } + + private native void setUpAxis(long characterId, int axis); + + public int getUpAxis() { + return upAxis; + } + + public void setFallSpeed(float fallSpeed) { + this.fallSpeed = fallSpeed; + setFallSpeed(characterId, fallSpeed); + } + + private native void setFallSpeed(long characterId, float fallSpeed); + + public float getFallSpeed() { + return fallSpeed; + } + + public void setJumpSpeed(float jumpSpeed) { + this.jumpSpeed = jumpSpeed; + setJumpSpeed(characterId, jumpSpeed); + } + + private native void setJumpSpeed(long characterId, float jumpSpeed); + + public float getJumpSpeed() { + return jumpSpeed; + } + + public void setGravity(float value) { + setGravity(characterId, value); + } + + private native void setGravity(long characterId, float gravity); + + public float getGravity() { + return getGravity(characterId); + } + + private native float getGravity(long characterId); + + public void setMaxSlope(float slopeRadians) { + setMaxSlope(characterId, slopeRadians); + } + + private native void setMaxSlope(long characterId, float slopeRadians); + + public float getMaxSlope() { + return getMaxSlope(characterId); + } + + private native float getMaxSlope(long characterId); + + public boolean onGround() { + return onGround(characterId); + } + + private native boolean onGround(long characterId); + + public void jump() { + jump(characterId); + } + + private native void jump(long characterId); + + @Override + public void setCollisionShape(CollisionShape collisionShape) { +// if (!(collisionShape.getObjectId() instanceof ConvexShape)) { +// throw (new UnsupportedOperationException("Kinematic character nodes cannot have mesh collision shapes")); +// } + super.setCollisionShape(collisionShape); + if (objectId == 0) { + buildObject(); + } else { + attachCollisionShape(objectId, collisionShape.getObjectId()); + } + } + + /** + * Set the physics location (same as warp()) + * @param location the location of the actual physics object + */ + public void setPhysicsLocation(Vector3f location) { + warp(location); + } + + /** + * @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 vec); + + /** + * @return the physicsLocation + */ + public Vector3f getPhysicsLocation() { + return getPhysicsLocation(null); + } + + public void setCcdSweptSphereRadius(float radius) { + setCcdSweptSphereRadius(objectId, radius); + } + + private native void setCcdSweptSphereRadius(long objectId, float radius); + + 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); + + /** + * used internally + */ + public long getControllerId() { + return characterId; + } + + public void destroy() { + } + + @Override + public void write(JmeExporter e) throws IOException { + super.write(e); + OutputCapsule capsule = e.getCapsule(this); + capsule.write(stepHeight, "stepHeight", 1.0f); + capsule.write(getGravity(), "gravity", 9.8f * 3); + capsule.write(getMaxSlope(), "maxSlope", 1.0f); + capsule.write(fallSpeed, "fallSpeed", 55.0f); + capsule.write(jumpSpeed, "jumpSpeed", 10.0f); + capsule.write(upAxis, "upAxis", 1); + capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0); + capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0); + capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f()); + } + + @Override + public void read(JmeImporter e) throws IOException { + super.read(e); + InputCapsule capsule = e.getCapsule(this); + stepHeight = capsule.readFloat("stepHeight", 1.0f); + buildObject(); + setGravity(capsule.readFloat("gravity", 9.8f * 3)); + setMaxSlope(capsule.readFloat("maxSlope", 1.0f)); + setFallSpeed(capsule.readFloat("fallSpeed", 55.0f)); + setJumpSpeed(capsule.readFloat("jumpSpeed", 10.0f)); + setUpAxis(capsule.readInt("upAxis", 1)); + setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0)); + setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0)); + setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f())); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + finalizeNativeCharacter(characterId); + } + + private native void finalizeNativeCharacter(long characterId); +} diff --git a/engine/src/bullet/com/jme3/bullet/objects/PhysicsGhostObject.java b/engine/src/bullet/com/jme3/bullet/objects/PhysicsGhostObject.java new file mode 100644 index 0000000..2f87164 --- /dev/null +++ b/engine/src/bullet/com/jme3/bullet/objects/PhysicsGhostObject.java @@ -0,0 +1,301 @@ +/* + * Copyright (c) 2009-2010 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.collision.PhysicsCollisionObject; +import com.jme3.bullet.collision.shapes.CollisionShape; +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.Spatial; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * <i>From Bullet manual:</i><br> + * GhostObject can keep track of all objects that are overlapping. + * By default, this overlap is based on the AABB. + * This is useful for creating a character controller, + * collision sensors/triggers, explosions etc.<br> + * @author normenhansen + */ +public class PhysicsGhostObject extends PhysicsCollisionObject { + + protected boolean locationDirty = false; + protected final Quaternion tmp_inverseWorldRotation = new Quaternion(); + private List<PhysicsCollisionObject> overlappingObjects = new LinkedList<PhysicsCollisionObject>(); + + public PhysicsGhostObject() { + } + + public PhysicsGhostObject(CollisionShape shape) { + collisionShape = shape; + buildObject(); + } + + public PhysicsGhostObject(Spatial child, CollisionShape shape) { + collisionShape = shape; + buildObject(); + } + + protected void buildObject() { + if (objectId == 0) { +// gObject = new PairCachingGhostObject(); + objectId = createGhostObject(); + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Ghost Object {0}", Long.toHexString(objectId)); + setGhostFlags(objectId); + initUserPointer(); + } +// if (gObject == null) { +// gObject = new PairCachingGhostObject(); +// gObject.setCollisionFlags(gObject.getCollisionFlags() | CollisionFlags.NO_CONTACT_RESPONSE); +// } + attachCollisionShape(objectId, collisionShape.getObjectId()); + } + + private native long createGhostObject(); + + private native void setGhostFlags(long objectId); + + @Override + public void setCollisionShape(CollisionShape collisionShape) { + super.setCollisionShape(collisionShape); + if (objectId == 0) { + buildObject(); + } else { + attachCollisionShape(objectId, collisionShape.getObjectId()); + } + } + + /** + * 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; + } + + /** + * used internally + */ +// public PairCachingGhostObject getObjectId() { +// return gObject; +// } + /** + * destroys this PhysicsGhostNode and removes it from memory + */ + public void destroy() { + } + + /** + * Another Object is overlapping with this GhostNode, + * if and if only there CollisionShapes overlaps. + * They could be both regular PhysicsRigidBodys or PhysicsGhostObjects. + * @return All CollisionObjects overlapping with this GhostNode. + */ + public List<PhysicsCollisionObject> getOverlappingObjects() { + overlappingObjects.clear(); + getOverlappingObjects(objectId); +// for (com.bulletphysics.collision.dispatch.CollisionObject collObj : gObject.getOverlappingPairs()) { +// overlappingObjects.add((PhysicsCollisionObject) collObj.getUserPointer()); +// } + return overlappingObjects; + } + + protected native void getOverlappingObjects(long objectId); + + private void addOverlappingObject_native(PhysicsCollisionObject co) { + overlappingObjects.add(co); + } + + /** + * + * @return With how many other CollisionObjects this GhostNode is currently overlapping. + */ + public int getOverlappingCount() { + return getOverlappingCount(objectId); + } + + private native int getOverlappingCount(long objectId); + + /** + * + * @param index The index of the overlapping Node to retrieve. + * @return The Overlapping CollisionObject at the given index. + */ + public PhysicsCollisionObject getOverlapping(int index) { + return overlappingObjects.get(index); + } + + public void setCcdSweptSphereRadius(float radius) { + setCcdSweptSphereRadius(objectId, radius); + } + + private native void setCcdSweptSphereRadius(long objectId, float radius); + + 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); + + @Override + public void write(JmeExporter e) throws IOException { + super.write(e); + OutputCapsule capsule = e.getCapsule(this); + capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f()); + capsule.write(getPhysicsRotationMatrix(new Matrix3f()), "physicsRotation", new Matrix3f()); + capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0); + capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0); + } + + @Override + public void read(JmeImporter e) throws IOException { + super.read(e); + InputCapsule capsule = e.getCapsule(this); + buildObject(); + setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f())); + setPhysicsRotation(((Matrix3f) capsule.readSavable("physicsRotation", new Matrix3f()))); + setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0)); + setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0)); + } +} diff --git a/engine/src/bullet/com/jme3/bullet/objects/PhysicsRigidBody.java b/engine/src/bullet/com/jme3/bullet/objects/PhysicsRigidBody.java new file mode 100644 index 0000000..0d41798 --- /dev/null +++ b/engine/src/bullet/com/jme3/bullet/objects/PhysicsRigidBody.java @@ -0,0 +1,752 @@ +/* + * Copyright (c) 2009-2010 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 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; +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.INFO, "Clearing RigidBody {0}", Long.toHexString(objectId)); + finalizeNative(objectId); + } + preRebuild(); + objectId = createRigidBody(mass, motionState.getObjectId(), collisionShape.getObjectId()); + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "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(objectId); + } + + private native float getAngularFactor(long objectId); + + public void setAngularFactor(float factor) { + setAngularFactor(objectId, factor); + } + + private native void setAngularFactor(long objectId, float 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; + } + + @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(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)); + 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); + } +} diff --git a/engine/src/bullet/com/jme3/bullet/objects/PhysicsVehicle.java b/engine/src/bullet/com/jme3/bullet/objects/PhysicsVehicle.java new file mode 100644 index 0000000..fba1e83 --- /dev/null +++ b/engine/src/bullet/com/jme3/bullet/objects/PhysicsVehicle.java @@ -0,0 +1,584 @@ +/* + * Copyright (c) 2009-2010 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.shapes.CollisionShape; +import com.jme3.bullet.objects.infos.VehicleTuning; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +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.logging.Level; +import java.util.logging.Logger; + +/** + * <p>PhysicsVehicleNode - Special PhysicsNode that implements vehicle functions</p> + * <p> + * <i>From bullet manual:</i><br> + * For most vehicle simulations, it is recommended to use the simplified Bullet + * vehicle model as provided in btRaycastVehicle. Instead of simulation each wheel + * and chassis as separate rigid bodies, connected by constraints, it uses a simplified model. + * This simplified model has many benefits, and is widely used in commercial driving games.<br> + * The entire vehicle is represented as a single rigidbody, the chassis. + * The collision detection of the wheels is approximated by ray casts, + * and the tire friction is a basic anisotropic friction model. + * </p> + * @author normenhansen + */ +public class PhysicsVehicle extends PhysicsRigidBody { + + protected long vehicleId = 0; + protected long rayCasterId = 0; + protected VehicleTuning tuning = new VehicleTuning(); + protected ArrayList<VehicleWheel> wheels = new ArrayList<VehicleWheel>(); + protected PhysicsSpace physicsSpace; + + public PhysicsVehicle() { + } + + public PhysicsVehicle(CollisionShape shape) { + super(shape); + } + + public PhysicsVehicle(CollisionShape shape, float mass) { + super(shape, mass); + } + + /** + * used internally + */ + public void updateWheels() { + if (vehicleId != 0) { + for (int i = 0; i < wheels.size(); i++) { + updateWheelTransform(vehicleId, i, true); + wheels.get(i).updatePhysicsState(); + } + } + } + + private native void updateWheelTransform(long vehicleId, int wheel, boolean interpolated); + + /** + * used internally + */ + public void applyWheelTransforms() { + if (wheels != null) { + for (int i = 0; i < wheels.size(); i++) { + wheels.get(i).applyWheelTransform(); + } + } + } + + @Override + protected void postRebuild() { + super.postRebuild(); + motionState.setVehicle(this); + createVehicle(physicsSpace); + } + + /** + * Used internally, creates the actual vehicle constraint when vehicle is added to phyicsspace + */ + public void createVehicle(PhysicsSpace space) { + physicsSpace = space; + if (space == null) { + return; + } + if (space.getSpaceId() == 0) { + throw new IllegalStateException("Physics space is not initialized!"); + } + if (rayCasterId != 0) { + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Clearing RayCaster {0}", Long.toHexString(rayCasterId)); + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Clearing Vehicle {0}", Long.toHexString(vehicleId)); + finalizeNative(rayCasterId, vehicleId); + } + rayCasterId = createVehicleRaycaster(objectId, space.getSpaceId()); + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created RayCaster {0}", Long.toHexString(rayCasterId)); + vehicleId = createRaycastVehicle(objectId, rayCasterId); + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created Vehicle {0}", Long.toHexString(vehicleId)); + setCoordinateSystem(vehicleId, 0, 1, 2); + for (VehicleWheel wheel : wheels) { + wheel.setVehicleId(vehicleId, addWheel(vehicleId, wheel.getLocation(), wheel.getDirection(), wheel.getAxle(), wheel.getRestLength(), wheel.getRadius(), tuning, wheel.isFrontWheel())); + } + } + + private native long createVehicleRaycaster(long objectId, long physicsSpaceId); + + private native long createRaycastVehicle(long objectId, long rayCasterId); + + private native void setCoordinateSystem(long objectId, int a, int b, int c); + + private native int addWheel(long objectId, Vector3f location, Vector3f direction, Vector3f axle, float restLength, float radius, VehicleTuning tuning, boolean frontWheel); + + /** + * Add a wheel to this vehicle + * @param connectionPoint The starting point of the ray, where the suspension connects to the chassis (chassis space) + * @param direction the direction of the wheel (should be -Y / 0,-1,0 for a normal car) + * @param axle The axis of the wheel, pointing right in vehicle direction (should be -X / -1,0,0 for a normal car) + * @param suspensionRestLength The current length of the suspension (metres) + * @param wheelRadius the wheel radius + * @param isFrontWheel sets if this wheel is a front wheel (steering) + * @return the PhysicsVehicleWheel object to get/set infos on the wheel + */ + public VehicleWheel addWheel(Vector3f connectionPoint, Vector3f direction, Vector3f axle, float suspensionRestLength, float wheelRadius, boolean isFrontWheel) { + return addWheel(null, connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel); + } + + /** + * Add a wheel to this vehicle + * @param spat the wheel Geometry + * @param connectionPoint The starting point of the ray, where the suspension connects to the chassis (chassis space) + * @param direction the direction of the wheel (should be -Y / 0,-1,0 for a normal car) + * @param axle The axis of the wheel, pointing right in vehicle direction (should be -X / -1,0,0 for a normal car) + * @param suspensionRestLength The current length of the suspension (metres) + * @param wheelRadius the wheel radius + * @param isFrontWheel sets if this wheel is a front wheel (steering) + * @return the PhysicsVehicleWheel object to get/set infos on the wheel + */ + public VehicleWheel addWheel(Spatial spat, Vector3f connectionPoint, Vector3f direction, Vector3f axle, float suspensionRestLength, float wheelRadius, boolean isFrontWheel) { + VehicleWheel wheel = null; + if (spat == null) { + wheel = new VehicleWheel(connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel); + } else { + wheel = new VehicleWheel(spat, connectionPoint, direction, axle, suspensionRestLength, wheelRadius, isFrontWheel); + } + wheel.setFrictionSlip(tuning.frictionSlip); + wheel.setMaxSuspensionTravelCm(tuning.maxSuspensionTravelCm); + wheel.setSuspensionStiffness(tuning.suspensionStiffness); + wheel.setWheelsDampingCompression(tuning.suspensionCompression); + wheel.setWheelsDampingRelaxation(tuning.suspensionDamping); + wheel.setMaxSuspensionForce(tuning.maxSuspensionForce); + wheels.add(wheel); + if (vehicleId != 0) { + wheel.setVehicleId(vehicleId, addWheel(vehicleId, wheel.getLocation(), wheel.getDirection(), wheel.getAxle(), wheel.getRestLength(), wheel.getRadius(), tuning, wheel.isFrontWheel())); + } + if (debugShape != null) { + updateDebugShape(); + } + return wheel; + } + + /** + * This rebuilds the vehicle as there is no way in bullet to remove a wheel. + * @param wheel + */ + public void removeWheel(int wheel) { + wheels.remove(wheel); + rebuildRigidBody(); +// updateDebugShape(); + } + + /** + * You can get access to the single wheels via this method. + * @param wheel the wheel index + * @return the WheelInfo of the selected wheel + */ + public VehicleWheel getWheel(int wheel) { + return wheels.get(wheel); + } + + public int getNumWheels() { + return wheels.size(); + } + + /** + * @return the frictionSlip + */ + public float getFrictionSlip() { + return tuning.frictionSlip; + } + + /** + * Use before adding wheels, this is the default used when adding wheels. + * After adding the wheel, use direct wheel access.<br> + * The coefficient of friction between the tyre and the ground. + * Should be about 0.8 for realistic cars, but can increased for better handling. + * Set large (10000.0) for kart racers + * @param frictionSlip the frictionSlip to set + */ + public void setFrictionSlip(float frictionSlip) { + tuning.frictionSlip = frictionSlip; + } + + /** + * The coefficient of friction between the tyre and the ground. + * Should be about 0.8 for realistic cars, but can increased for better handling. + * Set large (10000.0) for kart racers + * @param wheel + * @param frictionSlip + */ + public void setFrictionSlip(int wheel, float frictionSlip) { + wheels.get(wheel).setFrictionSlip(frictionSlip); + } + + /** + * Reduces the rolling torque applied from the wheels that cause the vehicle to roll over. + * This is a bit of a hack, but it's quite effective. 0.0 = no roll, 1.0 = physical behaviour. + * If m_frictionSlip is too high, you'll need to reduce this to stop the vehicle rolling over. + * You should also try lowering the vehicle's centre of mass + */ + public void setRollInfluence(int wheel, float rollInfluence) { + wheels.get(wheel).setRollInfluence(rollInfluence); + } + + /** + * @return the maxSuspensionTravelCm + */ + public float getMaxSuspensionTravelCm() { + return tuning.maxSuspensionTravelCm; + } + + /** + * Use before adding wheels, this is the default used when adding wheels. + * After adding the wheel, use direct wheel access.<br> + * The maximum distance the suspension can be compressed (centimetres) + * @param maxSuspensionTravelCm the maxSuspensionTravelCm to set + */ + public void setMaxSuspensionTravelCm(float maxSuspensionTravelCm) { + tuning.maxSuspensionTravelCm = maxSuspensionTravelCm; + } + + /** + * The maximum distance the suspension can be compressed (centimetres) + * @param wheel + * @param maxSuspensionTravelCm + */ + public void setMaxSuspensionTravelCm(int wheel, float maxSuspensionTravelCm) { + wheels.get(wheel).setMaxSuspensionTravelCm(maxSuspensionTravelCm); + } + + public float getMaxSuspensionForce() { + return tuning.maxSuspensionForce; + } + + /** + * This vaue caps the maximum suspension force, raise this above the default 6000 if your suspension cannot + * handle the weight of your vehcile. + * @param maxSuspensionForce + */ + public void setMaxSuspensionForce(float maxSuspensionForce) { + tuning.maxSuspensionForce = maxSuspensionForce; + } + + /** + * This vaue caps the maximum suspension force, raise this above the default 6000 if your suspension cannot + * handle the weight of your vehcile. + * @param wheel + * @param maxSuspensionForce + */ + public void setMaxSuspensionForce(int wheel, float maxSuspensionForce) { + wheels.get(wheel).setMaxSuspensionForce(maxSuspensionForce); + } + + /** + * @return the suspensionCompression + */ + public float getSuspensionCompression() { + return tuning.suspensionCompression; + } + + /** + * Use before adding wheels, this is the default used when adding wheels. + * After adding the wheel, use direct wheel access.<br> + * The damping coefficient for when the suspension is compressed. + * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br> + * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br> + * 0.1 to 0.3 are good values + * @param suspensionCompression the suspensionCompression to set + */ + public void setSuspensionCompression(float suspensionCompression) { + tuning.suspensionCompression = suspensionCompression; + } + + /** + * The damping coefficient for when the suspension is compressed. + * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br> + * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br> + * 0.1 to 0.3 are good values + * @param wheel + * @param suspensionCompression + */ + public void setSuspensionCompression(int wheel, float suspensionCompression) { + wheels.get(wheel).setWheelsDampingCompression(suspensionCompression); + } + + /** + * @return the suspensionDamping + */ + public float getSuspensionDamping() { + return tuning.suspensionDamping; + } + + /** + * Use before adding wheels, this is the default used when adding wheels. + * After adding the wheel, use direct wheel access.<br> + * The damping coefficient for when the suspension is expanding. + * See the comments for setSuspensionCompression for how to set k. + * @param suspensionDamping the suspensionDamping to set + */ + public void setSuspensionDamping(float suspensionDamping) { + tuning.suspensionDamping = suspensionDamping; + } + + /** + * The damping coefficient for when the suspension is expanding. + * See the comments for setSuspensionCompression for how to set k. + * @param wheel + * @param suspensionDamping + */ + public void setSuspensionDamping(int wheel, float suspensionDamping) { + wheels.get(wheel).setWheelsDampingRelaxation(suspensionDamping); + } + + /** + * @return the suspensionStiffness + */ + public float getSuspensionStiffness() { + return tuning.suspensionStiffness; + } + + /** + * Use before adding wheels, this is the default used when adding wheels. + * After adding the wheel, use direct wheel access.<br> + * The stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car + * @param suspensionStiffness + */ + public void setSuspensionStiffness(float suspensionStiffness) { + tuning.suspensionStiffness = suspensionStiffness; + } + + /** + * The stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car + * @param wheel + * @param suspensionStiffness + */ + public void setSuspensionStiffness(int wheel, float suspensionStiffness) { + wheels.get(wheel).setSuspensionStiffness(suspensionStiffness); + } + + /** + * Reset the suspension + */ + public void resetSuspension() { + resetSuspension(vehicleId); + } + + private native void resetSuspension(long vehicleId); + + /** + * Apply the given engine force to all wheels, works continuously + * @param force the force + */ + public void accelerate(float force) { + for (int i = 0; i < wheels.size(); i++) { + accelerate(i, force); + } + } + + /** + * Apply the given engine force, works continuously + * @param wheel the wheel to apply the force on + * @param force the force + */ + public void accelerate(int wheel, float force) { + applyEngineForce(vehicleId, wheel, force); + + } + + private native void applyEngineForce(long vehicleId, int wheel, float force); + + /** + * Set the given steering value to all front wheels (0 = forward) + * @param value the steering angle of the front wheels (Pi = 360deg) + */ + public void steer(float value) { + for (int i = 0; i < wheels.size(); i++) { + if (getWheel(i).isFrontWheel()) { + steer(i, value); + } + } + } + + /** + * Set the given steering value to the given wheel (0 = forward) + * @param wheel the wheel to set the steering on + * @param value the steering angle of the front wheels (Pi = 360deg) + */ + public void steer(int wheel, float value) { + steer(vehicleId, wheel, value); + } + + private native void steer(long vehicleId, int wheel, float value); + + /** + * Apply the given brake force to all wheels, works continuously + * @param force the force + */ + public void brake(float force) { + for (int i = 0; i < wheels.size(); i++) { + brake(i, force); + } + } + + /** + * Apply the given brake force, works continuously + * @param wheel the wheel to apply the force on + * @param force the force + */ + public void brake(int wheel, float force) { + brake(vehicleId, wheel, force); + } + + private native void brake(long vehicleId, int wheel, float force); + + /** + * Get the current speed of the vehicle in km/h + * @return + */ + public float getCurrentVehicleSpeedKmHour() { + return getCurrentVehicleSpeedKmHour(vehicleId); + } + + private native float getCurrentVehicleSpeedKmHour(long vehicleId); + + /** + * Get the current forward vector of the vehicle in world coordinates + * @param vector + * @return + */ + public Vector3f getForwardVector(Vector3f vector) { + if (vector == null) { + vector = new Vector3f(); + } + getForwardVector(vehicleId, vector); + return vector; + } + + private native void getForwardVector(long objectId, Vector3f vector); + + /** + * used internally + */ + public long getVehicleId() { + return vehicleId; + } + + @Override + protected Spatial getDebugShape() { + 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<VehicleWheel> it = wheels.iterator(); it.hasNext();) { + VehicleWheel physicsVehicleWheel = it.next(); + Vector3f location = physicsVehicleWheel.getLocation().clone(); + Vector3f direction = physicsVehicleWheel.getDirection().clone(); + Vector3f axle = physicsVehicleWheel.getAxle().clone(); + float restLength = physicsVehicleWheel.getRestLength(); + float radius = physicsVehicleWheel.getRadius(); + + Arrow locArrow = new Arrow(location); + Arrow axleArrow = new Arrow(axle.normalizeLocal().multLocal(0.3f)); + Arrow wheelArrow = new Arrow(direction.normalizeLocal().multLocal(radius)); + Arrow dirArrow = new Arrow(direction.normalizeLocal().multLocal(restLength)); + Geometry locGeom = new Geometry("WheelLocationDebugShape" + i, locArrow); + Geometry dirGeom = new Geometry("WheelDirectionDebugShape" + i, dirArrow); + Geometry axleGeom = new Geometry("WheelAxleDebugShape" + i, axleArrow); + Geometry wheelGeom = new Geometry("WheelRadiusDebugShape" + i, wheelArrow); + dirGeom.setLocalTranslation(location); + axleGeom.setLocalTranslation(location.add(direction)); + wheelGeom.setLocalTranslation(location.add(direction)); + locGeom.setMaterial(debugMaterialGreen); + dirGeom.setMaterial(debugMaterialGreen); + axleGeom.setMaterial(debugMaterialGreen); + wheelGeom.setMaterial(debugMaterialGreen); + node.attachChild(locGeom); + node.attachChild(dirGeom); + node.attachChild(axleGeom); + node.attachChild(wheelGeom); + i++; + } + return node; + } + + @Override + public void read(JmeImporter im) throws IOException { + InputCapsule capsule = im.getCapsule(this); + tuning = new VehicleTuning(); + tuning.frictionSlip = capsule.readFloat("frictionSlip", 10.5f); + tuning.maxSuspensionTravelCm = capsule.readFloat("maxSuspensionTravelCm", 500f); + tuning.maxSuspensionForce = capsule.readFloat("maxSuspensionForce", 6000f); + tuning.suspensionCompression = capsule.readFloat("suspensionCompression", 0.83f); + tuning.suspensionDamping = capsule.readFloat("suspensionDamping", 0.88f); + tuning.suspensionStiffness = capsule.readFloat("suspensionStiffness", 5.88f); + wheels = capsule.readSavableArrayList("wheelsList", new ArrayList<VehicleWheel>()); + motionState.setVehicle(this); + super.read(im); + } + + @Override + public void write(JmeExporter ex) throws IOException { + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(tuning.frictionSlip, "frictionSlip", 10.5f); + capsule.write(tuning.maxSuspensionTravelCm, "maxSuspensionTravelCm", 500f); + capsule.write(tuning.maxSuspensionForce, "maxSuspensionForce", 6000f); + capsule.write(tuning.suspensionCompression, "suspensionCompression", 0.83f); + capsule.write(tuning.suspensionDamping, "suspensionDamping", 0.88f); + capsule.write(tuning.suspensionStiffness, "suspensionStiffness", 5.88f); + capsule.writeSavableArrayList(wheels, "wheelsList", new ArrayList<VehicleWheel>()); + super.write(ex); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing RayCaster {0}", Long.toHexString(rayCasterId)); + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing Vehicle {0}", Long.toHexString(vehicleId)); + finalizeNative(rayCasterId, vehicleId); + } + + private native void finalizeNative(long rayCaster, long vehicle); +} diff --git a/engine/src/bullet/com/jme3/bullet/objects/VehicleWheel.java b/engine/src/bullet/com/jme3/bullet/objects/VehicleWheel.java new file mode 100644 index 0000000..11eab60 --- /dev/null +++ b/engine/src/bullet/com/jme3/bullet/objects/VehicleWheel.java @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2009-2010 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.collision.PhysicsCollisionObject; +import com.jme3.export.*; +import com.jme3.math.Matrix3f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.scene.Spatial; +import java.io.IOException; + +/** + * Stores info about one wheel of a PhysicsVehicle + * @author normenhansen + */ +public class VehicleWheel implements Savable { + + protected long wheelId = 0; + protected int wheelIndex = 0; + protected boolean frontWheel; + protected Vector3f location = new Vector3f(); + protected Vector3f direction = new Vector3f(); + protected Vector3f axle = new Vector3f(); + protected float suspensionStiffness = 20.0f; + protected float wheelsDampingRelaxation = 2.3f; + protected float wheelsDampingCompression = 4.4f; + protected float frictionSlip = 10.5f; + protected float rollInfluence = 1.0f; + protected float maxSuspensionTravelCm = 500f; + protected float maxSuspensionForce = 6000f; + protected float radius = 0.5f; + protected float restLength = 1f; + protected Vector3f wheelWorldLocation = new Vector3f(); + protected Quaternion wheelWorldRotation = new Quaternion(); + protected Spatial wheelSpatial; + protected Matrix3f tmp_Matrix = new com.jme3.math.Matrix3f(); + protected final Quaternion tmp_inverseWorldRotation = new Quaternion(); + private boolean applyLocal = false; + + public VehicleWheel() { + } + + public VehicleWheel(Spatial spat, Vector3f location, Vector3f direction, Vector3f axle, + float restLength, float radius, boolean frontWheel) { + this(location, direction, axle, restLength, radius, frontWheel); + wheelSpatial = spat; + } + + public VehicleWheel(Vector3f location, Vector3f direction, Vector3f axle, + float restLength, float radius, boolean frontWheel) { + this.location.set(location); + this.direction.set(direction); + this.axle.set(axle); + this.frontWheel = frontWheel; + this.restLength = restLength; + this.radius = radius; + } + + public synchronized void updatePhysicsState() { + getWheelLocation(wheelId, wheelIndex, wheelWorldLocation); + getWheelRotation(wheelId, wheelIndex, tmp_Matrix); + wheelWorldRotation.fromRotationMatrix(tmp_Matrix); + } + + private native void getWheelLocation(long vehicleId, int wheelId, Vector3f location); + + private native void getWheelRotation(long vehicleId, int wheelId, Matrix3f location); + + public synchronized void applyWheelTransform() { + if (wheelSpatial == null) { + return; + } + Quaternion localRotationQuat = wheelSpatial.getLocalRotation(); + Vector3f localLocation = wheelSpatial.getLocalTranslation(); + if (!applyLocal && wheelSpatial.getParent() != null) { + localLocation.set(wheelWorldLocation).subtractLocal(wheelSpatial.getParent().getWorldTranslation()); + localLocation.divideLocal(wheelSpatial.getParent().getWorldScale()); + tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation); + + localRotationQuat.set(wheelWorldRotation); + tmp_inverseWorldRotation.set(wheelSpatial.getParent().getWorldRotation()).inverseLocal().mult(localRotationQuat, localRotationQuat); + + wheelSpatial.setLocalTranslation(localLocation); + wheelSpatial.setLocalRotation(localRotationQuat); + } else { + wheelSpatial.setLocalTranslation(wheelWorldLocation); + wheelSpatial.setLocalRotation(wheelWorldRotation); + } + } + + public long getWheelId() { + return wheelId; + } + + public void setVehicleId(long vehicleId, int wheelIndex) { + this.wheelId = vehicleId; + this.wheelIndex = wheelIndex; + applyInfo(); + } + + public boolean isFrontWheel() { + return frontWheel; + } + + public void setFrontWheel(boolean frontWheel) { + this.frontWheel = frontWheel; + applyInfo(); + } + + public Vector3f getLocation() { + return location; + } + + public Vector3f getDirection() { + return direction; + } + + public Vector3f getAxle() { + return axle; + } + + public float getSuspensionStiffness() { + return suspensionStiffness; + } + + /** + * the stiffness constant for the suspension. 10.0 - Offroad buggy, 50.0 - Sports car, 200.0 - F1 Car + * @param suspensionStiffness + */ + public void setSuspensionStiffness(float suspensionStiffness) { + this.suspensionStiffness = suspensionStiffness; + applyInfo(); + } + + public float getWheelsDampingRelaxation() { + return wheelsDampingRelaxation; + } + + /** + * the damping coefficient for when the suspension is expanding. + * See the comments for setWheelsDampingCompression for how to set k. + * @param wheelsDampingRelaxation + */ + public void setWheelsDampingRelaxation(float wheelsDampingRelaxation) { + this.wheelsDampingRelaxation = wheelsDampingRelaxation; + applyInfo(); + } + + public float getWheelsDampingCompression() { + return wheelsDampingCompression; + } + + /** + * the damping coefficient for when the suspension is compressed. + * Set to k * 2.0 * FastMath.sqrt(m_suspensionStiffness) so k is proportional to critical damping.<br> + * k = 0.0 undamped & bouncy, k = 1.0 critical damping<br> + * 0.1 to 0.3 are good values + * @param wheelsDampingCompression + */ + public void setWheelsDampingCompression(float wheelsDampingCompression) { + this.wheelsDampingCompression = wheelsDampingCompression; + applyInfo(); + } + + public float getFrictionSlip() { + return frictionSlip; + } + + /** + * the coefficient of friction between the tyre and the ground. + * Should be about 0.8 for realistic cars, but can increased for better handling. + * Set large (10000.0) for kart racers + * @param frictionSlip + */ + public void setFrictionSlip(float frictionSlip) { + this.frictionSlip = frictionSlip; + applyInfo(); + } + + public float getRollInfluence() { + return rollInfluence; + } + + /** + * reduces the rolling torque applied from the wheels that cause the vehicle to roll over. + * This is a bit of a hack, but it's quite effective. 0.0 = no roll, 1.0 = physical behaviour. + * If m_frictionSlip is too high, you'll need to reduce this to stop the vehicle rolling over. + * You should also try lowering the vehicle's centre of mass + * @param rollInfluence the rollInfluence to set + */ + public void setRollInfluence(float rollInfluence) { + this.rollInfluence = rollInfluence; + applyInfo(); + } + + public float getMaxSuspensionTravelCm() { + return maxSuspensionTravelCm; + } + + /** + * the maximum distance the suspension can be compressed (centimetres) + * @param maxSuspensionTravelCm + */ + public void setMaxSuspensionTravelCm(float maxSuspensionTravelCm) { + this.maxSuspensionTravelCm = maxSuspensionTravelCm; + applyInfo(); + } + + public float getMaxSuspensionForce() { + return maxSuspensionForce; + } + + /** + * The maximum suspension force, raise this above the default 6000 if your suspension cannot + * handle the weight of your vehcile. + * @param maxSuspensionTravelCm + */ + public void setMaxSuspensionForce(float maxSuspensionForce) { + this.maxSuspensionForce = maxSuspensionForce; + applyInfo(); + } + + private void applyInfo() { + if (wheelId == 0) { + return; + } + applyInfo(wheelId, wheelIndex, suspensionStiffness, wheelsDampingRelaxation, wheelsDampingCompression, frictionSlip, rollInfluence, maxSuspensionTravelCm, maxSuspensionForce, radius, frontWheel, restLength); + } + + private native void applyInfo(long wheelId, int wheelIndex, + float suspensionStiffness, + float wheelsDampingRelaxation, + float wheelsDampingCompression, + float frictionSlip, + float rollInfluence, + float maxSuspensionTravelCm, + float maxSuspensionForce, + float wheelsRadius, + boolean frontWheel, + float suspensionRestLength); + + public float getRadius() { + return radius; + } + + public void setRadius(float radius) { + this.radius = radius; + applyInfo(); + } + + public float getRestLength() { + return restLength; + } + + public void setRestLength(float restLength) { + this.restLength = restLength; + applyInfo(); + } + + /** + * returns the object this wheel is in contact with or null if no contact + * @return the PhysicsCollisionObject (PhysicsRigidBody, PhysicsGhostObject) + */ + public PhysicsCollisionObject getGroundObject() { +// if (wheelInfo.raycastInfo.groundObject == null) { +// return null; +// } else if (wheelInfo.raycastInfo.groundObject instanceof RigidBody) { +// System.out.println("RigidBody"); +// return (PhysicsRigidBody) ((RigidBody) wheelInfo.raycastInfo.groundObject).getUserPointer(); +// } else { + return null; +// } + } + + /** + * returns the location where the wheel collides with the ground (world space) + */ + public Vector3f getCollisionLocation(Vector3f vec) { + getCollisionLocation(wheelId, wheelIndex, vec); + return vec; + } + + private native void getCollisionLocation(long wheelId, int wheelIndex, Vector3f vec); + + /** + * returns the location where the wheel collides with the ground (world space) + */ + public Vector3f getCollisionLocation() { + Vector3f vec = new Vector3f(); + getCollisionLocation(wheelId, wheelIndex, vec); + return vec; + } + + /** + * returns the normal where the wheel collides with the ground (world space) + */ + public Vector3f getCollisionNormal(Vector3f vec) { + getCollisionNormal(wheelId, wheelIndex, vec); + return vec; + } + + private native void getCollisionNormal(long wheelId, int wheelIndex, Vector3f vec); + + /** + * returns the normal where the wheel collides with the ground (world space) + */ + public Vector3f getCollisionNormal() { + Vector3f vec = new Vector3f(); + getCollisionNormal(wheelId, wheelIndex, vec); + return vec; + } + + /** + * returns how much the wheel skids on the ground (for skid sounds/smoke etc.)<br> + * 0.0 = wheels are sliding, 1.0 = wheels have traction. + */ + public float getSkidInfo() { + return getSkidInfo(wheelId, wheelIndex); + } + + public native float getSkidInfo(long wheelId, int wheelIndex); + + /** + * returns how many degrees the wheel has turned since the last physics + * step. + */ + public float getDeltaRotation() { + return getDeltaRotation(wheelId, wheelIndex); + } + + public native float getDeltaRotation(long wheelId, int wheelIndex); + + @Override + public void read(JmeImporter im) throws IOException { + InputCapsule capsule = im.getCapsule(this); + wheelSpatial = (Spatial) capsule.readSavable("wheelSpatial", null); + frontWheel = capsule.readBoolean("frontWheel", false); + location = (Vector3f) capsule.readSavable("wheelLocation", new Vector3f()); + direction = (Vector3f) capsule.readSavable("wheelDirection", new Vector3f()); + axle = (Vector3f) capsule.readSavable("wheelAxle", new Vector3f()); + suspensionStiffness = capsule.readFloat("suspensionStiffness", 20.0f); + wheelsDampingRelaxation = capsule.readFloat("wheelsDampingRelaxation", 2.3f); + wheelsDampingCompression = capsule.readFloat("wheelsDampingCompression", 4.4f); + frictionSlip = capsule.readFloat("frictionSlip", 10.5f); + rollInfluence = capsule.readFloat("rollInfluence", 1.0f); + maxSuspensionTravelCm = capsule.readFloat("maxSuspensionTravelCm", 500f); + maxSuspensionForce = capsule.readFloat("maxSuspensionForce", 6000f); + radius = capsule.readFloat("wheelRadius", 0.5f); + restLength = capsule.readFloat("restLength", 1f); + } + + @Override + public void write(JmeExporter ex) throws IOException { + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(wheelSpatial, "wheelSpatial", null); + capsule.write(frontWheel, "frontWheel", false); + capsule.write(location, "wheelLocation", new Vector3f()); + capsule.write(direction, "wheelDirection", new Vector3f()); + capsule.write(axle, "wheelAxle", new Vector3f()); + capsule.write(suspensionStiffness, "suspensionStiffness", 20.0f); + capsule.write(wheelsDampingRelaxation, "wheelsDampingRelaxation", 2.3f); + capsule.write(wheelsDampingCompression, "wheelsDampingCompression", 4.4f); + capsule.write(frictionSlip, "frictionSlip", 10.5f); + capsule.write(rollInfluence, "rollInfluence", 1.0f); + capsule.write(maxSuspensionTravelCm, "maxSuspensionTravelCm", 500f); + capsule.write(maxSuspensionForce, "maxSuspensionForce", 6000f); + capsule.write(radius, "wheelRadius", 0.5f); + capsule.write(restLength, "restLength", 1f); + } + + /** + * @return the wheelSpatial + */ + public Spatial getWheelSpatial() { + return wheelSpatial; + } + + /** + * @param wheelSpatial the wheelSpatial to set + */ + public void setWheelSpatial(Spatial wheelSpatial) { + this.wheelSpatial = wheelSpatial; + } + + public boolean isApplyLocal() { + return applyLocal; + } + + public void setApplyLocal(boolean applyLocal) { + this.applyLocal = applyLocal; + } + +} diff --git a/engine/src/bullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java b/engine/src/bullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java new file mode 100644 index 0000000..c60d6c5 --- /dev/null +++ b/engine/src/bullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2009-2010 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.infos; + +import com.jme3.bullet.objects.PhysicsVehicle; +import com.jme3.math.Matrix3f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.scene.Spatial; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * stores transform info of a PhysicsNode in a threadsafe manner to + * allow multithreaded access from the jme scenegraph and the bullet physicsspace + * @author normenhansen + */ +public class RigidBodyMotionState { + long motionStateId = 0; + private Vector3f worldLocation = new Vector3f(); + private Matrix3f worldRotation = new Matrix3f(); + private Quaternion worldRotationQuat = new Quaternion(); + private Quaternion tmp_inverseWorldRotation = new Quaternion(); + private PhysicsVehicle vehicle; + private boolean applyPhysicsLocal = false; +// protected LinkedList<PhysicsMotionStateListener> listeners = new LinkedList<PhysicsMotionStateListener>(); + + public RigidBodyMotionState() { + this.motionStateId = createMotionState(); + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Created MotionState {0}", Long.toHexString(motionStateId)); + } + + private native long createMotionState(); + + /** + * applies the current transform to the given jme Node if the location has been updated on the physics side + * @param spatial + */ + public synchronized boolean applyTransform(Spatial spatial) { + Vector3f localLocation = spatial.getLocalTranslation(); + Quaternion localRotationQuat = spatial.getLocalRotation(); + boolean physicsLocationDirty = applyTransform(motionStateId, localLocation, localRotationQuat); + if (!physicsLocationDirty) { + return false; + } + if (!applyPhysicsLocal && spatial.getParent() != null) { + localLocation.subtractLocal(spatial.getParent().getWorldTranslation()); + localLocation.divideLocal(spatial.getParent().getWorldScale()); + tmp_inverseWorldRotation.set(spatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation); + +// localRotationQuat.set(worldRotationQuat); + tmp_inverseWorldRotation.mult(localRotationQuat, localRotationQuat); + + spatial.setLocalTranslation(localLocation); + spatial.setLocalRotation(localRotationQuat); + } else { + spatial.setLocalTranslation(localLocation); + spatial.setLocalRotation(localRotationQuat); +// spatial.setLocalTranslation(worldLocation); +// spatial.setLocalRotation(worldRotationQuat); + } + if (vehicle != null) { + vehicle.updateWheels(); + } + return true; + } + + private synchronized native boolean applyTransform(long stateId, Vector3f location, Quaternion rotation); + + /** + * @return the worldLocation + */ + public Vector3f getWorldLocation() { + getWorldLocation(motionStateId, worldLocation); + return worldLocation; + } + + private native void getWorldLocation(long stateId, Vector3f vec); + + /** + * @return the worldRotation + */ + public Matrix3f getWorldRotation() { + getWorldRotation(motionStateId, worldRotation); + return worldRotation; + } + + private native void getWorldRotation(long stateId, Matrix3f vec); + + /** + * @return the worldRotationQuat + */ + public Quaternion getWorldRotationQuat() { + getWorldRotationQuat(motionStateId, worldRotationQuat); + return worldRotationQuat; + } + + private native void getWorldRotationQuat(long stateId, Quaternion vec); + + /** + * @param vehicle the vehicle to set + */ + public void setVehicle(PhysicsVehicle vehicle) { + this.vehicle = vehicle; + } + + public boolean isApplyPhysicsLocal() { + return applyPhysicsLocal; + } + + public void setApplyPhysicsLocal(boolean applyPhysicsLocal) { + this.applyPhysicsLocal = applyPhysicsLocal; + } + + public long getObjectId(){ + return motionStateId; + } +// public void addMotionStateListener(PhysicsMotionStateListener listener){ +// listeners.add(listener); +// } +// +// public void removeMotionStateListener(PhysicsMotionStateListener listener){ +// listeners.remove(listener); +// } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing MotionState {0}", Long.toHexString(motionStateId)); + finalizeNative(motionStateId); + } + + private native void finalizeNative(long objectId); +} diff --git a/engine/src/bullet/com/jme3/bullet/objects/infos/VehicleTuning.java b/engine/src/bullet/com/jme3/bullet/objects/infos/VehicleTuning.java new file mode 100644 index 0000000..892c9bd --- /dev/null +++ b/engine/src/bullet/com/jme3/bullet/objects/infos/VehicleTuning.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2009-2010 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.infos; + +/** + * + * @author normenhansen + */ +public class VehicleTuning { + public float suspensionStiffness = 5.88f; + public float suspensionCompression = 0.83f; + public float suspensionDamping = 0.88f; + public float maxSuspensionTravelCm = 500f; + public float maxSuspensionForce = 6000f; + public float frictionSlip = 10.5f; +} |