Stephen Gold %!s(int64=7) %!d(string=hai) anos
pai
achega
5bbc6bcf1e

+ 45 - 21
jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java

@@ -57,6 +57,7 @@ import com.jme3.math.Quaternion;
 import com.jme3.math.Vector3f;
 import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Mesh;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
 import com.jme3.scene.control.Control;
@@ -212,6 +213,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
          * @param ex exporter (not null)
          * @throws IOException from exporter
          */
+        @Override
         public void write(JmeExporter ex) throws IOException {
             OutputCapsule oc = ex.getCapsule(this);
             oc.write(rigidBody, "rigidBody", null);
@@ -229,6 +231,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
          * @param im importer (not null)
          * @throws IOException from importer
          */
+        @Override
         public void read(JmeImporter im) throws IOException {
             InputCapsule ic = im.getCapsule(this);
             rigidBody = (PhysicsRigidBody) ic.readSavable("rigidBody", null);
@@ -288,6 +291,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
      *
      * @param tpf the time interval between frames (in seconds, ≥0)
      */
+    @Override
     public void update(float tpf) {
         if (!enabled) {
             return;
@@ -348,15 +352,9 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
                 link.bone.setUserTransformsInModelSpace(position, tmpRot1);
 
             } else {
-                //If boneList is empty, every bone has a collision shape,
-                //so we simply 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();
@@ -391,17 +389,8 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
                 tmpRot1.set(tmpRot2);
                 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
             matchPhysicObjectToBone(link, position, tmpRot1);
@@ -589,6 +578,22 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
         model.removeControl(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
         // maybe don't reset to ragdoll out of animations?
         scanSpatial(model);
@@ -608,6 +613,25 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
         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.
@@ -669,7 +693,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
      */
     protected 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())) {
+        if (boneList.contains(bone.getName())) {
 
             PhysicsBoneLink link = new PhysicsBoneLink();
             link.bone = bone;

+ 44 - 2
jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java

@@ -56,6 +56,12 @@ import java.util.*;
  */
 public class RagdollUtils {
 
+    /**
+     * A private constructor to inhibit instantiation of this class.
+     */
+    private RagdollUtils() {
+    }
+
     /**
      * Alter the limits of the specified 6-DOF joint.
      *
@@ -172,12 +178,12 @@ public class RagdollUtils {
             }
         }
 
+        assert !points.isEmpty();
         float[] p = new float[points.size()];
         for (int i = 0; i < points.size(); i++) {
             p[i] = points.get(i);
         }
 
-
         return new HullCollisionShape(p);
     }
 
@@ -235,12 +241,13 @@ public class RagdollUtils {
                 }
             }
         }
+
+        assert !points.isEmpty();
         float[] p = new float[points.size()];
         for (int i = 0; i < points.size(); i++) {
             p[i] = points.get(i);
         }
 
-
         return new HullCollisionShape(p);
     }
 
@@ -343,4 +350,39 @@ public class RagdollUtils {
             setUserControl(child, bool);
         }
     }
+
+    /**
+     * Test whether the indexed bone has at least one vertex in the specified
+     * meshes with a weight greater than the specified threshold.
+     *
+     * @param boneIndex the index of the bone (&ge;0)
+     * @param targets the meshes to search (not null, no null elements)
+     * @param weightThreshold the threshold (&ge;0, &le;1)
+     * @return true if at least 1 vertex found, otherwise false
+     */
+    public static boolean hasVertices(int boneIndex, Mesh[] targets,
+            float weightThreshold) {
+        for (Mesh mesh : targets) {
+            ByteBuffer boneIndices
+                    = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
+            FloatBuffer boneWeight
+                    = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
+
+            boneIndices.rewind();
+            boneWeight.rewind();
+
+            int vertexComponents = mesh.getVertexCount() * 3;
+            for (int i = 0; i < vertexComponents; i += 3) {
+                int start = i / 3 * 4;
+                for (int k = start; k < start + 4; k++) {
+                    if (boneIndices.get(k) == boneIndex
+                            && boneWeight.get(k) >= weightThreshold) {
+                        return true;
+                    }
+                }
+            }
+        }
+
+        return false;
+    }
 }