• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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      */
getPhysicsSpace()126     public static PhysicsSpace getPhysicsSpace() {
127         return physicsSpaceTL.get();
128     }
129 
130     /**
131      * Used internally
132      * @param space
133      */
setLocalThreadPhysicsSpace(PhysicsSpace space)134     public static void setLocalThreadPhysicsSpace(PhysicsSpace space) {
135         physicsSpaceTL.set(space);
136     }
137 
PhysicsSpace()138     public PhysicsSpace() {
139         this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), BroadphaseType.DBVT);
140     }
141 
PhysicsSpace(BroadphaseType broadphaseType)142     public PhysicsSpace(BroadphaseType broadphaseType) {
143         this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType);
144     }
145 
PhysicsSpace(Vector3f worldMin, Vector3f worldMax)146     public PhysicsSpace(Vector3f worldMin, Vector3f worldMax) {
147         this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3);
148     }
149 
PhysicsSpace(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType)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      */
create()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 
setOverlapFilterCallback()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 
setTickCallback()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 
setContactCallbacks()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      */
update(float time)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      */
update(float time, int maxSteps)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 
distributeEvents()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 
enqueueOnThisThread(Callable<V> callable)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      */
enqueue(Callable<V> callable)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      */
add(Object obj)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 
addCollisionObject(PhysicsCollisionObject obj)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      */
remove(Object obj)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 
removeCollisionObject(PhysicsCollisionObject obj)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      */
addAll(Spatial spatial)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      */
removeAll(Spatial spatial)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 
addGhostObject(PhysicsGhostObject node)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 
removeGhostObject(PhysicsGhostObject node)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 
addCharacter(PhysicsCharacter node)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 
removeCharacter(PhysicsCharacter node)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 
addRigidBody(PhysicsRigidBody node)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 
removeRigidBody(PhysicsRigidBody node)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 
addJoint(PhysicsJoint joint)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 
removeJoint(PhysicsJoint joint)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      */
setGravity(Vector3f gravity)590     public void setGravity(Vector3f gravity) {
591         dynamicsWorld.setGravity(Converter.convert(gravity));
592     }
593 
594     /**
595      * applies gravity value to all objects
596      */
applyGravity()597     public void applyGravity() {
598         dynamicsWorld.applyGravity();
599     }
600 
601     /**
602      * clears forces of all objects
603      */
clearForces()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      */
addTickListener(PhysicsTickListener listener)614     public void addTickListener(PhysicsTickListener listener) {
615         tickListeners.add(listener);
616     }
617 
removeTickListener(PhysicsTickListener listener)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      */
addCollisionListener(PhysicsCollisionListener listener)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      */
removeCollisionListener(PhysicsCollisionListener listener)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      */
addCollisionGroupListener(PhysicsCollisionGroupListener listener, int collisionGroup)644     public void addCollisionGroupListener(PhysicsCollisionGroupListener listener, int collisionGroup) {
645         collisionGroupListeners.put(collisionGroup, listener);
646     }
647 
removeCollisionGroupListener(int collisionGroup)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      */
rayTest(Vector3f from, Vector3f to)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      */
rayTest(Vector3f from, Vector3f to, List<PhysicsRayTestResult> results)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 
InternalRayListener(List<PhysicsRayTestResult> results)674         public InternalRayListener(List<PhysicsRayTestResult> results) {
675             this.results = results;
676         }
677 
678         @Override
addSingleResult(LocalRayResult lrr, boolean bln)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      */
sweepTest(CollisionShape shape, Transform start, Transform end)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      */
sweepTest(CollisionShape shape, Transform start, Transform end, List<PhysicsSweepTestResult> results)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 
InternalSweepListener(List<PhysicsSweepTestResult> results)721         public InternalSweepListener(List<PhysicsSweepTestResult> results) {
722             this.results = results;
723         }
724 
725         @Override
addSingleResult(LocalConvexResult lcr, boolean bln)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      */
destroy()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      */
getDynamicsWorld()748     public DynamicsWorld getDynamicsWorld() {
749         return dynamicsWorld;
750     }
751 
getBroadphaseType()752     public BroadphaseType getBroadphaseType() {
753         return broadphaseType;
754     }
755 
setBroadphaseType(BroadphaseType broadphaseType)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      */
setMaxSubSteps(int steps)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      */
getAccuracy()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      */
setAccuracy(float accuracy)784     public void setAccuracy(float accuracy) {
785         this.accuracy = accuracy;
786     }
787 
getWorldMin()788     public Vector3f getWorldMin() {
789         return worldMin;
790     }
791 
792     /**
793      * only applies for AXIS_SWEEP broadphase
794      * @param worldMin
795      */
setWorldMin(Vector3f worldMin)796     public void setWorldMin(Vector3f worldMin) {
797         this.worldMin.set(worldMin);
798     }
799 
getWorldMax()800     public Vector3f getWorldMax() {
801         return worldMax;
802     }
803 
804     /**
805      * only applies for AXIS_SWEEP broadphase
806      * @param worldMax
807      */
setWorldMax(Vector3f worldMax)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      */
enableDebug(AssetManager manager)816     public void enableDebug(AssetManager manager) {
817         debugManager = manager;
818     }
819 
820     /**
821      * Disable debug display
822      */
disableDebug()823     public void disableDebug() {
824         debugManager = null;
825     }
826 
getDebugManager()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