diff options
Diffstat (limited to 'engine/src/jbullet')
35 files changed, 7347 insertions, 0 deletions
diff --git a/engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java b/engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java new file mode 100644 index 0000000..e4dba9c --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java @@ -0,0 +1,853 @@ +/* + * 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; + +import com.bulletphysics.BulletGlobals; +import com.bulletphysics.ContactAddedCallback; +import com.bulletphysics.ContactDestroyedCallback; +import com.bulletphysics.ContactProcessedCallback; +import com.bulletphysics.collision.broadphase.*; +import com.bulletphysics.collision.dispatch.CollisionWorld.LocalConvexResult; +import com.bulletphysics.collision.dispatch.CollisionWorld.LocalRayResult; +import com.bulletphysics.collision.dispatch.*; +import com.bulletphysics.collision.narrowphase.ManifoldPoint; +import com.bulletphysics.collision.shapes.ConvexShape; +import com.bulletphysics.dynamics.DiscreteDynamicsWorld; +import com.bulletphysics.dynamics.DynamicsWorld; +import com.bulletphysics.dynamics.InternalTickCallback; +import com.bulletphysics.dynamics.RigidBody; +import com.bulletphysics.dynamics.constraintsolver.ConstraintSolver; +import com.bulletphysics.dynamics.constraintsolver.SequentialImpulseConstraintSolver; +import com.bulletphysics.extras.gimpact.GImpactCollisionAlgorithm; +import com.jme3.app.AppTask; +import com.jme3.asset.AssetManager; +import com.jme3.bullet.collision.*; +import com.jme3.bullet.collision.shapes.CollisionShape; +import com.jme3.bullet.control.PhysicsControl; +import com.jme3.bullet.control.RigidBodyControl; +import com.jme3.bullet.joints.PhysicsJoint; +import com.jme3.bullet.objects.PhysicsCharacter; +import com.jme3.bullet.objects.PhysicsGhostObject; +import com.jme3.bullet.objects.PhysicsRigidBody; +import com.jme3.bullet.objects.PhysicsVehicle; +import com.jme3.bullet.util.Converter; +import com.jme3.math.Transform; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * <p>PhysicsSpace - The central jbullet-jme physics space</p> + * @author normenhansen + */ +public class PhysicsSpace { + + public static final int AXIS_X = 0; + public static final int AXIS_Y = 1; + public static final int AXIS_Z = 2; + private static ThreadLocal<ConcurrentLinkedQueue<AppTask<?>>> pQueueTL = + new ThreadLocal<ConcurrentLinkedQueue<AppTask<?>>>() { + + @Override + protected ConcurrentLinkedQueue<AppTask<?>> initialValue() { + return new ConcurrentLinkedQueue<AppTask<?>>(); + } + }; + private ConcurrentLinkedQueue<AppTask<?>> pQueue = new ConcurrentLinkedQueue<AppTask<?>>(); + private static ThreadLocal<PhysicsSpace> physicsSpaceTL = new ThreadLocal<PhysicsSpace>(); + private DiscreteDynamicsWorld dynamicsWorld = null; + private BroadphaseInterface broadphase; + private BroadphaseType broadphaseType = BroadphaseType.DBVT; + private CollisionDispatcher dispatcher; + private ConstraintSolver solver; + private DefaultCollisionConfiguration collisionConfiguration; +// private Map<GhostObject, PhysicsGhostObject> physicsGhostNodes = new ConcurrentHashMap<GhostObject, PhysicsGhostObject>(); + private Map<RigidBody, PhysicsRigidBody> physicsNodes = new ConcurrentHashMap<RigidBody, PhysicsRigidBody>(); + private List<PhysicsJoint> physicsJoints = new LinkedList<PhysicsJoint>(); + private List<PhysicsCollisionListener> collisionListeners = new LinkedList<PhysicsCollisionListener>(); + private List<PhysicsCollisionEvent> collisionEvents = new LinkedList<PhysicsCollisionEvent>(); + private Map<Integer, PhysicsCollisionGroupListener> collisionGroupListeners = new ConcurrentHashMap<Integer, PhysicsCollisionGroupListener>(); + private ConcurrentLinkedQueue<PhysicsTickListener> tickListeners = new ConcurrentLinkedQueue<PhysicsTickListener>(); + private PhysicsCollisionEventFactory eventFactory = new PhysicsCollisionEventFactory(); + private Vector3f worldMin = new Vector3f(-10000f, -10000f, -10000f); + private Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f); + private float accuracy = 1f / 60f; + private int maxSubSteps = 4; + private javax.vecmath.Vector3f rayVec1 = new javax.vecmath.Vector3f(); + private javax.vecmath.Vector3f rayVec2 = new javax.vecmath.Vector3f(); + private com.bulletphysics.linearmath.Transform sweepTrans1 = new com.bulletphysics.linearmath.Transform(new javax.vecmath.Matrix3f()); + private com.bulletphysics.linearmath.Transform sweepTrans2 = new com.bulletphysics.linearmath.Transform(new javax.vecmath.Matrix3f()); + private AssetManager debugManager; + + /** + * Get the current PhysicsSpace <b>running on this thread</b><br/> + * For parallel physics, this can also be called from the OpenGL thread to receive the PhysicsSpace + * @return the PhysicsSpace running on this thread + */ + public static PhysicsSpace getPhysicsSpace() { + return physicsSpaceTL.get(); + } + + /** + * Used internally + * @param space + */ + public static void setLocalThreadPhysicsSpace(PhysicsSpace space) { + physicsSpaceTL.set(space); + } + + public PhysicsSpace() { + this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), BroadphaseType.DBVT); + } + + public PhysicsSpace(BroadphaseType broadphaseType) { + this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType); + } + + public PhysicsSpace(Vector3f worldMin, Vector3f worldMax) { + this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3); + } + + public PhysicsSpace(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType) { + this.worldMin.set(worldMin); + this.worldMax.set(worldMax); + this.broadphaseType = broadphaseType; + create(); + } + + /** + * Has to be called from the (designated) physics thread + */ + public void create() { + pQueueTL.set(pQueue); + + collisionConfiguration = new DefaultCollisionConfiguration(); + dispatcher = new CollisionDispatcher(collisionConfiguration); + switch (broadphaseType) { + case SIMPLE: + broadphase = new SimpleBroadphase(); + break; + case AXIS_SWEEP_3: + broadphase = new AxisSweep3(Converter.convert(worldMin), Converter.convert(worldMax)); + break; + case AXIS_SWEEP_3_32: + broadphase = new AxisSweep3_32(Converter.convert(worldMin), Converter.convert(worldMax)); + break; + case DBVT: + broadphase = new DbvtBroadphase(); + break; + } + + solver = new SequentialImpulseConstraintSolver(); + + dynamicsWorld = new DiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration); + dynamicsWorld.setGravity(new javax.vecmath.Vector3f(0, -9.81f, 0)); + + broadphase.getOverlappingPairCache().setInternalGhostPairCallback(new GhostPairCallback()); + GImpactCollisionAlgorithm.registerAlgorithm(dispatcher); + + physicsSpaceTL.set(this); + //register filter callback for tick / collision + setTickCallback(); + setContactCallbacks(); + //register filter callback for collision groups + setOverlapFilterCallback(); + } + + private void setOverlapFilterCallback() { + OverlapFilterCallback callback = new OverlapFilterCallback() { + + public boolean needBroadphaseCollision(BroadphaseProxy bp, BroadphaseProxy bp1) { + boolean collides = (bp.collisionFilterGroup & bp1.collisionFilterMask) != 0; + if (collides) { + collides = (bp1.collisionFilterGroup & bp.collisionFilterMask) != 0; + } + if (collides) { + assert (bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject && bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject); + com.bulletphysics.collision.dispatch.CollisionObject colOb = (com.bulletphysics.collision.dispatch.CollisionObject) bp.clientObject; + com.bulletphysics.collision.dispatch.CollisionObject colOb1 = (com.bulletphysics.collision.dispatch.CollisionObject) bp1.clientObject; + assert (colOb.getUserPointer() != null && colOb1.getUserPointer() != null); + PhysicsCollisionObject collisionObject = (PhysicsCollisionObject) colOb.getUserPointer(); + PhysicsCollisionObject collisionObject1 = (PhysicsCollisionObject) colOb1.getUserPointer(); + if ((collisionObject.getCollideWithGroups() & collisionObject1.getCollisionGroup()) > 0 + || (collisionObject1.getCollideWithGroups() & collisionObject.getCollisionGroup()) > 0) { + PhysicsCollisionGroupListener listener = collisionGroupListeners.get(collisionObject.getCollisionGroup()); + PhysicsCollisionGroupListener listener1 = collisionGroupListeners.get(collisionObject1.getCollisionGroup()); + if (listener != null) { + return listener.collide(collisionObject, collisionObject1); + } else if (listener1 != null) { + return listener1.collide(collisionObject, collisionObject1); + } + return true; + } else { + return false; + } + } + return collides; + } + }; + dynamicsWorld.getPairCache().setOverlapFilterCallback(callback); + } + + private void setTickCallback() { + final PhysicsSpace space = this; + InternalTickCallback callback2 = new InternalTickCallback() { + + @Override + public void internalTick(DynamicsWorld dw, float f) { + //execute task list + AppTask task = pQueue.poll(); + task = pQueue.poll(); + while (task != null) { + while (task.isCancelled()) { + task = pQueue.poll(); + } + try { + task.invoke(); + } catch (Exception ex) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.SEVERE, null, ex); + } + task = pQueue.poll(); + } + for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) { + PhysicsTickListener physicsTickCallback = it.next(); + physicsTickCallback.prePhysicsTick(space, f); + } + } + }; + dynamicsWorld.setPreTickCallback(callback2); + InternalTickCallback callback = new InternalTickCallback() { + + @Override + public void internalTick(DynamicsWorld dw, float f) { + for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) { + PhysicsTickListener physicsTickCallback = it.next(); + physicsTickCallback.physicsTick(space, f); + } + } + }; + dynamicsWorld.setInternalTickCallback(callback, this); + } + + private void setContactCallbacks() { + BulletGlobals.setContactAddedCallback(new ContactAddedCallback() { + + public boolean contactAdded(ManifoldPoint cp, com.bulletphysics.collision.dispatch.CollisionObject colObj0, + int partId0, int index0, com.bulletphysics.collision.dispatch.CollisionObject colObj1, int partId1, + int index1) { + System.out.println("contact added"); + return true; + } + }); + + BulletGlobals.setContactProcessedCallback(new ContactProcessedCallback() { + + public boolean contactProcessed(ManifoldPoint cp, Object body0, Object body1) { + if (body0 instanceof CollisionObject && body1 instanceof CollisionObject) { + PhysicsCollisionObject node = null, node1 = null; + CollisionObject rBody0 = (CollisionObject) body0; + CollisionObject rBody1 = (CollisionObject) body1; + node = (PhysicsCollisionObject) rBody0.getUserPointer(); + node1 = (PhysicsCollisionObject) rBody1.getUserPointer(); + collisionEvents.add(eventFactory.getEvent(PhysicsCollisionEvent.TYPE_PROCESSED, node, node1, cp)); + } + return true; + } + }); + + BulletGlobals.setContactDestroyedCallback(new ContactDestroyedCallback() { + + public boolean contactDestroyed(Object userPersistentData) { + System.out.println("contact destroyed"); + return true; + } + }); + } + + /** + * updates the physics space + * @param time the current time value + */ + public void update(float time) { + update(time, maxSubSteps); + } + + /** + * updates the physics space, uses maxSteps<br> + * @param time the current time value + * @param maxSteps + */ + public void update(float time, int maxSteps) { + if (getDynamicsWorld() == null) { + return; + } + //step simulation + dynamicsWorld.stepSimulation(time, maxSteps, accuracy); + } + + public void distributeEvents() { + //add collision callbacks + synchronized (collisionEvents) { + for (Iterator<PhysicsCollisionEvent> it = collisionEvents.iterator(); it.hasNext();) { + PhysicsCollisionEvent physicsCollisionEvent = it.next(); + for (PhysicsCollisionListener listener : collisionListeners) { + listener.collision(physicsCollisionEvent); + } + //recycle events + eventFactory.recycle(physicsCollisionEvent); + it.remove(); + } + } + } + + public static <V> Future<V> enqueueOnThisThread(Callable<V> callable) { + AppTask<V> task = new AppTask<V>(callable); + System.out.println("created apptask"); + pQueueTL.get().add(task); + return task; + } + + /** + * calls the callable on the next physics tick (ensuring e.g. force applying) + * @param <V> + * @param callable + * @return + */ + public <V> Future<V> enqueue(Callable<V> callable) { + AppTask<V> task = new AppTask<V>(callable); + pQueue.add(task); + return task; + } + + /** + * adds an object to the physics space + * @param obj the PhysicsControl or Spatial with PhysicsControl to add + */ + public void add(Object obj) { + if (obj instanceof PhysicsControl) { + ((PhysicsControl) obj).setPhysicsSpace(this); + } else if (obj instanceof Spatial) { + Spatial node = (Spatial) obj; + PhysicsControl control = node.getControl(PhysicsControl.class); + control.setPhysicsSpace(this); + } else if (obj instanceof PhysicsCollisionObject) { + addCollisionObject((PhysicsCollisionObject) obj); + } else if (obj instanceof PhysicsJoint) { + addJoint((PhysicsJoint) obj); + } else { + throw (new UnsupportedOperationException("Cannot add this kind of object to the physics space.")); + } + } + + public void addCollisionObject(PhysicsCollisionObject obj) { + if (obj instanceof PhysicsGhostObject) { + addGhostObject((PhysicsGhostObject) obj); + } else if (obj instanceof PhysicsRigidBody) { + addRigidBody((PhysicsRigidBody) obj); + } else if (obj instanceof PhysicsVehicle) { + addRigidBody((PhysicsVehicle) obj); + } else if (obj instanceof PhysicsCharacter) { + addCharacter((PhysicsCharacter) obj); + } + } + + /** + * removes an object from the physics space + * @param obj the PhysicsControl or Spatial with PhysicsControl to remove + */ + public void remove(Object obj) { + if (obj instanceof PhysicsControl) { + ((PhysicsControl) obj).setPhysicsSpace(null); + } else if (obj instanceof Spatial) { + Spatial node = (Spatial) obj; + PhysicsControl control = node.getControl(PhysicsControl.class); + control.setPhysicsSpace(null); + } else if (obj instanceof PhysicsCollisionObject) { + removeCollisionObject((PhysicsCollisionObject) obj); + } else if (obj instanceof PhysicsJoint) { + removeJoint((PhysicsJoint) obj); + } else { + throw (new UnsupportedOperationException("Cannot remove this kind of object from the physics space.")); + } + } + + public void removeCollisionObject(PhysicsCollisionObject obj) { + if (obj instanceof PhysicsGhostObject) { + removeGhostObject((PhysicsGhostObject) obj); + } else if (obj instanceof PhysicsRigidBody) { + removeRigidBody((PhysicsRigidBody) obj); + } else if (obj instanceof PhysicsCharacter) { + removeCharacter((PhysicsCharacter) obj); + } + } + + /** + * adds all physics controls and joints in the given spatial node to the physics space + * (e.g. after loading from disk) - recursive if node + * @param spatial the rootnode containing the physics objects + */ + public void addAll(Spatial spatial) { + if (spatial.getControl(RigidBodyControl.class) != null) { + RigidBodyControl physicsNode = spatial.getControl(RigidBodyControl.class); + if (!physicsNodes.containsValue(physicsNode)) { + physicsNode.setPhysicsSpace(this); + } + //add joints + List<PhysicsJoint> joints = physicsNode.getJoints(); + for (Iterator<PhysicsJoint> it1 = joints.iterator(); it1.hasNext();) { + PhysicsJoint physicsJoint = it1.next(); + //add connected physicsnodes if they are not already added + if (!physicsNodes.containsValue(physicsJoint.getBodyA())) { + if (physicsJoint.getBodyA() instanceof PhysicsControl) { + add(physicsJoint.getBodyA()); + } else { + addRigidBody(physicsJoint.getBodyA()); + } + } + if (!physicsNodes.containsValue(physicsJoint.getBodyB())) { + if (physicsJoint.getBodyA() instanceof PhysicsControl) { + add(physicsJoint.getBodyB()); + } else { + addRigidBody(physicsJoint.getBodyB()); + } + } + if (!physicsJoints.contains(physicsJoint)) { + addJoint(physicsJoint); + } + } + } else if (spatial.getControl(PhysicsControl.class) != null) { + spatial.getControl(PhysicsControl.class).setPhysicsSpace(this); + } + //recursion + if (spatial instanceof Node) { + List<Spatial> children = ((Node) spatial).getChildren(); + for (Iterator<Spatial> it = children.iterator(); it.hasNext();) { + Spatial spat = it.next(); + addAll(spat); + } + } + } + + /** + * Removes all physics controls and joints in the given spatial from the physics space + * (e.g. before saving to disk) - recursive if node + * @param spatial the rootnode containing the physics objects + */ + public void removeAll(Spatial spatial) { + if (spatial.getControl(RigidBodyControl.class) != null) { + RigidBodyControl physicsNode = spatial.getControl(RigidBodyControl.class); + if (physicsNodes.containsValue(physicsNode)) { + physicsNode.setPhysicsSpace(null); + } + //remove joints + List<PhysicsJoint> joints = physicsNode.getJoints(); + for (Iterator<PhysicsJoint> it1 = joints.iterator(); it1.hasNext();) { + PhysicsJoint physicsJoint = it1.next(); + //add connected physicsnodes if they are not already added + if (physicsNodes.containsValue(physicsJoint.getBodyA())) { + if (physicsJoint.getBodyA() instanceof PhysicsControl) { + remove(physicsJoint.getBodyA()); + } else { + removeRigidBody(physicsJoint.getBodyA()); + } + } + if (physicsNodes.containsValue(physicsJoint.getBodyB())) { + if (physicsJoint.getBodyA() instanceof PhysicsControl) { + remove(physicsJoint.getBodyB()); + } else { + removeRigidBody(physicsJoint.getBodyB()); + } + } + if (physicsJoints.contains(physicsJoint)) { + removeJoint(physicsJoint); + } + } + } else if (spatial.getControl(PhysicsControl.class) != null) { + spatial.getControl(PhysicsControl.class).setPhysicsSpace(null); + } + //recursion + if (spatial instanceof Node) { + List<Spatial> children = ((Node) spatial).getChildren(); + for (Iterator<Spatial> it = children.iterator(); it.hasNext();) { + Spatial spat = it.next(); + removeAll(spat); + } + } + } + + private void addGhostObject(PhysicsGhostObject node) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding ghost object {0} to physics space.", node.getObjectId()); + dynamicsWorld.addCollisionObject(node.getObjectId()); + } + + private void removeGhostObject(PhysicsGhostObject node) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing ghost object {0} from physics space.", node.getObjectId()); + dynamicsWorld.removeCollisionObject(node.getObjectId()); + } + + private void addCharacter(PhysicsCharacter node) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding character {0} to physics space.", node.getObjectId()); +// dynamicsWorld.addCollisionObject(node.getObjectId()); + dynamicsWorld.addCollisionObject(node.getObjectId(), CollisionFilterGroups.CHARACTER_FILTER, (short) (CollisionFilterGroups.STATIC_FILTER | CollisionFilterGroups.DEFAULT_FILTER)); + dynamicsWorld.addAction(node.getControllerId()); + } + + private void removeCharacter(PhysicsCharacter node) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing character {0} from physics space.", node.getObjectId()); + dynamicsWorld.removeAction(node.getControllerId()); + dynamicsWorld.removeCollisionObject(node.getObjectId()); + } + + private void addRigidBody(PhysicsRigidBody node) { + physicsNodes.put(node.getObjectId(), node); + + //Workaround + //It seems that adding a Kinematic RigidBody to the dynamicWorld prevent it from being non kinematic again afterward. + //so we add it non kinematic, then set it kinematic again. + boolean kinematic = false; + if (node.isKinematic()) { + kinematic = true; + node.setKinematic(false); + } + dynamicsWorld.addRigidBody(node.getObjectId()); + if (kinematic) { + node.setKinematic(true); + } + + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding RigidBody {0} to physics space.", node.getObjectId()); + if (node instanceof PhysicsVehicle) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding vehicle constraint {0} to physics space.", ((PhysicsVehicle) node).getVehicleId()); + ((PhysicsVehicle) node).createVehicle(this); + dynamicsWorld.addVehicle(((PhysicsVehicle) node).getVehicleId()); + } + } + + private void removeRigidBody(PhysicsRigidBody node) { + if (node instanceof PhysicsVehicle) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing vehicle constraint {0} from physics space.", ((PhysicsVehicle) node).getVehicleId()); + dynamicsWorld.removeVehicle(((PhysicsVehicle) node).getVehicleId()); + } + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing RigidBody {0} from physics space.", node.getObjectId()); + physicsNodes.remove(node.getObjectId()); + dynamicsWorld.removeRigidBody(node.getObjectId()); + } + + private void addJoint(PhysicsJoint joint) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding Joint {0} to physics space.", joint.getObjectId()); + physicsJoints.add(joint); + dynamicsWorld.addConstraint(joint.getObjectId(), !joint.isCollisionBetweenLinkedBodys()); + } + + private void removeJoint(PhysicsJoint joint) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing Joint {0} from physics space.", joint.getObjectId()); + physicsJoints.remove(joint); + dynamicsWorld.removeConstraint(joint.getObjectId()); + } + + /** + * Sets the gravity of the PhysicsSpace, set before adding physics objects! + * @param gravity + */ + public void setGravity(Vector3f gravity) { + dynamicsWorld.setGravity(Converter.convert(gravity)); + } + + /** + * applies gravity value to all objects + */ + public void applyGravity() { + dynamicsWorld.applyGravity(); + } + + /** + * clears forces of all objects + */ + public void clearForces() { + dynamicsWorld.clearForces(); + } + + /** + * Adds the specified listener to the physics tick listeners. + * The listeners are called on each physics step, which is not necessarily + * each frame but is determined by the accuracy of the physics space. + * @param listener + */ + public void addTickListener(PhysicsTickListener listener) { + tickListeners.add(listener); + } + + public void removeTickListener(PhysicsTickListener listener) { + tickListeners.remove(listener); + } + + /** + * Adds a CollisionListener that will be informed about collision events + * @param listener the CollisionListener to add + */ + public void addCollisionListener(PhysicsCollisionListener listener) { + collisionListeners.add(listener); + } + + /** + * Removes a CollisionListener from the list + * @param listener the CollisionListener to remove + */ + public void removeCollisionListener(PhysicsCollisionListener listener) { + collisionListeners.remove(listener); + } + + /** + * Adds a listener for a specific collision group, such a listener can disable collisions when they happen.<br> + * There can be only one listener per collision group. + * @param listener + * @param collisionGroup + */ + public void addCollisionGroupListener(PhysicsCollisionGroupListener listener, int collisionGroup) { + collisionGroupListeners.put(collisionGroup, listener); + } + + public void removeCollisionGroupListener(int collisionGroup) { + collisionGroupListeners.remove(collisionGroup); + } + + /** + * Performs a ray collision test and returns the results as a list of PhysicsRayTestResults + */ + public List<PhysicsRayTestResult> rayTest(Vector3f from, Vector3f to) { + List<PhysicsRayTestResult> results = new LinkedList<PhysicsRayTestResult>(); + dynamicsWorld.rayTest(Converter.convert(from, rayVec1), Converter.convert(to, rayVec2), new InternalRayListener(results)); + return results; + } + + /** + * Performs a ray collision test and returns the results as a list of PhysicsRayTestResults + */ + public List<PhysicsRayTestResult> rayTest(Vector3f from, Vector3f to, List<PhysicsRayTestResult> results) { + results.clear(); + dynamicsWorld.rayTest(Converter.convert(from, rayVec1), Converter.convert(to, rayVec2), new InternalRayListener(results)); + return results; + } + + private class InternalRayListener extends CollisionWorld.RayResultCallback { + + private List<PhysicsRayTestResult> results; + + public InternalRayListener(List<PhysicsRayTestResult> results) { + this.results = results; + } + + @Override + public float addSingleResult(LocalRayResult lrr, boolean bln) { + PhysicsCollisionObject obj = (PhysicsCollisionObject) lrr.collisionObject.getUserPointer(); + results.add(new PhysicsRayTestResult(obj, Converter.convert(lrr.hitNormalLocal), lrr.hitFraction, bln)); + return lrr.hitFraction; + } + } + + /** + * Performs a sweep collision test and returns the results as a list of PhysicsSweepTestResults<br/> + * You have to use different Transforms for start and end (at least distance > 0.4f). + * SweepTest will not see a collision if it starts INSIDE an object and is moving AWAY from its center. + */ + public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end) { + List<PhysicsSweepTestResult> results = new LinkedList<PhysicsSweepTestResult>(); + if (!(shape.getCShape() instanceof ConvexShape)) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!"); + return results; + } + dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results)); + return results; + + } + + /** + * Performs a sweep collision test and returns the results as a list of PhysicsSweepTestResults<br/> + * You have to use different Transforms for start and end (at least distance > 0.4f). + * SweepTest will not see a collision if it starts INSIDE an object and is moving AWAY from its center. + */ + public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end, List<PhysicsSweepTestResult> results) { + results.clear(); + if (!(shape.getCShape() instanceof ConvexShape)) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!"); + return results; + } + dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results)); + return results; + } + + private class InternalSweepListener extends CollisionWorld.ConvexResultCallback { + + private List<PhysicsSweepTestResult> results; + + public InternalSweepListener(List<PhysicsSweepTestResult> results) { + this.results = results; + } + + @Override + public float addSingleResult(LocalConvexResult lcr, boolean bln) { + PhysicsCollisionObject obj = (PhysicsCollisionObject) lcr.hitCollisionObject.getUserPointer(); + results.add(new PhysicsSweepTestResult(obj, Converter.convert(lcr.hitNormalLocal), lcr.hitFraction, bln)); + return lcr.hitFraction; + } + } + + /** + * destroys the current PhysicsSpace so that a new one can be created + */ + public void destroy() { + physicsNodes.clear(); + physicsJoints.clear(); + + dynamicsWorld.destroy(); + dynamicsWorld = null; + } + + /** + * used internally + * @return the dynamicsWorld + */ + public DynamicsWorld getDynamicsWorld() { + return dynamicsWorld; + } + + public BroadphaseType getBroadphaseType() { + return broadphaseType; + } + + public void setBroadphaseType(BroadphaseType broadphaseType) { + this.broadphaseType = broadphaseType; + } + + /** + * Sets the maximum amount of extra steps that will be used to step the physics + * when the fps is below the physics fps. Doing this maintains determinism in physics. + * For example a maximum number of 2 can compensate for framerates as low as 30fps + * when the physics has the default accuracy of 60 fps. Note that setting this + * value too high can make the physics drive down its own fps in case its overloaded. + * @param steps The maximum number of extra steps, default is 4. + */ + public void setMaxSubSteps(int steps) { + maxSubSteps = steps; + } + + /** + * get the current accuracy of the physics computation + * @return the current accuracy + */ + public float getAccuracy() { + return accuracy; + } + + /** + * sets the accuracy of the physics computation, default=1/60s<br> + * @param accuracy + */ + public void setAccuracy(float accuracy) { + this.accuracy = accuracy; + } + + public Vector3f getWorldMin() { + return worldMin; + } + + /** + * only applies for AXIS_SWEEP broadphase + * @param worldMin + */ + public void setWorldMin(Vector3f worldMin) { + this.worldMin.set(worldMin); + } + + public Vector3f getWorldMax() { + return worldMax; + } + + /** + * only applies for AXIS_SWEEP broadphase + * @param worldMax + */ + public void setWorldMax(Vector3f worldMax) { + this.worldMax.set(worldMax); + } + + /** + * Enable debug display for physics + * @param manager AssetManager to use to create debug materials + */ + public void enableDebug(AssetManager manager) { + debugManager = manager; + } + + /** + * Disable debug display + */ + public void disableDebug() { + debugManager = null; + } + + public AssetManager getDebugManager() { + return debugManager; + } + + /** + * interface with Broadphase types + */ + public enum BroadphaseType { + + /** + * basic Broadphase + */ + SIMPLE, + /** + * better Broadphase, needs worldBounds , max Object number = 16384 + */ + AXIS_SWEEP_3, + /** + * better Broadphase, needs worldBounds , max Object number = 65536 + */ + AXIS_SWEEP_3_32, + /** + * Broadphase allowing quicker adding/removing of physics objects + */ + DBVT; + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java new file mode 100644 index 0000000..4dc768a --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java @@ -0,0 +1,197 @@ +/* + * 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.collision; + +import com.bulletphysics.collision.narrowphase.ManifoldPoint; +import com.jme3.bullet.util.Converter; +import com.jme3.math.Vector3f; +import com.jme3.scene.Spatial; +import java.util.EventObject; + +/** + * A CollisionEvent stores all information about a collision in the PhysicsWorld. + * Do not store this Object, as it will be reused after the collision() method has been called. + * Get/reference all data you need in the collide method. + * @author normenhansen + */ +public class PhysicsCollisionEvent extends EventObject { + + public static final int TYPE_ADDED = 0; + public static final int TYPE_PROCESSED = 1; + public static final int TYPE_DESTROYED = 2; + private int type; + private PhysicsCollisionObject nodeA; + private PhysicsCollisionObject nodeB; + private ManifoldPoint cp; + + public PhysicsCollisionEvent(int type, PhysicsCollisionObject source, PhysicsCollisionObject nodeB, ManifoldPoint cp) { + super(source); + this.type = type; + this.nodeA = source; + this.nodeB = nodeB; + this.cp = cp; + } + + /** + * used by event factory, called when event is destroyed + */ + public void clean() { + source = null; + type = 0; + nodeA = null; + nodeB = null; + cp = null; + } + + /** + * used by event factory, called when event reused + */ + public void refactor(int type, PhysicsCollisionObject source, PhysicsCollisionObject nodeB, ManifoldPoint cp) { + this.source = source; + this.type = type; + this.nodeA = source; + this.nodeB = nodeB; + this.cp = cp; + } + + public int getType() { + return type; + } + + /** + * @return A Spatial if the UserObject of the PhysicsCollisionObject is a Spatial + */ + public Spatial getNodeA() { + if (nodeA.getUserObject() instanceof Spatial) { + return (Spatial) nodeA.getUserObject(); + } + return null; + } + + /** + * @return A Spatial if the UserObject of the PhysicsCollisionObject is a Spatial + */ + public Spatial getNodeB() { + if (nodeB.getUserObject() instanceof Spatial) { + return (Spatial) nodeB.getUserObject(); + } + return null; + } + + public PhysicsCollisionObject getObjectA() { + return nodeA; + } + + public PhysicsCollisionObject getObjectB() { + return nodeB; + } + + public float getAppliedImpulse() { + return cp.appliedImpulse; + } + + public float getAppliedImpulseLateral1() { + return cp.appliedImpulseLateral1; + } + + public float getAppliedImpulseLateral2() { + return cp.appliedImpulseLateral2; + } + + public float getCombinedFriction() { + return cp.combinedFriction; + } + + public float getCombinedRestitution() { + return cp.combinedRestitution; + } + + public float getDistance1() { + return cp.distance1; + } + + public int getIndex0() { + return cp.index0; + } + + public int getIndex1() { + return cp.index1; + } + + public Vector3f getLateralFrictionDir1() { + return Converter.convert(cp.lateralFrictionDir1); + } + + public Vector3f getLateralFrictionDir2() { + return Converter.convert(cp.lateralFrictionDir2); + } + + public boolean isLateralFrictionInitialized() { + return cp.lateralFrictionInitialized; + } + + public int getLifeTime() { + return cp.lifeTime; + } + + public Vector3f getLocalPointA() { + return Converter.convert(cp.localPointA); + } + + public Vector3f getLocalPointB() { + return Converter.convert(cp.localPointB); + } + + public Vector3f getNormalWorldOnB() { + return Converter.convert(cp.normalWorldOnB); + } + + public int getPartId0() { + return cp.partId0; + } + + public int getPartId1() { + return cp.partId1; + } + + public Vector3f getPositionWorldOnA() { + return Converter.convert(cp.positionWorldOnA); + } + + public Vector3f getPositionWorldOnB() { + return Converter.convert(cp.positionWorldOnB); + } + + public Object getUserPersistentData() { + return cp.userPersistentData; + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java new file mode 100644 index 0000000..cf8d8ae --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java @@ -0,0 +1,59 @@ +/* + * 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.collision; + +import com.bulletphysics.collision.narrowphase.ManifoldPoint; +import java.util.concurrent.ConcurrentLinkedQueue; + +/** + * + * @author normenhansen + */ +public class PhysicsCollisionEventFactory { + + private ConcurrentLinkedQueue<PhysicsCollisionEvent> eventBuffer = new ConcurrentLinkedQueue<PhysicsCollisionEvent>(); + + public PhysicsCollisionEvent getEvent(int type, PhysicsCollisionObject source, PhysicsCollisionObject nodeB, ManifoldPoint cp) { + PhysicsCollisionEvent event = eventBuffer.poll(); + if (event == null) { + event = new PhysicsCollisionEvent(type, source, nodeB, cp); + }else{ + event.refactor(type, source, nodeB, cp); + } + return event; + } + + public void recycle(PhysicsCollisionEvent event) { + event.clean(); + eventBuffer.add(event); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionObject.java b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionObject.java new file mode 100644 index 0000000..0054cf0 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionObject.java @@ -0,0 +1,287 @@ +/* + * 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.collision; + +import com.jme3.asset.AssetManager; +import com.jme3.bullet.collision.shapes.CollisionShape; +import com.jme3.bullet.util.DebugShapeFactory; +import com.jme3.export.*; +import com.jme3.material.Material; +import com.jme3.math.ColorRGBA; +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.Iterator; +import java.util.List; + +/** + * Base class for collision objects (PhysicsRigidBody, PhysicsGhostObject) + * @author normenhansen + */ +public abstract class PhysicsCollisionObject implements Savable { + + protected Spatial debugShape; + protected Arrow debugArrow; + protected Geometry debugArrowGeom; + protected Material debugMaterialBlue; + protected Material debugMaterialRed; + protected Material debugMaterialGreen; + protected Material debugMaterialYellow; + protected CollisionShape collisionShape; + public static final int COLLISION_GROUP_NONE = 0x00000000; + public static final int COLLISION_GROUP_01 = 0x00000001; + public static final int COLLISION_GROUP_02 = 0x00000002; + public static final int COLLISION_GROUP_03 = 0x00000004; + public static final int COLLISION_GROUP_04 = 0x00000008; + public static final int COLLISION_GROUP_05 = 0x00000010; + public static final int COLLISION_GROUP_06 = 0x00000020; + public static final int COLLISION_GROUP_07 = 0x00000040; + public static final int COLLISION_GROUP_08 = 0x00000080; + public static final int COLLISION_GROUP_09 = 0x00000100; + public static final int COLLISION_GROUP_10 = 0x00000200; + public static final int COLLISION_GROUP_11 = 0x00000400; + public static final int COLLISION_GROUP_12 = 0x00000800; + public static final int COLLISION_GROUP_13 = 0x00001000; + public static final int COLLISION_GROUP_14 = 0x00002000; + public static final int COLLISION_GROUP_15 = 0x00004000; + public static final int COLLISION_GROUP_16 = 0x00008000; + protected int collisionGroup = 0x00000001; + protected int collisionGroupsMask = 0x00000001; + private Object userObject; + + /** + * Sets a CollisionShape to this physics object, note that the object should + * not be in the physics space when adding a new collision shape as it is rebuilt + * on the physics side. + * @param collisionShape the CollisionShape to set + */ + public void setCollisionShape(CollisionShape collisionShape) { + this.collisionShape = collisionShape; + updateDebugShape(); + } + + /** + * @return the CollisionShape of this PhysicsNode, to be able to reuse it with + * other physics nodes (increases performance) + */ + public CollisionShape getCollisionShape() { + return collisionShape; + } + + /** + * Returns the collision group for this collision shape + * @return + */ + public int getCollisionGroup() { + return collisionGroup; + } + + /** + * Sets the collision group number for this physics object. <br> + * The groups are integer bit masks and some pre-made variables are available in CollisionObject. + * All physics objects are by default in COLLISION_GROUP_01.<br> + * Two object will collide when <b>one</b> of the partys has the + * collisionGroup of the other in its collideWithGroups set. + * @param collisionGroup the collisionGroup to set + */ + public void setCollisionGroup(int collisionGroup) { + this.collisionGroup = collisionGroup; + } + + /** + * Add a group that this object will collide with.<br> + * Two object will collide when <b>one</b> of the partys has the + * collisionGroup of the other in its collideWithGroups set.<br> + * @param collisionGroup + */ + public void addCollideWithGroup(int collisionGroup) { + this.collisionGroupsMask = this.collisionGroupsMask | collisionGroup; + } + + /** + * Remove a group from the list this object collides with. + * @param collisionGroup + */ + public void removeCollideWithGroup(int collisionGroup) { + this.collisionGroupsMask = this.collisionGroupsMask & ~collisionGroup; + } + + /** + * Directly set the bitmask for collision groups that this object collides with. + * @param collisionGroups + */ + public void setCollideWithGroups(int collisionGroups) { + this.collisionGroupsMask = collisionGroups; + } + + /** + * Gets the bitmask of collision groups that this object collides with. + * @return + */ + public int getCollideWithGroups() { + return collisionGroupsMask; + } + + /** + * Creates a visual debug shape of the current collision shape of this physics object<br/> + * <b>Does not work with detached physics, please switch to PARALLEL or SEQUENTIAL for debugging</b> + * @param manager AssetManager to load the default wireframe material for the debug shape + */ + protected Spatial attachDebugShape(AssetManager manager) { + debugMaterialBlue = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md"); + debugMaterialBlue.getAdditionalRenderState().setWireframe(true); + debugMaterialBlue.setColor("Color", ColorRGBA.Blue); + debugMaterialGreen = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md"); + debugMaterialGreen.getAdditionalRenderState().setWireframe(true); + debugMaterialGreen.setColor("Color", ColorRGBA.Green); + debugMaterialRed = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md"); + debugMaterialRed.getAdditionalRenderState().setWireframe(true); + debugMaterialRed.setColor("Color", ColorRGBA.Red); + debugMaterialYellow = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md"); + debugMaterialYellow.getAdditionalRenderState().setWireframe(true); + debugMaterialYellow.setColor("Color", ColorRGBA.Yellow); + debugArrow = new Arrow(Vector3f.UNIT_XYZ); + debugArrowGeom = new Geometry("DebugArrow", debugArrow); + debugArrowGeom.setMaterial(debugMaterialGreen); + return attachDebugShape(); + } + + /** + * creates a debug shape for this CollisionObject + * @param manager + * @return + */ + public Spatial createDebugShape(AssetManager manager){ + return attachDebugShape(manager); + } + + protected Spatial attachDebugShape(Material material) { + debugMaterialBlue = material; + debugMaterialGreen = material; + debugMaterialRed = material; + debugMaterialYellow = material; + debugArrow = new Arrow(Vector3f.UNIT_XYZ); + debugArrowGeom = new Geometry("DebugArrow", debugArrow); + debugArrowGeom.setMaterial(debugMaterialGreen); + return attachDebugShape(); + } + + public Spatial debugShape() { + return debugShape; + } + + /** + * Creates a visual debug shape of the current collision shape of this physics object<br/> + * <b>Does not work with detached physics, please switch to PARALLEL or SEQUENTIAL for debugging</b> + * @param material Material to use for the debug shape + */ + protected Spatial attachDebugShape() { + if (debugShape != null) { + detachDebugShape(); + } + Spatial spatial = getDebugShape(); + this.debugShape = spatial; + return debugShape; + } + + protected void updateDebugShape() { + if (debugShape != null) { + detachDebugShape(); + attachDebugShape(); + } + } + + protected Spatial getDebugShape() { + Spatial spatial = DebugShapeFactory.getDebugShape(collisionShape); + if (spatial == null) { + return new Node("nullnode"); + } + if (spatial instanceof Node) { + List<Spatial> children = ((Node) spatial).getChildren(); + for (Iterator<Spatial> it1 = children.iterator(); it1.hasNext();) { + Spatial spatial1 = it1.next(); + Geometry geom = ((Geometry) spatial1); + geom.setMaterial(debugMaterialBlue); + geom.setCullHint(Spatial.CullHint.Never); + } + } else { + Geometry geom = ((Geometry) spatial); + geom.setMaterial(debugMaterialBlue); + geom.setCullHint(Spatial.CullHint.Never); + } + spatial.setCullHint(Spatial.CullHint.Never); + return spatial; + } + + /** + * Removes the debug shape + */ + public void detachDebugShape() { + debugShape = null; + } + + /** + * @return the userObject + */ + public Object getUserObject() { + return userObject; + } + + /** + * @param userObject the userObject to set + */ + public void setUserObject(Object userObject) { + this.userObject = userObject; + } + + @Override + public void write(JmeExporter e) throws IOException { + OutputCapsule capsule = e.getCapsule(this); + capsule.write(collisionGroup, "collisionGroup", 0x00000001); + capsule.write(collisionGroupsMask, "collisionGroupsMask", 0x00000001); + capsule.write(debugShape, "debugShape", null); + capsule.write(collisionShape, "collisionShape", null); + } + + @Override + public void read(JmeImporter e) throws IOException { + InputCapsule capsule = e.getCapsule(this); + collisionGroup = capsule.readInt("collisionGroup", 0x00000001); + collisionGroupsMask = capsule.readInt("collisionGroupsMask", 0x00000001); + debugShape = (Spatial) capsule.readSavable("debugShape", null); + CollisionShape shape = (CollisionShape) capsule.readSavable("collisionShape", null); + collisionShape = shape; + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/PhysicsRayTestResult.java b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsRayTestResult.java new file mode 100644 index 0000000..1941344 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsRayTestResult.java @@ -0,0 +1,91 @@ +/* + * 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.collision; + +import com.jme3.math.Vector3f; + +/** + * Contains the results of a PhysicsSpace rayTest + * @author normenhansen + */ +public class PhysicsRayTestResult { + + private PhysicsCollisionObject collisionObject; + private Vector3f hitNormalLocal; + private float hitFraction; + private boolean normalInWorldSpace; + + public PhysicsRayTestResult() { + } + + public PhysicsRayTestResult(PhysicsCollisionObject collisionObject, Vector3f hitNormalLocal, float hitFraction, boolean normalInWorldSpace) { + this.collisionObject = collisionObject; + this.hitNormalLocal = hitNormalLocal; + this.hitFraction = hitFraction; + this.normalInWorldSpace = normalInWorldSpace; + } + + /** + * @return the collisionObject + */ + public PhysicsCollisionObject getCollisionObject() { + return collisionObject; + } + + /** + * @return the hitNormalLocal + */ + public Vector3f getHitNormalLocal() { + return hitNormalLocal; + } + + /** + * @return the hitFraction + */ + public float getHitFraction() { + return hitFraction; + } + + /** + * @return the normalInWorldSpace + */ + public boolean isNormalInWorldSpace() { + return normalInWorldSpace; + } + + public void fill(PhysicsCollisionObject collisionObject, Vector3f hitNormalLocal, float hitFraction, boolean normalInWorldSpace) { + this.collisionObject = collisionObject; + this.hitNormalLocal = hitNormalLocal; + this.hitFraction = hitFraction; + this.normalInWorldSpace = normalInWorldSpace; + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java new file mode 100644 index 0000000..d513204 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java @@ -0,0 +1,91 @@ +/* + * 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.collision; + +import com.jme3.math.Vector3f; + +/** + * Contains the results of a PhysicsSpace rayTest + * @author normenhansen + */ +public class PhysicsSweepTestResult { + + private PhysicsCollisionObject collisionObject; + private Vector3f hitNormalLocal; + private float hitFraction; + private boolean normalInWorldSpace; + + public PhysicsSweepTestResult() { + } + + public PhysicsSweepTestResult(PhysicsCollisionObject collisionObject, Vector3f hitNormalLocal, float hitFraction, boolean normalInWorldSpace) { + this.collisionObject = collisionObject; + this.hitNormalLocal = hitNormalLocal; + this.hitFraction = hitFraction; + this.normalInWorldSpace = normalInWorldSpace; + } + + /** + * @return the collisionObject + */ + public PhysicsCollisionObject getCollisionObject() { + return collisionObject; + } + + /** + * @return the hitNormalLocal + */ + public Vector3f getHitNormalLocal() { + return hitNormalLocal; + } + + /** + * @return the hitFraction + */ + public float getHitFraction() { + return hitFraction; + } + + /** + * @return the normalInWorldSpace + */ + public boolean isNormalInWorldSpace() { + return normalInWorldSpace; + } + + public void fill(PhysicsCollisionObject collisionObject, Vector3f hitNormalLocal, float hitFraction, boolean normalInWorldSpace) { + this.collisionObject = collisionObject; + this.hitNormalLocal = hitNormalLocal; + this.hitFraction = hitFraction; + this.normalInWorldSpace = normalInWorldSpace; + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java new file mode 100644 index 0000000..ea3f605 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java @@ -0,0 +1,87 @@ +/* + * 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.collision.shapes; + +import com.bulletphysics.collision.shapes.BoxShape; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Vector3f; +import java.io.IOException; + +/** + * Basic box collision shape + * @author normenhansen + */ +public class BoxCollisionShape extends CollisionShape { + + private Vector3f halfExtents; + + public BoxCollisionShape() { + } + + /** + * creates a collision box from the given halfExtents + * @param halfExtents the halfExtents of the CollisionBox + */ + public BoxCollisionShape(Vector3f halfExtents) { + this.halfExtents = halfExtents; + createShape(); + } + + public final Vector3f getHalfExtents() { + return halfExtents; + } + + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(halfExtents, "halfExtents", new Vector3f(1, 1, 1)); + } + + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + Vector3f halfExtents = (Vector3f) capsule.readSavable("halfExtents", new Vector3f(1, 1, 1)); + this.halfExtents = halfExtents; + createShape(); + } + + protected void createShape() { + cShape = new BoxShape(Converter.convert(halfExtents)); + cShape.setLocalScaling(Converter.convert(getScale())); + cShape.setMargin(margin); + } + +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java new file mode 100644 index 0000000..61024eb --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java @@ -0,0 +1,126 @@ +/* + * 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.collision.shapes; + +import com.bulletphysics.collision.shapes.CapsuleShape; +import com.bulletphysics.collision.shapes.CapsuleShapeX; +import com.bulletphysics.collision.shapes.CapsuleShapeZ; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import java.io.IOException; + +/** + * Basic capsule collision shape + * @author normenhansen + */ +public class CapsuleCollisionShape extends CollisionShape{ + protected float radius,height; + protected int axis; + + public CapsuleCollisionShape() { + } + + /** + * creates a new CapsuleCollisionShape with the given radius and height + * @param radius the radius of the capsule + * @param height the height of the capsule + */ + public CapsuleCollisionShape(float radius, float height) { + this.radius=radius; + this.height=height; + this.axis=1; + CapsuleShape capShape=new CapsuleShape(radius,height); + cShape=capShape; + } + + /** + * creates a capsule shape around the given axis (0=X,1=Y,2=Z) + * @param radius + * @param height + * @param axis + */ + public CapsuleCollisionShape(float radius, float height, int axis) { + this.radius=radius; + this.height=height; + this.axis=axis; + createShape(); + } + + public float getRadius() { + return radius; + } + + public float getHeight() { + return height; + } + + public int getAxis() { + return axis; + } + + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(radius, "radius", 0.5f); + capsule.write(height, "height", 1); + capsule.write(axis, "axis", 1); + } + + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + radius = capsule.readFloat("radius", 0.5f); + height = capsule.readFloat("height", 0.5f); + axis = capsule.readInt("axis", 1); + createShape(); + } + + protected void createShape(){ + switch(axis){ + case 0: + cShape=new CapsuleShapeX(radius,height); + break; + case 1: + cShape=new CapsuleShape(radius,height); + break; + case 2: + cShape=new CapsuleShapeZ(radius,height); + break; + } + cShape.setLocalScaling(Converter.convert(getScale())); + cShape.setMargin(margin); + } + +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/CollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CollisionShape.java new file mode 100644 index 0000000..3a36a75 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CollisionShape.java @@ -0,0 +1,111 @@ +/* + * 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.collision.shapes; + +import com.jme3.bullet.util.Converter; +import com.jme3.export.*; +import com.jme3.math.Vector3f; +import java.io.IOException; + +/** + * This Object holds information about a jbullet CollisionShape to be able to reuse + * CollisionShapes (as suggested in bullet manuals) + * TODO: add static methods to create shapes from nodes (like jbullet-jme constructor) + * @author normenhansen + */ +public abstract class CollisionShape implements Savable { + + protected com.bulletphysics.collision.shapes.CollisionShape cShape; + protected Vector3f scale = new Vector3f(1, 1, 1); + protected float margin = 0.0f; + + public CollisionShape() { + } + + /** + * used internally, not safe + */ + public void calculateLocalInertia(float mass, javax.vecmath.Vector3f vector) { + if (cShape == null) { + return; + } + if (this instanceof MeshCollisionShape) { + vector.set(0, 0, 0); + } else { + cShape.calculateLocalInertia(mass, vector); + } + } + + /** + * used internally + */ + public com.bulletphysics.collision.shapes.CollisionShape getCShape() { + return cShape; + } + + /** + * used internally + */ + public void setCShape(com.bulletphysics.collision.shapes.CollisionShape cShape) { + this.cShape = cShape; + } + + public void setScale(Vector3f scale) { + this.scale.set(scale); + cShape.setLocalScaling(Converter.convert(scale)); + } + + public float getMargin() { + return cShape.getMargin(); + } + + public void setMargin(float margin) { + cShape.setMargin(margin); + this.margin = margin; + } + + public Vector3f getScale() { + return scale; + } + + public void write(JmeExporter ex) throws IOException { + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(scale, "scale", new Vector3f(1, 1, 1)); + capsule.write(getMargin(), "margin", 0.0f); + } + + public void read(JmeImporter im) throws IOException { + InputCapsule capsule = im.getCapsule(this); + this.scale = (Vector3f) capsule.readSavable("scale", new Vector3f(1, 1, 1)); + this.margin = capsule.readFloat("margin", 0.0f); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java new file mode 100644 index 0000000..1beb066 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java @@ -0,0 +1,150 @@ +/* + * 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.collision.shapes; + +import com.bulletphysics.collision.shapes.CompoundShape; +import com.bulletphysics.linearmath.Transform; +import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Matrix3f; +import com.jme3.math.Vector3f; +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; + +/** + * A CompoundCollisionShape allows combining multiple base shapes + * to generate a more sophisticated shape. + * @author normenhansen + */ +public class CompoundCollisionShape extends CollisionShape { + + protected ArrayList<ChildCollisionShape> children = new ArrayList<ChildCollisionShape>(); + + public CompoundCollisionShape() { + cShape = new CompoundShape(); + } + + /** + * adds a child shape at the given local translation + * @param shape the child shape to add + * @param location the local location of the child shape + */ + public void addChildShape(CollisionShape shape, Vector3f location) { + Transform transA = new Transform(Converter.convert(new Matrix3f())); + Converter.convert(location, transA.origin); + children.add(new ChildCollisionShape(location.clone(), new Matrix3f(), shape)); + ((CompoundShape) cShape).addChildShape(transA, shape.getCShape()); + } + + /** + * adds a child shape at the given local translation + * @param shape the child shape to add + * @param location the local location of the child shape + */ + public void addChildShape(CollisionShape shape, Vector3f location, Matrix3f rotation) { + if(shape instanceof CompoundCollisionShape){ + throw new IllegalStateException("CompoundCollisionShapes cannot have CompoundCollisionShapes as children!"); + } + Transform transA = new Transform(Converter.convert(rotation)); + Converter.convert(location, transA.origin); + Converter.convert(rotation, transA.basis); + children.add(new ChildCollisionShape(location.clone(), rotation.clone(), shape)); + ((CompoundShape) cShape).addChildShape(transA, shape.getCShape()); + } + + private void addChildShapeDirect(CollisionShape shape, Vector3f location, Matrix3f rotation) { + if(shape instanceof CompoundCollisionShape){ + throw new IllegalStateException("CompoundCollisionShapes cannot have CompoundCollisionShapes as children!"); + } + Transform transA = new Transform(Converter.convert(rotation)); + Converter.convert(location, transA.origin); + Converter.convert(rotation, transA.basis); + ((CompoundShape) cShape).addChildShape(transA, shape.getCShape()); + } + + /** + * removes a child shape + * @param shape the child shape to remove + */ + public void removeChildShape(CollisionShape shape) { + ((CompoundShape) cShape).removeChildShape(shape.getCShape()); + for (Iterator<ChildCollisionShape> it = children.iterator(); it.hasNext();) { + ChildCollisionShape childCollisionShape = it.next(); + if (childCollisionShape.shape == shape) { + it.remove(); + } + } + } + + public List<ChildCollisionShape> getChildren() { + return children; + } + + /** + * WARNING - CompoundCollisionShape scaling has no effect. + */ + @Override + public void setScale(Vector3f scale) { + Logger.getLogger(this.getClass().getName()).log(Level.WARNING, "CompoundCollisionShape cannot be scaled"); + } + + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.writeSavableArrayList(children, "children", new ArrayList<ChildCollisionShape>()); + } + + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + children = capsule.readSavableArrayList("children", new ArrayList<ChildCollisionShape>()); + cShape.setLocalScaling(Converter.convert(getScale())); + cShape.setMargin(margin); + loadChildren(); + } + + private void loadChildren() { + for (Iterator<ChildCollisionShape> it = children.iterator(); it.hasNext();) { + ChildCollisionShape child = it.next(); + addChildShapeDirect(child.shape, child.location, child.rotation); + } + } + +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java new file mode 100644 index 0000000..7103f0d --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java @@ -0,0 +1,77 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.bullet.collision.shapes; + +import com.bulletphysics.collision.shapes.ConeShape; +import com.bulletphysics.collision.shapes.ConeShapeX; +import com.bulletphysics.collision.shapes.ConeShapeZ; +import com.jme3.bullet.PhysicsSpace; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import java.io.IOException; + +/** + * + * @author normenhansen + */ +public class ConeCollisionShape extends CollisionShape { + + protected float radius; + protected float height; + protected int axis; + + public ConeCollisionShape() { + } + + public ConeCollisionShape(float radius, float height, int axis) { + this.radius = radius; + this.height = radius; + this.axis = axis; + createShape(); + } + + public ConeCollisionShape(float radius, float height) { + this.radius = radius; + this.height = radius; + this.axis = PhysicsSpace.AXIS_Y; + createShape(); + } + + public float getRadius() { + return radius; + } + + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(radius, "radius", 0.5f); + capsule.write(height, "height", 0.5f); + capsule.write(axis, "axis", 0.5f); + } + + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + radius = capsule.readFloat("radius", 0.5f); + radius = capsule.readFloat("height", 0.5f); + radius = capsule.readFloat("axis", 0.5f); + createShape(); + } + + protected void createShape() { + if (axis == PhysicsSpace.AXIS_X) { + cShape = new ConeShapeX(radius, height); + } else if (axis == PhysicsSpace.AXIS_Y) { + cShape = new ConeShape(radius, height); + } else if (axis == PhysicsSpace.AXIS_Z) { + cShape = new ConeShapeZ(radius, height); + } + cShape.setLocalScaling(Converter.convert(getScale())); + cShape.setMargin(margin); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java new file mode 100644 index 0000000..866a443 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java @@ -0,0 +1,117 @@ +/* + * 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.collision.shapes; + +import com.bulletphysics.collision.shapes.CylinderShape; +import com.bulletphysics.collision.shapes.CylinderShapeX; +import com.bulletphysics.collision.shapes.CylinderShapeZ; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Vector3f; +import java.io.IOException; + +/** + * Basic cylinder collision shape + * @author normenhansen + */ +public class CylinderCollisionShape extends CollisionShape { + + protected Vector3f halfExtents; + protected int axis; + + public CylinderCollisionShape() { + } + + /** + * creates a cylinder shape from the given halfextents + * @param halfExtents the halfextents to use + */ + public CylinderCollisionShape(Vector3f halfExtents) { + this.halfExtents = halfExtents; + this.axis = 2; + createShape(); + } + + /** + * Creates a cylinder shape around the given axis from the given halfextents + * @param halfExtents the halfextents to use + * @param axis (0=X,1=Y,2=Z) + */ + public CylinderCollisionShape(Vector3f halfExtents, int axis) { + this.halfExtents = halfExtents; + this.axis = axis; + createShape(); + } + + public final Vector3f getHalfExtents() { + return halfExtents; + } + + public int getAxis() { + return axis; + } + + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(halfExtents, "halfExtents", new Vector3f(0.5f, 0.5f, 0.5f)); + capsule.write(axis, "axis", 1); + } + + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + halfExtents = (Vector3f) capsule.readSavable("halfExtents", new Vector3f(0.5f, 0.5f, 0.5f)); + axis = capsule.readInt("axis", 1); + createShape(); + } + + protected void createShape() { + switch (axis) { + case 0: + cShape = new CylinderShapeX(Converter.convert(halfExtents)); + break; + case 1: + cShape = new CylinderShape(Converter.convert(halfExtents)); + break; + case 2: + cShape = new CylinderShapeZ(Converter.convert(halfExtents)); + break; + } + cShape.setLocalScaling(Converter.convert(getScale())); + cShape.setMargin(margin); + } + +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java new file mode 100644 index 0000000..6d12420 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java @@ -0,0 +1,133 @@ +/* + * 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.collision.shapes; + +import com.bulletphysics.collision.shapes.IndexedMesh; +import com.bulletphysics.collision.shapes.TriangleIndexVertexArray; +import com.bulletphysics.extras.gimpact.GImpactMeshShape; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Vector3f; +import com.jme3.scene.Mesh; +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * Basic mesh collision shape + * @author normenhansen + */ +public class GImpactCollisionShape extends CollisionShape{ + + protected Vector3f worldScale; + protected int numVertices, numTriangles, vertexStride, triangleIndexStride; + protected ByteBuffer triangleIndexBase, vertexBase; + protected IndexedMesh bulletMesh; + + public GImpactCollisionShape() { + } + + /** + * creates a collision shape from the given Mesh + * @param mesh the Mesh to use + */ + public GImpactCollisionShape(Mesh mesh) { + createCollisionMesh(mesh, new Vector3f(1,1,1)); + } + + + private void createCollisionMesh(Mesh mesh, Vector3f worldScale) { + this.worldScale = worldScale; + bulletMesh = Converter.convert(mesh); + this.numVertices = bulletMesh.numVertices; + this.numTriangles = bulletMesh.numTriangles; + this.vertexStride = bulletMesh.vertexStride; + this.triangleIndexStride = bulletMesh.triangleIndexStride; + this.triangleIndexBase = bulletMesh.triangleIndexBase; + this.vertexBase = bulletMesh.vertexBase; + createShape(); + } + + /** + * creates a jme mesh from the collision shape, only needed for debugging + */ + public Mesh createJmeMesh(){ + return Converter.convert(bulletMesh); + } + + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(worldScale, "worldScale", new Vector3f(1, 1, 1)); + capsule.write(numVertices, "numVertices", 0); + capsule.write(numTriangles, "numTriangles", 0); + capsule.write(vertexStride, "vertexStride", 0); + capsule.write(triangleIndexStride, "triangleIndexStride", 0); + + capsule.write(triangleIndexBase.array(), "triangleIndexBase", new byte[0]); + capsule.write(vertexBase.array(), "vertexBase", new byte[0]); + } + + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + worldScale = (Vector3f) capsule.readSavable("worldScale", new Vector3f(1, 1, 1)); + numVertices = capsule.readInt("numVertices", 0); + numTriangles = capsule.readInt("numTriangles", 0); + vertexStride = capsule.readInt("vertexStride", 0); + triangleIndexStride = capsule.readInt("triangleIndexStride", 0); + + triangleIndexBase = ByteBuffer.wrap(capsule.readByteArray("triangleIndexBase", new byte[0])); + vertexBase = ByteBuffer.wrap(capsule.readByteArray("vertexBase", new byte[0])); + createShape(); + } + + protected void createShape() { + bulletMesh = new IndexedMesh(); + bulletMesh.numVertices = numVertices; + bulletMesh.numTriangles = numTriangles; + bulletMesh.vertexStride = vertexStride; + bulletMesh.triangleIndexStride = triangleIndexStride; + bulletMesh.triangleIndexBase = triangleIndexBase; + bulletMesh.vertexBase = vertexBase; + bulletMesh.triangleIndexBase = triangleIndexBase; + TriangleIndexVertexArray tiv = new TriangleIndexVertexArray(numTriangles, triangleIndexBase, triangleIndexStride, numVertices, vertexBase, vertexStride); + cShape = new GImpactMeshShape(tiv); + cShape.setLocalScaling(Converter.convert(worldScale)); + ((GImpactMeshShape)cShape).updateBound(); + cShape.setLocalScaling(Converter.convert(getScale())); + cShape.setMargin(margin); + } + +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java new file mode 100644 index 0000000..7f0611e --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java @@ -0,0 +1,133 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package com.jme3.bullet.collision.shapes; + +import com.bulletphysics.dom.HeightfieldTerrainShape; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.FastMath; +import com.jme3.math.Vector3f; +import com.jme3.scene.Mesh; +import java.io.IOException; + +/** + * Uses Bullet Physics Heightfield terrain collision system. This is MUCH faster + * than using a regular mesh. + * There are a couple tricks though: + * -No rotation or translation is supported. + * -The collision bbox must be centered around 0,0,0 with the height above and below the y-axis being + * equal on either side. If not, the whole collision box is shifted vertically and things don't collide + * as they should. + * + * @author Brent Owens + */ +public class HeightfieldCollisionShape extends CollisionShape { + + //protected HeightfieldTerrainShape heightfieldShape; + protected int heightStickWidth; + protected int heightStickLength; + protected float[] heightfieldData; + protected float heightScale; + protected float minHeight; + protected float maxHeight; + protected int upAxis; + protected boolean flipQuadEdges; + + public HeightfieldCollisionShape() { + + } + + public HeightfieldCollisionShape(float[] heightmap) { + createCollisionHeightfield(heightmap, Vector3f.UNIT_XYZ); + } + + public HeightfieldCollisionShape(float[] heightmap, Vector3f scale) { + createCollisionHeightfield(heightmap, scale); + } + + protected void createCollisionHeightfield(float[] heightmap, Vector3f worldScale) { + this.scale = worldScale; + this.heightScale = 1;//don't change away from 1, we use worldScale instead to scale + + this.heightfieldData = heightmap; + + float min = heightfieldData[0]; + float max = heightfieldData[0]; + // calculate min and max height + for (int i=0; i<heightfieldData.length; i++) { + if (heightfieldData[i] < min) + min = heightfieldData[i]; + if (heightfieldData[i] > max) + max = heightfieldData[i]; + } + // we need to center the terrain collision box at 0,0,0 for BulletPhysics. And to do that we need to set the + // min and max height to be equal on either side of the y axis, otherwise it gets shifted and collision is incorrect. + if (max < 0) + max = -min; + else { + if (Math.abs(max) > Math.abs(min)) + min = -max; + else + max = -min; + } + this.minHeight = min; + this.maxHeight = max; + + this.upAxis = HeightfieldTerrainShape.YAXIS; + this.flipQuadEdges = false; + + heightStickWidth = (int) FastMath.sqrt(heightfieldData.length); + heightStickLength = heightStickWidth; + + + createShape(); + } + + protected void createShape() { + + HeightfieldTerrainShape shape = new HeightfieldTerrainShape(heightStickWidth, heightStickLength, heightfieldData, heightScale, minHeight, maxHeight, upAxis, flipQuadEdges); + shape.setLocalScaling(new javax.vecmath.Vector3f(scale.x, scale.y, scale.z)); + cShape = shape; + cShape.setLocalScaling(Converter.convert(getScale())); + cShape.setMargin(margin); + } + + public Mesh createJmeMesh(){ + //TODO return Converter.convert(bulletMesh); + return null; + } + + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(heightStickWidth, "heightStickWidth", 0); + capsule.write(heightStickLength, "heightStickLength", 0); + capsule.write(heightScale, "heightScale", 0); + capsule.write(minHeight, "minHeight", 0); + capsule.write(maxHeight, "maxHeight", 0); + capsule.write(upAxis, "upAxis", 1); + capsule.write(heightfieldData, "heightfieldData", new float[0]); + capsule.write(flipQuadEdges, "flipQuadEdges", false); + } + + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + heightStickWidth = capsule.readInt("heightStickWidth", 0); + heightStickLength = capsule.readInt("heightStickLength", 0); + heightScale = capsule.readFloat("heightScale", 0); + minHeight = capsule.readFloat("minHeight", 0); + maxHeight = capsule.readFloat("maxHeight", 0); + upAxis = capsule.readInt("upAxis", 1); + heightfieldData = capsule.readFloatArray("heightfieldData", new float[0]); + flipQuadEdges = capsule.readBoolean("flipQuadEdges", false); + createShape(); + } + +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java new file mode 100644 index 0000000..1aa4a67 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java @@ -0,0 +1,79 @@ +package com.jme3.bullet.collision.shapes; + +import com.bulletphysics.collision.shapes.ConvexHullShape; +import com.bulletphysics.util.ObjectArrayList; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.scene.Mesh; +import com.jme3.scene.VertexBuffer.Type; +import java.io.IOException; +import java.nio.FloatBuffer; +import javax.vecmath.Vector3f; + +public class HullCollisionShape extends CollisionShape { + + private float[] points; + + public HullCollisionShape() { + } + + public HullCollisionShape(Mesh mesh) { + this.points = getPoints(mesh); + createShape(this.points); + } + + public HullCollisionShape(float[] points) { + this.points = points; + createShape(this.points); + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(points, "points", null); + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + + // for backwards compatability + Mesh mesh = (Mesh) capsule.readSavable("hullMesh", null); + if (mesh != null) { + this.points = getPoints(mesh); + } else { + this.points = capsule.readFloatArray("points", null); + + } + createShape(this.points); + } + + protected void createShape(float[] points) { + ObjectArrayList<Vector3f> pointList = new ObjectArrayList<Vector3f>(); + for (int i = 0; i < points.length; i += 3) { + pointList.add(new Vector3f(points[i], points[i + 1], points[i + 2])); + } + cShape = new ConvexHullShape(pointList); + cShape.setLocalScaling(Converter.convert(getScale())); + cShape.setMargin(margin); + } + + protected float[] getPoints(Mesh mesh) { + FloatBuffer vertices = mesh.getFloatBuffer(Type.Position); + vertices.rewind(); + int components = mesh.getVertexCount() * 3; + float[] pointsArray = new float[components]; + for (int i = 0; i < components; i += 3) { + pointsArray[i] = vertices.get(); + pointsArray[i + 1] = vertices.get(); + pointsArray[i + 2] = vertices.get(); + } + return pointsArray; + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java new file mode 100644 index 0000000..661b8dc --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java @@ -0,0 +1,126 @@ +/* + * 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.collision.shapes; + +import com.bulletphysics.collision.shapes.BvhTriangleMeshShape; +import com.bulletphysics.collision.shapes.IndexedMesh; +import com.bulletphysics.collision.shapes.TriangleIndexVertexArray; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Vector3f; +import com.jme3.scene.Mesh; +import java.io.IOException; +import java.nio.ByteBuffer; + +/** + * Basic mesh collision shape + * @author normenhansen + */ +public class MeshCollisionShape extends CollisionShape { + + protected int numVertices, numTriangles, vertexStride, triangleIndexStride; + protected ByteBuffer triangleIndexBase, vertexBase; + protected IndexedMesh bulletMesh; + + public MeshCollisionShape() { + } + + /** + * creates a collision shape from the given TriMesh + * @param mesh the TriMesh to use + */ + public MeshCollisionShape(Mesh mesh) { + createCollisionMesh(mesh, new Vector3f(1, 1, 1)); + } + + private void createCollisionMesh(Mesh mesh, Vector3f worldScale) { + this.scale = worldScale; + bulletMesh = Converter.convert(mesh); + this.numVertices = bulletMesh.numVertices; + this.numTriangles = bulletMesh.numTriangles; + this.vertexStride = bulletMesh.vertexStride; + this.triangleIndexStride = bulletMesh.triangleIndexStride; + this.triangleIndexBase = bulletMesh.triangleIndexBase; + this.vertexBase = bulletMesh.vertexBase; + createShape(); + } + + /** + * creates a jme mesh from the collision shape, only needed for debugging + */ + public Mesh createJmeMesh(){ + return Converter.convert(bulletMesh); + } + + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(numVertices, "numVertices", 0); + capsule.write(numTriangles, "numTriangles", 0); + capsule.write(vertexStride, "vertexStride", 0); + capsule.write(triangleIndexStride, "triangleIndexStride", 0); + + capsule.write(triangleIndexBase.array(), "triangleIndexBase", new byte[0]); + capsule.write(vertexBase.array(), "vertexBase", new byte[0]); + } + + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + numVertices = capsule.readInt("numVertices", 0); + numTriangles = capsule.readInt("numTriangles", 0); + vertexStride = capsule.readInt("vertexStride", 0); + triangleIndexStride = capsule.readInt("triangleIndexStride", 0); + + triangleIndexBase = ByteBuffer.wrap(capsule.readByteArray("triangleIndexBase", new byte[0])); + vertexBase = ByteBuffer.wrap(capsule.readByteArray("vertexBase", new byte[0])); + createShape(); + } + + protected void createShape() { + bulletMesh = new IndexedMesh(); + bulletMesh.numVertices = numVertices; + bulletMesh.numTriangles = numTriangles; + bulletMesh.vertexStride = vertexStride; + bulletMesh.triangleIndexStride = triangleIndexStride; + bulletMesh.triangleIndexBase = triangleIndexBase; + bulletMesh.vertexBase = vertexBase; + bulletMesh.triangleIndexBase = triangleIndexBase; + TriangleIndexVertexArray tiv = new TriangleIndexVertexArray(numTriangles, triangleIndexBase, triangleIndexStride, numVertices, vertexBase, vertexStride); + cShape = new BvhTriangleMeshShape(tiv, true); + cShape.setLocalScaling(Converter.convert(getScale())); + cShape.setMargin(margin); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java new file mode 100644 index 0000000..ce51f18 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java @@ -0,0 +1,59 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ + +package com.jme3.bullet.collision.shapes; + +import com.bulletphysics.collision.shapes.StaticPlaneShape; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Plane; +import java.io.IOException; + +/** + * + * @author normenhansen + */ +public class PlaneCollisionShape extends CollisionShape{ + private Plane plane; + + public PlaneCollisionShape() { + } + + /** + * Creates a plane Collision shape + * @param plane the plane that defines the shape + */ + public PlaneCollisionShape(Plane plane) { + this.plane = plane; + createShape(); + } + + public final Plane getPlane() { + return plane; + } + + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(plane, "collisionPlane", new Plane()); + } + + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + plane = (Plane) capsule.readSavable("collisionPlane", new Plane()); + createShape(); + } + + protected void createShape() { + cShape = new StaticPlaneShape(Converter.convert(plane.getNormal()),plane.getConstant()); + cShape.setLocalScaling(Converter.convert(getScale())); + cShape.setMargin(margin); + } + +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java new file mode 100644 index 0000000..7ddefd3 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java @@ -0,0 +1,85 @@ +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package com.jme3.bullet.collision.shapes; + +import com.bulletphysics.collision.shapes.BU_Simplex1to4; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Vector3f; +import java.io.IOException; + +/** + * A simple point, line, triangle or quad collisionShape based on one to four points- + * @author normenhansen + */ +public class SimplexCollisionShape extends CollisionShape { + + private Vector3f vector1, vector2, vector3, vector4; + + public SimplexCollisionShape() { + } + + public SimplexCollisionShape(Vector3f point1, Vector3f point2, Vector3f point3, Vector3f point4) { + vector1 = point1; + vector2 = point2; + vector3 = point3; + vector4 = point4; + createShape(); + } + + public SimplexCollisionShape(Vector3f point1, Vector3f point2, Vector3f point3) { + vector1 = point1; + vector2 = point2; + vector3 = point3; + createShape(); + } + + public SimplexCollisionShape(Vector3f point1, Vector3f point2) { + vector1 = point1; + vector2 = point2; + createShape(); + } + + public SimplexCollisionShape(Vector3f point1) { + vector1 = point1; + createShape(); + } + + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(vector1, "simplexPoint1", null); + capsule.write(vector2, "simplexPoint2", null); + capsule.write(vector3, "simplexPoint3", null); + capsule.write(vector4, "simplexPoint4", null); + } + + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + vector1 = (Vector3f) capsule.readSavable("simplexPoint1", null); + vector2 = (Vector3f) capsule.readSavable("simplexPoint2", null); + vector3 = (Vector3f) capsule.readSavable("simplexPoint3", null); + vector4 = (Vector3f) capsule.readSavable("simplexPoint4", null); + createShape(); + } + + protected void createShape() { + if (vector4 != null) { + cShape = new BU_Simplex1to4(Converter.convert(vector1), Converter.convert(vector2), Converter.convert(vector3), Converter.convert(vector4)); + } else if (vector3 != null) { + cShape = new BU_Simplex1to4(Converter.convert(vector1), Converter.convert(vector2), Converter.convert(vector3)); + } else if (vector2 != null) { + cShape = new BU_Simplex1to4(Converter.convert(vector1), Converter.convert(vector2)); + } else { + cShape = new BU_Simplex1to4(Converter.convert(vector1)); + } + cShape.setLocalScaling(Converter.convert(getScale())); + cShape.setMargin(margin); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java b/engine/src/jbullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java new file mode 100644 index 0000000..787f597 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java @@ -0,0 +1,85 @@ +/* + * 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.collision.shapes; + +import com.bulletphysics.collision.shapes.SphereShape; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import java.io.IOException; + +/** + * Basic sphere collision shape + * @author normenhansen + */ +public class SphereCollisionShape extends CollisionShape { + + protected float radius; + + public SphereCollisionShape() { + } + + /** + * creates a SphereCollisionShape with the given radius + * @param radius + */ + public SphereCollisionShape(float radius) { + this.radius = radius; + createShape(); + } + + public float getRadius() { + return radius; + } + + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(radius, "radius", 0.5f); + } + + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + radius = capsule.readFloat("radius", 0.5f); + createShape(); + } + + protected void createShape() { + cShape = new SphereShape(radius); + cShape.setLocalScaling(Converter.convert(getScale())); + cShape.setMargin(margin); + } + +} diff --git a/engine/src/jbullet/com/jme3/bullet/joints/ConeJoint.java b/engine/src/jbullet/com/jme3/bullet/joints/ConeJoint.java new file mode 100644 index 0000000..2dc54f4 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/joints/ConeJoint.java @@ -0,0 +1,138 @@ +/* + * 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.joints; + +import com.bulletphysics.dynamics.constraintsolver.ConeTwistConstraint; +import com.bulletphysics.linearmath.Transform; +import com.jme3.bullet.objects.PhysicsRigidBody; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Matrix3f; +import com.jme3.math.Vector3f; +import java.io.IOException; + +/** + * <i>From bullet manual:</i><br> + * To create ragdolls, the conve twist constraint is very useful for limbs like the upper arm. + * It is a special point to point constraint that adds cone and twist axis limits. + * The x-axis serves as twist axis. + * @author normenhansen + */ +public class ConeJoint extends PhysicsJoint { + + protected Matrix3f rotA, rotB; + protected float swingSpan1 = 1e30f; + protected float swingSpan2 = 1e30f; + protected float twistSpan = 1e30f; + protected boolean angularOnly = false; + + public ConeJoint() { + } + + /** + * @param pivotA local translation of the joint connection point in node A + * @param pivotB local translation of the joint connection point in node B + */ + public ConeJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB) { + super(nodeA, nodeB, pivotA, pivotB); + this.rotA = new Matrix3f(); + this.rotB = new Matrix3f(); + createJoint(); + } + + /** + * @param pivotA local translation of the joint connection point in node A + * @param pivotB local translation of the joint connection point in node B + */ + public ConeJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB) { + super(nodeA, nodeB, pivotA, pivotB); + this.rotA = rotA; + this.rotB = rotB; + createJoint(); + } + + public void setLimit(float swingSpan1, float swingSpan2, float twistSpan) { + this.swingSpan1 = swingSpan1; + this.swingSpan2 = swingSpan2; + this.twistSpan = twistSpan; + ((ConeTwistConstraint) constraint).setLimit(swingSpan1, swingSpan2, twistSpan); + } + + public void setAngularOnly(boolean value) { + angularOnly = value; + ((ConeTwistConstraint) constraint).setAngularOnly(value); + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(rotA, "rotA", new Matrix3f()); + capsule.write(rotB, "rotB", new Matrix3f()); + + capsule.write(angularOnly, "angularOnly", false); + capsule.write(swingSpan1, "swingSpan1", 1e30f); + capsule.write(swingSpan2, "swingSpan2", 1e30f); + capsule.write(twistSpan, "twistSpan", 1e30f); + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + this.rotA = (Matrix3f) capsule.readSavable("rotA", new Matrix3f()); + this.rotB = (Matrix3f) capsule.readSavable("rotB", new Matrix3f()); + + this.angularOnly = capsule.readBoolean("angularOnly", false); + this.swingSpan1 = capsule.readFloat("swingSpan1", 1e30f); + this.swingSpan2 = capsule.readFloat("swingSpan2", 1e30f); + this.twistSpan = capsule.readFloat("twistSpan", 1e30f); + createJoint(); + } + + protected void createJoint() { + Transform transA = new Transform(Converter.convert(rotA)); + Converter.convert(pivotA, transA.origin); + Converter.convert(rotA, transA.basis); + + Transform transB = new Transform(Converter.convert(rotB)); + Converter.convert(pivotB, transB.origin); + Converter.convert(rotB, transB.basis); + + constraint = new ConeTwistConstraint(nodeA.getObjectId(), nodeB.getObjectId(), transA, transB); + ((ConeTwistConstraint) constraint).setLimit(swingSpan1, swingSpan2, twistSpan); + ((ConeTwistConstraint) constraint).setAngularOnly(angularOnly); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/joints/HingeJoint.java b/engine/src/jbullet/com/jme3/bullet/joints/HingeJoint.java new file mode 100644 index 0000000..47b0250 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/joints/HingeJoint.java @@ -0,0 +1,156 @@ +/* + * 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.joints; + +import com.bulletphysics.dynamics.constraintsolver.HingeConstraint; +import com.jme3.bullet.objects.PhysicsRigidBody; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Vector3f; +import java.io.IOException; + +/** + * <i>From bullet manual:</i><br> + * Hinge constraint, or revolute joint restricts two additional angular degrees of freedom, + * so the body can only rotate around one axis, the hinge axis. + * This can be useful to represent doors or wheels rotating around one axis. + * The user can specify limits and motor for the hinge. + * @author normenhansen + */ +public class HingeJoint extends PhysicsJoint { + + protected Vector3f axisA; + protected Vector3f axisB; + protected boolean angularOnly = false; + protected float biasFactor = 0.3f; + protected float relaxationFactor = 1.0f; + protected float limitSoftness = 0.9f; + + public HingeJoint() { + } + + /** + * Creates a new HingeJoint + * @param pivotA local translation of the joint connection point in node A + * @param pivotB local translation of the joint connection point in node B + */ + public HingeJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Vector3f axisA, Vector3f axisB) { + super(nodeA, nodeB, pivotA, pivotB); + this.axisA = axisA; + this.axisB = axisB; + createJoint(); + } + + public void enableMotor(boolean enable, float targetVelocity, float maxMotorImpulse) { + ((HingeConstraint) constraint).enableAngularMotor(enable, targetVelocity, maxMotorImpulse); + } + + public void setLimit(float low, float high) { + ((HingeConstraint) constraint).setLimit(low, high); + } + + public void setLimit(float low, float high, float _softness, float _biasFactor, float _relaxationFactor) { + biasFactor = _biasFactor; + relaxationFactor = _relaxationFactor; + limitSoftness = _softness; + ((HingeConstraint) constraint).setLimit(low, high, _softness, _biasFactor, _relaxationFactor); + } + + public float getUpperLimit(){ + return ((HingeConstraint) constraint).getUpperLimit(); + } + + public float getLowerLimit(){ + return ((HingeConstraint) constraint).getLowerLimit(); + } + + public void setAngularOnly(boolean angularOnly) { + this.angularOnly = angularOnly; + ((HingeConstraint) constraint).setAngularOnly(angularOnly); + } + + public float getHingeAngle() { + return ((HingeConstraint) constraint).getHingeAngle(); + } + + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(axisA, "axisA", new Vector3f()); + capsule.write(axisB, "axisB", new Vector3f()); + + capsule.write(angularOnly, "angularOnly", false); + + capsule.write(((HingeConstraint) constraint).getLowerLimit(), "lowerLimit", 1e30f); + capsule.write(((HingeConstraint) constraint).getUpperLimit(), "upperLimit", -1e30f); + + capsule.write(biasFactor, "biasFactor", 0.3f); + capsule.write(relaxationFactor, "relaxationFactor", 1f); + capsule.write(limitSoftness, "limitSoftness", 0.9f); + + capsule.write(((HingeConstraint) constraint).getEnableAngularMotor(), "enableAngularMotor", false); + capsule.write(((HingeConstraint) constraint).getMotorTargetVelosity(), "targetVelocity", 0.0f); + capsule.write(((HingeConstraint) constraint).getMaxMotorImpulse(), "maxMotorImpulse", 0.0f); + } + + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + this.axisA = (Vector3f) capsule.readSavable("axisA", new Vector3f()); + this.axisB = (Vector3f) capsule.readSavable("axisB", new Vector3f()); + + this.angularOnly = capsule.readBoolean("angularOnly", false); + float lowerLimit = capsule.readFloat("lowerLimit", 1e30f); + float upperLimit = capsule.readFloat("upperLimit", -1e30f); + + this.biasFactor = capsule.readFloat("biasFactor", 0.3f); + this.relaxationFactor = capsule.readFloat("relaxationFactor", 1f); + this.limitSoftness = capsule.readFloat("limitSoftness", 0.9f); + + boolean enableAngularMotor=capsule.readBoolean("enableAngularMotor", false); + float targetVelocity=capsule.readFloat("targetVelocity", 0.0f); + float maxMotorImpulse=capsule.readFloat("maxMotorImpulse", 0.0f); + + createJoint(); + enableMotor(enableAngularMotor, targetVelocity, maxMotorImpulse); + ((HingeConstraint) constraint).setLimit(lowerLimit, upperLimit, limitSoftness, biasFactor, relaxationFactor); + } + + protected void createJoint() { + constraint = new HingeConstraint(nodeA.getObjectId(), nodeB.getObjectId(), + Converter.convert(pivotA), Converter.convert(pivotB), + Converter.convert(axisA), Converter.convert(axisB)); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/joints/PhysicsJoint.java b/engine/src/jbullet/com/jme3/bullet/joints/PhysicsJoint.java new file mode 100644 index 0000000..533078a --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/joints/PhysicsJoint.java @@ -0,0 +1,136 @@ +/* + * 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.joints; + +import com.bulletphysics.dynamics.constraintsolver.TypedConstraint; +import com.jme3.bullet.objects.PhysicsRigidBody; +import com.jme3.export.*; +import com.jme3.math.Vector3f; +import java.io.IOException; + +/** + * <p>PhysicsJoint - Basic Phyiscs Joint</p> + * @author normenhansen + */ +public abstract class PhysicsJoint implements Savable { + + protected TypedConstraint constraint; + protected PhysicsRigidBody nodeA; + protected PhysicsRigidBody nodeB; + protected Vector3f pivotA; + protected Vector3f pivotB; + protected boolean collisionBetweenLinkedBodys = true; + + public PhysicsJoint() { + } + + /** + * @param pivotA local translation of the joint connection point in node A + * @param pivotB local translation of the joint connection point in node B + */ + public PhysicsJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB) { + this.nodeA = nodeA; + this.nodeB = nodeB; + this.pivotA = pivotA; + this.pivotB = pivotB; + nodeA.addJoint(this); + nodeB.addJoint(this); + } + + public float getAppliedImpulse(){ + return constraint.getAppliedImpulse(); + } + + /** + * @return the constraint + */ + public TypedConstraint getObjectId() { + return constraint; + } + + /** + * @return the collisionBetweenLinkedBodys + */ + public boolean isCollisionBetweenLinkedBodys() { + return collisionBetweenLinkedBodys; + } + + /** + * toggles collisions between linked bodys<br> + * joint has to be removed from and added to PhyiscsSpace to apply this. + * @param collisionBetweenLinkedBodys set to false to have no collisions between linked bodys + */ + public void setCollisionBetweenLinkedBodys(boolean collisionBetweenLinkedBodys) { + this.collisionBetweenLinkedBodys = collisionBetweenLinkedBodys; + } + + public PhysicsRigidBody getBodyA() { + return nodeA; + } + + public PhysicsRigidBody getBodyB() { + return nodeB; + } + + public Vector3f getPivotA() { + return pivotA; + } + + public Vector3f getPivotB() { + return pivotB; + } + + /** + * destroys this joint and removes it from its connected PhysicsRigidBodys joint lists + */ + public void destroy() { + getBodyA().removeJoint(this); + getBodyB().removeJoint(this); + } + + public void write(JmeExporter ex) throws IOException { + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(nodeA, "nodeA", null); + capsule.write(nodeB, "nodeB", null); + capsule.write(pivotA, "pivotA", null); + capsule.write(pivotB, "pivotB", null); + } + + public void read(JmeImporter im) throws IOException { + InputCapsule capsule = im.getCapsule(this); + this.nodeA = ((PhysicsRigidBody) capsule.readSavable("nodeA", new PhysicsRigidBody())); + this.nodeB = (PhysicsRigidBody) capsule.readSavable("nodeB", new PhysicsRigidBody()); + this.pivotA = (Vector3f) capsule.readSavable("pivotA", new Vector3f()); + this.pivotB = (Vector3f) capsule.readSavable("pivotB", new Vector3f()); + } + +} diff --git a/engine/src/jbullet/com/jme3/bullet/joints/Point2PointJoint.java b/engine/src/jbullet/com/jme3/bullet/joints/Point2PointJoint.java new file mode 100644 index 0000000..a3f768a --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/joints/Point2PointJoint.java @@ -0,0 +1,111 @@ +/* + * 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.joints; + +import com.bulletphysics.dynamics.constraintsolver.Point2PointConstraint; +import com.jme3.bullet.objects.PhysicsRigidBody; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Vector3f; +import java.io.IOException; + +/** + * <i>From bullet manual:</i><br> + * Point to point constraint, also known as ball socket joint limits the translation + * so that the local pivot points of 2 rigidbodies match in worldspace. + * A chain of rigidbodies can be connected using this constraint. + * @author normenhansen + */ +public class Point2PointJoint extends PhysicsJoint { + + public Point2PointJoint() { + } + + /** + * @param pivotA local translation of the joint connection point in node A + * @param pivotB local translation of the joint connection point in node B + */ + public Point2PointJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB) { + super(nodeA, nodeB, pivotA, pivotB); + createJoint(); + } + + public void setDamping(float value) { + ((Point2PointConstraint) constraint).setting.damping = value; + } + + public void setImpulseClamp(float value) { + ((Point2PointConstraint) constraint).setting.impulseClamp = value; + } + + public void setTau(float value) { + ((Point2PointConstraint) constraint).setting.tau = value; + } + + public float getDamping() { + return ((Point2PointConstraint) constraint).setting.damping; + } + + public float getImpulseClamp() { + return ((Point2PointConstraint) constraint).setting.impulseClamp; + } + + public float getTau() { + return ((Point2PointConstraint) constraint).setting.tau; + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule cap = ex.getCapsule(this); + cap.write(getDamping(), "damping", 1.0f); + cap.write(getTau(), "tau", 0.3f); + cap.write(getImpulseClamp(), "impulseClamp", 0f); + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + createJoint(); + InputCapsule cap=im.getCapsule(this); + setDamping(cap.readFloat("damping", 1.0f)); + setDamping(cap.readFloat("tau", 0.3f)); + setDamping(cap.readFloat("impulseClamp", 0f)); + } + + protected void createJoint() { + constraint = new Point2PointConstraint(nodeA.getObjectId(), nodeB.getObjectId(), Converter.convert(pivotA), Converter.convert(pivotB)); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/joints/SixDofJoint.java b/engine/src/jbullet/com/jme3/bullet/joints/SixDofJoint.java new file mode 100644 index 0000000..e153760 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/joints/SixDofJoint.java @@ -0,0 +1,228 @@ +/* + * 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.joints; + +import com.bulletphysics.dynamics.constraintsolver.Generic6DofConstraint; +import com.bulletphysics.linearmath.Transform; +import com.jme3.bullet.joints.motors.RotationalLimitMotor; +import com.jme3.bullet.joints.motors.TranslationalLimitMotor; +import com.jme3.bullet.objects.PhysicsRigidBody; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Matrix3f; +import com.jme3.math.Vector3f; +import java.io.IOException; +import java.util.Iterator; +import java.util.LinkedList; + +/** + * <i>From bullet manual:</i><br> + * This generic constraint can emulate a variety of standard constraints, + * by configuring each of the 6 degrees of freedom (dof). + * The first 3 dof axis are linear axis, which represent translation of rigidbodies, + * and the latter 3 dof axis represent the angular motion. Each axis can be either locked, + * free or limited. On construction of a new btGeneric6DofConstraint, all axis are locked. + * Afterwards the axis can be reconfigured. Note that several combinations that + * include free and/or limited angular degrees of freedom are undefined. + * @author normenhansen + */ +public class SixDofJoint extends PhysicsJoint { + + private boolean useLinearReferenceFrameA = true; + private LinkedList<RotationalLimitMotor> rotationalMotors = new LinkedList<RotationalLimitMotor>(); + private TranslationalLimitMotor translationalMotor; + private Vector3f angularUpperLimit = new Vector3f(Vector3f.POSITIVE_INFINITY); + private Vector3f angularLowerLimit = new Vector3f(Vector3f.NEGATIVE_INFINITY); + private Vector3f linearUpperLimit = new Vector3f(Vector3f.POSITIVE_INFINITY); + private Vector3f linearLowerLimit = new Vector3f(Vector3f.NEGATIVE_INFINITY); + + public SixDofJoint() { + } + + /** + * @param pivotA local translation of the joint connection point in node A + * @param pivotB local translation of the joint connection point in node B + */ + public SixDofJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB, boolean useLinearReferenceFrameA) { + super(nodeA, nodeB, pivotA, pivotB); + this.useLinearReferenceFrameA = useLinearReferenceFrameA; + + Transform transA = new Transform(Converter.convert(rotA)); + Converter.convert(pivotA, transA.origin); + Converter.convert(rotA, transA.basis); + + Transform transB = new Transform(Converter.convert(rotB)); + Converter.convert(pivotB, transB.origin); + Converter.convert(rotB, transB.basis); + + constraint = new Generic6DofConstraint(nodeA.getObjectId(), nodeB.getObjectId(), transA, transB, useLinearReferenceFrameA); + gatherMotors(); + } + + /** + * @param pivotA local translation of the joint connection point in node A + * @param pivotB local translation of the joint connection point in node B + */ + public SixDofJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, boolean useLinearReferenceFrameA) { + super(nodeA, nodeB, pivotA, pivotB); + this.useLinearReferenceFrameA = useLinearReferenceFrameA; + + Transform transA = new Transform(Converter.convert(new Matrix3f())); + Converter.convert(pivotA, transA.origin); + + Transform transB = new Transform(Converter.convert(new Matrix3f())); + Converter.convert(pivotB, transB.origin); + + constraint = new Generic6DofConstraint(nodeA.getObjectId(), nodeB.getObjectId(), transA, transB, useLinearReferenceFrameA); + gatherMotors(); + } + + private void gatherMotors() { + for (int i = 0; i < 3; i++) { + RotationalLimitMotor rmot = new RotationalLimitMotor(((Generic6DofConstraint) constraint).getRotationalLimitMotor(i)); + rotationalMotors.add(rmot); + } + translationalMotor = new TranslationalLimitMotor(((Generic6DofConstraint) constraint).getTranslationalLimitMotor()); + } + + /** + * returns the TranslationalLimitMotor of this 6DofJoint which allows + * manipulating the translational axis + * @return the TranslationalLimitMotor + */ + public TranslationalLimitMotor getTranslationalLimitMotor() { + return translationalMotor; + } + + /** + * returns one of the three RotationalLimitMotors of this 6DofJoint which + * allow manipulating the rotational axes + * @param index the index of the RotationalLimitMotor + * @return the RotationalLimitMotor at the given index + */ + public RotationalLimitMotor getRotationalLimitMotor(int index) { + return rotationalMotors.get(index); + } + + public void setLinearUpperLimit(Vector3f vector) { + linearUpperLimit.set(vector); + ((Generic6DofConstraint) constraint).setLinearUpperLimit(Converter.convert(vector)); + } + + public void setLinearLowerLimit(Vector3f vector) { + linearLowerLimit.set(vector); + ((Generic6DofConstraint) constraint).setLinearLowerLimit(Converter.convert(vector)); + } + + public void setAngularUpperLimit(Vector3f vector) { + angularUpperLimit.set(vector); + ((Generic6DofConstraint) constraint).setAngularUpperLimit(Converter.convert(vector)); + } + + public void setAngularLowerLimit(Vector3f vector) { + angularLowerLimit.set(vector); + ((Generic6DofConstraint) constraint).setAngularLowerLimit(Converter.convert(vector)); + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + + Transform transA = new Transform(Converter.convert(new Matrix3f())); + Converter.convert(pivotA, transA.origin); + + Transform transB = new Transform(Converter.convert(new Matrix3f())); + Converter.convert(pivotB, transB.origin); + constraint = new Generic6DofConstraint(nodeA.getObjectId(), nodeB.getObjectId(), transA, transB, useLinearReferenceFrameA); + gatherMotors(); + + setAngularUpperLimit((Vector3f) capsule.readSavable("angularUpperLimit", new Vector3f(Vector3f.POSITIVE_INFINITY))); + setAngularLowerLimit((Vector3f) capsule.readSavable("angularLowerLimit", new Vector3f(Vector3f.NEGATIVE_INFINITY))); + setLinearUpperLimit((Vector3f) capsule.readSavable("linearUpperLimit", new Vector3f(Vector3f.POSITIVE_INFINITY))); + setLinearLowerLimit((Vector3f) capsule.readSavable("linearLowerLimit", new Vector3f(Vector3f.NEGATIVE_INFINITY))); + + for (int i = 0; i < 3; i++) { + RotationalLimitMotor rotationalLimitMotor = getRotationalLimitMotor(i); + rotationalLimitMotor.setBounce(capsule.readFloat("rotMotor" + i + "_Bounce", 0.0f)); + rotationalLimitMotor.setDamping(capsule.readFloat("rotMotor" + i + "_Damping", 1.0f)); + rotationalLimitMotor.setERP(capsule.readFloat("rotMotor" + i + "_ERP", 0.5f)); + rotationalLimitMotor.setHiLimit(capsule.readFloat("rotMotor" + i + "_HiLimit", Float.POSITIVE_INFINITY)); + rotationalLimitMotor.setLimitSoftness(capsule.readFloat("rotMotor" + i + "_LimitSoftness", 0.5f)); + rotationalLimitMotor.setLoLimit(capsule.readFloat("rotMotor" + i + "_LoLimit", Float.NEGATIVE_INFINITY)); + rotationalLimitMotor.setMaxLimitForce(capsule.readFloat("rotMotor" + i + "_MaxLimitForce", 300.0f)); + rotationalLimitMotor.setMaxMotorForce(capsule.readFloat("rotMotor" + i + "_MaxMotorForce", 0.1f)); + rotationalLimitMotor.setTargetVelocity(capsule.readFloat("rotMotor" + i + "_TargetVelocity", 0)); + rotationalLimitMotor.setEnableMotor(capsule.readBoolean("rotMotor" + i + "_EnableMotor", false)); + } + getTranslationalLimitMotor().setAccumulatedImpulse((Vector3f) capsule.readSavable("transMotor_AccumulatedImpulse", Vector3f.ZERO)); + getTranslationalLimitMotor().setDamping(capsule.readFloat("transMotor_Damping", 1.0f)); + getTranslationalLimitMotor().setLimitSoftness(capsule.readFloat("transMotor_LimitSoftness", 0.7f)); + getTranslationalLimitMotor().setLowerLimit((Vector3f) capsule.readSavable("transMotor_LowerLimit", Vector3f.ZERO)); + getTranslationalLimitMotor().setRestitution(capsule.readFloat("transMotor_Restitution", 0.5f)); + getTranslationalLimitMotor().setUpperLimit((Vector3f) capsule.readSavable("transMotor_UpperLimit", Vector3f.ZERO)); + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + capsule.write(angularUpperLimit, "angularUpperLimit", new Vector3f(Vector3f.POSITIVE_INFINITY)); + capsule.write(angularLowerLimit, "angularLowerLimit", new Vector3f(Vector3f.NEGATIVE_INFINITY)); + capsule.write(linearUpperLimit, "linearUpperLimit", new Vector3f(Vector3f.POSITIVE_INFINITY)); + capsule.write(linearLowerLimit, "linearLowerLimit", new Vector3f(Vector3f.NEGATIVE_INFINITY)); + int i = 0; + for (Iterator<RotationalLimitMotor> it = rotationalMotors.iterator(); it.hasNext();) { + RotationalLimitMotor rotationalLimitMotor = it.next(); + capsule.write(rotationalLimitMotor.getBounce(), "rotMotor" + i + "_Bounce", 0.0f); + capsule.write(rotationalLimitMotor.getDamping(), "rotMotor" + i + "_Damping", 1.0f); + capsule.write(rotationalLimitMotor.getERP(), "rotMotor" + i + "_ERP", 0.5f); + capsule.write(rotationalLimitMotor.getHiLimit(), "rotMotor" + i + "_HiLimit", Float.POSITIVE_INFINITY); + capsule.write(rotationalLimitMotor.getLimitSoftness(), "rotMotor" + i + "_LimitSoftness", 0.5f); + capsule.write(rotationalLimitMotor.getLoLimit(), "rotMotor" + i + "_LoLimit", Float.NEGATIVE_INFINITY); + capsule.write(rotationalLimitMotor.getMaxLimitForce(), "rotMotor" + i + "_MaxLimitForce", 300.0f); + capsule.write(rotationalLimitMotor.getMaxMotorForce(), "rotMotor" + i + "_MaxMotorForce", 0.1f); + capsule.write(rotationalLimitMotor.getTargetVelocity(), "rotMotor" + i + "_TargetVelocity", 0); + capsule.write(rotationalLimitMotor.isEnableMotor(), "rotMotor" + i + "_EnableMotor", false); + i++; + } + capsule.write(getTranslationalLimitMotor().getAccumulatedImpulse(), "transMotor_AccumulatedImpulse", Vector3f.ZERO); + capsule.write(getTranslationalLimitMotor().getDamping(), "transMotor_Damping", 1.0f); + capsule.write(getTranslationalLimitMotor().getLimitSoftness(), "transMotor_LimitSoftness", 0.7f); + capsule.write(getTranslationalLimitMotor().getLowerLimit(), "transMotor_LowerLimit", Vector3f.ZERO); + capsule.write(getTranslationalLimitMotor().getRestitution(), "transMotor_Restitution", 0.5f); + capsule.write(getTranslationalLimitMotor().getUpperLimit(), "transMotor_UpperLimit", Vector3f.ZERO); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/joints/SliderJoint.java b/engine/src/jbullet/com/jme3/bullet/joints/SliderJoint.java new file mode 100644 index 0000000..4602018 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/joints/SliderJoint.java @@ -0,0 +1,430 @@ +/* + * 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.joints; + +import com.bulletphysics.dynamics.constraintsolver.SliderConstraint; +import com.bulletphysics.linearmath.Transform; +import com.jme3.bullet.objects.PhysicsRigidBody; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Matrix3f; +import com.jme3.math.Vector3f; +import java.io.IOException; + +/** + * <i>From bullet manual:</i><br> + * The slider constraint allows the body to rotate around one axis and translate along this axis. + * @author normenhansen + */ +public class SliderJoint extends PhysicsJoint { + protected Matrix3f rotA, rotB; + protected boolean useLinearReferenceFrameA; + + public SliderJoint() { + } + + /** + * @param pivotA local translation of the joint connection point in node A + * @param pivotB local translation of the joint connection point in node B + */ + public SliderJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, Matrix3f rotA, Matrix3f rotB, boolean useLinearReferenceFrameA) { + super(nodeA, nodeB, pivotA, pivotB); + this.rotA=rotA; + this.rotB=rotB; + this.useLinearReferenceFrameA=useLinearReferenceFrameA; + createJoint(); + } + + /** + * @param pivotA local translation of the joint connection point in node A + * @param pivotB local translation of the joint connection point in node B + */ + public SliderJoint(PhysicsRigidBody nodeA, PhysicsRigidBody nodeB, Vector3f pivotA, Vector3f pivotB, boolean useLinearReferenceFrameA) { + super(nodeA, nodeB, pivotA, pivotB); + this.rotA=new Matrix3f(); + this.rotB=new Matrix3f(); + this.useLinearReferenceFrameA=useLinearReferenceFrameA; + createJoint(); + } + + public float getLowerLinLimit() { + return ((SliderConstraint) constraint).getLowerLinLimit(); + } + + public void setLowerLinLimit(float lowerLinLimit) { + ((SliderConstraint) constraint).setLowerLinLimit(lowerLinLimit); + } + + public float getUpperLinLimit() { + return ((SliderConstraint) constraint).getUpperLinLimit(); + } + + public void setUpperLinLimit(float upperLinLimit) { + ((SliderConstraint) constraint).setUpperLinLimit(upperLinLimit); + } + + public float getLowerAngLimit() { + return ((SliderConstraint) constraint).getLowerAngLimit(); + } + + public void setLowerAngLimit(float lowerAngLimit) { + ((SliderConstraint) constraint).setLowerAngLimit(lowerAngLimit); + } + + public float getUpperAngLimit() { + return ((SliderConstraint) constraint).getUpperAngLimit(); + } + + public void setUpperAngLimit(float upperAngLimit) { + ((SliderConstraint) constraint).setUpperAngLimit(upperAngLimit); + } + + public float getSoftnessDirLin() { + return ((SliderConstraint) constraint).getSoftnessDirLin(); + } + + public void setSoftnessDirLin(float softnessDirLin) { + ((SliderConstraint) constraint).setSoftnessDirLin(softnessDirLin); + } + + public float getRestitutionDirLin() { + return ((SliderConstraint) constraint).getRestitutionDirLin(); + } + + public void setRestitutionDirLin(float restitutionDirLin) { + ((SliderConstraint) constraint).setRestitutionDirLin(restitutionDirLin); + } + + public float getDampingDirLin() { + return ((SliderConstraint) constraint).getDampingDirLin(); + } + + public void setDampingDirLin(float dampingDirLin) { + ((SliderConstraint) constraint).setDampingDirLin(dampingDirLin); + } + + public float getSoftnessDirAng() { + return ((SliderConstraint) constraint).getSoftnessDirAng(); + } + + public void setSoftnessDirAng(float softnessDirAng) { + ((SliderConstraint) constraint).setSoftnessDirAng(softnessDirAng); + } + + public float getRestitutionDirAng() { + return ((SliderConstraint) constraint).getRestitutionDirAng(); + } + + public void setRestitutionDirAng(float restitutionDirAng) { + ((SliderConstraint) constraint).setRestitutionDirAng(restitutionDirAng); + } + + public float getDampingDirAng() { + return ((SliderConstraint) constraint).getDampingDirAng(); + } + + public void setDampingDirAng(float dampingDirAng) { + ((SliderConstraint) constraint).setDampingDirAng(dampingDirAng); + } + + public float getSoftnessLimLin() { + return ((SliderConstraint) constraint).getSoftnessLimLin(); + } + + public void setSoftnessLimLin(float softnessLimLin) { + ((SliderConstraint) constraint).setSoftnessLimLin(softnessLimLin); + } + + public float getRestitutionLimLin() { + return ((SliderConstraint) constraint).getRestitutionLimLin(); + } + + public void setRestitutionLimLin(float restitutionLimLin) { + ((SliderConstraint) constraint).setRestitutionLimLin(restitutionLimLin); + } + + public float getDampingLimLin() { + return ((SliderConstraint) constraint).getDampingLimLin(); + } + + public void setDampingLimLin(float dampingLimLin) { + ((SliderConstraint) constraint).setDampingLimLin(dampingLimLin); + } + + public float getSoftnessLimAng() { + return ((SliderConstraint) constraint).getSoftnessLimAng(); + } + + public void setSoftnessLimAng(float softnessLimAng) { + ((SliderConstraint) constraint).setSoftnessLimAng(softnessLimAng); + } + + public float getRestitutionLimAng() { + return ((SliderConstraint) constraint).getRestitutionLimAng(); + } + + public void setRestitutionLimAng(float restitutionLimAng) { + ((SliderConstraint) constraint).setRestitutionLimAng(restitutionLimAng); + } + + public float getDampingLimAng() { + return ((SliderConstraint) constraint).getDampingLimAng(); + } + + public void setDampingLimAng(float dampingLimAng) { + ((SliderConstraint) constraint).setDampingLimAng(dampingLimAng); + } + + public float getSoftnessOrthoLin() { + return ((SliderConstraint) constraint).getSoftnessOrthoLin(); + } + + public void setSoftnessOrthoLin(float softnessOrthoLin) { + ((SliderConstraint) constraint).setSoftnessOrthoLin(softnessOrthoLin); + } + + public float getRestitutionOrthoLin() { + return ((SliderConstraint) constraint).getRestitutionOrthoLin(); + } + + public void setRestitutionOrthoLin(float restitutionOrthoLin) { + ((SliderConstraint) constraint).setRestitutionOrthoLin(restitutionOrthoLin); + } + + public float getDampingOrthoLin() { + return ((SliderConstraint) constraint).getDampingOrthoLin(); + } + + public void setDampingOrthoLin(float dampingOrthoLin) { + ((SliderConstraint) constraint).setDampingOrthoLin(dampingOrthoLin); + } + + public float getSoftnessOrthoAng() { + return ((SliderConstraint) constraint).getSoftnessOrthoAng(); + } + + public void setSoftnessOrthoAng(float softnessOrthoAng) { + ((SliderConstraint) constraint).setSoftnessOrthoAng(softnessOrthoAng); + } + + public float getRestitutionOrthoAng() { + return ((SliderConstraint) constraint).getRestitutionOrthoAng(); + } + + public void setRestitutionOrthoAng(float restitutionOrthoAng) { + ((SliderConstraint) constraint).setRestitutionOrthoAng(restitutionOrthoAng); + } + + public float getDampingOrthoAng() { + return ((SliderConstraint) constraint).getDampingOrthoAng(); + } + + public void setDampingOrthoAng(float dampingOrthoAng) { + ((SliderConstraint) constraint).setDampingOrthoAng(dampingOrthoAng); + } + + public boolean isPoweredLinMotor() { + return ((SliderConstraint) constraint).getPoweredLinMotor(); + } + + public void setPoweredLinMotor(boolean poweredLinMotor) { + ((SliderConstraint) constraint).setPoweredLinMotor(poweredLinMotor); + } + + public float getTargetLinMotorVelocity() { + return ((SliderConstraint) constraint).getTargetLinMotorVelocity(); + } + + public void setTargetLinMotorVelocity(float targetLinMotorVelocity) { + ((SliderConstraint) constraint).setTargetLinMotorVelocity(targetLinMotorVelocity); + } + + public float getMaxLinMotorForce() { + return ((SliderConstraint) constraint).getMaxLinMotorForce(); + } + + public void setMaxLinMotorForce(float maxLinMotorForce) { + ((SliderConstraint) constraint).setMaxLinMotorForce(maxLinMotorForce); + } + + public boolean isPoweredAngMotor() { + return ((SliderConstraint) constraint).getPoweredAngMotor(); + } + + public void setPoweredAngMotor(boolean poweredAngMotor) { + ((SliderConstraint) constraint).setPoweredAngMotor(poweredAngMotor); + } + + public float getTargetAngMotorVelocity() { + return ((SliderConstraint) constraint).getTargetAngMotorVelocity(); + } + + public void setTargetAngMotorVelocity(float targetAngMotorVelocity) { + ((SliderConstraint) constraint).setTargetAngMotorVelocity(targetAngMotorVelocity); + } + + public float getMaxAngMotorForce() { + return ((SliderConstraint) constraint).getMaxAngMotorForce(); + } + + public void setMaxAngMotorForce(float maxAngMotorForce) { + ((SliderConstraint) constraint).setMaxAngMotorForce(maxAngMotorForce); + } + + @Override + public void write(JmeExporter ex) throws IOException { + super.write(ex); + OutputCapsule capsule = ex.getCapsule(this); + //TODO: standard values.. + capsule.write(((SliderConstraint) constraint).getDampingDirAng(), "dampingDirAng", 0f); + capsule.write(((SliderConstraint) constraint).getDampingDirLin(), "dampingDirLin", 0f); + capsule.write(((SliderConstraint) constraint).getDampingLimAng(), "dampingLimAng", 0f); + capsule.write(((SliderConstraint) constraint).getDampingLimLin(), "dampingLimLin", 0f); + capsule.write(((SliderConstraint) constraint).getDampingOrthoAng(), "dampingOrthoAng", 0f); + capsule.write(((SliderConstraint) constraint).getDampingOrthoLin(), "dampingOrthoLin", 0f); + capsule.write(((SliderConstraint) constraint).getLowerAngLimit(), "lowerAngLimit", 0f); + capsule.write(((SliderConstraint) constraint).getLowerLinLimit(), "lowerLinLimit", 0f); + capsule.write(((SliderConstraint) constraint).getMaxAngMotorForce(), "maxAngMotorForce", 0f); + capsule.write(((SliderConstraint) constraint).getMaxLinMotorForce(), "maxLinMotorForce", 0f); + capsule.write(((SliderConstraint) constraint).getPoweredAngMotor(), "poweredAngMotor", false); + capsule.write(((SliderConstraint) constraint).getPoweredLinMotor(), "poweredLinMotor", false); + capsule.write(((SliderConstraint) constraint).getRestitutionDirAng(), "restitutionDirAng", 0f); + capsule.write(((SliderConstraint) constraint).getRestitutionDirLin(), "restitutionDirLin", 0f); + capsule.write(((SliderConstraint) constraint).getRestitutionLimAng(), "restitutionLimAng", 0f); + capsule.write(((SliderConstraint) constraint).getRestitutionLimLin(), "restitutionLimLin", 0f); + capsule.write(((SliderConstraint) constraint).getRestitutionOrthoAng(), "restitutionOrthoAng", 0f); + capsule.write(((SliderConstraint) constraint).getRestitutionOrthoLin(), "restitutionOrthoLin", 0f); + + capsule.write(((SliderConstraint) constraint).getSoftnessDirAng(), "softnessDirAng", 0f); + capsule.write(((SliderConstraint) constraint).getSoftnessDirLin(), "softnessDirLin", 0f); + capsule.write(((SliderConstraint) constraint).getSoftnessLimAng(), "softnessLimAng", 0f); + capsule.write(((SliderConstraint) constraint).getSoftnessLimLin(), "softnessLimLin", 0f); + capsule.write(((SliderConstraint) constraint).getSoftnessOrthoAng(), "softnessOrthoAng", 0f); + capsule.write(((SliderConstraint) constraint).getSoftnessOrthoLin(), "softnessOrthoLin", 0f); + + capsule.write(((SliderConstraint) constraint).getTargetAngMotorVelocity(), "targetAngMotorVelicoty", 0f); + capsule.write(((SliderConstraint) constraint).getTargetLinMotorVelocity(), "targetLinMotorVelicoty", 0f); + + capsule.write(((SliderConstraint) constraint).getUpperAngLimit(), "upperAngLimit", 0f); + capsule.write(((SliderConstraint) constraint).getUpperLinLimit(), "upperLinLimit", 0f); + + capsule.write(useLinearReferenceFrameA, "useLinearReferenceFrameA", false); + } + + @Override + public void read(JmeImporter im) throws IOException { + super.read(im); + InputCapsule capsule = im.getCapsule(this); + float dampingDirAng = capsule.readFloat("dampingDirAng", 0f); + float dampingDirLin = capsule.readFloat("dampingDirLin", 0f); + float dampingLimAng = capsule.readFloat("dampingLimAng", 0f); + float dampingLimLin = capsule.readFloat("dampingLimLin", 0f); + float dampingOrthoAng = capsule.readFloat("dampingOrthoAng", 0f); + float dampingOrthoLin = capsule.readFloat("dampingOrthoLin", 0f); + float lowerAngLimit = capsule.readFloat("lowerAngLimit", 0f); + float lowerLinLimit = capsule.readFloat("lowerLinLimit", 0f); + float maxAngMotorForce = capsule.readFloat("maxAngMotorForce", 0f); + float maxLinMotorForce = capsule.readFloat("maxLinMotorForce", 0f); + boolean poweredAngMotor = capsule.readBoolean("poweredAngMotor", false); + boolean poweredLinMotor = capsule.readBoolean("poweredLinMotor", false); + float restitutionDirAng = capsule.readFloat("restitutionDirAng", 0f); + float restitutionDirLin = capsule.readFloat("restitutionDirLin", 0f); + float restitutionLimAng = capsule.readFloat("restitutionLimAng", 0f); + float restitutionLimLin = capsule.readFloat("restitutionLimLin", 0f); + float restitutionOrthoAng = capsule.readFloat("restitutionOrthoAng", 0f); + float restitutionOrthoLin = capsule.readFloat("restitutionOrthoLin", 0f); + + float softnessDirAng = capsule.readFloat("softnessDirAng", 0f); + float softnessDirLin = capsule.readFloat("softnessDirLin", 0f); + float softnessLimAng = capsule.readFloat("softnessLimAng", 0f); + float softnessLimLin = capsule.readFloat("softnessLimLin", 0f); + float softnessOrthoAng = capsule.readFloat("softnessOrthoAng", 0f); + float softnessOrthoLin = capsule.readFloat("softnessOrthoLin", 0f); + + float targetAngMotorVelicoty = capsule.readFloat("targetAngMotorVelicoty", 0f); + float targetLinMotorVelicoty = capsule.readFloat("targetLinMotorVelicoty", 0f); + + float upperAngLimit = capsule.readFloat("upperAngLimit", 0f); + float upperLinLimit = capsule.readFloat("upperLinLimit", 0f); + + useLinearReferenceFrameA = capsule.readBoolean("useLinearReferenceFrameA", false); + + createJoint(); + + ((SliderConstraint)constraint).setDampingDirAng(dampingDirAng); + ((SliderConstraint)constraint).setDampingDirLin(dampingDirLin); + ((SliderConstraint)constraint).setDampingLimAng(dampingLimAng); + ((SliderConstraint)constraint).setDampingLimLin(dampingLimLin); + ((SliderConstraint)constraint).setDampingOrthoAng(dampingOrthoAng); + ((SliderConstraint)constraint).setDampingOrthoLin(dampingOrthoLin); + ((SliderConstraint)constraint).setLowerAngLimit(lowerAngLimit); + ((SliderConstraint)constraint).setLowerLinLimit(lowerLinLimit); + ((SliderConstraint)constraint).setMaxAngMotorForce(maxAngMotorForce); + ((SliderConstraint)constraint).setMaxLinMotorForce(maxLinMotorForce); + ((SliderConstraint)constraint).setPoweredAngMotor(poweredAngMotor); + ((SliderConstraint)constraint).setPoweredLinMotor(poweredLinMotor); + ((SliderConstraint)constraint).setRestitutionDirAng(restitutionDirAng); + ((SliderConstraint)constraint).setRestitutionDirLin(restitutionDirLin); + ((SliderConstraint)constraint).setRestitutionLimAng(restitutionLimAng); + ((SliderConstraint)constraint).setRestitutionLimLin(restitutionLimLin); + ((SliderConstraint)constraint).setRestitutionOrthoAng(restitutionOrthoAng); + ((SliderConstraint)constraint).setRestitutionOrthoLin(restitutionOrthoLin); + + ((SliderConstraint)constraint).setSoftnessDirAng(softnessDirAng); + ((SliderConstraint)constraint).setSoftnessDirLin(softnessDirLin); + ((SliderConstraint)constraint).setSoftnessLimAng(softnessLimAng); + ((SliderConstraint)constraint).setSoftnessLimLin(softnessLimLin); + ((SliderConstraint)constraint).setSoftnessOrthoAng(softnessOrthoAng); + ((SliderConstraint)constraint).setSoftnessOrthoLin(softnessOrthoLin); + + ((SliderConstraint)constraint).setTargetAngMotorVelocity(targetAngMotorVelicoty); + ((SliderConstraint)constraint).setTargetLinMotorVelocity(targetLinMotorVelicoty); + + ((SliderConstraint)constraint).setUpperAngLimit(upperAngLimit); + ((SliderConstraint)constraint).setUpperLinLimit(upperLinLimit); + } + + protected void createJoint(){ + Transform transA = new Transform(Converter.convert(rotA)); + Converter.convert(pivotA, transA.origin); + Converter.convert(rotA, transA.basis); + + Transform transB = new Transform(Converter.convert(rotB)); + Converter.convert(pivotB, transB.origin); + Converter.convert(rotB, transB.basis); + + constraint = new SliderConstraint(nodeA.getObjectId(), nodeB.getObjectId(), transA, transB, useLinearReferenceFrameA); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java b/engine/src/jbullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java new file mode 100644 index 0000000..b9df96d --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java @@ -0,0 +1,129 @@ +/* + * 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.joints.motors; + +/** + * + * @author normenhansen + */ +public class RotationalLimitMotor { + + private com.bulletphysics.dynamics.constraintsolver.RotationalLimitMotor motor; + + public RotationalLimitMotor(com.bulletphysics.dynamics.constraintsolver.RotationalLimitMotor motor) { + this.motor = motor; + } + + public com.bulletphysics.dynamics.constraintsolver.RotationalLimitMotor getMotor() { + return motor; + } + + public float getLoLimit() { + return motor.loLimit; + } + + public void setLoLimit(float loLimit) { + motor.loLimit = loLimit; + } + + public float getHiLimit() { + return motor.hiLimit; + } + + public void setHiLimit(float hiLimit) { + motor.hiLimit = hiLimit; + } + + public float getTargetVelocity() { + return motor.targetVelocity; + } + + public void setTargetVelocity(float targetVelocity) { + motor.targetVelocity = targetVelocity; + } + + public float getMaxMotorForce() { + return motor.maxMotorForce; + } + + public void setMaxMotorForce(float maxMotorForce) { + motor.maxMotorForce = maxMotorForce; + } + + public float getMaxLimitForce() { + return motor.maxLimitForce; + } + + public void setMaxLimitForce(float maxLimitForce) { + motor.maxLimitForce = maxLimitForce; + } + + public float getDamping() { + return motor.damping; + } + + public void setDamping(float damping) { + motor.damping = damping; + } + + public float getLimitSoftness() { + return motor.limitSoftness; + } + + public void setLimitSoftness(float limitSoftness) { + motor.limitSoftness = limitSoftness; + } + + public float getERP() { + return motor.ERP; + } + + public void setERP(float ERP) { + motor.ERP = ERP; + } + + public float getBounce() { + return motor.bounce; + } + + public void setBounce(float bounce) { + motor.bounce = bounce; + } + + public boolean isEnableMotor() { + return motor.enableMotor; + } + + public void setEnableMotor(boolean enableMotor) { + motor.enableMotor = enableMotor; + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java b/engine/src/jbullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java new file mode 100644 index 0000000..d5c23cb --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java @@ -0,0 +1,100 @@ +/* + * 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.joints.motors; + +import com.jme3.bullet.util.Converter; +import com.jme3.math.Vector3f; + +/** + * + * @author normenhansen + */ +public class TranslationalLimitMotor { + + private com.bulletphysics.dynamics.constraintsolver.TranslationalLimitMotor motor; + + public TranslationalLimitMotor(com.bulletphysics.dynamics.constraintsolver.TranslationalLimitMotor motor) { + this.motor = motor; + } + + public com.bulletphysics.dynamics.constraintsolver.TranslationalLimitMotor getMotor() { + return motor; + } + + public Vector3f getLowerLimit() { + return Converter.convert(motor.lowerLimit); + } + + public void setLowerLimit(Vector3f lowerLimit) { + Converter.convert(lowerLimit, motor.lowerLimit); + } + + public Vector3f getUpperLimit() { + return Converter.convert(motor.upperLimit); + } + + public void setUpperLimit(Vector3f upperLimit) { + Converter.convert(upperLimit, motor.upperLimit); + } + + public Vector3f getAccumulatedImpulse() { + return Converter.convert(motor.accumulatedImpulse); + } + + public void setAccumulatedImpulse(Vector3f accumulatedImpulse) { + Converter.convert(accumulatedImpulse, motor.accumulatedImpulse); + } + + public float getLimitSoftness() { + return motor.limitSoftness; + } + + public void setLimitSoftness(float limitSoftness) { + motor.limitSoftness = limitSoftness; + } + + public float getDamping() { + return motor.damping; + } + + public void setDamping(float damping) { + motor.damping = damping; + } + + public float getRestitution() { + return motor.restitution; + } + + public void setRestitution(float restitution) { + motor.restitution = restitution; + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/objects/PhysicsCharacter.java b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsCharacter.java new file mode 100644 index 0000000..932c2e1 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsCharacter.java @@ -0,0 +1,290 @@ +/* + * 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.bulletphysics.collision.dispatch.CollisionFlags; +import com.bulletphysics.collision.dispatch.PairCachingGhostObject; +import com.bulletphysics.collision.shapes.ConvexShape; +import com.bulletphysics.dynamics.character.KinematicCharacterController; +import com.bulletphysics.linearmath.Transform; +import com.jme3.bullet.collision.PhysicsCollisionObject; +import com.jme3.bullet.collision.shapes.CollisionShape; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Matrix3f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import java.io.IOException; + +/** + * Basic Bullet Character + * @author normenhansen + */ +public class PhysicsCharacter extends PhysicsCollisionObject { + + protected KinematicCharacterController character; + protected float stepHeight; + protected Vector3f walkDirection = new Vector3f(); + protected float fallSpeed = 55.0f; + protected float jumpSpeed = 10.0f; + protected int upAxis = 1; + protected PairCachingGhostObject gObject; + protected boolean locationDirty = false; + //TEMP VARIABLES + protected final Quaternion tmp_inverseWorldRotation = new Quaternion(); + private Transform tempTrans = new Transform(Converter.convert(new Matrix3f())); + private com.jme3.math.Transform physicsLocation = new com.jme3.math.Transform(); + private javax.vecmath.Vector3f tempVec = new javax.vecmath.Vector3f(); + + 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.getCShape() instanceof ConvexShape)) { + throw (new UnsupportedOperationException("Kinematic character nodes cannot have mesh collision shapes")); + } + this.stepHeight = stepHeight; + buildObject(); + } + + protected void buildObject() { + if (gObject == null) { + gObject = new PairCachingGhostObject(); + } + gObject.setCollisionFlags(CollisionFlags.CHARACTER_OBJECT); + gObject.setCollisionFlags(gObject.getCollisionFlags() & ~CollisionFlags.NO_CONTACT_RESPONSE); + gObject.setCollisionShape(collisionShape.getCShape()); + gObject.setUserPointer(this); + character = new KinematicCharacterController(gObject, (ConvexShape) collisionShape.getCShape(), stepHeight); + } + + /** + * Sets the location of this physics character + * @param location + */ + public void warp(Vector3f location) { + character.warp(Converter.convert(location, tempVec)); + } + + /** + * 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); + character.setWalkDirection(Converter.convert(walkDirection, tempVec)); + } + + /** + * @return the currently set walkDirection + */ + public Vector3f getWalkDirection() { + return walkDirection; + } + + public void setUpAxis(int axis) { + upAxis = axis; + character.setUpAxis(axis); + } + + public int getUpAxis() { + return upAxis; + } + + public void setFallSpeed(float fallSpeed) { + this.fallSpeed = fallSpeed; + character.setFallSpeed(fallSpeed); + } + + public float getFallSpeed() { + return fallSpeed; + } + + public void setJumpSpeed(float jumpSpeed) { + this.jumpSpeed = jumpSpeed; + character.setJumpSpeed(jumpSpeed); + } + + public float getJumpSpeed() { + return jumpSpeed; + } + + //does nothing.. +// public void setMaxJumpHeight(float height) { +// character.setMaxJumpHeight(height); +// } + public void setGravity(float value) { + character.setGravity(value); + } + + public float getGravity() { + return character.getGravity(); + } + + public void setMaxSlope(float slopeRadians) { + character.setMaxSlope(slopeRadians); + } + + public float getMaxSlope() { + return character.getMaxSlope(); + } + + public boolean onGround() { + return character.onGround(); + } + + public void jump() { + character.jump(); + } + + @Override + public void setCollisionShape(CollisionShape collisionShape) { + if (!(collisionShape.getCShape() instanceof ConvexShape)) { + throw (new UnsupportedOperationException("Kinematic character nodes cannot have mesh collision shapes")); + } + super.setCollisionShape(collisionShape); + if (gObject == null) { + buildObject(); + }else{ + gObject.setCollisionShape(collisionShape.getCShape()); + } + } + + /** + * 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(); + } + gObject.getWorldTransform(tempTrans); + Converter.convert(tempTrans.origin, physicsLocation.getTranslation()); + return trans.set(physicsLocation.getTranslation()); + } + + /** + * @return the physicsLocation + */ + public Vector3f getPhysicsLocation() { + gObject.getWorldTransform(tempTrans); + Converter.convert(tempTrans.origin, physicsLocation.getTranslation()); + return physicsLocation.getTranslation(); + } + + public void setCcdSweptSphereRadius(float radius) { + gObject.setCcdSweptSphereRadius(radius); + } + + public void setCcdMotionThreshold(float threshold) { + gObject.setCcdMotionThreshold(threshold); + } + + public float getCcdSweptSphereRadius() { + return gObject.getCcdSweptSphereRadius(); + } + + public float getCcdMotionThreshold() { + return gObject.getCcdMotionThreshold(); + } + + public float getCcdSquareMotionThreshold() { + return gObject.getCcdSquareMotionThreshold(); + } + + /** + * used internally + */ + public KinematicCharacterController getControllerId() { + return character; + } + + /** + * used internally + */ + public PairCachingGhostObject getObjectId() { + return gObject; + } + + 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(); + character = new KinematicCharacterController(gObject, (ConvexShape) collisionShape.getCShape(), stepHeight); + 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())); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/objects/PhysicsGhostObject.java b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsGhostObject.java new file mode 100644 index 0000000..2f53a82 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsGhostObject.java @@ -0,0 +1,284 @@ +/* + * 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.bulletphysics.collision.dispatch.CollisionFlags; +import com.bulletphysics.collision.dispatch.PairCachingGhostObject; +import com.bulletphysics.linearmath.Transform; +import com.jme3.bullet.collision.PhysicsCollisionObject; +import com.jme3.bullet.collision.shapes.CollisionShape; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Matrix3f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.scene.Spatial; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +/** + * <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 PairCachingGhostObject gObject; + protected boolean locationDirty = false; + //TEMP VARIABLES + protected final Quaternion tmp_inverseWorldRotation = new Quaternion(); + protected Transform tempTrans = new Transform(Converter.convert(new Matrix3f())); + private com.jme3.math.Transform physicsLocation = new com.jme3.math.Transform(); + protected javax.vecmath.Quat4f tempRot = new javax.vecmath.Quat4f(); + 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 (gObject == null) { + gObject = new PairCachingGhostObject(); + gObject.setCollisionFlags(gObject.getCollisionFlags() | CollisionFlags.NO_CONTACT_RESPONSE); + } + gObject.setCollisionShape(collisionShape.getCShape()); + gObject.setUserPointer(this); + } + + @Override + public void setCollisionShape(CollisionShape collisionShape) { + super.setCollisionShape(collisionShape); + if (gObject == null) { + buildObject(); + }else{ + gObject.setCollisionShape(collisionShape.getCShape()); + } + } + + /** + * Sets the physics object location + * @param location the location of the actual physics object + */ + public void setPhysicsLocation(Vector3f location) { + gObject.getWorldTransform(tempTrans); + Converter.convert(location, tempTrans.origin); + gObject.setWorldTransform(tempTrans); + } + + /** + * Sets the physics object rotation + * @param rotation the rotation of the actual physics object + */ + public void setPhysicsRotation(Matrix3f rotation) { + gObject.getWorldTransform(tempTrans); + Converter.convert(rotation, tempTrans.basis); + gObject.setWorldTransform(tempTrans); + } + + /** + * Sets the physics object rotation + * @param rotation the rotation of the actual physics object + */ + public void setPhysicsRotation(Quaternion rotation) { + gObject.getWorldTransform(tempTrans); + Converter.convert(rotation, tempTrans.basis); + gObject.setWorldTransform(tempTrans); + } + + /** + * @return the physicsLocation + */ + public com.jme3.math.Transform getPhysicsTransform() { + return physicsLocation; + } + + /** + * @return the physicsLocation + */ + public Vector3f getPhysicsLocation(Vector3f trans) { + if (trans == null) { + trans = new Vector3f(); + } + gObject.getWorldTransform(tempTrans); + Converter.convert(tempTrans.origin, physicsLocation.getTranslation()); + return trans.set(physicsLocation.getTranslation()); + } + + /** + * @return the physicsLocation + */ + public Quaternion getPhysicsRotation(Quaternion rot) { + if (rot == null) { + rot = new Quaternion(); + } + gObject.getWorldTransform(tempTrans); + Converter.convert(tempTrans.getRotation(tempRot), physicsLocation.getRotation()); + return rot.set(physicsLocation.getRotation()); + } + + /** + * @return the physicsLocation + */ + public Matrix3f getPhysicsRotationMatrix(Matrix3f rot) { + if (rot == null) { + rot = new Matrix3f(); + } + gObject.getWorldTransform(tempTrans); + Converter.convert(tempTrans.getRotation(tempRot), physicsLocation.getRotation()); + return rot.set(physicsLocation.getRotation()); + } + + /** + * @return the physicsLocation + */ + public Vector3f getPhysicsLocation() { + gObject.getWorldTransform(tempTrans); + Converter.convert(tempTrans.origin, physicsLocation.getTranslation()); + return physicsLocation.getTranslation(); + } + + /** + * @return the physicsLocation + */ + public Quaternion getPhysicsRotation() { + gObject.getWorldTransform(tempTrans); + Converter.convert(tempTrans.getRotation(tempRot), physicsLocation.getRotation()); + return physicsLocation.getRotation(); + } + + public Matrix3f getPhysicsRotationMatrix() { + gObject.getWorldTransform(tempTrans); + Converter.convert(tempTrans.getRotation(tempRot), physicsLocation.getRotation()); + return physicsLocation.getRotation().toRotationMatrix(); + } + + /** + * 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(); + for (com.bulletphysics.collision.dispatch.CollisionObject collObj : gObject.getOverlappingPairs()) { + overlappingObjects.add((PhysicsCollisionObject) collObj.getUserPointer()); + } + return overlappingObjects; + } + + /** + * + * @return With how many other CollisionObjects this GhostNode is currently overlapping. + */ + public int getOverlappingCount() { + return gObject.getNumOverlappingObjects(); + } + + /** + * + * @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) { + gObject.setCcdSweptSphereRadius(radius); + } + + public void setCcdMotionThreshold(float threshold) { + gObject.setCcdMotionThreshold(threshold); + } + + public float getCcdSweptSphereRadius() { + return gObject.getCcdSweptSphereRadius(); + } + + public float getCcdMotionThreshold() { + return gObject.getCcdMotionThreshold(); + } + + public float getCcdSquareMotionThreshold() { + return gObject.getCcdSquareMotionThreshold(); + } + + @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/jbullet/com/jme3/bullet/objects/PhysicsRigidBody.java b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsRigidBody.java new file mode 100644 index 0000000..d0aea98 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsRigidBody.java @@ -0,0 +1,703 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.bullet.objects; + +import com.bulletphysics.collision.dispatch.CollisionFlags; +import com.bulletphysics.dynamics.RigidBody; +import com.bulletphysics.dynamics.RigidBodyConstructionInfo; +import com.bulletphysics.linearmath.Transform; +import com.jme3.bullet.PhysicsSpace; +import com.jme3.bullet.collision.PhysicsCollisionObject; +import com.jme3.bullet.collision.shapes.CollisionShape; +import com.jme3.bullet.collision.shapes.MeshCollisionShape; +import com.jme3.bullet.joints.PhysicsJoint; +import com.jme3.bullet.objects.infos.RigidBodyMotionState; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.Matrix3f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.debug.Arrow; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * <p>PhysicsRigidBody - Basic physics object</p> + * @author normenhansen + */ +public class PhysicsRigidBody extends PhysicsCollisionObject { + + protected RigidBodyConstructionInfo constructionInfo; + protected RigidBody rBody; + protected RigidBodyMotionState motionState = new RigidBodyMotionState(); + protected float mass = 1.0f; + protected boolean kinematic = false; + protected javax.vecmath.Vector3f tempVec = new javax.vecmath.Vector3f(); + protected javax.vecmath.Vector3f tempVec2 = new javax.vecmath.Vector3f(); + protected Transform tempTrans = new Transform(new javax.vecmath.Matrix3f()); + protected javax.vecmath.Matrix3f tempMatrix = new javax.vecmath.Matrix3f(); + //TEMP VARIABLES + protected javax.vecmath.Vector3f localInertia = new javax.vecmath.Vector3f(); + protected ArrayList<PhysicsJoint> joints = new ArrayList<PhysicsJoint>(); + + public PhysicsRigidBody() { + } + + /** + * Creates a new PhysicsRigidBody with the supplied collision shape + * @param shape + */ + public PhysicsRigidBody(CollisionShape shape) { + collisionShape = shape; + rebuildRigidBody(); + } + + public PhysicsRigidBody(CollisionShape shape, float mass) { + collisionShape = shape; + this.mass = mass; + rebuildRigidBody(); + } + + /** + * Builds/rebuilds the phyiscs body when parameters have changed + */ + protected void rebuildRigidBody() { + boolean removed = false; + if(collisionShape instanceof MeshCollisionShape && mass != 0){ + throw new IllegalStateException("Dynamic rigidbody can not have mesh collision shape!"); + } + if (rBody != null) { + if (rBody.isInWorld()) { + PhysicsSpace.getPhysicsSpace().remove(this); + removed = true; + } + rBody.destroy(); + } + preRebuild(); + rBody = new RigidBody(constructionInfo); + postRebuild(); + if (removed) { + PhysicsSpace.getPhysicsSpace().add(this); + } + } + + protected void preRebuild() { + collisionShape.calculateLocalInertia(mass, localInertia); + if (constructionInfo == null) { + constructionInfo = new RigidBodyConstructionInfo(mass, motionState, collisionShape.getCShape(), localInertia); + } else { + constructionInfo.mass = mass; + constructionInfo.collisionShape = collisionShape.getCShape(); + constructionInfo.motionState = motionState; + } + } + + protected void postRebuild() { + rBody.setUserPointer(this); + if (mass == 0.0f) { + rBody.setCollisionFlags(rBody.getCollisionFlags() | CollisionFlags.STATIC_OBJECT); + } else { + rBody.setCollisionFlags(rBody.getCollisionFlags() & ~CollisionFlags.STATIC_OBJECT); + } + } + + /** + * @return the motionState + */ + public RigidBodyMotionState getMotionState() { + return motionState; + } + + /** + * Sets the physics object location + * @param location the location of the actual physics object + */ + public void setPhysicsLocation(Vector3f location) { + rBody.getCenterOfMassTransform(tempTrans); + Converter.convert(location, tempTrans.origin); + rBody.setCenterOfMassTransform(tempTrans); + motionState.setWorldTransform(tempTrans); + } + + /** + * Sets the physics object rotation + * @param rotation the rotation of the actual physics object + */ + public void setPhysicsRotation(Matrix3f rotation) { + rBody.getCenterOfMassTransform(tempTrans); + Converter.convert(rotation, tempTrans.basis); + rBody.setCenterOfMassTransform(tempTrans); + motionState.setWorldTransform(tempTrans); + } + + /** + * Sets the physics object rotation + * @param rotation the rotation of the actual physics object + */ + public void setPhysicsRotation(Quaternion rotation) { + rBody.getCenterOfMassTransform(tempTrans); + Converter.convert(rotation, tempTrans.basis); + rBody.setCenterOfMassTransform(tempTrans); + motionState.setWorldTransform(tempTrans); + } + + /** + * Gets the physics object location, instantiates a new Vector3f object + */ + public Vector3f getPhysicsLocation() { + return getPhysicsLocation(null); + } + + /** + * Gets the physics object rotation + */ + public Matrix3f getPhysicsRotationMatrix() { + return getPhysicsRotationMatrix(null); + } + + /** + * Gets the physics object location, no object instantiation + * @param location the location of the actual physics object is stored in this Vector3f + */ + public Vector3f getPhysicsLocation(Vector3f location) { + if (location == null) { + location = new Vector3f(); + } + rBody.getCenterOfMassTransform(tempTrans); + return Converter.convert(tempTrans.origin, location); + } + + /** + * Gets the physics object rotation as a matrix, no conversions and no object instantiation + * @param rotation the rotation of the actual physics object is stored in this Matrix3f + */ + public Matrix3f getPhysicsRotationMatrix(Matrix3f rotation) { + if (rotation == null) { + rotation = new Matrix3f(); + } + rBody.getCenterOfMassTransform(tempTrans); + return Converter.convert(tempTrans.basis, rotation); + } + + /** + * Gets the physics object rotation as a quaternion, converts the bullet Matrix3f value, + * instantiates new object + */ + public Quaternion getPhysicsRotation(){ + return getPhysicsRotation(null); + } + + /** + * Gets the physics object rotation as a quaternion, converts the bullet Matrix3f value + * @param rotation the rotation of the actual physics object is stored in this Quaternion + */ + public Quaternion getPhysicsRotation(Quaternion rotation){ + if (rotation == null) { + rotation = new Quaternion(); + } + rBody.getCenterOfMassTransform(tempTrans); + return Converter.convert(tempTrans.basis, rotation); + } + + /** + * Gets the physics object location + * @param location the location of the actual physics object is stored in this Vector3f + */ + public Vector3f getInterpolatedPhysicsLocation(Vector3f location) { + if (location == null) { + location = new Vector3f(); + } + rBody.getInterpolationWorldTransform(tempTrans); + return Converter.convert(tempTrans.origin, location); + } + + /** + * Gets the physics object rotation + * @param rotation the rotation of the actual physics object is stored in this Matrix3f + */ + public Matrix3f getInterpolatedPhysicsRotation(Matrix3f rotation) { + if (rotation == null) { + rotation = new Matrix3f(); + } + rBody.getInterpolationWorldTransform(tempTrans); + return Converter.convert(tempTrans.basis, rotation); + } + + /** + * Sets the node to kinematic mode. in this mode the node is not affected by physics + * but affects other physics objects. Its kinetic force is calculated by the amount + * of movement it is exposed to and its weight. + * @param kinematic + */ + public void setKinematic(boolean kinematic) { + this.kinematic = kinematic; + if (kinematic) { + rBody.setCollisionFlags(rBody.getCollisionFlags() | CollisionFlags.KINEMATIC_OBJECT); + rBody.setActivationState(com.bulletphysics.collision.dispatch.CollisionObject.DISABLE_DEACTIVATION); + } else { + rBody.setCollisionFlags(rBody.getCollisionFlags() & ~CollisionFlags.KINEMATIC_OBJECT); + rBody.setActivationState(com.bulletphysics.collision.dispatch.CollisionObject.ACTIVE_TAG); + } + } + + public boolean isKinematic() { + return kinematic; + } + + public void setCcdSweptSphereRadius(float radius) { + rBody.setCcdSweptSphereRadius(radius); + } + + /** + * Sets the amount of motion that has to happen in one physics tick to trigger the continuous motion detection<br/> + * This avoids the problem of fast objects moving through other objects, set to zero to disable (default) + * @param threshold + */ + public void setCcdMotionThreshold(float threshold) { + rBody.setCcdMotionThreshold(threshold); + } + + public float getCcdSweptSphereRadius() { + return rBody.getCcdSweptSphereRadius(); + } + + public float getCcdMotionThreshold() { + return rBody.getCcdMotionThreshold(); + } + + public float getCcdSquareMotionThreshold() { + return rBody.getCcdSquareMotionThreshold(); + } + + public float getMass() { + return mass; + } + + /** + * Sets the mass of this PhysicsRigidBody, objects with mass=0 are static. + * @param mass + */ + public void setMass(float mass) { + this.mass = mass; + if(collisionShape instanceof MeshCollisionShape && mass != 0){ + throw new IllegalStateException("Dynamic rigidbody can not have mesh collision shape!"); + } + if (collisionShape != null) { + collisionShape.calculateLocalInertia(mass, localInertia); + } + if (rBody != null) { + rBody.setMassProps(mass, localInertia); + if (mass == 0.0f) { + rBody.setCollisionFlags(rBody.getCollisionFlags() | CollisionFlags.STATIC_OBJECT); + } else { + rBody.setCollisionFlags(rBody.getCollisionFlags() & ~CollisionFlags.STATIC_OBJECT); + } + } + } + + public Vector3f getGravity() { + return getGravity(null); + } + + public Vector3f getGravity(Vector3f gravity) { + if (gravity == null) { + gravity = new Vector3f(); + } + rBody.getGravity(tempVec); + return Converter.convert(tempVec, gravity); + } + + /** + * Set the local gravity of this PhysicsRigidBody<br/> + * Set this after adding the node to the PhysicsSpace, + * the PhysicsSpace assigns its current gravity to the physics node when its added. + * @param gravity the gravity vector to set + */ + public void setGravity(Vector3f gravity) { + rBody.setGravity(Converter.convert(gravity, tempVec)); + } + + public float getFriction() { + return rBody.getFriction(); + } + + /** + * Sets the friction of this physics object + * @param friction the friction of this physics object + */ + public void setFriction(float friction) { + constructionInfo.friction = friction; + rBody.setFriction(friction); + } + + public void setDamping(float linearDamping, float angularDamping) { + constructionInfo.linearDamping = linearDamping; + constructionInfo.angularDamping = angularDamping; + rBody.setDamping(linearDamping, angularDamping); + } + + public void setLinearDamping(float linearDamping) { + constructionInfo.linearDamping = linearDamping; + rBody.setDamping(linearDamping, constructionInfo.angularDamping); + } + + public void setAngularDamping(float angularDamping) { + constructionInfo.angularDamping = angularDamping; + rBody.setDamping(constructionInfo.linearDamping, angularDamping); + } + + public float getLinearDamping() { + return constructionInfo.linearDamping; + } + + public float getAngularDamping() { + return constructionInfo.angularDamping; + } + + public float getRestitution() { + return rBody.getRestitution(); + } + + /** + * The "bouncyness" of the PhysicsRigidBody, best performance if restitution=0 + * @param restitution + */ + public void setRestitution(float restitution) { + constructionInfo.restitution = restitution; + rBody.setRestitution(restitution); + } + + /** + * Get the current angular velocity of this PhysicsRigidBody + * @return the current linear velocity + */ + public Vector3f getAngularVelocity() { + return Converter.convert(rBody.getAngularVelocity(tempVec)); + } + + /** + * Get the current angular velocity of this PhysicsRigidBody + * @param vec the vector to store the velocity in + */ + public void getAngularVelocity(Vector3f vec) { + Converter.convert(rBody.getAngularVelocity(tempVec), vec); + } + + /** + * Sets the angular velocity of this PhysicsRigidBody + * @param vec the angular velocity of this PhysicsRigidBody + */ + public void setAngularVelocity(Vector3f vec) { + rBody.setAngularVelocity(Converter.convert(vec, tempVec)); + rBody.activate(); + } + + /** + * Get the current linear velocity of this PhysicsRigidBody + * @return the current linear velocity + */ + public Vector3f getLinearVelocity() { + return Converter.convert(rBody.getLinearVelocity(tempVec)); + } + + /** + * Get the current linear velocity of this PhysicsRigidBody + * @param vec the vector to store the velocity in + */ + public void getLinearVelocity(Vector3f vec) { + Converter.convert(rBody.getLinearVelocity(tempVec), vec); + } + + /** + * Sets the linear velocity of this PhysicsRigidBody + * @param vec the linear velocity of this PhysicsRigidBody + */ + public void setLinearVelocity(Vector3f vec) { + rBody.setLinearVelocity(Converter.convert(vec, tempVec)); + rBody.activate(); + } + + /** + * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call + * updates the physics space.<br> + * To apply an impulse, use applyImpulse, use applyContinuousForce to apply continuous force. + * @param force the force + * @param location the location of the force + */ + public void applyForce(final Vector3f force, final Vector3f location) { + rBody.applyForce(Converter.convert(force, tempVec), Converter.convert(location, tempVec2)); + rBody.activate(); + } + + /** + * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call + * updates the physics space.<br> + * To apply an impulse, use applyImpulse. + * + * @param force the force + */ + public void applyCentralForce(final Vector3f force) { + rBody.applyCentralForce(Converter.convert(force, tempVec)); + rBody.activate(); + } + + /** + * Apply a force to the PhysicsRigidBody, only applies force if the next physics update call + * updates the physics space.<br> + * To apply an impulse, use applyImpulse. + * + * @param torque the torque + */ + public void applyTorque(final Vector3f torque) { + rBody.applyTorque(Converter.convert(torque, tempVec)); + rBody.activate(); + } + + /** + * Apply an impulse to the PhysicsRigidBody in the next physics update. + * @param impulse applied impulse + * @param rel_pos location relative to object + */ + public void applyImpulse(final Vector3f impulse, final Vector3f rel_pos) { + rBody.applyImpulse(Converter.convert(impulse, tempVec), Converter.convert(rel_pos, tempVec2)); + rBody.activate(); + } + + /** + * Apply a torque impulse to the PhysicsRigidBody in the next physics update. + * @param vec + */ + public void applyTorqueImpulse(final Vector3f vec) { + rBody.applyTorqueImpulse(Converter.convert(vec, tempVec)); + rBody.activate(); + } + + /** + * Clear all forces from the PhysicsRigidBody + * + */ + public void clearForces() { + rBody.clearForces(); + } + + public void setCollisionShape(CollisionShape collisionShape) { + super.setCollisionShape(collisionShape); + if(collisionShape instanceof MeshCollisionShape && mass!=0){ + throw new IllegalStateException("Dynamic rigidbody can not have mesh collision shape!"); + } + if (rBody == null) { + rebuildRigidBody(); + } else { + collisionShape.calculateLocalInertia(mass, localInertia); + constructionInfo.collisionShape = collisionShape.getCShape(); + rBody.setCollisionShape(collisionShape.getCShape()); + } + } + + /** + * reactivates this PhysicsRigidBody when it has been deactivated because it was not moving + */ + public void activate() { + rBody.activate(); + } + + public boolean isActive() { + return rBody.isActive(); + } + + /** + * sets the sleeping thresholds, these define when the object gets deactivated + * to save ressources. Low values keep the object active when it barely moves + * @param linear the linear sleeping threshold + * @param angular the angular sleeping threshold + */ + public void setSleepingThresholds(float linear, float angular) { + constructionInfo.linearSleepingThreshold = linear; + constructionInfo.angularSleepingThreshold = angular; + rBody.setSleepingThresholds(linear, angular); + } + + public void setLinearSleepingThreshold(float linearSleepingThreshold) { + constructionInfo.linearSleepingThreshold = linearSleepingThreshold; + rBody.setSleepingThresholds(linearSleepingThreshold, constructionInfo.angularSleepingThreshold); + } + + public void setAngularSleepingThreshold(float angularSleepingThreshold) { + constructionInfo.angularSleepingThreshold = angularSleepingThreshold; + rBody.setSleepingThresholds(constructionInfo.linearSleepingThreshold, angularSleepingThreshold); + } + + public float getLinearSleepingThreshold() { + return constructionInfo.linearSleepingThreshold; + } + + public float getAngularSleepingThreshold() { + return constructionInfo.angularSleepingThreshold; + } + + public float getAngularFactor() { + return rBody.getAngularFactor(); + } + + public void setAngularFactor(float factor) { + rBody.setAngularFactor(factor); + } + + /** + * do not use manually, joints are added automatically + */ + public void addJoint(PhysicsJoint joint) { + if (!joints.contains(joint)) { + joints.add(joint); + } + updateDebugShape(); + } + + /** + * + */ + public void removeJoint(PhysicsJoint joint) { + joints.remove(joint); + } + + /** + * Returns a list of connected joints. This list is only filled when + * the PhysicsRigidBody is actually added to the physics space or loaded from disk. + * @return list of active joints connected to this PhysicsRigidBody + */ + public List<PhysicsJoint> getJoints() { + return joints; + } + + /** + * used internally + */ + public RigidBody getObjectId() { + return rBody; + } + + /** + * destroys this PhysicsRigidBody and removes it from memory + */ + public void destroy() { + rBody.destroy(); + } + + @Override + protected Spatial getDebugShape() { + //add joints + Spatial shape = super.getDebugShape(); + Node node = null; + if (shape instanceof Node) { + node = (Node) shape; + } else { + node = new Node("DebugShapeNode"); + node.attachChild(shape); + } + int i = 0; + for (Iterator<PhysicsJoint> it = joints.iterator(); it.hasNext();) { + PhysicsJoint physicsJoint = it.next(); + Vector3f pivot = null; + if (physicsJoint.getBodyA() == this) { + pivot = physicsJoint.getPivotA(); + } else { + pivot = physicsJoint.getPivotB(); + } + Arrow arrow = new Arrow(pivot); + Geometry geom = new Geometry("DebugBone" + i, arrow); + geom.setMaterial(debugMaterialGreen); + node.attachChild(geom); + i++; + } + return node; + } + + @Override + public void write(JmeExporter e) throws IOException { + super.write(e); + OutputCapsule capsule = e.getCapsule(this); + + capsule.write(getMass(), "mass", 1.0f); + + capsule.write(getGravity(), "gravity", Vector3f.ZERO); + capsule.write(getFriction(), "friction", 0.5f); + capsule.write(getRestitution(), "restitution", 0); + capsule.write(getAngularFactor(), "angularFactor", 1); + capsule.write(kinematic, "kinematic", false); + + capsule.write(constructionInfo.linearDamping, "linearDamping", 0); + capsule.write(constructionInfo.angularDamping, "angularDamping", 0); + capsule.write(constructionInfo.linearSleepingThreshold, "linearSleepingThreshold", 0.8f); + capsule.write(constructionInfo.angularSleepingThreshold, "angularSleepingThreshold", 1.0f); + + capsule.write(getCcdMotionThreshold(), "ccdMotionThreshold", 0); + capsule.write(getCcdSweptSphereRadius(), "ccdSweptSphereRadius", 0); + + capsule.write(getPhysicsLocation(new Vector3f()), "physicsLocation", new Vector3f()); + capsule.write(getPhysicsRotationMatrix(new Matrix3f()), "physicsRotation", new Matrix3f()); + + capsule.writeSavableArrayList(joints, "joints", null); + } + + @Override + public void read(JmeImporter e) throws IOException { + super.read(e); + + InputCapsule capsule = e.getCapsule(this); + float mass = capsule.readFloat("mass", 1.0f); + this.mass = mass; + rebuildRigidBody(); + setGravity((Vector3f) capsule.readSavable("gravity", Vector3f.ZERO.clone())); + setFriction(capsule.readFloat("friction", 0.5f)); + setKinematic(capsule.readBoolean("kinematic", false)); + + setRestitution(capsule.readFloat("restitution", 0)); + setAngularFactor(capsule.readFloat("angularFactor", 1)); + setDamping(capsule.readFloat("linearDamping", 0), capsule.readFloat("angularDamping", 0)); + setSleepingThresholds(capsule.readFloat("linearSleepingThreshold", 0.8f), capsule.readFloat("angularSleepingThreshold", 1.0f)); + setCcdMotionThreshold(capsule.readFloat("ccdMotionThreshold", 0)); + setCcdSweptSphereRadius(capsule.readFloat("ccdSweptSphereRadius", 0)); + + setPhysicsLocation((Vector3f) capsule.readSavable("physicsLocation", new Vector3f())); + setPhysicsRotation((Matrix3f) capsule.readSavable("physicsRotation", new Matrix3f())); + + joints = capsule.readSavableArrayList("joints", null); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/objects/PhysicsVehicle.java b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsVehicle.java new file mode 100644 index 0000000..eacf534 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/objects/PhysicsVehicle.java @@ -0,0 +1,557 @@ +/* + * 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.bulletphysics.collision.dispatch.CollisionObject; +import com.bulletphysics.dynamics.vehicle.*; +import com.jme3.bullet.PhysicsSpace; +import com.jme3.bullet.collision.shapes.CollisionShape; +import com.jme3.bullet.util.Converter; +import com.jme3.export.InputCapsule; +import com.jme3.export.JmeExporter; +import com.jme3.export.JmeImporter; +import com.jme3.export.OutputCapsule; +import com.jme3.math.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; + +/** + * <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 RaycastVehicle vehicle; + protected VehicleTuning tuning; + protected VehicleRaycaster rayCaster; + 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 (vehicle != null) { + for (int i = 0; i < wheels.size(); i++) { + vehicle.updateWheelTransform(i, true); + wheels.get(i).updatePhysicsState(); + } + } + } + + /** + * 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(); + if (tuning == null) { + tuning = new VehicleTuning(); + } + rBody.setActivationState(CollisionObject.DISABLE_DEACTIVATION); + motionState.setVehicle(this); + if (physicsSpace != null) { + 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; + } + rayCaster = new DefaultVehicleRaycaster(space.getDynamicsWorld()); + vehicle = new RaycastVehicle(tuning, rBody, rayCaster); + vehicle.setCoordinateSystem(0, 1, 2); + for (VehicleWheel wheel : wheels) { + wheel.setWheelInfo(vehicle.addWheel(Converter.convert(wheel.getLocation()), Converter.convert(wheel.getDirection()), Converter.convert(wheel.getAxle()), + wheel.getRestLength(), wheel.getRadius(), tuning, wheel.isFrontWheel())); + } + } + + /** + * 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); + } + if (vehicle != null) { + WheelInfo info = vehicle.addWheel(Converter.convert(connectionPoint), Converter.convert(direction), Converter.convert(axle), + suspensionRestLength, wheelRadius, tuning, isFrontWheel); + wheel.setWheelInfo(info); + } + 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 (debugShape != null) { + detachDebugShape(); + } +// 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() { + vehicle.resetSuspension(); + } + + /** + * 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++) { + vehicle.applyEngineForce(force, i); + } + } + + /** + * 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) { + vehicle.applyEngineForce(force, wheel); + } + + /** + * 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()) { + vehicle.setSteeringValue(value, i); + } + } + } + + /** + * 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) { + vehicle.setSteeringValue(value, wheel); + } + + /** + * 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++) { + vehicle.setBrake(force, i); + } + } + + /** + * 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) { + vehicle.setBrake(force, wheel); + } + + /** + * Get the current speed of the vehicle in km/h + * @return + */ + public float getCurrentVehicleSpeedKmHour() { + return vehicle.getCurrentSpeedKmHour(); + } + + /** + * 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(); + } + vehicle.getForwardVector(tempVec); + Converter.convert(tempVec, vector); + return vector; + } + + /** + * used internally + */ + public RaycastVehicle getVehicleId() { + return vehicle; + } + + @Override + public void destroy() { + super.destroy(); + } + + @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); + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/objects/VehicleWheel.java b/engine/src/jbullet/com/jme3/bullet/objects/VehicleWheel.java new file mode 100644 index 0000000..7ac6bb0 --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/objects/VehicleWheel.java @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2009-2012 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.bullet.objects; + +import com.bulletphysics.dynamics.RigidBody; +import com.jme3.bullet.collision.PhysicsCollisionObject; +import com.jme3.bullet.util.Converter; +import com.jme3.export.*; +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 com.bulletphysics.dynamics.vehicle.WheelInfo wheelInfo; + 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 com.jme3.math.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() { + Converter.convert(wheelInfo.worldTransform.origin, wheelWorldLocation); + Converter.convert(wheelInfo.worldTransform.basis, tmp_Matrix); + wheelWorldRotation.fromRotationMatrix(tmp_Matrix); + } + + 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 com.bulletphysics.dynamics.vehicle.WheelInfo getWheelInfo() { + return wheelInfo; + } + + public void setWheelInfo(com.bulletphysics.dynamics.vehicle.WheelInfo wheelInfo) { + this.wheelInfo = wheelInfo; + 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 maxSuspensionForce + */ + public void setMaxSuspensionForce(float maxSuspensionForce) { + this.maxSuspensionForce = maxSuspensionForce; + applyInfo(); + } + + private void applyInfo() { + if (wheelInfo == null) { + return; + } + wheelInfo.suspensionStiffness = suspensionStiffness; + wheelInfo.wheelsDampingRelaxation = wheelsDampingRelaxation; + wheelInfo.wheelsDampingCompression = wheelsDampingCompression; + wheelInfo.frictionSlip = frictionSlip; + wheelInfo.rollInfluence = rollInfluence; + wheelInfo.maxSuspensionTravelCm = maxSuspensionTravelCm; + wheelInfo.maxSuspensionForce = maxSuspensionForce; + wheelInfo.wheelsRadius = radius; + wheelInfo.bIsFrontWheel = frontWheel; + wheelInfo.suspensionRestLength1 = restLength; + } + + 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) { + Converter.convert(wheelInfo.raycastInfo.contactPointWS, vec); + return vec; + } + + /** + * returns the location where the wheel collides with the ground (world space) + */ + public Vector3f getCollisionLocation() { + return Converter.convert(wheelInfo.raycastInfo.contactPointWS); + } + + /** + * returns the normal where the wheel collides with the ground (world space) + */ + public Vector3f getCollisionNormal(Vector3f vec) { + Converter.convert(wheelInfo.raycastInfo.contactNormalWS, vec); + return vec; + } + + /** + * returns the normal where the wheel collides with the ground (world space) + */ + public Vector3f getCollisionNormal() { + return Converter.convert(wheelInfo.raycastInfo.contactNormalWS); + } + + /** + * 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 wheelInfo.skidInfo; + } + + /** + * returns how many degrees the wheel has turned since the last physics + * step. + */ + public float getDeltaRotation() { + return wheelInfo.deltaRotation; + } + + @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/jbullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java b/engine/src/jbullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java new file mode 100644 index 0000000..8dd46bb --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java @@ -0,0 +1,206 @@ +/* + * 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.bulletphysics.linearmath.MotionState; +import com.bulletphysics.linearmath.Transform; +import com.jme3.bullet.objects.PhysicsVehicle; +import com.jme3.bullet.util.Converter; +import com.jme3.math.Matrix3f; +import com.jme3.math.Quaternion; +import com.jme3.math.Vector3f; +import com.jme3.scene.Spatial; + +/** + * 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 extends MotionState { + //stores the bullet transform + + private Transform motionStateTrans = new Transform(Converter.convert(new Matrix3f())); + private Vector3f worldLocation = new Vector3f(); + private Matrix3f worldRotation = new Matrix3f(); + private Quaternion worldRotationQuat = new Quaternion(); + private Vector3f localLocation = new Vector3f(); + private Quaternion localRotationQuat = new Quaternion(); + //keep track of transform changes + private boolean physicsLocationDirty = false; + private boolean jmeLocationDirty = false; + //temp variable for conversion + private Quaternion tmp_inverseWorldRotation = new Quaternion(); + private PhysicsVehicle vehicle; + private boolean applyPhysicsLocal = false; +// protected LinkedList<PhysicsMotionStateListener> listeners = new LinkedList<PhysicsMotionStateListener>(); + + public RigidBodyMotionState() { + } + + /** + * called from bullet when creating the rigidbody + * @param t + * @return + */ + public synchronized Transform getWorldTransform(Transform t) { + t.set(motionStateTrans); + return t; + } + + /** + * called from bullet when the transform of the rigidbody changes + * @param worldTrans + */ + public synchronized void setWorldTransform(Transform worldTrans) { + if (jmeLocationDirty) { + return; + } + motionStateTrans.set(worldTrans); + Converter.convert(worldTrans.origin, worldLocation); + Converter.convert(worldTrans.basis, worldRotation); + worldRotationQuat.fromRotationMatrix(worldRotation); +// for (Iterator<PhysicsMotionStateListener> it = listeners.iterator(); it.hasNext();) { +// PhysicsMotionStateListener physicsMotionStateListener = it.next(); +// physicsMotionStateListener.stateChanged(worldLocation, worldRotation); +// } + physicsLocationDirty = true; + if (vehicle != null) { + vehicle.updateWheels(); + } + } + + /** + * 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) { + if (!physicsLocationDirty) { + return false; + } + if (!applyPhysicsLocal && spatial.getParent() != null) { + localLocation.set(worldLocation).subtractLocal(spatial.getParent().getWorldTranslation()); + localLocation.divideLocal(spatial.getParent().getWorldScale()); + tmp_inverseWorldRotation.set(spatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation); + + localRotationQuat.set(worldRotationQuat); + tmp_inverseWorldRotation.set(spatial.getParent().getWorldRotation()).inverseLocal().mult(localRotationQuat, localRotationQuat); + + spatial.setLocalTranslation(localLocation); + spatial.setLocalRotation(localRotationQuat); + } else { + spatial.setLocalTranslation(worldLocation); + spatial.setLocalRotation(worldRotationQuat); + } + physicsLocationDirty = false; + return true; + } + + /** + * @return the worldLocation + */ + public Vector3f getWorldLocation() { + return worldLocation; + } + + /** + * @return the worldRotation + */ + public Matrix3f getWorldRotation() { + return worldRotation; + } + + /** + * @return the worldRotationQuat + */ + public Quaternion getWorldRotationQuat() { + return worldRotationQuat; + } + + /** + * @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 void addMotionStateListener(PhysicsMotionStateListener listener){ +// listeners.add(listener); +// } +// +// public void removeMotionStateListener(PhysicsMotionStateListener listener){ +// listeners.remove(listener); +// } +// public synchronized boolean applyTransform(com.jme3.math.Transform trans) { +// if (!physicsLocationDirty) { +// return false; +// } +// trans.setTranslation(worldLocation); +// trans.setRotation(worldRotationQuat); +// physicsLocationDirty = false; +// return true; +// } +// +// /** +// * called from jme when the location of the jme Node changes +// * @param location +// * @param rotation +// */ +// public synchronized void setWorldTransform(Vector3f location, Quaternion rotation) { +// worldLocation.set(location); +// worldRotationQuat.set(rotation); +// worldRotation.set(rotation.toRotationMatrix()); +// Converter.convert(worldLocation, motionStateTrans.origin); +// Converter.convert(worldRotation, motionStateTrans.basis); +// jmeLocationDirty = true; +// } +// +// /** +// * applies the current transform to the given RigidBody if the value has been changed on the jme side +// * @param rBody +// */ +// public synchronized void applyTransform(RigidBody rBody) { +// if (!jmeLocationDirty) { +// return; +// } +// assert (rBody != null); +// rBody.setWorldTransform(motionStateTrans); +// rBody.activate(); +// jmeLocationDirty = false; +// } +} diff --git a/engine/src/jbullet/com/jme3/bullet/util/Converter.java b/engine/src/jbullet/com/jme3/bullet/util/Converter.java new file mode 100644 index 0000000..15fb42c --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/util/Converter.java @@ -0,0 +1,282 @@ +/* + * 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.util; + +import com.bulletphysics.collision.shapes.IndexedMesh; +import com.bulletphysics.dom.HeightfieldTerrainShape; +import com.jme3.math.FastMath; +import com.jme3.scene.Mesh; +import com.jme3.scene.VertexBuffer.Type; +import com.jme3.scene.mesh.IndexBuffer; +import com.jme3.util.BufferUtils; +import java.nio.ByteBuffer; +import java.nio.FloatBuffer; + +/** + * Nice convenience methods for conversion between javax.vecmath and com.jme3.math + * Objects, also some jme to jbullet mesh conversion. + * @author normenhansen + */ +public class Converter { + + private Converter() { + } + + public static com.jme3.math.Vector3f convert(javax.vecmath.Vector3f oldVec) { + com.jme3.math.Vector3f newVec = new com.jme3.math.Vector3f(); + convert(oldVec, newVec); + return newVec; + } + + public static com.jme3.math.Vector3f convert(javax.vecmath.Vector3f oldVec, com.jme3.math.Vector3f newVec) { + newVec.x = oldVec.x; + newVec.y = oldVec.y; + newVec.z = oldVec.z; + return newVec; + } + + public static javax.vecmath.Vector3f convert(com.jme3.math.Vector3f oldVec) { + javax.vecmath.Vector3f newVec = new javax.vecmath.Vector3f(); + convert(oldVec, newVec); + return newVec; + } + + public static javax.vecmath.Vector3f convert(com.jme3.math.Vector3f oldVec, javax.vecmath.Vector3f newVec) { + newVec.x = oldVec.x; + newVec.y = oldVec.y; + newVec.z = oldVec.z; + return newVec; + } + + public static javax.vecmath.Quat4f convert(com.jme3.math.Quaternion oldQuat, javax.vecmath.Quat4f newQuat) { + newQuat.w = oldQuat.getW(); + newQuat.x = oldQuat.getX(); + newQuat.y = oldQuat.getY(); + newQuat.z = oldQuat.getZ(); + return newQuat; + } + + public static javax.vecmath.Quat4f convert(com.jme3.math.Quaternion oldQuat) { + javax.vecmath.Quat4f newQuat = new javax.vecmath.Quat4f(); + convert(oldQuat, newQuat); + return newQuat; + } + + public static com.jme3.math.Quaternion convert(javax.vecmath.Quat4f oldQuat, com.jme3.math.Quaternion newQuat) { + newQuat.set(oldQuat.x, oldQuat.y, oldQuat.z, oldQuat.w); + return newQuat; + } + + public static com.jme3.math.Quaternion convert(javax.vecmath.Quat4f oldQuat) { + com.jme3.math.Quaternion newQuat = new com.jme3.math.Quaternion(); + convert(oldQuat, newQuat); + return newQuat; + } + + public static com.jme3.math.Quaternion convert(javax.vecmath.Matrix3f oldMatrix, com.jme3.math.Quaternion newQuaternion) { + // the trace is the sum of the diagonal elements; see + // http://mathworld.wolfram.com/MatrixTrace.html + float t = oldMatrix.m00 + oldMatrix.m11 + oldMatrix.m22; + float w, x, y, z; + // we protect the division by s by ensuring that s>=1 + if (t >= 0) { // |w| >= .5 + float s = FastMath.sqrt(t + 1); // |s|>=1 ... + w = 0.5f * s; + s = 0.5f / s; // so this division isn't bad + x = (oldMatrix.m21 - oldMatrix.m12) * s; + y = (oldMatrix.m02 - oldMatrix.m20) * s; + z = (oldMatrix.m10 - oldMatrix.m01) * s; + } else if ((oldMatrix.m00 > oldMatrix.m11) && (oldMatrix.m00 > oldMatrix.m22)) { + float s = FastMath.sqrt(1.0f + oldMatrix.m00 - oldMatrix.m11 - oldMatrix.m22); // |s|>=1 + x = s * 0.5f; // |x| >= .5 + s = 0.5f / s; + y = (oldMatrix.m10 + oldMatrix.m01) * s; + z = (oldMatrix.m02 + oldMatrix.m20) * s; + w = (oldMatrix.m21 - oldMatrix.m12) * s; + } else if (oldMatrix.m11 > oldMatrix.m22) { + float s = FastMath.sqrt(1.0f + oldMatrix.m11 - oldMatrix.m00 - oldMatrix.m22); // |s|>=1 + y = s * 0.5f; // |y| >= .5 + s = 0.5f / s; + x = (oldMatrix.m10 + oldMatrix.m01) * s; + z = (oldMatrix.m21 + oldMatrix.m12) * s; + w = (oldMatrix.m02 - oldMatrix.m20) * s; + } else { + float s = FastMath.sqrt(1.0f + oldMatrix.m22 - oldMatrix.m00 - oldMatrix.m11); // |s|>=1 + z = s * 0.5f; // |z| >= .5 + s = 0.5f / s; + x = (oldMatrix.m02 + oldMatrix.m20) * s; + y = (oldMatrix.m21 + oldMatrix.m12) * s; + w = (oldMatrix.m10 - oldMatrix.m01) * s; + } + return newQuaternion.set(x, y, z, w); + } + + public static javax.vecmath.Matrix3f convert(com.jme3.math.Quaternion oldQuaternion, javax.vecmath.Matrix3f newMatrix) { + float norm = oldQuaternion.getW() * oldQuaternion.getW() + oldQuaternion.getX() * oldQuaternion.getX() + oldQuaternion.getY() * oldQuaternion.getY() + oldQuaternion.getZ() * oldQuaternion.getZ(); + float s = (norm == 1f) ? 2f : (norm > 0f) ? 2f / norm : 0; + + // compute xs/ys/zs first to save 6 multiplications, since xs/ys/zs + // will be used 2-4 times each. + float xs = oldQuaternion.getX() * s; + float ys = oldQuaternion.getY() * s; + float zs = oldQuaternion.getZ() * s; + float xx = oldQuaternion.getX() * xs; + float xy = oldQuaternion.getX() * ys; + float xz = oldQuaternion.getX() * zs; + float xw = oldQuaternion.getW() * xs; + float yy = oldQuaternion.getY() * ys; + float yz = oldQuaternion.getY() * zs; + float yw = oldQuaternion.getW() * ys; + float zz = oldQuaternion.getZ() * zs; + float zw = oldQuaternion.getW() * zs; + + // using s=2/norm (instead of 1/norm) saves 9 multiplications by 2 here + newMatrix.m00 = 1 - (yy + zz); + newMatrix.m01 = (xy - zw); + newMatrix.m02 = (xz + yw); + newMatrix.m10 = (xy + zw); + newMatrix.m11 = 1 - (xx + zz); + newMatrix.m12 = (yz - xw); + newMatrix.m20 = (xz - yw); + newMatrix.m21 = (yz + xw); + newMatrix.m22 = 1 - (xx + yy); + + return newMatrix; + } + + public static com.jme3.math.Matrix3f convert(javax.vecmath.Matrix3f oldMatrix) { + com.jme3.math.Matrix3f newMatrix = new com.jme3.math.Matrix3f(); + convert(oldMatrix, newMatrix); + return newMatrix; + } + + public static com.jme3.math.Matrix3f convert(javax.vecmath.Matrix3f oldMatrix, com.jme3.math.Matrix3f newMatrix) { + newMatrix.set(0, 0, oldMatrix.m00); + newMatrix.set(0, 1, oldMatrix.m01); + newMatrix.set(0, 2, oldMatrix.m02); + newMatrix.set(1, 0, oldMatrix.m10); + newMatrix.set(1, 1, oldMatrix.m11); + newMatrix.set(1, 2, oldMatrix.m12); + newMatrix.set(2, 0, oldMatrix.m20); + newMatrix.set(2, 1, oldMatrix.m21); + newMatrix.set(2, 2, oldMatrix.m22); + return newMatrix; + } + + public static javax.vecmath.Matrix3f convert(com.jme3.math.Matrix3f oldMatrix) { + javax.vecmath.Matrix3f newMatrix = new javax.vecmath.Matrix3f(); + convert(oldMatrix, newMatrix); + return newMatrix; + } + + public static javax.vecmath.Matrix3f convert(com.jme3.math.Matrix3f oldMatrix, javax.vecmath.Matrix3f newMatrix) { + newMatrix.m00 = oldMatrix.get(0, 0); + newMatrix.m01 = oldMatrix.get(0, 1); + newMatrix.m02 = oldMatrix.get(0, 2); + newMatrix.m10 = oldMatrix.get(1, 0); + newMatrix.m11 = oldMatrix.get(1, 1); + newMatrix.m12 = oldMatrix.get(1, 2); + newMatrix.m20 = oldMatrix.get(2, 0); + newMatrix.m21 = oldMatrix.get(2, 1); + newMatrix.m22 = oldMatrix.get(2, 2); + return newMatrix; + } + + public static com.bulletphysics.linearmath.Transform convert(com.jme3.math.Transform in, com.bulletphysics.linearmath.Transform out) { + convert(in.getTranslation(), out.origin); + convert(in.getRotation(), out.basis); + return out; + } + + public static com.jme3.math.Transform convert(com.bulletphysics.linearmath.Transform in, com.jme3.math.Transform out) { + convert(in.origin, out.getTranslation()); + convert(in.basis, out.getRotation()); + return out; + } + + public static IndexedMesh convert(Mesh mesh) { + IndexedMesh jBulletIndexedMesh = new IndexedMesh(); + jBulletIndexedMesh.triangleIndexBase = ByteBuffer.allocate(mesh.getTriangleCount() * 3 * 4); + jBulletIndexedMesh.vertexBase = ByteBuffer.allocate(mesh.getVertexCount() * 3 * 4); + + IndexBuffer indices = mesh.getIndicesAsList(); + + FloatBuffer vertices = mesh.getFloatBuffer(Type.Position); + vertices.rewind(); + + int verticesLength = mesh.getVertexCount() * 3; + jBulletIndexedMesh.numVertices = mesh.getVertexCount(); + jBulletIndexedMesh.vertexStride = 12; //3 verts * 4 bytes per. + for (int i = 0; i < verticesLength; i++) { + float tempFloat = vertices.get(); + jBulletIndexedMesh.vertexBase.putFloat(tempFloat); + } + + int indicesLength = mesh.getTriangleCount() * 3; + jBulletIndexedMesh.numTriangles = mesh.getTriangleCount(); + jBulletIndexedMesh.triangleIndexStride = 12; //3 index entries * 4 bytes each. + for (int i = 0; i < indicesLength; i++) { + jBulletIndexedMesh.triangleIndexBase.putInt(indices.get(i)); + } + vertices.rewind(); + vertices.clear(); + + return jBulletIndexedMesh; + } + + public static Mesh convert(IndexedMesh mesh) { + Mesh jmeMesh = new Mesh(); + + jmeMesh.setBuffer(Type.Index, 3, BufferUtils.createShortBuffer(mesh.numTriangles * 3)); + jmeMesh.setBuffer(Type.Position, 3, BufferUtils.createFloatBuffer(mesh.numVertices * 3)); + + IndexBuffer indicess = jmeMesh.getIndexBuffer(); + FloatBuffer vertices = jmeMesh.getFloatBuffer(Type.Position); + + for (int i = 0; i < mesh.numTriangles * 3; i++) { + indicess.put(i, mesh.triangleIndexBase.getInt(i * 4)); + } + + for (int i = 0; i < mesh.numVertices * 3; i++) { + vertices.put(i, mesh.vertexBase.getFloat(i * 4)); + } + jmeMesh.updateCounts(); + jmeMesh.updateBound(); + jmeMesh.getFloatBuffer(Type.Position).clear(); + + return jmeMesh; + } + + public static Mesh convert(HeightfieldTerrainShape heightfieldShape) { + return null; //TODO!! + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java b/engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java new file mode 100644 index 0000000..ff0009c --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java @@ -0,0 +1,249 @@ +/* + * 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.util; + +import com.bulletphysics.collision.shapes.ConcaveShape; +import com.bulletphysics.collision.shapes.ConvexShape; +import com.bulletphysics.collision.shapes.ShapeHull; +import com.bulletphysics.collision.shapes.TriangleCallback; +import com.bulletphysics.util.IntArrayList; +import com.jme3.bullet.collision.shapes.CollisionShape; +import com.jme3.bullet.collision.shapes.CompoundCollisionShape; +import com.jme3.bullet.collision.shapes.infos.ChildCollisionShape; +import com.jme3.math.Matrix3f; +import com.jme3.scene.Geometry; +import com.jme3.scene.Mesh; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import com.jme3.scene.VertexBuffer.Type; +import com.jme3.util.BufferUtils; +import com.jme3.util.TempVars; +import java.nio.FloatBuffer; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import javax.vecmath.Vector3f; + +/** + * + * @author CJ Hare, normenhansen + */ +public class DebugShapeFactory { + + /** The maximum corner for the aabb used for triangles to include in ConcaveShape processing.*/ + private static final Vector3f aabbMax = new Vector3f(1e30f, 1e30f, 1e30f); + /** The minimum corner for the aabb used for triangles to include in ConcaveShape processing.*/ + private static final Vector3f aabbMin = new Vector3f(-1e30f, -1e30f, -1e30f); + + /** + * Creates a debug shape from the given collision shape. This is mostly used internally.<br> + * To attach a debug shape to a physics object, call <code>attachDebugShape(AssetManager manager);</code> on it. + * @param collisionShape + * @return + */ + public static Spatial getDebugShape(CollisionShape collisionShape) { + if (collisionShape == null) { + return null; + } + Spatial debugShape; + if (collisionShape instanceof CompoundCollisionShape) { + CompoundCollisionShape shape = (CompoundCollisionShape) collisionShape; + List<ChildCollisionShape> children = shape.getChildren(); + Node node = new Node("DebugShapeNode"); + for (Iterator<ChildCollisionShape> it = children.iterator(); it.hasNext();) { + ChildCollisionShape childCollisionShape = it.next(); + CollisionShape ccollisionShape = childCollisionShape.shape; + Geometry geometry = createDebugShape(ccollisionShape); + + // apply translation + geometry.setLocalTranslation(childCollisionShape.location); + + // apply rotation + TempVars vars = TempVars.get(); + + Matrix3f tempRot = vars.tempMat3; + + tempRot.set(geometry.getLocalRotation()); + childCollisionShape.rotation.mult(tempRot, tempRot); + geometry.setLocalRotation(tempRot); + + vars.release(); + + node.attachChild(geometry); + } + debugShape = node; + } else { + debugShape = createDebugShape(collisionShape); + } + if (debugShape == null) { + return null; + } + debugShape.updateGeometricState(); + return debugShape; + } + + private static Geometry createDebugShape(CollisionShape shape) { + Geometry geom = new Geometry(); + geom.setMesh(DebugShapeFactory.getDebugMesh(shape)); +// geom.setLocalScale(shape.getScale()); + geom.updateModelBound(); + return geom; + } + + public static Mesh getDebugMesh(CollisionShape shape) { + Mesh mesh = null; + if (shape.getCShape() instanceof ConvexShape) { + mesh = new Mesh(); + mesh.setBuffer(Type.Position, 3, getVertices((ConvexShape) shape.getCShape())); + mesh.getFloatBuffer(Type.Position).clear(); + } else if (shape.getCShape() instanceof ConcaveShape) { + mesh = new Mesh(); + mesh.setBuffer(Type.Position, 3, getVertices((ConcaveShape) shape.getCShape())); + mesh.getFloatBuffer(Type.Position).clear(); + } + return mesh; + } + + /** + * Constructs the buffer for the vertices of the concave shape. + * + * @param concaveShape the shape to get the vertices for / from. + * @return the shape as stored by the given broadphase rigid body. + */ + private static FloatBuffer getVertices(ConcaveShape concaveShape) { + // Create the call back that'll create the vertex buffer + BufferedTriangleCallback triangleProcessor = new BufferedTriangleCallback(); + concaveShape.processAllTriangles(triangleProcessor, aabbMin, aabbMax); + + // Retrieve the vextex and index buffers + return triangleProcessor.getVertices(); + } + + /** + * Processes the given convex shape to retrieve a correctly ordered FloatBuffer to + * construct the shape from with a TriMesh. + * + * @param convexShape the shape to retreieve the vertices from. + * @return the vertices as a FloatBuffer, ordered as Triangles. + */ + private static FloatBuffer getVertices(ConvexShape convexShape) { + // Check there is a hull shape to render + if (convexShape.getUserPointer() == null) { + // create a hull approximation + ShapeHull hull = new ShapeHull(convexShape); + float margin = convexShape.getMargin(); + hull.buildHull(margin); + convexShape.setUserPointer(hull); + } + + // Assert state - should have a pointer to a hull (shape) that'll be drawn + assert convexShape.getUserPointer() != null : "Should have a shape for the userPointer, instead got null"; + ShapeHull hull = (ShapeHull) convexShape.getUserPointer(); + + // Assert we actually have a shape to render + assert hull.numTriangles() > 0 : "Expecting the Hull shape to have triangles"; + int numberOfTriangles = hull.numTriangles(); + + // The number of bytes needed is: (floats in a vertex) * (vertices in a triangle) * (# of triangles) * (size of float in bytes) + final int numberOfFloats = 3 * 3 * numberOfTriangles; + FloatBuffer vertices = BufferUtils.createFloatBuffer(numberOfFloats); + + // Force the limit, set the cap - most number of floats we will use the buffer for + vertices.limit(numberOfFloats); + + // Loop variables + final IntArrayList hullIndicies = hull.getIndexPointer(); + final List<Vector3f> hullVertices = hull.getVertexPointer(); + Vector3f vertexA, vertexB, vertexC; + int index = 0; + + for (int i = 0; i < numberOfTriangles; i++) { + // Grab the data for this triangle from the hull + vertexA = hullVertices.get(hullIndicies.get(index++)); + vertexB = hullVertices.get(hullIndicies.get(index++)); + vertexC = hullVertices.get(hullIndicies.get(index++)); + + // Put the verticies into the vertex buffer + vertices.put(vertexA.x).put(vertexA.y).put(vertexA.z); + vertices.put(vertexB.x).put(vertexB.y).put(vertexB.z); + vertices.put(vertexC.x).put(vertexC.y).put(vertexC.z); + } + + vertices.clear(); + return vertices; + } +} + +/** + * A callback is used to process the triangles of the shape as there is no direct access to a concave shapes, shape. + * <p/> + * The triangles are simply put into a list (which in extreme condition will cause memory problems) then put into a direct buffer. + * + * @author CJ Hare + */ +class BufferedTriangleCallback extends TriangleCallback { + + private ArrayList<Vector3f> vertices; + + public BufferedTriangleCallback() { + vertices = new ArrayList<Vector3f>(); + } + + @Override + public void processTriangle(Vector3f[] triangle, int partId, int triangleIndex) { + // Three sets of individual lines + // The new Vector is needed as the given triangle reference is from a pool + vertices.add(new Vector3f(triangle[0])); + vertices.add(new Vector3f(triangle[1])); + vertices.add(new Vector3f(triangle[2])); + } + + /** + * Retrieves the vertices from the Triangle buffer. + */ + public FloatBuffer getVertices() { + // There are 3 floats needed for each vertex (x,y,z) + final int numberOfFloats = vertices.size() * 3; + FloatBuffer verticesBuffer = BufferUtils.createFloatBuffer(numberOfFloats); + + // Force the limit, set the cap - most number of floats we will use the buffer for + verticesBuffer.limit(numberOfFloats); + + // Copy the values from the list to the direct float buffer + for (Vector3f v : vertices) { + verticesBuffer.put(v.x).put(v.y).put(v.z); + } + + vertices.clear(); + return verticesBuffer; + } +} |