From 32b947a0acf056e83a5718a3ac10d73e3440cd52 Mon Sep 17 00:00:00 2001 From: Nehon Date: Sun, 3 Sep 2017 16:18:09 +0200 Subject: [PATCH] Fixed an issue where loaded glTF models couldn't use software skinning --- .../com/jme3/animation/SkeletonControl.java | 22 +++-- .../main/java/com/jme3/util/BufferUtils.java | 19 +++++ .../jme3/scene/plugins/gltf/GltfUtils.java | 85 ++++++++++--------- 3 files changed, 76 insertions(+), 50 deletions(-) diff --git a/jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java b/jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java index 82a4c6e22..63e4d09e3 100644 --- a/jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java +++ b/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 materials = new HashSet(); - + + //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; diff --git a/jme3-core/src/main/java/com/jme3/util/BufferUtils.java b/jme3-core/src/main/java/com/jme3/util/BufferUtils.java index b589c7bab..72e50c8b0 100644 --- a/jme3-core/src/main/java/com/jme3/util/BufferUtils.java +++ b/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(); } diff --git a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java b/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java index d2e742687..bd92d6c01 100644 --- a/jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java +++ b/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 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 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 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() { - @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() { + @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); } }