OSDN Git Service

KinematicRagdollControl :
authorremy.bouquet@gmail.com <remy.bouquet@gmail.com@75d07b2b-3a1a-0410-a2c5-0572b91ccdca>
Sun, 1 May 2011 09:18:29 +0000 (09:18 +0000)
committerremy.bouquet@gmail.com <remy.bouquet@gmail.com@75d07b2b-3a1a-0410-a2c5-0572b91ccdca>
Sun, 1 May 2011 09:18:29 +0000 (09:18 +0000)
- Made a lot of clean up and optimization
- Better automagic creation of the ragdoll

git-svn-id: http://jmonkeyengine.googlecode.com/svn/trunk@7379 75d07b2b-3a1a-0410-a2c5-0572b91ccdca

engine/src/jbullet/com/jme3/bullet/control/KinematicRagdollControl.java
engine/src/jbullet/com/jme3/bullet/control/ragdoll/RagdollUtils.java [new file with mode: 0644]
engine/src/test/jme3test/bullet/TestBoneRagdoll.java

index 28a3fdb..e9783ba 100644 (file)
  */
 package com.jme3.bullet.control;
 
-import com.jme3.animation.AnimChannel;
 import com.jme3.bullet.control.ragdoll.RagdollPreset;
 import com.jme3.bullet.control.ragdoll.HumanoidRagdollPreset;
 import com.jme3.animation.AnimControl;
 import com.jme3.animation.Bone;
-import com.jme3.animation.LoopMode;
 import com.jme3.animation.Skeleton;
 import com.jme3.animation.SkeletonControl;
 import com.jme3.asset.AssetManager;
@@ -47,6 +45,7 @@ import com.jme3.bullet.collision.PhysicsCollisionObject;
 import com.jme3.bullet.collision.RagdollCollisionListener;
 import com.jme3.bullet.collision.shapes.BoxCollisionShape;
 import com.jme3.bullet.collision.shapes.HullCollisionShape;
+import com.jme3.bullet.control.ragdoll.RagdollUtils;
 import com.jme3.bullet.joints.SixDofJoint;
 import com.jme3.bullet.objects.PhysicsRigidBody;
 import com.jme3.export.JmeExporter;
@@ -56,22 +55,18 @@ import com.jme3.math.Transform;
 import com.jme3.math.Vector3f;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
-import com.jme3.scene.Geometry;
-import com.jme3.scene.Mesh;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
-import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.control.Control;
 import com.jme3.util.TempVars;
 import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.FloatBuffer;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -114,7 +109,7 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
     protected boolean enabled = true;
     protected boolean debug = false;
     protected PhysicsRigidBody baseRigidBody;
-    protected float weightThreshold = 1.0f;
+    protected float weightThreshold = -1.0f;
     protected Spatial targetModel;
     protected Vector3f initScale;
     protected Mode mode = Mode.Kinetmatic;
@@ -124,19 +119,29 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
     protected List<RagdollCollisionListener> listeners;
     protected float eventDispatchImpulseThreshold = 10;
     protected RagdollPreset preset = new HumanoidRagdollPreset();
-    protected List<String> boneList = new LinkedList<String>();
+    protected Set<String> boneList = new TreeSet<String>();
     protected Vector3f modelPosition = new Vector3f();
     protected Quaternion modelRotation = new Quaternion();
     protected float rootMass = 15;
     protected float totalMass = 0;
     protected boolean added = false;
 
-    public enum Mode {
+    public static enum Mode {
 
         Kinetmatic,
         Ragdoll
     }
 
+    protected class PhysicsBoneLink {
+
+        protected Bone bone;
+        protected Quaternion initalWorldRotation;
+        protected SixDofJoint joint;
+        protected PhysicsRigidBody rigidBody;
+        protected Quaternion startBlendingRot = new Quaternion();
+        protected Vector3f startBlendingPos = new Vector3f();
+    }
+
     /**
      * contruct a KinematicRagdollControl
      */
@@ -206,7 +211,7 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
                     } 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
-                        setTransform(link.bone, position, tmpRot1, false);
+                        RagdollUtils.setTransform(link.bone, position, tmpRot1, false, boneList);
                     }
                 }
             }
@@ -238,7 +243,7 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
                         //we give control back to the key framed animation.
                         link.bone.setUserControl(false);
                     } else {
-                        setTransform(link.bone, position, tmpRot1, true);
+                        RagdollUtils.setTransform(link.bone, position, tmpRot1, true, boneList);
                     }
 
                 }
@@ -252,7 +257,6 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
                 blendStart += tpf;
                 if (blendStart > blendTime) {
                     blendedControl = false;
-
                 }
             }
         }
@@ -261,35 +265,6 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
     }
 
     /**
-     * Updates a bone position and rotation.
-     * if the child bones are not in the bone list this means, they are not associated with a physic shape.
-     * So they have to be updated
-     * @param bone the bone
-     * @param pos the position
-     * @param rot the rotation
-     */
-    private void setTransform(Bone bone, Vector3f pos, Quaternion rot, boolean restoreBoneControl) {
-        //we ensure that we have the control
-        if (restoreBoneControl) {
-            bone.setUserControl(true);
-        }
-        //we set te user transforms of the bone
-        bone.setUserTransformsWorld(pos, rot);
-        for (Bone childBone : bone.getChildren()) {
-            //each child bone that is not in the list is updated
-            if (!boneList.contains(childBone.getName())) {
-                Transform t = childBone.getCombinedTransform(pos, rot);
-                setTransform(childBone, t.getTranslation(), t.getRotation(), restoreBoneControl);
-            }
-        }
-        //we give back the control to the keyframed animation
-        if (restoreBoneControl) {
-            bone.setUserControl(false);
-        }
-
-    }
-
-    /**
      * 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
      * @param link the link containing the bone and the rigidBody
@@ -309,16 +284,17 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
         link.rigidBody.setPhysicsLocation(position);
         link.rigidBody.setPhysicsRotation(tmpRot1);
 
-//        position.set(link.bone.getModelSpaceScale()).multLocal(targetModel.getWorldScale());
-//        //TODO support scale!
-//        link.rigidBody.getCollisionShape().setScale(position);
     }
 
     public Control cloneForSpatial(Spatial spatial) {
         throw new UnsupportedOperationException("Not supported yet.");
     }
 
-    public void reInit() {
+    /**
+     * rebuild the ragdoll
+     * this is useful if you applied scale on the ragdoll after it's been initialized
+     */
+    public void reBuild() {
         setSpatial(targetModel);
         addToPhysicsSpace();
     }
@@ -367,12 +343,21 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
         logger.log(Level.INFO, "Created physics ragdoll for skeleton {0}", skeleton);
     }
 
+    /**
+     * 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.
+     * @param name 
+     */
     public void addBoneName(String name) {
         boneList.add(name);
     }
 
     private void scanSpatial(Spatial model) {
         AnimControl animControl = model.getControl(AnimControl.class);
+        Map<Integer, List<Float>> pointsMap = null;
+        if (weightThreshold == -1.0f) {
+            pointsMap = RagdollUtils.buildPointMap(model);
+        }
 
         skeleton = animControl.getSkeleton();
         skeleton.resetAndUpdate();
@@ -382,19 +367,29 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
                 logger.log(Level.INFO, "Found root bone in skeleton {0}", skeleton);
                 baseRigidBody = new PhysicsRigidBody(new BoxCollisionShape(Vector3f.UNIT_XYZ.mult(0.1f)), 1);
                 baseRigidBody.setKinematic(mode == Mode.Kinetmatic);
-                boneRecursion(model, childBone, baseRigidBody, 1);
+                boneRecursion(model, childBone, baseRigidBody, 1, pointsMap);
             }
         }
     }
 
