// // Created by martin on 11.04.25. // #ifndef ANIMLIBRARY_H #define ANIMLIBRARY_H #include #include "AnimGraph/AnimGraphData.h" #include "ozz/base/io/archive.h" #include "ozz/base/io/stream.h" #include "ozz/base/log.h" /** Manage a set of animations used for an AnimGraph. * * By default, it behaves like a resource that allows to resolve animation names to * AnimationResources. However, it can also trigger loading of the referenced resources from their * filenames. This only happens on-demand. */ struct AnimLibrary { typedef std::map AnimationResourceMap; AnimationResourceMap mAnimations = {}; std::vector mManagedAnimations = {}; static constexpr const char* EXTERNAL_ANIMATION = ""; AnimLibrary() = default; ~AnimLibrary() { Reset(); } bool AddAnimation( const std::string& name, ozz::animation::Animation* animation) { AnimationResource animation_resource; animation_resource.m_name = name; animation_resource.m_filename = EXTERNAL_ANIMATION; animation_resource.m_animation = animation; mAnimations[name] = animation_resource; return true; } bool AddAnimationFile(const std::string& name, const std::string& filename) { if (mAnimations.find(name) != mAnimations.end()) { std::cerr << "Cannot add animation '" << name << "' to library. Animation already exists." << std::endl; return false; } AnimationResource animation_resource; animation_resource.m_name = name; animation_resource.m_animation = nullptr; animation_resource.m_filename = filename; mAnimations[name] = animation_resource; return true; } void Reset() { for (ozz::animation::Animation* animation : mManagedAnimations) { assert(animation->num_tracks() < 5); delete animation; } mManagedAnimations.clear(); mAnimations.clear(); } void LoadAnimations() { for (AnimationResourceMap::iterator iter = mAnimations.begin(); iter != mAnimations.end(); ++iter) { if (iter->second.m_filename == EXTERNAL_ANIMATION) { continue; } if (iter->second.m_animation != nullptr) { continue; } assert(!iter->second.m_filename.empty()); ozz::io::File file(iter->second.m_filename.c_str(), "rb"); if (!file.opened()) { ozz::log::Err() << "Failed to open animation file " << iter->second.m_filename << "." << std::endl; continue; } ozz::io::IArchive archive(&file); if (!archive.TestTag()) { ozz::log::Err() << "Failed to load animation instance from file " << iter->second.m_filename << "." << std::endl; continue; } iter->second.m_animation = new ozz::animation::Animation; archive >> *iter->second.m_animation; mManagedAnimations.push_back(iter->second.m_animation); } } }; inline void to_json(nlohmann::json& j, const AnimLibrary& animation_library) { j["type"] = "AnimationLibrary"; for (AnimLibrary::AnimationResourceMap::const_iterator iter = animation_library.mAnimations.cbegin(); iter != animation_library.mAnimations.cend(); ++iter) { j["animations"][iter->first] = iter->second; } } inline void from_json(const nlohmann::json& j, AnimLibrary& animation_library) { animation_library.Reset(); if (!j.contains("type") || j["type"] != "AnimationLibrary") { std::cerr << "Invalid type. Expected 'AnimationLibrary'." << std::endl; } if (!j.contains("animations")) { std::cerr << "Invalid AnimationLibrary. Expected 'animations' key." << std::endl; } for (nlohmann::json::const_iterator iter = j["animations"].begin(); iter != j["animations"].cend(); ++iter) { AnimationResource animation_resource = *iter; if (!animation_resource.m_filename.empty()) { animation_library.AddAnimationFile( iter.key(), j["animations"][iter.key()]["filename"]); animation_library.mAnimations[iter.key()].m_sync_track = animation_resource.m_sync_track; } else { animation_library.mAnimations[iter.key()] = *iter; } } } #endif //ANIMLIBRARY_H