SleakEngine 1.0.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
ModelLoader.hpp
Go to the documentation of this file.
1#ifndef _MODELLOADER_HPP_
2#define _MODELLOADER_HPP_
3
4#include <Core/OSDef.hpp>
5#include <Math/Vector.hpp>
6#include <Math/Matrix.hpp>
7#include <Math/Quaternion.hpp>
8#include <string>
9#include <unordered_map>
10#include <vector>
11
12struct aiNode;
13struct aiScene;
14struct aiMesh;
15struct aiMaterial;
16struct aiAnimation;
17
18namespace Sleak {
19
20 class GameObject;
21 class Material;
22 class Texture;
23 class Skeleton;
24 class AnimationClip;
25 struct MeshData;
26 template <typename T> class RefPtr;
27
28 /// Import-time adjustments applied while loading a model file.
29 /// @ingroup rendering
31 float scaleFactor = 1.0f;
32 bool flipUVs = true;
33 bool flipNormals = false;
34 bool flipWinding = false;
37 };
38
39 /// Per-load texture cache to avoid loading the same texture file multiple times.
40 using TextureCache = std::unordered_map<std::string, ::Sleak::Texture*>;
41
42 /// Assimp-backed importer that builds a GameObject hierarchy (meshes, materials, optionally a skeleton) from a model file.
43 ///
44 /// Load() reads any format Assimp supports (FBX, glTF, OBJ, and the
45 /// rest) and returns the root of a ready-to-use GameObject tree with
46 /// meshes, materials, and textures attached. Rigged files also get a
47 /// Skeleton and an AnimatorComponent. The caller owns the returned
48 /// root: hand it to SceneBase::AddObject() and the scene takes over.
49 /// Load() returns nullptr on failure, so always check.
50 ///
51 /// ModelLoadOptions covers the adjustments every import pipeline
52 /// eventually needs. `scaleFactor` converts source units to meters
53 /// (0.01 for a centimeter-based rig). `flipUVs` defaults to true
54 /// because most exporters disagree with the engine's texture origin.
55 /// `flipNormals` and `flipWinding` fix models that import inside out or
56 /// with black faces, which is nearly always an exporter handedness
57 /// mismatch rather than a shading problem.
58 ///
59 /// When your mesh and your animations ship as separate files, load the
60 /// mesh once and pull the clips in with LoadAnimationsOnly(), passing
61 /// the skeleton the mesh import produced. Textures are cached for the
62 /// duration of a single Load(), so a model reusing one texture across
63 /// many materials reads it from disk once.
64 ///
65 /// @code{.cpp}
66 /// Sleak::ModelLoadOptions opts;
67 /// opts.scaleFactor = 0.01f; // source is in centimeters
68 /// opts.flipUVs = true;
69 /// opts.position = Sleak::Math::Vector3D(0.0f, 0.0f, 0.0f);
70 ///
71 /// Sleak::GameObject* model =
72 /// Sleak::ModelLoader::Load("assets/models/Mannequin.fbx", opts);
73 /// if (!model) {
74 /// SLEAK_ERROR("Failed to load mannequin");
75 /// return;
76 /// }
77 /// AddObject(model);
78 ///
79 /// // Attach clips from separate files to the imported skeleton
80 /// if (auto* anim = model->GetComponent<Sleak::AnimatorComponent>()) {
81 /// for (auto* clip : Sleak::ModelLoader::LoadAnimationsOnly(
82 /// "assets/animations/Walking.fbx", anim->GetSkeleton())) {
83 /// if (!clip) continue;
84 /// clip->name = "Walking";
85 /// anim->AddClip(clip);
86 /// }
87 /// anim->Play("Walking", true);
88 /// }
89 /// @endcode
90 ///
91 /// @see ModelLoadOptions, GameObject, AnimatorComponent, Skeleton,
92 /// AnimationClip, Material
93 /// @ingroup rendering
94 class ENGINE_API ModelLoader {
95 public:
96 /// Imports a model file and returns the root of the resulting GameObject hierarchy.
97 static GameObject* Load(const std::string& filePath,
98 const ModelLoadOptions& options = {});
99
100 /// Load only animations from an FBX, reusing an existing skeleton.
101 static std::vector<AnimationClip*> LoadAnimationsOnly(
102 const std::string& filePath, Skeleton* skeleton);
103
104 private:
105 /// Static mesh path (with PreTransformVertices).
106 static void ProcessNode(aiNode* node, const aiScene* scene,
107 GameObject* parent, const std::string& directory,
108 const ModelLoadOptions& options,
109 TextureCache& textureCache);
110
111 /// Animated mesh path (without PreTransformVertices).
112 static void ProcessNodeAnimated(aiNode* node, const aiScene* scene,
113 GameObject* parent, const std::string& directory,
114 const ModelLoadOptions& options,
115 TextureCache& textureCache,
116 Skeleton* skeleton,
117 std::vector<AnimationClip*>& clips);
118
119 /// Converts one Assimp mesh into engine MeshData, wiring up bone weights when a skeleton is given.
120 static MeshData ProcessMesh(aiMesh* mesh, const ModelLoadOptions& options,
121 Skeleton* skeleton = nullptr);
122
123 /// Converts one Assimp material into an engine Material, loading its textures through textureCache.
124 static RefPtr<Material> ProcessMaterial(aiMaterial* mat,
125 const aiScene* scene,
126 const std::string& directory,
127 TextureCache& textureCache,
128 bool skinned = false);
129
130 /// Resolves and loads a single texture slot off an Assimp material, reusing textureCache when possible.
131 static ::Sleak::Texture* LoadMaterialTexture(aiMaterial* mat, int type,
132 const aiScene* scene,
133 const std::string& directory,
134 TextureCache& textureCache);
135
136 /// Builds a Skeleton from the scene's bone hierarchy.
137 static Skeleton* ExtractSkeleton(const aiScene* scene);
138 /// Converts every Assimp animation in the scene into engine AnimationClips bound to skeleton.
139 static std::vector<AnimationClip*> ExtractAnimations(const aiScene* scene,
140 Skeleton* skeleton);
141 /// Recursively registers each Assimp node as a skeleton bone under parentId.
142 static void BuildBoneHierarchy(const aiNode* node, Skeleton* skeleton,
143 int parentId);
144 /// Recursively mirrors the Assimp node tree into the skeleton's bone tree, returning the created bone's id.
145 static int BuildNodeTree(const aiNode* node, Skeleton* skeleton);
146
147 /// Assimp matrix to engine matrix conversion.
148 static Math::Matrix4 ConvertMatrix(const void* aiMat);
149 };
150
151} // namespace Sleak
152
153#endif // _MODELLOADER_HPP_
Represents a quaternion for 3D rotations.
static GameObject * Load(const std::string &filePath, const ModelLoadOptions &options={})
Imports a model file and returns the root of the resulting GameObject hierarchy.
static std::vector< AnimationClip * > LoadAnimationsOnly(const std::string &filePath, Skeleton *skeleton)
Load only animations from an FBX, reusing an existing skeleton.
Matrix< float, 4, 4 > Matrix4
Definition Matrix.hpp:413
Root namespace for everything the engine exposes.
Definition Camera.hpp:10
std::unordered_map< std::string, ::Sleak::Texture * > TextureCache
Per-load texture cache to avoid loading the same texture file multiple times.
Math::Vector3D position
Math::Quaternion rotation