diff --git a/engine/src/jbullet/com/jme3/bullet/control/RagdollControl.java b/engine/src/jbullet/com/jme3/bullet/control/KinematicRagdollControl.java
similarity index 86%
rename from engine/src/jbullet/com/jme3/bullet/control/RagdollControl.java
rename to engine/src/jbullet/com/jme3/bullet/control/KinematicRagdollControl.java
index 77e9d7f7c..28a3fdb69 100644
--- a/engine/src/jbullet/com/jme3/bullet/control/RagdollControl.java
+++ b/engine/src/jbullet/com/jme3/bullet/control/KinematicRagdollControl.java
@@ -78,6 +78,7 @@ import java.util.logging.Logger;
/**This control is still a WIP, use it at your own risk
* To use this control you need a model with an AnimControl and a SkeletonControl.
* This should be the case if you imported an animated model from Ogre or blender.
+ * Note enabling/disabling the control add/removes it from the physic space
*
* This control creates collision shapes for each bones of the skeleton when you call spatial.addControl(ragdollControl).
*
@@ -88,26 +89,25 @@ import java.util.logging.Logger;
*
*
*
- *There are 2 behavior for this control :
+ *There are 2 modes for this control :
*
- * - The kinematic behavior :
+ * - The kinematic modes :
* this is the default behavior, this means that the collision shapes of the body are able to interact with physics enabled objects.
* in this mode physic shapes follow the moovements of the animated skeleton (for example animated by a key framed animation)
- * this mode is enabled just by enabling the control with setEnabled(true);
- * disabling the control removes it from the phyic space
+ * this mode is enabled by calling setKinematicMode();
*
- * - The ragdoll behavior :
- * To enable this behavior, you need to call setRagdollEnabled(true) method.
- * In this mode the charater is entirely controled by physics, so it will fall under the garvity and move if any force is applied to it.
+ * - The ragdoll modes :
+ * To enable this behavior, you need to call setRagdollMode() method.
+ * In this mode the charater is entirely controled by physics, so it will fall under the gravity and move if any force is applied to it.
*
*
*
*
* @author Normen Hansen and Rémy Bouquet (Nehon)
*/
-public class RagdollControl implements PhysicsControl, PhysicsCollisionListener {
+public class KinematicRagdollControl implements PhysicsControl, PhysicsCollisionListener {
- protected static final Logger logger = Logger.getLogger(RagdollControl.class.getName());
+ protected static final Logger logger = Logger.getLogger(KinematicRagdollControl.class.getName());
protected Map boneLinks = new HashMap();
protected Skeleton skeleton;
protected PhysicsSpace space;
@@ -117,7 +117,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
protected float weightThreshold = 1.0f;
protected Spatial targetModel;
protected Vector3f initScale;
- protected boolean control = false;
+ protected Mode mode = Mode.Kinetmatic;
protected boolean blendedControl = false;
protected float blendTime = 1.0f;
protected float blendStart = 0.0f;
@@ -131,19 +131,28 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
protected float totalMass = 0;
protected boolean added = false;
- public RagdollControl() {
+ public enum Mode {
+
+ Kinetmatic,
+ Ragdoll
+ }
+
+ /**
+ * contruct a KinematicRagdollControl
+ */
+ public KinematicRagdollControl() {
}
- public RagdollControl(float weightThreshold) {
+ public KinematicRagdollControl(float weightThreshold) {
this.weightThreshold = weightThreshold;
}
- public RagdollControl(RagdollPreset preset, float weightThreshold) {
+ public KinematicRagdollControl(RagdollPreset preset, float weightThreshold) {
this.preset = preset;
this.weightThreshold = weightThreshold;
}
- public RagdollControl(RagdollPreset preset) {
+ public KinematicRagdollControl(RagdollPreset preset) {
this.preset = preset;
}
@@ -157,7 +166,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
Quaternion tmpRot2 = vars.quat2;
//if the ragdoll has the control of the skeleton, we update each bone with its position in physic world space.
- if (control) {
+ if (mode == mode.Ragdoll) {
for (PhysicsBoneLink link : boneLinks.values()) {
Vector3f position = vars.vect1;
@@ -205,10 +214,6 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
//the ragdoll does not have the controll, so the keyframed animation updates the physic position of the physic bonces
for (PhysicsBoneLink link : boneLinks.values()) {
- if (!link.rigidBody.isKinematic()) {
- link.rigidBody.setKinematic(true);
- }
-
Vector3f position = vars.vect1;
//if blended control this means, keyframed animation is updating the skeleton,
@@ -237,18 +242,9 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
}
}
+ //setting skeleton transforms to the ragdoll
+ matchPhysicObjectToBone(link, position, tmpRot1);
- //computing position from rotation and scale
- targetModel.getWorldTransform().transformVector(link.bone.getModelSpacePosition(), position);
-
- //computing rotation
- tmpRot1.set(link.bone.getModelSpaceRotation()).multLocal(link.bone.getWorldBindInverseRotation());
- targetModel.getWorldRotation().mult(tmpRot1, tmpRot1);
- tmpRot1.normalizeLocal();
-
- //updating physic location/rotation of the physic bone
- link.rigidBody.setPhysicsLocation(position);
- link.rigidBody.setPhysicsRotation(tmpRot1);
}
//time control for blending
@@ -293,10 +289,40 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
}
+ /**
+ * Set the transforms of a rigidBody to match the transforms of a bone.
+ * this is used to make the ragdoll follow the skeleton motion while in Kinematic mode
+ * @param link the link containing the bone and the rigidBody
+ * @param position just a temp vector for position
+ * @param tmpRot1 just a temp quaternion for rotation
+ */
+ private void matchPhysicObjectToBone(PhysicsBoneLink link, Vector3f position, Quaternion tmpRot1) {
+ //computing position from rotation and scale
+ targetModel.getWorldTransform().transformVector(link.bone.getModelSpacePosition(), position);
+
+ //computing rotation
+ tmpRot1.set(link.bone.getModelSpaceRotation()).multLocal(link.bone.getWorldBindInverseRotation());
+ targetModel.getWorldRotation().mult(tmpRot1, tmpRot1);
+ tmpRot1.normalizeLocal();
+
+ //updating physic location/rotation of the physic bone
+ link.rigidBody.setPhysicsLocation(position);
+ link.rigidBody.setPhysicsRotation(tmpRot1);
+
+// position.set(link.bone.getModelSpaceScale()).multLocal(targetModel.getWorldScale());
+// //TODO support scale!
+// link.rigidBody.getCollisionShape().setScale(position);
+ }
+
public Control cloneForSpatial(Spatial spatial) {
throw new UnsupportedOperationException("Not supported yet.");
}
+ public void reInit() {
+ setSpatial(targetModel);
+ addToPhysicsSpace();
+ }
+
public void setSpatial(Spatial model) {
if (model == null) {
removeFromPhysicsSpace();
@@ -313,7 +339,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
model.removeFromParent();
model.setLocalTranslation(Vector3f.ZERO);
- model.setLocalRotation(Quaternion.ZERO);
+ model.setLocalRotation(Quaternion.IDENTITY);
model.setLocalScale(1);
//HACK ALERT change this
//I remove the skeletonControl and readd it to the spatial to make sure it's after the ragdollControl in the stack
@@ -355,11 +381,10 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
if (childBone.getParent() == null) {
logger.log(Level.INFO, "Found root bone in skeleton {0}", skeleton);
baseRigidBody = new PhysicsRigidBody(new BoxCollisionShape(Vector3f.UNIT_XYZ.mult(0.1f)), 1);
+ baseRigidBody.setKinematic(mode == Mode.Kinetmatic);
boneRecursion(model, childBone, baseRigidBody, 1);
}
}
-
-
}
private void boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, int reccount) {
@@ -370,6 +395,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
link.bone = bone;
//creating the collision shape from the bone's associated vertices
PhysicsRigidBody shapeNode = new PhysicsRigidBody(makeShape(link, model), rootMass / (float) reccount);
+ shapeNode.setKinematic(mode == Mode.Kinetmatic);
totalMass += rootMass / (float) reccount;
link.rigidBody = shapeNode;
@@ -458,7 +484,9 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
}
private void addToPhysicsSpace() {
-
+ if (space == null) {
+ return;
+ }
if (baseRigidBody != null) {
space.add(baseRigidBody);
added = true;
@@ -467,17 +495,13 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
PhysicsBoneLink physicsBoneLink = it.next();
if (physicsBoneLink.rigidBody != null) {
space.add(physicsBoneLink.rigidBody);
+ if (physicsBoneLink.joint != null) {
+ space.add(physicsBoneLink.joint);
+
+ }
added = true;
}
}
- for (Iterator it = boneLinks.values().iterator(); it.hasNext();) {
- PhysicsBoneLink physicsBoneLink = it.next();
- if (physicsBoneLink.joint != null) {
- space.add(physicsBoneLink.joint);
- added = true;
- }
- }
-
}
/**
@@ -520,6 +544,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
p[i] = points.get(i);
}
+
return new HullCollisionShape(p);
}
@@ -585,7 +610,9 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
}
protected void removeFromPhysicsSpace() {
-
+ if (space == null) {
+ return;
+ }
if (baseRigidBody != null) {
space.remove(baseRigidBody);
}
@@ -593,12 +620,9 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
PhysicsBoneLink physicsBoneLink = it.next();
if (physicsBoneLink.joint != null) {
space.remove(physicsBoneLink.joint);
- }
- }
- for (Iterator it = boneLinks.values().iterator(); it.hasNext();) {
- PhysicsBoneLink physicsBoneLink = it.next();
- if (physicsBoneLink.rigidBody != null) {
- space.remove(physicsBoneLink.rigidBody);
+ if (physicsBoneLink.rigidBody != null) {
+ space.remove(physicsBoneLink.rigidBody);
+ }
}
}
added = false;
@@ -606,7 +630,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
/**
* enable or disable the control
- * note that if enabled is true and that the physic space has been set on the ragdoll, the ragdoll is added to the physic space
+ * note that if enabled is true and that the physic space has been set on the ragdoll, the ragdoll is added to the physic space
* if enabled is false the ragdoll is removed from physic space.
* @param enabled
*/
@@ -723,7 +747,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
PhysicsCollisionObject objA = event.getObjectA();
PhysicsCollisionObject objB = event.getObjectB();
- //excluding collisions that do not involve the ragdoll
+ //excluding collisions that involve 2 parts of the ragdoll
if (event.getNodeA() == null && event.getNodeB() == null) {
return;
}
@@ -773,40 +797,71 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
* but will be able to physically interact with its physic environnement
* @param ragdollEnabled
*/
- public void setRagdollEnabled(boolean ragdollEnabled) {
-
+ protected void setMode(Mode mode) {
+ this.mode = mode;
AnimControl animControl = targetModel.getControl(AnimControl.class);
- animControl.setEnabled(!ragdollEnabled);
+ animControl.setEnabled(mode == Mode.Kinetmatic);
- this.control = ragdollEnabled;
+ baseRigidBody.setKinematic(mode == Mode.Kinetmatic);
+ TempVars vars = TempVars.get();
+ assert vars.lock();
for (PhysicsBoneLink link : boneLinks.values()) {
- link.rigidBody.setKinematic(!ragdollEnabled);
+ link.rigidBody.setKinematic(mode == Mode.Kinetmatic);
+ if (mode == Mode.Ragdoll) {
+ Quaternion tmpRot1 = vars.quat1;
+ Vector3f position = vars.vect1;
+ //making sure that the ragdoll is at the correct place.
+ matchPhysicObjectToBone(link, position, tmpRot1);
+ }
+
}
+ assert vars.unlock();
for (Bone bone : skeleton.getRoots()) {
- setUserControl(bone, ragdollEnabled);
+ setUserControl(bone, mode == Mode.Ragdoll);
+ }
+ }
+
+ /**
+ * Set the control into Kinematic mode
+ * In theis mode, the collision shapes follow the movements of the skeleton,
+ * and can interact with physical environement
+ */
+ public void setKinematicMode() {
+ if (mode != Mode.Kinetmatic) {
+ setMode(Mode.Kinetmatic);
+ }
+ }
+
+ /**
+ * Sets the control into Ragdoll mode
+ * The skeleton is entirely controlled by physics.
+ */
+ public void setRagdollMode() {
+ if (mode != Mode.Ragdoll) {
+ setMode(Mode.Ragdoll);
}
}
/**
- * Blend motion between the ragdoll actual physic state to the given animation, in the given blendTime
- * Note that this disable the ragdoll behaviour of the control
- * @param anim the animation name to blend to
- * @param channel the channel to use for this animation
+ * Smoothly blend from Ragdoll mode to Kinematic mode
+ * This is useful to blend ragdoll actual position to a keyframe animation for example
* @param blendTime the blending time between ragdoll to anim.
*/
- public void blendRagdollToAnim(String anim, AnimChannel channel, float blendTime) {
+ public void blendToKinematicMode(float blendTime) {
+ if (mode == Mode.Kinetmatic) {
+ return;
+ }
blendedControl = true;
this.blendTime = blendTime;
- control = false;
+ mode = Mode.Kinetmatic;
AnimControl animControl = targetModel.getControl(AnimControl.class);
animControl.setEnabled(true);
- channel.setAnim(anim);
- channel.setLoopMode(LoopMode.DontLoop);
+
+
TempVars vars = TempVars.get();
assert vars.lock();
- //this.control = control;
for (PhysicsBoneLink link : boneLinks.values()) {
Vector3f p = link.rigidBody.getMotionState().getWorldLocation();
@@ -842,12 +897,11 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
}
/**
- * returns true if the ragdoll behaviour is enabled
+ * retruns the mode of this control
* @return
*/
- public boolean isRagdollEnabled() {
- return control;
-
+ public Mode getMode() {
+ return mode;
}
/**
diff --git a/engine/src/test/jme3test/bullet/TestBoneRagdoll.java b/engine/src/test/jme3test/bullet/TestBoneRagdoll.java
index 0be1091a6..95aaf7d80 100644
--- a/engine/src/test/jme3test/bullet/TestBoneRagdoll.java
+++ b/engine/src/test/jme3test/bullet/TestBoneRagdoll.java
@@ -39,12 +39,13 @@ import com.jme3.animation.LoopMode;
import com.jme3.bullet.BulletAppState;
import com.jme3.app.SimpleApplication;
import com.jme3.asset.TextureKey;
+import com.jme3.bounding.BoundingBox;
import com.jme3.bullet.PhysicsSpace;
import com.jme3.bullet.collision.PhysicsCollisionEvent;
import com.jme3.bullet.collision.PhysicsCollisionObject;
import com.jme3.bullet.collision.RagdollCollisionListener;
import com.jme3.bullet.collision.shapes.SphereCollisionShape;
-import com.jme3.bullet.control.RagdollControl;
+import com.jme3.bullet.control.KinematicRagdollControl;
import com.jme3.bullet.control.RigidBodyControl;
import com.jme3.font.BitmapText;
import com.jme3.input.KeyInput;
@@ -74,7 +75,7 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
private BulletAppState bulletAppState;
Material matBullet;
Node model;
- RagdollControl ragdoll;
+ KinematicRagdollControl ragdoll;
float bulletSize = 1f;
Material mat;
Material mat3;
@@ -106,7 +107,7 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
setupLight();
model = (Node) assetManager.loadModel("Models/Sinbad/Sinbad.mesh.xml");
- // model.setLocalTranslation(5, 0, 5);
+
// model.setLocalRotation(new Quaternion().fromAngleAxis(FastMath.HALF_PI, Vector3f.UNIT_X));
//debug view
@@ -119,7 +120,7 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
skeletonDebug.setLocalTranslation(model.getLocalTranslation());
//Note: PhysicsRagdollControl is still TODO, constructor will change
- ragdoll = new RagdollControl(0.5f);
+ ragdoll = new KinematicRagdollControl(0.5f);
setupSinbad(ragdoll);
ragdoll.addCollisionListener(this);
model.addControl(ragdoll);
@@ -157,12 +158,16 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
float[] angles = new float[3];
model.getLocalRotation().toAngles(angles);
q.fromAngleAxis(angles[1], Vector3f.UNIT_Y);
- //q.lookAt(model.getLocalRotation().toRotationMatrix().getColumn(2), Vector3f.UNIT_Y);
model.setLocalRotation(q);
if (angles[0] < 0) {
- ragdoll.blendRagdollToAnim("StandUpBack", animChannel, 1);
+ animChannel.setAnim("StandUpBack");
+ // ragdoll.setKinematicMode();
+ ragdoll.blendToKinematicMode(0.5f);
} else {
- ragdoll.blendRagdollToAnim("StandUpFront", animChannel, 1);
+ animChannel.setAnim("StandUpFront");
+ ragdoll.blendToKinematicMode(0.5f);
+ // ragdoll.setKinematicMode();
+
}
}
@@ -178,8 +183,10 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
// bulletSize = 0;
}
if (name.equals("stop") && isPressed) {
- bulletAppState.setEnabled(!bulletAppState.isEnabled());
+ // bulletAppState.setEnabled(!bulletAppState.isEnabled());
+ ragdoll.setEnabled(!ragdoll.isEnabled());
+ ragdoll.setRagdollMode();
}
if (name.equals("shoot") && !isPressed) {
Geometry bulletg = new Geometry("bullet", bullet);
@@ -265,13 +272,11 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
}
}
- if (!ragdoll.isRagdollEnabled()) {
- ragdoll.setRagdollEnabled(true);
+ ragdoll.setRagdollMode();
- }
}
- private void setupSinbad(RagdollControl ragdoll) {
+ private void setupSinbad(KinematicRagdollControl ragdoll) {
ragdoll.addBoneName("Ulna.L");
ragdoll.addBoneName("Ulna.R");
ragdoll.addBoneName("Chest");
@@ -304,6 +309,7 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
@Override
public void simpleUpdate(float tpf) {
+ System.out.println(((BoundingBox) model.getWorldBound()).getYExtent());
// elTime += tpf;
// if (elTime > 3) {
// elTime = 0;