//********************************** Banshee Engine (www.banshee3d.com) **************************************************// //**************** Copyright (c) 2016 Marko Pintera (marko.pintera@gmail.com). All rights reserved. **********************// using System; using System.Runtime.InteropServices; namespace BansheeEngine { /** @addtogroup Animation * @{ */ /// /// Determines how an animation clip behaves when it reaches the end. /// public enum AnimWrapMode // Note: Must match C++ enum AnimWrapMode { /// /// Loop around to the beginning/end when the last/first frame is reached. /// Loop, /// /// Clamp to end/beginning, keeping the last/first frame active. /// Clamp } /// /// Represents an animation clip used in 1D blending. Each clip has a position on the number line. /// public class BlendClipInfo { public AnimationClip clip; public float position; } /// /// Defines a 1D blend where two animation clips are blended between each other using linear interpolation. /// public class Blend1DInfo { public BlendClipInfo[] clips; } /// /// Defines a 2D blend where two animation clips are blended between each other using bilinear interpolation. /// public class Blend2DInfo { public AnimationClip topLeftClip; public AnimationClip topRightClip; public AnimationClip botLeftClip; public AnimationClip botRightClip; } /// /// Contains information about a currently playing animation clip. /// [StructLayout(LayoutKind.Sequential), SerializeObject] public struct AnimationClipState // Note: Must match C++ struct AnimationClipState { /// /// Layer the clip is playing on. Multiple clips can be played simulatenously on different layers. /// public int layer; /// /// Current time the animation is playing from. /// public float time; /// /// Speed at which the animation is playing. /// public float speed; /// /// Determines how much of an influence does the clip have on the final pose. /// public float weight; /// /// Determines what happens to other animation clips when a new clip starts playing. /// public AnimWrapMode wrapMode; /// /// Initializes the state with default values. /// public void InitDefault() { speed = 1.0f; weight = 1.0f; wrapMode = AnimWrapMode.Loop; } } /// /// Handles animation playback. Takes one or multiple animation clips as input and evaluates them every animation update /// tick depending on set properties.The evaluated data is used by the core thread for skeletal animation, by the sim /// thread for updating attached scene objects and bones (if skeleton is attached), or the data is made available for /// manual queries in the case of generic animation. /// public class Animation : Component { private NativeAnimation _native; [SerializeField] private SerializableData serializableData = new SerializableData(); /// /// Returns the non-component version of Animation that is wrapped by this component. /// internal NativeAnimation Native { get { return _native; } } /// /// Determines the default clip to play as soon as the component is enabled. If more control over playing clips is /// needed use the , , and /// methods to queue clips for playback manually, and method for modify their states /// individually. /// public AnimationClip DefaultClip { get { return serializableData.defaultClip; } set { serializableData.defaultClip = value; if (value != null && _native != null) _native.Play(value); } } /// /// Determines the wrap mode for all active animations. Wrap mode determines what happens when animation reaches the /// first or last frame. /// /// public AnimWrapMode WrapMode { get { return serializableData.wrapMode; } set { serializableData.wrapMode = value; if (_native != null) _native.WrapMode = value; } } /// /// Determines the speed for all animations. The default value is 1.0f. Use negative values to play-back in reverse. /// public float Speed { get { return serializableData.speed; } set { serializableData.speed = value; if (_native != null) _native.Speed = value; } } /// /// Checks if any animation clips are currently playing. /// public bool IsPlaying { get { if (_native != null) return _native.IsPlaying(); return false; } } /// /// Plays the specified animation clip. /// /// Clip to play. public void Play(AnimationClip clip) { if(_native != null) _native.Play(clip); } /// /// Plays the specified animation clip on top of the animation currently playing in the main layer. Multiple such /// clips can be playing at once, as long as you ensure each is given its own layer. Each animation can also have a /// weight that determines how much it influences the main animation. /// /// Clip to additively blend. Must contain additive animation curves. /// Determines how much of an effect will the blended animation have on the final output. /// In range [0, 1]. /// Applies the blend over a specified time period, increasing the weight as the time /// passes. Set to zero to blend immediately. In seconds. /// Layer to play the clip in. Multiple additive clips can be playing at once in separate layers /// and each layer has its own weight. public void BlendAdditive(AnimationClip clip, float weight, float fadeLength, int layer) { if (_native != null) _native.BlendAdditive(clip, weight, fadeLength, layer); } /// /// Blends multiple animation clips between each other using linear interpolation. Unlike normal animations these /// animations are not advanced with the progress of time, and is instead expected the user manually changes the /// parameter. /// /// Information about the clips to blend. Clip positions must be sorted from lowest to highest. /// /// Parameter that controls the blending, in range [0, 1]. t = 0 means left animation has full /// influence, t = 1 means right animation has full influence. public void Blend1D(Blend1DInfo info, float t) { if (_native != null) _native.Blend1D(info, t); } /// /// Blend four animation clips between each other using bilinear interpolation. Unlike normal animations these /// animations are not advanced with the progress of time, and is instead expected the user manually changes the /// parameter. /// /// Information about the clips to blend. /// Parameter that controls the blending, in range [(0, 0), (1, 1)]. t = (0, 0) means top left /// animation has full influence, t = (0, 1) means top right animation has full influence, /// t = (1, 0) means bottom left animation has full influence, t = (1, 1) means bottom right /// animation has full influence. /// public void Blend2D(Blend2DInfo info, Vector2 t) { if (_native != null) _native.Blend2D(info, t); } /// /// Fades the specified animation clip in, while fading other playing animation out, over the specified time period. /// /// Clip to fade in. /// Determines the time period over which the fade occurs. In seconds. public void CrossFade(AnimationClip clip, float fadeLength) { if (_native != null) _native.CrossFade(clip, fadeLength); } /// /// Stops playing all animations on the provided layer. /// /// Layer on which to stop animations on. public void Stop(int layer) { if (_native != null) _native.Stop(layer); } /// /// Stops playing all animations. /// public void StopAll() { if (_native != null) _native.StopAll(); } /// /// Retrieves detailed information about a currently playing animation clip. /// /// Clip to retrieve the information for. /// Animation clip state containing the requested information. Only valid if the method returns /// true. /// True if the state was found (animation clip is playing), false otherwise. public bool GetState(AnimationClip clip, out AnimationClipState state) { if (_native != null) return _native.GetState(clip, out state); state = new AnimationClipState(); return false; } /// /// Changes the state of a playing animation clip. If animation clip is not currently playing the state change is /// ignored. /// /// Clip to change the state for. /// New state of the animation (e.g. changing the time for seeking). public void SetState(AnimationClip clip, AnimationClipState state) { if (_native != null) _native.SetState(clip, state); } private void OnEnable() { RestoreNative(); } private void OnDisable() { DestroyNative(); } private void OnDestroy() { DestroyNative(); } /// /// Creates the internal representation of the animation and restores the values saved by the component. /// private void RestoreNative() { if (_native != null) _native.Destroy(); _native = new NativeAnimation(); _native.OnEventTriggered += EventTriggered; // Restore saved values after reset _native.WrapMode = serializableData.wrapMode; _native.Speed = serializableData.speed; if (serializableData.defaultClip != null) _native.Play(serializableData.defaultClip); Renderable renderable = SceneObject.GetComponent(); if (renderable == null) return; NativeRenderable nativeRenderable = renderable.Native; if (nativeRenderable != null) nativeRenderable.Animation = _native; } /// /// Destroys the internal animation representation. /// private void DestroyNative() { Renderable renderableComponent = SceneObject.GetComponent(); if (renderableComponent != null) { NativeRenderable renderable = renderableComponent.Native; if (renderable != null) renderable.Animation = null; } if (_native != null) { _native.Destroy(); _native = null; } } /// /// Called whenever an animation event triggers. /// /// Clip that the event originated from. /// Name of the event. private void EventTriggered(AnimationClip clip, string name) { // TODO - Find a scene object, component and method based on the event name, and call it } /// /// Holds all data the animation component needs to persist through serialization. /// [SerializeObject] private class SerializableData { public AnimationClip defaultClip; public AnimWrapMode wrapMode = AnimWrapMode.Loop; public float speed = 1.0f; } } /** @} */ }