5757import com .jme3 .math .Vector3f ;
5858import com .jme3 .renderer .RenderManager ;
5959import com .jme3 .renderer .ViewPort ;
60+ import com .jme3 .scene .Mesh ;
6061import com .jme3 .scene .Node ;
6162import com .jme3 .scene .Spatial ;
6263import 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 ;
0 commit comments