I’ve been in the process of converting to the new anim system for a while now and one of the things that has stood out to me is how the new ArmatureMask works a lot differently than the old AnimChannel.
I see the advantages of ArmatureMask being a lighter weight class, and allows the developer to run a channel on a specific layer using its String name, rather than requiring a reference to the whole ArmatureMask or AnimChannel object.
But ArmatureMask unfortunately does not store the list of joints (something I have found necessary when using DynamicAnimControl for ragdoll effects on specific bones). I’ve also noticed from my testing in an editor so far (please correct me if I’m wrong) that a bone cannot be in more than one ArmatureMask at a time, so I wrote this class so that boneChannels can be nested within other bone channels, this way you don’t have to constantly run the animation on the the torso, left arm, right arm, and head layers individually, and could instead group those into an upperBodyBoneChannel that contains all of them.
The other advantage of using this in place of ArmatureMask is that it can be used as a 1-to-1 drop in replacement for AnimChannel. I had a lot of code from the old anim system that handles running animations on different limbs effectively using AnimChannels, so I wrote BoneChannel to have all of these same methods and greatly reduce the amount of refactoring I needed to do.
Does anyone else think this class would be useful?
…
import com.jme3.anim.AnimClip;
import com.jme3.anim.AnimComposer;
import com.jme3.anim.Armature;
import com.jme3.anim.ArmatureMask;
import com.jme3.anim.SkinningControl;
import com.jme3.anim.tween.action.Action;
import com.jme3.animation.LoopMode;
import java.util.ArrayList;
/**
*
* @author ryan
*/
public class BoneChannel {
//this class is basically just an ArmatureMask that stores the name of the bones in the mask, since the mask is needed for AnimComposer but the bone names are needed for DynamicAnimControl. so this way
// you can easily switch a whole channel from running an anim to stopping the anim and applying ragDoll or dynamic force with DAC
public ArrayList<BoneChannel> subBoneChannels; //list of other channels that are a part of this one, e.i. store the left arm, right arm, and torso boneChannels into this list for a new upperBody boneChannel
// that re-uses the masks for those without creating new masks and causing a bone to exist in more than one mask? (still need more testing, but fairly sure a bone cannot be in more than 1 mask layer at a time)
private String layerName;
public ArrayList<String> bones;
public ArmatureMask armatureMask;
private AnimComposer animComposer;
private SkinningControl skinningControl;
private Armature armature;
public LoopMode loopMode;
public String currentAnimation;
public float speed;
public String getLayerName() { return layerName; }
public void setSpeed(float speed) { this.speed = speed; }
public float getSpeed() { return speed; }
public void setLoopMode(LoopMode loopMode) { this.loopMode = loopMode; }
public LoopMode getLoopMode() { return loopMode; }
public String getAnimationName() { return currentAnimation; }
public BoneChannel(String layerName, SkinningControl skinningControl, AnimComposer animComposer) {
this.skinningControl = skinningControl;
this.animComposer = animComposer;
armature = skinningControl.getArmature();
}
public void addBonesStrings(String... boneNames){
addBones(boneNames);
}
public void clear(){
animComposer.removeLayer(layerName); //clear existent layer
}
public void addBones(String[] boneNames){
if(armatureMask == null){
armatureMask = new ArmatureMask(armature);
}
armatureMask.addBones(armature, boneNames);
for(int b = 0; b < boneNames.length; b++){
String boneName = boneNames[b];
if(!bones.contains(boneName)){
bones.add(boneName);
}
}
animComposer.makeLayer(layerName, armatureMask);
}
public void setArmatureMask(ArmatureMask armatureMask){
clear();
this.armatureMask = armatureMask;
animComposer.makeLayer(layerName, armatureMask);
}
public void setAnim(String animName, float blendTime) { //is blend time relevent to new anim system in a way that can be incorporated here like it was with old animChannel?
currentAnimation = animName;
animComposer.setCurrentAction(currentAnimation, layerName);
}
public void setAnim(String animName) {
currentAnimation = animName;
animComposer.setCurrentAction(currentAnimation, layerName);
}
public AnimClip getCurrentAnimClip(){ return animComposer.getAnimClip(currentAnimation); }
public void setTime(double newTime) { animComposer.setTime(layerName, newTime); }
public double getTime() { return animComposer.getTime(layerName); }
public double getAnimMaxTime() {
AnimClip animClip = getCurrentAnimClip();
if(animClip != null){
return animClip.getLength();
}
return 0;
}
}
I should also mention that this class not entirely done, but I wanted to run this idea past some other JME users with more experience in the new animation system to see if this sounds useful or if I’m wasting my time reinventing something that already exists or isn’t necessary.