Animation.cs 68 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819
  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 System;
  30. using System.Collections.Generic;
  31. namespace Spine {
  32. /// <summary>
  33. /// A simple container for a list of timelines and a name.</summary>
  34. public class Animation {
  35. internal String name;
  36. internal ExposedList<Timeline> timelines;
  37. internal HashSet<int> timelineIds;
  38. internal float duration;
  39. public Animation (string name, ExposedList<Timeline> timelines, float duration) {
  40. if (name == null) throw new ArgumentNullException("name", "name cannot be null.");
  41. if (timelines == null) throw new ArgumentNullException("timelines", "timelines cannot be null.");
  42. // Note: avoiding reallocations by adding all hash set entries at
  43. // once (EnsureCapacity() is only available in newer .Net versions).
  44. int[] propertyIDs = new int[timelines.Count];
  45. for (int i = 0; i < timelines.Count; ++i) {
  46. propertyIDs[i] = timelines.Items[i].PropertyId;
  47. }
  48. this.timelineIds = new HashSet<int>(propertyIDs);
  49. this.name = name;
  50. this.timelines = timelines;
  51. this.duration = duration;
  52. }
  53. public ExposedList<Timeline> Timelines { get { return timelines; } set { timelines = value; } }
  54. /// <summary>The duration of the animation in seconds, which is the highest time of all keys in the timeline.</summary>
  55. public float Duration { get { return duration; } set { duration = value; } }
  56. /// <summary>The animation's name, which is unique across all animations in the skeleton.</summary>
  57. public string Name { get { return name; } }
  58. /// <summary>Whether the timeline with the property id is contained in this animation.</summary>
  59. public bool HasTimeline (int id) {
  60. return timelineIds.Contains(id);
  61. }
  62. /// <summary>Applies all the animation's timelines to the specified skeleton.</summary>
  63. /// <seealso cref="Timeline.Apply(Skeleton, float, float, ExposedList, float, MixBlend, MixDirection)"/>
  64. public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList<Event> events, float alpha, MixBlend blend,
  65. MixDirection direction) {
  66. if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
  67. if (loop && duration != 0) {
  68. time %= duration;
  69. if (lastTime > 0) lastTime %= duration;
  70. }
  71. ExposedList<Timeline> timelines = this.timelines;
  72. for (int i = 0, n = timelines.Count; i < n; i++)
  73. timelines.Items[i].Apply(skeleton, lastTime, time, events, alpha, blend, direction);
  74. }
  75. override public string ToString () {
  76. return name;
  77. }
  78. /// <param name="target">After the first and before the last entry.</param>
  79. /// <returns>Index of first value greater than the target.</returns>
  80. internal static int BinarySearch (float[] values, float target, int step) {
  81. int low = 0;
  82. int high = values.Length / step - 2;
  83. if (high == 0) return step;
  84. int current = (int)((uint)high >> 1);
  85. while (true) {
  86. if (values[(current + 1) * step] <= target)
  87. low = current + 1;
  88. else
  89. high = current;
  90. if (low == high) return (low + 1) * step;
  91. current = (int)((uint)(low + high) >> 1);
  92. }
  93. }
  94. /// <param name="target">After the first and before the last entry.</param>
  95. internal static int BinarySearch (float[] values, float target) {
  96. int low = 0;
  97. int high = values.Length - 2;
  98. if (high == 0) return 1;
  99. int current = (int)((uint)high >> 1);
  100. while (true) {
  101. if (values[current + 1] <= target)
  102. low = current + 1;
  103. else
  104. high = current;
  105. if (low == high) return (low + 1);
  106. current = (int)((uint)(low + high) >> 1);
  107. }
  108. }
  109. internal static int LinearSearch (float[] values, float target, int step) {
  110. for (int i = 0, last = values.Length - step; i <= last; i += step)
  111. if (values[i] > target) return i;
  112. return -1;
  113. }
  114. }
  115. /// <summary>
  116. /// The interface for all timelines.</summary>
  117. public interface Timeline {
  118. /// <summary>Applies this timeline to the skeleton.</summary>
  119. /// <param name="skeleton">The skeleton the timeline is being applied to. This provides access to the bones, slots, and other
  120. /// skeleton components the timeline may change.</param>
  121. /// <param name="lastTime"> The time this timeline was last applied. Timelines such as <see cref="EventTimeline"/> trigger only at specific
  122. /// times rather than every frame. In that case, the timeline triggers everything between <code>lastTime</code>
  123. /// (exclusive) and <code>time</code> (inclusive).</param>
  124. /// <param name="time"> The time within the animation. Most timelines find the key before and the key after this time so they can
  125. /// interpolate between the keys.</param>
  126. /// <param name="events"> If any events are fired, they are added to this list. Can be null to ignore firing events or if the
  127. /// timeline does not fire events.</param>
  128. /// <param name="alpha"> 0 applies the current or setup value (depending on <code>blend</code>). 1 applies the timeline value.
  129. /// Between 0 and 1 applies a value between the current or setup value and the timeline value. By adjusting
  130. /// <code>alpha</code> over time, an animation can be mixed in or out. <code>alpha</code> can also be useful to
  131. /// apply animations on top of each other (layered).</param>
  132. /// <param name="blend"> Controls how mixing is applied when <code>alpha</code> < 1.</param>
  133. /// <param name="direction"> Indicates whether the timeline is mixing in or out. Used by timelines which perform instant transitions,
  134. /// such as <see cref="DrawOrderTimeline"/> or <see cref="AttachmentTimeline"/>, and other such as {@link ScaleTimeline}.</param>
  135. void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> events, float alpha, MixBlend blend, MixDirection direction);
  136. /// <summary>Uniquely encodes both the type of this timeline and the skeleton property that it affects.</summary>
  137. int PropertyId { get; }
  138. }
  139. /// <summary>
  140. /// Controls how a timeline is mixed with the setup or current pose.</summary>
  141. /// <seealso cref="Timeline.Apply(Skeleton, float, float, ExposedList, float, MixBlend, MixDirection)"/>
  142. public enum MixBlend {
  143. /// <summary> Transitions from the setup value to the timeline value (the current value is not used). Before the first key, the setup
  144. /// value is set.</summary>
  145. Setup,
  146. /// <summary>
  147. /// <para>
  148. /// Transitions from the current value to the timeline value. Before the first key, transitions from the current value to
  149. /// the setup value. Timelines which perform instant transitions, such as <see cref="DrawOrderTimeline"/> or
  150. /// <see cref="AttachmentTimeline"/>, use the setup value before the first key.</para>
  151. /// <para>
  152. /// <code>First</code> is intended for the first animations applied, not for animations layered on top of those.</para>
  153. /// </summary>
  154. First,
  155. /// <summary>
  156. /// <para>
  157. /// Transitions from the current value to the timeline value. No change is made before the first key (the current value is
  158. /// kept until the first key).</para>
  159. /// <para>
  160. /// <code>Replace</code> is intended for animations layered on top of others, not for the first animations applied.</para>
  161. /// </summary>
  162. Replace,
  163. /// <summary>
  164. /// <para>
  165. /// Transitions from the current value to the current value plus the timeline value. No change is made before the first key
  166. /// (the current value is kept until the first key).</para>
  167. /// <para>
  168. /// <code>Add</code> is intended for animations layered on top of others, not for the first animations applied.</para>
  169. /// </summary>
  170. Add
  171. }
  172. /// <summary>
  173. /// Indicates whether a timeline's <code>alpha</code> is mixing out over time toward 0 (the setup or current pose value) or
  174. /// mixing in toward 1 (the timeline's value).</summary>
  175. /// <seealso cref="Timeline.Apply(Skeleton, float, float, ExposedList, float, MixBlend, MixDirection)"/>
  176. public enum MixDirection {
  177. In,
  178. Out
  179. }
  180. internal enum TimelineType {
  181. Rotate = 0, Translate, Scale, Shear, //
  182. Attachment, Color, Deform, //
  183. Event, DrawOrder, //
  184. IkConstraint, TransformConstraint, //
  185. PathConstraintPosition, PathConstraintSpacing, PathConstraintMix, //
  186. TwoColor
  187. }
  188. /// <summary>An interface for timelines which change the property of a bone.</summary>
  189. public interface IBoneTimeline {
  190. /// <summary>The index of the bone in <see cref="Skeleton.Bones"/> that will be changed.</summary>
  191. int BoneIndex { get; }
  192. }
  193. /// <summary>An interface for timelines which change the property of a slot.</summary>
  194. public interface ISlotTimeline {
  195. /// <summary>The index of the slot in <see cref="Skeleton.Slots"/> that will be changed.</summary>
  196. int SlotIndex { get; }
  197. }
  198. /// <summary>The base class for timelines that use interpolation between key frame values.</summary>
  199. abstract public class CurveTimeline : Timeline {
  200. protected const float LINEAR = 0, STEPPED = 1, BEZIER = 2;
  201. protected const int BEZIER_SIZE = 10 * 2 - 1;
  202. internal float[] curves; // type, x, y, ...
  203. /// <summary>The number of key frames for this timeline.</summary>
  204. public int FrameCount { get { return curves.Length / BEZIER_SIZE + 1; } }
  205. public CurveTimeline (int frameCount) {
  206. if (frameCount <= 0) throw new ArgumentOutOfRangeException("frameCount must be > 0: ");
  207. curves = new float[(frameCount - 1) * BEZIER_SIZE];
  208. }
  209. abstract public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend, MixDirection direction);
  210. abstract public int PropertyId { get; }
  211. /// <summary>Sets the specified key frame to linear interpolation.</summary>
  212. public void SetLinear (int frameIndex) {
  213. curves[frameIndex * BEZIER_SIZE] = LINEAR;
  214. }
  215. /// <summary>Sets the specified key frame to stepped interpolation.</summary>
  216. public void SetStepped (int frameIndex) {
  217. curves[frameIndex * BEZIER_SIZE] = STEPPED;
  218. }
  219. /// <summary>Returns the interpolation type for the specified key frame.</summary>
  220. /// <returns>Linear is 0, stepped is 1, Bezier is 2.</returns>
  221. public float GetCurveType (int frameIndex) {
  222. int index = frameIndex * BEZIER_SIZE;
  223. if (index == curves.Length) return LINEAR;
  224. float type = curves[index];
  225. if (type == LINEAR) return LINEAR;
  226. if (type == STEPPED) return STEPPED;
  227. return BEZIER;
  228. }
  229. /// <summary>Sets the specified key frame to Bezier interpolation. <code>cx1</code> and <code>cx2</code> are from 0 to 1,
  230. /// representing the percent of time between the two key frames. <code>cy1</code> and <code>cy2</code> are the percent of the
  231. /// difference between the key frame's values.</summary>
  232. public void SetCurve (int frameIndex, float cx1, float cy1, float cx2, float cy2) {
  233. float tmpx = (-cx1 * 2 + cx2) * 0.03f, tmpy = (-cy1 * 2 + cy2) * 0.03f;
  234. float dddfx = ((cx1 - cx2) * 3 + 1) * 0.006f, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006f;
  235. float ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy;
  236. float dfx = cx1 * 0.3f + tmpx + dddfx * 0.16666667f, dfy = cy1 * 0.3f + tmpy + dddfy * 0.16666667f;
  237. int i = frameIndex * BEZIER_SIZE;
  238. float[] curves = this.curves;
  239. curves[i++] = BEZIER;
  240. float x = dfx, y = dfy;
  241. for (int n = i + BEZIER_SIZE - 1; i < n; i += 2) {
  242. curves[i] = x;
  243. curves[i + 1] = y;
  244. dfx += ddfx;
  245. dfy += ddfy;
  246. ddfx += dddfx;
  247. ddfy += dddfy;
  248. x += dfx;
  249. y += dfy;
  250. }
  251. }
  252. /// <summary>Returns the interpolated percentage for the specified key frame and linear percentage.</summary>
  253. public float GetCurvePercent (int frameIndex, float percent) {
  254. percent = MathUtils.Clamp (percent, 0, 1);
  255. float[] curves = this.curves;
  256. int i = frameIndex * BEZIER_SIZE;
  257. float type = curves[i];
  258. if (type == LINEAR) return percent;
  259. if (type == STEPPED) return 0;
  260. i++;
  261. float x = 0;
  262. for (int start = i, n = i + BEZIER_SIZE - 1; i < n; i += 2) {
  263. x = curves[i];
  264. if (x >= percent) {
  265. if (i == start) return curves[i + 1] * percent / x; // First point is 0,0.
  266. float prevX = curves[i - 2], prevY = curves[i - 1];
  267. return prevY + (curves[i + 1] - prevY) * (percent - prevX) / (x - prevX);
  268. }
  269. }
  270. float y = curves[i - 1];
  271. return y + (1 - y) * (percent - x) / (1 - x); // Last point is 1,1.
  272. }
  273. }
  274. /// <summary>Changes a bone's local <see cref="Bone.Rotation"/>.</summary>
  275. public class RotateTimeline : CurveTimeline, IBoneTimeline {
  276. public const int ENTRIES = 2;
  277. internal const int PREV_TIME = -2, PREV_ROTATION = -1;
  278. internal const int ROTATION = 1;
  279. internal int boneIndex;
  280. internal float[] frames; // time, degrees, ...
  281. public RotateTimeline (int frameCount)
  282. : base(frameCount) {
  283. frames = new float[frameCount << 1];
  284. }
  285. override public int PropertyId {
  286. get { return ((int)TimelineType.Rotate << 24) + boneIndex; }
  287. }
  288. /// <summary>The index of the bone in <see cref="Skeleton.Bones"/> that will be changed.</summary>
  289. public int BoneIndex {
  290. set {
  291. if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0.");
  292. this.boneIndex = value;
  293. }
  294. get {
  295. return boneIndex;
  296. }
  297. }
  298. /// <summary>The time in seconds and rotation in degrees for each key frame.</summary>
  299. public float[] Frames { get { return frames; } set { frames = value; } }
  300. /// <summary>Sets the time in seconds and the rotation in degrees for the specified key frame.</summary>
  301. public void SetFrame (int frameIndex, float time, float degrees) {
  302. frameIndex <<= 1;
  303. frames[frameIndex] = time;
  304. frames[frameIndex + ROTATION] = degrees;
  305. }
  306. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  307. MixDirection direction) {
  308. Bone bone = skeleton.bones.Items[boneIndex];
  309. if (!bone.active) return;
  310. float[] frames = this.frames;
  311. if (time < frames[0]) { // Time is before first frame.
  312. switch (blend) {
  313. case MixBlend.Setup:
  314. bone.rotation = bone.data.rotation;
  315. return;
  316. case MixBlend.First:
  317. float r = bone.data.rotation - bone.rotation;
  318. bone.rotation += (r - (16384 - (int)(16384.499999999996 - r / 360)) * 360) * alpha;
  319. return;
  320. }
  321. return;
  322. }
  323. if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
  324. float r = frames[frames.Length + PREV_ROTATION];
  325. switch (blend) {
  326. case MixBlend.Setup:
  327. bone.rotation = bone.data.rotation + r * alpha;
  328. break;
  329. case MixBlend.First:
  330. case MixBlend.Replace:
  331. r += bone.data.rotation - bone.rotation;
  332. r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
  333. goto case MixBlend.Add; // Fall through.
  334. case MixBlend.Add:
  335. bone.rotation += r * alpha;
  336. break;
  337. }
  338. return;
  339. }
  340. // Interpolate between the previous frame and the current frame.
  341. int frame = Animation.BinarySearch(frames, time, ENTRIES);
  342. float prevRotation = frames[frame + PREV_ROTATION];
  343. float frameTime = frames[frame];
  344. float percent = GetCurvePercent((frame >> 1) - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
  345. // scope for 'r' to prevent compile error.
  346. {
  347. float r = frames[frame + ROTATION] - prevRotation;
  348. r = prevRotation + (r - (16384 - (int)(16384.499999999996 - r / 360)) * 360) * percent;
  349. switch (blend) {
  350. case MixBlend.Setup:
  351. bone.rotation = bone.data.rotation + (r - (16384 - (int)(16384.499999999996 - r / 360)) * 360) * alpha;
  352. break;
  353. case MixBlend.First:
  354. case MixBlend.Replace:
  355. r += bone.data.rotation - bone.rotation;
  356. goto case MixBlend.Add; // Fall through.
  357. case MixBlend.Add:
  358. bone.rotation += (r - (16384 - (int)(16384.499999999996 - r / 360)) * 360) * alpha;
  359. break;
  360. }
  361. }
  362. }
  363. }
  364. /// <summary>Changes a bone's local <see cref"Bone.X"/> and <see cref"Bone.Y"/>.</summary>
  365. public class TranslateTimeline : CurveTimeline, IBoneTimeline {
  366. public const int ENTRIES = 3;
  367. protected const int PREV_TIME = -3, PREV_X = -2, PREV_Y = -1;
  368. protected const int X = 1, Y = 2;
  369. internal int boneIndex;
  370. internal float[] frames; // time, x, y, ...
  371. public TranslateTimeline (int frameCount)
  372. : base(frameCount) {
  373. frames = new float[frameCount * ENTRIES];
  374. }
  375. override public int PropertyId {
  376. get { return ((int)TimelineType.Translate << 24) + boneIndex; }
  377. }
  378. /// <summary>The index of the bone in <see cref="Skeleton.Bones"/> that will be changed.</summary>
  379. public int BoneIndex {
  380. set {
  381. if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0.");
  382. this.boneIndex = value;
  383. }
  384. get {
  385. return boneIndex;
  386. }
  387. }
  388. /// <summary>The time in seconds, x, and y values for each key frame.</summary>
  389. public float[] Frames { get { return frames; } set { frames = value; } }
  390. /// <summary>Sets the time in seconds, x, and y values for the specified key frame.</summary>
  391. public void SetFrame (int frameIndex, float time, float x, float y) {
  392. frameIndex *= ENTRIES;
  393. frames[frameIndex] = time;
  394. frames[frameIndex + X] = x;
  395. frames[frameIndex + Y] = y;
  396. }
  397. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  398. MixDirection direction) {
  399. Bone bone = skeleton.bones.Items[boneIndex];
  400. if (!bone.active) return;
  401. float[] frames = this.frames;
  402. if (time < frames[0]) { // Time is before first frame.
  403. switch (blend) {
  404. case MixBlend.Setup:
  405. bone.x = bone.data.x;
  406. bone.y = bone.data.y;
  407. return;
  408. case MixBlend.First:
  409. bone.x += (bone.data.x - bone.x) * alpha;
  410. bone.y += (bone.data.y - bone.y) * alpha;
  411. return;
  412. }
  413. return;
  414. }
  415. float x, y;
  416. if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
  417. x = frames[frames.Length + PREV_X];
  418. y = frames[frames.Length + PREV_Y];
  419. } else {
  420. // Interpolate between the previous frame and the current frame.
  421. int frame = Animation.BinarySearch(frames, time, ENTRIES);
  422. x = frames[frame + PREV_X];
  423. y = frames[frame + PREV_Y];
  424. float frameTime = frames[frame];
  425. float percent = GetCurvePercent(frame / ENTRIES - 1,
  426. 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
  427. x += (frames[frame + X] - x) * percent;
  428. y += (frames[frame + Y] - y) * percent;
  429. }
  430. switch (blend) {
  431. case MixBlend.Setup:
  432. bone.x = bone.data.x + x * alpha;
  433. bone.y = bone.data.y + y * alpha;
  434. break;
  435. case MixBlend.First:
  436. case MixBlend.Replace:
  437. bone.x += (bone.data.x + x - bone.x) * alpha;
  438. bone.y += (bone.data.y + y - bone.y) * alpha;
  439. break;
  440. case MixBlend.Add:
  441. bone.x += x * alpha;
  442. bone.y += y * alpha;
  443. break;
  444. }
  445. }
  446. }
  447. /// <summary>Changes a bone's local <see cref="Bone.ScaleX"> and <see cref="Bone.ScaleY">.</summary>
  448. public class ScaleTimeline : TranslateTimeline, IBoneTimeline {
  449. public ScaleTimeline (int frameCount)
  450. : base(frameCount) {
  451. }
  452. override public int PropertyId {
  453. get { return ((int)TimelineType.Scale << 24) + boneIndex; }
  454. }
  455. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  456. MixDirection direction) {
  457. Bone bone = skeleton.bones.Items[boneIndex];
  458. if (!bone.active) return;
  459. float[] frames = this.frames;
  460. if (time < frames[0]) { // Time is before first frame.
  461. switch (blend) {
  462. case MixBlend.Setup:
  463. bone.scaleX = bone.data.scaleX;
  464. bone.scaleY = bone.data.scaleY;
  465. return;
  466. case MixBlend.First:
  467. bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
  468. bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
  469. return;
  470. }
  471. return;
  472. }
  473. float x, y;
  474. if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
  475. x = frames[frames.Length + PREV_X] * bone.data.scaleX;
  476. y = frames[frames.Length + PREV_Y] * bone.data.scaleY;
  477. } else {
  478. // Interpolate between the previous frame and the current frame.
  479. int frame = Animation.BinarySearch(frames, time, ENTRIES);
  480. x = frames[frame + PREV_X];
  481. y = frames[frame + PREV_Y];
  482. float frameTime = frames[frame];
  483. float percent = GetCurvePercent(frame / ENTRIES - 1,
  484. 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
  485. x = (x + (frames[frame + X] - x) * percent) * bone.data.scaleX;
  486. y = (y + (frames[frame + Y] - y) * percent) * bone.data.scaleY;
  487. }
  488. if (alpha == 1) {
  489. if (blend == MixBlend.Add) {
  490. bone.scaleX += x - bone.data.scaleX;
  491. bone.scaleY += y - bone.data.scaleY;
  492. } else {
  493. bone.scaleX = x;
  494. bone.scaleY = y;
  495. }
  496. } else {
  497. // Mixing out uses sign of setup or current pose, else use sign of key.
  498. float bx, by;
  499. if (direction == MixDirection.Out) {
  500. switch (blend) {
  501. case MixBlend.Setup:
  502. bx = bone.data.scaleX;
  503. by = bone.data.scaleY;
  504. bone.scaleX = bx + (Math.Abs(x) * Math.Sign(bx) - bx) * alpha;
  505. bone.scaleY = by + (Math.Abs(y) * Math.Sign(by) - by) * alpha;
  506. break;
  507. case MixBlend.First:
  508. case MixBlend.Replace:
  509. bx = bone.scaleX;
  510. by = bone.scaleY;
  511. bone.scaleX = bx + (Math.Abs(x) * Math.Sign(bx) - bx) * alpha;
  512. bone.scaleY = by + (Math.Abs(y) * Math.Sign(by) - by) * alpha;
  513. break;
  514. case MixBlend.Add:
  515. bx = bone.scaleX;
  516. by = bone.scaleY;
  517. bone.scaleX = bx + (Math.Abs(x) * Math.Sign(bx) - bone.data.scaleX) * alpha;
  518. bone.scaleY = by + (Math.Abs(y) * Math.Sign(by) - bone.data.scaleY) * alpha;
  519. break;
  520. }
  521. } else {
  522. switch (blend) {
  523. case MixBlend.Setup:
  524. bx = Math.Abs(bone.data.scaleX) * Math.Sign(x);
  525. by = Math.Abs(bone.data.scaleY) * Math.Sign(y);
  526. bone.scaleX = bx + (x - bx) * alpha;
  527. bone.scaleY = by + (y - by) * alpha;
  528. break;
  529. case MixBlend.First:
  530. case MixBlend.Replace:
  531. bx = Math.Abs(bone.scaleX) * Math.Sign(x);
  532. by = Math.Abs(bone.scaleY) * Math.Sign(y);
  533. bone.scaleX = bx + (x - bx) * alpha;
  534. bone.scaleY = by + (y - by) * alpha;
  535. break;
  536. case MixBlend.Add:
  537. bx = Math.Sign(x);
  538. by = Math.Sign(y);
  539. bone.scaleX = Math.Abs(bone.scaleX) * bx + (x - Math.Abs(bone.data.scaleX) * bx) * alpha;
  540. bone.scaleY = Math.Abs(bone.scaleY) * by + (y - Math.Abs(bone.data.scaleY) * by) * alpha;
  541. break;
  542. }
  543. }
  544. }
  545. }
  546. }
  547. /// <summary>Changes a bone's local <see cref="Bone.ShearX"/> and <see cref="Bone.ShearY"/>.</summary>
  548. public class ShearTimeline : TranslateTimeline, IBoneTimeline {
  549. public ShearTimeline (int frameCount)
  550. : base(frameCount) {
  551. }
  552. override public int PropertyId {
  553. get { return ((int)TimelineType.Shear << 24) + boneIndex; }
  554. }
  555. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  556. MixDirection direction) {
  557. Bone bone = skeleton.bones.Items[boneIndex];
  558. if (!bone.active) return;
  559. float[] frames = this.frames;
  560. if (time < frames[0]) { // Time is before first frame.
  561. switch (blend) {
  562. case MixBlend.Setup:
  563. bone.shearX = bone.data.shearX;
  564. bone.shearY = bone.data.shearY;
  565. return;
  566. case MixBlend.First:
  567. bone.shearX += (bone.data.shearX - bone.shearX) * alpha;
  568. bone.shearY += (bone.data.shearY - bone.shearY) * alpha;
  569. return;
  570. }
  571. return;
  572. }
  573. float x, y;
  574. if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
  575. x = frames[frames.Length + PREV_X];
  576. y = frames[frames.Length + PREV_Y];
  577. } else {
  578. // Interpolate between the previous frame and the current frame.
  579. int frame = Animation.BinarySearch(frames, time, ENTRIES);
  580. x = frames[frame + PREV_X];
  581. y = frames[frame + PREV_Y];
  582. float frameTime = frames[frame];
  583. float percent = GetCurvePercent(frame / ENTRIES - 1,
  584. 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
  585. x = x + (frames[frame + X] - x) * percent;
  586. y = y + (frames[frame + Y] - y) * percent;
  587. }
  588. switch (blend) {
  589. case MixBlend.Setup:
  590. bone.shearX = bone.data.shearX + x * alpha;
  591. bone.shearY = bone.data.shearY + y * alpha;
  592. break;
  593. case MixBlend.First:
  594. case MixBlend.Replace:
  595. bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
  596. bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
  597. break;
  598. case MixBlend.Add:
  599. bone.shearX += x * alpha;
  600. bone.shearY += y * alpha;
  601. break;
  602. }
  603. }
  604. }
  605. /// <summary>Changes a slot's <see cref="Slot.Color"/>.</summary>
  606. public class ColorTimeline : CurveTimeline, ISlotTimeline {
  607. public const int ENTRIES = 5;
  608. protected const int PREV_TIME = -5, PREV_R = -4, PREV_G = -3, PREV_B = -2, PREV_A = -1;
  609. protected const int R = 1, G = 2, B = 3, A = 4;
  610. internal int slotIndex;
  611. internal float[] frames; // time, r, g, b, a, ...
  612. public ColorTimeline (int frameCount)
  613. : base(frameCount) {
  614. frames = new float[frameCount * ENTRIES];
  615. }
  616. override public int PropertyId {
  617. get { return ((int)TimelineType.Color << 24) + slotIndex; }
  618. }
  619. /// <summary>The index of the slot in <see cref="Skeleton.Slots"/> that will be changed.</summary>
  620. public int SlotIndex {
  621. set {
  622. if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0.");
  623. this.slotIndex = value;
  624. }
  625. get {
  626. return slotIndex;
  627. }
  628. }
  629. /// <summary>The time in seconds, red, green, blue, and alpha values for each key frame.</summary>
  630. public float[] Frames { get { return frames; } set { frames = value; } }
  631. /// <summary>Sets the time in seconds, red, green, blue, and alpha for the specified key frame.</summary>
  632. public void SetFrame (int frameIndex, float time, float r, float g, float b, float a) {
  633. frameIndex *= ENTRIES;
  634. frames[frameIndex] = time;
  635. frames[frameIndex + R] = r;
  636. frames[frameIndex + G] = g;
  637. frames[frameIndex + B] = b;
  638. frames[frameIndex + A] = a;
  639. }
  640. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  641. MixDirection direction) {
  642. Slot slot = skeleton.slots.Items[slotIndex];
  643. if (!slot.bone.active) return;
  644. float[] frames = this.frames;
  645. if (time < frames[0]) { // Time is before first frame.
  646. var slotData = slot.data;
  647. switch (blend) {
  648. case MixBlend.Setup:
  649. slot.r = slotData.r;
  650. slot.g = slotData.g;
  651. slot.b = slotData.b;
  652. slot.a = slotData.a;
  653. return;
  654. case MixBlend.First:
  655. slot.r += (slotData.r - slot.r) * alpha;
  656. slot.g += (slotData.g - slot.g) * alpha;
  657. slot.b += (slotData.b - slot.b) * alpha;
  658. slot.a += (slotData.a - slot.a) * alpha;
  659. slot.ClampColor();
  660. return;
  661. }
  662. return;
  663. }
  664. float r, g, b, a;
  665. if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
  666. int i = frames.Length;
  667. r = frames[i + PREV_R];
  668. g = frames[i + PREV_G];
  669. b = frames[i + PREV_B];
  670. a = frames[i + PREV_A];
  671. } else {
  672. // Interpolate between the previous frame and the current frame.
  673. int frame = Animation.BinarySearch(frames, time, ENTRIES);
  674. r = frames[frame + PREV_R];
  675. g = frames[frame + PREV_G];
  676. b = frames[frame + PREV_B];
  677. a = frames[frame + PREV_A];
  678. float frameTime = frames[frame];
  679. float percent = GetCurvePercent(frame / ENTRIES - 1,
  680. 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
  681. r += (frames[frame + R] - r) * percent;
  682. g += (frames[frame + G] - g) * percent;
  683. b += (frames[frame + B] - b) * percent;
  684. a += (frames[frame + A] - a) * percent;
  685. }
  686. if (alpha == 1) {
  687. slot.r = r;
  688. slot.g = g;
  689. slot.b = b;
  690. slot.a = a;
  691. slot.ClampColor();
  692. } else {
  693. float br, bg, bb, ba;
  694. if (blend == MixBlend.Setup) {
  695. br = slot.data.r;
  696. bg = slot.data.g;
  697. bb = slot.data.b;
  698. ba = slot.data.a;
  699. } else {
  700. br = slot.r;
  701. bg = slot.g;
  702. bb = slot.b;
  703. ba = slot.a;
  704. }
  705. slot.r = br + ((r - br) * alpha);
  706. slot.g = bg + ((g - bg) * alpha);
  707. slot.b = bb + ((b - bb) * alpha);
  708. slot.a = ba + ((a - ba) * alpha);
  709. slot.ClampColor();
  710. }
  711. }
  712. }
  713. /// <summary>Changes a slot's <see cref="Slot.Color"/> and <see cref="Slot.DarkColor"/> for two color tinting.</summary>
  714. public class TwoColorTimeline : CurveTimeline, ISlotTimeline {
  715. public const int ENTRIES = 8;
  716. protected const int PREV_TIME = -8, PREV_R = -7, PREV_G = -6, PREV_B = -5, PREV_A = -4;
  717. protected const int PREV_R2 = -3, PREV_G2 = -2, PREV_B2 = -1;
  718. protected const int R = 1, G = 2, B = 3, A = 4, R2 = 5, G2 = 6, B2 = 7;
  719. internal int slotIndex;
  720. internal float[] frames; // time, r, g, b, a, r2, g2, b2, ...
  721. public TwoColorTimeline (int frameCount) :
  722. base(frameCount) {
  723. frames = new float[frameCount * ENTRIES];
  724. }
  725. override public int PropertyId {
  726. get { return ((int)TimelineType.TwoColor << 24) + slotIndex; }
  727. }
  728. /// <summary> The index of the slot in <see cref="Skeleton.Slots"/> that will be changed.</summary>
  729. public int SlotIndex {
  730. set {
  731. if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0.");
  732. this.slotIndex = value;
  733. }
  734. get {
  735. return slotIndex;
  736. }
  737. }
  738. /// <summary>The time in seconds, red, green, blue, and alpha values for each key frame.</summary>
  739. public float[] Frames { get { return frames; } }
  740. /// <summary>Sets the time in seconds, light, and dark colors for the specified key frame..</summary>
  741. public void SetFrame (int frameIndex, float time, float r, float g, float b, float a, float r2, float g2, float b2) {
  742. frameIndex *= ENTRIES;
  743. frames[frameIndex] = time;
  744. frames[frameIndex + R] = r;
  745. frames[frameIndex + G] = g;
  746. frames[frameIndex + B] = b;
  747. frames[frameIndex + A] = a;
  748. frames[frameIndex + R2] = r2;
  749. frames[frameIndex + G2] = g2;
  750. frames[frameIndex + B2] = b2;
  751. }
  752. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  753. MixDirection direction) {
  754. Slot slot = skeleton.slots.Items[slotIndex];
  755. if (!slot.bone.active) return;
  756. float[] frames = this.frames;
  757. if (time < frames[0]) { // Time is before first frame.
  758. var slotData = slot.data;
  759. switch (blend) {
  760. case MixBlend.Setup:
  761. // slot.color.set(slot.data.color);
  762. // slot.darkColor.set(slot.data.darkColor);
  763. slot.r = slotData.r;
  764. slot.g = slotData.g;
  765. slot.b = slotData.b;
  766. slot.a = slotData.a;
  767. slot.ClampColor();
  768. slot.r2 = slotData.r2;
  769. slot.g2 = slotData.g2;
  770. slot.b2 = slotData.b2;
  771. slot.ClampSecondColor();
  772. return;
  773. case MixBlend.First:
  774. slot.r += (slot.r - slotData.r) * alpha;
  775. slot.g += (slot.g - slotData.g) * alpha;
  776. slot.b += (slot.b - slotData.b) * alpha;
  777. slot.a += (slot.a - slotData.a) * alpha;
  778. slot.ClampColor();
  779. slot.r2 += (slot.r2 - slotData.r2) * alpha;
  780. slot.g2 += (slot.g2 - slotData.g2) * alpha;
  781. slot.b2 += (slot.b2 - slotData.b2) * alpha;
  782. slot.ClampSecondColor();
  783. return;
  784. }
  785. return;
  786. }
  787. float r, g, b, a, r2, g2, b2;
  788. if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
  789. int i = frames.Length;
  790. r = frames[i + PREV_R];
  791. g = frames[i + PREV_G];
  792. b = frames[i + PREV_B];
  793. a = frames[i + PREV_A];
  794. r2 = frames[i + PREV_R2];
  795. g2 = frames[i + PREV_G2];
  796. b2 = frames[i + PREV_B2];
  797. } else {
  798. // Interpolate between the previous frame and the current frame.
  799. int frame = Animation.BinarySearch(frames, time, ENTRIES);
  800. r = frames[frame + PREV_R];
  801. g = frames[frame + PREV_G];
  802. b = frames[frame + PREV_B];
  803. a = frames[frame + PREV_A];
  804. r2 = frames[frame + PREV_R2];
  805. g2 = frames[frame + PREV_G2];
  806. b2 = frames[frame + PREV_B2];
  807. float frameTime = frames[frame];
  808. float percent = GetCurvePercent(frame / ENTRIES - 1,
  809. 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
  810. r += (frames[frame + R] - r) * percent;
  811. g += (frames[frame + G] - g) * percent;
  812. b += (frames[frame + B] - b) * percent;
  813. a += (frames[frame + A] - a) * percent;
  814. r2 += (frames[frame + R2] - r2) * percent;
  815. g2 += (frames[frame + G2] - g2) * percent;
  816. b2 += (frames[frame + B2] - b2) * percent;
  817. }
  818. if (alpha == 1) {
  819. slot.r = r;
  820. slot.g = g;
  821. slot.b = b;
  822. slot.a = a;
  823. slot.ClampColor();
  824. slot.r2 = r2;
  825. slot.g2 = g2;
  826. slot.b2 = b2;
  827. slot.ClampSecondColor();
  828. } else {
  829. float br, bg, bb, ba, br2, bg2, bb2;
  830. if (blend == MixBlend.Setup) {
  831. br = slot.data.r;
  832. bg = slot.data.g;
  833. bb = slot.data.b;
  834. ba = slot.data.a;
  835. br2 = slot.data.r2;
  836. bg2 = slot.data.g2;
  837. bb2 = slot.data.b2;
  838. } else {
  839. br = slot.r;
  840. bg = slot.g;
  841. bb = slot.b;
  842. ba = slot.a;
  843. br2 = slot.r2;
  844. bg2 = slot.g2;
  845. bb2 = slot.b2;
  846. }
  847. slot.r = br + ((r - br) * alpha);
  848. slot.g = bg + ((g - bg) * alpha);
  849. slot.b = bb + ((b - bb) * alpha);
  850. slot.a = ba + ((a - ba) * alpha);
  851. slot.ClampColor();
  852. slot.r2 = br2 + ((r2 - br2) * alpha);
  853. slot.g2 = bg2 + ((g2 - bg2) * alpha);
  854. slot.b2 = bb2 + ((b2 - bb2) * alpha);
  855. slot.ClampSecondColor();
  856. }
  857. }
  858. }
  859. /// <summary>Changes a slot's <see cref="Slot.Attachment"/>.</summary>
  860. public class AttachmentTimeline : Timeline, ISlotTimeline {
  861. internal int slotIndex;
  862. internal float[] frames; // time, ...
  863. internal string[] attachmentNames;
  864. public AttachmentTimeline (int frameCount) {
  865. frames = new float[frameCount];
  866. attachmentNames = new String[frameCount];
  867. }
  868. public int PropertyId {
  869. get { return ((int)TimelineType.Attachment << 24) + slotIndex; }
  870. }
  871. /// <summary>The number of key frames for this timeline.</summary>
  872. public int FrameCount { get { return frames.Length; } }
  873. /// <summary>The index of the slot in <see cref="Skeleton.Slots"> that will be changed.</summary>
  874. public int SlotIndex {
  875. set {
  876. if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0.");
  877. this.slotIndex = value;
  878. }
  879. get {
  880. return slotIndex;
  881. }
  882. }
  883. /// <summary>The time in seconds for each key frame.</summary>
  884. public float[] Frames { get { return frames; } set { frames = value; } }
  885. /// <summary>The attachment name for each key frame. May contain null values to clear the attachment.</summary>
  886. public string[] AttachmentNames { get { return attachmentNames; } set { attachmentNames = value; } }
  887. /// <summary>Sets the time in seconds and the attachment name for the specified key frame.</summary>
  888. public void SetFrame (int frameIndex, float time, String attachmentName) {
  889. frames[frameIndex] = time;
  890. attachmentNames[frameIndex] = attachmentName;
  891. }
  892. public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  893. MixDirection direction) {
  894. Slot slot = skeleton.slots.Items[slotIndex];
  895. if (!slot.bone.active) return;
  896. if (direction == MixDirection.Out) {
  897. if (blend == MixBlend.Setup) SetAttachment(skeleton, slot, slot.data.attachmentName);
  898. return;
  899. }
  900. float[] frames = this.frames;
  901. if (time < frames[0]) { // Time is before first frame.
  902. if (blend == MixBlend.Setup || blend == MixBlend.First) SetAttachment(skeleton, slot, slot.data.attachmentName);
  903. return;
  904. }
  905. int frameIndex;
  906. if (time >= frames[frames.Length - 1]) // Time is after last frame.
  907. frameIndex = frames.Length - 1;
  908. else
  909. frameIndex = Animation.BinarySearch(frames, time) - 1;
  910. SetAttachment(skeleton, slot, attachmentNames[frameIndex]);
  911. }
  912. private void SetAttachment (Skeleton skeleton, Slot slot, string attachmentName) {
  913. slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
  914. }
  915. }
  916. /// <summary>Changes a slot's <see cref="Slot.Deform"/> to deform a <see cref="VertexAttachment"/>.</summary>
  917. public class DeformTimeline : CurveTimeline, ISlotTimeline {
  918. internal int slotIndex;
  919. internal VertexAttachment attachment;
  920. internal float[] frames; // time, ...
  921. internal float[][] frameVertices;
  922. public DeformTimeline (int frameCount)
  923. : base(frameCount) {
  924. frames = new float[frameCount];
  925. frameVertices = new float[frameCount][];
  926. }
  927. override public int PropertyId {
  928. get { return ((int)TimelineType.Deform << 27) + attachment.id + slotIndex; }
  929. }
  930. /// <summary>The index of the slot in <see cref="Skeleton.Slots"/> that will be changed.</summary>
  931. public int SlotIndex {
  932. set {
  933. if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0.");
  934. this.slotIndex = value;
  935. }
  936. get {
  937. return slotIndex;
  938. }
  939. }
  940. /// <summary>The attachment that will be deformed.</summary>
  941. public VertexAttachment Attachment { get { return attachment; } set { attachment = value; } }
  942. /// <summary>The time in seconds for each key frame.</summary>
  943. public float[] Frames { get { return frames; } set { frames = value; } }
  944. /// <summary>The vertices for each key frame.</summary>
  945. public float[][] Vertices { get { return frameVertices; } set { frameVertices = value; } }
  946. /// <summary>Sets the time in seconds and the vertices for the specified key frame.</summary>
  947. /// <param name="vertices">Vertex positions for an unweighted VertexAttachment, or deform offsets if it has weights.</param>
  948. public void SetFrame (int frameIndex, float time, float[] vertices) {
  949. frames[frameIndex] = time;
  950. frameVertices[frameIndex] = vertices;
  951. }
  952. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  953. MixDirection direction) {
  954. Slot slot = skeleton.slots.Items[slotIndex];
  955. if (!slot.bone.active) return;
  956. VertexAttachment vertexAttachment = slot.attachment as VertexAttachment;
  957. if (vertexAttachment == null || vertexAttachment.DeformAttachment != attachment) return;
  958. var deformArray = slot.Deform;
  959. if (deformArray.Count == 0) blend = MixBlend.Setup;
  960. float[][] frameVertices = this.frameVertices;
  961. int vertexCount = frameVertices[0].Length;
  962. float[] frames = this.frames;
  963. float[] deform;
  964. if (time < frames[0]) { // Time is before first frame.
  965. switch (blend) {
  966. case MixBlend.Setup:
  967. deformArray.Clear();
  968. return;
  969. case MixBlend.First:
  970. if (alpha == 1) {
  971. deformArray.Clear();
  972. return;
  973. }
  974. // deformArray.SetSize(vertexCount) // Ensure size and preemptively set count.
  975. if (deformArray.Capacity < vertexCount) deformArray.Capacity = vertexCount;
  976. deformArray.Count = vertexCount;
  977. deform = deformArray.Items;
  978. if (vertexAttachment.bones == null) {
  979. // Unweighted vertex positions.
  980. float[] setupVertices = vertexAttachment.vertices;
  981. for (int i = 0; i < vertexCount; i++)
  982. deform[i] += (setupVertices[i] - deform[i]) * alpha;
  983. } else {
  984. // Weighted deform offsets.
  985. alpha = 1 - alpha;
  986. for (int i = 0; i < vertexCount; i++)
  987. deform[i] *= alpha;
  988. }
  989. return;
  990. default:
  991. return;
  992. }
  993. }
  994. // deformArray.SetSize(vertexCount) // Ensure size and preemptively set count.
  995. if (deformArray.Capacity < vertexCount) deformArray.Capacity = vertexCount;
  996. deformArray.Count = vertexCount;
  997. deform = deformArray.Items;
  998. if (time >= frames[frames.Length - 1]) { // Time is after last frame.
  999. float[] lastVertices = frameVertices[frames.Length - 1];
  1000. if (alpha == 1) {
  1001. if (blend == MixBlend.Add) {
  1002. if (vertexAttachment.bones == null) {
  1003. // Unweighted vertex positions, no alpha.
  1004. float[] setupVertices = vertexAttachment.vertices;
  1005. for (int i = 0; i < vertexCount; i++)
  1006. deform[i] += lastVertices[i] - setupVertices[i];
  1007. } else {
  1008. // Weighted deform offsets, no alpha.
  1009. for (int i = 0; i < vertexCount; i++)
  1010. deform[i] += lastVertices[i];
  1011. }
  1012. } else {
  1013. // Vertex positions or deform offsets, no alpha.
  1014. Array.Copy(lastVertices, 0, deform, 0, vertexCount);
  1015. }
  1016. } else {
  1017. switch (blend) {
  1018. case MixBlend.Setup: {
  1019. if (vertexAttachment.bones == null) {
  1020. // Unweighted vertex positions, with alpha.
  1021. float[] setupVertices = vertexAttachment.vertices;
  1022. for (int i = 0; i < vertexCount; i++) {
  1023. float setup = setupVertices[i];
  1024. deform[i] = setup + (lastVertices[i] - setup) * alpha;
  1025. }
  1026. } else {
  1027. // Weighted deform offsets, with alpha.
  1028. for (int i = 0; i < vertexCount; i++)
  1029. deform[i] = lastVertices[i] * alpha;
  1030. }
  1031. break;
  1032. }
  1033. case MixBlend.First:
  1034. case MixBlend.Replace:
  1035. // Vertex positions or deform offsets, with alpha.
  1036. for (int i = 0; i < vertexCount; i++)
  1037. deform[i] += (lastVertices[i] - deform[i]) * alpha;
  1038. break;
  1039. case MixBlend.Add:
  1040. if (vertexAttachment.bones == null) {
  1041. // Unweighted vertex positions, no alpha.
  1042. float[] setupVertices = vertexAttachment.vertices;
  1043. for (int i = 0; i < vertexCount; i++)
  1044. deform[i] += (lastVertices[i] - setupVertices[i]) * alpha;
  1045. } else {
  1046. // Weighted deform offsets, alpha.
  1047. for (int i = 0; i < vertexCount; i++)
  1048. deform[i] += lastVertices[i] * alpha;
  1049. }
  1050. break;
  1051. }
  1052. }
  1053. return;
  1054. }
  1055. // Interpolate between the previous frame and the current frame.
  1056. int frame = Animation.BinarySearch(frames, time);
  1057. float[] prevVertices = frameVertices[frame - 1];
  1058. float[] nextVertices = frameVertices[frame];
  1059. float frameTime = frames[frame];
  1060. float percent = GetCurvePercent(frame - 1, 1 - (time - frameTime) / (frames[frame - 1] - frameTime));
  1061. if (alpha == 1) {
  1062. if (blend == MixBlend.Add) {
  1063. if (vertexAttachment.bones == null) {
  1064. // Unweighted vertex positions, no alpha.
  1065. float[] setupVertices = vertexAttachment.vertices;
  1066. for (int i = 0; i < vertexCount; i++) {
  1067. float prev = prevVertices[i];
  1068. deform[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i];
  1069. }
  1070. } else {
  1071. // Weighted deform offsets, no alpha.
  1072. for (int i = 0; i < vertexCount; i++) {
  1073. float prev = prevVertices[i];
  1074. deform[i] += prev + (nextVertices[i] - prev) * percent;
  1075. }
  1076. }
  1077. } else {
  1078. // Vertex positions or deform offsets, no alpha.
  1079. for (int i = 0; i < vertexCount; i++) {
  1080. float prev = prevVertices[i];
  1081. deform[i] = prev + (nextVertices[i] - prev) * percent;
  1082. }
  1083. }
  1084. } else {
  1085. switch (blend) {
  1086. case MixBlend.Setup: {
  1087. if (vertexAttachment.bones == null) {
  1088. // Unweighted vertex positions, with alpha.
  1089. float[] setupVertices = vertexAttachment.vertices;
  1090. for (int i = 0; i < vertexCount; i++) {
  1091. float prev = prevVertices[i], setup = setupVertices[i];
  1092. deform[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
  1093. }
  1094. } else {
  1095. // Weighted deform offsets, with alpha.
  1096. for (int i = 0; i < vertexCount; i++) {
  1097. float prev = prevVertices[i];
  1098. deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
  1099. }
  1100. }
  1101. break;
  1102. }
  1103. case MixBlend.First:
  1104. case MixBlend.Replace: {
  1105. // Vertex positions or deform offsets, with alpha.
  1106. for (int i = 0; i < vertexCount; i++) {
  1107. float prev = prevVertices[i];
  1108. deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha;
  1109. }
  1110. break;
  1111. }
  1112. case MixBlend.Add: {
  1113. if (vertexAttachment.bones == null) {
  1114. // Unweighted vertex positions, with alpha.
  1115. float[] setupVertices = vertexAttachment.vertices;
  1116. for (int i = 0; i < vertexCount; i++) {
  1117. float prev = prevVertices[i];
  1118. deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha;
  1119. }
  1120. } else {
  1121. // Weighted deform offsets, with alpha.
  1122. for (int i = 0; i < vertexCount; i++) {
  1123. float prev = prevVertices[i];
  1124. deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha;
  1125. }
  1126. }
  1127. break;
  1128. }
  1129. }
  1130. }
  1131. }
  1132. }
  1133. /// <summary>Fires an <see cref="Event"/> when specific animation times are reached.</summary>
  1134. public class EventTimeline : Timeline {
  1135. internal float[] frames; // time, ...
  1136. private Event[] events;
  1137. public EventTimeline (int frameCount) {
  1138. frames = new float[frameCount];
  1139. events = new Event[frameCount];
  1140. }
  1141. public int PropertyId {
  1142. get { return ((int)TimelineType.Event << 24); }
  1143. }
  1144. /// <summary>The number of key frames for this timeline.</summary>
  1145. public int FrameCount { get { return frames.Length; } }
  1146. /// <summary>The time in seconds for each key frame.</summary>
  1147. public float[] Frames { get { return frames; } set { frames = value; } }
  1148. /// <summary>The event for each key frame.</summary>
  1149. public Event[] Events { get { return events; } set { events = value; } }
  1150. /// <summary>Sets the time in seconds and the event for the specified key frame.</summary>
  1151. public void SetFrame (int frameIndex, Event e) {
  1152. frames[frameIndex] = e.Time;
  1153. events[frameIndex] = e;
  1154. }
  1155. /// <summary>Fires events for frames &gt; <code>lastTime</code> and &lt;= <code>time</code>.</summary>
  1156. public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1157. MixDirection direction) {
  1158. if (firedEvents == null) return;
  1159. float[] frames = this.frames;
  1160. int frameCount = frames.Length;
  1161. if (lastTime > time) { // Fire events after last time for looped animations.
  1162. Apply(skeleton, lastTime, int.MaxValue, firedEvents, alpha, blend, direction);
  1163. lastTime = -1f;
  1164. } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
  1165. return;
  1166. if (time < frames[0]) return; // Time is before first frame.
  1167. int frame;
  1168. if (lastTime < frames[0])
  1169. frame = 0;
  1170. else {
  1171. frame = Animation.BinarySearch(frames, lastTime);
  1172. float frameTime = frames[frame];
  1173. while (frame > 0) { // Fire multiple events with the same frame.
  1174. if (frames[frame - 1] != frameTime) break;
  1175. frame--;
  1176. }
  1177. }
  1178. for (; frame < frameCount && time >= frames[frame]; frame++)
  1179. firedEvents.Add(events[frame]);
  1180. }
  1181. }
  1182. /// <summary>Changes a skeleton's <see cref="Skeleton.DrawOrder"/>.</summary>
  1183. public class DrawOrderTimeline : Timeline {
  1184. internal float[] frames; // time, ...
  1185. private int[][] drawOrders;
  1186. public DrawOrderTimeline (int frameCount) {
  1187. frames = new float[frameCount];
  1188. drawOrders = new int[frameCount][];
  1189. }
  1190. public int PropertyId {
  1191. get { return ((int)TimelineType.DrawOrder << 24); }
  1192. }
  1193. /// <summary>The number of key frames for this timeline.</summary>
  1194. public int FrameCount { get { return frames.Length; } }
  1195. /// <summary>The time in seconds for each key frame.</summary>
  1196. public float[] Frames { get { return frames; } set { frames = value; } } // time, ...
  1197. /// <summary>The draw order for each key frame.</summary>
  1198. /// <seealso cref="Timeline.setFrame(int, float, int[])"/>.
  1199. public int[][] DrawOrders { get { return drawOrders; } set { drawOrders = value; } }
  1200. /// <summary>Sets the time in seconds and the draw order for the specified key frame.</summary>
  1201. /// <param name="drawOrder">For each slot in <see cref="Skeleton.Slots"/> the index of the new draw order. May be null to use setup pose
  1202. /// draw order..</param>
  1203. public void SetFrame (int frameIndex, float time, int[] drawOrder) {
  1204. frames[frameIndex] = time;
  1205. drawOrders[frameIndex] = drawOrder;
  1206. }
  1207. public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1208. MixDirection direction) {
  1209. ExposedList<Slot> drawOrder = skeleton.drawOrder;
  1210. ExposedList<Slot> slots = skeleton.slots;
  1211. if (direction == MixDirection.Out) {
  1212. if (blend == MixBlend.Setup) Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count);
  1213. return;
  1214. }
  1215. float[] frames = this.frames;
  1216. if (time < frames[0]) { // Time is before first frame.
  1217. if (blend == MixBlend.Setup || blend == MixBlend.First) Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count);
  1218. return;
  1219. }
  1220. int frame;
  1221. if (time >= frames[frames.Length - 1]) // Time is after last frame.
  1222. frame = frames.Length - 1;
  1223. else
  1224. frame = Animation.BinarySearch(frames, time) - 1;
  1225. int[] drawOrderToSetupIndex = drawOrders[frame];
  1226. if (drawOrderToSetupIndex == null) {
  1227. Array.Copy(slots.Items, 0, drawOrder.Items, 0, slots.Count);
  1228. } else {
  1229. var drawOrderItems = drawOrder.Items;
  1230. var slotsItems = slots.Items;
  1231. for (int i = 0, n = drawOrderToSetupIndex.Length; i < n; i++)
  1232. drawOrderItems[i] = slotsItems[drawOrderToSetupIndex[i]];
  1233. }
  1234. }
  1235. }
  1236. /// <summary>Changes an IK constraint's <see cref="IkConstraint.Mix"/>, <see cref="IkConstraint.Softness"/>,
  1237. /// <see cref="IkConstraint.BendDirection"/>, <see cref="IkConstraint.Stretch"/>, and <see cref="IkConstraint.Compress"/>.</summary>
  1238. public class IkConstraintTimeline : CurveTimeline {
  1239. public const int ENTRIES = 6;
  1240. private const int PREV_TIME = -6, PREV_MIX = -5, PREV_SOFTNESS = -4, PREV_BEND_DIRECTION = -3, PREV_COMPRESS = -2,
  1241. PREV_STRETCH = -1;
  1242. private const int MIX = 1, SOFTNESS = 2, BEND_DIRECTION = 3, COMPRESS = 4, STRETCH = 5;
  1243. internal int ikConstraintIndex;
  1244. internal float[] frames; // time, mix, softness, bendDirection, compress, stretch, ...
  1245. public IkConstraintTimeline (int frameCount)
  1246. : base(frameCount) {
  1247. frames = new float[frameCount * ENTRIES];
  1248. }
  1249. override public int PropertyId {
  1250. get { return ((int)TimelineType.IkConstraint << 24) + ikConstraintIndex; }
  1251. }
  1252. /// <summary>The index of the IK constraint slot in <see cref="Skeleton.IkConstraints"/> that will be changed.</summary>
  1253. public int IkConstraintIndex {
  1254. set {
  1255. if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0.");
  1256. this.ikConstraintIndex = value;
  1257. }
  1258. get {
  1259. return ikConstraintIndex;
  1260. }
  1261. }
  1262. /// <summary>The time in seconds, mix, softness, bend direction, compress, and stretch for each key frame.</summary>
  1263. public float[] Frames { get { return frames; } set { frames = value; } }
  1264. /// <summary>Sets the time in seconds, mix, softness, bend direction, compress, and stretch for the specified key frame.</summary>
  1265. public void SetFrame (int frameIndex, float time, float mix, float softness, int bendDirection, bool compress,
  1266. bool stretch) {
  1267. frameIndex *= ENTRIES;
  1268. frames[frameIndex] = time;
  1269. frames[frameIndex + MIX] = mix;
  1270. frames[frameIndex + SOFTNESS] = softness;
  1271. frames[frameIndex + BEND_DIRECTION] = bendDirection;
  1272. frames[frameIndex + COMPRESS] = compress ? 1 : 0;
  1273. frames[frameIndex + STRETCH] = stretch ? 1 : 0;
  1274. }
  1275. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1276. MixDirection direction) {
  1277. IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex];
  1278. if (!constraint.active) return;
  1279. float[] frames = this.frames;
  1280. if (time < frames[0]) { // Time is before first frame.
  1281. switch (blend) {
  1282. case MixBlend.Setup:
  1283. constraint.mix = constraint.data.mix;
  1284. constraint.softness = constraint.data.softness;
  1285. constraint.bendDirection = constraint.data.bendDirection;
  1286. constraint.compress = constraint.data.compress;
  1287. constraint.stretch = constraint.data.stretch;
  1288. return;
  1289. case MixBlend.First:
  1290. constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
  1291. constraint.softness += (constraint.data.softness - constraint.softness) * alpha;
  1292. constraint.bendDirection = constraint.data.bendDirection;
  1293. constraint.compress = constraint.data.compress;
  1294. constraint.stretch = constraint.data.stretch;
  1295. return;
  1296. }
  1297. return;
  1298. }
  1299. if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
  1300. if (blend == MixBlend.Setup) {
  1301. constraint.mix = constraint.data.mix + (frames[frames.Length + PREV_MIX] - constraint.data.mix) * alpha;
  1302. constraint.softness = constraint.data.softness
  1303. + (frames[frames.Length + PREV_SOFTNESS] - constraint.data.softness) * alpha;
  1304. if (direction == MixDirection.Out) {
  1305. constraint.bendDirection = constraint.data.bendDirection;
  1306. constraint.compress = constraint.data.compress;
  1307. constraint.stretch = constraint.data.stretch;
  1308. } else {
  1309. constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
  1310. constraint.compress = frames[frames.Length + PREV_COMPRESS] != 0;
  1311. constraint.stretch = frames[frames.Length + PREV_STRETCH] != 0;
  1312. }
  1313. } else {
  1314. constraint.mix += (frames[frames.Length + PREV_MIX] - constraint.mix) * alpha;
  1315. constraint.softness += (frames[frames.Length + PREV_SOFTNESS] - constraint.softness) * alpha;
  1316. if (direction == MixDirection.In) {
  1317. constraint.bendDirection = (int)frames[frames.Length + PREV_BEND_DIRECTION];
  1318. constraint.compress = frames[frames.Length + PREV_COMPRESS] != 0;
  1319. constraint.stretch = frames[frames.Length + PREV_STRETCH] != 0;
  1320. }
  1321. }
  1322. return;
  1323. }
  1324. // Interpolate between the previous frame and the current frame.
  1325. int frame = Animation.BinarySearch(frames, time, ENTRIES);
  1326. float mix = frames[frame + PREV_MIX];
  1327. float softness = frames[frame + PREV_SOFTNESS];
  1328. float frameTime = frames[frame];
  1329. float percent = GetCurvePercent(frame / ENTRIES - 1, 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
  1330. if (blend == MixBlend.Setup) {
  1331. constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha;
  1332. constraint.softness = constraint.data.softness
  1333. + (softness + (frames[frame + SOFTNESS] - softness) * percent - constraint.data.softness) * alpha;
  1334. if (direction == MixDirection.Out) {
  1335. constraint.bendDirection = constraint.data.bendDirection;
  1336. constraint.compress = constraint.data.compress;
  1337. constraint.stretch = constraint.data.stretch;
  1338. } else {
  1339. constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
  1340. constraint.compress = frames[frame + PREV_COMPRESS] != 0;
  1341. constraint.stretch = frames[frame + PREV_STRETCH] != 0;
  1342. }
  1343. } else {
  1344. constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
  1345. constraint.softness += (softness + (frames[frame + SOFTNESS] - softness) * percent - constraint.softness) * alpha;
  1346. if (direction == MixDirection.In) {
  1347. constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
  1348. constraint.compress = frames[frame + PREV_COMPRESS] != 0;
  1349. constraint.stretch = frames[frame + PREV_STRETCH] != 0;
  1350. }
  1351. }
  1352. }
  1353. }
  1354. /// <summary>Changes a transform constraint's mixes.</summary>
  1355. public class TransformConstraintTimeline : CurveTimeline {
  1356. public const int ENTRIES = 5;
  1357. private const int PREV_TIME = -5, PREV_ROTATE = -4, PREV_TRANSLATE = -3, PREV_SCALE = -2, PREV_SHEAR = -1;
  1358. private const int ROTATE = 1, TRANSLATE = 2, SCALE = 3, SHEAR = 4;
  1359. internal int transformConstraintIndex;
  1360. internal float[] frames; // time, rotate mix, translate mix, scale mix, shear mix, ...
  1361. public TransformConstraintTimeline (int frameCount)
  1362. : base(frameCount) {
  1363. frames = new float[frameCount * ENTRIES];
  1364. }
  1365. override public int PropertyId {
  1366. get { return ((int)TimelineType.TransformConstraint << 24) + transformConstraintIndex; }
  1367. }
  1368. /// <summary>The index of the transform constraint slot in <see cref="Skeleton.TransformConstraints"/> that will be changed.</summary>
  1369. public int TransformConstraintIndex {
  1370. set {
  1371. if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0.");
  1372. this.transformConstraintIndex = value;
  1373. }
  1374. get {
  1375. return transformConstraintIndex;
  1376. }
  1377. }
  1378. /// <summary>The time in seconds, rotate mix, translate mix, scale mix, and shear mix for each key frame.</summary>
  1379. public float[] Frames { get { return frames; } set { frames = value; } } // time, rotate mix, translate mix, scale mix, shear mix, ...
  1380. /// <summary>The time in seconds, rotate mix, translate mix, scale mix, and shear mix for the specified key frame.</summary>
  1381. public void SetFrame (int frameIndex, float time, float rotateMix, float translateMix, float scaleMix, float shearMix) {
  1382. frameIndex *= ENTRIES;
  1383. frames[frameIndex] = time;
  1384. frames[frameIndex + ROTATE] = rotateMix;
  1385. frames[frameIndex + TRANSLATE] = translateMix;
  1386. frames[frameIndex + SCALE] = scaleMix;
  1387. frames[frameIndex + SHEAR] = shearMix;
  1388. }
  1389. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1390. MixDirection direction) {
  1391. TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex];
  1392. if (!constraint.active) return;
  1393. float[] frames = this.frames;
  1394. if (time < frames[0]) { // Time is before first frame.
  1395. TransformConstraintData data = constraint.data;
  1396. switch (blend) {
  1397. case MixBlend.Setup:
  1398. constraint.rotateMix = data.rotateMix;
  1399. constraint.translateMix = data.translateMix;
  1400. constraint.scaleMix = data.scaleMix;
  1401. constraint.shearMix = data.shearMix;
  1402. return;
  1403. case MixBlend.First:
  1404. constraint.rotateMix += (data.rotateMix - constraint.rotateMix) * alpha;
  1405. constraint.translateMix += (data.translateMix - constraint.translateMix) * alpha;
  1406. constraint.scaleMix += (data.scaleMix - constraint.scaleMix) * alpha;
  1407. constraint.shearMix += (data.shearMix - constraint.shearMix) * alpha;
  1408. return;
  1409. }
  1410. return;
  1411. }
  1412. float rotate, translate, scale, shear;
  1413. if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
  1414. int i = frames.Length;
  1415. rotate = frames[i + PREV_ROTATE];
  1416. translate = frames[i + PREV_TRANSLATE];
  1417. scale = frames[i + PREV_SCALE];
  1418. shear = frames[i + PREV_SHEAR];
  1419. } else {
  1420. // Interpolate between the previous frame and the current frame.
  1421. int frame = Animation.BinarySearch(frames, time, ENTRIES);
  1422. rotate = frames[frame + PREV_ROTATE];
  1423. translate = frames[frame + PREV_TRANSLATE];
  1424. scale = frames[frame + PREV_SCALE];
  1425. shear = frames[frame + PREV_SHEAR];
  1426. float frameTime = frames[frame];
  1427. float percent = GetCurvePercent(frame / ENTRIES - 1,
  1428. 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
  1429. rotate += (frames[frame + ROTATE] - rotate) * percent;
  1430. translate += (frames[frame + TRANSLATE] - translate) * percent;
  1431. scale += (frames[frame + SCALE] - scale) * percent;
  1432. shear += (frames[frame + SHEAR] - shear) * percent;
  1433. }
  1434. if (blend == MixBlend.Setup) {
  1435. TransformConstraintData data = constraint.data;
  1436. constraint.rotateMix = data.rotateMix + (rotate - data.rotateMix) * alpha;
  1437. constraint.translateMix = data.translateMix + (translate - data.translateMix) * alpha;
  1438. constraint.scaleMix = data.scaleMix + (scale - data.scaleMix) * alpha;
  1439. constraint.shearMix = data.shearMix + (shear - data.shearMix) * alpha;
  1440. } else {
  1441. constraint.rotateMix += (rotate - constraint.rotateMix) * alpha;
  1442. constraint.translateMix += (translate - constraint.translateMix) * alpha;
  1443. constraint.scaleMix += (scale - constraint.scaleMix) * alpha;
  1444. constraint.shearMix += (shear - constraint.shearMix) * alpha;
  1445. }
  1446. }
  1447. }
  1448. /// <summary>Changes a path constraint's <see cref="PathConstraint.Position"/>.</summary>
  1449. public class PathConstraintPositionTimeline : CurveTimeline {
  1450. public const int ENTRIES = 2;
  1451. protected const int PREV_TIME = -2, PREV_VALUE = -1;
  1452. protected const int VALUE = 1;
  1453. internal int pathConstraintIndex;
  1454. internal float[] frames; // time, position, ...
  1455. public PathConstraintPositionTimeline (int frameCount)
  1456. : base(frameCount) {
  1457. frames = new float[frameCount * ENTRIES];
  1458. }
  1459. override public int PropertyId {
  1460. get { return ((int)TimelineType.PathConstraintPosition << 24) + pathConstraintIndex; }
  1461. }
  1462. /// <summary>The index of the path constraint slot in <see cref="Skeleton.PathConstraints"/> that will be changed.</summary>
  1463. public int PathConstraintIndex {
  1464. set {
  1465. if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0.");
  1466. this.pathConstraintIndex = value;
  1467. }
  1468. get {
  1469. return pathConstraintIndex;
  1470. }
  1471. }
  1472. /// <summary>The time in seconds and path constraint position for each key frame.</summary>
  1473. public float[] Frames { get { return frames; } set { frames = value; } } // time, position, ...
  1474. /// <summary>Sets the time in seconds and path constraint position for the specified key frame.</summary>
  1475. public void SetFrame (int frameIndex, float time, float position) {
  1476. frameIndex *= ENTRIES;
  1477. frames[frameIndex] = time;
  1478. frames[frameIndex + VALUE] = position;
  1479. }
  1480. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1481. MixDirection direction) {
  1482. PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
  1483. if (!constraint.active) return;
  1484. float[] frames = this.frames;
  1485. if (time < frames[0]) { // Time is before first frame.
  1486. switch (blend) {
  1487. case MixBlend.Setup:
  1488. constraint.position = constraint.data.position;
  1489. return;
  1490. case MixBlend.First:
  1491. constraint.position += (constraint.data.position - constraint.position) * alpha;
  1492. return;
  1493. }
  1494. return;
  1495. }
  1496. float position;
  1497. if (time >= frames[frames.Length - ENTRIES]) // Time is after last frame.
  1498. position = frames[frames.Length + PREV_VALUE];
  1499. else {
  1500. // Interpolate between the previous frame and the current frame.
  1501. int frame = Animation.BinarySearch(frames, time, ENTRIES);
  1502. position = frames[frame + PREV_VALUE];
  1503. float frameTime = frames[frame];
  1504. float percent = GetCurvePercent(frame / ENTRIES - 1,
  1505. 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
  1506. position += (frames[frame + VALUE] - position) * percent;
  1507. }
  1508. if (blend == MixBlend.Setup)
  1509. constraint.position = constraint.data.position + (position - constraint.data.position) * alpha;
  1510. else
  1511. constraint.position += (position - constraint.position) * alpha;
  1512. }
  1513. }
  1514. /// <summary>Changes a path constraint's <see cref="PathConstraint.Spacing"/>.</summary>
  1515. public class PathConstraintSpacingTimeline : PathConstraintPositionTimeline {
  1516. public PathConstraintSpacingTimeline (int frameCount)
  1517. : base(frameCount) {
  1518. }
  1519. override public int PropertyId {
  1520. get { return ((int)TimelineType.PathConstraintSpacing << 24) + pathConstraintIndex; }
  1521. }
  1522. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> events, float alpha, MixBlend blend,
  1523. MixDirection direction) {
  1524. PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
  1525. if (!constraint.active) return;
  1526. float[] frames = this.frames;
  1527. if (time < frames[0]) { // Time is before first frame.
  1528. switch (blend) {
  1529. case MixBlend.Setup:
  1530. constraint.spacing = constraint.data.spacing;
  1531. return;
  1532. case MixBlend.First:
  1533. constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha;
  1534. return;
  1535. }
  1536. return;
  1537. }
  1538. float spacing;
  1539. if (time >= frames[frames.Length - ENTRIES]) // Time is after last frame.
  1540. spacing = frames[frames.Length + PREV_VALUE];
  1541. else {
  1542. // Interpolate between the previous frame and the current frame.
  1543. int frame = Animation.BinarySearch(frames, time, ENTRIES);
  1544. spacing = frames[frame + PREV_VALUE];
  1545. float frameTime = frames[frame];
  1546. float percent = GetCurvePercent(frame / ENTRIES - 1,
  1547. 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
  1548. spacing += (frames[frame + VALUE] - spacing) * percent;
  1549. }
  1550. if (blend == MixBlend.Setup)
  1551. constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha;
  1552. else
  1553. constraint.spacing += (spacing - constraint.spacing) * alpha;
  1554. }
  1555. }
  1556. /// <summary>Changes a path constraint's mixes.</summary>
  1557. public class PathConstraintMixTimeline : CurveTimeline {
  1558. public const int ENTRIES = 3;
  1559. private const int PREV_TIME = -3, PREV_ROTATE = -2, PREV_TRANSLATE = -1;
  1560. private const int ROTATE = 1, TRANSLATE = 2;
  1561. internal int pathConstraintIndex;
  1562. internal float[] frames; // time, rotate mix, translate mix, ...
  1563. public PathConstraintMixTimeline (int frameCount)
  1564. : base(frameCount) {
  1565. frames = new float[frameCount * ENTRIES];
  1566. }
  1567. override public int PropertyId {
  1568. get { return ((int)TimelineType.PathConstraintMix << 24) + pathConstraintIndex; }
  1569. }
  1570. /// <summary>The index of the path constraint slot in <see cref="Skeleton.PathConstraints"/> that will be changed.</summary>
  1571. public int PathConstraintIndex {
  1572. set {
  1573. if (value < 0) throw new ArgumentOutOfRangeException("index must be >= 0.");
  1574. this.pathConstraintIndex = value;
  1575. }
  1576. get {
  1577. return pathConstraintIndex;
  1578. }
  1579. }
  1580. /// <summary>The time in seconds, rotate mix, and translate mix for each key frame.</summary>
  1581. public float[] Frames { get { return frames; } set { frames = value; } } // time, rotate mix, translate mix, ...
  1582. /// <summary>The time in seconds, rotate mix, and translate mix for the specified key frame.</summary>
  1583. public void SetFrame (int frameIndex, float time, float rotateMix, float translateMix) {
  1584. frameIndex *= ENTRIES;
  1585. frames[frameIndex] = time;
  1586. frames[frameIndex + ROTATE] = rotateMix;
  1587. frames[frameIndex + TRANSLATE] = translateMix;
  1588. }
  1589. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1590. MixDirection direction) {
  1591. PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
  1592. if (!constraint.active) return;
  1593. float[] frames = this.frames;
  1594. if (time < frames[0]) { // Time is before first frame.
  1595. switch (blend) {
  1596. case MixBlend.Setup:
  1597. constraint.rotateMix = constraint.data.rotateMix;
  1598. constraint.translateMix = constraint.data.translateMix;
  1599. return;
  1600. case MixBlend.First:
  1601. constraint.rotateMix += (constraint.data.rotateMix - constraint.rotateMix) * alpha;
  1602. constraint.translateMix += (constraint.data.translateMix - constraint.translateMix) * alpha;
  1603. return;
  1604. }
  1605. return;
  1606. }
  1607. float rotate, translate;
  1608. if (time >= frames[frames.Length - ENTRIES]) { // Time is after last frame.
  1609. rotate = frames[frames.Length + PREV_ROTATE];
  1610. translate = frames[frames.Length + PREV_TRANSLATE];
  1611. } else {
  1612. // Interpolate between the previous frame and the current frame.
  1613. int frame = Animation.BinarySearch(frames, time, ENTRIES);
  1614. rotate = frames[frame + PREV_ROTATE];
  1615. translate = frames[frame + PREV_TRANSLATE];
  1616. float frameTime = frames[frame];
  1617. float percent = GetCurvePercent(frame / ENTRIES - 1,
  1618. 1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime));
  1619. rotate += (frames[frame + ROTATE] - rotate) * percent;
  1620. translate += (frames[frame + TRANSLATE] - translate) * percent;
  1621. }
  1622. if (blend == MixBlend.Setup) {
  1623. constraint.rotateMix = constraint.data.rotateMix + (rotate - constraint.data.rotateMix) * alpha;
  1624. constraint.translateMix = constraint.data.translateMix + (translate - constraint.data.translateMix) * alpha;
  1625. } else {
  1626. constraint.rotateMix += (rotate - constraint.rotateMix) * alpha;
  1627. constraint.translateMix += (translate - constraint.translateMix) * alpha;
  1628. }
  1629. }
  1630. }
  1631. }