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