Ver código fonte

Adds animation mask to the animation system

Nehon 7 anos atrás
pai
commit
9df4449f49

+ 5 - 0
jme3-core/src/main/java/com/jme3/anim/AnimClip.java

@@ -66,6 +66,11 @@ public class AnimClip implements JmeCloneable, Savable {
         this.tracks = newTracks;
     }
 
+    @Override
+    public String toString() {
+        return "Clip " + name + ", " + length + 's';
+    }
+
     @Override
     public void write(JmeExporter ex) throws IOException {
         OutputCapsule oc = ex.getCapsule(this);

+ 68 - 10
jme3-core/src/main/java/com/jme3/anim/AnimComposer.java

@@ -8,6 +8,7 @@ import com.jme3.renderer.RenderManager;
 import com.jme3.renderer.ViewPort;
 import com.jme3.scene.control.AbstractControl;
 import com.jme3.util.clone.Cloner;
+import com.jme3.util.clone.JmeCloneable;
 
 import java.io.IOException;
 import java.util.*;
@@ -17,12 +18,16 @@ import java.util.*;
  */
 public class AnimComposer extends AbstractControl {
 
+    public static final String DEFAULT_LAYER = "Default";
     private Map<String, AnimClip> animClipMap = new HashMap<>();
 
-    private Action currentAction;
     private Map<String, Action> actions = new HashMap<>();
     private float globalSpeed = 1f;
-    private float time;
+    private Map<String, Layer> layers = new LinkedHashMap<>();
+
+    public AnimComposer() {
+        layers.put(DEFAULT_LAYER, new Layer());
+    }
 
     /**
      * Retrieve an animation from the list of animations.
@@ -60,8 +65,17 @@ public class AnimComposer extends AbstractControl {
     }
 
     public Action setCurrentAction(String name) {
-        currentAction = action(name);
-        time = 0;
+        return setCurrentAction(name, DEFAULT_LAYER);
+    }
+
+    public Action setCurrentAction(String actionName, String layerName) {
+        Layer l = layers.get(layerName);
+        if (l == null) {
+            throw new IllegalArgumentException("Unknown layer " + layerName);
+        }
+        Action currentAction = action(actionName);
+        l.time = 0;
+        l.currentAction = currentAction;
         return currentAction;
     }
 
@@ -84,6 +98,12 @@ public class AnimComposer extends AbstractControl {
         return action;
     }
 
+    public void makeLayer(String name, AnimationMask mask){
+        Layer l = new Layer();
+        l.mask = mask;
+        layers.put(name, l);
+    }
+
 
     public BaseAction actionSequence(String name, Tween... tweens) {
         BaseAction action = new BaseAction(Tweens.sequence(tweens));
@@ -103,8 +123,10 @@ public class AnimComposer extends AbstractControl {
     }
 
     public void reset() {
-        currentAction = null;
-        time = 0;
+        for (Layer layer : layers.values()) {
+            layer.currentAction = null;
+            layer.time = 0;
+        }
     }
 
     public Collection<AnimClip> getAnimClips() {
@@ -117,11 +139,17 @@ public class AnimComposer extends AbstractControl {
 
     @Override
     protected void controlUpdate(float tpf) {
-        if (currentAction != null) {
-            time += tpf;
-            boolean running = currentAction.interpolate(time * globalSpeed);
+        for (Layer layer : layers.values()) {
+            Action currentAction = layer.currentAction;
+            if (currentAction == null) {
+                continue;
+            }
+            layer.time += tpf;
+            currentAction.setMask(layer.mask);
+            boolean running = currentAction.interpolate(layer.time * globalSpeed);
+            currentAction.setMask(null);
             if (!running) {
-                time = 0;
+                layer.time = 0;
             }
         }
     }
@@ -162,6 +190,14 @@ public class AnimComposer extends AbstractControl {
         }
         actions = act;
         animClipMap = clips;
+
+        Map<String, Layer> newLayers = new LinkedHashMap<>();
+        for (String key : layers.keySet()) {
+            newLayers.put(key, cloner.clone(layers.get(key)));
+        }
+
+        layers = newLayers;
+
     }
 
     @Override
@@ -177,4 +213,26 @@ public class AnimComposer extends AbstractControl {
         OutputCapsule oc = ex.getCapsule(this);
         oc.writeStringSavableMap(animClipMap, "animClipMap", new HashMap<String, AnimClip>());
     }
+
+    public static class Layer implements JmeCloneable {
+        private Action currentAction;
+        private AnimationMask mask;
+        private float weight;
+        private float time;
+
+        @Override
+        public Object jmeClone() {
+            try {
+                Layer clone = (Layer) super.clone();
+                return clone;
+            } catch (CloneNotSupportedException ex) {
+                throw new AssertionError();
+            }
+        }
+
+        @Override
+        public void cloneFields(Cloner cloner, Object original) {
+            currentAction = null;
+        }
+    }
 }

+ 12 - 0
jme3-core/src/main/java/com/jme3/anim/AnimationMask.java

@@ -0,0 +1,12 @@
+package com.jme3.anim;
+
+/**
+ * Created by Nehon
+ * An AnimationMask is defining a subset of elements on which an animation will be applied.
+ * Most used implementation is the ArmatureMask that defines a subset of joints in an Armature.
+ */
+public interface AnimationMask {
+
+    boolean contains(Object target);
+
+}

+ 3 - 0
jme3-core/src/main/java/com/jme3/anim/Armature.java

@@ -46,6 +46,7 @@ public class Armature implements JmeCloneable, Savable {
         List<Joint> rootJointList = new ArrayList<>();
         for (int i = jointList.length - 1; i >= 0; i--) {
             Joint joint = jointList[i];
+            joint.setId(i);
             instanciateJointModelTransform(joint);
             if (joint.getParent() == null) {
                 rootJointList.add(joint);
@@ -276,7 +277,9 @@ public class Armature implements JmeCloneable, Savable {
             throw new AssetLoadException("Cannnot find class for name " + className);
         }
 
+        int i = 0;
         for (Joint joint : jointList) {
+            joint.setId(i++);
             instanciateJointModelTransform(joint);
         }
         createSkinningMatrices();

+ 62 - 0
jme3-core/src/main/java/com/jme3/anim/ArmatureMask.java

@@ -0,0 +1,62 @@
+package com.jme3.anim;
+
+import java.util.BitSet;
+
+public class ArmatureMask implements AnimationMask {
+
+    private BitSet affectedJoints = new BitSet();
+
+    @Override
+    public boolean contains(Object target) {
+        return affectedJoints.get(((Joint) target).getId());
+    }
+
+    public static ArmatureMask createMask(Armature armature, String fromJoint) {
+        ArmatureMask mask = new ArmatureMask();
+        mask.addFromJoint(armature, fromJoint);
+        return mask;
+    }
+
+    public static ArmatureMask createMask(Armature armature, String... joints) {
+        ArmatureMask mask = new ArmatureMask();
+        mask.addBones(armature, joints);
+        for (String joint : joints) {
+            mask.affectedJoints.set(armature.getJoint(joint).getId());
+        }
+        return mask;
+    }
+
+    /**
+     * Add joints to be influenced by this animation mask.
+     */
+    public void addBones(Armature armature, String... jointNames) {
+        for (String jointName : jointNames) {
+            Joint joint = findJoint(armature, jointName);
+            affectedJoints.set(joint.getId());
+        }
+    }
+
+    private Joint findJoint(Armature armature, String jointName) {
+        Joint joint = armature.getJoint(jointName);
+        if (joint == null) {
+            throw new IllegalArgumentException("Cannot find joint " + jointName);
+        }
+        return joint;
+    }
+
+    /**
+     * Add a joint and all its sub armature joints to be influenced by this animation mask.
+     */
+    public void addFromJoint(Armature armature, String jointName) {
+        Joint joint = findJoint(armature, jointName);
+        recurseAddJoint(joint);
+    }
+
+    private void recurseAddJoint(Joint joint) {
+        affectedJoints.set(joint.getId());
+        for (Joint j : joint.getChildren()) {
+            recurseAddJoint(j);
+        }
+    }
+
+}

+ 9 - 0
jme3-core/src/main/java/com/jme3/anim/Joint.java

@@ -22,6 +22,7 @@ import java.util.List;
 public class Joint implements Savable, JmeCloneable, HasLocalTransform {
 
     private String name;
+    private int id;
     private Joint parent;
     private SafeArrayList<Joint> children = new SafeArrayList<>(Joint.class);
     private Geometry targetGeometry;
@@ -280,6 +281,14 @@ public class Joint implements Savable, JmeCloneable, HasLocalTransform {
         return inverseModelBindMatrix;
     }
 
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
     @Override
     public Object jmeClone() {
         try {

+ 3 - 2
jme3-core/src/main/java/com/jme3/anim/MatrixJointModelTransform.java

@@ -19,11 +19,11 @@ public class MatrixJointModelTransform implements JointModelTransform {
         if (parent != null) {
             ((MatrixJointModelTransform) parent.getJointModelTransform()).getModelTransformMatrix().mult(modelTransformMatrix, modelTransformMatrix);
         }
-        modelTransform.fromTransformMatrix(modelTransformMatrix);
+
     }
 
     public void getOffsetTransform(Matrix4f outTransform, Matrix4f inverseModelBindMatrix) {
-        outTransform.set(modelTransformMatrix).mult(inverseModelBindMatrix, outTransform);
+        modelTransformMatrix.mult(inverseModelBindMatrix, outTransform);
     }
 
     @Override
@@ -41,6 +41,7 @@ public class MatrixJointModelTransform implements JointModelTransform {
 
     @Override
     public Transform getModelTransform() {
+        modelTransform.fromTransformMatrix(modelTransformMatrix);
         return modelTransform;
     }
 }

+ 10 - 0
jme3-core/src/main/java/com/jme3/anim/tween/action/Action.java

@@ -1,5 +1,6 @@
 package com.jme3.anim.tween.action;
 
+import com.jme3.anim.AnimationMask;
 import com.jme3.anim.tween.Tween;
 
 public abstract class Action implements Tween {
@@ -7,6 +8,7 @@ public abstract class Action implements Tween {
     protected Action[] actions;
     private double length;
     private double speed = 1;
+    private AnimationMask mask;
 
     protected Action(Tween... tweens) {
         this.actions = new Action[tweens.length];
@@ -46,4 +48,12 @@ public abstract class Action implements Tween {
     public void setSpeed(double speed) {
         this.speed = speed;
     }
+
+    public AnimationMask getMask() {
+        return mask;
+    }
+
+    public void setMask(AnimationMask mask) {
+        this.mask = mask;
+    }
 }

+ 9 - 1
jme3-core/src/main/java/com/jme3/anim/tween/action/ClipAction.java

@@ -25,7 +25,11 @@ public class ClipAction extends BlendableAction {
         AnimTrack[] tracks = clip.getTracks();
         for (AnimTrack track : tracks) {
             if (track instanceof TransformTrack) {
-                interpolateTransformTrack(t, (TransformTrack) track);
+                TransformTrack tt = (TransformTrack) track;
+                if(getMask() != null && !getMask().contains(tt.getTarget())){
+                    continue;
+                }
+                interpolateTransformTrack(t, tt);
             } else if (track instanceof MorphTrack) {
                 interpolateMorphTrack(t, (MorphTrack) track);
             }
@@ -60,6 +64,10 @@ public class ClipAction extends BlendableAction {
 
     }
 
+    public String toString() {
+        return clip.toString();
+    }
+
     @Override
     public Collection<HasLocalTransform> getTargets() {
         List<HasLocalTransform> targets = new ArrayList<>(clip.getTracks().length);

+ 14 - 2
jme3-examples/src/main/java/jme3test/model/anim/TestAnimMigration.java

@@ -1,7 +1,6 @@
 package jme3test.model.anim;
 
-import com.jme3.anim.AnimComposer;
-import com.jme3.anim.SkinningControl;
+import com.jme3.anim.*;
 import com.jme3.anim.tween.action.Action;
 import com.jme3.anim.tween.action.BlendAction;
 import com.jme3.anim.tween.action.BlendableAction;
@@ -125,8 +124,19 @@ public class TestAnimMigration extends SimpleApplication {
             }
         }, "toggleArmature");
 
+        inputManager.addMapping("mask", new KeyTrigger(KeyInput.KEY_M));
+        inputManager.addListener(new ActionListener() {
+            @Override
+            public void onAction(String name, boolean isPressed, float tpf) {
+                if (isPressed) {
+                    composer.setCurrentAction("Wave", "LeftArm");
+                }
+            }
+        }, "mask");
+
         inputManager.addMapping("blendUp", new KeyTrigger(KeyInput.KEY_UP));
         inputManager.addMapping("blendDown", new KeyTrigger(KeyInput.KEY_DOWN));
+
         inputManager.addListener(new AnalogListener() {
 
             @Override
@@ -174,6 +184,8 @@ public class TestAnimMigration extends SimpleApplication {
 
             composer.action("Walk").setSpeed(-1);
 
+            composer.makeLayer("LeftArm", ArmatureMask.createMask(sc.getArmature(), "shoulder.L"));
+
             anims.addFirst("Sequence");
             anims.addFirst("Blend");