diff --git a/engine/src/jbullet/com/jme3/bullet/control/HumanoidRagdollPreset.java b/engine/src/jbullet/com/jme3/bullet/control/HumanoidRagdollPreset.java new file mode 100644 index 000000000..c3455760c --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/control/HumanoidRagdollPreset.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); + + } +} diff --git a/engine/src/jbullet/com/jme3/bullet/control/RagdollControl.java b/engine/src/jbullet/com/jme3/bullet/control/RagdollControl.java index 832111674..3c755eedc 100644 --- a/engine/src/jbullet/com/jme3/bullet/control/RagdollControl.java +++ b/engine/src/jbullet/com/jme3/bullet/control/RagdollControl.java @@ -58,6 +58,9 @@ public class RagdollControl implements PhysicsControl, PhysicsCollisionListener protected Vector3f initScale; protected boolean control = false; protected List 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 boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, Map 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 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 points = new ArrayList(); 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; } } diff --git a/engine/src/jbullet/com/jme3/bullet/control/RagdollPreset.java b/engine/src/jbullet/com/jme3/bullet/control/RagdollPreset.java new file mode 100644 index 000000000..415b719fa --- /dev/null +++ b/engine/src/jbullet/com/jme3/bullet/control/RagdollPreset.java @@ -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 boneMap = new HashMap(); + protected Map lexicon = new HashMap(); + + 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 { + + 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; + } + } +} diff --git a/engine/src/test/jme3test/bullet/TestBoneRagdoll.java b/engine/src/test/jme3test/bullet/TestBoneRagdoll.java index e759cf830..359a3711a 100644 --- a/engine/src/test/jme3test/bullet/TestBoneRagdoll.java +++ b/engine/src/test/jme3test/bullet/TestBoneRagdoll.java @@ -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();