Animation.cs 90 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586
  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. /// Stores a list of timelines to animate a skeleton's pose over time.</summary>
  34. public class Animation {
  35. internal String name;
  36. internal ExposedList<Timeline> timelines;
  37. internal HashSet<string> 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. this.name = name;
  42. SetTimelines(timelines);
  43. this.duration = duration;
  44. }
  45. public ExposedList<Timeline> Timelines {
  46. get { return timelines; }
  47. set { SetTimelines(value); }
  48. }
  49. public void SetTimelines (ExposedList<Timeline> timelines) {
  50. if (timelines == null) throw new ArgumentNullException("timelines", "timelines cannot be null.");
  51. this.timelines = timelines;
  52. // Note: avoiding reallocations by adding all hash set entries at
  53. // once (EnsureCapacity() is only available in newer .Net versions).
  54. int idCount = 0;
  55. int timelinesCount = timelines.Count;
  56. Timeline[] timelinesItems = timelines.Items;
  57. for (int t = 0; t < timelinesCount; ++t)
  58. idCount += timelinesItems[t].PropertyIds.Length;
  59. string[] propertyIds = new string[idCount];
  60. int currentId = 0;
  61. for (int t = 0; t < timelinesCount; ++t) {
  62. var ids = timelinesItems[t].PropertyIds;
  63. for (int i = 0, idsLength = ids.Length; i < idsLength; ++i)
  64. propertyIds[currentId++] = ids[i];
  65. }
  66. this.timelineIds = new HashSet<string>(propertyIds);
  67. }
  68. /// <summary>The duration of the animation in seconds, which is usually the highest time of all frames in the timeline. The duration is
  69. /// used to know when it has completed and when it should loop back to the start.</summary>
  70. public float Duration { get { return duration; } set { duration = value; } }
  71. /// <summary>The animation's name, which is unique across all animations in the skeleton.</summary>
  72. public string Name { get { return name; } }
  73. /// <summary>Returns true if this animation contains a timeline with any of the specified property IDs.</summary>
  74. public bool HasTimeline (string[] propertyIds) {
  75. foreach (string id in propertyIds)
  76. if (timelineIds.Contains(id)) return true;
  77. return false;
  78. }
  79. /// <summary>Applies the animation's timelines to the specified skeleton.</summary>
  80. /// <seealso cref="Timeline.Apply(Skeleton, float, float, ExposedList, float, MixBlend, MixDirection)"/>
  81. /// <param name="skeleton">The skeleton the animation is being applied to. This provides access to the bones, slots, and other skeleton
  82. /// components the timelines may change.</param>
  83. /// <param name="lastTime">The last time in seconds this animation was applied. Some timelines trigger only at specific times rather
  84. /// than every frame. Pass -1 the first time an animation is applied to ensure frame 0 is triggered.</param>
  85. /// <param name="time"> The time in seconds the skeleton is being posed for. Most timelines find the frame before and the frame after
  86. /// this time and interpolate between the frame values. If beyond the <see cref="Duration"/> and <code>loop</code> is
  87. /// true then the animation will repeat, else the last frame will be applied.</param>
  88. /// <param name="loop">If true, the animation repeats after the <see cref="Duration"/>.</param>
  89. /// <param name="events">If any events are fired, they are added to this list. Can be null to ignore fired events or if no timelines
  90. /// fire events.</param>
  91. /// <param name="alpha"> 0 applies the current or setup values (depending on <code>blend</code>). 1 applies the timeline values. Between
  92. /// 0 and 1 applies values between the current or setup values and the timeline values. By adjusting
  93. /// <code>alpha</code> over time, an animation can be mixed in or out. <code>alpha</code> can also be useful to apply
  94. /// animations on top of each other (layering).</param>
  95. /// <param name="blend">Controls how mixing is applied when <code>alpha</code> < 1.</param>
  96. /// <param name="direction">Indicates whether the timelines are mixing in or out. Used by timelines which perform instant transitions,
  97. /// such as <see cref="DrawOrderTimeline"/> or <see cref="AttachmentTimeline"/>.</param>
  98. public void Apply (Skeleton skeleton, float lastTime, float time, bool loop, ExposedList<Event> events, float alpha,
  99. MixBlend blend, MixDirection direction) {
  100. if (skeleton == null) throw new ArgumentNullException("skeleton", "skeleton cannot be null.");
  101. if (loop && duration != 0) {
  102. time %= duration;
  103. if (lastTime > 0) lastTime %= duration;
  104. }
  105. var timelines = this.timelines.Items;
  106. for (int i = 0, n = this.timelines.Count; i < n; i++)
  107. timelines[i].Apply(skeleton, lastTime, time, events, alpha, blend, direction);
  108. }
  109. override public string ToString () {
  110. return name;
  111. }
  112. }
  113. /// <summary>
  114. /// Controls how timeline values are mixed with setup pose values or current pose values when a timeline is applied with
  115. /// <code>alpha</code> < 1.</summary>
  116. /// <seealso cref="Timeline.Apply(Skeleton, float, float, ExposedList, float, MixBlend, MixDirection)"/>
  117. public enum MixBlend {
  118. /// <summary> Transitions from the setup value to the timeline value (the current value is not used). Before the first frame, the
  119. /// setup value is set.</summary>
  120. Setup,
  121. /// <summary>
  122. /// <para>
  123. /// Transitions from the current value to the timeline value. Before the first frame, transitions from the current value to
  124. /// the setup value. Timelines which perform instant transitions, such as <see cref="DrawOrderTimeline"/> or
  125. /// <see cref="AttachmentTimeline"/>, use the setup value before the first frame.</para>
  126. /// <para>
  127. /// <code>First</code> is intended for the first animations applied, not for animations layered on top of those.</para>
  128. /// </summary>
  129. First,
  130. /// <summary>
  131. /// <para>
  132. /// Transitions from the current value to the timeline value. No change is made before the first frame (the current value is
  133. /// kept until the first frame).</para>
  134. /// <para>
  135. /// <code>Replace</code> is intended for animations layered on top of others, not for the first animations applied.</para>
  136. /// </summary>
  137. Replace,
  138. /// <summary>
  139. /// <para>
  140. /// Transitions from the current value to the current value plus the timeline value. No change is made before the first frame
  141. /// (the current value is kept until the first frame).</para>
  142. /// <para>
  143. /// <code>Add</code> is intended for animations layered on top of others, not for the first animations applied. Properties
  144. /// set by additive animations must be set manually or by another animation before applying the additive animations, else the
  145. /// property values will increase each time the additive animations are applied.
  146. /// </para>
  147. /// </summary>
  148. Add
  149. }
  150. /// <summary>
  151. /// Indicates whether a timeline's <code>alpha</code> is mixing out over time toward 0 (the setup or current pose value) or
  152. /// mixing in toward 1 (the timeline's value). Some timelines use this to decide how values are applied.</summary>
  153. /// <seealso cref="Timeline.Apply(Skeleton, float, float, ExposedList, float, MixBlend, MixDirection)"/>
  154. public enum MixDirection {
  155. In,
  156. Out
  157. }
  158. internal enum Property {
  159. Rotate = 0, X, Y, ScaleX, ScaleY, ShearX, ShearY, //
  160. RGB, Alpha, RGB2, //
  161. Attachment, Deform, //
  162. Event, DrawOrder, //
  163. IkConstraint, TransformConstraint, //
  164. PathConstraintPosition, PathConstraintSpacing, PathConstraintMix
  165. }
  166. /// <summary>
  167. /// The base class for all timelines.</summary>
  168. public abstract class Timeline {
  169. private readonly string[] propertyIds;
  170. internal readonly float[] frames;
  171. /// <param name="propertyIds">Unique identifiers for the properties the timeline modifies.</param>
  172. public Timeline (int frameCount, params string[] propertyIds) {
  173. if (propertyIds == null) throw new System.ArgumentNullException("propertyIds", "propertyIds cannot be null.");
  174. this.propertyIds = propertyIds;
  175. frames = new float[frameCount * FrameEntries];
  176. }
  177. /// <summary>Uniquely encodes both the type of this timeline and the skeleton properties that it affects.</summary>
  178. public string[] PropertyIds {
  179. get { return propertyIds; }
  180. }
  181. /// <summary>The time in seconds and any other values for each frame.</summary>
  182. public float[] Frames {
  183. get { return frames; }
  184. }
  185. /// <summary>The number of entries stored per frame.</summary>
  186. public virtual int FrameEntries {
  187. get { return 1; }
  188. }
  189. /// <summary>The number of frames for this timeline.</summary>
  190. public int FrameCount {
  191. get { return frames.Length / FrameEntries; }
  192. }
  193. public float Duration {
  194. get {
  195. return frames[frames.Length - FrameEntries];
  196. }
  197. }
  198. /// <summary>Applies this timeline to the skeleton.</summary>
  199. /// <param name="skeleton">The skeleton the timeline is being applied to. This provides access to the bones, slots, and other
  200. /// skeleton components the timeline may change.</param>
  201. /// <param name="lastTime">The time this timeline was last applied. Timelines such as <see cref="EventTimeline"/> trigger only
  202. /// at specific times rather than every frame. In that case, the timeline triggers everything between
  203. /// <code>lastTime</code> (exclusive) and <code>time</code> (inclusive). Pass -1 the first time an animation is
  204. /// applied to ensure frame 0 is triggered.</param>
  205. /// <param name="time">The time in seconds that the skeleton is being posed for. Most timelines find the frame before and the frame
  206. /// after this time and interpolate between the frame values.If beyond the last frame, the last frame will be
  207. /// applied.</param>
  208. /// <param name="events">If any events are fired, they are added to this list. Can be null to ignore fired events or if the timeline
  209. /// does not fire events.</param>
  210. /// <param name="alpha">0 applies the current or setup value (depending on <code>blend</code>). 1 applies the timeline value.
  211. /// Between 0 and 1 applies a value between the current or setup value and the timeline value.By adjusting
  212. /// <code>alpha</code> over time, an animation can be mixed in or out. <code>alpha</code> can also be useful to
  213. /// apply animations on top of each other (layering).</param>
  214. /// <param name="blend">Controls how mixing is applied when <code>alpha</code> < 1.</param>
  215. /// <param name="direction">Indicates whether the timeline is mixing in or out. Used by timelines which perform instant transitions,
  216. /// such as <see cref="DrawOrderTimeline"/> or <see cref="AttachmentTimeline"/>, and other such as <see cref="ScaleTimeline"/>.</param>
  217. public abstract void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> events, float alpha,
  218. MixBlend blend, MixDirection direction);
  219. /// <summary>Search using a stride of 1.</summary>
  220. /// <param name="time">Must be >= the first value in <code>frames</code>.</param>
  221. /// <returns>The index of the first value <= <code>time</code>.</returns>
  222. internal static int Search (float[] frames, float time) {
  223. int n = frames.Length;
  224. for (int i = 1; i < n; i++)
  225. if (frames[i] > time) return i - 1;
  226. return n - 1;
  227. }
  228. /// <summary>Search using the specified stride.</summary>
  229. /// <param name="time">Must be >= the first value in <code>frames</code>.</param>
  230. /// <returns>The index of the first value <= <code>time</code>.</returns>
  231. internal static int Search (float[] frames, float time, int step) {
  232. int n = frames.Length;
  233. for (int i = step; i < n; i += step)
  234. if (frames[i] > time) return i - step;
  235. return n - step;
  236. }
  237. }
  238. /// <summary>An interface for timelines which change the property of a bone.</summary>
  239. public interface IBoneTimeline {
  240. /// <summary>The index of the bone in <see cref="Skeleton.Bones"/> that will be changed when this timeline is applied.</summary>
  241. int BoneIndex { get; }
  242. }
  243. /// <summary>An interface for timelines which change the property of a slot.</summary>
  244. public interface ISlotTimeline {
  245. /// <summary>The index of the slot in <see cref="Skeleton.Slots"/> that will be changed when this timeline is applied.</summary>
  246. int SlotIndex { get; }
  247. }
  248. /// <summary>The base class for timelines that interpolate between frame values using stepped, linear, or a Bezier curve.</summary>
  249. public abstract class CurveTimeline : Timeline {
  250. public const int LINEAR = 0, STEPPED = 1, BEZIER = 2, BEZIER_SIZE = 18;
  251. internal float[] curves;
  252. /// <summary>The number of key frames for this timeline.</summary>
  253. /// <param name="bezierCount">The maximum number of Bezier curves. See <see cref="Shrink(int)"/>.</param>
  254. /// <param name="propertyIds">Unique identifiers for the properties the timeline modifies.</param>
  255. public CurveTimeline (int frameCount, int bezierCount, params string[] propertyIds)
  256. : base(frameCount, propertyIds) {
  257. curves = new float[frameCount + bezierCount * BEZIER_SIZE];
  258. curves[frameCount - 1] = STEPPED;
  259. }
  260. /// <summary>Sets the specified frame to linear interpolation.</summary>
  261. /// <param name="frame">Between 0 and <code>frameCount - 1</code>, inclusive.</param>
  262. public void SetLinear (int frame) {
  263. curves[frame] = LINEAR;
  264. }
  265. /// <summary>Sets the specified frame to stepped interpolation.</summary>
  266. /// <param name="frame">Between 0 and <code>frameCount - 1</code>, inclusive.</param>
  267. public void SetStepped (int frame) {
  268. curves[frame] = STEPPED;
  269. }
  270. /// <summary>Returns the interpolation type for the specified frame.</summary>
  271. /// <param name="frame">Between 0 and <code>frameCount - 1</code>, inclusive.</param>
  272. /// <returns><see cref="LINEAR"/>, <see cref="STEPPED"/> or <see cref="BEZIER"/> + the index of the Bezier segments.</returns>
  273. public float GetCurveType (int frame) {
  274. return (int)curves[frame];
  275. }
  276. /// <summary>Shrinks the storage for Bezier curves, for use when <code>bezierCount</code> (specified in the constructor) was larger
  277. /// than the actual number of Bezier curves.</summary>
  278. public void Shrink (int bezierCount) {
  279. int size = FrameCount + bezierCount * BEZIER_SIZE;
  280. if (curves.Length > size) {
  281. float[] newCurves = new float[size];
  282. Array.Copy(curves, 0, newCurves, 0, size);
  283. curves = newCurves;
  284. }
  285. }
  286. /// <summary>
  287. /// Stores the segments for the specified Bezier curve. For timelines that modify multiple values, there may be more than
  288. /// one curve per frame.</summary>
  289. /// <param name="bezier">The ordinal of this Bezier curve for this timeline, between 0 and <code>bezierCount - 1</code> (specified
  290. /// in the constructor), inclusive.</param>
  291. /// <param name="frame">Between 0 and <code>frameCount - 1</code>, inclusive.</param>
  292. /// <param name="value">The index of the value for the frame this curve is used for.</param>
  293. /// <param name="time1">The time for the first key.</param>
  294. /// <param name="value1">The value for the first key.</param>
  295. /// <param name="cx1">The time for the first Bezier handle.</param>
  296. /// <param name="cy1">The value for the first Bezier handle.</param>
  297. /// <param name="cx2">The time of the second Bezier handle.</param>
  298. /// <param name="cy2">The value for the second Bezier handle.</param>
  299. /// <param name="time2">The time for the second key.</param>
  300. /// <param name="value2">The value for the second key.</param>
  301. public void SetBezier (int bezier, int frame, int value, float time1, float value1, float cx1, float cy1, float cx2,
  302. float cy2, float time2, float value2) {
  303. float[] curves = this.curves;
  304. int i = FrameCount + bezier * BEZIER_SIZE;
  305. if (value == 0) curves[frame] = BEZIER + i;
  306. float tmpx = (time1 - cx1 * 2 + cx2) * 0.03f, tmpy = (value1 - cy1 * 2 + cy2) * 0.03f;
  307. float dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006f, dddy = ((cy1 - cy2) * 3 - value1 + value2) * 0.006f;
  308. float ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy;
  309. float dx = (cx1 - time1) * 0.3f + tmpx + dddx * 0.16666667f, dy = (cy1 - value1) * 0.3f + tmpy + dddy * 0.16666667f;
  310. float x = time1 + dx, y = value1 + dy;
  311. for (int n = i + BEZIER_SIZE; i < n; i += 2) {
  312. curves[i] = x;
  313. curves[i + 1] = y;
  314. dx += ddx;
  315. dy += ddy;
  316. ddx += dddx;
  317. ddy += dddy;
  318. x += dx;
  319. y += dy;
  320. }
  321. }
  322. /// <summary>
  323. /// Returns the Bezier interpolated value for the specified time.</summary>
  324. /// <param name="frameIndex">The index into <see cref="Frames"/> for the values of the frame before <code>time</code>.</param>
  325. /// <param name="valueOffset">The offset from <code>frameIndex</code> to the value this curve is used for.</param>
  326. /// <param name="i">The index of the Bezier segments. See <see cref="GetCurveType(int)"/>.</param>
  327. public float GetBezierValue (float time, int frameIndex, int valueOffset, int i) {
  328. float[] curves = this.curves;
  329. if (curves[i] > time) {
  330. float x = frames[frameIndex], y = frames[frameIndex + valueOffset];
  331. return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
  332. }
  333. int n = i + BEZIER_SIZE;
  334. for (i += 2; i < n; i += 2) {
  335. if (curves[i] >= time) {
  336. float x = curves[i - 2], y = curves[i - 1];
  337. return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
  338. }
  339. }
  340. frameIndex += FrameEntries;
  341. { // scope added to prevent compile error "float x and y declared in enclosing scope"
  342. float x = curves[n - 2], y = curves[n - 1];
  343. return y + (time - x) / (frames[frameIndex] - x) * (frames[frameIndex + valueOffset] - y);
  344. }
  345. }
  346. }
  347. /// <summary>The base class for a <see cref="CurveTimeline"/> that sets one property.</summary>
  348. public abstract class CurveTimeline1 : CurveTimeline {
  349. public const int ENTRIES = 2;
  350. internal const int VALUE = 1;
  351. /// <param name="bezierCount">The maximum number of Bezier curves. See <see cref="Shrink(int)"/>.</param>
  352. /// <param name="propertyIds">Unique identifiers for the properties the timeline modifies.</param>
  353. public CurveTimeline1 (int frameCount, int bezierCount, string propertyId)
  354. : base(frameCount, bezierCount, propertyId) {
  355. }
  356. public override int FrameEntries {
  357. get { return ENTRIES; }
  358. }
  359. /// <summary>Sets the time and value for the specified frame.</summary>
  360. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  361. /// <param name="time">The frame time in seconds</param>
  362. public void SetFrame (int frame, float time, float value) {
  363. frame <<= 1;
  364. frames[frame] = time;
  365. frames[frame + VALUE] = value;
  366. }
  367. /// <summary>Returns the interpolated value for the specified time.</summary>
  368. public float GetCurveValue (float time) {
  369. float[] frames = this.frames;
  370. int i = frames.Length - 2;
  371. for (int ii = 2; ii <= i; ii += 2) {
  372. if (frames[ii] > time) {
  373. i = ii - 2;
  374. break;
  375. }
  376. }
  377. int curveType = (int)curves[i >> 1];
  378. switch (curveType) {
  379. case LINEAR:
  380. float before = frames[i], value = frames[i + VALUE];
  381. return value + (time - before) / (frames[i + ENTRIES] - before) * (frames[i + ENTRIES + VALUE] - value);
  382. case STEPPED:
  383. return frames[i + VALUE];
  384. }
  385. return GetBezierValue(time, i, VALUE, curveType - BEZIER);
  386. }
  387. }
  388. /// <summary>The base class for a <see cref="CurveTimeline"/> which sets two properties.</summary>
  389. public abstract class CurveTimeline2 : CurveTimeline {
  390. public const int ENTRIES = 3;
  391. internal const int VALUE1 = 1, VALUE2 = 2;
  392. /// <param name="bezierCount">The maximum number of Bezier curves. See <see cref="Shrink(int)"/>.</param>
  393. /// <param name="propertyIds">Unique identifiers for the properties the timeline modifies.</param>
  394. public CurveTimeline2 (int frameCount, int bezierCount, string propertyId1, string propertyId2)
  395. : base(frameCount, bezierCount, propertyId1, propertyId2) {
  396. }
  397. public override int FrameEntries {
  398. get { return ENTRIES; }
  399. }
  400. /// <summary>Sets the time and values for the specified frame.</summary>
  401. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  402. /// <param name="time">The frame time in seconds.</param>
  403. public void SetFrame (int frame, float time, float value1, float value2) {
  404. frame *= ENTRIES;
  405. frames[frame] = time;
  406. frames[frame + VALUE1] = value1;
  407. frames[frame + VALUE2] = value2;
  408. }
  409. }
  410. /// <summary>Changes a bone's local <see cref="Bone.Rotation"/>.</summary>
  411. public class RotateTimeline : CurveTimeline1, IBoneTimeline {
  412. readonly int boneIndex;
  413. public RotateTimeline (int frameCount, int bezierCount, int boneIndex)
  414. : base(frameCount, bezierCount, (int)Property.Rotate + "|" + boneIndex) {
  415. this.boneIndex = boneIndex;
  416. }
  417. public int BoneIndex {
  418. get {
  419. return boneIndex;
  420. }
  421. }
  422. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  423. MixDirection direction) {
  424. Bone bone = skeleton.bones.Items[boneIndex];
  425. if (!bone.active) return;
  426. if (time < frames[0]) { // Time is before first frame.
  427. switch (blend) {
  428. case MixBlend.Setup:
  429. bone.rotation = bone.data.rotation;
  430. return;
  431. case MixBlend.First:
  432. bone.rotation += (bone.data.rotation - bone.rotation) * alpha;
  433. return;
  434. }
  435. return;
  436. }
  437. float r = GetCurveValue(time);
  438. switch (blend) {
  439. case MixBlend.Setup:
  440. bone.rotation = bone.data.rotation + r * alpha;
  441. break;
  442. case MixBlend.First:
  443. case MixBlend.Replace:
  444. r += bone.data.rotation - bone.rotation;
  445. goto case MixBlend.Add; // Fall through.
  446. case MixBlend.Add:
  447. bone.rotation += r * alpha;
  448. break;
  449. }
  450. }
  451. }
  452. /// <summary>Changes a bone's local <see cref"Bone.X"/> and <see cref"Bone.Y"/>.</summary>
  453. public class TranslateTimeline : CurveTimeline2, IBoneTimeline {
  454. readonly int boneIndex;
  455. public TranslateTimeline (int frameCount, int bezierCount, int boneIndex)
  456. : base(frameCount, bezierCount, //
  457. (int)Property.X + "|" + boneIndex, //
  458. (int)Property.Y + "|" + boneIndex) {
  459. this.boneIndex = boneIndex;
  460. }
  461. public int BoneIndex {
  462. get {
  463. return boneIndex;
  464. }
  465. }
  466. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  467. MixDirection direction) {
  468. Bone bone = skeleton.bones.Items[boneIndex];
  469. if (!bone.active) return;
  470. float[] frames = this.frames;
  471. if (time < frames[0]) { // Time is before first frame.
  472. switch (blend) {
  473. case MixBlend.Setup:
  474. bone.x = bone.data.x;
  475. bone.y = bone.data.y;
  476. return;
  477. case MixBlend.First:
  478. bone.x += (bone.data.x - bone.x) * alpha;
  479. bone.y += (bone.data.y - bone.y) * alpha;
  480. return;
  481. }
  482. return;
  483. }
  484. float x, y;
  485. GetCurveValue(out x, out y, time); // note: reference implementation has code inlined
  486. switch (blend) {
  487. case MixBlend.Setup:
  488. bone.x = bone.data.x + x * alpha;
  489. bone.y = bone.data.y + y * alpha;
  490. break;
  491. case MixBlend.First:
  492. case MixBlend.Replace:
  493. bone.x += (bone.data.x + x - bone.x) * alpha;
  494. bone.y += (bone.data.y + y - bone.y) * alpha;
  495. break;
  496. case MixBlend.Add:
  497. bone.x += x * alpha;
  498. bone.y += y * alpha;
  499. break;
  500. }
  501. }
  502. public void GetCurveValue (out float x, out float y, float time) {
  503. int i = Search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES];
  504. switch (curveType) {
  505. case LINEAR:
  506. float before = frames[i];
  507. x = frames[i + VALUE1];
  508. y = frames[i + VALUE2];
  509. float t = (time - before) / (frames[i + ENTRIES] - before);
  510. x += (frames[i + ENTRIES + VALUE1] - x) * t;
  511. y += (frames[i + ENTRIES + VALUE2] - y) * t;
  512. break;
  513. case STEPPED:
  514. x = frames[i + VALUE1];
  515. y = frames[i + VALUE2];
  516. break;
  517. default:
  518. x = GetBezierValue(time, i, VALUE1, curveType - BEZIER);
  519. y = GetBezierValue(time, i, VALUE2, curveType + BEZIER_SIZE - BEZIER);
  520. break;
  521. }
  522. }
  523. }
  524. /// <summary>Changes a bone's local <see cref"Bone.X"/>.</summary>
  525. public class TranslateXTimeline : CurveTimeline1, IBoneTimeline {
  526. readonly int boneIndex;
  527. public TranslateXTimeline (int frameCount, int bezierCount, int boneIndex)
  528. : base(frameCount, bezierCount, (int)Property.X + "|" + boneIndex) {
  529. this.boneIndex = boneIndex;
  530. }
  531. public int BoneIndex {
  532. get {
  533. return boneIndex;
  534. }
  535. }
  536. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  537. MixDirection direction) {
  538. Bone bone = skeleton.bones.Items[boneIndex];
  539. if (!bone.active) return;
  540. float[] frames = this.frames;
  541. if (time < frames[0]) { // Time is before first frame.
  542. switch (blend) {
  543. case MixBlend.Setup:
  544. bone.x = bone.data.x;
  545. return;
  546. case MixBlend.First:
  547. bone.x += (bone.data.x - bone.x) * alpha;
  548. return;
  549. }
  550. return;
  551. }
  552. float x = GetCurveValue(time);
  553. switch (blend) {
  554. case MixBlend.Setup:
  555. bone.x = bone.data.x + x * alpha;
  556. break;
  557. case MixBlend.First:
  558. case MixBlend.Replace:
  559. bone.x += (bone.data.x + x - bone.x) * alpha;
  560. break;
  561. case MixBlend.Add:
  562. bone.x += x * alpha;
  563. break;
  564. }
  565. }
  566. }
  567. /// <summary>Changes a bone's local <see cref"Bone.Y"/>.</summary>
  568. public class TranslateYTimeline : CurveTimeline1, IBoneTimeline {
  569. readonly int boneIndex;
  570. public TranslateYTimeline (int frameCount, int bezierCount, int boneIndex)
  571. : base(frameCount, bezierCount, (int)Property.Y + "|" + boneIndex) {
  572. this.boneIndex = boneIndex;
  573. }
  574. public int BoneIndex {
  575. get {
  576. return boneIndex;
  577. }
  578. }
  579. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  580. MixDirection direction) {
  581. Bone bone = skeleton.bones.Items[boneIndex];
  582. if (!bone.active) return;
  583. float[] frames = this.frames;
  584. if (time < frames[0]) { // Time is before first frame.
  585. switch (blend) {
  586. case MixBlend.Setup:
  587. bone.y = bone.data.y;
  588. return;
  589. case MixBlend.First:
  590. bone.y += (bone.data.y - bone.y) * alpha;
  591. return;
  592. }
  593. return;
  594. }
  595. float y = GetCurveValue(time);
  596. switch (blend) {
  597. case MixBlend.Setup:
  598. bone.y = bone.data.y + y * alpha;
  599. break;
  600. case MixBlend.First:
  601. case MixBlend.Replace:
  602. bone.y += (bone.data.y + y - bone.y) * alpha;
  603. break;
  604. case MixBlend.Add:
  605. bone.y += y * alpha;
  606. break;
  607. }
  608. }
  609. }
  610. /// <summary>Changes a bone's local <see cref="Bone.ScaleX"> and <see cref="Bone.ScaleY">.</summary>
  611. public class ScaleTimeline : CurveTimeline2, IBoneTimeline {
  612. readonly int boneIndex;
  613. public ScaleTimeline (int frameCount, int bezierCount, int boneIndex)
  614. : base(frameCount, bezierCount, //
  615. (int)Property.ScaleX + "|" + boneIndex, //
  616. (int)Property.ScaleY + "|" + boneIndex) {
  617. this.boneIndex = boneIndex;
  618. }
  619. public int BoneIndex {
  620. get {
  621. return boneIndex;
  622. }
  623. }
  624. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  625. MixDirection direction) {
  626. Bone bone = skeleton.bones.Items[boneIndex];
  627. if (!bone.active) return;
  628. float[] frames = this.frames;
  629. if (time < frames[0]) { // Time is before first frame.
  630. switch (blend) {
  631. case MixBlend.Setup:
  632. bone.scaleX = bone.data.scaleX;
  633. bone.scaleY = bone.data.scaleY;
  634. return;
  635. case MixBlend.First:
  636. bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
  637. bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
  638. return;
  639. }
  640. return;
  641. }
  642. float x, y;
  643. int i = Search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES];
  644. switch (curveType) {
  645. case LINEAR:
  646. float before = frames[i];
  647. x = frames[i + VALUE1];
  648. y = frames[i + VALUE2];
  649. float t = (time - before) / (frames[i + ENTRIES] - before);
  650. x += (frames[i + ENTRIES + VALUE1] - x) * t;
  651. y += (frames[i + ENTRIES + VALUE2] - y) * t;
  652. break;
  653. case STEPPED:
  654. x = frames[i + VALUE1];
  655. y = frames[i + VALUE2];
  656. break;
  657. default:
  658. x = GetBezierValue(time, i, VALUE1, curveType - BEZIER);
  659. y = GetBezierValue(time, i, VALUE2, curveType + BEZIER_SIZE - BEZIER);
  660. break;
  661. }
  662. x *= bone.data.scaleX;
  663. y *= bone.data.scaleY;
  664. if (alpha == 1) {
  665. if (blend == MixBlend.Add) {
  666. bone.scaleX += x - bone.data.scaleX;
  667. bone.scaleY += y - bone.data.scaleY;
  668. } else {
  669. bone.scaleX = x;
  670. bone.scaleY = y;
  671. }
  672. } else {
  673. // Mixing out uses sign of setup or current pose, else use sign of key.
  674. float bx, by;
  675. if (direction == MixDirection.Out) {
  676. switch (blend) {
  677. case MixBlend.Setup:
  678. bx = bone.data.scaleX;
  679. by = bone.data.scaleY;
  680. bone.scaleX = bx + (Math.Abs(x) * Math.Sign(bx) - bx) * alpha;
  681. bone.scaleY = by + (Math.Abs(y) * Math.Sign(by) - by) * alpha;
  682. break;
  683. case MixBlend.First:
  684. case MixBlend.Replace:
  685. bx = bone.scaleX;
  686. by = bone.scaleY;
  687. bone.scaleX = bx + (Math.Abs(x) * Math.Sign(bx) - bx) * alpha;
  688. bone.scaleY = by + (Math.Abs(y) * Math.Sign(by) - by) * alpha;
  689. break;
  690. case MixBlend.Add:
  691. bone.scaleX += (x - bone.data.scaleX) * alpha;
  692. bone.scaleY += (y - bone.data.scaleY) * alpha;
  693. break;
  694. }
  695. } else {
  696. switch (blend) {
  697. case MixBlend.Setup:
  698. bx = Math.Abs(bone.data.scaleX) * Math.Sign(x);
  699. by = Math.Abs(bone.data.scaleY) * Math.Sign(y);
  700. bone.scaleX = bx + (x - bx) * alpha;
  701. bone.scaleY = by + (y - by) * alpha;
  702. break;
  703. case MixBlend.First:
  704. case MixBlend.Replace:
  705. bx = Math.Abs(bone.scaleX) * Math.Sign(x);
  706. by = Math.Abs(bone.scaleY) * Math.Sign(y);
  707. bone.scaleX = bx + (x - bx) * alpha;
  708. bone.scaleY = by + (y - by) * alpha;
  709. break;
  710. case MixBlend.Add:
  711. bone.scaleX += (x - bone.data.scaleX) * alpha;
  712. bone.scaleY += (y - bone.data.scaleY) * alpha;
  713. break;
  714. }
  715. }
  716. }
  717. }
  718. }
  719. /// <summary>Changes a bone's local <see cref="Bone.ScaleX">.</summary>
  720. public class ScaleXTimeline : CurveTimeline1, IBoneTimeline {
  721. readonly int boneIndex;
  722. public ScaleXTimeline (int frameCount, int bezierCount, int boneIndex)
  723. : base(frameCount, bezierCount, (int)Property.ScaleX + "|" + boneIndex) {
  724. this.boneIndex = boneIndex;
  725. }
  726. public int BoneIndex {
  727. get {
  728. return boneIndex;
  729. }
  730. }
  731. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  732. MixDirection direction) {
  733. Bone bone = skeleton.bones.Items[boneIndex];
  734. if (!bone.active) return;
  735. float[] frames = this.frames;
  736. if (time < frames[0]) { // Time is before first frame.
  737. switch (blend) {
  738. case MixBlend.Setup:
  739. bone.scaleX = bone.data.scaleX;
  740. return;
  741. case MixBlend.First:
  742. bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
  743. return;
  744. }
  745. return;
  746. }
  747. float x = GetCurveValue(time) * bone.data.scaleX;
  748. if (alpha == 1) {
  749. if (blend == MixBlend.Add)
  750. bone.scaleX += x - bone.data.scaleX;
  751. else
  752. bone.scaleX = x;
  753. } else {
  754. // Mixing out uses sign of setup or current pose, else use sign of key.
  755. float bx;
  756. if (direction == MixDirection.Out) {
  757. switch (blend) {
  758. case MixBlend.Setup:
  759. bx = bone.data.scaleX;
  760. bone.scaleX = bx + (Math.Abs(x) * Math.Sign(bx) - bx) * alpha;
  761. break;
  762. case MixBlend.First:
  763. case MixBlend.Replace:
  764. bx = bone.scaleX;
  765. bone.scaleX = bx + (Math.Abs(x) * Math.Sign(bx) - bx) * alpha;
  766. break;
  767. case MixBlend.Add:
  768. bone.scaleX += (x - bone.data.scaleX) * alpha;
  769. break;
  770. }
  771. } else {
  772. switch (blend) {
  773. case MixBlend.Setup:
  774. bx = Math.Abs(bone.data.scaleX) * Math.Sign(x);
  775. bone.scaleX = bx + (x - bx) * alpha;
  776. break;
  777. case MixBlend.First:
  778. case MixBlend.Replace:
  779. bx = Math.Abs(bone.scaleX) * Math.Sign(x);
  780. bone.scaleX = bx + (x - bx) * alpha;
  781. break;
  782. case MixBlend.Add:
  783. bone.scaleX += (x - bone.data.scaleX) * alpha;
  784. break;
  785. }
  786. }
  787. }
  788. }
  789. }
  790. /// <summary>Changes a bone's local <see cref="Bone.ScaleY">.</summary>
  791. public class ScaleYTimeline : CurveTimeline1, IBoneTimeline {
  792. readonly int boneIndex;
  793. public ScaleYTimeline (int frameCount, int bezierCount, int boneIndex)
  794. : base(frameCount, bezierCount, (int)Property.ScaleY + "|" + boneIndex) {
  795. this.boneIndex = boneIndex;
  796. }
  797. public int BoneIndex {
  798. get {
  799. return boneIndex;
  800. }
  801. }
  802. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  803. MixDirection direction) {
  804. Bone bone = skeleton.bones.Items[boneIndex];
  805. if (!bone.active) return;
  806. float[] frames = this.frames;
  807. if (time < frames[0]) { // Time is before first frame.
  808. switch (blend) {
  809. case MixBlend.Setup:
  810. bone.scaleY = bone.data.scaleY;
  811. return;
  812. case MixBlend.First:
  813. bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
  814. return;
  815. }
  816. return;
  817. }
  818. float y = GetCurveValue(time) * bone.data.scaleY;
  819. if (alpha == 1) {
  820. if (blend == MixBlend.Add)
  821. bone.scaleY += y - bone.data.scaleY;
  822. else
  823. bone.scaleY = y;
  824. } else {
  825. // Mixing out uses sign of setup or current pose, else use sign of key.
  826. float by;
  827. if (direction == MixDirection.Out) {
  828. switch (blend) {
  829. case MixBlend.Setup:
  830. by = bone.data.scaleY;
  831. bone.scaleY = by + (Math.Abs(y) * Math.Sign(by) - by) * alpha;
  832. break;
  833. case MixBlend.First:
  834. case MixBlend.Replace:
  835. by = bone.scaleY;
  836. bone.scaleY = by + (Math.Abs(y) * Math.Sign(by) - by) * alpha;
  837. break;
  838. case MixBlend.Add:
  839. bone.scaleY += (y - bone.data.scaleY) * alpha;
  840. break;
  841. }
  842. } else {
  843. switch (blend) {
  844. case MixBlend.Setup:
  845. by = Math.Abs(bone.data.scaleY) * Math.Sign(y);
  846. bone.scaleY = by + (y - by) * alpha;
  847. break;
  848. case MixBlend.First:
  849. case MixBlend.Replace:
  850. by = Math.Abs(bone.scaleY) * Math.Sign(y);
  851. bone.scaleY = by + (y - by) * alpha;
  852. break;
  853. case MixBlend.Add:
  854. bone.scaleY += (y - bone.data.scaleY) * alpha;
  855. break;
  856. }
  857. }
  858. }
  859. }
  860. }
  861. /// <summary>Changes a bone's local <see cref="Bone.ShearX"/> and <see cref="Bone.ShearY"/>.</summary>
  862. public class ShearTimeline : CurveTimeline2, IBoneTimeline {
  863. readonly int boneIndex;
  864. public ShearTimeline (int frameCount, int bezierCount, int boneIndex)
  865. : base(frameCount, bezierCount, //
  866. (int)Property.ShearX + "|" + boneIndex, //
  867. (int)Property.ShearY + "|" + boneIndex) {
  868. this.boneIndex = boneIndex;
  869. }
  870. public int BoneIndex {
  871. get {
  872. return boneIndex;
  873. }
  874. }
  875. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  876. MixDirection direction) {
  877. Bone bone = skeleton.bones.Items[boneIndex];
  878. if (!bone.active) return;
  879. float[] frames = this.frames;
  880. if (time < frames[0]) { // Time is before first frame.
  881. switch (blend) {
  882. case MixBlend.Setup:
  883. bone.shearX = bone.data.shearX;
  884. bone.shearY = bone.data.shearY;
  885. return;
  886. case MixBlend.First:
  887. bone.shearX += (bone.data.shearX - bone.shearX) * alpha;
  888. bone.shearY += (bone.data.shearY - bone.shearY) * alpha;
  889. return;
  890. }
  891. return;
  892. }
  893. float x, y;
  894. int i = Search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES];
  895. switch (curveType) {
  896. case LINEAR:
  897. float before = frames[i];
  898. x = frames[i + VALUE1];
  899. y = frames[i + VALUE2];
  900. float t = (time - before) / (frames[i + ENTRIES] - before);
  901. x += (frames[i + ENTRIES + VALUE1] - x) * t;
  902. y += (frames[i + ENTRIES + VALUE2] - y) * t;
  903. break;
  904. case STEPPED:
  905. x = frames[i + VALUE1];
  906. y = frames[i + VALUE2];
  907. break;
  908. default:
  909. x = GetBezierValue(time, i, VALUE1, curveType - BEZIER);
  910. y = GetBezierValue(time, i, VALUE2, curveType + BEZIER_SIZE - BEZIER);
  911. break;
  912. }
  913. switch (blend) {
  914. case MixBlend.Setup:
  915. bone.shearX = bone.data.shearX + x * alpha;
  916. bone.shearY = bone.data.shearY + y * alpha;
  917. break;
  918. case MixBlend.First:
  919. case MixBlend.Replace:
  920. bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
  921. bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
  922. break;
  923. case MixBlend.Add:
  924. bone.shearX += x * alpha;
  925. bone.shearY += y * alpha;
  926. break;
  927. }
  928. }
  929. }
  930. /// <summary>Changes a bone's local <see cref="Bone.ShearX"/>.</summary>
  931. public class ShearXTimeline : CurveTimeline1, IBoneTimeline {
  932. readonly int boneIndex;
  933. public ShearXTimeline (int frameCount, int bezierCount, int boneIndex)
  934. : base(frameCount, bezierCount, (int)Property.ShearX + "|" + boneIndex) {
  935. this.boneIndex = boneIndex;
  936. }
  937. public int BoneIndex {
  938. get {
  939. return boneIndex;
  940. }
  941. }
  942. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  943. MixDirection direction) {
  944. Bone bone = skeleton.bones.Items[boneIndex];
  945. if (!bone.active) return;
  946. float[] frames = this.frames;
  947. if (time < frames[0]) { // Time is before first frame.
  948. switch (blend) {
  949. case MixBlend.Setup:
  950. bone.shearX = bone.data.shearX;
  951. return;
  952. case MixBlend.First:
  953. bone.shearX += (bone.data.shearX - bone.shearX) * alpha;
  954. return;
  955. }
  956. return;
  957. }
  958. float x = GetCurveValue(time);
  959. switch (blend) {
  960. case MixBlend.Setup:
  961. bone.shearX = bone.data.shearX + x * alpha;
  962. break;
  963. case MixBlend.First:
  964. case MixBlend.Replace:
  965. bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
  966. break;
  967. case MixBlend.Add:
  968. bone.shearX += x * alpha;
  969. break;
  970. }
  971. }
  972. }
  973. /// <summary>Changes a bone's local <see cref="Bone.ShearY"/>.</summary>
  974. public class ShearYTimeline : CurveTimeline1, IBoneTimeline {
  975. readonly int boneIndex;
  976. public ShearYTimeline (int frameCount, int bezierCount, int boneIndex)
  977. : base(frameCount, bezierCount, (int)Property.ShearY + "|" + boneIndex) {
  978. this.boneIndex = boneIndex;
  979. }
  980. public int BoneIndex {
  981. get {
  982. return boneIndex;
  983. }
  984. }
  985. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  986. MixDirection direction) {
  987. Bone bone = skeleton.bones.Items[boneIndex];
  988. if (!bone.active) return;
  989. float[] frames = this.frames;
  990. if (time < frames[0]) { // Time is before first frame.
  991. switch (blend) {
  992. case MixBlend.Setup:
  993. bone.shearY = bone.data.shearY;
  994. return;
  995. case MixBlend.First:
  996. bone.shearY += (bone.data.shearY - bone.shearY) * alpha;
  997. return;
  998. }
  999. return;
  1000. }
  1001. float y = GetCurveValue(time);
  1002. switch (blend) {
  1003. case MixBlend.Setup:
  1004. bone.shearY = bone.data.shearY + y * alpha;
  1005. break;
  1006. case MixBlend.First:
  1007. case MixBlend.Replace:
  1008. bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
  1009. break;
  1010. case MixBlend.Add:
  1011. bone.shearY += y * alpha;
  1012. break;
  1013. }
  1014. }
  1015. }
  1016. /// <summary>Changes a slot's <see cref="Slot.Color"/>.</summary>
  1017. public class RGBATimeline : CurveTimeline, ISlotTimeline {
  1018. public const int ENTRIES = 5;
  1019. protected const int R = 1, G = 2, B = 3, A = 4;
  1020. readonly int slotIndex;
  1021. public RGBATimeline (int frameCount, int bezierCount, int slotIndex)
  1022. : base(frameCount, bezierCount, //
  1023. (int)Property.RGB + "|" + slotIndex, //
  1024. (int)Property.Alpha + "|" + slotIndex) {
  1025. this.slotIndex = slotIndex;
  1026. }
  1027. public override int FrameEntries {
  1028. get { return ENTRIES; }
  1029. }
  1030. public int SlotIndex {
  1031. get {
  1032. return slotIndex;
  1033. }
  1034. }
  1035. /// <summary>Sets the time and color for the specified frame.</summary>
  1036. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  1037. /// <param name="time">The frame time in seconds.</param>
  1038. public void SetFrame (int frame, float time, float r, float g, float b, float a) {
  1039. frame *= ENTRIES;
  1040. frames[frame] = time;
  1041. frames[frame + R] = r;
  1042. frames[frame + G] = g;
  1043. frames[frame + B] = b;
  1044. frames[frame + A] = a;
  1045. }
  1046. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1047. MixDirection direction) {
  1048. Slot slot = skeleton.slots.Items[slotIndex];
  1049. if (!slot.bone.active) return;
  1050. float[] frames = this.frames;
  1051. if (time < frames[0]) { // Time is before first frame.
  1052. var setup = slot.data;
  1053. switch (blend) {
  1054. case MixBlend.Setup:
  1055. slot.r = setup.r;
  1056. slot.g = setup.g;
  1057. slot.b = setup.b;
  1058. slot.a = setup.a;
  1059. return;
  1060. case MixBlend.First:
  1061. slot.r += (setup.r - slot.r) * alpha;
  1062. slot.g += (setup.g - slot.g) * alpha;
  1063. slot.b += (setup.b - slot.b) * alpha;
  1064. slot.a += (setup.a - slot.a) * alpha;
  1065. slot.ClampColor();
  1066. return;
  1067. }
  1068. return;
  1069. }
  1070. float r, g, b, a;
  1071. int i = Search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES];
  1072. switch (curveType) {
  1073. case LINEAR:
  1074. float before = frames[i];
  1075. r = frames[i + R];
  1076. g = frames[i + G];
  1077. b = frames[i + B];
  1078. a = frames[i + A];
  1079. float t = (time - before) / (frames[i + ENTRIES] - before);
  1080. r += (frames[i + ENTRIES + R] - r) * t;
  1081. g += (frames[i + ENTRIES + G] - g) * t;
  1082. b += (frames[i + ENTRIES + B] - b) * t;
  1083. a += (frames[i + ENTRIES + A] - a) * t;
  1084. break;
  1085. case STEPPED:
  1086. r = frames[i + R];
  1087. g = frames[i + G];
  1088. b = frames[i + B];
  1089. a = frames[i + A];
  1090. break;
  1091. default:
  1092. r = GetBezierValue(time, i, R, curveType - BEZIER);
  1093. g = GetBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER);
  1094. b = GetBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER);
  1095. a = GetBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER);
  1096. break;
  1097. }
  1098. if (alpha == 1) {
  1099. slot.r = r;
  1100. slot.g = g;
  1101. slot.b = b;
  1102. slot.a = a;
  1103. } else {
  1104. float br, bg, bb, ba;
  1105. if (blend == MixBlend.Setup) {
  1106. br = slot.data.r;
  1107. bg = slot.data.g;
  1108. bb = slot.data.b;
  1109. ba = slot.data.a;
  1110. } else {
  1111. br = slot.r;
  1112. bg = slot.g;
  1113. bb = slot.b;
  1114. ba = slot.a;
  1115. }
  1116. slot.r = br + (r - br) * alpha;
  1117. slot.g = bg + (g - bg) * alpha;
  1118. slot.b = bb + (b - bb) * alpha;
  1119. slot.a = ba + (a - ba) * alpha;
  1120. }
  1121. slot.ClampColor();
  1122. }
  1123. }
  1124. /// <summary>Changes the RGB for a slot's <see cref="Slot.Color"/>.</summary>
  1125. public class RGBTimeline : CurveTimeline, ISlotTimeline {
  1126. public const int ENTRIES = 4;
  1127. protected const int R = 1, G = 2, B = 3;
  1128. readonly int slotIndex;
  1129. public RGBTimeline (int frameCount, int bezierCount, int slotIndex)
  1130. : base(frameCount, bezierCount, //
  1131. (int)Property.RGB + "|" + slotIndex) {
  1132. this.slotIndex = slotIndex;
  1133. }
  1134. public override int FrameEntries {
  1135. get { return ENTRIES; }
  1136. }
  1137. public int SlotIndex {
  1138. get {
  1139. return slotIndex;
  1140. }
  1141. }
  1142. /// <summary>Sets the time and color for the specified frame.</summary>
  1143. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  1144. /// <param name="time">The frame time in seconds.</param>
  1145. public void SetFrame (int frame, float time, float r, float g, float b) {
  1146. frame <<= 2;
  1147. frames[frame] = time;
  1148. frames[frame + R] = r;
  1149. frames[frame + G] = g;
  1150. frames[frame + B] = b;
  1151. }
  1152. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1153. MixDirection direction) {
  1154. Slot slot = skeleton.slots.Items[slotIndex];
  1155. if (!slot.bone.active) return;
  1156. float[] frames = this.frames;
  1157. if (time < frames[0]) { // Time is before first frame.
  1158. var setup = slot.data;
  1159. switch (blend) {
  1160. case MixBlend.Setup:
  1161. slot.r = setup.r;
  1162. slot.g = setup.g;
  1163. slot.b = setup.b;
  1164. return;
  1165. case MixBlend.First:
  1166. slot.r += (setup.r - slot.r) * alpha;
  1167. slot.g += (setup.g - slot.g) * alpha;
  1168. slot.b += (setup.b - slot.b) * alpha;
  1169. slot.ClampColor();
  1170. return;
  1171. }
  1172. return;
  1173. }
  1174. float r, g, b;
  1175. int i = Search(frames, time, ENTRIES), curveType = (int)curves[i >> 2];
  1176. switch (curveType) {
  1177. case LINEAR:
  1178. float before = frames[i];
  1179. r = frames[i + R];
  1180. g = frames[i + G];
  1181. b = frames[i + B];
  1182. float t = (time - before) / (frames[i + ENTRIES] - before);
  1183. r += (frames[i + ENTRIES + R] - r) * t;
  1184. g += (frames[i + ENTRIES + G] - g) * t;
  1185. b += (frames[i + ENTRIES + B] - b) * t;
  1186. break;
  1187. case STEPPED:
  1188. r = frames[i + R];
  1189. g = frames[i + G];
  1190. b = frames[i + B];
  1191. break;
  1192. default:
  1193. r = GetBezierValue(time, i, R, curveType - BEZIER);
  1194. g = GetBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER);
  1195. b = GetBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER);
  1196. break;
  1197. }
  1198. if (alpha == 1) {
  1199. slot.r = r;
  1200. slot.g = g;
  1201. slot.b = b;
  1202. } else {
  1203. float br, bg, bb;
  1204. if (blend == MixBlend.Setup) {
  1205. var setup = slot.data;
  1206. br = setup.r;
  1207. bg = setup.g;
  1208. bb = setup.b;
  1209. } else {
  1210. br = slot.r;
  1211. bg = slot.g;
  1212. bb = slot.b;
  1213. }
  1214. slot.r = br + (r - br) * alpha;
  1215. slot.g = bg + (g - bg) * alpha;
  1216. slot.b = bb + (b - bb) * alpha;
  1217. }
  1218. slot.ClampColor();
  1219. }
  1220. }
  1221. /// <summary>Changes the alpha for a slot's <see cref="Slot.Color"/>.</summary>
  1222. public class AlphaTimeline : CurveTimeline1, ISlotTimeline {
  1223. readonly int slotIndex;
  1224. public AlphaTimeline (int frameCount, int bezierCount, int slotIndex)
  1225. : base(frameCount, bezierCount, (int)Property.Alpha + "|" + slotIndex) {
  1226. this.slotIndex = slotIndex;
  1227. }
  1228. public int SlotIndex {
  1229. get {
  1230. return slotIndex;
  1231. }
  1232. }
  1233. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1234. MixDirection direction) {
  1235. Slot slot = skeleton.slots.Items[slotIndex];
  1236. if (!slot.bone.active) return;
  1237. float[] frames = this.frames;
  1238. if (time < frames[0]) { // Time is before first frame.
  1239. var setup = slot.data;
  1240. switch (blend) {
  1241. case MixBlend.Setup:
  1242. slot.a = setup.a;
  1243. return;
  1244. case MixBlend.First:
  1245. slot.a += (setup.a - slot.a) * alpha;
  1246. slot.ClampColor();
  1247. return;
  1248. }
  1249. return;
  1250. }
  1251. float a = GetCurveValue(time);
  1252. if (alpha == 1)
  1253. slot.a = a;
  1254. else {
  1255. if (blend == MixBlend.Setup) slot.a = slot.data.a;
  1256. slot.a += (a - slot.a) * alpha;
  1257. }
  1258. slot.ClampColor();
  1259. }
  1260. }
  1261. /// <summary>Changes a slot's <see cref="Slot.Color"/> and <see cref="Slot.DarkColor"/> for two color tinting.</summary>
  1262. public class RGBA2Timeline : CurveTimeline, ISlotTimeline {
  1263. public const int ENTRIES = 8;
  1264. protected const int R = 1, G = 2, B = 3, A = 4, R2 = 5, G2 = 6, B2 = 7;
  1265. readonly int slotIndex;
  1266. public RGBA2Timeline (int frameCount, int bezierCount, int slotIndex)
  1267. : base(frameCount, bezierCount, //
  1268. (int)Property.RGB + "|" + slotIndex, //
  1269. (int)Property.Alpha + "|" + slotIndex, //
  1270. (int)Property.RGB2 + "|" + slotIndex) {
  1271. this.slotIndex = slotIndex;
  1272. }
  1273. public override int FrameEntries {
  1274. get {
  1275. return ENTRIES;
  1276. }
  1277. }
  1278. /// <summary>
  1279. /// The index of the slot in <see cref="Skeleton.Slots"/> that will be changed when this timeline is applied. The
  1280. /// <see cref="Slot"/> must have a dark color available.</summary>
  1281. public int SlotIndex {
  1282. get {
  1283. return slotIndex;
  1284. }
  1285. }
  1286. /// <summary>Sets the time, light color, and dark color for the specified frame.</summary>
  1287. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  1288. /// <param name="time">The frame time in seconds.</param>
  1289. public void SetFrame (int frame, float time, float r, float g, float b, float a, float r2, float g2, float b2) {
  1290. frame <<= 3;
  1291. frames[frame] = time;
  1292. frames[frame + R] = r;
  1293. frames[frame + G] = g;
  1294. frames[frame + B] = b;
  1295. frames[frame + A] = a;
  1296. frames[frame + R2] = r2;
  1297. frames[frame + G2] = g2;
  1298. frames[frame + B2] = b2;
  1299. }
  1300. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1301. MixDirection direction) {
  1302. Slot slot = skeleton.slots.Items[slotIndex];
  1303. if (!slot.bone.active) return;
  1304. float[] frames = this.frames;
  1305. if (time < frames[0]) { // Time is before first frame.
  1306. SlotData setup = slot.data;
  1307. switch (blend) {
  1308. case MixBlend.Setup:
  1309. slot.r = setup.r;
  1310. slot.g = setup.g;
  1311. slot.b = setup.b;
  1312. slot.a = setup.a;
  1313. slot.ClampColor();
  1314. slot.r2 = setup.r2;
  1315. slot.g2 = setup.g2;
  1316. slot.b2 = setup.b2;
  1317. slot.ClampSecondColor();
  1318. return;
  1319. case MixBlend.First:
  1320. slot.r += (slot.r - setup.r) * alpha;
  1321. slot.g += (slot.g - setup.g) * alpha;
  1322. slot.b += (slot.b - setup.b) * alpha;
  1323. slot.a += (slot.a - setup.a) * alpha;
  1324. slot.ClampColor();
  1325. slot.r2 += (slot.r2 - setup.r2) * alpha;
  1326. slot.g2 += (slot.g2 - setup.g2) * alpha;
  1327. slot.b2 += (slot.b2 - setup.b2) * alpha;
  1328. slot.ClampSecondColor();
  1329. return;
  1330. }
  1331. return;
  1332. }
  1333. float r, g, b, a, r2, g2, b2;
  1334. int i = Search(frames, time, ENTRIES), curveType = (int)curves[i >> 3];
  1335. switch (curveType) {
  1336. case LINEAR:
  1337. float before = frames[i];
  1338. r = frames[i + R];
  1339. g = frames[i + G];
  1340. b = frames[i + B];
  1341. a = frames[i + A];
  1342. r2 = frames[i + R2];
  1343. g2 = frames[i + G2];
  1344. b2 = frames[i + B2];
  1345. float t = (time - before) / (frames[i + ENTRIES] - before);
  1346. r += (frames[i + ENTRIES + R] - r) * t;
  1347. g += (frames[i + ENTRIES + G] - g) * t;
  1348. b += (frames[i + ENTRIES + B] - b) * t;
  1349. a += (frames[i + ENTRIES + A] - a) * t;
  1350. r2 += (frames[i + ENTRIES + R2] - r2) * t;
  1351. g2 += (frames[i + ENTRIES + G2] - g2) * t;
  1352. b2 += (frames[i + ENTRIES + B2] - b2) * t;
  1353. break;
  1354. case STEPPED:
  1355. r = frames[i + R];
  1356. g = frames[i + G];
  1357. b = frames[i + B];
  1358. a = frames[i + A];
  1359. r2 = frames[i + R2];
  1360. g2 = frames[i + G2];
  1361. b2 = frames[i + B2];
  1362. break;
  1363. default:
  1364. r = GetBezierValue(time, i, R, curveType - BEZIER);
  1365. g = GetBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER);
  1366. b = GetBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER);
  1367. a = GetBezierValue(time, i, A, curveType + BEZIER_SIZE * 3 - BEZIER);
  1368. r2 = GetBezierValue(time, i, R2, curveType + BEZIER_SIZE * 4 - BEZIER);
  1369. g2 = GetBezierValue(time, i, G2, curveType + BEZIER_SIZE * 5 - BEZIER);
  1370. b2 = GetBezierValue(time, i, B2, curveType + BEZIER_SIZE * 6 - BEZIER);
  1371. break;
  1372. }
  1373. if (alpha == 1) {
  1374. slot.r = r;
  1375. slot.g = g;
  1376. slot.b = b;
  1377. slot.a = a;
  1378. slot.r2 = r2;
  1379. slot.g2 = g2;
  1380. slot.b2 = b2;
  1381. } else {
  1382. float br, bg, bb, ba, br2, bg2, bb2;
  1383. if (blend == MixBlend.Setup) {
  1384. br = slot.data.r;
  1385. bg = slot.data.g;
  1386. bb = slot.data.b;
  1387. ba = slot.data.a;
  1388. br2 = slot.data.r2;
  1389. bg2 = slot.data.g2;
  1390. bb2 = slot.data.b2;
  1391. } else {
  1392. br = slot.r;
  1393. bg = slot.g;
  1394. bb = slot.b;
  1395. ba = slot.a;
  1396. br2 = slot.r2;
  1397. bg2 = slot.g2;
  1398. bb2 = slot.b2;
  1399. }
  1400. slot.r = br + (r - br) * alpha;
  1401. slot.g = bg + (g - bg) * alpha;
  1402. slot.b = bb + (b - bb) * alpha;
  1403. slot.a = ba + (a - ba) * alpha;
  1404. slot.r2 = br2 + (r2 - br2) * alpha;
  1405. slot.g2 = bg2 + (g2 - bg2) * alpha;
  1406. slot.b2 = bb2 + (b2 - bb2) * alpha;
  1407. }
  1408. slot.ClampColor();
  1409. slot.ClampSecondColor();
  1410. }
  1411. }
  1412. /// <summary>Changes the RGB for a slot's <see cref="Slot.Color"/> and <see cref="Slot.DarkColor"/> for two color tinting.</summary>
  1413. public class RGB2Timeline : CurveTimeline, ISlotTimeline {
  1414. public const int ENTRIES = 7;
  1415. protected const int R = 1, G = 2, B = 3, R2 = 4, G2 = 5, B2 = 6;
  1416. readonly int slotIndex;
  1417. public RGB2Timeline (int frameCount, int bezierCount, int slotIndex)
  1418. : base(frameCount, bezierCount, //
  1419. (int)Property.RGB + "|" + slotIndex, //
  1420. (int)Property.RGB2 + "|" + slotIndex) {
  1421. this.slotIndex = slotIndex;
  1422. }
  1423. public override int FrameEntries {
  1424. get {
  1425. return ENTRIES;
  1426. }
  1427. }
  1428. /// <summary>
  1429. /// The index of the slot in <see cref="Skeleton.Slots"/> that will be changed when this timeline is applied. The
  1430. /// <see cref="Slot"/> must have a dark color available.</summary>
  1431. public int SlotIndex {
  1432. get {
  1433. return slotIndex;
  1434. }
  1435. }
  1436. /// <summary>Sets the time, light color, and dark color for the specified frame.</summary>
  1437. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  1438. /// <param name="time">The frame time in seconds.</param>
  1439. public void SetFrame (int frame, float time, float r, float g, float b, float r2, float g2, float b2) {
  1440. frame *= ENTRIES;
  1441. frames[frame] = time;
  1442. frames[frame + R] = r;
  1443. frames[frame + G] = g;
  1444. frames[frame + B] = b;
  1445. frames[frame + R2] = r2;
  1446. frames[frame + G2] = g2;
  1447. frames[frame + B2] = b2;
  1448. }
  1449. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1450. MixDirection direction) {
  1451. Slot slot = skeleton.slots.Items[slotIndex];
  1452. if (!slot.bone.active) return;
  1453. float[] frames = this.frames;
  1454. if (time < frames[0]) { // Time is before first frame.
  1455. SlotData setup = slot.data;
  1456. switch (blend) {
  1457. case MixBlend.Setup:
  1458. slot.r = setup.r;
  1459. slot.g = setup.g;
  1460. slot.b = setup.b;
  1461. slot.ClampColor();
  1462. slot.r2 = setup.r2;
  1463. slot.g2 = setup.g2;
  1464. slot.b2 = setup.b2;
  1465. slot.ClampSecondColor();
  1466. return;
  1467. case MixBlend.First:
  1468. slot.r += (slot.r - setup.r) * alpha;
  1469. slot.g += (slot.g - setup.g) * alpha;
  1470. slot.b += (slot.b - setup.b) * alpha;
  1471. slot.ClampColor();
  1472. slot.r2 += (slot.r2 - setup.r2) * alpha;
  1473. slot.g2 += (slot.g2 - setup.g2) * alpha;
  1474. slot.b2 += (slot.b2 - setup.b2) * alpha;
  1475. slot.ClampSecondColor();
  1476. return;
  1477. }
  1478. return;
  1479. }
  1480. float r, g, b, r2, g2, b2;
  1481. int i = Search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES];
  1482. switch (curveType) {
  1483. case LINEAR:
  1484. float before = frames[i];
  1485. r = frames[i + R];
  1486. g = frames[i + G];
  1487. b = frames[i + B];
  1488. r2 = frames[i + R2];
  1489. g2 = frames[i + G2];
  1490. b2 = frames[i + B2];
  1491. float t = (time - before) / (frames[i + ENTRIES] - before);
  1492. r += (frames[i + ENTRIES + R] - r) * t;
  1493. g += (frames[i + ENTRIES + G] - g) * t;
  1494. b += (frames[i + ENTRIES + B] - b) * t;
  1495. r2 += (frames[i + ENTRIES + R2] - r2) * t;
  1496. g2 += (frames[i + ENTRIES + G2] - g2) * t;
  1497. b2 += (frames[i + ENTRIES + B2] - b2) * t;
  1498. break;
  1499. case STEPPED:
  1500. r = frames[i + R];
  1501. g = frames[i + G];
  1502. b = frames[i + B];
  1503. r2 = frames[i + R2];
  1504. g2 = frames[i + G2];
  1505. b2 = frames[i + B2];
  1506. break;
  1507. default:
  1508. r = GetBezierValue(time, i, R, curveType - BEZIER);
  1509. g = GetBezierValue(time, i, G, curveType + BEZIER_SIZE - BEZIER);
  1510. b = GetBezierValue(time, i, B, curveType + BEZIER_SIZE * 2 - BEZIER);
  1511. r2 = GetBezierValue(time, i, R2, curveType + BEZIER_SIZE * 3 - BEZIER);
  1512. g2 = GetBezierValue(time, i, G2, curveType + BEZIER_SIZE * 4 - BEZIER);
  1513. b2 = GetBezierValue(time, i, B2, curveType + BEZIER_SIZE * 5 - BEZIER);
  1514. break;
  1515. }
  1516. if (alpha == 1) {
  1517. slot.r = r;
  1518. slot.g = g;
  1519. slot.b = b;
  1520. slot.r2 = r2;
  1521. slot.g2 = g2;
  1522. slot.b2 = b2;
  1523. } else {
  1524. float br, bg, bb, br2, bg2, bb2;
  1525. if (blend == MixBlend.Setup) {
  1526. SlotData setup = slot.data;
  1527. br = setup.r;
  1528. bg = setup.g;
  1529. bb = setup.b;
  1530. br2 = setup.r2;
  1531. bg2 = setup.g2;
  1532. bb2 = setup.b2;
  1533. } else {
  1534. br = slot.r;
  1535. bg = slot.g;
  1536. bb = slot.b;
  1537. br2 = slot.r2;
  1538. bg2 = slot.g2;
  1539. bb2 = slot.b2;
  1540. }
  1541. slot.r = br + (r - br) * alpha;
  1542. slot.g = bg + (g - bg) * alpha;
  1543. slot.b = bb + (b - bb) * alpha;
  1544. slot.r2 = br2 + (r2 - br2) * alpha;
  1545. slot.g2 = bg2 + (g2 - bg2) * alpha;
  1546. slot.b2 = bb2 + (b2 - bb2) * alpha;
  1547. }
  1548. slot.ClampColor();
  1549. slot.ClampSecondColor();
  1550. }
  1551. }
  1552. /// <summary>Changes a slot's <see cref="Slot.Attachment"/>.</summary>
  1553. public class AttachmentTimeline : Timeline, ISlotTimeline {
  1554. readonly int slotIndex;
  1555. readonly string[] attachmentNames;
  1556. public AttachmentTimeline (int frameCount, int slotIndex)
  1557. : base(frameCount, (int)Property.Attachment + "|" + slotIndex) {
  1558. this.slotIndex = slotIndex;
  1559. attachmentNames = new String[frameCount];
  1560. }
  1561. public int SlotIndex {
  1562. get {
  1563. return slotIndex;
  1564. }
  1565. }
  1566. /// <summary>The attachment name for each frame. May contain null values to clear the attachment. </summary>
  1567. public string[] AttachmentNames {
  1568. get {
  1569. return attachmentNames;
  1570. }
  1571. }
  1572. /// <summary>Sets the time and attachment name for the specified frame.</summary>
  1573. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  1574. /// <param name="time">The frame time in seconds.</param>
  1575. public void SetFrame (int frame, float time, String attachmentName) {
  1576. frames[frame] = time;
  1577. attachmentNames[frame] = attachmentName;
  1578. }
  1579. public override void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1580. MixDirection direction) {
  1581. Slot slot = skeleton.slots.Items[slotIndex];
  1582. if (!slot.bone.active) return;
  1583. if (direction == MixDirection.Out) {
  1584. if (blend == MixBlend.Setup) SetAttachment(skeleton, slot, slot.data.attachmentName);
  1585. return;
  1586. }
  1587. float[] frames = this.frames;
  1588. if (time < frames[0]) { // Time is before first frame.
  1589. if (blend == MixBlend.Setup || blend == MixBlend.First) SetAttachment(skeleton, slot, slot.data.attachmentName);
  1590. return;
  1591. }
  1592. SetAttachment(skeleton, slot, attachmentNames[Search(frames, time)]);
  1593. }
  1594. private void SetAttachment (Skeleton skeleton, Slot slot, string attachmentName) {
  1595. slot.Attachment = attachmentName == null ? null : skeleton.GetAttachment(slotIndex, attachmentName);
  1596. }
  1597. }
  1598. /// <summary>Changes a slot's <see cref="Slot.Deform"/> to deform a <see cref="VertexAttachment"/>.</summary>
  1599. public class DeformTimeline : CurveTimeline, ISlotTimeline {
  1600. readonly int slotIndex;
  1601. readonly VertexAttachment attachment;
  1602. internal float[][] vertices;
  1603. public DeformTimeline (int frameCount, int bezierCount, int slotIndex, VertexAttachment attachment)
  1604. : base(frameCount, bezierCount, (int)Property.Deform + "|" + slotIndex + "|" + attachment.Id) {
  1605. this.slotIndex = slotIndex;
  1606. this.attachment = attachment;
  1607. vertices = new float[frameCount][];
  1608. }
  1609. public int SlotIndex {
  1610. get {
  1611. return slotIndex;
  1612. }
  1613. }
  1614. /// <summary>The attachment that will be deformed.</summary>
  1615. /// <seealso cref="VertexAttachment.DeformAttachment"/>
  1616. public VertexAttachment Attachment {
  1617. get {
  1618. return attachment;
  1619. }
  1620. }
  1621. /// <summary>The vertices for each frame.</summary>
  1622. public float[][] Vertices {
  1623. get {
  1624. return vertices;
  1625. }
  1626. }
  1627. /// <summary>Sets the time and vertices for the specified frame.</summary>
  1628. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  1629. /// <param name="time">The frame time in seconds.</param>
  1630. /// <param name="vertices">Vertex positions for an unweighted VertexAttachment, or deform offsets if it has weights.</param>
  1631. public void SetFrame (int frame, float time, float[] vertices) {
  1632. frames[frame] = time;
  1633. this.vertices[frame] = vertices;
  1634. }
  1635. /// <param name="value1">Ignored (0 is used for a deform timeline).</param>
  1636. /// <param name="value2">Ignored (1 is used for a deform timeline).</param>
  1637. public void setBezier (int bezier, int frame, int value, float time1, float value1, float cx1, float cy1, float cx2,
  1638. float cy2, float time2, float value2) {
  1639. float[] curves = this.curves;
  1640. int i = FrameCount + bezier * BEZIER_SIZE;
  1641. if (value == 0) curves[frame] = BEZIER + i;
  1642. float tmpx = (time1 - cx1 * 2 + cx2) * 0.03f, tmpy = cy2 * 0.03f - cy1 * 0.06f;
  1643. float dddx = ((cx1 - cx2) * 3 - time1 + time2) * 0.006f, dddy = (cy1 - cy2 + 0.33333333f) * 0.018f;
  1644. float ddx = tmpx * 2 + dddx, ddy = tmpy * 2 + dddy;
  1645. float dx = (cx1 - time1) * 0.3f + tmpx + dddx * 0.16666667f, dy = cy1 * 0.3f + tmpy + dddy * 0.16666667f;
  1646. float x = time1 + dx, y = dy;
  1647. for (int n = i + BEZIER_SIZE; i < n; i += 2) {
  1648. curves[i] = x;
  1649. curves[i + 1] = y;
  1650. dx += ddx;
  1651. dy += ddy;
  1652. ddx += dddx;
  1653. ddy += dddy;
  1654. x += dx;
  1655. y += dy;
  1656. }
  1657. }
  1658. /// <summary>Returns the interpolated percentage for the specified time.</summary>
  1659. /// <param name="frame">The frame before <code>time</code>.</param>
  1660. private float GetCurvePercent (float time, int frame) {
  1661. float[] curves = this.curves;
  1662. int i = (int)curves[frame];
  1663. switch (i) {
  1664. case LINEAR:
  1665. float x = frames[frame];
  1666. return (time - x) / (frames[frame + FrameEntries] - x);
  1667. case STEPPED:
  1668. return 0;
  1669. }
  1670. i -= BEZIER;
  1671. if (curves[i] > time) {
  1672. float x = frames[frame];
  1673. return curves[i + 1] * (time - x) / (curves[i] - x);
  1674. }
  1675. int n = i + BEZIER_SIZE;
  1676. for (i += 2; i < n; i += 2) {
  1677. if (curves[i] >= time) {
  1678. float x = curves[i - 2], y = curves[i - 1];
  1679. return y + (time - x) / (curves[i] - x) * (curves[i + 1] - y);
  1680. }
  1681. }
  1682. { // scope added to prevent compile error "float x and y declared in enclosing scope"
  1683. float x = curves[n - 2], y = curves[n - 1];
  1684. return y + (1 - y) * (time - x) / (frames[frame + FrameEntries] - x);
  1685. }
  1686. }
  1687. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1688. MixDirection direction) {
  1689. Slot slot = skeleton.slots.Items[slotIndex];
  1690. if (!slot.bone.active) return;
  1691. var vertexAttachment = slot.attachment as VertexAttachment;
  1692. if (vertexAttachment == null || vertexAttachment.DeformAttachment != attachment) return;
  1693. var deformArray = slot.Deform;
  1694. if (deformArray.Count == 0) blend = MixBlend.Setup;
  1695. float[][] vertices = this.vertices;
  1696. int vertexCount = vertices[0].Length;
  1697. float[] deform;
  1698. float[] frames = this.frames;
  1699. if (time < frames[0]) { // Time is before first frame.
  1700. switch (blend) {
  1701. case MixBlend.Setup:
  1702. deformArray.Clear();
  1703. return;
  1704. case MixBlend.First:
  1705. if (alpha == 1) {
  1706. deformArray.Clear();
  1707. return;
  1708. }
  1709. // Ensure size and preemptively set count.
  1710. if (deformArray.Capacity < vertexCount) deformArray.Capacity = vertexCount;
  1711. deformArray.Count = vertexCount;
  1712. deform = deformArray.Items;
  1713. if (vertexAttachment.bones == null) {
  1714. // Unweighted vertex positions.
  1715. float[] setupVertices = vertexAttachment.vertices;
  1716. for (int i = 0; i < vertexCount; i++)
  1717. deform[i] += (setupVertices[i] - deform[i]) * alpha;
  1718. } else {
  1719. // Weighted deform offsets.
  1720. alpha = 1 - alpha;
  1721. for (int i = 0; i < vertexCount; i++)
  1722. deform[i] *= alpha;
  1723. }
  1724. return;
  1725. }
  1726. return;
  1727. }
  1728. // Ensure size and preemptively set count.
  1729. if (deformArray.Capacity < vertexCount) deformArray.Capacity = vertexCount;
  1730. deformArray.Count = vertexCount;
  1731. deform = deformArray.Items;
  1732. if (time >= frames[frames.Length - 1]) { // Time is after last frame.
  1733. float[] lastVertices = vertices[frames.Length - 1];
  1734. if (alpha == 1) {
  1735. if (blend == MixBlend.Add) {
  1736. if (vertexAttachment.bones == null) {
  1737. // Unweighted vertex positions, no alpha.
  1738. float[] setupVertices = vertexAttachment.vertices;
  1739. for (int i = 0; i < vertexCount; i++)
  1740. deform[i] += lastVertices[i] - setupVertices[i];
  1741. } else {
  1742. // Weighted deform offsets, no alpha.
  1743. for (int i = 0; i < vertexCount; i++)
  1744. deform[i] += lastVertices[i];
  1745. }
  1746. } else {
  1747. // Vertex positions or deform offsets, no alpha.
  1748. Array.Copy(lastVertices, 0, deform, 0, vertexCount);
  1749. }
  1750. } else {
  1751. switch (blend) {
  1752. case MixBlend.Setup: {
  1753. if (vertexAttachment.bones == null) {
  1754. // Unweighted vertex positions, with alpha.
  1755. float[] setupVertices = vertexAttachment.vertices;
  1756. for (int i = 0; i < vertexCount; i++) {
  1757. float setup = setupVertices[i];
  1758. deform[i] = setup + (lastVertices[i] - setup) * alpha;
  1759. }
  1760. } else {
  1761. // Weighted deform offsets, with alpha.
  1762. for (int i = 0; i < vertexCount; i++)
  1763. deform[i] = lastVertices[i] * alpha;
  1764. }
  1765. break;
  1766. }
  1767. case MixBlend.First:
  1768. case MixBlend.Replace:
  1769. // Vertex positions or deform offsets, with alpha.
  1770. for (int i = 0; i < vertexCount; i++)
  1771. deform[i] += (lastVertices[i] - deform[i]) * alpha;
  1772. break;
  1773. case MixBlend.Add:
  1774. if (vertexAttachment.bones == null) {
  1775. // Unweighted vertex positions, no alpha.
  1776. float[] setupVertices = vertexAttachment.vertices;
  1777. for (int i = 0; i < vertexCount; i++)
  1778. deform[i] += (lastVertices[i] - setupVertices[i]) * alpha;
  1779. } else {
  1780. // Weighted deform offsets, alpha.
  1781. for (int i = 0; i < vertexCount; i++)
  1782. deform[i] += lastVertices[i] * alpha;
  1783. }
  1784. break;
  1785. }
  1786. }
  1787. return;
  1788. }
  1789. int frame = Search(frames, time);
  1790. float percent = GetCurvePercent(time, frame);
  1791. float[] prevVertices = vertices[frame];
  1792. float[] nextVertices = vertices[frame + 1];
  1793. if (alpha == 1) {
  1794. if (blend == MixBlend.Add) {
  1795. if (vertexAttachment.bones == null) {
  1796. // Unweighted vertex positions, no alpha.
  1797. float[] setupVertices = vertexAttachment.vertices;
  1798. for (int i = 0; i < vertexCount; i++) {
  1799. float prev = prevVertices[i];
  1800. deform[i] += prev + (nextVertices[i] - prev) * percent - setupVertices[i];
  1801. }
  1802. } else {
  1803. // Weighted deform offsets, no alpha.
  1804. for (int i = 0; i < vertexCount; i++) {
  1805. float prev = prevVertices[i];
  1806. deform[i] += prev + (nextVertices[i] - prev) * percent;
  1807. }
  1808. }
  1809. } else {
  1810. // Vertex positions or deform offsets, no alpha.
  1811. for (int i = 0; i < vertexCount; i++) {
  1812. float prev = prevVertices[i];
  1813. deform[i] = prev + (nextVertices[i] - prev) * percent;
  1814. }
  1815. }
  1816. } else {
  1817. switch (blend) {
  1818. case MixBlend.Setup: {
  1819. if (vertexAttachment.bones == null) {
  1820. // Unweighted vertex positions, with alpha.
  1821. float[] setupVertices = vertexAttachment.vertices;
  1822. for (int i = 0; i < vertexCount; i++) {
  1823. float prev = prevVertices[i], setup = setupVertices[i];
  1824. deform[i] = setup + (prev + (nextVertices[i] - prev) * percent - setup) * alpha;
  1825. }
  1826. } else {
  1827. // Weighted deform offsets, with alpha.
  1828. for (int i = 0; i < vertexCount; i++) {
  1829. float prev = prevVertices[i];
  1830. deform[i] = (prev + (nextVertices[i] - prev) * percent) * alpha;
  1831. }
  1832. }
  1833. break;
  1834. }
  1835. case MixBlend.First:
  1836. case MixBlend.Replace: {
  1837. // Vertex positions or deform offsets, with alpha.
  1838. for (int i = 0; i < vertexCount; i++) {
  1839. float prev = prevVertices[i];
  1840. deform[i] += (prev + (nextVertices[i] - prev) * percent - deform[i]) * alpha;
  1841. }
  1842. break;
  1843. }
  1844. case MixBlend.Add:
  1845. if (vertexAttachment.bones == null) {
  1846. // Unweighted vertex positions, with alpha.
  1847. float[] setupVertices = vertexAttachment.vertices;
  1848. for (int i = 0; i < vertexCount; i++) {
  1849. float prev = prevVertices[i];
  1850. deform[i] += (prev + (nextVertices[i] - prev) * percent - setupVertices[i]) * alpha;
  1851. }
  1852. } else {
  1853. // Weighted deform offsets, with alpha.
  1854. for (int i = 0; i < vertexCount; i++) {
  1855. float prev = prevVertices[i];
  1856. deform[i] += (prev + (nextVertices[i] - prev) * percent) * alpha;
  1857. }
  1858. }
  1859. break;
  1860. }
  1861. }
  1862. }
  1863. }
  1864. /// <summary>Fires an <see cref="Event"/> when specific animation times are reached.</summary>
  1865. public class EventTimeline : Timeline {
  1866. readonly static string[] propertyIds = { ((int)Property.Event).ToString() };
  1867. readonly Event[] events;
  1868. public EventTimeline (int frameCount)
  1869. : base(frameCount, propertyIds) {
  1870. events = new Event[frameCount];
  1871. }
  1872. /// <summary>The event for each frame.</summary>
  1873. public Event[] Events {
  1874. get {
  1875. return events;
  1876. }
  1877. }
  1878. /// <summary>Sets the time and event for the specified frame.</summary>
  1879. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  1880. public void SetFrame (int frame, Event e) {
  1881. frames[frame] = e.time;
  1882. events[frame] = e;
  1883. }
  1884. /// <summary>Fires events for frames &gt; <code>lastTime</code> and &lt;= <code>time</code>.</summary>
  1885. public override void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha,
  1886. MixBlend blend, MixDirection direction) {
  1887. if (firedEvents == null) return;
  1888. float[] frames = this.frames;
  1889. int frameCount = frames.Length;
  1890. if (lastTime > time) { // Fire events after last time for looped animations.
  1891. Apply(skeleton, lastTime, int.MaxValue, firedEvents, alpha, blend, direction);
  1892. lastTime = -1f;
  1893. } else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
  1894. return;
  1895. if (time < frames[0]) return; // Time is before first frame.
  1896. int i;
  1897. if (lastTime < frames[0])
  1898. i = 0;
  1899. else {
  1900. i = Search(frames, lastTime) + 1;
  1901. float frameTime = frames[i];
  1902. while (i > 0) { // Fire multiple events with the same frame.
  1903. if (frames[i - 1] != frameTime) break;
  1904. i--;
  1905. }
  1906. }
  1907. for (; i < frameCount && time >= frames[i]; i++)
  1908. firedEvents.Add(events[i]);
  1909. }
  1910. }
  1911. /// <summary>Changes a skeleton's <see cref="Skeleton.DrawOrder"/>.</summary>
  1912. public class DrawOrderTimeline : Timeline {
  1913. static readonly string[] propertyIds = { ((int)Property.DrawOrder).ToString() };
  1914. readonly int[][] drawOrders;
  1915. public DrawOrderTimeline (int frameCount)
  1916. : base(frameCount, propertyIds) {
  1917. drawOrders = new int[frameCount][];
  1918. }
  1919. /// <summary>The draw order for each frame. </summary>
  1920. /// <seealso cref="Timeline.SetFrame(int, float, int[])"/>.
  1921. public int[][] DrawOrders {
  1922. get {
  1923. return drawOrders;
  1924. }
  1925. }
  1926. /// <summary>Sets the time and draw order for the specified frame.</summary>
  1927. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  1928. /// <param name="time">The frame time in seconds.</param>
  1929. /// <param name="drawOrder">For each slot in <see cref="Skeleton.Slots"/>, the index of the slot in the new draw order. May be null to use
  1930. /// setup pose draw order.</param>
  1931. public void SetFrame (int frame, float time, int[] drawOrder) {
  1932. frames[frame] = time;
  1933. drawOrders[frame] = drawOrder;
  1934. }
  1935. public override void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1936. MixDirection direction) {
  1937. if (direction == MixDirection.Out) {
  1938. if (blend == MixBlend.Setup) Array.Copy(skeleton.slots.Items, 0, skeleton.drawOrder.Items, 0, skeleton.slots.Count);
  1939. return;
  1940. }
  1941. float[] frames = this.frames;
  1942. if (time < frames[0]) { // Time is before first frame.
  1943. if (blend == MixBlend.Setup || blend == MixBlend.First) Array.Copy(skeleton.slots.Items, 0, skeleton.drawOrder.Items, 0, skeleton.slots.Count);
  1944. return;
  1945. }
  1946. int[] drawOrderToSetupIndex = drawOrders[Search(frames, time)];
  1947. if (drawOrderToSetupIndex == null)
  1948. Array.Copy(skeleton.slots.Items, 0, skeleton.drawOrder.Items, 0, skeleton.slots.Count);
  1949. else {
  1950. Slot[] slots = skeleton.slots.Items;
  1951. Slot[] drawOrder = skeleton.drawOrder.Items;
  1952. for (int i = 0, n = drawOrderToSetupIndex.Length; i < n; i++)
  1953. drawOrder[i] = slots[drawOrderToSetupIndex[i]];
  1954. }
  1955. }
  1956. }
  1957. /// <summary>Changes an IK constraint's <see cref="IkConstraint.Mix"/>, <see cref="IkConstraint.Softness"/>,
  1958. /// <see cref="IkConstraint.BendDirection"/>, <see cref="IkConstraint.Stretch"/>, and <see cref="IkConstraint.Compress"/>.</summary>
  1959. public class IkConstraintTimeline : CurveTimeline {
  1960. public const int ENTRIES = 6;
  1961. private const int MIX = 1, SOFTNESS = 2, BEND_DIRECTION = 3, COMPRESS = 4, STRETCH = 5;
  1962. readonly int ikConstraintIndex;
  1963. public IkConstraintTimeline (int frameCount, int bezierCount, int ikConstraintIndex)
  1964. : base(frameCount, bezierCount, (int)Property.IkConstraint + "|" + ikConstraintIndex) {
  1965. this.ikConstraintIndex = ikConstraintIndex;
  1966. }
  1967. public override int FrameEntries {
  1968. get {
  1969. return ENTRIES;
  1970. }
  1971. }
  1972. /// <summary>The index of the IK constraint slot in <see cref="Skeleton.IkConstraints"/> that will be changed when this timeline is
  1973. /// applied.</summary>
  1974. public int IkConstraintIndex {
  1975. get {
  1976. return ikConstraintIndex;
  1977. }
  1978. }
  1979. /// <summary>Sets the time, mix, softness, bend direction, compress, and stretch for the specified frame.</summary>
  1980. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  1981. /// <param name="time">The frame time in seconds.</param>
  1982. /// <param name="bendDirection">1 or -1.</param>
  1983. public void SetFrame (int frame, float time, float mix, float softness, int bendDirection, bool compress,
  1984. bool stretch) {
  1985. frame *= ENTRIES;
  1986. frames[frame] = time;
  1987. frames[frame + MIX] = mix;
  1988. frames[frame + SOFTNESS] = softness;
  1989. frames[frame + BEND_DIRECTION] = bendDirection;
  1990. frames[frame + COMPRESS] = compress ? 1 : 0;
  1991. frames[frame + STRETCH] = stretch ? 1 : 0;
  1992. }
  1993. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  1994. MixDirection direction) {
  1995. IkConstraint constraint = skeleton.ikConstraints.Items[ikConstraintIndex];
  1996. if (!constraint.active) return;
  1997. float[] frames = this.frames;
  1998. if (time < frames[0]) { // Time is before first frame.
  1999. switch (blend) {
  2000. case MixBlend.Setup:
  2001. constraint.mix = constraint.data.mix;
  2002. constraint.softness = constraint.data.softness;
  2003. constraint.bendDirection = constraint.data.bendDirection;
  2004. constraint.compress = constraint.data.compress;
  2005. constraint.stretch = constraint.data.stretch;
  2006. return;
  2007. case MixBlend.First:
  2008. constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
  2009. constraint.softness += (constraint.data.softness - constraint.softness) * alpha;
  2010. constraint.bendDirection = constraint.data.bendDirection;
  2011. constraint.compress = constraint.data.compress;
  2012. constraint.stretch = constraint.data.stretch;
  2013. return;
  2014. }
  2015. return;
  2016. }
  2017. float mix, softness;
  2018. int i = Search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES];
  2019. switch (curveType) {
  2020. case LINEAR:
  2021. float before = frames[i];
  2022. mix = frames[i + MIX];
  2023. softness = frames[i + SOFTNESS];
  2024. float t = (time - before) / (frames[i + ENTRIES] - before);
  2025. mix += (frames[i + ENTRIES + MIX] - mix) * t;
  2026. softness += (frames[i + ENTRIES + SOFTNESS] - softness) * t;
  2027. break;
  2028. case STEPPED:
  2029. mix = frames[i + MIX];
  2030. softness = frames[i + SOFTNESS];
  2031. break;
  2032. default:
  2033. mix = GetBezierValue(time, i, MIX, curveType - BEZIER);
  2034. softness = GetBezierValue(time, i, SOFTNESS, curveType + BEZIER_SIZE - BEZIER);
  2035. break;
  2036. }
  2037. if (blend == MixBlend.Setup) {
  2038. constraint.mix = constraint.data.mix + (mix - constraint.data.mix) * alpha;
  2039. constraint.softness = constraint.data.softness + (softness - constraint.data.softness) * alpha;
  2040. if (direction == MixDirection.Out) {
  2041. constraint.bendDirection = constraint.data.bendDirection;
  2042. constraint.compress = constraint.data.compress;
  2043. constraint.stretch = constraint.data.stretch;
  2044. } else {
  2045. constraint.bendDirection = (int)frames[i + BEND_DIRECTION];
  2046. constraint.compress = frames[i + COMPRESS] != 0;
  2047. constraint.stretch = frames[i + STRETCH] != 0;
  2048. }
  2049. } else {
  2050. constraint.mix += (mix - constraint.mix) * alpha;
  2051. constraint.softness += (softness - constraint.softness) * alpha;
  2052. if (direction == MixDirection.In) {
  2053. constraint.bendDirection = (int)frames[i + BEND_DIRECTION];
  2054. constraint.compress = frames[i + COMPRESS] != 0;
  2055. constraint.stretch = frames[i + STRETCH] != 0;
  2056. }
  2057. }
  2058. }
  2059. }
  2060. /// <summary>Changes a transform constraint's mixes.</summary>
  2061. public class TransformConstraintTimeline : CurveTimeline {
  2062. public const int ENTRIES = 7;
  2063. private const int ROTATE = 1, X = 2, Y = 3, SCALEX = 4, SCALEY = 5, SHEARY = 6;
  2064. readonly int transformConstraintIndex;
  2065. public TransformConstraintTimeline (int frameCount, int bezierCount, int transformConstraintIndex)
  2066. : base(frameCount, bezierCount, (int)Property.TransformConstraint + "|" + transformConstraintIndex) {
  2067. this.transformConstraintIndex = transformConstraintIndex;
  2068. }
  2069. public override int FrameEntries {
  2070. get {
  2071. return ENTRIES;
  2072. }
  2073. }
  2074. /// <summary>The index of the transform constraint slot in <see cref="Skeleton.TransformConstraints"/> that will be changed when this
  2075. /// timeline is applied.</summary>
  2076. public int TransformConstraintIndex {
  2077. get {
  2078. return transformConstraintIndex;
  2079. }
  2080. }
  2081. /// <summary>Sets the time, rotate mix, translate mix, scale mix, and shear mix for the specified frame.</summary>
  2082. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  2083. /// <param name="time">The frame time in seconds.</param>
  2084. public void SetFrame (int frame, float time, float mixRotate, float mixX, float mixY, float mixScaleX, float mixScaleY,
  2085. float mixShearY) {
  2086. frame *= ENTRIES;
  2087. frames[frame] = time;
  2088. frames[frame + ROTATE] = mixRotate;
  2089. frames[frame + X] = mixX;
  2090. frames[frame + Y] = mixY;
  2091. frames[frame + SCALEX] = mixScaleX;
  2092. frames[frame + SCALEY] = mixScaleY;
  2093. frames[frame + SHEARY] = mixShearY;
  2094. }
  2095. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  2096. MixDirection direction) {
  2097. TransformConstraint constraint = skeleton.transformConstraints.Items[transformConstraintIndex];
  2098. if (!constraint.active) return;
  2099. float[] frames = this.frames;
  2100. if (time < frames[0]) { // Time is before first frame.
  2101. TransformConstraintData data = constraint.data;
  2102. switch (blend) {
  2103. case MixBlend.Setup:
  2104. constraint.mixRotate = data.mixRotate;
  2105. constraint.mixX = data.mixX;
  2106. constraint.mixY = data.mixY;
  2107. constraint.mixScaleX = data.mixScaleX;
  2108. constraint.mixScaleY = data.mixScaleY;
  2109. constraint.mixShearY = data.mixShearY;
  2110. return;
  2111. case MixBlend.First:
  2112. constraint.mixRotate += (data.mixRotate - constraint.mixRotate) * alpha;
  2113. constraint.mixX += (data.mixX - constraint.mixX) * alpha;
  2114. constraint.mixY += (data.mixY - constraint.mixY) * alpha;
  2115. constraint.mixScaleX += (data.mixScaleX - constraint.mixScaleX) * alpha;
  2116. constraint.mixScaleY += (data.mixScaleY - constraint.mixScaleY) * alpha;
  2117. constraint.mixShearY += (data.mixShearY - constraint.mixShearY) * alpha;
  2118. return;
  2119. }
  2120. return;
  2121. }
  2122. float rotate, x, y, scaleX, scaleY, shearY;
  2123. int i = Search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES];
  2124. switch (curveType) {
  2125. case LINEAR:
  2126. float before = frames[i];
  2127. rotate = frames[i + ROTATE];
  2128. x = frames[i + X];
  2129. y = frames[i + Y];
  2130. scaleX = frames[i + SCALEX];
  2131. scaleY = frames[i + SCALEY];
  2132. shearY = frames[i + SHEARY];
  2133. float t = (time - before) / (frames[i + ENTRIES] - before);
  2134. rotate += (frames[i + ENTRIES + ROTATE] - rotate) * t;
  2135. x += (frames[i + ENTRIES + X] - x) * t;
  2136. y += (frames[i + ENTRIES + Y] - y) * t;
  2137. scaleX += (frames[i + ENTRIES + SCALEX] - scaleX) * t;
  2138. scaleY += (frames[i + ENTRIES + SCALEY] - scaleY) * t;
  2139. shearY += (frames[i + ENTRIES + SHEARY] - shearY) * t;
  2140. break;
  2141. case STEPPED:
  2142. rotate = frames[i + ROTATE];
  2143. x = frames[i + X];
  2144. y = frames[i + Y];
  2145. scaleX = frames[i + SCALEX];
  2146. scaleY = frames[i + SCALEY];
  2147. shearY = frames[i + SHEARY];
  2148. break;
  2149. default:
  2150. rotate = GetBezierValue(time, i, ROTATE, curveType - BEZIER);
  2151. x = GetBezierValue(time, i, X, curveType + BEZIER_SIZE - BEZIER);
  2152. y = GetBezierValue(time, i, Y, curveType + BEZIER_SIZE * 2 - BEZIER);
  2153. scaleX = GetBezierValue(time, i, SCALEX, curveType + BEZIER_SIZE * 3 - BEZIER);
  2154. scaleY = GetBezierValue(time, i, SCALEY, curveType + BEZIER_SIZE * 4 - BEZIER);
  2155. shearY = GetBezierValue(time, i, SHEARY, curveType + BEZIER_SIZE * 5 - BEZIER);
  2156. break;
  2157. }
  2158. if (blend == MixBlend.Setup) {
  2159. TransformConstraintData data = constraint.data;
  2160. constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
  2161. constraint.mixX = data.mixX + (x - data.mixX) * alpha;
  2162. constraint.mixY = data.mixY + (y - data.mixY) * alpha;
  2163. constraint.mixScaleX = data.mixScaleX + (scaleX - data.mixScaleX) * alpha;
  2164. constraint.mixScaleY = data.mixScaleY + (scaleY - data.mixScaleY) * alpha;
  2165. constraint.mixShearY = data.mixShearY + (shearY - data.mixShearY) * alpha;
  2166. } else {
  2167. constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
  2168. constraint.mixX += (x - constraint.mixX) * alpha;
  2169. constraint.mixY += (y - constraint.mixY) * alpha;
  2170. constraint.mixScaleX += (scaleX - constraint.mixScaleX) * alpha;
  2171. constraint.mixScaleY += (scaleY - constraint.mixScaleY) * alpha;
  2172. constraint.mixShearY += (shearY - constraint.mixShearY) * alpha;
  2173. }
  2174. }
  2175. }
  2176. /// <summary>Changes a path constraint's <see cref="PathConstraint.Position"/>.</summary>
  2177. public class PathConstraintPositionTimeline : CurveTimeline1 {
  2178. readonly int pathConstraintIndex;
  2179. public PathConstraintPositionTimeline (int frameCount, int bezierCount, int pathConstraintIndex)
  2180. : base(frameCount, bezierCount, (int)Property.PathConstraintPosition + "|" + pathConstraintIndex) {
  2181. this.pathConstraintIndex = pathConstraintIndex;
  2182. }
  2183. /// <summary>The index of the path constraint slot in <see cref="Skeleton.PathConstraints"/> that will be changed when this timeline
  2184. /// is applied.</summary>
  2185. public int PathConstraintIndex {
  2186. get {
  2187. return pathConstraintIndex;
  2188. }
  2189. }
  2190. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  2191. MixDirection direction) {
  2192. PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
  2193. if (!constraint.active) return;
  2194. if (time < frames[0]) { // Time is before first frame.
  2195. switch (blend) {
  2196. case MixBlend.Setup:
  2197. constraint.position = constraint.data.position;
  2198. return;
  2199. case MixBlend.First:
  2200. constraint.position += (constraint.data.position - constraint.position) * alpha;
  2201. return;
  2202. }
  2203. return;
  2204. }
  2205. float position = GetCurveValue(time);
  2206. if (blend == MixBlend.Setup)
  2207. constraint.position = constraint.data.position + (position - constraint.data.position) * alpha;
  2208. else
  2209. constraint.position += (position - constraint.position) * alpha;
  2210. }
  2211. }
  2212. /// <summary>Changes a path constraint's <see cref="PathConstraint.Spacing"/>.</summary>
  2213. public class PathConstraintSpacingTimeline : CurveTimeline1 {
  2214. readonly int pathConstraintIndex;
  2215. public PathConstraintSpacingTimeline (int frameCount, int bezierCount, int pathConstraintIndex)
  2216. : base(frameCount, bezierCount, (int)Property.PathConstraintSpacing + "|" + pathConstraintIndex) {
  2217. this.pathConstraintIndex = pathConstraintIndex;
  2218. }
  2219. /// <summary>The index of the path constraint slot in <see cref="Skeleton.PathConstraints"/> that will be changed when this timeline
  2220. /// is applied.</summary>
  2221. public int PathConstraintIndex {
  2222. get {
  2223. return pathConstraintIndex;
  2224. }
  2225. }
  2226. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> events, float alpha, MixBlend blend,
  2227. MixDirection direction) {
  2228. PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
  2229. if (!constraint.active) return;
  2230. float[] frames = this.frames;
  2231. if (time < frames[0]) { // Time is before first frame.
  2232. switch (blend) {
  2233. case MixBlend.Setup:
  2234. constraint.spacing = constraint.data.spacing;
  2235. return;
  2236. case MixBlend.First:
  2237. constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha;
  2238. return;
  2239. }
  2240. return;
  2241. }
  2242. float spacing = GetCurveValue(time);
  2243. if (blend == MixBlend.Setup)
  2244. constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha;
  2245. else
  2246. constraint.spacing += (spacing - constraint.spacing) * alpha;
  2247. }
  2248. }
  2249. /// <summary> Changes a transform constraint's <see cref="PathConstraint.MixRotate"/>, <see cref="PathConstraint.MixX"/>, and
  2250. /// <see cref="PathConstraint.MixY"/>.</summary>
  2251. public class PathConstraintMixTimeline : CurveTimeline {
  2252. public const int ENTRIES = 4;
  2253. private const int ROTATE = 1, X = 2, Y = 3;
  2254. readonly int pathConstraintIndex;
  2255. public PathConstraintMixTimeline (int frameCount, int bezierCount, int pathConstraintIndex)
  2256. : base(frameCount, bezierCount, (int)Property.PathConstraintMix + "|" + pathConstraintIndex) {
  2257. this.pathConstraintIndex = pathConstraintIndex;
  2258. }
  2259. public override int FrameEntries {
  2260. get { return ENTRIES; }
  2261. }
  2262. /// <summary>The index of the path constraint slot in <see cref="Skeleton.PathConstraints"/> that will be changed when this timeline
  2263. /// is applied.</summary>
  2264. public int PathConstraintIndex {
  2265. get {
  2266. return pathConstraintIndex;
  2267. }
  2268. }
  2269. /// <summary>Sets the time and color for the specified frame.</summary>
  2270. /// <param name="frame">Between 0 and <code>frameCount</code>, inclusive.</param>
  2271. /// <param name="time">The frame time in seconds.</param>
  2272. public void SetFrame (int frame, float time, float mixRotate, float mixX, float mixY) {
  2273. frame <<= 2;
  2274. frames[frame] = time;
  2275. frames[frame + ROTATE] = mixRotate;
  2276. frames[frame + X] = mixX;
  2277. frames[frame + Y] = mixY;
  2278. }
  2279. override public void Apply (Skeleton skeleton, float lastTime, float time, ExposedList<Event> firedEvents, float alpha, MixBlend blend,
  2280. MixDirection direction) {
  2281. PathConstraint constraint = skeleton.pathConstraints.Items[pathConstraintIndex];
  2282. if (!constraint.active) return;
  2283. float[] frames = this.frames;
  2284. if (time < frames[0]) { // Time is before first frame.
  2285. switch (blend) {
  2286. case MixBlend.Setup:
  2287. constraint.mixRotate = constraint.data.mixRotate;
  2288. constraint.mixX = constraint.data.mixX;
  2289. constraint.mixY = constraint.data.mixY;
  2290. return;
  2291. case MixBlend.First:
  2292. constraint.mixRotate += (constraint.data.mixRotate - constraint.mixRotate) * alpha;
  2293. constraint.mixX += (constraint.data.mixX - constraint.mixX) * alpha;
  2294. constraint.mixY += (constraint.data.mixY - constraint.mixY) * alpha;
  2295. return;
  2296. }
  2297. return;
  2298. }
  2299. float rotate, x, y;
  2300. int i = Search(frames, time, ENTRIES), curveType = (int)curves[i >> 2];
  2301. switch (curveType) {
  2302. case LINEAR:
  2303. float before = frames[i];
  2304. rotate = frames[i + ROTATE];
  2305. x = frames[i + X];
  2306. y = frames[i + Y];
  2307. float t = (time - before) / (frames[i + ENTRIES] - before);
  2308. rotate += (frames[i + ENTRIES + ROTATE] - rotate) * t;
  2309. x += (frames[i + ENTRIES + X] - x) * t;
  2310. y += (frames[i + ENTRIES + Y] - y) * t;
  2311. break;
  2312. case STEPPED:
  2313. rotate = frames[i + ROTATE];
  2314. x = frames[i + X];
  2315. y = frames[i + Y];
  2316. break;
  2317. default:
  2318. rotate = GetBezierValue(time, i, ROTATE, curveType - BEZIER);
  2319. x = GetBezierValue(time, i, X, curveType + BEZIER_SIZE - BEZIER);
  2320. y = GetBezierValue(time, i, Y, curveType + BEZIER_SIZE * 2 - BEZIER);
  2321. break;
  2322. }
  2323. if (blend == MixBlend.Setup) {
  2324. PathConstraintData data = constraint.data;
  2325. constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
  2326. constraint.mixX = data.mixX + (x - data.mixX) * alpha;
  2327. constraint.mixY = data.mixY + (y - data.mixY) * alpha;
  2328. } else {
  2329. constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
  2330. constraint.mixX += (x - constraint.mixX) * alpha;
  2331. constraint.mixY += (y - constraint.mixY) * alpha;
  2332. }
  2333. }
  2334. }
  2335. }