AssImpAnimationImporter.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AssImpTypeConverter.h>
  9. #include <assimp/mesh.h>
  10. #include <assimp/scene.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/std/algorithm.h>
  13. #include <AzCore/std/smart_ptr/make_shared.h>
  14. #include <AzCore/StringFunc/StringFunc.h>
  15. #include <AzToolsFramework/Debug/TraceContext.h>
  16. #include <SceneAPI/SceneBuilder/SceneSystem.h>
  17. #include <SceneAPI/SceneBuilder/ImportContexts/AssImpImportContexts.h>
  18. #include <SceneAPI/SceneBuilder/Importers/AssImpAnimationImporter.h>
  19. #include <SceneAPI/SceneBuilder/Importers/AssImpImporterUtilities.h>
  20. #include <SceneAPI/SceneBuilder/Importers/Utilities/RenamedNodesMap.h>
  21. #include <SceneAPI/SceneCore/Containers/Scene.h>
  22. #include <SceneAPI/SceneData/GraphData/AnimationData.h>
  23. #include <SceneAPI/SDKWrapper/AssImpNodeWrapper.h>
  24. #include <SceneAPI/SDKWrapper/AssImpSceneWrapper.h>
  25. namespace AZ
  26. {
  27. namespace SceneAPI
  28. {
  29. namespace SceneBuilder
  30. {
  31. const char* AssImpAnimationImporter::s_animationNodeName = "animation";
  32. // Downstream only supports 30 frames per second sample rate. Adjusting to 60 doubles the
  33. // length of the animations, they still play back at 30 frames per second.
  34. const double AssImpAnimationImporter::s_defaultTimeStepBetweenFrames = 1.0 / 30.0;
  35. AZ::u32 GetNumKeyFrames(AZ::u32 keysSize, double duration, double ticksPerSecond)
  36. {
  37. if (AZ::IsClose(ticksPerSecond, 0))
  38. {
  39. AZ_Warning("AnimationImporter", false, "Animation ticks per second should not be zero, defaulting to %d keyframes for animation.", keysSize);
  40. return keysSize;
  41. }
  42. const double totalTicks = duration / ticksPerSecond;
  43. AZ::u32 numKeys = keysSize;
  44. // +1 because the animation is from [0, duration] - we have a keyframe at the end of the duration which needs to be included
  45. double totalFramesAtDefaultTimeStep = totalTicks / AssImpAnimationImporter::s_defaultTimeStepBetweenFrames + 1;
  46. if (!AZ::IsClose(totalFramesAtDefaultTimeStep, numKeys, 1))
  47. {
  48. numKeys = static_cast<AZ::u32>(AZStd::ceilf(static_cast<float>(totalFramesAtDefaultTimeStep)));
  49. }
  50. return numKeys;
  51. }
  52. double GetTimeForFrame(AZ::u32 frame, double ticksPerSecond)
  53. {
  54. return frame * AssImpAnimationImporter::s_defaultTimeStepBetweenFrames * ticksPerSecond;
  55. }
  56. // Helper class to store key data, when translating from AssImp layout to the engine's scene format.
  57. struct KeyData
  58. {
  59. KeyData(float value, float time) :
  60. mValue(value),
  61. mTime(time)
  62. {
  63. }
  64. bool operator<(const KeyData& other) const
  65. {
  66. return mTime < other.mTime;
  67. }
  68. float mValue = 0;
  69. float mTime = 0;
  70. };
  71. template<class T>
  72. void LerpTemplate(T& start, const T& end, float t)
  73. {
  74. start = start * (1.0f - t) + end * t;
  75. }
  76. template<>
  77. void LerpTemplate(aiQuaternion& start, const aiQuaternion& end, float t)
  78. {
  79. aiQuaternion::Interpolate(start, start, end, t);
  80. }
  81. template<>
  82. void LerpTemplate(float& start, const float& end, float t)
  83. {
  84. start = AZ::Lerp(start, end, t);
  85. }
  86. template<class KeyContainerType, class FrameValueType>
  87. bool SampleKeyFrame(FrameValueType& result, const KeyContainerType& keys, AZ::u32 numKeys, double time, AZ::u32& lastIndex)
  88. {
  89. if (numKeys == 0)
  90. {
  91. AZ_Error("AnimationImporter", numKeys > 0, "Animation key set must have at least 1 key");
  92. return false;
  93. }
  94. if (numKeys == 1)
  95. {
  96. result = keys[0].mValue;
  97. return true;
  98. }
  99. while (lastIndex < numKeys - 1 && time >= keys[lastIndex + 1].mTime)
  100. {
  101. ++lastIndex;
  102. }
  103. result = keys[lastIndex].mValue;
  104. if (lastIndex < numKeys - 1)
  105. {
  106. auto nextValue = keys[lastIndex + 1].mValue;
  107. float normalizedTimeBetweenFrames = 0;
  108. if (keys[lastIndex + 1].mTime != keys[lastIndex].mTime)
  109. {
  110. normalizedTimeBetweenFrames =
  111. static_cast<float>((time - keys[lastIndex].mTime) / (keys[lastIndex + 1].mTime - keys[lastIndex].mTime));
  112. }
  113. else
  114. {
  115. AZ_Warning("AnimationImporter", false,
  116. "Animation has keys with duplicate time %5.5f, at indices %d and %d. The second will be ignored.",
  117. keys[lastIndex].mTime,
  118. lastIndex,
  119. lastIndex + 1);
  120. }
  121. LerpTemplate(result, nextValue, normalizedTimeBetweenFrames);
  122. }
  123. return true;
  124. }
  125. AssImpAnimationImporter::AssImpAnimationImporter()
  126. {
  127. BindToCall(&AssImpAnimationImporter::ImportAnimation);
  128. }
  129. void AssImpAnimationImporter::Reflect(ReflectContext* context)
  130. {
  131. SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(context);
  132. if (serializeContext)
  133. {
  134. // Revision 5: [LYN-4226] Invert PostRotation matrix in animation chains
  135. // Revision 6: Handle duplicate blend shape animations
  136. serializeContext->Class<AssImpAnimationImporter, SceneCore::LoadingComponent>()->Version(6);
  137. }
  138. }
  139. AZStd::string GetName(const aiMeshAnim* anim)
  140. {
  141. return anim->mName.C_Str();
  142. }
  143. AZStd::string GetName(const aiNodeAnim* anim)
  144. {
  145. return anim->mNodeName.C_Str();
  146. }
  147. AZStd::string GetName(const aiMeshMorphAnim* anim)
  148. {
  149. return anim->mName.C_Str();
  150. }
  151. struct ConsolidatedNodeAnim
  152. {
  153. ConsolidatedNodeAnim() = default;
  154. ConsolidatedNodeAnim(const ConsolidatedNodeAnim& other) = delete;
  155. ConsolidatedNodeAnim(ConsolidatedNodeAnim&& other) noexcept
  156. : mNumPositionKeys{ other.mNumPositionKeys },
  157. mPositionKeys{ other.mPositionKeys },
  158. mNumRotationKeys{ other.mNumRotationKeys },
  159. mRotationKeys{ other.mRotationKeys },
  160. mNumScalingKeys{ other.mNumScalingKeys },
  161. mScalingKeys{ other.mScalingKeys },
  162. m_ownedPositionKeys{ std::move(other.m_ownedPositionKeys) },
  163. m_ownedScalingKeys{ std::move(other.m_ownedScalingKeys) },
  164. m_ownedRotationKeys{ std::move(other.m_ownedRotationKeys) },
  165. mPreState{ other.mPreState },
  166. mPostState{ other.mPostState }
  167. {
  168. }
  169. ConsolidatedNodeAnim& operator=(const ConsolidatedNodeAnim& other) = delete;
  170. ConsolidatedNodeAnim& operator=(ConsolidatedNodeAnim&& other) noexcept
  171. {
  172. if (this == &other)
  173. {
  174. return *this;
  175. }
  176. mNumPositionKeys = other.mNumPositionKeys;
  177. mPositionKeys = other.mPositionKeys;
  178. mNumRotationKeys = other.mNumRotationKeys;
  179. mRotationKeys = other.mRotationKeys;
  180. mNumScalingKeys = other.mNumScalingKeys;
  181. mScalingKeys = other.mScalingKeys;
  182. m_ownedPositionKeys = std::move(other.m_ownedPositionKeys);
  183. m_ownedScalingKeys = std::move(other.m_ownedScalingKeys);
  184. m_ownedRotationKeys = std::move(other.m_ownedRotationKeys);
  185. mPreState = other.mPreState;
  186. mPostState = other.mPostState;
  187. return *this;
  188. }
  189. // These variables are named using AssImp naming convention for consistency
  190. AZ::u32 mNumPositionKeys{};
  191. aiVectorKey* mPositionKeys{};
  192. AZ::u32 mNumRotationKeys{};
  193. aiQuatKey* mRotationKeys{};
  194. AZ::u32 mNumScalingKeys{};
  195. aiVectorKey* mScalingKeys{};
  196. AZStd::vector<aiVectorKey> m_ownedPositionKeys{};
  197. AZStd::vector<aiVectorKey> m_ownedScalingKeys{};
  198. AZStd::vector<aiQuatKey> m_ownedRotationKeys{};
  199. aiAnimBehaviour mPreState{};
  200. aiAnimBehaviour mPostState{};
  201. };
  202. AZStd::pair<const aiAnimation*, ConsolidatedNodeAnim> MakeMyPair(const aiAnimation* animation, const aiNodeAnim* anim)
  203. {
  204. ConsolidatedNodeAnim consolidatedNodeAnim{};
  205. consolidatedNodeAnim.mNumRotationKeys = anim->mNumRotationKeys;
  206. consolidatedNodeAnim.mNumPositionKeys = anim->mNumPositionKeys;
  207. consolidatedNodeAnim.mNumScalingKeys = anim->mNumScalingKeys;
  208. consolidatedNodeAnim.mRotationKeys = anim->mRotationKeys;
  209. consolidatedNodeAnim.mPositionKeys = anim->mPositionKeys;
  210. consolidatedNodeAnim.mScalingKeys = anim->mScalingKeys;
  211. return {animation, AZStd::move(consolidatedNodeAnim)};
  212. }
  213. auto MakeMyPair(const aiAnimation* animation, const aiMeshAnim* anim)
  214. {
  215. return AZStd::make_pair(animation, anim);
  216. }
  217. auto MakeMyPair(const aiAnimation* animation, const aiMeshMorphAnim* anim)
  218. {
  219. return AZStd::make_pair(animation, anim);
  220. }
  221. Events::ProcessingResult AssImpAnimationImporter::ImportAnimation(AssImpSceneNodeAppendedContext& context)
  222. {
  223. AZ_TraceContext("Importer", "Animation");
  224. const aiNode* currentNode = context.m_sourceNode.GetAssImpNode();
  225. const aiScene* scene = context.m_sourceScene.GetAssImpScene();
  226. // Add check for animation layers at the scene level.
  227. if (!scene->HasAnimations() || IsPivotNode(currentNode->mName))
  228. {
  229. return Events::ProcessingResult::Ignored;
  230. }
  231. AZStd::unordered_multimap<AZStd::string, AZStd::pair<const aiAnimation*, ConsolidatedNodeAnim>> boneAnimations;
  232. typedef AZStd::pair<const aiAnimation*, const aiMeshMorphAnim*> AnimAndMorphAnim;
  233. typedef AZStd::unordered_map<AZStd::string, AnimAndMorphAnim> ChannelToMorphAnim;
  234. typedef AZStd::unordered_map<AZStd::string, ChannelToMorphAnim> NodeToChannelToMorphAnim;
  235. NodeToChannelToMorphAnim meshMorphAnimations;
  236. // Goes through all the animation channels of a given type and adds them to a map so we can easily find
  237. // all the animations for a given node
  238. // In the case of bone animations, the data is copied into a ConsolidatedNodeAnim so we can
  239. // do fix-ups later without affecting the original data
  240. auto mapAnimationsFunc = [](AZ::u32 numChannels, auto** channels, const aiAnimation* animation, auto& map)
  241. {
  242. map.reserve(numChannels);
  243. for (AZ::u32 channelIndex = 0; channelIndex < numChannels; ++channelIndex)
  244. {
  245. auto* nodeAnim = channels[channelIndex];
  246. AZStd::string name = GetName(nodeAnim);
  247. map.emplace(name, MakeMyPair(animation, nodeAnim));
  248. }
  249. };
  250. for (AZ::u32 animIndex = 0; animIndex < scene->mNumAnimations; ++animIndex)
  251. {
  252. const aiAnimation* animation = scene->mAnimations[animIndex];
  253. if (animation->mTicksPerSecond == 0)
  254. {
  255. AZ_Error(
  256. "AnimationImporter", false,
  257. "Animation name %s has a sample rate of 0 ticks per second and cannot be processed.",
  258. animation->mName.C_Str());
  259. return Events::ProcessingResult::Failure;
  260. }
  261. mapAnimationsFunc(animation->mNumChannels, animation->mChannels, animation, boneAnimations);
  262. for (AZ::u32 channelIndex = 0; channelIndex < animation->mNumMorphMeshChannels; ++channelIndex)
  263. {
  264. auto* nodeAnim = animation->mMorphMeshChannels[channelIndex];
  265. AZStd::string name = GetName(nodeAnim);
  266. AZStd::vector<AZStd::string> meshNodeNameAndChannel;
  267. // Morph target animations include the channel in the name,
  268. // so if a mesh is named Mesh01, the morph target for the first channel
  269. // will be named Mesh01*0
  270. AZ::StringFunc::Tokenize(name, meshNodeNameAndChannel, '*');
  271. if (meshNodeNameAndChannel.size() != 2)
  272. {
  273. AZ_Error(
  274. "AnimationImporter", false,
  275. "Morph animation name %s was not in the expected format of: node name, asterisk, node channel. "
  276. "Example: 'NodeName*0'",
  277. name.c_str());
  278. continue;
  279. }
  280. AZStd::string meshNodeName(meshNodeNameAndChannel[0]);
  281. AZStd::string channel(meshNodeNameAndChannel[1]);
  282. meshMorphAnimations[meshNodeName][channel] = AnimAndMorphAnim(animation, nodeAnim);
  283. }
  284. }
  285. // Go through all the bone animations and find any that reference a pivot node
  286. // We'll make a new node anim and store all the combined animation channels there
  287. // with the name set to the base bone name
  288. decltype(boneAnimations) combinedAnimations;
  289. for (auto&& animation : boneAnimations)
  290. {
  291. size_t pos = AZ::StringFunc::Find(animation.first, PivotNodeMarker, 0);
  292. if (pos != animation.first.npos)
  293. {
  294. AZStd::string_view name, part;
  295. SplitPivotNodeName(aiString(animation.first.c_str()), pos, name, part);
  296. auto&& iterator = combinedAnimations.find(name);
  297. if (iterator == combinedAnimations.end())
  298. {
  299. combinedAnimations.emplace(AZStd::string(name), AZStd::move(animation.second));
  300. iterator = combinedAnimations.find(name);
  301. }
  302. if (iterator != combinedAnimations.end())
  303. {
  304. auto& existingNode = iterator->second.second;
  305. auto&& src = animation.second.second;
  306. if (part == "Translation")
  307. {
  308. existingNode.mNumPositionKeys = src.mNumPositionKeys;
  309. existingNode.mPositionKeys = src.mPositionKeys;
  310. }
  311. else if (part == "Rotation")
  312. {
  313. existingNode.mNumRotationKeys = src.mNumRotationKeys;
  314. existingNode.mRotationKeys = src.mRotationKeys;
  315. }
  316. else if (part == "Scaling")
  317. {
  318. existingNode.mNumScalingKeys = src.mNumScalingKeys;
  319. existingNode.mScalingKeys = src.mScalingKeys;
  320. }
  321. }
  322. }
  323. }
  324. if (!combinedAnimations.empty())
  325. {
  326. boneAnimations.swap(combinedAnimations);
  327. }
  328. Events::ProcessingResultCombiner combinedAnimationResult;
  329. if (context.m_sourceNode.ContainsMesh())
  330. {
  331. const aiMesh* firstMesh = scene->mMeshes[currentNode->mMeshes[0]];
  332. if (NodeToChannelToMorphAnim::iterator channelsForMeshName = meshMorphAnimations.find(firstMesh->mName.C_Str());
  333. channelsForMeshName != meshMorphAnimations.end())
  334. {
  335. const auto [nodeIterName, channels] = *channelsForMeshName;
  336. for (const auto& [channel, animAndMorphAnim] : channels)
  337. {
  338. const auto& [animation, morphAnimation] = animAndMorphAnim;
  339. combinedAnimationResult += ImportBlendShapeAnimation(
  340. context, animation, morphAnimation, firstMesh);
  341. }
  342. }
  343. }
  344. AZStd::string nodeName = s_animationNodeName;
  345. RenamedNodesMap::SanitizeNodeName(nodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition);
  346. AZ_TraceContext("Animation node name", nodeName);
  347. // If there are no bone animations, but there are mesh animations,
  348. // then a stub animation needs to be created so the exporter can create the exported morph target animation.
  349. if (boneAnimations.empty() && !meshMorphAnimations.empty())
  350. {
  351. const aiAnimation* animation = scene->mAnimations[0];
  352. for (AZ::u32 channelIndex = 0; channelIndex < animation->mNumMorphMeshChannels; ++channelIndex)
  353. {
  354. const aiMeshMorphAnim* nodeAnim = animation->mMorphMeshChannels[channelIndex];
  355. // Morph animations need a regular animation on the node, as well.
  356. // If there is no bone animation on the current node, then generate one here.
  357. AZStd::shared_ptr<AZ::SceneData::GraphData::AnimationData> createdAnimationData =
  358. AZStd::make_shared<AZ::SceneData::GraphData::AnimationData>();
  359. const size_t numKeyframes = GetNumKeyFrames(
  360. nodeAnim->mNumKeys,
  361. animation->mDuration,
  362. animation->mTicksPerSecond);
  363. createdAnimationData->ReserveKeyFrames(numKeyframes);
  364. const double timeStepBetweenFrames = 1.0 / animation->mTicksPerSecond;
  365. createdAnimationData->SetTimeStepBetweenFrames(timeStepBetweenFrames);
  366. // Set every frame of the animation to the start location of the node.
  367. aiMatrix4x4 combinedTransform = GetConcatenatedLocalTransform(currentNode);
  368. DataTypes::MatrixType localTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(combinedTransform);
  369. context.m_sourceSceneSystem.SwapTransformForUpAxis(localTransform);
  370. context.m_sourceSceneSystem.ConvertUnit(localTransform);
  371. for (AZ::u32 time = 0; time <= numKeyframes; ++time)
  372. {
  373. createdAnimationData->AddKeyFrame(localTransform);
  374. }
  375. AZStd::string stubBoneAnimForMorphName(AZStd::string::format("%s%s", nodeName.c_str(), nodeAnim->mName.C_Str()));
  376. RenamedNodesMap::SanitizeNodeName(stubBoneAnimForMorphName, context.m_scene.GetGraph(), context.m_currentGraphPosition);
  377. Containers::SceneGraph::NodeIndex addNode = context.m_scene.GetGraph().AddChild(
  378. context.m_currentGraphPosition, stubBoneAnimForMorphName.c_str(), AZStd::move(createdAnimationData));
  379. context.m_scene.GetGraph().MakeEndPoint(addNode);
  380. }
  381. return combinedAnimationResult.GetResult();
  382. }
  383. AZStd::unordered_set<AZStd::string> nonPivotBoneList;
  384. for (unsigned int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
  385. {
  386. aiMesh* mesh = scene->mMeshes[meshIndex];
  387. if (!mesh)
  388. {
  389. AZ_Error(
  390. "AnimationImporter",
  391. false,
  392. "Mesh at index %d is invalid. This scene file may be corrupt and may need to be re-exported.",
  393. meshIndex);
  394. continue;
  395. }
  396. for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
  397. {
  398. aiBone* bone = mesh->mBones[boneIndex];
  399. if (!bone)
  400. {
  401. AZ_Error(
  402. "AnimationImporter",
  403. false,
  404. "Bone at index %d for mesh %s is invalid. This scene file may be corrupt and may need to be re-exported.",
  405. boneIndex,
  406. mesh->mName.C_Str());
  407. continue;
  408. }
  409. if (!IsPivotNode(bone->mName))
  410. {
  411. nonPivotBoneList.insert(bone->mName.C_Str());
  412. }
  413. }
  414. }
  415. decltype(boneAnimations) fillerAnimations;
  416. // Go through all the animations and make sure we create placeholder animations for any bones missing them
  417. for (auto&& anim : boneAnimations)
  418. {
  419. for (auto boneName : nonPivotBoneList)
  420. {
  421. if (!boneAnimations.contains(boneName) &&
  422. !fillerAnimations.contains(boneName))
  423. {
  424. // Create 1 key for each type that just copies the current transform
  425. ConsolidatedNodeAnim emptyAnimation;
  426. auto node = scene->mRootNode->FindNode(boneName.c_str());
  427. aiMatrix4x4 globalTransform = GetConcatenatedLocalTransform(node);
  428. aiVector3D position, scale;
  429. aiQuaternion rotation;
  430. globalTransform.Decompose(scale, rotation, position);
  431. emptyAnimation.mNumRotationKeys = emptyAnimation.mNumPositionKeys = emptyAnimation.mNumScalingKeys = 1;
  432. emptyAnimation.m_ownedPositionKeys.emplace_back(0, position);
  433. emptyAnimation.mPositionKeys = emptyAnimation.m_ownedPositionKeys.data();
  434. emptyAnimation.m_ownedRotationKeys.emplace_back(0, rotation);
  435. emptyAnimation.mRotationKeys = emptyAnimation.m_ownedRotationKeys.data();
  436. emptyAnimation.m_ownedScalingKeys.emplace_back(0, scale);
  437. emptyAnimation.mScalingKeys = emptyAnimation.m_ownedScalingKeys.data();
  438. fillerAnimations.insert(
  439. AZStd::make_pair(boneName, AZStd::make_pair(anim.second.first, AZStd::move(emptyAnimation))));
  440. }
  441. }
  442. }
  443. boneAnimations.insert(AZStd::make_move_iterator(fillerAnimations.begin()), AZStd::make_move_iterator(fillerAnimations.end()));
  444. auto animItr = boneAnimations.equal_range(currentNode->mName.C_Str());
  445. if (animItr.first == animItr.second)
  446. {
  447. return combinedAnimationResult.GetResult();
  448. }
  449. bool onlyOne = false;
  450. for (auto it = animItr.first; it != animItr.second; ++it)
  451. {
  452. if (onlyOne)
  453. {
  454. AZ_Error("AnimationImporter", false, "Bone %s has multiple animations. Only 1 animation per bone is supported", currentNode->mName.C_Str());
  455. break;
  456. }
  457. const aiAnimation* animation = it->second.first;
  458. const ConsolidatedNodeAnim* anim = &it->second.second;
  459. // We don't currently handle having a different number of keys, with 1 exception:
  460. // 1 key is essentially a constant so we do handle that case
  461. if ((anim->mNumPositionKeys != anim->mNumRotationKeys && anim->mNumPositionKeys > 1 && anim->mNumRotationKeys > 1) ||
  462. (anim->mNumPositionKeys != anim->mNumScalingKeys && anim->mNumPositionKeys > 1 && anim->mNumScalingKeys > 1) ||
  463. (anim->mNumRotationKeys != anim->mNumScalingKeys && anim->mNumRotationKeys > 1 && anim->mNumScalingKeys > 1))
  464. {
  465. AZ_Error("AnimationImporter", false, "Bone Animation with different number of position (%d)/rotation (%d)/scaling (%d) keys not supported",
  466. anim->mNumPositionKeys, anim->mNumRotationKeys, anim->mNumScalingKeys);
  467. return Events::ProcessingResult::Failure;
  468. }
  469. // Resample the animations at a fixed time step. This matches the behaviour of
  470. // the previous SDK used. Longer term, this could be data driven, or based on the
  471. // smallest time step between key frames.
  472. // AssImp has an animation->mTicksPerSecond and animation->mDuration, but those
  473. // are less predictable than just using a fixed time step.
  474. // AssImp documentation claims animation->mDuration is the duration of the animation in ticks, but
  475. // not all animations we've tested follow that pattern. Sometimes duration is in seconds.
  476. const size_t numKeyFrames = GetNumKeyFrames(
  477. AZStd::max(AZStd::max(anim->mNumScalingKeys, anim->mNumPositionKeys), anim->mNumRotationKeys),
  478. animation->mDuration,
  479. animation->mTicksPerSecond);
  480. AZStd::shared_ptr<AZ::SceneData::GraphData::AnimationData> createdAnimationData =
  481. AZStd::make_shared<AZ::SceneData::GraphData::AnimationData>();
  482. createdAnimationData->ReserveKeyFrames(numKeyFrames);
  483. createdAnimationData->SetTimeStepBetweenFrames(s_defaultTimeStepBetweenFrames);
  484. AZ::u32 lastScaleIndex = 0;
  485. AZ::u32 lastPositionIndex = 0;
  486. AZ::u32 lastRotationIndex = 0;
  487. for (AZ::u32 frame = 0; frame < numKeyFrames; ++frame)
  488. {
  489. const double time = GetTimeForFrame(frame, animation->mTicksPerSecond);
  490. aiVector3D scale(1.0f, 1.0f, 1.0f);
  491. aiVector3D position(0.0f, 0.0f, 0.0f);
  492. aiQuaternion rotation(1.0f, 0.0f, 0.0f, 0.0f);
  493. if (!SampleKeyFrame(scale, anim->mScalingKeys, anim->mNumScalingKeys, time, lastScaleIndex) ||
  494. !SampleKeyFrame(position, anim->mPositionKeys, anim->mNumPositionKeys, time, lastPositionIndex) ||
  495. !SampleKeyFrame(rotation, anim->mRotationKeys, anim->mNumRotationKeys, time, lastRotationIndex))
  496. {
  497. return Events::ProcessingResult::Failure;
  498. }
  499. aiMatrix4x4 transform(scale, rotation, position);
  500. DataTypes::MatrixType animTransform = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(transform);
  501. context.m_sourceSceneSystem.SwapTransformForUpAxis(animTransform);
  502. context.m_sourceSceneSystem.ConvertBoneUnit(animTransform);
  503. createdAnimationData->AddKeyFrame(animTransform);
  504. }
  505. Containers::SceneGraph::NodeIndex addNode = context.m_scene.GetGraph().AddChild(
  506. context.m_currentGraphPosition, nodeName.c_str(), AZStd::move(createdAnimationData));
  507. context.m_scene.GetGraph().MakeEndPoint(addNode);
  508. onlyOne = true;
  509. }
  510. combinedAnimationResult += Events::ProcessingResult::Success;
  511. return combinedAnimationResult.GetResult();
  512. }
  513. Events::ProcessingResult AssImpAnimationImporter::ImportBlendShapeAnimation(
  514. AssImpSceneNodeAppendedContext& context,
  515. const aiAnimation* animation,
  516. const aiMeshMorphAnim* meshMorphAnim,
  517. const aiMesh* mesh)
  518. {
  519. if (meshMorphAnim->mNumKeys == 0)
  520. {
  521. return Events::ProcessingResult::Ignored;
  522. }
  523. Events::ProcessingResultCombiner combinedAnimationResult;
  524. // In:
  525. // Key index
  526. // Time
  527. // Values in AssImp, Channel in FBX SDK
  528. // Weights in AssImp, Values in FBX SDK
  529. // Num values & weights
  530. // Out:
  531. // One BlendShapeAnimationData per value (Channel in FBX SDK) index
  532. // SetTimeStepBetweenFrames set on the animation data
  533. // Keyframes. Weights (Values in FBX SDK) per key time.
  534. // Keyframes generated for every single frame of the animation.
  535. typedef AZStd::map<int, AZStd::vector<KeyData>> ValueToKeyDataMap;
  536. ValueToKeyDataMap valueToKeyDataMap;
  537. // Key time can be less than zero, normalize to have zero be the lowest time.
  538. double keyOffset = 0;
  539. for (unsigned int keyIdx = 0; keyIdx < meshMorphAnim->mNumKeys; keyIdx++)
  540. {
  541. aiMeshMorphKey& key = meshMorphAnim->mKeys[keyIdx];
  542. for (unsigned int valIdx = 0; valIdx < key.mNumValuesAndWeights; ++valIdx)
  543. {
  544. int currentValue = key.mValues[valIdx];
  545. KeyData thisKey(static_cast<float>(key.mWeights[valIdx]), static_cast<float>(key.mTime));
  546. valueToKeyDataMap[currentValue].insert(
  547. AZStd::upper_bound(valueToKeyDataMap[currentValue].begin(), valueToKeyDataMap[currentValue].end(),thisKey),
  548. thisKey);
  549. if (key.mTime < keyOffset)
  550. {
  551. keyOffset = key.mTime;
  552. }
  553. }
  554. }
  555. for (const auto& [meshIdx, keys] : valueToKeyDataMap)
  556. {
  557. if (static_cast<AZ::u32>(meshIdx) >= mesh->mNumAnimMeshes)
  558. {
  559. AZ_Error(
  560. "AnimationImporter", false,
  561. "Mesh %s has an animation mesh index reference of %d, but only has %d animation meshes. Skipping importing this. This is an error in the source scene file that should be corrected.",
  562. mesh->mName.C_Str(), meshIdx, mesh->mNumAnimMeshes);
  563. continue;
  564. }
  565. AZStd::shared_ptr<AZ::SceneData::GraphData::BlendShapeAnimationData> morphAnimNode =
  566. AZStd::make_shared<AZ::SceneData::GraphData::BlendShapeAnimationData>();
  567. const size_t numKeyFrames = GetNumKeyFrames(static_cast<AZ::u32>(keys.size()), animation->mDuration, animation->mTicksPerSecond);
  568. morphAnimNode->ReserveKeyFrames(numKeyFrames);
  569. morphAnimNode->SetTimeStepBetweenFrames(s_defaultTimeStepBetweenFrames);
  570. aiAnimMesh* aiAnimMesh = mesh->mAnimMeshes[meshIdx];
  571. AZStd::string_view nodeName(aiAnimMesh->mName.C_Str());
  572. AZ::u32 keyIdx = 0;
  573. for (AZ::u32 frame = 0; frame < numKeyFrames; ++frame)
  574. {
  575. const double time = GetTimeForFrame(frame, animation->mTicksPerSecond);
  576. float weight = 0;
  577. if (!SampleKeyFrame(weight, keys, static_cast<AZ::u32>(keys.size()), time + keyOffset, keyIdx))
  578. {
  579. return Events::ProcessingResult::Failure;
  580. }
  581. morphAnimNode->AddKeyFrame(weight);
  582. }
  583. // Some DCC tools, like Maya, include a full path separated by '.' in the node names.
  584. // For example, "cone_skin_blendShapeNode.cone_squash"
  585. // Downstream processing doesn't want anything but the last part of that node name,
  586. // so find the last '.' and remove anything before it.
  587. const size_t dotIndex = nodeName.find_last_of('.');
  588. nodeName = nodeName.substr(dotIndex + 1);
  589. morphAnimNode->SetBlendShapeName(nodeName.data());
  590. // Duplicates can exist if an anim mesh had a name with a suffix like .001, in that case
  591. // AssImp will strip off that suffix. Note that this behavior is separate from the
  592. // scan for a period in the node name that came before this.
  593. AZStd::string originalNodeName(AZStd::string::format("%s_%s", s_animationNodeName, nodeName.data()));
  594. AZStd::string animNodeName(originalNodeName);
  595. if (RenamedNodesMap::SanitizeNodeName(
  596. animNodeName, context.m_scene.GetGraph(), context.m_currentGraphPosition, originalNodeName.c_str()))
  597. {
  598. AZ_Warning(
  599. "AnimationImporter", false,
  600. "Duplicate animations were found with the name %s on mesh %s. The duplicate will be named %s.",
  601. originalNodeName.c_str(), mesh->mName.C_Str(), animNodeName.c_str());
  602. }
  603. Containers::SceneGraph::NodeIndex addNode = context.m_scene.GetGraph().AddChild(
  604. context.m_currentGraphPosition, animNodeName.c_str(), AZStd::move(morphAnimNode));
  605. context.m_scene.GetGraph().MakeEndPoint(addNode);
  606. }
  607. return Events::ProcessingResult::Success;
  608. }
  609. } // namespace SceneBuilder
  610. } // namespace SceneAPI
  611. } // namespace AZ