94 lines
3.3 KiB
C++
94 lines
3.3 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;
|
|
std::unordered_map<std::string, osg::Vec3> 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<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);
|
|
}; |