Animation.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  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. public partial class Animation
  13. {
  14. private FloatCurvePropertyInfo[] floatProperties;
  15. /// <summary>
  16. /// Contains mapping for a suffix used by property paths used for curve identifiers, to their index and type.
  17. /// </summary>
  18. internal static readonly Dictionary<string, PropertySuffixInfo> PropertySuffixInfos = new Dictionary
  19. <string, PropertySuffixInfo>
  20. {
  21. {".x", new PropertySuffixInfo(0, true)},
  22. {".y", new PropertySuffixInfo(1, true)},
  23. {".z", new PropertySuffixInfo(2, true)},
  24. {".w", new PropertySuffixInfo(3, true)},
  25. {".r", new PropertySuffixInfo(0, false)},
  26. {".g", new PropertySuffixInfo(1, false)},
  27. {".b", new PropertySuffixInfo(2, false)},
  28. {".a", new PropertySuffixInfo(3, false)}
  29. };
  30. /// <summary>
  31. /// Allows the caller to play an animation clip during edit mode. This form of animation playback is limited as
  32. /// you have no control over clip properties, and features like blending, cross fade or animation events are not
  33. /// supported.
  34. ///
  35. /// Caller will need to manually call <see cref="UpdateFloatProperties"/> in order to apply evaluated animation data
  36. /// to relevant float properties (if required).
  37. ///
  38. /// Caller will also need to manually call <see cref="RefreshClipMappings"/> whenever the curves internal to the
  39. /// animation clip change. This should be called before the call to <see cref="UpdateFloatProperties"/>.
  40. /// </summary>
  41. /// <param name="clip">Animation clip to play.</param>
  42. /// <param name="startTime">Time to start playing at, in seconds.</param>
  43. /// <param name="freeze">If true, only the frame at the specified time will be shown, without advancing the
  44. /// animation.</param>
  45. internal void EditorPlay(AnimationClip clip, float startTime, bool freeze = false)
  46. {
  47. bool inPreviewMode = Internal__togglePreviewMode(mCachedPtr, true);
  48. if (!inPreviewMode)
  49. return;
  50. if (freeze)
  51. Sample(clip, startTime);
  52. else
  53. {
  54. AnimationClipState clipState = AnimationClipState.Default();
  55. clipState.time = startTime;
  56. SetState(clip, clipState);
  57. }
  58. Internal__refreshClipMappings(mCachedPtr);
  59. }
  60. /// <summary>
  61. /// Stops playback of animation whose playback what started using <see cref="EditorPlay"/>.
  62. /// </summary>
  63. internal void EditorStop()
  64. {
  65. Internal__togglePreviewMode(mCachedPtr, false);
  66. }
  67. /// <summary>
  68. /// Returns the current time of the currently playing editor animation clip.
  69. /// </summary>
  70. /// <returns>Time in seconds.</returns>
  71. internal float EditorGetTime()
  72. {
  73. AnimationClip clip = Internal_getClip(mCachedPtr, 0);
  74. AnimationClipState clipState;
  75. if (clip != null && GetState(clip, out clipState))
  76. return clipState.time;
  77. return 0.0f;
  78. }
  79. /// <summary>
  80. /// Updates generic float properties on relevant objects, based on the most recently evaluated animation curve
  81. /// values.
  82. /// </summary>
  83. internal void UpdateFloatProperties()
  84. {
  85. _UpdateFloatProperties();
  86. }
  87. /// <summary>
  88. /// Searches the scene object hierarchy to find a property at the given path.
  89. /// </summary>
  90. /// <param name="root">Root scene object to which the path is relative to.</param>
  91. /// <param name="path">Path to the property, where each element of the path is separated with "/".
  92. ///
  93. /// Path elements prefixed with "!" signify names of child scene objects (first one relative to
  94. /// <paramref name="root"/>. Name of the root element should not be included in the path.
  95. ///
  96. /// Path element prefixed with ":" signify names of components. If a path doesn't have a
  97. /// component element, it is assumed the field is relative to the scene object itself (only
  98. /// "Position", "Rotation" and "Scale" fields are supported in such case). Only one component
  99. /// path element per path is allowed.
  100. ///
  101. /// Path entries with no prefix are considered regular script object fields. Each path must have
  102. /// at least one such entry.
  103. ///
  104. /// A field path can be followed by an indexer [n] where n is a zero-based index. Such paths
  105. /// are assumed to be referencing an index within an array or a list.
  106. ///
  107. /// A field path can also be followed by a suffix (after the indexer, if any) separated from the
  108. /// path name with ".". This suffix is not parsed internally, but will be returned as
  109. /// <paramref name="suffix"/>.
  110. ///
  111. /// Path examples:
  112. /// :MyComponent/myInt (path to myInt variable on a component attached to the root object)
  113. /// :MyComponent/myArray[0] (path to first element of myArray on the same component as above)
  114. /// !childSO/:MyComponent/myInt (path to myInt variable on a child scene object)
  115. /// !childSO/Position (path to the scene object position)
  116. /// :MyComponent/myVector.z (path to the z component of myVector on the root object)
  117. /// </param>
  118. /// <param name="suffix">Suffix of the last field entry, if it has any. Contains the suffix separator ".".</param>
  119. /// <returns>If found, property object you can use for setting and getting the value from the property, otherwise
  120. /// null.</returns>
  121. internal static SerializableProperty FindProperty(SceneObject root, string path, out string suffix)
  122. {
  123. suffix = null;
  124. if (string.IsNullOrEmpty(path) || root == null)
  125. return null;
  126. string trimmedPath = path.Trim('/');
  127. string[] entries = trimmedPath.Split('/');
  128. // Find scene object referenced by the path
  129. SceneObject so = root;
  130. int pathIdx = 0;
  131. for (; pathIdx < entries.Length; pathIdx++)
  132. {
  133. string entry = entries[pathIdx];
  134. if (string.IsNullOrEmpty(entry))
  135. continue;
  136. // Not a scene object, break
  137. if (entry[0] != '!')
  138. break;
  139. string childName = entry.Substring(1, entry.Length - 1);
  140. so = so.FindChild(childName);
  141. if (so == null)
  142. break;
  143. }
  144. // Child scene object couldn't be found
  145. if (so == null)
  146. return null;
  147. // Path too short, no field entry
  148. if (pathIdx >= entries.Length)
  149. return null;
  150. // Check if path is referencing a component, and if so find it
  151. Component component = null;
  152. {
  153. string entry = entries[pathIdx];
  154. if (entry[0] == ':')
  155. {
  156. string componentName = entry.Substring(1, entry.Length - 1);
  157. Component[] components = so.GetComponents();
  158. component = Array.Find(components, x => x.GetType().Name == componentName);
  159. // Cannot find component with specified type
  160. if (component == null)
  161. return null;
  162. }
  163. }
  164. // Look for a field within a component
  165. if (component != null)
  166. {
  167. pathIdx++;
  168. if (pathIdx >= entries.Length)
  169. return null;
  170. SerializableObject componentObj = new SerializableObject(component);
  171. StringBuilder pathBuilder = new StringBuilder();
  172. for (; pathIdx < entries.Length - 1; pathIdx++)
  173. pathBuilder.Append(entries[pathIdx] + "/");
  174. // Check last path entry for suffix and remove it
  175. int suffixIdx = entries[pathIdx].LastIndexOf(".");
  176. if (suffixIdx != -1)
  177. {
  178. string entryNoSuffix = entries[pathIdx].Substring(0, suffixIdx);
  179. suffix = entries[pathIdx].Substring(suffixIdx, entries[pathIdx].Length - suffixIdx);
  180. pathBuilder.Append(entryNoSuffix);
  181. }
  182. else
  183. pathBuilder.Append(entries[pathIdx]);
  184. return componentObj.FindProperty(pathBuilder.ToString());
  185. }
  186. else // Field is one of the builtin ones on the SceneObject itself
  187. {
  188. if ((pathIdx + 1) < entries.Length)
  189. return null;
  190. string entry = entries[pathIdx];
  191. if (entry == "Position")
  192. {
  193. SerializableProperty property = new SerializableProperty(
  194. SerializableProperty.FieldType.Vector3,
  195. typeof(Vector3),
  196. () => so.LocalPosition,
  197. (x) => so.LocalPosition = (Vector3)x);
  198. return property;
  199. }
  200. else if (entry == "Rotation")
  201. {
  202. SerializableProperty property = new SerializableProperty(
  203. SerializableProperty.FieldType.Vector3,
  204. typeof(Vector3),
  205. () => so.LocalRotation.ToEuler(),
  206. (x) => so.LocalRotation = Quaternion.FromEuler((Vector3)x));
  207. return property;
  208. }
  209. else if (entry == "Scale")
  210. {
  211. SerializableProperty property = new SerializableProperty(
  212. SerializableProperty.FieldType.Vector3,
  213. typeof(Vector3),
  214. () => so.LocalScale,
  215. (x) => so.LocalScale = (Vector3)x);
  216. return property;
  217. }
  218. return null;
  219. }
  220. }
  221. /// <summary>
  222. /// Builds a list of properties that will be animated using float animation curves.
  223. /// </summary>
  224. /// <param name="clip">Clip to retrieve the float animation curves from.</param>
  225. partial void RebuildFloatProperties(AnimationClip clip)
  226. {
  227. if (clip == null)
  228. {
  229. floatProperties = null;
  230. return;
  231. }
  232. AnimationCurves curves = clip.Curves;
  233. List<FloatCurvePropertyInfo> newFloatProperties = new List<FloatCurvePropertyInfo>();
  234. for (int i = 0; i < curves.Generic.Length; i++)
  235. {
  236. bool isMorphCurve = curves.Generic[i].flags.HasFlag(AnimationCurveFlags.MorphWeight) ||
  237. curves.Generic[i].flags.HasFlag(AnimationCurveFlags.MorphFrame);
  238. if (isMorphCurve)
  239. continue;
  240. string suffix;
  241. SerializableProperty property = FindProperty(SceneObject, curves.Generic[i].name, out suffix);
  242. if (property == null)
  243. continue;
  244. int elementIdx = 0;
  245. if (!string.IsNullOrEmpty(suffix))
  246. {
  247. PropertySuffixInfo suffixInfo;
  248. if (PropertySuffixInfos.TryGetValue(suffix, out suffixInfo))
  249. elementIdx = suffixInfo.elementIdx;
  250. }
  251. Action<float> setter = null;
  252. Type internalType = property.InternalType;
  253. switch (property.Type)
  254. {
  255. case SerializableProperty.FieldType.Vector2:
  256. if (internalType == typeof(Vector2))
  257. {
  258. setter = f =>
  259. {
  260. Vector2 value = property.GetValue<Vector2>();
  261. value[elementIdx] = f;
  262. property.SetValue(value);
  263. };
  264. }
  265. break;
  266. case SerializableProperty.FieldType.Vector3:
  267. if (internalType == typeof(Vector3))
  268. {
  269. setter = f =>
  270. {
  271. Vector3 value = property.GetValue<Vector3>();
  272. value[elementIdx] = f;
  273. property.SetValue(value);
  274. };
  275. }
  276. break;
  277. case SerializableProperty.FieldType.Vector4:
  278. if (internalType == typeof(Vector4))
  279. {
  280. setter = f =>
  281. {
  282. Vector4 value = property.GetValue<Vector4>();
  283. value[elementIdx] = f;
  284. property.SetValue(value);
  285. };
  286. }
  287. else if (internalType == typeof(Quaternion))
  288. {
  289. setter = f =>
  290. {
  291. Quaternion value = property.GetValue<Quaternion>();
  292. value[elementIdx] = f;
  293. property.SetValue(value);
  294. };
  295. }
  296. break;
  297. case SerializableProperty.FieldType.Color:
  298. if (internalType == typeof(Color))
  299. {
  300. setter = f =>
  301. {
  302. Color value = property.GetValue<Color>();
  303. value[elementIdx] = f;
  304. property.SetValue(value);
  305. };
  306. }
  307. break;
  308. case SerializableProperty.FieldType.Bool:
  309. setter = f =>
  310. {
  311. bool value = f > 0.0f;
  312. property.SetValue(value);
  313. };
  314. break;
  315. case SerializableProperty.FieldType.Int:
  316. setter = f =>
  317. {
  318. int value = (int)f;
  319. property.SetValue(value);
  320. };
  321. break;
  322. case SerializableProperty.FieldType.Float:
  323. setter = f =>
  324. {
  325. property.SetValue(f);
  326. };
  327. break;
  328. }
  329. if (setter == null)
  330. continue;
  331. FloatCurvePropertyInfo propertyInfo = new FloatCurvePropertyInfo();
  332. propertyInfo.curveIdx = i;
  333. propertyInfo.setter = setter;
  334. newFloatProperties.Add(propertyInfo);
  335. }
  336. floatProperties = newFloatProperties.ToArray();
  337. }
  338. /// <summary>
  339. /// Called whenever an animation event triggers.
  340. /// </summary>
  341. /// <param name="clip">Clip that the event originated from.</param>
  342. /// <param name="name">Name of the event.</param>
  343. partial void EventTriggered(AnimationClip clip, string name)
  344. {
  345. // Event should be in format "ComponentType/MethodName"
  346. if (string.IsNullOrEmpty(name))
  347. return;
  348. string[] nameEntries = name.Split('/');
  349. if (nameEntries.Length != 2)
  350. return;
  351. string typeName = nameEntries[0];
  352. string methodName = nameEntries[1];
  353. Component[] components = SceneObject.GetComponents();
  354. for (int i = 0; i < components.Length; i++)
  355. {
  356. if (components[i].GetType().Name == typeName)
  357. {
  358. components[i].Invoke(methodName);
  359. break;
  360. }
  361. }
  362. }
  363. /// <summary>
  364. /// Partial method implementation for <see cref="UpdateFloatProperties"/>
  365. /// </summary>
  366. partial void _UpdateFloatProperties()
  367. {
  368. // Apply values from generic float curves
  369. if (floatProperties != null)
  370. {
  371. foreach (var entry in floatProperties)
  372. {
  373. float curveValue;
  374. if (Internal__getGenericCurveValue(mCachedPtr, (uint)entry.curveIdx, out curveValue))
  375. entry.setter(curveValue);
  376. }
  377. }
  378. }
  379. /// <summary>
  380. /// Contains information about a property animated by a generic animation curve.
  381. /// </summary>
  382. private class FloatCurvePropertyInfo
  383. {
  384. public int curveIdx;
  385. public Action<float> setter;
  386. }
  387. /// <summary>
  388. /// Information about a suffix used in a property path.
  389. /// </summary>
  390. internal struct PropertySuffixInfo
  391. {
  392. public PropertySuffixInfo(int elementIdx, bool isVector)
  393. {
  394. this.elementIdx = elementIdx;
  395. this.isVector = isVector;
  396. }
  397. public int elementIdx;
  398. public bool isVector;
  399. }
  400. }
  401. /** @} */
  402. }