fix for issue #740 (#906)

v3.2
Stephen Gold 6 years ago
parent 7e17cdc498
commit 5bbc6bcf1e
  1. 66
      jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java
  2. 46
      jme3-bullet/src/common/java/com/jme3/bullet/control/ragdoll/RagdollUtils.java

@ -57,6 +57,7 @@ import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f; import com.jme3.math.Vector3f;
import com.jme3.renderer.RenderManager; import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort; import com.jme3.renderer.ViewPort;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node; import com.jme3.scene.Node;
import com.jme3.scene.Spatial; import com.jme3.scene.Spatial;
import com.jme3.scene.control.Control; import com.jme3.scene.control.Control;
@ -212,6 +213,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
* @param ex exporter (not null) * @param ex exporter (not null)
* @throws IOException from exporter * @throws IOException from exporter
*/ */
@Override
public void write(JmeExporter ex) throws IOException { public void write(JmeExporter ex) throws IOException {
OutputCapsule oc = ex.getCapsule(this); OutputCapsule oc = ex.getCapsule(this);
oc.write(rigidBody, "rigidBody", null); oc.write(rigidBody, "rigidBody", null);
@ -229,6 +231,7 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
* @param im importer (not null) * @param im importer (not null)
* @throws IOException from importer * @throws IOException from importer
*/ */
@Override
public void read(JmeImporter im) throws IOException { public void read(JmeImporter im) throws IOException {
InputCapsule ic = im.getCapsule(this); InputCapsule ic = im.getCapsule(this);
rigidBody = (PhysicsRigidBody) ic.readSavable("rigidBody", null); 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) * @param tpf the time interval between frames (in seconds, ≥0)
*/ */
@Override
public void update(float tpf) { public void update(float tpf) {
if (!enabled) { if (!enabled) {
return; return;
@ -348,15 +352,9 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
link.bone.setUserTransformsInModelSpace(position, tmpRot1); link.bone.setUserTransformsInModelSpace(position, tmpRot1);
} else { } else {
//If boneList is empty, every bone has a collision shape, //some bones of the skeleton might not be associated with a collision shape.
//so we simply update the bone position. //So we update them recusively
if (boneList.isEmpty()) { RagdollUtils.setTransform(link.bone, position, tmpRot1, false, boneList);
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);
}
} }
} }
vars.release(); vars.release();
@ -391,17 +389,8 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
tmpRot1.set(tmpRot2); tmpRot1.set(tmpRot2);
position.set(position2); position.set(position2);
//updating bones transforms //update bone transforms
if (boneList.isEmpty()) { RagdollUtils.setTransform(link.bone, position, tmpRot1, true, boneList);
//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);
}
} }
//setting skeleton transforms to the ragdoll //setting skeleton transforms to the ragdoll
matchPhysicObjectToBone(link, position, tmpRot1); matchPhysicObjectToBone(link, position, tmpRot1);
@ -589,6 +578,22 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
model.removeControl(sc); model.removeControl(sc);
model.addControl(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 // put into bind pose and compute bone transforms in model space
// maybe don't reset to ragdoll out of animations? // maybe don't reset to ragdoll out of animations?
scanSpatial(model); scanSpatial(model);
@ -608,6 +613,25 @@ public class KinematicRagdollControl extends AbstractPhysicsControl implements P
logger.log(Level.FINE, "Created physics ragdoll for skeleton {0}", skeleton); 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 * Destroy spatial-dependent data. Invoked when this control is removed from
* a spatial. * 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) { protected void boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, int reccount, Map<Integer, List<Float>> pointsMap) {
PhysicsRigidBody parentShape = parent; PhysicsRigidBody parentShape = parent;
if (boneList.isEmpty() || boneList.contains(bone.getName())) { if (boneList.contains(bone.getName())) {
PhysicsBoneLink link = new PhysicsBoneLink(); PhysicsBoneLink link = new PhysicsBoneLink();
link.bone = bone; link.bone = bone;

@ -56,6 +56,12 @@ import java.util.*;
*/ */
public class RagdollUtils { public class RagdollUtils {
/**
* A private constructor to inhibit instantiation of this class.
*/
private RagdollUtils() {
}
/** /**
* Alter the limits of the specified 6-DOF joint. * 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()]; float[] p = new float[points.size()];
for (int i = 0; i < points.size(); i++) { for (int i = 0; i < points.size(); i++) {
p[i] = points.get(i); p[i] = points.get(i);
} }
return new HullCollisionShape(p); return new HullCollisionShape(p);
} }
@ -235,12 +241,13 @@ public class RagdollUtils {
} }
} }
} }
assert !points.isEmpty();
float[] p = new float[points.size()]; float[] p = new float[points.size()];
for (int i = 0; i < points.size(); i++) { for (int i = 0; i < points.size(); i++) {
p[i] = points.get(i); p[i] = points.get(i);
} }
return new HullCollisionShape(p); return new HullCollisionShape(p);
} }
@ -343,4 +350,39 @@ public class RagdollUtils {
setUserControl(child, bool); 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;
}
} }

Loading…
Cancel
Save