Quellcode durchsuchen

Fixed an issue where loaded glTF models couldn't use software skinning

Nehon vor 8 Jahren
Ursprung
Commit
32b947a0ac

+ 10 - 12
jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java

@@ -44,8 +44,7 @@ import com.jme3.scene.VertexBuffer.Type;
 import com.jme3.scene.control.AbstractControl;
 import com.jme3.scene.control.Control;
 import com.jme3.shader.VarType;
-import com.jme3.util.SafeArrayList;
-import com.jme3.util.TempVars;
+import com.jme3.util.*;
 import com.jme3.util.clone.Cloner;
 import com.jme3.util.clone.JmeCloneable;
 import java.io.IOException;
@@ -112,7 +111,9 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
      * Material references used for hardware skinning
      */
     private Set<Material> materials = new HashSet<Material>();
-    
+
+    //temp reader
+    private BufferUtils.ByteShortIntBufferReader indexReader = new BufferUtils.ByteShortIntBufferReader();
     /**
      * Serialization only. Do not use.
      */
@@ -533,7 +534,6 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
         if (maxWeightsPerVert <= 0) {
             throw new IllegalStateException("Max weights per vert is incorrectly set!");
         }
-
         int fourMinusMaxWeights = 4 - maxWeightsPerVert;
 
         // NOTE: This code assumes the vertex buffer is in bind pose
@@ -547,14 +547,13 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
         fnb.rewind();
 
         // get boneIndexes and weights for mesh
-        ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
+        indexReader.setBuffer(mesh.getBuffer(Type.BoneIndex).getData());
         FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
 
-        ib.rewind();
+        indexReader.rewind();
         wb.rewind();
 
         float[] weights = wb.array();
-        byte[] indices = ib.array();
         int idxWeights = 0;
 
         TempVars vars = TempVars.get();
@@ -592,7 +591,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
 
                 for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
                     float weight = weights[idxWeights];
-                    Matrix4f mat = offsetMatrices[indices[idxWeights++] & 0xff];
+                    Matrix4f mat = offsetMatrices[indexReader.getUnsigned(idxWeights++)];
 
                     rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
                     ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;
@@ -665,14 +664,13 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
 
 
         // get boneIndexes and weights for mesh
-        ByteBuffer ib = (ByteBuffer) mesh.getBuffer(Type.BoneIndex).getData();
+        indexReader.setBuffer(mesh.getBuffer(Type.BoneIndex).getData());
         FloatBuffer wb = (FloatBuffer) mesh.getBuffer(Type.BoneWeight).getData();
 
-        ib.rewind();
+        indexReader.rewind();
         wb.rewind();
 
         float[] weights = wb.array();
-        byte[] indices = ib.array();
         int idxWeights = 0;
 
         TempVars vars = TempVars.get();
