refactor(模块系统): 调整模块初始化优先级并重构文件加载逻辑

重构模块初始化顺序,确保文件模块优先加载
移除Shader类中直接文件操作,统一通过FileModule进行文件访问
添加FileModule实现跨平台文件系统操作,支持RomFS
重构资源加载逻辑,统一使用FileModule进行文件操作
This commit is contained in:
ChestnutYueyue 2026-03-03 05:53:40 +08:00
parent 8cd883ede7
commit ec6ced9db2
11 changed files with 602 additions and 122 deletions

View File

@ -33,7 +33,8 @@ namespace extra2d {
* - Handle<T>: * - Handle<T>:
*/ */
class AssetsModule : public Module { class AssetsModule : public Module {
E2D_REGISTER_MODULE(AssetsModule, "Assets", 2) // 优先级为 3在 FileModule (优先级 2) 之后初始化
E2D_REGISTER_MODULE(AssetsModule, "Assets", 3)
public: public:
AssetsModule(); AssetsModule();

View File

@ -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

View File

@ -172,8 +172,8 @@ using TouchCb = std::function<void(const TouchPoint &)>;
* 使 Module * 使 Module
*/ */
class InputModule : public Module { class InputModule : public Module {
// 自动注册到模块系统,优先级为 10 // 优先级为 7在 TimerModule (优先级 6) 之后初始化
E2D_REGISTER_MODULE(InputModule, "Input", 10) E2D_REGISTER_MODULE(InputModule, "Input", 7)
public: public:
InputModule(); InputModule();

View File

@ -24,7 +24,8 @@ class AssetsModule;
* - * -
*/ */
class RendererModule : public Module { class RendererModule : public Module {
E2D_REGISTER_MODULE(RendererModule, "Renderer", 3) // 优先级为 4在 AssetsModule (优先级 3) 之后初始化
E2D_REGISTER_MODULE(RendererModule, "Renderer", 4)
public: public:
/** /**

View File

@ -27,14 +27,6 @@ public:
*/ */
~Shader() override; ~Shader() override;
/**
* @brief
* @param vsPath
* @param fsPath
* @return
*/
bool loadFromFile(const std::string &vsPath, const std::string &fsPath);
/** /**
* @brief * @brief
* @param vsSource * @param vsSource
@ -43,14 +35,6 @@ public:
*/ */
bool loadFromSource(const std::string &vsSource, const std::string &fsSource); 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 * @brief
* @param vsSource * @param vsSource

View File

@ -13,8 +13,8 @@ namespace extra2d {
* *
*/ */
class SceneModule : public Module { class SceneModule : public Module {
// 自动注册到模块系统,优先级为 4在 Renderer 之后) // 优先级为 5在 RendererModule (优先级 4) 之后初始化
E2D_REGISTER_MODULE(SceneModule, "Scene", 4) E2D_REGISTER_MODULE(SceneModule, "Scene", 5)
public: public:
SceneModule(); SceneModule();

View File

@ -44,8 +44,8 @@ struct TimerInfo {
* 线 * 线
*/ */
class TimerModule : public Module { class TimerModule : public Module {
// 自动注册到模块系统,优先级为 5核心模块 // 优先级为 6在 SceneModule (优先级 5) 之后初始化
E2D_REGISTER_MODULE(TimerModule, "Timer", 5) E2D_REGISTER_MODULE(TimerModule, "Timer", 6)
public: public:
TimerModule(); TimerModule();

View File

@ -4,11 +4,66 @@
#include <assets/loaders/texture_loader.h> #include <assets/loaders/texture_loader.h>
#include <event/events.h> #include <event/events.h>
#include <filesystem> #include <filesystem>
#include <module/module_registry.h>
#include <platform/file_module.h>
#include <thread> #include <thread>
#include <utils/logger.h> #include <utils/logger.h>
namespace extra2d { 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 *g_assetsModule = nullptr;
AssetsModule::AssetsModule() { g_assetsModule = this; } AssetsModule::AssetsModule() { g_assetsModule = this; }
@ -299,7 +354,7 @@ AssetsModule::loadDir<Texture>(const std::string &directory,
try { try {
std::filesystem::path dirPath(directory); std::filesystem::path dirPath(directory);
if (!std::filesystem::exists(dirPath)) { if (!fileExists(directory)) {
E2D_LOG_WARN("Directory not found: {}", directory); E2D_LOG_WARN("Directory not found: {}", directory);
return handles; return handles;
} }
@ -390,27 +445,41 @@ bool AssetsModule::createDefaultResources() {
{ {
// 从文件加载默认着色器 // 从文件加载默认着色器
std::filesystem::path vertPath = "shader/default.vert"; // 使用 FileModule 的 assetPath 来获取正确的资源路径(支持 RomFS
std::filesystem::path fragPath = "shader/default.frag"; 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 (!fileExists(vertPath) || !fileExists(fragPath)) {
E2D_LOG_ERROR("Default shader files not found: {}, {}", vertPath,
if (!std::filesystem::exists(vertPath) || fragPath);
!std::filesystem::exists(fragPath)) {
E2D_LOG_ERROR("Default shader files not found: {}, {}", vertPath.string(),
fragPath.string());
return false; return false;
} }
if (!shader->loadFromFile(vertPath.string(), fragPath.string())) { // 读取着色器文件内容
E2D_LOG_ERROR("Failed to load default shader from files: {}, {}", std::string vsSource = readFileToString(vertPath);
vertPath.string(), fragPath.string()); 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; return false;
} }
defaultShader_ = shaders_.insert(shader); defaultShader_ = shaders_.insert(shader);
E2D_LOG_DEBUG("Loaded default shader from files: {}, {}", vertPath.string(), E2D_LOG_DEBUG("Loaded default shader from files: {}, {}", vertPath,
fragPath.string()); fragPath);
} }
{ {
@ -505,26 +574,41 @@ Material *AssetsModule::getInstancedMaterialPtr() {
bool AssetsModule::createInstancedResources() { bool AssetsModule::createInstancedResources() {
// 加载实例化着色器 // 加载实例化着色器
std::filesystem::path vertPath = "shader/sprite_instanced.vert"; // 使用 FileModule 的 assetPath 来获取正确的资源路径(支持 RomFS
std::filesystem::path fragPath = "shader/sprite_instanced.frag"; 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) || if (!fileExists(vertPath) || !fileExists(fragPath)) {
!std::filesystem::exists(fragPath)) { E2D_LOG_ERROR("Instanced shader files not found: {}, {}", vertPath,
E2D_LOG_ERROR("Instanced shader files not found: {}, {}", vertPath.string(), fragPath);
fragPath.string());
return false; 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>(); Ptr<Shader> shader = makePtr<Shader>();
if (!shader->loadInstancedFromFile(vertPath.string(), fragPath.string())) { if (!shader->loadInstancedFromSource(vsSource, fsSource)) {
E2D_LOG_ERROR("Failed to load instanced shader from files: {}, {}", E2D_LOG_ERROR("Failed to compile instanced shader: {}, {}", vertPath,
vertPath.string(), fragPath.string()); fragPath);
return false; return false;
} }
instancedShader_ = shaders_.insert(shader); instancedShader_ = shaders_.insert(shader);
E2D_LOG_DEBUG("Loaded instanced shader from files: {}, {}", vertPath.string(), E2D_LOG_DEBUG("Loaded instanced shader from files: {}, {}", vertPath,
fragPath.string()); fragPath);
// 创建实例化材质布局 // 创建实例化材质布局
Ptr<MaterialLayout> layout = makePtr<MaterialLayout>(); Ptr<MaterialLayout> layout = makePtr<MaterialLayout>();
@ -570,13 +654,13 @@ void AssetsModule::setHotReloadInterval(float interval) {
void AssetsModule::addFileWatch(const std::string &path, void AssetsModule::addFileWatch(const std::string &path,
Handle<Texture> handle) { Handle<Texture> handle) {
try { try {
if (!std::filesystem::exists(path)) { if (!fileExists(path)) {
return; return;
} }
FileWatchInfo info; FileWatchInfo info;
info.path = path; info.path = path;
info.lastWriteTime = std::filesystem::last_write_time(path); info.lastWriteTime = getLastWriteTime(path);
info.textureHandle = handle; info.textureHandle = handle;
info.isTexture = true; info.isTexture = true;
@ -591,13 +675,13 @@ void AssetsModule::addFileWatch(const std::string &path,
void AssetsModule::addFileWatch(const std::string &path, void AssetsModule::addFileWatch(const std::string &path,
Handle<Shader> handle) { Handle<Shader> handle) {
try { try {
if (!std::filesystem::exists(path)) { if (!fileExists(path)) {
return; return;
} }
FileWatchInfo info; FileWatchInfo info;
info.path = path; info.path = path;
info.lastWriteTime = std::filesystem::last_write_time(path); info.lastWriteTime = getLastWriteTime(path);
info.shaderHandle = handle; info.shaderHandle = handle;
info.isTexture = false; info.isTexture = false;
@ -691,11 +775,16 @@ void AssetsModule::checkForChanges() {
for (auto &info : fileWatchList_) { for (auto &info : fileWatchList_) {
try { try {
if (!std::filesystem::exists(info.path)) { // 跳过 RomFS 路径(只读文件系统)
if (isRomfsPath(info.path)) {
continue; continue;
} }
auto currentTime = std::filesystem::last_write_time(info.path); if (!fileExists(info.path)) {
continue;
}
auto currentTime = getLastWriteTime(info.path);
if (currentTime != info.lastWriteTime) { if (currentTime != info.lastWriteTime) {
info.lastWriteTime = currentTime; info.lastWriteTime = currentTime;

View File

@ -1,10 +1,44 @@
#include <assets/loaders/shader_loader.h> #include <assets/loaders/shader_loader.h>
#include <filesystem> #include <module/module_registry.h>
#include <platform/file_module.h>
#include <renderer/shader.h> #include <renderer/shader.h>
#include <utils/logger.h> #include <utils/logger.h>
namespace extra2d { 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) { Ptr<Shader> ShaderLoader::load(const std::string &path) {
std::string basePath = path; std::string basePath = path;
@ -16,10 +50,10 @@ Ptr<Shader> ShaderLoader::load(const std::string &path) {
std::string vertPath = basePath + ".vert"; std::string vertPath = basePath + ".vert";
std::string fragPath = basePath + ".frag"; std::string fragPath = basePath + ".frag";
if (!std::filesystem::exists(vertPath)) { if (!fileExists(vertPath)) {
vertPath = basePath + ".vs"; vertPath = basePath + ".vs";
} }
if (!std::filesystem::exists(fragPath)) { if (!fileExists(fragPath)) {
fragPath = basePath + ".fs"; fragPath = basePath + ".fs";
} }
@ -28,10 +62,24 @@ Ptr<Shader> ShaderLoader::load(const std::string &path) {
Ptr<Shader> ShaderLoader::load(const std::string &vertPath, Ptr<Shader> ShaderLoader::load(const std::string &vertPath,
const std::string &fragPath) { 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); fragPath);
return Ptr<Shader>(); return Ptr<Shader>();
} }

View File

@ -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

View File

@ -1,7 +1,5 @@
#include <fstream>
#include <renderer/rhi_module.h> #include <renderer/rhi_module.h>
#include <renderer/shader.h> #include <renderer/shader.h>
#include <sstream>
#include <utils/logger.h> #include <utils/logger.h>
namespace extra2d { namespace extra2d {
@ -15,31 +13,6 @@ Shader::~Shader() {
handle_ = ShaderHandle(); 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, bool Shader::loadFromSource(const std::string &vsSource,
const std::string &fsSource) { const std::string &fsSource) {
// 创建标准2D顶点布局位置 + 纹理坐标 + 颜色) // 创建标准2D顶点布局位置 + 纹理坐标 + 颜色)
@ -52,42 +25,17 @@ bool Shader::loadFromSource(const std::string &vsSource,
return loadFromSourceWithLayout(vsSource, fsSource, vertexLayout); 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, bool Shader::loadInstancedFromSource(const std::string &vsSource,
const std::string &fsSource) { const std::string &fsSource) {
// 创建实例化渲染顶点布局 // 创建实例化渲染顶点布局
// 槽位0顶点属性位置、UV、颜色 // 槽位0顶点属性位置、UV、颜色
// 槽位1实例属性位置、旋转、缩放、颜色、UV区域 // 槽位1实例属性位置、旋转、缩放、颜色、UV区域
VertexLayout vertexLayout; VertexLayout vertexLayout;
// 顶点属性(每个顶点)- 槽位0 // 顶点属性(每个顶点)- 槽位0
vertexLayout.addAttribute(0, VertexFormat::Float2, 0); // 位置 vertexLayout.addAttribute(0, VertexFormat::Float2, 0); // 位置
vertexLayout.addAttribute(1, VertexFormat::Float2, 8); // UV vertexLayout.addAttribute(1, VertexFormat::Float2, 8); // UV
vertexLayout.addAttribute(2, VertexFormat::Float4, 16); // 颜色 vertexLayout.addAttribute(2, VertexFormat::Float4, 16); // 颜色
// 实例属性(每个实例)- 槽位1 // 实例属性(每个实例)- 槽位1
// InstanceData 布局: // InstanceData 布局:
@ -101,11 +49,16 @@ bool Shader::loadInstancedFromSource(const std::string &vsSource,
// float uvY; // offset 52 // float uvY; // offset 52
// float uvWidth; // offset 56 // float uvWidth; // offset 56
// float uvHeight; // offset 60 // float uvHeight; // offset 60
vertexLayout.addAttribute(VertexAttribute::perInstance(3, VertexFormat::Float2, 0, 1)); // iPosition vertexLayout.addAttribute(
vertexLayout.addAttribute(VertexAttribute::perInstance(4, VertexFormat::Float1, 8, 1)); // iRotation VertexAttribute::perInstance(3, VertexFormat::Float2, 0, 1)); // iPosition
vertexLayout.addAttribute(VertexAttribute::perInstance(5, VertexFormat::Float2, 16, 1)); // iScale vertexLayout.addAttribute(
vertexLayout.addAttribute(VertexAttribute::perInstance(6, VertexFormat::Float4, 32, 1)); // iColor VertexAttribute::perInstance(4, VertexFormat::Float1, 8, 1)); // iRotation
vertexLayout.addAttribute(VertexAttribute::perInstance(7, VertexFormat::Float4, 48, 1)); // iUVRect 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 参数控制 // 注意stride 在实例化渲染中由 buffer 的 stride 参数控制
vertexLayout.stride = sizeof(float) * 8; // 顶点缓冲区步长 vertexLayout.stride = sizeof(float) * 8; // 顶点缓冲区步长