diff options
Diffstat (limited to 'engine/src/bullet/com/jme3/bullet/PhysicsSpace.java')
-rw-r--r-- | engine/src/bullet/com/jme3/bullet/PhysicsSpace.java | 921 |
1 files changed, 921 insertions, 0 deletions
diff --git a/engine/src/bullet/com/jme3/bullet/PhysicsSpace.java b/engine/src/bullet/com/jme3/bullet/PhysicsSpace.java new file mode 100644 index 0000000..14cc635 --- /dev/null +++ b/engine/src/bullet/com/jme3/bullet/PhysicsSpace.java @@ -0,0 +1,921 @@ +/* + * Copyright (c) 2009-2010 jMonkeyEngine + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of 'jMonkeyEngine' nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.jme3.bullet; + +import com.jme3.app.AppTask; +import com.jme3.asset.AssetManager; +import com.jme3.bullet.collision.*; +import com.jme3.bullet.collision.shapes.CollisionShape; +import com.jme3.bullet.control.PhysicsControl; +import com.jme3.bullet.control.RigidBodyControl; +import com.jme3.bullet.joints.PhysicsJoint; +import com.jme3.bullet.objects.PhysicsCharacter; +import com.jme3.bullet.objects.PhysicsGhostObject; +import com.jme3.bullet.objects.PhysicsRigidBody; +import com.jme3.bullet.objects.PhysicsVehicle; +import com.jme3.math.Transform; +import com.jme3.math.Vector3f; +import com.jme3.scene.Node; +import com.jme3.scene.Spatial; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * <p>PhysicsSpace - The central jbullet-jme physics space</p> + * @author normenhansen + */ +public class PhysicsSpace { + + public static final int AXIS_X = 0; + public static final int AXIS_Y = 1; + public static final int AXIS_Z = 2; + private long physicsSpaceId = 0; + private static ThreadLocal<ConcurrentLinkedQueue<AppTask<?>>> pQueueTL = + new ThreadLocal<ConcurrentLinkedQueue<AppTask<?>>>() { + + @Override + protected ConcurrentLinkedQueue<AppTask<?>> initialValue() { + return new ConcurrentLinkedQueue<AppTask<?>>(); + } + }; + private ConcurrentLinkedQueue<AppTask<?>> pQueue = new ConcurrentLinkedQueue<AppTask<?>>(); + private static ThreadLocal<PhysicsSpace> physicsSpaceTL = new ThreadLocal<PhysicsSpace>(); + private BroadphaseType broadphaseType = BroadphaseType.DBVT; +// private DiscreteDynamicsWorld dynamicsWorld = null; +// private BroadphaseInterface broadphase; +// private CollisionDispatcher dispatcher; +// private ConstraintSolver solver; +// private DefaultCollisionConfiguration collisionConfiguration; +// private Map<GhostObject, PhysicsGhostObject> physicsGhostNodes = new ConcurrentHashMap<GhostObject, PhysicsGhostObject>(); + private Map<Long, PhysicsRigidBody> physicsNodes = new ConcurrentHashMap<Long, PhysicsRigidBody>(); + private List<PhysicsJoint> physicsJoints = new LinkedList<PhysicsJoint>(); + private List<PhysicsCollisionListener> collisionListeners = new LinkedList<PhysicsCollisionListener>(); + private List<PhysicsCollisionEvent> collisionEvents = new LinkedList<PhysicsCollisionEvent>(); + private Map<Integer, PhysicsCollisionGroupListener> collisionGroupListeners = new ConcurrentHashMap<Integer, PhysicsCollisionGroupListener>(); + private ConcurrentLinkedQueue<PhysicsTickListener> tickListeners = new ConcurrentLinkedQueue<PhysicsTickListener>(); + private PhysicsCollisionEventFactory eventFactory = new PhysicsCollisionEventFactory(); + private Vector3f worldMin = new Vector3f(-10000f, -10000f, -10000f); + private Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f); + private float accuracy = 1f / 60f; + private int maxSubSteps = 4; + private AssetManager debugManager; + + static { +// System.loadLibrary("bulletjme"); +// initNativePhysics(); + } + + /** + * Get the current PhysicsSpace <b>running on this thread</b><br/> + * For parallel physics, this can also be called from the OpenGL thread to receive the PhysicsSpace + * @return the PhysicsSpace running on this thread + */ + public static PhysicsSpace getPhysicsSpace() { + return physicsSpaceTL.get(); + } + + /** + * Used internally + * @param space + */ + public static void setLocalThreadPhysicsSpace(PhysicsSpace space) { + physicsSpaceTL.set(space); + } + + public PhysicsSpace() { + this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), BroadphaseType.DBVT); + } + + public PhysicsSpace(BroadphaseType broadphaseType) { + this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType); + } + + public PhysicsSpace(Vector3f worldMin, Vector3f worldMax) { + this(worldMin, worldMax, BroadphaseType.AXIS_SWEEP_3); + } + + public PhysicsSpace(Vector3f worldMin, Vector3f worldMax, BroadphaseType broadphaseType) { + this.worldMin.set(worldMin); + this.worldMax.set(worldMax); + this.broadphaseType = broadphaseType; + create(); + } + + /** + * Has to be called from the (designated) physics thread + */ + public void create() { + //TODO: boroadphase! + physicsSpaceId = createPhysicsSpace(worldMin.x, worldMin.y, worldMin.z, worldMax.x, worldMax.y, worldMax.z, 3, false); + pQueueTL.set(pQueue); + physicsSpaceTL.set(this); + +// collisionConfiguration = new DefaultCollisionConfiguration(); +// dispatcher = new CollisionDispatcher(collisionConfiguration); +// switch (broadphaseType) { +// case SIMPLE: +// broadphase = new SimpleBroadphase(); +// break; +// case AXIS_SWEEP_3: +// broadphase = new AxisSweep3(Converter.convert(worldMin), Converter.convert(worldMax)); +// break; +// case AXIS_SWEEP_3_32: +// broadphase = new AxisSweep3_32(Converter.convert(worldMin), Converter.convert(worldMax)); +// break; +// case DBVT: +// broadphase = new DbvtBroadphase(); +// break; +// } +// +// solver = new SequentialImpulseConstraintSolver(); +// +// dynamicsWorld = new DiscreteDynamicsWorld(dispatcher, broadphase, solver, collisionConfiguration); +// dynamicsWorld.setGravity(new javax.vecmath.Vector3f(0, -9.81f, 0)); +// +// broadphase.getOverlappingPairCache().setInternalGhostPairCallback(new GhostPairCallback()); +// GImpactCollisionAlgorithm.registerAlgorithm(dispatcher); +// +// //register filter callback for tick / collision +// setTickCallback(); +// setContactCallbacks(); +// //register filter callback for collision groups +// setOverlapFilterCallback(); + } + + private native long createPhysicsSpace(float minX, float minY, float minZ, float maxX, float maxY, float maxZ, int broadphaseType, boolean threading); + + private void preTick_native(float f) { + AppTask task = pQueue.poll(); + task = pQueue.poll(); + while (task != null) { + while (task.isCancelled()) { + task = pQueue.poll(); + } + try { + task.invoke(); + } catch (Exception ex) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.SEVERE, null, ex); + } + task = pQueue.poll(); + } + for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) { + PhysicsTickListener physicsTickCallback = it.next(); + physicsTickCallback.prePhysicsTick(this, f); + } + } + + private void postTick_native(float f) { + for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) { + PhysicsTickListener physicsTickCallback = it.next(); + physicsTickCallback.physicsTick(this, f); + } + } + + private void addCollision_native() { + } + + private boolean needCollision_native(PhysicsCollisionObject objectA, PhysicsCollisionObject objectB) { + return false; + } + +// private void setOverlapFilterCallback() { +// OverlapFilterCallback callback = new OverlapFilterCallback() { +// +// public boolean needBroadphaseCollision(BroadphaseProxy bp, BroadphaseProxy bp1) { +// boolean collides = (bp.collisionFilterGroup & bp1.collisionFilterMask) != 0; +// if (collides) { +// collides = (bp1.collisionFilterGroup & bp.collisionFilterMask) != 0; +// } +// if (collides) { +// assert (bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject && bp.clientObject instanceof com.bulletphysics.collision.dispatch.CollisionObject); +// com.bulletphysics.collision.dispatch.CollisionObject colOb = (com.bulletphysics.collision.dispatch.CollisionObject) bp.clientObject; +// com.bulletphysics.collision.dispatch.CollisionObject colOb1 = (com.bulletphysics.collision.dispatch.CollisionObject) bp1.clientObject; +// assert (colOb.getUserPointer() != null && colOb1.getUserPointer() != null); +// PhysicsCollisionObject collisionObject = (PhysicsCollisionObject) colOb.getUserPointer(); +// PhysicsCollisionObject collisionObject1 = (PhysicsCollisionObject) colOb1.getUserPointer(); +// if ((collisionObject.getCollideWithGroups() & collisionObject1.getCollisionGroup()) > 0 +// || (collisionObject1.getCollideWithGroups() & collisionObject.getCollisionGroup()) > 0) { +// PhysicsCollisionGroupListener listener = collisionGroupListeners.get(collisionObject.getCollisionGroup()); +// PhysicsCollisionGroupListener listener1 = collisionGroupListeners.get(collisionObject1.getCollisionGroup()); +// if (listener != null) { +// return listener.collide(collisionObject, collisionObject1); +// } else if (listener1 != null) { +// return listener1.collide(collisionObject, collisionObject1); +// } +// return true; +// } else { +// return false; +// } +// } +// return collides; +// } +// }; +// dynamicsWorld.getPairCache().setOverlapFilterCallback(callback); +// } +// private void setTickCallback() { +// final PhysicsSpace space = this; +// InternalTickCallback callback2 = new InternalTickCallback() { +// +// @Override +// public void internalTick(DynamicsWorld dw, float f) { +// //execute task list +// AppTask task = pQueue.poll(); +// task = pQueue.poll(); +// while (task != null) { +// while (task.isCancelled()) { +// task = pQueue.poll(); +// } +// try { +// task.invoke(); +// } catch (Exception ex) { +// Logger.getLogger(PhysicsSpace.class.getName()).log(Level.SEVERE, null, ex); +// } +// task = pQueue.poll(); +// } +// for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) { +// PhysicsTickListener physicsTickCallback = it.next(); +// physicsTickCallback.prePhysicsTick(space, f); +// } +// } +// }; +// dynamicsWorld.setPreTickCallback(callback2); +// InternalTickCallback callback = new InternalTickCallback() { +// +// @Override +// public void internalTick(DynamicsWorld dw, float f) { +// for (Iterator<PhysicsTickListener> it = tickListeners.iterator(); it.hasNext();) { +// PhysicsTickListener physicsTickCallback = it.next(); +// physicsTickCallback.physicsTick(space, f); +// } +// } +// }; +// dynamicsWorld.setInternalTickCallback(callback, this); +// } +// private void setContactCallbacks() { +// BulletGlobals.setContactAddedCallback(new ContactAddedCallback() { +// +// public boolean contactAdded(ManifoldPoint cp, com.bulletphysics.collision.dispatch.CollisionObject colObj0, +// int partId0, int index0, com.bulletphysics.collision.dispatch.CollisionObject colObj1, int partId1, +// int index1) { +// System.out.println("contact added"); +// return true; +// } +// }); +// +// BulletGlobals.setContactProcessedCallback(new ContactProcessedCallback() { +// +// public boolean contactProcessed(ManifoldPoint cp, Object body0, Object body1) { +// if (body0 instanceof CollisionObject && body1 instanceof CollisionObject) { +// PhysicsCollisionObject node = null, node1 = null; +// CollisionObject rBody0 = (CollisionObject) body0; +// CollisionObject rBody1 = (CollisionObject) body1; +// node = (PhysicsCollisionObject) rBody0.getUserPointer(); +// node1 = (PhysicsCollisionObject) rBody1.getUserPointer(); +// collisionEvents.add(eventFactory.getEvent(PhysicsCollisionEvent.TYPE_PROCESSED, node, node1, cp)); +// } +// return true; +// } +// }); +// +// BulletGlobals.setContactDestroyedCallback(new ContactDestroyedCallback() { +// +// public boolean contactDestroyed(Object userPersistentData) { +// System.out.println("contact destroyed"); +// return true; +// } +// }); +// } + private void addCollisionEvent_native(PhysicsCollisionObject node, PhysicsCollisionObject node1, long manifoldPointObjectId) { +// System.out.println("addCollisionEvent:"+node.getObjectId()+" "+ node1.getObjectId()); + collisionEvents.add(eventFactory.getEvent(PhysicsCollisionEvent.TYPE_PROCESSED, node, node1, manifoldPointObjectId)); + } + + /** + * updates the physics space + * @param time the current time value + */ + public void update(float time) { + update(time, maxSubSteps); + } + + /** + * updates the physics space, uses maxSteps<br> + * @param time the current time value + * @param maxSteps + */ + public void update(float time, int maxSteps) { +// if (getDynamicsWorld() == null) { +// return; +// } + //step simulation + stepSimulation(physicsSpaceId, time, maxSteps, accuracy); + } + + private native void stepSimulation(long space, float time, int maxSteps, float accuracy); + + public void distributeEvents() { + //add collision callbacks + synchronized (collisionEvents) { + for (Iterator<PhysicsCollisionEvent> it = collisionEvents.iterator(); it.hasNext();) { + PhysicsCollisionEvent physicsCollisionEvent = it.next(); + for (PhysicsCollisionListener listener : collisionListeners) { + listener.collision(physicsCollisionEvent); + } + //recycle events + eventFactory.recycle(physicsCollisionEvent); + it.remove(); + } + } + } + + public static <V> Future<V> enqueueOnThisThread(Callable<V> callable) { + AppTask<V> task = new AppTask<V>(callable); + System.out.println("created apptask"); + pQueueTL.get().add(task); + return task; + } + + /** + * calls the callable on the next physics tick (ensuring e.g. force applying) + * @param <V> + * @param callable + * @return + */ + public <V> Future<V> enqueue(Callable<V> callable) { + AppTask<V> task = new AppTask<V>(callable); + pQueue.add(task); + return task; + } + + /** + * adds an object to the physics space + * @param obj the PhysicsControl or Spatial with PhysicsControl to add + */ + public void add(Object obj) { + if (obj instanceof PhysicsControl) { + ((PhysicsControl) obj).setPhysicsSpace(this); + } else if (obj instanceof Spatial) { + Spatial node = (Spatial) obj; + PhysicsControl control = node.getControl(PhysicsControl.class); + control.setPhysicsSpace(this); + } else if (obj instanceof PhysicsCollisionObject) { + addCollisionObject((PhysicsCollisionObject) obj); + } else if (obj instanceof PhysicsJoint) { + addJoint((PhysicsJoint) obj); + } else { + throw (new UnsupportedOperationException("Cannot add this kind of object to the physics space.")); + } + } + + public void addCollisionObject(PhysicsCollisionObject obj) { + if (obj instanceof PhysicsGhostObject) { + addGhostObject((PhysicsGhostObject) obj); + } else if (obj instanceof PhysicsRigidBody) { + addRigidBody((PhysicsRigidBody) obj); + } else if (obj instanceof PhysicsVehicle) { + addRigidBody((PhysicsVehicle) obj); + } else if (obj instanceof PhysicsCharacter) { + addCharacter((PhysicsCharacter) obj); + } + } + + /** + * removes an object from the physics space + * @param obj the PhysicsControl or Spatial with PhysicsControl to remove + */ + public void remove(Object obj) { + if (obj instanceof PhysicsControl) { + ((PhysicsControl) obj).setPhysicsSpace(null); + } else if (obj instanceof Spatial) { + Spatial node = (Spatial) obj; + PhysicsControl control = node.getControl(PhysicsControl.class); + control.setPhysicsSpace(null); + } else if (obj instanceof PhysicsCollisionObject) { + removeCollisionObject((PhysicsCollisionObject) obj); + } else if (obj instanceof PhysicsJoint) { + removeJoint((PhysicsJoint) obj); + } else { + throw (new UnsupportedOperationException("Cannot remove this kind of object from the physics space.")); + } + } + + public void removeCollisionObject(PhysicsCollisionObject obj) { + if (obj instanceof PhysicsGhostObject) { + removeGhostObject((PhysicsGhostObject) obj); + } else if (obj instanceof PhysicsRigidBody) { + removeRigidBody((PhysicsRigidBody) obj); + } else if (obj instanceof PhysicsCharacter) { + removeCharacter((PhysicsCharacter) obj); + } + } + + /** + * adds all physics controls and joints in the given spatial node to the physics space + * (e.g. after loading from disk) - recursive if node + * @param spatial the rootnode containing the physics objects + */ + public void addAll(Spatial spatial) { + if (spatial.getControl(RigidBodyControl.class) != null) { + RigidBodyControl physicsNode = spatial.getControl(RigidBodyControl.class); + if (!physicsNodes.containsValue(physicsNode)) { + physicsNode.setPhysicsSpace(this); + } + //add joints + List<PhysicsJoint> joints = physicsNode.getJoints(); + for (Iterator<PhysicsJoint> it1 = joints.iterator(); it1.hasNext();) { + PhysicsJoint physicsJoint = it1.next(); + //add connected physicsnodes if they are not already added + if (!physicsNodes.containsValue(physicsJoint.getBodyA())) { + if (physicsJoint.getBodyA() instanceof PhysicsControl) { + add(physicsJoint.getBodyA()); + } else { + addRigidBody(physicsJoint.getBodyA()); + } + } + if (!physicsNodes.containsValue(physicsJoint.getBodyB())) { + if (physicsJoint.getBodyA() instanceof PhysicsControl) { + add(physicsJoint.getBodyB()); + } else { + addRigidBody(physicsJoint.getBodyB()); + } + } + if (!physicsJoints.contains(physicsJoint)) { + addJoint(physicsJoint); + } + } + } else if (spatial.getControl(PhysicsControl.class) != null) { + spatial.getControl(PhysicsControl.class).setPhysicsSpace(this); + } + //recursion + if (spatial instanceof Node) { + List<Spatial> children = ((Node) spatial).getChildren(); + for (Iterator<Spatial> it = children.iterator(); it.hasNext();) { + Spatial spat = it.next(); + addAll(spat); + } + } + } + + /** + * Removes all physics controls and joints in the given spatial from the physics space + * (e.g. before saving to disk) - recursive if node + * @param spatial the rootnode containing the physics objects + */ + public void removeAll(Spatial spatial) { + if (spatial.getControl(RigidBodyControl.class) != null) { + RigidBodyControl physicsNode = spatial.getControl(RigidBodyControl.class); + if (physicsNodes.containsValue(physicsNode)) { + physicsNode.setPhysicsSpace(null); + } + //remove joints + List<PhysicsJoint> joints = physicsNode.getJoints(); + for (Iterator<PhysicsJoint> it1 = joints.iterator(); it1.hasNext();) { + PhysicsJoint physicsJoint = it1.next(); + //add connected physicsnodes if they are not already added + if (physicsNodes.containsValue(physicsJoint.getBodyA())) { + if (physicsJoint.getBodyA() instanceof PhysicsControl) { + remove(physicsJoint.getBodyA()); + } else { + removeRigidBody(physicsJoint.getBodyA()); + } + } + if (physicsNodes.containsValue(physicsJoint.getBodyB())) { + if (physicsJoint.getBodyA() instanceof PhysicsControl) { + remove(physicsJoint.getBodyB()); + } else { + removeRigidBody(physicsJoint.getBodyB()); + } + } + if (physicsJoints.contains(physicsJoint)) { + removeJoint(physicsJoint); + } + } + } else if (spatial.getControl(PhysicsControl.class) != null) { + spatial.getControl(PhysicsControl.class).setPhysicsSpace(null); + } + //recursion + if (spatial instanceof Node) { + List<Spatial> children = ((Node) spatial).getChildren(); + for (Iterator<Spatial> it = children.iterator(); it.hasNext();) { + Spatial spat = it.next(); + removeAll(spat); + } + } + } + + private native void addCollisionObject(long space, long id); + + private native void removeCollisionObject(long space, long id); + + private native void addRigidBody(long space, long id); + + private native void removeRigidBody(long space, long id); + + private native void addCharacterObject(long space, long id); + + private native void removeCharacterObject(long space, long id); + + private native void addAction(long space, long id); + + private native void removeAction(long space, long id); + + private native void addVehicle(long space, long id); + + private native void removeVehicle(long space, long id); + + private native void addConstraint(long space, long id); + + private native void removeConstraint(long space, long id); + + private void addGhostObject(PhysicsGhostObject node) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding ghost object {0} to physics space.", Long.toHexString(node.getObjectId())); + addCollisionObject(physicsSpaceId, node.getObjectId()); + } + + private void removeGhostObject(PhysicsGhostObject node) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing ghost object {0} from physics space.", Long.toHexString(node.getObjectId())); + removeCollisionObject(physicsSpaceId, node.getObjectId()); + } + + private void addCharacter(PhysicsCharacter node) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding character {0} to physics space.", Long.toHexString(node.getObjectId())); + addCharacterObject(physicsSpaceId, node.getObjectId()); + addAction(physicsSpaceId, node.getControllerId()); +// dynamicsWorld.addCollisionObject(node.getObjectId(), CollisionFilterGroups.CHARACTER_FILTER, (short) (CollisionFilterGroups.STATIC_FILTER | CollisionFilterGroups.DEFAULT_FILTER)); +// dynamicsWorld.addAction(node.getControllerId()); + } + + private void removeCharacter(PhysicsCharacter node) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing character {0} from physics space.", Long.toHexString(node.getObjectId())); + removeAction(physicsSpaceId, node.getControllerId()); + removeCharacterObject(physicsSpaceId, node.getObjectId()); +// dynamicsWorld.removeAction(node.getControllerId()); +// dynamicsWorld.removeCollisionObject(node.getObjectId()); + } + + private void addRigidBody(PhysicsRigidBody node) { + physicsNodes.put(node.getObjectId(), node); + + //Workaround + //It seems that adding a Kinematic RigidBody to the dynamicWorld prevent it from being non kinematic again afterward. + //so we add it non kinematic, then set it kinematic again. + boolean kinematic = false; + if (node.isKinematic()) { + kinematic = true; + node.setKinematic(false); + } + addRigidBody(physicsSpaceId, node.getObjectId()); + if (kinematic) { + node.setKinematic(true); + } + + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding RigidBody {0} to physics space.", node.getObjectId()); + if (node instanceof PhysicsVehicle) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding vehicle constraint {0} to physics space.", Long.toHexString(((PhysicsVehicle) node).getVehicleId())); + addVehicle(physicsSpaceId, ((PhysicsVehicle) node).getVehicleId()); + } + } + + private void removeRigidBody(PhysicsRigidBody node) { + if (node instanceof PhysicsVehicle) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing vehicle constraint {0} from physics space.", Long.toHexString(((PhysicsVehicle) node).getVehicleId())); + removeVehicle(physicsSpaceId, ((PhysicsVehicle) node).getVehicleId()); + } + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing RigidBody {0} from physics space.", Long.toHexString(node.getObjectId())); + physicsNodes.remove(node.getObjectId()); + removeRigidBody(physicsSpaceId, node.getObjectId()); + } + + private void addJoint(PhysicsJoint joint) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Adding Joint {0} to physics space.", Long.toHexString(joint.getObjectId())); + physicsJoints.add(joint); + addConstraint(physicsSpaceId, joint.getObjectId()); +// dynamicsWorld.addConstraint(joint.getObjectId(), !joint.isCollisionBetweenLinkedBodys()); + } + + private void removeJoint(PhysicsJoint joint) { + Logger.getLogger(PhysicsSpace.class.getName()).log(Level.INFO, "Removing Joint {0} from physics space.", Long.toHexString(joint.getObjectId())); + physicsJoints.remove(joint); + removeConstraint(physicsSpaceId, joint.getObjectId()); +// dynamicsWorld.removeConstraint(joint.getObjectId()); + } + + /** + * Sets the gravity of the PhysicsSpace, set before adding physics objects! + * @param gravity + */ + public void setGravity(Vector3f gravity) { +// dynamicsWorld.setGravity(Converter.convert(gravity)); + setGravity(physicsSpaceId, gravity); + } + + private native void setGravity(long spaceId, Vector3f gravity); + +// /** +// * applies gravity value to all objects +// */ +// public void applyGravity() { +//// dynamicsWorld.applyGravity(); +// } +// +// /** +// * clears forces of all objects +// */ +// public void clearForces() { +//// dynamicsWorld.clearForces(); +// } +// + /** + * Adds the specified listener to the physics tick listeners. + * The listeners are called on each physics step, which is not necessarily + * each frame but is determined by the accuracy of the physics space. + * @param listener + */ + public void addTickListener(PhysicsTickListener listener) { + tickListeners.add(listener); + } + + public void removeTickListener(PhysicsTickListener listener) { + tickListeners.remove(listener); + } + + /** + * Adds a CollisionListener that will be informed about collision events + * @param listener the CollisionListener to add + */ + public void addCollisionListener(PhysicsCollisionListener listener) { + collisionListeners.add(listener); + } + + /** + * Removes a CollisionListener from the list + * @param listener the CollisionListener to remove + */ + public void removeCollisionListener(PhysicsCollisionListener listener) { + collisionListeners.remove(listener); + } + + /** + * Adds a listener for a specific collision group, such a listener can disable collisions when they happen.<br> + * There can be only one listener per collision group. + * @param listener + * @param collisionGroup + */ + public void addCollisionGroupListener(PhysicsCollisionGroupListener listener, int collisionGroup) { + collisionGroupListeners.put(collisionGroup, listener); + } + + public void removeCollisionGroupListener(int collisionGroup) { + collisionGroupListeners.remove(collisionGroup); + } + + /** + * Performs a ray collision test and returns the results as a list of PhysicsRayTestResults + */ + public List rayTest(Vector3f from, Vector3f to) { + List results = new LinkedList(); + rayTest(from, to, results); + return (List<PhysicsRayTestResult>) results; + } + + /** + * Performs a ray collision test and returns the results as a list of PhysicsRayTestResults + */ + public List<PhysicsRayTestResult> rayTest(Vector3f from, Vector3f to, List<PhysicsRayTestResult> results) { + results.clear(); + rayTest_native(from, to, physicsSpaceId, results); + return results; + } + + public native void rayTest_native(Vector3f from, Vector3f to, long physicsSpaceId, List<PhysicsRayTestResult> results); + +// private class InternalRayListener extends CollisionWorld.RayResultCallback { +// +// private List<PhysicsRayTestResult> results; +// +// public InternalRayListener(List<PhysicsRayTestResult> results) { +// this.results = results; +// } +// +// @Override +// public float addSingleResult(LocalRayResult lrr, boolean bln) { +// PhysicsCollisionObject obj = (PhysicsCollisionObject) lrr.collisionObject.getUserPointer(); +// results.add(new PhysicsRayTestResult(obj, Converter.convert(lrr.hitNormalLocal), lrr.hitFraction, bln)); +// return lrr.hitFraction; +// } +// } + /** + * Performs a sweep collision test and returns the results as a list of PhysicsSweepTestResults<br/> + * You have to use different Transforms for start and end (at least distance > 0.4f). + * SweepTest will not see a collision if it starts INSIDE an object and is moving AWAY from its center. + */ + public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end) { + List<PhysicsSweepTestResult> results = new LinkedList<PhysicsSweepTestResult>(); +// if (!(shape.getCShape() instanceof ConvexShape)) { +// Logger.getLogger(PhysicsSpace.class.getName()).log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!"); +// return results; +// } +// dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results)); + return results; + + } + + /** + * Performs a sweep collision test and returns the results as a list of PhysicsSweepTestResults<br/> + * You have to use different Transforms for start and end (at least distance > 0.4f). + * SweepTest will not see a collision if it starts INSIDE an object and is moving AWAY from its center. + */ + public List<PhysicsSweepTestResult> sweepTest(CollisionShape shape, Transform start, Transform end, List<PhysicsSweepTestResult> results) { + results.clear(); +// if (!(shape.getCShape() instanceof ConvexShape)) { +// Logger.getLogger(PhysicsSpace.class.getName()).log(Level.WARNING, "Trying to sweep test with incompatible mesh shape!"); +// return results; +// } +// dynamicsWorld.convexSweepTest((ConvexShape) shape.getCShape(), Converter.convert(start, sweepTrans1), Converter.convert(end, sweepTrans2), new InternalSweepListener(results)); + return results; + } + +// private class InternalSweepListener extends CollisionWorld.ConvexResultCallback { +// +// private List<PhysicsSweepTestResult> results; +// +// public InternalSweepListener(List<PhysicsSweepTestResult> results) { +// this.results = results; +// } +// +// @Override +// public float addSingleResult(LocalConvexResult lcr, boolean bln) { +// PhysicsCollisionObject obj = (PhysicsCollisionObject) lcr.hitCollisionObject.getUserPointer(); +// results.add(new PhysicsSweepTestResult(obj, Converter.convert(lcr.hitNormalLocal), lcr.hitFraction, bln)); +// return lcr.hitFraction; +// } +// } + /** + * destroys the current PhysicsSpace so that a new one can be created + */ + public void destroy() { + physicsNodes.clear(); + physicsJoints.clear(); + +// dynamicsWorld.destroy(); +// dynamicsWorld = null; + } + + /** + // * used internally + // * @return the dynamicsWorld + // */ + public long getSpaceId() { + return physicsSpaceId; + } + + public BroadphaseType getBroadphaseType() { + return broadphaseType; + } + + public void setBroadphaseType(BroadphaseType broadphaseType) { + this.broadphaseType = broadphaseType; + } + + /** + * Sets the maximum amount of extra steps that will be used to step the physics + * when the fps is below the physics fps. Doing this maintains determinism in physics. + * For example a maximum number of 2 can compensate for framerates as low as 30fps + * when the physics has the default accuracy of 60 fps. Note that setting this + * value too high can make the physics drive down its own fps in case its overloaded. + * @param steps The maximum number of extra steps, default is 4. + */ + public void setMaxSubSteps(int steps) { + maxSubSteps = steps; + } + + /** + * get the current accuracy of the physics computation + * @return the current accuracy + */ + public float getAccuracy() { + return accuracy; + } + + /** + * sets the accuracy of the physics computation, default=1/60s<br> + * @param accuracy + */ + public void setAccuracy(float accuracy) { + this.accuracy = accuracy; + } + + public Vector3f getWorldMin() { + return worldMin; + } + + /** + * only applies for AXIS_SWEEP broadphase + * @param worldMin + */ + public void setWorldMin(Vector3f worldMin) { + this.worldMin.set(worldMin); + } + + public Vector3f getWorldMax() { + return worldMax; + } + + /** + * only applies for AXIS_SWEEP broadphase + * @param worldMax + */ + public void setWorldMax(Vector3f worldMax) { + this.worldMax.set(worldMax); + } + + /** + * Enable debug display for physics + * @param manager AssetManager to use to create debug materials + */ + public void enableDebug(AssetManager manager) { + debugManager = manager; + } + + /** + * Disable debug display + */ + public void disableDebug() { + debugManager = null; + } + + public AssetManager getDebugManager() { + return debugManager; + } + + public static native void initNativePhysics(); + + /** + * interface with Broadphase types + */ + public enum BroadphaseType { + + /** + * basic Broadphase + */ + SIMPLE, + /** + * better Broadphase, needs worldBounds , max Object number = 16384 + */ + AXIS_SWEEP_3, + /** + * better Broadphase, needs worldBounds , max Object number = 65536 + */ + AXIS_SWEEP_3_32, + /** + * Broadphase allowing quicker adding/removing of physics objects + */ + DBVT; + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + Logger.getLogger(this.getClass().getName()).log(Level.INFO, "Finalizing PhysicsSpace {0}", Long.toHexString(physicsSpaceId)); + finalizeNative(physicsSpaceId); + } + + private native void finalizeNative(long objectId); +} |