feat: fix scene loading that it loads from disk if not already loaded

This commit is contained in:
2026-08-11 01:52:14 +02:00
parent f298c7ada5
commit 662940793e
20 changed files with 964 additions and 434 deletions
@@ -0,0 +1,33 @@
#ifndef DESTRUM_ASSETMANAGER_H
#define DESTRUM_ASSETMANAGER_H
#include <filesystem>
#include <string_view>
#include <vector>
#include <destrum/Graphics/SkeletalAnimation.h>
#include <destrum/Graphics/Skeleton.h>
#include <destrum/Assets/AssetReference.h>
#include <destrum/Singleton.h>
#include <spdlog/spdlog.h>
#include <destrum/Util/ModelDoc.h>
class AssetManager final : public Singleton<AssetManager> {
public:
friend class Singleton<AssetManager>;
[[nodiscard]] std::filesystem::path Resolve(std::string_view reference) const;
[[nodiscard]] ModelDoc::ModelAsset LoadModel(
std::string_view reference,
const ModelDoc::LoadOptions& options = {}) const;
[[nodiscard]] std::vector<SkeletalAnimation> LoadAnimationClips(
std::string_view reference,
const Skeleton& targetSkeleton) const;
private:
AssetManager() = default;
};
#endif // DESTRUM_ASSETMANAGER_H
@@ -0,0 +1,35 @@
#ifndef DESTRUM_ASSETREFERENCE_H
#define DESTRUM_ASSETREFERENCE_H
#include <optional>
#include <string>
#include <string_view>
struct AssetReference {
std::string path;
std::string subresource;
[[nodiscard]] bool empty() const { return path.empty(); }
[[nodiscard]] std::string cacheKey() const {
return path.empty() ? std::string{} : path + "#" + subresource;
}
[[nodiscard]] static std::optional<AssetReference> fromCacheKey(
std::string_view key)
{
const auto separator = key.rfind('#');
if (separator == std::string_view::npos ||
separator == 0 ||
separator + 1 >= key.size()) {
return std::nullopt;
}
return AssetReference{
.path = std::string{key.substr(0, separator)},
.subresource = std::string{key.substr(separator + 1)},
};
}
};
#endif // DESTRUM_ASSETREFERENCE_H
@@ -28,6 +28,7 @@ public:
void Deserialize(const nlohmann::json& data) override;
void ImGuiInspector() override;
// The source reference is carried by the AssetManager-loaded clip.
void addClip(std::shared_ptr<SkeletalAnimation> clip);
void play(const std::string& name, float blendTime = 0.f);
void stop();
@@ -41,10 +42,15 @@ public:
return &m_skeleton;
}
// A source reference is required when this Animator is saved to a scene.
void setSkeleton(Skeleton skeleton) {
m_skeletonAsset = skeleton.assetReference;
m_skeleton = std::move(skeleton);
m_referencesResolved = true;
}
void ResolveReferences(const ObjectMap& objects) override;
private:
struct PlaybackState {
SkeletalAnimation* clip = nullptr;
@@ -59,14 +65,22 @@ private:
float m_blendT = 0.f;
float m_blendDuration = 0.f;
std::string m_currentClipName;
std::string m_previousClipName;
std::unordered_map<std::string, std::shared_ptr<SkeletalAnimation>> m_clips;
std::vector<std::string> m_clipOrder;
std::unordered_map<std::string, AssetReference> m_clipAssets;
AssetReference m_skeletonAsset;
bool m_referencesResolved{true};
std::vector<glm::mat4> computeJointMatrices(const Skeleton& skeleton);
glm::vec3 sampleTranslation(const SkeletalAnimation::Track& track, float t);
glm::quat sampleRotation (const SkeletalAnimation::Track& track, float t);
glm::vec3 sampleScale (const SkeletalAnimation::Track& track, float t);
void RestorePlaybackState(const nlohmann::json& data);
void LoadAssetReferences();
};
#endif // ANIMATOR_H
@@ -28,10 +28,16 @@ public:
void cleanup(GfxDevice& gfxDevice);
MaterialID addMaterial(Material material);
MaterialID addSimpleColorMaterial(
glm::vec3 color,
std::string name = {});
MaterialID addSimpleTextureMaterial(ImageID textureID);
[[nodiscard]] const Material& getMaterial(MaterialID id) const;
[[nodiscard]] const std::string& getMaterialKey(MaterialID id) const;
[[nodiscard]] std::optional<MaterialID> findMaterialByKey(std::string_view key) const;
[[nodiscard]] std::optional<MaterialID> findMaterialByName(
std::string_view name,
std::string_view sourceReference = {}) const;
[[nodiscard]] MaterialID getFreeMaterialId() const;
[[nodiscard]] MaterialID getPlaceholderMaterialId() const;
@@ -2,7 +2,9 @@
#define MATERIAL_H
#include <string>
#include <utility>
#include <glm/glm.hpp>
#include <destrum/Assets/AssetReference.h>
struct alignas(16) MaterialData {
alignas(16) glm::vec4 baseColor;
@@ -22,6 +24,19 @@ enum class TextureFilteringMode : std::uint32_t {
};
struct Material {
[[nodiscard]] static Material SimpleColor(
glm::vec3 color,
std::string materialName = {})
{
Material material{};
material.baseColor = color;
material.metallicFactor = 0.0f;
material.roughnessFactor = 1.0f;
material.emissiveFactor = 0.0f;
material.name = std::move(materialName);
return material;
}
glm::vec3 baseColor{1.f, 1.f, 1.f};
float metallicFactor{0.f};
float roughnessFactor{0.7f};
@@ -35,6 +50,7 @@ struct Material {
// ImageId emissiveTexture{NULL_IMAGE_ID};
std::string name;
AssetReference assetReference;
};
#endif //MATERIAL_H
@@ -9,6 +9,7 @@
#include <filesystem>
#include <memory>
#include <string_view>
class GfxDevice;
@@ -44,6 +45,10 @@ public:
ImageID addImageToCache(GPUImage image);
// Imports a file-backed model and uploads its mesh/material resources.
// References are keyed as "source#subresource" in the resource caches.
void loadModelAsset(GfxDevice& gfxDevice, std::string_view sourceReference);
[[nodiscard]] const GPUImage& getImage(ImageID id) const;
[[nodiscard]] ImageID getWhiteTextureID() const { return whiteImageId; }
@@ -72,4 +77,4 @@ private:
ImageID defaultNormalImageId{NULL_IMAGE_ID};
};
#endif
#endif
@@ -15,6 +15,7 @@
#include <destrum/Graphics/Resources/Buffer.h>
#include <destrum/Graphics/Frustum.h>
#include <destrum/Graphics/Skeleton.h>
#include <destrum/Assets/AssetReference.h>
// ─── CPU Mesh ─────────────────────────────────────────────────────────────────
struct CPUMesh {
@@ -36,6 +37,7 @@ struct CPUMesh {
std::vector<SkinningData> skinningData; // empty if no skeleton
std::string name;
AssetReference assetReference;
glm::vec3 minPos;
glm::vec3 maxPos;
@@ -115,4 +117,4 @@ inline std::vector<std::uint32_t> indices = {
} // namespace CubeMesh
#endif // MESH_H
#endif // MESH_H
@@ -7,8 +7,11 @@
#include <glm/gtc/quaternion.hpp>
#include <glm/vec3.hpp>
#include <destrum/Assets/AssetReference.h>
struct SkeletalAnimation {
AssetReference assetReference;
struct Keyframe {
float time;
glm::vec3 translation;
@@ -32,4 +35,4 @@ struct SkeletalAnimation {
const std::vector<std::string>& getEventsForFrame(int frame) const;
};
#endif // SKELETALANIMATION_H
#endif // SKELETALANIMATION_H
+4 -1
View File
@@ -10,6 +10,7 @@
#include <glm/gtc/matrix_transform.hpp>
#include <destrum/Graphics/ids.h>
#include <destrum/Assets/AssetReference.h>
struct Joint {
JointId id{NULL_JOINT_ID};
@@ -19,6 +20,8 @@ struct Joint {
};
struct Skeleton {
AssetReference assetReference;
struct JointNode {
JointId id{NULL_JOINT_ID};
std::vector<JointId> children;
@@ -55,4 +58,4 @@ inline void buildParentIndex(Skeleton& skeleton) {
}
}
#endif // SKELETON_H
#endif // SKELETON_H
+27 -1
View File
@@ -31,6 +31,7 @@
#include <glm/gtc/type_ptr.hpp>
#include <destrum/Graphics/Resources/Mesh.h>
#include <destrum/Assets/AssetReference.h>
#include <destrum/Graphics/SkeletalAnimation.h>
#include <destrum/Graphics/Skeleton.h>
@@ -91,6 +92,7 @@ struct TextureInfo {
struct MaterialInfo {
std::string name;
AssetReference assetReference;
glm::vec4 baseColor{1.0f};
glm::vec3 emissiveColor{0.0f};
@@ -1018,6 +1020,24 @@ static ModelAsset LoadModel(const std::string& path, const LoadOptions& options
CollectMergedPerNodeMeshes(scene, model.skeleton, options, model.primitives);
}
model.skeleton.assetReference.path = model.sourcePath;
for (std::size_t index = 0; index < model.primitives.size(); ++index) {
auto& primitive = model.primitives[index];
primitive.mesh.assetReference.path = model.sourcePath;
primitive.mesh.assetReference.subresource = primitive.sourceMeshIndex != InvalidIndex
? "mesh:" + std::to_string(primitive.sourceMeshIndex)
: "primitive:" + std::to_string(index);
}
for (std::size_t index = 0; index < model.materials.size(); ++index) {
model.materials[index].assetReference.path = model.sourcePath;
model.materials[index].assetReference.subresource = "material:" + std::to_string(index);
}
for (std::size_t index = 0; index < model.animations.size(); ++index) {
auto& animation = model.animations[index];
animation.assetReference.path = model.sourcePath;
animation.assetReference.subresource = "animation:" + std::to_string(index);
}
std::cout << "[ModelDoc] Loaded model: " << path
<< " | primitives: " << model.primitives.size()
<< " | materials: " << model.materials.size()
@@ -1121,7 +1141,13 @@ static const aiScene* LoadAnimationScene(
Assimp::Importer importer;
const aiScene* scene = LoadAnimationScene(importer, path);
return LoadAnimations(scene, targetSkeleton);
auto clips = LoadAnimations(scene, targetSkeleton);
for (std::size_t index = 0; index < clips.size(); ++index) {
auto& clip = clips[index];
clip.assetReference.path = path;
clip.assetReference.subresource = "animation:" + std::to_string(index);
}
return clips;
}
} // namespace ModelDoc