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