400 lines
11 KiB
C++
400 lines
11 KiB
C++
#pragma once
|
||
|
||
#include <event/events.h>
|
||
#include <module/module.h>
|
||
#include <module/module_registry.h>
|
||
#include <assets/handle.h>
|
||
#include <assets/asset_storage.h>
|
||
#include <assets/asset_loader.h>
|
||
#include <renderer/texture.h>
|
||
#include <renderer/shader.h>
|
||
#include <renderer/material.h>
|
||
#include <renderer/mesh.h>
|
||
#include <functional>
|
||
#include <memory>
|
||
#include <mutex>
|
||
#include <string>
|
||
#include <unordered_map>
|
||
#include <vector>
|
||
|
||
namespace extra2d {
|
||
|
||
/**
|
||
* @brief 资源管理模块(主流 ECS 风格)
|
||
*
|
||
* 设计参考 Bevy/Amethyst:
|
||
* - AssetStorage: 密集存储资源
|
||
* - load<T>(): 统一加载接口
|
||
* - Handle<T>: 轻量级句柄
|
||
*/
|
||
class AssetsModule : public Module {
|
||
E2D_REGISTER_MODULE(AssetsModule, "Assets", 2)
|
||
|
||
public:
|
||
AssetsModule();
|
||
~AssetsModule() override;
|
||
|
||
bool init() override;
|
||
void shutdown() override;
|
||
|
||
/**
|
||
* @brief 在 OpenGL 上下文就绪后创建默认资源
|
||
*/
|
||
void onGLContextReady();
|
||
|
||
//===========================================================================
|
||
// 核心加载接口(模板方法,主流设计)
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 加载资源(自动缓存,重复加载返回已有)
|
||
*
|
||
* 示例:
|
||
* @code
|
||
* Handle<Texture> tex = assets->load<Texture>("player.png");
|
||
* Handle<Shader> shader = assets->load<Shader>("sprite.vert", "sprite.frag");
|
||
* @endcode
|
||
*
|
||
* @tparam T 资源类型
|
||
* @param path 文件路径
|
||
* @return 资源句柄
|
||
*/
|
||
template<typename T, typename... Args>
|
||
Handle<T> load(const std::string& path, Args&&... args);
|
||
|
||
/**
|
||
* @brief 从内存加载资源
|
||
* @tparam T 资源类型
|
||
* @param key 缓存键名
|
||
* @param data 数据指针
|
||
* @param size 数据大小
|
||
* @return 资源句柄
|
||
*/
|
||
template<typename T>
|
||
Handle<T> loadFromMemory(const std::string& key, const uint8_t* data, size_t size);
|
||
|
||
/**
|
||
* @brief 获取资源(返回指针,可能为 nullptr)
|
||
*
|
||
* 示例:
|
||
* @code
|
||
* Texture* tex = assets->get(handle);
|
||
* if (tex) {
|
||
* tex->bind();
|
||
* }
|
||
* @endcode
|
||
*/
|
||
template<typename T>
|
||
T* get(Handle<T> handle);
|
||
|
||
/**
|
||
* @brief 获取资源(返回智能指针)
|
||
*/
|
||
template<typename T>
|
||
Ptr<T> getPtr(Handle<T> handle);
|
||
|
||
/**
|
||
* @brief 检查句柄是否有效
|
||
*/
|
||
template<typename T>
|
||
bool isValid(Handle<T> handle) const;
|
||
|
||
/**
|
||
* @brief 移除资源
|
||
*/
|
||
template<typename T>
|
||
void remove(Handle<T> handle);
|
||
|
||
//===========================================================================
|
||
// 批量加载(目录/包)
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 加载整个目录
|
||
* @tparam T 资源类型
|
||
* @param directory 目录路径
|
||
* @param pattern 文件模式(如 "*.png")
|
||
* @param recursive 是否递归
|
||
* @return 句柄列表
|
||
*/
|
||
template<typename T>
|
||
std::vector<Handle<T>> loadDir(const std::string& directory,
|
||
const std::string& pattern = "*",
|
||
bool recursive = true);
|
||
|
||
//===========================================================================
|
||
// 异步加载
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 异步加载资源
|
||
* @tparam T 资源类型
|
||
* @param path 文件路径
|
||
* @param callback 加载完成回调
|
||
*/
|
||
template<typename T>
|
||
void loadAsync(const std::string& path,
|
||
std::function<void(Handle<T>)> callback);
|
||
|
||
//===========================================================================
|
||
// 注册加载器(扩展点)
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 注册资源加载器
|
||
* @tparam T 资源类型
|
||
* @param loader 加载器实例
|
||
*/
|
||
template<typename T>
|
||
void registerLoader(std::unique_ptr<AssetLoader<T>> loader);
|
||
|
||
//===========================================================================
|
||
// 默认资源
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 创建默认资源
|
||
* @return 是否成功
|
||
*/
|
||
bool createDefaultResources();
|
||
|
||
/**
|
||
* @brief 销毁默认资源
|
||
*/
|
||
void destroyDefaultResources();
|
||
|
||
Handle<Texture> getDefaultTexture();
|
||
Handle<Shader> getDefaultShader();
|
||
Handle<Material> getDefaultMaterial();
|
||
Handle<Mesh> getDefaultQuad();
|
||
|
||
Texture* getDefaultTexturePtr();
|
||
Shader* getDefaultShaderPtr();
|
||
Material* getDefaultMaterialPtr();
|
||
Mesh* getDefaultQuadPtr();
|
||
|
||
//===========================================================================
|
||
// 热重载
|
||
//===========================================================================
|
||
|
||
/**
|
||
* @brief 启用/禁用热重载
|
||
* @param enable 是否启用
|
||
*/
|
||
void enableHotReload(bool enable);
|
||
|
||
/**
|
||
* @brief 检查文件变更并重新加载
|
||
*/
|
||
void checkForChanges();
|
||
|
||
//===========================================================================
|
||
// 统计
|
||
//===========================================================================
|
||
|
||
struct Stats {
|
||
size_t textureCount = 0;
|
||
size_t shaderCount = 0;
|
||
size_t materialCount = 0;
|
||
size_t meshCount = 0;
|
||
};
|
||
|
||
Stats getStats() const;
|
||
|
||
private:
|
||
// 资源存储
|
||
AssetStorage<Texture> textures_;
|
||
AssetStorage<Shader> shaders_;
|
||
AssetStorage<Material> materials_;
|
||
AssetStorage<Mesh> meshes_;
|
||
|
||
// 加载器
|
||
std::unique_ptr<AssetLoader<Texture>> textureLoader_;
|
||
std::unique_ptr<AssetLoader<Shader>> shaderLoader_;
|
||
|
||
// 路径缓存(避免重复加载)
|
||
std::unordered_map<std::string, Handle<Texture>> texturePathCache_;
|
||
std::unordered_map<std::string, Handle<Shader>> shaderPathCache_;
|
||
|
||
// 默认资源
|
||
Handle<Texture> defaultTexture_;
|
||
Handle<Shader> defaultShader_;
|
||
Handle<Material> defaultMaterial_;
|
||
Handle<Mesh> defaultQuad_;
|
||
|
||
// 热重载
|
||
bool hotReloadEnabled_ = false;
|
||
|
||
// 线程安全
|
||
mutable std::mutex mutex_;
|
||
|
||
// 事件监听器
|
||
std::unique_ptr<events::OnShow::Listener> onShowListener_;
|
||
|
||
// 标记默认资源是否已创建
|
||
bool defaultResourcesCreated_ = false;
|
||
};
|
||
|
||
// 全局访问
|
||
AssetsModule* getAssets();
|
||
|
||
//===========================================================================
|
||
// 模板实现
|
||
//===========================================================================
|
||
|
||
template<typename T, typename... Args>
|
||
Handle<T> AssetsModule::load(const std::string& path, Args&&... args) {
|
||
static_assert(sizeof...(Args) == 0 || sizeof...(Args) == 1,
|
||
"load() accepts 0 or 1 additional arguments");
|
||
return Handle<T>::invalid();
|
||
}
|
||
|
||
template<>
|
||
Handle<Texture> AssetsModule::load<Texture>(const std::string& path);
|
||
|
||
template<>
|
||
Handle<Shader> AssetsModule::load<Shader>(const std::string& path);
|
||
|
||
template<>
|
||
Handle<Shader> AssetsModule::load<Shader>(const std::string& vertPath, const std::string& fragPath);
|
||
|
||
template<typename T>
|
||
Handle<T> AssetsModule::loadFromMemory(const std::string& key, const uint8_t* data, size_t size) {
|
||
return Handle<T>::invalid();
|
||
}
|
||
|
||
template<>
|
||
Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string& key, const uint8_t* data, size_t size);
|
||
|
||
template<typename T>
|
||
T* AssetsModule::get(Handle<T> handle) {
|
||
return nullptr;
|
||
}
|
||
|
||
template<>
|
||
inline Texture* AssetsModule::get<Texture>(Handle<Texture> handle) {
|
||
return textures_.get(handle);
|
||
}
|
||
|
||
template<>
|
||
inline Shader* AssetsModule::get<Shader>(Handle<Shader> handle) {
|
||
return shaders_.get(handle);
|
||
}
|
||
|
||
template<>
|
||
inline Material* AssetsModule::get<Material>(Handle<Material> handle) {
|
||
return materials_.get(handle);
|
||
}
|
||
|
||
template<>
|
||
inline Mesh* AssetsModule::get<Mesh>(Handle<Mesh> handle) {
|
||
return meshes_.get(handle);
|
||
}
|
||
|
||
template<typename T>
|
||
Ptr<T> AssetsModule::getPtr(Handle<T> handle) {
|
||
return Ptr<T>();
|
||
}
|
||
|
||
template<>
|
||
inline Ptr<Texture> AssetsModule::getPtr<Texture>(Handle<Texture> handle) {
|
||
return textures_.getPtr(handle);
|
||
}
|
||
|
||
template<>
|
||
inline Ptr<Shader> AssetsModule::getPtr<Shader>(Handle<Shader> handle) {
|
||
return shaders_.getPtr(handle);
|
||
}
|
||
|
||
template<>
|
||
inline Ptr<Material> AssetsModule::getPtr<Material>(Handle<Material> handle) {
|
||
return materials_.getPtr(handle);
|
||
}
|
||
|
||
template<>
|
||
inline Ptr<Mesh> AssetsModule::getPtr<Mesh>(Handle<Mesh> handle) {
|
||
return meshes_.getPtr(handle);
|
||
}
|
||
|
||
template<typename T>
|
||
bool AssetsModule::isValid(Handle<T> handle) const {
|
||
return false;
|
||
}
|
||
|
||
template<>
|
||
inline bool AssetsModule::isValid<Texture>(Handle<Texture> handle) const {
|
||
return textures_.isValid(handle);
|
||
}
|
||
|
||
template<>
|
||
inline bool AssetsModule::isValid<Shader>(Handle<Shader> handle) const {
|
||
return shaders_.isValid(handle);
|
||
}
|
||
|
||
template<>
|
||
inline bool AssetsModule::isValid<Material>(Handle<Material> handle) const {
|
||
return materials_.isValid(handle);
|
||
}
|
||
|
||
template<>
|
||
inline bool AssetsModule::isValid<Mesh>(Handle<Mesh> handle) const {
|
||
return meshes_.isValid(handle);
|
||
}
|
||
|
||
template<typename T>
|
||
void AssetsModule::remove(Handle<T> handle) {
|
||
}
|
||
|
||
template<>
|
||
inline void AssetsModule::remove<Texture>(Handle<Texture> handle) {
|
||
textures_.remove(handle);
|
||
}
|
||
|
||
template<>
|
||
inline void AssetsModule::remove<Shader>(Handle<Shader> handle) {
|
||
shaders_.remove(handle);
|
||
}
|
||
|
||
template<>
|
||
inline void AssetsModule::remove<Material>(Handle<Material> handle) {
|
||
materials_.remove(handle);
|
||
}
|
||
|
||
template<>
|
||
inline void AssetsModule::remove<Mesh>(Handle<Mesh> handle) {
|
||
meshes_.remove(handle);
|
||
}
|
||
|
||
template<typename T>
|
||
std::vector<Handle<T>> AssetsModule::loadDir(const std::string& directory,
|
||
const std::string& pattern,
|
||
bool recursive) {
|
||
return {};
|
||
}
|
||
|
||
template<>
|
||
std::vector<Handle<Texture>> AssetsModule::loadDir<Texture>(const std::string& directory,
|
||
const std::string& pattern,
|
||
bool recursive);
|
||
|
||
template<typename T>
|
||
void AssetsModule::loadAsync(const std::string& path,
|
||
std::function<void(Handle<T>)> callback) {
|
||
}
|
||
|
||
template<>
|
||
void AssetsModule::loadAsync<Texture>(const std::string& path,
|
||
std::function<void(Handle<Texture>)> callback);
|
||
|
||
template<typename T>
|
||
void AssetsModule::registerLoader(std::unique_ptr<AssetLoader<T>> loader) {
|
||
}
|
||
|
||
template<>
|
||
void AssetsModule::registerLoader<Texture>(std::unique_ptr<AssetLoader<Texture>> loader);
|
||
|
||
template<>
|
||
void AssetsModule::registerLoader<Shader>(std::unique_ptr<AssetLoader<Shader>> loader);
|
||
|
||
} // namespace extra2d
|