|
|
|
@ -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; |
|
|
|
|
} |
|
|
|
|