|
@@ -75,8 +75,34 @@ import java.util.Map;
|
|
|
import java.util.logging.Level;
|
|
|
import java.util.logging.Logger;
|
|
|
|
|
|
-/**
|
|
|
- *
|
|
|
+/**<strong>This control is still a WIP, use it at your own risk</strong><br>
|
|
|
+ * To use this control you need a model with an AnimControl and a SkeletonControl.<br>
|
|
|
+ * This should be the case if you imported an animated model from Ogre or blender.<br>
|
|
|
+ * <p>
|
|
|
+ * This control creates collision shapes for each bones of the skeleton when you call spatial.addControl(ragdollControl).
|
|
|
+ * <ul>
|
|
|
+ * <li>The shape is HullCollision shape based on the vertices associated with each bone and based on a tweakable weight threshold (see setWeightThreshold)</li>
|
|
|
+ * <li>If you don't want each bone to be a collision shape, you can specify what bone to use by using the addBoneName method<br>
|
|
|
+ * By using this method, bone that are not used to create a shape, are "merged" to their parent to create the collision shape.
|
|
|
+ * </li>
|
|
|
+ * </ul>
|
|
|
+ *</p>
|
|
|
+ *<p>
|
|
|
+ *There are 2 behavior for this control :
|
|
|
+ * <ul>
|
|
|
+ * <li><strong>The kinematic behavior :</strong><br>
|
|
|
+ * 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
|
|
|
+ * </li>
|
|
|
+ * <li><strong>The ragdoll behavior :</strong><br>
|
|
|
+ * 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.
|
|
|
+ * </li>
|
|
|
+ * </ul>
|
|
|
+ *</p>
|
|
|
+ *
|
|
|
* @author Normen Hansen and Rémy Bouquet (Nehon)
|
|
|
*/
|
|
|
public class RagdollControl implements PhysicsControl, PhysicsCollisionListener {
|
|
@@ -97,14 +123,13 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
protected float blendStart = 0.0f;
|
|
|
protected List<RagdollCollisionListener> listeners;
|
|
|
protected float eventDispatchImpulseThreshold = 10;
|
|
|
- protected float eventDiscardImpulseThreshold = 3;
|
|
|
protected RagdollPreset preset = new HumanoidRagdollPreset();
|
|
|
protected List<String> boneList = new LinkedList<String>();
|
|
|
protected Vector3f modelPosition = new Vector3f();
|
|
|
protected Quaternion modelRotation = new Quaternion();
|
|
|
protected float rootMass = 15;
|
|
|
protected float totalMass = 0;
|
|
|
- protected boolean added=false;
|
|
|
+ protected boolean added = false;
|
|
|
|
|
|
public RagdollControl() {
|
|
|
}
|
|
@@ -128,116 +153,144 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
}
|
|
|
TempVars vars = TempVars.get();
|
|
|
assert vars.lock();
|
|
|
- Quaternion q2 = vars.quat1;
|
|
|
- Quaternion q3 = vars.quat2;
|
|
|
+ Quaternion tmpRot1 = vars.quat1;
|
|
|
+ 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) {
|
|
|
-
|
|
|
-
|
|
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
|
|
|
|
|
- Vector3f p = link.rigidBody.getMotionState().getWorldLocation();
|
|
|
Vector3f position = vars.vect1;
|
|
|
|
|
|
+ //retrieving bone position in physic world space
|
|
|
+ Vector3f p = link.rigidBody.getMotionState().getWorldLocation();
|
|
|
+ //transforming this position with inverse transforms of the model
|
|
|
targetModel.getWorldTransform().transformInverseVector(p, position);
|
|
|
|
|
|
+ //retrieving bone rotation in physic world space
|
|
|
Quaternion q = link.rigidBody.getMotionState().getWorldRotationQuat();
|
|
|
|
|
|
- q2.set(q).multLocal(link.initalWorldRotation).normalizeLocal();
|
|
|
- q3.set(targetModel.getWorldRotation()).inverseLocal().mult(q2, q2);
|
|
|
- q2.normalizeLocal();
|
|
|
+ //multiplying this rotation by the initialWorld rotation of the bone,
|
|
|
+ //then transforming it with the inverse world rotation of the model
|
|
|
+ tmpRot1.set(q).multLocal(link.initalWorldRotation);
|
|
|
+ tmpRot2.set(targetModel.getWorldRotation()).inverseLocal().mult(tmpRot1, tmpRot1);
|
|
|
+ tmpRot1.normalizeLocal();
|
|
|
+
|
|
|
+ //if the bone is the root bone, we apply the physic's transform to the model, so its position and rotation are correctly updated
|
|
|
if (link.bone.getParent() == null) {
|
|
|
+ //offsetting the physic's position/rotation by the root bone inverse model space position/rotaion
|
|
|
modelPosition.set(p).subtractLocal(link.bone.getInitialPos());
|
|
|
- modelRotation.set(q).multLocal(link.bone.getInitialRot().inverse());
|
|
|
+ modelRotation.set(q).multLocal(tmpRot2.set(link.bone.getInitialRot()).inverseLocal());
|
|
|
+
|
|
|
+ //applying transforms to the model
|
|
|
targetModel.setLocalTranslation(modelPosition);
|
|
|
targetModel.setLocalRotation(modelRotation);
|
|
|
- link.bone.setUserControl(true);
|
|
|
- link.bone.setUserTransformsWorld(position, q2);
|
|
|
+
|
|
|
+ //Applying computed transforms to the bone
|
|
|
+ link.bone.setUserTransformsWorld(position, tmpRot1);
|
|
|
|
|
|
} else {
|
|
|
+ //if boneList is empty, this means that every bone in the ragdoll has a collision shape,
|
|
|
+ //so we just update the bone position
|
|
|
if (boneList.isEmpty()) {
|
|
|
- link.bone.setUserControl(true);
|
|
|
- link.bone.setUserTransformsWorld(position, q2);
|
|
|
+ link.bone.setUserTransformsWorld(position, tmpRot1);
|
|
|
} else {
|
|
|
- setTransform(link.bone, position, q2);
|
|
|
+ //boneList is not empty, this means some bones of the skeleton might not be associated with a collision shape.
|
|
|
+ //So we update them recusively
|
|
|
+ setTransform(link.bone, position, tmpRot1, false);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
|
-
|
|
|
-
|
|
|
+ //the ragdoll does not have the controll, so the keyframed animation updates the physic position of the physic bonces
|
|
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
|
|
- //the ragdoll does not control the skeleton
|
|
|
- link.bone.setUserControl(false);
|
|
|
+
|
|
|
if (!link.rigidBody.isKinematic()) {
|
|
|
link.rigidBody.setKinematic(true);
|
|
|
}
|
|
|
|
|
|
- if (blendedControl) {
|
|
|
-
|
|
|
-
|
|
|
+ Vector3f position = vars.vect1;
|
|
|
|
|
|
- Vector3f position = vars.vect1.set(link.startBlendingPos);
|
|
|
+ //if blended control this means, keyframed animation is updating the skeleton,
|
|
|
+ //but to allow smooth transition, we blend this transformation with the saved position of the ragdoll
|
|
|
+ if (blendedControl) {
|
|
|
Vector3f position2 = vars.vect2;
|
|
|
+ //initializing tmp vars with the start position/rotation of the ragdoll
|
|
|
+ position.set(link.startBlendingPos);
|
|
|
+ tmpRot1.set(link.startBlendingRot);
|
|
|
|
|
|
- q2.set(link.startBlendingRot);
|
|
|
-
|
|
|
- q3.set(q2).slerp(link.bone.getModelSpaceRotation(), blendStart / blendTime);
|
|
|
+ //interpolating between ragdoll position/rotation and keyframed position/rotation
|
|
|
+ tmpRot2.set(tmpRot1).slerp(link.bone.getModelSpaceRotation(), blendStart / blendTime);
|
|
|
position2.set(position).interpolate(link.bone.getModelSpacePosition(), blendStart / blendTime);
|
|
|
- q2.set(q3);
|
|
|
+ tmpRot1.set(tmpRot2);
|
|
|
position.set(position2);
|
|
|
+
|
|
|
+ //updating bones transforms
|
|
|
if (boneList.isEmpty()) {
|
|
|
+ //we ensure we have the control to update the bone
|
|
|
link.bone.setUserControl(true);
|
|
|
- link.bone.setUserTransformsWorld(position, q2);
|
|
|
+ link.bone.setUserTransformsWorld(position, tmpRot1);
|
|
|
+ //we give control back to the key framed animation.
|
|
|
+ link.bone.setUserControl(false);
|
|
|
} else {
|
|
|
- setTransform(link.bone, position, q2);
|
|
|
+ setTransform(link.bone, position, tmpRot1, true);
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- Vector3f position = vars.vect1;
|
|
|
- Quaternion rotation = vars.quat1;
|
|
|
-
|
|
|
//computing position from rotation and scale
|
|
|
targetModel.getWorldTransform().transformVector(link.bone.getModelSpacePosition(), position);
|
|
|
|
|
|
//computing rotation
|
|
|
- rotation.set(link.bone.getModelSpaceRotation()).multLocal(link.bone.getWorldBindInverseRotation());
|
|
|
- targetModel.getWorldRotation().mult(rotation, rotation);
|
|
|
- rotation.normalize();
|
|
|
+ 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(rotation);
|
|
|
+ link.rigidBody.setPhysicsRotation(tmpRot1);
|
|
|
}
|
|
|
+
|
|
|
+ //time control for blending
|
|
|
if (blendedControl) {
|
|
|
blendStart += tpf;
|
|
|
-
|
|
|
if (blendStart > blendTime) {
|
|
|
blendedControl = false;
|
|
|
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-
|
|
|
assert vars.unlock();
|
|
|
|
|
|
}
|
|
|
|
|
|
- private void setTransform(Bone bone, Vector3f pos, Quaternion rot) {
|
|
|
- bone.setUserControl(true);
|
|
|
+ /**
|
|
|
+ * Updates a bone position and rotation.
|
|
|
+ * if the child bones are not in the bone list this means, they are not associated with a physic shape.
|
|
|
+ * So they have to be updated
|
|
|
+ * @param bone the bone
|
|
|
+ * @param pos the position
|
|
|
+ * @param rot the rotation
|
|
|
+ */
|
|
|
+ private void setTransform(Bone bone, Vector3f pos, Quaternion rot, boolean restoreBoneControl) {
|
|
|
+ //we ensure that we have the control
|
|
|
+ if (restoreBoneControl) {
|
|
|
+ bone.setUserControl(true);
|
|
|
+ }
|
|
|
+ //we set te user transforms of the bone
|
|
|
bone.setUserTransformsWorld(pos, rot);
|
|
|
for (Bone childBone : bone.getChildren()) {
|
|
|
+ //each child bone that is not in the list is updated
|
|
|
if (!boneList.contains(childBone.getName())) {
|
|
|
Transform t = childBone.getCombinedTransform(pos, rot);
|
|
|
- setTransform(childBone, t.getTranslation(), t.getRotation());
|
|
|
+ setTransform(childBone, t.getTranslation(), t.getRotation(), restoreBoneControl);
|
|
|
}
|
|
|
}
|
|
|
- bone.setUserControl(false);
|
|
|
+ //we give back the control to the keyframed animation
|
|
|
+ if (restoreBoneControl) {
|
|
|
+ bone.setUserControl(false);
|
|
|
+ }
|
|
|
+
|
|
|
}
|
|
|
|
|
|
public Control cloneForSpatial(Spatial spatial) {
|
|
@@ -285,7 +338,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
model.setLocalRotation(initRotation);
|
|
|
model.setLocalScale(initScale);
|
|
|
|
|
|
- logger.log(Level.INFO, "Create physics ragdoll for skeleton {0}", skeleton);
|
|
|
+ logger.log(Level.INFO, "Created physics ragdoll for skeleton {0}", skeleton);
|
|
|
}
|
|
|
|
|
|
public void addBoneName(String name) {
|
|
@@ -405,29 +458,36 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
}
|
|
|
|
|
|
private void addToPhysicsSpace() {
|
|
|
-
|
|
|
+
|
|
|
if (baseRigidBody != null) {
|
|
|
space.add(baseRigidBody);
|
|
|
- added=true;
|
|
|
+ added = true;
|
|
|
}
|
|
|
for (Iterator<PhysicsBoneLink> it = boneLinks.values().iterator(); it.hasNext();) {
|
|
|
PhysicsBoneLink physicsBoneLink = it.next();
|
|
|
if (physicsBoneLink.rigidBody != null) {
|
|
|
space.add(physicsBoneLink.rigidBody);
|
|
|
- added=true;
|
|
|
+ added = true;
|
|
|
}
|
|
|
}
|
|
|
for (Iterator<PhysicsBoneLink> it = boneLinks.values().iterator(); it.hasNext();) {
|
|
|
PhysicsBoneLink physicsBoneLink = it.next();
|
|
|
if (physicsBoneLink.joint != null) {
|
|
|
space.add(physicsBoneLink.joint);
|
|
|
- added=true;
|
|
|
+ added = true;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
}
|
|
|
|
|
|
- private HullCollisionShape makeShape(PhysicsBoneLink link, Spatial model) {
|
|
|
+ /**
|
|
|
+ * Create a hull collision shape from linked vertices to this bone.
|
|
|
+ *
|
|
|
+ * @param link
|
|
|
+ * @param model
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ protected HullCollisionShape makeShape(PhysicsBoneLink link, Spatial model) {
|
|
|
Bone bone = link.bone;
|
|
|
List<Integer> boneIndices = null;
|
|
|
if (boneList.isEmpty()) {
|
|
@@ -437,12 +497,11 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
boneIndices = getBoneIndices(bone, skeleton);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
ArrayList<Float> points = new ArrayList<Float>();
|
|
|
if (model instanceof Geometry) {
|
|
|
Geometry g = (Geometry) model;
|
|
|
for (Integer index : boneIndices) {
|
|
|
- points.addAll(getPoints(g.getMesh(), index, bone.getModelSpacePosition(), link));
|
|
|
+ points.addAll(getPoints(g.getMesh(), index, bone.getModelSpacePosition()));
|
|
|
}
|
|
|
} else if (model instanceof Node) {
|
|
|
Node node = (Node) model;
|
|
@@ -450,7 +509,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
if (s instanceof Geometry) {
|
|
|
Geometry g = (Geometry) s;
|
|
|
for (Integer index : boneIndices) {
|
|
|
- points.addAll(getPoints(g.getMesh(), index, bone.getModelSpacePosition(), link));
|
|
|
+ points.addAll(getPoints(g.getMesh(), index, bone.getModelSpacePosition()));
|
|
|
}
|
|
|
|
|
|
}
|
|
@@ -464,7 +523,8 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
return new HullCollisionShape(p);
|
|
|
}
|
|
|
|
|
|
- private List<Integer> getBoneIndices(Bone bone, Skeleton skeleton) {
|
|
|
+ //retruns the list of bone indices of the given bone and its child(if they are not in the boneList)
|
|
|
+ protected List<Integer> getBoneIndices(Bone bone, Skeleton skeleton) {
|
|
|
List<Integer> list = new LinkedList<Integer>();
|
|
|
list.add(skeleton.getBoneIndex(bone));
|
|
|
for (Bone chilBone : bone.getChildren()) {
|
|
@@ -475,7 +535,15 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
return list;
|
|
|
}
|
|
|
|
|
|
- protected List<Float> getPoints(Mesh mesh, int boneIndex, Vector3f offset, PhysicsBoneLink link) {
|
|
|
+ /**
|
|
|
+ * returns a list of points for the given bone
|
|
|
+ * @param mesh
|
|
|
+ * @param boneIndex
|
|
|
+ * @param offset
|
|
|
+ * @param link
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private List<Float> getPoints(Mesh mesh, int boneIndex, Vector3f offset) {
|
|
|
|
|
|
FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
|
|
|
ByteBuffer boneIndices = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
|
|
@@ -516,8 +584,8 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
return results;
|
|
|
}
|
|
|
|
|
|
- private void removeFromPhysicsSpace() {
|
|
|
-
|
|
|
+ protected void removeFromPhysicsSpace() {
|
|
|
+
|
|
|
if (baseRigidBody != null) {
|
|
|
space.remove(baseRigidBody);
|
|
|
}
|
|
@@ -533,9 +601,15 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
space.remove(physicsBoneLink.rigidBody);
|
|
|
}
|
|
|
}
|
|
|
- added=false;
|
|
|
+ added = false;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * 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
|
|
|
+ * if enabled is false the ragdoll is removed from physic space.
|
|
|
+ * @param enabled
|
|
|
+ */
|
|
|
public void setEnabled(boolean enabled) {
|
|
|
if (this.enabled == enabled) {
|
|
|
return;
|
|
@@ -548,11 +622,15 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * returns true if the control is enabled
|
|
|
+ * @return
|
|
|
+ */
|
|
|
public boolean isEnabled() {
|
|
|
return enabled;
|
|
|
}
|
|
|
|
|
|
- public void attachDebugShape(AssetManager manager) {
|
|
|
+ protected void attachDebugShape(AssetManager manager) {
|
|
|
for (Iterator<PhysicsBoneLink> it = boneLinks.values().iterator(); it.hasNext();) {
|
|
|
PhysicsBoneLink physicsBoneLink = it.next();
|
|
|
physicsBoneLink.rigidBody.createDebugShape(manager);
|
|
@@ -560,7 +638,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
debug = true;
|
|
|
}
|
|
|
|
|
|
- public void detachDebugShape() {
|
|
|
+ protected void detachDebugShape() {
|
|
|
for (Iterator<PhysicsBoneLink> it = boneLinks.values().iterator(); it.hasNext();) {
|
|
|
PhysicsBoneLink physicsBoneLink = it.next();
|
|
|
physicsBoneLink.rigidBody.detachDebugShape();
|
|
@@ -568,6 +646,12 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
debug = false;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * For internal use only
|
|
|
+ * specific render for the ragdoll(if debugging)
|
|
|
+ * @param rm
|
|
|
+ * @param vp
|
|
|
+ */
|
|
|
public void render(RenderManager rm, ViewPort vp) {
|
|
|
if (enabled && space != null && space.getDebugManager() != null) {
|
|
|
if (!debug) {
|
|
@@ -586,6 +670,10 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * set the physic space to this ragdoll
|
|
|
+ * @param space
|
|
|
+ */
|
|
|
public void setPhysicsSpace(PhysicsSpace space) {
|
|
|
if (space == null) {
|
|
|
removeFromPhysicsSpace();
|
|
@@ -600,27 +688,48 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
this.space.addCollisionListener(this);
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * returns the physic space
|
|
|
+ * @return
|
|
|
+ */
|
|
|
public PhysicsSpace getPhysicsSpace() {
|
|
|
return space;
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * serialize this control
|
|
|
+ * @param ex
|
|
|
+ * @throws IOException
|
|
|
+ */
|
|
|
public void write(JmeExporter ex) throws IOException {
|
|
|
throw new UnsupportedOperationException("Not supported yet.");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * de-serialize this control
|
|
|
+ * @param im
|
|
|
+ * @throws IOException
|
|
|
+ */
|
|
|
public void read(JmeImporter im) throws IOException {
|
|
|
throw new UnsupportedOperationException("Not supported yet.");
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * For internal use only
|
|
|
+ * callback for collisionevent
|
|
|
+ * @param event
|
|
|
+ */
|
|
|
public void collision(PhysicsCollisionEvent event) {
|
|
|
PhysicsCollisionObject objA = event.getObjectA();
|
|
|
PhysicsCollisionObject objB = event.getObjectB();
|
|
|
|
|
|
+ //excluding collisions that do not involve the ragdoll
|
|
|
if (event.getNodeA() == null && event.getNodeB() == null) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- if (event.getAppliedImpulse() < eventDiscardImpulseThreshold) {
|
|
|
+ //discarding low impulse collision
|
|
|
+ if (event.getAppliedImpulse() < eventDispatchImpulseThreshold) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -628,7 +737,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
Bone hitBone = null;
|
|
|
PhysicsCollisionObject hitObject = null;
|
|
|
|
|
|
-
|
|
|
+ //Computing which bone has been hit
|
|
|
if (objA.getUserObject() instanceof PhysicsBoneLink) {
|
|
|
PhysicsBoneLink link = (PhysicsBoneLink) objA.getUserObject();
|
|
|
if (link != null) {
|
|
@@ -648,7 +757,8 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (hit && event.getAppliedImpulse() > eventDispatchImpulseThreshold) {
|
|
|
+ //dispatching the event if the ragdoll has been hit
|
|
|
+ if (hit) {
|
|
|
for (RagdollCollisionListener listener : listeners) {
|
|
|
listener.collide(hitBone, hitObject, event);
|
|
|
}
|
|
@@ -656,26 +766,38 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
|
|
|
}
|
|
|
|
|
|
- public void setControl(boolean control) {
|
|
|
+ /**
|
|
|
+ * Enable or disable the ragdoll behaviour.
|
|
|
+ * if ragdollEnabled is true, the character motion will only be powerd by physics
|
|
|
+ * else, the characted will be animated by the keyframe animation,
|
|
|
+ * but will be able to physically interact with its physic environnement
|
|
|
+ * @param ragdollEnabled
|
|
|
+ */
|
|
|
+ public void setRagdollEnabled(boolean ragdollEnabled) {
|
|
|
|
|
|
AnimControl animControl = targetModel.getControl(AnimControl.class);
|
|
|
- animControl.setEnabled(!control);
|
|
|
+ animControl.setEnabled(!ragdollEnabled);
|
|
|
|
|
|
- this.control = control;
|
|
|
+ this.control = ragdollEnabled;
|
|
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
|
|
- // link.bone.setUserControl(control);
|
|
|
- link.rigidBody.setKinematic(!control);
|
|
|
+ link.rigidBody.setKinematic(!ragdollEnabled);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
for (Bone bone : skeleton.getRoots()) {
|
|
|
- setUserControl(bone, control);
|
|
|
+ setUserControl(bone, ragdollEnabled);
|
|
|
}
|
|
|
-
|
|
|
}
|
|
|
|
|
|
- public void blendControlToAnim(String anim, AnimChannel channel) {
|
|
|
+ /**
|
|
|
+ * 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
|
|
|
+ * @param blendTime the blending time between ragdoll to anim.
|
|
|
+ */
|
|
|
+ public void blendRagdollToAnim(String anim, AnimChannel channel, float blendTime) {
|
|
|
blendedControl = true;
|
|
|
+ this.blendTime = blendTime;
|
|
|
control = false;
|
|
|
AnimControl animControl = targetModel.getControl(AnimControl.class);
|
|
|
animControl.setEnabled(true);
|
|
@@ -696,9 +818,9 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
Quaternion q2 = vars.quat1;
|
|
|
Quaternion q3 = vars.quat2;
|
|
|
|
|
|
- q2.set(q).multLocal(link.initalWorldRotation).normalize();
|
|
|
+ q2.set(q).multLocal(link.initalWorldRotation).normalizeLocal();
|
|
|
q3.set(targetModel.getWorldRotation()).inverseLocal().mult(q2, q2);
|
|
|
- q2.normalize();
|
|
|
+ q2.normalizeLocal();
|
|
|
link.startBlendingPos.set(position);
|
|
|
link.startBlendingRot.set(q2);
|
|
|
link.rigidBody.setKinematic(true);
|
|
@@ -719,12 +841,19 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public boolean hasControl() {
|
|
|
-
|
|
|
+ /**
|
|
|
+ * returns true if the ragdoll behaviour is enabled
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ public boolean isRagdollEnabled() {
|
|
|
return control;
|
|
|
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * add a
|
|
|
+ * @param listener
|
|
|
+ */
|
|
|
public void addCollisionListener(RagdollCollisionListener listener) {
|
|
|
if (listeners == null) {
|
|
|
listeners = new ArrayList<RagdollCollisionListener>();
|
|
@@ -760,14 +889,6 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
|
|
|
this.weightThreshold = weightThreshold;
|
|
|
}
|
|
|
|
|
|
- public float getEventDiscardImpulseThreshold() {
|
|
|
- return eventDiscardImpulseThreshold;
|
|
|
- }
|
|
|
-
|
|
|
- public void setEventDiscardImpulseThreshold(float eventDiscardImpulseThreshold) {
|
|
|
- this.eventDiscardImpulseThreshold = eventDiscardImpulseThreshold;
|
|
|
- }
|
|
|
-
|
|
|
public float getEventDispatchImpulseThreshold() {
|
|
|
return eventDispatchImpulseThreshold;
|
|
|
}
|