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