• 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.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      */
getPhysicsSpace()110     public static PhysicsSpace getPhysicsSpace() {
111         return physicsSpaceTL.get();
112     }
113 
114     /**
115      * Used internally
116      * @param space
117      */
setLocalThreadPhysicsSpace(PhysicsSpace space)118     public static void setLocalThreadPhysicsSpace(PhysicsSpace space) {
119         physicsSpaceTL.set(space);
120     }
121 
PhysicsSpace()122     public PhysicsSpace() {
123         this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), BroadphaseType.DBVT);
124     }
125 
PhysicsSpace(BroadphaseType broadphaseType)126     public PhysicsSpace(BroadphaseType broadphaseType) {
127         this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType);
128     }
129 
PhysicsSpace(Vector3f worldMin, Vector3f worldMax)130     public PhysicsSpace(Vector3f worldMin, Vector3f worldMax) {
131         this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3);
132     }
133 
PhysicsSpace(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType)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      */
create()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 
createPhysicsSpace(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int broadphaseType, boolean threading)182     private native long createPhysicsSpace(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int broadphaseType, boolean threading);
183 
preTick_native(float f)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 
postTick_native(float f)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 
addCollision_native()211     private void addCollision_native() {
212     }
213 
needCollision_native(PhysicsCollisionObject objectA, PhysicsCollisionObject objectB)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 //    }
addCollisionEvent_native(PhysicsCollisionObject node, PhysicsCollisionObject node1, long manifoldPointObjectId)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      */
update(float time)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      */
update(float time, int maxSteps)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 
stepSimulation(long space, float time, int maxSteps, float accuracy)351     private native void stepSimulation(long space, float time, int maxSteps, float accuracy);
352 
distributeEvents()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 
enqueueOnThisThread(Callable<V> callable)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      */
enqueue(Callable<V> callable)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      */
add(Object obj)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 
addCollisionObject(PhysicsCollisionObject obj)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      */
remove(Object obj)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 
removeCollisionObject(PhysicsCollisionObject obj)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      */
addAll(Spatial spatial)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      */
removeAll(Spatial spatial)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 
addCollisionObject(long space, long id)543     private native void addCollisionObject(long space, long id);
544 
removeCollisionObject(long space, long id)545     private native void removeCollisionObject(long space, long id);
546 
addRigidBody(long space, long id)547     private native void addRigidBody(long space, long id);
548 
removeRigidBody(long space, long id)549     private native void removeRigidBody(long space, long id);
550 
addCharacterObject(long space, long id)551     private native void addCharacterObject(long space, long id);
552 
removeCharacterObject(long space, long id)553     private native void removeCharacterObject(long space, long id);
554 
addAction(long space, long id)555     private native void addAction(long space, long id);
556 
removeAction(long space, long id)557     private native void removeAction(long space, long id);
558 
addVehicle(long space, long id)559     private native void addVehicle(long space, long id);
560 
removeVehicle(long space, long id)561     private native void removeVehicle(long space, long id);
562 
addConstraint(long space, long id)563     private native void addConstraint(long space, long id);
564 
removeConstraint(long space, long id)565     private native void removeConstraint(long space, long id);
566 
addGhostObject(PhysicsGhostObject node)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 
removeGhostObject(PhysicsGhostObject node)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 
addCharacter(PhysicsCharacter node)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 
removeCharacter(PhysicsCharacter node)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 
addRigidBody(PhysicsRigidBody node)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 
removeRigidBody(PhysicsRigidBody node)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 
addJoint(PhysicsJoint joint)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 
removeJoint(PhysicsJoint joint)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      */
setGravity(Vector3f gravity)644     public void setGravity(Vector3f gravity) {
645 //        dynamicsWorld.setGravity(Converter.convert(gravity));
646         setGravity(physicsSpaceId, gravity);
647     }
648 
setGravity(long spaceId, Vector3f gravity)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      */
addTickListener(PhysicsTickListener listener)671     public void addTickListener(PhysicsTickListener listener) {
672         tickListeners.add(listener);
673     }
674 
removeTickListener(PhysicsTickListener listener)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      */
addCollisionListener(PhysicsCollisionListener listener)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      */
removeCollisionListener(PhysicsCollisionListener listener)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      */
addCollisionGroupListener(PhysicsCollisionGroupListener listener, int collisionGroup)701     public void addCollisionGroupListener(PhysicsCollisionGroupListener listener, int collisionGroup) {
702         collisionGroupListeners.put(collisionGroup, listener);
703     }
704 
removeCollisionGroupListener(int collisionGroup)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      */
rayTest(Vector3f from, Vector3f to)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      */
rayTest(Vector3f from, Vector3f to, List<PhysicsRayTestResult> results)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 
rayTest_native(Vector3f from, Vector3f to, long physicsSpaceId, List<PhysicsRayTestResult> results)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      */
sweepTest(CollisionShape shape, Transform start, Transform end)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      */
sweepTest(CollisionShape shape, Transform start, Transform end, List<PhysicsSweepTestResult> results)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      */
destroy()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     //     */
getSpaceId()805     public long getSpaceId() {
806         return physicsSpaceId;
807     }
808 
getBroadphaseType()809     public BroadphaseType getBroadphaseType() {
810         return broadphaseType;
811     }
812 
setBroadphaseType(BroadphaseType broadphaseType)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      */
setMaxSubSteps(int steps)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      */
getAccuracy()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      */
setAccuracy(float accuracy)841     public void setAccuracy(float accuracy) {
842         this.accuracy = accuracy;
843     }
844 
getWorldMin()845     public Vector3f getWorldMin() {
846         return worldMin;
847     }
848 
849     /**
850      * only applies for AXIS_SWEEP broadphase
851      * @param worldMin
852      */
setWorldMin(Vector3f worldMin)853     public void setWorldMin(Vector3f worldMin) {
854         this.worldMin.set(worldMin);
855     }
856 
getWorldMax()857     public Vector3f getWorldMax() {
858         return worldMax;
859     }
860 
861     /**
862      * only applies for AXIS_SWEEP broadphase
863      * @param worldMax
864      */
setWorldMax(Vector3f worldMax)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      */
enableDebug(AssetManager manager)873     public void enableDebug(AssetManager manager) {
874         debugManager = manager;
875     }
876 
877     /**
878      * Disable debug display
879      */
disableDebug()880     public void disableDebug() {
881         debugManager = null;
882     }
883 
getDebugManager()884     public AssetManager getDebugManager() {
885         return debugManager;
886     }
887 
initNativePhysics()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
finalize()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 
finalizeNative(long objectId)920     private native void finalizeNative(long objectId);
921 }
922