I was trying to do weighted animation blending, and I thought I’d post it here. This allows a blend weight set per channel and/or per bone.
New additions in AnimChannel.java,
[java]
public static final float DEFAULT_BLEND_WEIGHT = 1f;
private boolean useBoneWeightsFrom = false;
private float blendWeight = DEFAULT_BLEND_WEIGHT;
private float blendWeightFrom = DEFAULT_BLEND_WEIGHT;
private float[] boneWeights = null;
private float[] boneWeightsFrom = null;
/**
-
@return the blend weight for the purpose of playing multiple simultaneous animations
*/
public float getBlendWeight() {
return blendWeight;
}
/**
-
@param weight Set the blend weight of the current animation
*/
public void setBlendWeight(float weight) {
blendWeight = weight;
}
/**
-
@return whether this channel has per-bone blend weights
*/
public boolean hasWeightPerBone() {
return useBoneWeightsFrom || boneWeights != null;
}
/**
- create per-bone weights defaulting to default value; the weight array has the same number as the
- affected bones
*/
public void createBoneWeights() {
createBoneWeights(DEFAULT_BLEND_WEIGHT);
}
/**
- create per-bone weights defaulting to a weight value; the weight array has the same number as the
- affected bones
*
-
@param weight default weight value to fill the weight array
*/
public void createBoneWeights(final float weight) {
assert(weight >= 0f && weight <= 1f);
if (boneWeights == null) {
boneWeights = new float[affectedBones.size()];
for (int i = 0; i < boneWeights.length; ++i) {
if ( affectedBones.get(i) ) {
boneWeights = weight;
} else {
boneWeights = 0;
}
}
}
}
/**
-
@return The per-bone blend weight at the index
-
@param index the index, corresponding to bone index
-
@throws ArrayIndexOutOfBoundsException if index is out of bound
-
@throws NullPointerException if attempted access when <code>boneWeights</code>
- have not been created
*/
public float getBoneWeight(final int index) throws ArrayIndexOutOfBoundsException, NullPointerException
if (useBoneWeightsFrom) {
return boneWeightsFrom[index];
}
return boneWeights[index];
}
/**
- replace bone weight array with input array
*
-
@param weights source weight array
*/
public void setBoneWeights(final float[] weights) {
setBoneWeights(weights, false);
}
/**
- set per-bone weights from source
*
-
@param weights source weight array
-
@param copy whether to copy iteratively from the source; just assign and replace if false
*/
public void setBoneWeights(final float[] weights, final boolean copy) {
assert( weights.length == affectedBones.size() );
if (copy) {
assert(weights != null);
if (boneWeights == null || boneWeights.length != weights.length) {
boneWeights = new float[weights.length];
}
System.arraycopy(weights, 0, boneWeights, 0, weights.length);
} else {
boneWeights = weights;
}
}
/**
- clear per-bone weights by pointing the array to null
*/
public void clearBoneWeights() {
boneWeights = null;
}
[/java]
Changes to AnimChannel.setAnim():
[java]
if (animation != null && blendTime > 0f){
//…
blendWeightFrom = blendWeight;
boneWeightsFrom = boneWeights;
[/java]
Changes to AnimChannel.update():
[java]
//blendFrom.setTime(timeBlendFrom, 1f - blendAmount, control, this, vars);
//replaced by:
useBoneWeightsFrom = true;
blendFrom.setTime(timeBlendFrom, 1f - blendAmount * blendWeightFrom, control, this, vars);
useBoneWeightsFrom = false;
//…
//animation.setTime(time, blendAmount, control, this, vars);
//replaced by:
animation.setTime(time, blendAmount * blendWeight, control, this, vars);
[/java]
Changes to BoneTrack.setTime():
[java]
BitSet affectedBones = channel.getAffectedBones();
if (affectedBones != null && !affectedBones.get(targetBoneIndex)) {
return;
}
float adjustedWeight = weight;
if ( channel.hasWeightPerBone() ) {
adjustedWeight *= channel.getBoneWeight(targetBoneIndex);
if (adjustedWeight == 0f) {
return;
}
}
//…skip to last line
target.blendAnimTransforms(tempV, tempQ, scales != null ? tempS : null, adjustedWeight);
[/java].