diff --git a/Core/Source/bee/Convert/SceneConverter.Animation.cpp b/Core/Source/bee/Convert/SceneConverter.Animation.cpp index bbf962e..af48e7a 100644 --- a/Core/Source/bee/Convert/SceneConverter.Animation.cpp +++ b/Core/Source/bee/Convert/SceneConverter.Animation.cpp @@ -1,4 +1,5 @@ +#include #include #include #include @@ -44,6 +45,37 @@ fbxsdk::FbxQuaternion slerp(const fbxsdk::FbxQuaternion &from_, } namespace bee { +/// +/// Animation on node {} has too long animation. Usually because negative timeline +/// +class InvalidNodeAnimationRange : public NodeError { +public: + constexpr static inline std::u8string_view code = + u8"invalid_node_animation_range"; + + const double limit; + + const double received; + + const std::string take_name; + + InvalidNodeAnimationRange(const std::string_view &node_, + const double limit_, + const double received_, + const std::string_view take_name_) + : NodeError(node_), limit(limit_), received(received_), + take_name(take_name_) { + } +}; + +void to_json(nlohmann::json &j_, const InvalidNodeAnimationRange &error_) { + to_json(j_, + static_cast &>(error_)); + j_["take"] = error_.take_name; + j_["limit"] = error_.limit; + j_["received"] = error_.received; +} + void SceneConverter::_convertAnimation(fbxsdk::FbxScene &fbx_scene_) { const auto nAnimStacks = fbx_scene_.GetSrcObjectCount(); for (std::remove_const_t iAnimStack = 0; @@ -59,7 +91,7 @@ void SceneConverter::_convertAnimation(fbxsdk::FbxScene &fbx_scene_) { continue; } - const auto timeSpan = _getAnimStackTimeSpan(*animStack); + const auto timeSpan = _getAnimStackTimeSpan(*animStack, fbx_scene_); if (timeSpan.GetDuration() == 0) { if (_options.verbose) { _log(Logger::Level::verbose, u8"The animation layer's duration is 0."); @@ -93,34 +125,64 @@ void SceneConverter::_convertAnimation(fbxsdk::FbxScene &fbx_scene_) { } } -fbxsdk::FbxTimeSpan SceneConverter::_getAnimStackTimeSpan( - const fbxsdk::FbxAnimStack &fbx_anim_stack_) { +namespace { +constexpr double maxAllowedAnimDurationSeconds = 60.0 * 10.0; +} + +fbxsdk::FbxTimeSpan +SceneConverter::_getAnimStackTimeSpan(fbxsdk::FbxAnimStack &fbx_anim_stack_, + fbxsdk::FbxScene &fbx_scene_) { const auto nAnimLayers = fbx_anim_stack_.GetMemberCount(); if (!nAnimLayers) { return {}; } + + const auto localTimeSpan = fbx_anim_stack_.GetLocalTimeSpan(); + _log(Logger::Level::verbose, + "Local time: [" + + std::to_string(localTimeSpan.GetStart().GetSecondDouble()) + ", " + + std::to_string(localTimeSpan.GetStop().GetSecondDouble()) + "]"); + const auto referenceTimeSpan = fbx_anim_stack_.GetReferenceTimeSpan(); + _log(Logger::Level::verbose, + "Reference time: [" + + std::to_string(referenceTimeSpan.GetStart().GetSecondDouble()) + + ", " + + std::to_string(referenceTimeSpan.GetStop().GetSecondDouble()) + "]"); + fbxsdk::FbxTimeSpan animTimeSpan; for (std::remove_const_t iAnimLayer = 0; iAnimLayer < nAnimLayers; ++iAnimLayer) { const auto animLayer = fbx_anim_stack_.GetMember(iAnimLayer); - const auto nCurveNodes = - animLayer->GetMemberCount(); - for (std::remove_const_t iCurveNode = 0; - iCurveNode < nCurveNodes; ++iCurveNode) { - const auto curveNode = - animLayer->GetMember(iCurveNode); - if (!curveNode->IsAnimated()) { - continue; - } - fbxsdk::FbxTimeSpan curveNodeInterval; - if (!curveNode->GetAnimationInterval(curveNodeInterval)) { - continue; - } - animTimeSpan.UnionAssignment(curveNodeInterval); - } + + const std::function getInterval = + [&](fbxsdk::FbxNode &fbx_node_) { + fbxsdk::FbxTimeSpan interval; + if (fbx_node_.GetAnimationInterval(interval, &fbx_anim_stack_, + iAnimLayer)) { + if (const auto duration = interval.GetDuration().GetSecondDouble(); + duration > maxAllowedAnimDurationSeconds) { + _log(Logger::Level::warning, + InvalidNodeAnimationRange{ + fbx_node_.GetName(), maxAllowedAnimDurationSeconds, + duration, fbx_anim_stack_.GetName()}); + } else { + animTimeSpan.UnionAssignment(interval); + } + } + + const auto nChilds = fbx_node_.GetChildCount(); + for (std::remove_const_t iChild = 0; + iChild < nChilds; ++iChild) { + auto childNode = fbx_node_.GetChild(iChild); + getInterval(*childNode); + } + }; + + getInterval(*fbx_scene_.GetRootNode()); } + return animTimeSpan; } diff --git a/Core/Source/bee/Convert/SceneConverter.h b/Core/Source/bee/Convert/SceneConverter.h index 2b5b6ca..fe224ae 100644 --- a/Core/Source/bee/Convert/SceneConverter.h +++ b/Core/Source/bee/Convert/SceneConverter.h @@ -373,7 +373,8 @@ class SceneConverter { void _convertAnimation(fbxsdk::FbxScene &fbx_scene_); fbxsdk::FbxTimeSpan - _getAnimStackTimeSpan(const fbxsdk::FbxAnimStack &fbx_anim_stack_); + _getAnimStackTimeSpan(fbxsdk::FbxAnimStack &fbx_anim_stack_, + fbxsdk::FbxScene &fbx_scene_); void _convertAnimationLayer(fx::gltf::Animation &glTF_animation_, fbxsdk::FbxAnimLayer &fbx_anim_layer_,