Selaa lähdekoodia

getAttachmentNode(): move target selection from SkeletonControl to Bone

Stephen Gold 8 vuotta sitten
vanhempi
commit
9905c4f011

+ 19 - 6
jme3-core/src/main/java/com/jme3/animation/Bone.java

@@ -34,8 +34,10 @@ package com.jme3.animation;
 import com.jme3.export.*;
 import com.jme3.math.*;
 import com.jme3.scene.Geometry;
+import com.jme3.scene.Mesh;
 import com.jme3.scene.Node;
 import com.jme3.scene.Spatial;
+import com.jme3.util.SafeArrayList;
 import com.jme3.util.TempVars;
 import com.jme3.util.clone.JmeCloneable;
 import com.jme3.util.clone.Cloner;
@@ -701,16 +703,27 @@ public final class Bone implements Savable, JmeCloneable {
      * have an attachments node, create one. Models and effects attached to the
      * attachments node will follow this bone's motions.
      *
-     * @param target a geometry animated by this bone, or null to indicate that
-     * all geometries affected by this bone have the same global transform as
-     * the attachment node's parent
-     */
-    Node getAttachmentsNode(Geometry target) {
+     * @param boneIndex this bone's index in its skeleton (≥0)
+     * @param targets a list of geometries animated by this bone's skeleton (not
+     * null, unaffected)
+     */
+    Node getAttachmentsNode(int boneIndex, SafeArrayList<Geometry> targets) {
+        targetGeometry = null;
+        /*
+         * Search for a geometry animated by this particular bone.
+         */
+        for (Geometry geometry : targets) {
+            Mesh mesh = geometry.getMesh();
+            if (mesh != null && mesh.isAnimatedByBone(boneIndex)) {
+                targetGeometry = geometry;
+                break;
+            }
+        }
+
         if (attachNode == null) {
             attachNode = new Node(name + "_attachnode");
             attachNode.setUserData("AttachedBone", this);
         }
-        targetGeometry = target;
 
         return attachNode;
     }

+ 34 - 26
jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java

@@ -70,15 +70,12 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
      * The skeleton of the model.
      */
     private Skeleton skeleton;
+
     /**
-     * List of targets which this controller effects.
-     */
-    private SafeArrayList<Mesh> targets = new SafeArrayList<Mesh>(Mesh.class);
-    /**
-     * Geometry with an animated mesh, for calculating attachments node
-     * transforms. A null means no geometry is subject to this control.
+     * List of geometries affected by this control.
      */
-    private Geometry targetGeometry = null;
+    private SafeArrayList<Geometry> targets = new SafeArrayList<Geometry>(Geometry.class);
+
     /**
      * Used to track when a mesh was updated. Meshes are only updated if they
      * are visible in at least one camera.
@@ -128,8 +125,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
         for (Material m : materials) {
             m.setInt("NumberOfBones", numBones);
         }
-        for (Mesh mesh : targets) {
-            if (mesh.isAnimated()) {
+        for (Geometry geometry : targets) {
+            Mesh mesh = geometry.getMesh();
+            if (mesh != null && mesh.isAnimated()) {
                 mesh.prepareForAnim(false);
             }
         }
@@ -141,8 +139,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
                 m.clearParam("NumberOfBones");
             }
         }
-        for (Mesh mesh : targets) {
-            if (mesh.isAnimated()) {
+        for (Geometry geometry : targets) {
+            Mesh mesh = geometry.getMesh();
+            if (mesh != null && mesh.isAnimated()) {
                 mesh.prepareForAnim(true);
             }
         }
@@ -220,9 +219,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
      */
     private void findTargets(Geometry geometry) {
         Mesh mesh = geometry.getMesh();
-        if (mesh.isAnimated()) {
-            targets.add(mesh);
-            targetGeometry = geometry;
+        if (mesh != null && mesh.isAnimated()) {
+            targets.add(geometry);
             materials.add(geometry.getMaterial());
         }
     }
@@ -248,10 +246,11 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
 
         offsetMatrices = skeleton.computeSkinningMatrices();
 
-        for (Mesh mesh : targets) {
-            // NOTE: This assumes that code higher up
-            // Already ensured those targets are animated
-            // otherwise a crash will happen in skin update
+        for (Geometry geometry : targets) {
+            Mesh mesh = geometry.getMesh();
+            // NOTE: This assumes code higher up has
+            // already ensured this mesh is animated.
+            // Otherwise a crash will happen in skin update.
             softwareSkinUpdate(mesh, offsetMatrices);
         }     
     }
@@ -325,8 +324,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
 
     //only do this for software updates
     void resetToBind() {
-        for (Mesh mesh : targets) {
-            if (mesh.isAnimated()) {
+        for (Geometry geometry : targets) {
+            Mesh mesh = geometry.getMesh();
+            if (mesh != null && mesh.isAnimated()) {
                 Buffer bwBuff = mesh.getBuffer(Type.BoneWeight).getData();
                 Buffer biBuff = mesh.getBuffer(Type.BoneIndex).getData();
                 if (!biBuff.hasArray() || !bwBuff.hasArray()) {
@@ -460,7 +460,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
         }
 
         updateTargetsAndMaterials(spatial);
-        Node n = b.getAttachmentsNode(targetGeometry);
+        int boneIndex = skeleton.getBoneIndex(b);
+        Node n = b.getAttachmentsNode(boneIndex, targets);
         /*
          * Select a node to parent the attachments node.
          */
@@ -485,12 +486,20 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
     }
 
     /**
-     * returns a copy of array of the targets meshes of this control
+     * Enumerate the target meshes of this control.
      *
-     * @return
+     * @return a new array
      */
-    public Mesh[] getTargets() {        
-        return targets.toArray(new Mesh[targets.size()]);
+    public Mesh[] getTargets() {
+        Mesh[] result = new Mesh[targets.size()];
+        int i = 0;
+        for (Geometry geometry : targets) {
+            Mesh mesh = geometry.getMesh();
+            result[i] = mesh;
+            i++;
+        }
+
+        return result;
     }
 
     /**
@@ -791,7 +800,6 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
      */
     private void updateTargetsAndMaterials(Spatial spatial) {
         targets.clear();
-        targetGeometry = null;
         materials.clear();
 
         if (spatial instanceof Node) {

+ 40 - 1
jme3-core/src/main/java/com/jme3/scene/Mesh.java

@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009-2012 jMonkeyEngine
+ * Copyright (c) 2009-2017 jMonkeyEngine
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -1408,6 +1408,45 @@ public class Mesh implements Savable, Cloneable, JmeCloneable {
                getBuffer(Type.HWBoneIndex) != null;
     }
 
+    /**
+     * Test whether the specified bone animates this mesh.
+     *
+     * @param boneIndex the bone's index in its skeleton
+     * @return true if the specified bone animates this mesh, otherwise false
+     */
+    public boolean isAnimatedByBone(int boneIndex) {
+        VertexBuffer biBuf = getBuffer(VertexBuffer.Type.BoneIndex);
+        VertexBuffer wBuf = getBuffer(VertexBuffer.Type.BoneWeight);
+        if (biBuf == null || wBuf == null) {
+            return false; // no bone animation data
+        }
+
+        ByteBuffer boneIndexBuffer = (ByteBuffer) biBuf.getData();
+        boneIndexBuffer.rewind();
+        int numBoneIndices = boneIndexBuffer.remaining();
+        assert numBoneIndices % 4 == 0 : numBoneIndices;
+        int numVertices = boneIndexBuffer.remaining() / 4;
+
+        FloatBuffer weightBuffer = (FloatBuffer) wBuf.getData();
+        weightBuffer.rewind();
+        int numWeights = weightBuffer.remaining();
+        assert numWeights == numVertices * 4 : numWeights;
+        /*
+         * Test each vertex to determine whether the bone affects it.
+         */
+        byte biByte = (byte) boneIndex; // bone indices wrap after 127
+        for (int vIndex = 0; vIndex < numVertices; vIndex++) {
+            for (int wIndex = 0; wIndex < 4; wIndex++) {
+                byte bIndex = boneIndexBuffer.get();
+                float weight = weightBuffer.get();
+                if (wIndex < maxNumWeights && bIndex == biByte && weight != 0f) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     /**
      * Sets the count of vertices used for each tessellation patch
      * @param patchVertexCount