@@ -725,7 +723,7 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
 
                 for (int w = maxWeightsPerVert - 1; w >= 0; w--) {
                     float weight = weights[idxWeights];
-                    Matrix4f mat = offsetMatrices[indices[idxWeights++] & 0xff];
+                    Matrix4f mat = offsetMatrices[indexReader.getUnsigned(idxWeights++)];
 
                     rx += (mat.m00 * vtx + mat.m01 * vty + mat.m02 * vtz + mat.m03) * weight;
                     ry += (mat.m10 * vtx + mat.m11 * vty + mat.m12 * vtz + mat.m13) * weight;

+ 19 - 0
jme3-core/src/main/java/com/jme3/util/BufferUtils.java

@@ -1344,6 +1344,9 @@ public final class BufferUtils {
     public static class ByteShortIntBufferReader {
         Buffer buffer;
 
+        public ByteShortIntBufferReader() {
+        }
+
         public ByteShortIntBufferReader(Buffer buffer) {
             this.buffer = buffer;
         }
@@ -1372,6 +1375,22 @@ public final class BufferUtils {
             }
         }
 
+        public int getUnsigned(int index) {
+            if (buffer instanceof ByteBuffer) {
+                return ((ByteBuffer) buffer).get(index) & 0xff;
+            } else if (buffer instanceof ShortBuffer) {
+                return ((ShortBuffer) buffer).get(index) & 0xffff;
+            } else if (buffer instanceof IntBuffer) {
+                return ((IntBuffer) buffer).get(index) & 0xffffff;
+            } else {
+                throw new UnsupportedOperationException("Buffer must be a ByteBuffer, a ShortBuffer or an IntBuffer");
+            }
+        }
+
+        public void setBuffer(Buffer buffer) {
+            this.buffer = buffer;
+        }
+
         public void rewind() {
             buffer.rewind();
         }

+ 47 - 38
jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java

@@ -382,49 +382,58 @@ public class GltfUtils {
 
     public static void handleSkinningBuffers(Mesh mesh, IntMap<GltfLoader.SkinBuffers> skinBuffers) {
         if (skinBuffers.size() > 0) {
-            if (skinBuffers.size() == 1) {
-                GltfLoader.SkinBuffers buffs = skinBuffers.get(0);
-                setSkinBuffers(mesh, buffs.joints, skinBuffers.get(0).weights, buffs.componentSize);
-            } else {
-
-                int length = skinBuffers.get(0).joints.length;
-                short[] jointsArray = new short[length];
-                float[] weightsArray = new float[length];
-                List<GltfLoader.WeightData> weightData = new ArrayList<>();
-                int componentSize = 1;
-
-                for (int i = 0; i < weightsArray.length; i += 4) {
-                    weightData.clear();
-                    for (int j = 0; j < skinBuffers.size(); j++) {
-                        GltfLoader.SkinBuffers buffs = skinBuffers.get(j);
-                        for (int k = 0; k < 4; k++) {
-                            weightData.add(new GltfLoader.WeightData(buffs.weights[i + k], buffs.joints[i + k], buffs.componentSize));
-                        }
-
+            int length = skinBuffers.get(0).joints.length;
+            short[] jointsArray = new short[length];
+            float[] weightsArray = new float[length];
+            List<GltfLoader.WeightData> weightData = new ArrayList<>();
+            int componentSize = 1;
+
+            for (int i = 0; i < weightsArray.length; i += 4) {
+                weightData.clear();
+                for (int j = 0; j < skinBuffers.size(); j++) {
+                    GltfLoader.SkinBuffers buffs = skinBuffers.get(j);
+                    for (int k = 0; k < 4; k++) {
+                        weightData.add(new GltfLoader.WeightData(buffs.weights[i + k], buffs.joints[i + k], buffs.componentSize));
                     }
-                    Collections.sort(weightData, new Comparator<GltfLoader.WeightData>() {
-                        @Override
-                        public int compare(GltfLoader.WeightData o1, GltfLoader.WeightData o2) {
-                            if (o1.value > o2.value) {
-                                return -1;
-                            } else if (o1.value < o2.value) {
-                                return 1;
-                            } else {
-                                return 0;
-                            }
-                        }
-                    });
-                    for (int j = 0; j < 4; j++) {
-                        GltfLoader.WeightData data = weightData.get(j);
-                        jointsArray[i + j] = data.index;
-                        weightsArray[i + j] = data.value;
-                        if (data.componentSize > componentSize) {
-                            componentSize = data.componentSize;
+
+                }
+                Collections.sort(weightData, new Comparator<GltfLoader.WeightData>() {
+                    @Override
+                    public int compare(GltfLoader.WeightData o1, GltfLoader.WeightData o2) {
+                        if (o1.value > o2.value) {
+                            return -1;
+                        } else if (o1.value < o2.value) {
+                            return 1;
+                        } else {
+                            return 0;
                         }
                     }
+                });
+
+                float sum = 0;
+                for (int j = 0; j < 4; j++) {
+                    GltfLoader.WeightData data = weightData.get(j);
+                    jointsArray[i + j] = data.index;
+                    weightsArray[i + j] = data.value;
+                    sum += data.value;
+                    if (data.value > 0 && (j + 1) > mesh.getMaxNumWeights()) {
+                        mesh.setMaxNumWeights(j + 1);
+                    }
+                    if (data.componentSize > componentSize) {
+                        componentSize = data.componentSize;
+                    }
+                }
+
+                if (sum != 1f) {
+                    // compute new vals based on sum
+                    float sumToB = sum == 0 ? 0 : 1f / sum;
+                    weightsArray[i] *= sumToB;
+                    weightsArray[i + 1] *= sumToB;
+                    weightsArray[i + 2] *= sumToB;
+                    weightsArray[i + 3] *= sumToB;
                 }
-                setSkinBuffers(mesh, jointsArray, weightsArray, componentSize);
             }
+            setSkinBuffers(mesh, jointsArray, weightsArray, componentSize);
         }
     }