Home | History | Annotate | Download | only in bullet
      1 /*
      2  * Copyright (c) 2009-2010 jMonkeyEngine
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are
      7  * met:
      8  *
      9  * * Redistributions of source code must retain the above copyright
     10  *   notice, this list of conditions and the following disclaimer.
     11  *
     12  * * Redistributions in binary form must reproduce the above copyright
     13  *   notice, this list of conditions and the following disclaimer in the
     14  *   documentation and/or other materials provided with the distribution.
     15  *
     16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
     17  *   may be used to endorse or promote products derived from this software
     18  *   without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 package com.jme3.bullet;
     33 
     34 import com.bulletphysics.BulletGlobals;
     35 import com.bulletphysics.ContactAddedCallback;
     36 import com.bulletphysics.ContactDestroyedCallback;
     37 import com.bulletphysics.ContactProcessedCallback;
     38 import com.bulletphysics.collision.broadphase.*;
     39 import com.bulletphysics.collision.dispatch.CollisionWorld.LocalConvexResult;
     40 import com.bulletphysics.collision.dispatch.CollisionWorld.LocalRayResult;
     41 import com.bulletphysics.collision.dispatch.*;
     42 import com.bulletphysics.collision.narrowphase.ManifoldPoint;
     43 import com.bulletphysics.collision.shapes.ConvexShape;
     44 import com.bulletphysics.dynamics.DiscreteDynamicsWorld;
     45 import com.bulletphysics.dynamics.DynamicsWorld;
     46 import com.bulletphysics.dynamics.InternalTickCallback;
     47 import com.bulletphysics.dynamics.RigidBody;
     48 import com.bulletphysics.dynamics.constraintsolver.ConstraintSolver;
     49 import com.bulletphysics.dynamics.constraintsolver.SequentialImpulseConstraintSolver;
     50 import com.bulletphysics.extras.gimpact.GImpactCollisionAlgorithm;
     51 import com.jme3.app.AppTask;
     52 import com.jme3.asset.AssetManager;
     53 import com.jme3.bullet.collision.*;
     54 import com.jme3.bullet.collision.shapes.CollisionShape;
     55 import com.jme3.bullet.control.PhysicsControl;
     56 import com.jme3.bullet.control.RigidBodyControl;
     57 import com.jme3.bullet.joints.PhysicsJoint;
     58 import com.jme3.bullet.objects.PhysicsCharacter;
     59 import com.jme3.bullet.objects.PhysicsGhostObject;
     60 import com.jme3.bullet.objects.PhysicsRigidBody;
     61 import com.jme3.bullet.objects.PhysicsVehicle;
     62 import com.jme3.bullet.util.Converter;
     63 import com.jme3.math.Transform;
     64 import com.jme3.math.Vector3f;
     65 import com.jme3.scene.Node;
     66 import com.jme3.scene.Spatial;
     67 import java.util.Iterator;
     68 import java.util.LinkedList;
     69 import java.util.List;
     70 import java.util.Map;
     71 import java.util.concurrent.Callable;
     72 import java.util.concurrent.ConcurrentHashMap;
     73 import java.util.concurrent.ConcurrentLinkedQueue;
     74 import java.util.concurrent.Future;
     75 import java.util.logging.Level;
     76 import java.util.logging.Logger;
     77 
     78 /**
     79  * <p>PhysicsSpace - The central jbullet-jme physics space</p>
     80  * @author normenhansen
     81  */
     82 public class PhysicsSpace {
     83 
     84     public static final int AXIS_X = 0;
     85     public static final int AXIS_Y = 1;
     86     public static final int AXIS_Z = 2;
     87     private static ThreadLocal<ConcurrentLinkedQueue<AppTask<?>>> pQueueTL =
     88             new ThreadLocal<ConcurrentLinkedQueue<AppTask<?>>>() {
     89 
     90                 @Override
     91                 protected ConcurrentLinkedQueue<AppTask<?>> initialValue() {
     92                     return new ConcurrentLinkedQueue<AppTask<?>>();
     93                 }
     94             };
     95     private ConcurrentLinkedQueue<AppTask<?>> pQueue = new ConcurrentLinkedQueue<AppTask<?>>();
     96     private static ThreadLocal<PhysicsSpace> physicsSpaceTL = new ThreadLocal<PhysicsSpace>();
     97     private DiscreteDynamicsWorld dynamicsWorld = null;
     98     private BroadphaseInterface broadphase;
     99     private BroadphaseType broadphaseType = BroadphaseType.DBVT;
    100     private CollisionDispatcher dispatcher;
    101     private ConstraintSolver solver;
    102     private DefaultCollisionConfiguration collisionConfiguration;
    103 //    private Map<GhostObject, PhysicsGhostObject> physicsGhostNodes = new ConcurrentHashMap<GhostObject, PhysicsGhostObject>();
    104     private Map<RigidBody, PhysicsRigidBody> physicsNodes = new ConcurrentHashMap<RigidBody, PhysicsRigidBody>();
    105     private List<PhysicsJoint> physicsJoints = new LinkedList<PhysicsJoint>();
    106     private List<PhysicsCollisionListener> collisionListeners = new LinkedList<PhysicsCollisionListener>();
    107     private List<PhysicsCollisionEvent> collisionEvents = new LinkedList<PhysicsCollisionEvent>();
    108     private Map<Integer, PhysicsCollisionGroupListener> collisionGroupListeners = new ConcurrentHashMap<Integer, PhysicsCollisionGroupListener>();
    109     private ConcurrentLinkedQueue<PhysicsTickListener> tickListeners = new ConcurrentLinkedQueue<PhysicsTickListener>();
    110     private PhysicsCollisionEventFactory eventFactory = new PhysicsCollisionEventFactory();
    111     private Vector3f worldMin = new Vector3f(-10000f, -10000f, -10000f);
    112     private Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f);
    113     private float accuracy = 1f / 60f;
    114     private int maxSubSteps = 4;
    115     private javax.vecmath.Vector3f rayVec1 = new javax.vecmath.Vector3f();
    116     private javax.vecmath.Vector3f rayVec2 = new javax.vecmath.Vector3f();
    117     private com.bulletphysics.linearmath.Transform sweepTrans1 = new com.bulletphysics.linearmath.Transform(new javax.vecmath.Matrix3f());
    118     private com.bulletphysics.linearmath.Transform sweepTrans2 = new com.bulletphysics.linearmath.Transform(new javax.vecmath.Matrix3f());
    119     private AssetManager debugManager;
    120 
    121     /**
    122      * Get the current PhysicsSpace <b>running on this thread</b><br/>
    123      * For parallel physics, this can also be called from the OpenGL thread to receive the PhysicsSpace
    124      * @return the PhysicsSpace running on this thread
    125      */
    126     public static PhysicsSpace getPhysicsSpace() {
    127         return physicsSpaceTL.get();
    128     }
    129 
    130     /**
    131      * Used internally
    132      * @param space
    133      */
    134     public static void setLocalThreadPhysicsSpace(PhysicsSpace space) {
    135         physicsSpaceTL.set(space);
    136     }
    137 
    138     public PhysicsSpace() {
    139         this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), BroadphaseType.DBVT);
    140     }
    141 
    142     public PhysicsSpace(BroadphaseType broadphaseType) {
    143         this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType);
    144     }
    145 
    146     public PhysicsSpace(Vector3f worldMin, Vector3f worldMax) {
    147         this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3);
    148     }
    149 
    150     public PhysicsSpace(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType) {
    151         this.worldMin.set(worldMin);
    152         this.worldMax.set(worldMax);
    153         this.broadphaseType = broadphaseType;
    154         create();
    155     }
    156 
    157     /**
    158      * Has to be called from the (designated) physics thread
    159      */
    160     public void create() {
    161         pQueueTL.set(pQueue);
    162 
    163         collisionConfiguration = new DefaultCollisionConfiguration();
    164         dispatcher = new CollisionDispatcher(collisionConfiguration);
    165         switch (broadphaseType) {
    166             case SIMPLE:
    167                 broadphase = new SimpleBroadphase();
    168                 break;
    169             case AXIS_SWEEP_3:
    170                 broadphase = new AxisSweep3(Converter.convert(worldMin), Converter.convert(worldMax));
    171                 break;
    172             case AXIS_SWEEP_3_32:
    173                 broadphase = new AxisSweep3_32(Converter.convert(worldMin), Converter.convert(worldMax));
    174                 break;
    175             case DBVT:
    176                 broadphase = new DbvtBroadphase();
    177                 break;
    178         }
    179 
    180         solver = new SequentialImpulseConstraintSolver();
    181 
    182         dynamicsWorld = new DiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration);
    183         dynamicsWorld.setGravity(new javax.vecmath.Vector3f(0, -9.81f, 0));
    184 
    185         broadphase.getOverlappingPairCache().setInternalGhostPairCallback(new GhostPairCallback());
    186         GImpactCollisionAlgorithm.registerAlgorithm(dispatcher);
    187 
    188         physicsSpaceTL.set(this);
    189         //register filter callback for tick / collision
    190         setTickCallback();
    191         setContactCallbacks();
    192         //register filter callback for collision groups
    193         setOverlapFilterCallback();
    194     }
    195 
    196     private void setOverlapFilterCallback() {
    197         OverlapFilterCallback callback = new OverlapFilterCallback() {
    198 
    199             public boolean needBroadphaseCollision(BroadphaseProxy bp, BroadphaseProxy bp1) {
    200                 boolean collides = (bp.collisionFilterGroup & bp1.collisionFilterMask) != 0;
    201                 if (collides) {
    202                     collides = (bp1.collisionFilterGroup & bp.collisionFilterMask) != 0;
    203                 }
    204                 if (collides) {
    205                     assert (bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject && bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject);
    206                     com.bulletphysics.collision.dispatch.CollisionObject colOb = (com.bulletphysics.collision.dispatch.CollisionObject) bp.clientObject;
    207                     com.bulletphysics.collision.dispatch.CollisionObject colOb1 = (com.bulletphysics.collision.dispatch.CollisionObject) bp1.clientObject;
    208                     assert (colOb.getUserPointer() != null && colOb1.getUserPointer() != null);
    209                     PhysicsCollisionObject collisionObject = (PhysicsCollisionObject) colOb.getUserPointer();
    210                     PhysicsCollisionObject collisionObject1 = (PhysicsCollisionObject) colOb1.getUserPointer();
    211                     if ((collisionObject.getCollideWithGroups() & collisionObject1.getCollisionGroup()) > 0
    212                             || (collisionObject1.getCollideWithGroups() & collisionObject.getCollisionGroup()) > 0) {
    213                         PhysicsCollisionGroupListener listener = collisionGroupListeners.get(collisionObject.getCollisionGroup());
    214                         PhysicsCollisionGroupListener listener1 = collisionGroupListeners.get(collisionObject1.getCollisionGroup());
    215                         if (listener != null) {
    216                             return listener.collide(collisionObject, collisionObject1);
    217                         } else if (listener1 != null) {
    218                             return listener1.collide(collisionObject, collisionObject1);
    219                         }
    220                         return true;
    221                     } else {
    222                         return false;
    223                     }
    224                 }
    225                 return collides;
    226             }
    227         };
    228         dynamicsWorld.getPairCache().setOverlapFilterCallback(callback);
    229     }
    230 
    231     private void setTickCallback() {
    232         final PhysicsSpace space = this;
    233         InternalTickCallback callback2 = new InternalTickCallback() {
    234 
    235             @Override
    236             public void internalTick(DynamicsWorld dw, float f) {
    237                 //execute task list
    238                 AppTask task = pQueue.poll();
    239                 task = pQueue.poll();
    240                 while (task != null) {
    241                     while (task.isCancelled()) {
    242                         task = pQueue.poll();
    243                     }
    244                     try {
    245                         task.invoke();
    246                     } catch (Exception ex) {
    247                         Logger.getLogger(PhysicsSpace.class.getName()).log(Level.SEVERE, null, ex);
    248                     }
    249                     task = pQueue.poll();
    250                 }
    251                 for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) {
    252                     PhysicsTickListener physicsTickCallback = it.next();
    253                     physicsTickCallback.prePhysicsTick(space, f);
    254                 }
    255             }
    256         };
    257         dynamicsWorld.setPreTickCallback(callback2);
    258         InternalTickCallback callback = new InternalTickCallback() {
    259 
    260             @Override
    261             public void internalTick(DynamicsWorld dw, float f) {
    262                 for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) {
    263                     PhysicsTickListener physicsTickCallback = it.next();
    264                     physicsTickCallback.physicsTick(space, f);
    265                 }
    266             }
    267         };
    268         dynamicsWorld.setInternalTickCallback(callback, this);
    269     }
    270 
    271     private void setContactCallbacks() {
    272         BulletGlobals.setContactAddedCallback(new ContactAddedCallback() {
    273 
    274             public boolean contactAdded(ManifoldPoint cp, com.bulletphysics.collision.dispatch.CollisionObject colObj0,
    275                     int partId0, int index0, com.bulletphysics.collision.dispatch.CollisionObject colObj1, int partId1,
    276                     int index1) {
    277                 System.out.println("contact added");
    278                 return true;
    279             }
    280         });
    281 
    282         BulletGlobals.setContactProcessedCallback(new ContactProcessedCallback() {
    283 
    284             public boolean contactProcessed(ManifoldPoint cp, Object body0, Object body1) {
    285                 if (body0 instanceof CollisionObject && body1 instanceof CollisionObject) {
    286                     PhysicsCollisionObject node = null, node1 = null;
    287                     CollisionObject rBody0 = (CollisionObject) body0;
    288                     CollisionObject rBody1 = (CollisionObject) body1;
    289                     node = (PhysicsCollisionObject) rBody0.getUserPointer();
    290                     node1 = (PhysicsCollisionObject) rBody1.getUserPointer();
    291                     collisionEvents.add(eventFactory.getEvent(PhysicsCollisionEvent.TYPE_PROCESSED, node, node1, cp));
    292                 }
    293                 return true;
    294             }
    295         });
    296 
    297         BulletGlobals.setContactDestroyedCallback(new ContactDestroyedCallback() {
    298 
    299             public boolean contactDestroyed(Object userPersistentData) {
    300                 System.out.println("contact destroyed");
    301                 return true;
    302             }
    303         });
    304     }
    305 
    306     /**
    307      * updates the physics space
    308      * @param time the current time value
    309      */
    310     public void update(float time) {
    311         update(time, maxSubSteps);
    312     }
    313 
    314     /**
    315      * updates the physics space, uses maxSteps<br>
    316      * @param time the current time value
    317      * @param maxSteps
    318      */
    319     public void update(float time, int maxSteps) {
    320         if (getDynamicsWorld() == null) {
    321             return;
    322         }
    323         //step simulation
    324         dynamicsWorld.stepSimulation(time, maxSteps, accuracy);
    325     }
    326 
    327     public void distributeEvents() {
    328         //add collision callbacks
    329         synchronized (collisionEvents) {
    330             for (Iterator<PhysicsCollisionEvent> it = collisionEvents.iterator(); it.hasNext();) {
    331                 PhysicsCollisionEvent physicsCollisionEvent = it.next();
    332                 for (PhysicsCollisionListener listener : collisionListeners) {
    333                     listener.collision(physicsCollisionEvent);
    334                 }
    335                 //recycle events
    336                 eventFactory.recycle(physicsCollisionEvent);
    337                 it.remove();
    338             }
    339         }
    340     }
    341 
    342     public static <V> Future<V> enqueueOnThisThread(Callable<V> callable) {
    343         AppTask<V> task = new AppTask<V>(callable);
    344         System.out.println("created apptask");
    345         pQueueTL.get().add(task);
    346         return task;
    347     }
    348 
    349     /**
    350      * calls the callable on the next physics tick (ensuring e.g. force applying)
    351      * @param <V>
    352      * @param callable
    353      * @return
    354      */
    355     public <V> Future<V> enqueue(Callable<V> callable) {
    356         AppTask<V> task = new AppTask<V>(callable);
    357         pQueue.add(task);
    358         return task;
    359     }
    360 
    361     /**
    362      * adds an object to the physics space
    363      * @param obj the PhysicsControl or Spatial with PhysicsControl to add
    364      */
    365     public void add(Object obj) {
    366         if (obj instanceof PhysicsControl) {
    367             ((PhysicsControl) obj).setPhysicsSpace(this);
    368         } else if (obj instanceof Spatial) {
    369             Spatial node = (Spatial) obj;
    370             PhysicsControl control = node.getControl(PhysicsControl.class);
    371             control.setPhysicsSpace(this);
    372         } else if (obj instanceof PhysicsCollisionObject) {
    373             addCollisionObject((PhysicsCollisionObject) obj);
    374         } else if (obj instanceof PhysicsJoint) {
    375             addJoint((PhysicsJoint) obj);
    376         } else {
    377             throw (new UnsupportedOperationException("Cannot add this kind of object to the physics space."));
    378         }
    379     }
    380 
    381     public void addCollisionObject(PhysicsCollisionObject obj) {
    382         if (obj instanceof PhysicsGhostObject) {
    383             addGhostObject((PhysicsGhostObject) obj);
    384         } else if (obj instanceof PhysicsRigidBody) {
    385             addRigidBody((PhysicsRigidBody) obj);
    386         } else if (obj instanceof PhysicsVehicle) {
    387             addRigidBody((PhysicsVehicle) obj);
    388         } else if (obj instanceof PhysicsCharacter) {
    389             addCharacter((PhysicsCharacter) obj);
    390         }
    391     }
    392 
    393     /**
    394      * removes an object from the physics space
    395      * @param obj the PhysicsControl or Spatial with PhysicsControl to remove
    396      */
    397     public void remove(Object obj) {
    398         if (obj instanceof PhysicsControl) {
    399             ((PhysicsControl) obj).setPhysicsSpace(null);
    400         } else if (obj instanceof Spatial) {
    401             Spatial node = (Spatial) obj;
    402             PhysicsControl control = node.getControl(PhysicsControl.class);
    403             control.setPhysicsSpace(null);
    404         } else if (obj instanceof PhysicsCollisionObject) {
    405             removeCollisionObject((PhysicsCollisionObject) obj);
    406         } else if (obj instanceof PhysicsJoint) {
    407             removeJoint((PhysicsJoint) obj);
    408         } else {
    409             throw (new UnsupportedOperationException("Cannot remove this kind of object from the physics space."));
    410         }
    411     }
    412 
    413     public void removeCollisionObject(PhysicsCollisionObject obj) {
    414         if (obj instanceof PhysicsGhostObject) {
    415             removeGhostObject((PhysicsGhostObject) obj);
    416         } else if (obj instanceof PhysicsRigidBody) {
    417             removeRigidBody((PhysicsRigidBody) obj);
    418         } else if (obj instanceof PhysicsCharacter) {
    419             removeCharacter((PhysicsCharacter) obj);
    420         }
    421     }
    422 
    423     /**
    424      * adds all physics controls and joints in the given spatial node to the physics space
    425      * (e.g. after loading from disk) - recursive if node
    426      * @param spatial the rootnode containing the physics objects
    427      */
    428     public void addAll(Spatial spatial) {
    429         if (spatial.getControl(RigidBodyControl.class) != null) {
    430             RigidBodyControl physicsNode = spatial.getControl(RigidBodyControl.class);
    431             if (!physicsNodes.containsValue(physicsNode)) {
    432                 physicsNode.setPhysicsSpace(this);
    433             }
    434             //add joints
    435             List<PhysicsJoint> joints = physicsNode.getJoints();
    436             for (Iterator<PhysicsJoint> it1 = joints.iterator(); it1.hasNext();) {
    437                 PhysicsJoint physicsJoint = it1.next();
    438                 //add connected physicsnodes if they are not already added
    439                 if (!physicsNodes.containsValue(physicsJoint.getBodyA())) {
    440                     if (physicsJoint.getBodyA() instanceof PhysicsControl) {
    441                         add(physicsJoint.getBodyA());
    442                     } else {
    443                         addRigidBody(physicsJoint.getBodyA());
    444                     }
    445                 }
    446                 if (!physicsNodes.containsValue(physicsJoint.getBodyB())) {
    447                     if (physicsJoint.getBodyA() instanceof PhysicsControl) {
    448                         add(physicsJoint.getBodyB());
    449                     } else {
    450                         addRigidBody(physicsJoint.getBodyB());
    451                     }
    452                 }
    453                 if (!physicsJoints.contains(physicsJoint)) {
    454                     addJoint(physicsJoint);
    455                 }
    456             }
    457         } else if (spatial.getControl(PhysicsControl.class) != null) {
    458             spatial.getControl(PhysicsControl.class).setPhysicsSpace(this);
    459         }
    460         //recursion
    461         if (spatial instanceof Node) {
    462             List<Spatial> children = ((Node) spatial).getChildren();
    463             for (Iterator<Spatial> it = children.iterator(); it.hasNext();) {
    464                 Spatial spat = it.next();
    465                 addAll(spat);
    466             }
    467         }
    468     }
    469 
    470     /**
    471      * Removes all physics controls and joints in the given spatial from the physics space
    472      * (e.g. before saving to disk) - recursive if node
    473      * @param spatial the rootnode containing the physics objects
    474      */
    475     public void removeAll(Spatial spatial) {
    476         if (spatial.getControl(RigidBodyControl.class) != null) {
    477             RigidBodyControl physicsNode = spatial.getControl(RigidBodyControl.class);
    478             if (physicsNodes.containsValue(physicsNode)) {
    479                 physicsNode.setPhysicsSpace(null);
    480             }
    481             //remove joints
    482             List<PhysicsJoint> joints = physicsNode.getJoints();
    483             for (Iterator<PhysicsJoint> it1 = joints.iterator(); it1.hasNext();) {
    484                 PhysicsJoint physicsJoint = it1.next();
    485                 //add connected physicsnodes if they are not already added
    486                 if (physicsNodes.containsValue(physicsJoint.getBodyA())) {
    487                     if (physicsJoint.getBodyA() instanceof PhysicsControl) {
    488                         remove(physicsJoint.getBodyA());
    489                     } else {
    490                         removeRigidBody(physicsJoint.getBodyA());
    491                     }
    492                 }
    493                 if (physicsNodes.containsValue(physicsJoint.getBodyB())) {
    494                     if (physicsJoint.getBodyA() instanceof PhysicsControl) {
    495                         remove(physicsJoint.getBodyB());
    496                     } else {
    497                         removeRigidBody(physicsJoint.getBodyB());
    498                     }
    499                 }
    500                 if (physicsJoints.contains(physicsJoint)) {
    501                     removeJoint(physicsJoint);
    502                 }
    503             }
    504         } else if (spatial.getControl(PhysicsControl.class) != null) {
    505             spatial.getControl(PhysicsControl.class).setPhysicsSpace(null);
    506         }
    507         //recursion
    508         if (spatial instanceof Node) {
    509             List<Spatial> children = ((Node) spatial).getChildren();
    510             for (Iterator<Spatial> it = children.iterator(); it.hasNext();) {
    511                 Spatial spat = it.next();
    512                 removeAll(spat);
    513             }
    514         }
    515     }
    516 
    517     private void addGhostObject(PhysicsGhostObject node) {
    518         Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding ghost object {0} to physics space.", node.getObjectId());
    519         dynamicsWorld.addCollisionObject(node.getObjectId());
    520     }
    521 
    522     private void removeGhostObject(PhysicsGhostObject node) {
    523         Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing ghost object {0} from physics space.", node.getObjectId());
    524         dynamicsWorld.removeCollisionObject(node.getObjectId());
    525     }
    526 
    527     private void addCharacter(PhysicsCharacter node) {
    528         Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding character {0} to physics space.", node.getObjectId());
    529 //        dynamicsWorld.addCollisionObject(node.getObjectId());
    530         dynamicsWorld.addCollisionObject(node.getObjectId(), CollisionFilterGroups.CHARACTER_FILTER, (short) (CollisionFilterGroups.STATIC_FILTER | CollisionFilterGroups.DEFAULT_FILTER));
    531         dynamicsWorld.addAction(node.getControllerId());
    532     }
    533 
    534     private void removeCharacter(PhysicsCharacter node) {
    535         Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing character {0} from physics space.", node.getObjectId());
    536         dynamicsWorld.removeAction(node.getControllerId());
    537         dynamicsWorld.removeCollisionObject(node.getObjectId());
    538     }
    539 
    540     private void addRigidBody(PhysicsRigidBody node) {
    541         physicsNodes.put(node.getObjectId(), node);
    542 
    543         //Workaround
    544         //It seems that adding a Kinematic RigidBody to the dynamicWorld prevent it from being non kinematic again afterward.
    545         //so we add it non kinematic, then set it kinematic again.
    546         boolean kinematic = false;
    547         if (node.isKinematic()) {
    548             kinematic = true;
    549             node.setKinematic(false);
    550         }
    551         dynamicsWorld.addRigidBody(node.getObjectId());
    552         if (kinematic) {
    553             node.setKinematic(true);
    554         }
    555 
    556         Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding RigidBody {0} to physics space.", node.getObjectId());
    557         if (node instanceof PhysicsVehicle) {
    558             Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding vehicle constraint {0} to physics space.", ((PhysicsVehicle) node).getVehicleId());
    559             ((PhysicsVehicle) node).createVehicle(this);
    560             dynamicsWorld.addVehicle(((PhysicsVehicle) node).getVehicleId());
    561         }
    562     }
    563 
    564     private void removeRigidBody(PhysicsRigidBody node) {
    565         if (node instanceof PhysicsVehicle) {
    566             Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing vehicle constraint {0} from physics space.", ((PhysicsVehicle) node).getVehicleId());
    567             dynamicsWorld.removeVehicle(((PhysicsVehicle) node).getVehicleId());
    568         }
    569         Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing RigidBody {0} from physics space.", node.getObjectId());
    570         physicsNodes.remove(node.getObjectId());
    571         dynamicsWorld.removeRigidBody(node.getObjectId());
    572     }
    573 
    574     private void addJoint(PhysicsJoint joint) {
    575         Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding Joint {0} to physics space.", joint.getObjectId());
    576         physicsJoints.add(joint);
    577         dynamicsWorld.addConstraint(joint.getObjectId(), !joint.isCollisionBetweenLinkedBodys());
    578     }
    579 
    580     private void removeJoint(PhysicsJoint joint) {
    581         Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing Joint {0} from physics space.", joint.getObjectId());
    582         physicsJoints.remove(joint);
    583         dynamicsWorld.removeConstraint(joint.getObjectId());
    584     }
    585 
    586     /**
    587      * Sets the gravity of the PhysicsSpace, set before adding physics objects!
    588      * @param gravity
    589      */
    590     public void setGravity(Vector3f gravity) {
    591         dynamicsWorld.setGravity(Converter.convert(gravity));
    592     }
    593 
    594     /**
    595      * applies gravity value to all objects
    596      */
    597     public void applyGravity() {
    598         dynamicsWorld.applyGravity();
    599     }
    600 
    601     /**
    602      * clears forces of all objects
    603      */
    604     public void clearForces() {
    605         dynamicsWorld.clearForces();
    606     }
    607 
    608     /**
    609      * Adds the specified listener to the physics tick listeners.
    610      * The listeners are called on each physics step, which is not necessarily
    611      * each frame but is determined by the accuracy of the physics space.
    612      * @param listener
    613      */
    614     public void addTickListener(PhysicsTickListener listener) {
    615         tickListeners.add(listener);
    616     }
    617 
    618     public void removeTickListener(PhysicsTickListener listener) {
    619         tickListeners.remove(listener);
    620     }
    621 
    622     /**
    623      * Adds a CollisionListener that will be informed about collision events
    624      * @param listener the CollisionListener to add
    625      */
    626     public void addCollisionListener(PhysicsCollisionListener listener) {
    627         collisionListeners.add(listener);
    628     }
    629 
    630     /**
    631      * Removes a CollisionListener from the list
    632      * @param listener the CollisionListener to remove
    633      */
    634     public void removeCollisionListener(PhysicsCollisionListener listener) {
    635         collisionListeners.remove(listener);
    636     }
    637 
    638     /**
    639      * Adds a listener for a specific collision group, such a listener can disable collisions when they happen.<br>
    640      * There can be only one listener per collision group.
    641      * @param listener
    642      * @param collisionGroup
    643      */
    644     public void addCollisionGroupListener(PhysicsCollisionGroupListener listener, int collisionGroup) {
    645         collisionGroupListeners.put(collisionGroup, listener);
    646     }
    647 
    648     public void removeCollisionGroupListener(int collisionGroup) {
    649         collisionGroupListeners.remove(collisionGroup);
    650     }
    651 
    652     /**
    653      * Performs a ray collision test and returns the results as a list of PhysicsRayTestResults
    654      */
    655     public List<PhysicsRayTestResult> rayTest(Vector3f from, Vector3f to) {
    656         List<PhysicsRayTestResult> results = new LinkedList<PhysicsRayTestResult>();
    657         dynamicsWorld.rayTest(Converter.convert(from, rayVec1), Converter.convert(to, rayVec2), new InternalRayListener(results));
    658         return results;
    659     }
    660 
    661     /**
    662      * Performs a ray collision test and returns the results as a list of PhysicsRayTestResults
    663      */
    664     public List<PhysicsRayTestResult> rayTest(Vector3f from, Vector3f to, List<PhysicsRayTestResult> results) {
    665         results.clear();
    666         dynamicsWorld.rayTest(Converter.convert(from, rayVec1), Converter.convert(to, rayVec2), new InternalRayListener(results));
    667         return results;
    668     }
    669 
    670     private class InternalRayListener extends CollisionWorld.RayResultCallback {
    671 
    672         private List<PhysicsRayTestResult> results;
    673 
    674         public InternalRayListener(List<PhysicsRayTestResult> results) {
    675             this.results = results;
    676         }
    677 
    678         @Override
    679         public float addSingleResult(LocalRayResult lrr, boolean bln) {
    680             PhysicsCollisionObject obj = (PhysicsCollisionObject) lrr.collisionObject.getUserPointer();
    681             results.add(new PhysicsRayTestResult(obj, Converter.convert(lrr.hitNormalLocal), lrr.hitFraction, bln));
    682             return lrr.hitFraction;
    683         }
    684     }
    685 
    686     /**
    687      * Performs a sweep collision test and returns the results as a list of PhysicsSweepTestResults<br/>
    688      * You have to use different Transforms for start and end (at least distance > 0.4f).
    689      * SweepTest will not see a collision if it starts INSIDE an object and is moving AWAY from its center.
    690      */
    691     public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end) {
    692         List<PhysicsSweepTestResult> results = new LinkedList<PhysicsSweepTestResult>();
    693         if (!(shape.getCShape() instanceof ConvexShape)) {
    694             Logger.getLogger(PhysicsSpace.class.getName()).log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!");
    695             return results;
    696         }
    697         dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results));
    698         return results;
    699 
    700     }
    701 
    702     /**
    703      * Performs a sweep collision test and returns the results as a list of PhysicsSweepTestResults<br/>
    704      * You have to use different Transforms for start and end (at least distance > 0.4f).
    705      * SweepTest will not see a collision if it starts INSIDE an object and is moving AWAY from its center.
    706      */
    707     public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end, List<PhysicsSweepTestResult> results) {
    708         results.clear();
    709         if (!(shape.getCShape() instanceof ConvexShape)) {
    710             Logger.getLogger(PhysicsSpace.class.getName()).log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!");
    711             return results;
    712         }
    713         dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results));
    714         return results;
    715     }
    716 
    717     private class InternalSweepListener extends CollisionWorld.ConvexResultCallback {
    718 
    719         private List<PhysicsSweepTestResult> results;
    720 
    721         public InternalSweepListener(List<PhysicsSweepTestResult> results) {
    722             this.results = results;
    723         }
    724 
    725         @Override
    726         public float addSingleResult(LocalConvexResult lcr, boolean bln) {
    727             PhysicsCollisionObject obj = (PhysicsCollisionObject) lcr.hitCollisionObject.getUserPointer();
    728             results.add(new PhysicsSweepTestResult(obj, Converter.convert(lcr.hitNormalLocal), lcr.hitFraction, bln));
    729             return lcr.hitFraction;
    730         }
    731     }
    732 
    733     /**
    734      * destroys the current PhysicsSpace so that a new one can be created
    735      */
    736     public void destroy() {
    737         physicsNodes.clear();
    738         physicsJoints.clear();
    739 
    740         dynamicsWorld.destroy();
    741         dynamicsWorld = null;
    742     }
    743 
    744     /**
    745      * used internally
    746      * @return the dynamicsWorld
    747      */
    748     public DynamicsWorld getDynamicsWorld() {
    749         return dynamicsWorld;
    750     }
    751 
    752     public BroadphaseType getBroadphaseType() {
    753         return broadphaseType;
    754     }
    755 
    756     public void setBroadphaseType(BroadphaseType broadphaseType) {
    757         this.broadphaseType = broadphaseType;
    758     }
    759 
    760     /**
    761      * Sets the maximum amount of extra steps that will be used to step the physics
    762      * when the fps is below the physics fps. Doing this maintains determinism in physics.
    763      * For example a maximum number of 2 can compensate for framerates as low as 30fps
    764      * when the physics has the default accuracy of 60 fps. Note that setting this
    765      * value too high can make the physics drive down its own fps in case its overloaded.
    766      * @param steps The maximum number of extra steps, default is 4.
    767      */
    768     public void setMaxSubSteps(int steps) {
    769         maxSubSteps = steps;
    770     }
    771 
    772     /**
    773      * get the current accuracy of the physics computation
    774      * @return the current accuracy
    775      */
    776     public float getAccuracy() {
    777         return accuracy;
    778     }
    779 
    780     /**
    781      * sets the accuracy of the physics computation, default=1/60s<br>
    782      * @param accuracy
    783      */
    784     public void setAccuracy(float accuracy) {
    785         this.accuracy = accuracy;
    786     }
    787 
    788     public Vector3f getWorldMin() {
    789         return worldMin;
    790     }
    791 
    792     /**
    793      * only applies for AXIS_SWEEP broadphase
    794      * @param worldMin
    795      */
    796     public void setWorldMin(Vector3f worldMin) {
    797         this.worldMin.set(worldMin);
    798     }
    799 
    800     public Vector3f getWorldMax() {
    801         return worldMax;
    802     }
    803 
    804     /**
    805      * only applies for AXIS_SWEEP broadphase
    806      * @param worldMax
    807      */
    808     public void setWorldMax(Vector3f worldMax) {
    809         this.worldMax.set(worldMax);
    810     }
    811 
    812     /**
    813      * Enable debug display for physics
    814      * @param manager AssetManager to use to create debug materials
    815      */
    816     public void enableDebug(AssetManager manager) {
    817         debugManager = manager;
    818     }
    819 
    820     /**
    821      * Disable debug display
    822      */
    823     public void disableDebug() {
    824         debugManager = null;
    825     }
    826 
    827     public AssetManager getDebugManager() {
    828         return debugManager;
    829     }
    830 
    831     /**
    832      * interface with Broadphase types
    833      */
    834     public enum BroadphaseType {
    835 
    836         /**
    837          * basic Broadphase
    838          */
    839         SIMPLE,
    840         /**
    841          * better Broadphase, needs worldBounds , max Object number = 16384
    842          */
    843         AXIS_SWEEP_3,
    844         /**
    845          * better Broadphase, needs worldBounds , max Object number = 65536
    846          */
    847         AXIS_SWEEP_3_32,
    848         /**
    849          * Broadphase allowing quicker adding/removing of physics objects
    850          */
    851         DBVT;
    852     }
    853 }
    854