From ec6ced9db2db460d98357baabb5d078745d06e75 Mon Sep 17 00:00:00 2001 From: ChestnutYueyue <952134128@qq.com> Date: Tue, 3 Mar 2026 05:53:40 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E6=A8=A1=E5=9D=97=E7=B3=BB=E7=BB=9F):?= =?UTF-8?q?=20=E8=B0=83=E6=95=B4=E6=A8=A1=E5=9D=97=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E4=BC=98=E5=85=88=E7=BA=A7=E5=B9=B6=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=8A=A0=E8=BD=BD=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构模块初始化顺序,确保文件模块优先加载 移除Shader类中直接文件操作,统一通过FileModule进行文件访问 添加FileModule实现跨平台文件系统操作,支持RomFS 重构资源加载逻辑,统一使用FileModule进行文件操作 --- include/assets/assets_module.h | 3 +- include/platform/file_module.h | 179 +++++++++++++++++++++ include/platform/input_module.h | 4 +- include/renderer/renderer_module.h | 3 +- include/renderer/shader.h | 16 -- include/scene/scene_module.h | 4 +- include/utils/timer_module.h | 4 +- src/assets/assets_module.cpp | 151 ++++++++++++++---- src/assets/loaders/shader_loader.cpp | 60 ++++++- src/platform/file_module.cpp | 225 +++++++++++++++++++++++++++ src/renderer/shader.cpp | 75 ++------- 11 files changed, 602 insertions(+), 122 deletions(-) create mode 100644 include/platform/file_module.h create mode 100644 src/platform/file_module.cpp diff --git a/include/assets/assets_module.h b/include/assets/assets_module.h index fddd32a..1895ca4 100644 --- a/include/assets/assets_module.h +++ b/include/assets/assets_module.h @@ -33,7 +33,8 @@ namespace extra2d { * - Handle: 轻量级句柄 */ class AssetsModule : public Module { - E2D_REGISTER_MODULE(AssetsModule, "Assets", 2) + // 优先级为 3,在 FileModule (优先级 2) 之后初始化 + E2D_REGISTER_MODULE(AssetsModule, "Assets", 3) public: AssetsModule(); diff --git a/include/platform/file_module.h b/include/platform/file_module.h new file mode 100644 index 0000000..8dd0e00 --- /dev/null +++ b/include/platform/file_module.h @@ -0,0 +1,179 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +/** + * @brief 文件信息 + */ +struct FileInfo { + std::string path; + std::string name; + bool isDir = false; + int64 size = 0; +}; + +/** + * @brief 文件读取结果 + */ +struct FileData { + bool ok = false; + std::vector data; + std::string error; + + operator bool() const { return ok; } + const uint8 *ptr() const { return data.data(); } + size_t size() const { return data.size(); } +}; + +/** + * @brief 文件模块 + * + * 提供跨平台文件系统操作 + * 非单例设计,通过 Context 管理生命周期 + */ +class FileModule : public Module { + // 优先级为 2,系统级模块,在 TimerModule (优先级 4) 之前初始化 + E2D_REGISTER_MODULE(FileModule, "File", 2) + +public: + FileModule(); + ~FileModule() override; + + // 禁止拷贝 + FileModule(const FileModule &) = delete; + FileModule &operator=(const FileModule &) = delete; + + // 允许移动 + FileModule(FileModule &&) noexcept; + FileModule &operator=(FileModule &&) noexcept; + + // Module 接口实现 + bool init() override; + void shutdown() override; + + /** + * @brief 检查文件是否存在 + */ + bool exists(const std::string &path) const; + + /** + * @brief 检查是否为目录 + */ + bool isDir(const std::string &path) const; + + /** + * @brief 读取整个文件到内存 + */ + FileData read(const std::string &path) const; + + /** + * @brief 读取文件为字符串 + */ + std::string readString(const std::string &path) const; + + /** + * @brief 写入数据到文件 + */ + bool write(const std::string &path, const void *data, size_t size) const; + + /** + * @brief 写入字符串到文件 + */ + bool writeString(const std::string &path, const std::string &content) const; + + /** + * @brief 追加数据到文件 + */ + bool append(const std::string &path, const void *data, size_t size) const; + + /** + * @brief 删除文件 + */ + bool remove(const std::string &path) const; + + /** + * @brief 创建目录 + */ + bool mkdir(const std::string &path) const; + + /** + * @brief 列出目录内容 + */ + std::vector listDir(const std::string &path) const; + + /** + * @brief 获取文件大小 + */ + int64 fileSize(const std::string &path) const; + + /** + * @brief 获取文件扩展名 + */ + std::string ext(const std::string &path) const; + + /** + * @brief 获取文件名(不含路径) + */ + std::string fileName(const std::string &path) const; + + /** + * @brief 获取文件所在目录 + */ + std::string dirName(const std::string &path) const; + + /** + * @brief 连接路径 + */ + std::string join(const std::string &a, const std::string &b) const; + + /** + * @brief 获取可写目录(用于存档等) + */ + std::string writableDir() const; + + /** + * @brief 设置资源根目录 + */ + void setAssetRoot(const std::string &root) { assetRoot_ = root; } + + /** + * @brief 获取资源根目录 + */ + const std::string &assetRoot() const { return assetRoot_; } + + /** + * @brief 获取资源完整路径 + */ + std::string assetPath(const std::string &relPath) const; + + /** + * @brief 检查路径是否为 RomFS 路径 + * @param path 文件路径 + * @return 是否以 "romfs:/" 开头 + */ + bool isRomfsPath(const std::string &path) const; + + /** + * @brief 设置 RomFS 资源根目录(用于 Switch 平台) + * @param root RomFS 根目录,默认为 "romfs:/" + */ + void setRomfsRoot(const std::string &root) { romfsRoot_ = root; } + + /** + * @brief 获取 RomFS 资源根目录 + */ + const std::string &romfsRoot() const { return romfsRoot_; } + +private: + std::string assetRoot_; + std::string writableDir_; + std::string romfsRoot_ = "romfs:/"; +}; + +} // namespace extra2d diff --git a/include/platform/input_module.h b/include/platform/input_module.h index 6f6007b..b7865f4 100644 --- a/include/platform/input_module.h +++ b/include/platform/input_module.h @@ -172,8 +172,8 @@ using TouchCb = std::function; * 使用新的 Module 基类,支持自动注册 */ class InputModule : public Module { - // 自动注册到模块系统,优先级为 10 - E2D_REGISTER_MODULE(InputModule, "Input", 10) + // 优先级为 7,在 TimerModule (优先级 6) 之后初始化 + E2D_REGISTER_MODULE(InputModule, "Input", 7) public: InputModule(); diff --git a/include/renderer/renderer_module.h b/include/renderer/renderer_module.h index 80bbbac..f818aaf 100644 --- a/include/renderer/renderer_module.h +++ b/include/renderer/renderer_module.h @@ -24,7 +24,8 @@ class AssetsModule; * - 执行实际渲染 */ class RendererModule : public Module { - E2D_REGISTER_MODULE(RendererModule, "Renderer", 3) + // 优先级为 4,在 AssetsModule (优先级 3) 之后初始化 + E2D_REGISTER_MODULE(RendererModule, "Renderer", 4) public: /** diff --git a/include/renderer/shader.h b/include/renderer/shader.h index c6a39b7..82badac 100644 --- a/include/renderer/shader.h +++ b/include/renderer/shader.h @@ -27,14 +27,6 @@ public: */ ~Shader() override; - /** - * @brief 从文件加载着色器 - * @param vsPath 顶点着色器文件路径 - * @param fsPath 片段着色器文件路径 - * @return 加载是否成功 - */ - bool loadFromFile(const std::string &vsPath, const std::string &fsPath); - /** * @brief 从源码加载着色器 * @param vsSource 顶点着色器源码 @@ -43,14 +35,6 @@ public: */ bool loadFromSource(const std::string &vsSource, const std::string &fsSource); - /** - * @brief 从文件加载实例化着色器(支持实例属性) - * @param vsPath 顶点着色器文件路径 - * @param fsPath 片段着色器文件路径 - * @return 加载是否成功 - */ - bool loadInstancedFromFile(const std::string &vsPath, const std::string &fsPath); - /** * @brief 从源码加载实例化着色器(支持实例属性) * @param vsSource 顶点着色器源码 diff --git a/include/scene/scene_module.h b/include/scene/scene_module.h index 554273a..09290b5 100644 --- a/include/scene/scene_module.h +++ b/include/scene/scene_module.h @@ -13,8 +13,8 @@ namespace extra2d { * 将导演系统集成到引擎模块系统中 */ class SceneModule : public Module { - // 自动注册到模块系统,优先级为 4(在 Renderer 之后) - E2D_REGISTER_MODULE(SceneModule, "Scene", 4) + // 优先级为 5,在 RendererModule (优先级 4) 之后初始化 + E2D_REGISTER_MODULE(SceneModule, "Scene", 5) public: SceneModule(); diff --git a/include/utils/timer_module.h b/include/utils/timer_module.h index 8a02e83..8cfcf8e 100644 --- a/include/utils/timer_module.h +++ b/include/utils/timer_module.h @@ -44,8 +44,8 @@ struct TimerInfo { * 通过事件总线接收更新事件,无需直接依赖 */ class TimerModule : public Module { - // 自动注册到模块系统,优先级为 5(核心模块) - E2D_REGISTER_MODULE(TimerModule, "Timer", 5) + // 优先级为 6,在 SceneModule (优先级 5) 之后初始化 + E2D_REGISTER_MODULE(TimerModule, "Timer", 6) public: TimerModule(); diff --git a/src/assets/assets_module.cpp b/src/assets/assets_module.cpp index 7e83a8a..e2e0342 100644 --- a/src/assets/assets_module.cpp +++ b/src/assets/assets_module.cpp @@ -4,11 +4,66 @@ #include #include #include +#include +#include #include #include namespace extra2d { +namespace { + +/** + * @brief 获取 FileModule 实例 + */ +FileModule *getFileModule() { return getModule(); } + +/** + * @brief 检查路径是否为 RomFS 路径 + */ +bool isRomfsPath(const std::string &path) { + FileModule *fileModule = getFileModule(); + if (fileModule) { + return fileModule->isRomfsPath(path); + } + return false; +} + +/** + * @brief 检查文件是否存在(使用 FileModule) + */ +bool fileExists(const std::string &path) { + FileModule *fileModule = getFileModule(); + if (fileModule) { + return fileModule->exists(path); + } + return false; +} + +/** + * @brief 读取文件内容为字符串(使用 FileModule,支持 RomFS) + */ +std::string readFileToString(const std::string &path) { + FileModule *fileModule = getFileModule(); + if (fileModule) { + return fileModule->readString(path); + } + return ""; +} + +/** + * @brief 获取文件最后写入时间(支持 RomFS 路径) + */ +std::filesystem::file_time_type getLastWriteTime(const std::string &path) { + if (isRomfsPath(path)) { + // RomFS 文件是只读的,返回一个固定时间 + return std::filesystem::file_time_type::min(); + } + return std::filesystem::last_write_time(path); +} + +} // anonymous namespace + AssetsModule *g_assetsModule = nullptr; AssetsModule::AssetsModule() { g_assetsModule = this; } @@ -299,7 +354,7 @@ AssetsModule::loadDir(const std::string &directory, try { std::filesystem::path dirPath(directory); - if (!std::filesystem::exists(dirPath)) { + if (!fileExists(directory)) { E2D_LOG_WARN("Directory not found: {}", directory); return handles; } @@ -390,27 +445,41 @@ bool AssetsModule::createDefaultResources() { { // 从文件加载默认着色器 - std::filesystem::path vertPath = "shader/default.vert"; - std::filesystem::path fragPath = "shader/default.frag"; + // 使用 FileModule 的 assetPath 来获取正确的资源路径(支持 RomFS) + FileModule *fileModule = getFileModule(); + std::string vertPath = fileModule + ? fileModule->assetPath("shader/default.vert") + : "shader/default.vert"; + std::string fragPath = fileModule + ? fileModule->assetPath("shader/default.frag") + : "shader/default.frag"; - Ptr shader = makePtr(); - - if (!std::filesystem::exists(vertPath) || - !std::filesystem::exists(fragPath)) { - E2D_LOG_ERROR("Default shader files not found: {}, {}", vertPath.string(), - fragPath.string()); + if (!fileExists(vertPath) || !fileExists(fragPath)) { + E2D_LOG_ERROR("Default shader files not found: {}, {}", vertPath, + fragPath); return false; } - if (!shader->loadFromFile(vertPath.string(), fragPath.string())) { - E2D_LOG_ERROR("Failed to load default shader from files: {}, {}", - vertPath.string(), fragPath.string()); + // 读取着色器文件内容 + std::string vsSource = readFileToString(vertPath); + std::string fsSource = readFileToString(fragPath); + if (vsSource.empty() || fsSource.empty()) { + E2D_LOG_ERROR("Failed to read default shader files: {}, {}", vertPath, + fragPath); + return false; + } + + // 从源码加载着色器 + Ptr shader = makePtr(); + if (!shader->loadFromSource(vsSource, fsSource)) { + E2D_LOG_ERROR("Failed to compile default shader: {}, {}", vertPath, + fragPath); return false; } defaultShader_ = shaders_.insert(shader); - E2D_LOG_DEBUG("Loaded default shader from files: {}, {}", vertPath.string(), - fragPath.string()); + E2D_LOG_DEBUG("Loaded default shader from files: {}, {}", vertPath, + fragPath); } { @@ -505,26 +574,41 @@ Material *AssetsModule::getInstancedMaterialPtr() { bool AssetsModule::createInstancedResources() { // 加载实例化着色器 - std::filesystem::path vertPath = "shader/sprite_instanced.vert"; - std::filesystem::path fragPath = "shader/sprite_instanced.frag"; + // 使用 FileModule 的 assetPath 来获取正确的资源路径(支持 RomFS) + FileModule *fileModule = getFileModule(); + std::string vertPath = + fileModule ? fileModule->assetPath("shader/sprite_instanced.vert") + : "shader/sprite_instanced.vert"; + std::string fragPath = + fileModule ? fileModule->assetPath("shader/sprite_instanced.frag") + : "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()); + if (!fileExists(vertPath) || !fileExists(fragPath)) { + E2D_LOG_ERROR("Instanced shader files not found: {}, {}", vertPath, + fragPath); return false; } + // 读取着色器文件内容 + std::string vsSource = readFileToString(vertPath); + std::string fsSource = readFileToString(fragPath); + if (vsSource.empty() || fsSource.empty()) { + E2D_LOG_ERROR("Failed to read instanced shader files: {}, {}", vertPath, + fragPath); + return false; + } + + // 从源码加载实例化着色器 Ptr shader = makePtr(); - if (!shader->loadInstancedFromFile(vertPath.string(), fragPath.string())) { - E2D_LOG_ERROR("Failed to load instanced shader from files: {}, {}", - vertPath.string(), fragPath.string()); + if (!shader->loadInstancedFromSource(vsSource, fsSource)) { + E2D_LOG_ERROR("Failed to compile instanced shader: {}, {}", vertPath, + fragPath); return false; } instancedShader_ = shaders_.insert(shader); - E2D_LOG_DEBUG("Loaded instanced shader from files: {}, {}", vertPath.string(), - fragPath.string()); + E2D_LOG_DEBUG("Loaded instanced shader from files: {}, {}", vertPath, + fragPath); // 创建实例化材质布局 Ptr layout = makePtr(); @@ -570,13 +654,13 @@ void AssetsModule::setHotReloadInterval(float interval) { void AssetsModule::addFileWatch(const std::string &path, Handle handle) { try { - if (!std::filesystem::exists(path)) { + if (!fileExists(path)) { return; } FileWatchInfo info; info.path = path; - info.lastWriteTime = std::filesystem::last_write_time(path); + info.lastWriteTime = getLastWriteTime(path); info.textureHandle = handle; info.isTexture = true; @@ -591,13 +675,13 @@ void AssetsModule::addFileWatch(const std::string &path, void AssetsModule::addFileWatch(const std::string &path, Handle handle) { try { - if (!std::filesystem::exists(path)) { + if (!fileExists(path)) { return; } FileWatchInfo info; info.path = path; - info.lastWriteTime = std::filesystem::last_write_time(path); + info.lastWriteTime = getLastWriteTime(path); info.shaderHandle = handle; info.isTexture = false; @@ -691,11 +775,16 @@ void AssetsModule::checkForChanges() { for (auto &info : fileWatchList_) { try { - if (!std::filesystem::exists(info.path)) { + // 跳过 RomFS 路径(只读文件系统) + if (isRomfsPath(info.path)) { continue; } - auto currentTime = std::filesystem::last_write_time(info.path); + if (!fileExists(info.path)) { + continue; + } + + auto currentTime = getLastWriteTime(info.path); if (currentTime != info.lastWriteTime) { info.lastWriteTime = currentTime; diff --git a/src/assets/loaders/shader_loader.cpp b/src/assets/loaders/shader_loader.cpp index e672e3e..c73e7d9 100644 --- a/src/assets/loaders/shader_loader.cpp +++ b/src/assets/loaders/shader_loader.cpp @@ -1,10 +1,44 @@ #include -#include +#include +#include #include #include namespace extra2d { +namespace { + +/** + * @brief 获取 FileModule 实例 + */ +FileModule *getFileModule() { + return getModule(); +} + +/** + * @brief 检查文件是否存在(使用 FileModule) + */ +bool fileExists(const std::string &path) { + FileModule *fileModule = getFileModule(); + if (fileModule) { + return fileModule->exists(path); + } + return false; +} + +/** + * @brief 读取文件内容为字符串(使用 FileModule,支持 RomFS) + */ +std::string readFileToString(const std::string &path) { + FileModule *fileModule = getFileModule(); + if (fileModule) { + return fileModule->readString(path); + } + return ""; +} + +} // anonymous namespace + Ptr ShaderLoader::load(const std::string &path) { std::string basePath = path; @@ -16,10 +50,10 @@ Ptr ShaderLoader::load(const std::string &path) { std::string vertPath = basePath + ".vert"; std::string fragPath = basePath + ".frag"; - if (!std::filesystem::exists(vertPath)) { + if (!fileExists(vertPath)) { vertPath = basePath + ".vs"; } - if (!std::filesystem::exists(fragPath)) { + if (!fileExists(fragPath)) { fragPath = basePath + ".fs"; } @@ -28,10 +62,24 @@ Ptr ShaderLoader::load(const std::string &path) { Ptr ShaderLoader::load(const std::string &vertPath, const std::string &fragPath) { - Ptr shader = makePtr(); + // 读取顶点着色器文件 + std::string vsSource = readFileToString(vertPath); + if (vsSource.empty()) { + E2D_LOG_ERROR("ShaderLoader: Failed to read vertex shader: {}", vertPath); + return Ptr(); + } - if (!shader->loadFromFile(vertPath, fragPath)) { - E2D_LOG_ERROR("ShaderLoader: Failed to load shader {} + {}", vertPath, + // 读取片段着色器文件 + std::string fsSource = readFileToString(fragPath); + if (fsSource.empty()) { + E2D_LOG_ERROR("ShaderLoader: Failed to read fragment shader: {}", fragPath); + return Ptr(); + } + + // 从源码加载着色器 + Ptr shader = makePtr(); + if (!shader->loadFromSource(vsSource, fsSource)) { + E2D_LOG_ERROR("ShaderLoader: Failed to compile shader {} + {}", vertPath, fragPath); return Ptr(); } diff --git a/src/platform/file_module.cpp b/src/platform/file_module.cpp new file mode 100644 index 0000000..cf75588 --- /dev/null +++ b/src/platform/file_module.cpp @@ -0,0 +1,225 @@ +#include +#include +#include +#include +#include +#include + +#ifdef __SWITCH__ +#include +#endif + +namespace extra2d { + +namespace fs = std::filesystem; + +FileModule::FileModule() = default; + +FileModule::~FileModule() = default; + +FileModule::FileModule(FileModule &&) noexcept = default; +FileModule &FileModule::operator=(FileModule &&) noexcept = default; + +bool FileModule::init() { + writableDir_ = SDL_GetPrefPath("Extra2D", "Extra2D"); + if (writableDir_.empty()) { + writableDir_ = "./"; + } + +#ifdef __SWITCH__ + // 初始化 Switch 的 RomFS + Result rc = romfsInit(); + if (R_SUCCEEDED(rc)) { + E2D_LOG_INFO("RomFS initialized successfully"); + // 在 Switch 上设置资源根目录为 RomFS + assetRoot_ = "romfs:/"; + } else { + E2D_LOG_WARN("romfsInit failed: {:#08X}", rc); + // RomFS 初始化失败,使用当前目录 + assetRoot_ = "./"; + } +#else + // 非 Switch 平台,使用当前目录作为资源根目录 + assetRoot_ = "./"; +#endif + + E2D_LOG_INFO("Asset root set to: {}", assetRoot_); + return true; +} + +void FileModule::shutdown() { +#ifdef __SWITCH__ + // 关闭 RomFS + romfsExit(); +#endif +} + +bool FileModule::isRomfsPath(const std::string &path) const { + return path.find(romfsRoot_) == 0; +} + +bool FileModule::exists(const std::string &path) const { + return fs::exists(path); +} + +bool FileModule::isDir(const std::string &path) const { + return fs::is_directory(path); +} + +FileData FileModule::read(const std::string &path) const { + FileData result; + + std::ifstream file(path, std::ios::binary | std::ios::ate); + if (!file.is_open()) { + result.error = "Cannot open file: " + path; + return result; + } + + std::streamsize size = file.tellg(); + file.seekg(0, std::ios::beg); + + result.data.resize(static_cast(size)); + if (!file.read(reinterpret_cast(result.data.data()), size)) { + result.error = "Failed to read file: " + path; + return result; + } + + result.ok = true; + return result; +} + +std::string FileModule::readString(const std::string &path) const { + std::ifstream file(path); + if (!file.is_open()) + return ""; + + std::stringstream buffer; + buffer << file.rdbuf(); + return buffer.str(); +} + +bool FileModule::write(const std::string &path, const void *data, + size_t size) const { + // RomFS 是只读的,不允许写入 + if (isRomfsPath(path)) { + E2D_LOG_WARN("Cannot write to RomFS path: {}", path); + return false; + } + + std::ofstream file(path, std::ios::binary); + if (!file.is_open()) + return false; + + file.write(static_cast(data), + static_cast(size)); + return file.good(); +} + +bool FileModule::writeString(const std::string &path, + const std::string &content) const { + return write(path, content.data(), content.size()); +} + +bool FileModule::append(const std::string &path, const void *data, + size_t size) const { + // RomFS 是只读的,不允许写入 + if (isRomfsPath(path)) { + E2D_LOG_WARN("Cannot append to RomFS path: {}", path); + return false; + } + + std::ofstream file(path, std::ios::binary | std::ios::app); + if (!file.is_open()) + return false; + + file.write(static_cast(data), + static_cast(size)); + return file.good(); +} + +bool FileModule::remove(const std::string &path) const { + // RomFS 是只读的,不允许删除 + if (isRomfsPath(path)) { + E2D_LOG_WARN("Cannot remove RomFS path: {}", path); + return false; + } + return fs::remove(path); +} + +bool FileModule::mkdir(const std::string &path) const { + // RomFS 是只读的,不允许创建目录 + if (isRomfsPath(path)) { + E2D_LOG_WARN("Cannot create directory in RomFS: {}", path); + return false; + } + + return fs::create_directories(path); +} + +std::vector FileModule::listDir(const std::string &path) const { + std::vector result; + + if (!fs::exists(path) || !fs::is_directory(path)) { + return result; + } + + for (const auto &entry : fs::directory_iterator(path)) { + std::string name = entry.path().filename().string(); + if (name == "." || name == "..") + continue; + + FileInfo info; + info.name = name; + info.path = entry.path().string(); + info.isDir = entry.is_directory(); + if (!info.isDir && entry.is_regular_file()) { + info.size = static_cast(entry.file_size()); + } + result.push_back(info); + } + + return result; +} + +int64 FileModule::fileSize(const std::string &path) const { + if (!fs::exists(path) || !fs::is_regular_file(path)) { + return -1; + } + return static_cast(fs::file_size(path)); +} + +std::string FileModule::ext(const std::string &path) const { + fs::path p(path); + return p.extension().string(); +} + +std::string FileModule::fileName(const std::string &path) const { + fs::path p(path); + return p.filename().string(); +} + +std::string FileModule::dirName(const std::string &path) const { + fs::path p(path); + return p.parent_path().string(); +} + +std::string FileModule::join(const std::string &a, const std::string &b) const { + if (a.empty()) + return b; + if (b.empty()) + return a; + + fs::path base(a); + fs::path sub(b); + return (base / sub).string(); +} + +std::string FileModule::writableDir() const { return writableDir_; } + +std::string FileModule::assetPath(const std::string &relPath) const { + if (assetRoot_.empty()) + return relPath; + return join(assetRoot_, relPath); +} + +} // namespace extra2d diff --git a/src/renderer/shader.cpp b/src/renderer/shader.cpp index 9f297a2..62aaef2 100644 --- a/src/renderer/shader.cpp +++ b/src/renderer/shader.cpp @@ -1,7 +1,5 @@ -#include #include #include -#include #include namespace extra2d { @@ -15,31 +13,6 @@ Shader::~Shader() { handle_ = ShaderHandle(); } -bool Shader::loadFromFile(const std::string &vsPath, - const std::string &fsPath) { - // 读取顶点着色器 - std::ifstream vsFile(vsPath); - if (!vsFile.is_open()) { - E2D_LOG_ERROR("Failed to open vertex shader: {}", vsPath); - return false; - } - std::stringstream vsStream; - vsStream << vsFile.rdbuf(); - std::string vsSource = vsStream.str(); - - // 读取片段着色器 - std::ifstream fsFile(fsPath); - if (!fsFile.is_open()) { - E2D_LOG_ERROR("Failed to open fragment shader: {}", fsPath); - return false; - } - std::stringstream fsStream; - fsStream << fsFile.rdbuf(); - std::string fsSource = fsStream.str(); - - return loadFromSource(vsSource, fsSource); -} - bool Shader::loadFromSource(const std::string &vsSource, const std::string &fsSource) { // 创建标准2D顶点布局(位置 + 纹理坐标 + 颜色) @@ -52,42 +25,17 @@ bool Shader::loadFromSource(const std::string &vsSource, return loadFromSourceWithLayout(vsSource, fsSource, vertexLayout); } -bool Shader::loadInstancedFromFile(const std::string &vsPath, - const std::string &fsPath) { - // 读取顶点着色器 - std::ifstream vsFile(vsPath); - if (!vsFile.is_open()) { - E2D_LOG_ERROR("Failed to open instanced vertex shader: {}", vsPath); - return false; - } - std::stringstream vsStream; - vsStream << vsFile.rdbuf(); - std::string vsSource = vsStream.str(); - - // 读取片段着色器 - std::ifstream fsFile(fsPath); - if (!fsFile.is_open()) { - E2D_LOG_ERROR("Failed to open instanced fragment shader: {}", fsPath); - return false; - } - std::stringstream fsStream; - fsStream << fsFile.rdbuf(); - std::string fsSource = fsStream.str(); - - return loadInstancedFromSource(vsSource, fsSource); -} - bool Shader::loadInstancedFromSource(const std::string &vsSource, - const std::string &fsSource) { + const std::string &fsSource) { // 创建实例化渲染顶点布局 // 槽位0:顶点属性(位置、UV、颜色) // 槽位1:实例属性(位置、旋转、缩放、颜色、UV区域) VertexLayout vertexLayout; // 顶点属性(每个顶点)- 槽位0 - vertexLayout.addAttribute(0, VertexFormat::Float2, 0); // 位置 - vertexLayout.addAttribute(1, VertexFormat::Float2, 8); // UV - vertexLayout.addAttribute(2, VertexFormat::Float4, 16); // 颜色 + vertexLayout.addAttribute(0, VertexFormat::Float2, 0); // 位置 + vertexLayout.addAttribute(1, VertexFormat::Float2, 8); // UV + vertexLayout.addAttribute(2, VertexFormat::Float4, 16); // 颜色 // 实例属性(每个实例)- 槽位1 // InstanceData 布局: @@ -101,11 +49,16 @@ bool Shader::loadInstancedFromSource(const std::string &vsSource, // float uvY; // offset 52 // float uvWidth; // offset 56 // float uvHeight; // offset 60 - vertexLayout.addAttribute(VertexAttribute::perInstance(3, VertexFormat::Float2, 0, 1)); // iPosition - vertexLayout.addAttribute(VertexAttribute::perInstance(4, VertexFormat::Float1, 8, 1)); // iRotation - vertexLayout.addAttribute(VertexAttribute::perInstance(5, VertexFormat::Float2, 16, 1)); // iScale - vertexLayout.addAttribute(VertexAttribute::perInstance(6, VertexFormat::Float4, 32, 1)); // iColor - vertexLayout.addAttribute(VertexAttribute::perInstance(7, VertexFormat::Float4, 48, 1)); // iUVRect + vertexLayout.addAttribute( + VertexAttribute::perInstance(3, VertexFormat::Float2, 0, 1)); // iPosition + vertexLayout.addAttribute( + VertexAttribute::perInstance(4, VertexFormat::Float1, 8, 1)); // iRotation + vertexLayout.addAttribute( + VertexAttribute::perInstance(5, VertexFormat::Float2, 16, 1)); // iScale + vertexLayout.addAttribute( + VertexAttribute::perInstance(6, VertexFormat::Float4, 32, 1)); // iColor + vertexLayout.addAttribute( + VertexAttribute::perInstance(7, VertexFormat::Float4, 48, 1)); // iUVRect // 注意:stride 在实例化渲染中由 buffer 的 stride 参数控制 vertexLayout.stride = sizeof(float) * 8; // 顶点缓冲区步长