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
This commit is contained in:
parent
e4d715d216
commit
1c2baedbc1
@ -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,42 +297,52 @@ 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 (weight == 0.0f) {
|
if(weightIndex < MAXIMUM_WEIGHTS_PER_VERTEX) {
|
||||||
boneIndex = Integer.valueOf(0);
|
if (weight == 0.0f) {
|
||||||
}
|
boneIndex = Integer.valueOf(0);
|
||||||
// we apply the weight to all referenced vertices
|
}
|
||||||
for (Integer index : vertexIndices) {
|
// we apply the weight to all referenced vertices
|
||||||
weightsFloatData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, weight);
|
for (Integer index : vertexIndices) {
|
||||||
indicesData.put(index * MAXIMUM_WEIGHTS_PER_VERTEX + weightIndex, boneIndex.byteValue());
|
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;
|
++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…
x
Reference in New Issue
Block a user