Animation.cs 41 KB

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