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

fix-456
Nehon 7 years ago
parent 510562a62d
commit 32b947a0ac
  1. 20
      jme3-core/src/main/java/com/jme3/animation/SkeletonControl.java
  2. 19
      jme3-core/src/main/java/com/jme3/util/BufferUtils.java
  3. 85
      jme3-plugins/src/gltf/java/com/jme3/scene/plugins/gltf/GltfUtils.java

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

@ -1344,6 +1344,9 @@ public final class BufferUtils {
public static class ByteShortIntBufferReader { public static class ByteShortIntBufferReader {
Buffer buffer; Buffer buffer;
public ByteShortIntBufferReader() {
}
public ByteShortIntBufferReader(Buffer buffer) { public ByteShortIntBufferReader(Buffer buffer) {
this.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() { public void rewind() {
buffer.rewind(); buffer.rewind();
} }

@ -382,49 +382,58 @@ public class GltfUtils {
public static void handleSkinningBuffers(Mesh mesh, IntMap<GltfLoader.SkinBuffers> skinBuffers) { public static void handleSkinningBuffers(Mesh mesh, IntMap<GltfLoader.SkinBuffers> skinBuffers) {
if (skinBuffers.size() > 0) { if (skinBuffers.size() > 0) {
if (skinBuffers.size() == 1) { int length = skinBuffers.get(0).joints.length;
GltfLoader.SkinBuffers buffs = skinBuffers.get(0); short[] jointsArray = new short[length];
setSkinBuffers(mesh, buffs.joints, skinBuffers.get(0).weights, buffs.componentSize); float[] weightsArray = new float[length];
} else { List<GltfLoader.WeightData> weightData = new ArrayList<>();
int componentSize = 1;
int length = skinBuffers.get(0).joints.length;
short[] jointsArray = new short[length]; for (int i = 0; i < weightsArray.length; i += 4) {
float[] weightsArray = new float[length]; weightData.clear();
List<GltfLoader.WeightData> weightData = new ArrayList<>(); for (int j = 0; j < skinBuffers.size(); j++) {
int componentSize = 1; GltfLoader.SkinBuffers buffs = skinBuffers.get(j);
for (int k = 0; k < 4; k++) {
for (int i = 0; i < weightsArray.length; i += 4) { weightData.add(new GltfLoader.WeightData(buffs.weights[i + k], buffs.joints[i + k], buffs.componentSize));
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) { Collections.sort(weightData, new Comparator<GltfLoader.WeightData>() {
if (o1.value > o2.value) { @Override
return -1; public int compare(GltfLoader.WeightData o1, GltfLoader.WeightData o2) {
} else if (o1.value < o2.value) { if (o1.value > o2.value) {
return 1; return -1;
} else { } else if (o1.value < o2.value) {
return 0; 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;
} }
} }
});
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);
} }
} }

Loading…
Cancel
Save