Skip to content

Commit 7911a61

Browse files
authored
Some enhancement to new animation system (#1845)
* Some enhancement to new animation system, including: * Option to enable/disable animation mask propagation to child actions. * Option to control max transition weight. For example useful for controlling smooth animation transition when an animation is removed from an upper layer. * Added animation loop support in AnimLayer. * AnimLayer can now also keep action name, so one can easily lookup currently playing action name in an specific layer. * Minor Javadoc fix. * AnimLayer: clear `currentActionName` inside `cloneFields` method.
1 parent cf9aa9c commit 7911a61

File tree

5 files changed

+225
-11
lines changed

5 files changed

+225
-11
lines changed

jme3-core/src/main/java/com/jme3/anim/AnimComposer.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2021 jMonkeyEngine
2+
* Copyright (c) 2009-2022 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -115,7 +115,7 @@ public void removeAnimClip(AnimClip anim) {
115115
}
116116

117117
/**
118-
* Run an action on the default layer.
118+
* Run an action on the default layer. By default action will loop.
119119
*
120120
* @param name The name of the action to run.
121121
* @return The action corresponding to the given name.
@@ -125,16 +125,28 @@ public Action setCurrentAction(String name) {
125125
}
126126

127127
/**
128-
* Run an action on specified layer.
128+
* Run an action on specified layer. By default action will loop.
129129
*
130130
* @param actionName The name of the action to run.
131131
* @param layerName The layer on which action should run.
132132
* @return The action corresponding to the given name.
133133
*/
134134
public Action setCurrentAction(String actionName, String layerName) {
135+
return setCurrentAction(actionName, layerName, true);
136+
}
137+
138+
/**
139+
* Run an action on specified layer.
140+
*
141+
* @param actionName The name of the action to run.
142+
* @param layerName The layer on which action should run.
143+
* @param loop True if the action must loop.
144+
* @return The action corresponding to the given name.
145+
*/
146+
public Action setCurrentAction(String actionName, String layerName, boolean loop) {
135147
AnimLayer l = getLayer(layerName);
136148
Action currentAction = action(actionName);
137-
l.setCurrentAction(currentAction);
149+
l.setCurrentAction(actionName, currentAction, loop);
138150

139151
return currentAction;
140152
}

jme3-core/src/main/java/com/jme3/anim/AnimLayer.java

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2009-2021 jMonkeyEngine
2+
* Copyright (c) 2009-2022 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -53,6 +53,10 @@ public class AnimLayer implements JmeCloneable {
5353
* The Action currently running on this layer, or null if none.
5454
*/
5555
private Action currentAction;
56+
/**
57+
* The name of Action currently running on this layer, or null if none.
58+
*/
59+
private String currentActionName;
5660
/**
5761
* The composer that owns this layer. Were it not for cloning, this field
5862
* would be final.
@@ -77,6 +81,8 @@ public class AnimLayer implements JmeCloneable {
7781
*/
7882
final private String name;
7983

84+
private boolean loop = true;
85+
8086
/**
8187
* Instantiates a layer without a manager or a current Action, owned by the
8288
* specified composer.
@@ -105,6 +111,15 @@ public Action getCurrentAction() {
105111
return currentAction;
106112
}
107113

114+
/**
115+
* Returns the name of the Action that's currently running.
116+
*
117+
* @return the pre-existing instance, or null if none
118+
*/
119+
public String getCurrentActionName() {
120+
return currentActionName;
121+
}
122+
108123
/**
109124
* Returns the current manager.
110125
*
@@ -145,14 +160,42 @@ public double getTime() {
145160

146161
/**
147162
* Runs the specified Action, starting from time = 0. This cancels any
148-
* Action previously running on this layer.
163+
* Action previously running on this layer. By default Action will loop.
149164
*
150165
* @param actionToRun the Action to run (alias created) or null for no
151166
* action
152167
*/
153168
public void setCurrentAction(Action actionToRun) {
169+
this.setCurrentAction(null, actionToRun);
170+
}
171+
172+
/**
173+
* Runs the specified Action, starting from time = 0. This cancels any
174+
* Action previously running on this layer. By default Action will loop.
175+
*
176+
* @param actionName the Action name or null for no action name
177+
* @param actionToRun the Action to run (alias created) or null for no
178+
* action
179+
*/
180+
public void setCurrentAction(String actionName, Action actionToRun) {
181+
this.setCurrentAction(actionName, actionToRun, true);
182+
}
183+
184+
/**
185+
* Runs the specified Action, starting from time = 0. This cancels any
186+
* Action previously running on this layer.
187+
*
188+
* @param actionName the Action name or null for no action name
189+
* @param actionToRun the Action to run (alias created) or null for no
190+
* action
191+
* @param loop true if Action must loop. If it is false, Action will be
192+
* removed after finished running
193+
*/
194+
public void setCurrentAction(String actionName, Action actionToRun, boolean loop) {
154195
this.time = 0.0;
155196
this.currentAction = actionToRun;
197+
this.currentActionName = actionName;
198+
this.loop = loop;
156199
}
157200

158201
/**
@@ -181,6 +224,24 @@ public void setTime(double animationTime) {
181224
}
182225
}
183226

227+
/**
228+
* @return True if the Action will keep looping after it is done playing,
229+
* otherwise, returns false
230+
*/
231+
public boolean isLooping() {
232+
return loop;
233+
}
234+
235+
/**
236+
* Sets the looping mode for this layer. The default is true.
237+
*
238+
* @param loop True if the action should keep looping after it is done
239+
* playing
240+
*/
241+
public void setLooping(boolean loop) {
242+
this.loop = loop;
243+
}
244+
184245
/**
185246
* Updates the animation time and the current Action during a
186247
* controlUpdate().
@@ -211,6 +272,10 @@ void update(float appDeltaTimeInSeconds) {
211272

212273
if (!running) { // went past the end of the current Action
213274
time = 0.0;
275+
if (!loop) {
276+
// Clear the current action
277+
setCurrentAction(null);
278+
}
214279
}
215280
}
216281

@@ -229,6 +294,7 @@ void update(float appDeltaTimeInSeconds) {
229294
public void cloneFields(Cloner cloner, Object original) {
230295
composer = cloner.clone(composer);
231296
currentAction = null;
297+
currentActionName = null;
232298
}
233299

234300
@Override

jme3-core/src/main/java/com/jme3/anim/tween/action/BaseAction.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,37 @@
1+
/*
2+
* Copyright (c) 2009-2022 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
132
package com.jme3.anim.tween.action;
233

34+
import com.jme3.anim.AnimationMask;
335
import com.jme3.anim.tween.ContainsTweens;
436
import com.jme3.anim.tween.Tween;
537
import com.jme3.util.SafeArrayList;
@@ -9,6 +41,7 @@
941
public class BaseAction extends Action {
1042

1143
final private Tween tween;
44+
private boolean maskPropagationEnabled = true;
1245

1346
public BaseAction(Tween tween) {
1447
this.tween = tween;
@@ -30,6 +63,33 @@ private void gatherActions(Tween tween, List<Action> subActions) {
3063
}
3164
}
3265

66+
/**
67+
* @return true if mask propagation to child actions is enabled else returns false
68+
*/
69+
public boolean isMaskPropagationEnabled() {
70+
return maskPropagationEnabled;
71+
}
72+
73+
/**
74+
*
75+
* @param maskPropagationEnabled If true, then mask set by AnimLayer will be
76+
* forwarded to all child actions (Default=true)
77+
*/
78+
public void setMaskPropagationEnabled(boolean maskPropagationEnabled) {
79+
this.maskPropagationEnabled = maskPropagationEnabled;
80+
}
81+
82+
@Override
83+
public void setMask(AnimationMask mask) {
84+
super.setMask(mask);
85+
86+
if (maskPropagationEnabled) {
87+
for (Action action : actions) {
88+
action.setMask(mask);
89+
}
90+
}
91+
}
92+
3393
@Override
3494
public boolean interpolate(double t) {
3595
return tween.interpolate(t);

jme3-core/src/main/java/com/jme3/anim/tween/action/BlendableAction.java

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,34 @@
1+
/*
2+
* Copyright (c) 2009-2022 jMonkeyEngine
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are
7+
* met:
8+
*
9+
* * Redistributions of source code must retain the above copyright
10+
* notice, this list of conditions and the following disclaimer.
11+
*
12+
* * Redistributions in binary form must reproduce the above copyright
13+
* notice, this list of conditions and the following disclaimer in the
14+
* documentation and/or other materials provided with the distribution.
15+
*
16+
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17+
* may be used to endorse or promote products derived from this software
18+
* without specific prior written permission.
19+
*
20+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22+
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23+
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24+
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25+
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26+
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27+
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28+
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29+
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
*/
132
package com.jme3.anim.tween.action;
233

334
import com.jme3.anim.tween.AbstractTween;
@@ -11,6 +42,7 @@ public abstract class BlendableAction extends Action {
1142

1243
protected BlendableAction collectTransformDelegate;
1344
private float transitionWeight = 1.0f;
45+
private double maxTransitionWeight = 1.0;
1446
private double transitionLength = 0.4f;
1547
private float weight = 1f;
1648
private TransitionTween transition = new TransitionTween(transitionLength);
@@ -81,6 +113,23 @@ protected float getTransitionWeight() {
81113
return transitionWeight;
82114
}
83115

116+
/**
117+
* @param maxTransitionWeight The max transition weight. Must be &gt= 0 and &lt=1 (default=1)
118+
*/
119+
public void setMaxTransitionWeight(double maxTransitionWeight) {
120+
assert maxTransitionWeight >= 0 && maxTransitionWeight <= 1;
121+
122+
this.maxTransitionWeight = maxTransitionWeight;
123+
}
124+
125+
/**
126+
*
127+
* @return The max transition weight (default=1)
128+
*/
129+
public double getMaxTransitionWeight() {
130+
return maxTransitionWeight;
131+
}
132+
84133
/**
85134
* Create a shallow clone for the JME cloner.
86135
*
@@ -121,7 +170,7 @@ public TransitionTween(double length) {
121170

122171
@Override
123172
protected void doInterpolate(double t) {
124-
transitionWeight = (float) t;
173+
transitionWeight = (float) Math.min(t, maxTransitionWeight);
125174
}
126175
}
127176

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

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017-2021 jMonkeyEngine
2+
* Copyright (c) 2017-2022 jMonkeyEngine
33
* All rights reserved.
44
*
55
* Redistribution and use in source and binary forms, with or without
@@ -32,8 +32,7 @@
3232
package jme3test.model.anim;
3333

3434
import com.jme3.anim.*;
35-
import com.jme3.anim.tween.action.BlendAction;
36-
import com.jme3.anim.tween.action.LinearBlendSpace;
35+
import com.jme3.anim.tween.action.*;
3736
import com.jme3.anim.util.AnimMigrationUtils;
3837
import com.jme3.app.ChaseCameraAppState;
3938
import com.jme3.app.SimpleApplication;
@@ -157,9 +156,10 @@ public void onAction(String name, boolean isPressed, float tpf) {
157156
@Override
158157
public void onAction(String name, boolean isPressed, float tpf) {
159158
if (isPressed) {
160-
composer.setCurrentAction("Wave", "LeftArm");
159+
((BlendableAction)composer.setCurrentAction("Wave", "LeftArm", false)).setMaxTransitionWeight(0.9);
161160
}
162161
}
162+
163163
}, "mask");
164164

165165
inputManager.addMapping("blendUp", new KeyTrigger(KeyInput.KEY_UP));
@@ -184,6 +184,33 @@ public void onAnalog(String name, float value, float tpf) {
184184
//System.err.println(blendValue);
185185
}
186186
}, "blendUp", "blendDown");
187+
188+
inputManager.addMapping("maxTransitionWeightInc", new KeyTrigger(KeyInput.KEY_ADD));
189+
inputManager.addMapping("maxTransitionWeightDec", new KeyTrigger(KeyInput.KEY_SUBTRACT));
190+
191+
inputManager.addListener(new AnalogListener() {
192+
193+
@Override
194+
public void onAnalog(String name, float value, float tpf) {
195+
if (name.equals("maxTransitionWeightInc")) {
196+
Action action = composer.getCurrentAction();
197+
if (action instanceof BlendableAction) {
198+
BlendableAction ba = (BlendableAction) action;
199+
ba.setMaxTransitionWeight(Math.min(ba.getMaxTransitionWeight() + 0.01, 1.0));
200+
System.out.println("MaxTransitionWeight=" + ba.getMaxTransitionWeight());
201+
}
202+
}
203+
if (name.equals("maxTransitionWeightDec")) {
204+
Action action = composer.getCurrentAction();
205+
if (action instanceof BlendableAction) {
206+
BlendableAction ba = (BlendableAction) action;
207+
ba.setMaxTransitionWeight(Math.max(ba.getMaxTransitionWeight() - 0.01, 0.0));
208+
System.out.println("MaxTransitionWeight=" + ba.getMaxTransitionWeight());
209+
}
210+
}
211+
//System.err.println(blendValue);
212+
}
213+
}, "maxTransitionWeightInc", "maxTransitionWeightDec");
187214
}
188215

189216
private void setupModel(Spatial model) {

0 commit comments

Comments
 (0)