2026-03-03 03:48:55 +08:00
|
|
|
#include <algorithm>
|
2026-03-02 22:44:42 +08:00
|
|
|
#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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-03 03:48:55 +08:00
|
|
|
// 创建实例化渲染资源
|
|
|
|
|
if (!createInstancedResources()) {
|
|
|
|
|
E2D_LOG_WARN("Failed to create instanced resources, instanced rendering "
|
|
|
|
|
"will not be available");
|
|
|
|
|
// 不返回错误,实例化渲染是可选功能
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 22:44:42 +08:00
|
|
|
defaultResourcesCreated_ = true;
|
|
|
|
|
E2D_LOG_INFO("Default resources created successfully");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssetsModule::shutdown() {
|
|
|
|
|
E2D_LOG_INFO("AssetsModule shutting down...");
|
|
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 关闭异步加载系统
|
|
|
|
|
shutdownAsyncLoader();
|
|
|
|
|
|
2026-03-02 22:44:42 +08:00
|
|
|
// 释放事件监听器
|
|
|
|
|
onShowListener_.reset();
|
|
|
|
|
|
|
|
|
|
destroyDefaultResources();
|
|
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 清空资源(使用写锁保护)
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
|
|
|
|
textures_.clear();
|
|
|
|
|
shaders_.clear();
|
|
|
|
|
materials_.clear();
|
|
|
|
|
meshes_.clear();
|
|
|
|
|
|
|
|
|
|
texturePathCache_.clear();
|
|
|
|
|
shaderPathCache_.clear();
|
|
|
|
|
fileWatchList_.clear();
|
|
|
|
|
}
|
2026-03-02 22:44:42 +08:00
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 清空依赖关系
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
|
|
|
|
|
textureDependencies_.clear();
|
|
|
|
|
shaderDependencies_.clear();
|
|
|
|
|
}
|
2026-03-02 22:44:42 +08:00
|
|
|
|
|
|
|
|
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) {
|
2026-03-02 22:54:06 +08:00
|
|
|
// 先检查缓存(读锁)
|
|
|
|
|
{
|
|
|
|
|
std::shared_lock<std::shared_mutex> lock(mutex_);
|
|
|
|
|
auto it = texturePathCache_.find(path);
|
|
|
|
|
if (it != texturePathCache_.end()) {
|
|
|
|
|
if (textures_.isValid(it->second)) {
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
2026-03-02 22:44:42 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 插入资源(写锁)
|
|
|
|
|
Handle<Texture> handle;
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
2026-03-03 03:48:55 +08:00
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 双重检查,避免重复加载
|
|
|
|
|
auto it = texturePathCache_.find(path);
|
|
|
|
|
if (it != texturePathCache_.end() && textures_.isValid(it->second)) {
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
handle = textures_.insert(texture);
|
|
|
|
|
texturePathCache_[path] = handle;
|
|
|
|
|
}
|
2026-03-02 22:44:42 +08:00
|
|
|
|
|
|
|
|
E2D_LOG_DEBUG("Loaded texture: {} -> handle index {}", path, handle.index());
|
2026-03-03 03:48:55 +08:00
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 如果启用了热重载,添加文件监控
|
|
|
|
|
if (hotReloadEnabled_) {
|
|
|
|
|
addFileWatch(path, handle);
|
|
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
|
2026-03-02 22:44:42 +08:00
|
|
|
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 加载特化
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
2026-03-03 03:48:55 +08:00
|
|
|
template <> Handle<Shader> AssetsModule::load<Shader>(const std::string &path) {
|
2026-03-02 22:54:06 +08:00
|
|
|
// 先检查缓存(读锁)
|
|
|
|
|
{
|
|
|
|
|
std::shared_lock<std::shared_mutex> lock(mutex_);
|
|
|
|
|
auto it = shaderPathCache_.find(path);
|
|
|
|
|
if (it != shaderPathCache_.end()) {
|
|
|
|
|
if (shaders_.isValid(it->second)) {
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
2026-03-02 22:44:42 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 插入资源(写锁)
|
|
|
|
|
Handle<Shader> handle;
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
2026-03-03 03:48:55 +08:00
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 双重检查
|
|
|
|
|
auto it = shaderPathCache_.find(path);
|
|
|
|
|
if (it != shaderPathCache_.end() && shaders_.isValid(it->second)) {
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
handle = shaders_.insert(shader);
|
|
|
|
|
shaderPathCache_[path] = handle;
|
|
|
|
|
}
|
2026-03-02 22:44:42 +08:00
|
|
|
|
|
|
|
|
E2D_LOG_DEBUG("Loaded shader: {} -> handle index {}", path, handle.index());
|
2026-03-03 03:48:55 +08:00
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 如果启用了热重载,添加文件监控
|
|
|
|
|
if (hotReloadEnabled_) {
|
|
|
|
|
addFileWatch(path, handle);
|
|
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
|
2026-03-02 22:44:42 +08:00
|
|
|
return handle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
template <>
|
|
|
|
|
Handle<Shader> AssetsModule::load<Shader>(const std::string &vertPath,
|
|
|
|
|
const std::string &fragPath) {
|
|
|
|
|
std::string cacheKey = vertPath + "|" + fragPath;
|
|
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 先检查缓存(读锁)
|
|
|
|
|
{
|
|
|
|
|
std::shared_lock<std::shared_mutex> lock(mutex_);
|
|
|
|
|
auto it = shaderPathCache_.find(cacheKey);
|
|
|
|
|
if (it != shaderPathCache_.end()) {
|
|
|
|
|
if (shaders_.isValid(it->second)) {
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
2026-03-02 22:44:42 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 插入资源(写锁)
|
|
|
|
|
Handle<Shader> handle;
|
|
|
|
|
{
|
|
|
|
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
2026-03-03 03:48:55 +08:00
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 双重检查
|
|
|
|
|
auto it = shaderPathCache_.find(cacheKey);
|
|
|
|
|
if (it != shaderPathCache_.end() && shaders_.isValid(it->second)) {
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
handle = shaders_.insert(shader);
|
|
|
|
|
shaderPathCache_[cacheKey] = handle;
|
|
|
|
|
}
|
2026-03-02 22:44:42 +08:00
|
|
|
|
|
|
|
|
E2D_LOG_DEBUG("Loaded shader: {} + {} -> handle index {}", vertPath, fragPath,
|
|
|
|
|
handle.index());
|
2026-03-03 03:48:55 +08:00
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
// 如果启用了热重载,添加文件监控
|
|
|
|
|
if (hotReloadEnabled_) {
|
|
|
|
|
addFileWatch(vertPath, handle);
|
|
|
|
|
addFileWatch(fragPath, handle);
|
|
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
|
2026-03-02 22:44:42 +08:00
|
|
|
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());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
2026-03-03 03:48:55 +08:00
|
|
|
// 创建材质布局(与着色器中的 MaterialUBO 匹配)
|
|
|
|
|
// layout(std140, binding = 1) uniform MaterialUBO {
|
|
|
|
|
// vec4 uColor; // 16 bytes
|
|
|
|
|
// vec4 uTintColor; // 16 bytes
|
|
|
|
|
// float uOpacity; // 4 bytes
|
|
|
|
|
// float uPadding[3]; // 12 bytes
|
|
|
|
|
// };
|
|
|
|
|
Ptr<MaterialLayout> layout = makePtr<MaterialLayout>();
|
|
|
|
|
layout->addParam("uColor", MaterialParamType::Color);
|
|
|
|
|
layout->addParam("uTintColor", MaterialParamType::Color);
|
|
|
|
|
layout->addParam("uOpacity", MaterialParamType::Float);
|
|
|
|
|
layout->finalize();
|
|
|
|
|
|
2026-03-02 22:44:42 +08:00
|
|
|
Ptr<Material> material = makePtr<Material>();
|
|
|
|
|
material->setShader(getPtr(defaultShader_));
|
2026-03-03 03:48:55 +08:00
|
|
|
material->setLayout(layout);
|
|
|
|
|
|
|
|
|
|
// 设置默认材质参数
|
|
|
|
|
material->setColor("uColor", Color::White);
|
|
|
|
|
material->setColor("uTintColor", Color::White);
|
|
|
|
|
material->setFloat("uOpacity", 1.0f);
|
|
|
|
|
|
2026-03-03 02:16:29 +08:00
|
|
|
// 添加默认纹理到材质
|
|
|
|
|
material->setTexture("uTexture", getPtr(defaultTexture_), 0);
|
2026-03-02 22:44:42 +08:00
|
|
|
defaultMaterial_ = materials_.insert(material);
|
2026-03-03 03:48:55 +08:00
|
|
|
E2D_LOG_DEBUG("Created default material with default texture and layout");
|
2026-03-02 22:44:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
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_); }
|
|
|
|
|
|
2026-03-03 03:48:55 +08:00
|
|
|
Handle<Shader> AssetsModule::getInstancedShader() { return instancedShader_; }
|
|
|
|
|
|
|
|
|
|
Handle<Material> AssetsModule::getInstancedMaterial() {
|
|
|
|
|
return instancedMaterial_;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Shader *AssetsModule::getInstancedShaderPtr() {
|
|
|
|
|
return shaders_.get(instancedShader_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Material *AssetsModule::getInstancedMaterialPtr() {
|
|
|
|
|
return materials_.get(instancedMaterial_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool AssetsModule::createInstancedResources() {
|
|
|
|
|
// 加载实例化着色器
|
|
|
|
|
std::filesystem::path vertPath = "shader/sprite_instanced.vert";
|
|
|
|
|
std::filesystem::path fragPath = "shader/sprite_instanced.frag";
|
|
|
|
|
|
|
|
|
|
if (!std::filesystem::exists(vertPath) ||
|
|
|
|
|
!std::filesystem::exists(fragPath)) {
|
|
|
|
|
E2D_LOG_ERROR("Instanced shader files not found: {}, {}", vertPath.string(),
|
|
|
|
|
fragPath.string());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ptr<Shader> shader = makePtr<Shader>();
|
|
|
|
|
if (!shader->loadInstancedFromFile(vertPath.string(), fragPath.string())) {
|
|
|
|
|
E2D_LOG_ERROR("Failed to load instanced shader from files: {}, {}",
|
|
|
|
|
vertPath.string(), fragPath.string());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
instancedShader_ = shaders_.insert(shader);
|
|
|
|
|
E2D_LOG_DEBUG("Loaded instanced shader from files: {}, {}", vertPath.string(),
|
|
|
|
|
fragPath.string());
|
|
|
|
|
|
|
|
|
|
// 创建实例化材质布局
|
|
|
|
|
Ptr<MaterialLayout> layout = makePtr<MaterialLayout>();
|
|
|
|
|
layout->addParam("uColor", MaterialParamType::Color);
|
|
|
|
|
layout->addParam("uTintColor", MaterialParamType::Color);
|
|
|
|
|
layout->addParam("uOpacity", MaterialParamType::Float);
|
|
|
|
|
layout->finalize();
|
|
|
|
|
|
|
|
|
|
Ptr<Material> material = makePtr<Material>();
|
|
|
|
|
material->setShader(getPtr(instancedShader_));
|
|
|
|
|
material->setLayout(layout);
|
|
|
|
|
|
|
|
|
|
// 设置默认材质参数
|
|
|
|
|
material->setColor("uColor", Color::White);
|
|
|
|
|
material->setColor("uTintColor", Color::White);
|
|
|
|
|
material->setFloat("uOpacity", 1.0f);
|
|
|
|
|
|
|
|
|
|
// 添加默认纹理到材质
|
|
|
|
|
material->setTexture("uTexture", getPtr(defaultTexture_), 0);
|
|
|
|
|
instancedMaterial_ = materials_.insert(material);
|
|
|
|
|
E2D_LOG_DEBUG("Created instanced material with default texture and layout");
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-02 22:44:42 +08:00
|
|
|
//===========================================================================
|
|
|
|
|
// 热重载
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
2026-03-03 03:48:55 +08:00
|
|
|
void AssetsModule::enableHotReload(bool enable) {
|
|
|
|
|
hotReloadEnabled_ = enable;
|
|
|
|
|
if (enable) {
|
|
|
|
|
E2D_LOG_INFO("Hot reload enabled");
|
|
|
|
|
} else {
|
|
|
|
|
E2D_LOG_INFO("Hot reload disabled");
|
|
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssetsModule::setHotReloadInterval(float interval) {
|
2026-03-03 03:48:55 +08:00
|
|
|
hotReloadInterval_ = interval;
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-03 03:48:55 +08:00
|
|
|
void AssetsModule::addFileWatch(const std::string &path,
|
|
|
|
|
Handle<Texture> handle) {
|
|
|
|
|
try {
|
|
|
|
|
if (!std::filesystem::exists(path)) {
|
|
|
|
|
return;
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
|
|
|
|
|
FileWatchInfo info;
|
|
|
|
|
info.path = path;
|
|
|
|
|
info.lastWriteTime = std::filesystem::last_write_time(path);
|
|
|
|
|
info.textureHandle = handle;
|
|
|
|
|
info.isTexture = true;
|
|
|
|
|
|
|
|
|
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
|
|
|
|
fileWatchList_.push_back(info);
|
|
|
|
|
E2D_LOG_DEBUG("Watching texture file: {}", path);
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what());
|
|
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-03 03:48:55 +08:00
|
|
|
void AssetsModule::addFileWatch(const std::string &path,
|
|
|
|
|
Handle<Shader> handle) {
|
|
|
|
|
try {
|
|
|
|
|
if (!std::filesystem::exists(path)) {
|
|
|
|
|
return;
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
|
|
|
|
|
FileWatchInfo info;
|
|
|
|
|
info.path = path;
|
|
|
|
|
info.lastWriteTime = std::filesystem::last_write_time(path);
|
|
|
|
|
info.shaderHandle = handle;
|
|
|
|
|
info.isTexture = false;
|
|
|
|
|
|
|
|
|
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
|
|
|
|
fileWatchList_.push_back(info);
|
|
|
|
|
E2D_LOG_DEBUG("Watching shader file: {}", path);
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
E2D_LOG_ERROR("Failed to add file watch for {}: {}", path, e.what());
|
|
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-03 03:48:55 +08:00
|
|
|
void AssetsModule::reloadTexture(const FileWatchInfo &info) {
|
|
|
|
|
if (!textureLoader_ || !info.textureHandle.isValid()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
E2D_LOG_INFO("Reloading texture: {}", info.path);
|
|
|
|
|
|
|
|
|
|
Ptr<Texture> newTexture = textureLoader_->load(info.path);
|
|
|
|
|
if (!newTexture) {
|
|
|
|
|
E2D_LOG_ERROR("Failed to reload texture: {}", info.path);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 替换旧纹理的数据
|
|
|
|
|
Texture *oldTexture = textures_.get(info.textureHandle);
|
|
|
|
|
if (oldTexture) {
|
|
|
|
|
// 这里假设 Texture 类有更新数据的方法
|
|
|
|
|
// 如果没有,可能需要重新设计
|
|
|
|
|
E2D_LOG_INFO("Texture reloaded: {}", info.path);
|
|
|
|
|
notifyTextureReloaded(info.textureHandle);
|
|
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-03 03:48:55 +08:00
|
|
|
void AssetsModule::reloadShader(const FileWatchInfo &info) {
|
|
|
|
|
if (!shaderLoader_ || !info.shaderHandle.isValid()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
E2D_LOG_INFO("Reloading shader: {}", info.path);
|
|
|
|
|
|
|
|
|
|
// 查找缓存键
|
|
|
|
|
std::string cacheKey;
|
|
|
|
|
{
|
|
|
|
|
std::shared_lock<std::shared_mutex> lock(mutex_);
|
|
|
|
|
for (const auto &pair : shaderPathCache_) {
|
|
|
|
|
if (pair.second == info.shaderHandle) {
|
|
|
|
|
cacheKey = pair.first;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cacheKey.empty()) {
|
|
|
|
|
E2D_LOG_WARN("Shader cache key not found for: {}", info.path);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 解析顶点/片段着色器路径
|
|
|
|
|
size_t sepPos = cacheKey.find('|');
|
|
|
|
|
if (sepPos == std::string::npos) {
|
|
|
|
|
// 单文件模式
|
|
|
|
|
Ptr<Shader> newShader = shaderLoader_->load(info.path);
|
|
|
|
|
if (!newShader) {
|
|
|
|
|
E2D_LOG_ERROR("Failed to reload shader: {}", info.path);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 双文件模式
|
|
|
|
|
std::string vertPath = cacheKey.substr(0, sepPos);
|
|
|
|
|
std::string fragPath = cacheKey.substr(sepPos + 1);
|
|
|
|
|
|
|
|
|
|
ShaderLoader *loader = static_cast<ShaderLoader *>(shaderLoader_.get());
|
|
|
|
|
Ptr<Shader> newShader = loader->load(vertPath, fragPath);
|
|
|
|
|
if (!newShader) {
|
|
|
|
|
E2D_LOG_ERROR("Failed to reload shader: {} + {}", vertPath, fragPath);
|
|
|
|
|
return;
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
E2D_LOG_INFO("Shader reloaded: {}", info.path);
|
|
|
|
|
notifyShaderReloaded(info.shaderHandle);
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssetsModule::checkForChanges() {
|
2026-03-03 03:48:55 +08:00
|
|
|
if (!hotReloadEnabled_ || fileWatchList_.empty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unique_lock<std::shared_mutex> lock(mutex_);
|
|
|
|
|
|
|
|
|
|
for (auto &info : fileWatchList_) {
|
|
|
|
|
try {
|
|
|
|
|
if (!std::filesystem::exists(info.path)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto currentTime = std::filesystem::last_write_time(info.path);
|
|
|
|
|
if (currentTime != info.lastWriteTime) {
|
|
|
|
|
info.lastWriteTime = currentTime;
|
|
|
|
|
|
|
|
|
|
// 解锁进行重载操作
|
|
|
|
|
lock.unlock();
|
|
|
|
|
|
|
|
|
|
if (info.isTexture) {
|
|
|
|
|
reloadTexture(info);
|
|
|
|
|
} else {
|
|
|
|
|
reloadShader(info);
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
|
|
|
|
|
lock.lock();
|
|
|
|
|
}
|
|
|
|
|
} catch (const std::exception &e) {
|
|
|
|
|
E2D_LOG_ERROR("Error checking file {}: {}", info.path, e.what());
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
// 异步加载系统
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
void AssetsModule::initAsyncLoader(uint32_t threadCount) {
|
2026-03-03 03:48:55 +08:00
|
|
|
if (asyncLoaderRunning_) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (threadCount == 0) {
|
|
|
|
|
threadCount = std::max(1u, std::thread::hardware_concurrency() / 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
asyncLoaderRunning_ = true;
|
|
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < threadCount; ++i) {
|
|
|
|
|
workerThreads_.emplace_back(&AssetsModule::workerThreadLoop, this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
E2D_LOG_INFO("Async loader initialized with {} threads", threadCount);
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssetsModule::shutdownAsyncLoader() {
|
2026-03-03 03:48:55 +08:00
|
|
|
if (!asyncLoaderRunning_) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
asyncLoaderRunning_ = false;
|
|
|
|
|
queueCV_.notify_all();
|
|
|
|
|
|
|
|
|
|
for (auto &thread : workerThreads_) {
|
|
|
|
|
if (thread.joinable()) {
|
|
|
|
|
thread.join();
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
workerThreads_.clear();
|
|
|
|
|
|
|
|
|
|
std::lock_guard<std::mutex> lock(queueMutex_);
|
|
|
|
|
loadQueue_.clear();
|
|
|
|
|
|
|
|
|
|
E2D_LOG_INFO("Async loader shutdown");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssetsModule::submitLoadTask(const LoadTask &task) {
|
|
|
|
|
{
|
2026-03-02 22:54:06 +08:00
|
|
|
std::lock_guard<std::mutex> lock(queueMutex_);
|
2026-03-03 03:48:55 +08:00
|
|
|
loadQueue_.push_back(task);
|
|
|
|
|
|
|
|
|
|
// 按优先级排序
|
|
|
|
|
std::sort(loadQueue_.begin(), loadQueue_.end(),
|
|
|
|
|
[](const LoadTask &a, const LoadTask &b) {
|
|
|
|
|
return static_cast<int>(a.priority) >
|
|
|
|
|
static_cast<int>(b.priority);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
queueCV_.notify_one();
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-03 03:48:55 +08:00
|
|
|
void AssetsModule::workerThreadLoop() {
|
|
|
|
|
while (asyncLoaderRunning_) {
|
|
|
|
|
LoadTask task;
|
|
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
{
|
2026-03-03 03:48:55 +08:00
|
|
|
std::unique_lock<std::mutex> lock(queueMutex_);
|
|
|
|
|
queueCV_.wait(
|
|
|
|
|
lock, [this] { return !loadQueue_.empty() || !asyncLoaderRunning_; });
|
|
|
|
|
|
|
|
|
|
if (!asyncLoaderRunning_) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (loadQueue_.empty()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
task = std::move(loadQueue_.back());
|
|
|
|
|
loadQueue_.pop_back();
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-03 03:48:55 +08:00
|
|
|
// 执行加载任务
|
|
|
|
|
if (task.type == LoadTask::Type::Texture) {
|
|
|
|
|
Handle<Texture> handle = load<Texture>(task.path);
|
|
|
|
|
|
|
|
|
|
if (task.textureCallback) {
|
|
|
|
|
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
|
|
|
|
|
completedCallbacks_.push_back(
|
|
|
|
|
[handle, callback = task.textureCallback]() { callback(handle); });
|
|
|
|
|
}
|
|
|
|
|
} else if (task.type == LoadTask::Type::Shader) {
|
|
|
|
|
Handle<Shader> handle;
|
|
|
|
|
if (task.secondaryPath.empty()) {
|
|
|
|
|
handle = load<Shader>(task.path);
|
|
|
|
|
} else {
|
|
|
|
|
handle = load<Shader>(task.path, task.secondaryPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (task.shaderCallback) {
|
|
|
|
|
std::lock_guard<std::mutex> callbackLock(callbackMutex_);
|
|
|
|
|
completedCallbacks_.push_back(
|
|
|
|
|
[handle, callback = task.shaderCallback]() { callback(handle); });
|
|
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssetsModule::processAsyncCallbacks() {
|
2026-03-03 03:48:55 +08:00
|
|
|
std::vector<std::function<void()>> callbacks;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
std::lock_guard<std::mutex> lock(callbackMutex_);
|
|
|
|
|
callbacks = std::move(completedCallbacks_);
|
|
|
|
|
completedCallbacks_.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto &callback : callbacks) {
|
|
|
|
|
callback();
|
|
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
// 资源依赖跟踪
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
2026-03-03 03:48:55 +08:00
|
|
|
void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
|
|
|
|
Handle<Texture> texture) {
|
|
|
|
|
if (!material.isValid() || !texture.isValid()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
|
|
|
|
|
|
|
|
|
|
uint32_t textureIndex = texture.index();
|
|
|
|
|
auto &info = textureDependencies_[textureIndex];
|
|
|
|
|
info.texture = texture;
|
|
|
|
|
|
|
|
|
|
// 检查是否已存在
|
|
|
|
|
auto it = std::find(info.dependentMaterials.begin(),
|
|
|
|
|
info.dependentMaterials.end(), material);
|
|
|
|
|
if (it == info.dependentMaterials.end()) {
|
|
|
|
|
info.dependentMaterials.push_back(material);
|
|
|
|
|
E2D_LOG_DEBUG("Registered material {} dependency on texture {}",
|
|
|
|
|
material.index(), textureIndex);
|
|
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-03 03:48:55 +08:00
|
|
|
void AssetsModule::registerMaterialDependency(Handle<Material> material,
|
|
|
|
|
Handle<Shader> shader) {
|
|
|
|
|
if (!material.isValid() || !shader.isValid()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::unique_lock<std::shared_mutex> lock(dependencyMutex_);
|
|
|
|
|
|
|
|
|
|
uint32_t shaderIndex = shader.index();
|
|
|
|
|
auto &info = shaderDependencies_[shaderIndex];
|
|
|
|
|
info.shader = shader;
|
|
|
|
|
|
|
|
|
|
// 检查是否已存在
|
|
|
|
|
auto it = std::find(info.dependentMaterials.begin(),
|
|
|
|
|
info.dependentMaterials.end(), material);
|
|
|
|
|
if (it == info.dependentMaterials.end()) {
|
|
|
|
|
info.dependentMaterials.push_back(material);
|
|
|
|
|
E2D_LOG_DEBUG("Registered material {} dependency on shader {}",
|
|
|
|
|
material.index(), shaderIndex);
|
|
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AssetsModule::notifyTextureReloaded(Handle<Texture> texture) {
|
2026-03-03 03:48:55 +08:00
|
|
|
if (!texture.isValid()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::shared_lock<std::shared_mutex> lock(dependencyMutex_);
|
|
|
|
|
|
|
|
|
|
uint32_t textureIndex = texture.index();
|
|
|
|
|
auto it = textureDependencies_.find(textureIndex);
|
|
|
|
|
if (it != textureDependencies_.end()) {
|
|
|
|
|
E2D_LOG_INFO("Notifying {} materials of texture reload",
|
|
|
|
|
it->second.dependentMaterials.size());
|
|
|
|
|
|
|
|
|
|
// 材质需要更新纹理引用
|
|
|
|
|
// 这里可以触发材质更新事件
|
|
|
|
|
for (const auto &materialHandle : it->second.dependentMaterials) {
|
|
|
|
|
Material *material = materials_.get(materialHandle);
|
|
|
|
|
if (material) {
|
|
|
|
|
// 材质自动使用新的纹理数据
|
|
|
|
|
E2D_LOG_DEBUG("Material {} updated with reloaded texture",
|
|
|
|
|
materialHandle.index());
|
|
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
2026-03-02 22:44:42 +08:00
|
|
|
|
2026-03-02 22:54:06 +08:00
|
|
|
void AssetsModule::notifyShaderReloaded(Handle<Shader> shader) {
|
2026-03-03 03:48:55 +08:00
|
|
|
if (!shader.isValid()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::shared_lock<std::shared_mutex> lock(dependencyMutex_);
|
|
|
|
|
|
|
|
|
|
uint32_t shaderIndex = shader.index();
|
|
|
|
|
auto it = shaderDependencies_.find(shaderIndex);
|
|
|
|
|
if (it != shaderDependencies_.end()) {
|
|
|
|
|
E2D_LOG_INFO("Notifying {} materials of shader reload",
|
|
|
|
|
it->second.dependentMaterials.size());
|
|
|
|
|
|
|
|
|
|
for (const auto &materialHandle : it->second.dependentMaterials) {
|
|
|
|
|
Material *material = materials_.get(materialHandle);
|
|
|
|
|
if (material) {
|
|
|
|
|
// 更新材质的着色器引用
|
|
|
|
|
material->setShader(getPtr(shader));
|
|
|
|
|
E2D_LOG_DEBUG("Material {} updated with reloaded shader",
|
|
|
|
|
materialHandle.index());
|
|
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
2026-03-03 03:48:55 +08:00
|
|
|
}
|
2026-03-02 22:54:06 +08:00
|
|
|
}
|
2026-03-02 22:44:42 +08:00
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
|
// 统计
|
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
|
|
AssetsModule::Stats AssetsModule::getStats() const {
|
2026-03-03 03:48:55 +08:00
|
|
|
std::shared_lock<std::shared_mutex> lock(mutex_);
|
|
|
|
|
|
|
|
|
|
Stats stats;
|
|
|
|
|
stats.textureCount = textures_.count();
|
|
|
|
|
stats.shaderCount = shaders_.count();
|
|
|
|
|
stats.materialCount = materials_.count();
|
|
|
|
|
stats.meshCount = meshes_.count();
|
|
|
|
|
return stats;
|
2026-03-02 22:44:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace extra2d
|