Skip to content

Commit 5bbc6bc

Browse files
committed
fix for issue #740 (#906)
1 parent 7e17cdc commit 5bbc6bc

File tree

2 files changed

+89
-23
lines changed

2 files changed

+89
-23
lines changed

jme3-bullet/src/common/java/com/jme3/bullet/control/KinematicRagdollControl.java

Lines changed: 45 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
import com.jme3.math.Vector3f;
5858
import com.jme3.renderer.RenderManager;
5959
import com.jme3.renderer.ViewPort;
60+
import com.jme3.scene.Mesh;
6061
import com.jme3.scene.Node;
6162
import com.jme3.scene.Spatial;
6263
import com.jme3.scene.control.Control;
@@ -212,6 +213,7 @@ public PhysicsRigidBody getRigidBody() {
212213
* @param ex exporter (not null)
213214
* @throws IOException from exporter
214215
*/
216+
@Override
215217
public void write(JmeExporter ex) throws IOException {
216218
OutputCapsule oc = ex.getCapsule(this);
217219
oc.write(rigidBody, "rigidBody", null);
@@ -229,6 +231,7 @@ public void write(JmeExporter ex) throws IOException {
229231
* @param im importer (not null)
230232
* @throws IOException from importer
231233
*/
234+
@Override
232235
public void read(JmeImporter im) throws IOException {
233236
InputCapsule ic = im.getCapsule(this);
234237
rigidBody = (PhysicsRigidBody) ic.readSavable("rigidBody", null);
@@ -288,6 +291,7 @@ public KinematicRagdollControl(RagdollPreset preset) {
288291
*
289292
* @param tpf the time interval between frames (in seconds, ≥0)
290293
*/
294+
@Override
291295
public void update(float tpf) {
292296
if (!enabled) {
293297
return;
@@ -348,15 +352,9 @@ protected void ragDollUpdate(float tpf) {
348352
link.bone.setUserTransformsInModelSpace(position, tmpRot1);
349353

350354
} else {
351-
//If boneList is empty, every bone has a collision shape,
352-
//so we simply update the bone position.
353-
if (boneList.isEmpty()) {
354-
link.bone.setUserTransformsInModelSpace(position, tmpRot1);
355-
} else {
356-
//boneList is not empty, this means some bones of the skeleton might not be associated with a collision shape.
357-
//So we update them recusively
358-
RagdollUtils.setTransform(link.bone, position, tmpRot1, false, boneList);
359-
}
355+
//some bones of the skeleton might not be associated with a collision shape.
356+
//So we update them recusively
357+
RagdollUtils.setTransform(link.bone, position, tmpRot1, false, boneList);
360358
}
361359
}
362360
vars.release();
@@ -391,17 +389,8 @@ protected void kinematicUpdate(float tpf) {
391389
tmpRot1.set(tmpRot2);
392390
position.set(position2);
393391

394-
//updating bones transforms
395-
if (boneList.isEmpty()) {
396-
//we ensure we have the control to update the bone
397-
link.bone.setUserControl(true);
398-
link.bone.setUserTransformsInModelSpace(position, tmpRot1);
399-
//we give control back to the key framed animation.
400-
link.bone.setUserControl(false);
401-
} else {
402-
RagdollUtils.setTransform(link.bone, position, tmpRot1, true, boneList);
403-
}
404-
392+
//update bone transforms
393+
RagdollUtils.setTransform(link.bone, position, tmpRot1, true, boneList);
405394
}
406395
//setting skeleton transforms to the ragdoll
407396
matchPhysicObjectToBone(link, position, tmpRot1);
@@ -589,6 +578,22 @@ protected void createSpatialData(Spatial model) {
589578
model.removeControl(sc);
590579
model.addControl(sc);
591580

581+
if (boneList.isEmpty()) {
582+
// add all bones to the list
583+
skeleton = sc.getSkeleton();
584+
for (int boneI = 0; boneI < skeleton.getBoneCount(); boneI++) {
585+
String boneName = skeleton.getBone(boneI).getName();
586+
boneList.add(boneName);
587+
}
588+
}
589+
// filter out bones without vertices
590+
filterBoneList(sc);
591+
592+
if (boneList.isEmpty()) {
593+
throw new IllegalArgumentException(
594+
"No suitable bones were found in the model's skeleton.");
595+
}
596+
592597
// put into bind pose and compute bone transforms in model space
593598
// maybe don't reset to ragdoll out of animations?
594599
scanSpatial(model);
@@ -608,6 +613,25 @@ protected void createSpatialData(Spatial model) {
608613
logger.log(Level.FINE, "Created physics ragdoll for skeleton {0}", skeleton);
609614
}
610615

616+
/**
617+
* Remove any bones without vertices from the boneList, so that every hull
618+
* shape will contain at least 1 vertex.
619+
*/
620+
private void filterBoneList(SkeletonControl skeletonControl) {
621+
Mesh[] targets = skeletonControl.getTargets();
622+
Skeleton skel = skeletonControl.getSkeleton();
623+
for (int boneI = 0; boneI < skel.getBoneCount(); boneI++) {
624+
String boneName = skel.getBone(boneI).getName();
625+
if (boneList.contains(boneName)) {
626+
boolean hasVertices = RagdollUtils.hasVertices(boneI, targets,
627+
weightThreshold);
628+
if (!hasVertices) {
629+
boneList.remove(boneName);
630+
}
631+
}
632+
}
633+
}
634+
611635
/**
612636
* Destroy spatial-dependent data. Invoked when this control is removed from
613637
* a spatial.
@@ -669,7 +693,7 @@ protected void scanSpatial(Spatial model) {
669693
*/
670694
protected void boneRecursion(Spatial model, Bone bone, PhysicsRigidBody parent, int reccount, Map<Integer, List<Float>> pointsMap) {
671695
PhysicsRigidBody parentShape = parent;
672-
if (boneList.isEmpty() || boneList.contains(bone.getName())) {
696+
if (boneList.contains(bone.getName())) {
673697

674698
PhysicsBoneLink link = new PhysicsBoneLink();
675699
link.bone = bone;

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

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@
5656
*/
5757
public class RagdollUtils {
5858

59+
/**
60+
* A private constructor to inhibit instantiation of this class.
61+
*/
62+
private RagdollUtils() {
63+
}
64+
5965
/**
6066
* Alter the limits of the specified 6-DOF joint.
6167
*
@@ -172,12 +178,12 @@ public static HullCollisionShape makeShapeFromPointMap(Map<Integer, List<Float>>
172178
}
173179
}
174180

181+
assert !points.isEmpty();
175182
float[] p = new float[points.size()];
176183
for (int i = 0; i < points.size(); i++) {
177184
p[i] = points.get(i);
178185
}
179186

180-
181187
return new HullCollisionShape(p);
182188
}
183189

@@ -235,12 +241,13 @@ public static HullCollisionShape makeShapeFromVerticeWeights(Spatial model, List
235241
}
236242
}
237243
}
244+
245+
assert !points.isEmpty();
238246
float[] p = new float[points.size()];
239247
for (int i = 0; i < points.size(); i++) {
240248
p[i] = points.get(i);
241249
}
242250

243-
244251
return new HullCollisionShape(p);
245252
}
246253

@@ -343,4 +350,39 @@ public static void setUserControl(Bone bone, boolean bool) {
343350
setUserControl(child, bool);
344351
}
345352
}
353+
354+
/**
355+
* Test whether the indexed bone has at least one vertex in the specified
356+
* meshes with a weight greater than the specified threshold.
357+
*
358+
* @param boneIndex the index of the bone (&ge;0)
359+
* @param targets the meshes to search (not null, no null elements)
360+
* @param weightThreshold the threshold (&ge;0, &le;1)
361+
* @return true if at least 1 vertex found, otherwise false
362+
*/
363+
public static boolean hasVertices(int boneIndex, Mesh[] targets,
364+
float weightThreshold) {
365+
for (Mesh mesh : targets) {
366+
ByteBuffer boneIndices
367+
= (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
368+
FloatBuffer boneWeight
369+
= (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
370+
371+
boneIndices.rewind();
372+
boneWeight.rewind();
373+
374+
int vertexComponents = mesh.getVertexCount() * 3;
375+
for (int i = 0; i < vertexComponents; i += 3) {
376+
int start = i / 3 * 4;
377+
for (int k = start; k < start + 4; k++) {
378+
if (boneIndices.get(k) == boneIndex
379+
&& boneWeight.get(k) >= weightThreshold) {
380+
return true;
381+
}
382+
}
383+
}
384+
}
385+
386+
return false;
387+
}
346388
}

0 commit comments

Comments
 (0)