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