- RagdollControl now has automagic joint setup via RagdollPreset and HumanoidRagdollPreset

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7230 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
rem..om 14 years ago
parent 3b9cf4cd35
commit 1f286f424f
  1. 99
      engine/src/jbullet/com/jme3/bullet/control/HumanoidRagdollPreset.java
  2. 81
      engine/src/jbullet/com/jme3/bullet/control/RagdollControl.java
  3. 108
      engine/src/jbullet/com/jme3/bullet/control/RagdollPreset.java
  4. 56
      engine/src/test/jme3test/bullet/TestBoneRagdoll.java

@ -0,0 +1,99 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.bullet.control;
import com.jme3.math.FastMath;
/**
*
* @author Nehon
*/
public class HumanoidRagdollPreset extends RagdollPreset {
@Override
protected void initBoneMap() {
boneMap.put("head", new JointPreset(FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI));
boneMap.put("torso", new JointPreset(FastMath.QUARTER_PI, -FastMath.QUARTER_PI, 0, 0, FastMath.QUARTER_PI, -FastMath.QUARTER_PI));
boneMap.put("upperleg", new JointPreset(FastMath.PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI));
boneMap.put("lowerleg", new JointPreset(0, -FastMath.PI, 0, 0, 0, 0));
boneMap.put("foot", new JointPreset(0, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI));
boneMap.put("upperarm", new JointPreset(FastMath.HALF_PI, -FastMath.QUARTER_PI, 0, 0, FastMath.QUARTER_PI, -FastMath.HALF_PI));
boneMap.put("lowerarm", new JointPreset(FastMath.PI, 0, 0, 0, 0, 0));
boneMap.put("hand", new JointPreset(FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI));
}
@Override
protected void initLexicon() {
LexiconEntry entry = new LexiconEntry();
entry.addSynonym("head", 100);
lexicon.put("head", entry);
entry = new LexiconEntry();
entry.addSynonym("torso", 100);
entry.addSynonym("chest", 100);
entry.addSynonym("spine", 45);
entry.addSynonym("high", 25);
lexicon.put("torso", entry);
entry = new LexiconEntry();
entry.addSynonym("upperleg", 100);
entry.addSynonym("thigh", 100);
entry.addSynonym("hip", 75);
entry.addSynonym("leg", 40);
entry.addSynonym("high", 10);
entry.addSynonym("up", 15);
entry.addSynonym("upper", 15);
lexicon.put("upperleg", entry);
entry = new LexiconEntry();
entry.addSynonym("lowerleg", 100);
entry.addSynonym("calf", 100);
entry.addSynonym("knee", 75);
entry.addSynonym("leg", 50);
entry.addSynonym("low", 10);
entry.addSynonym("lower", 10);
lexicon.put("lowerleg", entry);
entry = new LexiconEntry();
entry.addSynonym("foot", 100);
entry.addSynonym("ankle", 75);
lexicon.put("foot", entry);
entry = new LexiconEntry();
entry.addSynonym("upperarm", 100);
entry.addSynonym("humerus", 100);
entry.addSynonym("shoulder", 50);
entry.addSynonym("arm", 40);
entry.addSynonym("high", 10);
entry.addSynonym("up", 15);
entry.addSynonym("upper", 15);
lexicon.put("upperarm", entry);
entry = new LexiconEntry();
entry.addSynonym("lowerarm", 100);
entry.addSynonym("ulna", 100);
entry.addSynonym("elbow", 75);
entry.addSynonym("arm", 50);
entry.addSynonym("low", 10);
entry.addSynonym("lower", 10);
lexicon.put("lowerarm", entry);
entry = new LexiconEntry();
entry.addSynonym("hand", 100);
entry.addSynonym("fist", 100);
entry.addSynonym("wrist", 75);
lexicon.put("hand", entry);
}
}

