Files
vr-poser/include/SkeletonLoader.h
2026-03-16 00:22:49 -04:00

93 lines
3.2 KiB
C++

#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#include <osg/ref_ptr>
#include <osg/Group>
#include <osgAnimation/Skeleton>
#include <osgAnimation/Bone>
#include <osgAnimation/RigGeometry>
#include <osgAnimation/MorphGeometry>
// 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<osg::Group> root;
osg::ref_ptr<osgAnimation::Skeleton> skeleton;
std::unordered_map<std::string,
osg::ref_ptr<osgAnimation::Bone>> boneMap;
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<osgAnimation::Bone> buildBoneTree(
const aiNode* node,
const std::unordered_map<std::string, bool>& boneNames,
std::unordered_map<std::string,
osg::ref_ptr<osgAnimation::Bone>>& 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<std::string, bool>& boneNames);
// ── Mesh conversion ───────────────────────────────────────────────────────
/// Convert a skinned aiMesh to RigGeometry.
osg::ref_ptr<osgAnimation::RigGeometry> convertSkinnedMesh(
const aiMesh* mesh,
const aiScene* scene,
const std::string& baseDir,
MorphManager* morphMgr,
const std::unordered_map<std::string,
osg::ref_ptr<osgAnimation::Bone>>& boneMap);
/// Convert an unskinned aiMesh to plain osg::Geometry (same as before).
osg::ref_ptr<osg::Geode> 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);
};