refactor(模块系统): 调整模块初始化优先级并重构文件加载逻辑
重构模块初始化顺序,确保文件模块优先加载 移除Shader类中直接文件操作,统一通过FileModule进行文件访问 添加FileModule实现跨平台文件系统操作,支持RomFS 重构资源加载逻辑,统一使用FileModule进行文件操作
This commit is contained in:
parent
8cd883ede7
commit
ec6ced9db2
|
|
@ -33,7 +33,8 @@ namespace extra2d {
|
|||
* - Handle<T>: 轻量级句柄
|
||||
*/
|
||||
class AssetsModule : public Module {
|
||||
E2D_REGISTER_MODULE(AssetsModule, "Assets", 2)
|
||||
// 优先级为 3,在 FileModule (优先级 2) 之后初始化
|
||||
E2D_REGISTER_MODULE(AssetsModule, "Assets", 3)
|
||||
|
||||
public:
|
||||
AssetsModule();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,179 @@
|
|||
#pragma once
|
||||
|
||||
#include <module/module.h>
|
||||
#include <module/module_registry.h>
|
||||
#include <string>
|
||||
#include <types/base/types.h>
|
||||
#include <vector>
|
||||
|
||||
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<uint8> 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<FileInfo> 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
|
||||
|
|
@ -172,8 +172,8 @@ using TouchCb = std::function<void(const TouchPoint &)>;
|
|||
* 使用新的 Module 基类,支持自动注册
|
||||
*/
|
||||
class InputModule : public Module {
|
||||
// 自动注册到模块系统,优先级为 10
|
||||
E2D_REGISTER_MODULE(InputModule, "Input", 10)
|
||||
// 优先级为 7,在 TimerModule (优先级 6) 之后初始化
|
||||
E2D_REGISTER_MODULE(InputModule, "Input", 7)
|
||||
|
||||
public:
|
||||
InputModule();
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 顶点着色器源码
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -4,11 +4,66 @@
|
|||
#include <assets/loaders/texture_loader.h>
|
||||
#include <event/events.h>
|
||||
#include <filesystem>
|
||||
#include <module/module_registry.h>
|
||||
#include <platform/file_module.h>
|
||||
#include <thread>
|
||||
#include <utils/logger.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief 获取 FileModule 实例
|
||||
*/
|
||||
FileModule *getFileModule() { return getModule<FileModule>(); }
|
||||
|
||||
/**
|
||||
* @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<Texture>(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> shader = makePtr<Shader>();
|
||||
|
||||
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> shader = makePtr<Shader>();
|
||||
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> shader = makePtr<Shader>();
|
||||
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<MaterialLayout> layout = makePtr<MaterialLayout>();
|
||||
|
|
@ -570,13 +654,13 @@ void AssetsModule::setHotReloadInterval(float interval) {
|
|||
void AssetsModule::addFileWatch(const std::string &path,
|
||||
Handle<Texture> 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<Shader> 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,44 @@
|
|||
#include <assets/loaders/shader_loader.h>
|
||||
#include <filesystem>
|
||||
#include <module/module_registry.h>
|
||||
#include <platform/file_module.h>
|
||||
#include <renderer/shader.h>
|
||||
#include <utils/logger.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief 获取 FileModule 实例
|
||||
*/
|
||||
FileModule *getFileModule() {
|
||||
return getModule<FileModule>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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<Shader> ShaderLoader::load(const std::string &path) {
|
||||
std::string basePath = path;
|
||||
|
||||
|
|
@ -16,10 +50,10 @@ Ptr<Shader> 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<Shader> ShaderLoader::load(const std::string &path) {
|
|||
|
||||
Ptr<Shader> ShaderLoader::load(const std::string &vertPath,
|
||||
const std::string &fragPath) {
|
||||
Ptr<Shader> shader = makePtr<Shader>();
|
||||
// 读取顶点着色器文件
|
||||
std::string vsSource = readFileToString(vertPath);
|
||||
if (vsSource.empty()) {
|
||||
E2D_LOG_ERROR("ShaderLoader: Failed to read vertex shader: {}", vertPath);
|
||||
return Ptr<Shader>();
|
||||
}
|
||||
|
||||
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<Shader>();
|
||||
}
|
||||
|
||||
// 从源码加载着色器
|
||||
Ptr<Shader> shader = makePtr<Shader>();
|
||||
if (!shader->loadFromSource(vsSource, fsSource)) {
|
||||
E2D_LOG_ERROR("ShaderLoader: Failed to compile shader {} + {}", vertPath,
|
||||
fragPath);
|
||||
return Ptr<Shader>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,225 @@
|
|||
#include <SDL.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <platform/file_module.h>
|
||||
#include <sstream>
|
||||
#include <utils/logger.h>
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include <switch.h>
|
||||
#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_t>(size));
|
||||
if (!file.read(reinterpret_cast<char *>(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<const char *>(data),
|
||||
static_cast<std::streamsize>(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<const char *>(data),
|
||||
static_cast<std::streamsize>(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<FileInfo> FileModule::listDir(const std::string &path) const {
|
||||
std::vector<FileInfo> 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<int64>(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<int64>(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
|
||||
|
|
@ -1,7 +1,5 @@
|
|||
#include <fstream>
|
||||
#include <renderer/rhi_module.h>
|
||||
#include <renderer/shader.h>
|
||||
#include <sstream>
|
||||
#include <utils/logger.h>
|
||||
|
||||
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; // 顶点缓冲区步长
|
||||
|
|
|
|||
Loading…
Reference in New Issue