Animation.cs 41 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136
  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. mappingInfo.RemoveAt(i);
  595. _native.UnmapSceneObject(mappingInfo[i].sceneObject);
  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 BlendClipInfo[] clips;
  928. }
  929. /// <summary>
  930. /// Defines a 2D blend where two animation clips are blended between each other using bilinear interpolation.
  931. /// </summary>
  932. public class Blend2DInfo
  933. {
  934. public AnimationClip topLeftClip;
  935. public AnimationClip topRightClip;
  936. public AnimationClip botLeftClip;
  937. public AnimationClip botRightClip;
  938. }
  939. /// <summary>
  940. /// Contains information about a currently playing animation clip.
  941. /// </summary>
  942. [StructLayout(LayoutKind.Sequential), SerializeObject]
  943. public struct AnimationClipState // Note: Must match C++ struct AnimationClipState
  944. {
  945. /// <summary>
  946. /// Layer the clip is playing on. Multiple clips can be played simulatenously on different layers.
  947. /// </summary>
  948. public int layer;
  949. /// <summary>
  950. /// Current time the animation is playing from.
  951. /// </summary>
  952. public float time;
  953. /// <summary>
  954. /// Speed at which the animation is playing.
  955. /// </summary>
  956. public float speed;
  957. /// <summary>
  958. /// Determines how much of an influence does the clip have on the final pose.
  959. /// </summary>
  960. public float weight;
  961. /// <summary>
  962. /// Determines what happens to other animation clips when a new clip starts playing.
  963. /// </summary>
  964. public AnimWrapMode wrapMode;
  965. /// <summary>
  966. /// Initializes the state with default values.
  967. /// </summary>
  968. public void InitDefault()
  969. {
  970. speed = 1.0f;
  971. weight = 1.0f;
  972. wrapMode = AnimWrapMode.Loop;
  973. }
  974. }
  975. /** @} */
  976. }