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.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;
@ -113,6 +112,8 @@ public class SkeletonControl extends AbstractControl implements Cloneable, JmeCl
*/
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;

@ -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();
}

@ -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);
}
}

Loading…
Cancel
Save