BabylonExporter.Animation.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. using System;
  2. using System.Collections.Generic;
  3. using Autodesk.Max;
  4. using BabylonExport.Entities;
  5. using System.Runtime.InteropServices;
  6. namespace Max2Babylon
  7. {
  8. partial class BabylonExporter
  9. {
  10. const int Ticks = 160;
  11. private static bool ExportBabylonKeys(List<BabylonAnimationKey> keys, string property, List<BabylonAnimation> animations, BabylonAnimation.DataType dataType, BabylonAnimation.LoopBehavior loopBehavior)
  12. {
  13. if (keys.Count == 0)
  14. {
  15. return false;
  16. }
  17. var end = Loader.Core.AnimRange.End;
  18. if (keys[keys.Count - 1].frame != end / Ticks)
  19. {
  20. keys.Add(new BabylonAnimationKey()
  21. {
  22. frame = end / Ticks,
  23. values = keys[keys.Count - 1].values
  24. });
  25. }
  26. var babylonAnimation = new BabylonAnimation
  27. {
  28. dataType = (int)dataType,
  29. name = property + " animation",
  30. keys = keys.ToArray(),
  31. framePerSecond = Loader.Global.FrameRate,
  32. loopBehavior = (int)loopBehavior,
  33. property = property
  34. };
  35. animations.Add(babylonAnimation);
  36. return true;
  37. }
  38. // -----------------------
  39. // -- From GameControl ---
  40. // -----------------------
  41. private bool ExportFloatGameController(IIGameControl control, string property, List<BabylonAnimation> animations)
  42. {
  43. return ExportGameController(control, property, animations, IGameControlType.Float, BabylonAnimation.DataType.Float, gameKey => new float[] { gameKey.SampleKey.Fval / 100.0f });
  44. }
  45. private bool ExportGameController(IIGameControl control, string property, List<BabylonAnimation> animations, IGameControlType type, BabylonAnimation.DataType dataType, Func<IIGameKey, float[]> extractValueFunc)
  46. {
  47. var keys = ExportBabylonKeysFromGameController(control, type, extractValueFunc);
  48. if (keys == null)
  49. {
  50. return false;
  51. }
  52. var loopBehavior = BabylonAnimation.LoopBehavior.Cycle;
  53. return ExportBabylonKeys(keys, property, animations, dataType, loopBehavior);
  54. }
  55. private List<BabylonAnimationKey> ExportBabylonKeysFromGameController(IIGameControl control, IGameControlType type, Func<IIGameKey, float[]> extractValueFunc)
  56. {
  57. if (control == null)
  58. {
  59. return null;
  60. }
  61. ITab<IIGameKey> gameKeyTab = GlobalInterface.Instance.Tab.Create<IIGameKey>();
  62. control.GetQuickSampledKeys(gameKeyTab, type);
  63. if (gameKeyTab == null)
  64. {
  65. return null;
  66. }
  67. var keys = new List<BabylonAnimationKey>();
  68. for (int indexKey = 0; indexKey < gameKeyTab.Count; indexKey++)
  69. {
  70. #if MAX2017
  71. var indexer = indexKey;
  72. #else
  73. var indexer = new IntPtr(indexKey);
  74. Marshal.FreeHGlobal(indexer);
  75. #endif
  76. var gameKey = gameKeyTab[indexer];
  77. var key = new BabylonAnimationKey()
  78. {
  79. frame = gameKey.T / Ticks,
  80. values = extractValueFunc(gameKey)
  81. };
  82. keys.Add(key);
  83. }
  84. return keys;
  85. }
  86. // -----------------------
  87. // ---- From Control -----
  88. // -----------------------
  89. private static BabylonAnimationKey GenerateFloatFunc(int index, IIKeyControl keyControl)
  90. {
  91. var key = Loader.Global.ILinFloatKey.Create();
  92. keyControl.GetKey(index, key);
  93. return new BabylonAnimationKey
  94. {
  95. frame = key.Time / Ticks,
  96. values = new[] { key.Val }
  97. };
  98. }
  99. private static bool ExportFloatController(IControl control, string property, List<BabylonAnimation> animations)
  100. {
  101. return ExportController(control, property, animations, 0x2001, BabylonAnimation.DataType.Float, GenerateFloatFunc);
  102. }
  103. private static bool ExportQuaternionController(IControl control, string property, List<BabylonAnimation> animations)
  104. {
  105. IQuat previousQuat = null;
  106. return ExportController(control, property, animations, 0x2003, BabylonAnimation.DataType.Quaternion,
  107. (index, keyControl) =>
  108. {
  109. var key = Loader.Global.ILinRotKey.Create();
  110. keyControl.GetKey(index, key);
  111. var newQuat = key.Val;
  112. if (index > 0)
  113. {
  114. newQuat = previousQuat.Multiply(newQuat);
  115. }
  116. previousQuat = newQuat;
  117. return new BabylonAnimationKey
  118. {
  119. frame = key.Time / Ticks,
  120. values = newQuat.ToArray()
  121. };
  122. });
  123. }
  124. private static bool ExportVector3Controller(IControl control, string property, List<BabylonAnimation> animations)
  125. {
  126. var result = false;
  127. if (control == null)
  128. {
  129. return false;
  130. }
  131. if (control.XController != null || control.YController != null || control.ZController != null)
  132. {
  133. result |= ExportFloatController(control.XController, property + ".x", animations);
  134. result |= ExportFloatController(control.ZController, property + ".y", animations);
  135. result |= ExportFloatController(control.YController, property + ".z", animations);
  136. return result;
  137. }
  138. if (ExportController(control, property, animations, 0x2002, BabylonAnimation.DataType.Vector3,
  139. (index, keyControl) =>
  140. {
  141. var key = Loader.Global.ILinPoint3Key.Create();
  142. keyControl.GetKey(index, key);
  143. return new BabylonAnimationKey
  144. {
  145. frame = key.Time / Ticks,
  146. values = key.Val.ToArraySwitched()
  147. };
  148. }))
  149. {
  150. return true;
  151. }
  152. return ExportController(control, property, animations, 0x2004, BabylonAnimation.DataType.Vector3,
  153. (index, keyControl) =>
  154. {
  155. var key = Loader.Global.ILinScaleKey.Create();
  156. keyControl.GetKey(index, key);
  157. return new BabylonAnimationKey
  158. {
  159. frame = key.Time / Ticks,
  160. values = key.Val.S.ToArraySwitched()
  161. };
  162. });
  163. }
  164. private static bool ExportController(IControl control, string property, List<BabylonAnimation> animations, uint classId, BabylonAnimation.DataType dataType, Func<int, IIKeyControl, BabylonAnimationKey> generateFunc)
  165. {
  166. if (control == null)
  167. {
  168. return false;
  169. }
  170. var keyControl = control.GetInterface(InterfaceID.Keycontrol) as IIKeyControl;
  171. if (keyControl == null)
  172. {
  173. return false;
  174. }
  175. if (control.ClassID.PartA != classId)
  176. {
  177. return false;
  178. }
  179. BabylonAnimation.LoopBehavior loopBehavior;
  180. switch (control.GetORT(2))
  181. {
  182. case 2:
  183. loopBehavior = BabylonAnimation.LoopBehavior.Cycle;
  184. break;
  185. default:
  186. loopBehavior = BabylonAnimation.LoopBehavior.Relative;
  187. break;
  188. }
  189. var keys = new List<BabylonAnimationKey>();
  190. for (var index = 0; index < keyControl.NumKeys; index++)
  191. {
  192. keys.Add(generateFunc(index, keyControl));
  193. }
  194. return ExportBabylonKeys(keys, property, animations, dataType, loopBehavior);
  195. }
  196. // -----------------------
  197. // ---- From ext func ----
  198. // -----------------------
  199. private static void ExportColor3Animation(string property, List<BabylonAnimation> animations,
  200. Func<int, float[]> extractValueFunc)
  201. {
  202. ExportAnimation(property, animations, extractValueFunc, BabylonAnimation.DataType.Color3);
  203. }
  204. private static void ExportVector3Animation(string property, List<BabylonAnimation> animations,
  205. Func<int, float[]> extractValueFunc)
  206. {
  207. ExportAnimation(property, animations, extractValueFunc, BabylonAnimation.DataType.Vector3);
  208. }
  209. private static void ExportQuaternionAnimation(string property, List<BabylonAnimation> animations,
  210. Func<int, float[]> extractValueFunc)
  211. {
  212. ExportAnimation(property, animations, extractValueFunc, BabylonAnimation.DataType.Quaternion);
  213. }
  214. private static void ExportFloatAnimation(string property, List<BabylonAnimation> animations,
  215. Func<int, float[]> extractValueFunc)
  216. {
  217. ExportAnimation(property, animations, extractValueFunc, BabylonAnimation.DataType.Float);
  218. }
  219. private static BabylonAnimation ExportMatrixAnimation(string property, Func<int, float[]> extractValueFunc, bool removeLinearAnimationKeys = true)
  220. {
  221. return ExportAnimation(property, extractValueFunc, BabylonAnimation.DataType.Matrix, removeLinearAnimationKeys);
  222. }
  223. static void RemoveLinearAnimationKeys(List<BabylonAnimationKey> keys)
  224. {
  225. for (int ixFirst = keys.Count - 3; ixFirst >= 0; --ixFirst)
  226. {
  227. while (keys.Count - ixFirst >= 3)
  228. {
  229. if (!RemoveAnimationKey(keys, ixFirst))
  230. {
  231. break;
  232. }
  233. }
  234. }
  235. }
  236. static float[] weightedLerp(int frame0, int frame1, int frame2, float[] value0, float[] value2)
  237. {
  238. double weight2 = (frame1 - frame0) / (double)(frame2 - frame0);
  239. double weight0 = 1 - weight2;
  240. float[] result = new float[value0.Length];
  241. for (int i = 0; i < result.Length; ++i)
  242. {
  243. result[i] = (float)(value0[i] * weight0 + value2[i] * weight2);
  244. }
  245. return result;
  246. }
  247. private static bool RemoveAnimationKey(List<BabylonAnimationKey> keys, int ixFirst)
  248. {
  249. var first = keys[ixFirst];
  250. var middle = keys[ixFirst + 1];
  251. var last = keys[ixFirst + 2];
  252. // first pass, frame equality
  253. if (first.values.IsEqualTo(last.values) && first.values.IsEqualTo(middle.values))
  254. {
  255. keys.RemoveAt(ixFirst + 1);
  256. return true;
  257. }
  258. // second pass : linear interpolation detection
  259. var computedMiddleValue = weightedLerp(first.frame, middle.frame, last.frame, first.values, last.values);
  260. if (computedMiddleValue.IsEqualTo(middle.values))
  261. {
  262. keys.RemoveAt(ixFirst + 1);
  263. return true;
  264. }
  265. return false;
  266. }
  267. private static void ExportAnimation(string property, List<BabylonAnimation> animations, Func<int, float[]> extractValueFunc, BabylonAnimation.DataType dataType, bool removeLinearAnimationKeys = true)
  268. {
  269. var babylonAnimation = ExportAnimation(property, extractValueFunc, dataType, removeLinearAnimationKeys);
  270. if (babylonAnimation != null)
  271. {
  272. animations.Add(babylonAnimation);
  273. }
  274. }
  275. private static BabylonAnimation ExportAnimation(string property, Func<int, float[]> extractValueFunc, BabylonAnimation.DataType dataType, bool removeLinearAnimationKeys = true)
  276. {
  277. var optimizeAnimations = !Loader.Core.RootNode.GetBoolProperty("babylonjs_donotoptimizeanimations"); // reverse negation for clarity
  278. var start = Loader.Core.AnimRange.Start;
  279. var end = Loader.Core.AnimRange.End;
  280. float[] previous = null;
  281. var keys = new List<BabylonAnimationKey>();
  282. for (var key = start; key <= end; key += Ticks)
  283. {
  284. var current = extractValueFunc(key);
  285. // if animations are not optimized, all keys from start to end are created
  286. // otherwise, keys are added only for non constant values
  287. if (optimizeAnimations && previous != null && previous.IsEqualTo(current))
  288. {
  289. continue; // Do not add key
  290. }
  291. keys.Add(new BabylonAnimationKey()
  292. {
  293. frame = key / Ticks,
  294. values = current
  295. });
  296. previous = current;
  297. }
  298. // if animations are not optimized, do the following as minimal optimization
  299. if (removeLinearAnimationKeys && !optimizeAnimations)
  300. {
  301. RemoveLinearAnimationKeys(keys);
  302. }
  303. if (keys.Count > 1)
  304. {
  305. var animationPresent = true;
  306. if (keys.Count == 2)
  307. {
  308. if (keys[0].values.IsEqualTo(keys[1].values))
  309. {
  310. animationPresent = false;
  311. }
  312. }
  313. if (animationPresent)
  314. {
  315. if (keys[keys.Count - 1].frame != end / Ticks)
  316. {
  317. keys.Add(new BabylonAnimationKey()
  318. {
  319. frame = end / Ticks,
  320. values = keys[keys.Count - 1].values
  321. });
  322. }
  323. var babylonAnimation = new BabylonAnimation
  324. {
  325. dataType = (int)dataType,
  326. name = property + " animation",
  327. keys = keys.ToArray(),
  328. framePerSecond = Loader.Global.FrameRate,
  329. loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle,
  330. property = property
  331. };
  332. return babylonAnimation;
  333. }
  334. }
  335. return null;
  336. }
  337. }
  338. }