aboutsummaryrefslogtreecommitdiff
path: root/engine/src/jbullet
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/jbullet')
-rw-r--r--engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java853
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEvent.java197
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionEventFactory.java59
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/PhysicsCollisionObject.java287
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/PhysicsRayTestResult.java91
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/PhysicsSweepTestResult.java91
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/BoxCollisionShape.java87
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/CapsuleCollisionShape.java126
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/CollisionShape.java111
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/CompoundCollisionShape.java150
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/ConeCollisionShape.java77
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/CylinderCollisionShape.java117
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/GImpactCollisionShape.java133
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/HeightfieldCollisionShape.java133
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/HullCollisionShape.java79
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/MeshCollisionShape.java126
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/PlaneCollisionShape.java59
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/SimplexCollisionShape.java85
-rw-r--r--engine/src/jbullet/com/jme3/bullet/collision/shapes/SphereCollisionShape.java85
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/ConeJoint.java138
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/HingeJoint.java156
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/PhysicsJoint.java136
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/Point2PointJoint.java111
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/SixDofJoint.java228
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/SliderJoint.java430
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/motors/RotationalLimitMotor.java129
-rw-r--r--engine/src/jbullet/com/jme3/bullet/joints/motors/TranslationalLimitMotor.java100
-rw-r--r--engine/src/jbullet/com/jme3/bullet/objects/PhysicsCharacter.java290
-rw-r--r--engine/src/jbullet/com/jme3/bullet/objects/PhysicsGhostObject.java284
-rw-r--r--engine/src/jbullet/com/jme3/bullet/objects/PhysicsRigidBody.java703
-rw-r--r--engine/src/jbullet/com/jme3/bullet/objects/PhysicsVehicle.java557
-rw-r--r--engine/src/jbullet/com/jme3/bullet/objects/VehicleWheel.java402
-rw-r--r--engine/src/jbullet/com/jme3/bullet/objects/infos/RigidBodyMotionState.java206
-rw-r--r--engine/src/jbullet/com/jme3/bullet/util/Converter.java282
-rw-r--r--engine/src/jbullet/com/jme3/bullet/util/DebugShapeFactory.java249
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;
+ }
+}