refactor(assets): 重构资源管理系统为ECS风格
- 引入新的Handle<T>轻量级资源句柄,替代原有的uint64_t句柄 - 新增AssetStorage类实现密集存储管理 - 添加AssetsModule模块统一管理资源加载和生命周期 - 实现TextureLoader和ShaderLoader资源加载器 - 重构RendererModule移除资源管理职责,改为使用AssetsModule - 更新SpriteRenderer和相关组件使用新Handle接口 - 修改RenderTypes.h中的资源句柄类型定义
This commit is contained in:
parent
3b827149ba
commit
46ec1c665f
|
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assets/handle.h>
|
||||||
|
#include <types/ptr/intrusive_ptr.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 资源加载器接口
|
||||||
|
*
|
||||||
|
* 每种资源类型实现一个加载器,插件化设计。
|
||||||
|
*
|
||||||
|
* @tparam T 资源类型
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
class AssetLoader {
|
||||||
|
public:
|
||||||
|
virtual ~AssetLoader() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从文件加载资源
|
||||||
|
* @param path 文件路径
|
||||||
|
* @return 资源指针,失败返回 nullptr
|
||||||
|
*/
|
||||||
|
virtual Ptr<T> load(const std::string& path) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从内存加载资源
|
||||||
|
* @param data 数据指针
|
||||||
|
* @param size 数据大小
|
||||||
|
* @return 资源指针,失败返回 nullptr
|
||||||
|
*/
|
||||||
|
virtual Ptr<T> loadFromMemory(const uint8_t* data, size_t size) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取支持的文件扩展名
|
||||||
|
* @return 扩展名列表(如 ".png", ".jpg")
|
||||||
|
*/
|
||||||
|
virtual std::vector<std::string> getExtensions() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assets/handle.h>
|
||||||
|
#include <types/ptr/intrusive_ptr.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 密集存储的资源仓库
|
||||||
|
*
|
||||||
|
* 使用 Dense Vec 存储,类似 ECS 的组件存储。
|
||||||
|
* 提供 O(1) 的插入、删除、访问。
|
||||||
|
*
|
||||||
|
* @tparam T 资源类型
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
class AssetStorage {
|
||||||
|
public:
|
||||||
|
struct Slot {
|
||||||
|
Ptr<T> asset;
|
||||||
|
typename Handle<T>::Generation generation = 0;
|
||||||
|
bool active = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
AssetStorage() = default;
|
||||||
|
~AssetStorage() = default;
|
||||||
|
|
||||||
|
AssetStorage(const AssetStorage&) = delete;
|
||||||
|
AssetStorage& operator=(const AssetStorage&) = delete;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 插入资源,返回句柄
|
||||||
|
* @param asset 资源指针
|
||||||
|
* @return 资源句柄
|
||||||
|
*/
|
||||||
|
Handle<T> insert(Ptr<T> asset) {
|
||||||
|
uint32_t index;
|
||||||
|
|
||||||
|
if (!freeIndices_.empty()) {
|
||||||
|
index = freeIndices_.back();
|
||||||
|
freeIndices_.pop_back();
|
||||||
|
slots_[index].asset = std::move(asset);
|
||||||
|
slots_[index].active = true;
|
||||||
|
} else {
|
||||||
|
index = static_cast<uint32_t>(slots_.size());
|
||||||
|
Slot slot;
|
||||||
|
slot.asset = std::move(asset);
|
||||||
|
slot.generation = nextGeneration_++;
|
||||||
|
slot.active = true;
|
||||||
|
slots_.push_back(std::move(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
++activeCount_;
|
||||||
|
return Handle<T>(index, slots_[index].generation);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 移除资源
|
||||||
|
* @param handle 资源句柄
|
||||||
|
*/
|
||||||
|
void remove(Handle<T> handle) {
|
||||||
|
if (!isValid(handle)) return;
|
||||||
|
|
||||||
|
uint32_t index = handle.index();
|
||||||
|
slots_[index].asset.reset();
|
||||||
|
slots_[index].active = false;
|
||||||
|
slots_[index].generation = nextGeneration_++;
|
||||||
|
freeIndices_.push_back(index);
|
||||||
|
--activeCount_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取资源(返回指针,可能为 nullptr)
|
||||||
|
* @param handle 资源句柄
|
||||||
|
* @return 资源指针
|
||||||
|
*/
|
||||||
|
T* get(Handle<T> handle) const {
|
||||||
|
if (!isValid(handle)) return nullptr;
|
||||||
|
return slots_[handle.index()].asset.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取资源(返回智能指针)
|
||||||
|
* @param handle 资源句柄
|
||||||
|
* @return 资源智能指针
|
||||||
|
*/
|
||||||
|
Ptr<T> getPtr(Handle<T> handle) const {
|
||||||
|
if (!isValid(handle)) return Ptr<T>();
|
||||||
|
return slots_[handle.index()].asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查句柄是否有效
|
||||||
|
* @param handle 资源句柄
|
||||||
|
* @return 是否有效
|
||||||
|
*/
|
||||||
|
bool isValid(Handle<T> handle) const {
|
||||||
|
if (!handle.isValid()) return false;
|
||||||
|
uint32_t index = handle.index();
|
||||||
|
if (index >= slots_.size()) return false;
|
||||||
|
const Slot& slot = slots_[index];
|
||||||
|
return slot.active && slot.generation == handle.generation();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 遍历所有资源
|
||||||
|
* @param func 回调函数,签名 void(Handle<T>, T*)
|
||||||
|
*/
|
||||||
|
template<typename Func>
|
||||||
|
void forEach(Func&& func) const {
|
||||||
|
for (size_t i = 0; i < slots_.size(); ++i) {
|
||||||
|
const Slot& slot = slots_[i];
|
||||||
|
if (slot.active && slot.asset) {
|
||||||
|
func(Handle<T>(static_cast<uint32_t>(i), slot.generation), slot.asset.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清空所有资源
|
||||||
|
*/
|
||||||
|
void clear() {
|
||||||
|
slots_.clear();
|
||||||
|
freeIndices_.clear();
|
||||||
|
activeCount_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取活跃资源数量
|
||||||
|
*/
|
||||||
|
size_t count() const { return activeCount_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取存储容量
|
||||||
|
*/
|
||||||
|
size_t capacity() const { return slots_.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<Slot> slots_;
|
||||||
|
std::vector<uint32_t> freeIndices_;
|
||||||
|
uint32_t nextGeneration_ = 1;
|
||||||
|
size_t activeCount_ = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,399 @@
|
||||||
|
#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
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 轻量级资源句柄
|
||||||
|
*
|
||||||
|
* 只包含 32-bit 索引和世代,类似 ECS 中的 Entity ID。
|
||||||
|
* 不直接管理生命周期,由 AssetStorage 统一管理。
|
||||||
|
*
|
||||||
|
* @tparam T 资源类型
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
class Handle {
|
||||||
|
public:
|
||||||
|
using Index = uint32_t;
|
||||||
|
using Generation = uint32_t;
|
||||||
|
|
||||||
|
Handle() : index_(0), generation_(0) {}
|
||||||
|
Handle(Index index, Generation generation)
|
||||||
|
: index_(index), generation_(generation) {}
|
||||||
|
|
||||||
|
bool isValid() const { return generation_ != 0; }
|
||||||
|
Index index() const { return index_; }
|
||||||
|
Generation generation() const { return generation_; }
|
||||||
|
|
||||||
|
bool operator==(const Handle& other) const {
|
||||||
|
return index_ == other.index_ && generation_ == other.generation_;
|
||||||
|
}
|
||||||
|
bool operator!=(const Handle& other) const { return !(*this == other); }
|
||||||
|
|
||||||
|
static Handle invalid() { return Handle(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Index index_;
|
||||||
|
Generation generation_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<typename T>
|
||||||
|
struct hash<extra2d::Handle<T>> {
|
||||||
|
size_t operator()(const extra2d::Handle<T>& h) const {
|
||||||
|
return std::hash<uint64_t>{}(
|
||||||
|
(static_cast<uint64_t>(h.index()) << 32) | h.generation()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assets/asset_loader.h>
|
||||||
|
#include <renderer/shader.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 着色器资源加载器
|
||||||
|
*
|
||||||
|
* 支持从文件或源码加载着色器资源
|
||||||
|
*/
|
||||||
|
class ShaderLoader : public AssetLoader<Shader> {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 从文件加载着色器
|
||||||
|
* @param path 单个文件路径(自动推断 .vert/.frag)
|
||||||
|
* @return 着色器指针,失败返回 nullptr
|
||||||
|
*/
|
||||||
|
Ptr<Shader> load(const std::string& path) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从文件加载着色器(指定顶点和片段着色器)
|
||||||
|
* @param vertPath 顶点着色器路径
|
||||||
|
* @param fragPath 片段着色器路径
|
||||||
|
* @return 着色器指针,失败返回 nullptr
|
||||||
|
*/
|
||||||
|
Ptr<Shader> load(const std::string& vertPath, const std::string& fragPath);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从内存加载着色器(不支持)
|
||||||
|
* @param data 数据指针
|
||||||
|
* @param size 数据大小
|
||||||
|
* @return nullptr
|
||||||
|
*/
|
||||||
|
Ptr<Shader> loadFromMemory(const uint8_t* data, size_t size) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从源码加载着色器
|
||||||
|
* @param vsSource 顶点着色器源码
|
||||||
|
* @param fsSource 片段着色器源码
|
||||||
|
* @return 着色器指针,失败返回 nullptr
|
||||||
|
*/
|
||||||
|
Ptr<Shader> loadFromSource(const std::string& vsSource, const std::string& fsSource);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取支持的文件扩展名
|
||||||
|
* @return 扩展名列表
|
||||||
|
*/
|
||||||
|
std::vector<std::string> getExtensions() const override {
|
||||||
|
return {".glsl", ".vert", ".frag"};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assets/asset_loader.h>
|
||||||
|
#include <renderer/texture.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 纹理资源加载器
|
||||||
|
*
|
||||||
|
* 支持从文件或内存加载纹理资源
|
||||||
|
*/
|
||||||
|
class TextureLoader : public AssetLoader<Texture> {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 从文件加载纹理
|
||||||
|
* @param path 文件路径
|
||||||
|
* @return 纹理指针,失败返回 nullptr
|
||||||
|
*/
|
||||||
|
Ptr<Texture> load(const std::string& path) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从内存加载纹理
|
||||||
|
* @param data 图像数据
|
||||||
|
* @param size 数据大小
|
||||||
|
* @return 纹理指针,失败返回 nullptr
|
||||||
|
*/
|
||||||
|
Ptr<Texture> loadFromMemory(const uint8_t* data, size_t size) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取支持的文件扩展名
|
||||||
|
* @return 扩展名列表
|
||||||
|
*/
|
||||||
|
std::vector<std::string> getExtensions() const override {
|
||||||
|
return {".png", ".jpg", ".jpeg", ".bmp", ".tga"};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -30,7 +30,7 @@ struct MaterialParamInfo {
|
||||||
* @brief 纹理槽位信息
|
* @brief 纹理槽位信息
|
||||||
*/
|
*/
|
||||||
struct TextureSlot {
|
struct TextureSlot {
|
||||||
TextureHandle handle;
|
Handle<Texture> handle;
|
||||||
uint32_t slot;
|
uint32_t slot;
|
||||||
std::string uniformName;
|
std::string uniformName;
|
||||||
};
|
};
|
||||||
|
|
@ -196,7 +196,7 @@ public:
|
||||||
* @param texture 纹理句柄
|
* @param texture 纹理句柄
|
||||||
* @param slot 纹理槽位(0-15)
|
* @param slot 纹理槽位(0-15)
|
||||||
*/
|
*/
|
||||||
void setTexture(const std::string& uniformName, TextureHandle texture, uint32_t slot);
|
void setTexture(const std::string& uniformName, Handle<Texture> texture, uint32_t slot);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取所有纹理槽位
|
* @brief 获取所有纹理槽位
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <types/math/vec2.h>
|
#include <types/math/vec2.h>
|
||||||
#include <types/math/color.h>
|
#include <types/math/color.h>
|
||||||
#include <types/math/transform.h>
|
#include <types/math/transform.h>
|
||||||
|
#include <assets/handle.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
|
|
@ -14,15 +15,10 @@ class Material;
|
||||||
class Mesh;
|
class Mesh;
|
||||||
class Texture;
|
class Texture;
|
||||||
|
|
||||||
// 资源句柄类型(64位,替代智能指针)
|
// 资源句柄类型(使用新的 Handle<T>)
|
||||||
using MaterialHandle = uint64_t;
|
using MaterialHandle = Handle<Material>;
|
||||||
using MeshHandle = uint64_t;
|
using MeshHandle = Handle<Mesh>;
|
||||||
using TextureHandle = uint64_t;
|
using TextureHandle = Handle<Texture>;
|
||||||
|
|
||||||
// 无效句柄值
|
|
||||||
constexpr MaterialHandle INVALID_MATERIAL_HANDLE = 0;
|
|
||||||
constexpr MeshHandle INVALID_MESH_HANDLE = 0;
|
|
||||||
constexpr TextureHandle INVALID_TEXTURE_HANDLE = 0;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 渲染命令类型
|
* @brief 渲染命令类型
|
||||||
|
|
@ -87,7 +83,7 @@ inline uint32_t getMaterialParamSize(MaterialParamType type) {
|
||||||
case MaterialParamType::Vec4: return sizeof(float) * 4;
|
case MaterialParamType::Vec4: return sizeof(float) * 4;
|
||||||
case MaterialParamType::Color: return sizeof(float) * 4;
|
case MaterialParamType::Color: return sizeof(float) * 4;
|
||||||
case MaterialParamType::Mat4: return sizeof(float) * 16;
|
case MaterialParamType::Mat4: return sizeof(float) * 16;
|
||||||
case MaterialParamType::Texture: return sizeof(TextureHandle);
|
case MaterialParamType::Texture: return sizeof(uint32_t) * 2; // Handle size
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -143,8 +139,8 @@ struct RenderCommand {
|
||||||
* @brief 默认构造函数
|
* @brief 默认构造函数
|
||||||
*/
|
*/
|
||||||
RenderCommand() : type(RenderCommandType::DrawMesh), sortKey(0) {
|
RenderCommand() : type(RenderCommandType::DrawMesh), sortKey(0) {
|
||||||
drawMesh.mesh = INVALID_MESH_HANDLE;
|
drawMesh.mesh = MeshHandle::invalid();
|
||||||
drawMesh.material = INVALID_MATERIAL_HANDLE;
|
drawMesh.material = MaterialHandle::invalid();
|
||||||
drawMesh.pos = Vec2(0.0f, 0.0f);
|
drawMesh.pos = Vec2(0.0f, 0.0f);
|
||||||
drawMesh.scale = Vec2(1.0f, 1.0f);
|
drawMesh.scale = Vec2(1.0f, 1.0f);
|
||||||
drawMesh.rot = 0.0f;
|
drawMesh.rot = 0.0f;
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@
|
||||||
#include <event/events.h>
|
#include <event/events.h>
|
||||||
#include <module/module.h>
|
#include <module/module.h>
|
||||||
#include <module/module_registry.h>
|
#include <module/module_registry.h>
|
||||||
#include <queue>
|
|
||||||
#include <renderer/material.h>
|
#include <renderer/material.h>
|
||||||
#include <renderer/mesh.h>
|
#include <renderer/mesh.h>
|
||||||
#include <renderer/render_types.h>
|
#include <renderer/render_types.h>
|
||||||
|
|
@ -16,17 +15,20 @@
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
// 前向声明
|
||||||
|
class AssetsModule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 渲染器模块
|
* @brief 渲染器模块
|
||||||
*
|
*
|
||||||
* 核心渲染系统模块,负责:
|
* 核心渲染系统模块,负责:
|
||||||
* - 通过事件接收渲染命令
|
* - 通过事件接收渲染命令
|
||||||
* - 自动批处理和排序
|
* - 自动批处理和排序
|
||||||
* - GPU 资源管理
|
|
||||||
* - 执行实际渲染
|
* - 执行实际渲染
|
||||||
|
*
|
||||||
|
* 资源管理已迁移到 AssetsModule
|
||||||
*/
|
*/
|
||||||
class RendererModule : public Module {
|
class RendererModule : public Module {
|
||||||
// 自动注册到模块系统,优先级为 3(在 Window、Timer、Input 之后)
|
|
||||||
E2D_REGISTER_MODULE(RendererModule, "Renderer", 3)
|
E2D_REGISTER_MODULE(RendererModule, "Renderer", 3)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -65,94 +67,26 @@ public:
|
||||||
void shutdown() override;
|
void shutdown() override;
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// 资源注册接口(供其他模块使用)
|
// 默认资源(通过 AssetsModule 获取)
|
||||||
//===========================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 注册材质
|
|
||||||
* @param material 材质对象
|
|
||||||
* @return 材质句柄
|
|
||||||
*/
|
|
||||||
MaterialHandle registerMaterial(Ptr<Material> material);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 注册网格
|
|
||||||
* @param mesh 网格对象
|
|
||||||
* @return 网格句柄
|
|
||||||
*/
|
|
||||||
MeshHandle registerMesh(Ptr<Mesh> mesh);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 注册纹理
|
|
||||||
* @param texture 纹理对象
|
|
||||||
* @return 纹理句柄
|
|
||||||
*/
|
|
||||||
TextureHandle registerTexture(Ptr<Texture> texture);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 注销材质
|
|
||||||
* @param handle 材质句柄
|
|
||||||
*/
|
|
||||||
void unregisterMaterial(MaterialHandle handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 注销网格
|
|
||||||
* @param handle 网格句柄
|
|
||||||
*/
|
|
||||||
void unregisterMesh(MeshHandle handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 注销纹理
|
|
||||||
* @param handle 纹理句柄
|
|
||||||
*/
|
|
||||||
void unregisterTexture(TextureHandle handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取材质
|
|
||||||
* @param handle 材质句柄
|
|
||||||
* @return 材质对象,无效句柄返回 nullptr
|
|
||||||
*/
|
|
||||||
Ptr<Material> getMaterial(MaterialHandle handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取网格
|
|
||||||
* @param handle 网格句柄
|
|
||||||
* @return 网格对象,无效句柄返回 nullptr
|
|
||||||
*/
|
|
||||||
Ptr<Mesh> getMesh(MeshHandle handle);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取纹理
|
|
||||||
* @param handle 纹理句柄
|
|
||||||
* @return 纹理对象,无效句柄返回 nullptr
|
|
||||||
*/
|
|
||||||
Ptr<Texture> getTexture(TextureHandle handle);
|
|
||||||
|
|
||||||
//===========================================================================
|
|
||||||
// 默认资源
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取默认材质句柄
|
* @brief 获取默认材质句柄
|
||||||
* @return 默认材质句柄
|
* @return 默认材质句柄
|
||||||
*/
|
*/
|
||||||
MaterialHandle getDefaultMaterialHandle() const {
|
MaterialHandle getDefaultMaterialHandle() const;
|
||||||
return defaultMaterialHandle_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取默认四边形网格句柄
|
* @brief 获取默认四边形网格句柄
|
||||||
* @return 默认四边形网格句柄
|
* @return 默认四边形网格句柄
|
||||||
*/
|
*/
|
||||||
MeshHandle getDefaultQuadHandle() const { return defaultQuadHandle_; }
|
MeshHandle getDefaultQuadHandle() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取默认纹理句柄(1x1 白色纹理)
|
* @brief 获取默认纹理句柄(1x1 白色纹理)
|
||||||
* @return 默认纹理句柄
|
* @return 默认纹理句柄
|
||||||
*/
|
*/
|
||||||
TextureHandle getDefaultTextureHandle() const {
|
TextureHandle getDefaultTextureHandle() const;
|
||||||
return defaultTextureHandle_;
|
|
||||||
}
|
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// 渲染状态设置
|
// 渲染状态设置
|
||||||
|
|
@ -266,105 +200,6 @@ private:
|
||||||
*/
|
*/
|
||||||
void executeCommand(const RenderCommand &cmd);
|
void executeCommand(const RenderCommand &cmd);
|
||||||
|
|
||||||
//===========================================================================
|
|
||||||
// 默认资源创建与销毁
|
|
||||||
//===========================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 创建默认资源
|
|
||||||
* @return 创建是否成功
|
|
||||||
*/
|
|
||||||
bool createDefaultResources();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 销毁默认资源
|
|
||||||
*/
|
|
||||||
void destroyDefaultResources();
|
|
||||||
|
|
||||||
//===========================================================================
|
|
||||||
// 资源句柄管理
|
|
||||||
//===========================================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 资源槽位结构
|
|
||||||
*
|
|
||||||
* 用于管理资源对象和句柄生命周期
|
|
||||||
*/
|
|
||||||
struct ResourceSlot {
|
|
||||||
Ptr<Material> material;
|
|
||||||
Ptr<Mesh> mesh;
|
|
||||||
Ptr<Texture> texture;
|
|
||||||
uint32 generation = 0;
|
|
||||||
bool active = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 句柄池模板类
|
|
||||||
*
|
|
||||||
* 管理资源句柄的分配和回收
|
|
||||||
*/
|
|
||||||
template <typename T> struct HandlePool {
|
|
||||||
std::vector<ResourceSlot> slots;
|
|
||||||
std::queue<uint32> freeIndices;
|
|
||||||
uint32 nextGeneration = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 分配新句柄
|
|
||||||
* @return 编码后的句柄(高32位索引,低32位世代)
|
|
||||||
*/
|
|
||||||
uint64 acquire() {
|
|
||||||
uint32 index;
|
|
||||||
if (!freeIndices.empty()) {
|
|
||||||
index = freeIndices.front();
|
|
||||||
freeIndices.pop();
|
|
||||||
} else {
|
|
||||||
index = static_cast<uint32>(slots.size());
|
|
||||||
slots.emplace_back();
|
|
||||||
}
|
|
||||||
|
|
||||||
slots[index].active = true;
|
|
||||||
slots[index].generation = nextGeneration++;
|
|
||||||
|
|
||||||
// 编码句柄:高32位是索引,低32位是世代
|
|
||||||
return (static_cast<uint64>(index) << 32) | slots[index].generation;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 释放句柄
|
|
||||||
* @param handle 要释放的句柄
|
|
||||||
*/
|
|
||||||
void release(uint64 handle) {
|
|
||||||
uint32 index = static_cast<uint32>(handle >> 32);
|
|
||||||
if (index < slots.size() && slots[index].active) {
|
|
||||||
slots[index].active = false;
|
|
||||||
slots[index].material.reset();
|
|
||||||
slots[index].mesh.reset();
|
|
||||||
slots[index].texture.reset();
|
|
||||||
freeIndices.push(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取资源槽位
|
|
||||||
* @param handle 资源句柄
|
|
||||||
* @return 资源槽位指针,无效句柄返回 nullptr
|
|
||||||
*/
|
|
||||||
ResourceSlot *get(uint64 handle) {
|
|
||||||
uint32 index = static_cast<uint32>(handle >> 32);
|
|
||||||
uint32 generation = static_cast<uint32>(handle & 0xFFFFFFFF);
|
|
||||||
|
|
||||||
if (index < slots.size() && slots[index].active &&
|
|
||||||
slots[index].generation == generation) {
|
|
||||||
return &slots[index];
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
HandlePool<Material> materialPool_; // 材质资源池
|
|
||||||
HandlePool<Mesh> meshPool_; // 网格资源池
|
|
||||||
HandlePool<Texture> texturePool_; // 纹理资源池
|
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// 命令缓冲区
|
// 命令缓冲区
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
@ -379,14 +214,6 @@ private:
|
||||||
|
|
||||||
UniformBufferManager uniformManager_;
|
UniformBufferManager uniformManager_;
|
||||||
|
|
||||||
//===========================================================================
|
|
||||||
// 默认资源句柄
|
|
||||||
//===========================================================================
|
|
||||||
|
|
||||||
MaterialHandle defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE;
|
|
||||||
MeshHandle defaultQuadHandle_ = INVALID_MESH_HANDLE;
|
|
||||||
TextureHandle defaultTextureHandle_ = INVALID_TEXTURE_HANDLE;
|
|
||||||
|
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
// 事件监听器
|
// 事件监听器
|
||||||
//===========================================================================
|
//===========================================================================
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,14 @@
|
||||||
#include <scene/component.h>
|
#include <scene/component.h>
|
||||||
#include <renderer/render_types.h>
|
#include <renderer/render_types.h>
|
||||||
#include <types/math/color.h>
|
#include <types/math/color.h>
|
||||||
|
#include <assets/handle.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
// 前向声明
|
||||||
|
class Material;
|
||||||
|
class Texture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 精灵渲染组件
|
* @brief 精灵渲染组件
|
||||||
*
|
*
|
||||||
|
|
@ -22,7 +27,7 @@ namespace extra2d {
|
||||||
* material->setShader(customShader);
|
* material->setShader(customShader);
|
||||||
* material->setTexture("uTexture", textureHandle, 0);
|
* material->setTexture("uTexture", textureHandle, 0);
|
||||||
* material->setColor("uTintColor", Color::Red);
|
* material->setColor("uTintColor", Color::Red);
|
||||||
* MaterialHandle matHandle = renderer->registerMaterial(material);
|
* Handle<Material> matHandle = assets->load<Material>("material.mat");
|
||||||
* sprite->setMaterial(matHandle);
|
* sprite->setMaterial(matHandle);
|
||||||
*
|
*
|
||||||
* // 方式2:只设置纹理(使用默认材质)
|
* // 方式2:只设置纹理(使用默认材质)
|
||||||
|
|
@ -60,13 +65,13 @@ public:
|
||||||
* 材质包含着色器、参数和纹理
|
* 材质包含着色器、参数和纹理
|
||||||
* @param material 材质句柄
|
* @param material 材质句柄
|
||||||
*/
|
*/
|
||||||
void setMaterial(MaterialHandle material);
|
void setMaterial(Handle<Material> material);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取材质
|
* @brief 获取材质
|
||||||
* @return 材质句柄
|
* @return 材质句柄
|
||||||
*/
|
*/
|
||||||
MaterialHandle getMaterial() const { return material_; }
|
Handle<Material> getMaterial() const { return material_; }
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// 纹理设置(便捷方式)
|
// 纹理设置(便捷方式)
|
||||||
|
|
@ -79,13 +84,13 @@ public:
|
||||||
* 如果设置了材质,此设置被忽略(材质中的纹理优先)
|
* 如果设置了材质,此设置被忽略(材质中的纹理优先)
|
||||||
* @param texture 纹理句柄
|
* @param texture 纹理句柄
|
||||||
*/
|
*/
|
||||||
void setTexture(TextureHandle texture);
|
void setTexture(Handle<Texture> texture);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 获取纹理
|
* @brief 获取纹理
|
||||||
* @return 纹理句柄
|
* @return 纹理句柄
|
||||||
*/
|
*/
|
||||||
TextureHandle getTexture() const { return texture_; }
|
Handle<Texture> getTexture() const { return texture_; }
|
||||||
|
|
||||||
// ========================================
|
// ========================================
|
||||||
// 颜色(顶点颜色)
|
// 颜色(顶点颜色)
|
||||||
|
|
@ -113,9 +118,9 @@ public:
|
||||||
void render() override;
|
void render() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MaterialHandle material_ = INVALID_MATERIAL_HANDLE; // 材质句柄
|
Handle<Material> material_; // 材质句柄
|
||||||
TextureHandle texture_ = INVALID_TEXTURE_HANDLE; // 纹理句柄(便捷方式)
|
Handle<Texture> texture_; // 纹理句柄(便捷方式)
|
||||||
Color color_ = Color::White; // 顶点颜色
|
Color color_ = Color::White; // 顶点颜色
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,404 @@
|
||||||
|
#include <assets/assets_module.h>
|
||||||
|
#include <assets/loaders/shader_loader.h>
|
||||||
|
#include <assets/loaders/texture_loader.h>
|
||||||
|
#include <event/events.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <thread>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
AssetsModule *g_assetsModule = nullptr;
|
||||||
|
|
||||||
|
AssetsModule::AssetsModule() { g_assetsModule = this; }
|
||||||
|
|
||||||
|
AssetsModule::~AssetsModule() {
|
||||||
|
if (g_assetsModule == this) {
|
||||||
|
g_assetsModule = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AssetsModule::init() {
|
||||||
|
E2D_LOG_INFO("AssetsModule initializing...");
|
||||||
|
|
||||||
|
textureLoader_ = std::make_unique<TextureLoader>();
|
||||||
|
shaderLoader_ = std::make_unique<ShaderLoader>();
|
||||||
|
|
||||||
|
// 监听窗口显示事件,在 OpenGL 上下文创建完成后创建默认资源
|
||||||
|
onShowListener_ = std::make_unique<events::OnShow::Listener>();
|
||||||
|
onShowListener_->bind([this]() { this->onGLContextReady(); });
|
||||||
|
|
||||||
|
E2D_LOG_INFO(
|
||||||
|
"AssetsModule initialized successfully (waiting for GL context)");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetsModule::onGLContextReady() {
|
||||||
|
if (defaultResourcesCreated_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
E2D_LOG_INFO("OpenGL context ready, creating default resources...");
|
||||||
|
|
||||||
|
if (!createDefaultResources()) {
|
||||||
|
E2D_LOG_ERROR("Failed to create default resources");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultResourcesCreated_ = true;
|
||||||
|
E2D_LOG_INFO("Default resources created successfully");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetsModule::shutdown() {
|
||||||
|
E2D_LOG_INFO("AssetsModule shutting down...");
|
||||||
|
|
||||||
|
// 释放事件监听器
|
||||||
|
onShowListener_.reset();
|
||||||
|
|
||||||
|
destroyDefaultResources();
|
||||||
|
|
||||||
|
textures_.clear();
|
||||||
|
shaders_.clear();
|
||||||
|
materials_.clear();
|
||||||
|
meshes_.clear();
|
||||||
|
|
||||||
|
texturePathCache_.clear();
|
||||||
|
shaderPathCache_.clear();
|
||||||
|
|
||||||
|
textureLoader_.reset();
|
||||||
|
shaderLoader_.reset();
|
||||||
|
|
||||||
|
defaultResourcesCreated_ = false;
|
||||||
|
|
||||||
|
E2D_LOG_INFO("AssetsModule shutdown complete");
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetsModule *getAssets() { return g_assetsModule; }
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
// Texture 加载特化
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
template <>
|
||||||
|
Handle<Texture> AssetsModule::load<Texture>(const std::string &path) {
|
||||||
|
auto it = texturePathCache_.find(path);
|
||||||
|
if (it != texturePathCache_.end()) {
|
||||||
|
if (textures_.isValid(it->second)) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
texturePathCache_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!textureLoader_) {
|
||||||
|
E2D_LOG_ERROR("TextureLoader not registered");
|
||||||
|
return Handle<Texture>::invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<Texture> texture = textureLoader_->load(path);
|
||||||
|
if (!texture) {
|
||||||
|
E2D_LOG_ERROR("Failed to load texture: {}", path);
|
||||||
|
return Handle<Texture>::invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Texture> handle = textures_.insert(texture);
|
||||||
|
texturePathCache_[path] = handle;
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("Loaded texture: {} -> handle index {}", path, handle.index());
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
Handle<Texture> AssetsModule::loadFromMemory<Texture>(const std::string &key,
|
||||||
|
const uint8_t *data,
|
||||||
|
size_t size) {
|
||||||
|
auto it = texturePathCache_.find(key);
|
||||||
|
if (it != texturePathCache_.end()) {
|
||||||
|
if (textures_.isValid(it->second)) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
texturePathCache_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!textureLoader_) {
|
||||||
|
E2D_LOG_ERROR("TextureLoader not registered");
|
||||||
|
return Handle<Texture>::invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<Texture> texture = textureLoader_->loadFromMemory(data, size);
|
||||||
|
if (!texture) {
|
||||||
|
E2D_LOG_ERROR("Failed to load texture from memory: {}", key);
|
||||||
|
return Handle<Texture>::invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Texture> handle = textures_.insert(texture);
|
||||||
|
texturePathCache_[key] = handle;
|
||||||
|
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
// Shader 加载特化
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
|
||||||
|
auto it = shaderPathCache_.find(path);
|
||||||
|
if (it != shaderPathCache_.end()) {
|
||||||
|
if (shaders_.isValid(it->second)) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
shaderPathCache_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shaderLoader_) {
|
||||||
|
E2D_LOG_ERROR("ShaderLoader not registered");
|
||||||
|
return Handle<Shader>::invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<Shader> shader = shaderLoader_->load(path);
|
||||||
|
if (!shader) {
|
||||||
|
E2D_LOG_ERROR("Failed to load shader: {}", path);
|
||||||
|
return Handle<Shader>::invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Shader> handle = shaders_.insert(shader);
|
||||||
|
shaderPathCache_[path] = handle;
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("Loaded shader: {} -> handle index {}", path, handle.index());
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
|
||||||
|
const std::string &fragPath) {
|
||||||
|
std::string cacheKey = vertPath + "|" + fragPath;
|
||||||
|
|
||||||
|
auto it = shaderPathCache_.find(cacheKey);
|
||||||
|
if (it != shaderPathCache_.end()) {
|
||||||
|
if (shaders_.isValid(it->second)) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
shaderPathCache_.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shaderLoader_) {
|
||||||
|
E2D_LOG_ERROR("ShaderLoader not registered");
|
||||||
|
return Handle<Shader>::invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
ShaderLoader *shaderLoader = static_cast<ShaderLoader *>(shaderLoader_.get());
|
||||||
|
Ptr<Shader> shader = shaderLoader->load(vertPath, fragPath);
|
||||||
|
if (!shader) {
|
||||||
|
E2D_LOG_ERROR("Failed to load shader: {} + {}", vertPath, fragPath);
|
||||||
|
return Handle<Shader>::invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Shader> handle = shaders_.insert(shader);
|
||||||
|
shaderPathCache_[cacheKey] = handle;
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("Loaded shader: {} + {} -> handle index {}", vertPath, fragPath,
|
||||||
|
handle.index());
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
// 批量加载
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
template <>
|
||||||
|
std::vector<Handle<Texture>>
|
||||||
|
AssetsModule::loadDir<Texture>(const std::string &directory,
|
||||||
|
const std::string &pattern, bool recursive) {
|
||||||
|
std::vector<Handle<Texture>> handles;
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::filesystem::path dirPath(directory);
|
||||||
|
if (!std::filesystem::exists(dirPath)) {
|
||||||
|
E2D_LOG_WARN("Directory not found: {}", directory);
|
||||||
|
return handles;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto loadFile = [this, &handles](const std::filesystem::path &filePath) {
|
||||||
|
std::string ext = filePath.extension().string();
|
||||||
|
std::string pathStr = filePath.string();
|
||||||
|
|
||||||
|
for (const auto &supportedExt : textureLoader_->getExtensions()) {
|
||||||
|
if (ext == supportedExt) {
|
||||||
|
Handle<Texture> handle = load<Texture>(pathStr);
|
||||||
|
if (handle.isValid()) {
|
||||||
|
handles.push_back(handle);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (recursive) {
|
||||||
|
for (const auto &entry :
|
||||||
|
std::filesystem::recursive_directory_iterator(dirPath)) {
|
||||||
|
if (entry.is_regular_file()) {
|
||||||
|
loadFile(entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const auto &entry : std::filesystem::directory_iterator(dirPath)) {
|
||||||
|
if (entry.is_regular_file()) {
|
||||||
|
loadFile(entry.path());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (const std::exception &e) {
|
||||||
|
E2D_LOG_ERROR("Error loading directory {}: {}", directory, e.what());
|
||||||
|
}
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("Loaded {} textures from {}", handles.size(), directory);
|
||||||
|
return handles;
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
// 异步加载
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void AssetsModule::loadAsync<Texture>(
|
||||||
|
const std::string &path, std::function<void(Handle<Texture>)> callback) {
|
||||||
|
std::thread([this, path, callback]() {
|
||||||
|
Handle<Texture> handle = load<Texture>(path);
|
||||||
|
if (callback) {
|
||||||
|
callback(handle);
|
||||||
|
}
|
||||||
|
}).detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
// 加载器注册
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void AssetsModule::registerLoader<Texture>(
|
||||||
|
std::unique_ptr<AssetLoader<Texture>> loader) {
|
||||||
|
textureLoader_ = std::move(loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
void AssetsModule::registerLoader<Shader>(
|
||||||
|
std::unique_ptr<AssetLoader<Shader>> loader) {
|
||||||
|
shaderLoader_ = std::move(loader);
|
||||||
|
}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
// 默认资源
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
bool AssetsModule::createDefaultResources() {
|
||||||
|
{
|
||||||
|
Ptr<Texture> texture = makePtr<Texture>();
|
||||||
|
uint8_t whitePixel[] = {255, 255, 255, 255};
|
||||||
|
if (!texture->loadFromMemory(whitePixel, 1, 1, TextureFormat::RGBA8)) {
|
||||||
|
E2D_LOG_ERROR("Failed to create default texture");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
defaultTexture_ = textures_.insert(texture);
|
||||||
|
E2D_LOG_DEBUG("Created default texture");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// 从文件加载默认着色器
|
||||||
|
std::filesystem::path vertPath = "shader/default.vert";
|
||||||
|
std::filesystem::path fragPath = "shader/default.frag";
|
||||||
|
|
||||||
|
Ptr<Shader> shader = makePtr<Shader>();
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(vertPath) ||
|
||||||
|
!std::filesystem::exists(fragPath)) {
|
||||||
|
E2D_LOG_ERROR("Default shader files not found: {}, {}", vertPath.string(),
|
||||||
|
fragPath.string());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shader->loadFromFile(vertPath.string(), fragPath.string())) {
|
||||||
|
E2D_LOG_ERROR("Failed to load default shader from files: {}, {}",
|
||||||
|
vertPath.string(), fragPath.string());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
defaultShader_ = shaders_.insert(shader);
|
||||||
|
E2D_LOG_DEBUG("Loaded default shader from files: {}, {}", vertPath.string(),
|
||||||
|
fragPath.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Ptr<Material> material = makePtr<Material>();
|
||||||
|
material->setShader(getPtr(defaultShader_));
|
||||||
|
defaultMaterial_ = materials_.insert(material);
|
||||||
|
E2D_LOG_DEBUG("Created default material");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Ptr<Mesh> mesh = Mesh::createQuad(Vec2(1.0f, 1.0f));
|
||||||
|
if (!mesh) {
|
||||||
|
E2D_LOG_ERROR("Failed to create default quad mesh");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
defaultQuad_ = meshes_.insert(mesh);
|
||||||
|
E2D_LOG_DEBUG("Created default quad mesh");
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AssetsModule::destroyDefaultResources() {
|
||||||
|
materials_.remove(defaultMaterial_);
|
||||||
|
meshes_.remove(defaultQuad_);
|
||||||
|
textures_.remove(defaultTexture_);
|
||||||
|
shaders_.remove(defaultShader_);
|
||||||
|
|
||||||
|
defaultMaterial_ = Handle<Material>::invalid();
|
||||||
|
defaultQuad_ = Handle<Mesh>::invalid();
|
||||||
|
defaultTexture_ = Handle<Texture>::invalid();
|
||||||
|
defaultShader_ = Handle<Shader>::invalid();
|
||||||
|
}
|
||||||
|
|
||||||
|
Handle<Texture> AssetsModule::getDefaultTexture() { return defaultTexture_; }
|
||||||
|
|
||||||
|
Handle<Shader> AssetsModule::getDefaultShader() { return defaultShader_; }
|
||||||
|
|
||||||
|
Handle<Material> AssetsModule::getDefaultMaterial() { return defaultMaterial_; }
|
||||||
|
|
||||||
|
Handle<Mesh> AssetsModule::getDefaultQuad() { return defaultQuad_; }
|
||||||
|
|
||||||
|
Texture *AssetsModule::getDefaultTexturePtr() {
|
||||||
|
return textures_.get(defaultTexture_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Shader *AssetsModule::getDefaultShaderPtr() {
|
||||||
|
return shaders_.get(defaultShader_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Material *AssetsModule::getDefaultMaterialPtr() {
|
||||||
|
return materials_.get(defaultMaterial_);
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh *AssetsModule::getDefaultQuadPtr() { return meshes_.get(defaultQuad_); }
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
// 热重载
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
void AssetsModule::enableHotReload(bool enable) { hotReloadEnabled_ = enable; }
|
||||||
|
|
||||||
|
void AssetsModule::checkForChanges() {}
|
||||||
|
|
||||||
|
//===========================================================================
|
||||||
|
// 统计
|
||||||
|
//===========================================================================
|
||||||
|
|
||||||
|
AssetsModule::Stats AssetsModule::getStats() const {
|
||||||
|
Stats stats;
|
||||||
|
stats.textureCount = textures_.count();
|
||||||
|
stats.shaderCount = shaders_.count();
|
||||||
|
stats.materialCount = materials_.count();
|
||||||
|
stats.meshCount = meshes_.count();
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
#include <assets/loaders/shader_loader.h>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <renderer/shader.h>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
Ptr<Shader> ShaderLoader::load(const std::string &path) {
|
||||||
|
std::string basePath = path;
|
||||||
|
|
||||||
|
std::string::size_type dotPos = basePath.rfind('.');
|
||||||
|
if (dotPos != std::string::npos) {
|
||||||
|
basePath = basePath.substr(0, dotPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string vertPath = basePath + ".vert";
|
||||||
|
std::string fragPath = basePath + ".frag";
|
||||||
|
|
||||||
|
if (!std::filesystem::exists(vertPath)) {
|
||||||
|
vertPath = basePath + ".vs";
|
||||||
|
}
|
||||||
|
if (!std::filesystem::exists(fragPath)) {
|
||||||
|
fragPath = basePath + ".fs";
|
||||||
|
}
|
||||||
|
|
||||||
|
return load(vertPath, fragPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<Shader> ShaderLoader::load(const std::string &vertPath,
|
||||||
|
const std::string &fragPath) {
|
||||||
|
Ptr<Shader> shader = makePtr<Shader>();
|
||||||
|
|
||||||
|
if (!shader->loadFromFile(vertPath, fragPath)) {
|
||||||
|
E2D_LOG_ERROR("ShaderLoader: Failed to load shader {} + {}", vertPath,
|
||||||
|
fragPath);
|
||||||
|
return Ptr<Shader>();
|
||||||
|
}
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("ShaderLoader: Loaded shader {} + {}", vertPath, fragPath);
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<Shader> ShaderLoader::loadFromMemory(const uint8_t *data, size_t size) {
|
||||||
|
E2D_LOG_ERROR("ShaderLoader: loadFromMemory not supported for shaders");
|
||||||
|
return Ptr<Shader>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<Shader> ShaderLoader::loadFromSource(const std::string &vsSource,
|
||||||
|
const std::string &fsSource) {
|
||||||
|
Ptr<Shader> shader = makePtr<Shader>();
|
||||||
|
|
||||||
|
if (!shader->loadFromSource(vsSource, fsSource)) {
|
||||||
|
E2D_LOG_ERROR("ShaderLoader: Failed to compile shader from source");
|
||||||
|
return Ptr<Shader>();
|
||||||
|
}
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("ShaderLoader: Compiled shader from source");
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
#include <assets/loaders/texture_loader.h>
|
||||||
|
#include <renderer/texture.h>
|
||||||
|
#include <utils/logger.h>
|
||||||
|
|
||||||
|
#include <stb/stb_image.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
Ptr<Texture> TextureLoader::load(const std::string &path) {
|
||||||
|
Ptr<Texture> texture = makePtr<Texture>();
|
||||||
|
|
||||||
|
if (!texture->loadFromFile(path)) {
|
||||||
|
E2D_LOG_ERROR("TextureLoader: Failed to load texture from {}", path);
|
||||||
|
return Ptr<Texture>();
|
||||||
|
}
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("TextureLoader: Loaded texture {}", path);
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<Texture> TextureLoader::loadFromMemory(const uint8_t *data, size_t size) {
|
||||||
|
int width, height, channels;
|
||||||
|
|
||||||
|
stbi_set_flip_vertically_on_load(true);
|
||||||
|
uint8_t *imageData = stbi_load_from_memory(data, static_cast<int>(size),
|
||||||
|
&width, &height, &channels, 0);
|
||||||
|
|
||||||
|
if (!imageData) {
|
||||||
|
E2D_LOG_ERROR("TextureLoader: Failed to decode image from memory");
|
||||||
|
return Ptr<Texture>();
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureFormat format;
|
||||||
|
switch (channels) {
|
||||||
|
case 1:
|
||||||
|
format = TextureFormat::R8;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
format = TextureFormat::RG8;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
format = TextureFormat::RGB8;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
format = TextureFormat::RGBA8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
format = TextureFormat::RGBA8;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ptr<Texture> texture = makePtr<Texture>();
|
||||||
|
bool success = texture->loadFromMemory(imageData, width, height, format);
|
||||||
|
stbi_image_free(imageData);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
E2D_LOG_ERROR("TextureLoader: Failed to create texture from memory");
|
||||||
|
return Ptr<Texture>();
|
||||||
|
}
|
||||||
|
|
||||||
|
E2D_LOG_DEBUG("TextureLoader: Created texture from memory ({}x{})", width,
|
||||||
|
height);
|
||||||
|
return texture;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -197,7 +197,7 @@ void Material::setMat4(const std::string& name, const float* value) {
|
||||||
* @param texture 纹理句柄
|
* @param texture 纹理句柄
|
||||||
* @param slot 纹理槽位(0-15)
|
* @param slot 纹理槽位(0-15)
|
||||||
*/
|
*/
|
||||||
void Material::setTexture(const std::string& uniformName, TextureHandle texture, uint32_t slot) {
|
void Material::setTexture(const std::string& uniformName, Handle<Texture> texture, uint32_t slot) {
|
||||||
// 查找是否已存在相同名称的纹理
|
// 查找是否已存在相同名称的纹理
|
||||||
for (auto& texSlot : textures_) {
|
for (auto& texSlot : textures_) {
|
||||||
if (texSlot.uniformName == uniformName) {
|
if (texSlot.uniformName == uniformName) {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
#include "glad/glad.h"
|
#include "glad/glad.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <assets/assets_module.h>
|
||||||
#include <event/events.h>
|
#include <event/events.h>
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
#include <renderer/renderer_module.h>
|
#include <renderer/renderer_module.h>
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
|
|
||||||
// SDL for window size query
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
@ -15,29 +15,22 @@ RendererModule::RendererModule() = default;
|
||||||
RendererModule::~RendererModule() = default;
|
RendererModule::~RendererModule() = default;
|
||||||
|
|
||||||
RendererModule::RendererModule(RendererModule &&other) noexcept
|
RendererModule::RendererModule(RendererModule &&other) noexcept
|
||||||
: materialPool_(std::move(other.materialPool_)),
|
: commandBuffer_(std::move(other.commandBuffer_)),
|
||||||
meshPool_(std::move(other.meshPool_)),
|
|
||||||
texturePool_(std::move(other.texturePool_)),
|
|
||||||
commandBuffer_(std::move(other.commandBuffer_)),
|
|
||||||
commandCount_(other.commandCount_),
|
commandCount_(other.commandCount_),
|
||||||
uniformManager_(std::move(other.uniformManager_)),
|
uniformManager_(std::move(other.uniformManager_)),
|
||||||
defaultMaterialHandle_(other.defaultMaterialHandle_),
|
|
||||||
defaultQuadHandle_(other.defaultQuadHandle_),
|
|
||||||
defaultTextureHandle_(other.defaultTextureHandle_),
|
|
||||||
onRenderBeginListener_(std::move(other.onRenderBeginListener_)),
|
onRenderBeginListener_(std::move(other.onRenderBeginListener_)),
|
||||||
onRenderSubmitListener_(std::move(other.onRenderSubmitListener_)),
|
onRenderSubmitListener_(std::move(other.onRenderSubmitListener_)),
|
||||||
|
onRenderSetCameraListener_(std::move(other.onRenderSetCameraListener_)),
|
||||||
onRenderEndListener_(std::move(other.onRenderEndListener_)),
|
onRenderEndListener_(std::move(other.onRenderEndListener_)),
|
||||||
onResizeListener_(std::move(other.onResizeListener_)),
|
onResizeListener_(std::move(other.onResizeListener_)),
|
||||||
onShowListener_(std::move(other.onShowListener_)),
|
onShowListener_(std::move(other.onShowListener_)),
|
||||||
glInitialized_(other.glInitialized_), stats_(other.stats_),
|
glInitialized_(other.glInitialized_), stats_(other.stats_),
|
||||||
viewportX_(other.viewportX_), viewportY_(other.viewportY_),
|
viewportX_(other.viewportX_), viewportY_(other.viewportY_),
|
||||||
viewportWidth_(other.viewportWidth_),
|
viewportWidth_(other.viewportWidth_),
|
||||||
viewportHeight_(other.viewportHeight_) {
|
viewportHeight_(other.viewportHeight_),
|
||||||
// 重置源对象状态
|
viewportAdapter_(std::move(other.viewportAdapter_)),
|
||||||
|
viewProjectionMatrix_(std::move(other.viewProjectionMatrix_)) {
|
||||||
other.commandCount_ = 0;
|
other.commandCount_ = 0;
|
||||||
other.defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE;
|
|
||||||
other.defaultQuadHandle_ = INVALID_MESH_HANDLE;
|
|
||||||
other.defaultTextureHandle_ = INVALID_TEXTURE_HANDLE;
|
|
||||||
other.glInitialized_ = false;
|
other.glInitialized_ = false;
|
||||||
other.stats_ = {};
|
other.stats_ = {};
|
||||||
other.viewportX_ = 0;
|
other.viewportX_ = 0;
|
||||||
|
|
@ -48,24 +41,16 @@ RendererModule::RendererModule(RendererModule &&other) noexcept
|
||||||
|
|
||||||
RendererModule &RendererModule::operator=(RendererModule &&other) noexcept {
|
RendererModule &RendererModule::operator=(RendererModule &&other) noexcept {
|
||||||
if (this != &other) {
|
if (this != &other) {
|
||||||
// 清理当前资源
|
|
||||||
if (glInitialized_) {
|
if (glInitialized_) {
|
||||||
destroyDefaultResources();
|
|
||||||
uniformManager_.shutdown();
|
uniformManager_.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 移动资源
|
|
||||||
materialPool_ = std::move(other.materialPool_);
|
|
||||||
meshPool_ = std::move(other.meshPool_);
|
|
||||||
texturePool_ = std::move(other.texturePool_);
|
|
||||||
commandBuffer_ = std::move(other.commandBuffer_);
|
commandBuffer_ = std::move(other.commandBuffer_);
|
||||||
commandCount_ = other.commandCount_;
|
commandCount_ = other.commandCount_;
|
||||||
uniformManager_ = std::move(other.uniformManager_);
|
uniformManager_ = std::move(other.uniformManager_);
|
||||||
defaultMaterialHandle_ = other.defaultMaterialHandle_;
|
|
||||||
defaultQuadHandle_ = other.defaultQuadHandle_;
|
|
||||||
defaultTextureHandle_ = other.defaultTextureHandle_;
|
|
||||||
onRenderBeginListener_ = std::move(other.onRenderBeginListener_);
|
onRenderBeginListener_ = std::move(other.onRenderBeginListener_);
|
||||||
onRenderSubmitListener_ = std::move(other.onRenderSubmitListener_);
|
onRenderSubmitListener_ = std::move(other.onRenderSubmitListener_);
|
||||||
|
onRenderSetCameraListener_ = std::move(other.onRenderSetCameraListener_);
|
||||||
onRenderEndListener_ = std::move(other.onRenderEndListener_);
|
onRenderEndListener_ = std::move(other.onRenderEndListener_);
|
||||||
onResizeListener_ = std::move(other.onResizeListener_);
|
onResizeListener_ = std::move(other.onResizeListener_);
|
||||||
onShowListener_ = std::move(other.onShowListener_);
|
onShowListener_ = std::move(other.onShowListener_);
|
||||||
|
|
@ -75,12 +60,10 @@ RendererModule &RendererModule::operator=(RendererModule &&other) noexcept {
|
||||||
viewportY_ = other.viewportY_;
|
viewportY_ = other.viewportY_;
|
||||||
viewportWidth_ = other.viewportWidth_;
|
viewportWidth_ = other.viewportWidth_;
|
||||||
viewportHeight_ = other.viewportHeight_;
|
viewportHeight_ = other.viewportHeight_;
|
||||||
|
viewportAdapter_ = std::move(other.viewportAdapter_);
|
||||||
|
viewProjectionMatrix_ = std::move(other.viewProjectionMatrix_);
|
||||||
|
|
||||||
// 重置源对象状态
|
|
||||||
other.commandCount_ = 0;
|
other.commandCount_ = 0;
|
||||||
other.defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE;
|
|
||||||
other.defaultQuadHandle_ = INVALID_MESH_HANDLE;
|
|
||||||
other.defaultTextureHandle_ = INVALID_TEXTURE_HANDLE;
|
|
||||||
other.glInitialized_ = false;
|
other.glInitialized_ = false;
|
||||||
other.stats_ = {};
|
other.stats_ = {};
|
||||||
other.viewportX_ = 0;
|
other.viewportX_ = 0;
|
||||||
|
|
@ -94,16 +77,14 @@ RendererModule &RendererModule::operator=(RendererModule &&other) noexcept {
|
||||||
bool RendererModule::init() {
|
bool RendererModule::init() {
|
||||||
E2D_LOG_INFO("Initializing RendererModule...");
|
E2D_LOG_INFO("Initializing RendererModule...");
|
||||||
|
|
||||||
// 绑定事件监听器
|
|
||||||
onRenderBeginListener_.bind([this]() { onRenderBegin(); });
|
onRenderBeginListener_.bind([this]() { onRenderBegin(); });
|
||||||
onRenderSubmitListener_.bind(
|
onRenderSubmitListener_.bind(
|
||||||
[this](const RenderCommand &cmd) { onRenderSubmit(cmd); });
|
[this](const RenderCommand &cmd) { onRenderSubmit(cmd); });
|
||||||
onRenderSetCameraListener_.bind(
|
onRenderSetCameraListener_.bind(
|
||||||
[this](const Mat4 &viewProj) { onRenderSetCamera(viewProj); });
|
[this](const Mat4 &viewProj) { onRenderSetCamera(viewProj); });
|
||||||
onRenderEndListener_.bind([this]() { onRenderEnd(); });
|
onRenderEndListener_.bind([this]() { onRenderEnd(); });
|
||||||
onResizeListener_.bind([this](int32_t w, int32_t h) { onResize(w, h); });
|
onResizeListener_.bind([this](int32 w, int32 h) { onResize(w, h); });
|
||||||
|
|
||||||
// 延迟 GL 初始化到窗口显示时
|
|
||||||
onShowListener_.bind([this]() { onWindowShow(); });
|
onShowListener_.bind([this]() { onWindowShow(); });
|
||||||
|
|
||||||
E2D_LOG_INFO("RendererModule initialized (waiting for GL context)");
|
E2D_LOG_INFO("RendererModule initialized (waiting for GL context)");
|
||||||
|
|
@ -117,26 +98,17 @@ void RendererModule::onWindowShow() {
|
||||||
|
|
||||||
E2D_LOG_INFO("Initializing OpenGL context...");
|
E2D_LOG_INFO("Initializing OpenGL context...");
|
||||||
|
|
||||||
// 初始化 UBO 管理器(需要 GL 上下文)
|
|
||||||
if (!uniformManager_.initialize()) {
|
if (!uniformManager_.initialize()) {
|
||||||
E2D_LOG_ERROR("Failed to initialize UniformBufferManager");
|
E2D_LOG_ERROR("Failed to initialize UniformBufferManager");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 创建默认资源(需要 GL 上下文)
|
|
||||||
if (!createDefaultResources()) {
|
|
||||||
E2D_LOG_ERROR("Failed to create default resources");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取实际窗口大小并设置视口
|
|
||||||
// 查询当前 SDL 窗口大小
|
|
||||||
int windowWidth = 800, windowHeight = 600;
|
int windowWidth = 800, windowHeight = 600;
|
||||||
SDL_Window *sdlWindow = SDL_GL_GetCurrentWindow();
|
SDL_Window *sdlWindow = SDL_GL_GetCurrentWindow();
|
||||||
if (sdlWindow) {
|
if (sdlWindow) {
|
||||||
SDL_GetWindowSize(sdlWindow, &windowWidth, &windowHeight);
|
SDL_GetWindowSize(sdlWindow, &windowWidth, &windowHeight);
|
||||||
E2D_LOG_INFO("Setting initial viewport to window size: {}x{}", windowWidth,
|
E2D_LOG_INFO("Setting initial viewport to window size: {}x{}", windowWidth,
|
||||||
windowHeight);
|
windowHeight);
|
||||||
} else {
|
} else {
|
||||||
E2D_LOG_WARN("Could not get SDL window, using default viewport 800x600");
|
E2D_LOG_WARN("Could not get SDL window, using default viewport 800x600");
|
||||||
}
|
}
|
||||||
|
|
@ -153,85 +125,28 @@ void RendererModule::onWindowShow() {
|
||||||
void RendererModule::shutdown() {
|
void RendererModule::shutdown() {
|
||||||
E2D_LOG_INFO("Shutting down RendererModule...");
|
E2D_LOG_INFO("Shutting down RendererModule...");
|
||||||
|
|
||||||
// 只有在 GL 初始化后才销毁 GL 相关资源
|
|
||||||
if (glInitialized_) {
|
if (glInitialized_) {
|
||||||
// 销毁默认资源
|
|
||||||
destroyDefaultResources();
|
|
||||||
|
|
||||||
// 关闭 UBO 管理器
|
|
||||||
uniformManager_.shutdown();
|
uniformManager_.shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清理资源池
|
|
||||||
materialPool_.slots.clear();
|
|
||||||
while (!materialPool_.freeIndices.empty())
|
|
||||||
materialPool_.freeIndices.pop();
|
|
||||||
|
|
||||||
meshPool_.slots.clear();
|
|
||||||
while (!meshPool_.freeIndices.empty())
|
|
||||||
meshPool_.freeIndices.pop();
|
|
||||||
|
|
||||||
texturePool_.slots.clear();
|
|
||||||
while (!texturePool_.freeIndices.empty())
|
|
||||||
texturePool_.freeIndices.pop();
|
|
||||||
|
|
||||||
glInitialized_ = false;
|
glInitialized_ = false;
|
||||||
|
|
||||||
E2D_LOG_INFO("RendererModule shutdown complete");
|
E2D_LOG_INFO("RendererModule shutdown complete");
|
||||||
}
|
}
|
||||||
|
|
||||||
MaterialHandle RendererModule::registerMaterial(Ptr<Material> material) {
|
MaterialHandle RendererModule::getDefaultMaterialHandle() const {
|
||||||
uint64_t handle = materialPool_.acquire();
|
auto* assets = getAssets();
|
||||||
auto *slot = materialPool_.get(handle);
|
return assets ? assets->getDefaultMaterial() : MaterialHandle::invalid();
|
||||||
if (slot) {
|
|
||||||
slot->material = material;
|
|
||||||
}
|
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MeshHandle RendererModule::registerMesh(Ptr<Mesh> mesh) {
|
MeshHandle RendererModule::getDefaultQuadHandle() const {
|
||||||
uint64_t handle = meshPool_.acquire();
|
auto* assets = getAssets();
|
||||||
auto *slot = meshPool_.get(handle);
|
return assets ? assets->getDefaultQuad() : MeshHandle::invalid();
|
||||||
if (slot) {
|
|
||||||
slot->mesh = mesh;
|
|
||||||
}
|
|
||||||
return handle;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureHandle RendererModule::registerTexture(Ptr<Texture> texture) {
|
TextureHandle RendererModule::getDefaultTextureHandle() const {
|
||||||
uint64_t handle = texturePool_.acquire();
|
auto* assets = getAssets();
|
||||||
auto *slot = texturePool_.get(handle);
|
return assets ? assets->getDefaultTexture() : TextureHandle::invalid();
|
||||||
if (slot) {
|
|
||||||
slot->texture = texture;
|
|
||||||
}
|
|
||||||
return handle;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RendererModule::unregisterMaterial(MaterialHandle handle) {
|
|
||||||
materialPool_.release(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RendererModule::unregisterMesh(MeshHandle handle) {
|
|
||||||
meshPool_.release(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RendererModule::unregisterTexture(TextureHandle handle) {
|
|
||||||
texturePool_.release(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ptr<Material> RendererModule::getMaterial(MaterialHandle handle) {
|
|
||||||
auto *slot = materialPool_.get(handle);
|
|
||||||
return slot ? slot->material : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ptr<Mesh> RendererModule::getMesh(MeshHandle handle) {
|
|
||||||
auto *slot = meshPool_.get(handle);
|
|
||||||
return slot ? slot->mesh : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ptr<Texture> RendererModule::getTexture(TextureHandle handle) {
|
|
||||||
auto *slot = texturePool_.get(handle);
|
|
||||||
return slot ? slot->texture : nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) {
|
void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) {
|
||||||
|
|
@ -240,10 +155,8 @@ void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) {
|
||||||
viewportWidth_ = width;
|
viewportWidth_ = width;
|
||||||
viewportHeight_ = height;
|
viewportHeight_ = height;
|
||||||
|
|
||||||
// 更新视口适配器
|
|
||||||
viewportAdapter_.update(width, height);
|
viewportAdapter_.update(width, height);
|
||||||
|
|
||||||
// 获取适配后的视口
|
|
||||||
auto result = viewportAdapter_.getResult();
|
auto result = viewportAdapter_.getResult();
|
||||||
glViewport(static_cast<GLint>(result.viewport.x),
|
glViewport(static_cast<GLint>(result.viewport.x),
|
||||||
static_cast<GLint>(result.viewport.y),
|
static_cast<GLint>(result.viewport.y),
|
||||||
|
|
@ -251,7 +164,7 @@ void RendererModule::setViewport(int32 x, int32 y, int32 width, int32 height) {
|
||||||
static_cast<GLsizei>(result.viewport.h));
|
static_cast<GLsizei>(result.viewport.h));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::clear(const Color &color, uint32_t flags) {
|
void RendererModule::clear(const Color &color, uint32 flags) {
|
||||||
GLbitfield mask = 0;
|
GLbitfield mask = 0;
|
||||||
|
|
||||||
if (flags & CLEAR_COLOR_FLAG) {
|
if (flags & CLEAR_COLOR_FLAG) {
|
||||||
|
|
@ -273,68 +186,53 @@ void RendererModule::clear(const Color &color, uint32_t flags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::onRenderBegin() {
|
void RendererModule::onRenderBegin() {
|
||||||
// 如果 GL 未初始化,跳过渲染
|
|
||||||
if (!glInitialized_) {
|
if (!glInitialized_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 清除屏幕(黑色背景)
|
|
||||||
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
// 清空命令缓冲区
|
|
||||||
commandCount_ = 0;
|
commandCount_ = 0;
|
||||||
|
|
||||||
// 重置统计
|
|
||||||
stats_ = {};
|
stats_ = {};
|
||||||
|
|
||||||
// 重置 UBO 管理器
|
|
||||||
uniformManager_.resetMaterialUBOs();
|
uniformManager_.resetMaterialUBOs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
|
void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
|
||||||
// 如果 GL 未初始化,跳过提交
|
|
||||||
if (!glInitialized_) {
|
if (!glInitialized_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查缓冲区是否已满
|
|
||||||
if (commandCount_ >= MAX_RENDER_COMMANDS) {
|
if (commandCount_ >= MAX_RENDER_COMMANDS) {
|
||||||
E2D_LOG_WARN("Render command buffer full!");
|
E2D_LOG_WARN("Render command buffer full!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 直接存入预分配缓冲区(无动态分配)
|
|
||||||
commandBuffer_[commandCount_++] = cmd;
|
commandBuffer_[commandCount_++] = cmd;
|
||||||
stats_.commandsSubmitted++;
|
stats_.commandsSubmitted++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::onRenderSetCamera(const Mat4 &viewProj) {
|
void RendererModule::onRenderSetCamera(const Mat4 &viewProj) {
|
||||||
// 如果 GL 未初始化,跳过
|
|
||||||
if (!glInitialized_) {
|
if (!glInitialized_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存视图投影矩阵
|
|
||||||
viewProjectionMatrix_ = viewProj;
|
viewProjectionMatrix_ = viewProj;
|
||||||
|
|
||||||
// 更新全局 UBO 中的视图投影矩阵
|
|
||||||
uniformManager_.updateGlobalUBO(&viewProj, sizeof(Mat4));
|
uniformManager_.updateGlobalUBO(&viewProj, sizeof(Mat4));
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::onRenderEnd() {
|
void RendererModule::onRenderEnd() {
|
||||||
// 如果 GL 未初始化,跳过渲染
|
|
||||||
if (!glInitialized_) {
|
if (!glInitialized_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 排序命令
|
|
||||||
sortCommands();
|
sortCommands();
|
||||||
|
|
||||||
// 批处理并绘制
|
|
||||||
batchAndDraw();
|
batchAndDraw();
|
||||||
|
|
||||||
// 输出统计
|
|
||||||
E2D_LOG_DEBUG("Render: {} commands, {} draw calls, {} batches",
|
E2D_LOG_DEBUG("Render: {} commands, {} draw calls, {} batches",
|
||||||
stats_.commandsExecuted, stats_.drawCalls, stats_.batches);
|
stats_.commandsExecuted, stats_.drawCalls, stats_.batches);
|
||||||
}
|
}
|
||||||
|
|
@ -344,7 +242,6 @@ void RendererModule::onResize(int32 width, int32 height) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::sortCommands() {
|
void RendererModule::sortCommands() {
|
||||||
// 使用 std::sort 对命令进行排序
|
|
||||||
std::sort(commandBuffer_.begin(), commandBuffer_.begin() + commandCount_,
|
std::sort(commandBuffer_.begin(), commandBuffer_.begin() + commandCount_,
|
||||||
[](const RenderCommand &a, const RenderCommand &b) {
|
[](const RenderCommand &a, const RenderCommand &b) {
|
||||||
return a.sortKey < b.sortKey;
|
return a.sortKey < b.sortKey;
|
||||||
|
|
@ -352,8 +249,14 @@ void RendererModule::sortCommands() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RendererModule::batchAndDraw() {
|
void RendererModule::batchAndDraw() {
|
||||||
MaterialHandle lastMaterial = INVALID_MATERIAL_HANDLE;
|
auto* assets = getAssets();
|
||||||
MeshHandle lastMesh = INVALID_MESH_HANDLE;
|
if (!assets) {
|
||||||
|
E2D_LOG_ERROR("AssetsModule not available");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MaterialHandle lastMaterial = MaterialHandle::invalid();
|
||||||
|
MeshHandle lastMesh = MeshHandle::invalid();
|
||||||
uint32_t batchStart = 0;
|
uint32_t batchStart = 0;
|
||||||
uint32_t batchCount = 0;
|
uint32_t batchCount = 0;
|
||||||
|
|
||||||
|
|
@ -361,7 +264,6 @@ void RendererModule::batchAndDraw() {
|
||||||
const auto &cmd = commandBuffer_[i];
|
const auto &cmd = commandBuffer_[i];
|
||||||
|
|
||||||
if (cmd.type != RenderCommandType::DrawMesh) {
|
if (cmd.type != RenderCommandType::DrawMesh) {
|
||||||
// 处理非绘制命令
|
|
||||||
if (batchCount > 0) {
|
if (batchCount > 0) {
|
||||||
drawBatch(batchStart, batchCount, lastMaterial, lastMesh);
|
drawBatch(batchStart, batchCount, lastMaterial, lastMesh);
|
||||||
stats_.batches++;
|
stats_.batches++;
|
||||||
|
|
@ -371,11 +273,9 @@ void RendererModule::batchAndDraw() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查是否需要刷新批次
|
|
||||||
if (cmd.drawMesh.material != lastMaterial ||
|
if (cmd.drawMesh.material != lastMaterial ||
|
||||||
cmd.drawMesh.mesh != lastMesh) {
|
cmd.drawMesh.mesh != lastMesh) {
|
||||||
|
|
||||||
// 刷新上一批次
|
|
||||||
if (batchCount > 0) {
|
if (batchCount > 0) {
|
||||||
drawBatch(batchStart, batchCount, lastMaterial, lastMesh);
|
drawBatch(batchStart, batchCount, lastMaterial, lastMesh);
|
||||||
stats_.batches++;
|
stats_.batches++;
|
||||||
|
|
@ -392,7 +292,6 @@ void RendererModule::batchAndDraw() {
|
||||||
stats_.commandsExecuted++;
|
stats_.commandsExecuted++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新最后一批
|
|
||||||
if (batchCount > 0) {
|
if (batchCount > 0) {
|
||||||
drawBatch(batchStart, batchCount, lastMaterial, lastMesh);
|
drawBatch(batchStart, batchCount, lastMaterial, lastMesh);
|
||||||
stats_.batches++;
|
stats_.batches++;
|
||||||
|
|
@ -402,79 +301,66 @@ void RendererModule::batchAndDraw() {
|
||||||
void RendererModule::drawBatch(uint32_t start, uint32_t count,
|
void RendererModule::drawBatch(uint32_t start, uint32_t count,
|
||||||
MaterialHandle materialHandle,
|
MaterialHandle materialHandle,
|
||||||
MeshHandle meshHandle) {
|
MeshHandle meshHandle) {
|
||||||
auto material = getMaterial(materialHandle);
|
auto* assets = getAssets();
|
||||||
// 如果材质无效,使用默认材质
|
if (!assets) return;
|
||||||
|
|
||||||
|
Material* material = assets->get(materialHandle);
|
||||||
if (!material) {
|
if (!material) {
|
||||||
material = getMaterial(defaultMaterialHandle_);
|
material = assets->getDefaultMaterialPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mesh = getMesh(meshHandle);
|
Mesh* mesh = assets->get(meshHandle);
|
||||||
// 如果网格无效,使用默认四边形
|
|
||||||
if (!mesh) {
|
if (!mesh) {
|
||||||
mesh = getMesh(defaultQuadHandle_);
|
mesh = assets->getDefaultQuadPtr();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!material || !mesh)
|
if (!material || !mesh)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 获取着色器
|
Shader* shader = material->getShader().get();
|
||||||
auto shader = material->getShader();
|
|
||||||
if (!shader)
|
if (!shader)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 绑定着色器
|
|
||||||
shader->bind();
|
shader->bind();
|
||||||
|
|
||||||
// 设置视图投影矩阵
|
|
||||||
shader->setMat4("uViewProjection", glm::value_ptr(viewProjectionMatrix_));
|
shader->setMat4("uViewProjection", glm::value_ptr(viewProjectionMatrix_));
|
||||||
|
|
||||||
// 设置材质参数
|
|
||||||
// 注意:直接使用默认值,因为材质数据布局可能不匹配
|
|
||||||
// tintColor 和 opacity 由着色器默认值处理
|
|
||||||
shader->setVec4("uTintColor", 1.0f, 1.0f, 1.0f, 1.0f);
|
shader->setVec4("uTintColor", 1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
shader->setFloat("uOpacity", 1.0f);
|
shader->setFloat("uOpacity", 1.0f);
|
||||||
|
|
||||||
// 绑定材质中的纹理
|
|
||||||
const auto &textureSlots = material->getTextures();
|
const auto &textureSlots = material->getTextures();
|
||||||
bool hasMaterialTexture = false;
|
bool hasMaterialTexture = false;
|
||||||
for (const auto &slot : textureSlots) {
|
for (const auto &slot : textureSlots) {
|
||||||
if (slot.handle != INVALID_TEXTURE_HANDLE) {
|
if (slot.handle.isValid()) {
|
||||||
auto texture = getTexture(slot.handle);
|
Texture* texture = assets->get(slot.handle);
|
||||||
if (texture) {
|
if (texture) {
|
||||||
texture->bind(slot.slot);
|
texture->bind(slot.slot);
|
||||||
// 设置采样器 uniform
|
|
||||||
shader->setInt(slot.uniformName, static_cast<int>(slot.slot));
|
shader->setInt(slot.uniformName, static_cast<int>(slot.slot));
|
||||||
hasMaterialTexture = true;
|
hasMaterialTexture = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果材质没有纹理,绑定默认纹理
|
if (!hasMaterialTexture) {
|
||||||
if (!hasMaterialTexture && defaultTextureHandle_ != INVALID_TEXTURE_HANDLE) {
|
Texture* defaultTexture = assets->getDefaultTexturePtr();
|
||||||
auto defaultTexture = getTexture(defaultTextureHandle_);
|
|
||||||
if (defaultTexture) {
|
if (defaultTexture) {
|
||||||
defaultTexture->bind(0);
|
defaultTexture->bind(0);
|
||||||
shader->setInt("uTexture", 0);
|
shader->setInt("uTexture", 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 绑定网格
|
|
||||||
mesh->bind();
|
mesh->bind();
|
||||||
|
|
||||||
// 对每个实例单独绘制
|
|
||||||
for (uint32_t i = 0; i < count; ++i) {
|
for (uint32_t i = 0; i < count; ++i) {
|
||||||
Transform transform = commandBuffer_[start + i].getTransform();
|
Transform transform = commandBuffer_[start + i].getTransform();
|
||||||
float matrix[16];
|
float matrix[16];
|
||||||
transform.toMatrix(matrix);
|
transform.toMatrix(matrix);
|
||||||
|
|
||||||
// 设置模型矩阵
|
|
||||||
shader->setMat4("uModelMatrix", matrix);
|
shader->setMat4("uModelMatrix", matrix);
|
||||||
|
|
||||||
// 设置顶点颜色
|
|
||||||
Color color = commandBuffer_[start + i].getColor();
|
Color color = commandBuffer_[start + i].getColor();
|
||||||
shader->setVec4("uColor", color.r, color.g, color.b, color.a);
|
shader->setVec4("uColor", color.r, color.g, color.b, color.a);
|
||||||
|
|
||||||
// 绘制
|
|
||||||
mesh->draw();
|
mesh->draw();
|
||||||
stats_.drawCalls++;
|
stats_.drawCalls++;
|
||||||
}
|
}
|
||||||
|
|
@ -496,59 +382,4 @@ void RendererModule::executeCommand(const RenderCommand &cmd) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RendererModule::createDefaultResources() {
|
|
||||||
// 创建默认着色器
|
|
||||||
auto defaultShader = makePtr<Shader>();
|
|
||||||
if (!defaultShader->loadFromFile("shader/default.vert",
|
|
||||||
"shader/default.frag")) {
|
|
||||||
E2D_LOG_ERROR("Failed to load default shader");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建默认材质布局
|
|
||||||
auto defaultLayout = makePtr<MaterialLayout>();
|
|
||||||
defaultLayout->addParam("tintColor", MaterialParamType::Color);
|
|
||||||
defaultLayout->addParam("opacity", MaterialParamType::Float);
|
|
||||||
defaultLayout->finalize();
|
|
||||||
|
|
||||||
// 创建默认材质
|
|
||||||
auto defaultMaterial = makePtr<Material>();
|
|
||||||
defaultMaterial->setLayout(defaultLayout);
|
|
||||||
defaultMaterial->setShader(defaultShader);
|
|
||||||
defaultMaterial->setColor("tintColor", Color::White);
|
|
||||||
defaultMaterial->setFloat("opacity", 1.0f);
|
|
||||||
|
|
||||||
defaultMaterialHandle_ = registerMaterial(defaultMaterial);
|
|
||||||
|
|
||||||
// 创建默认四边形
|
|
||||||
auto defaultQuad = Mesh::createQuad(Vec2(1.0f, 1.0f));
|
|
||||||
defaultQuadHandle_ = registerMesh(defaultQuad);
|
|
||||||
|
|
||||||
// 创建默认纹理(1x1 白色)
|
|
||||||
auto defaultTexture = makePtr<Texture>();
|
|
||||||
uint8_t whitePixel[4] = {255, 255, 255, 255};
|
|
||||||
defaultTexture->loadFromMemory(whitePixel, 1, 1, TextureFormat::RGBA8);
|
|
||||||
defaultTextureHandle_ = registerTexture(defaultTexture);
|
|
||||||
|
|
||||||
E2D_LOG_INFO("Default resources created");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RendererModule::destroyDefaultResources() {
|
|
||||||
if (defaultMaterialHandle_ != INVALID_MATERIAL_HANDLE) {
|
|
||||||
unregisterMaterial(defaultMaterialHandle_);
|
|
||||||
defaultMaterialHandle_ = INVALID_MATERIAL_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultQuadHandle_ != INVALID_MESH_HANDLE) {
|
|
||||||
unregisterMesh(defaultQuadHandle_);
|
|
||||||
defaultQuadHandle_ = INVALID_MESH_HANDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (defaultTextureHandle_ != INVALID_TEXTURE_HANDLE) {
|
|
||||||
unregisterTexture(defaultTextureHandle_);
|
|
||||||
defaultTextureHandle_ = INVALID_TEXTURE_HANDLE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <assets/assets_module.h>
|
||||||
#include <event/events.h>
|
#include <event/events.h>
|
||||||
#include <scene/components/sprite_renderer.h>
|
#include <scene/components/sprite_renderer.h>
|
||||||
#include <scene/node.h>
|
#include <scene/node.h>
|
||||||
|
|
@ -6,17 +7,15 @@
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
SpriteRenderer::SpriteRenderer() {
|
SpriteRenderer::SpriteRenderer() {
|
||||||
// 默认材质和纹理为无效句柄
|
|
||||||
// 渲染时会使用默认材质
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpriteRenderer::onAttach(Node *owner) { Component::onAttach(owner); }
|
void SpriteRenderer::onAttach(Node *owner) { Component::onAttach(owner); }
|
||||||
|
|
||||||
void SpriteRenderer::setMaterial(MaterialHandle material) {
|
void SpriteRenderer::setMaterial(Handle<Material> material) {
|
||||||
material_ = material;
|
material_ = material;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpriteRenderer::setTexture(TextureHandle texture) { texture_ = texture; }
|
void SpriteRenderer::setTexture(Handle<Texture> texture) { texture_ = texture; }
|
||||||
|
|
||||||
void SpriteRenderer::setColor(const Color &color) { color_ = color; }
|
void SpriteRenderer::setColor(const Color &color) { color_ = color; }
|
||||||
|
|
||||||
|
|
@ -25,27 +24,21 @@ void SpriteRenderer::render() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取世界变换(已包含锚点计算)
|
|
||||||
Transform worldTransform = owner_->getWorldTransform();
|
Transform worldTransform = owner_->getWorldTransform();
|
||||||
|
|
||||||
// 构建渲染命令
|
|
||||||
RenderCommand cmd;
|
RenderCommand cmd;
|
||||||
cmd.type = RenderCommandType::DrawMesh;
|
cmd.type = RenderCommandType::DrawMesh;
|
||||||
|
|
||||||
// 计算排序键(简单实现:材质ID + 纹理ID)
|
uint32_t materialId = material_.index();
|
||||||
uint32_t materialId = static_cast<uint32_t>(material_ & 0xFFFFFFFF);
|
uint32_t textureId = texture_.index();
|
||||||
uint32_t textureId = static_cast<uint32_t>(texture_ & 0xFFFFFFFF);
|
|
||||||
cmd.sortKey = (materialId << 16) | (textureId & 0xFFFF);
|
cmd.sortKey = (materialId << 16) | (textureId & 0xFFFF);
|
||||||
|
|
||||||
// 设置网格(使用默认四边形)
|
cmd.drawMesh.mesh = MeshHandle::invalid();
|
||||||
cmd.drawMesh.mesh = INVALID_MESH_HANDLE; // 渲染器会使用默认四边形
|
cmd.drawMesh.material = material_.isValid() ? material_ : MaterialHandle::invalid();
|
||||||
cmd.drawMesh.material = material_;
|
|
||||||
cmd.setTransform(worldTransform);
|
cmd.setTransform(worldTransform);
|
||||||
|
|
||||||
// 设置顶点颜色
|
|
||||||
cmd.setColor(color_);
|
cmd.setColor(color_);
|
||||||
|
|
||||||
// 提交渲染命令
|
|
||||||
events::OnRenderSubmit::emit(cmd);
|
events::OnRenderSubmit::emit(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue