From 5bbc6bcf1efd31a2aa7db9f0cbc71e895ce93f37 Mon Sep 17 00:00:00 2001 From: Stephen Gold Date: Sun, 23 Sep 2018 19:19:36 -0700 Subject: [PATCH] fix for issue #740 (#906) --- .../control/KinematicRagdollControl.java | 66 +++++++++++++------ .../bullet/control/ragdoll/RagdollUtils.java | 46 ++++++++++++- 2 files changed, 89 insertions(+), 23 deletions(-) diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java index 3424d7249..d1d335cca 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java +++ b/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> pointsMap) { PhysicsRigidBody parentShape = parent; - if (boneList.isEmpty() || boneList.contains(bone.getName())) { + if (boneList.contains(bone.getName())) { PhysicsBoneLink link = new PhysicsBoneLink(); link.bone = bone; diff --git a/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java b/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java index 455a62f7f..965bb9c7e 100644 --- a/jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java +++ b/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 (≥0) + * @param targets the meshes to search (not null, no null elements) + * @param weightThreshold the threshold (≥0, ≤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; + } }