Animation.cs 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122
  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 : Component
  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. /// <summary>
  27. /// Contains mapping for a suffix used by property paths used for curve identifiers, to their index and type.
  28. /// </summary>
  29. internal static readonly Dictionary<string, PropertySuffixInfo> PropertySuffixInfos = new Dictionary
  30. <string, PropertySuffixInfo>
  31. {
  32. {".x", new PropertySuffixInfo(0, true)},
  33. {".y", new PropertySuffixInfo(1, true)},
  34. {".z", new PropertySuffixInfo(2, true)},
  35. {".w", new PropertySuffixInfo(3, true)},
  36. {".r", new PropertySuffixInfo(0, false)},
  37. {".g", new PropertySuffixInfo(1, false)},
  38. {".b", new PropertySuffixInfo(2, false)},
  39. {".a", new PropertySuffixInfo(3, false)}
  40. };
  41. /// <summary>
  42. /// Returns the non-component version of Animation that is wrapped by this component.
  43. /// </summary>
  44. internal NativeAnimation Native
  45. {
  46. get { return _native; }
  47. }
  48. /// <summary>
  49. /// Determines the default clip to play as soon as the component is enabled. If more control over playing clips is
  50. /// needed use the <see cref="Play"/>, <see cref="Blend1D"/>, <see cref="Blend2D"/> and <see cref="CrossFade"/>
  51. /// methods to queue clips for playback manually, and <see cref="SetState"/> method for modify their states
  52. /// individually.
  53. /// </summary>
  54. public AnimationClip DefaultClip
  55. {
  56. get { return serializableData.defaultClip; }
  57. set
  58. {
  59. serializableData.defaultClip = value;
  60. if (value != null && _native != null)
  61. _native.Play(value);
  62. }
  63. }
  64. /// <summary>
  65. /// Determines the wrap mode for all active animations. Wrap mode determines what happens when animation reaches the
  66. /// first or last frame.
  67. /// <see cref="AnimWrapMode"/>
  68. /// </summary>
  69. public AnimWrapMode WrapMode
  70. {
  71. get { return serializableData.wrapMode; }
  72. set
  73. {
  74. serializableData.wrapMode = value;
  75. if (_native != null)
  76. _native.WrapMode = value;
  77. }
  78. }
  79. /// <summary>
  80. /// Determines the speed for all animations. The default value is 1.0f. Use negative values to play-back in reverse.
  81. /// </summary>
  82. public float Speed
  83. {
  84. get { return serializableData.speed; }
  85. set
  86. {
  87. serializableData.speed = value;
  88. if (_native != null)
  89. _native.Speed = value;
  90. }
  91. }
  92. /// <summary>
  93. /// Checks if any animation clips are currently playing.
  94. /// </summary>
  95. public bool IsPlaying
  96. {
  97. get
  98. {
  99. if (_native != null)
  100. return _native.IsPlaying();
  101. return false;
  102. }
  103. }
  104. /// <summary>
  105. /// Sets bounds that will be used for animation and mesh culling. Only relevant if <see cref="UseBounds"/> is set
  106. /// to true.
  107. /// </summary>
  108. public AABox Bounds
  109. {
  110. get { return serializableData.bounds; }
  111. set
  112. {
  113. serializableData.bounds = value;
  114. if (serializableData.useBounds)
  115. {
  116. if (animatedRenderable != null && animatedRenderable.Native != null)
  117. animatedRenderable.Native.OverrideBounds = value;
  118. if (_native != null)
  119. {
  120. AABox bounds = serializableData.bounds;
  121. bounds.TransformAffine(SceneObject.WorldTransform);
  122. _native.Bounds = bounds;
  123. }
  124. }
  125. }
  126. }
  127. /// <summary>
  128. /// Determines should animation bounds be used for visibility determination (culling). If false the bounds of the
  129. /// mesh attached to the relevant <see cref="Renderable"/> component will be used instead.
  130. /// </summary>
  131. public bool UseBounds
  132. {
  133. get { return serializableData.useBounds; }
  134. set
  135. {
  136. serializableData.useBounds = value;
  137. UpdateBounds();
  138. }
  139. }
  140. /// <summary>
  141. /// If true, the animation will not be evaluated when it is out of view.
  142. /// </summary>
  143. public bool Cull
  144. {
  145. get { return serializableData.cull; }
  146. set
  147. {
  148. serializableData.cull = value;
  149. if (_native != null)
  150. _native.Cull = value;
  151. }
  152. }
  153. /// <summary>
  154. /// Plays the specified animation clip.
  155. /// </summary>
  156. /// <param name="clip">Clip to play.</param>
  157. public void Play(AnimationClip clip)
  158. {
  159. if (_native != null)
  160. _native.Play(clip);
  161. }
  162. /// <summary>
  163. /// Plays the specified animation clip on top of the animation currently playing in the main layer. Multiple such
  164. /// clips can be playing at once, as long as you ensure each is given its own layer. Each animation can also have a
  165. /// weight that determines how much it influences the main animation.
  166. /// </summary>
  167. /// <param name="clip">Clip to additively blend. Must contain additive animation curves.</param>
  168. /// <param name="weight">Determines how much of an effect will the blended animation have on the final output.
  169. /// In range [0, 1].</param>
  170. /// <param name="fadeLength">Applies the blend over a specified time period, increasing the weight as the time
  171. /// passes. Set to zero to blend immediately. In seconds.</param>
  172. /// <param name="layer">Layer to play the clip in. Multiple additive clips can be playing at once in separate layers
  173. /// and each layer has its own weight.</param>
  174. public void BlendAdditive(AnimationClip clip, float weight, float fadeLength, int layer)
  175. {
  176. if (_native != null)
  177. _native.BlendAdditive(clip, weight, fadeLength, layer);
  178. }
  179. /// <summary>
  180. /// Blends multiple animation clips between each other using linear interpolation. Unlike normal animations these
  181. /// animations are not advanced with the progress of time, and is instead expected the user manually changes the
  182. /// <see cref="t"/> parameter.
  183. /// </summary>
  184. /// <param name="info">Information about the clips to blend. Clip positions must be sorted from lowest to highest.
  185. /// </param>
  186. /// <param name="t">Parameter that controls the blending, in range [0, 1]. t = 0 means left animation has full
  187. /// influence, t = 1 means right animation has full influence.</param>
  188. public void Blend1D(Blend1DInfo info, float t)
  189. {
  190. if (_native != null)
  191. _native.Blend1D(info, t);
  192. }
  193. /// <summary>
  194. /// Blend four animation clips between each other using bilinear interpolation. Unlike normal animations these
  195. /// animations are not advanced with the progress of time, and is instead expected the user manually changes the
  196. /// <see cref="t"/> parameter.
  197. /// </summary>
  198. /// <param name="info">Information about the clips to blend.</param>
  199. /// <param name="t">Parameter that controls the blending, in range [(0, 0), (1, 1)]. t = (0, 0) means top left
  200. /// animation has full influence, t = (0, 1) means top right animation has full influence,
  201. /// t = (1, 0) means bottom left animation has full influence, t = (1, 1) means bottom right
  202. /// animation has full influence.
  203. /// </param>
  204. public void Blend2D(Blend2DInfo info, Vector2 t)
  205. {
  206. if (_native != null)
  207. _native.Blend2D(info, t);
  208. }
  209. /// <summary>
  210. /// Fades the specified animation clip in, while fading other playing animation out, over the specified time period.
  211. /// </summary>
  212. /// <param name="clip">Clip to fade in.</param>
  213. /// <param name="fadeLength">Determines the time period over which the fade occurs. In seconds.</param>
  214. public void CrossFade(AnimationClip clip, float fadeLength)
  215. {
  216. if (_native != null)
  217. _native.CrossFade(clip, fadeLength);
  218. }
  219. /// <summary>
  220. /// Stops playing all animations on the provided layer.
  221. /// </summary>
  222. /// <param name="layer">Layer on which to stop animations on.</param>
  223. public void Stop(int layer)
  224. {
  225. if (_native != null)
  226. _native.Stop(layer);
  227. }
  228. /// <summary>
  229. /// Stops playing all animations.
  230. /// </summary>
  231. public void StopAll()
  232. {
  233. if (_native != null)
  234. _native.StopAll();
  235. }
  236. /// <summary>
  237. /// Retrieves detailed information about a currently playing animation clip.
  238. /// </summary>
  239. /// <param name="clip">Clip to retrieve the information for.</param>
  240. /// <param name="state">Animation clip state containing the requested information. Only valid if the method returns
  241. /// true.</param>
  242. /// <returns>True if the state was found (animation clip is playing), false otherwise.</returns>
  243. public bool GetState(AnimationClip clip, out AnimationClipState state)
  244. {
  245. if (_native != null)
  246. return _native.GetState(clip, out state);
  247. state = new AnimationClipState();
  248. return false;
  249. }
  250. /// <summary>
  251. /// Searches the scene object hierarchy to find a property at the given path.
  252. /// </summary>
  253. /// <param name="root">Root scene object to which the path is relative to.</param>
  254. /// <param name="path">Path to the property, where each element of the path is separated with "/".
  255. ///
  256. /// Path elements prefixed with "!" signify names of child scene objects (first one relative to
  257. /// <paramref name="root"/>. Name of the root element should not be included in the path.
  258. ///
  259. /// Path element prefixed with ":" signify names of components. If a path doesn't have a
  260. /// component element, it is assumed the field is relative to the scene object itself (only
  261. /// "Translation", "Rotation" and "Scale fields are supported in such case). Only one component
  262. /// path element per path is allowed.
  263. ///
  264. /// Path entries with no prefix are considered regular script object fields. Each path must have
  265. /// at least one such entry. Last field entry can optionally have a suffix separated from the
  266. /// path name with ".". This suffix is not parsed internally, but will be returned as
  267. /// <paramref name="suffix"/>.
  268. ///
  269. /// Path examples:
  270. /// :MyComponent/myInt (path to myInt variable on a component attached to this object)
  271. /// !childSO/:MyComponent/myInt (path to myInt variable on a child scene object)
  272. /// !childSO/Translation (path to the scene object translation)
  273. /// :MyComponent/myVector.z (path to the z component of myVector on this object)
  274. /// </param>
  275. /// <param name="suffix">Suffix of the last field entry, if it has any. Contains the suffix separator ".".</param>
  276. /// <returns>If found, property object you can use for setting and getting the value from the property, otherwise
  277. /// null.</returns>
  278. internal static SerializableProperty FindProperty(SceneObject root, string path, out string suffix)
  279. {
  280. suffix = null;
  281. if (string.IsNullOrEmpty(path) || root == null)
  282. return null;
  283. string trimmedPath = path.Trim('/');
  284. string[] entries = trimmedPath.Split('/');
  285. // Find scene object referenced by the path
  286. SceneObject so = root;
  287. int pathIdx = 0;
  288. for (; pathIdx < entries.Length; pathIdx++)
  289. {
  290. string entry = entries[pathIdx];
  291. if (string.IsNullOrEmpty(entry))
  292. continue;
  293. // Not a scene object, break
  294. if (entry[0] != '!')
  295. break;
  296. string childName = entry.Substring(1, entry.Length - 1);
  297. so = so.FindChild(childName);
  298. if (so == null)
  299. break;
  300. }
  301. // Child scene object couldn't be found
  302. if (so == null)
  303. return null;
  304. // Path too short, no field entry
  305. if (pathIdx >= entries.Length)
  306. return null;
  307. // Check if path is referencing a component, and if so find it
  308. Component component = null;
  309. {
  310. string entry = entries[pathIdx];
  311. if (entry[0] == ':')
  312. {
  313. string componentName = entry.Substring(1, entry.Length - 1);
  314. Component[] components = so.GetComponents();
  315. component = Array.Find(components, x => x.GetType().Name == componentName);
  316. // Cannot find component with specified type
  317. if (component == null)
  318. return null;
  319. }
  320. }
  321. // Look for a field within a component
  322. if (component != null)
  323. {
  324. pathIdx++;
  325. if (pathIdx >= entries.Length)
  326. return null;
  327. SerializableObject componentObj = new SerializableObject(component);
  328. StringBuilder pathBuilder = new StringBuilder();
  329. for (; pathIdx < entries.Length - 1; pathIdx++)
  330. pathBuilder.Append(entries[pathIdx] + "/");
  331. // Check last path entry for suffix and remove it
  332. int suffixIdx = entries[pathIdx].LastIndexOf(".");
  333. if (suffixIdx != -1)
  334. {
  335. string entryNoSuffix = entries[pathIdx].Substring(0, suffixIdx);
  336. suffix = entries[pathIdx].Substring(suffixIdx, entries[pathIdx].Length - suffixIdx);
  337. pathBuilder.Append(entryNoSuffix);
  338. }
  339. else
  340. pathBuilder.Append(entries[pathIdx]);
  341. return componentObj.FindProperty(pathBuilder.ToString());
  342. }
  343. else // Field is one of the builtin ones on the SceneObject itself
  344. {
  345. if ((pathIdx + 1) < entries.Length)
  346. return null;
  347. string entry = entries[pathIdx];
  348. if (entry == "Position")
  349. {
  350. SerializableProperty property = new SerializableProperty(
  351. SerializableProperty.FieldType.Vector3,
  352. typeof(Vector3),
  353. () => so.LocalPosition,
  354. (x) => so.LocalPosition = (Vector3) x);
  355. return property;
  356. }
  357. else if (entry == "Rotation")
  358. {
  359. SerializableProperty property = new SerializableProperty(
  360. SerializableProperty.FieldType.Vector3,
  361. typeof(Vector3),
  362. () => so.LocalRotation.ToEuler(),
  363. (x) => so.LocalRotation = Quaternion.FromEuler((Vector3) x));
  364. return property;
  365. }
  366. else if (entry == "Scale")
  367. {
  368. SerializableProperty property = new SerializableProperty(
  369. SerializableProperty.FieldType.Vector3,
  370. typeof(Vector3),
  371. () => so.LocalScale,
  372. (x) => so.LocalScale = (Vector3) x);
  373. return property;
  374. }
  375. return null;
  376. }
  377. }
  378. /// <summary>
  379. /// Searches the scene object hierarchy to find a child scene object using the provided path.
  380. /// </summary>
  381. /// <param name="root">Root scene object to which the path is relative to.</param>
  382. /// <param name="path">Path to the property, where each element of the path is separated with "/".
  383. ///
  384. /// Path elements signify names of child scene objects (first one relative to
  385. /// <paramref name="root"/>. Name of the root element should not be included in the path.
  386. /// Elements must be prefixed with "!" in order to match the path format of
  387. /// <see cref="FindProperty"/>.</param>
  388. /// <returns>Child scene object if found, or null otherwise.</returns>
  389. internal static SceneObject FindSceneObject(SceneObject root, string path)
  390. {
  391. if (string.IsNullOrEmpty(path) || root == null)
  392. return null;
  393. string trimmedPath = path.Trim('/');
  394. string[] entries = trimmedPath.Split('/');
  395. // Find scene object referenced by the path
  396. SceneObject so = root;
  397. int pathIdx = 0;
  398. for (; pathIdx < entries.Length; pathIdx++)
  399. {
  400. string entry = entries[pathIdx];
  401. if (string.IsNullOrEmpty(entry))
  402. continue;
  403. // Not a scene object, break
  404. if (entry[0] != '!')
  405. break;
  406. string childName = entry.Substring(1, entry.Length - 1);
  407. so = so.FindChild(childName);
  408. if (so == null)
  409. break;
  410. }
  411. return so;
  412. }
  413. /// <summary>
  414. /// Changes the state of a playing animation clip. If animation clip is not currently playing the state change is
  415. /// ignored.
  416. /// </summary>
  417. /// <param name="clip">Clip to change the state for.</param>
  418. /// <param name="state">New state of the animation (e.g. changing the time for seeking).</param>
  419. public void SetState(AnimationClip clip, AnimationClipState state)
  420. {
  421. if (_native != null)
  422. _native.SetState(clip, state);
  423. }
  424. private void OnUpdate()
  425. {
  426. if (_native == null)
  427. return;
  428. AnimationClip newPrimaryClip = _native.GetClip(0);
  429. if (newPrimaryClip != primaryClip)
  430. {
  431. RebuildFloatProperties(newPrimaryClip);
  432. primaryClip = newPrimaryClip;
  433. UpdateSceneObjectMapping();
  434. }
  435. // Apply values from generic float curves
  436. if (floatProperties != null)
  437. {
  438. foreach (var entry in floatProperties)
  439. {
  440. float curveValue;
  441. if (_native.GetGenericCurveValue(entry.curveIdx, out curveValue))
  442. entry.setter(curveValue);
  443. }
  444. }
  445. }
  446. private void OnInitialize()
  447. {
  448. NotifyFlags = TransformChangedFlags.Transform;
  449. }
  450. private void OnEnable()
  451. {
  452. RestoreNative();
  453. }
  454. private void OnDisable()
  455. {
  456. DestroyNative();
  457. }
  458. private void OnDestroy()
  459. {
  460. DestroyNative();
  461. }
  462. /// <summary>
  463. /// Creates the internal representation of the animation and restores the values saved by the component.
  464. /// </summary>
  465. private void RestoreNative()
  466. {
  467. if (_native != null)
  468. _native.Destroy();
  469. _native = new NativeAnimation();
  470. _native.OnEventTriggered += EventTriggered;
  471. animatedRenderable = SceneObject.GetComponent<Renderable>();
  472. // Restore saved values after reset
  473. _native.WrapMode = serializableData.wrapMode;
  474. _native.Speed = serializableData.speed;
  475. _native.Cull = serializableData.cull;
  476. UpdateBounds();
  477. if (serializableData.defaultClip != null)
  478. _native.Play(serializableData.defaultClip);
  479. primaryClip = _native.GetClip(0);
  480. if (primaryClip != null)
  481. RebuildFloatProperties(primaryClip);
  482. SetBoneMappings();
  483. UpdateSceneObjectMapping();
  484. if(animatedRenderable != null)
  485. animatedRenderable.RegisterAnimation(this);
  486. }
  487. /// <summary>
  488. /// Destroys the internal animation representation.
  489. /// </summary>
  490. private void DestroyNative()
  491. {
  492. if (animatedRenderable != null)
  493. animatedRenderable.UnregisterAnimation();
  494. if (_native != null)
  495. {
  496. _native.Destroy();
  497. _native = null;
  498. }
  499. primaryClip = null;
  500. mappingInfo.Clear();
  501. floatProperties = null;
  502. }
  503. private void OnTransformChanged(TransformChangedFlags flags)
  504. {
  505. if (!SceneObject.Active)
  506. return;
  507. if ((flags & (TransformChangedFlags.Transform)) != 0)
  508. UpdateBounds(false);
  509. }
  510. /// <summary>
  511. /// Finds any curves that affect a transform of a specific scene object, and ensures that animation properly updates
  512. /// those transforms. This does not include curves referencing bones.
  513. /// </summary>
  514. private void UpdateSceneObjectMapping()
  515. {
  516. List<SceneObjectMappingInfo> newMappingInfos = new List<SceneObjectMappingInfo>();
  517. foreach(var entry in mappingInfo)
  518. {
  519. if (entry.isMappedToBone)
  520. newMappingInfos.Add(entry);
  521. else
  522. _native.UnmapSceneObject(entry.sceneObject);
  523. }
  524. if (primaryClip != null)
  525. {
  526. SceneObject root = SceneObject;
  527. Action<NamedVector3Curve[]> findMappings = x =>
  528. {
  529. foreach (var curve in x)
  530. {
  531. if (curve.Flags.HasFlag(AnimationCurveFlags.ImportedCurve))
  532. continue;
  533. SceneObject currentSO = FindSceneObject(root, curve.Name);
  534. bool found = false;
  535. for (int i = 0; i < newMappingInfos.Count; i++)
  536. {
  537. if (newMappingInfos[i].sceneObject == currentSO)
  538. {
  539. found = true;
  540. break;
  541. }
  542. }
  543. if (!found)
  544. {
  545. SceneObjectMappingInfo newMappingInfo = new SceneObjectMappingInfo();
  546. newMappingInfo.isMappedToBone = false;
  547. newMappingInfo.sceneObject = currentSO;
  548. newMappingInfos.Add(newMappingInfo);
  549. _native.MapCurveToSceneObject(curve.Name, currentSO);
  550. }
  551. }
  552. };
  553. AnimationCurves curves = primaryClip.Curves;
  554. findMappings(curves.PositionCurves);
  555. findMappings(curves.RotationCurves);
  556. findMappings(curves.ScaleCurves);
  557. }
  558. mappingInfo = newMappingInfos;
  559. }
  560. /// <summary>
  561. /// Registers a new bone component, creating a new transform mapping from the bone name to the scene object
  562. /// the component is attached to.
  563. /// </summary>
  564. /// <param name="bone">Bone component to register.</param>
  565. internal void AddBone(Bone bone)
  566. {
  567. if (_native == null)
  568. return;
  569. SceneObject currentSO = bone.SceneObject;
  570. SceneObjectMappingInfo newMapping = new SceneObjectMappingInfo();
  571. newMapping.sceneObject = currentSO;
  572. newMapping.isMappedToBone = true;
  573. newMapping.bone = bone;
  574. mappingInfo.Add(newMapping);
  575. _native.MapCurveToSceneObject(bone.Name, newMapping.sceneObject);
  576. }
  577. /// <summary>
  578. /// Unregisters a bone component, removing the bone -> scene object mapping.
  579. /// </summary>
  580. /// <param name="bone">Bone to unregister.</param>
  581. internal void RemoveBone(Bone bone)
  582. {
  583. if (_native == null)
  584. return;
  585. for (int i = 0; i < mappingInfo.Count; i++)
  586. {
  587. if (mappingInfo[i].bone == bone)
  588. {
  589. mappingInfo.RemoveAt(i);
  590. _native.UnmapSceneObject(mappingInfo[i].sceneObject);
  591. i--;
  592. }
  593. }
  594. }
  595. /// <summary>
  596. /// Called whenever the bone the <see cref="Bone"/> component points to changed.
  597. /// </summary>
  598. /// <param name="bone">Bone component to modify.</param>
  599. internal void NotifyBoneChanged(Bone bone)
  600. {
  601. if (_native == null)
  602. return;
  603. for (int i = 0; i < mappingInfo.Count; i++)
  604. {
  605. if (mappingInfo[i].bone == bone)
  606. {
  607. _native.UnmapSceneObject(mappingInfo[i].sceneObject);
  608. _native.MapCurveToSceneObject(bone.Name, mappingInfo[i].sceneObject);
  609. break;
  610. }
  611. }
  612. }
  613. /// <summary>
  614. /// Finds any scene objects that are mapped to bone transforms. Such object's transforms will be affected by
  615. /// skeleton bone animation.
  616. /// </summary>
  617. private void SetBoneMappings()
  618. {
  619. mappingInfo.Clear();
  620. SceneObjectMappingInfo rootMapping = new SceneObjectMappingInfo();
  621. rootMapping.sceneObject = SceneObject;
  622. rootMapping.isMappedToBone = true;
  623. mappingInfo.Add(rootMapping);
  624. _native.MapCurveToSceneObject("", rootMapping.sceneObject);
  625. Bone[] childBones = FindChildBones();
  626. foreach (var entry in childBones)
  627. AddBone(entry);
  628. }
  629. /// <summary>
  630. /// Searches child scene objects for <see cref="Bone"/> components and returns any found ones.
  631. /// </summary>
  632. private Bone[] FindChildBones()
  633. {
  634. Stack<SceneObject> todo = new Stack<SceneObject>();
  635. todo.Push(SceneObject);
  636. List<Bone> bones = new List<Bone>();
  637. while (todo.Count > 0)
  638. {
  639. SceneObject currentSO = todo.Pop();
  640. Bone bone = currentSO.GetComponent<Bone>();
  641. if (bone != null)
  642. {
  643. bone.SetParent(this, true);
  644. bones.Add(bone);
  645. }
  646. int childCount = currentSO.GetNumChildren();
  647. for (int i = 0; i < childCount; i++)
  648. {
  649. SceneObject child = currentSO.GetChild(i);
  650. if (child.GetComponent<Animation>() != null)
  651. continue;
  652. todo.Push(child);
  653. }
  654. }
  655. return bones.ToArray();
  656. }
  657. /// <summary>
  658. /// Re-applies the bounds to the internal animation object, and the relevant renderable object if one exists.
  659. /// </summary>
  660. internal void UpdateBounds(bool updateRenderable = true)
  661. {
  662. NativeRenderable renderable = null;
  663. if (updateRenderable && animatedRenderable != null)
  664. renderable = animatedRenderable.Native;
  665. if (serializableData.useBounds)
  666. {
  667. if (renderable != null)
  668. {
  669. renderable.UseOverrideBounds = true;
  670. renderable.OverrideBounds = serializableData.bounds;
  671. }
  672. if (_native != null)
  673. {
  674. AABox bounds = serializableData.bounds;
  675. bounds.TransformAffine(SceneObject.WorldTransform);
  676. _native.Bounds = bounds;
  677. }
  678. }
  679. else
  680. {
  681. if (renderable != null)
  682. renderable.UseOverrideBounds = false;
  683. if (_native != null)
  684. {
  685. AABox bounds = new AABox();
  686. if (animatedRenderable != null)
  687. bounds = animatedRenderable.Bounds.Box;
  688. _native.Bounds = bounds;
  689. }
  690. }
  691. }
  692. /// <summary>
  693. /// Registers an <see cref="Renderable"/> component with the animation. Rendering will be affected by the animation.
  694. /// </summary>
  695. /// <param name="renderable">Component that was added</param>
  696. internal void RegisterRenderable(Renderable renderable)
  697. {
  698. animatedRenderable = renderable;
  699. UpdateBounds();
  700. }
  701. /// <summary>
  702. /// Removes renderable from the animation component. Rendering will no longer be affected by animation.
  703. /// </summary>
  704. internal void UnregisterRenderable()
  705. {
  706. animatedRenderable = null;
  707. }
  708. /// <summary>
  709. /// Builds a list of properties that will be animated using float animation curves.
  710. /// </summary>
  711. /// <param name="clip">Clip to retrieve the float animation curves from.</param>
  712. private void RebuildFloatProperties(AnimationClip clip)
  713. {
  714. if (clip == null)
  715. {
  716. floatProperties = null;
  717. return;
  718. }
  719. AnimationCurves curves = clip.Curves;
  720. List<FloatCurvePropertyInfo> newFloatProperties = new List<FloatCurvePropertyInfo>();
  721. for (int i = 0; i < curves.FloatCurves.Length; i++)
  722. {
  723. string suffix;
  724. SerializableProperty property = FindProperty(SceneObject, curves.FloatCurves[i].Name, out suffix);
  725. if (property == null)
  726. continue;
  727. int elementIdx = 0;
  728. if (!string.IsNullOrEmpty(suffix))
  729. {
  730. PropertySuffixInfo suffixInfo;
  731. if (PropertySuffixInfos.TryGetValue(suffix, out suffixInfo))
  732. elementIdx = suffixInfo.elementIdx;
  733. }
  734. Action<float> setter = null;
  735. Type internalType = property.InternalType;
  736. switch (property.Type)
  737. {
  738. case SerializableProperty.FieldType.Vector2:
  739. if (internalType == typeof(Vector2))
  740. {
  741. setter = f =>
  742. {
  743. Vector2 value = property.GetValue<Vector2>();
  744. value[elementIdx] = f;
  745. property.SetValue(value);
  746. };
  747. }
  748. break;
  749. case SerializableProperty.FieldType.Vector3:
  750. if (internalType == typeof(Vector3))
  751. {
  752. setter = f =>
  753. {
  754. Vector3 value = property.GetValue<Vector3>();
  755. value[elementIdx] = f;
  756. property.SetValue(value);
  757. };
  758. }
  759. break;
  760. case SerializableProperty.FieldType.Vector4:
  761. if (internalType == typeof(Vector4))
  762. {
  763. setter = f =>
  764. {
  765. Vector4 value = property.GetValue<Vector4>();
  766. value[elementIdx] = f;
  767. property.SetValue(value);
  768. };
  769. }
  770. else if (internalType == typeof(Quaternion))
  771. {
  772. setter = f =>
  773. {
  774. Quaternion value = property.GetValue<Quaternion>();
  775. value[elementIdx] = f;
  776. property.SetValue(value);
  777. };
  778. }
  779. break;
  780. case SerializableProperty.FieldType.Color:
  781. if (internalType == typeof(Color))
  782. {
  783. setter = f =>
  784. {
  785. Color value = property.GetValue<Color>();
  786. value[elementIdx] = f;
  787. property.SetValue(value);
  788. };
  789. }
  790. break;
  791. case SerializableProperty.FieldType.Bool:
  792. setter = f =>
  793. {
  794. bool value = f > 0.0f;
  795. property.SetValue(value);
  796. };
  797. break;
  798. case SerializableProperty.FieldType.Int:
  799. setter = f =>
  800. {
  801. int value = (int)f;
  802. property.SetValue(value);
  803. };
  804. break;
  805. case SerializableProperty.FieldType.Float:
  806. setter = f =>
  807. {
  808. property.SetValue(f);
  809. };
  810. break;
  811. }
  812. if (setter == null)
  813. continue;
  814. FloatCurvePropertyInfo propertyInfo = new FloatCurvePropertyInfo();
  815. propertyInfo.curveIdx = i;
  816. propertyInfo.setter = setter;
  817. newFloatProperties.Add(propertyInfo);
  818. }
  819. floatProperties = newFloatProperties.ToArray();
  820. }
  821. /// <summary>
  822. /// Called whenever an animation event triggers.
  823. /// </summary>
  824. /// <param name="clip">Clip that the event originated from.</param>
  825. /// <param name="name">Name of the event.</param>
  826. private void EventTriggered(AnimationClip clip, string name)
  827. {
  828. // Event should be in format "ComponentType/MethodName"
  829. if (string.IsNullOrEmpty(name))
  830. return;
  831. string[] nameEntries = name.Split('/');
  832. if (nameEntries.Length != 2)
  833. return;
  834. string typeName = nameEntries[0];
  835. string methodName = nameEntries[1];
  836. Component[] components = SceneObject.GetComponents();
  837. for (int i = 0; i < components.Length; i++)
  838. {
  839. if (components[i].GetType().Name == typeName)
  840. {
  841. components[i].Invoke(methodName);
  842. break;
  843. }
  844. }
  845. }
  846. /// <summary>
  847. /// Holds all data the animation component needs to persist through serialization.
  848. /// </summary>
  849. [SerializeObject]
  850. private class SerializableData
  851. {
  852. public AnimationClip defaultClip;
  853. public AnimWrapMode wrapMode = AnimWrapMode.Loop;
  854. public float speed = 1.0f;
  855. public AABox bounds;
  856. public bool useBounds;
  857. public bool cull = true;
  858. }
  859. /// <summary>
  860. /// Contains information about a property animated by a generic animation curve.
  861. /// </summary>
  862. private class FloatCurvePropertyInfo
  863. {
  864. public int curveIdx;
  865. public Action<float> setter;
  866. }
  867. /// <summary>
  868. /// Information about a suffix used in a property path.
  869. /// </summary>
  870. internal struct PropertySuffixInfo
  871. {
  872. public PropertySuffixInfo(int elementIdx, bool isVector)
  873. {
  874. this.elementIdx = elementIdx;
  875. this.isVector = isVector;
  876. }
  877. public int elementIdx;
  878. public bool isVector;
  879. }
  880. /// <summary>
  881. /// Information about scene objects bound to a specific animation curve.
  882. /// </summary>
  883. internal struct SceneObjectMappingInfo
  884. {
  885. public SceneObject sceneObject;
  886. public bool isMappedToBone;
  887. public Bone bone;
  888. }
  889. }
  890. /// <summary>
  891. /// Determines how an animation clip behaves when it reaches the end.
  892. /// </summary>
  893. public enum AnimWrapMode // Note: Must match C++ enum AnimWrapMode
  894. {
  895. /// <summary>
  896. /// Loop around to the beginning/end when the last/first frame is reached.
  897. /// </summary>
  898. Loop,
  899. /// <summary>
  900. /// Clamp to end/beginning, keeping the last/first frame active.
  901. /// </summary>
  902. Clamp
  903. }
  904. /// <summary>
  905. /// Represents an animation clip used in 1D blending. Each clip has a position on the number line.
  906. /// </summary>
  907. public class BlendClipInfo
  908. {
  909. public AnimationClip clip;
  910. public float position;
  911. }
  912. /// <summary>
  913. /// Defines a 1D blend where two animation clips are blended between each other using linear interpolation.
  914. /// </summary>
  915. public class Blend1DInfo
  916. {
  917. public BlendClipInfo[] clips;
  918. }
  919. /// <summary>
  920. /// Defines a 2D blend where two animation clips are blended between each other using bilinear interpolation.
  921. /// </summary>
  922. public class Blend2DInfo
  923. {
  924. public AnimationClip topLeftClip;
  925. public AnimationClip topRightClip;
  926. public AnimationClip botLeftClip;
  927. public AnimationClip botRightClip;
  928. }
  929. /// <summary>
  930. /// Contains information about a currently playing animation clip.
  931. /// </summary>
  932. [StructLayout(LayoutKind.Sequential), SerializeObject]
  933. public struct AnimationClipState // Note: Must match C++ struct AnimationClipState
  934. {
  935. /// <summary>
  936. /// Layer the clip is playing on. Multiple clips can be played simulatenously on different layers.
  937. /// </summary>
  938. public int layer;
  939. /// <summary>
  940. /// Current time the animation is playing from.
  941. /// </summary>
  942. public float time;
  943. /// <summary>
  944. /// Speed at which the animation is playing.
  945. /// </summary>
  946. public float speed;
  947. /// <summary>
  948. /// Determines how much of an influence does the clip have on the final pose.
  949. /// </summary>
  950. public float weight;
  951. /// <summary>
  952. /// Determines what happens to other animation clips when a new clip starts playing.
  953. /// </summary>
  954. public AnimWrapMode wrapMode;
  955. /// <summary>
  956. /// Initializes the state with default values.
  957. /// </summary>
  958. public void InitDefault()
  959. {
  960. speed = 1.0f;
  961. weight = 1.0f;
  962. wrapMode = AnimWrapMode.Loop;
  963. }
  964. }
  965. /** @} */
  966. }