GltfImporterAnimation.cpp 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Importer/GltfImporter.h>
  6. #include <AnKi/Util/Xml.h>
  7. namespace anki {
  8. template<typename T>
  9. class GltfAnimKey
  10. {
  11. public:
  12. Second m_time;
  13. T m_value;
  14. };
  15. class GltfAnimChannel
  16. {
  17. public:
  18. ImporterString m_name;
  19. ImporterDynamicArray<GltfAnimKey<Vec3>> m_positions;
  20. ImporterDynamicArray<GltfAnimKey<Quat>> m_rotations;
  21. ImporterDynamicArray<GltfAnimKey<F32>> m_scales;
  22. const cgltf_node* m_targetNode;
  23. };
  24. /// Optimize out same animation keys.
  25. template<typename T, typename TIsIdentityFunc, typename TAlmostEqualFunc, typename TLerpFunc>
  26. static void optimizeChannel(ImporterDynamicArray<GltfAnimKey<T>>& arr, TIsIdentityFunc isIdentityFunc, TAlmostEqualFunc almostEqualFunc,
  27. TLerpFunc lerpFunc)
  28. {
  29. constexpr F32 kMinSkippedToTotalRatio = 0.1f;
  30. U32 iterationCount = 0;
  31. while(true)
  32. {
  33. if(arr.getSize() < 3)
  34. {
  35. break;
  36. }
  37. ImporterDynamicArray<GltfAnimKey<T>> newArr;
  38. U32 it = 0;
  39. while(true)
  40. {
  41. const GltfAnimKey<T>& left = arr[it];
  42. const GltfAnimKey<T>& middle = arr[it + 1];
  43. const GltfAnimKey<T>& right = arr[it + 2];
  44. newArr.emplaceBack(left);
  45. if(left.m_value == middle.m_value && middle.m_value == right.m_value)
  46. {
  47. // Skip it
  48. }
  49. else
  50. {
  51. const F32 factor = F32((middle.m_time - left.m_time) / (right.m_time - left.m_time));
  52. ANKI_ASSERT(factor > 0.0f && factor < 1.0f);
  53. const T lerpRez = lerpFunc(left.m_value, right.m_value, factor);
  54. if(almostEqualFunc(middle.m_value, lerpRez))
  55. {
  56. // It's redundant, skip it
  57. }
  58. else
  59. {
  60. newArr.emplaceBack(middle);
  61. }
  62. }
  63. it += 2;
  64. if(it + 2 >= arr.getSize())
  65. {
  66. break;
  67. }
  68. }
  69. for(; it < arr.getSize(); ++it)
  70. {
  71. newArr.emplaceBack(arr[it]);
  72. }
  73. ANKI_ASSERT(newArr.getSize() <= arr.getSize());
  74. // Check if identity
  75. if(newArr.getSize() == 2 && isIdentityFunc(newArr[0].m_value) && isIdentityFunc(newArr[1].m_value))
  76. {
  77. newArr.destroy();
  78. }
  79. const F32 skippedToTotalRatio = 1.0f - F32(newArr.getSize()) / F32(arr.getSize());
  80. arr.destroy();
  81. arr = std::move(newArr);
  82. ++iterationCount;
  83. if(skippedToTotalRatio <= kMinSkippedToTotalRatio)
  84. {
  85. break;
  86. }
  87. }
  88. ANKI_IMPORTER_LOGV("Channel optimization iteration count: %u", iterationCount);
  89. }
  90. Error GltfImporter::writeAnimation(const cgltf_animation& anim)
  91. {
  92. ImporterString fname;
  93. ImporterString animFname = computeAnimationResourceFilename(anim);
  94. fname.sprintf("%s%s", m_outDir.cstr(), animFname.cstr());
  95. fname = fixFilename(fname);
  96. ANKI_IMPORTER_LOGV("Importing animation %s", fname.cstr());
  97. // Gather the channels
  98. ImporterHashMap<CString, Array<const cgltf_animation_channel*, 3>> channelMap;
  99. U32 channelCount = 0;
  100. for(U i = 0; i < anim.channels_count; ++i)
  101. {
  102. const cgltf_animation_channel& channel = anim.channels[i];
  103. const ImporterString channelName = getNodeName(*channel.target_node);
  104. U idx;
  105. switch(channel.target_path)
  106. {
  107. case cgltf_animation_path_type_translation:
  108. idx = 0;
  109. break;
  110. case cgltf_animation_path_type_rotation:
  111. idx = 1;
  112. break;
  113. case cgltf_animation_path_type_scale:
  114. idx = 2;
  115. break;
  116. default:
  117. ANKI_ASSERT(0);
  118. idx = 0;
  119. }
  120. auto it = channelMap.find(channelName.toCString());
  121. if(it != channelMap.getEnd())
  122. {
  123. (*it)[idx] = &channel;
  124. }
  125. else
  126. {
  127. Array<const cgltf_animation_channel*, 3> arr = {};
  128. arr[idx] = &channel;
  129. channelMap.emplace(channelName.toCString(), arr);
  130. ++channelCount;
  131. }
  132. }
  133. // Gather the keys
  134. ImporterDynamicArray<GltfAnimChannel> tempChannels;
  135. tempChannels.resize(channelCount);
  136. channelCount = 0;
  137. for(auto it = channelMap.getBegin(); it != channelMap.getEnd(); ++it)
  138. {
  139. Array<const cgltf_animation_channel*, 3> arr = *it;
  140. const cgltf_animation_channel& anyChannel = (arr[0]) ? *arr[0] : ((arr[1]) ? *arr[1] : *arr[2]);
  141. const ImporterString channelName = getNodeName(*anyChannel.target_node);
  142. tempChannels[channelCount].m_name = channelName;
  143. tempChannels[channelCount].m_targetNode = anyChannel.target_node;
  144. // Positions
  145. if(arr[0])
  146. {
  147. const cgltf_animation_channel& channel = *arr[0];
  148. ImporterDynamicArray<F32> keys;
  149. readAccessor(*channel.sampler->input, keys);
  150. ImporterDynamicArray<Vec3> positions;
  151. readAccessor(*channel.sampler->output, positions);
  152. if(keys.getSize() != positions.getSize())
  153. {
  154. ANKI_IMPORTER_LOGE("Position count should match they keyframes");
  155. return Error::kUserData;
  156. }
  157. for(U32 i = 0; i < keys.getSize(); ++i)
  158. {
  159. GltfAnimKey<Vec3> key;
  160. key.m_time = keys[i];
  161. key.m_value = Vec3(positions[i].x(), positions[i].y(), positions[i].z());
  162. tempChannels[channelCount].m_positions.emplaceBack(key);
  163. }
  164. }
  165. // Rotations
  166. if(arr[1])
  167. {
  168. const cgltf_animation_channel& channel = *arr[1];
  169. ImporterDynamicArray<F32> keys;
  170. readAccessor(*channel.sampler->input, keys);
  171. ImporterDynamicArray<Quat> rotations;
  172. readAccessor(*channel.sampler->output, rotations);
  173. if(keys.getSize() != rotations.getSize())
  174. {
  175. ANKI_IMPORTER_LOGE("Rotation count should match they keyframes");
  176. return Error::kUserData;
  177. }
  178. for(U32 i = 0; i < keys.getSize(); ++i)
  179. {
  180. GltfAnimKey<Quat> key;
  181. key.m_time = keys[i];
  182. key.m_value = Quat(rotations[i].x(), rotations[i].y(), rotations[i].z(), rotations[i].w());
  183. tempChannels[channelCount].m_rotations.emplaceBack(key);
  184. }
  185. }
  186. // Scales
  187. if(arr[2])
  188. {
  189. const cgltf_animation_channel& channel = *arr[2];
  190. ImporterDynamicArray<F32> keys;
  191. readAccessor(*channel.sampler->input, keys);
  192. ImporterDynamicArray<Vec3> scales;
  193. readAccessor(*channel.sampler->output, scales);
  194. if(keys.getSize() != scales.getSize())
  195. {
  196. ANKI_IMPORTER_LOGE("Scale count should match they keyframes");
  197. return Error::kUserData;
  198. }
  199. Bool scaleErrorReported = false;
  200. for(U32 i = 0; i < keys.getSize(); ++i)
  201. {
  202. const F32 scaleEpsilon = 0.0001f;
  203. // Normalize the scale because scaleEpsilon is relative
  204. Vec3 scale = scales[i].normalize();
  205. if(!scaleErrorReported && (absolute(scale[0] - scale[1]) > scaleEpsilon || absolute(scale[0] - scale[2]) > scaleEpsilon))
  206. {
  207. ANKI_IMPORTER_LOGW("Expecting uniform scale (%f %f %f)", scales[i].x(), scales[i].y(), scales[i].z());
  208. scaleErrorReported = true;
  209. }
  210. GltfAnimKey<F32> key;
  211. key.m_time = keys[i];
  212. key.m_value = scales[i][0];
  213. if(absolute(key.m_value - 1.0f) <= scaleEpsilon)
  214. {
  215. key.m_value = 1.0f;
  216. }
  217. tempChannels[channelCount].m_scales.emplaceBack(key);
  218. }
  219. }
  220. ++channelCount;
  221. }
  222. // Optimize animation
  223. if(m_optimizeAnimations)
  224. {
  225. constexpr F32 kKillEpsilon = 1.0_cm;
  226. for(GltfAnimChannel& channel : tempChannels)
  227. {
  228. optimizeChannel(
  229. channel.m_positions,
  230. [&](const Vec3& a) -> Bool {
  231. return a.abs() < kKillEpsilon;
  232. },
  233. [&](const Vec3& a, const Vec3& b) -> Bool {
  234. return (a - b).length() < kKillEpsilon;
  235. },
  236. [&](const Vec3& a, const Vec3& b, F32 u) -> Vec3 {
  237. return linearInterpolate(a, b, u);
  238. });
  239. optimizeChannel(
  240. channel.m_rotations,
  241. [&](const Quat& a) -> Bool {
  242. return (Vec4(a) - Vec4(0.0f, 0.0f, 0.0f, 1.0f)).abs() < 0.001f;
  243. },
  244. [&](const Quat& a, const Quat& b) -> Bool {
  245. return (Vec4(a) - Vec4(b)).abs() < 0.001f;
  246. },
  247. [&](const Quat& a, const Quat& b, F32 u) -> Quat {
  248. return a.slerp(b, u);
  249. });
  250. optimizeChannel(
  251. channel.m_scales,
  252. [&](const F32& a) -> Bool {
  253. return absolute(a - 1.0f) < kKillEpsilon;
  254. },
  255. [&](const F32& a, const F32& b) -> Bool {
  256. return absolute(a - b) < kKillEpsilon;
  257. },
  258. [&](const F32& a, const F32& b, F32 u) -> F32 {
  259. return linearInterpolate(a, b, u);
  260. });
  261. }
  262. }
  263. // Write file
  264. File file;
  265. ANKI_CHECK(file.open(fname.toCString(), FileOpenFlag::kWrite));
  266. ANKI_CHECK(file.writeTextf("%s\n<animation>\n", XmlDocument<MemoryPoolPtrWrapper<BaseMemoryPool>>::kXmlHeader.cstr()));
  267. ANKI_CHECK(file.writeText("\t<channels>\n"));
  268. for(const GltfAnimChannel& channel : tempChannels)
  269. {
  270. ANKI_CHECK(file.writeTextf("\t\t<channel name=\"%s\">\n", channel.m_name.cstr()));
  271. // Positions
  272. if(channel.m_positions.getSize())
  273. {
  274. ANKI_CHECK(file.writeText("\t\t\t<positionKeys>\n"));
  275. for(const GltfAnimKey<Vec3>& key : channel.m_positions)
  276. {
  277. ANKI_CHECK(
  278. file.writeTextf("\t\t\t\t<key time=\"%f\">%f %f %f</key>\n", key.m_time, key.m_value.x(), key.m_value.y(), key.m_value.z()));
  279. }
  280. ANKI_CHECK(file.writeText("\t\t\t</positionKeys>\n"));
  281. }
  282. // Rotations
  283. if(channel.m_rotations.getSize())
  284. {
  285. ANKI_CHECK(file.writeText("\t\t\t<rotationKeys>\n"));
  286. for(const GltfAnimKey<Quat>& key : channel.m_rotations)
  287. {
  288. ANKI_CHECK(file.writeTextf("\t\t\t\t<key time=\"%f\">%f %f %f %f</key>\n", key.m_time, key.m_value.x(), key.m_value.y(),
  289. key.m_value.z(), key.m_value.w()));
  290. }
  291. ANKI_CHECK(file.writeText("\t\t\t</rotationKeys>\n"));
  292. }
  293. // Scales
  294. if(channel.m_scales.getSize())
  295. {
  296. ANKI_CHECK(file.writeText("\t\t\t<scaleKeys>\n"));
  297. for(const GltfAnimKey<F32>& key : channel.m_scales)
  298. {
  299. ANKI_CHECK(file.writeTextf("\t\t\t\t<key time=\"%f\">%f</key>\n", key.m_time, key.m_value));
  300. }
  301. ANKI_CHECK(file.writeText("\t\t\t</scaleKeys>\n"));
  302. }
  303. ANKI_CHECK(file.writeText("\t\t</channel>\n"));
  304. }
  305. ANKI_CHECK(file.writeText("\t</channels>\n"));
  306. ANKI_CHECK(file.writeText("</animation>\n"));
  307. // Hook up the animation to the scene
  308. for(const GltfAnimChannel& channel : tempChannels)
  309. {
  310. if(channel.m_targetNode == nullptr)
  311. {
  312. continue;
  313. }
  314. const cgltf_node& node = *channel.m_targetNode;
  315. if(node.name == nullptr)
  316. {
  317. continue;
  318. }
  319. // No idea how to distinguise the bone nodes so wrap it in an if
  320. ANKI_CHECK(m_sceneFile.writeTextf("\nnode = scene:tryFindSceneNode(\"%s\")\n", node.name));
  321. ANKI_CHECK(m_sceneFile.writeText("if node ~= nil then\n"));
  322. ANKI_CHECK(
  323. m_sceneFile.writeTextf("\tgetEventManager():newAnimationEvent(\"%s%s\", \"%s\", node)\n", m_rpath.cstr(), animFname.cstr(), node.name));
  324. ANKI_CHECK(m_sceneFile.writeText("end\n"));
  325. }
  326. return Error::kNone;
  327. }
  328. } // end namespace anki