diff --git a/engine/src/bullet-common/com/jme3/bullet/BulletAppState.java b/engine/src/bullet-common/com/jme3/bullet/BulletAppState.java
index 8fd167e18..61165c3dd 100644
--- a/engine/src/bullet-common/com/jme3/bullet/BulletAppState.java
+++ b/engine/src/bullet-common/com/jme3/bullet/BulletAppState.java
@@ -35,6 +35,7 @@ import com.jme3.app.Application;
import com.jme3.app.state.AppState;
import com.jme3.app.state.AppStateManager;
import com.jme3.bullet.PhysicsSpace.BroadphaseType;
+import com.jme3.bullet.debug.BulletDebugAppState;
import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager;
import java.util.concurrent.*;
@@ -43,6 +44,7 @@ import java.util.logging.Logger;
/**
* BulletAppState
allows using bullet physics in an Application.
+ *
* @author normenhansen
*/
public class BulletAppState implements AppState, PhysicsTickListener {
@@ -56,31 +58,38 @@ public class BulletAppState implements AppState, PhysicsTickListener {
protected BroadphaseType broadphaseType = BroadphaseType.DBVT;
protected Vector3f worldMin = new Vector3f(-10000f, -10000f, -10000f);
protected Vector3f worldMax = new Vector3f(10000f, 10000f, 10000f);
- private float speed = 1;
+ protected float speed = 1;
protected boolean active = true;
+ protected boolean debugEnabled = false;
+ protected BulletDebugAppState debugAppState;
protected float tpf;
protected Future physicsFuture;
/**
- * Creates a new BulletAppState running a PhysicsSpace for physics simulation,
- * use getStateManager().addState(bulletAppState) to enable physics for an Application.
+ * Creates a new BulletAppState running a PhysicsSpace for physics
+ * simulation, use getStateManager().addState(bulletAppState) to enable
+ * physics for an Application.
*/
public BulletAppState() {
}
/**
- * Creates a new BulletAppState running a PhysicsSpace for physics simulation,
- * use getStateManager().addState(bulletAppState) to enable physics for an Application.
- * @param broadphaseType The type of broadphase collision detection, BroadphaseType.DVBT is the default
+ * Creates a new BulletAppState running a PhysicsSpace for physics
+ * simulation, use getStateManager().addState(bulletAppState) to enable
+ * physics for an Application.
+ *
+ * @param broadphaseType The type of broadphase collision detection,
+ * BroadphaseType.DVBT is the default
*/
public BulletAppState(BroadphaseType broadphaseType) {
this(new Vector3f(-10000f, -10000f, -10000f), new Vector3f(10000f, 10000f, 10000f), broadphaseType);
}
/**
- * Creates a new BulletAppState running a PhysicsSpace for physics simulation,
- * use getStateManager().addState(bulletAppState) to enable physics for an Application.
- * An AxisSweep broadphase is used.
+ * Creates a new BulletAppState running a PhysicsSpace for physics
+ * simulation, use getStateManager().addState(bulletAppState) to enable
+ * physics for an Application. An AxisSweep broadphase is used.
+ *
* @param worldMin The minimum world extent
* @param worldMax The maximum world extent
*/
@@ -101,7 +110,6 @@ public class BulletAppState implements AppState, PhysicsTickListener {
executor = new ScheduledThreadPoolExecutor(1);
final BulletAppState app = this;
Callable call = new Callable() {
-
public Boolean call() throws Exception {
detachedPhysicsLastUpdate = System.currentTimeMillis();
pSpace = new PhysicsSpace(worldMin, worldMax, broadphaseType);
@@ -120,7 +128,6 @@ public class BulletAppState implements AppState, PhysicsTickListener {
}
}
private Callable parallelPhysicsUpdate = new Callable() {
-
public Boolean call() throws Exception {
pSpace.update(tpf * getSpeed());
return true;
@@ -128,7 +135,6 @@ public class BulletAppState implements AppState, PhysicsTickListener {
};
long detachedPhysicsLastUpdate = 0;
private Callable detachedPhysicsUpdate = new Callable() {
-
public Boolean call() throws Exception {
pSpace.update(getPhysicsSpace().getAccuracy() * getSpeed());
pSpace.distributeEvents();
@@ -144,8 +150,8 @@ public class BulletAppState implements AppState, PhysicsTickListener {
}
/**
- * The physics system is started automatically on attaching, if you want to start it
- * before for some reason, you can use this method.
+ * The physics system is started automatically on attaching, if you want to
+ * start it before for some reason, you can use this method.
*/
public void startPhysics() {
//start physics thread(pool)
@@ -165,21 +171,30 @@ public class BulletAppState implements AppState, PhysicsTickListener {
if (!initialized) {
startPhysics();
}
+ this.stateManager = stateManager;
initialized = true;
}
public boolean isInitialized() {
return initialized;
}
-
+
public void setEnabled(boolean enabled) {
this.active = enabled;
}
-
+
public boolean isEnabled() {
return active;
}
+ public void setDebugEnabled(boolean debugEnabled) {
+ this.debugEnabled = debugEnabled;
+ }
+
+ public boolean isDebugEnabled() {
+ return debugEnabled;
+ }
+
public void stateAttached(AppStateManager stateManager) {
if (!initialized) {
startPhysics();
@@ -187,17 +202,36 @@ public class BulletAppState implements AppState, PhysicsTickListener {
if (threadingType == ThreadingType.PARALLEL) {
PhysicsSpace.setLocalThreadPhysicsSpace(pSpace);
}
+ if (debugEnabled) {
+ debugAppState = new BulletDebugAppState(pSpace);
+ stateManager.attach(debugAppState);
+ }
}
public void stateDetached(AppStateManager stateManager) {
}
public void update(float tpf) {
+ if (debugEnabled && debugAppState == null) {
+ debugAppState = new BulletDebugAppState(pSpace);
+ stateManager.attach(debugAppState);
+ pSpace.enableDebug(app.getAssetManager());
+ } else if (!debugEnabled && debugAppState != null) {
+ stateManager.detach(debugAppState);
+ debugAppState = null;
+ pSpace.enableDebug(null);
+ }
+ //TODO: remove when deprecation of PhysicsSpace.enableDebug is through
+ if (pSpace.getDebugManager() != null && !debugEnabled) {
+ debugEnabled = true;
+ } else if (pSpace.getDebugManager() == null && debugEnabled) {
+ debugEnabled = false;
+ }
if (!active) {
return;
}
// if (threadingType != ThreadingType.DETACHED) {
- pSpace.distributeEvents();
+ pSpace.distributeEvents();
// }
this.tpf = tpf;
}
@@ -228,6 +262,10 @@ public class BulletAppState implements AppState, PhysicsTickListener {
}
public void cleanup() {
+ if (debugAppState != null) {
+ stateManager.detach(debugAppState);
+ debugAppState = null;
+ }
if (executor != null) {
executor.shutdown();
executor = null;
@@ -245,6 +283,7 @@ public class BulletAppState implements AppState, PhysicsTickListener {
/**
* Use before attaching state
+ *
* @param threadingType the threadingType to set
*/
public void setThreadingType(ThreadingType threadingType) {
@@ -289,13 +328,14 @@ public class BulletAppState implements AppState, PhysicsTickListener {
public enum ThreadingType {
/**
- * Default mode; user update, physics update and rendering happen sequentially (single threaded)
+ * Default mode; user update, physics update and rendering happen
+ * sequentially (single threaded)
*/
SEQUENTIAL,
/**
- * Parallel threaded mode; physics update and rendering are executed in parallel, update order is kept.
- * Multiple BulletAppStates will execute in parallel in this mode.
+ * Parallel threaded mode; physics update and rendering are executed in
+ * parallel, update order is kept.
Multiple BulletAppStates will
+ * execute in parallel in this mode.
*/
- PARALLEL,
- }
+ PARALLEL,}
}
diff --git a/engine/src/bullet-common/com/jme3/bullet/debug/AbstractPhysicsDebugControl.java b/engine/src/bullet-common/com/jme3/bullet/debug/AbstractPhysicsDebugControl.java
new file mode 100644
index 000000000..75e7bbe45
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/debug/AbstractPhysicsDebugControl.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2009-2012 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.debug;
+
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.control.AbstractControl;
+
+/**
+ *
+ * @author normenhansen
+ */
+public abstract class AbstractPhysicsDebugControl extends AbstractControl {
+
+ private final Quaternion tmp_inverseWorldRotation = new Quaternion();
+ protected final BulletDebugAppState debugAppState;
+
+ public AbstractPhysicsDebugControl(BulletDebugAppState debugAppState) {
+ this.debugAppState = debugAppState;
+ }
+
+ /**
+ * This is called on the physics thread for debug controls
+ */
+ @Override
+ protected abstract void controlUpdate(float tpf);
+
+ protected void applyPhysicsTransform(Vector3f worldLocation, Quaternion worldRotation) {
+ applyPhysicsTransform(worldLocation, worldRotation, this.spatial);
+ }
+
+ protected void applyPhysicsTransform(Vector3f worldLocation, Quaternion worldRotation, Spatial spatial) {
+ if (spatial != null) {
+ Vector3f localLocation = spatial.getLocalTranslation();
+ Quaternion localRotationQuat = spatial.getLocalRotation();
+ if (spatial.getParent() != null) {
+ localLocation.set(worldLocation).subtractLocal(spatial.getParent().getWorldTranslation());
+ localLocation.divideLocal(spatial.getParent().getWorldScale());
+ tmp_inverseWorldRotation.set(spatial.getParent().getWorldRotation()).inverseLocal().multLocal(localLocation);
+ localRotationQuat.set(worldRotation);
+ tmp_inverseWorldRotation.set(spatial.getParent().getWorldRotation()).inverseLocal().mult(localRotationQuat, localRotationQuat);
+ spatial.setLocalTranslation(localLocation);
+ spatial.setLocalRotation(localRotationQuat);
+ } else {
+ spatial.setLocalTranslation(worldLocation);
+ spatial.setLocalRotation(worldRotation);
+ }
+ }
+
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/debug/BulletCharacterDebugControl.java b/engine/src/bullet-common/com/jme3/bullet/debug/BulletCharacterDebugControl.java
new file mode 100644
index 000000000..de5b03a3f
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/debug/BulletCharacterDebugControl.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2009-2012 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.debug;
+
+import com.jme3.bullet.objects.PhysicsCharacter;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class BulletCharacterDebugControl extends AbstractPhysicsDebugControl {
+
+ protected final PhysicsCharacter body;
+ protected final Geometry geom;
+ protected final Vector3f location = new Vector3f();
+ protected final Quaternion rotation = new Quaternion();
+
+ public BulletCharacterDebugControl(BulletDebugAppState debugAppState, PhysicsCharacter body) {
+ super(debugAppState);
+ this.body = body;
+ this.geom = new Geometry(body.toString());
+ geom.setMaterial(debugAppState.DEBUG_PINK);
+ }
+
+ @Override
+ public void setSpatial(Spatial spatial) {
+ if (spatial != null && spatial instanceof Node) {
+ Node node = (Node) spatial;
+ node.attachChild(geom);
+ } else if (spatial == null && this.spatial != null) {
+ Node node = (Node) this.spatial;
+ node.detachChild(geom);
+ }
+ super.setSpatial(spatial);
+ }
+
+ @Override
+ protected void controlUpdate(float tpf) {
+ Mesh mesh = debugAppState.getShapeBuffer().getShapeMesh(body.getCollisionShape());
+ if (mesh != null) {
+ if (geom.getMesh() != mesh) {
+ geom.setMesh(mesh);
+ }
+ } else {
+ if (geom.getMesh() != BulletDebugAppState.CollisionShapeBuffer.NO_MESH) {
+ geom.setMesh(BulletDebugAppState.CollisionShapeBuffer.NO_MESH);
+ }
+ }
+ applyPhysicsTransform(body.getPhysicsLocation(location), Quaternion.IDENTITY);
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/debug/BulletDebugAppState.java b/engine/src/bullet-common/com/jme3/bullet/debug/BulletDebugAppState.java
new file mode 100644
index 000000000..5294d7b9e
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/debug/BulletDebugAppState.java
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 2009-2012 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.debug;
+
+import com.jme3.app.Application;
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.app.state.AppStateManager;
+import com.jme3.asset.AssetManager;
+import com.jme3.bullet.PhysicsSpace;
+import com.jme3.bullet.collision.shapes.CollisionShape;
+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.bullet.util.DebugShapeFactory;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class BulletDebugAppState extends AbstractAppState {
+
+ protected static final Logger logger = Logger.getLogger(BulletDebugAppState.class.getName());
+ protected final PhysicsSpace space;
+ protected final CollisionShapeBuffer shapeBuffer = new CollisionShapeBuffer();
+ protected final ArrowBuffer arrowBuffer = new ArrowBuffer();
+ protected final Node physicsDebugRootNode = new Node("Physics Debug Root Node");
+ protected ViewPort viewPort;
+ protected RenderManager rm;
+ public Material DEBUG_BLUE;
+ public Material DEBUG_RED;
+ public Material DEBUG_GREEN;
+ public Material DEBUG_YELLOW;
+ public Material DEBUG_MAGENTA;
+ public Material DEBUG_PINK;
+ protected HashMap bodies = new HashMap();
+ protected HashMap joints = new HashMap();
+ protected HashMap ghosts = new HashMap();
+ protected HashMap characters = new HashMap();
+ protected HashMap vehicles = new HashMap();
+
+ public BulletDebugAppState(PhysicsSpace space) {
+ this.space = space;
+ }
+
+ @Override
+ public void initialize(AppStateManager stateManager, Application app) {
+ super.initialize(stateManager, app);
+ this.rm = app.getRenderManager();
+ setupMaterials(app);
+ physicsDebugRootNode.setCullHint(Spatial.CullHint.Never);
+ viewPort = rm.createMainView("Physics Debug Overlay", app.getCamera());
+ viewPort.setClearFlags(false, true, false);
+ viewPort.attachScene(physicsDebugRootNode);
+ }
+
+ @Override
+ public void cleanup() {
+ rm.removeMainView(viewPort);
+ super.cleanup();
+ }
+
+ @Override
+ public void update(float tpf) {
+ super.update(tpf);
+ //update all object links
+ updateRigidBodies();
+ updateGhosts();
+ updateCharacters();
+ updateJoints();
+ updateVehicles();
+ //update our debug root node
+ physicsDebugRootNode.updateLogicalState(tpf);
+ physicsDebugRootNode.updateGeometricState();
+ //reset shapes -> this removes all meshes for shapes that were not used this update cycle
+ shapeBuffer.resetShapes();
+ //reset arrows -> this makes arrow meshes available for the next update cycle
+ arrowBuffer.resetArrows();
+ }
+
+ @Override
+ public void render(RenderManager rm) {
+ super.render(rm);
+ if (viewPort != null) {
+ rm.renderScene(physicsDebugRootNode, viewPort);
+ }
+ }
+
+ private void setupMaterials(Application app) {
+ AssetManager manager = app.getAssetManager();
+ DEBUG_BLUE = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ DEBUG_BLUE.getAdditionalRenderState().setWireframe(true);
+ DEBUG_BLUE.setColor("Color", ColorRGBA.Blue);
+ DEBUG_GREEN = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ DEBUG_GREEN.getAdditionalRenderState().setWireframe(true);
+ DEBUG_GREEN.setColor("Color", ColorRGBA.Green);
+ DEBUG_RED = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ DEBUG_RED.getAdditionalRenderState().setWireframe(true);
+ DEBUG_RED.setColor("Color", ColorRGBA.Red);
+ DEBUG_YELLOW = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ DEBUG_YELLOW.getAdditionalRenderState().setWireframe(true);
+ DEBUG_YELLOW.setColor("Color", ColorRGBA.Yellow);
+ DEBUG_MAGENTA = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ DEBUG_MAGENTA.getAdditionalRenderState().setWireframe(true);
+ DEBUG_MAGENTA.setColor("Color", ColorRGBA.Magenta);
+ DEBUG_PINK = new Material(manager, "Common/MatDefs/Misc/Unshaded.j3md");
+ DEBUG_PINK.getAdditionalRenderState().setWireframe(true);
+ DEBUG_PINK.setColor("Color", ColorRGBA.Pink);
+ }
+
+ private void updateRigidBodies() {
+ HashMap oldObjects = bodies;
+ bodies = new HashMap();
+ Collection current = space.getRigidBodyList();
+ //create new map
+ for (Iterator it = current.iterator(); it.hasNext();) {
+ PhysicsRigidBody physicsObject = it.next();
+ //copy existing spatials
+ if (oldObjects.containsKey(physicsObject)) {
+ Spatial spat = oldObjects.get(physicsObject);
+ bodies.put(physicsObject, spat);
+ oldObjects.remove(physicsObject);
+ } else {
+ logger.log(Level.FINE, "Create new debug RigidBody");
+ //create new spatial
+ Node node = new Node(physicsObject.toString());
+ node.addControl(new BulletRigidBodyDebugControl(this, physicsObject));
+ bodies.put(physicsObject, node);
+ physicsDebugRootNode.attachChild(node);
+ }
+ }
+ //remove leftover spatials
+ for (Map.Entry entry : oldObjects.entrySet()) {
+ PhysicsRigidBody object = entry.getKey();
+ Spatial spatial = entry.getValue();
+ spatial.removeFromParent();
+ }
+ }
+
+ private void updateJoints() {
+ HashMap oldObjects = joints;
+ joints = new HashMap();
+ Collection current = space.getJointList();
+ //create new map
+ for (Iterator it = current.iterator(); it.hasNext();) {
+ PhysicsJoint physicsObject = it.next();
+ //copy existing spatials
+ if (oldObjects.containsKey(physicsObject)) {
+ Spatial spat = oldObjects.get(physicsObject);
+ joints.put(physicsObject, spat);
+ oldObjects.remove(physicsObject);
+ } else {
+ logger.log(Level.FINE, "Create new debug Joint");
+ //create new spatial
+ Node node = new Node(physicsObject.toString());
+ node.addControl(new BulletJointDebugControl(this, physicsObject));
+ joints.put(physicsObject, node);
+ physicsDebugRootNode.attachChild(node);
+ }
+ }
+ //remove leftover spatials
+ for (Map.Entry entry : oldObjects.entrySet()) {
+ PhysicsJoint object = entry.getKey();
+ Spatial spatial = entry.getValue();
+ spatial.removeFromParent();
+ }
+ }
+
+ private void updateGhosts() {
+ HashMap oldObjects = ghosts;
+ ghosts = new HashMap();
+ Collection current = space.getGhostObjectList();
+ //create new map
+ for (Iterator it = current.iterator(); it.hasNext();) {
+ PhysicsGhostObject physicsObject = it.next();
+ //copy existing spatials
+ if (oldObjects.containsKey(physicsObject)) {
+ Spatial spat = oldObjects.get(physicsObject);
+ ghosts.put(physicsObject, spat);
+ oldObjects.remove(physicsObject);
+ } else {
+ logger.log(Level.FINE, "Create new debug GhostObject");
+ //create new spatial
+ Node node = new Node(physicsObject.toString());
+ node.addControl(new BulletGhostObjectDebugControl(this, physicsObject));
+ ghosts.put(physicsObject, node);
+ physicsDebugRootNode.attachChild(node);
+ }
+ }
+ //remove leftover spatials
+ for (Map.Entry entry : oldObjects.entrySet()) {
+ PhysicsGhostObject object = entry.getKey();
+ Spatial spatial = entry.getValue();
+ spatial.removeFromParent();
+ }
+ }
+
+ private void updateCharacters() {
+ HashMap oldObjects = characters;
+ characters = new HashMap();
+ Collection current = space.getCharacterList();
+ //create new map
+ for (Iterator it = current.iterator(); it.hasNext();) {
+ PhysicsCharacter physicsObject = it.next();
+ //copy existing spatials
+ if (oldObjects.containsKey(physicsObject)) {
+ Spatial spat = oldObjects.get(physicsObject);
+ characters.put(physicsObject, spat);
+ oldObjects.remove(physicsObject);
+ } else {
+ logger.log(Level.FINE, "Create new debug Character");
+ //create new spatial
+ Node node = new Node(physicsObject.toString());
+ node.addControl(new BulletCharacterDebugControl(this, physicsObject));
+ characters.put(physicsObject, node);
+ physicsDebugRootNode.attachChild(node);
+ }
+ }
+ //remove leftover spatials
+ for (Map.Entry entry : oldObjects.entrySet()) {
+ PhysicsCharacter object = entry.getKey();
+ Spatial spatial = entry.getValue();
+ spatial.removeFromParent();
+ }
+ }
+
+ private void updateVehicles() {
+ HashMap oldObjects = vehicles;
+ vehicles = new HashMap();
+ Collection current = space.getVehicleList();
+ //create new map
+ for (Iterator it = current.iterator(); it.hasNext();) {
+ PhysicsVehicle physicsObject = it.next();
+ //copy existing spatials
+ if (oldObjects.containsKey(physicsObject)) {
+ Spatial spat = oldObjects.get(physicsObject);
+ vehicles.put(physicsObject, spat);
+ oldObjects.remove(physicsObject);
+ } else {
+ logger.log(Level.FINE, "Create new debug Vehicle");
+ //create new spatial
+ Node node = new Node(physicsObject.toString());
+ node.addControl(new BulletVehicleDebugControl(this, physicsObject));
+ vehicles.put(physicsObject, node);
+ physicsDebugRootNode.attachChild(node);
+ }
+ }
+ //remove leftover spatials
+ for (Map.Entry entry : oldObjects.entrySet()) {
+ PhysicsVehicle object = entry.getKey();
+ Spatial spatial = entry.getValue();
+ spatial.removeFromParent();
+ }
+ }
+
+ public ArrowBuffer getArrowBuffer() {
+ return arrowBuffer;
+ }
+
+ public CollisionShapeBuffer getShapeBuffer() {
+ return shapeBuffer;
+ }
+
+ public static class CollisionShapeBuffer {
+
+ public static final Mesh NO_MESH = new Arrow(new Vector3f(0, 0, 0));
+ private HashMap shapes = new HashMap();
+ private HashMap usedShapes = new HashMap();
+
+ public void resetShapes() {
+ shapes = usedShapes;
+ usedShapes = new HashMap();
+ }
+
+ public Mesh getShapeMesh(CollisionShape shape) {
+ if (shape == null) {
+ return null;
+ }
+ Mesh mesh = shapes.get(shape);
+ if (mesh == null) {
+ logger.log(Level.FINE, "Create new debug MESH");
+ mesh = DebugShapeFactory.getDebugMesh(shape);
+ shapes.put(shape, mesh);
+ }
+ usedShapes.put(shape, mesh);
+ return mesh;
+ }
+ }
+
+ public static class ArrowBuffer {
+
+ private final Queue arrows = new ConcurrentLinkedQueue();
+ private final Queue usedArrows = new ConcurrentLinkedQueue();
+
+ public void resetArrows() {
+ arrows.addAll(usedArrows);
+ }
+
+ public Arrow getArrow() {
+ return getArrow(Vector3f.UNIT_Y);
+ }
+
+ public Arrow getArrow(Vector3f extent) {
+ Arrow arrow = arrows.poll();
+ if (arrow == null) {
+ arrow = new Arrow(extent);
+ } else {
+ arrow.setArrowExtent(extent);
+ }
+ usedArrows.add(arrow);
+ return arrow;
+ }
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/debug/BulletGhostObjectDebugControl.java b/engine/src/bullet-common/com/jme3/bullet/debug/BulletGhostObjectDebugControl.java
new file mode 100644
index 000000000..6227714e1
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/debug/BulletGhostObjectDebugControl.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2009-2012 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.debug;
+
+import com.jme3.bullet.objects.PhysicsGhostObject;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class BulletGhostObjectDebugControl extends AbstractPhysicsDebugControl{
+ protected final PhysicsGhostObject body;
+ protected final Geometry geom;
+ protected final Vector3f location = new Vector3f();
+ protected final Quaternion rotation = new Quaternion();
+
+ public BulletGhostObjectDebugControl(BulletDebugAppState debugAppState, PhysicsGhostObject body) {
+ super(debugAppState);
+ this.body = body;
+ this.geom = new Geometry(body.toString());
+ geom.setMaterial(debugAppState.DEBUG_YELLOW);
+ }
+
+ @Override
+ public void setSpatial(Spatial spatial) {
+ if (spatial != null && spatial instanceof Node) {
+ Node node = (Node) spatial;
+ node.attachChild(geom);
+ } else if (spatial == null && this.spatial != null) {
+ Node node = (Node) this.spatial;
+ node.detachChild(geom);
+ }
+ super.setSpatial(spatial);
+ }
+
+ @Override
+ protected void controlUpdate(float tpf) {
+ Mesh mesh = debugAppState.getShapeBuffer().getShapeMesh(body.getCollisionShape());
+ if (mesh != null) {
+ if (geom.getMesh() != mesh) {
+ geom.setMesh(mesh);
+ }
+ } else {
+ if (geom.getMesh() != BulletDebugAppState.CollisionShapeBuffer.NO_MESH) {
+ geom.setMesh(BulletDebugAppState.CollisionShapeBuffer.NO_MESH);
+ }
+ }
+ applyPhysicsTransform(body.getPhysicsLocation(location), Quaternion.IDENTITY);
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/debug/BulletJointDebugControl.java b/engine/src/bullet-common/com/jme3/bullet/debug/BulletJointDebugControl.java
new file mode 100644
index 000000000..571452811
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/debug/BulletJointDebugControl.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2009-2012 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.debug;
+
+import com.jme3.bullet.joints.PhysicsJoint;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class BulletJointDebugControl extends AbstractPhysicsDebugControl {
+
+ protected final PhysicsJoint body;
+ protected final Geometry geomA;
+ protected final Arrow arrowA;
+ protected final Geometry geomB;
+ protected final Arrow arrowB;
+ protected final Transform a = new Transform(new Vector3f(), new Quaternion());
+ protected final Transform b = new Transform(new Vector3f(), new Quaternion());
+ protected final Vector3f offA = new Vector3f();
+ protected final Vector3f offB = new Vector3f();
+
+ public BulletJointDebugControl(BulletDebugAppState debugAppState, PhysicsJoint body) {
+ super(debugAppState);
+ this.body = body;
+ this.geomA = new Geometry(body.toString());
+ arrowA = new Arrow(Vector3f.ZERO);
+ geomA.setMesh(arrowA);
+ geomA.setMaterial(debugAppState.DEBUG_GREEN);
+ this.geomB = new Geometry(body.toString());
+ arrowB = new Arrow(Vector3f.ZERO);
+ geomB.setMesh(arrowB);
+ geomB.setMaterial(debugAppState.DEBUG_GREEN);
+ }
+
+ @Override
+ public void setSpatial(Spatial spatial) {
+ if (spatial != null && spatial instanceof Node) {
+ Node node = (Node) spatial;
+ node.attachChild(geomA);
+ node.attachChild(geomB);
+ } else if (spatial == null && this.spatial != null) {
+ Node node = (Node) this.spatial;
+ node.detachChild(geomA);
+ node.detachChild(geomB);
+ }
+ super.setSpatial(spatial);
+ }
+
+ @Override
+ protected void controlUpdate(float tpf) {
+ body.getBodyA().getPhysicsLocation(a.getTranslation());
+ body.getBodyA().getPhysicsRotation(a.getRotation());
+
+ body.getBodyB().getPhysicsLocation(b.getTranslation());
+ body.getBodyB().getPhysicsRotation(b.getRotation());
+
+ geomA.setLocalTransform(a);
+ geomB.setLocalTransform(b);
+
+ arrowA.setArrowExtent(body.getPivotA());
+ arrowB.setArrowExtent(body.getPivotB());
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/debug/BulletRigidBodyDebugControl.java b/engine/src/bullet-common/com/jme3/bullet/debug/BulletRigidBodyDebugControl.java
new file mode 100644
index 000000000..b82d1e9fc
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/debug/BulletRigidBodyDebugControl.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009-2012 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.debug;
+
+import com.jme3.bullet.debug.BulletDebugAppState.CollisionShapeBuffer;
+import com.jme3.bullet.objects.PhysicsRigidBody;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class BulletRigidBodyDebugControl extends AbstractPhysicsDebugControl {
+
+ private static final Logger logger = Logger.getLogger(BulletRigidBodyDebugControl.class.getName());
+ protected final PhysicsRigidBody body;
+ protected final Geometry geom;
+ protected final Vector3f location = new Vector3f();
+ protected final Quaternion rotation = new Quaternion();
+
+ public BulletRigidBodyDebugControl(BulletDebugAppState debugAppState, PhysicsRigidBody body) {
+ super(debugAppState);
+ this.body = body;
+ this.geom = new Geometry(body.toString());
+ geom.setMaterial(debugAppState.DEBUG_BLUE);
+ }
+
+ @Override
+ public void setSpatial(Spatial spatial) {
+ if (spatial != null && spatial instanceof Node) {
+ Node node = (Node) spatial;
+ node.attachChild(geom);
+ } else if (spatial == null && this.spatial != null) {
+ Node node = (Node) this.spatial;
+ node.detachChild(geom);
+ }
+ super.setSpatial(spatial);
+ }
+
+ @Override
+ protected void controlUpdate(float tpf) {
+ Mesh mesh = debugAppState.getShapeBuffer().getShapeMesh(body.getCollisionShape());
+ if (mesh != null) {
+ if (geom.getMesh() != mesh) {
+ geom.setMesh(mesh);
+ }
+ } else {
+ if (geom.getMesh() != CollisionShapeBuffer.NO_MESH) {
+ geom.setMesh(CollisionShapeBuffer.NO_MESH);
+ }
+ }
+ applyPhysicsTransform(body.getPhysicsLocation(location), body.getPhysicsRotation(rotation));
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+ }
+}
diff --git a/engine/src/bullet-common/com/jme3/bullet/debug/BulletVehicleDebugControl.java b/engine/src/bullet-common/com/jme3/bullet/debug/BulletVehicleDebugControl.java
new file mode 100644
index 000000000..9b1458c2c
--- /dev/null
+++ b/engine/src/bullet-common/com/jme3/bullet/debug/BulletVehicleDebugControl.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2009-2012 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.debug;
+
+import com.jme3.bullet.objects.PhysicsVehicle;
+import com.jme3.bullet.objects.VehicleWheel;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.debug.Arrow;
+
+/**
+ *
+ * @author normenhansen
+ */
+public class BulletVehicleDebugControl extends AbstractPhysicsDebugControl {
+
+ protected final PhysicsVehicle body;
+ protected final Node suspensionNode;
+ protected final Vector3f location = new Vector3f();
+ protected final Quaternion rotation = new Quaternion();
+
+ public BulletVehicleDebugControl(BulletDebugAppState debugAppState, PhysicsVehicle body) {
+ super(debugAppState);
+ this.body = body;
+ suspensionNode = new Node("Suspension");
+ createVehicle();
+ }
+
+ @Override
+ public void setSpatial(Spatial spatial) {
+ if (spatial != null && spatial instanceof Node) {
+ Node node = (Node) spatial;
+ node.attachChild(suspensionNode);
+ } else if (spatial == null && this.spatial != null) {
+ Node node = (Node) this.spatial;
+ node.detachChild(suspensionNode);
+ }
+ super.setSpatial(spatial);
+ }
+
+ private void createVehicle() {
+ suspensionNode.detachAllChildren();
+ for (int i = 0; i < body.getNumWheels(); i++) {
+ VehicleWheel physicsVehicleWheel = body.getWheel(i);
+ Vector3f location = physicsVehicleWheel.getLocation().clone();
+ Vector3f direction = physicsVehicleWheel.getDirection().clone();
+ Vector3f axle = physicsVehicleWheel.getAxle().clone();
+ float restLength = physicsVehicleWheel.getRestLength();
+ float radius = physicsVehicleWheel.getRadius();
+
+ Arrow locArrow = new Arrow(location);
+ Arrow axleArrow = new Arrow(axle.normalizeLocal().multLocal(0.3f));
+ Arrow wheelArrow = new Arrow(direction.normalizeLocal().multLocal(radius));
+ Arrow dirArrow = new Arrow(direction.normalizeLocal().multLocal(restLength));
+ Geometry locGeom = new Geometry("WheelLocationDebugShape" + i, locArrow);
+ Geometry dirGeom = new Geometry("WheelDirectionDebugShape" + i, dirArrow);
+ Geometry axleGeom = new Geometry("WheelAxleDebugShape" + i, axleArrow);
+ Geometry wheelGeom = new Geometry("WheelRadiusDebugShape" + i, wheelArrow);
+ dirGeom.setLocalTranslation(location);
+ axleGeom.setLocalTranslation(location.add(direction));
+ wheelGeom.setLocalTranslation(location.add(direction));
+ locGeom.setMaterial(debugAppState.DEBUG_GREEN);
+ dirGeom.setMaterial(debugAppState.DEBUG_GREEN);
+ axleGeom.setMaterial(debugAppState.DEBUG_GREEN);
+ wheelGeom.setMaterial(debugAppState.DEBUG_GREEN);
+ suspensionNode.attachChild(locGeom);
+ suspensionNode.attachChild(dirGeom);
+ suspensionNode.attachChild(axleGeom);
+ suspensionNode.attachChild(wheelGeom);
+ }
+ }
+
+ @Override
+ protected void controlUpdate(float tpf) {
+ for (int i = 0; i < body.getNumWheels(); i++) {
+ VehicleWheel physicsVehicleWheel = body.getWheel(i);
+ Vector3f location = physicsVehicleWheel.getLocation().clone();
+ Vector3f direction = physicsVehicleWheel.getDirection().clone();
+ Vector3f axle = physicsVehicleWheel.getAxle().clone();
+ float restLength = physicsVehicleWheel.getRestLength();
+ float radius = physicsVehicleWheel.getRadius();
+
+ Geometry locGeom = (Geometry) suspensionNode.getChild("WheelLocationDebugShape" + i);
+ Geometry dirGeom = (Geometry) suspensionNode.getChild("WheelDirectionDebugShape" + i);
+ Geometry axleGeom = (Geometry) suspensionNode.getChild("WheelAxleDebugShape" + i);
+ Geometry wheelGeom = (Geometry) suspensionNode.getChild("WheelRadiusDebugShape" + i);
+
+ Arrow locArrow = (Arrow) locGeom.getMesh();
+ locArrow.setArrowExtent(location);
+ Arrow axleArrow = (Arrow) axleGeom.getMesh();
+ axleArrow.setArrowExtent(axle.normalizeLocal().multLocal(0.3f));
+ Arrow wheelArrow = (Arrow) wheelGeom.getMesh();
+ wheelArrow.setArrowExtent(direction.normalizeLocal().multLocal(radius));
+ Arrow dirArrow = (Arrow) dirGeom.getMesh();
+ dirArrow.setArrowExtent(direction.normalizeLocal().multLocal(restLength));
+
+ dirGeom.setLocalTranslation(location);
+ axleGeom.setLocalTranslation(location.addLocal(direction));
+ wheelGeom.setLocalTranslation(location);
+ i++;
+ }
+ applyPhysicsTransform(body.getPhysicsLocation(location), body.getPhysicsRotation(rotation));
+ }
+
+ @Override
+ protected void controlRender(RenderManager rm, ViewPort vp) {
+ }
+}
diff --git a/engine/src/bullet/com/jme3/bullet/PhysicsSpace.java b/engine/src/bullet/com/jme3/bullet/PhysicsSpace.java
index f97ebf198..d84bf62f2 100644
--- a/engine/src/bullet/com/jme3/bullet/PhysicsSpace.java
+++ b/engine/src/bullet/com/jme3/bullet/PhysicsSpace.java
@@ -920,7 +920,7 @@ public class PhysicsSpace {
/**
* Enable debug display for physics
* @param manager AssetManager to use to create debug materials
- * @Deprecated in favor of BulletDebugAppState, use BulletAppState.enableDebug() to add automatically
+ * @Deprecated in favor of BulletDebugAppState, use BulletAppState.setDebugEnabled(boolean) to add automatically
*/
@Deprecated
public void enableDebug(AssetManager manager) {
diff --git a/engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java b/engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java
index 123779ad9..90a35f390 100644
--- a/engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java
+++ b/engine/src/jbullet/com/jme3/bullet/PhysicsSpace.java
@@ -859,7 +859,7 @@ public class PhysicsSpace {
/**
* Enable debug display for physics
* @param manager AssetManager to use to create debug materials
- * @Deprecated in favor of BulletDebugAppState, use BulletAppState.enableDebug() to add automatically
+ * @Deprecated in favor of BulletDebugAppState, use BulletAppState.setDebugEnabled(boolean) to add automatically
*/
@Deprecated
public void enableDebug(AssetManager manager) {