Improvements to the weight and index buffers loading:

- if more than 4 weights is applied to the vertex then the 'strongest' weights are used
- warning is logged only once when model has vertices with more than 4 weights (and not for every vertex as it was before)

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10109 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
Kae..pl 12 years ago
parent e4d715d216
commit 1c2baedbc1
  1. 76
      engine/src/blender/com/jme3/scene/plugins/blender/modifiers/ArmatureModifier.java

@ -6,6 +6,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -42,16 +44,7 @@ import com.jme3.util.BufferUtils;
*/
/* package */class ArmatureModifier extends Modifier {
private static final Logger LOGGER = Logger.getLogger(ArmatureModifier.class.getName());
private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4;
// @Marcin it was an Ogre limitation, but as long as we use a MaxNumWeight
// variable in mesh,
// i guess this limitation has no sense for the blender loader...so i guess
// it's up to you. You'll have to deternine the max weight according to the
// provided blend file
// I added a check to avoid crash when loading a model that has more than 4
// weight per vertex on line 258
// If you decide to remove this limitation, remove this code.
// Rémy
private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4;//JME limitation
private Skeleton skeleton;
private Structure objectStructure;
@ -304,42 +297,52 @@ import com.jme3.util.BufferUtils;
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
if (pDvert.isNotNull()) {// assigning weights and bone indices
boolean warnAboutTooManyVertexWeights = false;
List<Structure> dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size() == verticesAmount (one dvert per vertex in blender)
int vertexIndex = 0;
//use tree map to sort weights from the lowest to the highest ones
TreeMap<Float, Integer> weightToIndexMap = new TreeMap<Float, Integer>();
for (Structure dvert : dverts) {
List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here
if(vertexIndices != null) {
int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex (max. 4 in JME)
Pointer pDW = (Pointer) dvert.getFieldValue("dw");
if (totweight > 0 && pDW.isNotNull() && groupToBoneIndexMap!=null) {// pDW should never be null here, but I check it just in case :)
if (totweight > 0 && groupToBoneIndexMap != null) {
weightToIndexMap.clear();
int weightIndex = 0;
List<Structure> dw = pDW.fetchData(blenderContext.getInputStream());
for (Structure deformWeight : dw) {
Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue());
// Remove this code if 4 weights limitation is removed
if (weightIndex == 4) {
LOGGER.log(Level.WARNING, "{0} has more than 4 weight on bone index {1}", new Object[] { meshStructure.getName(), boneIndex });
break;
}
// null here means that we came accross group that has no bone attached to
if (boneIndex != null) {
float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
if (weight == 0.0f) {
boneIndex = Integer.valueOf(0);
}
// we apply the weight to all referenced vertices
for (Integer index : vertexIndices) {
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
if(weightIndex < MAXIMUM_WEIGHTS_PER_VERTEX) {
if (weight == 0.0f) {
boneIndex = Integer.valueOf(0);
}
// we apply the weight to all referenced vertices
for (Integer index : vertexIndices) {
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
}
weightToIndexMap.put(weight, weightIndex);
bonesGroups[0] = Math.max(bonesGroups[0], weightIndex + 1);
} else if(weight > 0) {//if weight is zero the simply ignore it
warnAboutTooManyVertexWeights = true;
Entry<Float, Integer> lowestWeightAndIndex = weightToIndexMap.firstEntry();
if(lowestWeightAndIndex.getKey() < weight) {
weightsFloatData.put(lowestWeightAndIndex.getValue(), weight);
indicesData.put(lowestWeightAndIndex.getValue(), boneIndex.byteValue());
weightToIndexMap.remove(lowestWeightAndIndex.getKey());
weightToIndexMap.put(weight, lowestWeightAndIndex.getValue());
}
}
}
++weightIndex;
}
bonesGroups[0] = Math.max(bonesGroups[0], weightIndex);
} else {
// 0.0 weight indicates, do not transform this vertex, but keep it in bind pose.
for (Integer index : vertexIndices) {
@ -350,6 +353,10 @@ import com.jme3.util.BufferUtils;
}
++vertexIndex;
}
if(warnAboutTooManyVertexWeights) {
LOGGER.log(Level.WARNING, "{0} has vertices with more than 4 weights assigned. The model may not behave as it should.", meshStructure.getName());
}
} else {
// always bind all vertices to 0-indexed bone
// this bone makes the model look normally if vertices have no bone
@ -386,17 +393,20 @@ import com.jme3.util.BufferUtils;
*/
private void endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
weightsFloatData.rewind();
float[] weights = new float[MAXIMUM_WEIGHTS_PER_VERTEX];
for (int v = 0; v < vertCount; ++v) {
float w0 = weightsFloatData.get(), w1 = weightsFloatData.get(), w2 = weightsFloatData.get(), w3 = weightsFloatData.get();
float sum = w0 + w1 + w2 + w3;
float sum = 0;
for (int i = 0; i < MAXIMUM_WEIGHTS_PER_VERTEX; ++i) {
weights[i] = weightsFloatData.get();
sum += weights[i];
}
if (sum != 1f && sum != 0.0f) {
weightsFloatData.position(weightsFloatData.position() - 4);
weightsFloatData.position(weightsFloatData.position() - MAXIMUM_WEIGHTS_PER_VERTEX);
// compute new vals based on sum
float sumToB = 1f / sum;
weightsFloatData.put(w0 * sumToB);
weightsFloatData.put(w1 * sumToB);
weightsFloatData.put(w2 * sumToB);
weightsFloatData.put(w3 * sumToB);
for (int i = 0; i < MAXIMUM_WEIGHTS_PER_VERTEX; ++i) {
weightsFloatData.put(weights[i] * sumToB);
}
}
}
weightsFloatData.rewind();

Loading…
Cancel
Save