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