|
@@ -1,5 +1,5 @@
|
|
/*
|
|
/*
|
|
- * Copyright (c) 2009-2012 jMonkeyEngine
|
|
|
|
|
|
+ * Copyright (c) 2009-2018 jMonkeyEngine
|
|
* All rights reserved.
|
|
* All rights reserved.
|
|
*
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* Redistribution and use in source and binary forms, with or without
|
|
@@ -31,37 +31,22 @@
|
|
*/
|
|
*/
|
|
package com.jme3.bullet.control;
|
|
package com.jme3.bullet.control;
|
|
|
|
|
|
-import com.jme3.animation.AnimControl;
|
|
|
|
-import com.jme3.animation.Bone;
|
|
|
|
-import com.jme3.animation.Skeleton;
|
|
|
|
-import com.jme3.animation.SkeletonControl;
|
|
|
|
|
|
+import com.jme3.animation.*;
|
|
import com.jme3.bullet.PhysicsSpace;
|
|
import com.jme3.bullet.PhysicsSpace;
|
|
-import com.jme3.bullet.collision.PhysicsCollisionEvent;
|
|
|
|
-import com.jme3.bullet.collision.PhysicsCollisionListener;
|
|
|
|
-import com.jme3.bullet.collision.PhysicsCollisionObject;
|
|
|
|
-import com.jme3.bullet.collision.RagdollCollisionListener;
|
|
|
|
|
|
+import com.jme3.bullet.collision.*;
|
|
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
|
|
import com.jme3.bullet.collision.shapes.BoxCollisionShape;
|
|
import com.jme3.bullet.collision.shapes.HullCollisionShape;
|
|
import com.jme3.bullet.collision.shapes.HullCollisionShape;
|
|
-import com.jme3.bullet.control.ragdoll.HumanoidRagdollPreset;
|
|
|
|
-import com.jme3.bullet.control.ragdoll.RagdollPreset;
|
|
|
|
-import com.jme3.bullet.control.ragdoll.RagdollUtils;
|
|
|
|
|
|
+import com.jme3.bullet.control.ragdoll.*;
|
|
import com.jme3.bullet.joints.SixDofJoint;
|
|
import com.jme3.bullet.joints.SixDofJoint;
|
|
import com.jme3.bullet.objects.PhysicsRigidBody;
|
|
import com.jme3.bullet.objects.PhysicsRigidBody;
|
|
-import com.jme3.export.InputCapsule;
|
|
|
|
-import com.jme3.export.JmeExporter;
|
|
|
|
-import com.jme3.export.JmeImporter;
|
|
|
|
-import com.jme3.export.OutputCapsule;
|
|
|
|
-import com.jme3.export.Savable;
|
|
|
|
-import com.jme3.math.FastMath;
|
|
|
|
-import com.jme3.math.Quaternion;
|
|
|
|
-import com.jme3.math.Vector3f;
|
|
|
|
|
|
+import com.jme3.export.*;
|
|
|
|
+import com.jme3.math.*;
|
|
import com.jme3.renderer.RenderManager;
|
|
import com.jme3.renderer.RenderManager;
|
|
import com.jme3.renderer.ViewPort;
|
|
import com.jme3.renderer.ViewPort;
|
|
|
|
+import com.jme3.scene.Mesh;
|
|
import com.jme3.scene.Node;
|
|
import com.jme3.scene.Node;
|
|
import com.jme3.scene.Spatial;
|
|
import com.jme3.scene.Spatial;
|
|
-import com.jme3.scene.control.Control;
|
|
|
|
import com.jme3.util.TempVars;
|
|
import com.jme3.util.TempVars;
|
|
-import com.jme3.util.clone.Cloner;
|
|
|
|
import com.jme3.util.clone.JmeCloneable;
|
|
import com.jme3.util.clone.JmeCloneable;
|
|
import java.io.IOException;
|
|
import java.io.IOException;
|
|
import java.util.*;
|
|
import java.util.*;
|
|
@@ -73,29 +58,39 @@ import java.util.logging.Logger;
|
|
* use this control you need a model with an AnimControl and a
|
|
* use this control you need a model with an AnimControl and a
|
|
* SkeletonControl.<br> This should be the case if you imported an animated
|
|
* SkeletonControl.<br> This should be the case if you imported an animated
|
|
* model from Ogre or blender.<br> Note enabling/disabling the control
|
|
* model from Ogre or blender.<br> Note enabling/disabling the control
|
|
- * add/removes it from the physic space<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 modes for this control : <ul> <li><strong>The
|
|
|
|
- * kinematic modes :</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 by calling setKinematicMode(); </li> <li><strong>The ragdoll modes
|
|
|
|
- * :</strong><br> 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. </li>
|
|
|
|
- * </ul> </p>
|
|
|
|
|
|
+ * add/removes it from the physics space<br>
|
|
|
|
+ * <p>
|
|
|
|
+ * This control creates collision shapes for each bones of the skeleton when you
|
|
|
|
+ * invoke 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>
|
|
|
|
+ * There are 2 modes for this control : <ul> <li><strong>The kinematic modes
|
|
|
|
+ * :</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 physics shapes follow the motion of the animated skeleton (for example
|
|
|
|
+ * animated by a key framed animation) this mode is enabled by calling
|
|
|
|
+ * setKinematicMode(); </li> <li><strong>The ragdoll modes:</strong><br> To
|
|
|
|
+ * enable this behavior, you need to invoke the setRagdollMode() method. In this
|
|
|
|
+ * mode the character is entirely controlled by physics, so it will fall under
|
|
|
|
+ * the gravity and move if any force is applied to it.</li>
|
|
|
|
+ * </ul>
|
|
|
|
+ * <p>
|
|
|
|
+ * This class is shared between JBullet and Native Bullet.
|
|
*
|
|
*
|
|
- * @author Normen Hansen and Rémy Bouquet (Nehon)
|
|
|
|
|
|
+ * @author Normen Hansen and Rémy Bouquet (Nehon)
|
|
*/
|
|
*/
|
|
|
|
+@Deprecated
|
|
public class KinematicRagdollControl extends AbstractPhysicsControl implements PhysicsCollisionListener, JmeCloneable {
|
|
public class KinematicRagdollControl extends AbstractPhysicsControl implements PhysicsCollisionListener, JmeCloneable {
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * list of registered collision listeners
|
|
|
|
+ */
|
|
protected static final Logger logger = Logger.getLogger(KinematicRagdollControl.class.getName());
|
|
protected static final Logger logger = Logger.getLogger(KinematicRagdollControl.class.getName());
|
|
protected List<RagdollCollisionListener> listeners;
|
|
protected List<RagdollCollisionListener> listeners;
|
|
protected final Set<String> boneList = new TreeSet<String>();
|
|
protected final Set<String> boneList = new TreeSet<String>();
|
|
@@ -103,7 +98,13 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
protected final Vector3f modelPosition = new Vector3f();
|
|
protected final Vector3f modelPosition = new Vector3f();
|
|
protected final Quaternion modelRotation = new Quaternion();
|
|
protected final Quaternion modelRotation = new Quaternion();
|
|
protected final PhysicsRigidBody baseRigidBody;
|
|
protected final PhysicsRigidBody baseRigidBody;
|
|
|
|
+ /**
|
|
|
|
+ * model being controlled
|
|
|
|
+ */
|
|
protected Spatial targetModel;
|
|
protected Spatial targetModel;
|
|
|
|
+ /**
|
|
|
|
+ * skeleton being controlled
|
|
|
|
+ */
|
|
protected Skeleton skeleton;
|
|
protected Skeleton skeleton;
|
|
protected RagdollPreset preset = new HumanoidRagdollPreset();
|
|
protected RagdollPreset preset = new HumanoidRagdollPreset();
|
|
protected Vector3f initScale;
|
|
protected Vector3f initScale;
|
|
@@ -112,23 +113,52 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
protected boolean blendedControl = false;
|
|
protected boolean blendedControl = false;
|
|
protected float weightThreshold = -1.0f;
|
|
protected float weightThreshold = -1.0f;
|
|
protected float blendStart = 0.0f;
|
|
protected float blendStart = 0.0f;
|
|
|
|
+ /**
|
|
|
|
+ * blending interval for animations (in seconds, ≥0)
|
|
|
|
+ */
|
|
protected float blendTime = 1.0f;
|
|
protected float blendTime = 1.0f;
|
|
protected float eventDispatchImpulseThreshold = 10;
|
|
protected float eventDispatchImpulseThreshold = 10;
|
|
protected float rootMass = 15;
|
|
protected float rootMass = 15;
|
|
|
|
+ /**
|
|
|
|
+ * accumulate total mass of ragdoll when control is added to a scene
|
|
|
|
+ */
|
|
protected float totalMass = 0;
|
|
protected float totalMass = 0;
|
|
private Map<String, Vector3f> ikTargets = new HashMap<String, Vector3f>();
|
|
private Map<String, Vector3f> ikTargets = new HashMap<String, Vector3f>();
|
|
private Map<String, Integer> ikChainDepth = new HashMap<String, Integer>();
|
|
private Map<String, Integer> ikChainDepth = new HashMap<String, Integer>();
|
|
|
|
+ /**
|
|
|
|
+ * rotational speed for inverse kinematics (radians per second, default=7)
|
|
|
|
+ */
|
|
private float ikRotSpeed = 7f;
|
|
private float ikRotSpeed = 7f;
|
|
|
|
+ /**
|
|
|
|
+ * viscous limb-damping ratio (0→no damping, 1→critically damped,
|
|
|
|
+ * default=0.6)
|
|
|
|
+ */
|
|
private float limbDampening = 0.6f;
|
|
private float limbDampening = 0.6f;
|
|
-
|
|
|
|
|
|
+ /**
|
|
|
|
+ * distance threshold for inverse kinematics (default=0.1)
|
|
|
|
+ */
|
|
private float IKThreshold = 0.1f;
|
|
private float IKThreshold = 0.1f;
|
|
|
|
+ /**
|
|
|
|
+ * Enumerate joint-control modes for this control.
|
|
|
|
+ */
|
|
public static enum Mode {
|
|
public static enum Mode {
|
|
-
|
|
|
|
|
|
+ /**
|
|
|
|
+ * collision shapes follow the movements of bones in the skeleton
|
|
|
|
+ */
|
|
Kinematic,
|
|
Kinematic,
|
|
|
|
+ /**
|
|
|
|
+ * skeleton is controlled by Bullet physics (gravity and collisions)
|
|
|
|
+ */
|
|
Ragdoll,
|
|
Ragdoll,
|
|
|
|
+ /**
|
|
|
|
+ * skeleton is controlled by inverse-kinematic targets
|
|
|
|
+ */
|
|
IK
|
|
IK
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Link a bone to a jointed rigid body.
|
|
|
|
+ */
|
|
public class PhysicsBoneLink implements Savable {
|
|
public class PhysicsBoneLink implements Savable {
|
|
|
|
|
|
protected PhysicsRigidBody rigidBody;
|
|
protected PhysicsRigidBody rigidBody;
|
|
@@ -138,17 +168,37 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
protected Quaternion startBlendingRot = new Quaternion();
|
|
protected Quaternion startBlendingRot = new Quaternion();
|
|
protected Vector3f startBlendingPos = new Vector3f();
|
|
protected Vector3f startBlendingPos = new Vector3f();
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Instantiate an uninitialized link.
|
|
|
|
+ */
|
|
public PhysicsBoneLink() {
|
|
public PhysicsBoneLink() {
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Access the linked bone.
|
|
|
|
+ *
|
|
|
|
+ * @return the pre-existing instance or null
|
|
|
|
+ */
|
|
public Bone getBone() {
|
|
public Bone getBone() {
|
|
return bone;
|
|
return bone;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Access the linked body.
|
|
|
|
+ *
|
|
|
|
+ * @return the pre-existing instance or null
|
|
|
|
+ */
|
|
public PhysicsRigidBody getRigidBody() {
|
|
public PhysicsRigidBody getRigidBody() {
|
|
return rigidBody;
|
|
return rigidBody;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Serialize this bone link, for example when saving to a J3O file.
|
|
|
|
+ *
|
|
|
|
+ * @param ex exporter (not null)
|
|
|
|
+ * @throws IOException from exporter
|
|
|
|
+ */
|
|
|
|
+ @Override
|
|
public void write(JmeExporter ex) throws IOException {
|
|
public void write(JmeExporter ex) throws IOException {
|
|
OutputCapsule oc = ex.getCapsule(this);
|
|
OutputCapsule oc = ex.getCapsule(this);
|
|
oc.write(rigidBody, "rigidBody", null);
|
|
oc.write(rigidBody, "rigidBody", null);
|
|
@@ -159,6 +209,14 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
oc.write(startBlendingPos, "startBlendingPos", new Vector3f());
|
|
oc.write(startBlendingPos, "startBlendingPos", new Vector3f());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * De-serialize this bone link, for example when loading from a J3O
|
|
|
|
+ * file.
|
|
|
|
+ *
|
|
|
|
+ * @param im importer (not null)
|
|
|
|
+ * @throws IOException from importer
|
|
|
|
+ */
|
|
|
|
+ @Override
|
|
public void read(JmeImporter im) throws IOException {
|
|
public void read(JmeImporter im) throws IOException {
|
|
InputCapsule ic = im.getCapsule(this);
|
|
InputCapsule ic = im.getCapsule(this);
|
|
rigidBody = (PhysicsRigidBody) ic.readSavable("rigidBody", null);
|
|
rigidBody = (PhysicsRigidBody) ic.readSavable("rigidBody", null);
|
|
@@ -171,29 +229,54 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * contruct a KinematicRagdollControl
|
|
|
|
|
|
+ * Instantiate an enabled control.
|
|
*/
|
|
*/
|
|
public KinematicRagdollControl() {
|
|
public KinematicRagdollControl() {
|
|
baseRigidBody = new PhysicsRigidBody(new BoxCollisionShape(Vector3f.UNIT_XYZ.mult(0.1f)), 1);
|
|
baseRigidBody = new PhysicsRigidBody(new BoxCollisionShape(Vector3f.UNIT_XYZ.mult(0.1f)), 1);
|
|
baseRigidBody.setKinematic(mode == Mode.Kinematic);
|
|
baseRigidBody.setKinematic(mode == Mode.Kinematic);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Instantiate an enabled control with the specified weight threshold.
|
|
|
|
+ *
|
|
|
|
+ * @param weightThreshold (>0, <1)
|
|
|
|
+ */
|
|
public KinematicRagdollControl(float weightThreshold) {
|
|
public KinematicRagdollControl(float weightThreshold) {
|
|
this();
|
|
this();
|
|
this.weightThreshold = weightThreshold;
|
|
this.weightThreshold = weightThreshold;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Instantiate an enabled control with the specified preset and weight
|
|
|
|
+ * threshold.
|
|
|
|
+ *
|
|
|
|
+ * @param preset (not null)
|
|
|
|
+ * @param weightThreshold (>0, <1)
|
|
|
|
+ */
|
|
public KinematicRagdollControl(RagdollPreset preset, float weightThreshold) {
|
|
public KinematicRagdollControl(RagdollPreset preset, float weightThreshold) {
|
|
this();
|
|
this();
|
|
this.preset = preset;
|
|
this.preset = preset;
|
|
this.weightThreshold = weightThreshold;
|
|
this.weightThreshold = weightThreshold;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Instantiate an enabled control with the specified preset.
|
|
|
|
+ *
|
|
|
|
+ * @param preset (not null)
|
|
|
|
+ */
|
|
public KinematicRagdollControl(RagdollPreset preset) {
|
|
public KinematicRagdollControl(RagdollPreset preset) {
|
|
this();
|
|
this();
|
|
this.preset = preset;
|
|
this.preset = preset;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Update this control. Invoked once per frame during the logical-state
|
|
|
|
+ * update, provided the control is added to a scene. Do not invoke directly
|
|
|
|
+ * from user code.
|
|
|
|
+ *
|
|
|
|
+ * @param tpf the time interval between frames (in seconds, ≥0)
|
|
|
|
+ */
|
|
|
|
+ @Override
|
|
public void update(float tpf) {
|
|
public void update(float tpf) {
|
|
if (!enabled) {
|
|
if (!enabled) {
|
|
return;
|
|
return;
|
|
@@ -201,13 +284,18 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
if(mode == Mode.IK){
|
|
if(mode == Mode.IK){
|
|
ikUpdate(tpf);
|
|
ikUpdate(tpf);
|
|
} else if (mode == mode.Ragdoll && targetModel.getLocalTranslation().equals(modelPosition)) {
|
|
} else if (mode == mode.Ragdoll && targetModel.getLocalTranslation().equals(modelPosition)) {
|
|
- //if the ragdoll has the control of the skeleton, we update each bone with its position in physic world space.
|
|
|
|
|
|
+ //if the ragdoll has the control of the skeleton, we update each bone with its position in physics world space.
|
|
ragDollUpdate(tpf);
|
|
ragDollUpdate(tpf);
|
|
} else {
|
|
} else {
|
|
kinematicUpdate(tpf);
|
|
kinematicUpdate(tpf);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Update this control in Ragdoll mode, based on Bullet physics.
|
|
|
|
+ *
|
|
|
|
+ * @param tpf the time interval between frames (in seconds, ≥0)
|
|
|
|
+ */
|
|
protected void ragDollUpdate(float tpf) {
|
|
protected void ragDollUpdate(float tpf) {
|
|
TempVars vars = TempVars.get();
|
|
TempVars vars = TempVars.get();
|
|
Quaternion tmpRot1 = vars.quat1;
|
|
Quaternion tmpRot1 = vars.quat1;
|
|
@@ -217,15 +305,15 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
|
|
|
|
Vector3f position = vars.vect1;
|
|
Vector3f position = vars.vect1;
|
|
|
|
|
|
- //retrieving bone position in physic world space
|
|
|
|
|
|
+ //retrieving bone position in physics world space
|
|
Vector3f p = link.rigidBody.getMotionState().getWorldLocation();
|
|
Vector3f p = link.rigidBody.getMotionState().getWorldLocation();
|
|
//transforming this position with inverse transforms of the model
|
|
//transforming this position with inverse transforms of the model
|
|
targetModel.getWorldTransform().transformInverseVector(p, position);
|
|
targetModel.getWorldTransform().transformInverseVector(p, position);
|
|
|
|
|
|
- //retrieving bone rotation in physic world space
|
|
|
|
|
|
+ //retrieving bone rotation in physics world space
|
|
Quaternion q = link.rigidBody.getMotionState().getWorldRotationQuat();
|
|
Quaternion q = link.rigidBody.getMotionState().getWorldRotationQuat();
|
|
|
|
|
|
- //multiplying this rotation by the initialWorld rotation of the bone,
|
|
|
|
|
|
+ //multiplying this rotation by the initialWorld rotation of the bone,
|
|
//then transforming it with the inverse world rotation of the model
|
|
//then transforming it with the inverse world rotation of the model
|
|
tmpRot1.set(q).multLocal(link.initalWorldRotation);
|
|
tmpRot1.set(q).multLocal(link.initalWorldRotation);
|
|
tmpRot2.set(targetModel.getWorldRotation()).inverseLocal().mult(tmpRot1, tmpRot1);
|
|
tmpRot2.set(targetModel.getWorldRotation()).inverseLocal().mult(tmpRot1, tmpRot1);
|
|
@@ -249,22 +337,21 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
link.bone.setUserTransformsInModelSpace(position, tmpRot1);
|
|
link.bone.setUserTransformsInModelSpace(position, tmpRot1);
|
|
|
|
|
|
} else {
|
|
} 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.setUserTransformsInModelSpace(position, tmpRot1);
|
|
|
|
- } else {
|
|
|
|
- //boneList is not empty, this means some bones of the skeleton might not be associated with a collision shape.
|
|
|
|
- //So we update them recusively
|
|
|
|
- RagdollUtils.setTransform(link.bone, position, tmpRot1, false, boneList);
|
|
|
|
- }
|
|
|
|
|
|
+ //some bones of the skeleton might not be associated with a collision shape.
|
|
|
|
+ //So we update them recusively
|
|
|
|
+ RagdollUtils.setTransform(link.bone, position, tmpRot1, false, boneList);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
vars.release();
|
|
vars.release();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Update this control in Kinematic mode, based on bone animation tracks.
|
|
|
|
+ *
|
|
|
|
+ * @param tpf the time interval between frames (in seconds, ≥0)
|
|
|
|
+ */
|
|
protected void kinematicUpdate(float tpf) {
|
|
protected void kinematicUpdate(float tpf) {
|
|
- //the ragdoll does not have the controll, so the keyframed animation updates the physic position of the physic bonces
|
|
|
|
|
|
+ //the ragdoll does not have control, so the keyframed animation updates the physics position of the physics bonces
|
|
TempVars vars = TempVars.get();
|
|
TempVars vars = TempVars.get();
|
|
Quaternion tmpRot1 = vars.quat1;
|
|
Quaternion tmpRot1 = vars.quat1;
|
|
Quaternion tmpRot2 = vars.quat2;
|
|
Quaternion tmpRot2 = vars.quat2;
|
|
@@ -273,7 +360,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
// if(link.usedbyIK){
|
|
// if(link.usedbyIK){
|
|
// continue;
|
|
// continue;
|
|
// }
|
|
// }
|
|
- //if blended control this means, keyframed animation is updating the skeleton,
|
|
|
|
|
|
+ //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
|
|
//but to allow smooth transition, we blend this transformation with the saved position of the ragdoll
|
|
if (blendedControl) {
|
|
if (blendedControl) {
|
|
Vector3f position2 = vars.vect2;
|
|
Vector3f position2 = vars.vect2;
|
|
@@ -287,17 +374,8 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
tmpRot1.set(tmpRot2);
|
|
tmpRot1.set(tmpRot2);
|
|
position.set(position2);
|
|
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.setUserTransformsInModelSpace(position, tmpRot1);
|
|
|
|
- //we give control back to the key framed animation.
|
|
|
|
- link.bone.setUserControl(false);
|
|
|
|
- } else {
|
|
|
|
- RagdollUtils.setTransform(link.bone, position, tmpRot1, true, boneList);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ //update bone transforms
|
|
|
|
+ RagdollUtils.setTransform(link.bone, position, tmpRot1, true, boneList);
|
|
}
|
|
}
|
|
//setting skeleton transforms to the ragdoll
|
|
//setting skeleton transforms to the ragdoll
|
|
matchPhysicObjectToBone(link, position, tmpRot1);
|
|
matchPhysicObjectToBone(link, position, tmpRot1);
|
|
@@ -313,6 +391,11 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
vars.release();
|
|
vars.release();
|
|
}
|
|
}
|
|
|
|
+ /**
|
|
|
|
+ * Update this control in IK mode, based on IK targets.
|
|
|
|
+ *
|
|
|
|
+ * @param tpf the time interval between frames (in seconds, ≥0)
|
|
|
|
+ */
|
|
private void ikUpdate(float tpf){
|
|
private void ikUpdate(float tpf){
|
|
TempVars vars = TempVars.get();
|
|
TempVars vars = TempVars.get();
|
|
|
|
|
|
@@ -349,6 +432,19 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
vars.release();
|
|
vars.release();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Update a bone and its ancestors in IK mode. Note: recursive!
|
|
|
|
+ *
|
|
|
|
+ * @param link the bone link for the affected bone (may be null)
|
|
|
|
+ * @param tpf the time interval between frames (in seconds, ≥0)
|
|
|
|
+ * @param vars unused
|
|
|
|
+ * @param tmpRot1 temporary storage used in calculations (not null)
|
|
|
|
+ * @param tmpRot2 temporary storage used in calculations (not null)
|
|
|
|
+ * @param tipBone (not null)
|
|
|
|
+ * @param target the location target in model space (not null, unaffected)
|
|
|
|
+ * @param depth depth of the recursion (≥0)
|
|
|
|
+ * @param maxDepth recursion limit (≥0)
|
|
|
|
+ */
|
|
public void updateBone(PhysicsBoneLink link, float tpf, TempVars vars, Quaternion tmpRot1, Quaternion[] tmpRot2, Bone tipBone, Vector3f target, int depth, int maxDepth) {
|
|
public void updateBone(PhysicsBoneLink link, float tpf, TempVars vars, Quaternion tmpRot1, Quaternion[] tmpRot2, Bone tipBone, Vector3f target, int depth, int maxDepth) {
|
|
if (link == null || link.bone.getParent() == null) {
|
|
if (link == null || link.bone.getParent() == null) {
|
|
return;
|
|
return;
|
|
@@ -403,13 +499,12 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * 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
|
|
|
|
|
|
+ * Alter the transforms of a rigidBody to match the transforms of a bone.
|
|
|
|
+ * This is used to make the ragdoll follow animated motion 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
|
|
|
|
|
|
+ * @param link the bone link connecting the bone and the rigidBody
|
|
|
|
+ * @param position temporary storage used in calculations (not null)
|
|
|
|
+ * @param tmpRot1 temporary storage used in calculations (not null)
|
|
*/
|
|
*/
|
|
protected void matchPhysicObjectToBone(PhysicsBoneLink link, Vector3f position, Quaternion tmpRot1) {
|
|
protected void matchPhysicObjectToBone(PhysicsBoneLink link, Vector3f position, Quaternion tmpRot1) {
|
|
//computing position from rotation and scale
|
|
//computing position from rotation and scale
|
|
@@ -420,15 +515,15 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
targetModel.getWorldRotation().mult(tmpRot1, tmpRot1);
|
|
targetModel.getWorldRotation().mult(tmpRot1, tmpRot1);
|
|
tmpRot1.normalizeLocal();
|
|
tmpRot1.normalizeLocal();
|
|
|
|
|
|
- //updating physic location/rotation of the physic bone
|
|
|
|
|
|
+ //updating physics location/rotation of the physics bone
|
|
link.rigidBody.setPhysicsLocation(position);
|
|
link.rigidBody.setPhysicsLocation(position);
|
|
link.rigidBody.setPhysicsRotation(tmpRot1);
|
|
link.rigidBody.setPhysicsRotation(tmpRot1);
|
|
|
|
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * rebuild the ragdoll this is useful if you applied scale on the ragdoll
|
|
|
|
- * after it's been initialized, same as reattaching.
|
|
|
|
|
|
+ * Rebuild the ragdoll. This is useful if you applied scale on the ragdoll
|
|
|
|
+ * after it was initialized. Same as re-attaching.
|
|
*/
|
|
*/
|
|
public void reBuild() {
|
|
public void reBuild() {
|
|
if (spatial == null) {
|
|
if (spatial == null) {
|
|
@@ -438,6 +533,12 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
createSpatialData(spatial);
|
|
createSpatialData(spatial);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Create spatial-dependent data. Invoked when this control is added to a
|
|
|
|
+ * scene.
|
|
|
|
+ *
|
|
|
|
+ * @param model the controlled spatial (not null)
|
|
|
|
+ */
|
|
@Override
|
|
@Override
|
|
protected void createSpatialData(Spatial model) {
|
|
protected void createSpatialData(Spatial model) {
|
|
targetModel = model;
|
|
targetModel = model;
|
|
@@ -462,8 +563,24 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
model.removeControl(sc);
|
|
model.removeControl(sc);
|
|
model.addControl(sc);
|
|
model.addControl(sc);
|
|
|
|
|
|
|
|
+ if (boneList.isEmpty()) {
|
|
|
|
+ // add all bones to the list
|
|
|
|
+ skeleton = sc.getSkeleton();
|
|
|
|
+ for (int boneI = 0; boneI < skeleton.getBoneCount(); boneI++) {
|
|
|
|
+ String boneName = skeleton.getBone(boneI).getName();
|
|
|
|
+ boneList.add(boneName);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ // filter out bones without vertices
|
|
|
|
+ filterBoneList(sc);
|
|
|
|
+
|
|
|
|
+ if (boneList.isEmpty()) {
|
|
|
|
+ throw new IllegalArgumentException(
|
|
|
|
+ "No suitable bones were found in the model's skeleton.");
|
|
|
|
+ }
|
|
|
|
+
|
|
// put into bind pose and compute bone transforms in model space
|
|
// put into bind pose and compute bone transforms in model space
|
|
- // maybe dont reset to ragdoll out of animations?
|
|
|
|
|
|
+ // maybe don't reset to ragdoll out of animations?
|
|
scanSpatial(model);
|
|
scanSpatial(model);
|
|
|
|
|
|
|
|
|
|
@@ -481,6 +598,31 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
logger.log(Level.FINE, "Created physics ragdoll for skeleton {0}", skeleton);
|
|
logger.log(Level.FINE, "Created physics ragdoll for skeleton {0}", skeleton);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Remove any bones without vertices from the boneList, so that every hull
|
|
|
|
+ * shape will contain at least 1 vertex.
|
|
|
|
+ */
|
|
|
|
+ private void filterBoneList(SkeletonControl skeletonControl) {
|
|
|
|
+ Mesh[] targets = skeletonControl.getTargets();
|
|
|
|
+ Skeleton skel = skeletonControl.getSkeleton();
|
|
|
|
+ for (int boneI = 0; boneI < skel.getBoneCount(); boneI++) {
|
|
|
|
+ String boneName = skel.getBone(boneI).getName();
|
|
|
|
+ if (boneList.contains(boneName)) {
|
|
|
|
+ boolean hasVertices = RagdollUtils.hasVertices(boneI, targets,
|
|
|
|
+ weightThreshold);
|
|
|
|
+ if (!hasVertices) {
|
|
|
|
+ boneList.remove(boneName);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Destroy spatial-dependent data. Invoked when this control is removed from
|
|
|
|
+ * a spatial.
|
|
|
|
+ *
|
|
|
|
+ * @param spat the previously controlled spatial (not null)
|
|
|
|
+ */
|
|
@Override
|
|
@Override
|
|
protected void removeSpatialData(Spatial spat) {
|
|
protected void removeSpatialData(Spatial spat) {
|
|
if (added) {
|
|
if (added) {
|
|
@@ -490,15 +632,22 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Add a bone name to this control Using this method you can specify which
|
|
|
|
- * bones of the skeleton will be used to build the collision shapes.
|
|
|
|
|
|
+ * Add a bone name to this control. Repeated invocations of this method can
|
|
|
|
+ * be used to specify which bones to use when generating collision shapes.
|
|
|
|
+ * <p>
|
|
|
|
+ * Not allowed after attaching the control.
|
|
*
|
|
*
|
|
- * @param name
|
|
|
|
|
|
+ * @param name the name of the bone to add
|
|
*/
|
|
*/
|
|
public void addBoneName(String name) {
|
|
public void addBoneName(String name) {
|
|
boneList.add(name);
|
|
boneList.add(name);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Generate physics shapes and bone links for the skeleton.
|
|
|
|
+ *
|
|
|
|
+ * @param model the spatial with the model's SkeletonControl (not null)
|
|
|
|
+ */
|
|
protected void scanSpatial(Spatial model) {
|
|
protected void scanSpatial(Spatial model) {
|
|
AnimControl animControl = model.getControl(AnimControl.class);
|
|
AnimControl animControl = model.getControl(AnimControl.class);
|
|
Map<Integer, List<Float>> pointsMap = null;
|
|
Map<Integer, List<Float>> pointsMap = null;
|
|
@@ -517,14 +666,24 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Generate a physics shape and bone links for the specified bone. Note:
|
|
|
|
+ * recursive!
|
|
|
|
+ *
|
|
|
|
+ * @param model the spatial with the model's SkeletonControl (not null)
|
|
|
|
+ * @param bone the bone to be linked (not null)
|
|
|
|
+ * @param parent the body linked to the parent bone (not null)
|
|
|
|
+ * @param reccount depth of the recursion (≥1)
|
|
|
|
+ * @param pointsMap (not null)
|
|
|
|
+ */
|
|
protected void boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, int reccount, Map<Integer, List<Float>> pointsMap) {
|
|
protected void boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, int reccount, Map<Integer, List<Float>> pointsMap) {
|
|
PhysicsRigidBody parentShape = parent;
|
|
PhysicsRigidBody parentShape = parent;
|
|
- if (boneList.isEmpty() || boneList.contains(bone.getName())) {
|
|
|
|
|
|
+ if (boneList.contains(bone.getName())) {
|
|
|
|
|
|
PhysicsBoneLink link = new PhysicsBoneLink();
|
|
PhysicsBoneLink link = new PhysicsBoneLink();
|
|
link.bone = bone;
|
|
link.bone = bone;
|
|
|
|
|
|
- //creating the collision shape
|
|
|
|
|
|
+ //create the collision shape
|
|
HullCollisionShape shape = null;
|
|
HullCollisionShape shape = null;
|
|
if (pointsMap != null) {
|
|
if (pointsMap != null) {
|
|
//build a shape for the bone, using the vertices that are most influenced by this bone
|
|
//build a shape for the bone, using the vertices that are most influenced by this bone
|
|
@@ -567,16 +726,16 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Set the joint limits for the joint between the given bone and its parent.
|
|
|
|
- * This method can't work before attaching the control to a spatial
|
|
|
|
|
|
+ * Alter the limits of the joint connecting the specified bone to its
|
|
|
|
+ * parent. Can only be invoked after adding the control to a spatial.
|
|
*
|
|
*
|
|
* @param boneName the name of the bone
|
|
* @param boneName the name of the bone
|
|
- * @param maxX the maximum rotation on the x axis (in radians)
|
|
|
|
- * @param minX the minimum rotation on the x axis (in radians)
|
|
|
|
- * @param maxY the maximum rotation on the y axis (in radians)
|
|
|
|
- * @param minY the minimum rotation on the z axis (in radians)
|
|
|
|
- * @param maxZ the maximum rotation on the z axis (in radians)
|
|
|
|
- * @param minZ the minimum rotation on the z axis (in radians)
|
|
|
|
|
|
+ * @param maxX the maximum rotation on the X axis (in radians)
|
|
|
|
+ * @param minX the minimum rotation on the X axis (in radians)
|
|
|
|
+ * @param maxY the maximum rotation on the Y axis (in radians)
|
|
|
|
+ * @param minY the minimum rotation on the Y axis (in radians)
|
|
|
|
+ * @param maxZ the maximum rotation on the Z axis (in radians)
|
|
|
|
+ * @param minZ the minimum rotation on the Z axis (in radians)
|
|
*/
|
|
*/
|
|
public void setJointLimit(String boneName, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) {
|
|
public void setJointLimit(String boneName, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) {
|
|
PhysicsBoneLink link = boneLinks.get(boneName);
|
|
PhysicsBoneLink link = boneLinks.get(boneName);
|
|
@@ -588,8 +747,8 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Return the joint between the given bone and its parent. This return null
|
|
|
|
- * if it's called before attaching the control to a spatial
|
|
|
|
|
|
+ * Return the joint between the specified bone and its parent. This return
|
|
|
|
+ * null if it's invoked before adding the control to a spatial
|
|
*
|
|
*
|
|
* @param boneName the name of the bone
|
|
* @param boneName the name of the bone
|
|
* @return the joint between the given bone and its parent
|
|
* @return the joint between the given bone and its parent
|
|
@@ -654,9 +813,9 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * For internal use only callback for collisionevent
|
|
|
|
|
|
+ * For internal use only: callback for collision event
|
|
*
|
|
*
|
|
- * @param event
|
|
|
|
|
|
+ * @param event (not null)
|
|
*/
|
|
*/
|
|
public void collision(PhysicsCollisionEvent event) {
|
|
public void collision(PhysicsCollisionEvent event) {
|
|
PhysicsCollisionObject objA = event.getObjectA();
|
|
PhysicsCollisionObject objA = event.getObjectA();
|
|
@@ -707,11 +866,11 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
|
|
|
|
/**
|
|
/**
|
|
* Enable or disable the ragdoll behaviour. if ragdollEnabled is true, the
|
|
* Enable or disable the ragdoll behaviour. if ragdollEnabled is true, the
|
|
- * character motion will only be powerd by physics else, the characted will
|
|
|
|
|
|
+ * character motion will only be powered by physics else, the character will
|
|
* be animated by the keyframe animation, but will be able to physically
|
|
* be animated by the keyframe animation, but will be able to physically
|
|
- * interact with its physic environnement
|
|
|
|
|
|
+ * interact with its physics environment
|
|
*
|
|
*
|
|
- * @param ragdollEnabled
|
|
|
|
|
|
+ * @param mode an enum value (not null)
|
|
*/
|
|
*/
|
|
protected void setMode(Mode mode) {
|
|
protected void setMode(Mode mode) {
|
|
this.mode = mode;
|
|
this.mode = mode;
|
|
@@ -789,9 +948,9 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Set the control into Kinematic mode In theis mode, the collision shapes
|
|
|
|
- * follow the movements of the skeleton, and can interact with physical
|
|
|
|
- * environement
|
|
|
|
|
|
+ * Put the control into Kinematic mode. In this mode, the collision shapes
|
|
|
|
+ * follow the movements of the skeleton while interacting with the physics
|
|
|
|
+ * environment.
|
|
*/
|
|
*/
|
|
public void setKinematicMode() {
|
|
public void setKinematicMode() {
|
|
if (mode != Mode.Kinematic) {
|
|
if (mode != Mode.Kinematic) {
|
|
@@ -810,8 +969,8 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Sets the control into Inverse Kinematics mode. The affected bones are affected by IK.
|
|
|
|
- * physics.
|
|
|
|
|
|
+ * Sets the control into Inverse Kinematics mode. The affected bones are
|
|
|
|
+ * affected by IK. physics.
|
|
*/
|
|
*/
|
|
public void setIKMode() {
|
|
public void setIKMode() {
|
|
if (mode != Mode.IK) {
|
|
if (mode != Mode.IK) {
|
|
@@ -820,18 +979,18 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * retruns the mode of this control
|
|
|
|
|
|
+ * returns the mode of this control
|
|
*
|
|
*
|
|
- * @return
|
|
|
|
|
|
+ * @return an enum value
|
|
*/
|
|
*/
|
|
public Mode getMode() {
|
|
public Mode getMode() {
|
|
return mode;
|
|
return mode;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * add a
|
|
|
|
|
|
+ * Add a collision listener to this control.
|
|
*
|
|
*
|
|
- * @param listener
|
|
|
|
|
|
+ * @param listener (not null, alias created)
|
|
*/
|
|
*/
|
|
public void addCollisionListener(RagdollCollisionListener listener) {
|
|
public void addCollisionListener(RagdollCollisionListener listener) {
|
|
if (listeners == null) {
|
|
if (listeners == null) {
|
|
@@ -840,35 +999,66 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
listeners.add(listener);
|
|
listeners.add(listener);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Alter the ragdoll's root mass.
|
|
|
|
+ *
|
|
|
|
+ * @param rootMass the desired mass (≥0)
|
|
|
|
+ */
|
|
public void setRootMass(float rootMass) {
|
|
public void setRootMass(float rootMass) {
|
|
this.rootMass = rootMass;
|
|
this.rootMass = rootMass;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Read the ragdoll's total mass.
|
|
|
|
+ *
|
|
|
|
+ * @return mass (≥0)
|
|
|
|
+ */
|
|
public float getTotalMass() {
|
|
public float getTotalMass() {
|
|
return totalMass;
|
|
return totalMass;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Read the ragdoll's weight threshold.
|
|
|
|
+ *
|
|
|
|
+ * @return threshold
|
|
|
|
+ */
|
|
public float getWeightThreshold() {
|
|
public float getWeightThreshold() {
|
|
return weightThreshold;
|
|
return weightThreshold;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Alter the ragdoll's weight threshold.
|
|
|
|
+ *
|
|
|
|
+ * @param weightThreshold the desired threshold
|
|
|
|
+ */
|
|
public void setWeightThreshold(float weightThreshold) {
|
|
public void setWeightThreshold(float weightThreshold) {
|
|
this.weightThreshold = weightThreshold;
|
|
this.weightThreshold = weightThreshold;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Read the ragdoll's event-dispatch impulse threshold.
|
|
|
|
+ *
|
|
|
|
+ * @return threshold
|
|
|
|
+ */
|
|
public float getEventDispatchImpulseThreshold() {
|
|
public float getEventDispatchImpulseThreshold() {
|
|
return eventDispatchImpulseThreshold;
|
|
return eventDispatchImpulseThreshold;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Alter the ragdoll's event-dispatch impulse threshold.
|
|
|
|
+ *
|
|
|
|
+ * @param eventDispatchImpulseThreshold desired threshold
|
|
|
|
+ */
|
|
public void setEventDispatchImpulseThreshold(float eventDispatchImpulseThreshold) {
|
|
public void setEventDispatchImpulseThreshold(float eventDispatchImpulseThreshold) {
|
|
this.eventDispatchImpulseThreshold = eventDispatchImpulseThreshold;
|
|
this.eventDispatchImpulseThreshold = eventDispatchImpulseThreshold;
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Set the CcdMotionThreshold of all the bone's rigidBodies of the ragdoll
|
|
|
|
|
|
+ * Alter the CcdMotionThreshold of all rigid bodies in the ragdoll.
|
|
*
|
|
*
|
|
* @see PhysicsRigidBody#setCcdMotionThreshold(float)
|
|
* @see PhysicsRigidBody#setCcdMotionThreshold(float)
|
|
- * @param value
|
|
|
|
|
|
+ * @param value the desired threshold value (velocity, >0) or zero to
|
|
|
|
+ * disable CCD (default=0)
|
|
*/
|
|
*/
|
|
public void setCcdMotionThreshold(float value) {
|
|
public void setCcdMotionThreshold(float value) {
|
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
|
@@ -877,10 +1067,11 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * Set the CcdSweptSphereRadius of all the bone's rigidBodies of the ragdoll
|
|
|
|
|
|
+ * Alter the CcdSweptSphereRadius of all rigid bodies in the ragdoll.
|
|
*
|
|
*
|
|
* @see PhysicsRigidBody#setCcdSweptSphereRadius(float)
|
|
* @see PhysicsRigidBody#setCcdSweptSphereRadius(float)
|
|
- * @param value
|
|
|
|
|
|
+ * @param value the desired radius of the sphere used for continuous
|
|
|
|
+ * collision detection (≥0)
|
|
*/
|
|
*/
|
|
public void setCcdSweptSphereRadius(float value) {
|
|
public void setCcdSweptSphereRadius(float value) {
|
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
|
@@ -889,7 +1080,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * return the rigidBody associated to the given bone
|
|
|
|
|
|
+ * Access the rigidBody associated with the named bone.
|
|
*
|
|
*
|
|
* @param boneName the name of the bone
|
|
* @param boneName the name of the bone
|
|
* @return the associated rigidBody.
|
|
* @return the associated rigidBody.
|
|
@@ -903,25 +1094,22 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * For internal use only specific render for the ragdoll(if debugging)
|
|
|
|
|
|
+ * Render this control. Invoked once per view port per frame, provided the
|
|
|
|
+ * control is added to a scene. Should be invoked only by a subclass or by
|
|
|
|
+ * the RenderManager.
|
|
*
|
|
*
|
|
- * @param rm
|
|
|
|
- * @param vp
|
|
|
|
|
|
+ * @param rm the render manager (not null)
|
|
|
|
+ * @param vp the view port to render (not null)
|
|
*/
|
|
*/
|
|
@Override
|
|
@Override
|
|
public void render(RenderManager rm, ViewPort vp) {
|
|
public void render(RenderManager rm, ViewPort vp) {
|
|
}
|
|
}
|
|
|
|
|
|
- @Override
|
|
|
|
- public Control cloneForSpatial(Spatial spatial) {
|
|
|
|
- KinematicRagdollControl control = new KinematicRagdollControl(preset, weightThreshold);
|
|
|
|
- control.setMode(mode);
|
|
|
|
- control.setRootMass(rootMass);
|
|
|
|
- control.setWeightThreshold(weightThreshold);
|
|
|
|
- control.setApplyPhysicsLocal(applyLocal);
|
|
|
|
- return control;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Create a shallow clone for the JME cloner.
|
|
|
|
+ *
|
|
|
|
+ * @return a new control (not null)
|
|
|
|
+ */
|
|
@Override
|
|
@Override
|
|
public Object jmeClone() {
|
|
public Object jmeClone() {
|
|
KinematicRagdollControl control = new KinematicRagdollControl(preset, weightThreshold);
|
|
KinematicRagdollControl control = new KinematicRagdollControl(preset, weightThreshold);
|
|
@@ -933,6 +1121,14 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
return control;
|
|
return control;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Add a target for inverse kinematics.
|
|
|
|
+ *
|
|
|
|
+ * @param bone which bone the IK applies to (not null)
|
|
|
|
+ * @param worldPos the world coordinates of the goal (not null)
|
|
|
|
+ * @param chainLength number of bones in the chain
|
|
|
|
+ * @return a new instance (not null, already added to ikTargets)
|
|
|
|
+ */
|
|
public Vector3f setIKTarget(Bone bone, Vector3f worldPos, int chainLength) {
|
|
public Vector3f setIKTarget(Bone bone, Vector3f worldPos, int chainLength) {
|
|
Vector3f target = worldPos.subtract(targetModel.getWorldTranslation());
|
|
Vector3f target = worldPos.subtract(targetModel.getWorldTranslation());
|
|
ikTargets.put(bone.getName(), target);
|
|
ikTargets.put(bone.getName(), target);
|
|
@@ -951,6 +1147,11 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
return target;
|
|
return target;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Remove the inverse-kinematics target for the specified bone.
|
|
|
|
+ *
|
|
|
|
+ * @param bone which bone has the target (not null, modified)
|
|
|
|
+ */
|
|
public void removeIKTarget(Bone bone) {
|
|
public void removeIKTarget(Bone bone) {
|
|
int depth = ikChainDepth.remove(bone.getName());
|
|
int depth = ikChainDepth.remove(bone.getName());
|
|
int i = 0;
|
|
int i = 0;
|
|
@@ -964,11 +1165,19 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Remove all inverse-kinematics targets.
|
|
|
|
+ */
|
|
public void removeAllIKTargets(){
|
|
public void removeAllIKTargets(){
|
|
ikTargets.clear();
|
|
ikTargets.clear();
|
|
ikChainDepth.clear();
|
|
ikChainDepth.clear();
|
|
applyUserControl();
|
|
applyUserControl();
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Ensure that user control is enabled for any bones used by inverse
|
|
|
|
+ * kinematics and disabled for any other bones.
|
|
|
|
+ */
|
|
public void applyUserControl() {
|
|
public void applyUserControl() {
|
|
for (Bone bone : skeleton.getRoots()) {
|
|
for (Bone bone : skeleton.getRoots()) {
|
|
RagdollUtils.setUserControl(bone, false);
|
|
RagdollUtils.setUserControl(bone, false);
|
|
@@ -995,39 +1204,77 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
vars.release();
|
|
vars.release();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Read the rotation speed for inverse kinematics.
|
|
|
|
+ *
|
|
|
|
+ * @return speed (≥0)
|
|
|
|
+ */
|
|
public float getIkRotSpeed() {
|
|
public float getIkRotSpeed() {
|
|
return ikRotSpeed;
|
|
return ikRotSpeed;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Alter the rotation speed for inverse kinematics.
|
|
|
|
+ *
|
|
|
|
+ * @param ikRotSpeed the desired speed (≥0, default=7)
|
|
|
|
+ */
|
|
public void setIkRotSpeed(float ikRotSpeed) {
|
|
public void setIkRotSpeed(float ikRotSpeed) {
|
|
this.ikRotSpeed = ikRotSpeed;
|
|
this.ikRotSpeed = ikRotSpeed;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Read the distance threshold for inverse kinematics.
|
|
|
|
+ *
|
|
|
|
+ * @return distance threshold
|
|
|
|
+ */
|
|
public float getIKThreshold() {
|
|
public float getIKThreshold() {
|
|
return IKThreshold;
|
|
return IKThreshold;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Alter the distance threshold for inverse kinematics.
|
|
|
|
+ *
|
|
|
|
+ * @param IKThreshold the desired distance threshold (default=0.1)
|
|
|
|
+ */
|
|
public void setIKThreshold(float IKThreshold) {
|
|
public void setIKThreshold(float IKThreshold) {
|
|
this.IKThreshold = IKThreshold;
|
|
this.IKThreshold = IKThreshold;
|
|
}
|
|
}
|
|
|
|
|
|
-
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Read the limb damping.
|
|
|
|
+ *
|
|
|
|
+ * @return the viscous damping ratio (0→no damping, 1→critically
|
|
|
|
+ * damped)
|
|
|
|
+ */
|
|
public float getLimbDampening() {
|
|
public float getLimbDampening() {
|
|
return limbDampening;
|
|
return limbDampening;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Alter the limb damping.
|
|
|
|
+ *
|
|
|
|
+ * @param limbDampening the desired viscous damping ratio (0→no
|
|
|
|
+ * damping, 1→critically damped, default=0.6)
|
|
|
|
+ */
|
|
public void setLimbDampening(float limbDampening) {
|
|
public void setLimbDampening(float limbDampening) {
|
|
this.limbDampening = limbDampening;
|
|
this.limbDampening = limbDampening;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
+ * Access the named bone.
|
|
|
|
+ *
|
|
|
|
+ * @param name which bone to access
|
|
|
|
+ * @return the pre-existing instance, or null if not found
|
|
|
|
+ */
|
|
public Bone getBone(String name){
|
|
public Bone getBone(String name){
|
|
return skeleton.getBone(name);
|
|
return skeleton.getBone(name);
|
|
}
|
|
}
|
|
/**
|
|
/**
|
|
- * serialize this control
|
|
|
|
|
|
+ * Serialize this control, for example when saving to a J3O file.
|
|
*
|
|
*
|
|
- * @param ex
|
|
|
|
- * @throws IOException
|
|
|
|
|
|
+ * @param ex exporter (not null)
|
|
|
|
+ * @throws IOException from exporter
|
|
*/
|
|
*/
|
|
@Override
|
|
@Override
|
|
public void write(JmeExporter ex) throws IOException {
|
|
public void write(JmeExporter ex) throws IOException {
|
|
@@ -1054,10 +1301,10 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
/**
|
|
- * de-serialize this control
|
|
|
|
|
|
+ * De-serialize this control, for example when loading from a J3O file.
|
|
*
|
|
*
|
|
- * @param im
|
|
|
|
- * @throws IOException
|
|
|
|
|
|
+ * @param im importer (not null)
|
|
|
|
+ * @throws IOException from importer
|
|
*/
|
|
*/
|
|
@Override
|
|
@Override
|
|
public void read(JmeImporter im) throws IOException {
|
|
public void read(JmeImporter im) throws IOException {
|