diff options
6 files changed, 140 insertions, 62 deletions
diff --git a/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/AnimationManager.java b/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/AnimationManager.java index 2bd7ad9..5ec9bf5 100644 --- a/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/AnimationManager.java +++ b/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/AnimationManager.java @@ -10,7 +10,6 @@ package com.ardor3d.extension.animation.skeletal; -import java.util.Collection; import java.util.List; import java.util.Map; @@ -40,52 +39,55 @@ import com.google.common.collect.MapMaker; public class AnimationManager { public enum AnimationUpdateState { - play, pause, stop + Play, Pause, Stop } /** * A timer to use as our "global" time keeper. All animation sources under this manager will use this timer as their * time reference. */ - private ReadOnlyTimer _globalTimer; + protected ReadOnlyTimer _globalTimer; /** The pose(s) this manager manipulates on update. */ - private List<SkeletonPose> _applyToPoses; + protected List<SkeletonPose> _applyToPoses; /** The root of a scenegraph we can look for transform animation targets under. */ - private final Spatial _sceneRoot; + protected final Spatial _sceneRoot; /** Local instance information for any clips referenced by the layers/blend trees in this manager. */ - private final Map<AnimationClip, AnimationClipInstance> _clipInstances = new MapMaker().weakKeys().makeMap(); + protected final Map<AnimationClip, AnimationClipInstance> _clipInstances = new MapMaker().weakKeys().makeMap(); /** A logic object responsible for taking animation data and applying it to skeleton poses. */ - private AnimationApplier _applier; + protected AnimationApplier _applier; /** Our animation layers. */ - private final List<AnimationLayer> _layers = Lists.newArrayList(); + protected final List<AnimationLayer> _layers = Lists.newArrayList(); /** * A map of key->Double values, allowing control over elements under this manager without needing precise knowledge * of the layout of those layers, blend trees, etc. Missing keys will return 0.0 and log a warning. */ - private final LoggingMap<String, Double> _valuesStore = new LoggingMap<String, Double>(); + protected final LoggingMap<String, Double> _valuesStore = new LoggingMap<String, Double>(); /** * The throttle rate of animation. Default is 60fps (1/60.0). Set to 0 to disable throttling. */ - private double _updateRate = 1.0 / 60.0; + protected double _updateRate = 1.0 / 60.0; /** * The global time we last processed an animation. (To use when checking our throttle.) */ - private double _lastUpdate = 0.0; + protected double _lastUpdate = 0.0; /** * Sets the current animationState used to control if animation is playing, pausing or stopped. */ - protected AnimationUpdateState _currentAnimationState = AnimationUpdateState.play; + protected AnimationUpdateState _currentAnimationState = AnimationUpdateState.Play; - protected AnimationUpdateState _previousAnimationState; + /** + * Listeners for changes to this manager's AnimationUpdateState. + */ + protected final List<AnimationUpdateStateListener> _updateStateListeners = Lists.newArrayList(); /** * Construct a new AnimationManager. @@ -150,22 +152,114 @@ public class AnimationManager { _globalTimer = timer; } + public void play() { + setAnimationUpdateState(AnimationUpdateState.Play); + } + + public void pause() { + setAnimationUpdateState(AnimationUpdateState.Pause); + } + + public void stop() { + setAnimationUpdateState(AnimationUpdateState.Stop); + } + + public boolean isPlaying() { + return _currentAnimationState == AnimationUpdateState.Play; + } + + public boolean isPaused() { + return _currentAnimationState == AnimationUpdateState.Pause; + } + /** * @param newAnimationState * the new animation state in the animation Manager. */ - public void setAnimationState(final AnimationUpdateState newAnimationState) { - if (newAnimationState == AnimationUpdateState.pause && _currentAnimationState == AnimationUpdateState.stop) { + public void setAnimationUpdateState(final AnimationUpdateState newAnimationState) { + if (newAnimationState == _currentAnimationState) { + // ignore if unchanged. return; } - _previousAnimationState = _currentAnimationState; + final double currentTime = _globalTimer.getTimeInSeconds(); + if (newAnimationState == AnimationUpdateState.Pause) { + if (_currentAnimationState == AnimationUpdateState.Stop) { + // ignore a non-allowed situation + return; + } + + // Keep track of current time so we can resume active clips + _lastUpdate = currentTime; + } else if (newAnimationState == AnimationUpdateState.Play) { + // reset instances + if (_currentAnimationState == AnimationUpdateState.Pause) { + final double offset = currentTime - _lastUpdate; + for (final AnimationClipInstance instance : _clipInstances.values()) { + if (instance.isActive()) { + instance.setStartTime(instance.getStartTime() + offset); + } + } + } else { + // must be resuming from stop, so restart clips + for (final AnimationClipInstance instance : _clipInstances.values()) { + if (instance.isActive()) { + instance.setStartTime(currentTime); + } + } + } + } + + final AnimationUpdateState oldState = _currentAnimationState; _currentAnimationState = newAnimationState; + + // Let listeners know we have changed state. + fireAnimationUpdateStateChange(oldState); + } + + /** + * Notify any listeners of the state change + * + * @param oldState + * previous state + */ + protected void fireAnimationUpdateStateChange(final AnimationUpdateState oldState) { + for (final AnimationUpdateStateListener listener : _updateStateListeners) { + listener.stateChanged(oldState, _currentAnimationState); + } + } + + /** + * Add an AnimationUpdateStateListener to this manager. + * + * @param listener + * the listener to add. + */ + public void addAnimationUpdateStateListener(final AnimationUpdateStateListener listener) { + _updateStateListeners.add(listener); + } + + /** + * Remove an AnimationUpdateStateListener from this manager. + * + * @param listener + * the listener to remove. + * @return true if the listener was found + */ + public boolean removeAnimationUpdateStateListener(final AnimationUpdateStateListener listener) { + return _updateStateListeners.remove(listener); + } + + /** + * Remove any AnimationUpdateStateListeners registered with this manager. + */ + public void clearAnimationUpdateStateListeners() { + _updateStateListeners.clear(); } /** * @return the currentAnimationState. */ - public AnimationUpdateState getAnimationState() { + public AnimationUpdateState getAnimationUpdateState() { return _currentAnimationState; } @@ -231,6 +325,12 @@ public class AnimationManager { * SkeletonPoses set on the manager. */ public void update() { + + if (_currentAnimationState != AnimationUpdateState.Play) { + // no animation allowed. Exit. + return; + } + // grab current global time final double globalTime = _globalTimer.getTimeInSeconds(); @@ -244,10 +344,6 @@ public class AnimationManager { _lastUpdate = globalTime - (globalTime - _lastUpdate) % _updateRate; } - // update animationState - updateLayersForAnimationState(globalTime); - - // move the time forward on the layers for (int i = 0; i < _layers.size(); ++i) { final AnimationLayer layer = _layers.get(i); @@ -279,32 +375,6 @@ public class AnimationManager { } /** - * @param globalTime - * current global time in seconds - * @return - */ - protected void updateLayersForAnimationState(final double globalTime) { - - final Collection<AnimationClipInstance> clipInstances = _clipInstances.values(); - for (final AnimationClipInstance instance : clipInstances) { - switch (_currentAnimationState) { - case stop: - instance.setActive(false); - break; - case pause: - if (instance.isActive()) { - final double startTime = globalTime - instance.getCurrentTime() / instance.getTimeScale(); - instance.setStartTime(startTime); - } - break; - case play: - instance.setActive(true); - break; - } - } - } - - /** * Retrieve and track an instance of an animation clip to be used with this manager. * * @param clip diff --git a/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/AnimationUpdateStateListener.java b/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/AnimationUpdateStateListener.java new file mode 100644 index 0000000..0e6a6fc --- /dev/null +++ b/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/AnimationUpdateStateListener.java @@ -0,0 +1,19 @@ +/**
+ * Copyright (c) 2008-2012 Ardor Labs, Inc.
+ *
+ * This file is part of Ardor3D.
+ *
+ * Ardor3D is free software: you can redistribute it and/or modify it
+ * under the terms of its license which may be found in the accompanying
+ * LICENSE file or at <http://www.ardor3d.com/LICENSE>.
+ */
+
+package com.ardor3d.extension.animation.skeletal;
+
+import com.ardor3d.extension.animation.skeletal.AnimationManager.AnimationUpdateState;
+
+public interface AnimationUpdateStateListener {
+
+ public void stateChanged(AnimationUpdateState oldState, AnimationUpdateState newState);
+
+}
diff --git a/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/blendtree/ClipSource.java b/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/blendtree/ClipSource.java index 6dfb208..9674c8f 100644 --- a/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/blendtree/ClipSource.java +++ b/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/blendtree/ClipSource.java @@ -94,8 +94,6 @@ public class ClipSource implements BlendTreeSource { // update the clip with the correct clip local time. _clip.update(clockTime, instance); - - instance.setCurrentTime(clockTime); } return instance.isActive(); } diff --git a/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/clip/AnimationClipInstance.java b/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/clip/AnimationClipInstance.java index d56a391..4d7d542 100644 --- a/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/clip/AnimationClipInstance.java +++ b/trunk/ardor3d-animation/src/main/java/com/ardor3d/extension/animation/skeletal/clip/AnimationClipInstance.java @@ -39,8 +39,6 @@ public class AnimationClipInstance { /** The global start time of our clip instance. */ private double _startTime = 0.0; - private double _currentTime = 0.0; - /** Map of channel name -> state tracking objects. */ private final Map<String, Object> _clipStateObjects = Maps.newHashMap(); @@ -120,14 +118,6 @@ public class AnimationClipInstance { _startTime = startTime; } - public double getCurrentTime() { - return _currentTime; - } - - public void setCurrentTime(final double duration) { - _currentTime = duration; - } - public Object getApplyTo(final AbstractAnimationChannel channel) { final String channelName = channel.getChannelName(); Object rVal = _clipStateObjects.get(channelName); diff --git a/trunk/ardor3d-examples/src/main/java/com/ardor3d/example/pipeline/AnimationStateExample.java b/trunk/ardor3d-examples/src/main/java/com/ardor3d/example/pipeline/AnimationStateExample.java index ef95fc2..10a6002 100644 --- a/trunk/ardor3d-examples/src/main/java/com/ardor3d/example/pipeline/AnimationStateExample.java +++ b/trunk/ardor3d-examples/src/main/java/com/ardor3d/example/pipeline/AnimationStateExample.java @@ -180,10 +180,10 @@ public class AnimationStateExample extends ExampleBase { playPauseButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent event) { if (playPauseButton.getText().equals("Pause")) { - manager.setAnimationState(AnimationUpdateState.pause); + manager.setAnimationUpdateState(AnimationUpdateState.Pause); playPauseButton.setButtonText("Play"); } else { - manager.setAnimationState(AnimationUpdateState.play); + manager.setAnimationUpdateState(AnimationUpdateState.Play); playPauseButton.setButtonText("Pause"); } } @@ -195,7 +195,7 @@ public class AnimationStateExample extends ExampleBase { .setLayoutData(new AnchorLayoutData(Alignment.TOP_LEFT, playPauseButton, Alignment.BOTTOM_LEFT, 0, -5)); stopButton.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent event) { - manager.setAnimationState(AnimationUpdateState.stop); + manager.setAnimationUpdateState(AnimationUpdateState.Stop); playPauseButton.setButtonText("Play"); } }); diff --git a/trunk/ardor3d-examples/src/main/resources/com/ardor3d/example/i18n/example_descriptions.properties b/trunk/ardor3d-examples/src/main/resources/com/ardor3d/example/i18n/example_descriptions.properties index 8b1b7b6..03145e0 100644 --- a/trunk/ardor3d-examples/src/main/resources/com/ardor3d/example/i18n/example_descriptions.properties +++ b/trunk/ardor3d-examples/src/main/resources/com/ardor3d/example/i18n/example_descriptions.properties @@ -38,6 +38,7 @@ com.ardor3d.example.interpolation.LinearVector3InterpolationControllerExample=A com.ardor3d.example.interpolation.QuaternionInterpolationControllerExample=A demonstration of the QuaternionInterpolationController class; which will rotate a Node each epoch by interpolating between the given quaternions.
com.ardor3d.example.pipeline.AnimationBlinnPhongExample=Illustrates gpu skinning with normal map, specular map and diffuse coloring.
com.ardor3d.example.pipeline.AnimationDemoExample=Illustrates loading several animations from Collada and arranging them in a controllable blend tree.
+com.ardor3d.example.pipeline.AnimationStateExample=Adds play/pause/stop controls to AnimationDemoExample.
com.ardor3d.example.pipeline.ColladaExample=Illustrates loading a model from Collada. If the model also contains an animation, the animation is played as well.
com.ardor3d.example.pipeline.ColladaManualAnimationExample=Illustrates loading a model from Collada and procedurally animating its joints.
com.ardor3d.example.pipeline.ExportImportExample=A demonstration of the BinaryImporter and BinaryExporter classes; which can export/import a Node to/from a data stream.
|