-    private void boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, int reccount) {
+    private void boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, int reccount, Map<Integer, List<Float>> pointsMap) {
         PhysicsRigidBody parentShape = parent;
         if (boneList.isEmpty() || boneList.contains(bone.getName())) {
 
             PhysicsBoneLink link = new PhysicsBoneLink();
             link.bone = bone;
-            //creating the collision shape from the bone's associated vertices
-            PhysicsRigidBody shapeNode = new PhysicsRigidBody(makeShape(link, model), rootMass / (float) reccount);
+
+            //creating the collision shape 
+            HullCollisionShape shape = null;
+            if (pointsMap != null) {
+                //build a shape for the bone, using the vertices that are most influenced by this bone
+                shape = RagdollUtils.makeShapeFromPointMap(pointsMap, RagdollUtils.getBoneIndices(link.bone, skeleton, boneList), initScale, link.bone.getModelSpacePosition());
+            } else {
+                //build a shape for the bone, using the vertices associated with this bone with a weight above the threshold
+                shape = RagdollUtils.makeShapeFromVerticeWeights(model, RagdollUtils.getBoneIndices(link.bone, skeleton, boneList), initScale, link.bone.getModelSpacePosition(), weightThreshold);
+            }
+
+            PhysicsRigidBody shapeNode = new PhysicsRigidBody(shape, rootMass / (float) reccount);
             shapeNode.setKinematic(mode == Mode.Kinetmatic);
             totalMass += rootMass / (float) reccount;
 
@@ -408,12 +403,7 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
                     bone.getModelSpacePosition().subtract(bone.getParent().getModelSpacePosition(), posToParent).multLocal(initScale);
                 }
 
-                //Joint local position from parent
-                link.pivotA = posToParent;
-                //joint local position from current bone
-                link.pivotB = new Vector3f(0, 0, 0f);
-
-                SixDofJoint joint = new SixDofJoint(parent, shapeNode, link.pivotA, link.pivotB, true);
+                SixDofJoint joint = new SixDofJoint(parent, shapeNode, posToParent, new Vector3f(0, 0, 0f), true);
                 preset.setupJointForBone(bone.getName(), joint);
 
                 link.joint = joint;
@@ -426,10 +416,8 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
 
         for (Iterator<Bone> it = bone.getChildren().iterator(); it.hasNext();) {
             Bone childBone = it.next();
-            boneRecursion(model, childBone, parentShape, reccount + 1);
+            boneRecursion(model, childBone, parentShape, reccount + 1, pointsMap);
         }
-
-
     }
 
     /**
@@ -446,7 +434,7 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
     public void setJointLimit(String boneName, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) {
         PhysicsBoneLink link = boneLinks.get(boneName);
         if (link != null) {
-            setJointLimit(link.joint, maxX, minX, maxY, minY, maxZ, minZ);
+            RagdollUtils.setJointLimit(link.joint, maxX, minX, maxY, minY, maxZ, minZ);
         } else {
             logger.log(Level.WARNING, "Not joint was found for bone {0}. make sure you call spatial.addControl(ragdoll) before setting joints limit", boneName);
         }
@@ -468,16 +456,6 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
         }
     }
 
-    private void setJointLimit(SixDofJoint joint, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) {
-
-        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);
-    }
-
     private void clearData() {
         boneLinks.clear();
         baseRigidBody = null;
@@ -504,111 +482,6 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
         }
     }
 
-    /**
-     * Create a hull collision shape from linked vertices to this bone.
-     * 
-     * @param link
-     * @param model
-     * @return 
-     */
-    protected HullCollisionShape makeShape(PhysicsBoneLink link, Spatial model) {
-        Bone bone = link.bone;
-        List<Integer> boneIndices = null;
-        if (boneList.isEmpty()) {
-            boneIndices = new LinkedList<Integer>();
-            boneIndices.add(skeleton.getBoneIndex(bone));
-        } else {
-            boneIndices = getBoneIndices(bone, skeleton);
-        }
-
-        ArrayList<Float> points = new ArrayList<Float>();
-        if (model instanceof Geometry) {
-            Geometry g = (Geometry) model;
-            for (Integer index : boneIndices) {
-                points.addAll(getPoints(g.getMesh(), index, bone.getModelSpacePosition()));
-            }
-        } else if (model instanceof Node) {
-            Node node = (Node) model;
-            for (Spatial s : node.getChildren()) {
-                if (s instanceof Geometry) {
-                    Geometry g = (Geometry) s;
-                    for (Integer index : boneIndices) {
-                        points.addAll(getPoints(g.getMesh(), index, bone.getModelSpacePosition()));
-                    }
-
-                }
-            }
-        }
-        float[] p = new float[points.size()];
-        for (int i = 0; i < points.size(); i++) {
-            p[i] = points.get(i);
-        }
-
-
-        return new HullCollisionShape(p);
-    }
-
-    //retruns the list of bone indices of the given bone and its child(if they are not in the boneList)
-    protected List<Integer> getBoneIndices(Bone bone, Skeleton skeleton) {
-        List<Integer> list = new LinkedList<Integer>();
-        list.add(skeleton.getBoneIndex(bone));
-        for (Bone chilBone : bone.getChildren()) {
-            if (!boneList.contains(chilBone.getName())) {
-                list.addAll(getBoneIndices(chilBone, skeleton));
-            }
-        }
-        return list;
-    }
-
-    /**
-     * returns a list of points for the given bone
-     * @param mesh
-     * @param boneIndex
-     * @param offset
-     * @param link
-     * @return 
-     */
-    private List<Float> getPoints(Mesh mesh, int boneIndex, Vector3f offset) {
-
-        FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
-        ByteBuffer boneIndices = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
-        FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
-
-        vertices.rewind();
-        boneIndices.rewind();
-        boneWeight.rewind();
-
-        ArrayList<Float> results = new ArrayList<Float>();
-
-        int vertexComponents = mesh.getVertexCount() * 3;
-
-        for (int i = 0; i < vertexComponents; i += 3) {
-            int k;
-            boolean add = false;
-            int start = i / 3 * 4;
-            for (k = start; k < start + 4; k++) {
-                if (boneIndices.get(k) == boneIndex && boneWeight.get(k) >= weightThreshold) {
-                    add = true;
-                    break;
-                }
-            }
-            if (add) {
-
-                Vector3f pos = new Vector3f();
-                pos.x = vertices.get(i);
-                pos.y = vertices.get(i + 1);
-                pos.z = vertices.get(i + 2);
-                pos.subtractLocal(offset).multLocal(initScale);
-                results.add(pos.x);
-                results.add(pos.y);
-                results.add(pos.z);
-
-            }
-        }
-
-        return results;
-    }
-
     protected void removeFromPhysicsSpace() {
         if (space == null) {
             return;
@@ -818,28 +691,7 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
         assert vars.unlock();
 
         for (Bone bone : skeleton.getRoots()) {
-            setUserControl(bone, mode == Mode.Ragdoll);
-        }
-    }
-
-    /**
-     * Set the control into Kinematic mode
-     * In theis mode, the collision shapes follow the movements of the skeleton,
-     * and can interact with physical environement
-     */
-    public void setKinematicMode() {
-        if (mode != Mode.Kinetmatic) {
-            setMode(Mode.Kinetmatic);
-        }
-    }
-
-    /**
-     * Sets the control into Ragdoll mode
-     * The skeleton is entirely controlled by physics.
-     */
-    public void setRagdollMode() {
-        if (mode != Mode.Ragdoll) {
-            setMode(Mode.Ragdoll);
+            RagdollUtils.setUserControl(bone, mode == Mode.Ragdoll);
         }
     }
 
@@ -883,16 +735,30 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
         assert vars.unlock();
 
         for (Bone bone : skeleton.getRoots()) {
-            setUserControl(bone, false);
+            RagdollUtils.setUserControl(bone, false);
         }
 
         blendStart = 0;
     }
 
-    private void setUserControl(Bone bone, boolean bool) {
-        bone.setUserControl(bool);
-        for (Bone child : bone.getChildren()) {
-            setUserControl(child, bool);
+    /**
+     * Set the control into Kinematic mode
+     * In theis mode, the collision shapes follow the movements of the skeleton,
+     * and can interact with physical environement
+     */
+    public void setKinematicMode() {
+        if (mode != Mode.Kinetmatic) {
+            setMode(Mode.Kinetmatic);
+        }
+    }
+
+    /**
+     * Sets the control into Ragdoll mode
+     * The skeleton is entirely controlled by physics.
+     */
+    public void setRagdollMode() {
+        if (mode != Mode.Ragdoll) {
+            setMode(Mode.Ragdoll);
         }
     }
 
@@ -915,18 +781,6 @@ public class KinematicRagdollControl implements PhysicsControl, PhysicsCollision
         listeners.add(listener);
     }
 
-    protected static class PhysicsBoneLink {
-
-        Bone bone;
-        Quaternion initalWorldRotation;
-        SixDofJoint joint;
-        PhysicsRigidBody rigidBody;
-        Vector3f pivotA;
-        Vector3f pivotB;
-        Quaternion startBlendingRot = new Quaternion();
-        Vector3f startBlendingPos = new Vector3f();
-    }
-
     public void setRootMass(float rootMass) {
         this.rootMass = rootMass;
     }
diff --git a/engine/src/jbullet/com/jme3/bullet/control/ragdoll/RagdollUtils.java b/engine/src/jbullet/com/jme3/bullet/control/ragdoll/RagdollUtils.java
new file mode 100644 (file)
index 0000000..7aeb3a9
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.bullet.control.ragdoll;
+
+import com.jme3.animation.Bone;
+import com.jme3.animation.Skeleton;
+import com.jme3.bullet.collision.shapes.HullCollisionShape;
+import com.jme3.bullet.joints.SixDofJoint;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Transform;
+import com.jme3.math.Vector3f;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.scene.VertexBuffer.Type;
+import java.nio.ByteBuffer;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ *
+ * @author Nehon
+ */
+public class RagdollUtils {
+
+    public static void setJointLimit(SixDofJoint joint, float maxX, float minX, float maxY, float minY, float maxZ, float minZ) {
+
+        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);
+    }
+
+    public static Map<Integer, List<Float>> buildPointMap(Spatial model) {
+
+
+        Map<Integer, List<Float>> map = new HashMap<Integer, List<Float>>();
+        if (model instanceof Geometry) {
+            Geometry g = (Geometry) model;
+            buildPointMapForMesh(g.getMesh(), map);
+        } else if (model instanceof Node) {
+            Node node = (Node) model;
+            for (Spatial s : node.getChildren()) {
+                if (s instanceof Geometry) {
+                    Geometry g = (Geometry) s;
+                    buildPointMapForMesh(g.getMesh(), map);
+                }
+            }
+        }
+        return map;
+    }
+
+    private static Map<Integer, List<Float>> buildPointMapForMesh(Mesh mesh, Map<Integer, List<Float>> map) {
+
+        FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
+        ByteBuffer boneIndices = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
+        FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
+
+        vertices.rewind();
+        boneIndices.rewind();
+        boneWeight.rewind();
+
+        int vertexComponents = mesh.getVertexCount() * 3;
+        int k, start, index;
+        float maxWeight = 0;
+
+        for (int i = 0; i < vertexComponents; i += 3) {
+
+
+            start = i / 3 * 4;
+            index = 0;
+            maxWeight = -1;
+            for (k = start; k < start + 4; k++) {
+                float weight = boneWeight.get(k);
+                if (weight > maxWeight) {
+                    maxWeight = weight;
+                    index = boneIndices.get(k);
+                }
+            }
+            List<Float> points = map.get(index);
+            if (points == null) {
+                points = new ArrayList<Float>();
+                map.put(index, points);
+            }
+            points.add(vertices.get(i));
+            points.add(vertices.get(i + 1));
+            points.add(vertices.get(i + 2));
+        }
+        return map;
+    }
+
+    /**
+     * Create a hull collision shape from linked vertices to this bone.
+     * Vertices have to be previoulsly gathered in a map using buildPointMap method
+     * @param link
+     * @param model
+     * @return 
+     */
+    public static HullCollisionShape makeShapeFromPointMap(Map<Integer, List<Float>> pointsMap, List<Integer> boneIndices, Vector3f initialScale, Vector3f initialPosition) {
+
+        ArrayList<Float> points = new ArrayList<Float>();
+        for (Integer index : boneIndices) {
+            List<Float> l = pointsMap.get(index);
+            if (l != null) {
+
+                for (int i = 0; i < l.size(); i += 3) {
+                    Vector3f pos = new Vector3f();
+                    pos.x = l.get(i);
+                    pos.y = l.get(i + 1);
+                    pos.z = l.get(i + 2);
+                    pos.subtractLocal(initialPosition).multLocal(initialScale);
+                    points.add(pos.x);
+                    points.add(pos.y);
+                    points.add(pos.z);
+                }
+            }
+        }
+
+        float[] p = new float[points.size()];
+        for (int i = 0; i < points.size(); i++) {
+            p[i] = points.get(i);
+        }
+
+
+        return new HullCollisionShape(p);
+    }
+
+    //retruns the list of bone indices of the given bone and its child(if they are not in the boneList)
+    public static List<Integer> getBoneIndices(Bone bone, Skeleton skeleton, Set<String> boneList) {
+        List<Integer> list = new LinkedList<Integer>();
+        if (boneList.isEmpty()) {
+            list.add(skeleton.getBoneIndex(bone));
+        } else {
+            list.add(skeleton.getBoneIndex(bone));
+            for (Bone chilBone : bone.getChildren()) {
+                if (!boneList.contains(chilBone.getName())) {
+                    list.addAll(getBoneIndices(chilBone, skeleton, boneList));
+                }
+            }
+        }
+        return list;
+    }
+
+    /**
+     * Create a hull collision shape from linked vertices to this bone.
+     * 
+     * @param link
+     * @param model
+     * @return 
+     */
+    public static HullCollisionShape makeShapeFromVerticeWeights(Spatial model, List<Integer> boneIndices, Vector3f initialScale, Vector3f initialPosition, float weightThreshold) {
+
+        ArrayList<Float> points = new ArrayList<Float>();
+        if (model instanceof Geometry) {
+            Geometry g = (Geometry) model;
+            for (Integer index : boneIndices) {
+                points.addAll(getPoints(g.getMesh(), index, initialScale, initialPosition, weightThreshold));
+            }
+        } else if (model instanceof Node) {
+            Node node = (Node) model;
+            for (Spatial s : node.getChildren()) {
+                if (s instanceof Geometry) {
+                    Geometry g = (Geometry) s;
+                    for (Integer index : boneIndices) {
+                        points.addAll(getPoints(g.getMesh(), index, initialScale, initialPosition, weightThreshold));
+                    }
+
+                }
+            }
+        }
+        float[] p = new float[points.size()];
+        for (int i = 0; i < points.size(); i++) {
+            p[i] = points.get(i);
+        }
+
+
+        return new HullCollisionShape(p);
+    }
+
+    /**
+     * returns a list of points for the given bone
+     * @param mesh
+     * @param boneIndex
+     * @param offset
+     * @param link
+     * @return 
+     */
+    private static List<Float> getPoints(Mesh mesh, int boneIndex, Vector3f initialScale, Vector3f offset, float weightThreshold) {
+
+        FloatBuffer vertices = mesh.getFloatBuffer(Type.Position);
+        ByteBuffer boneIndices = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
+        FloatBuffer boneWeight = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
+
+        vertices.rewind();
+        boneIndices.rewind();
+        boneWeight.rewind();
+
+        ArrayList<Float> results = new ArrayList<Float>();
+
+        int vertexComponents = mesh.getVertexCount() * 3;
+
+        for (int i = 0; i < vertexComponents; i += 3) {
+            int k;
+            boolean add = false;
+            int start = i / 3 * 4;
+            for (k = start; k < start + 4; k++) {
+                if (boneIndices.get(k) == boneIndex && boneWeight.get(k) >= weightThreshold) {
+                    add = true;
+                    break;
+                }
+            }
+            if (add) {
+
+                Vector3f pos = new Vector3f();
+                pos.x = vertices.get(i);
+                pos.y = vertices.get(i + 1);
+                pos.z = vertices.get(i + 2);
+                pos.subtractLocal(offset).multLocal(initialScale);
+                results.add(pos.x);
+                results.add(pos.y);
+                results.add(pos.z);
+
+            }
+        }
+
+        return results;
+    }
+
+    /**
+     * Updates a bone position and rotation.
+     * if the child bones are not in the bone list this means, they are not associated with a physic shape.
+     * So they have to be updated
+     * @param bone the bone
+     * @param pos the position
+     * @param rot the rotation
+     */
+    public static void setTransform(Bone bone, Vector3f pos, Quaternion rot, boolean restoreBoneControl, Set<String> boneList) {
+        //we ensure that we have the control
+        if (restoreBoneControl) {
+            bone.setUserControl(true);
+        }
+        //we set te user transforms of the bone
+        bone.setUserTransformsWorld(pos, rot);
+        for (Bone childBone : bone.getChildren()) {
+            //each child bone that is not in the list is updated
+            if (!boneList.contains(childBone.getName())) {
+                Transform t = childBone.getCombinedTransform(pos, rot);
+                setTransform(childBone, t.getTranslation(), t.getRotation(), restoreBoneControl, boneList);
+            }
+        }
+        //we give back the control to the keyframed animation
+        if (restoreBoneControl) {
+            bone.setUserControl(false);
+        }
+    }
+
+    public static void setUserControl(Bone bone, boolean bool) {
+        bone.setUserControl(bool);
+        for (Bone child : bone.getChildren()) {
+            setUserControl(child, bool);
+        }
+    }
+}
index 36afbc7..f78c9f3 100644 (file)
@@ -39,7 +39,6 @@ import com.jme3.animation.LoopMode;
 import com.jme3.bullet.BulletAppState;
 import com.jme3.app.SimpleApplication;
 import com.jme3.asset.TextureKey;
-import com.jme3.bounding.BoundingBox;
 import com.jme3.bullet.PhysicsSpace;
 import com.jme3.bullet.collision.PhysicsCollisionEvent;
 import com.jme3.bullet.collision.PhysicsCollisionObject;
@@ -102,7 +101,7 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
         bullet.setTextureMode(TextureMode.Projected);
         bulletCollisionShape = new SphereCollisionShape(1.0f);
 
-        bulletAppState.getPhysicsSpace().enableDebug(assetManager);
+//        bulletAppState.getPhysicsSpace().enableDebug(assetManager);
         PhysicsTestHelper.createPhysicsTestWorld(rootNode, assetManager, bulletAppState.getPhysicsSpace());
         setupLight();
 
@@ -161,13 +160,10 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
                     model.setLocalRotation(q);
                     if (angles[0] < 0) {
                         animChannel.setAnim("StandUpBack");
-                        //  ragdoll.setKinematicMode();
                         ragdoll.blendToKinematicMode(0.5f);
                     } else {
                         animChannel.setAnim("StandUpFront");
                         ragdoll.blendToKinematicMode(0.5f);
-                        // ragdoll.setKinematicMode();
-
                     }
 
                 }
@@ -179,15 +175,12 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
                     bulletSize -= 0.1f;
 
                 }
-                if (name.equals("shoot") && isPressed) {
-//                    bulletSize = 0;
-                }
-                if (name.equals("stop") && isPressed) {
-                    // bulletAppState.setEnabled(!bulletAppState.isEnabled());
 
+                if (name.equals("stop") && isPressed) {
                     ragdoll.setEnabled(!ragdoll.isEnabled());
                     ragdoll.setRagdollMode();
                 }
+
                 if (name.equals("shoot") && !isPressed) {
                     Geometry bulletg = new Geometry("bullet", bullet);
                     bulletg.setMaterial(matBullet);
@@ -208,8 +201,8 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
                     bulletg.setLocalScale(bulletSize);
                     bulletCollisionShape = new SphereCollisionShape(bulletSize);
                     BombControl bulletNode = new BombControl(assetManager, bulletCollisionShape, 1);
-                    bulletNode.setForceFactor(10);
-                    bulletNode.setExplosionRadius(30);
+                    bulletNode.setForceFactor(8);
+                    bulletNode.setExplosionRadius(20);
                     bulletNode.setCcdMotionThreshold(0.001f);
                     bulletNode.setLinearVelocity(cam.getDirection().mult(180));
                     bulletg.addControl(bulletNode);
@@ -285,7 +278,6 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
         ragdoll.addBoneName("Hand.R");
         ragdoll.addBoneName("Hand.L");
         ragdoll.addBoneName("Neck");
-        //  ragdoll.addBoneName("Head");
         ragdoll.addBoneName("Root");
         ragdoll.addBoneName("Stomach");
         ragdoll.addBoneName("Waist");
@@ -309,7 +301,7 @@ public class TestBoneRagdoll extends SimpleApplication implements RagdollCollisi
 
     @Override
     public void simpleUpdate(float tpf) {
-       // System.out.println(((BoundingBox) model.getWorldBound()).getYExtent());
+        // System.out.println(((BoundingBox) model.getWorldBound()).getYExtent());
 //        elTime += tpf;
 //        if (elTime > 3) {
 //            elTime = 0;