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