PhysicsSpace.java 36 KB

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