Created
July 29, 2017 16:27
-
-
Save pharan/3e7a4908d01a453c4ccb7f953c206f31 to your computer and use it in GitHub Desktop.
Spine.AnimationMixer. For adapting native Spine Animation Mixing into external animation track systems.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
* Spine Runtimes Software License v2.5 | |
* | |
* Copyright (c) 2013-2016, Esoteric Software | |
* All rights reserved. | |
* | |
* You are granted a perpetual, non-exclusive, non-sublicensable, and | |
* non-transferable license to use, install, execute, and perform the Spine | |
* Runtimes software and derivative works solely for personal or internal | |
* use. Without the written permission of Esoteric Software (see Section 2 of | |
* the Spine Software License Agreement), you may not (a) modify, translate, | |
* adapt, or develop new applications using the Spine Runtimes or otherwise | |
* create derivative works or improvements of the Spine Runtimes or (b) remove, | |
* delete, alter, or obscure any trademarks or any copyright, trademark, patent, | |
* or other intellectual property or proprietary rights notices on or in the | |
* Software, including any copy thereof. Redistributions in binary or source | |
* form must include this license and terms. | |
* | |
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
* EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF | |
* USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | |
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
* POSSIBILITY OF SUCH DAMAGE. | |
*****************************************************************************/ | |
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
namespace Spine { | |
// CURRENTLY NONFUNCTIONAL | |
/// <summary> | |
/// AnimationMixer is a utility class that provides logic for Spine.AnimationState-like mixing | |
/// to adapt for external animation systems such as Unity Mecanim and Unity Playables. | |
/// </summary> | |
public class AnimationMixer { | |
readonly ExposedList<MixEntry> mixEntries = new ExposedList<MixEntry>(); | |
readonly HashSet<int> propertyIDs = new HashSet<int>(); | |
readonly ExposedList<MixEntry> mixingTo = new ExposedList<MixEntry>(); | |
public void Clear () { | |
propertyIDs.Clear(); | |
mixingTo.Clear(); | |
mixEntries.Clear(); | |
} | |
public void AddAnimation (Animation animation, bool loop) { | |
mixEntries.Add( | |
new MixEntry { | |
animation = animation, | |
loop = loop | |
} | |
); | |
} | |
public void UpdateMixingData () { | |
// Update MixEntry mixing data | |
// update AnimationMixer mixing data. | |
var propertyIDs = this.propertyIDs; | |
propertyIDs.Clear(); | |
var mixingTo = this.mixingTo; | |
var tracksItems = mixEntries.Items; | |
for (int i = 0, n = mixEntries.Count; i < n; i++) { | |
var entry = tracksItems[i]; | |
if (entry != null) entry.SetTimelineData(null, mixingTo, propertyIDs); | |
} | |
} | |
public void Apply (Skeleton skeleton) { | |
} | |
Pool<MixEntry> mixEntryPool = new Pool<MixEntry>(); | |
internal class MixEntry { | |
// Stateless | |
internal Animation animation; | |
internal bool loop; | |
internal readonly ExposedList<int> timelineData = new ExposedList<int>(); | |
internal readonly ExposedList<MixEntry> timelineDipMix = new ExposedList<MixEntry>(); | |
internal readonly ExposedList<float> timelinesRotation = new ExposedList<float>(); | |
// Stateful | |
internal bool lastAppliedAlpha; // Used to tell whether the entry is mixing in or mixing out based on the current and previous applied alpha. | |
public void Reset () { | |
animation = null; | |
timelineData.Clear(); | |
timelineDipMix.Clear(); | |
timelinesRotation.Clear(); | |
} | |
bool HasTimeline (int id) { | |
var timelines = animation.timelines.Items; | |
for (int i = 0, n = animation.timelines.Count; i < n; i++) | |
if (timelines[i].PropertyId == id) return true; | |
return false; | |
} | |
/// <remarks><param name="to">May be null.</param></remarks> | |
internal MixEntry SetTimelineData (MixEntry to, ExposedList<MixEntry> mixingToArray, HashSet<int> propertyIDs) { | |
if (to != null) mixingToArray.Add(to); | |
// var lastEntry = mixingFrom != null ? mixingFrom.SetTimelineData(this, mixingToArray, propertyIDs) : this; | |
if (to != null) mixingToArray.RemoveAt(mixingToArray.Count - 1); // mixingToArray.pop(); | |
var mixingTo = mixingToArray.Items; | |
int mixingToLast = mixingToArray.Count - 1; | |
var timelines = animation.timelines.Items; | |
int timelinesCount = animation.timelines.Count; | |
var timelineDataItems = timelineData.Resize(timelinesCount).Items; // timelineData.setSize(timelinesCount); | |
timelineDipMix.Clear(); | |
var timelineDipMixItems = timelineDipMix.Resize(timelinesCount).Items; //timelineDipMix.setSize(timelinesCount); | |
// outer: | |
for (int i = 0; i < timelinesCount; i++) { | |
int id = timelines[i].PropertyId; | |
if (!propertyIDs.Add(id)) { | |
timelineDataItems[i] = AnimationState.SUBSEQUENT; | |
} else if (to == null || !to.HasTimeline(id)) { | |
timelineDataItems[i] = AnimationState.FIRST; | |
} else { | |
for (int ii = mixingToLast; ii >= 0; ii--) { | |
var entry = mixingTo[ii]; | |
if (!entry.HasTimeline(id)) { | |
timelineDataItems[i] = AnimationState.DIP_MIX; | |
timelineDipMixItems[i] = entry; | |
goto outer; // continue outer; | |
} | |
} | |
timelineDataItems[i] = AnimationState.DIP; | |
} | |
outer: {} | |
} | |
// return lastEntry; | |
return null; | |
} | |
/// <summary> | |
/// Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the | |
/// long way around when using <see cref="alpha"/> and starting animations on other tracks. | |
/// | |
/// Mixing involves finding a rotation between two others, which has two possible solutions: the short way or the long way around. | |
/// The two rotations likely change over time, so which direction is the short or long way also changes. | |
/// If the short way was always chosen, bones would flip to the other side when that direction became the long way. | |
/// TrackEntry chooses the short way the first time it is applied and remembers that direction.</summary> | |
public void ResetRotationDirections () { | |
timelinesRotation.Clear(); | |
} | |
override public string ToString () { | |
return animation == null ? "<none>" : animation.name; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment