AnimationMatchModifierAsset.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /******************************************************************************
  2. * Spine Runtimes License Agreement
  3. * Last updated January 1, 2020. Replaces all prior versions.
  4. *
  5. * Copyright (c) 2013-2020, Esoteric Software LLC
  6. *
  7. * Integration of the Spine Runtimes into software or otherwise creating
  8. * derivative works of the Spine Runtimes is permitted under the terms and
  9. * conditions of Section 2 of the Spine Editor License Agreement:
  10. * http://esotericsoftware.com/spine-editor-license
  11. *
  12. * Otherwise, it is permitted to integrate the Spine Runtimes into software
  13. * or otherwise create derivative works of the Spine Runtimes (collectively,
  14. * "Products"), provided that each user of the Products must obtain their own
  15. * Spine Editor license and redistribution of the Products in any form must
  16. * include this license and copyright notice.
  17. *
  18. * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
  19. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
  24. * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  27. * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *****************************************************************************/
  29. using Spine;
  30. using Spine.Unity;
  31. using System.Collections;
  32. using System.Collections.Generic;
  33. using UnityEngine;
  34. namespace Spine.Unity.Examples {
  35. //[CreateAssetMenu(menuName = "Spine/SkeletonData Modifiers/Animation Match", order = 200)]
  36. public class AnimationMatchModifierAsset : SkeletonDataModifierAsset {
  37. public bool matchAllAnimations = true;
  38. public override void Apply (SkeletonData skeletonData) {
  39. if (matchAllAnimations)
  40. AnimationTools.MatchAnimationTimelines(skeletonData.Animations, skeletonData);
  41. }
  42. public static class AnimationTools {
  43. #region Filler Timelines
  44. /// <summary>
  45. /// Matches the animation timelines across the given set of animations.
  46. /// This allows unkeyed properties to assume setup pose when animations are naively mixed using Animation.Apply.
  47. /// </summary>
  48. /// <param name="animations">An enumerable collection animations whose timelines will be matched.</param>
  49. /// <param name="skeletonData">The SkeletonData where the animations belong.</param>
  50. public static void MatchAnimationTimelines (IEnumerable<Spine.Animation> animations, SkeletonData skeletonData) {
  51. if (animations == null) return;
  52. if (skeletonData == null) throw new System.ArgumentNullException("skeletonData", "Timelines can't be matched without a SkeletonData source.");
  53. // Build a reference collection of timelines to match
  54. // and a collection of dummy timelines that can be used to fill-in missing items.
  55. var timelineDictionary = new Dictionary<string, Spine.Timeline>();
  56. foreach (var animation in animations) {
  57. foreach (var timeline in animation.Timelines) {
  58. if (timeline is EventTimeline) continue;
  59. foreach (string propertyId in timeline.PropertyIds) {
  60. if (!timelineDictionary.ContainsKey(propertyId)) {
  61. timelineDictionary.Add(propertyId, GetFillerTimeline(timeline, skeletonData));
  62. }
  63. }
  64. }
  65. }
  66. var idsToMatch = new List<string>(timelineDictionary.Keys);
  67. // For each animation in the list, check for and add missing timelines.
  68. var currentAnimationIDs = new HashSet<string>();
  69. foreach (var animation in animations) {
  70. currentAnimationIDs.Clear();
  71. foreach (var timeline in animation.Timelines) {
  72. if (timeline is EventTimeline) continue;
  73. foreach (string propertyId in timeline.PropertyIds) {
  74. currentAnimationIDs.Add(propertyId);
  75. }
  76. }
  77. var animationTimelines = animation.Timelines;
  78. foreach (string propertyId in idsToMatch) {
  79. if (!currentAnimationIDs.Contains(propertyId))
  80. animationTimelines.Add(timelineDictionary[propertyId]);
  81. }
  82. }
  83. // These are locals, but sometimes Unity's GC does weird stuff. So let's clean up.
  84. timelineDictionary.Clear();
  85. timelineDictionary = null;
  86. idsToMatch.Clear();
  87. idsToMatch = null;
  88. currentAnimationIDs.Clear();
  89. currentAnimationIDs = null;
  90. }
  91. static Timeline GetFillerTimeline (Timeline timeline, SkeletonData skeletonData) {
  92. if (timeline is RotateTimeline)
  93. return GetFillerTimeline((RotateTimeline)timeline, skeletonData);
  94. if (timeline is TranslateTimeline)
  95. return GetFillerTimeline((TranslateTimeline)timeline, skeletonData);
  96. if (timeline is ScaleTimeline)
  97. return GetFillerTimeline((ScaleTimeline)timeline, skeletonData);
  98. if (timeline is ShearTimeline)
  99. return GetFillerTimeline((ShearTimeline)timeline, skeletonData);
  100. if (timeline is AttachmentTimeline)
  101. return GetFillerTimeline((AttachmentTimeline)timeline, skeletonData);
  102. if (timeline is RGBATimeline)
  103. return GetFillerTimeline((RGBATimeline)timeline, skeletonData);
  104. if (timeline is RGBA2Timeline)
  105. return GetFillerTimeline((RGBA2Timeline)timeline, skeletonData);
  106. if (timeline is DeformTimeline)
  107. return GetFillerTimeline((DeformTimeline)timeline, skeletonData);
  108. if (timeline is DrawOrderTimeline)
  109. return GetFillerTimeline((DrawOrderTimeline)timeline, skeletonData);
  110. if (timeline is IkConstraintTimeline)
  111. return GetFillerTimeline((IkConstraintTimeline)timeline, skeletonData);
  112. if (timeline is TransformConstraintTimeline)
  113. return GetFillerTimeline((TransformConstraintTimeline)timeline, skeletonData);
  114. if (timeline is PathConstraintPositionTimeline)
  115. return GetFillerTimeline((PathConstraintPositionTimeline)timeline, skeletonData);
  116. if (timeline is PathConstraintSpacingTimeline)
  117. return GetFillerTimeline((PathConstraintSpacingTimeline)timeline, skeletonData);
  118. if (timeline is PathConstraintMixTimeline)
  119. return GetFillerTimeline((PathConstraintMixTimeline)timeline, skeletonData);
  120. return null;
  121. }
  122. static RotateTimeline GetFillerTimeline (RotateTimeline timeline, SkeletonData skeletonData) {
  123. var t = new RotateTimeline(1, 0, timeline.BoneIndex);
  124. t.SetFrame(0, 0, 0);
  125. return t;
  126. }
  127. static TranslateTimeline GetFillerTimeline (TranslateTimeline timeline, SkeletonData skeletonData) {
  128. var t = new TranslateTimeline(1, 0, timeline.BoneIndex);
  129. t.SetFrame(0, 0, 0, 0);
  130. return t;
  131. }
  132. static ScaleTimeline GetFillerTimeline (ScaleTimeline timeline, SkeletonData skeletonData) {
  133. var t = new ScaleTimeline(1, 0, timeline.BoneIndex);
  134. t.SetFrame(0, 0, 0, 0);
  135. return t;
  136. }
  137. static ShearTimeline GetFillerTimeline (ShearTimeline timeline, SkeletonData skeletonData) {
  138. var t = new ShearTimeline(1, 0, timeline.BoneIndex);
  139. t.SetFrame(0, 0, 0, 0);
  140. return t;
  141. }
  142. static AttachmentTimeline GetFillerTimeline (AttachmentTimeline timeline, SkeletonData skeletonData) {
  143. var t = new AttachmentTimeline(1, timeline.SlotIndex);
  144. var slotData = skeletonData.Slots.Items[t.SlotIndex];
  145. t.SetFrame(0, 0, slotData.AttachmentName);
  146. return t;
  147. }
  148. static RGBATimeline GetFillerTimeline (RGBATimeline timeline, SkeletonData skeletonData) {
  149. var t = new RGBATimeline(1, 0, timeline.SlotIndex);
  150. var slotData = skeletonData.Slots.Items[t.SlotIndex];
  151. t.SetFrame(0, 0, slotData.R, slotData.G, slotData.B, slotData.A);
  152. return t;
  153. }
  154. static RGBA2Timeline GetFillerTimeline (RGBA2Timeline timeline, SkeletonData skeletonData) {
  155. var t = new RGBA2Timeline(1, 0, timeline.SlotIndex);
  156. var slotData = skeletonData.Slots.Items[t.SlotIndex];
  157. t.SetFrame(0, 0, slotData.R, slotData.G, slotData.B, slotData.A, slotData.R2, slotData.G2, slotData.B2);
  158. return t;
  159. }
  160. static DeformTimeline GetFillerTimeline (DeformTimeline timeline, SkeletonData skeletonData) {
  161. var t = new DeformTimeline(1, 0, timeline.SlotIndex, timeline.Attachment);
  162. if (t.Attachment.IsWeighted()) {
  163. t.SetFrame(0, 0, new float[t.Attachment.Vertices.Length]);
  164. } else {
  165. t.SetFrame(0, 0, t.Attachment.Vertices.Clone() as float[]);
  166. }
  167. return t;
  168. }
  169. static DrawOrderTimeline GetFillerTimeline (DrawOrderTimeline timeline, SkeletonData skeletonData) {
  170. var t = new DrawOrderTimeline(1);
  171. t.SetFrame(0, 0, null); // null means use setup pose in DrawOrderTimeline.Apply.
  172. return t;
  173. }
  174. static IkConstraintTimeline GetFillerTimeline (IkConstraintTimeline timeline, SkeletonData skeletonData) {
  175. var t = new IkConstraintTimeline(1, 0, timeline.IkConstraintIndex);
  176. var ikConstraintData = skeletonData.IkConstraints.Items[timeline.IkConstraintIndex];
  177. t.SetFrame(0, 0, ikConstraintData.Mix, ikConstraintData.Softness, ikConstraintData.BendDirection, ikConstraintData.Compress, ikConstraintData.Stretch);
  178. return t;
  179. }
  180. static TransformConstraintTimeline GetFillerTimeline (TransformConstraintTimeline timeline, SkeletonData skeletonData) {
  181. var t = new TransformConstraintTimeline(1, 0, timeline.TransformConstraintIndex);
  182. var data = skeletonData.TransformConstraints.Items[timeline.TransformConstraintIndex];
  183. t.SetFrame(0, 0, data.MixRotate, data.MixX, data.MixY, data.MixScaleX, data.MixScaleY, data.MixShearY);
  184. return t;
  185. }
  186. static PathConstraintPositionTimeline GetFillerTimeline (PathConstraintPositionTimeline timeline, SkeletonData skeletonData) {
  187. var t = new PathConstraintPositionTimeline(1, 0, timeline.PathConstraintIndex);
  188. var data = skeletonData.PathConstraints.Items[timeline.PathConstraintIndex];
  189. t.SetFrame(0, 0, data.Position);
  190. return t;
  191. }
  192. static PathConstraintSpacingTimeline GetFillerTimeline (PathConstraintSpacingTimeline timeline, SkeletonData skeletonData) {
  193. var t = new PathConstraintSpacingTimeline(1, 0, timeline.PathConstraintIndex);
  194. var data = skeletonData.PathConstraints.Items[timeline.PathConstraintIndex];
  195. t.SetFrame(0, 0, data.Spacing);
  196. return t;
  197. }
  198. static PathConstraintMixTimeline GetFillerTimeline (PathConstraintMixTimeline timeline, SkeletonData skeletonData) {
  199. var t = new PathConstraintMixTimeline(1, 0, timeline.PathConstraintIndex);
  200. var data = skeletonData.PathConstraints.Items[timeline.PathConstraintIndex];
  201. t.SetFrame(0, 0, data.RotateMix, data.MixX, data.MixY);
  202. return t;
  203. }
  204. #endregion
  205. }
  206. }
  207. }