2
0

Animation.cs 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Runtime.InteropServices;
  6. using System.Text;
  7. namespace BansheeEngine
  8. {
  9. /** @addtogroup Animation
  10. * @{
  11. */
  12. /// <summary>
  13. /// Handles animation playback. Takes one or multiple animation clips as input and evaluates them every animation update
  14. /// tick depending on set properties.The evaluated data is used by the core thread for skeletal animation, by the sim
  15. /// thread for updating attached scene objects and bones (if skeleton is attached), or the data is made available for
  16. /// manual queries in the case of generic animation.
  17. /// </summary>
  18. public class Animation : ManagedComponent
  19. {
  20. private NativeAnimation _native;
  21. [SerializeField] private SerializableData serializableData = new SerializableData();
  22. private FloatCurvePropertyInfo[] floatProperties;
  23. private List<SceneObjectMappingInfo> mappingInfo = new List<SceneObjectMappingInfo>();
  24. private AnimationClip primaryClip;
  25. private Renderable animatedRenderable;
  26. private State state = State.Inactive;
  27. /// <summary>
  28. /// Contains mapping for a suffix used by property paths used for curve identifiers, to their index and type.
  29. /// </summary>
  30. internal static readonly Dictionary<string, PropertySuffixInfo> PropertySuffixInfos = new Dictionary
  31. <string, PropertySuffixInfo>
  32. {
  33. {".x", new PropertySuffixInfo(0, true)},
  34. {".y", new PropertySuffixInfo(1, true)},
  35. {".z", new PropertySuffixInfo(2, true)},
  36. {".w", new PropertySuffixInfo(3, true)},
  37. {".r", new PropertySuffixInfo(0, false)},
  38. {".g", new PropertySuffixInfo(1, false)},
  39. {".b", new PropertySuffixInfo(2, false)},
  40. {".a", new PropertySuffixInfo(3, false)}
  41. };
  42. /// <summary>
  43. /// Returns the non-component version of Animation that is wrapped by this component.
  44. /// </summary>
  45. internal NativeAnimation Native
  46. {
  47. get { return _native; }
  48. }
  49. /// <summary>
  50. /// Determines the default clip to play as soon as the component is enabled. If more control over playing clips is
  51. /// needed use the <see cref="Play"/>, <see cref="Blend1D"/>, <see cref="Blend2D"/> and <see cref="CrossFade"/>
  52. /// methods to queue clips for playback manually, and <see cref="SetState"/> method for modify their states
  53. /// individually.
  54. /// </summary>
  55. public AnimationClip DefaultClip
  56. {
  57. get { return serializableData.defaultClip; }
  58. set
  59. {
  60. serializableData.defaultClip = value;
  61. if (value != null)
  62. {
  63. switch (state)
  64. {
  65. case State.Active:
  66. _native.Play(value);
  67. break;
  68. }
  69. }
  70. }
  71. }
  72. /// <summary>
  73. /// Determines the wrap mode for all active animations. Wrap mode determines what happens when animation reaches the
  74. /// first or last frame.
  75. /// <see cref="AnimWrapMode"/>
  76. /// </summary>
  77. public AnimWrapMode WrapMode
  78. {
  79. get { return serializableData.wrapMode; }
  80. set
  81. {
  82. serializableData.wrapMode = value;
  83. switch (state)
  84. {
  85. case State.Active:
  86. _native.WrapMode = value;
  87. break;
  88. }
  89. }
  90. }
  91. /// <summary>
  92. /// Determines the speed for all animations. The default value is 1.0f. Use negative values to play-back in reverse.
  93. /// </summary>
  94. public float Speed
  95. {
  96. get { return serializableData.speed; }
  97. set
  98. {
  99. serializableData.speed = value;
  100. switch (state)
  101. {
  102. case State.Active:
  103. _native.Speed = value;
  104. break;
  105. }
  106. }
  107. }
  108. /// <summary>
  109. /// Checks if any animation clips are currently playing.
  110. /// </summary>
  111. public bool IsPlaying
  112. {
  113. get
  114. {
  115. switch (state)
  116. {
  117. case State.Active:
  118. return _native.IsPlaying();
  119. default:
  120. return false;
  121. }
  122. }
  123. }
  124. /// <summary>
  125. /// Sets bounds that will be used for animation and mesh culling. Only relevant if <see cref="UseBounds"/> is set
  126. /// to true.
  127. /// </summary>
  128. public AABox Bounds
  129. {
  130. get { return serializableData.bounds; }
  131. set
  132. {
  133. serializableData.bounds = value;
  134. if (serializableData.useBounds)
  135. {
  136. if (animatedRenderable != null && animatedRenderable.Native != null)
  137. animatedRenderable.Native.OverrideBounds = value;
  138. AABox bounds = serializableData.bounds;
  139. Matrix4 parentTfrm;
  140. if (SceneObject.Parent != null)
  141. parentTfrm = SceneObject.Parent.WorldTransform;
  142. else
  143. parentTfrm = Matrix4.Identity;
  144. bounds.TransformAffine(parentTfrm);
  145. switch (state)
  146. {
  147. case State.Active:
  148. _native.Bounds = bounds;
  149. break;
  150. }
  151. }
  152. }
  153. }
  154. /// <summary>
  155. /// Determines should animation bounds be used for visibility determination (culling). If false the bounds of the
  156. /// mesh attached to the relevant <see cref="Renderable"/> component will be used instead.
  157. /// </summary>
  158. public bool UseBounds
  159. {
  160. get { return serializableData.useBounds; }
  161. set
  162. {
  163. serializableData.useBounds = value;
  164. UpdateBounds();
  165. }
  166. }
  167. /// <summary>
  168. /// If true, the animation will not be evaluated when it is out of view.
  169. /// </summary>
  170. public bool Cull
  171. {
  172. get { return serializableData.cull; }
  173. set
  174. {
  175. serializableData.cull = value;
  176. switch (state)
  177. {
  178. case State.Active:
  179. _native.Cull = value;
  180. break;
  181. }
  182. }
  183. }
  184. /// <summary>
  185. /// Plays the specified animation clip.
  186. /// </summary>
  187. /// <param name="clip">Clip to play.</param>
  188. public void Play(AnimationClip clip)
  189. {
  190. switch (state)
  191. {
  192. case State.Active:
  193. _native.Play(clip);
  194. break;
  195. }
  196. }
  197. /// <summary>
  198. /// Plays the specified animation clip on top of the animation currently playing in the main layer. Multiple such
  199. /// clips can be playing at once, as long as you ensure each is given its own layer. Each animation can also have a
  200. /// weight that determines how much it influences the main animation.
  201. /// </summary>
  202. /// <param name="clip">Clip to additively blend. Must contain additive animation curves.</param>
  203. /// <param name="weight">Determines how much of an effect will the blended animation have on the final output.
  204. /// In range [0, 1].</param>
  205. /// <param name="fadeLength">Applies the blend over a specified time period, increasing the weight as the time
  206. /// passes. Set to zero to blend immediately. In seconds.</param>
  207. /// <param name="layer">Layer to play the clip in. Multiple additive clips can be playing at once in separate layers
  208. /// and each layer has its own weight.</param>
  209. public void BlendAdditive(AnimationClip clip, float weight, float fadeLength, int layer)
  210. {
  211. switch (state)
  212. {
  213. case State.Active:
  214. _native.BlendAdditive(clip, weight, fadeLength, layer);
  215. break;
  216. }
  217. }
  218. /// <summary>
  219. /// Blends multiple animation clips between each other using linear interpolation. Unlike normal animations these
  220. /// animations are not advanced with the progress of time, and is instead expected the user manually changes the
  221. /// <see cref="t"/> parameter.
  222. /// </summary>
  223. /// <param name="info">Information about the clips to blend. Clip positions must be sorted from lowest to highest.
  224. /// </param>
  225. /// <param name="t">Parameter that controls the blending, in range [0, 1]. t = 0 means left animation has full
  226. /// influence, t = 1 means right animation has full influence.</param>
  227. public void Blend1D(Blend1DInfo info, float t)
  228. {
  229. switch (state)
  230. {
  231. case State.Active:
  232. _native.Blend1D(info, t);
  233. break;
  234. }
  235. }
  236. /// <summary>
  237. /// Blend four animation clips between each other using bilinear interpolation. Unlike normal animations these
  238. /// animations are not advanced with the progress of time, and is instead expected the user manually changes the
  239. /// <see cref="t"/> parameter.
  240. /// </summary>
  241. /// <param name="info">Information about the clips to blend.</param>
  242. /// <param name="t">Parameter that controls the blending, in range [(0, 0), (1, 1)]. t = (0, 0) means top left
  243. /// animation has full influence, t = (0, 1) means top right animation has full influence,
  244. /// t = (1, 0) means bottom left animation has full influence, t = (1, 1) means bottom right
  245. /// animation has full influence.
  246. /// </param>
  247. public void Blend2D(Blend2DInfo info, Vector2 t)
  248. {
  249. switch (state)
  250. {
  251. case State.Active:
  252. _native.Blend2D(info, t);
  253. break;
  254. }
  255. }
  256. /// <summary>
  257. /// Fades the specified animation clip in, while fading other playing animation out, over the specified time period.
  258. /// </summary>
  259. /// <param name="clip">Clip to fade in.</param>
  260. /// <param name="fadeLength">Determines the time period over which the fade occurs. In seconds.</param>
  261. public void CrossFade(AnimationClip clip, float fadeLength)
  262. {
  263. switch (state)
  264. {
  265. case State.Active:
  266. _native.CrossFade(clip, fadeLength);
  267. break;
  268. }
  269. }
  270. /// <summary>
  271. /// Samples an animation clip at the specified time, displaying only that particular frame without further playback.
  272. /// </summary>
  273. /// <param name="clip">Animation clip to sample.</param>
  274. /// <param name="time">Time to sample the clip at.</param>
  275. public void Sample(AnimationClip clip, float time)
  276. {
  277. switch (state)
  278. {
  279. case State.Active:
  280. case State.EditorActive:
  281. _native.Sample(clip, time);
  282. break;
  283. }
  284. }
  285. /// <summary>
  286. /// Stops playing all animations on the provided layer.
  287. /// </summary>
  288. /// <param name="layer">Layer on which to stop animations on.</param>
  289. public void Stop(int layer)
  290. {
  291. switch (state)
  292. {
  293. case State.Active:
  294. _native.Stop(layer);
  295. break;
  296. }
  297. }
  298. /// <summary>
  299. /// Stops playing all animations.
  300. /// </summary>
  301. public void StopAll()
  302. {
  303. switch (state)
  304. {
  305. case State.Active:
  306. _native.StopAll();
  307. break;
  308. }
  309. }
  310. /// <summary>
  311. /// Retrieves detailed information about a currently playing animation clip.
  312. /// </summary>
  313. /// <param name="clip">Clip to retrieve the information for.</param>
  314. /// <param name="state">Animation clip state containing the requested information. Only valid if the method returns
  315. /// true.</param>
  316. /// <returns>True if the state was found (animation clip is playing), false otherwise.</returns>
  317. public bool GetState(AnimationClip clip, out AnimationClipState state)
  318. {
  319. switch (this.state)
  320. {
  321. case State.Active:
  322. case State.EditorActive:
  323. return _native.GetState(clip, out state);
  324. default:
  325. state = new AnimationClipState();
  326. return false;
  327. }
  328. }
  329. /// <summary>
  330. /// Changes the state of a playing animation clip. If animation clip is not currently playing the state change is
  331. /// ignored.
  332. /// </summary>
  333. /// <param name="clip">Clip to change the state for.</param>
  334. /// <param name="state">New state of the animation (e.g. changing the time for seeking).</param>
  335. public void SetState(AnimationClip clip, AnimationClipState state)
  336. {
  337. switch (this.state)
  338. {
  339. case State.Active:
  340. case State.EditorActive:
  341. _native.SetState(clip, state);
  342. break;
  343. }
  344. }
  345. /// <summary>
  346. /// Changes a weight of a single morph channel, determining how much of it to apply on top of the base mesh.
  347. /// </summary>
  348. /// <param name="name">Name of the morph channel to modify the weight for. This depends on the mesh the animation
  349. /// is currently animating.</param>
  350. /// <param name="weight">Weight that determines how much of the channel to apply to the mesh, in range [0, 1].
  351. /// </param>
  352. public void SetMorphChannelWeight(string name, float weight)
  353. {
  354. switch (state)
  355. {
  356. case State.Active:
  357. if (animatedRenderable == null)
  358. return;
  359. Mesh mesh = animatedRenderable.Mesh;
  360. if (mesh == null)
  361. return;
  362. MorphShapes morphShapes = mesh.MorphShapes;
  363. if (morphShapes == null)
  364. return;
  365. MorphChannel[] channels = morphShapes.Channels;
  366. for (int i = 0; i < channels.Length; i++)
  367. {
  368. if (channels[i].Name == name)
  369. {
  370. _native.SetMorphChannelWeight(i, weight);
  371. break;
  372. }
  373. }
  374. break;
  375. }
  376. }
  377. /// <summary>
  378. /// Allows the caller to play an animation clip during edit mode. This form of animation playback is limited as
  379. /// you have no control over clip properties, and features like blending, cross fade or animation events are not
  380. /// supported.
  381. ///
  382. /// Caller will need to manually call <see cref="UpdateFloatProperties"/> in order to apply evaluated animation data
  383. /// to relevant float properties (if required).
  384. ///
  385. /// Caller will also need to manually call <see cref="RefreshClipMappings"/> whenever the curves internal to the
  386. /// animation clip change. This should be called before the call to <see cref="UpdateFloatProperties"/>.
  387. /// </summary>
  388. /// <param name="clip">Animation clip to play.</param>
  389. /// <param name="startTime">Time to start playing at, in seconds.</param>
  390. /// <param name="freeze">If true, only the frame at the specified time will be shown, without advancing the
  391. /// animation.</param>
  392. internal void EditorPlay(AnimationClip clip, float startTime, bool freeze = false)
  393. {
  394. switch (state)
  395. {
  396. case State.Inactive:
  397. SwitchState(State.EditorActive);
  398. break;
  399. }
  400. switch (state)
  401. {
  402. case State.EditorActive:
  403. if (freeze)
  404. Sample(clip, startTime);
  405. else
  406. {
  407. AnimationClipState clipState = AnimationClipState.Create();
  408. clipState.time = startTime;
  409. SetState(clip, clipState);
  410. }
  411. RefreshClipMappings();
  412. break;
  413. }
  414. }
  415. /// <summary>
  416. /// Stops playback of animation whose playback what started using <see cref="EditorPlay"/>.
  417. /// </summary>
  418. internal void EditorStop()
  419. {
  420. switch (state)
  421. {
  422. case State.EditorActive:
  423. SwitchState(State.Inactive);
  424. break;
  425. }
  426. }
  427. /// <summary>
  428. /// Returns the current time of the currently playing editor animation clip.
  429. /// </summary>
  430. /// <returns>Time in seconds.</returns>
  431. internal float EditorGetTime()
  432. {
  433. switch (state)
  434. {
  435. case State.EditorActive:
  436. AnimationClip clip = _native.GetClip(0);
  437. AnimationClipState clipState;
  438. if (clip != null && GetState(clip, out clipState))
  439. return clipState.time;
  440. return 0.0f;
  441. }
  442. return 0.0f;
  443. }
  444. /// <summary>
  445. /// Rebuilds internal curve -> property mapping about the currently playing animation clip. This mapping allows the
  446. /// animation component to know which property to assign which values from an animation curve. This Should be called
  447. /// whenever playback for a new clip starts, or when clip curves change.
  448. /// </summary>
  449. internal void RefreshClipMappings()
  450. {
  451. primaryClip = _native.GetClip(0);
  452. RebuildFloatProperties(primaryClip);
  453. UpdateSceneObjectMapping();
  454. }
  455. /// <summary>
  456. /// Updates generic float properties on relevant objects, based on the most recently evaluated animation curve
  457. /// values.
  458. /// </summary>
  459. internal void UpdateFloatProperties()
  460. {
  461. // Apply values from generic float curves
  462. if (floatProperties != null)
  463. {
  464. foreach (var entry in floatProperties)
  465. {
  466. float curveValue;
  467. if (_native.GetGenericCurveValue(entry.curveIdx, out curveValue))
  468. entry.setter(curveValue);
  469. }
  470. }
  471. }
  472. /// <summary>
  473. /// Searches the scene object hierarchy to find a property at the given path.
  474. /// </summary>
  475. /// <param name="root">Root scene object to which the path is relative to.</param>
  476. /// <param name="path">Path to the property, where each element of the path is separated with "/".
  477. ///
  478. /// Path elements prefixed with "!" signify names of child scene objects (first one relative to
  479. /// <paramref name="root"/>. Name of the root element should not be included in the path.
  480. ///
  481. /// Path element prefixed with ":" signify names of components. If a path doesn't have a
  482. /// component element, it is assumed the field is relative to the scene object itself (only
  483. /// "Translation", "Rotation" and "Scale" fields are supported in such case). Only one component
  484. /// path element per path is allowed.
  485. ///
  486. /// Path entries with no prefix are considered regular script object fields. Each path must have
  487. /// at least one such entry.
  488. ///
  489. /// A field path can be followed by an indexer [n] where n is a zero-based index. Such paths
  490. /// are assumed to be referencing an index within an array or a list.
  491. ///
  492. /// A field path can also be followed by a suffix (after the indexer, if any) separated from the
  493. /// path name with ".". This suffix is not parsed internally, but will be returned as
  494. /// <paramref name="suffix"/>.
  495. ///
  496. /// Path examples:
  497. /// :MyComponent/myInt (path to myInt variable on a component attached to this object)
  498. /// :MyComponent/myArray[0] (path to first element of myArray on the same component)
  499. /// !childSO/:MyComponent/myInt (path to myInt variable on a child scene object)
  500. /// !childSO/Translation (path to the scene object translation)
  501. /// :MyComponent/myVector.z (path to the z component of myVector on this object)
  502. /// </param>
  503. /// <param name="suffix">Suffix of the last field entry, if it has any. Contains the suffix separator ".".</param>
  504. /// <returns>If found, property object you can use for setting and getting the value from the property, otherwise
  505. /// null.</returns>
  506. internal static SerializableProperty FindProperty(SceneObject root, string path, out string suffix)
  507. {
  508. suffix = null;
  509. if (string.IsNullOrEmpty(path) || root == null)
  510. return null;
  511. string trimmedPath = path.Trim('/');
  512. string[] entries = trimmedPath.Split('/');
  513. // Find scene object referenced by the path
  514. SceneObject so = root;
  515. int pathIdx = 0;
  516. for (; pathIdx < entries.Length; pathIdx++)
  517. {
  518. string entry = entries[pathIdx];
  519. if (string.IsNullOrEmpty(entry))
  520. continue;
  521. // Not a scene object, break
  522. if (entry[0] != '!')
  523. break;
  524. string childName = entry.Substring(1, entry.Length - 1);
  525. so = so.FindChild(childName);
  526. if (so == null)
  527. break;
  528. }
  529. // Child scene object couldn't be found
  530. if (so == null)
  531. return null;
  532. // Path too short, no field entry
  533. if (pathIdx >= entries.Length)
  534. return null;
  535. // Check if path is referencing a component, and if so find it
  536. Component component = null;
  537. {
  538. string entry = entries[pathIdx];
  539. if (entry[0] == ':')
  540. {
  541. string componentName = entry.Substring(1, entry.Length - 1);
  542. Component[] components = so.GetComponents();
  543. component = Array.Find(components, x => x.GetType().Name == componentName);
  544. // Cannot find component with specified type
  545. if (component == null)
  546. return null;
  547. }
  548. }
  549. // Look for a field within a component
  550. if (component != null)
  551. {
  552. pathIdx++;
  553. if (pathIdx >= entries.Length)
  554. return null;
  555. SerializableObject componentObj = new SerializableObject(component);
  556. StringBuilder pathBuilder = new StringBuilder();
  557. for (; pathIdx < entries.Length - 1; pathIdx++)
  558. pathBuilder.Append(entries[pathIdx] + "/");
  559. // Check last path entry for suffix and remove it
  560. int suffixIdx = entries[pathIdx].LastIndexOf(".");
  561. if (suffixIdx != -1)
  562. {
  563. string entryNoSuffix = entries[pathIdx].Substring(0, suffixIdx);
  564. suffix = entries[pathIdx].Substring(suffixIdx, entries[pathIdx].Length - suffixIdx);
  565. pathBuilder.Append(entryNoSuffix);
  566. }
  567. else
  568. pathBuilder.Append(entries[pathIdx]);
  569. return componentObj.FindProperty(pathBuilder.ToString());
  570. }
  571. else // Field is one of the builtin ones on the SceneObject itself
  572. {
  573. if ((pathIdx + 1) < entries.Length)
  574. return null;
  575. string entry = entries[pathIdx];
  576. if (entry == "Position")
  577. {
  578. SerializableProperty property = new SerializableProperty(
  579. SerializableProperty.FieldType.Vector3,
  580. typeof(Vector3),
  581. () => so.LocalPosition,
  582. (x) => so.LocalPosition = (Vector3)x);
  583. return property;
  584. }
  585. else if (entry == "Rotation")
  586. {
  587. SerializableProperty property = new SerializableProperty(
  588. SerializableProperty.FieldType.Vector3,
  589. typeof(Vector3),
  590. () => so.LocalRotation.ToEuler(),
  591. (x) => so.LocalRotation = Quaternion.FromEuler((Vector3)x));
  592. return property;
  593. }
  594. else if (entry == "Scale")
  595. {
  596. SerializableProperty property = new SerializableProperty(
  597. SerializableProperty.FieldType.Vector3,
  598. typeof(Vector3),
  599. () => so.LocalScale,
  600. (x) => so.LocalScale = (Vector3)x);
  601. return property;
  602. }
  603. return null;
  604. }
  605. }
  606. /// <summary>
  607. /// Searches the scene object hierarchy to find a child scene object using the provided path.
  608. /// </summary>
  609. /// <param name="root">Root scene object to which the path is relative to.</param>
  610. /// <param name="path">Path to the property, where each element of the path is separated with "/".
  611. ///
  612. /// Path elements signify names of child scene objects (first one relative to
  613. /// <paramref name="root"/>. Name of the root element should not be included in the path.
  614. /// Elements must be prefixed with "!" in order to match the path format of
  615. /// <see cref="FindProperty"/>.</param>
  616. /// <returns>Child scene object if found, or null otherwise.</returns>
  617. internal static SceneObject FindSceneObject(SceneObject root, string path)
  618. {
  619. if (string.IsNullOrEmpty(path) || root == null)
  620. return null;
  621. string trimmedPath = path.Trim('/');
  622. string[] entries = trimmedPath.Split('/');
  623. // Find scene object referenced by the path
  624. SceneObject so = root;
  625. int pathIdx = 0;
  626. for (; pathIdx < entries.Length; pathIdx++)
  627. {
  628. string entry = entries[pathIdx];
  629. if (string.IsNullOrEmpty(entry))
  630. continue;
  631. // Not a scene object, break
  632. if (entry[0] != '!')
  633. break;
  634. string childName = entry.Substring(1, entry.Length - 1);
  635. so = so.FindChild(childName);
  636. if (so == null)
  637. break;
  638. }
  639. return so;
  640. }
  641. private void OnInitialize()
  642. {
  643. NotifyFlags = TransformChangedFlags.Transform;
  644. }
  645. private void OnEnable()
  646. {
  647. switch (state)
  648. {
  649. case State.Inactive:
  650. SwitchState(State.Active);
  651. break;
  652. case State.EditorActive:
  653. SwitchState(State.Inactive);
  654. SwitchState(State.Active);
  655. break;
  656. }
  657. }
  658. private void OnDisable()
  659. {
  660. switch (state)
  661. {
  662. case State.Active:
  663. case State.EditorActive:
  664. SwitchState(State.Inactive);
  665. break;
  666. }
  667. }
  668. private void OnDestroy()
  669. {
  670. switch (state)
  671. {
  672. case State.Active:
  673. case State.EditorActive:
  674. SwitchState(State.Inactive);
  675. break;
  676. }
  677. }
  678. private void OnUpdate()
  679. {
  680. switch (state)
  681. {
  682. case State.Active:
  683. AnimationClip newPrimaryClip = _native.GetClip(0);
  684. if (newPrimaryClip != primaryClip)
  685. RefreshClipMappings();
  686. UpdateFloatProperties();
  687. break;
  688. }
  689. }
  690. private void OnTransformChanged(TransformChangedFlags flags)
  691. {
  692. if (!SceneObject.Active)
  693. return;
  694. if ((flags & (TransformChangedFlags.Transform)) != 0)
  695. UpdateBounds(false);
  696. }
  697. /// <summary>
  698. /// Changes the state of the animation state machine. Doesn't check for valid transitions.
  699. /// </summary>
  700. /// <param name="state">New state of the animation.</param>
  701. private void SwitchState(State state)
  702. {
  703. this.state = state;
  704. switch (state)
  705. {
  706. case State.Active:
  707. if (_native != null)
  708. _native.Destroy();
  709. _native = new NativeAnimation();
  710. _native.OnEventTriggered += EventTriggered;
  711. animatedRenderable = SceneObject.GetComponent<Renderable>();
  712. // Restore saved values after reset
  713. _native.WrapMode = serializableData.wrapMode;
  714. _native.Speed = serializableData.speed;
  715. _native.Cull = serializableData.cull;
  716. UpdateBounds();
  717. if (serializableData.defaultClip != null)
  718. _native.Play(serializableData.defaultClip);
  719. primaryClip = _native.GetClip(0);
  720. if (primaryClip != null)
  721. RebuildFloatProperties(primaryClip);
  722. SetBoneMappings();
  723. UpdateSceneObjectMapping();
  724. if (animatedRenderable != null)
  725. animatedRenderable.RegisterAnimation(this);
  726. break;
  727. case State.EditorActive:
  728. if (_native != null)
  729. _native.Destroy();
  730. _native = new NativeAnimation();
  731. animatedRenderable = SceneObject.GetComponent<Renderable>();
  732. UpdateBounds();
  733. SetBoneMappings();
  734. if (animatedRenderable != null)
  735. animatedRenderable.RegisterAnimation(this);
  736. break;
  737. case State.Inactive:
  738. if (animatedRenderable != null)
  739. animatedRenderable.UnregisterAnimation();
  740. if (_native != null)
  741. {
  742. _native.Destroy();
  743. _native = null;
  744. }
  745. primaryClip = null;
  746. mappingInfo.Clear();
  747. floatProperties = null;
  748. break;
  749. }
  750. }
  751. /// <summary>
  752. /// Finds any curves that affect a transform of a specific scene object, and ensures that animation properly updates
  753. /// those transforms. This does not include curves referencing bones.
  754. /// </summary>
  755. private void UpdateSceneObjectMapping()
  756. {
  757. List<SceneObjectMappingInfo> newMappingInfos = new List<SceneObjectMappingInfo>();
  758. foreach(var entry in mappingInfo)
  759. {
  760. if (entry.isMappedToBone)
  761. newMappingInfos.Add(entry);
  762. else
  763. _native.UnmapSceneObject(entry.sceneObject);
  764. }
  765. if (primaryClip != null)
  766. {
  767. SceneObject root = SceneObject;
  768. Action<string,AnimationCurveFlags> findMappings = (name, flags) =>
  769. {
  770. if (flags.HasFlag(AnimationCurveFlags.ImportedCurve))
  771. return;
  772. SceneObject currentSO = FindSceneObject(root, name);
  773. bool found = false;
  774. for (int i = 0; i < newMappingInfos.Count; i++)
  775. {
  776. if (newMappingInfos[i].sceneObject == currentSO)
  777. {
  778. found = true;
  779. break;
  780. }
  781. }
  782. if (!found)
  783. {
  784. SceneObjectMappingInfo newMappingInfo = new SceneObjectMappingInfo();
  785. newMappingInfo.isMappedToBone = false;
  786. newMappingInfo.sceneObject = currentSO;
  787. newMappingInfos.Add(newMappingInfo);
  788. _native.MapCurveToSceneObject(name, currentSO);
  789. }
  790. };
  791. AnimationCurves curves = primaryClip.Curves;
  792. NamedVector3Curve[] posCurves = curves.Position;
  793. foreach (var curve in posCurves)
  794. findMappings(curve.name, curve.flags);
  795. NamedQuaternionCurve[] rotCurves = curves.Rotation;
  796. foreach (var curve in rotCurves)
  797. findMappings(curve.name, curve.flags);
  798. NamedVector3Curve[] scaleCurves = curves.Scale;
  799. foreach (var curve in scaleCurves)
  800. findMappings(curve.name, curve.flags);
  801. }
  802. mappingInfo = newMappingInfos;
  803. }
  804. /// <summary>
  805. /// Registers a new bone component, creating a new transform mapping from the bone name to the scene object
  806. /// the component is attached to.
  807. /// </summary>
  808. /// <param name="bone">Bone component to register.</param>
  809. internal void AddBone(Bone bone)
  810. {
  811. switch (state)
  812. {
  813. case State.Active:
  814. SceneObject currentSO = bone.SceneObject;
  815. SceneObjectMappingInfo newMapping = new SceneObjectMappingInfo();
  816. newMapping.sceneObject = currentSO;
  817. newMapping.isMappedToBone = true;
  818. newMapping.bone = bone;
  819. mappingInfo.Add(newMapping);
  820. _native.MapCurveToSceneObject(bone.Name, newMapping.sceneObject);
  821. break;
  822. }
  823. }
  824. /// <summary>
  825. /// Unregisters a bone component, removing the bone -> scene object mapping.
  826. /// </summary>
  827. /// <param name="bone">Bone to unregister.</param>
  828. internal void RemoveBone(Bone bone)
  829. {
  830. switch (state)
  831. {
  832. case State.Active:
  833. for (int i = 0; i < mappingInfo.Count; i++)
  834. {
  835. if (mappingInfo[i].bone == bone)
  836. {
  837. _native.UnmapSceneObject(mappingInfo[i].sceneObject);
  838. mappingInfo.RemoveAt(i);
  839. i--;
  840. }
  841. }
  842. break;
  843. }
  844. }
  845. /// <summary>
  846. /// Called whenever the bone the <see cref="Bone"/> component points to changed.
  847. /// </summary>
  848. /// <param name="bone">Bone component to modify.</param>
  849. internal void NotifyBoneChanged(Bone bone)
  850. {
  851. switch (state)
  852. {
  853. case State.Active:
  854. for (int i = 0; i < mappingInfo.Count; i++)
  855. {
  856. if (mappingInfo[i].bone == bone)
  857. {
  858. _native.UnmapSceneObject(mappingInfo[i].sceneObject);
  859. _native.MapCurveToSceneObject(bone.Name, mappingInfo[i].sceneObject);
  860. break;
  861. }
  862. }
  863. break;
  864. }
  865. }
  866. /// <summary>
  867. /// Finds any scene objects that are mapped to bone transforms. Such object's transforms will be affected by
  868. /// skeleton bone animation.
  869. /// </summary>
  870. private void SetBoneMappings()
  871. {
  872. mappingInfo.Clear();
  873. SceneObjectMappingInfo rootMapping = new SceneObjectMappingInfo();
  874. rootMapping.sceneObject = SceneObject;
  875. rootMapping.isMappedToBone = true;
  876. mappingInfo.Add(rootMapping);
  877. _native.MapCurveToSceneObject("", rootMapping.sceneObject);
  878. Bone[] childBones = FindChildBones();
  879. foreach (var entry in childBones)
  880. AddBone(entry);
  881. }
  882. /// <summary>
  883. /// Searches child scene objects for <see cref="Bone"/> components and returns any found ones.
  884. /// </summary>
  885. private Bone[] FindChildBones()
  886. {
  887. Stack<SceneObject> todo = new Stack<SceneObject>();
  888. todo.Push(SceneObject);
  889. List<Bone> bones = new List<Bone>();
  890. while (todo.Count > 0)
  891. {
  892. SceneObject currentSO = todo.Pop();
  893. Bone bone = currentSO.GetComponent<Bone>();
  894. if (bone != null)
  895. {
  896. bone.SetParent(this, true);
  897. bones.Add(bone);
  898. }
  899. int childCount = currentSO.GetNumChildren();
  900. for (int i = 0; i < childCount; i++)
  901. {
  902. SceneObject child = currentSO.GetChild(i);
  903. if (child.GetComponent<Animation>() != null)
  904. continue;
  905. todo.Push(child);
  906. }
  907. }
  908. return bones.ToArray();
  909. }
  910. /// <summary>
  911. /// Re-applies the bounds to the internal animation object, and the relevant renderable object if one exists.
  912. /// </summary>
  913. internal void UpdateBounds(bool updateRenderable = true)
  914. {
  915. NativeRenderable renderable = null;
  916. if (updateRenderable && animatedRenderable != null)
  917. renderable = animatedRenderable.Native;
  918. if (serializableData.useBounds)
  919. {
  920. if (renderable != null)
  921. {
  922. renderable.UseOverrideBounds = true;
  923. renderable.OverrideBounds = serializableData.bounds;
  924. }
  925. if (_native != null)
  926. {
  927. AABox bounds = serializableData.bounds;
  928. Matrix4 parentTfrm;
  929. if (SceneObject.Parent != null)
  930. parentTfrm = SceneObject.Parent.WorldTransform;
  931. else
  932. parentTfrm = Matrix4.Identity;
  933. bounds.TransformAffine(parentTfrm);
  934. _native.Bounds = bounds;
  935. }
  936. }
  937. else
  938. {
  939. if (renderable != null)
  940. renderable.UseOverrideBounds = false;
  941. if (_native != null)
  942. {
  943. AABox bounds = new AABox();
  944. if (animatedRenderable != null)
  945. bounds = animatedRenderable.Bounds.Box;
  946. _native.Bounds = bounds;
  947. }
  948. }
  949. }
  950. /// <summary>
  951. /// Registers an <see cref="Renderable"/> component with the animation. When registered the animation will use the
  952. /// renderable's bounds for culling, and the animation override bounds will be applied to the renderable.
  953. /// </summary>
  954. /// <param name="renderable">Component that was added</param>
  955. internal void RegisterRenderable(Renderable renderable)
  956. {
  957. animatedRenderable = renderable;
  958. UpdateBounds();
  959. }
  960. /// <summary>
  961. /// Removes renderable from the animation component. <see cref="RegisterRenderable"/>.
  962. /// </summary>
  963. internal void UnregisterRenderable()
  964. {
  965. animatedRenderable = null;
  966. }
  967. /// <summary>
  968. /// Builds a list of properties that will be animated using float animation curves.
  969. /// </summary>
  970. /// <param name="clip">Clip to retrieve the float animation curves from.</param>
  971. private void RebuildFloatProperties(AnimationClip clip)
  972. {
  973. if (clip == null)
  974. {
  975. floatProperties = null;
  976. return;
  977. }
  978. AnimationCurves curves = clip.Curves;
  979. List<FloatCurvePropertyInfo> newFloatProperties = new List<FloatCurvePropertyInfo>();
  980. for (int i = 0; i < curves.Generic.Length; i++)
  981. {
  982. bool isMorphCurve = curves.Generic[i].flags.HasFlag(AnimationCurveFlags.MorphWeight) ||
  983. curves.Generic[i].flags.HasFlag(AnimationCurveFlags.MorphFrame);
  984. if (isMorphCurve)
  985. continue;
  986. string suffix;
  987. SerializableProperty property = FindProperty(SceneObject, curves.Generic[i].name, out suffix);
  988. if (property == null)
  989. continue;
  990. int elementIdx = 0;
  991. if (!string.IsNullOrEmpty(suffix))
  992. {
  993. PropertySuffixInfo suffixInfo;
  994. if (PropertySuffixInfos.TryGetValue(suffix, out suffixInfo))
  995. elementIdx = suffixInfo.elementIdx;
  996. }
  997. Action<float> setter = null;
  998. Type internalType = property.InternalType;
  999. switch (property.Type)
  1000. {
  1001. case SerializableProperty.FieldType.Vector2:
  1002. if (internalType == typeof(Vector2))
  1003. {
  1004. setter = f =>
  1005. {
  1006. Vector2 value = property.GetValue<Vector2>();
  1007. value[elementIdx] = f;
  1008. property.SetValue(value);
  1009. };
  1010. }
  1011. break;
  1012. case SerializableProperty.FieldType.Vector3:
  1013. if (internalType == typeof(Vector3))
  1014. {
  1015. setter = f =>
  1016. {
  1017. Vector3 value = property.GetValue<Vector3>();
  1018. value[elementIdx] = f;
  1019. property.SetValue(value);
  1020. };
  1021. }
  1022. break;
  1023. case SerializableProperty.FieldType.Vector4:
  1024. if (internalType == typeof(Vector4))
  1025. {
  1026. setter = f =>
  1027. {
  1028. Vector4 value = property.GetValue<Vector4>();
  1029. value[elementIdx] = f;
  1030. property.SetValue(value);
  1031. };
  1032. }
  1033. else if (internalType == typeof(Quaternion))
  1034. {
  1035. setter = f =>
  1036. {
  1037. Quaternion value = property.GetValue<Quaternion>();
  1038. value[elementIdx] = f;
  1039. property.SetValue(value);
  1040. };
  1041. }
  1042. break;
  1043. case SerializableProperty.FieldType.Color:
  1044. if (internalType == typeof(Color))
  1045. {
  1046. setter = f =>
  1047. {
  1048. Color value = property.GetValue<Color>();
  1049. value[elementIdx] = f;
  1050. property.SetValue(value);
  1051. };
  1052. }
  1053. break;
  1054. case SerializableProperty.FieldType.Bool:
  1055. setter = f =>
  1056. {
  1057. bool value = f > 0.0f;
  1058. property.SetValue(value);
  1059. };
  1060. break;
  1061. case SerializableProperty.FieldType.Int:
  1062. setter = f =>
  1063. {
  1064. int value = (int)f;
  1065. property.SetValue(value);
  1066. };
  1067. break;
  1068. case SerializableProperty.FieldType.Float:
  1069. setter = f =>
  1070. {
  1071. property.SetValue(f);
  1072. };
  1073. break;
  1074. }
  1075. if (setter == null)
  1076. continue;
  1077. FloatCurvePropertyInfo propertyInfo = new FloatCurvePropertyInfo();
  1078. propertyInfo.curveIdx = i;
  1079. propertyInfo.setter = setter;
  1080. newFloatProperties.Add(propertyInfo);
  1081. }
  1082. floatProperties = newFloatProperties.ToArray();
  1083. }
  1084. /// <summary>
  1085. /// Called whenever an animation event triggers.
  1086. /// </summary>
  1087. /// <param name="clip">Clip that the event originated from.</param>
  1088. /// <param name="name">Name of the event.</param>
  1089. private void EventTriggered(AnimationClip clip, string name)
  1090. {
  1091. // Event should be in format "ComponentType/MethodName"
  1092. if (string.IsNullOrEmpty(name))
  1093. return;
  1094. string[] nameEntries = name.Split('/');
  1095. if (nameEntries.Length != 2)
  1096. return;
  1097. string typeName = nameEntries[0];
  1098. string methodName = nameEntries[1];
  1099. Component[] components = SceneObject.GetComponents();
  1100. for (int i = 0; i < components.Length; i++)
  1101. {
  1102. if (components[i].GetType().Name == typeName)
  1103. {
  1104. components[i].Invoke(methodName);
  1105. break;
  1106. }
  1107. }
  1108. }
  1109. /// <summary>
  1110. /// Holds all data the animation component needs to persist through serialization.
  1111. /// </summary>
  1112. [SerializeObject]
  1113. private class SerializableData
  1114. {
  1115. public AnimationClip defaultClip;
  1116. public AnimWrapMode wrapMode = AnimWrapMode.Loop;
  1117. public float speed = 1.0f;
  1118. public AABox bounds;
  1119. public bool useBounds;
  1120. public bool cull = true;
  1121. }
  1122. /// <summary>
  1123. /// Contains information about a property animated by a generic animation curve.
  1124. /// </summary>
  1125. private class FloatCurvePropertyInfo
  1126. {
  1127. public int curveIdx;
  1128. public Action<float> setter;
  1129. }
  1130. /// <summary>
  1131. /// Information about a suffix used in a property path.
  1132. /// </summary>
  1133. internal struct PropertySuffixInfo
  1134. {
  1135. public PropertySuffixInfo(int elementIdx, bool isVector)
  1136. {
  1137. this.elementIdx = elementIdx;
  1138. this.isVector = isVector;
  1139. }
  1140. public int elementIdx;
  1141. public bool isVector;
  1142. }
  1143. /// <summary>
  1144. /// Information about scene objects bound to a specific animation curve.
  1145. /// </summary>
  1146. internal struct SceneObjectMappingInfo
  1147. {
  1148. public SceneObject sceneObject;
  1149. public bool isMappedToBone;
  1150. public Bone bone;
  1151. }
  1152. /// <summary>
  1153. /// Possible states the animation component can be in.
  1154. /// </summary>
  1155. private enum State
  1156. {
  1157. /// <summary>
  1158. /// Animation object isn't constructed.
  1159. /// </summary>
  1160. Inactive,
  1161. /// <summary>
  1162. /// Animation object is constructed and fully functional.
  1163. /// </summary>
  1164. Active,
  1165. /// <summary>
  1166. /// Animation object is constructed and functional with limited funcionality for editor purposes.
  1167. /// </summary>
  1168. EditorActive
  1169. }
  1170. }
  1171. /// <summary>
  1172. /// Determines how an animation clip behaves when it reaches the end.
  1173. /// </summary>
  1174. public enum AnimWrapMode // Note: Must match C++ enum AnimWrapMode
  1175. {
  1176. /// <summary>
  1177. /// Loop around to the beginning/end when the last/first frame is reached.
  1178. /// </summary>
  1179. Loop,
  1180. /// <summary>
  1181. /// Clamp to end/beginning, keeping the last/first frame active.
  1182. /// </summary>
  1183. Clamp
  1184. }
  1185. /// <summary>
  1186. /// Represents an animation clip used in 1D blending. Each clip has a position on the number line.
  1187. /// </summary>
  1188. public class BlendClipInfo
  1189. {
  1190. public AnimationClip clip;
  1191. public float position;
  1192. }
  1193. /// <summary>
  1194. /// Defines a 1D blend where two animation clips are blended between each other using linear interpolation.
  1195. /// </summary>
  1196. public class Blend1DInfo
  1197. {
  1198. public Blend1DInfo()
  1199. { }
  1200. public Blend1DInfo(int numClips)
  1201. {
  1202. clips = new BlendClipInfo[numClips];
  1203. }
  1204. public BlendClipInfo[] clips;
  1205. }
  1206. /// <summary>
  1207. /// Defines a 2D blend where two animation clips are blended between each other using bilinear interpolation.
  1208. /// </summary>
  1209. public class Blend2DInfo
  1210. {
  1211. public AnimationClip topLeftClip;
  1212. public AnimationClip topRightClip;
  1213. public AnimationClip botLeftClip;
  1214. public AnimationClip botRightClip;
  1215. }
  1216. /// <summary>
  1217. /// Contains information about a currently playing animation clip.
  1218. /// </summary>
  1219. [StructLayout(LayoutKind.Sequential), SerializeObject]
  1220. public struct AnimationClipState // Note: Must match C++ struct AnimationClipState
  1221. {
  1222. /// <summary>
  1223. /// Layer the clip is playing on. Multiple clips can be played simulatenously on different layers.
  1224. /// </summary>
  1225. public int layer;
  1226. /// <summary>
  1227. /// Current time the animation is playing from.
  1228. /// </summary>
  1229. public float time;
  1230. /// <summary>
  1231. /// Speed at which the animation is playing.
  1232. /// </summary>
  1233. public float speed;
  1234. /// <summary>
  1235. /// Determines how much of an influence does the clip have on the final pose.
  1236. /// </summary>
  1237. public float weight;
  1238. /// <summary>
  1239. /// Determines what happens to other animation clips when a new clip starts playing.
  1240. /// </summary>
  1241. public AnimWrapMode wrapMode;
  1242. /// <summary>
  1243. /// Creates a new clip state, with default values initialized.
  1244. /// </summary>
  1245. /// <returns>New animation clip state.</returns>
  1246. public static AnimationClipState Create()
  1247. {
  1248. AnimationClipState state = new AnimationClipState();
  1249. state.speed = 1.0f;
  1250. state.weight = 1.0f;
  1251. state.wrapMode = AnimWrapMode.Loop;
  1252. return state;
  1253. }
  1254. }
  1255. /** @} */
  1256. }