@ -58,6 +58,9 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
protected Vector3f initScale;
protected boolean control = false;
protected List<RagdollCollisionListener> listeners;
protected float eventDispatchImpulseThreshold = 10;
protected float eventDiscardImpulseThreshold = 3;
protected RagdollPreset preset = new HumanoidRagdollPreset();
public RagdollControl() {
}
@ -162,7 +165,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
// maybe dont reset to ragdoll out of animations?
scanSpatial(model);
System.out.println("parent " + parent);
if (parent != null) {
parent.attachChild(model);
@ -193,14 +196,14 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
baseRigidBody = new PhysicsRigidBody(new BoxCollisionShape(Vector3f.UNIT_XYZ.mult(.1f)), 1);
baseRigidBody.setPhysicsLocation(parentPos);
// baseRigidBody.setPhysicsRotation(parentRot);
boneLinks = boneRecursion(model, childBone, baseRigidBody, boneLinks, 1);
boneRecursion(model, childBone, baseRigidBody, 1);
return;
}
}
}
private Map<String, PhysicsBoneLink> boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, Map<String, PhysicsBoneLink> list, int reccount) {
private void boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, int reccount) {
//get world space position of the bone
Vector3f pos = bone.getModelSpacePosition().add(model.getLocalTranslation());
@ -209,14 +212,15 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
//creating the collision shape from the bone's associated vertices
PhysicsRigidBody shapeNode = new PhysicsRigidBody(makeShape(bone, model), 10.0f / (float) reccount);
shapeNode.setPhysicsLocation(pos);
// shapeNode.setPhysicsRotation(rot);
// shapeNode.setPhysicsRotation(rot);
PhysicsBoneLink link = new PhysicsBoneLink();
link.bone = bone;
link.rigidBody = shapeNode;
link.initalWorldRotation = bone.getModelSpaceRotation().clone();
// link.mass = 10.0f / (float) reccount;
// link.mass = 10.0f / (float) reccount;
//TODO: ragdoll mass 1
if (parent != null) {
@ -233,18 +237,20 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
SixDofJoint joint = new SixDofJoint(parent, shapeNode, link.pivotA, link.pivotB, true);
//TODO find a way to correctly compute/import joints (maybe based on their names)
setJointLimit(joint, 0, 0, 0, 0, 0, 0);
preset.setupJointForBone(bone.getName(), joint);
//setJointLimit(joint, 0, 0, 0, 0, 0, 0);
link.joint = joint;
joint.setCollisionBetweenLinkedBodys(false);
}
list.put(bone.getName(), link);
boneLinks.put(bone.getName(), link);
shapeNode.setUserObject(link);
for (Iterator<Bone> it = bone.getChildren().iterator(); it.hasNext();) {
Bone childBone = it.next();
boneRecursion(model, childBone, shapeNode, list, reccount++);
boneRecursion(model, childBone, shapeNode, reccount++);
}
return list;
}
/**
@ -317,8 +323,7 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
}
private HullCollisionShape makeShape(Bone bone, Spatial model) {
int boneIndex = skeleton.getBoneIndex(bone);
System.out.println(boneIndex);
int boneIndex = skeleton.getBoneIndex(bone);
ArrayList<Float> points = new ArrayList<Float>();
if (model instanceof Geometry) {
Geometry g = (Geometry) model;
@ -396,7 +401,9 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
}
public void setEnabled(boolean enabled) {
if(this.enabled == enabled) return;
if (this.enabled == enabled) {
return;
}
this.enabled = enabled;
if (!enabled && space != null) {
removeFromPhysicsSpace();
@ -473,14 +480,11 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
PhysicsCollisionObject objA = event.getObjectA();
PhysicsCollisionObject objB = event.getObjectB();
if (event.getNodeA() != null && "Floor".equals(event.getNodeA().getName())) {
return;
}
if (event.getNodeB() != null && "Floor".equals(event.getNodeB().getName())) {
if (event.getNodeA() == null && event.getNodeB() == null) {
return;
}
if (event.getAppliedImpulse() < 3.0) {
if (event.getAppliedImpulse() < eventDiscardImpulseThreshold) {
return;
}
@ -488,31 +492,27 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
Bone hitBone = null;
PhysicsCollisionObject hitObject = null;
if (objA instanceof PhysicsRigidBody) {
PhysicsRigidBody prb = (PhysicsRigidBody) objA;
for (PhysicsBoneLink physicsBoneLink : boneLinks.values()) {
if (physicsBoneLink.rigidBody == prb) {
hit = true;
hitBone = physicsBoneLink.bone;
hitObject = objB;
// System.out.println("objA " + physicsBoneLink.bone.getName() + " " + event.getAppliedImpulse() + " " + event.getAppliedImpulseLateral1() + " " + event.getAppliedImpulseLateral2());
}
if (objA.getUserObject() instanceof PhysicsBoneLink) {
PhysicsBoneLink link = (PhysicsBoneLink) objA.getUserObject();
if (link != null) {
hit = true;
hitBone = link.bone;
hitObject = objB;
}
}
if (objB instanceof PhysicsRigidBody) {
PhysicsRigidBody prb = (PhysicsRigidBody) objB;
for (PhysicsBoneLink physicsBoneLink : boneLinks.values()) {
if (physicsBoneLink.rigidBody == prb) {
hit = false;
hitBone = physicsBoneLink.bone;
hitObject = objA;
// System.out.println("objB " + physicsBoneLink.bone.getName() + " " + event.getAppliedImpulse() + " " + event.getAppliedImpulseLateral1() + " " + event.getAppliedImpulseLateral2());
}
if (objB.getUserObject() instanceof PhysicsBoneLink) {
PhysicsBoneLink link = (PhysicsBoneLink) objB.getUserObject();
if (link != null) {
hit = true;
hitBone = link.bone;
hitObject = objA;
}
}
if (hit && event.getAppliedImpulse() > 10.0) {
System.out.println("trigger impact " + event.getNodeA() + " " + event.getNodeB() + " " + event.getAppliedImpulse());
if (hit && event.getAppliedImpulse() > eventDispatchImpulseThreshold) {
// System.out.println("trigger impact " + event.getNodeA() + " " + event.getNodeB() + " " + event.getAppliedImpulse());
//setControl(true);
for (RagdollCollisionListener listener : listeners) {
listener.collide(hitBone, hitObject);
@ -553,12 +553,13 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener
}
protected static class PhysicsBoneLink {
Bone bone;
Quaternion initalWorldRotation;
SixDofJoint joint;
PhysicsRigidBody rigidBody;
Vector3f pivotA;
Vector3f pivotB;
// float mass;
// float mass;
}
}

@ -0,0 +1,108 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package com.jme3.bullet.control;
import com.jme3.bullet.joints.SixDofJoint;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author Nehon
*/
public abstract class RagdollPreset {
protected static final Logger logger = Logger.getLogger(RagdollPreset.class.getName());
protected Map<String, JointPreset> boneMap = new HashMap<String, JointPreset>();
protected Map<String, LexiconEntry> lexicon = new HashMap<String, LexiconEntry>();
protected abstract void initBoneMap();
protected abstract void initLexicon();
public void setupJointForBone(String boneName, SixDofJoint joint) {
if (boneMap.isEmpty()) {
initBoneMap();
}
if (lexicon.isEmpty()) {
initLexicon();
}
String resultName = "";
int resultScore = 0;
System.out.println("-------------- " +boneName);
for (String key : lexicon.keySet()) {
int score = lexicon.get(key).getScore(boneName);
System.out.println(key+" " +score);
if (score > resultScore) {
resultScore = score;
resultName = key;
}
}
System.out.println("-------------- ");
JointPreset preset = boneMap.get(resultName);
if (preset != null && resultScore >= 50) {
logger.log(Level.INFO, "Found matching joint for bone {0} : {1} with score {2}", new Object[]{boneName, resultName, resultScore});
preset.setupJoint(joint);
} else {
logger.log(Level.INFO, "No joint match found for bone {0}", boneName);
if (resultScore > 0) {
logger.log(Level.INFO, "Best match found is {0} with score {1}", new Object[]{resultName, resultScore});
}
new JointPreset().setupJoint(joint);
}
}
protected class JointPreset {
private float maxX, minX, maxY, minY, maxZ, minZ;
public JointPreset() {
}
public JointPreset(float maxX, float minX, float maxY, float minY, float maxZ, float minZ) {
this.maxX = maxX;
this.minX = minX;
this.maxY = maxY;
this.minY = minY;
this.maxZ = maxZ;
this.minZ = minZ;
}
public void setupJoint(SixDofJoint joint) {
joint.getRotationalLimitMotor(0).setHiLimit(maxX);
joint.getRotationalLimitMotor(0).setLoLimit(minX);
joint.getRotationalLimitMotor(1).setHiLimit(maxY);
joint.getRotationalLimitMotor(1).setLoLimit(minY);
joint.getRotationalLimitMotor(2).setHiLimit(maxZ);
joint.getRotationalLimitMotor(2).setLoLimit(minZ);
}
}
protected class LexiconEntry extends HashMap<String, Integer> {
public void addSynonym(String word, int score) {
put(word.toLowerCase(), score);
}
public int getScore(String word) {
int score = 0;
String searchWord = word.toLowerCase();
for (String key : this.keySet()) {
if (searchWord.indexOf(key) >= 0) {
score += get(key);
}
}
return score;
}
}
}

@ -91,7 +91,7 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
bulletAppState = new BulletAppState();
bulletAppState.setEnabled(true);
stateManager.attach(bulletAppState);
// bulletAppState.getPhysicsSpace().enableDebug(assetManager);
// bulletAppState.getPhysicsSpace().enableDebug(assetManager);
PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
setupLight();
@ -107,12 +107,6 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
mat2.getAdditionalRenderState().setDepthTest(false);
skeletonDebug.setMaterial(mat2);
skeletonDebug.setLocalTranslation(model.getLocalTranslation());
// AnimChannel channel=control.createChannel();
// channel.setAnim("Dodge");
// channel.setLoopMode(LoopMode.Cycle);
// channel.setSpeed(0.1f);
//Note: PhysicsRagdollControl is still TODO, constructor will change
ragdoll = new RagdollControl(1.0f);
@ -121,41 +115,15 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
float eighth_pi = FastMath.PI * 0.125f;
ragdoll.setJointLimit("head", eighth_pi, -eighth_pi, eighth_pi, -eighth_pi, eighth_pi, -eighth_pi);
ragdoll.setJointLimit("spinehigh", FastMath.QUARTER_PI, -FastMath.QUARTER_PI, 0, 0, FastMath.QUARTER_PI, -FastMath.QUARTER_PI);
ragdoll.setJointLimit("hip.right", FastMath.PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI);
ragdoll.setJointLimit("hip.left", FastMath.PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI);
ragdoll.setJointLimit("leg.left", 0, -FastMath.PI, 0, 0, 0, 0);
ragdoll.setJointLimit("leg.right", 0, -FastMath.PI, 0, 0, 0, 0);
ragdoll.setJointLimit("foot.right", 0, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI);
ragdoll.setJointLimit("foot.left", 0, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI);
ragdoll.setJointLimit("uparm.right", FastMath.HALF_PI, -FastMath.QUARTER_PI, 0, 0, FastMath.QUARTER_PI, -FastMath.HALF_PI);
ragdoll.setJointLimit("uparm.left", FastMath.HALF_PI, -FastMath.QUARTER_PI, 0, 0, FastMath.QUARTER_PI, -FastMath.HALF_PI);
ragdoll.setJointLimit("arm.right", FastMath.PI, 0, 0, 0, 0, 0);
ragdoll.setJointLimit("arm.left", FastMath.PI, 0, 0, 0, 0, 0);
ragdoll.setJointLimit("hand.right", FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI);
ragdoll.setJointLimit("hand.left", FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI, FastMath.QUARTER_PI, -FastMath.QUARTER_PI);
//Oto's head is almost rigid
ragdoll.setJointLimit("head", 0, 0, eighth_pi, -eighth_pi, 0, 0);
getPhysicsSpace().add(ragdoll);
speed = 1.3f;
rootNode.attachChild(model);
// rootNode.attachChild(skeletonDebug);
//flyCam.setEnabled(false);
flyCam.setMoveSpeed(50);
// ChaseCamera chaseCamera=new ChaseCamera(cam, inputManager);
// chaseCamera.setLookAtOffset(Vector3f.UNIT_Y.mult(4));
// model.addControl(chaseCamera);
final AnimChannel channel = control.createChannel();
channel.setAnim("Walk");
@ -176,8 +144,8 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
bulletg.setLocalTranslation(cam.getLocation());
bulletg.setLocalScale(timer);
bulletCollisionShape = new SphereCollisionShape(timer);
RigidBodyControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1);
// RigidBodyControl bulletNode = new RigidBodyControl(bulletCollisionShape, timer * 10);
// RigidBodyControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1);
RigidBodyControl bulletNode = new RigidBodyControl(bulletCollisionShape, timer * 10);
bulletNode.setCcdMotionThreshold(0.001f);
bulletNode.setLinearVelocity(cam.getDirection().mult(80));
bulletg.addControl(bulletNode);
@ -195,8 +163,8 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
private void setupLight() {
AmbientLight al = new AmbientLight();
al.setColor(ColorRGBA.White.mult(10));
rootNode.addLight(al);
// al.setColor(ColorRGBA.White.mult(1));
// rootNode.addLight(al);
DirectionalLight dl = new DirectionalLight();
dl.setDirection(new Vector3f(-0.1f, -0.7f, -1).normalizeLocal());
@ -240,6 +208,14 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
public void collide(Bone bone, PhysicsCollisionObject object) {
if(object.getUserObject()!=null && object.getUserObject() instanceof Geometry){
Geometry geom=(Geometry)object.getUserObject();
if ("Floor".equals(geom.getName())) {
return;
}
}
if (!ragdoll.hasControl()) {
// bulletTime();

Loading…
Cancel
Save