This is the long-overdue initial check in of KinematicRagdollControl with IK support. It also contains some modifications to Bone. Tested with TestBoneRagdoll to ensure backwards compatibility. Please review.
This commit is contained in:
parent
ed6256ef47
commit
8738f961ea
@ -52,6 +52,7 @@ import com.jme3.export.JmeExporter;
|
|||||||
import com.jme3.export.JmeImporter;
|
import com.jme3.export.JmeImporter;
|
||||||
import com.jme3.export.OutputCapsule;
|
import com.jme3.export.OutputCapsule;
|
||||||
import com.jme3.export.Savable;
|
import com.jme3.export.Savable;
|
||||||
|
import com.jme3.math.FastMath;
|
||||||
import com.jme3.math.Quaternion;
|
import com.jme3.math.Quaternion;
|
||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
import com.jme3.renderer.RenderManager;
|
import com.jme3.renderer.RenderManager;
|
||||||
@ -113,11 +114,17 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
protected float eventDispatchImpulseThreshold = 10;
|
protected float eventDispatchImpulseThreshold = 10;
|
||||||
protected float rootMass = 15;
|
protected float rootMass = 15;
|
||||||
protected float totalMass = 0;
|
protected float totalMass = 0;
|
||||||
|
private Map<String, Vector3f> ikTargets = new HashMap<String, Vector3f>();
|
||||||
|
private Map<String, Integer> ikChainDepth = new HashMap<String, Integer>();
|
||||||
|
private float ikRotSpeed = 7f;
|
||||||
|
private float limbDampening = 0.6f;
|
||||||
|
|
||||||
|
private float IKThreshold = 0.1f;
|
||||||
public static enum Mode {
|
public static enum Mode {
|
||||||
|
|
||||||
Kinematic,
|
Kinematic,
|
||||||
Ragdoll
|
Ragdoll,
|
||||||
|
IK
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PhysicsBoneLink implements Savable {
|
public class PhysicsBoneLink implements Savable {
|
||||||
@ -189,7 +196,9 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
if (!enabled) {
|
if (!enabled) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if(mode == Mode.IK){
|
||||||
|
ikUpdate(tpf);
|
||||||
|
}
|
||||||
//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 physic world space.
|
||||||
if (mode == mode.Ragdoll && targetModel.getLocalTranslation().equals(modelPosition)) {
|
if (mode == mode.Ragdoll && targetModel.getLocalTranslation().equals(modelPosition)) {
|
||||||
ragDollUpdate(tpf);
|
ragDollUpdate(tpf);
|
||||||
@ -260,6 +269,9 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
Quaternion tmpRot2 = vars.quat2;
|
Quaternion tmpRot2 = vars.quat2;
|
||||||
Vector3f position = vars.vect1;
|
Vector3f position = vars.vect1;
|
||||||
for (PhysicsBoneLink link : boneLinks.values()) {
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
||||||
|
// if(link.usedbyIK){
|
||||||
|
// 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) {
|
||||||
@ -300,6 +312,95 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
}
|
}
|
||||||
vars.release();
|
vars.release();
|
||||||
}
|
}
|
||||||
|
private void ikUpdate(float tpf){
|
||||||
|
TempVars vars = TempVars.get();
|
||||||
|
|
||||||
|
Quaternion tmpRot1 = vars.quat1;
|
||||||
|
Quaternion[] tmpRot2 = new Quaternion[]{vars.quat2, new Quaternion()};
|
||||||
|
|
||||||
|
Iterator<String> it = ikTargets.keySet().iterator();
|
||||||
|
float distance;
|
||||||
|
Bone bone;
|
||||||
|
String boneName;
|
||||||
|
while (it.hasNext()) {
|
||||||
|
|
||||||
|
boneName = it.next();
|
||||||
|
Logger.getLogger(KinematicRagdollControl.class.getSimpleName()).log(Level.INFO, "updationg {0}", boneName);
|
||||||
|
bone = (Bone) boneLinks.get(boneName).bone;
|
||||||
|
if (!bone.hasUserControl()) {
|
||||||
|
Logger.getLogger(KinematicRagdollControl.class.getSimpleName()).log(Level.INFO, "{0} doesn't have user control", boneName);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
distance = bone.getModelSpacePosition().distance(ikTargets.get(boneName));
|
||||||
|
if (distance < IKThreshold) {
|
||||||
|
Logger.getLogger(KinematicRagdollControl.class.getSimpleName()).log(Level.INFO, "Distance is close enough");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int depth = 0;
|
||||||
|
int maxDepth = ikChainDepth.get(bone.getName());
|
||||||
|
updateBone(boneLinks.get(bone.getName()), tpf * (float) FastMath.sqrt(distance), vars, tmpRot1, tmpRot2, bone, ikTargets.get(boneName), depth, maxDepth);
|
||||||
|
|
||||||
|
Vector3f position = vars.vect1;
|
||||||
|
|
||||||
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
||||||
|
matchPhysicObjectToBone(link, position, tmpRot1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vars.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Quaternion preQuat = link.bone.getLocalRotation();
|
||||||
|
Vector3f vectorAxis;
|
||||||
|
|
||||||
|
float[] measureDist = new float[]{Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY};
|
||||||
|
for (int dirIndex = 0; dirIndex < 3; dirIndex++) {
|
||||||
|
if (dirIndex == 0) {
|
||||||
|
vectorAxis = Vector3f.UNIT_Z;
|
||||||
|
} else if (dirIndex == 1) {
|
||||||
|
vectorAxis = Vector3f.UNIT_X;
|
||||||
|
} else {
|
||||||
|
vectorAxis = Vector3f.UNIT_Y;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int posOrNeg = 0; posOrNeg < 2; posOrNeg++) {
|
||||||
|
float rot = ikRotSpeed * tpf / (link.rigidBody.getMass() * 2);
|
||||||
|
|
||||||
|
rot = FastMath.clamp(rot, link.joint.getRotationalLimitMotor(dirIndex).getLoLimit(), link.joint.getRotationalLimitMotor(dirIndex).getHiLimit());
|
||||||
|
tmpRot1.fromAngleAxis(rot, vectorAxis);
|
||||||
|
// tmpRot1.fromAngleAxis(rotSpeed * tpf / (link.rigidBody.getMass() * 2), vectorAxis);
|
||||||
|
|
||||||
|
|
||||||
|
tmpRot2[posOrNeg] = link.bone.getLocalRotation().mult(tmpRot1);
|
||||||
|
tmpRot2[posOrNeg].normalizeLocal();
|
||||||
|
|
||||||
|
ikRotSpeed = -ikRotSpeed;
|
||||||
|
|
||||||
|
link.bone.setLocalRotation(tmpRot2[posOrNeg]);
|
||||||
|
link.bone.update();
|
||||||
|
measureDist[posOrNeg] = tipBone.getModelSpacePosition().distance(target);
|
||||||
|
link.bone.setLocalRotation(preQuat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (measureDist[0] < measureDist[1]) {
|
||||||
|
link.bone.setLocalRotation(tmpRot2[0]);
|
||||||
|
} else if (measureDist[0] > measureDist[1]) {
|
||||||
|
link.bone.setLocalRotation(tmpRot2[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
link.bone.getLocalRotation().normalizeLocal();
|
||||||
|
|
||||||
|
link.bone.update();
|
||||||
|
// link.usedbyIK = true;
|
||||||
|
if (link.bone.getParent() != null && depth < maxDepth) {
|
||||||
|
|
||||||
|
updateBone(boneLinks.get(link.bone.getParent().getName()), tpf * limbDampening, vars, tmpRot1, tmpRot2, tipBone, target, depth + 1, maxDepth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the transforms of a rigidBody to match the transforms of a bone. this
|
* Set the transforms of a rigidBody to match the transforms of a bone. this
|
||||||
@ -618,6 +719,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
animControl.setEnabled(mode == Mode.Kinematic);
|
animControl.setEnabled(mode == Mode.Kinematic);
|
||||||
|
|
||||||
baseRigidBody.setKinematic(mode == Mode.Kinematic);
|
baseRigidBody.setKinematic(mode == Mode.Kinematic);
|
||||||
|
if (mode != Mode.IK) {
|
||||||
TempVars vars = TempVars.get();
|
TempVars vars = TempVars.get();
|
||||||
|
|
||||||
for (PhysicsBoneLink link : boneLinks.values()) {
|
for (PhysicsBoneLink link : boneLinks.values()) {
|
||||||
@ -631,6 +733,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
|
|
||||||
}
|
}
|
||||||
vars.release();
|
vars.release();
|
||||||
|
}
|
||||||
|
|
||||||
for (Bone bone : skeleton.getRoots()) {
|
for (Bone bone : skeleton.getRoots()) {
|
||||||
RagdollUtils.setUserControl(bone, mode == Mode.Ragdoll);
|
RagdollUtils.setUserControl(bone, mode == Mode.Ragdoll);
|
||||||
@ -703,6 +806,16 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the control into Inverse Kinematics mode. The affected bones are affected by IK.
|
||||||
|
* physics.
|
||||||
|
*/
|
||||||
|
public void setIKMode() {
|
||||||
|
if (mode != Mode.IK) {
|
||||||
|
setMode(Mode.IK);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* retruns the mode of this control
|
* retruns the mode of this control
|
||||||
*
|
*
|
||||||
@ -805,6 +918,92 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
return control;
|
return control;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vector3f setIKTarget(Bone bone, Vector3f worldPos, int chainLength) {
|
||||||
|
Vector3f target = worldPos.subtract(targetModel.getWorldTranslation());
|
||||||
|
ikTargets.put(bone.getName(), target);
|
||||||
|
ikChainDepth.put(bone.getName(), chainLength);
|
||||||
|
int i = 0;
|
||||||
|
while (i < chainLength+2 && bone.getParent() != null) {
|
||||||
|
if (!bone.hasUserControl()) {
|
||||||
|
bone.setUserControl(true);
|
||||||
|
}
|
||||||
|
bone = bone.getParent();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// setIKMode();
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeIKTarget(Bone bone) {
|
||||||
|
int depth = ikChainDepth.remove(bone.getName());
|
||||||
|
int i = 0;
|
||||||
|
while (i < depth+2 && bone.getParent() != null) {
|
||||||
|
if (!bone.hasUserControl()) {
|
||||||
|
// matchPhysicObjectToBone(boneLinks.get(bone.getName()), position, tmpRot1);
|
||||||
|
bone.setUserControl(false);
|
||||||
|
}
|
||||||
|
bone = bone.getParent();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeAllIKTargets(){
|
||||||
|
ikTargets.clear();
|
||||||
|
ikChainDepth.clear();
|
||||||
|
applyUserControl();
|
||||||
|
}
|
||||||
|
public void applyUserControl() {
|
||||||
|
for (Bone bone : skeleton.getRoots()) {
|
||||||
|
RagdollUtils.setUserControl(bone, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ikTargets.isEmpty()) {
|
||||||
|
setKinematicMode();
|
||||||
|
} else {
|
||||||
|
Iterator iterator = ikTargets.keySet().iterator();
|
||||||
|
|
||||||
|
TempVars vars = TempVars.get();
|
||||||
|
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Bone bone = (Bone) iterator.next();
|
||||||
|
while (bone.getParent() != null) {
|
||||||
|
|
||||||
|
Quaternion tmpRot1 = vars.quat1;
|
||||||
|
Vector3f position = vars.vect1;
|
||||||
|
matchPhysicObjectToBone(boneLinks.get(bone.getName()), position, tmpRot1);
|
||||||
|
bone.setUserControl(true);
|
||||||
|
bone = bone.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vars.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public float getIkRotSpeed() {
|
||||||
|
return ikRotSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIkRotSpeed(float ikRotSpeed) {
|
||||||
|
this.ikRotSpeed = ikRotSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getIKThreshold() {
|
||||||
|
return IKThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIKThreshold(float IKThreshold) {
|
||||||
|
this.IKThreshold = IKThreshold;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public float getLimbDampening() {
|
||||||
|
return limbDampening;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setLimbDampening(float limbDampening) {
|
||||||
|
this.limbDampening = limbDampening;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* serialize this control
|
* serialize this control
|
||||||
*
|
*
|
||||||
@ -831,6 +1030,8 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
|
|||||||
oc.write(eventDispatchImpulseThreshold, "eventDispatchImpulseThreshold", 10);
|
oc.write(eventDispatchImpulseThreshold, "eventDispatchImpulseThreshold", 10);
|
||||||
oc.write(rootMass, "rootMass", 15);
|
oc.write(rootMass, "rootMass", 15);
|
||||||
oc.write(totalMass, "totalMass", 0);
|
oc.write(totalMass, "totalMass", 0);
|
||||||
|
oc.write(ikRotSpeed, "rotSpeed", 7f);
|
||||||
|
oc.write(limbDampening, "limbDampening", 0.6f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -459,7 +459,7 @@ public final class Bone implements Savable {
|
|||||||
/**
|
/**
|
||||||
* Updates world transforms for this bone and it's children.
|
* Updates world transforms for this bone and it's children.
|
||||||
*/
|
*/
|
||||||
final void update() {
|
public final void update() {
|
||||||
this.updateModelTransforms();
|
this.updateModelTransforms();
|
||||||
|
|
||||||
for (int i = children.size() - 1; i >= 0; i--) {
|
for (int i = children.size() - 1; i >= 0; i--) {
|
||||||
@ -796,4 +796,15 @@ public final class Bone implements Savable {
|
|||||||
output.write(bindScale, "bindScale", new Vector3f(1.0f, 1.0f, 1.0f));
|
output.write(bindScale, "bindScale", new Vector3f(1.0f, 1.0f, 1.0f));
|
||||||
output.writeSavableArrayList(children, "children", null);
|
output.writeSavableArrayList(children, "children", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setLocalRotation(Quaternion rot){
|
||||||
|
if (!userControl) {
|
||||||
|
throw new IllegalStateException("User control must be on bone to allow user transforms");
|
||||||
|
}
|
||||||
|
this.localRot = rot;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasUserControl(){
|
||||||
|
return userControl;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user