#pragma once #include #include #include #include #include #include #include #include #include // Forward declarations struct aiScene; struct aiNode; struct aiMesh; /** * SkeletonLoader * -------------- * Reads the Assimp scene's bone hierarchy and mesh skin weights, * and builds an osgAnimation scene graph: * * osgAnimation::Skeleton * └─ osgAnimation::Bone (Hips) * └─ osgAnimation::Bone (Spine) * └─ ... * * Each skinned aiMesh becomes an osgAnimation::RigGeometry instead * of a plain osg::Geometry, so the GPU handles skinning automatically. * * Usage: * SkeletonLoader loader; * auto result = loader.load(assimpScene, baseDir, morphMgr); * // result.root — attach to scene graph * // result.boneMap — name → Bone* for posing */ class MorphManager; class SkeletonLoader { public: struct Result { osg::ref_ptr root; osg::ref_ptr skeleton; std::unordered_map> boneMap; std::unordered_map bindPositions; // world pos from inverted offset bool valid = false; }; Result load(const struct aiScene* scene, const std::string& baseDir, MorphManager* morphMgr = nullptr); private: // ── Bone hierarchy ──────────────────────────────────────────────────────── /// Recursively build Bone nodes from the aiNode tree. osg::ref_ptr buildBoneTree( const aiNode* node, const std::unordered_map& boneNames, std::unordered_map>& boneMap, const osg::Matrix& parentAccum = osg::Matrix::identity()); /// Find the armature root node (first ancestor whose children are all bones). const aiNode* findArmatureRoot(const aiNode* root, const std::unordered_map& boneNames); // ── Mesh conversion ─────────────────────────────────────────────────────── /// Convert a skinned aiMesh to RigGeometry. osg::ref_ptr convertSkinnedMesh( const aiMesh* mesh, const aiScene* scene, const std::string& baseDir, MorphManager* morphMgr, const std::unordered_map>& boneMap); /// Convert an unskinned aiMesh to plain osg::Geometry (same as before). osg::ref_ptr convertStaticMesh( const aiMesh* mesh, const aiScene* scene, const std::string& baseDir, MorphManager* morphMgr); /// Shared material/texture setup used by both converters. void applyMaterial(osg::StateSet* ss, const aiMesh* mesh, const aiScene* scene, const std::string& baseDir); };