Prechádzať zdrojové kódy

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

Kae..pl 12 rokov pred
rodič
commit
1c2baedbc1

+ 43 - 33
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();