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. 62
      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.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -42,16 +44,7 @@ import com.jme3.util.BufferUtils;
*/ */
/* package */class ArmatureModifier extends Modifier { /* package */class ArmatureModifier extends Modifier {
private static final Logger LOGGER = Logger.getLogger(ArmatureModifier.class.getName()); private static final Logger LOGGER = Logger.getLogger(ArmatureModifier.class.getName());
private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4; private static final int MAXIMUM_WEIGHTS_PER_VERTEX = 4;//JME limitation
// @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 Skeleton skeleton; private Skeleton skeleton;
private Structure objectStructure; private Structure objectStructure;
@ -304,30 +297,29 @@ import com.jme3.util.BufferUtils;
Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices Pointer pDvert = (Pointer) meshStructure.getFieldValue("dvert");// dvert = DeformVERTices
FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX); FloatBuffer weightsFloatData = BufferUtils.createFloatBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX); ByteBuffer indicesData = BufferUtils.createByteBuffer(vertexListSize * MAXIMUM_WEIGHTS_PER_VERTEX);
if (pDvert.isNotNull()) {// assigning weights and bone indices 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) List<Structure> dverts = pDvert.fetchData(blenderContext.getInputStream());// dverts.size() == verticesAmount (one dvert per vertex in blender)
int vertexIndex = 0; 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) { for (Structure dvert : dverts) {
List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here List<Integer> vertexIndices = vertexReferenceMap.get(Integer.valueOf(vertexIndex));// we fetch the referenced vertices here
if(vertexIndices != null) { if(vertexIndices != null) {
int totweight = ((Number) dvert.getFieldValue("totweight")).intValue();// total amount of weights assignet to the vertex (max. 4 in JME) 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"); 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; int weightIndex = 0;
List<Structure> dw = pDW.fetchData(blenderContext.getInputStream()); List<Structure> dw = pDW.fetchData(blenderContext.getInputStream());
for (Structure deformWeight : dw) { for (Structure deformWeight : dw) {
Integer boneIndex = groupToBoneIndexMap.get(((Number) deformWeight.getFieldValue("def_nr")).intValue()); 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 // null here means that we came accross group that has no bone attached to
if (boneIndex != null) { if (boneIndex != null) {
float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue(); float weight = ((Number) deformWeight.getFieldValue("weight")).floatValue();
if(weightIndex < MAXIMUM_WEIGHTS_PER_VERTEX) {
if (weight == 0.0f) { if (weight == 0.0f) {
boneIndex = Integer.valueOf(0); boneIndex = Integer.valueOf(0);
} }
@ -336,10 +328,21 @@ import com.jme3.util.BufferUtils;
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight); weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue()); 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; ++weightIndex;
} }
bonesGroups[0] = Math.max(bonesGroups[0], weightIndex);
} else { } else {
// 0.0 weight indicates, do not transform this vertex, but keep it in bind pose. // 0.0 weight indicates, do not transform this vertex, but keep it in bind pose.
for (Integer index : vertexIndices) { for (Integer index : vertexIndices) {
@ -350,6 +353,10 @@ import com.jme3.util.BufferUtils;
} }
++vertexIndex; ++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 { } else {
// always bind all vertices to 0-indexed bone // always bind all vertices to 0-indexed bone
// this bone makes the model look normally if vertices have no 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) { private void endBoneAssigns(int vertCount, FloatBuffer weightsFloatData) {
weightsFloatData.rewind(); weightsFloatData.rewind();
float[] weights = new float[MAXIMUM_WEIGHTS_PER_VERTEX];
for (int v = 0; v < vertCount; ++v) { for (int v = 0; v < vertCount; ++v) {
float w0 = weightsFloatData.get(), w1 = weightsFloatData.get(), w2 = weightsFloatData.get(), w3 = weightsFloatData.get(); float sum = 0;
float sum = w0 + w1 + w2 + w3; for (int i = 0; i < MAXIMUM_WEIGHTS_PER_VERTEX; ++i) {
weights[i] = weightsFloatData.get();
sum += weights[i];
}
if (sum != 1f && sum != 0.0f) { 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 // compute new vals based on sum
float sumToB = 1f / sum; float sumToB = 1f / sum;
weightsFloatData.put(w0 * sumToB); for (int i = 0; i < MAXIMUM_WEIGHTS_PER_VERTEX; ++i) {
weightsFloatData.put(w1 * sumToB); weightsFloatData.put(weights[i] * sumToB);
weightsFloatData.put(w2 * sumToB); }
weightsFloatData.put(w3 * sumToB);
} }
} }
weightsFloatData.rewind(); weightsFloatData.rewind();

Loading…
Cancel
Save