feat: 添加核心模块和图形渲染基础架构
实现核心对象、资源管理、场景图和图形渲染管线的基础架构,包括: 1. 核心模块:Object、Asset、RefCounted 等基础类 2. 场景图:Node、Component、Scene 等场景管理类 3. 图形渲染:GFXObject、GFXDevice、GFXBuffer 等渲染抽象层 4. OpenGL 实现:GLBuffer、GLTexture、GLShader 等具体实现 5. 2D 组件:SpriteFrame 等 2D 渲染相关组件 这些改动为引擎提供了基本的对象生命周期管理、资源加载和图形渲染能力
This commit is contained in:
parent
3e00b290d0
commit
5fddc4a209
|
|
@ -1,528 +0,0 @@
|
|||
# Extra2D 资源服务系统
|
||||
|
||||
## 概述
|
||||
|
||||
Extra2D 资源服务系统提供了一套完整的资源管理解决方案,包括资源加载、缓存、打包和加密压缩功能。
|
||||
|
||||
### 主要特性
|
||||
|
||||
- **类型安全**:使用模板和强类型 ID 避免类型错误
|
||||
- **自动缓存**:LRU 缓存策略,自动管理内存
|
||||
- **异步加载**:后台线程加载,不阻塞主线程
|
||||
- **资源打包**:支持压缩和加密的资源包格式
|
||||
- **多格式支持**:纹理、字体、着色器、音频等
|
||||
|
||||
## 模块结构
|
||||
|
||||
```
|
||||
extra2d/asset/
|
||||
├── asset_types.h # 资源类型定义
|
||||
├── asset.h # 资源基类
|
||||
├── asset_handle.h # 资源句柄
|
||||
├── asset_cache.h # 资源缓存
|
||||
├── asset_loader.h # 资源加载器
|
||||
├── asset_pack.h # 资源包
|
||||
├── data_processor.h # 数据处理器
|
||||
└── asset_service.h # 资源服务
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 初始化资源服务
|
||||
|
||||
```cpp
|
||||
#include <extra2d/extra2d.h>
|
||||
|
||||
using namespace extra2d;
|
||||
|
||||
// 创建资源服务
|
||||
AssetService service;
|
||||
service.init();
|
||||
service.setRoot("assets");
|
||||
|
||||
// 注册加载器
|
||||
service.registerLoader<TextureAsset>(AssetLoaderFactory::createTextureLoader());
|
||||
service.registerLoader<FontAsset>(AssetLoaderFactory::createFontLoader());
|
||||
service.registerLoader<ShaderAsset>(AssetLoaderFactory::createShaderLoader());
|
||||
```
|
||||
|
||||
### 2. 同步加载资源
|
||||
|
||||
```cpp
|
||||
// 加载纹理
|
||||
AssetHandle<TextureAsset> texture = service.load<TextureAsset>("sprites/player.png");
|
||||
if (texture.valid()) {
|
||||
int width = texture->width();
|
||||
int height = texture->height();
|
||||
const u8* data = texture->data();
|
||||
}
|
||||
```
|
||||
|
||||
### 3. 异步加载资源
|
||||
|
||||
```cpp
|
||||
// 异步加载
|
||||
service.loadAsync<TextureAsset>("sprites/background.png",
|
||||
[](AssetHandle<TextureAsset> handle) {
|
||||
if (handle.valid()) {
|
||||
// 资源加载完成,在主线程处理
|
||||
}
|
||||
});
|
||||
|
||||
// 在主线程处理回调
|
||||
service.process();
|
||||
```
|
||||
|
||||
### 4. 使用资源包
|
||||
|
||||
```cpp
|
||||
// 挂载资源包
|
||||
service.mount("data.pak");
|
||||
|
||||
// 从资源包加载
|
||||
auto texture = service.load<TextureAsset>("textures/hero.png");
|
||||
|
||||
// 卸载资源包
|
||||
service.unmount("data.pak");
|
||||
```
|
||||
|
||||
### 5. 缓存管理
|
||||
|
||||
```cpp
|
||||
// 设置缓存上限 (100MB)
|
||||
service.setLimit(100 * 1024 * 1024);
|
||||
|
||||
// 获取缓存统计
|
||||
CacheStats stats = service.stats();
|
||||
std::cout << "缓存使用: " << stats.bytes << " 字节\n";
|
||||
std::cout << "命中率: " << (stats.hitRate() * 100) << "%\n";
|
||||
|
||||
// 清理无引用资源
|
||||
service.purge();
|
||||
|
||||
// 清空缓存
|
||||
service.clear();
|
||||
```
|
||||
|
||||
## API 参考
|
||||
|
||||
### AssetID
|
||||
|
||||
资源标识符,使用哈希值进行快速比较。
|
||||
|
||||
```cpp
|
||||
struct AssetID {
|
||||
u64 hash; // 哈希值
|
||||
std::string path; // 原始路径
|
||||
|
||||
explicit AssetID(const std::string& path);
|
||||
bool valid() const;
|
||||
bool operator==(const AssetID& other) const;
|
||||
};
|
||||
```
|
||||
|
||||
### AssetHandle<T>
|
||||
|
||||
类型安全的资源句柄,使用弱引用避免阻止资源回收。
|
||||
|
||||
```cpp
|
||||
template<typename T>
|
||||
class AssetHandle {
|
||||
public:
|
||||
bool valid() const; // 检查是否有效
|
||||
Ref<T> get() const; // 获取强引用
|
||||
T* operator->() const; // 解引用
|
||||
bool loaded() const; // 检查是否已加载
|
||||
AssetState state() const; // 获取状态
|
||||
};
|
||||
```
|
||||
|
||||
### AssetCache
|
||||
|
||||
LRU 缓存管理器。
|
||||
|
||||
```cpp
|
||||
class AssetCache {
|
||||
public:
|
||||
explicit AssetCache(size_t limit = 0);
|
||||
|
||||
template<typename T>
|
||||
AssetHandle<T> add(Ref<T> asset);
|
||||
|
||||
template<typename T>
|
||||
AssetHandle<T> get(const AssetID& id);
|
||||
|
||||
bool has(const AssetID& id) const;
|
||||
bool remove(const AssetID& id);
|
||||
|
||||
void setLimit(size_t limit);
|
||||
size_t bytes() const;
|
||||
size_t count() const;
|
||||
|
||||
size_t purge(); // 清理无引用资源
|
||||
void clear(); // 清空缓存
|
||||
CacheStats stats() const;
|
||||
};
|
||||
```
|
||||
|
||||
### IAssetService
|
||||
|
||||
资源服务接口。
|
||||
|
||||
```cpp
|
||||
class IAssetService : public IService {
|
||||
public:
|
||||
// 同步加载
|
||||
template<typename T>
|
||||
AssetHandle<T> load(const std::string& path);
|
||||
|
||||
// 异步加载
|
||||
template<typename T>
|
||||
void loadAsync(const std::string& path, AssetLoadCallback<T> callback);
|
||||
|
||||
// 获取已缓存资源
|
||||
template<typename T>
|
||||
AssetHandle<T> get(const std::string& path);
|
||||
|
||||
// 预加载
|
||||
template<typename T>
|
||||
void preload(const std::string& path);
|
||||
|
||||
// 状态查询
|
||||
bool isLoaded(const std::string& path) const;
|
||||
bool isLoading(const std::string& path) const;
|
||||
|
||||
// 资源管理
|
||||
void unload(const std::string& path);
|
||||
void setLimit(size_t maxBytes);
|
||||
size_t size() const;
|
||||
void purge();
|
||||
void clear();
|
||||
CacheStats stats() const;
|
||||
|
||||
// 加载器注册
|
||||
template<typename T>
|
||||
void registerLoader(Unique<AssetLoader<T>> loader);
|
||||
|
||||
// 资源包管理
|
||||
bool mount(const std::string& path);
|
||||
void unmount(const std::string& path);
|
||||
|
||||
// 数据处理管道
|
||||
void setPipe(DataPipe pipe);
|
||||
|
||||
// 根目录
|
||||
void setRoot(const std::string& path);
|
||||
std::string root() const;
|
||||
|
||||
// 处理异步回调
|
||||
void process();
|
||||
};
|
||||
```
|
||||
|
||||
### DataPipe
|
||||
|
||||
数据处理管道,支持链式调用。
|
||||
|
||||
```cpp
|
||||
class DataPipe {
|
||||
public:
|
||||
DataPipe& decrypt(const std::string& key, Decryptor::Type type = Decryptor::Type::XOR);
|
||||
DataPipe& decompress(Compression algo);
|
||||
DataPipe& encrypt(const std::string& key, Decryptor::Type type = Decryptor::Type::XOR);
|
||||
DataPipe& compress(Compression algo, int level = 3);
|
||||
DataPipe& add(Unique<DataProcessor> processor);
|
||||
|
||||
std::vector<u8> process(const std::vector<u8>& input);
|
||||
void clear();
|
||||
bool empty() const;
|
||||
size_t size() const;
|
||||
};
|
||||
```
|
||||
|
||||
### AssetPackBuilder
|
||||
|
||||
资源包构建器。
|
||||
|
||||
```cpp
|
||||
class AssetPackBuilder {
|
||||
public:
|
||||
explicit AssetPackBuilder(Compression compression = Compression::None, int level = 3);
|
||||
|
||||
void add(const std::string& path, const std::vector<u8>& data);
|
||||
void add(const std::string& path, std::vector<u8>&& data);
|
||||
bool addFile(const std::string& filePath, const std::string& packPath = "");
|
||||
size_t addDirectory(const std::string& dirPath, const std::string& prefix = "");
|
||||
|
||||
void setEncryption(const std::string& key, Decryptor::Type type = Decryptor::Type::XOR);
|
||||
bool build(const std::string& outputPath);
|
||||
|
||||
void clear();
|
||||
size_t count() const;
|
||||
size_t totalOriginalSize() const;
|
||||
size_t totalCompressedSize() const;
|
||||
};
|
||||
```
|
||||
|
||||
## 资源类型
|
||||
|
||||
### TextureAsset
|
||||
|
||||
纹理资源,使用 stb_image 加载。
|
||||
|
||||
```cpp
|
||||
class TextureAsset : public Asset {
|
||||
public:
|
||||
AssetType type() const override { return AssetType::Texture; }
|
||||
|
||||
int width() const;
|
||||
int height() const;
|
||||
int channels() const;
|
||||
const u8* data() const;
|
||||
size_t dataSize() const;
|
||||
};
|
||||
```
|
||||
|
||||
### FontAsset
|
||||
|
||||
字体资源,支持 TrueType 字体。
|
||||
|
||||
```cpp
|
||||
class FontAsset : public Asset {
|
||||
public:
|
||||
AssetType type() const override { return AssetType::Font; }
|
||||
|
||||
float scaleForPixelHeight(float pixels) const;
|
||||
const u8* data() const;
|
||||
size_t dataSize() const;
|
||||
};
|
||||
```
|
||||
|
||||
### ShaderAsset
|
||||
|
||||
着色器资源。
|
||||
|
||||
```cpp
|
||||
class ShaderAsset : public Asset {
|
||||
public:
|
||||
AssetType type() const override { return AssetType::Shader; }
|
||||
|
||||
const std::string& vertexSource() const;
|
||||
const std::string& fragmentSource() const;
|
||||
};
|
||||
```
|
||||
|
||||
### AudioAsset
|
||||
|
||||
音频资源。
|
||||
|
||||
```cpp
|
||||
class AudioAsset : public Asset {
|
||||
public:
|
||||
AssetType type() const override { return AssetType::Audio; }
|
||||
|
||||
AudioFormat format() const;
|
||||
int channels() const;
|
||||
int sampleRate() const;
|
||||
int bitsPerSample() const;
|
||||
float duration() const;
|
||||
const u8* data() const;
|
||||
size_t dataSize() const;
|
||||
bool streaming() const;
|
||||
};
|
||||
```
|
||||
|
||||
### DataAsset
|
||||
|
||||
通用二进制数据。
|
||||
|
||||
```cpp
|
||||
class DataAsset : public Asset {
|
||||
public:
|
||||
AssetType type() const override { return AssetType::Data; }
|
||||
|
||||
const u8* data() const;
|
||||
size_t size() const;
|
||||
};
|
||||
```
|
||||
|
||||
## 资源打包工具
|
||||
|
||||
### 安装
|
||||
|
||||
```bash
|
||||
xmake build asset_packer
|
||||
```
|
||||
|
||||
### 用法
|
||||
|
||||
```
|
||||
Extra2D 资源打包工具 v1.0.0
|
||||
|
||||
用法:
|
||||
asset_packer create <output.pack> [options] <inputs...>
|
||||
asset_packer list <pack file>
|
||||
asset_packer extract <pack file> <output dir>
|
||||
|
||||
命令:
|
||||
create 创建资源包
|
||||
list 列出资源包内容
|
||||
extract 提取资源包内容
|
||||
|
||||
选项:
|
||||
-c, --compression <algo> 压缩算法 (none, zstd, lz4, zlib),默认 zstd
|
||||
-l, --level <level> 压缩级别 (1-22),默认 3
|
||||
-e, --encrypt <key> 加密密钥
|
||||
-t, --encrypt-type <type> 加密类型 (xor, aes),默认 xor
|
||||
-v, --verbose 详细输出
|
||||
-h, --help 显示帮助
|
||||
```
|
||||
|
||||
### 示例
|
||||
|
||||
```bash
|
||||
# 创建资源包(使用 Zstd 压缩)
|
||||
asset_packer create game.pak -c zstd -v assets/
|
||||
|
||||
# 创建加密资源包
|
||||
asset_packer create game.pak -c zstd -e "secret_key" -t xor -v assets/
|
||||
|
||||
# 列出资源包内容
|
||||
asset_packer list game.pak
|
||||
|
||||
# 提取资源包
|
||||
asset_packer extract game.pak extracted/
|
||||
|
||||
# 使用不同压缩算法
|
||||
asset_packer create game.pak -c lz4 -v assets/
|
||||
asset_packer create game.pak -c zlib -l 9 -v assets/
|
||||
```
|
||||
|
||||
## 压缩算法对比
|
||||
|
||||
| 算法 | 压缩率 | 压缩速度 | 解压速度 | 适用场景 |
|
||||
|------|--------|----------|----------|----------|
|
||||
| Zstd | 高 | 快 | 很快 | 通用推荐 |
|
||||
| LZ4 | 中 | 很快 | 很快 | 实时解压 |
|
||||
| Zlib | 高 | 中 | 中 | 兼容性好 |
|
||||
| None | - | - | - | 已压缩资源 |
|
||||
|
||||
## 设计模式
|
||||
|
||||
### 策略模式 (AssetLoader)
|
||||
|
||||
不同资源类型使用不同的加载策略。
|
||||
|
||||
```cpp
|
||||
class AssetLoader<T> {
|
||||
public:
|
||||
virtual Ref<T> load(const std::string& path) = 0;
|
||||
virtual Ref<T> loadFromMemory(const u8* data, size_t size) = 0;
|
||||
virtual bool canLoad(const std::string& path) const = 0;
|
||||
virtual AssetType type() const = 0;
|
||||
virtual std::vector<std::string> extensions() const = 0;
|
||||
};
|
||||
```
|
||||
|
||||
### 享元模式 (AssetCache)
|
||||
|
||||
共享资源实例,减少内存占用。
|
||||
|
||||
### 装饰器模式 (DataProcessor)
|
||||
|
||||
链式处理数据流。
|
||||
|
||||
```cpp
|
||||
DataPipe pipe;
|
||||
pipe.encrypt("key")
|
||||
.compress(Compression::Zstd);
|
||||
```
|
||||
|
||||
### 服务定位器模式
|
||||
|
||||
全局访问资源服务。
|
||||
|
||||
```cpp
|
||||
auto* service = ServiceLocator::get<IAssetService>();
|
||||
```
|
||||
|
||||
## 线程安全
|
||||
|
||||
- `AssetCache` 使用读写锁 (`std::shared_mutex`)
|
||||
- `AssetService` 使用读写锁保护资源映射
|
||||
- 异步加载使用独立的工作线程
|
||||
- 回调在主线程执行(需要调用 `process()`)
|
||||
|
||||
## 最佳实践
|
||||
|
||||
### 1. 资源路径约定
|
||||
|
||||
```
|
||||
assets/
|
||||
├── textures/
|
||||
│ ├── sprites/
|
||||
│ └── backgrounds/
|
||||
├── fonts/
|
||||
├── shaders/
|
||||
└── audio/
|
||||
```
|
||||
|
||||
### 2. 预加载关键资源
|
||||
|
||||
```cpp
|
||||
// 游戏启动时预加载
|
||||
service.preload<TextureAsset>("textures/loading.png");
|
||||
service.preload<FontAsset>("fonts/main.ttf");
|
||||
```
|
||||
|
||||
### 3. 合理设置缓存上限
|
||||
|
||||
```cpp
|
||||
// 根据目标平台设置
|
||||
#ifdef MOBILE
|
||||
service.setLimit(50 * 1024 * 1024); // 50MB
|
||||
#else
|
||||
service.setLimit(200 * 1024 * 1024); // 200MB
|
||||
#endif
|
||||
```
|
||||
|
||||
### 4. 定期清理缓存
|
||||
|
||||
```cpp
|
||||
// 场景切换时清理
|
||||
void onSceneChange() {
|
||||
service.purge();
|
||||
}
|
||||
```
|
||||
|
||||
### 5. 使用资源包减少文件数量
|
||||
|
||||
```cpp
|
||||
// 将小文件打包成资源包
|
||||
// 减少文件 I/O 操作,提高加载速度
|
||||
service.mount("textures.pak");
|
||||
service.mount("audio.pak");
|
||||
```
|
||||
|
||||
## 错误处理
|
||||
|
||||
```cpp
|
||||
auto handle = service.load<TextureAsset>("missing.png");
|
||||
if (!handle.valid()) {
|
||||
// 资源加载失败
|
||||
std::cerr << "Failed to load texture\n";
|
||||
}
|
||||
|
||||
// 检查加载状态
|
||||
if (handle.state() == AssetState::Failed) {
|
||||
// 处理失败情况
|
||||
}
|
||||
```
|
||||
|
||||
## 版本历史
|
||||
|
||||
- **v1.0.0** - 初始版本
|
||||
- 资源加载和缓存
|
||||
- 资源打包和加密
|
||||
- 异步加载支持
|
||||
- 多种压缩算法
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
/**
|
||||
* @file hello_world.cpp
|
||||
* @brief Hello World 示例程序
|
||||
*/
|
||||
|
||||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <extra2d/render/renderer.h>
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <iostream>
|
||||
|
||||
using namespace extra2d;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
std::cout << "========================================" << std::endl;
|
||||
std::cout << " Extra2D - Hello World Example" << std::endl;
|
||||
std::cout << "========================================" << std::endl;
|
||||
|
||||
auto& app = Application::get();
|
||||
app.appName = "Hello World - Extra2D";
|
||||
app.appVersion = "1.0.0";
|
||||
|
||||
app.use<WindowModule>([](WindowCfg& cfg) {
|
||||
cfg.title = "Hello World - Extra2D";
|
||||
cfg.w = 1024;
|
||||
cfg.h = 768;
|
||||
cfg.vsync = true;
|
||||
});
|
||||
|
||||
if (!app.init()) {
|
||||
std::cerr << "Failed to initialize application" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto logger = ServiceLocator::instance().get<ILogger>();
|
||||
if (logger) {
|
||||
logger->info("Hello World example initialized");
|
||||
}
|
||||
|
||||
auto renderer = IRenderer2D::create(true);
|
||||
if (!renderer) {
|
||||
std::cerr << "Failed to create renderer" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto window = app.window();
|
||||
if (!window) {
|
||||
std::cerr << "Failed to get window" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!renderer->init(window->native())) {
|
||||
std::cerr << "Failed to initialize renderer" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (logger) {
|
||||
logger->info("Renderer: %s (Vulkan: %s)",
|
||||
renderer->backendName(),
|
||||
renderer->isVulkan() ? "yes" : "no");
|
||||
}
|
||||
|
||||
float totalTime = 0.0f;
|
||||
|
||||
while (app.running() && !window->shouldClose()) {
|
||||
float dt = app.dt();
|
||||
totalTime += dt;
|
||||
|
||||
renderer->beginFrame();
|
||||
|
||||
i32 width = renderer->width();
|
||||
i32 height = renderer->height();
|
||||
|
||||
Viewport viewport;
|
||||
viewport.x = 0;
|
||||
viewport.y = 0;
|
||||
viewport.width = width;
|
||||
viewport.height = height;
|
||||
renderer->setViewport(viewport);
|
||||
|
||||
ClearCommand clearCmd;
|
||||
clearCmd.color = Color(20, 20, 30, 255);
|
||||
clearCmd.clearColor = true;
|
||||
clearCmd.clearDepth = true;
|
||||
renderer->clear(clearCmd);
|
||||
|
||||
float centerX = static_cast<float>(width) / 2.0f;
|
||||
float centerY = static_cast<float>(height) / 2.0f;
|
||||
|
||||
float pulse = 0.5f + 0.5f * std::sin(totalTime * 2.0f);
|
||||
Color textColor(
|
||||
static_cast<u8>(100 + 155 * pulse),
|
||||
static_cast<u8>(200 + 55 * pulse),
|
||||
static_cast<u8>(255),
|
||||
255
|
||||
);
|
||||
|
||||
float rectWidth = 320.0f;
|
||||
float rectHeight = 70.0f;
|
||||
float rectX = centerX - rectWidth / 2;
|
||||
float rectY = centerY - rectHeight / 2;
|
||||
|
||||
Color rectColor(50, 50, 70, 255);
|
||||
renderer->drawRect(Vec2{rectX, rectY}, Vec2{rectWidth, rectHeight}, rectColor, true);
|
||||
|
||||
Color borderColor(
|
||||
static_cast<u8>(100 + 100 * pulse),
|
||||
static_cast<u8>(180 + 50 * pulse),
|
||||
255,
|
||||
255
|
||||
);
|
||||
renderer->drawRect(Vec2{rectX, rectY}, Vec2{rectWidth, rectHeight}, borderColor, false);
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
float x = 100.0f + i * 200.0f;
|
||||
float y = static_cast<float>(height) - 50.0f;
|
||||
float radius = 20.0f;
|
||||
|
||||
float phase = totalTime * 1.5f + i * 0.5f;
|
||||
float offsetY = std::sin(phase) * 20.0f;
|
||||
|
||||
Color circleColor(
|
||||
static_cast<u8>(50 + i * 30),
|
||||
static_cast<u8>(100 + i * 20),
|
||||
static_cast<u8>(150 + i * 20),
|
||||
200
|
||||
);
|
||||
|
||||
renderer->drawCircle(Vec2{x, y + offsetY}, radius, circleColor, true, 16);
|
||||
}
|
||||
|
||||
renderer->flushBatch();
|
||||
renderer->endFrame();
|
||||
renderer->present();
|
||||
|
||||
window->poll();
|
||||
}
|
||||
|
||||
renderer->shutdown();
|
||||
app.shutdown();
|
||||
|
||||
std::cout << "Application terminated successfully" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/scene-graph/Component.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/2d/components/SpriteFrame.h>
|
||||
#include <extra2d/2d/renderer/RenderEntity.h>
|
||||
#include <extra2d/2d/renderer/RenderDrawInfo.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 精灵组件
|
||||
*
|
||||
* 用于渲染 2D 精灵图像的组件。
|
||||
* 参考 Cocos Creator 的 Sprite 组件设计。
|
||||
*/
|
||||
class Sprite : public Component {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
Sprite();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Sprite() override;
|
||||
|
||||
// ==================== 精灵帧 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取精灵帧
|
||||
* @return 精灵帧指针
|
||||
*/
|
||||
SpriteFrame* getSpriteFrame() const;
|
||||
|
||||
/**
|
||||
* @brief 设置精灵帧
|
||||
* @param frame 精灵帧指针
|
||||
*/
|
||||
void setSpriteFrame(SpriteFrame* frame);
|
||||
|
||||
// ==================== 颜色和透明度 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取颜色
|
||||
* @return 颜色
|
||||
*/
|
||||
Vec4 getColor() const;
|
||||
|
||||
/**
|
||||
* @brief 设置颜色
|
||||
* @param color 颜色
|
||||
*/
|
||||
void setColor(const Vec4& color);
|
||||
|
||||
/**
|
||||
* @brief 设置颜色(RGB)
|
||||
* @param r 红色
|
||||
* @param g 绿色
|
||||
* @param b 蓝色
|
||||
*/
|
||||
void setColor(float r, float g, float b);
|
||||
|
||||
/**
|
||||
* @brief 设置颜色(RGBA)
|
||||
* @param r 红色
|
||||
* @param g 绿色
|
||||
* @param b 蓝色
|
||||
* @param a 透明度
|
||||
*/
|
||||
void setColor(float r, float g, float b, float a);
|
||||
|
||||
// ==================== 尺寸 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取宽度
|
||||
* @return 宽度
|
||||
*/
|
||||
float getWidth() const;
|
||||
|
||||
/**
|
||||
* @brief 设置宽度
|
||||
* @param width 宽度
|
||||
*/
|
||||
void setWidth(float width);
|
||||
|
||||
/**
|
||||
* @brief 获取高度
|
||||
* @return 高度
|
||||
*/
|
||||
float getHeight() const;
|
||||
|
||||
/**
|
||||
* @brief 设置高度
|
||||
* @param height 高度
|
||||
*/
|
||||
void setHeight(float height);
|
||||
|
||||
/**
|
||||
* @brief 获取尺寸
|
||||
* @return 尺寸
|
||||
*/
|
||||
Vec2 getSize() const;
|
||||
|
||||
/**
|
||||
* @brief 设置尺寸
|
||||
* @param size 尺寸
|
||||
*/
|
||||
void setSize(const Vec2& size);
|
||||
|
||||
// ==================== 渲染状态 ====================
|
||||
|
||||
/**
|
||||
* @brief 检查是否脏
|
||||
* @return 如果脏返回 true
|
||||
*/
|
||||
bool isDirty() const;
|
||||
|
||||
/**
|
||||
* @brief 设置脏标记
|
||||
* @param dirty 是否脏
|
||||
*/
|
||||
void setDirty(bool dirty);
|
||||
|
||||
/**
|
||||
* @brief 标记需要更新
|
||||
*/
|
||||
void markForUpdate();
|
||||
|
||||
// ==================== 生命周期回调 ====================
|
||||
|
||||
void onAdd() override;
|
||||
void onRemove() override;
|
||||
void update(f32 dt) override;
|
||||
void render() override;
|
||||
|
||||
/**
|
||||
* @brief 获取类型名称
|
||||
* @return "Sprite"
|
||||
*/
|
||||
const char* getTypeName() const override { return "Sprite"; }
|
||||
|
||||
protected:
|
||||
void updateGeometry();
|
||||
void updateRenderEntity();
|
||||
void updateVertexData();
|
||||
|
||||
private:
|
||||
SpriteFrame* spriteFrame_{nullptr};
|
||||
RenderEntity* renderEntity_{nullptr};
|
||||
RenderDrawInfo* drawInfo_{nullptr};
|
||||
|
||||
Vec4 color_{1, 1, 1, 1};
|
||||
Vec2 size_{0, 0};
|
||||
bool dirty_{true};
|
||||
bool sizeSetByUser_{false};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,134 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/assets/Asset.h>
|
||||
#include <extra2d/core/assets/Texture2D.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 精灵帧
|
||||
*
|
||||
* 表示精灵的一个帧,包含纹理区域信息。
|
||||
*/
|
||||
class SpriteFrame : public Asset {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
SpriteFrame();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~SpriteFrame() override;
|
||||
|
||||
// ==================== 纹理区域 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取关联纹理
|
||||
* @return 纹理指针
|
||||
*/
|
||||
Texture2D* getTexture() const;
|
||||
|
||||
/**
|
||||
* @brief 设置关联纹理
|
||||
* @param texture 纹理指针
|
||||
*/
|
||||
void setTexture(Texture2D* texture);
|
||||
|
||||
/**
|
||||
* @brief 获取纹理区域矩形
|
||||
* @return 区域矩形
|
||||
*/
|
||||
Rect getRect() const;
|
||||
|
||||
/**
|
||||
* @brief 设置纹理区域矩形
|
||||
* @param rect 区域矩形
|
||||
*/
|
||||
void setRect(const Rect& rect);
|
||||
|
||||
/**
|
||||
* @brief 获取未旋转区域
|
||||
* @return 未旋转区域
|
||||
*/
|
||||
Rect getUnrotatedRect() const;
|
||||
|
||||
/**
|
||||
* @brief 设置未旋转区域
|
||||
* @param rect 未旋转区域
|
||||
*/
|
||||
void setUnrotatedRect(const Rect& rect);
|
||||
|
||||
// ==================== 旋转信息 ====================
|
||||
|
||||
/**
|
||||
* @brief 检查是否旋转
|
||||
* @return 如果旋转返回 true
|
||||
*/
|
||||
bool isRotated() const;
|
||||
|
||||
/**
|
||||
* @brief 设置是否旋转
|
||||
* @param rotated 是否旋转
|
||||
*/
|
||||
void setRotated(bool rotated);
|
||||
|
||||
// ==================== 偏移和大小 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取偏移
|
||||
* @return 偏移向量
|
||||
*/
|
||||
Vec2 getOffset() const;
|
||||
|
||||
/**
|
||||
* @brief 设置偏移
|
||||
* @param offset 偏移向量
|
||||
*/
|
||||
void setOffset(const Vec2& offset);
|
||||
|
||||
/**
|
||||
* @brief 获取原始大小
|
||||
* @return 原始大小
|
||||
*/
|
||||
Vec2 getOriginalSize() const;
|
||||
|
||||
/**
|
||||
* @brief 设置原始大小
|
||||
* @param size 原始大小
|
||||
*/
|
||||
void setOriginalSize(const Vec2& size);
|
||||
|
||||
// ==================== UV 计算 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取 UV 坐标
|
||||
* @return UV 坐标数组
|
||||
*/
|
||||
std::array<Vec2, 4> getUVs() const;
|
||||
|
||||
/**
|
||||
* @brief 计算 UV 坐标
|
||||
*/
|
||||
void calculateUV();
|
||||
|
||||
/**
|
||||
* @brief 获取类型名称
|
||||
* @return "SpriteFrame"
|
||||
*/
|
||||
const char* getTypeName() const override { return "SpriteFrame"; }
|
||||
|
||||
private:
|
||||
Texture2D* texture_{nullptr};
|
||||
Rect rect_{0, 0, 0, 0};
|
||||
Rect unrotatedRect_{0, 0, 0, 0};
|
||||
Vec2 offset_{0, 0};
|
||||
Vec2 originalSize_{0, 0};
|
||||
bool rotated_{false};
|
||||
|
||||
std::array<Vec2, 4> uvs_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/2d/renderer/RenderEntity.h>
|
||||
#include <extra2d/2d/renderer/RenderDrawInfo.h>
|
||||
#include <extra2d/2d/renderer/UIMeshBuffer.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace gfx {
|
||||
class Device;
|
||||
class Shader;
|
||||
class PipelineState;
|
||||
class InputAssembler;
|
||||
}
|
||||
|
||||
class Node;
|
||||
class Material;
|
||||
|
||||
/**
|
||||
* @brief 绘制批次
|
||||
*/
|
||||
struct DrawBatch2D {
|
||||
gfx::InputAssembler* inputAssembler{nullptr};
|
||||
gfx::Shader* shader{nullptr};
|
||||
gfx::Texture* texture{nullptr};
|
||||
u32 vertexOffset{0};
|
||||
u32 indexOffset{0};
|
||||
u32 vbCount{0};
|
||||
u32 ibCount{0};
|
||||
u32 stencilStage{0};
|
||||
u32 stencilValue{0};
|
||||
u32 priority{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 2D 批处理器
|
||||
*
|
||||
* 管理 2D 渲染的批处理合并和渲染流程。
|
||||
* 参考 Cocos Creator 的 Batcher2d 设计。
|
||||
*/
|
||||
class Batcher2d {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 批处理器实例
|
||||
*/
|
||||
static Batcher2d* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化批处理器
|
||||
* @param device GFX 设备
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(gfx::Device* device);
|
||||
|
||||
/**
|
||||
* @brief 销毁批处理器
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 更新批处理器
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* @brief 上传缓冲区数据
|
||||
*/
|
||||
void uploadBuffers();
|
||||
|
||||
/**
|
||||
* @brief 重置批处理器
|
||||
*/
|
||||
void reset();
|
||||
|
||||
// ==================== 渲染流程 ====================
|
||||
|
||||
/**
|
||||
* @brief 填充缓冲区并合并批次
|
||||
*/
|
||||
void fillBuffersAndMergeBatches();
|
||||
|
||||
/**
|
||||
* @brief 遍历节点树
|
||||
* @param node 节点
|
||||
* @param parentOpacity 父节点透明度
|
||||
* @param parentColorDirty 父节点颜色是否脏
|
||||
*/
|
||||
void walk(Node* node, float parentOpacity, bool parentColorDirty);
|
||||
|
||||
/**
|
||||
* @brief 处理绘制信息
|
||||
* @param entity 渲染实体
|
||||
* @param drawInfo 绘制信息
|
||||
* @param node 节点
|
||||
*/
|
||||
void handleDrawInfo(RenderEntity* entity, RenderDrawInfo* drawInfo, Node* node);
|
||||
|
||||
/**
|
||||
* @brief 生成批次
|
||||
* @param entity 渲染实体
|
||||
* @param drawInfo 绘制信息
|
||||
*/
|
||||
void generateBatch(RenderEntity* entity, RenderDrawInfo* drawInfo);
|
||||
|
||||
// ==================== 缓冲区管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取网格缓冲区
|
||||
* @param accId 账户 ID
|
||||
* @param bufferId 缓冲区 ID
|
||||
* @return 网格缓冲区指针
|
||||
*/
|
||||
UIMeshBuffer* getMeshBuffer(u16 accId, u16 bufferId);
|
||||
|
||||
/**
|
||||
* @brief 同步网格缓冲区
|
||||
* @param accId 账户 ID
|
||||
* @param buffers 缓冲区列表
|
||||
*/
|
||||
void syncMeshBuffers(u16 accId, std::vector<UIMeshBuffer*>&& buffers);
|
||||
|
||||
/**
|
||||
* @brief 同步根节点
|
||||
* @param rootNodes 根节点列表
|
||||
*/
|
||||
void syncRootNodes(std::vector<Node*>&& rootNodes);
|
||||
|
||||
// ==================== 渲染执行 ====================
|
||||
|
||||
/**
|
||||
* @brief 渲染所有批次
|
||||
*/
|
||||
void renderBatches();
|
||||
|
||||
/**
|
||||
* @brief 获取 GFX 设备
|
||||
* @return 设备指针
|
||||
*/
|
||||
gfx::Device* getDevice();
|
||||
|
||||
/**
|
||||
* @brief 获取默认属性列表
|
||||
* @return 属性列表
|
||||
*/
|
||||
const std::vector<gfx::Attribute>& getDefaultAttributes() const;
|
||||
|
||||
private:
|
||||
Batcher2d();
|
||||
~Batcher2d();
|
||||
|
||||
Batcher2d(const Batcher2d&) = delete;
|
||||
Batcher2d& operator=(const Batcher2d&) = delete;
|
||||
|
||||
void initDefaultAttributes();
|
||||
void updateBatch(DrawBatch2D* batch);
|
||||
|
||||
static Batcher2d* instance_;
|
||||
|
||||
gfx::Device* device_{nullptr};
|
||||
std::vector<gfx::Attribute> defaultAttributes_;
|
||||
|
||||
std::vector<DrawBatch2D*> batches_;
|
||||
std::unordered_map<u16, std::vector<UIMeshBuffer*>> meshBuffersMap_;
|
||||
std::vector<Node*> rootNodes_;
|
||||
|
||||
RenderEntity* currEntity_{nullptr};
|
||||
RenderDrawInfo* currDrawInfo_{nullptr};
|
||||
UIMeshBuffer* currMeshBuffer_{nullptr};
|
||||
|
||||
u32 currStencilStage_{0};
|
||||
u32 currStencilValue_{0};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,273 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Material;
|
||||
class UIMeshBuffer;
|
||||
namespace gfx {
|
||||
class Texture;
|
||||
class InputAssembler;
|
||||
class Buffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 渲染绘制信息类型
|
||||
*/
|
||||
enum class RenderDrawInfoType : u8 {
|
||||
Component,
|
||||
Model,
|
||||
Middleware,
|
||||
SubNode,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 2D 渲染顶点布局
|
||||
*/
|
||||
struct Render2dVertex {
|
||||
Vec3 position{0, 0, 0};
|
||||
Vec2 uv{0, 0};
|
||||
Vec4 color{1, 1, 1, 1};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 绘制属性
|
||||
*/
|
||||
struct DrawInfoAttrs {
|
||||
RenderDrawInfoType type{RenderDrawInfoType::Component};
|
||||
bool vertDirty{false};
|
||||
bool isMeshBuffer{false};
|
||||
bool isVertexPositionInWorld{false};
|
||||
u8 stride{0};
|
||||
u16 bufferId{0};
|
||||
u16 accId{0};
|
||||
u32 vertexOffset{0};
|
||||
u32 indexOffset{0};
|
||||
u32 vbCount{0};
|
||||
u32 ibCount{0};
|
||||
u64 dataHash{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染绘制信息
|
||||
*
|
||||
* 封装单次绘制所需的所有信息。
|
||||
* 参考 Cocos Creator 的 RenderDrawInfo 设计。
|
||||
*/
|
||||
class RenderDrawInfo {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
RenderDrawInfo();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~RenderDrawInfo();
|
||||
|
||||
// ==================== 类型与标识 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取绘制类型
|
||||
* @return 类型
|
||||
*/
|
||||
RenderDrawInfoType getDrawInfoType() const;
|
||||
|
||||
/**
|
||||
* @brief 设置绘制类型
|
||||
* @param type 类型
|
||||
*/
|
||||
void setDrawInfoType(RenderDrawInfoType type);
|
||||
|
||||
/**
|
||||
* @brief 获取账户 ID
|
||||
* @return 账户 ID
|
||||
*/
|
||||
u16 getAccId() const;
|
||||
|
||||
/**
|
||||
* @brief 设置账户 ID
|
||||
* @param accId 账户 ID
|
||||
*/
|
||||
void setAccId(u16 accId);
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区 ID
|
||||
* @return 缓冲区 ID
|
||||
*/
|
||||
u16 getBufferId() const;
|
||||
|
||||
/**
|
||||
* @brief 设置缓冲区 ID
|
||||
* @param bufferId 缓冲区 ID
|
||||
*/
|
||||
void setBufferId(u16 bufferId);
|
||||
|
||||
// ==================== 缓冲区信息 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取顶点偏移
|
||||
* @return 顶点偏移
|
||||
*/
|
||||
u32 getVertexOffset() const;
|
||||
|
||||
/**
|
||||
* @brief 设置顶点偏移
|
||||
* @param offset 偏移
|
||||
*/
|
||||
void setVertexOffset(u32 offset);
|
||||
|
||||
/**
|
||||
* @brief 获取索引偏移
|
||||
* @return 索引偏移
|
||||
*/
|
||||
u32 getIndexOffset() const;
|
||||
|
||||
/**
|
||||
* @brief 设置索引偏移
|
||||
* @param offset 偏移
|
||||
*/
|
||||
void setIndexOffset(u32 offset);
|
||||
|
||||
/**
|
||||
* @brief 获取顶点数量
|
||||
* @return 顶点数量
|
||||
*/
|
||||
u32 getVbCount() const;
|
||||
|
||||
/**
|
||||
* @brief 设置顶点数量
|
||||
* @param count 数量
|
||||
*/
|
||||
void setVbCount(u32 count);
|
||||
|
||||
/**
|
||||
* @brief 获取索引数量
|
||||
* @return 索引数量
|
||||
*/
|
||||
u32 getIbCount() const;
|
||||
|
||||
/**
|
||||
* @brief 设置索引数量
|
||||
* @param count 数量
|
||||
*/
|
||||
void setIbCount(u32 count);
|
||||
|
||||
// ==================== 资源绑定 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取材质
|
||||
* @return 材质指针
|
||||
*/
|
||||
Material* getMaterial() const;
|
||||
|
||||
/**
|
||||
* @brief 设置材质
|
||||
* @param material 材质指针
|
||||
*/
|
||||
void setMaterial(Material* material);
|
||||
|
||||
/**
|
||||
* @brief 获取网格缓冲区
|
||||
* @return 网格缓冲区指针
|
||||
*/
|
||||
UIMeshBuffer* getMeshBuffer() const;
|
||||
|
||||
/**
|
||||
* @brief 设置网格缓冲区
|
||||
* @param buffer 网格缓冲区指针
|
||||
*/
|
||||
void setMeshBuffer(UIMeshBuffer* buffer);
|
||||
|
||||
/**
|
||||
* @brief 获取纹理
|
||||
* @return 纹理指针
|
||||
*/
|
||||
gfx::Texture* getTexture() const;
|
||||
|
||||
/**
|
||||
* @brief 设置纹理
|
||||
* @param texture 纹理指针
|
||||
*/
|
||||
void setTexture(gfx::Texture* texture);
|
||||
|
||||
// ==================== 数据缓冲区 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取顶点数据
|
||||
* @return 顶点数据指针
|
||||
*/
|
||||
float* getVDataBuffer() const;
|
||||
|
||||
/**
|
||||
* @brief 设置顶点数据
|
||||
* @param data 数据指针
|
||||
*/
|
||||
void setVDataBuffer(float* data);
|
||||
|
||||
/**
|
||||
* @brief 获取索引数据
|
||||
* @return 索引数据指针
|
||||
*/
|
||||
u16* getIDataBuffer() const;
|
||||
|
||||
/**
|
||||
* @brief 设置索引数据
|
||||
* @param data 数据指针
|
||||
*/
|
||||
void setIDataBuffer(u16* data);
|
||||
|
||||
// ==================== 渲染操作 ====================
|
||||
|
||||
/**
|
||||
* @brief 请求输入装配器
|
||||
* @param device GFX 设备
|
||||
* @return 输入装配器指针
|
||||
*/
|
||||
gfx::InputAssembler* requestIA(gfx::Device* device);
|
||||
|
||||
/**
|
||||
* @brief 上传缓冲区数据
|
||||
*/
|
||||
void uploadBuffers();
|
||||
|
||||
/**
|
||||
* @brief 检查顶点是否脏
|
||||
* @return 如果脏返回 true
|
||||
*/
|
||||
bool isVertDirty() const;
|
||||
|
||||
/**
|
||||
* @brief 设置顶点脏标记
|
||||
* @param dirty 是否脏
|
||||
*/
|
||||
void setVertDirty(bool dirty);
|
||||
|
||||
/**
|
||||
* @brief 获取数据哈希
|
||||
* @return 哈希值
|
||||
*/
|
||||
u64 getDataHash() const;
|
||||
|
||||
/**
|
||||
* @brief 设置数据哈希
|
||||
* @param hash 哈希值
|
||||
*/
|
||||
void setDataHash(u64 hash);
|
||||
|
||||
private:
|
||||
DrawInfoAttrs attrs_;
|
||||
Material* material_{nullptr};
|
||||
UIMeshBuffer* meshBuffer_{nullptr};
|
||||
gfx::Texture* texture_{nullptr};
|
||||
gfx::InputAssembler* inputAssembler_{nullptr};
|
||||
|
||||
float* vData_{nullptr};
|
||||
u16* iData_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,250 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Node;
|
||||
class RenderDrawInfo;
|
||||
class UIMeshBuffer;
|
||||
|
||||
/**
|
||||
* @brief 渲染实体类型
|
||||
*/
|
||||
enum class RenderEntityType : u8 {
|
||||
Static,
|
||||
Dynamic,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 遮罩模式
|
||||
*/
|
||||
enum class MaskMode : u8 {
|
||||
None,
|
||||
Mask,
|
||||
MaskInverted,
|
||||
MaskNode,
|
||||
MaskNodeInverted,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 填充颜色类型
|
||||
*/
|
||||
enum class FillColorType : u8 {
|
||||
Color,
|
||||
Vertex,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 实体属性布局
|
||||
*/
|
||||
struct EntityAttrLayout {
|
||||
u32 priority{0};
|
||||
u8 colorR{255};
|
||||
u8 colorG{255};
|
||||
u8 colorB{255};
|
||||
u8 colorA{255};
|
||||
u8 maskMode{0};
|
||||
FillColorType fillColorType{FillColorType::Color};
|
||||
u8 enabledIndex : 1;
|
||||
u8 useLocal : 1;
|
||||
u8 paddings : 6;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染实体
|
||||
*
|
||||
* 表示一个可渲染的实体,管理渲染状态和绘制信息集合。
|
||||
* 参考 Cocos Creator 的 RenderEntity 设计。
|
||||
*/
|
||||
class RenderEntity {
|
||||
public:
|
||||
static constexpr u32 STATIC_DRAW_INFO_CAPACITY = 4;
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
RenderEntity();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~RenderEntity();
|
||||
|
||||
// ==================== DrawInfo 管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 添加动态绘制信息
|
||||
* @param drawInfo 绘制信息
|
||||
*/
|
||||
void addDynamicRenderDrawInfo(RenderDrawInfo* drawInfo);
|
||||
|
||||
/**
|
||||
* @brief 设置动态绘制信息
|
||||
* @param drawInfo 绘制信息
|
||||
* @param index 索引
|
||||
*/
|
||||
void setDynamicRenderDrawInfo(RenderDrawInfo* drawInfo, u32 index);
|
||||
|
||||
/**
|
||||
* @brief 移除动态绘制信息
|
||||
*/
|
||||
void removeDynamicRenderDrawInfo();
|
||||
|
||||
/**
|
||||
* @brief 清除动态绘制信息
|
||||
*/
|
||||
void clearDynamicRenderDrawInfos();
|
||||
|
||||
/**
|
||||
* @brief 清除静态绘制信息
|
||||
*/
|
||||
void clearStaticRenderDrawInfos();
|
||||
|
||||
/**
|
||||
* @brief 获取绘制信息
|
||||
* @param index 索引
|
||||
* @return 绘制信息指针
|
||||
*/
|
||||
RenderDrawInfo* getRenderDrawInfoAt(u32 index);
|
||||
|
||||
/**
|
||||
* @brief 获取绘制信息数量
|
||||
* @return 数量
|
||||
*/
|
||||
u32 getRenderDrawInfoCount() const;
|
||||
|
||||
// ==================== 属性访问 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取关联节点
|
||||
* @return 节点指针
|
||||
*/
|
||||
Node* getNode() const;
|
||||
|
||||
/**
|
||||
* @brief 设置关联节点
|
||||
* @param node 节点指针
|
||||
*/
|
||||
void setNode(Node* node);
|
||||
|
||||
/**
|
||||
* @brief 获取渲染实体类型
|
||||
* @return 类型
|
||||
*/
|
||||
RenderEntityType getRenderEntityType() const;
|
||||
|
||||
/**
|
||||
* @brief 设置渲染实体类型
|
||||
* @param type 类型
|
||||
*/
|
||||
void setRenderEntityType(RenderEntityType type);
|
||||
|
||||
/**
|
||||
* @brief 获取优先级
|
||||
* @return 优先级
|
||||
*/
|
||||
u32 getPriority() const;
|
||||
|
||||
/**
|
||||
* @brief 设置优先级
|
||||
* @param priority 优先级
|
||||
*/
|
||||
void setPriority(u32 priority);
|
||||
|
||||
/**
|
||||
* @brief 获取遮罩模式
|
||||
* @return 遮罩模式
|
||||
*/
|
||||
MaskMode getMaskMode() const;
|
||||
|
||||
/**
|
||||
* @brief 设置遮罩模式
|
||||
* @param mode 遮罩模式
|
||||
*/
|
||||
void setMaskMode(MaskMode mode);
|
||||
|
||||
/**
|
||||
* @brief 检查是否为遮罩
|
||||
* @return 如果是遮罩返回 true
|
||||
*/
|
||||
bool isMask() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否为子遮罩
|
||||
* @return 如果是子遮罩返回 true
|
||||
*/
|
||||
bool isSubMask() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否为反转遮罩
|
||||
* @return 如果是反转遮罩返回 true
|
||||
*/
|
||||
bool isMaskInverted() const;
|
||||
|
||||
/**
|
||||
* @brief 获取填充颜色类型
|
||||
* @return 填充颜色类型
|
||||
*/
|
||||
FillColorType getFillColorType() const;
|
||||
|
||||
/**
|
||||
* @brief 设置填充颜色类型
|
||||
* @param type 填充颜色类型
|
||||
*/
|
||||
void setFillColorType(FillColorType type);
|
||||
|
||||
/**
|
||||
* @brief 获取颜色
|
||||
* @return 颜色(RGBA)
|
||||
*/
|
||||
Vec4 getColor() const;
|
||||
|
||||
/**
|
||||
* @brief 设置颜色
|
||||
* @param r 红色
|
||||
* @param g 绿色
|
||||
* @param b 蓝色
|
||||
* @param a 透明度
|
||||
*/
|
||||
void setColor(u8 r, u8 g, u8 b, u8 a);
|
||||
|
||||
/**
|
||||
* @brief 获取模板阶段
|
||||
* @return 模板阶段
|
||||
*/
|
||||
u32 getStencilStage() const;
|
||||
|
||||
/**
|
||||
* @brief 设置模板阶段
|
||||
* @param stage 模板阶段
|
||||
*/
|
||||
void setStencilStage(u32 stage);
|
||||
|
||||
/**
|
||||
* @brief 检查是否使用本地坐标
|
||||
* @return 如果使用本地坐标返回 true
|
||||
*/
|
||||
bool useLocal() const;
|
||||
|
||||
/**
|
||||
* @brief 设置是否使用本地坐标
|
||||
* @param useLocal 是否使用本地坐标
|
||||
*/
|
||||
void setUseLocal(bool useLocal);
|
||||
|
||||
private:
|
||||
Node* node_{nullptr};
|
||||
RenderEntityType entityType_{RenderEntityType::Static};
|
||||
EntityAttrLayout attr_{};
|
||||
|
||||
std::vector<RenderDrawInfo*> dynamicDrawInfos_;
|
||||
u32 drawInfoCount_{0};
|
||||
|
||||
u32 stencilStage_{0};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class RenderEntity;
|
||||
|
||||
/**
|
||||
* @brief 模板阶段枚举
|
||||
*/
|
||||
enum class StencilStage : u8 {
|
||||
Disabled = 0,
|
||||
Clear = 1,
|
||||
EnterLevel = 2,
|
||||
Enabled = 3,
|
||||
ExitLevel = 4,
|
||||
ClearInverted = 5,
|
||||
EnterLevelInverted = 6,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模板实体结构
|
||||
*/
|
||||
struct StencilEntity {
|
||||
u32 stencilTest{0};
|
||||
gfx::ComparisonFunc func{gfx::ComparisonFunc::Always};
|
||||
u32 stencilMask{0};
|
||||
u32 writeMask{0};
|
||||
gfx::StencilOp failOp{gfx::StencilOp::Keep};
|
||||
gfx::StencilOp zFailOp{gfx::StencilOp::Keep};
|
||||
gfx::StencilOp passOp{gfx::StencilOp::Keep};
|
||||
u32 ref{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模板缓冲区管理器
|
||||
*
|
||||
* 管理 2D 渲染中的模板缓冲区操作,用于遮罩效果。
|
||||
* 参考 Cocos Creator 的 StencilManager 设计。
|
||||
*/
|
||||
class StencilManager final {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 模板管理器实例
|
||||
*/
|
||||
static StencilManager* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
StencilManager() = default;
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~StencilManager();
|
||||
|
||||
/**
|
||||
* @brief 获取当前模板阶段
|
||||
* @return 模板阶段
|
||||
*/
|
||||
StencilStage getStencilStage() const;
|
||||
|
||||
/**
|
||||
* @brief 获取深度模板状态
|
||||
* @param stage 模板阶段
|
||||
* @return 深度模板状态
|
||||
*/
|
||||
gfx::DepthStencilState* getDepthStencilState(StencilStage stage);
|
||||
|
||||
/**
|
||||
* @brief 从阶段设置深度模板状态
|
||||
* @param stage 模板阶段
|
||||
*/
|
||||
void setDepthStencilStateFromStage(StencilStage stage);
|
||||
|
||||
/**
|
||||
* @brief 获取遮罩栈大小
|
||||
* @return 遮罩栈大小
|
||||
*/
|
||||
u32 getMaskStackSize() const;
|
||||
|
||||
/**
|
||||
* @brief 设置遮罩栈大小
|
||||
* @param size 大小
|
||||
*/
|
||||
void setMaskStackSize(u32 size);
|
||||
|
||||
/**
|
||||
* @brief 压入遮罩
|
||||
*/
|
||||
void pushMask();
|
||||
|
||||
/**
|
||||
* @brief 清除模板
|
||||
* @param entity 渲染实体
|
||||
* @return 模板阶段
|
||||
*/
|
||||
StencilStage clear(RenderEntity* entity);
|
||||
|
||||
/**
|
||||
* @brief 进入层级
|
||||
* @param entity 渲染实体
|
||||
*/
|
||||
void enterLevel(RenderEntity* entity);
|
||||
|
||||
/**
|
||||
* @brief 启用遮罩
|
||||
*/
|
||||
void enableMask();
|
||||
|
||||
/**
|
||||
* @brief 退出遮罩
|
||||
*/
|
||||
void exitMask();
|
||||
|
||||
/**
|
||||
* @brief 获取写入掩码
|
||||
* @return 写入掩码
|
||||
*/
|
||||
u32 getWriteMask() const;
|
||||
|
||||
/**
|
||||
* @brief 获取退出写入掩码
|
||||
* @return 退出写入掩码
|
||||
*/
|
||||
u32 getExitWriteMask() const;
|
||||
|
||||
/**
|
||||
* @brief 获取模板引用值
|
||||
* @return 模板引用值
|
||||
*/
|
||||
u32 getStencilRef() const;
|
||||
|
||||
/**
|
||||
* @brief 获取模板哈希
|
||||
* @param stage 模板阶段
|
||||
* @return 哈希值
|
||||
*/
|
||||
u32 getStencilHash(StencilStage stage) const;
|
||||
|
||||
/**
|
||||
* @brief 设置模板阶段
|
||||
* @param stageIndex 阶段索引
|
||||
*/
|
||||
void setStencilStage(u32 stageIndex);
|
||||
|
||||
private:
|
||||
StencilManager(const StencilManager&) = delete;
|
||||
StencilManager& operator=(const StencilManager&) = delete;
|
||||
|
||||
StencilEntity stencilPattern_;
|
||||
StencilStage stage_{StencilStage::Disabled};
|
||||
u32 maskStackSize_{0};
|
||||
|
||||
std::unordered_map<u32, gfx::DepthStencilState*> cacheStateMap_;
|
||||
|
||||
static StencilManager* instance_;
|
||||
};
|
||||
|
||||
inline StencilStage StencilManager::getStencilStage() const {
|
||||
return stage_;
|
||||
}
|
||||
|
||||
inline u32 StencilManager::getMaskStackSize() const {
|
||||
return maskStackSize_;
|
||||
}
|
||||
|
||||
inline void StencilManager::setMaskStackSize(u32 size) {
|
||||
maskStackSize_ = size;
|
||||
}
|
||||
|
||||
inline void StencilManager::pushMask() {
|
||||
++maskStackSize_;
|
||||
}
|
||||
|
||||
inline void StencilManager::enableMask() {
|
||||
stage_ = StencilStage::Enabled;
|
||||
}
|
||||
|
||||
inline void StencilManager::exitMask() {
|
||||
if (maskStackSize_ == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
--maskStackSize_;
|
||||
if (maskStackSize_ == 0) {
|
||||
stage_ = StencilStage::Disabled;
|
||||
} else {
|
||||
stage_ = StencilStage::Enabled;
|
||||
}
|
||||
}
|
||||
|
||||
inline u32 StencilManager::getWriteMask() const {
|
||||
return 1 << (maskStackSize_ - 1);
|
||||
}
|
||||
|
||||
inline u32 StencilManager::getExitWriteMask() const {
|
||||
return 1 << maskStackSize_;
|
||||
}
|
||||
|
||||
inline u32 StencilManager::getStencilRef() const {
|
||||
u32 result = 0;
|
||||
for (u32 i = 0; i < maskStackSize_; i++) {
|
||||
result += (1 << i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline u32 StencilManager::getStencilHash(StencilStage stage) const {
|
||||
return (static_cast<u32>(stage) << 8) | maskStackSize_;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace gfx {
|
||||
class Buffer;
|
||||
class InputAssembler;
|
||||
class Device;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 网格缓冲区布局
|
||||
*/
|
||||
struct MeshBufferLayout {
|
||||
u32 byteOffset{0};
|
||||
u32 vertexOffset{0};
|
||||
u32 indexOffset{0};
|
||||
u32 dirtyMark{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief UI 网格缓冲区
|
||||
*
|
||||
* 管理顶点和索引数据的 GPU 缓冲区。
|
||||
*/
|
||||
class UIMeshBuffer {
|
||||
public:
|
||||
UIMeshBuffer();
|
||||
~UIMeshBuffer();
|
||||
|
||||
bool initialize(gfx::Device* device,
|
||||
const std::vector<gfx::Attribute>& attrs,
|
||||
u32 vertexCapacity = 65536,
|
||||
u32 indexCapacity = 196608);
|
||||
|
||||
void reset();
|
||||
void destroy();
|
||||
|
||||
float* getVData() const;
|
||||
void setVData(float* vData);
|
||||
|
||||
u16* getIData() const;
|
||||
void setIData(u16* iData);
|
||||
|
||||
u32 getByteOffset() const;
|
||||
void setByteOffset(u32 offset);
|
||||
|
||||
u32 getVertexOffset() const;
|
||||
void setVertexOffset(u32 offset);
|
||||
|
||||
u32 getIndexOffset() const;
|
||||
void setIndexOffset(u32 offset);
|
||||
|
||||
bool isDirty() const;
|
||||
void setDirty(bool dirty);
|
||||
|
||||
u32 getVertexCapacity() const;
|
||||
u32 getIndexCapacity() const;
|
||||
u32 getVertexFormatBytes() const;
|
||||
|
||||
gfx::Buffer* getVertexBuffer() const;
|
||||
gfx::Buffer* getIndexBuffer() const;
|
||||
gfx::InputAssembler* getInputAssembler() const;
|
||||
|
||||
gfx::InputAssembler* requireFreeIA(gfx::Device* device);
|
||||
void uploadBuffers();
|
||||
void resetIA();
|
||||
|
||||
private:
|
||||
bool createBuffers();
|
||||
bool createInputAssembler();
|
||||
|
||||
gfx::Device* device_{nullptr};
|
||||
std::vector<gfx::Attribute> attributes_;
|
||||
|
||||
float* vData_{nullptr};
|
||||
u16* iData_{nullptr};
|
||||
bool ownsData_{false};
|
||||
|
||||
u32 vertexCapacity_{65536};
|
||||
u32 indexCapacity_{196608};
|
||||
u32 vertexFormatBytes_{36};
|
||||
|
||||
u32 byteOffset_{0};
|
||||
u32 vertexOffset_{0};
|
||||
u32 indexOffset_{0};
|
||||
bool dirty_{false};
|
||||
|
||||
gfx::Buffer* vertexBuffer_{nullptr};
|
||||
gfx::Buffer* indexBuffer_{nullptr};
|
||||
gfx::InputAssembler* inputAssembler_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,113 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class GLFWWindow;
|
||||
class WindowModule;
|
||||
|
||||
/**
|
||||
* @brief 应用程序类
|
||||
*/
|
||||
class Application {
|
||||
public:
|
||||
static Application &get();
|
||||
|
||||
Application(const Application &) = delete;
|
||||
Application &operator=(const Application &) = delete;
|
||||
|
||||
/**
|
||||
* @brief 应用信息
|
||||
*/
|
||||
std::string appName = "Extra2D App";
|
||||
std::string appVersion = "1.0.0";
|
||||
std::string organization = "";
|
||||
|
||||
/**
|
||||
* @brief 注册模块
|
||||
* @tparam T 模块类型
|
||||
* @tparam Args 构造函数参数
|
||||
* @return 模块指针
|
||||
*/
|
||||
template <typename T, typename... Args> T *use(Args &&...args) {
|
||||
return Registry::instance().use<T>(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取模块
|
||||
* @tparam T 模块类型
|
||||
* @return 模块指针
|
||||
*/
|
||||
template <typename T> T *get() const { return Registry::instance().get<T>(); }
|
||||
|
||||
/**
|
||||
* @brief 初始化
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief 关闭
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 运行主循环
|
||||
*/
|
||||
void run();
|
||||
|
||||
/**
|
||||
* @brief 请求退出
|
||||
*/
|
||||
void quit();
|
||||
|
||||
/**
|
||||
* @brief 暂停
|
||||
*/
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* @brief 恢复
|
||||
*/
|
||||
void resume();
|
||||
|
||||
bool paused() const { return paused_; }
|
||||
bool running() const { return running_; }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口
|
||||
* @return 窗口指针
|
||||
*/
|
||||
GLFWWindow *window();
|
||||
|
||||
f32 dt() const { return dt_; }
|
||||
f32 totalTime() const { return totalTime_; }
|
||||
int fps() const { return fps_; }
|
||||
|
||||
private:
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
void mainLoop();
|
||||
void update();
|
||||
void render();
|
||||
|
||||
bool initialized_ = false;
|
||||
bool running_ = false;
|
||||
bool paused_ = false;
|
||||
bool shouldQuit_ = false;
|
||||
|
||||
f32 dt_ = 0.0f;
|
||||
f32 totalTime_ = 0.0f;
|
||||
f64 lastFrameTime_ = 0.0;
|
||||
int frameCount_ = 0;
|
||||
f32 fpsTimer_ = 0.0f;
|
||||
int fps_ = 0;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/platform/Window.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Engine;
|
||||
class Window;
|
||||
class Input;
|
||||
|
||||
/**
|
||||
* @brief 应用程序配置
|
||||
*/
|
||||
struct AppConfig {
|
||||
std::string title = "Extra2D Application";
|
||||
i32 width = 1280;
|
||||
i32 height = 720;
|
||||
bool fullscreen = false;
|
||||
bool resizable = true;
|
||||
bool vsync = true;
|
||||
bool borderless = false;
|
||||
bool highDPI = true;
|
||||
i32 fps = 60;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 应用程序类
|
||||
*
|
||||
* 引擎的入口点,管理主循环、窗口和引擎生命周期。
|
||||
* 参考 Cocos Creator 的 Application 设计。
|
||||
*/
|
||||
class Application {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 应用程序实例
|
||||
*/
|
||||
static Application* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化应用程序
|
||||
* @param config 应用程序配置
|
||||
* @return 成功返回 0,失败返回错误码
|
||||
*/
|
||||
i32 init(const AppConfig& config);
|
||||
|
||||
/**
|
||||
* @brief 运行主循环
|
||||
* @return 退出码
|
||||
*/
|
||||
i32 run();
|
||||
|
||||
/**
|
||||
* @brief 暂停应用程序
|
||||
*/
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* @brief 恢复应用程序
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* @brief 关闭应用程序
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @brief 检查是否正在运行
|
||||
* @return 如果正在运行返回 true
|
||||
*/
|
||||
bool isRunning() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否暂停
|
||||
* @return 如果暂停返回 true
|
||||
*/
|
||||
bool isPaused() const;
|
||||
|
||||
/**
|
||||
* @brief 获取引擎实例
|
||||
* @return 引擎指针
|
||||
*/
|
||||
Engine* getEngine();
|
||||
|
||||
/**
|
||||
* @brief 获取窗口实例
|
||||
* @return 窗口指针
|
||||
*/
|
||||
Window* getWindow();
|
||||
|
||||
/**
|
||||
* @brief 获取输入实例
|
||||
* @return 输入指针
|
||||
*/
|
||||
Input* getInput();
|
||||
|
||||
/**
|
||||
* @brief 获取命令行参数
|
||||
* @return 参数列表
|
||||
*/
|
||||
const std::vector<std::string>& getArguments() const;
|
||||
|
||||
/**
|
||||
* @brief 设置命令行参数
|
||||
* @param argc 参数数量
|
||||
* @param argv 参数数组
|
||||
*/
|
||||
void setArguments(i32 argc, char* argv[]);
|
||||
|
||||
/**
|
||||
* @brief 设置帧回调
|
||||
* @param callback 每帧调用的回调函数
|
||||
*/
|
||||
void setFrameCallback(const std::function<void(f32)>& callback);
|
||||
|
||||
private:
|
||||
Application();
|
||||
~Application();
|
||||
|
||||
Application(const Application&) = delete;
|
||||
Application& operator=(const Application&) = delete;
|
||||
|
||||
void mainLoop();
|
||||
void processEvents();
|
||||
|
||||
static Application* instance_;
|
||||
|
||||
Engine* engine_{nullptr};
|
||||
Window* window_{nullptr};
|
||||
Input* input_{nullptr};
|
||||
|
||||
std::vector<std::string> arguments_;
|
||||
std::function<void(f32)> frameCallback_;
|
||||
|
||||
bool running_{false};
|
||||
bool paused_{false};
|
||||
bool initialized_{false};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,477 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <extra2d/asset/asset_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Asset - 资源基类
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源基类
|
||||
*
|
||||
* 所有资源类型的基类,继承自 enable_shared_from_this 支持自动引用计数。
|
||||
* 提供资源的基本属性和生命周期管理接口。
|
||||
*/
|
||||
class Asset : public std::enable_shared_from_this<Asset> {
|
||||
public:
|
||||
virtual ~Asset() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取资源类型
|
||||
* @return 资源类型枚举值
|
||||
*/
|
||||
virtual AssetType type() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查资源是否已加载
|
||||
* @return 已加载返回 true
|
||||
*/
|
||||
virtual bool loaded() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取资源内存占用大小
|
||||
* @return 内存占用字节数
|
||||
*/
|
||||
virtual size_t memSize() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取资源ID
|
||||
* @return 资源ID
|
||||
*/
|
||||
const AssetID &id() const { return id_; }
|
||||
|
||||
/**
|
||||
* @brief 获取资源路径
|
||||
* @return 资源路径
|
||||
*/
|
||||
const std::string &path() const { return path_; }
|
||||
|
||||
/**
|
||||
* @brief 获取资源状态
|
||||
* @return 资源状态
|
||||
*/
|
||||
AssetState state() const { return state_; }
|
||||
|
||||
/**
|
||||
* @brief 获取当前引用计数
|
||||
* @return 引用计数(用于调试和监控)
|
||||
*/
|
||||
long refs() const { return shared_from_this().use_count(); }
|
||||
|
||||
protected:
|
||||
AssetID id_;
|
||||
std::string path_;
|
||||
std::atomic<AssetState> state_{AssetState::Unloaded};
|
||||
|
||||
/**
|
||||
* @brief 设置资源状态
|
||||
* @param state 新状态
|
||||
*/
|
||||
void setState(AssetState state) {
|
||||
state_.store(state, std::memory_order_release);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置资源ID
|
||||
* @param id 资源ID
|
||||
*/
|
||||
void setId(const AssetID &id) { id_ = id; }
|
||||
|
||||
/**
|
||||
* @brief 设置资源路径
|
||||
* @param path 资源路径
|
||||
*/
|
||||
void setPath(const std::string &path) { path_ = path; }
|
||||
|
||||
friend class AssetCache;
|
||||
friend class AssetService;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TextureAsset - 纹理资源
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 纹理资源类
|
||||
*
|
||||
* 存储纹理图像数据,支持多种像素格式。
|
||||
*/
|
||||
class TextureAsset : public Asset {
|
||||
public:
|
||||
AssetType type() const override { return AssetType::Texture; }
|
||||
|
||||
bool loaded() const override {
|
||||
return state_.load(std::memory_order_acquire) == AssetState::Loaded &&
|
||||
data_ != nullptr;
|
||||
}
|
||||
|
||||
size_t memSize() const override {
|
||||
return static_cast<size_t>(width_) * height_ * channels_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取纹理宽度
|
||||
* @return 宽度(像素)
|
||||
*/
|
||||
int width() const { return width_; }
|
||||
|
||||
/**
|
||||
* @brief 获取纹理高度
|
||||
* @return 高度(像素)
|
||||
*/
|
||||
int height() const { return height_; }
|
||||
|
||||
/**
|
||||
* @brief 获取通道数
|
||||
* @return 通道数(1-4)
|
||||
*/
|
||||
int channels() const { return channels_; }
|
||||
|
||||
/**
|
||||
* @brief 获取像素数据
|
||||
* @return 像素数据指针
|
||||
*/
|
||||
const u8 *data() const { return data_.get(); }
|
||||
|
||||
/**
|
||||
* @brief 获取像素数据大小
|
||||
* @return 数据大小(字节)
|
||||
*/
|
||||
size_t dataSize() const { return memSize(); }
|
||||
|
||||
/**
|
||||
* @brief 设置纹理数据
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param channels 通道数
|
||||
* @param data 像素数据(转移所有权)
|
||||
*/
|
||||
void setData(int width, int height, int channels, Unique<u8[]> data) {
|
||||
width_ = width;
|
||||
height_ = height;
|
||||
channels_ = channels;
|
||||
data_ = std::move(data);
|
||||
setState(AssetState::Loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放纹理数据
|
||||
*/
|
||||
void release() {
|
||||
data_.reset();
|
||||
width_ = 0;
|
||||
height_ = 0;
|
||||
channels_ = 0;
|
||||
setState(AssetState::Unloaded);
|
||||
}
|
||||
|
||||
private:
|
||||
int width_ = 0;
|
||||
int height_ = 0;
|
||||
int channels_ = 0;
|
||||
Unique<u8[]> data_;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FontAsset - 字体资源
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 字体资源类
|
||||
*
|
||||
* 存储TrueType字体数据,支持字形渲染。
|
||||
* 使用 Pimpl 模式隐藏 stbtt_fontinfo 实现细节。
|
||||
*/
|
||||
class FontAsset : public Asset {
|
||||
public:
|
||||
FontAsset();
|
||||
~FontAsset() override;
|
||||
|
||||
AssetType type() const override { return AssetType::Font; }
|
||||
|
||||
bool loaded() const override;
|
||||
|
||||
size_t memSize() const override { return data_.size(); }
|
||||
|
||||
/**
|
||||
* @brief 获取指定像素高度的缩放因子
|
||||
* @param pixels 像素高度
|
||||
* @return 缩放因子
|
||||
*/
|
||||
float scaleForPixelHeight(float pixels) const;
|
||||
|
||||
/**
|
||||
* @brief 获取字体数据
|
||||
* @return 字体数据指针
|
||||
*/
|
||||
const u8 *data() const { return data_.data(); }
|
||||
|
||||
/**
|
||||
* @brief 获取字体数据大小
|
||||
* @return 数据大小(字节)
|
||||
*/
|
||||
size_t dataSize() const { return data_.size(); }
|
||||
|
||||
/**
|
||||
* @brief 设置字体数据
|
||||
* @param data 字体数据
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool setData(std::vector<u8> data);
|
||||
|
||||
/**
|
||||
* @brief 释放字体数据
|
||||
*/
|
||||
void release();
|
||||
|
||||
private:
|
||||
std::vector<u8> data_;
|
||||
class Impl;
|
||||
Unique<Impl> impl_;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ShaderAsset - 着色器资源
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 着色器资源类
|
||||
*
|
||||
* 存储顶点和片段着色器源代码。
|
||||
*/
|
||||
class ShaderAsset : public Asset {
|
||||
public:
|
||||
AssetType type() const override { return AssetType::Shader; }
|
||||
|
||||
bool loaded() const override {
|
||||
return state_.load(std::memory_order_acquire) == AssetState::Loaded;
|
||||
}
|
||||
|
||||
size_t memSize() const override {
|
||||
return vertexSrc_.size() + fragmentSrc_.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取顶点着色器源码
|
||||
* @return 顶点着色器源码
|
||||
*/
|
||||
const std::string &vertexSource() const { return vertexSrc_; }
|
||||
|
||||
/**
|
||||
* @brief 获取片段着色器源码
|
||||
* @return 片段着色器源码
|
||||
*/
|
||||
const std::string &fragmentSource() const { return fragmentSrc_; }
|
||||
|
||||
/**
|
||||
* @brief 设置着色器源码
|
||||
* @param vertex 顶点着色器源码
|
||||
* @param fragment 片段着色器源码
|
||||
*/
|
||||
void setSource(std::string vertex, std::string fragment) {
|
||||
vertexSrc_ = std::move(vertex);
|
||||
fragmentSrc_ = std::move(fragment);
|
||||
setState(AssetState::Loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放着色器源码
|
||||
*/
|
||||
void release() {
|
||||
vertexSrc_.clear();
|
||||
fragmentSrc_.clear();
|
||||
setState(AssetState::Unloaded);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string vertexSrc_;
|
||||
std::string fragmentSrc_;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AudioAsset - 音频资源
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 音频格式枚举
|
||||
*/
|
||||
enum class AudioFormat : u8 { PCM = 0, MP3 = 1, OGG = 2, WAV = 3 };
|
||||
|
||||
/**
|
||||
* @brief 音频资源类
|
||||
*
|
||||
* 存储音频数据,支持多种格式。
|
||||
*/
|
||||
class AudioAsset : public Asset {
|
||||
public:
|
||||
AssetType type() const override { return AssetType::Audio; }
|
||||
|
||||
bool loaded() const override {
|
||||
return state_.load(std::memory_order_acquire) == AssetState::Loaded &&
|
||||
!data_.empty();
|
||||
}
|
||||
|
||||
size_t memSize() const override { return data_.size(); }
|
||||
|
||||
/**
|
||||
* @brief 获取音频格式
|
||||
* @return 音频格式
|
||||
*/
|
||||
AudioFormat format() const { return format_; }
|
||||
|
||||
/**
|
||||
* @brief 获取声道数
|
||||
* @return 声道数
|
||||
*/
|
||||
int channels() const { return channels_; }
|
||||
|
||||
/**
|
||||
* @brief 获取采样率
|
||||
* @return 采样率
|
||||
*/
|
||||
int sampleRate() const { return sampleRate_; }
|
||||
|
||||
/**
|
||||
* @brief 获取每样本位数
|
||||
* @return 每样本位数
|
||||
*/
|
||||
int bitsPerSample() const { return bitsPerSample_; }
|
||||
|
||||
/**
|
||||
* @brief 获取时长(秒)
|
||||
* @return 时长
|
||||
*/
|
||||
float duration() const { return duration_; }
|
||||
|
||||
/**
|
||||
* @brief 获取音频数据
|
||||
* @return 音频数据指针
|
||||
*/
|
||||
const u8 *data() const { return data_.data(); }
|
||||
|
||||
/**
|
||||
* @brief 获取音频数据大小
|
||||
* @return 数据大小(字节)
|
||||
*/
|
||||
size_t dataSize() const { return data_.size(); }
|
||||
|
||||
/**
|
||||
* @brief 是否为流式音频
|
||||
* @return 流式音频返回 true
|
||||
*/
|
||||
bool streaming() const { return streaming_; }
|
||||
|
||||
/**
|
||||
* @brief 设置音频数据
|
||||
* @param format 音频格式
|
||||
* @param channels 声道数
|
||||
* @param sampleRate 采样率
|
||||
* @param bitsPerSample 每样本位数
|
||||
* @param data 音频数据
|
||||
*/
|
||||
void setData(AudioFormat format, int channels, int sampleRate,
|
||||
int bitsPerSample, std::vector<u8> data) {
|
||||
format_ = format;
|
||||
channels_ = channels;
|
||||
sampleRate_ = sampleRate;
|
||||
bitsPerSample_ = bitsPerSample;
|
||||
data_ = std::move(data);
|
||||
|
||||
if (sampleRate > 0 && channels > 0 && bitsPerSample > 0) {
|
||||
size_t bytesPerSecond =
|
||||
static_cast<size_t>(sampleRate) * channels * (bitsPerSample / 8);
|
||||
if (bytesPerSecond > 0) {
|
||||
duration_ = static_cast<float>(data_.size()) /
|
||||
static_cast<float>(bytesPerSecond);
|
||||
}
|
||||
}
|
||||
|
||||
streaming_ = duration_ > 5.0f;
|
||||
setState(AssetState::Loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放音频数据
|
||||
*/
|
||||
void release() {
|
||||
data_.clear();
|
||||
format_ = AudioFormat::PCM;
|
||||
channels_ = 0;
|
||||
sampleRate_ = 0;
|
||||
bitsPerSample_ = 0;
|
||||
duration_ = 0.0f;
|
||||
streaming_ = false;
|
||||
setState(AssetState::Unloaded);
|
||||
}
|
||||
|
||||
private:
|
||||
AudioFormat format_ = AudioFormat::PCM;
|
||||
int channels_ = 0;
|
||||
int sampleRate_ = 0;
|
||||
int bitsPerSample_ = 0;
|
||||
float duration_ = 0.0f;
|
||||
std::vector<u8> data_;
|
||||
bool streaming_ = false;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DataAsset - 通用数据资源
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 通用数据资源类
|
||||
*
|
||||
* 存储任意二进制数据。
|
||||
*/
|
||||
class DataAsset : public Asset {
|
||||
public:
|
||||
AssetType type() const override { return AssetType::Data; }
|
||||
|
||||
bool loaded() const override {
|
||||
return state_.load(std::memory_order_acquire) == AssetState::Loaded;
|
||||
}
|
||||
|
||||
size_t memSize() const override { return data_.size(); }
|
||||
|
||||
/**
|
||||
* @brief 获取数据
|
||||
* @return 数据指针
|
||||
*/
|
||||
const u8 *data() const { return data_.data(); }
|
||||
|
||||
/**
|
||||
* @brief 获取数据大小
|
||||
* @return 数据大小(字节)
|
||||
*/
|
||||
size_t size() const { return data_.size(); }
|
||||
|
||||
/**
|
||||
* @brief 设置数据
|
||||
* @param data 数据
|
||||
*/
|
||||
void setData(std::vector<u8> data) {
|
||||
data_ = std::move(data);
|
||||
setState(AssetState::Loaded);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放数据
|
||||
*/
|
||||
void release() {
|
||||
data_.clear();
|
||||
setState(AssetState::Unloaded);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<u8> data_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,263 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/asset/asset.h>
|
||||
#include <extra2d/asset/asset_handle.h>
|
||||
#include <extra2d/asset/asset_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CacheEntry - 缓存条目
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 缓存条目结构
|
||||
*
|
||||
* 存储资源引用和访问信息,用于LRU缓存管理。
|
||||
*/
|
||||
struct CacheEntry {
|
||||
Ref<Asset> asset;
|
||||
std::chrono::steady_clock::time_point lastAccess;
|
||||
size_t accessCount = 0;
|
||||
|
||||
/**
|
||||
* @brief 构造缓存条目
|
||||
* @param a 资源引用
|
||||
*/
|
||||
explicit CacheEntry(Ref<Asset> a)
|
||||
: asset(std::move(a)),
|
||||
lastAccess(std::chrono::steady_clock::now()),
|
||||
accessCount(1) {}
|
||||
|
||||
/**
|
||||
* @brief 更新访问信息
|
||||
*/
|
||||
void touch() {
|
||||
lastAccess = std::chrono::steady_clock::now();
|
||||
++accessCount;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetCache - 资源缓存
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源缓存类
|
||||
*
|
||||
* 实现享元模式,提供资源共享和缓存管理功能。
|
||||
*
|
||||
* 特性:
|
||||
* - LRU缓存淘汰策略
|
||||
* - 线程安全(读写锁)
|
||||
* - 引用计数自动回收
|
||||
* - 缓存统计和监控
|
||||
* - 内存上限管理
|
||||
*/
|
||||
class AssetCache {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param limit 缓存内存上限(字节),0表示无限制
|
||||
*/
|
||||
explicit AssetCache(size_t limit = 0);
|
||||
|
||||
~AssetCache() = default;
|
||||
|
||||
AssetCache(const AssetCache&) = delete;
|
||||
AssetCache& operator=(const AssetCache&) = delete;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 资源管理
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 添加资源到缓存
|
||||
* @tparam T 资源类型
|
||||
* @param asset 资源引用
|
||||
* @return 资源句柄
|
||||
*/
|
||||
template<typename T>
|
||||
AssetHandle<T> add(Ref<T> asset) {
|
||||
static_assert(std::is_base_of_v<Asset, T>,
|
||||
"T must derive from Asset");
|
||||
|
||||
if (!asset) {
|
||||
return AssetHandle<T>();
|
||||
}
|
||||
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
|
||||
AssetID id = asset->id();
|
||||
|
||||
size_t memSize = asset->memSize();
|
||||
bytes_ += memSize;
|
||||
|
||||
auto it = lruList_.insert(lruList_.end(), id);
|
||||
|
||||
entries_[id] = CacheEntryData{
|
||||
ptr::makeUnique<CacheEntry>(std::static_pointer_cast<Asset>(asset)),
|
||||
it
|
||||
};
|
||||
|
||||
++stats_.count;
|
||||
|
||||
if (limit_ > 0 && bytes_ > limit_) {
|
||||
evict();
|
||||
}
|
||||
|
||||
return AssetHandle<T>(id, Weak<T>(asset));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从缓存获取资源
|
||||
* @tparam T 资源类型
|
||||
* @param id 资源ID
|
||||
* @return 资源句柄
|
||||
*/
|
||||
template<typename T>
|
||||
AssetHandle<T> get(const AssetID& id) {
|
||||
static_assert(std::is_base_of_v<Asset, T>,
|
||||
"T must derive from Asset");
|
||||
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
|
||||
auto it = entries_.find(id);
|
||||
if (it == entries_.end()) {
|
||||
++stats_.misses;
|
||||
return AssetHandle<T>();
|
||||
}
|
||||
|
||||
it->second.entry->touch();
|
||||
|
||||
lruList_.erase(it->second.lruIterator);
|
||||
it->second.lruIterator = lruList_.insert(lruList_.end(), id);
|
||||
|
||||
++stats_.hits;
|
||||
|
||||
auto typedAsset = std::static_pointer_cast<T>(it->second.entry->asset);
|
||||
return AssetHandle<T>(id, Weak<T>(typedAsset));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查缓存是否包含资源
|
||||
* @param id 资源ID
|
||||
* @return 包含返回 true
|
||||
*/
|
||||
bool has(const AssetID& id) const;
|
||||
|
||||
/**
|
||||
* @brief 从缓存移除资源
|
||||
* @param id 资源ID
|
||||
* @return 移除成功返回 true
|
||||
*/
|
||||
bool remove(const AssetID& id);
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// 缓存管理
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 设置缓存内存上限
|
||||
* @param limit 上限(字节),0表示无限制
|
||||
*/
|
||||
void setLimit(size_t limit);
|
||||
|
||||
/**
|
||||
* @brief 获取缓存内存上限
|
||||
* @return 上限(字节)
|
||||
*/
|
||||
size_t limit() const { return limit_; }
|
||||
|
||||
/**
|
||||
* @brief 获取当前缓存内存使用量
|
||||
* @return 使用量(字节)
|
||||
*/
|
||||
size_t bytes() const { return bytes_; }
|
||||
|
||||
/**
|
||||
* @brief 获取缓存条目数量
|
||||
* @return 条目数量
|
||||
*/
|
||||
size_t count() const;
|
||||
|
||||
/**
|
||||
* @brief 获取当前缓存内存使用量(别名)
|
||||
* @return 使用量(字节)
|
||||
*/
|
||||
size_t size() const { return bytes_; }
|
||||
|
||||
/**
|
||||
* @brief 记录缓存命中
|
||||
*/
|
||||
void hit() { ++stats_.hits; }
|
||||
|
||||
/**
|
||||
* @brief 记录缓存未命中
|
||||
*/
|
||||
void miss() { ++stats_.misses; }
|
||||
|
||||
/**
|
||||
* @brief 清理无外部引用的资源
|
||||
* @return 清理的资源数量
|
||||
*/
|
||||
size_t purge();
|
||||
|
||||
/**
|
||||
* @brief 清空所有缓存
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取缓存统计信息
|
||||
* @return 统计信息
|
||||
*/
|
||||
CacheStats stats() const;
|
||||
|
||||
/**
|
||||
* @brief 重置统计信息
|
||||
*/
|
||||
void resetStats();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 缓存条目数据(包含LRU迭代器)
|
||||
*/
|
||||
struct CacheEntryData {
|
||||
Ref<CacheEntry> entry;
|
||||
std::list<AssetID>::iterator lruIterator;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 淘汰资源(LRU策略)
|
||||
*/
|
||||
void evict();
|
||||
|
||||
/**
|
||||
* @brief 检查资源是否可以被淘汰
|
||||
* @param entry 缓存条目
|
||||
* @return 可淘汰返回 true
|
||||
*/
|
||||
bool canEvict(const CacheEntry& entry) const;
|
||||
|
||||
mutable std::shared_mutex mutex_;
|
||||
std::unordered_map<AssetID, CacheEntryData> entries_;
|
||||
std::list<AssetID> lruList_;
|
||||
|
||||
size_t limit_ = 0;
|
||||
size_t bytes_ = 0;
|
||||
mutable CacheStats stats_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,304 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/asset/asset_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <memory>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetHandleBase - 资源句柄非模板基类
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源句柄非模板基类
|
||||
*
|
||||
* 用于类型擦除,允许在容器中存储不同类型的句柄。
|
||||
*/
|
||||
class AssetHandleBase {
|
||||
public:
|
||||
AssetHandleBase() = default;
|
||||
|
||||
/**
|
||||
* @brief 从资源ID和弱引用构造
|
||||
* @param id 资源ID
|
||||
* @param ref 资源弱引用
|
||||
*/
|
||||
AssetHandleBase(const AssetID& id, Weak<Asset> ref)
|
||||
: id_(id), cacheRef_(std::move(ref)) {}
|
||||
|
||||
/**
|
||||
* @brief 从强引用构造
|
||||
* @param ptr 资源强引用
|
||||
*/
|
||||
explicit AssetHandleBase(Ref<Asset> ptr)
|
||||
: id_(ptr ? ptr->id() : AssetID()),
|
||||
cacheRef_(ptr) {}
|
||||
|
||||
/**
|
||||
* @brief 检查句柄是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
bool valid() const {
|
||||
return !cacheRef_.expired();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取资源强引用
|
||||
* @return 资源强引用
|
||||
*/
|
||||
Ref<Asset> get() const {
|
||||
return cacheRef_.lock();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取资源ID
|
||||
* @return 资源ID
|
||||
*/
|
||||
const AssetID& id() const { return id_; }
|
||||
|
||||
/**
|
||||
* @brief 获取资源路径
|
||||
* @return 资源路径
|
||||
*/
|
||||
const std::string& path() const { return id_.path; }
|
||||
|
||||
/**
|
||||
* @brief 隐式转换为bool
|
||||
*/
|
||||
explicit operator bool() const { return valid(); }
|
||||
|
||||
/**
|
||||
* @brief 重置句柄
|
||||
*/
|
||||
void reset() {
|
||||
id_ = AssetID();
|
||||
cacheRef_.reset();
|
||||
}
|
||||
|
||||
protected:
|
||||
AssetID id_;
|
||||
Weak<Asset> cacheRef_;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetHandle - 资源句柄
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源句柄模板类
|
||||
*
|
||||
* 使用强类型句柄替代裸指针,提供类型安全和自动生命周期管理。
|
||||
* 内部使用 weak_ptr 弱引用,不阻止资源回收。
|
||||
*
|
||||
* 特性:
|
||||
* - 类型安全:编译期检查资源类型
|
||||
* - 自动生命周期:资源无引用时自动回收
|
||||
* - 弱引用:不阻止缓存清理
|
||||
* - 空安全:使用前检查 valid()
|
||||
*
|
||||
* @tparam T 资源类型,必须继承自 Asset
|
||||
*/
|
||||
template<typename T>
|
||||
class AssetHandle : public AssetHandleBase {
|
||||
static_assert(std::is_base_of_v<Asset, T>,
|
||||
"T must derive from Asset");
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief 默认构造函数
|
||||
*/
|
||||
AssetHandle() = default;
|
||||
|
||||
/**
|
||||
* @brief 从基类句柄构造
|
||||
* @param base 基类句柄
|
||||
*/
|
||||
explicit AssetHandle(const AssetHandleBase& base)
|
||||
: AssetHandleBase(base) {}
|
||||
|
||||
/**
|
||||
* @brief 从资源ID和弱引用构造
|
||||
* @param id 资源ID
|
||||
* @param ref 资源弱引用
|
||||
*/
|
||||
AssetHandle(const AssetID& id, Weak<T> ref)
|
||||
: AssetHandleBase(id, std::move(ref)) {}
|
||||
|
||||
/**
|
||||
* @brief 从强引用构造
|
||||
* @param ptr 资源强引用
|
||||
*/
|
||||
explicit AssetHandle(Ref<T> ptr)
|
||||
: AssetHandleBase(ptr) {}
|
||||
|
||||
/**
|
||||
* @brief 获取资源强引用
|
||||
* @return 资源强引用,如果资源已被回收返回 nullptr
|
||||
*/
|
||||
Ref<T> get() const {
|
||||
return std::static_pointer_cast<T>(cacheRef_.lock());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解引用操作符
|
||||
* @return 资源指针
|
||||
* @note 使用前应检查 valid()
|
||||
*/
|
||||
T* operator->() const {
|
||||
auto ptr = get();
|
||||
return ptr ? ptr.get() : nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 解引用操作符
|
||||
* @return 资源引用
|
||||
* @note 使用前应检查 valid()
|
||||
*/
|
||||
T& operator*() const {
|
||||
auto ptr = get();
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 相等比较
|
||||
* @param other 其他句柄
|
||||
* @return 相等返回 true
|
||||
*/
|
||||
bool operator==(const AssetHandle& other) const {
|
||||
return id_ == other.id_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 不等比较
|
||||
* @param other 其他句柄
|
||||
* @return 不等返回 true
|
||||
*/
|
||||
bool operator!=(const AssetHandle& other) const {
|
||||
return id_ != other.id_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 小于比较(用于有序容器)
|
||||
* @param other 其他句柄
|
||||
* @return 小于返回 true
|
||||
*/
|
||||
bool operator<(const AssetHandle& other) const {
|
||||
return id_ < other.id_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 重置句柄
|
||||
*/
|
||||
void reset() {
|
||||
id_ = AssetID();
|
||||
cacheRef_.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查资源是否已加载
|
||||
* @return 已加载返回 true
|
||||
*/
|
||||
bool loaded() const {
|
||||
auto ptr = get();
|
||||
return ptr && ptr->loaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取资源状态
|
||||
* @return 资源状态
|
||||
*/
|
||||
AssetState state() const {
|
||||
auto ptr = get();
|
||||
return ptr ? ptr->state() : AssetState::Unloaded;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetLoadResult - 资源加载结果
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源加载结果
|
||||
*
|
||||
* 封装资源加载的结果状态,支持成功、失败、加载中等状态。
|
||||
*
|
||||
* @tparam T 资源类型
|
||||
*/
|
||||
template<typename T>
|
||||
struct AssetLoadResult {
|
||||
AssetHandle<T> handle;
|
||||
AssetState state = AssetState::Unloaded;
|
||||
std::string error;
|
||||
|
||||
/**
|
||||
* @brief 检查是否成功
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool success() const {
|
||||
return state == AssetState::Loaded && handle.valid();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否失败
|
||||
* @return 失败返回 true
|
||||
*/
|
||||
bool failed() const {
|
||||
return state == AssetState::Failed;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否正在加载
|
||||
* @return 正在加载返回 true
|
||||
*/
|
||||
bool loading() const {
|
||||
return state == AssetState::Loading;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建成功结果
|
||||
* @param handle 资源句柄
|
||||
* @return 加载结果
|
||||
*/
|
||||
static AssetLoadResult ok(AssetHandle<T> handle) {
|
||||
return { std::move(handle), AssetState::Loaded, "" };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建失败结果
|
||||
* @param error 错误信息
|
||||
* @return 加载结果
|
||||
*/
|
||||
static AssetLoadResult err(const std::string& error) {
|
||||
return { AssetHandle<T>(), AssetState::Failed, error };
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建加载中结果
|
||||
* @param handle 资源句柄(可能为空)
|
||||
* @return 加载结果
|
||||
*/
|
||||
static AssetLoadResult pending(AssetHandle<T> handle = {}) {
|
||||
return { std::move(handle), AssetState::Loading, "" };
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetLoadCallback - 资源加载回调
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源加载回调类型
|
||||
* @tparam T 资源类型
|
||||
*/
|
||||
template<typename T>
|
||||
using AssetLoadCallback = Fn<void(AssetHandle<T>)>;
|
||||
|
||||
/**
|
||||
* @brief 资源加载结果回调类型
|
||||
* @tparam T 资源类型
|
||||
*/
|
||||
template<typename T>
|
||||
using AssetLoadResultCallback = Fn<void(AssetLoadResult<T>)>;
|
||||
|
||||
}
|
||||
|
|
@ -1,312 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/asset/asset.h>
|
||||
#include <extra2d/asset/asset_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetLoaderBase - 资源加载器非模板基类
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源加载器非模板基类
|
||||
*
|
||||
* 用于类型擦除,允许在容器中存储不同类型的加载器。
|
||||
*/
|
||||
class AssetLoaderBase {
|
||||
public:
|
||||
virtual ~AssetLoaderBase() = default;
|
||||
|
||||
/**
|
||||
* @brief 从文件加载资源(返回 Asset 基类指针)
|
||||
* @param path 文件路径
|
||||
* @return 资源实例
|
||||
*/
|
||||
virtual Ref<Asset> loadBase(const std::string& path) = 0;
|
||||
|
||||
/**
|
||||
* @brief 从内存数据加载资源
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
* @return 资源实例
|
||||
*/
|
||||
virtual Ref<Asset> loadFromMemoryBase(const u8* data, size_t size) = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查是否能加载指定路径的资源
|
||||
* @param path 文件路径
|
||||
* @return 能加载返回 true
|
||||
*/
|
||||
virtual bool canLoad(const std::string& path) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取资源类型
|
||||
* @return 资源类型枚举值
|
||||
*/
|
||||
virtual AssetType type() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取支持的文件扩展名列表
|
||||
* @return 扩展名列表
|
||||
*/
|
||||
virtual std::vector<std::string> extensions() const = 0;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetLoader - 资源加载器接口
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源加载器接口模板
|
||||
*
|
||||
* 使用策略模式支持不同资源类型的加载。
|
||||
* 每种资源类型可以实现自己的加载器。
|
||||
*
|
||||
* @tparam T 资源类型
|
||||
*/
|
||||
template<typename T>
|
||||
class AssetLoader : public AssetLoaderBase {
|
||||
static_assert(std::is_base_of_v<Asset, T>,
|
||||
"T must derive from Asset");
|
||||
|
||||
public:
|
||||
virtual ~AssetLoader() = default;
|
||||
|
||||
/**
|
||||
* @brief 从文件加载资源
|
||||
* @param path 文件路径
|
||||
* @return 资源实例,失败返回 nullptr
|
||||
*/
|
||||
virtual Ref<T> load(const std::string& path) = 0;
|
||||
|
||||
/**
|
||||
* @brief 从内存数据加载资源
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
* @return 资源实例,失败返回 nullptr
|
||||
*/
|
||||
virtual Ref<T> loadFromMemory(const u8* data, size_t size) = 0;
|
||||
|
||||
Ref<Asset> loadBase(const std::string& path) override {
|
||||
return load(path);
|
||||
}
|
||||
|
||||
Ref<Asset> loadFromMemoryBase(const u8* data, size_t size) override {
|
||||
return loadFromMemory(data, size);
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TextureLoader - 纹理加载器
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 纹理加载器
|
||||
*
|
||||
* 使用 stb_image 加载各种格式的图片文件。
|
||||
* 支持 PNG, JPG, BMP, TGA, GIF, PSD, HDR, PIC 等格式。
|
||||
*/
|
||||
class TextureLoader : public AssetLoader<TextureAsset> {
|
||||
public:
|
||||
TextureLoader();
|
||||
~TextureLoader() override;
|
||||
|
||||
Ref<TextureAsset> load(const std::string& path) override;
|
||||
Ref<TextureAsset> loadFromMemory(const u8* data, size_t size) override;
|
||||
bool canLoad(const std::string& path) const override;
|
||||
AssetType type() const override { return AssetType::Texture; }
|
||||
std::vector<std::string> extensions() const override;
|
||||
|
||||
/**
|
||||
* @brief 设置期望的通道数
|
||||
* @param channels 通道数(1-4),0表示自动
|
||||
*/
|
||||
void setDesiredChannels(int channels);
|
||||
|
||||
/**
|
||||
* @brief 获取期望的通道数
|
||||
* @return 通道数
|
||||
*/
|
||||
int desiredChannels() const { return desiredChannels_; }
|
||||
|
||||
private:
|
||||
int desiredChannels_ = 4;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FontLoader - 字体加载器
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 字体加载器
|
||||
*
|
||||
* 加载 TrueType 字体文件(.ttf, .otf)。
|
||||
*/
|
||||
class FontLoader : public AssetLoader<FontAsset> {
|
||||
public:
|
||||
Ref<FontAsset> load(const std::string& path) override;
|
||||
Ref<FontAsset> loadFromMemory(const u8* data, size_t size) override;
|
||||
bool canLoad(const std::string& path) const override;
|
||||
AssetType type() const override { return AssetType::Font; }
|
||||
std::vector<std::string> extensions() const override;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ShaderLoader - 着色器加载器
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 着色器加载器
|
||||
*
|
||||
* 加载着色器源文件,支持以下格式:
|
||||
* - .vert/.frag: 分离的顶点/片段着色器
|
||||
* - .glsl: 合并的着色器文件(使用标记分隔)
|
||||
*/
|
||||
class ShaderLoader : public AssetLoader<ShaderAsset> {
|
||||
public:
|
||||
Ref<ShaderAsset> load(const std::string& path) override;
|
||||
Ref<ShaderAsset> loadFromMemory(const u8* data, size_t size) override;
|
||||
bool canLoad(const std::string& path) const override;
|
||||
AssetType type() const override { return AssetType::Shader; }
|
||||
std::vector<std::string> extensions() const override;
|
||||
|
||||
/**
|
||||
* @brief 设置顶点着色器标记
|
||||
* @param marker 标记字符串(默认 "[VERTEX]")
|
||||
*/
|
||||
void setVertexMarker(const std::string& marker) { vertexMarker_ = marker; }
|
||||
|
||||
/**
|
||||
* @brief 设置片段着色器标记
|
||||
* @param marker 标记字符串(默认 "[FRAGMENT]")
|
||||
*/
|
||||
void setFragmentMarker(const std::string& marker) { fragmentMarker_ = marker; }
|
||||
|
||||
private:
|
||||
std::string vertexMarker_ = "[VERTEX]";
|
||||
std::string fragmentMarker_ = "[FRAGMENT]";
|
||||
|
||||
/**
|
||||
* @brief 解析合并的着色器文件
|
||||
* @param content 文件内容
|
||||
* @param vertex 输出顶点着色器源码
|
||||
* @param fragment 输出片段着色器源码
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool parseCombined(const std::string& content,
|
||||
std::string& vertex,
|
||||
std::string& fragment);
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AudioLoader - 音频加载器
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 音频加载器
|
||||
*
|
||||
* 加载音频文件,支持 WAV 格式。
|
||||
* 可扩展支持 MP3, OGG 等格式。
|
||||
*/
|
||||
class AudioLoader : public AssetLoader<AudioAsset> {
|
||||
public:
|
||||
Ref<AudioAsset> load(const std::string& path) override;
|
||||
Ref<AudioAsset> loadFromMemory(const u8* data, size_t size) override;
|
||||
bool canLoad(const std::string& path) const override;
|
||||
AssetType type() const override { return AssetType::Audio; }
|
||||
std::vector<std::string> extensions() const override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 加载 WAV 格式音频
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
* @return 音频资源
|
||||
*/
|
||||
Ref<AudioAsset> loadWav(const u8* data, size_t size);
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DataLoader - 通用数据加载器
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 通用数据加载器
|
||||
*
|
||||
* 加载任意二进制数据文件。
|
||||
*/
|
||||
class DataLoader : public AssetLoader<DataAsset> {
|
||||
public:
|
||||
Ref<DataAsset> load(const std::string& path) override;
|
||||
Ref<DataAsset> loadFromMemory(const u8* data, size_t size) override;
|
||||
bool canLoad(const std::string& path) const override;
|
||||
AssetType type() const override { return AssetType::Data; }
|
||||
std::vector<std::string> extensions() const override;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetLoaderFactory - 加载器工厂
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 加载器工厂
|
||||
*
|
||||
* 根据资源类型或文件扩展名创建对应的加载器。
|
||||
* 使用模板方法返回具体类型的加载器。
|
||||
*/
|
||||
class AssetLoaderFactory {
|
||||
public:
|
||||
/**
|
||||
* @brief 根据资源类型创建纹理加载器
|
||||
* @return 纹理加载器实例
|
||||
*/
|
||||
static Unique<TextureLoader> createTextureLoader() {
|
||||
return ptr::makeUnique<TextureLoader>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据资源类型创建字体加载器
|
||||
* @return 字体加载器实例
|
||||
*/
|
||||
static Unique<FontLoader> createFontLoader() {
|
||||
return ptr::makeUnique<FontLoader>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据资源类型创建着色器加载器
|
||||
* @return 着色器加载器实例
|
||||
*/
|
||||
static Unique<ShaderLoader> createShaderLoader() {
|
||||
return ptr::makeUnique<ShaderLoader>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据资源类型创建音频加载器
|
||||
* @return 音频加载器实例
|
||||
*/
|
||||
static Unique<AudioLoader> createAudioLoader() {
|
||||
return ptr::makeUnique<AudioLoader>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据资源类型创建数据加载器
|
||||
* @return 数据加载器实例
|
||||
*/
|
||||
static Unique<DataLoader> createDataLoader() {
|
||||
return ptr::makeUnique<DataLoader>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 根据文件扩展名获取资源类型
|
||||
* @param extension 文件扩展名(包含点,如 ".png")
|
||||
* @return 资源类型,无法识别返回 AssetType::Unknown
|
||||
*/
|
||||
static AssetType getTypeByExtension(const std::string& extension);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,399 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/asset/asset_types.h>
|
||||
#include <extra2d/asset/data_processor.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetPack - 资源包
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源包类
|
||||
*
|
||||
* 支持从打包文件中加载资源。
|
||||
* 资源包格式:
|
||||
* - 头部:AssetPackageHeader
|
||||
* - 索引表:条目数量 + AssetPackageEntry 数组
|
||||
* - 数据区:按索引顺序存储的资源数据
|
||||
*/
|
||||
class AssetPack {
|
||||
public:
|
||||
AssetPack() = default;
|
||||
|
||||
/**
|
||||
* @brief 移动构造函数
|
||||
* @param other 其他资源包
|
||||
*/
|
||||
AssetPack(AssetPack&& other) noexcept;
|
||||
|
||||
/**
|
||||
* @brief 移动赋值操作符
|
||||
* @param other 其他资源包
|
||||
* @return 当前资源包引用
|
||||
*/
|
||||
AssetPack& operator=(AssetPack&& other) noexcept;
|
||||
|
||||
/**
|
||||
* @brief 析构函数,自动关闭文件
|
||||
*/
|
||||
~AssetPack();
|
||||
|
||||
/**
|
||||
* @brief 打开资源包
|
||||
* @param path 资源包文件路径
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool open(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 关闭资源包
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @brief 检查资源包是否已打开
|
||||
* @return 已打开返回 true
|
||||
*/
|
||||
bool isOpen() const { return file_.is_open(); }
|
||||
|
||||
/**
|
||||
* @brief 检查资源是否存在
|
||||
* @param id 资源ID
|
||||
* @return 存在返回 true
|
||||
*/
|
||||
bool has(const AssetID& id) const;
|
||||
|
||||
/**
|
||||
* @brief 检查资源是否存在
|
||||
* @param path 资源路径
|
||||
* @return 存在返回 true
|
||||
*/
|
||||
bool has(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* @brief 读取资源数据(自动解压/解密)
|
||||
* @param id 资源ID
|
||||
* @return 资源数据,失败返回空
|
||||
*/
|
||||
std::vector<u8> read(const AssetID& id);
|
||||
|
||||
/**
|
||||
* @brief 读取资源数据(自动解压/解密)
|
||||
* @param path 资源路径
|
||||
* @return 资源数据,失败返回空
|
||||
*/
|
||||
std::vector<u8> read(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 读取原始资源数据(不解压/不解密)
|
||||
* @param id 资源ID
|
||||
* @return 原始资源数据
|
||||
*/
|
||||
std::vector<u8> readRaw(const AssetID& id);
|
||||
|
||||
/**
|
||||
* @brief 获取所有资源ID
|
||||
* @return 资源ID列表
|
||||
*/
|
||||
std::vector<AssetID> assets() const;
|
||||
|
||||
/**
|
||||
* @brief 获取资源包路径
|
||||
* @return 资源包路径
|
||||
*/
|
||||
const std::string& path() const { return path_; }
|
||||
|
||||
/**
|
||||
* @brief 获取资源包头部
|
||||
* @return 头部信息
|
||||
*/
|
||||
const AssetPackageHeader& header() const { return header_; }
|
||||
|
||||
/**
|
||||
* @brief 获取资源条目数量
|
||||
* @return 条目数量
|
||||
*/
|
||||
size_t count() const { return entries_.size(); }
|
||||
|
||||
/**
|
||||
* @brief 设置数据处理管道
|
||||
* @param pipe 处理管道
|
||||
*/
|
||||
void setPipe(DataPipe pipe) { pipe_ = std::move(pipe); }
|
||||
|
||||
/**
|
||||
* @brief 获取资源条目信息
|
||||
* @param id 资源ID
|
||||
* @return 条目指针,不存在返回 nullptr
|
||||
*/
|
||||
const AssetPackageEntry* getEntry(const AssetID& id) const;
|
||||
|
||||
private:
|
||||
std::string path_;
|
||||
mutable std::ifstream file_;
|
||||
AssetPackageHeader header_;
|
||||
std::unordered_map<AssetID, AssetPackageEntry> entries_;
|
||||
DataPipe pipe_;
|
||||
|
||||
/**
|
||||
* @brief 读取并解析头部
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool readHeader();
|
||||
|
||||
/**
|
||||
* @brief 读取并解析索引表
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool readIndex();
|
||||
|
||||
/**
|
||||
* @brief 读取条目数据
|
||||
* @param entry 条目信息
|
||||
* @return 原始数据
|
||||
*/
|
||||
std::vector<u8> readEntryData(const AssetPackageEntry& entry);
|
||||
|
||||
AssetPack(const AssetPack&) = delete;
|
||||
AssetPack& operator=(const AssetPack&) = delete;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PackManager - 资源包管理器
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源包管理器
|
||||
*
|
||||
* 管理多个资源包,支持资源查找和加载。
|
||||
* 支持挂载/卸载资源包。
|
||||
*/
|
||||
class PackManager {
|
||||
public:
|
||||
PackManager() = default;
|
||||
|
||||
/**
|
||||
* @brief 析构函数,自动卸载所有资源包
|
||||
*/
|
||||
~PackManager() = default;
|
||||
|
||||
/**
|
||||
* @brief 挂载资源包
|
||||
* @param path 资源包路径
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool mount(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 卸载资源包
|
||||
* @param path 资源包路径
|
||||
*/
|
||||
void unmount(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 卸载所有资源包
|
||||
*/
|
||||
void unmountAll();
|
||||
|
||||
/**
|
||||
* @brief 查找资源所在的包
|
||||
* @param id 资源ID
|
||||
* @return 资源包指针,未找到返回 nullptr
|
||||
*/
|
||||
AssetPack* find(const AssetID& id);
|
||||
|
||||
/**
|
||||
* @brief 查找资源所在的包
|
||||
* @param path 资源路径
|
||||
* @return 资源包指针,未找到返回 nullptr
|
||||
*/
|
||||
AssetPack* find(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 检查资源是否存在
|
||||
* @param id 资源ID
|
||||
* @return 存在返回 true
|
||||
*/
|
||||
bool has(const AssetID& id) const;
|
||||
|
||||
/**
|
||||
* @brief 检查资源是否存在
|
||||
* @param path 资源路径
|
||||
* @return 存在返回 true
|
||||
*/
|
||||
bool has(const std::string& path) const;
|
||||
|
||||
/**
|
||||
* @brief 读取资源数据
|
||||
* @param id 资源ID
|
||||
* @return 资源数据
|
||||
*/
|
||||
std::vector<u8> read(const AssetID& id);
|
||||
|
||||
/**
|
||||
* @brief 读取资源数据
|
||||
* @param path 资源路径
|
||||
* @return 资源数据
|
||||
*/
|
||||
std::vector<u8> read(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 设置默认处理管道
|
||||
* @param pipe 处理管道
|
||||
*/
|
||||
void setPipe(DataPipe pipe) { defaultPipe_ = std::move(pipe); }
|
||||
|
||||
/**
|
||||
* @brief 获取默认处理管道
|
||||
* @return 处理管道引用
|
||||
*/
|
||||
const DataPipe& pipe() const { return defaultPipe_; }
|
||||
|
||||
/**
|
||||
* @brief 获取已挂载的资源包数量
|
||||
* @return 资源包数量
|
||||
*/
|
||||
size_t count() const { return packs_.size(); }
|
||||
|
||||
/**
|
||||
* @brief 获取所有资源ID
|
||||
* @return 资源ID列表
|
||||
*/
|
||||
std::vector<AssetID> allAssets() const;
|
||||
|
||||
/**
|
||||
* @brief 获取已挂载的资源包路径列表
|
||||
* @return 路径列表
|
||||
*/
|
||||
std::vector<std::string> mountedPacks() const;
|
||||
|
||||
private:
|
||||
std::vector<Unique<AssetPack>> packs_;
|
||||
DataPipe defaultPipe_;
|
||||
|
||||
PackManager(const PackManager&) = delete;
|
||||
PackManager& operator=(const PackManager&) = delete;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetPackBuilder - 资源包构建器(用于打包工具)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源包构建器
|
||||
*
|
||||
* 用于创建资源包文件。
|
||||
* 支持添加资源、压缩、加密等操作。
|
||||
*/
|
||||
class AssetPackBuilder {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param compression 压缩算法
|
||||
* @param level 压缩级别
|
||||
*/
|
||||
explicit AssetPackBuilder(Compression compression = Compression::None, int level = 3);
|
||||
|
||||
/**
|
||||
* @brief 添加资源
|
||||
* @param path 资源路径(包内路径)
|
||||
* @param data 资源数据
|
||||
*/
|
||||
void add(const std::string& path, const std::vector<u8>& data);
|
||||
|
||||
/**
|
||||
* @brief 添加资源(移动语义)
|
||||
* @param path 资源路径
|
||||
* @param data 资源数据
|
||||
*/
|
||||
void add(const std::string& path, std::vector<u8>&& data);
|
||||
|
||||
/**
|
||||
* @brief 添加文件
|
||||
* @param filePath 文件路径
|
||||
* @param packPath 包内路径(可选,默认使用文件名)
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool addFile(const std::string& filePath, const std::string& packPath = "");
|
||||
|
||||
/**
|
||||
* @brief 添加目录
|
||||
* @param dirPath 目录路径
|
||||
* @param prefix 包内路径前缀(可选)
|
||||
* @return 添加的文件数量
|
||||
*/
|
||||
size_t addDirectory(const std::string& dirPath, const std::string& prefix = "");
|
||||
|
||||
/**
|
||||
* @brief 设置加密密钥
|
||||
* @param key 加密密钥
|
||||
* @param type 加密类型
|
||||
*/
|
||||
void setEncryption(const std::string& key, Decryptor::Type type = Decryptor::Type::XOR);
|
||||
|
||||
/**
|
||||
* @brief 构建资源包
|
||||
* @param outputPath 输出文件路径
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool build(const std::string& outputPath);
|
||||
|
||||
/**
|
||||
* @brief 清空所有资源
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取资源数量
|
||||
* @return 资源数量
|
||||
*/
|
||||
size_t count() const { return entries_.size(); }
|
||||
|
||||
/**
|
||||
* @brief 获取总原始大小
|
||||
* @return 总原始大小
|
||||
*/
|
||||
size_t totalOriginalSize() const { return totalOriginalSize_; }
|
||||
|
||||
/**
|
||||
* @brief 获取总压缩大小
|
||||
* @return 总压缩大小
|
||||
*/
|
||||
size_t totalCompressedSize() const { return totalCompressedSize_; }
|
||||
|
||||
private:
|
||||
struct BuilderEntry {
|
||||
AssetID id;
|
||||
std::vector<u8> data;
|
||||
std::vector<u8> compressedData;
|
||||
u64 offset;
|
||||
u32 compression;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
Compression compression_;
|
||||
int level_;
|
||||
std::string encryptKey_;
|
||||
Decryptor::Type encryptType_ = Decryptor::Type::None;
|
||||
std::vector<BuilderEntry> entries_;
|
||||
size_t totalOriginalSize_ = 0;
|
||||
size_t totalCompressedSize_ = 0;
|
||||
|
||||
/**
|
||||
* @brief 处理数据(压缩/加密)
|
||||
* @param data 原始数据
|
||||
* @return 处理后的数据
|
||||
*/
|
||||
std::vector<u8> processData(const std::vector<u8>& data);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,270 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetID - 强类型资源标识符
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 强类型资源ID
|
||||
*
|
||||
* 使用类型安全的ID替代裸字符串,支持:
|
||||
* - 哈希计算(用于快速比较和查找)
|
||||
* - 原始路径存储(用于调试和日志)
|
||||
* - 隐式转换和比较操作
|
||||
*/
|
||||
struct AssetID {
|
||||
u64 hash = 0;
|
||||
std::string path;
|
||||
|
||||
AssetID() = default;
|
||||
|
||||
/**
|
||||
* @brief 从路径构造资源ID
|
||||
* @param p 资源路径
|
||||
*/
|
||||
explicit AssetID(const std::string& p)
|
||||
: hash(hashPath(p)), path(p) {}
|
||||
|
||||
/**
|
||||
* @brief 从路径构造资源ID(移动语义)
|
||||
* @param p 资源路径
|
||||
*/
|
||||
explicit AssetID(std::string&& p)
|
||||
: hash(hashPath(p)), path(std::move(p)) {}
|
||||
|
||||
/**
|
||||
* @brief 计算路径哈希值
|
||||
* @param p 路径字符串
|
||||
* @return 64位哈希值
|
||||
*/
|
||||
static u64 hashPath(const std::string& p) {
|
||||
u64 result = 14695981039346656037ULL;
|
||||
for (char c : p) {
|
||||
result ^= static_cast<u64>(static_cast<u8>(c));
|
||||
result *= 1099511628211ULL;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查ID是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
bool valid() const { return hash != 0 || !path.empty(); }
|
||||
|
||||
/**
|
||||
* @brief 布尔转换操作符
|
||||
*/
|
||||
explicit operator bool() const { return valid(); }
|
||||
|
||||
/**
|
||||
* @brief 相等比较
|
||||
*/
|
||||
bool operator==(const AssetID& other) const { return hash == other.hash; }
|
||||
|
||||
/**
|
||||
* @brief 不等比较
|
||||
*/
|
||||
bool operator!=(const AssetID& other) const { return hash != other.hash; }
|
||||
|
||||
/**
|
||||
* @brief 小于比较(用于有序容器)
|
||||
*/
|
||||
bool operator<(const AssetID& other) const { return hash < other.hash; }
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetType - 资源类型枚举
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源类型枚举
|
||||
* 定义支持的资源类型,用于类型安全的资源加载和管理
|
||||
*/
|
||||
enum class AssetType : u8 {
|
||||
Unknown = 0,
|
||||
Texture = 1,
|
||||
Font = 2,
|
||||
Shader = 3,
|
||||
Audio = 4,
|
||||
Data = 5,
|
||||
Custom = 255
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 获取资源类型名称
|
||||
* @param type 资源类型
|
||||
* @return 类型名称字符串
|
||||
*/
|
||||
inline const char* assetTypeName(AssetType type) {
|
||||
switch (type) {
|
||||
case AssetType::Texture: return "Texture";
|
||||
case AssetType::Font: return "Font";
|
||||
case AssetType::Shader: return "Shader";
|
||||
case AssetType::Audio: return "Audio";
|
||||
case AssetType::Data: return "Data";
|
||||
case AssetType::Custom: return "Custom";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetState - 资源状态枚举
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源状态枚举
|
||||
* 定义资源的生命周期状态
|
||||
*/
|
||||
enum class AssetState : u8 {
|
||||
Unloaded = 0,
|
||||
Loading = 1,
|
||||
Loaded = 2,
|
||||
Failed = 3,
|
||||
Unloading = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 获取资源状态名称
|
||||
* @param state 资源状态
|
||||
* @return 状态名称字符串
|
||||
*/
|
||||
inline const char* assetStateName(AssetState state) {
|
||||
switch (state) {
|
||||
case AssetState::Unloaded: return "Unloaded";
|
||||
case AssetState::Loading: return "Loading";
|
||||
case AssetState::Loaded: return "Loaded";
|
||||
case AssetState::Failed: return "Failed";
|
||||
case AssetState::Unloading: return "Unloading";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Compression - 压缩算法枚举
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 压缩算法枚举
|
||||
* 定义支持的压缩算法类型
|
||||
*/
|
||||
enum class Compression : u8 {
|
||||
None = 0,
|
||||
Zstd = 1,
|
||||
LZ4 = 2,
|
||||
Zlib = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 获取压缩算法名称
|
||||
* @param comp 压缩算法
|
||||
* @return 算法名称字符串
|
||||
*/
|
||||
inline const char* compressionName(Compression comp) {
|
||||
switch (comp) {
|
||||
case Compression::Zstd: return "Zstd";
|
||||
case Compression::LZ4: return "LZ4";
|
||||
case Compression::Zlib: return "Zlib";
|
||||
default: return "None";
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// CacheStats - 缓存统计结构
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 缓存统计信息
|
||||
* 用于监控资源缓存的使用情况和性能
|
||||
*/
|
||||
struct CacheStats {
|
||||
size_t bytes = 0;
|
||||
size_t limit = 0;
|
||||
size_t count = 0;
|
||||
size_t hits = 0;
|
||||
size_t misses = 0;
|
||||
|
||||
/**
|
||||
* @brief 计算缓存命中率
|
||||
* @return 命中率(0.0 - 1.0)
|
||||
*/
|
||||
float hitRate() const {
|
||||
size_t total = hits + misses;
|
||||
return total > 0 ? static_cast<float>(hits) / static_cast<float>(total) : 0.0f;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算缓存使用率
|
||||
* @return 使用率(0.0 - 1.0)
|
||||
*/
|
||||
float usage() const {
|
||||
return limit > 0 ? static_cast<float>(bytes) / static_cast<float>(limit) : 0.0f;
|
||||
}
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetPackageHeader - 资源包头部信息
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源包头部信息
|
||||
* 用于识别压缩和加密类型
|
||||
*/
|
||||
struct AssetPackageHeader {
|
||||
u32 magic = 0;
|
||||
u32 version = 0;
|
||||
u32 compressionType = 0;
|
||||
u32 encryptionType = 0;
|
||||
u64 originalSize = 0;
|
||||
u64 compressedSize = 0;
|
||||
u8 checksum[32] = {};
|
||||
|
||||
static constexpr u32 MAGIC = 0x4B325045; // 'E2PK'
|
||||
|
||||
/**
|
||||
* @brief 检查头部是否有效
|
||||
* @return 有效返回 true
|
||||
*/
|
||||
bool valid() const { return magic == MAGIC; }
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetPackageEntry - 资源包索引项
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源包索引项
|
||||
* 描述资源在包中的位置和属性
|
||||
*/
|
||||
struct AssetPackageEntry {
|
||||
AssetID id;
|
||||
u64 offset = 0;
|
||||
u64 size = 0;
|
||||
u64 originalSize = 0;
|
||||
u32 compression = 0;
|
||||
u32 flags = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// std::hash 特化 - 支持在 unordered_map/unordered_set 中使用 AssetID
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace std {
|
||||
|
||||
template<>
|
||||
struct hash<extra2d::AssetID> {
|
||||
size_t operator()(const extra2d::AssetID& id) const noexcept {
|
||||
return static_cast<size_t>(id.hash);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,397 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/asset/asset_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DataProcessor - 数据处理器接口(装饰器模式)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 数据处理器接口
|
||||
*
|
||||
* 使用装饰器模式支持链式处理数据流。
|
||||
* 支持压缩、解压、加密、解密等操作的组合。
|
||||
*/
|
||||
class DataProcessor {
|
||||
public:
|
||||
virtual ~DataProcessor() = default;
|
||||
|
||||
/**
|
||||
* @brief 处理输入数据
|
||||
* @param input 输入数据
|
||||
* @return 处理后的数据
|
||||
*/
|
||||
virtual std::vector<u8> process(const std::vector<u8>& input) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置下一个处理器
|
||||
* @param next 下一个处理器
|
||||
*/
|
||||
void setNext(Unique<DataProcessor> next) { next_ = std::move(next); }
|
||||
|
||||
/**
|
||||
* @brief 获取下一个处理器
|
||||
* @return 下一个处理器的指针
|
||||
*/
|
||||
DataProcessor* next() const { return next_.get(); }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 调用下一个处理器处理数据
|
||||
* @param input 输入数据
|
||||
* @return 处理后的数据,如果没有下一个处理器则返回原数据
|
||||
*/
|
||||
std::vector<u8> processNext(const std::vector<u8>& input) {
|
||||
return next_ ? next_->process(input) : input;
|
||||
}
|
||||
|
||||
Unique<DataProcessor> next_;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Decryptor - 解密器
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 解密处理器
|
||||
*
|
||||
* 使用 XOR 或 AES 算法解密数据。
|
||||
* 支持简单的 XOR 加密和 AES-256 加密。
|
||||
*/
|
||||
class Decryptor : public DataProcessor {
|
||||
public:
|
||||
/**
|
||||
* @brief 加密类型枚举
|
||||
*/
|
||||
enum class Type : u8 {
|
||||
None = 0,
|
||||
XOR = 1,
|
||||
AES256 = 2
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 构造解密器
|
||||
* @param key 解密密钥
|
||||
* @param type 加密类型
|
||||
*/
|
||||
explicit Decryptor(const std::string& key, Type type = Type::XOR);
|
||||
|
||||
/**
|
||||
* @brief 处理(解密)输入数据
|
||||
* @param input 加密的输入数据
|
||||
* @return 解密后的数据
|
||||
*/
|
||||
std::vector<u8> process(const std::vector<u8>& input) override;
|
||||
|
||||
/**
|
||||
* @brief 获取加密类型
|
||||
* @return 加密类型
|
||||
*/
|
||||
Type type() const { return type_; }
|
||||
|
||||
private:
|
||||
std::string key_;
|
||||
Type type_;
|
||||
|
||||
/**
|
||||
* @brief XOR 解密
|
||||
* @param input 输入数据
|
||||
* @return 解密后的数据
|
||||
*/
|
||||
std::vector<u8> decryptXOR(const std::vector<u8>& input);
|
||||
|
||||
/**
|
||||
* @brief AES-256 解密
|
||||
* @param input 输入数据
|
||||
* @return 解密后的数据
|
||||
*/
|
||||
std::vector<u8> decryptAES256(const std::vector<u8>& input);
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Decompressor - 解压器
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 解压处理器
|
||||
*
|
||||
* 支持多种压缩算法的解压操作。
|
||||
* 目前支持 Zstd、LZ4 和 Zlib。
|
||||
*/
|
||||
class Decompressor : public DataProcessor {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造解压器
|
||||
* @param algo 压缩算法
|
||||
*/
|
||||
explicit Decompressor(Compression algo = Compression::Zstd);
|
||||
|
||||
/**
|
||||
* @brief 处理(解压)输入数据
|
||||
* @param input 压缩的输入数据
|
||||
* @return 解压后的数据
|
||||
*/
|
||||
std::vector<u8> process(const std::vector<u8>& input) override;
|
||||
|
||||
/**
|
||||
* @brief 获取压缩算法
|
||||
* @return 压缩算法
|
||||
*/
|
||||
Compression algorithm() const { return algo_; }
|
||||
|
||||
private:
|
||||
Compression algo_;
|
||||
|
||||
/**
|
||||
* @brief Zstd 解压
|
||||
* @param input 压缩数据
|
||||
* @return 解压后的数据
|
||||
*/
|
||||
std::vector<u8> decompressZstd(const std::vector<u8>& input);
|
||||
|
||||
/**
|
||||
* @brief LZ4 解压
|
||||
* @param input 压缩数据
|
||||
* @return 解压后的数据
|
||||
*/
|
||||
std::vector<u8> decompressLZ4(const std::vector<u8>& input);
|
||||
|
||||
/**
|
||||
* @brief Zlib 解压
|
||||
* @param input 压缩数据
|
||||
* @return 解压后的数据
|
||||
*/
|
||||
std::vector<u8> decompressZlib(const std::vector<u8>& input);
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Encryptor - 加密器
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 加密处理器
|
||||
*
|
||||
* 使用 XOR 或 AES 算法加密数据。
|
||||
*/
|
||||
class Encryptor : public DataProcessor {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造加密器
|
||||
* @param key 加密密钥
|
||||
* @param type 加密类型
|
||||
*/
|
||||
explicit Encryptor(const std::string& key, Decryptor::Type type = Decryptor::Type::XOR);
|
||||
|
||||
/**
|
||||
* @brief 处理(加密)输入数据
|
||||
* @param input 原始输入数据
|
||||
* @return 加密后的数据
|
||||
*/
|
||||
std::vector<u8> process(const std::vector<u8>& input) override;
|
||||
|
||||
/**
|
||||
* @brief 获取加密类型
|
||||
* @return 加密类型
|
||||
*/
|
||||
Decryptor::Type type() const { return type_; }
|
||||
|
||||
private:
|
||||
std::string key_;
|
||||
Decryptor::Type type_;
|
||||
|
||||
/**
|
||||
* @brief XOR 加密
|
||||
* @param input 输入数据
|
||||
* @return 加密后的数据
|
||||
*/
|
||||
std::vector<u8> encryptXOR(const std::vector<u8>& input);
|
||||
|
||||
/**
|
||||
* @brief AES-256 加密
|
||||
* @param input 输入数据
|
||||
* @return 加密后的数据
|
||||
*/
|
||||
std::vector<u8> encryptAES256(const std::vector<u8>& input);
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Compressor - 压缩器
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 压缩处理器
|
||||
*
|
||||
* 支持多种压缩算法的压缩操作。
|
||||
*/
|
||||
class Compressor : public DataProcessor {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造压缩器
|
||||
* @param algo 压缩算法
|
||||
* @param level 压缩级别(1-22,仅对某些算法有效)
|
||||
*/
|
||||
explicit Compressor(Compression algo = Compression::Zstd, int level = 3);
|
||||
|
||||
/**
|
||||
* @brief 处理(压缩)输入数据
|
||||
* @param input 原始输入数据
|
||||
* @return 压缩后的数据
|
||||
*/
|
||||
std::vector<u8> process(const std::vector<u8>& input) override;
|
||||
|
||||
/**
|
||||
* @brief 获取压缩算法
|
||||
* @return 压缩算法
|
||||
*/
|
||||
Compression algorithm() const { return algo_; }
|
||||
|
||||
private:
|
||||
Compression algo_;
|
||||
int level_;
|
||||
|
||||
/**
|
||||
* @brief Zstd 压缩
|
||||
* @param input 原始数据
|
||||
* @return 压缩后的数据
|
||||
*/
|
||||
std::vector<u8> compressZstd(const std::vector<u8>& input);
|
||||
|
||||
/**
|
||||
* @brief LZ4 压缩
|
||||
* @param input 原始数据
|
||||
* @return 压缩后的数据
|
||||
*/
|
||||
std::vector<u8> compressLZ4(const std::vector<u8>& input);
|
||||
|
||||
/**
|
||||
* @brief Zlib 压缩
|
||||
* @param input 原始数据
|
||||
* @return 压缩后的数据
|
||||
*/
|
||||
std::vector<u8> compressZlib(const std::vector<u8>& input);
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DataPipe - 数据处理管道
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 数据处理管道
|
||||
*
|
||||
* 使用流式 API 构建数据处理链。
|
||||
* 支持链式调用添加多个处理器。
|
||||
*
|
||||
* @example
|
||||
* DataPipe pipe;
|
||||
* pipe.decrypt("secret-key").decompress(Compression::Zstd);
|
||||
* auto result = pipe.process(data);
|
||||
*/
|
||||
class DataPipe {
|
||||
public:
|
||||
DataPipe() = default;
|
||||
|
||||
/**
|
||||
* @brief 移动构造函数
|
||||
* @param other 其他管道
|
||||
*/
|
||||
DataPipe(DataPipe&& other) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief 移动赋值操作符
|
||||
* @param other 其他管道
|
||||
* @return 当前管道引用
|
||||
*/
|
||||
DataPipe& operator=(DataPipe&& other) noexcept = default;
|
||||
|
||||
/**
|
||||
* @brief 添加解密处理器
|
||||
* @param key 解密密钥
|
||||
* @param type 加密类型
|
||||
* @return 当前管道引用(支持链式调用)
|
||||
*/
|
||||
DataPipe& decrypt(const std::string& key, Decryptor::Type type = Decryptor::Type::XOR);
|
||||
|
||||
/**
|
||||
* @brief 添加解压处理器
|
||||
* @param algo 压缩算法
|
||||
* @return 当前管道引用(支持链式调用)
|
||||
*/
|
||||
DataPipe& decompress(Compression algo);
|
||||
|
||||
/**
|
||||
* @brief 添加加密处理器
|
||||
* @param key 加密密钥
|
||||
* @param type 加密类型
|
||||
* @return 当前管道引用(支持链式调用)
|
||||
*/
|
||||
DataPipe& encrypt(const std::string& key, Decryptor::Type type = Decryptor::Type::XOR);
|
||||
|
||||
/**
|
||||
* @brief 添加压缩处理器
|
||||
* @param algo 压缩算法
|
||||
* @param level 压缩级别
|
||||
* @return 当前管道引用(支持链式调用)
|
||||
*/
|
||||
DataPipe& compress(Compression algo, int level = 3);
|
||||
|
||||
/**
|
||||
* @brief 添加自定义处理器
|
||||
* @param processor 处理器
|
||||
* @return 当前管道引用(支持链式调用)
|
||||
*/
|
||||
DataPipe& add(Unique<DataProcessor> processor);
|
||||
|
||||
/**
|
||||
* @brief 处理数据
|
||||
* @param input 输入数据
|
||||
* @return 处理后的数据
|
||||
*/
|
||||
std::vector<u8> process(const std::vector<u8>& input);
|
||||
|
||||
/**
|
||||
* @brief 清空所有处理器
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 检查管道是否为空
|
||||
* @return 为空返回 true
|
||||
*/
|
||||
bool empty() const { return processors_.empty(); }
|
||||
|
||||
/**
|
||||
* @brief 获取处理器数量
|
||||
* @return 处理器数量
|
||||
*/
|
||||
size_t size() const { return processors_.size(); }
|
||||
|
||||
private:
|
||||
std::vector<Unique<DataProcessor>> processors_;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 工具函数
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 计算数据的 SHA-256 校验和
|
||||
* @param data 输入数据
|
||||
* @return 32字节的校验和
|
||||
*/
|
||||
std::vector<u8> computeChecksum(const std::vector<u8>& data);
|
||||
|
||||
/**
|
||||
* @brief 验证数据的 SHA-256 校验和
|
||||
* @param data 输入数据
|
||||
* @param checksum 预期的校验和
|
||||
* @return 匹配返回 true
|
||||
*/
|
||||
bool verifyChecksum(const std::vector<u8>& data, const std::vector<u8>& checksum);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class RefCounted;
|
||||
|
||||
/**
|
||||
* @brief 自动释放池
|
||||
*
|
||||
* 管理标记为 autorelease 的对象,在帧结束时自动释放
|
||||
*/
|
||||
class AutoreleasePool {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 自动释放池实例
|
||||
*/
|
||||
static AutoreleasePool* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 添加对象到自动释放池
|
||||
* @param object 要添加的对象
|
||||
*/
|
||||
void addObject(RefCounted* object);
|
||||
|
||||
/**
|
||||
* @brief 清空自动释放池
|
||||
*
|
||||
* 释放池中所有对象的引用
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取池中对象数量
|
||||
* @return 对象数量
|
||||
*/
|
||||
size_t getObjectCount() const;
|
||||
|
||||
private:
|
||||
AutoreleasePool();
|
||||
~AutoreleasePool();
|
||||
|
||||
AutoreleasePool(const AutoreleasePool&) = delete;
|
||||
AutoreleasePool& operator=(const AutoreleasePool&) = delete;
|
||||
|
||||
static AutoreleasePool* instance_;
|
||||
std::vector<RefCounted*> objects_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <atomic>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 引用计数基类
|
||||
*
|
||||
* 提供自动内存管理功能,通过引用计数跟踪对象生命周期。
|
||||
* 当引用计数降为 0 时,对象自动销毁。
|
||||
*/
|
||||
class RefCounted {
|
||||
public:
|
||||
RefCounted();
|
||||
virtual ~RefCounted();
|
||||
|
||||
/**
|
||||
* @brief 增加引用计数
|
||||
*/
|
||||
void addRef();
|
||||
|
||||
/**
|
||||
* @brief 减少引用计数
|
||||
* @return 如果引用计数降为 0 返回 true
|
||||
*/
|
||||
bool release();
|
||||
|
||||
/**
|
||||
* @brief 获取当前引用计数
|
||||
* @return 引用计数值
|
||||
*/
|
||||
u32 getRefCount() const;
|
||||
|
||||
/**
|
||||
* @brief 自动释放管理
|
||||
*
|
||||
* 标记对象为自动释放状态,在当前帧结束时自动释放
|
||||
*/
|
||||
void autorelease();
|
||||
|
||||
/**
|
||||
* @brief 检查是否为自动释放状态
|
||||
* @return 如果标记为自动释放返回 true
|
||||
*/
|
||||
bool isAutorelease() const;
|
||||
|
||||
protected:
|
||||
std::atomic<u32> refCount_{1};
|
||||
bool autorelease_{false};
|
||||
|
||||
private:
|
||||
RefCounted(const RefCounted&) = delete;
|
||||
RefCounted& operator=(const RefCounted&) = delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 引用计数智能指针
|
||||
*
|
||||
* 自动管理 RefCounted 对象的生命周期
|
||||
*/
|
||||
template <typename T>
|
||||
class IntrusivePtr {
|
||||
public:
|
||||
IntrusivePtr() : ptr_(nullptr) {}
|
||||
|
||||
IntrusivePtr(T* ptr) : ptr_(ptr) {
|
||||
if (ptr_) {
|
||||
ptr_->addRef();
|
||||
}
|
||||
}
|
||||
|
||||
IntrusivePtr(const IntrusivePtr& other) : ptr_(other.ptr_) {
|
||||
if (ptr_) {
|
||||
ptr_->addRef();
|
||||
}
|
||||
}
|
||||
|
||||
IntrusivePtr(IntrusivePtr&& other) noexcept : ptr_(other.ptr_) {
|
||||
other.ptr_ = nullptr;
|
||||
}
|
||||
|
||||
~IntrusivePtr() {
|
||||
if (ptr_) {
|
||||
ptr_->release();
|
||||
}
|
||||
}
|
||||
|
||||
IntrusivePtr& operator=(const IntrusivePtr& other) {
|
||||
if (this != &other) {
|
||||
if (ptr_) {
|
||||
ptr_->release();
|
||||
}
|
||||
ptr_ = other.ptr_;
|
||||
if (ptr_) {
|
||||
ptr_->addRef();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
IntrusivePtr& operator=(IntrusivePtr&& other) noexcept {
|
||||
if (this != &other) {
|
||||
if (ptr_) {
|
||||
ptr_->release();
|
||||
}
|
||||
ptr_ = other.ptr_;
|
||||
other.ptr_ = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
IntrusivePtr& operator=(T* ptr) {
|
||||
if (ptr_ != ptr) {
|
||||
if (ptr_) {
|
||||
ptr_->release();
|
||||
}
|
||||
ptr_ = ptr;
|
||||
if (ptr_) {
|
||||
ptr_->addRef();
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
T* get() const { return ptr_; }
|
||||
T* operator->() const { return ptr_; }
|
||||
T& operator*() const { return *ptr_; }
|
||||
explicit operator bool() const { return ptr_ != nullptr; }
|
||||
bool operator!() const { return ptr_ == nullptr; }
|
||||
|
||||
bool operator==(const IntrusivePtr& other) const { return ptr_ == other.ptr_; }
|
||||
bool operator!=(const IntrusivePtr& other) const { return ptr_ != other.ptr_; }
|
||||
bool operator==(T* ptr) const { return ptr_ == ptr; }
|
||||
bool operator!=(T* ptr) const { return ptr_ != ptr; }
|
||||
|
||||
void reset() {
|
||||
if (ptr_) {
|
||||
ptr_->release();
|
||||
ptr_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void swap(IntrusivePtr& other) noexcept {
|
||||
T* tmp = ptr_;
|
||||
ptr_ = other.ptr_;
|
||||
other.ptr_ = tmp;
|
||||
}
|
||||
|
||||
private:
|
||||
T* ptr_;
|
||||
};
|
||||
|
||||
template <typename T, typename... Args>
|
||||
IntrusivePtr<T> makeIntrusive(Args&&... args) {
|
||||
return IntrusivePtr<T>(new T(std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,162 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
using SchedulerCallback = std::function<void(f32)>;
|
||||
|
||||
/**
|
||||
* @brief 定时器结构
|
||||
*/
|
||||
struct Timer {
|
||||
SchedulerCallback callback;
|
||||
f32 interval{0.0f};
|
||||
f32 delay{0.0f};
|
||||
u32 repeat{0};
|
||||
u32 elapsed{0};
|
||||
bool paused{false};
|
||||
std::string key;
|
||||
void* target{nullptr};
|
||||
f32 accumulator{0.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 调度器
|
||||
*
|
||||
* 管理定时任务和回调调度。
|
||||
* 参考 Cocos Creator 的 Scheduler 设计。
|
||||
*/
|
||||
class Scheduler {
|
||||
public:
|
||||
Scheduler();
|
||||
~Scheduler();
|
||||
|
||||
/**
|
||||
* @brief 每帧更新
|
||||
* @param dt 帧间隔时间
|
||||
*/
|
||||
void update(f32 dt);
|
||||
|
||||
/**
|
||||
* @brief 调度定时回调
|
||||
* @param callback 回调函数
|
||||
* @param target 目标对象指针
|
||||
* @param interval 间隔时间(秒)
|
||||
* @param repeat 重复次数(0 = 无限)
|
||||
* @param delay 延迟时间(秒)
|
||||
* @param paused 是否暂停
|
||||
* @param key 标识键
|
||||
*/
|
||||
void schedule(const SchedulerCallback& callback, void* target,
|
||||
f32 interval, u32 repeat, f32 delay,
|
||||
bool paused, const std::string& key);
|
||||
|
||||
/**
|
||||
* @brief 调度定时回调(简化版)
|
||||
* @param callback 回调函数
|
||||
* @param target 目标对象指针
|
||||
* @param interval 间隔时间(秒)
|
||||
* @param paused 是否暂停
|
||||
* @param key 标识键
|
||||
*/
|
||||
void schedule(const SchedulerCallback& callback, void* target,
|
||||
f32 interval, bool paused, const std::string& key);
|
||||
|
||||
/**
|
||||
* @brief 调度单次回调
|
||||
* @param callback 回调函数
|
||||
* @param target 目标对象指针
|
||||
* @param delay 延迟时间(秒)
|
||||
* @param key 标识键
|
||||
*/
|
||||
void scheduleOnce(const SchedulerCallback& callback, void* target,
|
||||
f32 delay, const std::string& key);
|
||||
|
||||
/**
|
||||
* @brief 取消调度
|
||||
* @param key 标识键
|
||||
* @param target 目标对象指针
|
||||
*/
|
||||
void unschedule(const std::string& key, void* target);
|
||||
|
||||
/**
|
||||
* @brief 取消目标对象的所有调度
|
||||
* @param target 目标对象指针
|
||||
*/
|
||||
void unscheduleAllForTarget(void* target);
|
||||
|
||||
/**
|
||||
* @brief 取消所有调度
|
||||
*/
|
||||
void unscheduleAll();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已调度
|
||||
* @param key 标识键
|
||||
* @param target 目标对象指针
|
||||
* @return 如果已调度返回 true
|
||||
*/
|
||||
bool isScheduled(const std::string& key, void* target);
|
||||
|
||||
/**
|
||||
* @brief 暂停目标对象的所有调度
|
||||
* @param target 目标对象指针
|
||||
*/
|
||||
void pauseTarget(void* target);
|
||||
|
||||
/**
|
||||
* @brief 恢复目标对象的所有调度
|
||||
* @param target 目标对象指针
|
||||
*/
|
||||
void resumeTarget(void* target);
|
||||
|
||||
/**
|
||||
* @brief 检查目标对象是否暂停
|
||||
* @param target 目标对象指针
|
||||
* @return 如果暂停返回 true
|
||||
*/
|
||||
bool isTargetPaused(void* target);
|
||||
|
||||
/**
|
||||
* @brief 在主线程执行函数
|
||||
* @param func 要执行的函数
|
||||
*/
|
||||
void performFunctionInMainThread(const std::function<void()>& func);
|
||||
|
||||
/**
|
||||
* @brief 执行所有待执行的主线程函数
|
||||
*/
|
||||
void runFunctionsToBePerformed();
|
||||
|
||||
/**
|
||||
* @brief 获取时间缩放因子
|
||||
* @return 时间缩放因子
|
||||
*/
|
||||
f32 getTimeScale() const;
|
||||
|
||||
/**
|
||||
* @brief 设置时间缩放因子
|
||||
* @param scale 时间缩放因子
|
||||
*/
|
||||
void setTimeScale(f32 scale);
|
||||
|
||||
private:
|
||||
struct TargetEntry {
|
||||
std::vector<Timer*> timers;
|
||||
bool paused{false};
|
||||
};
|
||||
|
||||
std::unordered_map<void*, TargetEntry> targets_;
|
||||
std::vector<std::function<void()>> functionsToPerform_;
|
||||
std::mutex performMutex_;
|
||||
f32 timeScale_{1.0f};
|
||||
bool updateLocked_{false};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/base/RefCounted.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 对象标志位
|
||||
*/
|
||||
enum class ObjectFlags : u32 {
|
||||
None = 0,
|
||||
Destroyed = 1 << 0,
|
||||
DontDestroy = 1 << 1,
|
||||
Dirty = 1 << 2,
|
||||
HideInHierarchy = 1 << 3,
|
||||
EditorOnly = 1 << 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 实现枚举标志位的位运算
|
||||
*/
|
||||
inline ObjectFlags operator|(ObjectFlags a, ObjectFlags b) {
|
||||
return static_cast<ObjectFlags>(static_cast<u32>(a) | static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline ObjectFlags operator&(ObjectFlags a, ObjectFlags b) {
|
||||
return static_cast<ObjectFlags>(static_cast<u32>(a) & static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline ObjectFlags operator~(ObjectFlags a) {
|
||||
return static_cast<ObjectFlags>(~static_cast<u32>(a));
|
||||
}
|
||||
|
||||
inline ObjectFlags& operator|=(ObjectFlags& a, ObjectFlags b) {
|
||||
a = a | b;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline ObjectFlags& operator&=(ObjectFlags& a, ObjectFlags b) {
|
||||
a = a & b;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline bool hasFlag(ObjectFlags flags, ObjectFlags flag) {
|
||||
return (static_cast<u32>(flags) & static_cast<u32>(flag)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 对象基类
|
||||
*
|
||||
* 所有引擎对象的基类,提供名称管理、生命周期控制和标志位管理。
|
||||
* 参考 Cocos Creator 的 CCObject 设计。
|
||||
*/
|
||||
class Object : public RefCounted {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param name 对象名称
|
||||
*/
|
||||
explicit Object(const std::string& name = "");
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Object() override;
|
||||
|
||||
/**
|
||||
* @brief 获取对象名称
|
||||
* @return 对象名称
|
||||
*/
|
||||
const std::string& getName() const;
|
||||
|
||||
/**
|
||||
* @brief 设置对象名称
|
||||
* @param name 新名称
|
||||
*/
|
||||
void setName(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 销毁对象
|
||||
* @return 销毁成功返回 true
|
||||
*/
|
||||
virtual bool destroy();
|
||||
|
||||
/**
|
||||
* @brief 检查对象是否有效
|
||||
* @return 如果对象未被销毁返回 true
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/**
|
||||
* @brief 检查对象是否已销毁
|
||||
* @return 如果对象已销毁返回 true
|
||||
*/
|
||||
bool isDestroyed() const;
|
||||
|
||||
/**
|
||||
* @brief 获取对象标志位
|
||||
* @return 标志位
|
||||
*/
|
||||
ObjectFlags getFlags() const;
|
||||
|
||||
/**
|
||||
* @brief 设置对象标志位
|
||||
* @param flags 标志位
|
||||
*/
|
||||
void setFlags(ObjectFlags flags);
|
||||
|
||||
/**
|
||||
* @brief 添加标志位
|
||||
* @param flag 要添加的标志
|
||||
*/
|
||||
void addFlag(ObjectFlags flag);
|
||||
|
||||
/**
|
||||
* @brief 移除标志位
|
||||
* @param flag 要移除的标志
|
||||
*/
|
||||
void removeFlag(ObjectFlags flag);
|
||||
|
||||
/**
|
||||
* @brief 检查是否有指定标志位
|
||||
* @param flag 要检查的标志
|
||||
* @return 如果有该标志返回 true
|
||||
*/
|
||||
bool hasFlag(ObjectFlags flag) const;
|
||||
|
||||
/**
|
||||
* @brief 获取对象 ID
|
||||
* @return 对象唯一 ID
|
||||
*/
|
||||
u64 getObjectId() const;
|
||||
|
||||
/**
|
||||
* @brief 转换为字符串表示
|
||||
* @return 对象的字符串表示
|
||||
*/
|
||||
virtual std::string toString() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 销毁时的回调
|
||||
*
|
||||
* 子类可重写此方法进行清理工作
|
||||
*/
|
||||
virtual void onDestroy();
|
||||
|
||||
std::string name_;
|
||||
ObjectFlags flags_{ObjectFlags::None};
|
||||
u64 objectId_{0};
|
||||
|
||||
private:
|
||||
static u64 nextObjectId_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,257 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/Object.h>
|
||||
#include <extra2d/core/event/EventTarget.h>
|
||||
#include <extra2d/core/scene-graph/RenderScene.h>
|
||||
#include <extra2d/core/scene-graph/Camera.h>
|
||||
#include <extra2d/core/scene-graph/Model.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace gfx {
|
||||
class Device;
|
||||
class Swapchain;
|
||||
}
|
||||
|
||||
class Batcher2d;
|
||||
class RenderWindow;
|
||||
|
||||
/**
|
||||
* @brief Root 事件类型
|
||||
*/
|
||||
struct RootBeforeCommitEvent {};
|
||||
struct RootBeforeRenderEvent {};
|
||||
struct RootAfterRenderEvent {};
|
||||
struct RootAfterPresentEvent {};
|
||||
struct RootPipelineChangedEvent {};
|
||||
|
||||
/**
|
||||
* @brief 引擎根对象
|
||||
*
|
||||
* 管理渲染管线、窗口、场景等核心资源。
|
||||
* 参考 Cocos Creator 的 Root 设计。
|
||||
*/
|
||||
class Root : public Object {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return Root 实例
|
||||
*/
|
||||
static Root* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param device GFX 设备
|
||||
*/
|
||||
explicit Root(gfx::Device* device);
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Root() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化 Root
|
||||
* @param swapchain 交换链
|
||||
*/
|
||||
void initialize(gfx::Swapchain* swapchain);
|
||||
|
||||
/**
|
||||
* @brief 销毁 Root
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 重置大小
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
* @param windowId 窗口 ID
|
||||
*/
|
||||
void resize(u32 width, u32 height, u32 windowId = 0);
|
||||
|
||||
/**
|
||||
* @brief 全局管线状态改变回调
|
||||
*/
|
||||
void onGlobalPipelineStateChanged();
|
||||
|
||||
/**
|
||||
* @brief 重置累计时间
|
||||
*/
|
||||
void resetCumulativeTime();
|
||||
|
||||
/**
|
||||
* @brief 每帧执行函数
|
||||
* @param deltaTime 帧间隔时间
|
||||
* @param totalFrames 总帧数
|
||||
*/
|
||||
void frameMove(float deltaTime, i32 totalFrames);
|
||||
|
||||
// ==================== 场景管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 创建渲染场景
|
||||
* @param info 场景信息
|
||||
* @return 渲染场景
|
||||
*/
|
||||
RenderScene* createScene(const RenderSceneInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 销毁渲染场景
|
||||
* @param scene 渲染场景
|
||||
*/
|
||||
void destroyScene(RenderScene* scene);
|
||||
|
||||
/**
|
||||
* @brief 销毁所有场景
|
||||
*/
|
||||
void destroyScenes();
|
||||
|
||||
/**
|
||||
* @brief 获取所有场景
|
||||
* @return 场景列表
|
||||
*/
|
||||
const std::vector<Ref<RenderScene>>& getScenes() const;
|
||||
|
||||
// ==================== 模型管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 创建模型
|
||||
* @return 模型指针
|
||||
*/
|
||||
template<typename T>
|
||||
T* createModel() {
|
||||
T* model = new T();
|
||||
model->initialize(ModelInfo{});
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 销毁模型
|
||||
* @param model 模型
|
||||
*/
|
||||
void destroyModel(Model* model);
|
||||
|
||||
// ==================== 相机管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 创建相机
|
||||
* @return 相机指针
|
||||
*/
|
||||
Camera* createCamera() const;
|
||||
|
||||
// ==================== 属性访问器 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取 GFX 设备
|
||||
* @return 设备指针
|
||||
*/
|
||||
gfx::Device* getDevice() const;
|
||||
|
||||
/**
|
||||
* @brief 设置 GFX 设备
|
||||
* @param device 设备指针
|
||||
*/
|
||||
void setDevice(gfx::Device* device);
|
||||
|
||||
/**
|
||||
* @brief 获取主窗口
|
||||
* @return 主窗口指针
|
||||
*/
|
||||
RenderWindow* getMainWindow() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 2D 批处理器
|
||||
* @return 批处理器指针
|
||||
*/
|
||||
Batcher2d* getBatcher2D() const;
|
||||
|
||||
/**
|
||||
* @brief 获取累计时间
|
||||
* @return 累计时间(秒)
|
||||
*/
|
||||
float getCumulativeTime() const;
|
||||
|
||||
/**
|
||||
* @brief 获取帧时间
|
||||
* @return 帧时间(秒)
|
||||
*/
|
||||
float getFrameTime() const;
|
||||
|
||||
/**
|
||||
* @brief 获取帧计数
|
||||
* @return 帧计数
|
||||
*/
|
||||
u32 getFrameCount() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 FPS
|
||||
* @return 每秒帧率
|
||||
*/
|
||||
u32 getFps() const;
|
||||
|
||||
/**
|
||||
* @brief 设置固定 FPS
|
||||
* @param fps 固定帧率
|
||||
*/
|
||||
void setFixedFPS(u32 fps);
|
||||
|
||||
/**
|
||||
* @brief 获取固定 FPS
|
||||
* @return 固定帧率
|
||||
*/
|
||||
u32 getFixedFPS() const;
|
||||
|
||||
// ==================== 事件系统 ====================
|
||||
|
||||
/**
|
||||
* @brief 订阅事件
|
||||
*/
|
||||
template<typename EventT, typename Fn>
|
||||
EventListenerID on(Fn&& callback) {
|
||||
return eventTarget_.on<EventT>(std::forward<Fn>(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 取消订阅事件
|
||||
*/
|
||||
template<typename EventT>
|
||||
void off(EventListenerID id) {
|
||||
eventTarget_.off<EventT>(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发布事件
|
||||
*/
|
||||
template<typename EventT>
|
||||
void emit(const EventT& event) {
|
||||
eventTarget_.emit(event);
|
||||
}
|
||||
|
||||
private:
|
||||
void frameMoveBegin();
|
||||
void frameMoveProcess(bool isNeedUpdateScene, i32 totalFrames);
|
||||
void frameMoveEnd();
|
||||
|
||||
static Root* s_instance;
|
||||
|
||||
gfx::Device* device_{nullptr};
|
||||
gfx::Swapchain* swapchain_{nullptr};
|
||||
Batcher2d* batcher_{nullptr};
|
||||
|
||||
std::vector<Ref<RenderScene>> scenes_;
|
||||
RenderWindow* mainWindow_{nullptr};
|
||||
|
||||
float cumulativeTime_{0.0f};
|
||||
float frameTime_{0.0f};
|
||||
float fpsTime_{0.0f};
|
||||
u32 frameCount_{0};
|
||||
u32 fps_{0};
|
||||
u32 fixedFPS_{0};
|
||||
|
||||
EventTarget eventTarget_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/Object.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 资源基类
|
||||
*
|
||||
* 所有资源类型的基类,提供资源标识、引用计数和生命周期管理。
|
||||
* 参考 Cocos Creator 的 Asset 设计。
|
||||
*/
|
||||
class Asset : public Object {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param name 资源名称
|
||||
*/
|
||||
explicit Asset(const std::string& name = "");
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Asset() override;
|
||||
|
||||
// ==================== 资源标识 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取资源 UUID
|
||||
* @return UUID 字符串
|
||||
*/
|
||||
const std::string& getUuid() const;
|
||||
|
||||
/**
|
||||
* @brief 设置资源 UUID
|
||||
* @param uuid UUID 字符串
|
||||
*/
|
||||
void setUuid(const std::string& uuid);
|
||||
|
||||
/**
|
||||
* @brief 获取原生资源 URL
|
||||
* @return URL 字符串
|
||||
*/
|
||||
const std::string& getNativeUrl() const;
|
||||
|
||||
/**
|
||||
* @brief 设置原生资源 URL
|
||||
* @param url URL 字符串
|
||||
*/
|
||||
void setNativeUrl(const std::string& url);
|
||||
|
||||
// ==================== 引用计数 ====================
|
||||
|
||||
/**
|
||||
* @brief 增加资源引用计数
|
||||
*/
|
||||
void addAssetRef();
|
||||
|
||||
/**
|
||||
* @brief 减少资源引用计数
|
||||
* @param autoRelease 是否自动释放
|
||||
*/
|
||||
void decAssetRef(bool autoRelease = true);
|
||||
|
||||
/**
|
||||
* @brief 获取资源引用计数
|
||||
* @return 引用计数值
|
||||
*/
|
||||
u32 getAssetRefCount() const;
|
||||
|
||||
// ==================== 加载状态 ====================
|
||||
|
||||
/**
|
||||
* @brief 检查是否已加载
|
||||
* @return 如果已加载返回 true
|
||||
*/
|
||||
bool isLoaded() const;
|
||||
|
||||
/**
|
||||
* @brief 设置加载状态
|
||||
* @param loaded 是否已加载
|
||||
*/
|
||||
void setLoaded(bool loaded);
|
||||
|
||||
/**
|
||||
* @brief 加载完成回调
|
||||
*/
|
||||
virtual void onLoaded();
|
||||
|
||||
// ==================== 默认资源 ====================
|
||||
|
||||
/**
|
||||
* @brief 检查是否为默认资源
|
||||
* @return 如果是默认资源返回 true
|
||||
*/
|
||||
bool isDefault() const;
|
||||
|
||||
/**
|
||||
* @brief 初始化默认资源
|
||||
* @param uuid 可选的 UUID
|
||||
*/
|
||||
virtual void initDefault(const std::string& uuid = "");
|
||||
|
||||
/**
|
||||
* @brief 验证资源有效性
|
||||
* @return 如果资源有效返回 true
|
||||
*/
|
||||
virtual bool validate() const;
|
||||
|
||||
// ==================== 销毁 ====================
|
||||
|
||||
/**
|
||||
* @brief 销毁资源
|
||||
* @return 销毁成功返回 true
|
||||
*/
|
||||
bool destroy() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 销毁时的回调
|
||||
*/
|
||||
void onDestroy() override;
|
||||
|
||||
std::string uuid_;
|
||||
std::string nativeUrl_;
|
||||
u32 assetRefCount_{0};
|
||||
bool loaded_{false};
|
||||
bool isDefault_{false};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/assets/Asset.h>
|
||||
#include <extra2d/core/assets/Texture2D.h>
|
||||
#include <extra2d/core/assets/ImageAsset.h>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 资源加载状态
|
||||
*/
|
||||
enum class AssetLoadState {
|
||||
NotLoaded,
|
||||
Loading,
|
||||
Loaded,
|
||||
Failed,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 资源加载进度回调
|
||||
*/
|
||||
using AssetLoadProgress = std::function<void(float)>;
|
||||
using AssetLoadComplete = std::function<void(Asset*)>;
|
||||
using AssetLoadError = std::function<void(const std::string&)>;
|
||||
|
||||
/**
|
||||
* @brief 资源加载选项
|
||||
*/
|
||||
struct AssetLoadOptions {
|
||||
bool async{false};
|
||||
bool cache{true};
|
||||
bool preload{false};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 资源管理器
|
||||
*
|
||||
* 管理所有资源的加载、缓存和生命周期。
|
||||
*/
|
||||
class AssetManager {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 资源管理器实例
|
||||
*/
|
||||
static AssetManager* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化资源管理器
|
||||
* @param device GFX 设备
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(gfx::Device* device);
|
||||
|
||||
/**
|
||||
* @brief 销毁资源管理器
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
// ==================== 资源加载 ====================
|
||||
|
||||
/**
|
||||
* @brief 加载资源
|
||||
* @param path 资源路径
|
||||
* @param type 资源类型
|
||||
* @param options 加载选项
|
||||
* @return 资源指针
|
||||
*/
|
||||
Asset* load(const std::string& path, const std::string& type = "",
|
||||
const AssetLoadOptions& options = AssetLoadOptions{});
|
||||
|
||||
/**
|
||||
* @brief 异步加载资源
|
||||
* @param path 资源路径
|
||||
* @param type 资源类型
|
||||
* @param progress 进度回调
|
||||
* @param complete 完成回调
|
||||
* @param error 错误回调
|
||||
* @param options 加载选项
|
||||
*/
|
||||
void loadAsync(const std::string& path, const std::string& type,
|
||||
AssetLoadProgress progress, AssetLoadComplete complete,
|
||||
AssetLoadError error,
|
||||
const AssetLoadOptions& options = AssetLoadOptions{});
|
||||
|
||||
/**
|
||||
* @brief 从缓存获取资源
|
||||
* @param path 资源路径
|
||||
* @return 资源指针, */
|
||||
Asset* getAsset(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 从缓存获取资源(类型化)
|
||||
* @tparam T 资源类型
|
||||
* @param path 资源路径
|
||||
* @return 资源指针
|
||||
*/
|
||||
template<typename T>
|
||||
T* getAsset(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 从 UUID 获取资源
|
||||
* @param uuid 资源 UUID
|
||||
* @return 资源指针
|
||||
*/
|
||||
Asset* getAssetByUuid(const std::string& uuid);
|
||||
|
||||
/**
|
||||
* @brief 释放资源
|
||||
* @param path 资源路径
|
||||
*/
|
||||
void releaseAsset(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 释放所有资源
|
||||
*/
|
||||
void releaseAll();
|
||||
|
||||
// ==================== 资源缓存 ====================
|
||||
|
||||
/**
|
||||
* @brief 添加资源到缓存
|
||||
* @param path 资源路径
|
||||
* @param asset 资源指针
|
||||
*/
|
||||
void addAsset(const std::string& path, Asset* asset);
|
||||
|
||||
/**
|
||||
* @brief 移除资源从缓存
|
||||
* @param path 资源路径
|
||||
*/
|
||||
void removeAsset(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 检查资源是否存在
|
||||
* @param path 资源路径
|
||||
* @return 如果存在返回 true
|
||||
*/
|
||||
bool hasAsset(const std::string& path);
|
||||
|
||||
/**
|
||||
* @brief 获取缓存资源数量
|
||||
* @return 资源数量
|
||||
*/
|
||||
size_t getAssetCount() const;
|
||||
|
||||
// ==================== 默认资源 ====================
|
||||
|
||||
/**
|
||||
* @brief 设置默认纹理
|
||||
* @param name 名称
|
||||
* @param texture 纹理资源
|
||||
*/
|
||||
void setDefaultTexture(const std::string& name, Texture2D* texture);
|
||||
|
||||
/**
|
||||
* @brief 获取默认纹理
|
||||
* @param name 名称
|
||||
* @return 纹理资源
|
||||
*/
|
||||
Texture2D* getDefaultTexture(const std::string& name);
|
||||
|
||||
private:
|
||||
AssetManager();
|
||||
~AssetManager();
|
||||
|
||||
AssetManager(const AssetManager&) = delete;
|
||||
AssetManager& operator=(const AssetManager&) = delete;
|
||||
|
||||
void initDefaultAssets();
|
||||
|
||||
gfx::Device* device_{nullptr};
|
||||
std::unordered_map<std::string, Ref<Asset>> assets_;
|
||||
std::unordered_map<std::string, Ref<Texture2D>> defaultTextures_;
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,173 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/assets/Asset.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 像素格式
|
||||
*/
|
||||
enum class PixelFormat : u32 {
|
||||
Unknown,
|
||||
RGB565,
|
||||
RGB5A1,
|
||||
RGBA4444,
|
||||
RGB888,
|
||||
RGBA8888,
|
||||
RGB32F,
|
||||
RGBA32F,
|
||||
A8,
|
||||
I8,
|
||||
AI8,
|
||||
R8,
|
||||
RG8,
|
||||
R16F,
|
||||
RG16F,
|
||||
RGBA16F,
|
||||
R32F,
|
||||
RG32F,
|
||||
RGBA32F,
|
||||
Depth,
|
||||
DepthStencil,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 图像资源类
|
||||
*
|
||||
* 表示内存中的图像数据,继承自 Asset。
|
||||
*/
|
||||
class ImageAsset : public Asset {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
ImageAsset();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~ImageAsset() override;
|
||||
|
||||
// ==================== 数据访问 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取图像数据指针
|
||||
* @return 数据指针
|
||||
*/
|
||||
const u8* getData() const;
|
||||
|
||||
/**
|
||||
* @brief 获取数据大小
|
||||
* @return 数据大小(字节)
|
||||
*/
|
||||
u32 getDataSize() const;
|
||||
|
||||
/**
|
||||
* @brief 设置图像数据
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
* @param ownsData 是否拥有数据
|
||||
*/
|
||||
void setData(u8* data, u32 size, bool ownsData = true);
|
||||
|
||||
// ==================== 图像属性 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取图像宽度
|
||||
* @return 宽度(像素)
|
||||
*/
|
||||
u32 getWidth() const;
|
||||
|
||||
/**
|
||||
* @brief 设置图像宽度
|
||||
* @param width 宽度
|
||||
*/
|
||||
void setWidth(u32 width);
|
||||
|
||||
/**
|
||||
* @brief 获取图像高度
|
||||
* @return 高度(像素)
|
||||
*/
|
||||
u32 getHeight() const;
|
||||
|
||||
/**
|
||||
* @brief 设置图像高度
|
||||
* @param height 高度
|
||||
*/
|
||||
void setHeight(u32 height);
|
||||
|
||||
/**
|
||||
* @brief 获取像素格式
|
||||
* @return 像素格式
|
||||
*/
|
||||
PixelFormat getFormat() const;
|
||||
|
||||
/**
|
||||
* @brief 设置像素格式
|
||||
* @param format 像素格式
|
||||
*/
|
||||
void setFormat(PixelFormat format);
|
||||
|
||||
/**
|
||||
* @brief 检查是否为压缩格式
|
||||
* @return 如果是压缩格式返回 true
|
||||
*/
|
||||
bool isCompressed() const;
|
||||
|
||||
/**
|
||||
* @brief 设置是否压缩
|
||||
* @param compressed 是否压缩
|
||||
*/
|
||||
void setCompressed(bool compressed);
|
||||
|
||||
// ==================== Mipmap 信息 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取 Mipmap 层级数据大小列表
|
||||
* @return 大小列表
|
||||
*/
|
||||
const std::vector<u32>& getMipmapLevelDataSize() const;
|
||||
|
||||
/**
|
||||
* @brief 设置 Mipmap 层级数据大小列表
|
||||
* @param sizes 大小列表
|
||||
*/
|
||||
void setMipmapLevelDataSize(const std::vector<u32>& sizes);
|
||||
|
||||
// ==================== 工具方法 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取每像素字节数
|
||||
* @return 字节数
|
||||
*/
|
||||
u32 getBytesPerPixel() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 GFX 格式
|
||||
* @return GFX 格式
|
||||
*/
|
||||
gfx::Format getGFXFormat() const;
|
||||
|
||||
/**
|
||||
* @brief 验证资源有效性
|
||||
* @return 如果资源有效返回 true
|
||||
*/
|
||||
bool validate() const override;
|
||||
|
||||
protected:
|
||||
void onDestroy() override;
|
||||
|
||||
private:
|
||||
u8* data_{nullptr};
|
||||
u32 dataSize_{0};
|
||||
u32 width_{0};
|
||||
u32 height_{0};
|
||||
PixelFormat format_{PixelFormat::RGBA8888};
|
||||
bool compressed_{false};
|
||||
bool ownsData_{false};
|
||||
std::vector<u32> mipmapLevelDataSize_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,384 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/assets/Asset.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace gfx {
|
||||
class Shader;
|
||||
class PipelineState;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 宏定义记录
|
||||
*/
|
||||
using MacroRecord = std::unordered_map<std::string, i32>;
|
||||
|
||||
/**
|
||||
* @brief 材质属性变体类型
|
||||
*/
|
||||
using MaterialPropertyVariant = std::variant<
|
||||
std::monostate,
|
||||
float,
|
||||
i32,
|
||||
Vec2,
|
||||
Vec3,
|
||||
Vec4,
|
||||
Color,
|
||||
std::vector<float>,
|
||||
std::vector<i32>,
|
||||
std::vector<Vec2>,
|
||||
std::vector<Vec3>,
|
||||
std::vector<Vec4>,
|
||||
std::vector<Color>>;
|
||||
|
||||
/**
|
||||
* @brief Pass 重载信息
|
||||
*/
|
||||
struct PassOverrides {
|
||||
gfx::RasterizerState rasterizerState;
|
||||
gfx::DepthStencilState depthStencilState;
|
||||
gfx::BlendState blendState;
|
||||
|
||||
void reset() {
|
||||
rasterizerState = gfx::RasterizerState{};
|
||||
depthStencilState = gfx::DepthStencilState{};
|
||||
blendState = gfx::BlendState{};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 材质初始化信息
|
||||
*/
|
||||
struct MaterialInfo {
|
||||
std::string effectName;
|
||||
u32 technique{0};
|
||||
MacroRecord defines;
|
||||
PassOverrides states;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染 Pass
|
||||
*/
|
||||
class Pass {
|
||||
public:
|
||||
Pass();
|
||||
~Pass();
|
||||
|
||||
/**
|
||||
* @brief 初始化 Pass
|
||||
* @param shader 着色器
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(gfx::Shader* shader);
|
||||
|
||||
/**
|
||||
* @brief 销毁 Pass
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 设置 uniform 值
|
||||
* @param name 名称
|
||||
* @param value 值
|
||||
*/
|
||||
void setUniform(const std::string& name, const MaterialPropertyVariant& value);
|
||||
|
||||
/**
|
||||
* @brief 获取 uniform 值
|
||||
* @param name 名称
|
||||
* @return 值
|
||||
*/
|
||||
const MaterialPropertyVariant* getUniform(const std::string& name) const;
|
||||
|
||||
/**
|
||||
* @brief 设置管线状态
|
||||
* @param state 管线状态
|
||||
*/
|
||||
void setPipelineState(gfx::PipelineState* state);
|
||||
|
||||
/**
|
||||
* @brief 获取管线状态
|
||||
* @return 管线状态
|
||||
*/
|
||||
gfx::PipelineState* getPipelineState() const;
|
||||
|
||||
/**
|
||||
* @brief 获取着色器
|
||||
* @return 着色器
|
||||
*/
|
||||
gfx::Shader* getShader() const;
|
||||
|
||||
/**
|
||||
* @brief 设置混合状态
|
||||
* @param state 混合状态
|
||||
*/
|
||||
void setBlendState(const gfx::BlendState& state);
|
||||
|
||||
/**
|
||||
* @brief 获取混合状态
|
||||
* @return 混合状态
|
||||
*/
|
||||
const gfx::BlendState& getBlendState() const;
|
||||
|
||||
/**
|
||||
* @brief 设置深度模板状态
|
||||
* @param state 深度模板状态
|
||||
*/
|
||||
void setDepthStencilState(const gfx::DepthStencilState& state);
|
||||
|
||||
/**
|
||||
* @brief 获取深度模板状态
|
||||
* @return 深度模板状态
|
||||
*/
|
||||
const gfx::DepthStencilState& getDepthStencilState() const;
|
||||
|
||||
/**
|
||||
* @brief 设置光栅化状态
|
||||
* @param state 光栅化状态
|
||||
*/
|
||||
void setRasterizerState(const gfx::RasterizerState& state);
|
||||
|
||||
/**
|
||||
* @brief 获取光栅化状态
|
||||
* @return 光栅化状态
|
||||
*/
|
||||
const gfx::RasterizerState& getRasterizerState() const;
|
||||
|
||||
/**
|
||||
* @brief 获取哈希值
|
||||
* @return 哈希值
|
||||
*/
|
||||
size_t getHash() const;
|
||||
|
||||
private:
|
||||
gfx::Shader* shader_{nullptr};
|
||||
gfx::PipelineState* pipelineState_{nullptr};
|
||||
|
||||
gfx::BlendState blendState_;
|
||||
gfx::DepthStencilState depthStencilState_;
|
||||
gfx::RasterizerState rasterizerState_;
|
||||
|
||||
std::unordered_map<std::string, MaterialPropertyVariant> uniforms_;
|
||||
size_t hash_{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 材质类
|
||||
*
|
||||
* 管理渲染材质,包含着色器和渲染状态。
|
||||
* 参考 Cocos Creator 的 Material 设计。
|
||||
*/
|
||||
class Material : public Asset {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建材质
|
||||
* @return 材质引用
|
||||
*/
|
||||
static Ref<Material> create();
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
Material();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Material() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化材质
|
||||
* @param info 材质信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(const MaterialInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 重置材质
|
||||
* @param info 材质信息
|
||||
*/
|
||||
void reset(const MaterialInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 销毁材质
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool destroy() override;
|
||||
|
||||
// ==================== 属性设置 ====================
|
||||
|
||||
/**
|
||||
* @brief 设置属性值
|
||||
* @param name 属性名
|
||||
* @param value 值
|
||||
* @param passIdx Pass 索引(默认所有 Pass)
|
||||
*/
|
||||
void setProperty(const std::string& name, const MaterialPropertyVariant& value, i32 passIdx = -1);
|
||||
|
||||
/**
|
||||
* @brief 设置浮点属性
|
||||
* @param name 属性名
|
||||
* @param value 值
|
||||
* @param passIdx Pass 索引
|
||||
*/
|
||||
void setPropertyFloat(const std::string& name, float value, i32 passIdx = -1);
|
||||
|
||||
/**
|
||||
* @brief 设置整数属性
|
||||
* @param name 属性名
|
||||
* @param value 值
|
||||
* @param passIdx Pass 索引
|
||||
*/
|
||||
void setPropertyInt(const std::string& name, i32 value, i32 passIdx = -1);
|
||||
|
||||
/**
|
||||
* @brief 设置 Vec2 属性
|
||||
* @param name 属性名
|
||||
* @param value 值
|
||||
* @param passIdx Pass 索引
|
||||
*/
|
||||
void setPropertyVec2(const std::string& name, const Vec2& value, i32 passIdx = -1);
|
||||
|
||||
/**
|
||||
* @brief 设置 Vec3 属性
|
||||
* @param name 属性名
|
||||
* @param value 值
|
||||
* @param passIdx Pass 索引
|
||||
*/
|
||||
void setPropertyVec3(const std::string& name, const Vec3& value, i32 passIdx = -1);
|
||||
|
||||
/**
|
||||
* @brief 设置 Vec4 属性
|
||||
* @param name 属性名
|
||||
* @param value 值
|
||||
* @param passIdx Pass 索引
|
||||
*/
|
||||
void setPropertyVec4(const std::string& name, const Vec4& value, i32 passIdx = -1);
|
||||
|
||||
/**
|
||||
* @brief 设置颜色属性
|
||||
* @param name 属性名
|
||||
* @param value 值
|
||||
* @param passIdx Pass 索引
|
||||
*/
|
||||
void setPropertyColor(const std::string& name, const Color& value, i32 passIdx = -1);
|
||||
|
||||
/**
|
||||
* @brief 获取属性值
|
||||
* @param name 属性名
|
||||
* @param passIdx Pass 索引
|
||||
* @return 属性值
|
||||
*/
|
||||
const MaterialPropertyVariant* getProperty(const std::string& name, i32 passIdx = -1) const;
|
||||
|
||||
// ==================== Pass 管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取 Pass 数量
|
||||
* @return Pass 数量
|
||||
*/
|
||||
size_t getPassCount() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 Pass
|
||||
* @param index 索引
|
||||
* @return Pass 指针
|
||||
*/
|
||||
Pass* getPass(size_t index);
|
||||
|
||||
/**
|
||||
* @brief 获取所有 Pass
|
||||
* @return Pass 列表
|
||||
*/
|
||||
const std::vector<Ref<Pass>>& getPasses() const;
|
||||
|
||||
/**
|
||||
* @brief 添加 Pass
|
||||
* @param pass Pass
|
||||
*/
|
||||
void addPass(Ref<Pass> pass);
|
||||
|
||||
// ==================== 状态重载 ====================
|
||||
|
||||
/**
|
||||
* @brief 重载管线状态
|
||||
* @param overrides 重载信息
|
||||
* @param passIdx Pass 索引
|
||||
*/
|
||||
void overridePipelineStates(const PassOverrides& overrides, i32 passIdx = -1);
|
||||
|
||||
/**
|
||||
* @brief 重编译着色器
|
||||
* @param defines 宏定义
|
||||
* @param passIdx Pass 索引
|
||||
*/
|
||||
void recompileShaders(const MacroRecord& defines, i32 passIdx = -1);
|
||||
|
||||
// ==================== 属性访问器 ====================
|
||||
|
||||
/**
|
||||
* @brief 设置 Effect 名称
|
||||
* @param name 名称
|
||||
*/
|
||||
void setEffectName(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 获取 Effect 名称
|
||||
* @return 名称
|
||||
*/
|
||||
const std::string& getEffectName() const;
|
||||
|
||||
/**
|
||||
* @brief 设置 Technique 索引
|
||||
* @param index 索引
|
||||
*/
|
||||
void setTechniqueIndex(u32 index);
|
||||
|
||||
/**
|
||||
* @brief 获取 Technique 索引
|
||||
* @return 索引
|
||||
*/
|
||||
u32 getTechniqueIndex() const;
|
||||
|
||||
/**
|
||||
* @brief 获取哈希值
|
||||
* @return 哈希值
|
||||
*/
|
||||
size_t getHash() const;
|
||||
|
||||
/**
|
||||
* @brief 复制材质
|
||||
* @param other 源材质
|
||||
*/
|
||||
void copy(const Material* other);
|
||||
|
||||
/**
|
||||
* @brief 重置 uniform 为默认值
|
||||
*/
|
||||
void resetUniforms();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 更新哈希值
|
||||
*/
|
||||
void updateHash();
|
||||
|
||||
private:
|
||||
std::string effectName_;
|
||||
u32 techniqueIndex_{0};
|
||||
|
||||
std::vector<Ref<Pass>> passes_;
|
||||
std::vector<MacroRecord> defines_;
|
||||
std::vector<PassOverrides> states_;
|
||||
|
||||
std::unordered_map<std::string, MaterialPropertyVariant> properties_;
|
||||
size_t hash_{0};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/assets/Asset.h>
|
||||
#include <extra2d/core/assets/ImageAsset.h>
|
||||
#include <extra2d/gfx/GFXTexture.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace gfx {
|
||||
class Device;
|
||||
class Texture;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 纹理过滤模式
|
||||
*/
|
||||
enum class Filter : u32 {
|
||||
None,
|
||||
Nearest,
|
||||
Linear,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理寻址模式
|
||||
*/
|
||||
enum class WrapMode : u32 {
|
||||
Repeat,
|
||||
ClampToEdge,
|
||||
MirroredRepeat,
|
||||
ClampToBorder,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理创建信息
|
||||
*/
|
||||
struct Texture2DCreateInfo {
|
||||
u32 width{0};
|
||||
u32 height{0};
|
||||
PixelFormat format{PixelFormat::RGBA8888};
|
||||
u32 mipmapLevel{1};
|
||||
u32 baseLevel{0};
|
||||
u32 maxLevel{1000};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 2D 纹理资源类
|
||||
*
|
||||
* 表示 GPU 纹理资源,继承自 Asset。
|
||||
*/
|
||||
class Texture2D : public Asset {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
Texture2D();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Texture2D() override;
|
||||
|
||||
// ==================== 初始化 ====================
|
||||
|
||||
/**
|
||||
* @brief 初始化纹理
|
||||
* @param device GFX 设备
|
||||
* @param info 创建信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(gfx::Device* device, const Texture2DCreateInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 重置纹理
|
||||
* @param info 创建信息
|
||||
*/
|
||||
void reset(const Texture2DCreateInfo& info);
|
||||
|
||||
// ==================== 图像数据 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取 Mipmap 图像列表
|
||||
* @return 图像列表
|
||||
*/
|
||||
const std::vector<Ref<ImageAsset>>& getMipmaps() const;
|
||||
|
||||
/**
|
||||
* @brief 设置 Mipmap 图像列表
|
||||
* @param mipmaps 图像列表
|
||||
*/
|
||||
void setMipmaps(const std::vector<Ref<ImageAsset>>& mipmaps);
|
||||
|
||||
/**
|
||||
* @brief 获取 0 级 Mipmap 图像
|
||||
* @return 图像资源
|
||||
*/
|
||||
ImageAsset* getImage() const;
|
||||
|
||||
/**
|
||||
* @brief 设置 0 级 Mipmap 图像
|
||||
* @param image 图像资源
|
||||
*/
|
||||
void setImage(ImageAsset* image);
|
||||
|
||||
// ==================== 纹理属性 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取宽度
|
||||
* @return 宽度(像素)
|
||||
*/
|
||||
u32 getWidth() const;
|
||||
|
||||
/**
|
||||
* @brief 获取高度
|
||||
* @return 高度(像素)
|
||||
*/
|
||||
u32 getHeight() const;
|
||||
|
||||
/**
|
||||
* @brief 获取像素格式
|
||||
* @return 像素格式
|
||||
*/
|
||||
PixelFormat getFormat() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 Mipmap 层级数
|
||||
* @return 层级数
|
||||
*/
|
||||
u32 getMipmapLevel() const;
|
||||
|
||||
// ==================== 采样参数 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取最小过滤模式
|
||||
* @return 过滤模式
|
||||
*/
|
||||
Filter getMinFilter() const;
|
||||
|
||||
/**
|
||||
* @brief 设置最小过滤模式
|
||||
* @param filter 过滤模式
|
||||
*/
|
||||
void setMinFilter(Filter filter);
|
||||
|
||||
/**
|
||||
* @brief 获取最大过滤模式
|
||||
* @return 过滤模式
|
||||
*/
|
||||
Filter getMagFilter() const;
|
||||
|
||||
/**
|
||||
* @brief 设置最大过滤模式
|
||||
* @param filter 过滤模式
|
||||
*/
|
||||
void setMagFilter(Filter filter);
|
||||
|
||||
/**
|
||||
* @brief 获取 S 轴寻址模式
|
||||
* @return 寻址模式
|
||||
*/
|
||||
WrapMode getWrapModeS() const;
|
||||
|
||||
/**
|
||||
* @brief 设置 S 轴寻址模式
|
||||
* @param mode 寻址模式
|
||||
*/
|
||||
void setWrapModeS(WrapMode mode);
|
||||
|
||||
/**
|
||||
* @brief 获取 T 轴寻址模式
|
||||
* @return 寻址模式
|
||||
*/
|
||||
WrapMode getWrapModeT() const;
|
||||
|
||||
/**
|
||||
* @brief 设置 T 轴寻址模式
|
||||
* @param mode 寻址模式
|
||||
*/
|
||||
void setWrapModeT(WrapMode mode);
|
||||
|
||||
// ==================== GPU 资源 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取 GFX 纹理
|
||||
* @return GFX 纹理指针
|
||||
*/
|
||||
gfx::Texture* getGFXTexture() const;
|
||||
|
||||
/**
|
||||
* @brief 更新图像数据到 GPU
|
||||
*/
|
||||
void updateImage();
|
||||
|
||||
/**
|
||||
* @brief 上传数据到 GPU
|
||||
* @param data 数据指针
|
||||
* @param level Mipmap 层级
|
||||
*/
|
||||
void uploadData(const u8* data, u32 level = 0);
|
||||
|
||||
// ==================== 生命周期 ====================
|
||||
|
||||
/**
|
||||
* @brief 加载完成回调
|
||||
*/
|
||||
void onLoaded() override;
|
||||
|
||||
/**
|
||||
* @brief 验证资源有效性
|
||||
* @return 如果资源有效返回 true
|
||||
*/
|
||||
bool validate() const override;
|
||||
|
||||
protected:
|
||||
void onDestroy() override;
|
||||
|
||||
private:
|
||||
void createGFXTexture();
|
||||
|
||||
gfx::Device* device_{nullptr};
|
||||
gfx::Texture* gfxTexture_{nullptr};
|
||||
|
||||
std::vector<Ref<ImageAsset>> mipmaps_;
|
||||
|
||||
u32 width_{0};
|
||||
u32 height_{0};
|
||||
PixelFormat format_{PixelFormat::RGBA8888};
|
||||
u32 mipmapLevel_{1};
|
||||
u32 baseLevel_{0};
|
||||
u32 maxLevel_{1000};
|
||||
|
||||
Filter minFilter_{Filter::Linear};
|
||||
Filter magFilter_{Filter::Linear};
|
||||
WrapMode wrapModeS_{WrapMode::ClampToEdge};
|
||||
WrapMode wrapModeT_{WrapMode::ClampToEdge};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,343 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <typeindex>
|
||||
#include <any>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 事件监听器 ID
|
||||
*/
|
||||
struct EventListenerID {
|
||||
u32 id{0};
|
||||
|
||||
EventListenerID() = default;
|
||||
explicit EventListenerID(u32 value) : id(value) {}
|
||||
|
||||
bool isValid() const { return id != 0; }
|
||||
void invalidate() { id = 0; }
|
||||
|
||||
bool operator==(const EventListenerID& other) const { return id == other.id; }
|
||||
bool operator!=(const EventListenerID& other) const { return id != other.id; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 键盘事件动作
|
||||
*/
|
||||
enum class KeyAction {
|
||||
Press,
|
||||
Release,
|
||||
Repeat
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 键盘事件
|
||||
*/
|
||||
struct KeyboardEvent {
|
||||
i32 key{-1};
|
||||
i32 scancode{-1};
|
||||
KeyAction action{KeyAction::Press};
|
||||
bool shift{false};
|
||||
bool ctrl{false};
|
||||
bool alt{false};
|
||||
bool gui{false};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 鼠标事件类型
|
||||
*/
|
||||
enum class MouseEventType {
|
||||
Down,
|
||||
Up,
|
||||
Move,
|
||||
Wheel
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 鼠标事件
|
||||
*/
|
||||
struct MouseEvent {
|
||||
MouseEventType type{MouseEventType::Move};
|
||||
float x{0};
|
||||
float y{0};
|
||||
float xDelta{0};
|
||||
float yDelta{0};
|
||||
u16 button{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 触摸事件类型
|
||||
*/
|
||||
enum class TouchEventType {
|
||||
Began,
|
||||
Moved,
|
||||
Ended,
|
||||
Cancelled
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 触摸点信息
|
||||
*/
|
||||
struct TouchPoint {
|
||||
float x{0};
|
||||
float y{0};
|
||||
float dx{0};
|
||||
float dy{0};
|
||||
float pressure{1.0f};
|
||||
i64 id{-1};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 触摸事件
|
||||
*/
|
||||
struct TouchEvent {
|
||||
TouchEventType type{TouchEventType::Began};
|
||||
std::vector<TouchPoint> touches;
|
||||
i64 touchDeviceId{-1};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 手势事件类型
|
||||
*/
|
||||
enum class GestureEventType {
|
||||
Pinch,
|
||||
Rotate,
|
||||
Swipe,
|
||||
Tap,
|
||||
LongPress
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 手势事件
|
||||
*/
|
||||
struct GestureEvent {
|
||||
GestureEventType type{GestureEventType::Pinch};
|
||||
float scale{1.0f};
|
||||
Vec2 center{0, 0};
|
||||
float rotation{0};
|
||||
Vec2 direction{0, 0};
|
||||
float velocity{0};
|
||||
Vec2 position{0, 0};
|
||||
i32 tapCount{1};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 手柄事件类型
|
||||
*/
|
||||
enum class ControllerEventType {
|
||||
Connected,
|
||||
Disconnected,
|
||||
ButtonDown,
|
||||
ButtonUp,
|
||||
AxisMotion
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 手柄事件
|
||||
*/
|
||||
struct ControllerEvent {
|
||||
ControllerEventType type{ControllerEventType::Connected};
|
||||
i32 controllerId{-1};
|
||||
i32 button{-1};
|
||||
i32 axis{-1};
|
||||
float value{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 窗口事件类型
|
||||
*/
|
||||
enum class WindowEventType {
|
||||
Quit,
|
||||
Resize,
|
||||
FocusGained,
|
||||
FocusLost,
|
||||
Close,
|
||||
Show,
|
||||
Hide,
|
||||
Minimize,
|
||||
Maximize,
|
||||
Restore
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 窗口事件
|
||||
*/
|
||||
struct WindowEvent {
|
||||
WindowEventType type{WindowEventType::Quit};
|
||||
i32 width{0};
|
||||
i32 height{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 应用事件类型
|
||||
*/
|
||||
enum class AppEventType {
|
||||
WillEnterBackground,
|
||||
DidEnterBackground,
|
||||
WillEnterForeground,
|
||||
DidEnterForeground,
|
||||
MemoryWarning
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 应用事件
|
||||
*/
|
||||
struct AppEvent {
|
||||
AppEventType type{AppEventType::MemoryWarning};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 事件监听器基类
|
||||
*/
|
||||
class EventListenerBase {
|
||||
public:
|
||||
virtual ~EventListenerBase() = default;
|
||||
virtual void invoke(const std::any& event) = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 类型化事件监听器
|
||||
*/
|
||||
template <typename EventT>
|
||||
class EventListener : public EventListenerBase {
|
||||
public:
|
||||
using Callback = std::function<void(const EventT&)>;
|
||||
|
||||
explicit EventListener(const Callback& callback) : callback_(callback) {}
|
||||
|
||||
void invoke(const std::any& event) override {
|
||||
try {
|
||||
callback_(std::any_cast<const EventT&>(event));
|
||||
} catch (const std::bad_any_cast&) {
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Callback callback_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 事件总线
|
||||
*
|
||||
* 全局事件分发系统,支持类型安全的事件订阅和发布。
|
||||
* 参考 Cocos Creator 的 EventBus 设计。
|
||||
*/
|
||||
class EventBus {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 事件总线实例
|
||||
*/
|
||||
static EventBus* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 订阅事件
|
||||
* @tparam EventT 事件类型
|
||||
* @param callback 回调函数
|
||||
* @return 监听器 ID,用于取消订阅
|
||||
*/
|
||||
template <typename EventT>
|
||||
EventListenerID on(std::function<void(const EventT&)> callback) {
|
||||
auto typeIndex = std::type_index(typeid(EventT));
|
||||
auto listener = new EventListener<EventT>(callback);
|
||||
|
||||
u32 id = nextId_++;
|
||||
ListenerEntry entry;
|
||||
entry.id = id;
|
||||
entry.listener = listener;
|
||||
|
||||
listeners_[typeIndex].push_back(entry);
|
||||
return EventListenerID(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 取消订阅事件
|
||||
* @tparam EventT 事件类型
|
||||
* @param listenerId 监听器 ID
|
||||
*/
|
||||
template <typename EventT>
|
||||
void off(EventListenerID listenerId) {
|
||||
if (!listenerId.isValid()) return;
|
||||
|
||||
auto typeIndex = std::type_index(typeid(EventT));
|
||||
auto it = listeners_.find(typeIndex);
|
||||
if (it == listeners_.end()) return;
|
||||
|
||||
auto& entries = it->second;
|
||||
for (auto entryIt = entries.begin(); entryIt != entries.end(); ++entryIt) {
|
||||
if (entryIt->id == listenerId.id) {
|
||||
delete entryIt->listener;
|
||||
entries.erase(entryIt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发布事件
|
||||
* @tparam EventT 事件类型
|
||||
* @param event 事件数据
|
||||
*/
|
||||
template <typename EventT>
|
||||
void emit(const EventT& event) {
|
||||
auto typeIndex = std::type_index(typeid(EventT));
|
||||
auto it = listeners_.find(typeIndex);
|
||||
if (it == listeners_.end()) return;
|
||||
|
||||
std::any eventAny = event;
|
||||
for (const auto& entry : it->second) {
|
||||
entry.listener->invoke(eventAny);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移除所有监听器
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 移除指定类型的所有监听器
|
||||
* @tparam EventT 事件类型
|
||||
*/
|
||||
template <typename EventT>
|
||||
void clear() {
|
||||
auto typeIndex = std::type_index(typeid(EventT));
|
||||
auto it = listeners_.find(typeIndex);
|
||||
if (it != listeners_.end()) {
|
||||
for (auto& entry : it->second) {
|
||||
delete entry.listener;
|
||||
}
|
||||
listeners_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
EventBus();
|
||||
~EventBus();
|
||||
|
||||
EventBus(const EventBus&) = delete;
|
||||
EventBus& operator=(const EventBus&) = delete;
|
||||
|
||||
struct ListenerEntry {
|
||||
u32 id;
|
||||
EventListenerBase* listener;
|
||||
};
|
||||
|
||||
static EventBus* instance_;
|
||||
std::unordered_map<std::type_index, std::vector<ListenerEntry>> listeners_;
|
||||
u32 nextId_{1};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 全局事件便捷访问命名空间
|
||||
*/
|
||||
namespace events {
|
||||
inline EventBus* bus() { return EventBus::getInstance(); }
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/event/EventBus.h>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <typeindex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 事件目标
|
||||
*
|
||||
* 对象级事件系统,支持对象自身的事件订阅和发布。
|
||||
* 参考 Cocos Creator 的 EventTarget 设计。
|
||||
*/
|
||||
class EventTarget {
|
||||
public:
|
||||
EventTarget();
|
||||
~EventTarget();
|
||||
|
||||
/**
|
||||
* @brief 订阅事件
|
||||
* @tparam EventT 事件类型
|
||||
* @param callback 回调函数
|
||||
* @return 监听器 ID
|
||||
*/
|
||||
template <typename EventT, typename Fn>
|
||||
EventListenerID on(Fn&& callback) {
|
||||
auto typeIndex = std::type_index(typeid(EventT));
|
||||
u32 id = nextId_++;
|
||||
|
||||
ListenerEntry entry;
|
||||
entry.id = id;
|
||||
entry.callback = [cb = std::forward<Fn>(callback)](const std::any& e) {
|
||||
try {
|
||||
cb(std::any_cast<const EventT&>(e));
|
||||
} catch (const std::bad_any_cast&) {
|
||||
}
|
||||
};
|
||||
|
||||
listeners_[typeIndex].push_back(entry);
|
||||
return EventListenerID(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 取消订阅事件
|
||||
* @tparam EventT 事件类型
|
||||
* @param listenerId 监听器 ID
|
||||
*/
|
||||
template <typename EventT>
|
||||
void off(EventListenerID listenerId) {
|
||||
if (!listenerId.isValid()) return;
|
||||
|
||||
auto typeIndex = std::type_index(typeid(EventT));
|
||||
auto it = listeners_.find(typeIndex);
|
||||
if (it == listeners_.end()) return;
|
||||
|
||||
auto& entries = it->second;
|
||||
for (auto entryIt = entries.begin(); entryIt != entries.end(); ++entryIt) {
|
||||
if (entryIt->id == listenerId.id) {
|
||||
entries.erase(entryIt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发布事件
|
||||
* @tparam EventT 事件类型
|
||||
* @param event 事件数据
|
||||
*/
|
||||
template <typename EventT>
|
||||
void emit(const EventT& event) {
|
||||
auto typeIndex = std::type_index(typeid(EventT));
|
||||
auto it = listeners_.find(typeIndex);
|
||||
if (it == listeners_.end()) return;
|
||||
|
||||
std::any eventAny = event;
|
||||
for (const auto& entry : it->second) {
|
||||
entry.callback(eventAny);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移除所有监听器
|
||||
*/
|
||||
void offAll();
|
||||
|
||||
/**
|
||||
* @brief 检查是否有指定类型的监听器
|
||||
* @tparam EventT 事件类型
|
||||
* @return 如果有监听器返回 true
|
||||
*/
|
||||
template <typename EventT>
|
||||
bool hasListener() const {
|
||||
auto typeIndex = std::type_index(typeid(EventT));
|
||||
auto it = listeners_.find(typeIndex);
|
||||
return it != listeners_.end() && !it->second.empty();
|
||||
}
|
||||
|
||||
private:
|
||||
struct ListenerEntry {
|
||||
u32 id;
|
||||
std::function<void(const std::any&)> callback;
|
||||
};
|
||||
|
||||
std::unordered_map<std::type_index, std::vector<ListenerEntry>> listeners_;
|
||||
u32 nextId_{1};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <typeindex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Application;
|
||||
|
||||
/**
|
||||
* @brief 模块基类
|
||||
* 所有模块必须继承此类
|
||||
*/
|
||||
class Module {
|
||||
public:
|
||||
virtual ~Module() = default;
|
||||
|
||||
/**
|
||||
* @brief 初始化模块
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
virtual bool init() = 0;
|
||||
|
||||
/**
|
||||
* @brief 关闭模块
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查模块是否已初始化
|
||||
* @return 已初始化返回 true
|
||||
*/
|
||||
virtual bool ok() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取模块名称
|
||||
* @return 模块名称
|
||||
*/
|
||||
virtual const char* name() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取模块优先级(数值越小越优先)
|
||||
* @return 优先级值
|
||||
*/
|
||||
virtual int priority() const { return 100; }
|
||||
|
||||
/**
|
||||
* @brief 获取模块依赖列表
|
||||
* @return 依赖模块类型列表
|
||||
*/
|
||||
virtual std::vector<std::type_index> deps() const { return {}; }
|
||||
|
||||
/**
|
||||
* @brief 检查模块是否支持并行初始化
|
||||
* @return 支持并行初始化返回 true
|
||||
*/
|
||||
virtual bool parallel() const { return true; }
|
||||
|
||||
/**
|
||||
* @brief 设置所属Application
|
||||
* @param app Application指针
|
||||
*/
|
||||
void setApp(class Application* app) { app_ = app; }
|
||||
|
||||
/**
|
||||
* @brief 获取Application
|
||||
* @return Application指针
|
||||
*/
|
||||
class Application* app() const { return app_; }
|
||||
|
||||
protected:
|
||||
class Application* app_ = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模块工厂函数类型
|
||||
*/
|
||||
using ModuleFactory = std::function<Unique<Module>()>;
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <queue>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 固定大小对象池
|
||||
* @tparam T 对象类型
|
||||
* @tparam Size 池大小
|
||||
*/
|
||||
template <typename T, size_t Size> class ObjectPool {
|
||||
public:
|
||||
ObjectPool() {
|
||||
for (size_t i = 0; i < Size; ++i) {
|
||||
available_.push(&pool_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取对象
|
||||
* @return 对象指针,池耗尽返回nullptr
|
||||
*/
|
||||
T *acquire() {
|
||||
if (available_.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
T *obj = available_.front();
|
||||
available_.pop();
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 释放对象回池
|
||||
* @param obj 对象指针
|
||||
*/
|
||||
void release(T *obj) {
|
||||
if (obj >= pool_ && obj < pool_ + Size) {
|
||||
obj->~T();
|
||||
new (obj) T();
|
||||
available_.push(obj);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取可用对象数量
|
||||
*/
|
||||
size_t available() const { return available_.size(); }
|
||||
|
||||
/**
|
||||
* @brief 获取池总大小
|
||||
*/
|
||||
static constexpr size_t capacity() { return Size; }
|
||||
|
||||
private:
|
||||
alignas(alignof(T)) std::array<u8, sizeof(T) * Size> storage_;
|
||||
T *pool_ = reinterpret_cast<T *>(storage_.data());
|
||||
std::queue<T *> available_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,170 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <array>
|
||||
#include <typeindex>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <future>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Application;
|
||||
|
||||
/**
|
||||
* @brief 编译期类型ID生成器
|
||||
*/
|
||||
using TypeId = size_t;
|
||||
|
||||
namespace detail {
|
||||
inline TypeId nextTypeId() {
|
||||
static TypeId id = 0;
|
||||
return ++id;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline TypeId getTypeId() {
|
||||
static TypeId id = nextTypeId();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 模块注册表
|
||||
* 管理模块的注册、拓扑排序和生命周期
|
||||
*/
|
||||
class Registry {
|
||||
public:
|
||||
static constexpr size_t MAX_MODULES = 64;
|
||||
|
||||
static Registry& instance();
|
||||
|
||||
Registry(const Registry&) = delete;
|
||||
Registry& operator=(const Registry&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 注册模块
|
||||
* @tparam T 模块类型
|
||||
* @tparam Args 构造函数参数类型
|
||||
* @param args 构造函数参数
|
||||
* @return 模块指针
|
||||
*/
|
||||
template<typename T, typename... Args>
|
||||
T* use(Args&&... args) {
|
||||
static_assert(std::is_base_of_v<Module, T>, "T must derive from Module");
|
||||
|
||||
TypeId typeId = detail::getTypeId<T>();
|
||||
|
||||
// 数组查找,O(n) 但 n 很小,缓存友好
|
||||
for (size_t i = 0; i < moduleCount_; ++i) {
|
||||
if (modules_[i].id == typeId) {
|
||||
return static_cast<T*>(modules_[i].module.get());
|
||||
}
|
||||
}
|
||||
|
||||
// 添加新模块
|
||||
if (moduleCount_ >= MAX_MODULES) {
|
||||
return nullptr; // 模块数量超过上限
|
||||
}
|
||||
|
||||
auto module = ptr::makeUnique<T>(std::forward<Args>(args)...);
|
||||
T* ptr = module.get();
|
||||
module->setApp(app_);
|
||||
|
||||
modules_[moduleCount_].id = typeId;
|
||||
modules_[moduleCount_].module = std::move(module);
|
||||
modules_[moduleCount_].valid = true;
|
||||
++moduleCount_;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取模块
|
||||
* @tparam T 模块类型
|
||||
* @return 模块指针,不存在返回 nullptr
|
||||
*/
|
||||
template<typename T>
|
||||
T* get() const {
|
||||
TypeId typeId = detail::getTypeId<T>();
|
||||
|
||||
for (size_t i = 0; i < moduleCount_; ++i) {
|
||||
if (modules_[i].id == typeId && modules_[i].valid) {
|
||||
return static_cast<T*>(modules_[i].module.get());
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取模块(基类版本)
|
||||
* @param typeIdx 类型索引
|
||||
* @return 模块指针
|
||||
*/
|
||||
Module* get(std::type_index typeIdx) const {
|
||||
// 这里仍然使用type_index作为后备方案
|
||||
for (size_t i = 0; i < moduleCount_; ++i) {
|
||||
if (modules_[i].valid &&
|
||||
std::type_index(typeid(*modules_[i].module)) == typeIdx) {
|
||||
return modules_[i].module.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 设置Application
|
||||
*/
|
||||
void setApp(Application* app) { app_ = app; }
|
||||
|
||||
/**
|
||||
* @brief 初始化所有模块(按优先级拓扑排序,支持并行初始化)
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief 关闭所有模块
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 清空所有模块
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取模块数量
|
||||
*/
|
||||
size_t size() const { return moduleCount_; }
|
||||
|
||||
private:
|
||||
Registry() = default;
|
||||
~Registry() = default;
|
||||
|
||||
struct ModuleEntry {
|
||||
TypeId id = 0;
|
||||
Unique<Module> module;
|
||||
bool valid = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 拓扑排序模块
|
||||
* @return 排序后的模块列表
|
||||
*/
|
||||
std::vector<Module*> sort();
|
||||
|
||||
/**
|
||||
* @brief 按层级对模块进行分组
|
||||
* 同一层级的模块没有相互依赖,可以并行初始化
|
||||
* @return 按层级分组的模块列表
|
||||
*/
|
||||
std::vector<std::vector<Module*>> group();
|
||||
|
||||
std::array<ModuleEntry, MAX_MODULES> modules_;
|
||||
size_t moduleCount_ = 0;
|
||||
Application* app_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,398 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
#include <variant>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 错误码枚举
|
||||
*/
|
||||
enum class ErrorCode {
|
||||
None = 0,
|
||||
Unknown = 1,
|
||||
InvalidArgument = 2,
|
||||
OutOfMemory = 3,
|
||||
FileNotFound = 4,
|
||||
PermissionDenied = 5,
|
||||
NotImplemented = 6,
|
||||
AlreadyExists = 7,
|
||||
NotInitialized = 8,
|
||||
AlreadyInitialized = 9,
|
||||
OperationFailed = 10,
|
||||
Timeout = 11,
|
||||
Cancelled = 12,
|
||||
InvalidState = 13,
|
||||
ResourceExhausted = 14,
|
||||
Unavailable = 15,
|
||||
DataLoss = 16,
|
||||
Unauthenticated = 17,
|
||||
PermissionDenied2 = 18,
|
||||
ResourceNotFound = 19,
|
||||
Aborted = 20,
|
||||
OutOfRange = 21,
|
||||
Unimplemented = 22,
|
||||
Internal = 23,
|
||||
DataCorrupted = 24,
|
||||
RequestTooLarge = 25,
|
||||
ResourceBusy = 26,
|
||||
QuotaExceeded = 27,
|
||||
DeadlineExceeded = 28,
|
||||
LoadBalancing = 29,
|
||||
NetworkError = 30,
|
||||
ProtocolError = 31,
|
||||
ServiceUnavailable = 32,
|
||||
GatewayError = 33,
|
||||
RateLimited = 34,
|
||||
BadRequest = 35,
|
||||
Unauthorized = 36,
|
||||
Forbidden = 37,
|
||||
NotFound = 38,
|
||||
MethodNotAllowed = 39,
|
||||
Conflict = 40,
|
||||
Gone = 41,
|
||||
LengthRequired = 42,
|
||||
PreconditionFailed = 43,
|
||||
PayloadTooLarge = 44,
|
||||
UriTooLong = 45,
|
||||
UnsupportedMediaType = 46,
|
||||
RangeNotSatisfiable = 47,
|
||||
ExpectationFailed = 48,
|
||||
ImATeapot = 49,
|
||||
MisdirectedRequest = 50,
|
||||
UnprocessableEntity = 51,
|
||||
Locked = 52,
|
||||
FailedDependency = 53,
|
||||
TooEarly = 54,
|
||||
UpgradeRequired = 55,
|
||||
PreconditionRequired = 56,
|
||||
TooManyRequests = 57,
|
||||
RequestHeaderFieldsTooLarge = 58,
|
||||
UnavailableForLegalReasons = 59
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 错误信息结构
|
||||
*/
|
||||
struct Error {
|
||||
ErrorCode code = ErrorCode::None;
|
||||
std::string message;
|
||||
std::string file;
|
||||
int line = 0;
|
||||
|
||||
Error() = default;
|
||||
Error(ErrorCode c, const std::string& msg) : code(c), message(msg) {}
|
||||
Error(ErrorCode c, const std::string& msg, const std::string& f, int l)
|
||||
: code(c), message(msg), file(f), line(l) {}
|
||||
|
||||
bool ok() const { return code == ErrorCode::None; }
|
||||
|
||||
static Error none() { return Error(); }
|
||||
static Error unknown(const std::string& msg) { return Error(ErrorCode::Unknown, msg); }
|
||||
static Error invalidArgument(const std::string& msg) { return Error(ErrorCode::InvalidArgument, msg); }
|
||||
static Error outOfMemory(const std::string& msg) { return Error(ErrorCode::OutOfMemory, msg); }
|
||||
static Error fileNotFound(const std::string& msg) { return Error(ErrorCode::FileNotFound, msg); }
|
||||
static Error permissionDenied(const std::string& msg) { return Error(ErrorCode::PermissionDenied, msg); }
|
||||
static Error notImplemented(const std::string& msg) { return Error(ErrorCode::NotImplemented, msg); }
|
||||
static Error alreadyExists(const std::string& msg) { return Error(ErrorCode::AlreadyExists, msg); }
|
||||
static Error notInitialized(const std::string& msg) { return Error(ErrorCode::NotInitialized, msg); }
|
||||
static Error alreadyInitialized(const std::string& msg) { return Error(ErrorCode::AlreadyInitialized, msg); }
|
||||
static Error operationFailed(const std::string& msg) { return Error(ErrorCode::OperationFailed, msg); }
|
||||
static Error timeout(const std::string& msg) { return Error(ErrorCode::Timeout, msg); }
|
||||
static Error cancelled(const std::string& msg) { return Error(ErrorCode::Cancelled, msg); }
|
||||
static Error invalidState(const std::string& msg) { return Error(ErrorCode::InvalidState, msg); }
|
||||
static Error resourceExhausted(const std::string& msg) { return Error(ErrorCode::ResourceExhausted, msg); }
|
||||
static Error unavailable(const std::string& msg) { return Error(ErrorCode::Unavailable, msg); }
|
||||
static Error dataLoss(const std::string& msg) { return Error(ErrorCode::DataLoss, msg); }
|
||||
static Error unauthenticated(const std::string& msg) { return Error(ErrorCode::Unauthenticated, msg); }
|
||||
static Error permissionDenied2(const std::string& msg) { return Error(ErrorCode::PermissionDenied2, msg); }
|
||||
static Error resourceNotFound(const std::string& msg) { return Error(ErrorCode::ResourceNotFound, msg); }
|
||||
static Error aborted(const std::string& msg) { return Error(ErrorCode::Aborted, msg); }
|
||||
static Error outOfRange(const std::string& msg) { return Error(ErrorCode::OutOfRange, msg); }
|
||||
static Error unimplemented(const std::string& msg) { return Error(ErrorCode::Unimplemented, msg); }
|
||||
static Error internal(const std::string& msg) { return Error(ErrorCode::Internal, msg); }
|
||||
static Error dataCorrupted(const std::string& msg) { return Error(ErrorCode::DataCorrupted, msg); }
|
||||
static Error requestTooLarge(const std::string& msg) { return Error(ErrorCode::RequestTooLarge, msg); }
|
||||
static Error resourceBusy(const std::string& msg) { return Error(ErrorCode::ResourceBusy, msg); }
|
||||
static Error quotaExceeded(const std::string& msg) { return Error(ErrorCode::QuotaExceeded, msg); }
|
||||
static Error deadlineExceeded(const std::string& msg) { return Error(ErrorCode::DeadlineExceeded, msg); }
|
||||
static Error loadBalancing(const std::string& msg) { return Error(ErrorCode::LoadBalancing, msg); }
|
||||
static Error networkError(const std::string& msg) { return Error(ErrorCode::NetworkError, msg); }
|
||||
static Error protocolError(const std::string& msg) { return Error(ErrorCode::ProtocolError, msg); }
|
||||
static Error serviceUnavailable(const std::string& msg) { return Error(ErrorCode::ServiceUnavailable, msg); }
|
||||
static Error gatewayError(const std::string& msg) { return Error(ErrorCode::GatewayError, msg); }
|
||||
static Error rateLimited(const std::string& msg) { return Error(ErrorCode::RateLimited, msg); }
|
||||
static Error badRequest(const std::string& msg) { return Error(ErrorCode::BadRequest, msg); }
|
||||
static Error unauthorized(const std::string& msg) { return Error(ErrorCode::Unauthorized, msg); }
|
||||
static Error forbidden(const std::string& msg) { return Error(ErrorCode::Forbidden, msg); }
|
||||
static Error notFound(const std::string& msg) { return Error(ErrorCode::NotFound, msg); }
|
||||
static Error methodNotAllowed(const std::string& msg) { return Error(ErrorCode::MethodNotAllowed, msg); }
|
||||
static Error conflict(const std::string& msg) { return Error(ErrorCode::Conflict, msg); }
|
||||
static Error gone(const std::string& msg) { return Error(ErrorCode::Gone, msg); }
|
||||
static Error lengthRequired(const std::string& msg) { return Error(ErrorCode::LengthRequired, msg); }
|
||||
static Error preconditionFailed(const std::string& msg) { return Error(ErrorCode::PreconditionFailed, msg); }
|
||||
static Error payloadTooLarge(const std::string& msg) { return Error(ErrorCode::PayloadTooLarge, msg); }
|
||||
static Error uriTooLong(const std::string& msg) { return Error(ErrorCode::UriTooLong, msg); }
|
||||
static Error unsupportedMediaType(const std::string& msg) { return Error(ErrorCode::UnsupportedMediaType, msg); }
|
||||
static Error rangeNotSatisfiable(const std::string& msg) { return Error(ErrorCode::RangeNotSatisfiable, msg); }
|
||||
static Error expectationFailed(const std::string& msg) { return Error(ErrorCode::ExpectationFailed, msg); }
|
||||
static Error imATeapot(const std::string& msg) { return Error(ErrorCode::ImATeapot, msg); }
|
||||
static Error misdirectedRequest(const std::string& msg) { return Error(ErrorCode::MisdirectedRequest, msg); }
|
||||
static Error unprocessableEntity(const std::string& msg) { return Error(ErrorCode::UnprocessableEntity, msg); }
|
||||
static Error locked(const std::string& msg) { return Error(ErrorCode::Locked, msg); }
|
||||
static Error failedDependency(const std::string& msg) { return Error(ErrorCode::FailedDependency, msg); }
|
||||
static Error tooEarly(const std::string& msg) { return Error(ErrorCode::TooEarly, msg); }
|
||||
static Error upgradeRequired(const std::string& msg) { return Error(ErrorCode::UpgradeRequired, msg); }
|
||||
static Error preconditionRequired(const std::string& msg) { return Error(ErrorCode::PreconditionRequired, msg); }
|
||||
static Error tooManyRequests(const std::string& msg) { return Error(ErrorCode::TooManyRequests, msg); }
|
||||
static Error requestHeaderFieldsTooLarge(const std::string& msg) { return Error(ErrorCode::RequestHeaderFieldsTooLarge, msg); }
|
||||
static Error unavailableForLegalReasons(const std::string& msg) { return Error(ErrorCode::UnavailableForLegalReasons, msg); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Result类型,用于错误处理
|
||||
* @tparam T 成功时的值类型
|
||||
* @tparam E 错误类型,默认为Error
|
||||
*/
|
||||
template<typename T, typename E = Error>
|
||||
class Result {
|
||||
public:
|
||||
Result() : hasValue_(false) {
|
||||
new (&storage_.error) E();
|
||||
}
|
||||
|
||||
~Result() {
|
||||
if (hasValue_) {
|
||||
storage_.value.~T();
|
||||
} else {
|
||||
storage_.error.~E();
|
||||
}
|
||||
}
|
||||
|
||||
Result(const Result& other) : hasValue_(other.hasValue_) {
|
||||
if (hasValue_) {
|
||||
new (&storage_.value) T(other.storage_.value);
|
||||
} else {
|
||||
new (&storage_.error) E(other.storage_.error);
|
||||
}
|
||||
}
|
||||
|
||||
Result(Result&& other) noexcept : hasValue_(other.hasValue_) {
|
||||
if (hasValue_) {
|
||||
new (&storage_.value) T(std::move(other.storage_.value));
|
||||
} else {
|
||||
new (&storage_.error) E(std::move(other.storage_.error));
|
||||
}
|
||||
}
|
||||
|
||||
Result& operator=(const Result& other) {
|
||||
if (this != &other) {
|
||||
this->~Result();
|
||||
hasValue_ = other.hasValue_;
|
||||
if (hasValue_) {
|
||||
new (&storage_.value) T(other.storage_.value);
|
||||
} else {
|
||||
new (&storage_.error) E(other.storage_.error);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Result& operator=(Result&& other) noexcept {
|
||||
if (this != &other) {
|
||||
this->~Result();
|
||||
hasValue_ = other.hasValue_;
|
||||
if (hasValue_) {
|
||||
new (&storage_.value) T(std::move(other.storage_.value));
|
||||
} else {
|
||||
new (&storage_.error) E(std::move(other.storage_.error));
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
static Result<T, E> ok(T value) {
|
||||
Result<T, E> result;
|
||||
result.hasValue_ = true;
|
||||
new (&result.storage_.value) T(std::move(value));
|
||||
return result;
|
||||
}
|
||||
|
||||
static Result<T, E> err(E error) {
|
||||
Result<T, E> result;
|
||||
result.hasValue_ = false;
|
||||
new (&result.storage_.error) E(std::move(error));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ok() const { return hasValue_; }
|
||||
bool isOk() const { return hasValue_; }
|
||||
bool isErr() const { return !hasValue_; }
|
||||
|
||||
T& value() & {
|
||||
return storage_.value;
|
||||
}
|
||||
|
||||
const T& value() const & {
|
||||
return storage_.value;
|
||||
}
|
||||
|
||||
T&& value() && {
|
||||
return std::move(storage_.value);
|
||||
}
|
||||
|
||||
E& error() & {
|
||||
return storage_.error;
|
||||
}
|
||||
|
||||
const E& error() const & {
|
||||
return storage_.error;
|
||||
}
|
||||
|
||||
E&& error() && {
|
||||
return std::move(storage_.error);
|
||||
}
|
||||
|
||||
T valueOr(T defaultValue) const {
|
||||
return hasValue_ ? storage_.value : std::move(defaultValue);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
Result<T, E> map(F&& f) {
|
||||
if (hasValue_) {
|
||||
return Result<T, E>::ok(f(storage_.value));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
Result<T, E> mapErr(F&& f) {
|
||||
if (!hasValue_) {
|
||||
return Result<T, E>::err(f(storage_.error));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
auto andThen(F&& f) -> decltype(f(std::declval<T>())) {
|
||||
if (hasValue_) {
|
||||
return f(storage_.value);
|
||||
}
|
||||
return Result<typename decltype(f(std::declval<T>()))::ValueType, E>::err(storage_.error);
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
Result<T, E> orElse(F&& f) {
|
||||
if (!hasValue_) {
|
||||
return f(storage_.error);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
union Storage {
|
||||
T value;
|
||||
E error;
|
||||
|
||||
Storage() {}
|
||||
~Storage() {}
|
||||
} storage_;
|
||||
|
||||
bool hasValue_;
|
||||
};
|
||||
|
||||
// 特化void类型
|
||||
template<typename E>
|
||||
class Result<void, E> {
|
||||
public:
|
||||
Result() : hasValue_(true) {}
|
||||
|
||||
~Result() {
|
||||
if (!hasValue_) {
|
||||
storage_.error.~E();
|
||||
}
|
||||
}
|
||||
|
||||
Result(const Result& other) : hasValue_(other.hasValue_) {
|
||||
if (!hasValue_) {
|
||||
new (&storage_.error) E(other.storage_.error);
|
||||
}
|
||||
}
|
||||
|
||||
Result(Result&& other) noexcept : hasValue_(other.hasValue_) {
|
||||
if (!hasValue_) {
|
||||
new (&storage_.error) E(std::move(other.storage_.error));
|
||||
}
|
||||
}
|
||||
|
||||
Result& operator=(const Result& other) {
|
||||
if (this != &other) {
|
||||
this->~Result();
|
||||
hasValue_ = other.hasValue_;
|
||||
if (!hasValue_) {
|
||||
new (&storage_.error) E(other.storage_.error);
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Result& operator=(Result&& other) noexcept {
|
||||
if (this != &other) {
|
||||
this->~Result();
|
||||
hasValue_ = other.hasValue_;
|
||||
if (!hasValue_) {
|
||||
new (&storage_.error) E(std::move(other.storage_.error));
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
static Result<void, E> ok() {
|
||||
return Result<void, E>();
|
||||
}
|
||||
|
||||
static Result<void, E> err(E error) {
|
||||
Result<void, E> result;
|
||||
result.hasValue_ = false;
|
||||
new (&result.storage_.error) E(std::move(error));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ok() const { return hasValue_; }
|
||||
bool isOk() const { return hasValue_; }
|
||||
bool isErr() const { return !hasValue_; }
|
||||
|
||||
E& error() & {
|
||||
return storage_.error;
|
||||
}
|
||||
|
||||
const E& error() const & {
|
||||
return storage_.error;
|
||||
}
|
||||
|
||||
E&& error() && {
|
||||
return std::move(storage_.error);
|
||||
}
|
||||
|
||||
private:
|
||||
union Storage {
|
||||
E error;
|
||||
|
||||
Storage() {}
|
||||
~Storage() {}
|
||||
} storage_;
|
||||
|
||||
bool hasValue_;
|
||||
};
|
||||
|
||||
// 便捷宏
|
||||
#define E2D_TRY(result) \
|
||||
do { \
|
||||
auto _res = (result); \
|
||||
if (!_res.ok()) { \
|
||||
return Result<decltype(_res)::ValueType, decltype(_res)::ErrorType>::err(_res.error()); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 无锁环形缓冲区(单生产者单消费者)
|
||||
* @tparam T 元素类型
|
||||
* @tparam Size 缓冲区大小(必须是2的幂)
|
||||
*/
|
||||
template <typename T, size_t Size>
|
||||
class RingBuffer {
|
||||
static_assert((Size & (Size - 1)) == 0, "Size must be a power of 2");
|
||||
|
||||
public:
|
||||
RingBuffer() = default;
|
||||
|
||||
/**
|
||||
* @brief 入队
|
||||
* @param item 元素
|
||||
* @return 成功返回true,缓冲区满返回false
|
||||
*/
|
||||
bool push(const T& item) {
|
||||
const size_t currentHead = head_.load(std::memory_order_relaxed);
|
||||
const size_t currentTail = tail_.load(std::memory_order_acquire);
|
||||
|
||||
if ((currentHead - currentTail) >= Size) {
|
||||
return false; // 缓冲区满
|
||||
}
|
||||
|
||||
buffer_[currentHead & mask_] = item;
|
||||
head_.store(currentHead + 1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 入队(移动语义)
|
||||
* @param item 元素
|
||||
* @return 成功返回true,缓冲区满返回false
|
||||
*/
|
||||
bool push(T&& item) {
|
||||
const size_t currentHead = head_.load(std::memory_order_relaxed);
|
||||
const size_t currentTail = tail_.load(std::memory_order_acquire);
|
||||
|
||||
if ((currentHead - currentTail) >= Size) {
|
||||
return false; // 缓冲区满
|
||||
}
|
||||
|
||||
buffer_[currentHead & mask_] = std::move(item);
|
||||
head_.store(currentHead + 1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 出队
|
||||
* @param item 输出元素
|
||||
* @return 成功返回true,缓冲区空返回false
|
||||
*/
|
||||
bool pop(T& item) {
|
||||
const size_t currentTail = tail_.load(std::memory_order_relaxed);
|
||||
const size_t currentHead = head_.load(std::memory_order_acquire);
|
||||
|
||||
if (currentTail == currentHead) {
|
||||
return false; // 缓冲区空
|
||||
}
|
||||
|
||||
item = std::move(buffer_[currentTail & mask_]);
|
||||
tail_.store(currentTail + 1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查是否为空
|
||||
*/
|
||||
bool empty() const {
|
||||
return head_.load(std::memory_order_acquire) ==
|
||||
tail_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取当前大小
|
||||
*/
|
||||
size_t size() const {
|
||||
return head_.load(std::memory_order_acquire) -
|
||||
tail_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取容量
|
||||
*/
|
||||
static constexpr size_t capacity() { return Size; }
|
||||
|
||||
private:
|
||||
static constexpr size_t mask_ = Size - 1;
|
||||
alignas(64) std::array<T, Size> buffer_;
|
||||
alignas(64) std::atomic<size_t> head_{0};
|
||||
alignas(64) std::atomic<size_t> tail_{0};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,383 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/Object.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <glm/mat4x4.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
namespace gfx {
|
||||
class Device;
|
||||
}
|
||||
|
||||
class Node;
|
||||
class RenderScene;
|
||||
|
||||
/**
|
||||
* @brief 相机投影类型
|
||||
*/
|
||||
enum class CameraProjection {
|
||||
Ortho,
|
||||
Perspective,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 相机信息结构
|
||||
*/
|
||||
struct CameraInfo {
|
||||
std::string name;
|
||||
Node* node{nullptr};
|
||||
CameraProjection projection{CameraProjection::Ortho};
|
||||
u32 priority{0};
|
||||
u32 targetDisplay{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 2D 相机类
|
||||
*
|
||||
* 管理 2D 场景的视图和投影变换。
|
||||
* 参考 Cocos Creator 的 Camera 设计。
|
||||
*/
|
||||
class Camera : public RefCounted {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建相机
|
||||
* @param device GFX 设备
|
||||
* @return 相机引用
|
||||
*/
|
||||
static Ref<Camera> create(gfx::Device* device);
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param device GFX 设备
|
||||
*/
|
||||
explicit Camera(gfx::Device* device);
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Camera() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化相机
|
||||
* @param info 相机信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(const CameraInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 销毁相机
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 更新相机
|
||||
* @param forceUpdate 是否强制更新
|
||||
*/
|
||||
void update(bool forceUpdate = false);
|
||||
|
||||
/**
|
||||
* @brief 调整相机大小
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
*/
|
||||
void resize(u32 width, u32 height);
|
||||
|
||||
// ==================== 变换方法 ====================
|
||||
|
||||
/**
|
||||
* @brief 将屏幕坐标转换为世界坐标
|
||||
* @param screenPos 屏幕坐标
|
||||
* @return 世界坐标
|
||||
*/
|
||||
Vec2 screenToWorld(const Vec2& screenPos);
|
||||
|
||||
/**
|
||||
* @brief 将世界坐标转换为屏幕坐标
|
||||
* @param worldPos 世界坐标
|
||||
* @return 屏幕坐标
|
||||
*/
|
||||
Vec2 worldToScreen(const Vec2& worldPos);
|
||||
|
||||
/**
|
||||
* @brief 将屏幕点转换为射线(用于拾取)
|
||||
* @param x 屏幕 X 坐标
|
||||
* @param y 屏幕 Y 坐标
|
||||
* @return 射线原点(2D 使用)
|
||||
*/
|
||||
Vec2 screenPointToRay(float x, float y);
|
||||
|
||||
// ==================== 属性访问器 ====================
|
||||
|
||||
/**
|
||||
* @brief 设置关联节点
|
||||
* @param node 节点指针
|
||||
*/
|
||||
void setNode(Node* node);
|
||||
|
||||
/**
|
||||
* @brief 获取关联节点
|
||||
* @return 节点指针
|
||||
*/
|
||||
Node* getNode() const;
|
||||
|
||||
/**
|
||||
* @brief 设置是否启用
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief 检查是否启用
|
||||
* @return 如果启用返回 true
|
||||
*/
|
||||
bool isEnabled() const;
|
||||
|
||||
/**
|
||||
* @brief 设置正交高度
|
||||
* @param height 正交高度
|
||||
*/
|
||||
void setOrthoHeight(float height);
|
||||
|
||||
/**
|
||||
* @brief 获取正交高度
|
||||
* @return 正交高度
|
||||
*/
|
||||
float getOrthoHeight() const;
|
||||
|
||||
/**
|
||||
* @brief 设置投影类型
|
||||
* @param projection 投影类型
|
||||
*/
|
||||
void setProjectionType(CameraProjection projection);
|
||||
|
||||
/**
|
||||
* @brief 获取投影类型
|
||||
* @return 投影类型
|
||||
*/
|
||||
CameraProjection getProjectionType() const;
|
||||
|
||||
/**
|
||||
* @brief 设置视场角
|
||||
* @param fov 视场角(弧度)
|
||||
*/
|
||||
void setFov(float fov);
|
||||
|
||||
/**
|
||||
* @brief 获取视场角
|
||||
* @return 视场角(弧度)
|
||||
*/
|
||||
float getFov() const;
|
||||
|
||||
/**
|
||||
* @brief 设置近裁剪面
|
||||
* @param near 近裁剪面距离
|
||||
*/
|
||||
void setNearClip(float near);
|
||||
|
||||
/**
|
||||
* @brief 获取近裁剪面
|
||||
* @return 近裁剪面距离
|
||||
*/
|
||||
float getNearClip() const;
|
||||
|
||||
/**
|
||||
* @brief 设置远裁剪面
|
||||
* @param far 远裁剪面距离
|
||||
*/
|
||||
void setFarClip(float far);
|
||||
|
||||
/**
|
||||
* @brief 获取远裁剪面
|
||||
* @return 远裁剪面距离
|
||||
*/
|
||||
float getFarClip() const;
|
||||
|
||||
/**
|
||||
* @brief 设置清除颜色
|
||||
* @param color 清除颜色
|
||||
*/
|
||||
void setClearColor(const gfx::Color& color);
|
||||
|
||||
/**
|
||||
* @brief 获取清除颜色
|
||||
* @return 清除颜色
|
||||
*/
|
||||
const gfx::Color& getClearColor() const;
|
||||
|
||||
/**
|
||||
* @brief 设置清除标志
|
||||
* @param flag 清除标志
|
||||
*/
|
||||
void setClearFlag(gfx::ClearFlagBit flag);
|
||||
|
||||
/**
|
||||
* @brief 获取清除标志
|
||||
* @return 清除标志
|
||||
*/
|
||||
gfx::ClearFlagBit getClearFlag() const;
|
||||
|
||||
/**
|
||||
* @brief 设置优先级
|
||||
* @param priority 优先级
|
||||
*/
|
||||
void setPriority(u32 priority);
|
||||
|
||||
/**
|
||||
* @brief 获取优先级
|
||||
* @return 优先级
|
||||
*/
|
||||
u32 getPriority() const;
|
||||
|
||||
/**
|
||||
* @brief 获取宽度
|
||||
* @return 宽度
|
||||
*/
|
||||
u32 getWidth() const;
|
||||
|
||||
/**
|
||||
* @brief 获取高度
|
||||
* @return 高度
|
||||
*/
|
||||
u32 getHeight() const;
|
||||
|
||||
/**
|
||||
* @brief 获取宽高比
|
||||
* @return 宽高比
|
||||
*/
|
||||
float getAspect() const;
|
||||
|
||||
/**
|
||||
* @brief 获取视图矩阵
|
||||
* @return 视图矩阵
|
||||
*/
|
||||
const glm::mat4& getMatView() const;
|
||||
|
||||
/**
|
||||
* @brief 获取投影矩阵
|
||||
* @return 投影矩阵
|
||||
*/
|
||||
const glm::mat4& getMatProj() const;
|
||||
|
||||
/**
|
||||
* @brief 获取视图投影矩阵
|
||||
* @return 视图投影矩阵
|
||||
*/
|
||||
const glm::mat4& getMatViewProj() const;
|
||||
|
||||
/**
|
||||
* @brief 获取相机名称
|
||||
* @return 相机名称
|
||||
*/
|
||||
const std::string& getName() const;
|
||||
|
||||
/**
|
||||
* @brief 获取所属渲染场景
|
||||
* @return 渲染场景指针
|
||||
*/
|
||||
RenderScene* getScene() const;
|
||||
|
||||
/**
|
||||
* @brief 附加到渲染场景
|
||||
* @param scene 渲染场景
|
||||
*/
|
||||
void attachToScene(RenderScene* scene);
|
||||
|
||||
/**
|
||||
* @brief 从渲染场景分离
|
||||
*/
|
||||
void detachFromScene();
|
||||
|
||||
/**
|
||||
* @brief 设置可见性掩码
|
||||
* @param visibility 可见性掩码
|
||||
*/
|
||||
void setVisibility(u32 visibility);
|
||||
|
||||
/**
|
||||
* @brief 获取可见性掩码
|
||||
* @return 可见性掩码
|
||||
*/
|
||||
u32 getVisibility() const;
|
||||
|
||||
/**
|
||||
* @brief 设置相机位置
|
||||
* @param pos 位置
|
||||
*/
|
||||
void setPosition(const Vec2& pos);
|
||||
|
||||
/**
|
||||
* @brief 获取相机位置
|
||||
* @return 位置
|
||||
*/
|
||||
const Vec2& getPosition() const;
|
||||
|
||||
/**
|
||||
* @brief 设置相机前向
|
||||
* @param forward 前向向量
|
||||
*/
|
||||
void setForward(const Vec2& forward);
|
||||
|
||||
/**
|
||||
* @brief 获取相机前向
|
||||
* @return 前向向量
|
||||
*/
|
||||
const Vec2& getForward() const;
|
||||
|
||||
/**
|
||||
* @brief 获取相机 ID
|
||||
* @return 相机 ID
|
||||
*/
|
||||
u32 getCameraId() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 更新投影矩阵
|
||||
*/
|
||||
void updateProjection();
|
||||
|
||||
/**
|
||||
* @brief 更新视图矩阵
|
||||
*/
|
||||
void updateView();
|
||||
|
||||
private:
|
||||
gfx::Device* device_{nullptr};
|
||||
RenderScene* scene_{nullptr};
|
||||
Node* node_{nullptr};
|
||||
|
||||
std::string name_;
|
||||
u32 cameraId_{0};
|
||||
bool enabled_{false};
|
||||
|
||||
CameraProjection projection_{CameraProjection::Ortho};
|
||||
float aspect_{1.0f};
|
||||
float orthoHeight_{10.0f};
|
||||
float fov_{0.785398f};
|
||||
float nearClip_{1.0f};
|
||||
float farClip_{1000.0f};
|
||||
|
||||
gfx::Color clearColor_{0.2f, 0.2f, 0.2f, 1.0f};
|
||||
gfx::ClearFlagBit clearFlag_{gfx::ClearFlagBit::ALL};
|
||||
|
||||
u32 width_{0};
|
||||
u32 height_{0};
|
||||
u32 priority_{0};
|
||||
u32 visibility_{0xFFFFFFFF};
|
||||
|
||||
Vec2 position_{0, 0};
|
||||
Vec2 forward_{0, -1};
|
||||
|
||||
glm::mat4 matView_{1.0f};
|
||||
glm::mat4 matProj_{1.0f};
|
||||
glm::mat4 matViewProj_{1.0f};
|
||||
|
||||
bool isProjDirty_{true};
|
||||
bool isViewDirty_{true};
|
||||
|
||||
static u32 s_cameraIdCounter;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/Object.h>
|
||||
#include <extra2d/core/event/EventTarget.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Node;
|
||||
|
||||
/**
|
||||
* @brief 组件基类
|
||||
*
|
||||
* 所有组件的基类,提供生命周期回调。
|
||||
* 参考 Cocos Creator 的 Component 设计。
|
||||
*/
|
||||
class Component : public Object {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param name 组件名称
|
||||
*/
|
||||
explicit Component(const std::string& name = "");
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Component() override;
|
||||
|
||||
/**
|
||||
* @brief 获取所属节点
|
||||
* @return 节点指针
|
||||
*/
|
||||
Node* getNode() const;
|
||||
|
||||
/**
|
||||
* @brief 设置所属节点
|
||||
* @param node 节点指针
|
||||
*/
|
||||
void setNode(Node* node);
|
||||
|
||||
/**
|
||||
* @brief 检查组件是否启用
|
||||
* @return 如果启用返回 true
|
||||
*/
|
||||
bool isEnabled() const;
|
||||
|
||||
/**
|
||||
* @brief 设置组件是否启用
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
// ==================== 生命周期回调 ====================
|
||||
|
||||
/**
|
||||
* @brief 组件添加到节点时调用
|
||||
*/
|
||||
virtual void onAdd();
|
||||
|
||||
/**
|
||||
* @brief 组件从节点移除时调用
|
||||
*/
|
||||
virtual void onRemove();
|
||||
|
||||
/**
|
||||
* @brief 组件启用时调用
|
||||
*/
|
||||
virtual void onEnable();
|
||||
|
||||
/**
|
||||
* @brief 组件禁用时调用
|
||||
*/
|
||||
virtual void onDisable();
|
||||
|
||||
/**
|
||||
* @brief 每帧更新
|
||||
* @param dt 帧间隔时间
|
||||
*/
|
||||
virtual void update(f32 dt);
|
||||
|
||||
/**
|
||||
* @brief 每帧晚期更新
|
||||
* @param dt 帧间隔时间
|
||||
*/
|
||||
virtual void lateUpdate(f32 dt);
|
||||
|
||||
/**
|
||||
* @brief 渲染
|
||||
*/
|
||||
virtual void render();
|
||||
|
||||
/**
|
||||
* @brief 销毁组件
|
||||
* @return 销毁成功返回 true
|
||||
*/
|
||||
bool destroy() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 销毁时的回调
|
||||
*/
|
||||
void onDestroy() override;
|
||||
|
||||
Node* node_{nullptr};
|
||||
bool enabled_{true};
|
||||
|
||||
friend class Node;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 节点层管理器
|
||||
*
|
||||
* 层数据是以掩码方式存储在 Node.layer 中,用于射线检测、物理碰撞和用户自定义逻辑。
|
||||
* 每个节点可属于一个或多个层,可通过包含式或排除式检测器进行层检测。
|
||||
*/
|
||||
class Layers final {
|
||||
public:
|
||||
Layers() = delete;
|
||||
~Layers() = delete;
|
||||
|
||||
/**
|
||||
* @brief 内置层列表
|
||||
*/
|
||||
enum class LayerList : u32 {
|
||||
None = 0,
|
||||
IgnoreRaycast = (1 << 20),
|
||||
Gizmos = (1 << 21),
|
||||
Editor = (1 << 22),
|
||||
UI3D = (1 << 23),
|
||||
SceneGizmo = (1 << 24),
|
||||
UI2D = (1 << 25),
|
||||
Profiler = (1 << 28),
|
||||
Default = (1 << 30),
|
||||
All = 0xFFFFFFFF,
|
||||
};
|
||||
|
||||
using Enum = LayerList;
|
||||
|
||||
/**
|
||||
* @brief 创建包含式层检测器
|
||||
* @param includes 包含的层列表
|
||||
* @return 掩码值
|
||||
*/
|
||||
static u32 makeMaskInclude(const std::vector<u32>& includes) {
|
||||
u32 mask = 0;
|
||||
for (u32 inc : includes) {
|
||||
mask |= inc;
|
||||
}
|
||||
return mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 创建排除式层检测器
|
||||
* @param excludes 排除的层列表
|
||||
* @return 掩码值
|
||||
*/
|
||||
static u32 makeMaskExclude(const std::vector<u32>& excludes) {
|
||||
return ~makeMaskInclude(excludes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 添加一个新层
|
||||
* @param name 层名称
|
||||
* @param bitNum 层位位置(0-19 为用户自定义层)
|
||||
*/
|
||||
static void addLayer(const std::string& name, u32 bitNum);
|
||||
|
||||
/**
|
||||
* @brief 移除一个层
|
||||
* @param bitNum 层位位置
|
||||
*/
|
||||
static void deleteLayer(u32 bitNum);
|
||||
|
||||
/**
|
||||
* @brief 根据层名称获取层索引
|
||||
* @param name 层名称
|
||||
* @return 层索引
|
||||
*/
|
||||
static u32 nameToLayer(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 根据层索引获取层名称
|
||||
* @param bitNum 层位位置
|
||||
* @return 层名称
|
||||
*/
|
||||
static std::string layerToName(u32 bitNum);
|
||||
|
||||
/**
|
||||
* @brief 检查层是否包含指定层
|
||||
* @param layer 层值
|
||||
* @param mask 掩码
|
||||
* @return 如果包含返回 true
|
||||
*/
|
||||
static bool contains(u32 layer, u32 mask) {
|
||||
return (layer & mask) != 0;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::unordered_map<std::string, u32> s_nameToLayer;
|
||||
static std::unordered_map<u32, std::string> s_layerToName;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 层枚举位运算操作符
|
||||
*/
|
||||
inline Layers::LayerList operator|(Layers::LayerList a, Layers::LayerList b) {
|
||||
return static_cast<Layers::LayerList>(static_cast<u32>(a) | static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline Layers::LayerList operator&(Layers::LayerList a, Layers::LayerList b) {
|
||||
return static_cast<Layers::LayerList>(static_cast<u32>(a) & static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline Layers::LayerList operator~(Layers::LayerList a) {
|
||||
return static_cast<Layers::LayerList>(~static_cast<u32>(a));
|
||||
}
|
||||
|
||||
inline Layers::LayerList& operator|=(Layers::LayerList& a, Layers::LayerList b) {
|
||||
a = a | b;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline Layers::LayerList& operator&=(Layers::LayerList& a, Layers::LayerList b) {
|
||||
a = a & b;
|
||||
return a;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,241 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/Object.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/assets/Material.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Node;
|
||||
class RenderScene;
|
||||
|
||||
/**
|
||||
* @brief 模型信息
|
||||
*/
|
||||
struct ModelInfo {
|
||||
Node* node{nullptr};
|
||||
u32 priority{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 子模型
|
||||
*/
|
||||
class SubModel {
|
||||
public:
|
||||
SubModel();
|
||||
~SubModel();
|
||||
|
||||
/**
|
||||
* @brief 初始化子模型
|
||||
* @param vertexCount 顶点数
|
||||
* @param indexCount 索引数
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(u32 vertexCount, u32 indexCount);
|
||||
|
||||
/**
|
||||
* @brief 销毁子模型
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 设置材质
|
||||
* @param material 材质
|
||||
*/
|
||||
void setMaterial(Material* material);
|
||||
|
||||
/**
|
||||
* @brief 获取材质
|
||||
* @return 材质
|
||||
*/
|
||||
Material* getMaterial() const;
|
||||
|
||||
/**
|
||||
* @brief 设置输入装配器
|
||||
* @param ia 输入装配器
|
||||
*/
|
||||
void setInputAssembler(gfx::InputAssembler* ia);
|
||||
|
||||
/**
|
||||
* @brief 获取输入装配器
|
||||
* @return 输入装配器
|
||||
*/
|
||||
gfx::InputAssembler* getInputAssembler() const;
|
||||
|
||||
private:
|
||||
Material* material_{nullptr};
|
||||
gfx::InputAssembler* inputAssembler_{nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模型类
|
||||
*
|
||||
* 管理 2D/3D 渲染模型。
|
||||
* 参考 Cocos Creator 的 Model 设计。
|
||||
*/
|
||||
class Model : public RefCounted {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建模型
|
||||
* @return 模型引用
|
||||
*/
|
||||
static Ref<Model> create();
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
Model();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Model() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化模型
|
||||
* @param info 模型信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(const ModelInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 销毁模型
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 更新模型
|
||||
* @param stamp 时间戳
|
||||
*/
|
||||
void update(u32 stamp);
|
||||
|
||||
// ==================== 子模型管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 添加子模型
|
||||
* @param subModel 子模型
|
||||
*/
|
||||
void addSubModel(SubModel* subModel);
|
||||
|
||||
/**
|
||||
* @brief 获取子模型数量
|
||||
* @return 子模型数量
|
||||
*/
|
||||
size_t getSubModelCount() const;
|
||||
|
||||
/**
|
||||
* @brief 获取子模型
|
||||
* @param index 索引
|
||||
* @return 子模型
|
||||
*/
|
||||
SubModel* getSubModel(size_t index) const;
|
||||
|
||||
/**
|
||||
* @brief 获取所有子模型
|
||||
* @return 子模型列表
|
||||
*/
|
||||
const std::vector<SubModel*>& getSubModels() const;
|
||||
|
||||
// ==================== 属性访问器 ====================
|
||||
|
||||
/**
|
||||
* @brief 设置关联节点
|
||||
* @param node 节点
|
||||
*/
|
||||
void setNode(Node* node);
|
||||
|
||||
/**
|
||||
* @brief 获取关联节点
|
||||
* @return 节点
|
||||
*/
|
||||
Node* getNode() const;
|
||||
|
||||
/**
|
||||
* @brief 设置优先级
|
||||
* @param priority 优先级
|
||||
*/
|
||||
void setPriority(u32 priority);
|
||||
|
||||
/**
|
||||
* @brief 获取优先级
|
||||
* @return 优先级
|
||||
*/
|
||||
u32 getPriority() const;
|
||||
|
||||
/**
|
||||
* @brief 设置可见性
|
||||
* @param visible 是否可见
|
||||
*/
|
||||
void setVisible(bool visible);
|
||||
|
||||
/**
|
||||
* @brief 检查是否可见
|
||||
* @return 如果可见返回 true
|
||||
*/
|
||||
bool isVisible() const;
|
||||
|
||||
/**
|
||||
* @brief 设置世界矩阵
|
||||
* @param matrix 世界矩阵
|
||||
*/
|
||||
void setWorldMatrix(const Transform2D& matrix);
|
||||
|
||||
/**
|
||||
* @brief 获取世界矩阵
|
||||
* @return 世界矩阵
|
||||
*/
|
||||
const Transform2D& getWorldMatrix() const;
|
||||
|
||||
/**
|
||||
* @brief 设置包围盒
|
||||
* @param aabb 包围盒
|
||||
*/
|
||||
void setAABB(const Rect& aabb);
|
||||
|
||||
/**
|
||||
* @brief 获取包围盒
|
||||
* @return 包围盒
|
||||
*/
|
||||
const Rect& getAABB() const;
|
||||
|
||||
/**
|
||||
* @brief 设置所属渲染场景
|
||||
* @param scene 渲染场景
|
||||
*/
|
||||
void setScene(RenderScene* scene);
|
||||
|
||||
/**
|
||||
* @brief 获取所属渲染场景
|
||||
* @return 渲染场景
|
||||
*/
|
||||
RenderScene* getScene() const;
|
||||
|
||||
/**
|
||||
* @brief 获取模型 ID
|
||||
* @return 模型 ID
|
||||
*/
|
||||
u64 getModelId() const;
|
||||
|
||||
/**
|
||||
* @brief 设置模型 ID
|
||||
* @param id 模型 ID
|
||||
*/
|
||||
void setModelId(u64 id);
|
||||
|
||||
private:
|
||||
Node* node_{nullptr};
|
||||
RenderScene* scene_{nullptr};
|
||||
|
||||
std::vector<SubModel*> subModels_;
|
||||
|
||||
u32 priority_{0};
|
||||
bool visible_{true};
|
||||
u64 modelId_{0};
|
||||
|
||||
Transform2D worldMatrix_;
|
||||
Rect aabb_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,598 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/Object.h>
|
||||
#include <extra2d/core/event/EventTarget.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Node;
|
||||
class Component;
|
||||
class Scene;
|
||||
|
||||
/**
|
||||
* @brief 变换脏标记位
|
||||
*/
|
||||
enum class TransformBit : u32 {
|
||||
None = 0,
|
||||
Position = 1 << 0,
|
||||
Rotation = 1 << 1,
|
||||
Scale = 1 << 2,
|
||||
All = Position | Rotation | Scale,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 实现枚举标志位的位运算
|
||||
*/
|
||||
inline TransformBit operator|(TransformBit a, TransformBit b) {
|
||||
return static_cast<TransformBit>(static_cast<u32>(a) | static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline TransformBit operator&(TransformBit a, TransformBit b) {
|
||||
return static_cast<TransformBit>(static_cast<u32>(a) & static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline TransformBit operator~(TransformBit a) {
|
||||
return static_cast<TransformBit>(~static_cast<u32>(a));
|
||||
}
|
||||
|
||||
inline TransformBit& operator|=(TransformBit& a, TransformBit b) {
|
||||
a = a | b;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline bool hasTransformBit(TransformBit flags, TransformBit bit) {
|
||||
return (static_cast<u32>(flags) & static_cast<u32>(bit)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 节点遍历回调类型
|
||||
*/
|
||||
using NodeWalkCallback = std::function<void(class Node*)>;
|
||||
|
||||
/**
|
||||
* @brief 场景节点类
|
||||
*
|
||||
* 场景图的核心类,管理变换、层级关系和组件。
|
||||
* 参考 Cocos Creator 的 Node 设计。
|
||||
*/
|
||||
class Node : public Object {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建节点
|
||||
* @return 节点引用
|
||||
*/
|
||||
static Ref<Node> create();
|
||||
|
||||
/**
|
||||
* @brief 通过路径查找节点
|
||||
* @param path 路径字符串(如 "Parent/Child/Grandchild")
|
||||
* @param reference 参考节点(默认从根节点开始)
|
||||
* @return 找到的节点,如果未找到返回 nullptr
|
||||
*/
|
||||
static Node* find(const std::string& path, Node* reference = nullptr);
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param name 节点名称
|
||||
*/
|
||||
explicit Node(const std::string& name = "");
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Node() override;
|
||||
|
||||
// ==================== 变换属性 ====================
|
||||
|
||||
/**
|
||||
* @brief 设置本地位置
|
||||
* @param pos 位置向量
|
||||
*/
|
||||
void setPosition(const Vec2& pos);
|
||||
|
||||
/**
|
||||
* @brief 设置本地位置
|
||||
* @param x X 坐标
|
||||
* @param y Y 坐标
|
||||
*/
|
||||
void setPosition(float x, float y);
|
||||
|
||||
/**
|
||||
* @brief 获取本地位置
|
||||
* @return 本地位置
|
||||
*/
|
||||
const Vec2& getPosition() const;
|
||||
|
||||
/**
|
||||
* @brief 设置本地旋转角度
|
||||
* @param degrees 角度(度数)
|
||||
*/
|
||||
void setRotation(float degrees);
|
||||
|
||||
/**
|
||||
* @brief 获取本地旋转角度
|
||||
* @return 角度(度数)
|
||||
*/
|
||||
float getRotation() const;
|
||||
|
||||
/**
|
||||
* @brief 设置本地缩放
|
||||
* @param scale 缩放向量
|
||||
*/
|
||||
void setScale(const Vec2& scale);
|
||||
|
||||
/**
|
||||
* @brief 设置本地缩放
|
||||
* @param sx X 轴缩放
|
||||
* @param sy Y 轴缩放
|
||||
*/
|
||||
void setScale(float sx, float sy);
|
||||
|
||||
/**
|
||||
* @brief 设置统一缩放
|
||||
* @param s 缩放值
|
||||
*/
|
||||
void setScale(float s);
|
||||
|
||||
/**
|
||||
* @brief 获取本地缩放
|
||||
* @return 本地缩放
|
||||
*/
|
||||
const Vec2& getScale() const;
|
||||
|
||||
// ==================== 世界变换 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取世界位置
|
||||
* @return 世界位置
|
||||
*/
|
||||
Vec2 getWorldPosition();
|
||||
|
||||
/**
|
||||
* @brief 获取世界旋转角度
|
||||
* @return 世界旋转角度(度数)
|
||||
*/
|
||||
float getWorldRotation();
|
||||
|
||||
/**
|
||||
* @brief 获取世界缩放
|
||||
* @return 世界缩放
|
||||
*/
|
||||
Vec2 getWorldScale();
|
||||
|
||||
/**
|
||||
* @brief 获取世界变换矩阵
|
||||
* @return 世界变换矩阵
|
||||
*/
|
||||
const Transform2D& getWorldMatrix();
|
||||
|
||||
/**
|
||||
* @brief 将本地坐标转换为世界坐标
|
||||
* @param localPoint 本地坐标点
|
||||
* @return 世界坐标点
|
||||
*/
|
||||
Vec2 localToWorld(const Vec2& localPoint);
|
||||
|
||||
/**
|
||||
* @brief 将世界坐标转换为本地坐标
|
||||
* @param worldPoint 世界坐标点
|
||||
* @return 本地坐标点
|
||||
*/
|
||||
Vec2 worldToLocal(const Vec2& worldPoint);
|
||||
|
||||
// ==================== 层级管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 添加子节点
|
||||
* @param child 子节点
|
||||
*/
|
||||
void addChild(Ref<Node> child);
|
||||
|
||||
/**
|
||||
* @brief 移除子节点
|
||||
* @param child 子节点
|
||||
*/
|
||||
void removeChild(Node* child);
|
||||
|
||||
/**
|
||||
* @brief 从父节点移除自身
|
||||
*/
|
||||
void removeFromParent();
|
||||
|
||||
/**
|
||||
* @brief 移除所有子节点
|
||||
*/
|
||||
void removeAllChildren();
|
||||
|
||||
/**
|
||||
* @brief 获取父节点
|
||||
* @return 父节点指针
|
||||
*/
|
||||
Node* getParent();
|
||||
|
||||
/**
|
||||
* @brief 获取父节点(const 版本)
|
||||
* @return 父节点指针
|
||||
*/
|
||||
const Node* getParent() const;
|
||||
|
||||
/**
|
||||
* @brief 获取所有子节点
|
||||
* @return 子节点列表
|
||||
*/
|
||||
const std::vector<Ref<Node>>& getChildren() const;
|
||||
|
||||
/**
|
||||
* @brief 获取子节点数量
|
||||
* @return 子节点数量
|
||||
*/
|
||||
size_t getChildCount() const;
|
||||
|
||||
// ==================== 遍历和查找 ====================
|
||||
|
||||
/**
|
||||
* @brief 遍历节点树(前序遍历)
|
||||
* @param preFunc 进入节点时的回调
|
||||
*/
|
||||
void walk(const NodeWalkCallback& preFunc);
|
||||
|
||||
/**
|
||||
* @brief 遍历节点树(前序和后序)
|
||||
* @param preFunc 进入节点时的回调
|
||||
* @param postFunc 离开节点时的回调
|
||||
*/
|
||||
void walk(const NodeWalkCallback& preFunc, const NodeWalkCallback& postFunc);
|
||||
|
||||
/**
|
||||
* @brief 通过名称查找子节点
|
||||
* @param name 节点名称
|
||||
* @return 找到的节点,如果未找到返回 nullptr
|
||||
*/
|
||||
Node* getChildByName(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 通过标签查找子节点
|
||||
* @param tag 标签值
|
||||
* @return 找到的节点,如果未找到返回 nullptr
|
||||
*/
|
||||
Node* getChildByTag(i32 tag);
|
||||
|
||||
/**
|
||||
* @brief 通过路径查找子节点
|
||||
* @param path 路径字符串
|
||||
* @return 找到的节点,如果未找到返回 nullptr
|
||||
*/
|
||||
Node* getChildByPath(const std::string& path);
|
||||
|
||||
// ==================== 组件系统 ====================
|
||||
|
||||
/**
|
||||
* @brief 添加组件
|
||||
* @param component 组件
|
||||
*/
|
||||
void addComponent(Ref<Component> component);
|
||||
|
||||
/**
|
||||
* @brief 移除组件
|
||||
* @param component 组件
|
||||
*/
|
||||
void removeComponent(Component* component);
|
||||
|
||||
/**
|
||||
* @brief 获取指定类型的组件
|
||||
* @return 组件指针,如果未找到返回 nullptr
|
||||
*/
|
||||
template<typename T>
|
||||
T* getComponent();
|
||||
|
||||
/**
|
||||
* @brief 获取子节点中的指定类型组件
|
||||
* @return 组件指针,如果未找到返回 nullptr
|
||||
*/
|
||||
template<typename T>
|
||||
T* getComponentInChildren();
|
||||
|
||||
/**
|
||||
* @brief 获取所有指定类型的组件
|
||||
* @return 组件列表
|
||||
*/
|
||||
template<typename T>
|
||||
std::vector<T*> getComponents();
|
||||
|
||||
// ==================== 标识和属性 ====================
|
||||
|
||||
/**
|
||||
* @brief 设置标签
|
||||
* @param tag 标签值
|
||||
*/
|
||||
void setTag(i32 tag);
|
||||
|
||||
/**
|
||||
* @brief 获取标签
|
||||
* @return 标签值
|
||||
*/
|
||||
i32 getTag() const;
|
||||
|
||||
/**
|
||||
* @brief 设置层级(用于渲染排序)
|
||||
* @param layer 层级值
|
||||
*/
|
||||
void setLayer(u32 layer);
|
||||
|
||||
/**
|
||||
* @brief 获取层级
|
||||
* @return 层级值
|
||||
*/
|
||||
u32 getLayer() const;
|
||||
|
||||
// ==================== 激活和可见性 ====================
|
||||
|
||||
/**
|
||||
* @brief 设置节点是否激活
|
||||
* @param active 是否激活
|
||||
*/
|
||||
void setActive(bool active);
|
||||
|
||||
/**
|
||||
* @brief 检查节点是否激活
|
||||
* @return 如果激活返回 true
|
||||
*/
|
||||
bool isActive() const;
|
||||
|
||||
/**
|
||||
* @brief 检查节点在层级中是否激活
|
||||
* @return 如果节点及其所有父节点都激活返回 true
|
||||
*/
|
||||
bool isActiveInHierarchy() const;
|
||||
|
||||
/**
|
||||
* @brief 设置节点是否可见
|
||||
* @param visible 是否可见
|
||||
*/
|
||||
void setVisible(bool visible);
|
||||
|
||||
/**
|
||||
* @brief 检查节点是否可见
|
||||
* @return 如果可见返回 true
|
||||
*/
|
||||
bool isVisible() const;
|
||||
|
||||
// ==================== 渲染顺序 ====================
|
||||
|
||||
/**
|
||||
* @brief 设置兄弟索引
|
||||
* @param index 索引值
|
||||
*/
|
||||
void setSiblingIndex(i32 index);
|
||||
|
||||
/**
|
||||
* @brief 获取兄弟索引
|
||||
* @return 索引值
|
||||
*/
|
||||
i32 getSiblingIndex() const;
|
||||
|
||||
// ==================== 更新和渲染 ====================
|
||||
|
||||
/**
|
||||
* @brief 更新节点
|
||||
* @param dt 帧间隔时间
|
||||
*/
|
||||
virtual void update(f32 dt);
|
||||
|
||||
/**
|
||||
* @brief 渲染节点
|
||||
*/
|
||||
virtual void render();
|
||||
|
||||
/**
|
||||
* @brief 晚期更新
|
||||
* @param dt 帧间隔时间
|
||||
*/
|
||||
virtual void lateUpdate(f32 dt);
|
||||
|
||||
// ==================== 生命周期回调 ====================
|
||||
|
||||
/**
|
||||
* @brief 进入场景时调用
|
||||
*/
|
||||
virtual void onEnter();
|
||||
|
||||
/**
|
||||
* @brief 退出场景时调用
|
||||
*/
|
||||
virtual void onExit();
|
||||
|
||||
/**
|
||||
* @brief 激活状态改变时调用
|
||||
* @param active 是否激活
|
||||
*/
|
||||
virtual void onActiveChanged(bool active);
|
||||
|
||||
// ==================== 事件系统 ====================
|
||||
|
||||
/**
|
||||
* @brief 订阅事件
|
||||
* @tparam EventT 事件类型
|
||||
* @param callback 回调函数
|
||||
* @return 监听器 ID
|
||||
*/
|
||||
template<typename EventT, typename Fn>
|
||||
EventListenerID on(Fn&& callback) {
|
||||
return eventTarget_.on<EventT>(std::forward<Fn>(callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 取消订阅事件
|
||||
* @tparam EventT 事件类型
|
||||
* @param id 监听器 ID
|
||||
*/
|
||||
template<typename EventT>
|
||||
void off(EventListenerID id) {
|
||||
eventTarget_.off<EventT>(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发布事件
|
||||
* @tparam EventT 事件类型
|
||||
* @param event 事件数据
|
||||
*/
|
||||
template<typename EventT>
|
||||
void emit(const EventT& event) {
|
||||
eventTarget_.emit(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 移除所有事件监听器
|
||||
*/
|
||||
void clearAllEventListeners();
|
||||
|
||||
// ==================== 场景关联 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取所属场景
|
||||
* @return 场景指针
|
||||
*/
|
||||
Scene* getScene();
|
||||
|
||||
/**
|
||||
* @brief 设置所属场景
|
||||
* @param scene 场景指针
|
||||
*/
|
||||
void setScene(Scene* scene);
|
||||
|
||||
// ==================== 销毁 ====================
|
||||
|
||||
/**
|
||||
* @brief 销毁节点
|
||||
* @return 销毁成功返回 true
|
||||
*/
|
||||
bool destroy() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 销毁时的回调
|
||||
*/
|
||||
void onDestroy() override;
|
||||
|
||||
/**
|
||||
* @brief 更新世界变换
|
||||
*/
|
||||
void updateWorldTransform();
|
||||
|
||||
/**
|
||||
* @brief 使变换失效
|
||||
* @param dirtyBit 脏标记位
|
||||
*/
|
||||
void invalidateTransform(TransformBit dirtyBit);
|
||||
|
||||
/**
|
||||
* @brief 设置父节点
|
||||
* @param parent 新父节点
|
||||
*/
|
||||
void setParent(Node* parent);
|
||||
|
||||
/**
|
||||
* @brief 父节点改变时的回调
|
||||
* @param oldParent 旧父节点
|
||||
*/
|
||||
virtual void onSetParent(Node* oldParent);
|
||||
|
||||
/**
|
||||
* @brief 子节点添加时的回调
|
||||
* @param child 添加的子节点
|
||||
*/
|
||||
virtual void onChildAdded(Node* child);
|
||||
|
||||
/**
|
||||
* @brief 子节点移除时的回调
|
||||
* @param child 移除的子节点
|
||||
*/
|
||||
virtual void onChildRemoved(Node* child);
|
||||
|
||||
/**
|
||||
* @brief 更新激活状态
|
||||
*/
|
||||
void updateActiveInHierarchy();
|
||||
|
||||
// 本地变换
|
||||
Vec2 localPosition_{0, 0};
|
||||
float localRotation_{0};
|
||||
Vec2 localScale_{1, 1};
|
||||
|
||||
// 世界变换缓存
|
||||
mutable Vec2 worldPosition_{0, 0};
|
||||
mutable float worldRotation_{0};
|
||||
mutable Vec2 worldScale_{1, 1};
|
||||
mutable Transform2D worldMatrix_;
|
||||
mutable u32 transformFlags_{static_cast<u32>(TransformBit::All)};
|
||||
|
||||
// 层级关系
|
||||
Node* parent_{nullptr};
|
||||
std::vector<Ref<Node>> children_;
|
||||
i32 siblingIndex_{0};
|
||||
|
||||
// 组件
|
||||
std::vector<Ref<Component>> components_;
|
||||
|
||||
// 属性
|
||||
i32 tag_{0};
|
||||
u32 layer_{0};
|
||||
bool active_{true};
|
||||
bool activeInHierarchy_{false};
|
||||
bool visible_{true};
|
||||
|
||||
// 事件目标
|
||||
EventTarget eventTarget_;
|
||||
|
||||
// 场景关联
|
||||
Scene* scene_{nullptr};
|
||||
|
||||
friend class Scene;
|
||||
friend class Component;
|
||||
};
|
||||
|
||||
// ==================== 组件获取模板实现 ====================
|
||||
|
||||
template<typename T>
|
||||
T* Node::getComponent() {
|
||||
for (auto& comp : components_) {
|
||||
T* result = dynamic_cast<T*>(comp.get());
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T* Node::getComponentInChildren() {
|
||||
T* result = getComponent<T>();
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (auto& child : children_) {
|
||||
result = child->getComponentInChildren<T>();
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
std::vector<T*> Node::getComponents() {
|
||||
std::vector<T*> results;
|
||||
for (auto& comp : components_) {
|
||||
T* result = dynamic_cast<T*>(comp.get());
|
||||
if (result) {
|
||||
results.push_back(result);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/Object.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class Node;
|
||||
class Camera;
|
||||
class Model;
|
||||
struct DrawBatch2D;
|
||||
|
||||
/**
|
||||
* @brief 射线检测结果
|
||||
*/
|
||||
struct RaycastResult {
|
||||
Node* node{nullptr};
|
||||
float distance{0.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染场景信息
|
||||
*/
|
||||
struct RenderSceneInfo {
|
||||
std::string name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 渲染场景类
|
||||
*
|
||||
* 管理渲染场景中的相机、模型和绘制批次。
|
||||
* 参考 Cocos Creator 的 RenderScene 设计。
|
||||
*/
|
||||
class RenderScene : public RefCounted {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建渲染场景
|
||||
* @return 渲染场景引用
|
||||
*/
|
||||
static Ref<RenderScene> create();
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
RenderScene();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~RenderScene() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化渲染场景
|
||||
* @param info 场景信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(const RenderSceneInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 销毁渲染场景
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 激活渲染场景
|
||||
*/
|
||||
void activate();
|
||||
|
||||
/**
|
||||
* @brief 更新渲染场景
|
||||
* @param stamp 时间戳
|
||||
*/
|
||||
void update(u32 stamp);
|
||||
|
||||
// ==================== 相机管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 添加相机
|
||||
* @param camera 相机
|
||||
*/
|
||||
void addCamera(Camera* camera);
|
||||
|
||||
/**
|
||||
* @brief 移除相机
|
||||
* @param camera 相机
|
||||
*/
|
||||
void removeCamera(Camera* camera);
|
||||
|
||||
/**
|
||||
* @brief 移除所有相机
|
||||
*/
|
||||
void removeCameras();
|
||||
|
||||
/**
|
||||
* @brief 获取所有相机
|
||||
* @return 相机列表
|
||||
*/
|
||||
const std::vector<Ref<Camera>>& getCameras() const;
|
||||
|
||||
/**
|
||||
* @brief 获取主相机
|
||||
* @return 主相机指针
|
||||
*/
|
||||
Camera* getMainCamera() const;
|
||||
|
||||
/**
|
||||
* @brief 设置主相机
|
||||
* @param camera 相机
|
||||
*/
|
||||
void setMainCamera(Camera* camera);
|
||||
|
||||
// ==================== 模型管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 添加模型
|
||||
* @param model 模型
|
||||
*/
|
||||
void addModel(Model* model);
|
||||
|
||||
/**
|
||||
* @brief 移除模型
|
||||
* @param model 模型
|
||||
*/
|
||||
void removeModel(Model* model);
|
||||
|
||||
/**
|
||||
* @brief 移除所有模型
|
||||
*/
|
||||
void removeModels();
|
||||
|
||||
/**
|
||||
* @brief 获取所有模型
|
||||
* @return 模型列表
|
||||
*/
|
||||
const std::vector<Ref<Model>>& getModels() const;
|
||||
|
||||
/**
|
||||
* @brief 生成模型 ID
|
||||
* @return 模型 ID
|
||||
*/
|
||||
u64 generateModelId();
|
||||
|
||||
// ==================== 批次管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 添加绘制批次
|
||||
* @param batch 批次
|
||||
*/
|
||||
void addBatch(DrawBatch2D* batch);
|
||||
|
||||
/**
|
||||
* @brief 移除绘制批次
|
||||
* @param batch 批次
|
||||
*/
|
||||
void removeBatch(DrawBatch2D* batch);
|
||||
|
||||
/**
|
||||
* @brief 移除所有批次
|
||||
*/
|
||||
void removeBatches();
|
||||
|
||||
/**
|
||||
* @brief 获取所有批次
|
||||
* @return 批次列表
|
||||
*/
|
||||
const std::vector<DrawBatch2D*>& getBatches() const;
|
||||
|
||||
// ==================== 属性访问器 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取场景名称
|
||||
* @return 场景名称
|
||||
*/
|
||||
const std::string& getName() const;
|
||||
|
||||
/**
|
||||
* @brief 设置根节点
|
||||
* @param node 根节点
|
||||
*/
|
||||
void setRootNode(Node* node);
|
||||
|
||||
/**
|
||||
* @brief 获取根节点
|
||||
* @return 根节点
|
||||
*/
|
||||
Node* getRootNode() const;
|
||||
|
||||
// ==================== 全局状态 ====================
|
||||
|
||||
/**
|
||||
* @brief 全局管线状态改变回调
|
||||
*/
|
||||
void onGlobalPipelineStateChanged();
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
u64 modelId_{0};
|
||||
|
||||
std::vector<Ref<Camera>> cameras_;
|
||||
std::vector<Ref<Model>> models_;
|
||||
std::vector<DrawBatch2D*> batches_;
|
||||
|
||||
Camera* mainCamera_{nullptr};
|
||||
Node* rootNode_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/scene-graph/Node.h>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 场景类
|
||||
*
|
||||
* 管理场景图的根节点和场景生命周期。
|
||||
* 参考 Cocos Creator 的 Scene 设计。
|
||||
*/
|
||||
class Scene : public Object {
|
||||
public:
|
||||
/**
|
||||
* @brief 创建场景
|
||||
* @param name 场景名称
|
||||
* @return 场景引用
|
||||
*/
|
||||
static Ref<Scene> create(const std::string& name = "Scene");
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
* @param name 场景名称
|
||||
*/
|
||||
explicit Scene(const std::string& name = "Scene");
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Scene() override;
|
||||
|
||||
// ==================== 根节点管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取根节点
|
||||
* @return 根节点指针
|
||||
*/
|
||||
Node* getRoot();
|
||||
|
||||
/**
|
||||
* @brief 获取根节点(const 版本)
|
||||
* @return 根节点指针
|
||||
*/
|
||||
const Node* getRoot() const;
|
||||
|
||||
// ==================== 节点查找 ====================
|
||||
|
||||
/**
|
||||
* @brief 通过名称查找节点
|
||||
* @param name 节点名称
|
||||
* @return 找到的节点,如果未找到返回 nullptr
|
||||
*/
|
||||
Node* getNodeByName(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 通过标签查找节点
|
||||
* @param tag 标签值
|
||||
* @return 找到的节点,如果未找到返回 nullptr
|
||||
*/
|
||||
Node* getNodeByTag(i32 tag);
|
||||
|
||||
/**
|
||||
* @brief 通过路径查找节点
|
||||
* @param path 路径字符串
|
||||
* @return 找到的节点,如果未找到返回 nullptr
|
||||
*/
|
||||
Node* getNodeByPath(const std::string& path);
|
||||
|
||||
// ==================== 更新和渲染 ====================
|
||||
|
||||
/**
|
||||
* @brief 更新场景
|
||||
* @param dt 帧间隔时间
|
||||
*/
|
||||
void update(f32 dt);
|
||||
|
||||
/**
|
||||
* @brief 渲染场景
|
||||
*/
|
||||
void render();
|
||||
|
||||
/**
|
||||
* @brief 晚期更新
|
||||
* @param dt 帧间隔时间
|
||||
*/
|
||||
void lateUpdate(f32 dt);
|
||||
|
||||
// ==================== 生命周期 ====================
|
||||
|
||||
/**
|
||||
* @brief 场景激活时调用
|
||||
*/
|
||||
void onActivate();
|
||||
|
||||
/**
|
||||
* @brief 场景停用时调用
|
||||
*/
|
||||
void onDeactivate();
|
||||
|
||||
// ==================== 销毁 ====================
|
||||
|
||||
/**
|
||||
* @brief 销毁场景
|
||||
* @return 销毁成功返回 true
|
||||
*/
|
||||
bool destroy() override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 销毁时的回调
|
||||
*/
|
||||
void onDestroy() override;
|
||||
|
||||
Ref<Node> root_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,144 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 服务优先级枚举
|
||||
* 定义服务的初始化顺序,数值越小越先初始化
|
||||
*/
|
||||
enum class ServicePriority : i32 {
|
||||
Core = 0,
|
||||
Event = 100,
|
||||
Timer = 200,
|
||||
Scene = 300,
|
||||
Camera = 400,
|
||||
Resource = 500,
|
||||
Audio = 600,
|
||||
User = 1000
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务状态枚举
|
||||
*/
|
||||
enum class ServiceState {
|
||||
Uninitialized,
|
||||
Initializing,
|
||||
Running,
|
||||
Paused,
|
||||
Stopping,
|
||||
Stopped
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务信息结构体
|
||||
*/
|
||||
struct ServiceInfo {
|
||||
std::string name;
|
||||
ServicePriority priority = ServicePriority::User;
|
||||
ServiceState state = ServiceState::Uninitialized;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务接口基类
|
||||
* 所有服务必须实现此接口,支持依赖注入和生命周期管理
|
||||
*/
|
||||
class IService {
|
||||
friend class ServiceLocator;
|
||||
|
||||
public:
|
||||
virtual ~IService() = default;
|
||||
|
||||
/**
|
||||
* @brief 获取服务信息
|
||||
* @return 服务信息结构体
|
||||
*/
|
||||
virtual ServiceInfo info() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 初始化服务
|
||||
* @return 初始化成功返回 true
|
||||
*/
|
||||
virtual bool init() = 0;
|
||||
|
||||
/**
|
||||
* @brief 关闭服务
|
||||
*/
|
||||
virtual void shutdown() = 0;
|
||||
|
||||
/**
|
||||
* @brief 暂停服务
|
||||
*/
|
||||
virtual void pause() {
|
||||
info_.state = ServiceState::Paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 恢复服务
|
||||
*/
|
||||
virtual void resume() {
|
||||
if (info_.state == ServiceState::Paused) {
|
||||
info_.state = ServiceState::Running;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 更新服务
|
||||
* @param dt 帧间隔时间
|
||||
*/
|
||||
virtual void update(f32 dt) { }
|
||||
|
||||
/**
|
||||
* @brief 检查服务是否已初始化
|
||||
* @return 已初始化返回 true
|
||||
*/
|
||||
virtual bool initialized() const {
|
||||
return info_.state == ServiceState::Running ||
|
||||
info_.state == ServiceState::Paused;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取服务状态
|
||||
* @return 当前服务状态
|
||||
*/
|
||||
ServiceState state() const { return info_.state; }
|
||||
|
||||
/**
|
||||
* @brief 获取服务名称
|
||||
* @return 服务名称
|
||||
*/
|
||||
const std::string& name() const { return info_.name; }
|
||||
|
||||
protected:
|
||||
ServiceInfo info_;
|
||||
|
||||
/**
|
||||
* @brief 设置服务状态
|
||||
* @param state 新状态
|
||||
*/
|
||||
void setState(ServiceState state) { info_.state = state; }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 类型ID生成器
|
||||
* 用于为每种服务类型生成唯一ID
|
||||
*/
|
||||
using ServiceTypeId = size_t;
|
||||
|
||||
namespace detail {
|
||||
inline ServiceTypeId nextServiceTypeId() {
|
||||
static ServiceTypeId id = 0;
|
||||
return ++id;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
ServiceTypeId getServiceTypeId() {
|
||||
static ServiceTypeId id = nextServiceTypeId();
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,307 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <extra2d/core/service_interface.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 服务工厂函数类型
|
||||
*/
|
||||
template <typename T> using ServiceFactory = Fn<Ref<T>()>;
|
||||
|
||||
/**
|
||||
* @brief 服务定位器
|
||||
* 实现依赖注入和服务发现模式,解耦模块间依赖
|
||||
*
|
||||
* 特性:
|
||||
* - 类型安全的服务注册和获取
|
||||
* - 支持服务工厂延迟创建
|
||||
* - 支持服务依赖声明
|
||||
* - 线程安全(读写锁)
|
||||
* - 支持 Mock 测试
|
||||
*/
|
||||
class ServiceLocator {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 服务定位器实例引用
|
||||
*/
|
||||
static ServiceLocator &instance();
|
||||
|
||||
ServiceLocator(const ServiceLocator &) = delete;
|
||||
ServiceLocator &operator=(const ServiceLocator &) = delete;
|
||||
|
||||
/**
|
||||
* @brief 注册服务实例
|
||||
* @tparam T 服务接口类型
|
||||
* @param svc 服务实例
|
||||
*/
|
||||
template <typename T> void add(Ref<T> svc) {
|
||||
static_assert(std::is_base_of_v<IService, T>,
|
||||
"T must derive from IService");
|
||||
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
auto typeId = std::type_index(typeid(T));
|
||||
services_[typeId] = std::static_pointer_cast<IService>(svc);
|
||||
orderedServices_.push_back(svc);
|
||||
sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册服务工厂
|
||||
* @tparam T 服务接口类型
|
||||
* @param fn 服务工厂函数
|
||||
*/
|
||||
template <typename T> void setFactory(ServiceFactory<T> fn) {
|
||||
static_assert(std::is_base_of_v<IService, T>,
|
||||
"T must derive from IService");
|
||||
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
auto typeId = std::type_index(typeid(T));
|
||||
factories_[typeId] = [fn]() -> Ref<IService> {
|
||||
return std::static_pointer_cast<IService>(fn());
|
||||
};
|
||||
|
||||
// 立即创建服务实例并添加到有序列表
|
||||
auto svc = factories_[typeId]();
|
||||
services_[typeId] = svc;
|
||||
orderedServices_.push_back(svc);
|
||||
sort();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取服务实例
|
||||
* @tparam T 服务接口类型
|
||||
* @return 服务实例,不存在返回 nullptr
|
||||
*/
|
||||
template <typename T> Ref<T> get() const {
|
||||
static_assert(std::is_base_of_v<IService, T>,
|
||||
"T must derive from IService");
|
||||
|
||||
auto typeId = std::type_index(typeid(T));
|
||||
|
||||
// 读锁查询
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
|
||||
auto it = services_.find(typeId);
|
||||
if (it != services_.end()) {
|
||||
return std::static_pointer_cast<T>(it->second);
|
||||
}
|
||||
|
||||
auto factoryIt = factories_.find(typeId);
|
||||
if (factoryIt != factories_.end()) {
|
||||
auto svc = factoryIt->second();
|
||||
services_[typeId] = svc;
|
||||
return std::static_pointer_cast<T>(svc);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 尝试获取服务实例(不创建)
|
||||
* @tparam T 服务接口类型
|
||||
* @return 服务实例,不存在返回 nullptr
|
||||
*/
|
||||
template <typename T> Ref<T> tryGet() const {
|
||||
static_assert(std::is_base_of_v<IService, T>,
|
||||
"T must derive from IService");
|
||||
|
||||
auto typeId = std::type_index(typeid(T));
|
||||
|
||||
// 读锁查询
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
auto it = services_.find(typeId);
|
||||
if (it != services_.end()) {
|
||||
return std::static_pointer_cast<T>(it->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查服务是否已注册
|
||||
* @tparam T 服务接口类型
|
||||
* @return 已注册返回 true
|
||||
*/
|
||||
template <typename T> bool has() const {
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
auto typeId = std::type_index(typeid(T));
|
||||
return services_.find(typeId) != services_.end() ||
|
||||
factories_.find(typeId) != factories_.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注销服务
|
||||
* @tparam T 服务接口类型
|
||||
*/
|
||||
template <typename T> void remove() {
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
auto typeId = std::type_index(typeid(T));
|
||||
|
||||
auto it = services_.find(typeId);
|
||||
if (it != services_.end()) {
|
||||
auto svc = it->second;
|
||||
services_.erase(it);
|
||||
|
||||
auto orderIt =
|
||||
std::find(orderedServices_.begin(), orderedServices_.end(), svc);
|
||||
if (orderIt != orderedServices_.end()) {
|
||||
orderedServices_.erase(orderIt);
|
||||
}
|
||||
}
|
||||
|
||||
factories_.erase(typeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 初始化所有已注册的服务
|
||||
* @return 所有服务初始化成功返回 true
|
||||
*/
|
||||
bool init();
|
||||
|
||||
/**
|
||||
* @brief 关闭所有服务
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 更新所有服务
|
||||
* @param dt 帧间隔时间
|
||||
*/
|
||||
void update(f32 dt);
|
||||
|
||||
/**
|
||||
* @brief 暂停所有服务
|
||||
*/
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* @brief 恢复所有服务
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* @brief 获取所有服务(按优先级排序)
|
||||
* @return 服务列表
|
||||
*/
|
||||
std::vector<Ref<IService>> all() const;
|
||||
|
||||
/**
|
||||
* @brief 清空所有服务和工厂
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief 获取已注册服务数量
|
||||
* @return 服务数量
|
||||
*/
|
||||
size_t size() const { return services_.size(); }
|
||||
|
||||
private:
|
||||
ServiceLocator() = default;
|
||||
~ServiceLocator() = default;
|
||||
|
||||
/**
|
||||
* @brief 按优先级排序服务
|
||||
*/
|
||||
void sort();
|
||||
|
||||
mutable std::unordered_map<std::type_index, Ref<IService>> services_;
|
||||
std::unordered_map<std::type_index, std::function<Ref<IService>()>>
|
||||
factories_;
|
||||
std::vector<Ref<IService>> orderedServices_;
|
||||
mutable std::shared_mutex mutex_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务注册器
|
||||
* 用于静态注册服务
|
||||
*/
|
||||
template <typename Interface, typename Implementation> class ServiceRegistrar {
|
||||
public:
|
||||
explicit ServiceRegistrar(ServiceFactory<Interface> fn = nullptr) {
|
||||
if (fn) {
|
||||
ServiceLocator::instance().setFactory<Interface>(fn);
|
||||
} else {
|
||||
ServiceLocator::instance().setFactory<Interface>(
|
||||
[]() -> Ref<Interface> {
|
||||
return ptr::make<Implementation>();
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务注册元数据模板
|
||||
* 使用模板元编程实现编译期服务注册
|
||||
* 通过静态成员变量的初始化触发注册
|
||||
*/
|
||||
template <typename Interface, typename Implementation> struct ServiceAutoReg {
|
||||
/**
|
||||
* @brief 注册标记,访问此变量时触发服务注册
|
||||
*/
|
||||
static const bool registered;
|
||||
|
||||
/**
|
||||
* @brief 执行实际的服务注册
|
||||
* @return true 表示注册成功
|
||||
*/
|
||||
static bool doRegister() {
|
||||
::extra2d::ServiceLocator::instance().setFactory<Interface>(
|
||||
[]() -> ::extra2d::Ref<Interface> {
|
||||
return ::extra2d::ptr::make<Implementation>();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
// 静态成员定义,在此处触发注册
|
||||
template <typename Interface, typename Implementation>
|
||||
const bool ServiceAutoReg<Interface, Implementation>::registered =
|
||||
ServiceAutoReg<Interface, Implementation>::doRegister();
|
||||
|
||||
/**
|
||||
* @brief 服务注册元数据(带自定义工厂)
|
||||
*/
|
||||
template <typename Interface> struct ServiceAutoRegFactory {
|
||||
template <typename Factory> struct Impl {
|
||||
static const bool registered;
|
||||
|
||||
static bool doRegister(Factory fn) {
|
||||
::extra2d::ServiceLocator::instance().setFactory<Interface>(fn);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template <typename Interface>
|
||||
template <typename Factory>
|
||||
const bool ServiceAutoRegFactory<Interface>::Impl<Factory>::registered =
|
||||
ServiceAutoRegFactory<Interface>::Impl<Factory>::doRegister(Factory{});
|
||||
|
||||
/**
|
||||
* @brief 自动注册服务宏(元数据驱动)
|
||||
* 在服务实现类中使用,通过模板元编程实现自动注册
|
||||
* 比静态对象更可靠,不易被编译器优化
|
||||
*/
|
||||
#define E2D_AUTO_REGISTER_SERVICE(Interface, Implementation) \
|
||||
static inline const bool E2D_CONCAT(_service_reg_, __LINE__) = \
|
||||
ServiceAutoReg<Interface, Implementation>::registered
|
||||
|
||||
/**
|
||||
* @brief 带自定义工厂的自动注册服务宏(元数据驱动)
|
||||
*/
|
||||
#define E2D_AUTO_REGISTER_SERVICE_FACTORY(Interface, Factory) \
|
||||
static inline const bool E2D_CONCAT(_service_factory_reg_, __LINE__) = \
|
||||
ServiceAutoRegFactory<Interface>::Impl<Factory>::registered
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,137 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/service_interface.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 服务注册信息
|
||||
*/
|
||||
struct ServiceRegistration {
|
||||
std::string name;
|
||||
ServicePriority priority;
|
||||
std::function<Ref<IService>()> factory;
|
||||
bool enabled = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 服务注册表
|
||||
* 管理服务的注册信息,支持延迟创建和配置
|
||||
*/
|
||||
class ServiceRegistry {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 服务注册表实例引用
|
||||
*/
|
||||
static ServiceRegistry& instance();
|
||||
|
||||
ServiceRegistry(const ServiceRegistry&) = delete;
|
||||
ServiceRegistry& operator=(const ServiceRegistry&) = delete;
|
||||
|
||||
/**
|
||||
* @brief 注册服务
|
||||
* @tparam T 服务接口类型
|
||||
* @tparam Impl 服务实现类型
|
||||
* @param name 服务名称
|
||||
* @param priority 服务优先级
|
||||
*/
|
||||
template<typename T, typename Impl>
|
||||
void add(const std::string& name, ServicePriority priority) {
|
||||
static_assert(std::is_base_of_v<IService, T>,
|
||||
"T must derive from IService");
|
||||
static_assert(std::is_base_of_v<T, Impl>,
|
||||
"Impl must derive from T");
|
||||
|
||||
ServiceRegistration reg;
|
||||
reg.name = name;
|
||||
reg.priority = priority;
|
||||
reg.factory = []() -> Ref<IService> {
|
||||
return std::static_pointer_cast<IService>(ptr::make<Impl>());
|
||||
};
|
||||
registrations_.push_back(reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 注册服务(带工厂函数)
|
||||
* @tparam T 服务接口类型
|
||||
* @param name 服务名称
|
||||
* @param priority 服务优先级
|
||||
* @param factory 工厂函数
|
||||
*/
|
||||
template<typename T>
|
||||
void addWithFactory(
|
||||
const std::string& name,
|
||||
ServicePriority priority,
|
||||
std::function<Ref<T>()> factory) {
|
||||
static_assert(std::is_base_of_v<IService, T>,
|
||||
"T must derive from IService");
|
||||
|
||||
ServiceRegistration reg;
|
||||
reg.name = name;
|
||||
reg.priority = priority;
|
||||
reg.factory = [factory]() -> Ref<IService> {
|
||||
return std::static_pointer_cast<IService>(factory());
|
||||
};
|
||||
registrations_.push_back(reg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用服务
|
||||
* @param name 服务名称
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void setEnabled(const std::string& name, bool enabled);
|
||||
|
||||
/**
|
||||
* @brief 创建所有已注册的服务
|
||||
* 并注册到 ServiceLocator
|
||||
*/
|
||||
void createAll();
|
||||
|
||||
/**
|
||||
* @brief 获取所有注册信息
|
||||
* @return 注册信息列表
|
||||
*/
|
||||
const std::vector<ServiceRegistration>& all() const {
|
||||
return registrations_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 清空所有注册
|
||||
*/
|
||||
void clear() {
|
||||
registrations_.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
ServiceRegistry() = default;
|
||||
~ServiceRegistry() = default;
|
||||
|
||||
std::vector<ServiceRegistration> registrations_;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 自动服务注册器
|
||||
* 在全局作用域使用,自动注册服务
|
||||
*/
|
||||
template<typename Interface, typename Implementation>
|
||||
class AutoServiceRegistrar {
|
||||
public:
|
||||
AutoServiceRegistrar(const std::string& name, ServicePriority priority) {
|
||||
ServiceRegistry::instance().add<Interface, Implementation>(
|
||||
name, priority);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#define E2D_REGISTER_SERVICE_AUTO(Interface, Implementation, Name, Priority) \
|
||||
namespace { \
|
||||
static ::extra2d::AutoServiceRegistrar<Interface, Implementation> \
|
||||
E2D_CONCAT(auto_service_registrar_, __LINE__)(Name, Priority); \
|
||||
}
|
||||
|
|
@ -0,0 +1,172 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/base/Scheduler.h>
|
||||
#include <chrono>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
class EventBus;
|
||||
|
||||
/**
|
||||
* @brief 引擎状态
|
||||
*/
|
||||
enum class EngineStatus {
|
||||
Start,
|
||||
Pause,
|
||||
Resume,
|
||||
Close
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 引擎配置
|
||||
*/
|
||||
struct EngineConfig {
|
||||
i32 fps{60};
|
||||
bool fixedTimeStep{true};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 引擎核心类
|
||||
*
|
||||
* 管理主循环、时间系统和调度器。
|
||||
* 参考 Cocos Creator 的 Engine 设计。
|
||||
*/
|
||||
class Engine {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 引擎实例
|
||||
*/
|
||||
static Engine* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化引擎
|
||||
* @return 成功返回 0
|
||||
*/
|
||||
i32 init();
|
||||
|
||||
/**
|
||||
* @brief 关闭引擎
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @brief 执行一帧
|
||||
*/
|
||||
void tick();
|
||||
|
||||
/**
|
||||
* @brief 暂停引擎
|
||||
*/
|
||||
void pause();
|
||||
|
||||
/**
|
||||
* @brief 恢复引擎
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
* @return 如果已初始化返回 true
|
||||
*/
|
||||
bool isInited() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否暂停
|
||||
* @return 如果暂停返回 true
|
||||
*/
|
||||
bool isPaused() const;
|
||||
|
||||
/**
|
||||
* @brief 设置目标帧率
|
||||
* @param fps 目标帧率
|
||||
*/
|
||||
void setPreferredFramesPerSecond(i32 fps);
|
||||
|
||||
/**
|
||||
* @brief 获取目标帧率
|
||||
* @return 目标帧率
|
||||
*/
|
||||
i32 getPreferredFramesPerSecond() const;
|
||||
|
||||
/**
|
||||
* @brief 获取总帧数
|
||||
* @return 总帧数
|
||||
*/
|
||||
u32 getTotalFrames() const;
|
||||
|
||||
/**
|
||||
* @brief 获取帧间隔时间
|
||||
* @return 帧间隔时间(秒)
|
||||
*/
|
||||
f32 getDeltaTime() const;
|
||||
|
||||
/**
|
||||
* @brief 获取总运行时间
|
||||
* @return 总运行时间(秒)
|
||||
*/
|
||||
f32 getTotalTime() const;
|
||||
|
||||
/**
|
||||
* @brief 获取帧开始时间
|
||||
* @return 帧开始时间(秒)
|
||||
*/
|
||||
f32 getFrameStartTime() const;
|
||||
|
||||
/**
|
||||
* @brief 获取调度器
|
||||
* @return 调度器指针
|
||||
*/
|
||||
Scheduler* getScheduler();
|
||||
|
||||
/**
|
||||
* @brief 获取事件总线
|
||||
* @return 事件总线指针
|
||||
*/
|
||||
EventBus* getEventBus();
|
||||
|
||||
/**
|
||||
* @brief 设置时间缩放
|
||||
* @param scale 时间缩放因子
|
||||
*/
|
||||
void setTimeScale(f32 scale);
|
||||
|
||||
/**
|
||||
* @brief 获取时间缩放
|
||||
* @return 时间缩放因子
|
||||
*/
|
||||
f32 getTimeScale() const;
|
||||
|
||||
/**
|
||||
* @brief 计算帧间隔时间
|
||||
* @return 帧间隔时间(秒)
|
||||
*/
|
||||
f32 calculateDeltaTime();
|
||||
|
||||
private:
|
||||
Engine();
|
||||
~Engine();
|
||||
|
||||
Engine(const Engine&) = delete;
|
||||
Engine& operator=(const Engine&) = delete;
|
||||
|
||||
static Engine* instance_;
|
||||
|
||||
Unique<Scheduler> scheduler_;
|
||||
EventBus* eventBus_{nullptr};
|
||||
|
||||
i64 preferredNanosecondsPerFrame_{16666667L};
|
||||
u32 totalFrames_{0};
|
||||
f32 deltaTime_{0.0f};
|
||||
f32 totalTime_{0.0f};
|
||||
f32 frameStartTime_{0.0f};
|
||||
|
||||
std::chrono::high_resolution_clock::time_point lastFrameTime_;
|
||||
std::chrono::high_resolution_clock::time_point startTime_;
|
||||
|
||||
bool inited_{false};
|
||||
bool paused_{false};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <variant>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 事件类型枚举
|
||||
// ============================================================================
|
||||
enum class EventType {
|
||||
None = 0,
|
||||
|
||||
// 窗口事件
|
||||
WindowClose,
|
||||
WindowResize,
|
||||
WindowFocus,
|
||||
WindowLostFocus,
|
||||
WindowMoved,
|
||||
|
||||
// 键盘事件
|
||||
KeyPressed,
|
||||
KeyReleased,
|
||||
KeyRepeat,
|
||||
|
||||
// 鼠标事件
|
||||
MouseButtonPressed,
|
||||
MouseButtonReleased,
|
||||
MouseMoved,
|
||||
MouseScrolled,
|
||||
|
||||
// UI 事件
|
||||
UIHoverEnter,
|
||||
UIHoverExit,
|
||||
UIPressed,
|
||||
UIReleased,
|
||||
UIClicked,
|
||||
|
||||
// 游戏手柄事件
|
||||
GamepadConnected,
|
||||
GamepadDisconnected,
|
||||
GamepadButtonPressed,
|
||||
GamepadButtonReleased,
|
||||
GamepadAxisMoved,
|
||||
|
||||
// 触摸事件 (移动端)
|
||||
TouchBegan,
|
||||
TouchMoved,
|
||||
TouchEnded,
|
||||
TouchCancelled,
|
||||
|
||||
// 自定义事件
|
||||
Custom
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 键盘事件数据
|
||||
// ============================================================================
|
||||
struct KeyEvent {
|
||||
i32 key;
|
||||
i32 scancode;
|
||||
i32 mods; // 修饰键 (Shift, Ctrl, Alt, etc.)
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 鼠标事件数据
|
||||
// ============================================================================
|
||||
struct MouseButtonEvent {
|
||||
i32 button;
|
||||
i32 mods;
|
||||
Vec2 pos;
|
||||
};
|
||||
|
||||
struct MouseMoveEvent {
|
||||
Vec2 pos;
|
||||
Vec2 delta;
|
||||
};
|
||||
|
||||
struct MouseScrollEvent {
|
||||
Vec2 offset;
|
||||
Vec2 pos;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 窗口事件数据
|
||||
// ============================================================================
|
||||
struct WindowResizeEvent {
|
||||
i32 w;
|
||||
i32 h;
|
||||
};
|
||||
|
||||
struct WindowMoveEvent {
|
||||
i32 x;
|
||||
i32 y;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 游戏手柄事件数据
|
||||
// ============================================================================
|
||||
struct GamepadButtonEvent {
|
||||
i32 gamepadId;
|
||||
i32 button;
|
||||
};
|
||||
|
||||
struct GamepadAxisEvent {
|
||||
i32 gamepadId;
|
||||
i32 axis;
|
||||
f32 value;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 触摸事件数据
|
||||
// ============================================================================
|
||||
struct TouchEvent {
|
||||
i32 touchId;
|
||||
Vec2 pos;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 自定义事件数据
|
||||
// ============================================================================
|
||||
struct CustomEvent {
|
||||
u32 id;
|
||||
void *data;
|
||||
};
|
||||
|
||||
// ============================================================================
|
||||
// 事件结构
|
||||
// ============================================================================
|
||||
struct Event {
|
||||
EventType type = EventType::None;
|
||||
f64 timestamp = 0.0;
|
||||
bool handled = false;
|
||||
|
||||
// 事件数据联合体
|
||||
std::variant<std::monostate, KeyEvent, MouseButtonEvent, MouseMoveEvent,
|
||||
MouseScrollEvent, WindowResizeEvent, WindowMoveEvent,
|
||||
GamepadButtonEvent, GamepadAxisEvent, TouchEvent, CustomEvent>
|
||||
data;
|
||||
|
||||
// 便捷访问方法
|
||||
bool window() const {
|
||||
return type == EventType::WindowClose || type == EventType::WindowResize ||
|
||||
type == EventType::WindowFocus ||
|
||||
type == EventType::WindowLostFocus || type == EventType::WindowMoved;
|
||||
}
|
||||
|
||||
bool keyboard() const {
|
||||
return type == EventType::KeyPressed || type == EventType::KeyReleased ||
|
||||
type == EventType::KeyRepeat;
|
||||
}
|
||||
|
||||
bool mouse() const {
|
||||
return type == EventType::MouseButtonPressed ||
|
||||
type == EventType::MouseButtonReleased ||
|
||||
type == EventType::MouseMoved || type == EventType::MouseScrolled;
|
||||
}
|
||||
|
||||
// 静态工厂方法
|
||||
static Event windowResize(i32 w, i32 h);
|
||||
static Event windowClose();
|
||||
static Event keyPress(i32 key, i32 scancode, i32 mods);
|
||||
static Event keyRelease(i32 key, i32 scancode, i32 mods);
|
||||
static Event mousePress(i32 btn, i32 mods, Vec2 pos);
|
||||
static Event mouseRelease(i32 btn, i32 mods, Vec2 pos);
|
||||
static Event mouseMove(Vec2 pos, Vec2 delta);
|
||||
static Event mouseScroll(Vec2 offset, Vec2 pos);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/event/event.h>
|
||||
#include <functional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 事件监听器 ID
|
||||
// ============================================================================
|
||||
using ListenerID = u64;
|
||||
|
||||
// ============================================================================
|
||||
// 事件分发器
|
||||
// ============================================================================
|
||||
class EventDispatcher {
|
||||
public:
|
||||
using EventFn = Fn<void(Event &)>;
|
||||
|
||||
EventDispatcher();
|
||||
~EventDispatcher() = default;
|
||||
|
||||
// 添加监听器
|
||||
ListenerID on(EventType type, EventFn fn);
|
||||
|
||||
// 移除监听器
|
||||
void off(ListenerID id);
|
||||
void offAll(EventType type);
|
||||
void offAll();
|
||||
|
||||
// 分发事件
|
||||
void dispatch(Event &event);
|
||||
void dispatch(const Event &event);
|
||||
|
||||
// 处理事件队列
|
||||
void process(class EventQueue &queue);
|
||||
|
||||
// 统计
|
||||
size_t listenerCount(EventType type) const;
|
||||
size_t totalListeners() const;
|
||||
|
||||
private:
|
||||
struct Listener {
|
||||
ListenerID id;
|
||||
EventType type;
|
||||
EventFn fn;
|
||||
};
|
||||
|
||||
std::unordered_map<EventType, std::vector<Listener>> listeners_;
|
||||
ListenerID nextId_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/ring_buffer.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/event/event.h>
|
||||
#include <mutex>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ============================================================================
|
||||
// 事件队列 - 线程安全的事件队列
|
||||
// ============================================================================
|
||||
class EventQueue {
|
||||
public:
|
||||
static constexpr size_t DEFAULT_CAPACITY = 1024;
|
||||
|
||||
EventQueue();
|
||||
~EventQueue() = default;
|
||||
|
||||
// 添加事件到队列
|
||||
bool push(const Event &event);
|
||||
bool push(Event &&event);
|
||||
|
||||
// 从队列取出事件
|
||||
bool poll(Event &event);
|
||||
|
||||
// 查看队列头部事件(不移除)
|
||||
bool peek(Event &event) const;
|
||||
|
||||
// 清空队列
|
||||
void clear();
|
||||
|
||||
// 队列状态
|
||||
bool empty() const;
|
||||
size_t size() const;
|
||||
size_t capacity() const { return buffer_.capacity(); }
|
||||
|
||||
private:
|
||||
RingBuffer<Event, DEFAULT_CAPACITY> buffer_;
|
||||
mutable std::mutex mutex_; // 用于peek和clear的互斥
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -3,45 +3,79 @@
|
|||
// Extra2D - 统一入口头文件
|
||||
// 包含所有公共 API
|
||||
|
||||
// Base
|
||||
#include <extra2d/base/RefCounted.h>
|
||||
#include <extra2d/base/Scheduler.h>
|
||||
|
||||
// Core
|
||||
#include <extra2d/core/color.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/Object.h>
|
||||
|
||||
// Core - Event
|
||||
#include <extra2d/core/event/EventBus.h>
|
||||
#include <extra2d/core/event/EventTarget.h>
|
||||
|
||||
// Core - Scene Graph
|
||||
#include <extra2d/core/scene-graph/Node.h>
|
||||
#include <extra2d/core/scene-graph/Scene.h>
|
||||
#include <extra2d/core/scene-graph/Component.h>
|
||||
#include <extra2d/core/scene-graph/Camera.h>
|
||||
#include <extra2d/core/scene-graph/Model.h>
|
||||
#include <extra2d/core/scene-graph/RenderScene.h>
|
||||
|
||||
// Core - Assets
|
||||
#include <extra2d/core/assets/Asset.h>
|
||||
#include <extra2d/core/assets/ImageAsset.h>
|
||||
#include <extra2d/core/assets/Texture2D.h>
|
||||
#include <extra2d/core/assets/AssetManager.h>
|
||||
#include <extra2d/core/assets/Material.h>
|
||||
|
||||
// Platform
|
||||
#include <extra2d/platform/glfw/glfw_window.h>
|
||||
#include <extra2d/platform/keys.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/platform/SDLHelper.h>
|
||||
#include <extra2d/platform/Window.h>
|
||||
#include <extra2d/platform/Input.h>
|
||||
|
||||
// Event
|
||||
#include <extra2d/event/event.h>
|
||||
#include <extra2d/event/event_dispatcher.h>
|
||||
#include <extra2d/event/event_queue.h>
|
||||
// Engine
|
||||
#include <extra2d/engine/Engine.h>
|
||||
|
||||
// Application
|
||||
#include <extra2d/application/Application.h>
|
||||
|
||||
// GFX - Base
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <extra2d/gfx/GFXObject.h>
|
||||
#include <extra2d/gfx/GFXBuffer.h>
|
||||
#include <extra2d/gfx/GFXTexture.h>
|
||||
#include <extra2d/gfx/GFXShader.h>
|
||||
#include <extra2d/gfx/GFXPipeline.h>
|
||||
#include <extra2d/gfx/GFXInputAssembler.h>
|
||||
#include <extra2d/gfx/GFXDevice.h>
|
||||
|
||||
// GFX - OpenGL Backend
|
||||
#include <extra2d/gfx/opengl/GLDevice.h>
|
||||
#include <extra2d/gfx/opengl/GLBuffer.h>
|
||||
#include <extra2d/gfx/opengl/GLTexture.h>
|
||||
#include <extra2d/gfx/opengl/GLShader.h>
|
||||
#include <extra2d/gfx/opengl/GLPipelineState.h>
|
||||
#include <extra2d/gfx/opengl/GLInputAssembler.h>
|
||||
|
||||
// 2D Renderer
|
||||
#include <extra2d/2d/renderer/RenderEntity.h>
|
||||
#include <extra2d/2d/renderer/RenderDrawInfo.h>
|
||||
#include <extra2d/2d/renderer/UIMeshBuffer.h>
|
||||
#include <extra2d/2d/renderer/Batcher2d.h>
|
||||
#include <extra2d/2d/renderer/StencilManager.h>
|
||||
|
||||
// 2D Components
|
||||
#include <extra2d/2d/components/SpriteFrame.h>
|
||||
#include <extra2d/2d/components/Sprite.h>
|
||||
|
||||
// Utils
|
||||
#include <extra2d/utils/random.h>
|
||||
#include <extra2d/utils/timer.h>
|
||||
|
||||
// Services
|
||||
#include <extra2d/services/asset_service.h>
|
||||
#include <extra2d/services/event_service.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <extra2d/services/timer_service.h>
|
||||
|
||||
// Asset
|
||||
#include <extra2d/asset/asset.h>
|
||||
#include <extra2d/asset/asset_cache.h>
|
||||
#include <extra2d/asset/asset_handle.h>
|
||||
#include <extra2d/asset/asset_loader.h>
|
||||
#include <extra2d/asset/asset_pack.h>
|
||||
#include <extra2d/asset/asset_types.h>
|
||||
#include <extra2d/asset/data_processor.h>
|
||||
|
||||
// Application
|
||||
#include <extra2d/app/application.h>
|
||||
|
||||
#ifdef __SWITCH__
|
||||
#include <switch.h>
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,170 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXObject.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief 缓冲区创建信息
|
||||
*/
|
||||
struct BufferInfo {
|
||||
BufferUsage usage{BufferUsage::None};
|
||||
MemoryUsage memUsage{MemoryUsage::Device};
|
||||
u32 size{0};
|
||||
u32 stride{0};
|
||||
BufferFlag flags{BufferFlag::None};
|
||||
const void* initData{nullptr};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 缓冲区视图信息
|
||||
*/
|
||||
struct BufferViewInfo {
|
||||
class Buffer* buffer{nullptr};
|
||||
u32 offset{0};
|
||||
u32 range{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 缓冲区抽象基类
|
||||
*
|
||||
* GPU 缓冲区的抽象接口,支持顶点缓冲、索引缓冲、Uniform 缓冲等。
|
||||
*/
|
||||
class Buffer : public GFXObject {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
Buffer();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Buffer() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化缓冲区
|
||||
* @param info 缓冲区信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(const BufferInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 初始化缓冲区视图
|
||||
* @param info 视图信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(const BufferViewInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 销毁缓冲区
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 调整缓冲区大小
|
||||
* @param size 新大小
|
||||
*/
|
||||
void resize(u32 size);
|
||||
|
||||
/**
|
||||
* @brief 更新缓冲区数据
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
*/
|
||||
virtual void update(const void* data, u32 size) = 0;
|
||||
|
||||
/**
|
||||
* @brief 写入数据到指定偏移
|
||||
* @param data 数据指针
|
||||
* @param offset 偏移量
|
||||
* @param size 数据大小
|
||||
*/
|
||||
void write(const void* data, u32 offset, u32 size);
|
||||
|
||||
/**
|
||||
* @brief 获取用途
|
||||
* @return 用途标志
|
||||
*/
|
||||
BufferUsage getUsage() const;
|
||||
|
||||
/**
|
||||
* @brief 获取内存用途
|
||||
* @return 内存用途
|
||||
*/
|
||||
MemoryUsage getMemUsage() const;
|
||||
|
||||
/**
|
||||
* @brief 获取缓冲区大小
|
||||
* @return 大小(字节)
|
||||
*/
|
||||
u32 getSize() const;
|
||||
|
||||
/**
|
||||
* @brief 获取步长
|
||||
* @return 步长(字节)
|
||||
*/
|
||||
u32 getStride() const;
|
||||
|
||||
/**
|
||||
* @brief 获取元素数量
|
||||
* @return 元素数量
|
||||
*/
|
||||
u32 getCount() const;
|
||||
|
||||
/**
|
||||
* @brief 获取标志
|
||||
* @return 标志
|
||||
*/
|
||||
BufferFlag getFlags() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否为视图
|
||||
* @return 如果是视图返回 true
|
||||
*/
|
||||
bool isBufferView() const;
|
||||
|
||||
/**
|
||||
* @brief 获取类型名称
|
||||
* @return "Buffer"
|
||||
*/
|
||||
const char* getTypeName() const override { return "Buffer"; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 子类实现初始化
|
||||
*/
|
||||
virtual bool doInit(const BufferInfo& info) = 0;
|
||||
|
||||
/**
|
||||
* @brief 子类实现视图初始化
|
||||
*/
|
||||
virtual bool doInit(const BufferViewInfo& info) = 0;
|
||||
|
||||
/**
|
||||
* @brief 子类实现销毁
|
||||
*/
|
||||
virtual void doDestroy() = 0;
|
||||
|
||||
/**
|
||||
* @brief 子类实现调整大小
|
||||
*/
|
||||
virtual void doResize(u32 size) = 0;
|
||||
|
||||
BufferUsage usage_{BufferUsage::None};
|
||||
MemoryUsage memUsage_{MemoryUsage::Device};
|
||||
u32 size_{0};
|
||||
u32 stride_{0};
|
||||
u32 count_{0};
|
||||
BufferFlag flags_{BufferFlag::None};
|
||||
bool isView_{false};
|
||||
|
||||
Buffer* sourceBuffer_{nullptr};
|
||||
u32 viewOffset_{0};
|
||||
u32 viewRange_{0};
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXObject.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
class Buffer;
|
||||
class Texture;
|
||||
class RenderPass;
|
||||
class Framebuffer;
|
||||
class PipelineState;
|
||||
class InputAssembler;
|
||||
class DescriptorSet;
|
||||
|
||||
/**
|
||||
* @brief 绘制区域信息
|
||||
*/
|
||||
struct Viewport {
|
||||
float left{0.0f};
|
||||
float top{1.0f};
|
||||
float width{1.0f};
|
||||
float height{1.0f};
|
||||
float minDepth{0.0f};
|
||||
float maxDepth{1.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 矩形区域
|
||||
*/
|
||||
struct Rect {
|
||||
float x{0.0f};
|
||||
float y{0.0f};
|
||||
float width{1.0f};
|
||||
float height{0.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 清除标志位运算
|
||||
*/
|
||||
inline ClearFlagBit operator|(ClearFlagBit a, ClearFlagBit b) {
|
||||
return static_cast<ClearFlagBit>(static_cast<u32>(a) | static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline ClearFlagBit& operator|=(ClearFlagBit& a, ClearFlagBit b) {
|
||||
a = a | b;
|
||||
return a;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 命令缓冲区类
|
||||
*
|
||||
* 记录和执行渲染命令。
|
||||
* 参考 Cocos Creator 的 CommandBuffer 设计。
|
||||
*/
|
||||
class CommandBuffer : public Object {
|
||||
public:
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~CommandBuffer() override;
|
||||
|
||||
// ==================== 绑定命令 ====================
|
||||
|
||||
/**
|
||||
* @brief 绑定管线状态
|
||||
* @param pipelineState 管线状态
|
||||
*/
|
||||
virtual void bindPipelineState(PipelineState* pipelineState) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绑定输入装配器
|
||||
* @param inputAssembler 输入装配器
|
||||
*/
|
||||
virtual void bindInputAssembler(InputAssembler* inputAssembler) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绑定描述符集
|
||||
* @param descriptorSet 描述符集
|
||||
* @param setIndex 集合索引
|
||||
*/
|
||||
virtual void bindDescriptorSet(DescriptorSet* descriptorSet, u32 setIndex) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绑定帧缓冲
|
||||
* @param framebuffer 帧缓冲
|
||||
* @param renderPass 渲染通道
|
||||
*/
|
||||
virtual void bindFramebuffer(Framebuffer* framebuffer, RenderPass* renderPass) = 0;
|
||||
|
||||
// ==================== 设置命令 ====================
|
||||
|
||||
/**
|
||||
* @brief 设置视口
|
||||
* @param viewport 视口
|
||||
*/
|
||||
virtual void setViewport(const Viewport& viewport) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置矩形区域
|
||||
* @param rect 矩形区域
|
||||
*/
|
||||
virtual void setScissor(const Rect& rect) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置混合常量
|
||||
* @param constants 常量数组
|
||||
*/
|
||||
virtual void setBlendConstants(const float* constants) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置深度偏移
|
||||
* @param bias 偏移值
|
||||
*/
|
||||
virtual void setDepthBias(float bias, float slope, float clamp) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置模板参考值
|
||||
* @param front 前面参考值
|
||||
* @param back 后面参考值
|
||||
*/
|
||||
virtual void setStencilReference(u32 front, u32 back) = 0;
|
||||
|
||||
// ==================== 绘制命令 ====================
|
||||
|
||||
/**
|
||||
* @brief 清除颜色
|
||||
* @param color 颜色值
|
||||
*/
|
||||
virtual void clearColor(const Color& color) = 0;
|
||||
|
||||
/**
|
||||
* @brief 清除深度模板
|
||||
* @param depth 深度值
|
||||
* @param stencil 模板值
|
||||
*/
|
||||
virtual void clearDepthStencil(float depth, u32 stencil) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制
|
||||
* @param vertexCount 顶点数量
|
||||
* @param firstVertex 起始顶点
|
||||
* @param vertexCount 顶点数量
|
||||
*/
|
||||
virtual void drawArrays(u32 vertexCount, u32 firstVertex = 0, u32 instanceCount = 1, u32 firstInstance = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制索引
|
||||
* @param indexCount 索引数量
|
||||
* @param firstIndex 起始索引
|
||||
* @param instanceCount 实例数量
|
||||
* @param firstInstance 起始实例
|
||||
*/
|
||||
virtual void drawIndexed(u32 indexCount, u32 firstIndex = 0, u32 instanceCount = 1, u32 firstInstance = 0) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制实例化
|
||||
* @param inputAssembler 输入装配器
|
||||
* @param instanceCount 实例数量
|
||||
*/
|
||||
virtual void drawInstanced(InputAssembler* inputAssembler, u32 instanceCount) = 0;
|
||||
|
||||
// ==================== 更新命令 ====================
|
||||
|
||||
/**
|
||||
* @brief 更新缓冲区
|
||||
* @param buffer 缓冲区
|
||||
* @param offset 偏移量
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
*/
|
||||
virtual void updateBuffer(Buffer* buffer, u32 offset, const void* data, u32 size) = 0;
|
||||
|
||||
/**
|
||||
* @brief 复制缓冲区
|
||||
* @param src 源缓冲区
|
||||
* @param srcOffset 源偏移量
|
||||
* @param dst 目标缓冲区
|
||||
* @param dstOffset 目标偏移量
|
||||
* @param size 大小
|
||||
*/
|
||||
virtual void copyBuffer(Buffer* src, u32 srcOffset, Buffer* dst, u32 dstOffset, u32 size) = 0;
|
||||
|
||||
/**
|
||||
* @brief 复制纹理
|
||||
* @param src 源纹理
|
||||
* @param dst 目标纹理
|
||||
*/
|
||||
virtual void copyTexture(Texture* src, Texture* dst) = 0;
|
||||
|
||||
/**
|
||||
* @brief 执行渲染通道
|
||||
* @param renderPass 渲染通道
|
||||
* @param callback 回调函数
|
||||
*/
|
||||
virtual void executeRenderPass(RenderPass* renderPass, const std::function<void()>& callback) = 0;
|
||||
|
||||
protected:
|
||||
CommandBuffer() = default;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,450 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief 图形 API 类型
|
||||
*/
|
||||
enum class API : u32 {
|
||||
Unknown,
|
||||
GLES2,
|
||||
GLES3,
|
||||
Metal,
|
||||
Vulkan,
|
||||
OpenGL,
|
||||
WebGL,
|
||||
WebGL2,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 设备特性
|
||||
*/
|
||||
enum class Feature : u32 {
|
||||
ElementIndexUint,
|
||||
InstancedArrays,
|
||||
MultipleRenderTargets,
|
||||
BlendMinmax,
|
||||
ComputeShader,
|
||||
InputAttachmentBenefit,
|
||||
SubpassColorInput,
|
||||
SubpassDepthStencilInput,
|
||||
MultiSampleResolveDepthStencil,
|
||||
Count,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 格式类型
|
||||
*/
|
||||
enum class Format : u32 {
|
||||
Unknown,
|
||||
|
||||
// 8-bit formats
|
||||
R8,
|
||||
R8SN,
|
||||
R8UI,
|
||||
R8I,
|
||||
|
||||
// 16-bit formats
|
||||
R16UI,
|
||||
R16I,
|
||||
R16F,
|
||||
RG8,
|
||||
RG8SN,
|
||||
RG8UI,
|
||||
RG8I,
|
||||
|
||||
// 32-bit formats
|
||||
R32UI,
|
||||
R32I,
|
||||
R32F,
|
||||
RG16UI,
|
||||
RG16I,
|
||||
RG16F,
|
||||
RGBA8,
|
||||
RGBA8SN,
|
||||
RGBA8UI,
|
||||
RGBA8I,
|
||||
BGRA8,
|
||||
|
||||
// Packed formats
|
||||
RGB10A2,
|
||||
RGB10A2UI,
|
||||
R11G11B10F,
|
||||
|
||||
// 64-bit formats
|
||||
RG32UI,
|
||||
RG32I,
|
||||
RG32F,
|
||||
RGBA16UI,
|
||||
RGBA16I,
|
||||
RGBA16F,
|
||||
|
||||
// 128-bit formats
|
||||
RGBA32UI,
|
||||
RGBA32I,
|
||||
RGBA32F,
|
||||
|
||||
// Depth formats
|
||||
Depth,
|
||||
DepthStencil,
|
||||
Depth24Stencil8,
|
||||
|
||||
// Compressed formats
|
||||
BC1,
|
||||
BC1Alpha,
|
||||
BC2,
|
||||
BC3,
|
||||
BC4,
|
||||
BC4SN,
|
||||
BC5,
|
||||
BC5SN,
|
||||
BC6H,
|
||||
BC6HUF,
|
||||
BC7,
|
||||
|
||||
Count,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 格式特性标志
|
||||
*/
|
||||
enum class FormatFeature : u32 {
|
||||
None = 0,
|
||||
Sampled = 1 << 0,
|
||||
Storage = 1 << 1,
|
||||
ColorAttachment = 1 << 2,
|
||||
DepthStencilAttachment = 1 << 3,
|
||||
Blend = 1 << 4,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 缓冲区用途标志
|
||||
*/
|
||||
enum class BufferUsage : u32 {
|
||||
None = 0,
|
||||
TransferSrc = 1 << 0,
|
||||
TransferDst = 1 << 1,
|
||||
Index = 1 << 2,
|
||||
Vertex = 1 << 3,
|
||||
Uniform = 1 << 4,
|
||||
Storage = 1 << 5,
|
||||
Indirect = 1 << 6,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 缓冲区标志
|
||||
*/
|
||||
enum class BufferFlag : u32 {
|
||||
None = 0,
|
||||
EnableStagingWrite = 1 << 0,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 内存用途
|
||||
*/
|
||||
enum class MemoryUsage : u32 {
|
||||
None = 0,
|
||||
Device = 1 << 0,
|
||||
Host = 1 << 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理类型
|
||||
*/
|
||||
enum class TextureType : u32 {
|
||||
Tex1D,
|
||||
Tex2D,
|
||||
Tex3D,
|
||||
Cube,
|
||||
Tex1DArray,
|
||||
Tex2DArray,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理用途标志
|
||||
*/
|
||||
enum class TextureUsage : u32 {
|
||||
None = 0,
|
||||
TransferSrc = 1 << 0,
|
||||
TransferDst = 1 << 1,
|
||||
Sampled = 1 << 2,
|
||||
Storage = 1 << 3,
|
||||
ColorAttachment = 1 << 4,
|
||||
DepthStencilAttachment = 1 << 5,
|
||||
InputAttachment = 1 << 6,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理标志
|
||||
*/
|
||||
enum class TextureFlag : u32 {
|
||||
None = 0,
|
||||
GenMipmaps = 1 << 0,
|
||||
ExternalOES = 1 << 1,
|
||||
LazilyAllocated = 1 << 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 采样计数
|
||||
*/
|
||||
enum class SampleCount : u32 {
|
||||
X1 = 1,
|
||||
X2 = 2,
|
||||
X4 = 4,
|
||||
X8 = 8,
|
||||
X16 = 16,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器阶段标志
|
||||
*/
|
||||
enum class ShaderStage : u32 {
|
||||
None = 0,
|
||||
Vertex = 1 << 0,
|
||||
Geometry = 1 << 1,
|
||||
Fragment = 1 << 2,
|
||||
Compute = 1 << 3,
|
||||
All = Vertex | Geometry | Fragment | Compute,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 图元模式
|
||||
*/
|
||||
enum class PrimitiveMode : u32 {
|
||||
PointList,
|
||||
LineList,
|
||||
LineStrip,
|
||||
LineLoop,
|
||||
TriangleList,
|
||||
TriangleStrip,
|
||||
TriangleFan,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 多边形模式
|
||||
*/
|
||||
enum class PolygonMode : u32 {
|
||||
Fill,
|
||||
Point,
|
||||
Line,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 剔除模式
|
||||
*/
|
||||
enum class CullMode : u32 {
|
||||
None,
|
||||
Front,
|
||||
Back,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 深度比较函数
|
||||
*/
|
||||
enum class ComparisonFunc : u32 {
|
||||
Never,
|
||||
Less,
|
||||
Equal,
|
||||
LessEqual,
|
||||
Greater,
|
||||
NotEqual,
|
||||
GreaterEqual,
|
||||
Always,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 模板操作
|
||||
*/
|
||||
enum class StencilOp : u32 {
|
||||
Zero,
|
||||
Keep,
|
||||
Replace,
|
||||
Increment,
|
||||
Decrement,
|
||||
Invert,
|
||||
IncrementWrap,
|
||||
DecrementWrap,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 混合因子
|
||||
*/
|
||||
enum class BlendFactor : u32 {
|
||||
Zero,
|
||||
One,
|
||||
SrcAlpha,
|
||||
DstAlpha,
|
||||
OneMinusSrcAlpha,
|
||||
OneMinusDstAlpha,
|
||||
SrcColor,
|
||||
DstColor,
|
||||
OneMinusSrcColor,
|
||||
OneMinusDstColor,
|
||||
SrcAlphaSaturate,
|
||||
ConstantColor,
|
||||
OneMinusConstantColor,
|
||||
ConstantAlpha,
|
||||
OneMinusConstantAlpha,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 混合操作
|
||||
*/
|
||||
enum class BlendOp : u32 {
|
||||
Add,
|
||||
Subtract,
|
||||
ReverseSubtract,
|
||||
Min,
|
||||
Max,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 管线绑定点
|
||||
*/
|
||||
enum class PipelineBindPoint : u32 {
|
||||
Graphics,
|
||||
Compute,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 动态状态标志
|
||||
*/
|
||||
enum class DynamicStateFlag : u32 {
|
||||
None = 0,
|
||||
Viewport = 1 << 0,
|
||||
Scissor = 1 << 1,
|
||||
LineWidth = 1 << 2,
|
||||
DepthBias = 1 << 3,
|
||||
BlendConstants = 1 << 4,
|
||||
DepthBounds = 1 << 5,
|
||||
StencilCompareMask = 1 << 6,
|
||||
StencilWriteMask = 1 << 7,
|
||||
StencilReference = 1 << 8,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 过滤模式
|
||||
*/
|
||||
enum class Filter : u32 {
|
||||
None,
|
||||
Point,
|
||||
Linear,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 寻址模式
|
||||
*/
|
||||
enum class Address : u32 {
|
||||
Wrap,
|
||||
Mirror,
|
||||
Clamp,
|
||||
Border,
|
||||
MirrorOnce,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 设备能力信息
|
||||
*/
|
||||
struct DeviceCaps {
|
||||
API api{API::Unknown};
|
||||
u32 maxVertexAttributes{16};
|
||||
u32 maxVertexUniformVectors{256};
|
||||
u32 maxFragmentUniformVectors{256};
|
||||
u32 maxTextureUnits{16};
|
||||
u32 maxVertexTextureUnits{8};
|
||||
u32 maxDrawBuffers{8};
|
||||
u32 maxColorAttachments{8};
|
||||
u32 maxTextureSize{4096};
|
||||
u32 maxCubeMapSize{4096};
|
||||
u32 maxArrayLayers{256};
|
||||
u32 maxSamples{4};
|
||||
float maxAnisotropy{16.0f};
|
||||
bool instancedArrays{false};
|
||||
bool standardDerivatives{false};
|
||||
bool elementIndexUint{false};
|
||||
bool depth24{false};
|
||||
bool depth32{false};
|
||||
bool depth24Stencil8{false};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 格式特性标志位运算
|
||||
*/
|
||||
inline FormatFeature operator|(FormatFeature a, FormatFeature b) {
|
||||
return static_cast<FormatFeature>(static_cast<u32>(a) | static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline FormatFeature operator&(FormatFeature a, FormatFeature b) {
|
||||
return static_cast<FormatFeature>(static_cast<u32>(a) & static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline bool hasFeature(FormatFeature flags, FormatFeature flag) {
|
||||
return (static_cast<u32>(flags) & static_cast<u32>(flag)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 缓冲区用途标志位运算
|
||||
*/
|
||||
inline BufferUsage operator|(BufferUsage a, BufferUsage b) {
|
||||
return static_cast<BufferUsage>(static_cast<u32>(a) | static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline BufferUsage operator&(BufferUsage a, BufferUsage b) {
|
||||
return static_cast<BufferUsage>(static_cast<u32>(a) & static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline bool hasUsage(BufferUsage flags, BufferUsage flag) {
|
||||
return (static_cast<u32>(flags) & static_cast<u32>(flag)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 纹理用途标志位运算
|
||||
*/
|
||||
inline TextureUsage operator|(TextureUsage a, TextureUsage b) {
|
||||
return static_cast<TextureUsage>(static_cast<u32>(a) | static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline TextureUsage operator&(TextureUsage a, TextureUsage b) {
|
||||
return static_cast<TextureUsage>(static_cast<u32>(a) & static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline bool hasUsage(TextureUsage flags, TextureUsage flag) {
|
||||
return (static_cast<u32>(flags) & static_cast<u32>(flag)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 着色器阶段标志位运算
|
||||
*/
|
||||
inline ShaderStage operator|(ShaderStage a, ShaderStage b) {
|
||||
return static_cast<ShaderStage>(static_cast<u32>(a) | static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline ShaderStage operator&(ShaderStage a, ShaderStage b) {
|
||||
return static_cast<ShaderStage>(static_cast<u32>(a) & static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline bool hasStage(ShaderStage flags, ShaderStage flag) {
|
||||
return (static_cast<u32>(flags) & static_cast<u32>(flag)) != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 动态状态标志位运算
|
||||
*/
|
||||
inline DynamicStateFlag operator|(DynamicStateFlag a, DynamicStateFlag b) {
|
||||
return static_cast<DynamicStateFlag>(static_cast<u32>(a) | static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline DynamicStateFlag operator&(DynamicStateFlag a, DynamicStateFlag b) {
|
||||
return static_cast<DynamicStateFlag>(static_cast<u32>(a) & static_cast<u32>(b));
|
||||
}
|
||||
|
||||
inline bool hasDynamicState(DynamicStateFlag flags, DynamicStateFlag flag) {
|
||||
return (static_cast<u32>(flags) & static_cast<u32>(flag)) != 0;
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <extra2d/gfx/GFXObject.h>
|
||||
#include <extra2d/gfx/GFXBuffer.h>
|
||||
#include <extra2d/gfx/GFXTexture.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
class Shader;
|
||||
class PipelineState;
|
||||
class PipelineLayout;
|
||||
class RenderPass;
|
||||
class Framebuffer;
|
||||
class InputAssembler;
|
||||
class CommandBuffer;
|
||||
class DescriptorSet;
|
||||
class DescriptorSetLayout;
|
||||
|
||||
/**
|
||||
* @brief 设备创建信息
|
||||
*/
|
||||
struct DeviceInfo {
|
||||
std::string windowHandle;
|
||||
u32 width{1280};
|
||||
u32 height{720};
|
||||
bool debug{false};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief GFX 设备抽象基类
|
||||
*
|
||||
* 图形设备的抽象接口,管理所有 GPU 资源的创建和生命周期。
|
||||
* 参考 Cocos Creator 的 GFXDevice 设计。
|
||||
*/
|
||||
class Device : public GFXObject {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 设备实例
|
||||
*/
|
||||
static Device* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
Device();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Device() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化设备
|
||||
* @param info 设备信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(const DeviceInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 销毁设备
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 帧同步
|
||||
*
|
||||
* 等待 GPU 完成当前帧的所有工作
|
||||
*/
|
||||
virtual void frameSync() = 0;
|
||||
|
||||
/**
|
||||
* @brief 呈现帧
|
||||
*/
|
||||
virtual void present() = 0;
|
||||
|
||||
// ==================== 资源创建工厂方法 ====================
|
||||
|
||||
/**
|
||||
* @brief 创建缓冲区
|
||||
* @param info 缓冲区信息
|
||||
* @return 缓冲区指针
|
||||
*/
|
||||
virtual Buffer* createBuffer(const BufferInfo& info) = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建纹理
|
||||
* @param info 纹理信息
|
||||
* @return 纹理指针
|
||||
*/
|
||||
virtual Texture* createTexture(const TextureInfo& info) = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建着色器
|
||||
* @param name 着色器名称
|
||||
* @param vertexSource 顶点着色器源码
|
||||
* @param fragmentSource 片段着色器源码
|
||||
* @return 着色器指针
|
||||
*/
|
||||
virtual Shader* createShader(const std::string& name,
|
||||
const std::string& vertexSource,
|
||||
const std::string& fragmentSource) = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建管线状态
|
||||
* @param info 管线状态信息
|
||||
* @return 管线状态指针
|
||||
*/
|
||||
virtual PipelineState* createPipelineState(const PipelineStateInfo& info) = 0;
|
||||
|
||||
/**
|
||||
* @brief 创建输入装配器
|
||||
* @param info 输入装配器信息
|
||||
* @return 输入装配器指针
|
||||
*/
|
||||
virtual InputAssembler* createInputAssembler(const InputAssemblerInfo& info) = 0;
|
||||
|
||||
// ==================== 数据传输 ====================
|
||||
|
||||
/**
|
||||
* @brief 复制缓冲区数据到纹理
|
||||
* @param buffers 缓冲区数组
|
||||
* @param dst 目标纹理
|
||||
* @param regions 复制区域
|
||||
* @param count 区域数量
|
||||
*/
|
||||
virtual void copyBuffersToTexture(const u8* const* buffers, Texture* dst,
|
||||
const BufferTextureCopy* regions, u32 count) = 0;
|
||||
|
||||
// ==================== 设备能力查询 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取设备能力
|
||||
* @return 设备能力信息
|
||||
*/
|
||||
const DeviceCaps& getCapabilities() const;
|
||||
|
||||
/**
|
||||
* @brief 获取图形 API 类型
|
||||
* @return API 类型
|
||||
*/
|
||||
API getGfxAPI() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否支持指定特性
|
||||
* @param feature 特性
|
||||
* @return 如果支持返回 true
|
||||
*/
|
||||
bool hasFeature(Feature feature) const;
|
||||
|
||||
/**
|
||||
* @brief 获取格式特性
|
||||
* @param format 格式
|
||||
* @return 格式特性标志
|
||||
*/
|
||||
FormatFeature getFormatFeatures(Format format) const;
|
||||
|
||||
/**
|
||||
* @brief 获取类型名称
|
||||
* @return "Device"
|
||||
*/
|
||||
const char* getTypeName() const override { return "Device"; }
|
||||
|
||||
// ==================== 状态管理 ====================
|
||||
|
||||
/**
|
||||
* @brief 设置视口
|
||||
* @param x X 坐标
|
||||
* @param y Y 坐标
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
*/
|
||||
virtual void setViewport(float x, float y, float width, float height) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置裁剪区域
|
||||
* @param x X 坐标
|
||||
* @param y Y 坐标
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
*/
|
||||
virtual void setScissor(i32 x, i32 y, u32 width, u32 height) = 0;
|
||||
|
||||
/**
|
||||
* @brief 清除颜色缓冲区
|
||||
* @param r 红色分量
|
||||
* @param g 绿色分量
|
||||
* @param b 蓝色分量
|
||||
* @param a 透明度
|
||||
*/
|
||||
virtual void clearColor(float r, float g, float b, float a) = 0;
|
||||
|
||||
/**
|
||||
* @brief 清除深度和模板缓冲区
|
||||
* @param depth 深度值
|
||||
* @param stencil 模板值
|
||||
*/
|
||||
virtual void clearDepthStencil(float depth, u8 stencil) = 0;
|
||||
|
||||
/**
|
||||
* @brief 开始渲染通道
|
||||
*/
|
||||
virtual void beginRenderPass() = 0;
|
||||
|
||||
/**
|
||||
* @brief 结束渲染通道
|
||||
*/
|
||||
virtual void endRenderPass() = 0;
|
||||
|
||||
/**
|
||||
* @brief 绑定管线状态
|
||||
* @param pipeline 管线状态
|
||||
*/
|
||||
virtual void bindPipelineState(PipelineState* pipeline) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绑定输入装配器
|
||||
* @param ia 输入装配器
|
||||
*/
|
||||
virtual void bindInputAssembler(InputAssembler* ia) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制
|
||||
* @param firstVertex 起始顶点
|
||||
* @param vertexCount 顶点数量
|
||||
*/
|
||||
virtual void draw(u32 firstVertex, u32 vertexCount) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制索引
|
||||
* @param firstIndex 起始索引
|
||||
* @param indexCount 索引数量
|
||||
* @param vertexOffset 顶点偏移
|
||||
*/
|
||||
virtual void drawIndexed(u32 firstIndex, u32 indexCount, i32 vertexOffset) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制实例
|
||||
* @param firstVertex 起始顶点
|
||||
* @param vertexCount 顶点数量
|
||||
* @param instanceCount 实例数量
|
||||
*/
|
||||
virtual void drawInstanced(u32 firstVertex, u32 vertexCount, u32 instanceCount) = 0;
|
||||
|
||||
/**
|
||||
* @brief 绘制索引实例
|
||||
* @param firstIndex 起始索引
|
||||
* @param indexCount 索引数量
|
||||
* @param instanceCount 实例数量
|
||||
* @param vertexOffset 顶点偏移
|
||||
*/
|
||||
virtual void drawIndexedInstanced(u32 firstIndex, u32 indexCount,
|
||||
u32 instanceCount, i32 vertexOffset) = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 子类实现初始化
|
||||
*/
|
||||
virtual bool doInit(const DeviceInfo& info) = 0;
|
||||
|
||||
/**
|
||||
* @brief 子类实现销毁
|
||||
*/
|
||||
virtual void doDestroy() = 0;
|
||||
|
||||
static Device* instance_;
|
||||
DeviceCaps caps_;
|
||||
bool initialized_{false};
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXObject.h>
|
||||
#include <extra2d/gfx/GFXBuffer.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief 顶点流信息
|
||||
*/
|
||||
struct VertexStream {
|
||||
Buffer* buffer{nullptr};
|
||||
u32 offset{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 输入装配器信息
|
||||
*/
|
||||
struct InputAssemblerInfo {
|
||||
std::vector<VertexStream> vertexStreams;
|
||||
Buffer* indexBuffer{nullptr};
|
||||
u32 indexOffset{0};
|
||||
u32 firstIndex{0};
|
||||
u32 indexCount{0};
|
||||
u32 vertexCount{0};
|
||||
u32 instanceCount{1};
|
||||
u32 firstInstance{0};
|
||||
u32 vertexOffset{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 输入装配器抽象基类
|
||||
*
|
||||
* 管理顶点缓冲区和索引缓冲区的绑定。
|
||||
*/
|
||||
class InputAssembler : public GFXObject {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
InputAssembler();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~InputAssembler() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化输入装配器
|
||||
* @param info 输入装配器信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(const InputAssemblerInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 销毁输入装配器
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 获取顶点流列表
|
||||
* @return 顶点流列表
|
||||
*/
|
||||
const std::vector<VertexStream>& getVertexStreams() const;
|
||||
|
||||
/**
|
||||
* @brief 获取索引缓冲区
|
||||
* @return 索引缓冲区指针
|
||||
*/
|
||||
Buffer* getIndexBuffer() const;
|
||||
|
||||
/**
|
||||
* @brief 获取索引偏移
|
||||
* @return 索引偏移
|
||||
*/
|
||||
u32 getIndexOffset() const;
|
||||
|
||||
/**
|
||||
* @brief 获取起始索引
|
||||
* @return 起始索引
|
||||
*/
|
||||
u32 getFirstIndex() const;
|
||||
|
||||
/**
|
||||
* @brief 获取索引数量
|
||||
* @return 索引数量
|
||||
*/
|
||||
u32 getIndexCount() const;
|
||||
|
||||
/**
|
||||
* @brief 获取顶点数量
|
||||
* @return 顶点数量
|
||||
*/
|
||||
u32 getVertexCount() const;
|
||||
|
||||
/**
|
||||
* @brief 获取实例数量
|
||||
* @return 实例数量
|
||||
*/
|
||||
u32 getInstanceCount() const;
|
||||
|
||||
/**
|
||||
* @brief 设置实例数量
|
||||
* @param count 实例数量
|
||||
*/
|
||||
void setInstanceCount(u32 count);
|
||||
|
||||
/**
|
||||
* @brief 获取类型名称
|
||||
* @return "InputAssembler"
|
||||
*/
|
||||
const char* getTypeName() const override { return "InputAssembler"; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 子类实现初始化
|
||||
*/
|
||||
virtual bool doInit(const InputAssemblerInfo& info) = 0;
|
||||
|
||||
/**
|
||||
* @brief 子类实现销毁
|
||||
*/
|
||||
virtual void doDestroy() = 0;
|
||||
|
||||
std::vector<VertexStream> vertexStreams_;
|
||||
Buffer* indexBuffer_{nullptr};
|
||||
u32 indexOffset_{0};
|
||||
u32 firstIndex_{0};
|
||||
u32 indexCount_{0};
|
||||
u32 vertexCount_{0};
|
||||
u32 instanceCount_{1};
|
||||
u32 firstInstance_{0};
|
||||
u32 vertexOffset_{0};
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/base/RefCounted.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief GFX 对象基类
|
||||
*
|
||||
* 所有 GFX 资源的基类,提供类型标识和调试名称。
|
||||
*/
|
||||
class GFXObject : public RefCounted {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GFXObject();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GFXObject() override;
|
||||
|
||||
/**
|
||||
* @brief 获取对象类型名称
|
||||
* @return 类型名称
|
||||
*/
|
||||
virtual const char* getTypeName() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取调试名称
|
||||
* @return 调试名称
|
||||
*/
|
||||
const std::string& getDebugName() const;
|
||||
|
||||
/**
|
||||
* @brief 设置调试名称
|
||||
* @param name 调试名称
|
||||
*/
|
||||
void setDebugName(const std::string& name);
|
||||
|
||||
protected:
|
||||
std::string debugName_;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXObject.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <extra2d/gfx/GFXBuffer.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
class Shader;
|
||||
|
||||
/**
|
||||
* @brief 混合目标状态
|
||||
*/
|
||||
struct BlendTarget {
|
||||
bool blend{false};
|
||||
BlendFactor srcColor{BlendFactor::SrcAlpha};
|
||||
BlendFactor dstColor{BlendFactor::OneMinusSrcAlpha};
|
||||
BlendOp blendColorOp{BlendOp::Add};
|
||||
BlendFactor srcAlpha{BlendFactor::One};
|
||||
BlendFactor dstAlpha{BlendFactor::OneMinusSrcAlpha};
|
||||
BlendOp blendAlphaOp{BlendOp::Add};
|
||||
u32 colorWriteMask{0xF};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 混合状态
|
||||
*/
|
||||
struct BlendState {
|
||||
bool alphaToCoverage{false};
|
||||
bool independentBlend{false};
|
||||
float blendColor[4]{0, 0, 0, 0};
|
||||
std::vector<BlendTarget> targets;
|
||||
|
||||
BlendState() {
|
||||
targets.push_back(BlendTarget{});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 光栅化状态
|
||||
*/
|
||||
struct RasterizerState {
|
||||
bool discard{false};
|
||||
PolygonMode polygonMode{PolygonMode::Fill};
|
||||
CullMode cullMode{CullMode::Back};
|
||||
bool frontFaceCCW{true};
|
||||
bool depthBiasEnabled{false};
|
||||
float depthBias{0.0f};
|
||||
float depthBiasClamp{0.0f};
|
||||
float depthBiasSlop{0.0f};
|
||||
bool depthClip{true};
|
||||
bool multisample{false};
|
||||
float lineWidth{1.0f};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 深度模板状态
|
||||
*/
|
||||
struct DepthStencilState {
|
||||
bool depthTest{true};
|
||||
bool depthWrite{true};
|
||||
ComparisonFunc depthFunc{ComparisonFunc::Less};
|
||||
|
||||
bool stencilTestFront{false};
|
||||
ComparisonFunc stencilFuncFront{ComparisonFunc::Always};
|
||||
u8 stencilReadMaskFront{0xFF};
|
||||
u8 stencilWriteMaskFront{0xFF};
|
||||
u8 stencilRefFront{0};
|
||||
StencilOp stencilFailOpFront{StencilOp::Keep};
|
||||
StencilOp stencilDepthFailOpFront{StencilOp::Keep};
|
||||
StencilOp stencilPassOpFront{StencilOp::Keep};
|
||||
|
||||
bool stencilTestBack{false};
|
||||
ComparisonFunc stencilFuncBack{ComparisonFunc::Always};
|
||||
u8 stencilReadMaskBack{0xFF};
|
||||
u8 stencilWriteMaskBack{0xFF};
|
||||
u8 stencilRefBack{0};
|
||||
StencilOp stencilFailOpBack{StencilOp::Keep};
|
||||
StencilOp stencilDepthFailOpBack{StencilOp::Keep};
|
||||
StencilOp stencilPassOpBack{StencilOp::Keep};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 输入状态
|
||||
*/
|
||||
struct InputState {
|
||||
std::vector<Attribute> attributes;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 管线状态信息
|
||||
*/
|
||||
struct PipelineStateInfo {
|
||||
Shader* shader{nullptr};
|
||||
PipelineBindPoint bindPoint{PipelineBindPoint::Graphics};
|
||||
PrimitiveMode primitive{PrimitiveMode::TriangleList};
|
||||
DynamicStateFlag dynamicStates{DynamicStateFlag::None};
|
||||
InputState inputState;
|
||||
RasterizerState rasterizerState;
|
||||
DepthStencilState depthStencilState;
|
||||
BlendState blendState;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 管线状态抽象基类
|
||||
*/
|
||||
class PipelineState : public GFXObject {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
PipelineState();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~PipelineState() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化管线状态
|
||||
* @param info 管线状态信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(const PipelineStateInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 销毁管线状态
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 获取着色器
|
||||
* @return 着色器指针
|
||||
*/
|
||||
Shader* getShader() const;
|
||||
|
||||
/**
|
||||
* @brief 获取绑定点
|
||||
* @return 绑定点
|
||||
*/
|
||||
PipelineBindPoint getBindPoint() const;
|
||||
|
||||
/**
|
||||
* @brief 获取图元模式
|
||||
* @return 图元模式
|
||||
*/
|
||||
PrimitiveMode getPrimitive() const;
|
||||
|
||||
/**
|
||||
* @brief 获取光栅化状态
|
||||
* @return 光栅化状态
|
||||
*/
|
||||
const RasterizerState& getRasterizerState() const;
|
||||
|
||||
/**
|
||||
* @brief 获取深度模板状态
|
||||
* @return 深度模板状态
|
||||
*/
|
||||
const DepthStencilState& getDepthStencilState() const;
|
||||
|
||||
/**
|
||||
* @brief 获取混合状态
|
||||
* @return 混合状态
|
||||
*/
|
||||
const BlendState& getBlendState() const;
|
||||
|
||||
/**
|
||||
* @brief 获取类型名称
|
||||
* @return "PipelineState"
|
||||
*/
|
||||
const char* getTypeName() const override { return "PipelineState"; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 子类实现初始化
|
||||
*/
|
||||
virtual bool doInit(const PipelineStateInfo& info) = 0;
|
||||
|
||||
/**
|
||||
* @brief 子类实现销毁
|
||||
*/
|
||||
virtual void doDestroy() = 0;
|
||||
|
||||
Shader* shader_{nullptr};
|
||||
PipelineBindPoint bindPoint_{PipelineBindPoint::Graphics};
|
||||
PrimitiveMode primitive_{PrimitiveMode::TriangleList};
|
||||
DynamicStateFlag dynamicStates_{DynamicStateFlag::None};
|
||||
InputState inputState_;
|
||||
RasterizerState rasterizerState_;
|
||||
DepthStencilState depthStencilState_;
|
||||
BlendState blendState_;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,145 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXObject.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief 顶点属性
|
||||
*/
|
||||
struct Attribute {
|
||||
std::string name;
|
||||
Format format{Format::RGBA8};
|
||||
bool normalized{false};
|
||||
u32 stream{0};
|
||||
bool instanced{false};
|
||||
u32 location{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Uniform 成员
|
||||
*/
|
||||
struct Uniform {
|
||||
std::string name;
|
||||
Format format{Format::Unknown};
|
||||
u32 count{1};
|
||||
u32 offset{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Uniform 块
|
||||
*/
|
||||
struct UniformBlock {
|
||||
u32 set{0};
|
||||
u32 binding{0};
|
||||
std::string name;
|
||||
std::vector<Uniform> members;
|
||||
u32 count{1};
|
||||
u32 size{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 采样器纹理
|
||||
*/
|
||||
struct UniformSamplerTexture {
|
||||
u32 set{0};
|
||||
u32 binding{0};
|
||||
std::string name;
|
||||
TextureType type{TextureType::Tex2D};
|
||||
u32 count{1};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器创建信息
|
||||
*/
|
||||
struct ShaderInfo {
|
||||
std::string name;
|
||||
std::string vertexSource;
|
||||
std::string fragmentSource;
|
||||
std::vector<Attribute> attributes;
|
||||
std::vector<UniformBlock> blocks;
|
||||
std::vector<UniformSamplerTexture> samplerTextures;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 着色器抽象基类
|
||||
*/
|
||||
class Shader : public GFXObject {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
Shader();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Shader() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化着色器
|
||||
* @param info 着色器信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(const ShaderInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 销毁着色器
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 获取着色器名称
|
||||
* @return 名称
|
||||
*/
|
||||
const std::string& getName() const;
|
||||
|
||||
/**
|
||||
* @brief 获取顶点属性列表
|
||||
* @return 属性列表
|
||||
*/
|
||||
const std::vector<Attribute>& getAttributes() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 Uniform 块列表
|
||||
* @return Uniform 块列表
|
||||
*/
|
||||
const std::vector<UniformBlock>& getBlocks() const;
|
||||
|
||||
/**
|
||||
* @brief 获取采样器纹理列表
|
||||
* @return 采样器纹理列表
|
||||
*/
|
||||
const std::vector<UniformSamplerTexture>& getSamplerTextures() const;
|
||||
|
||||
/**
|
||||
* @brief 获取类型名称
|
||||
* @return "Shader"
|
||||
*/
|
||||
const char* getTypeName() const override { return "Shader"; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 子类实现初始化
|
||||
*/
|
||||
virtual bool doInit(const ShaderInfo& info) = 0;
|
||||
|
||||
/**
|
||||
* @brief 子类实现销毁
|
||||
*/
|
||||
virtual void doDestroy() = 0;
|
||||
|
||||
std::string name_;
|
||||
std::string vertexSource_;
|
||||
std::string fragmentSource_;
|
||||
std::vector<Attribute> attributes_;
|
||||
std::vector<UniformBlock> blocks_;
|
||||
std::vector<UniformSamplerTexture> samplerTextures_;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXObject.h>
|
||||
#include <extra2d/gfx/GFXDef.h>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
class Buffer;
|
||||
|
||||
/**
|
||||
* @brief 纹理创建信息
|
||||
*/
|
||||
struct TextureInfo {
|
||||
TextureType type{TextureType::Tex2D};
|
||||
TextureUsage usage{TextureUsage::None};
|
||||
Format format{Format::RGBA8};
|
||||
u32 width{1};
|
||||
u32 height{1};
|
||||
u32 depth{1};
|
||||
u32 arrayLength{1};
|
||||
u32 mipLevels{1};
|
||||
SampleCount samples{SampleCount::X1};
|
||||
TextureFlag flags{TextureFlag::None};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理视图信息
|
||||
*/
|
||||
struct TextureViewInfo {
|
||||
class Texture* texture{nullptr};
|
||||
TextureType type{TextureType::Tex2D};
|
||||
Format format{Format::Unknown};
|
||||
u32 baseLevel{0};
|
||||
u32 levelCount{1};
|
||||
u32 baseLayer{0};
|
||||
u32 layerCount{1};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 缓冲区到纹理复制区域
|
||||
*/
|
||||
struct BufferTextureCopy {
|
||||
u32 bufferOffset{0};
|
||||
u32 bufferStride{0};
|
||||
u32 bufferTexStride{0};
|
||||
u32 texOffsetX{0};
|
||||
u32 texOffsetY{0};
|
||||
u32 texOffsetZ{0};
|
||||
u32 texExtentX{0};
|
||||
u32 texExtentY{0};
|
||||
u32 texExtentZ{0};
|
||||
u32 texBaseLevel{0};
|
||||
u32 texLevelCount{1};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 纹理抽象基类
|
||||
*
|
||||
* GPU 纹理资源的抽象接口。
|
||||
*/
|
||||
class Texture : public GFXObject {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
Texture();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~Texture() override;
|
||||
|
||||
/**
|
||||
* @brief 初始化纹理
|
||||
* @param info 纹理信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(const TextureInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 初始化纹理视图
|
||||
* @param info 视图信息
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize(const TextureViewInfo& info);
|
||||
|
||||
/**
|
||||
* @brief 销毁纹理
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 调整纹理大小
|
||||
* @param width 新宽度
|
||||
* @param height 新高度
|
||||
*/
|
||||
void resize(u32 width, u32 height);
|
||||
|
||||
/**
|
||||
* @brief 获取纹理信息
|
||||
* @return 纹理信息
|
||||
*/
|
||||
const TextureInfo& getInfo() const;
|
||||
|
||||
/**
|
||||
* @brief 获取视图信息
|
||||
* @return 视图信息
|
||||
*/
|
||||
const TextureViewInfo& getViewInfo() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否为视图
|
||||
* @return 如果是视图返回 true
|
||||
*/
|
||||
bool isTextureView() const;
|
||||
|
||||
/**
|
||||
* @brief 获取格式
|
||||
* @return 格式
|
||||
*/
|
||||
Format getFormat() const;
|
||||
|
||||
/**
|
||||
* @brief 获取宽度
|
||||
* @return 宽度
|
||||
*/
|
||||
u32 getWidth() const;
|
||||
|
||||
/**
|
||||
* @brief 获取高度
|
||||
* @return 高度
|
||||
*/
|
||||
u32 getHeight() const;
|
||||
|
||||
/**
|
||||
* @brief 获取深度
|
||||
* @return 深度
|
||||
*/
|
||||
u32 getDepth() const;
|
||||
|
||||
/**
|
||||
* @brief 获取类型名称
|
||||
* @return "Texture"
|
||||
*/
|
||||
const char* getTypeName() const override { return "Texture"; }
|
||||
|
||||
/**
|
||||
* @brief 获取原始纹理(视图使用)
|
||||
* @return 原始纹理指针
|
||||
*/
|
||||
virtual const Texture* getRaw() const { return this; }
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 子类实现初始化
|
||||
*/
|
||||
virtual bool doInit(const TextureInfo& info) = 0;
|
||||
|
||||
/**
|
||||
* @brief 子类实现视图初始化
|
||||
*/
|
||||
virtual bool doInit(const TextureViewInfo& info) = 0;
|
||||
|
||||
/**
|
||||
* @brief 子类实现销毁
|
||||
*/
|
||||
virtual void doDestroy() = 0;
|
||||
|
||||
/**
|
||||
* @brief 子类实现调整大小
|
||||
*/
|
||||
virtual void doResize(u32 width, u32 height) = 0;
|
||||
|
||||
TextureInfo info_;
|
||||
TextureViewInfo viewInfo_;
|
||||
bool isView_{false};
|
||||
Texture* sourceTexture_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXBuffer.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief OpenGL 缓冲区实现
|
||||
*/
|
||||
class GLBuffer : public Buffer {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLBuffer();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLBuffer() override;
|
||||
|
||||
/**
|
||||
* @brief 更新缓冲区数据
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
*/
|
||||
void update(const void* data, u32 size) override;
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 缓冲区句柄
|
||||
* @return 缓冲区句柄
|
||||
*/
|
||||
u32 getGLHandle() const;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 实现初始化
|
||||
*/
|
||||
bool doInit(const BufferInfo& info) override;
|
||||
|
||||
/**
|
||||
* @brief 实现视图初始化
|
||||
*/
|
||||
bool doInit(const BufferViewInfo& info) override;
|
||||
|
||||
/**
|
||||
* @brief 实现销毁
|
||||
*/
|
||||
void doDestroy() override;
|
||||
|
||||
/**
|
||||
* @brief 实现调整大小
|
||||
*/
|
||||
void doResize(u32 size) override;
|
||||
|
||||
private:
|
||||
u32 glHandle_{0};
|
||||
u32 glTarget_{0};
|
||||
void* mappedData_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
#include <extra2d/gfx/GFXCommandBuffer.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief OpenGL 命令缓冲区实现
|
||||
*/
|
||||
class GLCommandBuffer : public CommandBuffer {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLCommandBuffer();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLCommandBuffer() override;
|
||||
|
||||
// ==================== 生命周期 ====================
|
||||
|
||||
/**
|
||||
* @brief 初始化命令缓冲区
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
bool initialize();
|
||||
|
||||
/**
|
||||
* @brief 销毁命令缓冲区
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
// ==================== 开始/结束 ====================
|
||||
|
||||
/**
|
||||
* @brief 开始录制命令
|
||||
*/
|
||||
void begin() override;
|
||||
|
||||
/**
|
||||
* @brief 结束录制命令
|
||||
*/
|
||||
void end() override;
|
||||
|
||||
// ==================== 渲染通道 ====================
|
||||
|
||||
/**
|
||||
* @brief 开始渲染通道
|
||||
* @param renderPass 渲染通道
|
||||
* @param framebuffer 帧缓冲
|
||||
* @param viewport 视口
|
||||
* @param clearFlags 清除标志
|
||||
* @param clearColor 清除颜色
|
||||
* @param clearDepth 清除深度
|
||||
* @param clearStencil 清除模板
|
||||
*/
|
||||
void beginRenderPass(RenderPass* renderPass, Framebuffer* framebuffer,
|
||||
const Viewport& viewport,
|
||||
ClearFlagBit clearFlags,
|
||||
const Color& clearColor,
|
||||
float clearDepth, u32 clearStencil) override;
|
||||
|
||||
/**
|
||||
* @brief 结束渲染通道
|
||||
*/
|
||||
void endRenderPass() override;
|
||||
|
||||
// ==================== 绑定命令 ====================
|
||||
|
||||
/**
|
||||
* @brief 绑定管线状态
|
||||
* @param pipelineState 管线状态
|
||||
*/
|
||||
void bindPipelineState(PipelineState* pipelineState) override;
|
||||
|
||||
/**
|
||||
* @brief 绑定输入装配器
|
||||
* @param inputAssembler 输入装配器
|
||||
*/
|
||||
void bindInputAssembler(InputAssembler* inputAssembler) override;
|
||||
|
||||
/**
|
||||
* @brief 绑定描述符集
|
||||
* @param descriptorSet 描述符集
|
||||
* @param setIndex 集合索引
|
||||
*/
|
||||
void bindDescriptorSet(DescriptorSet* descriptorSet, u32 setIndex) override;
|
||||
|
||||
// ==================== 状态设置 ====================
|
||||
|
||||
/**
|
||||
* @brief 设置视口
|
||||
* @param viewport 视口
|
||||
*/
|
||||
void setViewport(const Viewport& viewport) override;
|
||||
|
||||
/**
|
||||
* @brief 设置矩形区域
|
||||
* @param rect 矩形区域
|
||||
*/
|
||||
void setScissor(const Rect& rect) override;
|
||||
|
||||
/**
|
||||
* @brief 设置混合常量
|
||||
* @param constants 常量数组
|
||||
*/
|
||||
void setBlendConstants(const float* constants) override;
|
||||
|
||||
/**
|
||||
* @brief 设置深度偏移
|
||||
* @param bias 偏移值
|
||||
*/
|
||||
void setDepthBias(float bias, float slope, float clamp) override;
|
||||
|
||||
/**
|
||||
* @brief 设置模板参考值
|
||||
* @param front 前面参考值
|
||||
* @param back 后面参考值
|
||||
*/
|
||||
void setStencilReference(u32 front, u32 back) override;
|
||||
|
||||
// ==================== 绘制命令 ====================
|
||||
|
||||
/**
|
||||
* @brief 绘制数组
|
||||
* @param vertexCount 顶点数量
|
||||
* @param firstVertex 起始顶点
|
||||
* @param instanceCount 实例数量
|
||||
* @param firstInstance 起始实例
|
||||
*/
|
||||
void drawArrays(u32 vertexCount, u32 firstVertex = 0,
|
||||
u32 instanceCount = 1, u32 firstInstance = 0) override;
|
||||
|
||||
/**
|
||||
* @brief 绘制索引
|
||||
* @param indexCount 索引数量
|
||||
* @param firstIndex 起始索引
|
||||
* @param instanceCount 实例数量
|
||||
* @param firstInstance 起始实例
|
||||
*/
|
||||
void drawIndexed(u32 indexCount, u32 firstIndex = 0,
|
||||
u32 instanceCount = 1, u32 firstInstance = 0) override;
|
||||
|
||||
// ==================== 更新命令 ====================
|
||||
|
||||
/**
|
||||
* @brief 更新缓冲区
|
||||
* @param buffer 缓冲区
|
||||
* @param offset 偏移量
|
||||
* @param data 数据指针
|
||||
* @param size 数据大小
|
||||
*/
|
||||
void updateBuffer(Buffer* buffer, u32 offset, const void* data, u32 size) override;
|
||||
|
||||
/**
|
||||
* @brief 复制缓冲区
|
||||
* @param src 源缓冲区
|
||||
* @param srcOffset 源偏移量
|
||||
* @param dst 目标缓冲区
|
||||
* @param dstOffset 目标偏移量
|
||||
* @param size 大小
|
||||
*/
|
||||
void copyBuffer(Buffer* src, u32 srcOffset, Buffer* dst, u32 dstOffset, u32 size) override;
|
||||
|
||||
/**
|
||||
* @brief 复制纹理
|
||||
* @param src 源纹理
|
||||
* @param dst 目标纹理
|
||||
*/
|
||||
void copyTexture(Texture* src, Texture* dst) override;
|
||||
|
||||
private:
|
||||
bool recording_{false};
|
||||
RenderPass* currentRenderPass_{nullptr};
|
||||
Framebuffer* currentFramebuffer_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXDevice.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief OpenGL 设备实现
|
||||
*/
|
||||
class GLDevice : public Device {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLDevice();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLDevice() override;
|
||||
|
||||
// ==================== 设备生命周期 ====================
|
||||
|
||||
void frameSync() override;
|
||||
void present() override;
|
||||
|
||||
// ==================== 资源创建 ====================
|
||||
|
||||
Buffer* createBuffer(const BufferInfo& info) override;
|
||||
Texture* createTexture(const TextureInfo& info) override;
|
||||
Shader* createShader(const std::string& name,
|
||||
const std::string& vertexSource,
|
||||
const std::string& fragmentSource) override;
|
||||
PipelineState* createPipelineState(const PipelineStateInfo& info) override;
|
||||
InputAssembler* createInputAssembler(const InputAssemblerInfo& info) override;
|
||||
|
||||
// ==================== 数据传输 ====================
|
||||
|
||||
void copyBuffersToTexture(const u8* const* buffers, Texture* dst,
|
||||
const BufferTextureCopy* regions, u32 count) override;
|
||||
|
||||
// ==================== 状态管理 ====================
|
||||
|
||||
void setViewport(float x, float y, float width, float height) override;
|
||||
void setScissor(i32 x, i32 y, u32 width, u32 height) override;
|
||||
void clearColor(float r, float g, float b, float a) override;
|
||||
void clearDepthStencil(float depth, u8 stencil) override;
|
||||
|
||||
void beginRenderPass() override;
|
||||
void endRenderPass() override;
|
||||
|
||||
void bindPipelineState(PipelineState* pipeline) override;
|
||||
void bindInputAssembler(InputAssembler* ia) override;
|
||||
|
||||
void draw(u32 firstVertex, u32 vertexCount) override;
|
||||
void drawIndexed(u32 firstIndex, u32 indexCount, i32 vertexOffset) override;
|
||||
void drawInstanced(u32 firstVertex, u32 vertexCount, u32 instanceCount) override;
|
||||
void drawIndexedInstanced(u32 firstIndex, u32 indexCount,
|
||||
u32 instanceCount, i32 vertexOffset) override;
|
||||
|
||||
protected:
|
||||
bool doInit(const DeviceInfo& info) override;
|
||||
void doDestroy() override;
|
||||
|
||||
private:
|
||||
void initCapabilities();
|
||||
|
||||
class GLPipelineState* currentPipeline_{nullptr};
|
||||
class GLInputAssembler* currentIA_{nullptr};
|
||||
class GLShader* currentShader_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXDevice.h>
|
||||
#include <extra2d/gfx/GFXInputAssembler.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief OpenGL 输入装配器实现
|
||||
*/
|
||||
class GLInputAssembler : public InputAssembler {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLInputAssembler();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLInputAssembler() override;
|
||||
|
||||
/**
|
||||
* @brief 绑定顶点属性
|
||||
* @param shader 着色器
|
||||
*/
|
||||
void bindAttributes(class GLShader* shader);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 实现初始化
|
||||
*/
|
||||
bool doInit(const InputAssemblerInfo& info) override;
|
||||
|
||||
/**
|
||||
* @brief 实现销毁
|
||||
*/
|
||||
void doDestroy() override;
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXPipeline.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief OpenGL 管线状态实现
|
||||
*/
|
||||
class GLPipelineState : public PipelineState {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLPipelineState();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLPipelineState() override;
|
||||
|
||||
/**
|
||||
* @brief 应用管线状态
|
||||
*/
|
||||
void apply();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 实现初始化
|
||||
*/
|
||||
bool doInit(const PipelineStateInfo& info) override;
|
||||
|
||||
/**
|
||||
* @brief 实现销毁
|
||||
*/
|
||||
void doDestroy() override;
|
||||
|
||||
private:
|
||||
void applyRasterizerState();
|
||||
void applyDepthStencilState();
|
||||
void applyBlendState();
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXShader.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief OpenGL 着色器实现
|
||||
*/
|
||||
class GLShader : public Shader {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLShader();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLShader() override;
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 程序句柄
|
||||
* @return 程序句柄
|
||||
*/
|
||||
u32 getGLProgram() const;
|
||||
|
||||
/**
|
||||
* @brief 激活着色器
|
||||
*/
|
||||
void bind();
|
||||
|
||||
/**
|
||||
* @brief 获取 Uniform 位置
|
||||
* @param name Uniform 名称
|
||||
* @return 位置
|
||||
*/
|
||||
i32 getUniformLocation(const std::string& name);
|
||||
|
||||
/**
|
||||
* @brief 获取属性位置
|
||||
* @param name 属性名称
|
||||
* @return 位置
|
||||
*/
|
||||
i32 getAttributeLocation(const std::string& name);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 实现初始化
|
||||
*/
|
||||
bool doInit(const ShaderInfo& info) override;
|
||||
|
||||
/**
|
||||
* @brief 实现销毁
|
||||
*/
|
||||
void doDestroy() override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief 编译着色器
|
||||
* @param source 源码
|
||||
* @param type 着色器类型
|
||||
* @return 着色器句柄
|
||||
*/
|
||||
u32 compileShader(const std::string& source, u32 type);
|
||||
|
||||
/**
|
||||
* @brief 链接程序
|
||||
* @param vertexShader 顶点着色器
|
||||
* @param fragmentShader 片段着色器
|
||||
* @return 是否成功
|
||||
*/
|
||||
bool linkProgram(u32 vertexShader, u32 fragmentShader);
|
||||
|
||||
u32 glProgram_{0};
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/gfx/GFXTexture.h>
|
||||
|
||||
namespace extra2d {
|
||||
namespace gfx {
|
||||
|
||||
/**
|
||||
* @brief OpenGL 纹理实现
|
||||
*/
|
||||
class GLTexture : public Texture {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数
|
||||
*/
|
||||
GLTexture();
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~GLTexture() override;
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 纹理句柄
|
||||
* @return 纹理句柄
|
||||
*/
|
||||
u32 getGLHandle() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 纹理目标
|
||||
* @return 纹理目标
|
||||
*/
|
||||
u32 getGLTarget() const;
|
||||
|
||||
/**
|
||||
* @brief 上传纹理数据
|
||||
* @param data 数据指针
|
||||
* @param level Mipmap 级别
|
||||
*/
|
||||
void uploadData(const void* data, u32 level = 0);
|
||||
|
||||
/**
|
||||
* @brief 生成 Mipmap
|
||||
*/
|
||||
void generateMipmaps();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 实现初始化
|
||||
*/
|
||||
bool doInit(const TextureInfo& info) override;
|
||||
|
||||
/**
|
||||
* @brief 实现视图初始化
|
||||
*/
|
||||
bool doInit(const TextureViewInfo& info) override;
|
||||
|
||||
/**
|
||||
* @brief 实现销毁
|
||||
*/
|
||||
void doDestroy() override;
|
||||
|
||||
/**
|
||||
* @brief 实现调整大小
|
||||
*/
|
||||
void doResize(u32 width, u32 height) override;
|
||||
|
||||
private:
|
||||
u32 glHandle_{0};
|
||||
u32 glTarget_{0};
|
||||
u32 glInternalFormat_{0};
|
||||
u32 glFormat_{0};
|
||||
u32 glType_{0};
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,542 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
struct SDL_GameController;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 触摸点信息
|
||||
*/
|
||||
struct Touch {
|
||||
i64 id{-1};
|
||||
Vec2 position{0, 0};
|
||||
Vec2 startPosition{0, 0};
|
||||
Vec2 delta{0, 0};
|
||||
float pressure{1.0f};
|
||||
float timestamp{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 按键码常量
|
||||
*/
|
||||
namespace KeyCode {
|
||||
constexpr i32 A = 4;
|
||||
constexpr i32 B = 5;
|
||||
constexpr i32 C = 6;
|
||||
constexpr i32 D = 7;
|
||||
constexpr i32 E = 8;
|
||||
constexpr i32 F = 9;
|
||||
constexpr i32 G = 10;
|
||||
constexpr i32 H = 11;
|
||||
constexpr i32 I = 12;
|
||||
constexpr i32 J = 13;
|
||||
constexpr i32 K = 14;
|
||||
constexpr i32 L = 15;
|
||||
constexpr i32 M = 16;
|
||||
constexpr i32 N = 17;
|
||||
constexpr i32 O = 18;
|
||||
constexpr i32 P = 19;
|
||||
constexpr i32 Q = 20;
|
||||
constexpr i32 R = 21;
|
||||
constexpr i32 S = 22;
|
||||
constexpr i32 T = 23;
|
||||
constexpr i32 U = 24;
|
||||
constexpr i32 V = 25;
|
||||
constexpr i32 W = 26;
|
||||
constexpr i32 X = 27;
|
||||
constexpr i32 Y = 28;
|
||||
constexpr i32 Z = 29;
|
||||
|
||||
constexpr i32 Num1 = 30;
|
||||
constexpr i32 Num2 = 31;
|
||||
constexpr i32 Num3 = 32;
|
||||
constexpr i32 Num4 = 33;
|
||||
constexpr i32 Num5 = 34;
|
||||
constexpr i32 Num6 = 35;
|
||||
constexpr i32 Num7 = 36;
|
||||
constexpr i32 Num8 = 37;
|
||||
constexpr i32 Num9 = 38;
|
||||
constexpr i32 Num0 = 39;
|
||||
|
||||
constexpr i32 Return = 40;
|
||||
constexpr i32 Escape = 41;
|
||||
constexpr i32 Backspace = 42;
|
||||
constexpr i32 Tab = 43;
|
||||
constexpr i32 Space = 44;
|
||||
|
||||
constexpr i32 Minus = 45;
|
||||
constexpr i32 Equals = 46;
|
||||
constexpr i32 LeftBracket = 47;
|
||||
constexpr i32 RightBracket = 48;
|
||||
constexpr i32 Backslash = 49;
|
||||
constexpr i32 Semicolon = 51;
|
||||
constexpr i32 Quote = 52;
|
||||
constexpr i32 Grave = 53;
|
||||
constexpr i32 Comma = 54;
|
||||
constexpr i32 Period = 55;
|
||||
constexpr i32 Slash = 56;
|
||||
|
||||
constexpr i32 CapsLock = 57;
|
||||
|
||||
constexpr i32 F1 = 58;
|
||||
constexpr i32 F2 = 59;
|
||||
constexpr i32 F3 = 60;
|
||||
constexpr i32 F4 = 61;
|
||||
constexpr i32 F5 = 62;
|
||||
constexpr i32 F6 = 63;
|
||||
constexpr i32 F7 = 64;
|
||||
constexpr i32 F8 = 65;
|
||||
constexpr i32 F9 = 66;
|
||||
constexpr i32 F10 = 67;
|
||||
constexpr i32 F11 = 68;
|
||||
constexpr i32 F12 = 69;
|
||||
|
||||
constexpr i32 PrintScreen = 70;
|
||||
constexpr i32 ScrollLock = 71;
|
||||
constexpr i32 Pause = 72;
|
||||
constexpr i32 Insert = 73;
|
||||
constexpr i32 Home = 74;
|
||||
constexpr i32 PageUp = 75;
|
||||
constexpr i32 Delete = 76;
|
||||
constexpr i32 End = 77;
|
||||
constexpr i32 PageDown = 78;
|
||||
|
||||
constexpr i32 Right = 79;
|
||||
constexpr i32 Left = 80;
|
||||
constexpr i32 Down = 81;
|
||||
constexpr i32 Up = 82;
|
||||
|
||||
constexpr i32 NumLockClear = 83;
|
||||
constexpr i32 KPDivide = 84;
|
||||
constexpr i32 KPMultiply = 85;
|
||||
constexpr i32 KPMinus = 86;
|
||||
constexpr i32 KPPlus = 87;
|
||||
constexpr i32 KPEnter = 88;
|
||||
constexpr i32 KP1 = 89;
|
||||
constexpr i32 KP2 = 90;
|
||||
constexpr i32 KP3 = 91;
|
||||
constexpr i32 KP4 = 92;
|
||||
constexpr i32 KP5 = 93;
|
||||
constexpr i32 KP6 = 94;
|
||||
constexpr i32 KP7 = 95;
|
||||
constexpr i32 KP8 = 96;
|
||||
constexpr i32 KP9 = 97;
|
||||
constexpr i32 KP0 = 98;
|
||||
constexpr i32 KPPeriod = 99;
|
||||
|
||||
constexpr i32 LCtrl = 224;
|
||||
constexpr i32 LShift = 225;
|
||||
constexpr i32 LAlt = 226;
|
||||
constexpr i32 LGUI = 227;
|
||||
constexpr i32 RCtrl = 228;
|
||||
constexpr i32 RShift = 229;
|
||||
constexpr i32 RAlt = 230;
|
||||
constexpr i32 RGUI = 231;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 鼠标按钮常量
|
||||
*/
|
||||
namespace MouseButton {
|
||||
constexpr i32 Left = 1;
|
||||
constexpr i32 Middle = 2;
|
||||
constexpr i32 Right = 3;
|
||||
constexpr i32 X1 = 4;
|
||||
constexpr i32 X2 = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 手柄按钮常量
|
||||
*/
|
||||
namespace GamepadButton {
|
||||
constexpr i32 A = 0;
|
||||
constexpr i32 B = 1;
|
||||
constexpr i32 X = 2;
|
||||
constexpr i32 Y = 3;
|
||||
constexpr i32 Back = 4;
|
||||
constexpr i32 Guide = 5;
|
||||
constexpr i32 Start = 6;
|
||||
constexpr i32 LeftStick = 7;
|
||||
constexpr i32 RightStick = 8;
|
||||
constexpr i32 LeftShoulder = 9;
|
||||
constexpr i32 RightShoulder = 10;
|
||||
constexpr i32 DPadUp = 11;
|
||||
constexpr i32 DPadDown = 12;
|
||||
constexpr i32 DPadLeft = 13;
|
||||
constexpr i32 DPadRight = 14;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 手柄轴常量
|
||||
*/
|
||||
namespace GamepadAxis {
|
||||
constexpr i32 LeftX = 0;
|
||||
constexpr i32 LeftY = 1;
|
||||
constexpr i32 RightX = 2;
|
||||
constexpr i32 RightY = 3;
|
||||
constexpr i32 LeftTrigger = 4;
|
||||
constexpr i32 RightTrigger = 5;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 输入管理类
|
||||
*
|
||||
* 管理键盘、鼠标、触摸和手柄输入状态。
|
||||
*/
|
||||
class Input {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 输入实例
|
||||
*/
|
||||
static Input* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 每帧更新
|
||||
*/
|
||||
void update();
|
||||
|
||||
/**
|
||||
* @brief 重置帧状态
|
||||
*/
|
||||
void resetFrameState();
|
||||
|
||||
// ==================== 键盘输入 ====================
|
||||
|
||||
/**
|
||||
* @brief 检查按键是否刚按下
|
||||
* @param keycode 按键码
|
||||
* @return 如果刚按下返回 true
|
||||
*/
|
||||
bool isKeyDown(i32 keycode) const;
|
||||
|
||||
/**
|
||||
* @brief 检查按键是否刚释放
|
||||
* @param keycode 按键码
|
||||
* @return 如果刚释放返回 true
|
||||
*/
|
||||
bool isKeyUp(i32 keycode) const;
|
||||
|
||||
/**
|
||||
* @brief 检查按键是否按住
|
||||
* @param keycode 按键码
|
||||
* @return 如果按住返回 true
|
||||
*/
|
||||
bool isKeyPressed(i32 keycode) const;
|
||||
|
||||
/**
|
||||
* @brief 获取任意按键是否按下
|
||||
* @return 如果有任意按键按下返回 true
|
||||
*/
|
||||
bool isAnyKeyPressed() const;
|
||||
|
||||
// ==================== 鼠标输入 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取鼠标位置
|
||||
* @return 鼠标位置
|
||||
*/
|
||||
Vec2 getMousePosition() const;
|
||||
|
||||
/**
|
||||
* @brief 获取鼠标移动增量
|
||||
* @return 鼠标移动增量
|
||||
*/
|
||||
Vec2 getMouseDelta() const;
|
||||
|
||||
/**
|
||||
* @brief 检查鼠标按钮是否刚按下
|
||||
* @param button 鼠标按钮
|
||||
* @return 如果刚按下返回 true
|
||||
*/
|
||||
bool isMouseButtonDown(i32 button) const;
|
||||
|
||||
/**
|
||||
* @brief 检查鼠标按钮是否刚释放
|
||||
* @param button 鼠标按钮
|
||||
* @return 如果刚释放返回 true
|
||||
*/
|
||||
bool isMouseButtonUp(i32 button) const;
|
||||
|
||||
/**
|
||||
* @brief 检查鼠标按钮是否按住
|
||||
* @param button 鼠标按钮
|
||||
* @return 如果按住返回 true
|
||||
*/
|
||||
bool isMouseButtonPressed(i32 button) const;
|
||||
|
||||
/**
|
||||
* @brief 获取鼠标滚轮增量
|
||||
* @return 鼠标滚轮增量
|
||||
*/
|
||||
Vec2 getMouseWheelDelta() const;
|
||||
|
||||
/**
|
||||
* @brief 设置鼠标锁定模式
|
||||
* @param locked 是否锁定
|
||||
*/
|
||||
void setMouseLocked(bool locked);
|
||||
|
||||
/**
|
||||
* @brief 检查鼠标是否锁定
|
||||
* @return 如果锁定返回 true
|
||||
*/
|
||||
bool isMouseLocked() const;
|
||||
|
||||
// ==================== 触摸输入 ====================
|
||||
|
||||
/**
|
||||
* @brief 检查是否有触摸输入
|
||||
* @return 如果有触摸返回 true
|
||||
*/
|
||||
bool hasTouch() const;
|
||||
|
||||
/**
|
||||
* @brief 获取当前触摸点数量
|
||||
* @return 触摸点数量
|
||||
*/
|
||||
i32 getTouchCount() const;
|
||||
|
||||
/**
|
||||
* @brief 获取指定 ID 的触摸点
|
||||
* @param id 触摸点 ID
|
||||
* @return 触摸点指针,如果不存在返回 nullptr
|
||||
*/
|
||||
const Touch* getTouch(i64 id) const;
|
||||
|
||||
/**
|
||||
* @brief 按索引获取触摸点
|
||||
* @param index 索引
|
||||
* @return 触摸点指针
|
||||
*/
|
||||
const Touch* getTouchByIndex(i32 index) const;
|
||||
|
||||
/**
|
||||
* @brief 获取所有触摸点
|
||||
* @return 触摸点列表
|
||||
*/
|
||||
const std::vector<Touch>& getTouches() const;
|
||||
|
||||
/**
|
||||
* @brief 检查触摸点是否刚按下
|
||||
* @param id 触摸点 ID
|
||||
* @return 如果刚按下返回 true
|
||||
*/
|
||||
bool isTouchDown(i64 id) const;
|
||||
|
||||
/**
|
||||
* @brief 检查触摸点是否刚抬起
|
||||
* @param id 触摸点 ID
|
||||
* @return 如果刚抬起返回 true
|
||||
*/
|
||||
bool isTouchUp(i64 id) const;
|
||||
|
||||
/**
|
||||
* @brief 检查触摸点是否移动中
|
||||
* @param id 触摸点 ID
|
||||
* @return 如果移动中返回 true
|
||||
*/
|
||||
bool isTouchMoving(i64 id) const;
|
||||
|
||||
// ==================== 多点触控手势 ====================
|
||||
|
||||
/**
|
||||
* @brief 检查是否正在双指缩放
|
||||
* @return 如果正在缩放返回 true
|
||||
*/
|
||||
bool isPinching() const;
|
||||
|
||||
/**
|
||||
* @brief 获取缩放比例
|
||||
* @return 缩放比例
|
||||
*/
|
||||
float getPinchScale() const;
|
||||
|
||||
/**
|
||||
* @brief 获取缩放中心点
|
||||
* @return 缩放中心点
|
||||
*/
|
||||
Vec2 getPinchCenter() const;
|
||||
|
||||
/**
|
||||
* @brief 检查是否正在双指旋转
|
||||
* @return 如果正在旋转返回 true
|
||||
*/
|
||||
bool isRotating() const;
|
||||
|
||||
/**
|
||||
* @brief 获取旋转角度
|
||||
* @return 旋转角度(度数)
|
||||
*/
|
||||
float getRotationAngle() const;
|
||||
|
||||
// ==================== 手柄输入 ====================
|
||||
|
||||
/**
|
||||
* @brief 获取已连接手柄数量
|
||||
* @return 手柄数量
|
||||
*/
|
||||
i32 getConnectedGamepadCount() const;
|
||||
|
||||
/**
|
||||
* @brief 检查手柄是否已连接
|
||||
* @param id 手柄 ID
|
||||
* @return 如果已连接返回 true
|
||||
*/
|
||||
bool isGamepadConnected(i32 id) const;
|
||||
|
||||
/**
|
||||
* @brief 获取手柄名称
|
||||
* @param id 手柄 ID
|
||||
* @return 手柄名称
|
||||
*/
|
||||
std::string getGamepadName(i32 id) const;
|
||||
|
||||
/**
|
||||
* @brief 检查手柄按钮是否按下
|
||||
* @param id 手柄 ID
|
||||
* @param button 按钮索引
|
||||
* @return 如果按下返回 true
|
||||
*/
|
||||
bool isGamepadButtonDown(i32 id, i32 button) const;
|
||||
|
||||
/**
|
||||
* @brief 获取手柄轴值
|
||||
* @param id 手柄 ID
|
||||
* @param axis 轴索引
|
||||
* @return 轴值 (-1.0 到 1.0)
|
||||
*/
|
||||
float getGamepadAxis(i32 id, i32 axis) const;
|
||||
|
||||
/**
|
||||
* @brief 设置手柄震动
|
||||
* @param id 手柄 ID
|
||||
* @param lowFreq 低频震动强度 (0.0 到 1.0)
|
||||
* @param highFreq 高频震动强度 (0.0 到 1.0)
|
||||
* @param durationMs 震动持续时间(毫秒)
|
||||
*/
|
||||
void setGamepadRumble(i32 id, float lowFreq, float highFreq, u32 durationMs);
|
||||
|
||||
// ==================== 内部方法 ====================
|
||||
|
||||
/**
|
||||
* @brief 处理键盘按下事件
|
||||
*/
|
||||
void handleKeyDown(i32 keycode);
|
||||
|
||||
/**
|
||||
* @brief 处理键盘释放事件
|
||||
*/
|
||||
void handleKeyUp(i32 keycode);
|
||||
|
||||
/**
|
||||
* @brief 处理鼠标移动事件
|
||||
*/
|
||||
void handleMouseMove(float x, float y, float dx, float dy);
|
||||
|
||||
/**
|
||||
* @brief 处理鼠标按钮按下事件
|
||||
*/
|
||||
void handleMouseButtonDown(i32 button);
|
||||
|
||||
/**
|
||||
* @brief 处理鼠标按钮释放事件
|
||||
*/
|
||||
void handleMouseButtonUp(i32 button);
|
||||
|
||||
/**
|
||||
* @brief 处理鼠标滚轮事件
|
||||
*/
|
||||
void handleMouseWheel(float dx, float dy);
|
||||
|
||||
/**
|
||||
* @brief 处理触摸开始事件
|
||||
*/
|
||||
void handleTouchDown(i64 id, float x, float y, float pressure);
|
||||
|
||||
/**
|
||||
* @brief 处理触摸移动事件
|
||||
*/
|
||||
void handleTouchMove(i64 id, float x, float y, float dx, float dy, float pressure);
|
||||
|
||||
/**
|
||||
* @brief 处理触摸结束事件
|
||||
*/
|
||||
void handleTouchUp(i64 id);
|
||||
|
||||
/**
|
||||
* @brief 处理手柄连接事件
|
||||
*/
|
||||
void handleGamepadConnected(i32 id);
|
||||
|
||||
/**
|
||||
* @brief 处理手柄断开事件
|
||||
*/
|
||||
void handleGamepadDisconnected(i32 id);
|
||||
|
||||
/**
|
||||
* @brief 处理手柄按钮事件
|
||||
*/
|
||||
void handleGamepadButton(i32 id, i32 button, bool pressed);
|
||||
|
||||
/**
|
||||
* @brief 处理手柄轴事件
|
||||
*/
|
||||
void handleGamepadAxis(i32 id, i32 axis, float value);
|
||||
|
||||
private:
|
||||
Input();
|
||||
~Input();
|
||||
|
||||
Input(const Input&) = delete;
|
||||
Input& operator=(const Input&) = delete;
|
||||
|
||||
void updateGestures();
|
||||
|
||||
static Input* instance_;
|
||||
|
||||
static constexpr size_t KEY_COUNT = 512;
|
||||
static constexpr size_t MOUSE_BUTTON_COUNT = 8;
|
||||
static constexpr size_t GAMEPAD_COUNT = 4;
|
||||
static constexpr size_t GAMEPAD_BUTTON_COUNT = 32;
|
||||
static constexpr size_t GAMEPAD_AXIS_COUNT = 8;
|
||||
|
||||
std::array<bool, KEY_COUNT> currentKeyState_{};
|
||||
std::array<bool, KEY_COUNT> previousKeyState_{};
|
||||
|
||||
Vec2 mousePosition_{0, 0};
|
||||
Vec2 mouseDelta_{0, 0};
|
||||
Vec2 mouseWheelDelta_{0, 0};
|
||||
std::array<bool, MOUSE_BUTTON_COUNT> currentMouseState_{};
|
||||
std::array<bool, MOUSE_BUTTON_COUNT> previousMouseState_{};
|
||||
bool mouseLocked_{false};
|
||||
|
||||
std::vector<Touch> activeTouches_;
|
||||
std::vector<i64> beganTouchIds_;
|
||||
std::vector<i64> endedTouchIds_;
|
||||
|
||||
float lastPinchDistance_{0};
|
||||
float pinchScale_{1.0f};
|
||||
Vec2 pinchCenter_{0, 0};
|
||||
bool pinching_{false};
|
||||
|
||||
float lastRotationAngle_{0};
|
||||
float rotationAngle_{0};
|
||||
bool rotating_{false};
|
||||
|
||||
struct GamepadState {
|
||||
SDL_GameController* controller{nullptr};
|
||||
std::array<bool, GAMEPAD_BUTTON_COUNT> buttonState_{};
|
||||
std::array<float, GAMEPAD_AXIS_COUNT> axisState_{};
|
||||
bool connected{false};
|
||||
std::string name;
|
||||
};
|
||||
std::array<GamepadState, GAMEPAD_COUNT> gamepads_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <extra2d/core/event/EventBus.h>
|
||||
|
||||
struct SDL_Window;
|
||||
struct SDL_Event;
|
||||
struct SDL_KeyboardEvent;
|
||||
struct SDL_MouseMotionEvent;
|
||||
struct SDL_MouseButtonEvent;
|
||||
struct SDL_MouseWheelEvent;
|
||||
struct SDL_ControllerDeviceEvent;
|
||||
struct SDL_ControllerButtonEvent;
|
||||
struct SDL_ControllerAxisEvent;
|
||||
struct SDL_TouchFingerEvent;
|
||||
struct SDL_MultiGestureEvent;
|
||||
struct SDL_DollarGestureEvent;
|
||||
struct SDL_WindowEvent;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief SDL2 辅助类
|
||||
*
|
||||
* 封装 SDL2 的初始化、窗口创建和事件分发功能。
|
||||
* 参考 Cocos Creator 的 SDLHelper 设计。
|
||||
*/
|
||||
class SDLHelper {
|
||||
public:
|
||||
/**
|
||||
* @brief 初始化 SDL2
|
||||
* @return 成功返回 0,失败返回错误码
|
||||
*/
|
||||
static i32 init();
|
||||
|
||||
/**
|
||||
* @brief 关闭 SDL2
|
||||
*/
|
||||
static void quit();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
* @return 如果已初始化返回 true
|
||||
*/
|
||||
static bool isInitialized();
|
||||
|
||||
/**
|
||||
* @brief 创建窗口
|
||||
* @param title 窗口标题
|
||||
* @param w 窗口宽度
|
||||
* @param h 窗口高度
|
||||
* @param flags 窗口标志
|
||||
* @return SDL 窗口指针
|
||||
*/
|
||||
static SDL_Window* createWindow(const char* title, i32 w, i32 h, u32 flags);
|
||||
|
||||
/**
|
||||
* @brief 创建窗口(指定位置)
|
||||
* @param title 窗口标题
|
||||
* @param x 窗口 X 坐标
|
||||
* @param y 窗口 Y 坐标
|
||||
* @param w 窗口宽度
|
||||
* @param h 窗口高度
|
||||
* @param flags 窗口标志
|
||||
* @return SDL 窗口指针
|
||||
*/
|
||||
static SDL_Window* createWindow(const char* title, i32 x, i32 y, i32 w, i32 h, u32 flags);
|
||||
|
||||
/**
|
||||
* @brief 销毁窗口
|
||||
* @param window SDL 窗口指针
|
||||
*/
|
||||
static void destroyWindow(SDL_Window* window);
|
||||
|
||||
/**
|
||||
* @brief 获取窗口原生句柄
|
||||
* @param window SDL 窗口指针
|
||||
* @return 原生窗口句柄
|
||||
*/
|
||||
static void* getWindowHandle(SDL_Window* window);
|
||||
|
||||
/**
|
||||
* @brief 获取窗口位置
|
||||
* @param window SDL 窗口指针
|
||||
* @return 窗口位置
|
||||
*/
|
||||
static Vec2 getWindowPosition(SDL_Window* window);
|
||||
|
||||
/**
|
||||
* @brief 获取窗口大小
|
||||
* @param window SDL 窗口指针
|
||||
* @return 窗口大小
|
||||
*/
|
||||
static Vec2 getWindowSize(SDL_Window* window);
|
||||
|
||||
/**
|
||||
* @brief 检查窗口是否最小化
|
||||
* @param window SDL 窗口指针
|
||||
* @return 如果最小化返回 true
|
||||
*/
|
||||
static bool isWindowMinimized(SDL_Window* window);
|
||||
|
||||
/**
|
||||
* @brief 检查窗口是否获得焦点
|
||||
* @param window SDL 窗口指针
|
||||
* @return 如果获得焦点返回 true
|
||||
*/
|
||||
static bool isWindowFocused(SDL_Window* window);
|
||||
|
||||
/**
|
||||
* @brief 设置光标可见性
|
||||
* @param enabled 是否可见
|
||||
*/
|
||||
static void setCursorEnabled(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief 检查光标是否可见
|
||||
* @return 如果可见返回 true
|
||||
*/
|
||||
static bool isCursorEnabled();
|
||||
|
||||
/**
|
||||
* @brief 设置光标锁定模式
|
||||
* @param locked 是否锁定
|
||||
*/
|
||||
static void setCursorLocked(bool locked);
|
||||
|
||||
/**
|
||||
* @brief 检查光标是否锁定
|
||||
* @return 如果锁定返回 true
|
||||
*/
|
||||
static bool isCursorLocked();
|
||||
|
||||
/**
|
||||
* @brief 开始文本输入
|
||||
*/
|
||||
static void startTextInput();
|
||||
|
||||
/**
|
||||
* @brief 停止文本输入
|
||||
*/
|
||||
static void stopTextInput();
|
||||
|
||||
/**
|
||||
* @brief 检查是否在文本输入模式
|
||||
* @return 如果在文本输入模式返回 true
|
||||
*/
|
||||
static bool isTextInputActive();
|
||||
|
||||
/**
|
||||
* @brief 分发 SDL 事件
|
||||
* @param event SDL 事件
|
||||
*/
|
||||
static void dispatchSDLEvent(const SDL_Event& event);
|
||||
|
||||
/**
|
||||
* @brief 轮询所有 SDL 事件并分发
|
||||
*/
|
||||
static void pollAndDispatchEvents();
|
||||
|
||||
/**
|
||||
* @brief 获取触摸设备 ID
|
||||
* @return 触摸设备 ID,如果没有返回 -1
|
||||
*/
|
||||
static i64 getTouchDeviceId();
|
||||
|
||||
/**
|
||||
* @brief 检查是否有触摸设备
|
||||
* @return 如果有触摸设备返回 true
|
||||
*/
|
||||
static bool hasTouchDevice();
|
||||
|
||||
private:
|
||||
static void dispatchWindowEvent(const SDL_WindowEvent& event);
|
||||
static void dispatchKeyboardEvent(const SDL_KeyboardEvent& event);
|
||||
static void dispatchMouseMotionEvent(const SDL_MouseMotionEvent& event);
|
||||
static void dispatchMouseButtonEvent(const SDL_MouseButtonEvent& event);
|
||||
static void dispatchMouseWheelEvent(const SDL_MouseWheelEvent& event);
|
||||
static void dispatchControllerDeviceEvent(const SDL_ControllerDeviceEvent& event);
|
||||
static void dispatchControllerButtonEvent(const SDL_ControllerButtonEvent& event);
|
||||
static void dispatchControllerAxisEvent(const SDL_ControllerAxisEvent& event);
|
||||
static void dispatchTouchFingerEvent(const SDL_TouchFingerEvent& event);
|
||||
static void dispatchMultiGestureEvent(const SDL_MultiGestureEvent& event);
|
||||
static void dispatchDollarGestureEvent(const SDL_DollarGestureEvent& event);
|
||||
|
||||
static bool initialized_;
|
||||
static bool cursorVisible_;
|
||||
static bool cursorLocked_;
|
||||
static i64 touchDeviceId_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,233 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <string>
|
||||
|
||||
struct SDL_Window;
|
||||
struct SDL_GLContext;
|
||||
typedef void* SDL_GLContext;
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 窗口配置
|
||||
*/
|
||||
struct WindowConfig {
|
||||
std::string title = "Extra2D";
|
||||
i32 width = 1280;
|
||||
i32 height = 720;
|
||||
bool fullscreen = false;
|
||||
bool resizable = true;
|
||||
bool vsync = true;
|
||||
bool borderless = false;
|
||||
bool highDPI = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 窗口类
|
||||
*
|
||||
* 封装 SDL2 窗口和 OpenGL 上下文管理。
|
||||
*/
|
||||
class Window {
|
||||
public:
|
||||
/**
|
||||
* @brief 获取单例实例
|
||||
* @return 窗口实例
|
||||
*/
|
||||
static Window* getInstance();
|
||||
|
||||
/**
|
||||
* @brief 初始化窗口
|
||||
* @param config 窗口配置
|
||||
* @return 成功返回 0,失败返回错误码
|
||||
*/
|
||||
i32 initialize(const WindowConfig& config);
|
||||
|
||||
/**
|
||||
* @brief 关闭窗口
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief 检查是否已初始化
|
||||
* @return 如果已初始化返回 true
|
||||
*/
|
||||
bool isInitialized() const;
|
||||
|
||||
/**
|
||||
* @brief 获取窗口大小
|
||||
* @return 窗口大小 (宽度, 高度)
|
||||
*/
|
||||
Vec2 getSize() const;
|
||||
|
||||
/**
|
||||
* @brief 设置窗口大小
|
||||
* @param width 宽度
|
||||
* @param height 高度
|
||||
*/
|
||||
void setSize(i32 width, i32 height);
|
||||
|
||||
/**
|
||||
* @brief 获取窗口位置
|
||||
* @return 窗口位置
|
||||
*/
|
||||
Vec2 getPosition() const;
|
||||
|
||||
/**
|
||||
* @brief 设置窗口位置
|
||||
* @param x X 坐标
|
||||
* @param y Y 坐标
|
||||
*/
|
||||
void setPosition(i32 x, i32 y);
|
||||
|
||||
/**
|
||||
* @brief 获取窗口标题
|
||||
* @return 窗口标题
|
||||
*/
|
||||
std::string getTitle() const;
|
||||
|
||||
/**
|
||||
* @brief 设置窗口标题
|
||||
* @param title 标题
|
||||
*/
|
||||
void setTitle(const std::string& title);
|
||||
|
||||
/**
|
||||
* @brief 检查是否全屏
|
||||
* @return 如果全屏返回 true
|
||||
*/
|
||||
bool isFullscreen() const;
|
||||
|
||||
/**
|
||||
* @brief 设置全屏模式
|
||||
* @param fullscreen 是否全屏
|
||||
*/
|
||||
void setFullscreen(bool fullscreen);
|
||||
|
||||
/**
|
||||
* @brief 设置无边框窗口模式
|
||||
* @param borderless 是否无边框
|
||||
*/
|
||||
void setBorderless(bool borderless);
|
||||
|
||||
/**
|
||||
* @brief 检查是否可调整大小
|
||||
* @return 如果可调整大小返回 true
|
||||
*/
|
||||
bool isResizable() const;
|
||||
|
||||
/**
|
||||
* @brief 设置是否可调整大小
|
||||
* @param resizable 是否可调整大小
|
||||
*/
|
||||
void setResizable(bool resizable);
|
||||
|
||||
/**
|
||||
* @brief 检查窗口是否获得焦点
|
||||
* @return 如果获得焦点返回 true
|
||||
*/
|
||||
bool hasFocus() const;
|
||||
|
||||
/**
|
||||
* @brief 检查窗口是否最小化
|
||||
* @return 如果最小化返回 true
|
||||
*/
|
||||
bool isMinimized() const;
|
||||
|
||||
/**
|
||||
* @brief 检查窗口是否最大化
|
||||
* @return 如果最大化返回 true
|
||||
*/
|
||||
bool isMaximized() const;
|
||||
|
||||
/**
|
||||
* @brief 设置垂直同步
|
||||
* @param enabled 是否启用
|
||||
*/
|
||||
void setVSync(bool enabled);
|
||||
|
||||
/**
|
||||
* @brief 检查是否启用垂直同步
|
||||
* @return 如果启用返回 true
|
||||
*/
|
||||
bool isVSync() const;
|
||||
|
||||
/**
|
||||
* @brief 显示窗口
|
||||
*/
|
||||
void show();
|
||||
|
||||
/**
|
||||
* @brief 隐藏窗口
|
||||
*/
|
||||
void hide();
|
||||
|
||||
/**
|
||||
* @brief 最小化窗口
|
||||
*/
|
||||
void minimize();
|
||||
|
||||
/**
|
||||
* @brief 最大化窗口
|
||||
*/
|
||||
void maximize();
|
||||
|
||||
/**
|
||||
* @brief 还原窗口
|
||||
*/
|
||||
void restore();
|
||||
|
||||
/**
|
||||
* @brief 交换缓冲区
|
||||
*/
|
||||
void swapBuffers();
|
||||
|
||||
/**
|
||||
* @brief 获取 SDL 窗口指针
|
||||
* @return SDL 窗口指针
|
||||
*/
|
||||
SDL_Window* getSDLWindow();
|
||||
|
||||
/**
|
||||
* @brief 获取 OpenGL 上下文
|
||||
* @return OpenGL 上下文
|
||||
*/
|
||||
SDL_GLContext getGLContext();
|
||||
|
||||
/**
|
||||
* @brief 获取帧缓冲大小(可能在高 DPI 显示器上与窗口大小不同)
|
||||
* @return 帧缓冲大小
|
||||
*/
|
||||
Vec2 getFramebufferSize() const;
|
||||
|
||||
/**
|
||||
* @brief 获取显示缩放因子
|
||||
* @return 缩放因子
|
||||
*/
|
||||
float getContentScale() const;
|
||||
|
||||
/**
|
||||
* @brief 获取原生窗口句柄
|
||||
* @return 原生窗口句柄
|
||||
*/
|
||||
void* getNativeHandle();
|
||||
|
||||
private:
|
||||
Window();
|
||||
~Window();
|
||||
|
||||
Window(const Window&) = delete;
|
||||
Window& operator=(const Window&) = delete;
|
||||
|
||||
static Window* instance_;
|
||||
|
||||
SDL_Window* sdlWindow_{nullptr};
|
||||
SDL_GLContext glContext_{nullptr};
|
||||
bool initialized_{false};
|
||||
bool vsync_{true};
|
||||
bool fullscreen_{false};
|
||||
bool focused_{true};
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,226 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/types.h>
|
||||
#include <extra2d/core/math_types.h>
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 光标形状
|
||||
*/
|
||||
enum class Cursor {
|
||||
Arrow,
|
||||
IBeam,
|
||||
Crosshair,
|
||||
Hand,
|
||||
HResize,
|
||||
VResize,
|
||||
Hidden
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief GLFW 窗口实现
|
||||
*/
|
||||
class GLFWWindow {
|
||||
public:
|
||||
using ResizeFn = std::function<void(int, int)>;
|
||||
using CloseFn = std::function<void()>;
|
||||
using FocusFn = std::function<void(bool)>;
|
||||
|
||||
GLFWWindow();
|
||||
~GLFWWindow();
|
||||
|
||||
/**
|
||||
* @brief 创建窗口
|
||||
* @param title 窗口标题
|
||||
* @param w 窗口宽度
|
||||
* @param h 窗口高度
|
||||
* @param vsync 是否启用垂直同步
|
||||
* @return 创建是否成功
|
||||
*/
|
||||
bool create(const std::string& title, int w, int h, bool vsync = true);
|
||||
|
||||
/**
|
||||
* @brief 销毁窗口
|
||||
*/
|
||||
void destroy();
|
||||
|
||||
/**
|
||||
* @brief 轮询事件
|
||||
*/
|
||||
void poll();
|
||||
|
||||
/**
|
||||
* @brief 交换缓冲区
|
||||
*/
|
||||
void swap();
|
||||
|
||||
/**
|
||||
* @brief 窗口是否应该关闭
|
||||
*/
|
||||
bool shouldClose() const;
|
||||
|
||||
/**
|
||||
* @brief 设置窗口关闭标志
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @brief 设置窗口标题
|
||||
*/
|
||||
void title(const std::string& t);
|
||||
|
||||
/**
|
||||
* @brief 设置窗口大小
|
||||
*/
|
||||
void size(int w, int h);
|
||||
|
||||
/**
|
||||
* @brief 设置窗口位置
|
||||
*/
|
||||
void pos(int x, int y);
|
||||
|
||||
/**
|
||||
* @brief 设置全屏模式
|
||||
*/
|
||||
void fullscreen(bool fs);
|
||||
|
||||
/**
|
||||
* @brief 设置垂直同步
|
||||
*/
|
||||
void vsync(bool v);
|
||||
|
||||
/**
|
||||
* @brief 设置窗口可见性
|
||||
*/
|
||||
void visible(bool v);
|
||||
|
||||
/**
|
||||
* @brief 获取窗口宽度
|
||||
*/
|
||||
int w() const { return w_; }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口高度
|
||||
*/
|
||||
int h() const { return h_; }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口大小
|
||||
*/
|
||||
Size size() const;
|
||||
|
||||
/**
|
||||
* @brief 获取窗口位置
|
||||
*/
|
||||
Vec2 pos() const;
|
||||
|
||||
/**
|
||||
* @brief 是否全屏
|
||||
*/
|
||||
bool fullscreen() const { return fullscreen_; }
|
||||
|
||||
/**
|
||||
* @brief 是否启用垂直同步
|
||||
*/
|
||||
bool vsync() const { return vsync_; }
|
||||
|
||||
/**
|
||||
* @brief 窗口是否获得焦点
|
||||
*/
|
||||
bool focused() const { return focused_; }
|
||||
|
||||
/**
|
||||
* @brief 窗口是否最小化
|
||||
*/
|
||||
bool minimized() const { return minimized_; }
|
||||
|
||||
/**
|
||||
* @brief 获取内容缩放X
|
||||
*/
|
||||
f32 scaleX() const { return scaleX_; }
|
||||
|
||||
/**
|
||||
* @brief 获取内容缩放Y
|
||||
*/
|
||||
f32 scaleY() const { return scaleY_; }
|
||||
|
||||
/**
|
||||
* @brief 设置光标形状
|
||||
*/
|
||||
void cursor(Cursor c);
|
||||
|
||||
/**
|
||||
* @brief 显示/隐藏光标
|
||||
*/
|
||||
void showCursor(bool show);
|
||||
|
||||
/**
|
||||
* @brief 锁定/解锁光标
|
||||
*/
|
||||
void lockCursor(bool lock);
|
||||
|
||||
/**
|
||||
* @brief 设置大小改变回调
|
||||
*/
|
||||
void onResize(ResizeFn fn) { resizeFn_ = fn; }
|
||||
|
||||
/**
|
||||
* @brief 设置关闭回调
|
||||
*/
|
||||
void onClose(CloseFn fn) { closeFn_ = fn; }
|
||||
|
||||
/**
|
||||
* @brief 设置焦点改变回调
|
||||
*/
|
||||
void onFocus(FocusFn fn) { focusFn_ = fn; }
|
||||
|
||||
/**
|
||||
* @brief 获取原生窗口句柄
|
||||
*/
|
||||
void* native() const;
|
||||
|
||||
/**
|
||||
* @brief 获取 GLFW 窗口句柄
|
||||
*/
|
||||
GLFWwindow* handle() const { return handle_; }
|
||||
|
||||
private:
|
||||
bool initGLFW();
|
||||
void deinitGLFW();
|
||||
void updateContentScale();
|
||||
|
||||
// GLFW 回调函数(静态)
|
||||
static void framebufferSizeCallback(GLFWwindow* window, int w, int h);
|
||||
static void windowCloseCallback(GLFWwindow* window);
|
||||
static void windowFocusCallback(GLFWwindow* window, int focused);
|
||||
static void windowIconifyCallback(GLFWwindow* window, int iconified);
|
||||
static void cursorPosCallback(GLFWwindow* window, double x, double y);
|
||||
static void mouseButtonCallback(GLFWwindow* window, int btn, int action, int mods);
|
||||
static void scrollCallback(GLFWwindow* window, double x, double y);
|
||||
static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
|
||||
static void joystickCallback(int jid, int event);
|
||||
|
||||
GLFWwindow* handle_ = nullptr;
|
||||
|
||||
int w_ = 1280;
|
||||
int h_ = 720;
|
||||
bool fullscreen_ = false;
|
||||
bool vsync_ = true;
|
||||
bool focused_ = true;
|
||||
bool minimized_ = false;
|
||||
bool shouldClose_ = false;
|
||||
f32 scaleX_ = 1.0f;
|
||||
f32 scaleY_ = 1.0f;
|
||||
bool cursorVisible_ = true;
|
||||
bool cursorLocked_ = false;
|
||||
|
||||
ResizeFn resizeFn_;
|
||||
CloseFn closeFn_;
|
||||
FocusFn focusFn_;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 键盘按键码
|
||||
*/
|
||||
enum class Key : int {
|
||||
None = 0,
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M,
|
||||
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||
Num0, Num1, Num2, Num3, Num4,
|
||||
Num5, Num6, Num7, Num8, Num9,
|
||||
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
|
||||
Space, Enter, Escape, Tab, Backspace,
|
||||
Insert, Delete, Home, End, PageUp, PageDown,
|
||||
Up, Down, Left, Right,
|
||||
LShift, RShift, LCtrl, RCtrl, LAlt, RAlt,
|
||||
CapsLock, NumLock, ScrollLock,
|
||||
Count
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 鼠标按钮
|
||||
*/
|
||||
enum class Mouse : int {
|
||||
Left = 0,
|
||||
Right,
|
||||
Middle,
|
||||
X1,
|
||||
X2,
|
||||
Count
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 游戏手柄按钮
|
||||
*/
|
||||
enum class Gamepad : int {
|
||||
A = 0,
|
||||
B,
|
||||
X,
|
||||
Y,
|
||||
LB,
|
||||
RB,
|
||||
LT,
|
||||
RT,
|
||||
Back,
|
||||
Start,
|
||||
Guide,
|
||||
LStick,
|
||||
RStick,
|
||||
DUp,
|
||||
DDown,
|
||||
DLeft,
|
||||
DRight,
|
||||
Count
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 手柄轴
|
||||
*/
|
||||
enum class GamepadAxis : int {
|
||||
LeftX = 0,
|
||||
LeftY,
|
||||
RightX,
|
||||
RightY,
|
||||
LeftTrigger,
|
||||
RightTrigger,
|
||||
Count
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/module.h>
|
||||
#include <extra2d/platform/glfw/glfw_window.h>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 窗口模块配置结构
|
||||
*/
|
||||
struct WindowCfg {
|
||||
std::string title;
|
||||
int w;
|
||||
int h;
|
||||
bool vsync;
|
||||
int priority;
|
||||
|
||||
WindowCfg()
|
||||
: title("Extra2D"), w(1280), h(720), vsync(true), priority(0) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 窗口模块
|
||||
* 管理窗口创建和生命周期
|
||||
*/
|
||||
class WindowModule : public Module {
|
||||
public:
|
||||
/**
|
||||
* @brief 构造函数(Lambda 配置)
|
||||
* @param configFn 配置函数
|
||||
*/
|
||||
explicit WindowModule(std::function<void(WindowCfg &)> configFn);
|
||||
|
||||
/**
|
||||
* @brief 析构函数
|
||||
*/
|
||||
~WindowModule() override;
|
||||
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
bool ok() const override { return initialized_; }
|
||||
const char *name() const override { return "window"; }
|
||||
int priority() const override { return cfg_.priority; }
|
||||
|
||||
/**
|
||||
* @brief 获取窗口
|
||||
* @return 窗口指针
|
||||
*/
|
||||
GLFWWindow *win() const { return win_.get(); }
|
||||
|
||||
private:
|
||||
WindowCfg cfg_;
|
||||
Unique<GLFWWindow> win_;
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,334 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <extra2d/asset/asset.h>
|
||||
#include <extra2d/asset/asset_cache.h>
|
||||
#include <extra2d/asset/asset_handle.h>
|
||||
#include <extra2d/asset/asset_loader.h>
|
||||
#include <extra2d/asset/asset_pack.h>
|
||||
#include <extra2d/asset/asset_types.h>
|
||||
#include <extra2d/asset/data_processor.h>
|
||||
#include <extra2d/core/service_interface.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <typeindex>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// IAssetService - 资源服务接口
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源服务接口
|
||||
*
|
||||
* 提供资源加载、缓存、异步加载等功能。
|
||||
* 使用模板方法支持类型安全的资源加载。
|
||||
*/
|
||||
class IAssetService : public IService {
|
||||
public:
|
||||
virtual ~IAssetService() = default;
|
||||
|
||||
/**
|
||||
* @brief 同步加载资源
|
||||
* @tparam T 资源类型
|
||||
* @param path 资源路径
|
||||
* @return 资源句柄
|
||||
*/
|
||||
template <typename T> AssetHandle<T> load(const std::string &path) {
|
||||
static_assert(std::is_base_of_v<Asset, T>, "T must derive from Asset");
|
||||
return AssetHandle<T>(loadImpl(AssetID(path), typeid(T)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 异步加载资源
|
||||
* @tparam T 资源类型
|
||||
* @param path 资源路径
|
||||
* @param callback 加载完成回调
|
||||
*/
|
||||
template <typename T>
|
||||
void loadAsync(const std::string &path, AssetLoadCallback<T> callback) {
|
||||
static_assert(std::is_base_of_v<Asset, T>, "T must derive from Asset");
|
||||
loadAsyncImpl(AssetID(path), typeid(T),
|
||||
[cb = std::move(callback)](AssetHandleBase handle) {
|
||||
cb(AssetHandle<T>(handle));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取已缓存的资源
|
||||
* @tparam T 资源类型
|
||||
* @param path 资源路径
|
||||
* @return 资源句柄,不存在返回空句柄
|
||||
*/
|
||||
template <typename T> AssetHandle<T> get(const std::string &path) {
|
||||
static_assert(std::is_base_of_v<Asset, T>, "T must derive from Asset");
|
||||
return AssetHandle<T>(getImpl(AssetID(path), typeid(T)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 预加载资源(后台加载,不返回句柄)
|
||||
* @tparam T 资源类型
|
||||
* @param path 资源路径
|
||||
*/
|
||||
template <typename T> void preload(const std::string &path) {
|
||||
static_assert(std::is_base_of_v<Asset, T>, "T must derive from Asset");
|
||||
preloadImpl(AssetID(path), typeid(T));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 检查资源是否已加载
|
||||
* @param path 资源路径
|
||||
* @return 已加载返回 true
|
||||
*/
|
||||
virtual bool isLoaded(const std::string &path) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查资源是否正在加载
|
||||
* @param path 资源路径
|
||||
* @return 正在加载返回 true
|
||||
*/
|
||||
virtual bool isLoading(const std::string &path) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 卸载资源
|
||||
* @param path 资源路径
|
||||
*/
|
||||
virtual void unload(const std::string &path) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置缓存上限
|
||||
* @param maxBytes 最大字节数
|
||||
*/
|
||||
virtual void setLimit(size_t maxBytes) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取当前缓存大小
|
||||
* @return 缓存字节数
|
||||
*/
|
||||
virtual size_t size() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 清理无引用资源
|
||||
*/
|
||||
virtual void purge() = 0;
|
||||
|
||||
/**
|
||||
* @brief 清空所有缓存
|
||||
*/
|
||||
virtual void clear() = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取缓存统计信息
|
||||
* @return 缓存统计
|
||||
*/
|
||||
virtual CacheStats stats() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 注册加载器
|
||||
* @tparam T 资源类型
|
||||
* @param loader 加载器实例
|
||||
*/
|
||||
template <typename T> void registerLoader(Unique<AssetLoader<T>> loader) {
|
||||
static_assert(std::is_base_of_v<Asset, T>, "T must derive from Asset");
|
||||
registerLoaderImpl(typeid(T), std::move(loader));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 挂载资源包
|
||||
* @param path 资源包路径
|
||||
* @return 成功返回 true
|
||||
*/
|
||||
virtual bool mount(const std::string &path) = 0;
|
||||
|
||||
/**
|
||||
* @brief 卸载资源包
|
||||
* @param path 资源包路径
|
||||
*/
|
||||
virtual void unmount(const std::string &path) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置数据处理管道
|
||||
* @param pipe 处理管道
|
||||
*/
|
||||
virtual void setPipe(DataPipe pipe) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置资源根目录
|
||||
* @param path 根目录路径
|
||||
*/
|
||||
virtual void setRoot(const std::string &path) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取资源根目录
|
||||
* @return 根目录路径
|
||||
*/
|
||||
virtual std::string root() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 处理异步加载完成回调(在主线程调用)
|
||||
*/
|
||||
virtual void process() = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief 同步加载实现
|
||||
*/
|
||||
virtual AssetHandleBase loadImpl(const AssetID &id, std::type_index type) = 0;
|
||||
|
||||
/**
|
||||
* @brief 异步加载实现
|
||||
*/
|
||||
virtual void loadAsyncImpl(const AssetID &id, std::type_index type,
|
||||
std::function<void(AssetHandleBase)> callback) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取资源实现
|
||||
*/
|
||||
virtual AssetHandleBase getImpl(const AssetID &id, std::type_index type) = 0;
|
||||
|
||||
/**
|
||||
* @brief 预加载实现
|
||||
*/
|
||||
virtual void preloadImpl(const AssetID &id, std::type_index type) = 0;
|
||||
|
||||
/**
|
||||
* @brief 注册加载器实现
|
||||
*/
|
||||
virtual void registerLoaderImpl(std::type_index type,
|
||||
Unique<AssetLoaderBase> loader) = 0;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetService - 资源服务实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief 资源服务实现
|
||||
*
|
||||
* 实现资源加载、缓存、异步加载等功能。
|
||||
* 使用线程池处理异步加载任务。
|
||||
*/
|
||||
class AssetService : public IAssetService {
|
||||
public:
|
||||
AssetService();
|
||||
~AssetService() override;
|
||||
|
||||
ServiceInfo info() const override {
|
||||
ServiceInfo i;
|
||||
i.name = "AssetService";
|
||||
i.priority = ServicePriority::Resource;
|
||||
i.enabled = true;
|
||||
return i;
|
||||
}
|
||||
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
|
||||
bool isLoaded(const std::string &path) const override;
|
||||
bool isLoading(const std::string &path) const override;
|
||||
void unload(const std::string &path) override;
|
||||
|
||||
void setLimit(size_t maxBytes) override;
|
||||
size_t size() const override;
|
||||
void purge() override;
|
||||
void clear() override;
|
||||
CacheStats stats() const override;
|
||||
|
||||
bool mount(const std::string &path) override;
|
||||
void unmount(const std::string &path) override;
|
||||
void setPipe(DataPipe pipe) override;
|
||||
void setRoot(const std::string &path) override;
|
||||
std::string root() const override;
|
||||
|
||||
void process() override;
|
||||
|
||||
protected:
|
||||
AssetHandleBase loadImpl(const AssetID &id, std::type_index type) override;
|
||||
void loadAsyncImpl(const AssetID &id, std::type_index type,
|
||||
std::function<void(AssetHandleBase)> callback) override;
|
||||
AssetHandleBase getImpl(const AssetID &id, std::type_index type) override;
|
||||
void preloadImpl(const AssetID &id, std::type_index type) override;
|
||||
void registerLoaderImpl(std::type_index type,
|
||||
Unique<AssetLoaderBase> loader) override;
|
||||
|
||||
private:
|
||||
struct LoadTask {
|
||||
AssetID id;
|
||||
std::type_index type = typeid(void);
|
||||
std::function<void(AssetHandleBase)> callback;
|
||||
};
|
||||
|
||||
struct LoadedAsset {
|
||||
Ref<Asset> asset;
|
||||
std::type_index type = typeid(void);
|
||||
};
|
||||
|
||||
std::string root_;
|
||||
Unique<AssetCache> cache_;
|
||||
PackManager packManager_;
|
||||
DataPipe pipe_;
|
||||
|
||||
mutable std::shared_mutex mutex_;
|
||||
std::unordered_map<AssetID, LoadedAsset> assets_;
|
||||
std::unordered_map<AssetID, AssetState> states_;
|
||||
std::unordered_map<std::type_index, Unique<AssetLoaderBase>> loaders_;
|
||||
|
||||
std::thread workerThread_;
|
||||
std::queue<LoadTask> taskQueue_;
|
||||
std::mutex taskMutex_;
|
||||
std::condition_variable taskCv_;
|
||||
std::atomic<bool> running_{false};
|
||||
|
||||
std::queue<std::function<void()>> callbackQueue_;
|
||||
std::mutex callbackMutex_;
|
||||
|
||||
/**
|
||||
* @brief 工作线程函数
|
||||
*/
|
||||
void workerFunc();
|
||||
|
||||
/**
|
||||
* @brief 从文件系统加载资源
|
||||
* @param id 资源ID
|
||||
* @param type 资源类型
|
||||
* @return 加载的资源
|
||||
*/
|
||||
Ref<Asset> loadFromFile(const AssetID &id, std::type_index type);
|
||||
|
||||
/**
|
||||
* @brief 从资源包加载资源
|
||||
* @param id 资源ID
|
||||
* @param type 资源类型
|
||||
* @return 加载的资源
|
||||
*/
|
||||
Ref<Asset> loadFromPack(const AssetID &id, std::type_index type);
|
||||
|
||||
/**
|
||||
* @brief 获取加载器
|
||||
* @param type 资源类型
|
||||
* @return 加载器指针
|
||||
*/
|
||||
AssetLoaderBase *getLoader(std::type_index type);
|
||||
|
||||
/**
|
||||
* @brief 根据路径推断资源类型
|
||||
* @param path 资源路径
|
||||
* @return 资源类型索引
|
||||
*/
|
||||
std::type_index inferType(const std::string &path);
|
||||
|
||||
E2D_AUTO_REGISTER_SERVICE(IAssetService, AssetService);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/service_interface.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/event/event_dispatcher.h>
|
||||
#include <extra2d/event/event_queue.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 事件服务接口
|
||||
*/
|
||||
class IEventService : public IService {
|
||||
public:
|
||||
virtual ~IEventService() = default;
|
||||
|
||||
virtual void push(const Event& event) = 0;
|
||||
virtual void push(Event&& event) = 0;
|
||||
virtual bool poll(Event& event) = 0;
|
||||
|
||||
virtual ListenerID on(EventType type, EventDispatcher::EventFn fn) = 0;
|
||||
virtual void off(ListenerID id) = 0;
|
||||
virtual void offAll(EventType type) = 0;
|
||||
virtual void offAll() = 0;
|
||||
|
||||
virtual void dispatch(Event& event) = 0;
|
||||
virtual void process() = 0;
|
||||
|
||||
virtual size_t listenerCount(EventType type) const = 0;
|
||||
virtual size_t totalListeners() const = 0;
|
||||
virtual size_t queueSize() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 事件服务实现
|
||||
*/
|
||||
class EventService : public IEventService {
|
||||
public:
|
||||
EventService();
|
||||
~EventService() override = default;
|
||||
|
||||
ServiceInfo info() const override;
|
||||
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
void update(f32 dt) override;
|
||||
|
||||
void push(const Event& event) override;
|
||||
void push(Event&& event) override;
|
||||
bool poll(Event& event) override;
|
||||
|
||||
ListenerID on(EventType type, EventDispatcher::EventFn fn) override;
|
||||
void off(ListenerID id) override;
|
||||
void offAll(EventType type) override;
|
||||
void offAll() override;
|
||||
|
||||
void dispatch(Event& event) override;
|
||||
void process() override;
|
||||
|
||||
size_t listenerCount(EventType type) const override;
|
||||
size_t totalListeners() const override;
|
||||
size_t queueSize() const override;
|
||||
|
||||
EventQueue& queue() { return queue_; }
|
||||
const EventQueue& queue() const { return queue_; }
|
||||
EventDispatcher& dispatcher() { return dispatcher_; }
|
||||
const EventDispatcher& dispatcher() const { return dispatcher_; }
|
||||
|
||||
private:
|
||||
EventQueue queue_;
|
||||
EventDispatcher dispatcher_;
|
||||
|
||||
// 服务注册元数据
|
||||
E2D_AUTO_REGISTER_SERVICE(IEventService, EventService);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,310 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <extra2d/core/service_interface.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/core/types.h>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 日志颜色结构
|
||||
*/
|
||||
struct LogColor {
|
||||
u8 r, g, b;
|
||||
|
||||
constexpr LogColor() : r(255), g(255), b(255) {}
|
||||
constexpr LogColor(u8 r, u8 g, u8 b) : r(r), g(g), b(b) {}
|
||||
|
||||
static constexpr LogColor White() { return LogColor(255, 255, 255); }
|
||||
static constexpr LogColor Gray() { return LogColor(128, 128, 128); }
|
||||
static constexpr LogColor Red() { return LogColor(255, 85, 85); }
|
||||
static constexpr LogColor Green() { return LogColor(85, 255, 85); }
|
||||
static constexpr LogColor Yellow() { return LogColor(255, 255, 85); }
|
||||
static constexpr LogColor Blue() { return LogColor(85, 85, 255); }
|
||||
static constexpr LogColor Magenta() { return LogColor(255, 85, 255); }
|
||||
static constexpr LogColor Cyan() { return LogColor(85, 255, 255); }
|
||||
static constexpr LogColor Orange() { return LogColor(255, 165, 0); }
|
||||
|
||||
static constexpr LogColor Slate() { return LogColor(100, 116, 139); }
|
||||
static constexpr LogColor SlateLight() { return LogColor(148, 163, 184); }
|
||||
static constexpr LogColor Sky() { return LogColor(14, 165, 233); }
|
||||
static constexpr LogColor SkyLight() { return LogColor(125, 211, 252); }
|
||||
static constexpr LogColor Emerald() { return LogColor(16, 185, 129); }
|
||||
static constexpr LogColor EmeraldLight() { return LogColor(110, 231, 183); }
|
||||
static constexpr LogColor Amber() { return LogColor(245, 158, 11); }
|
||||
static constexpr LogColor AmberLight() { return LogColor(252, 211, 77); }
|
||||
static constexpr LogColor Rose() { return LogColor(244, 63, 94); }
|
||||
static constexpr LogColor RoseLight() { return LogColor(253, 164, 175); }
|
||||
static constexpr LogColor Violet() { return LogColor(139, 92, 246); }
|
||||
static constexpr LogColor VioletLight() { return LogColor(196, 181, 253); }
|
||||
static constexpr LogColor Indigo() { return LogColor(99, 102, 241); }
|
||||
static constexpr LogColor IndigoLight() { return LogColor(165, 180, 252); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 日志级别枚举
|
||||
*/
|
||||
enum class LogLevel {
|
||||
Trace = 0,
|
||||
Debug = 1,
|
||||
Info = 2,
|
||||
Registry = 3,
|
||||
Warn = 4,
|
||||
Error = 5,
|
||||
Fatal = 6,
|
||||
Off = 7
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 日志服务接口
|
||||
*/
|
||||
class ILogger : public IService {
|
||||
public:
|
||||
virtual ~ILogger() = default;
|
||||
|
||||
/**
|
||||
* @brief 设置日志级别
|
||||
*/
|
||||
virtual void level(LogLevel lvl) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取日志级别
|
||||
*/
|
||||
virtual LogLevel level() const = 0;
|
||||
|
||||
/**
|
||||
* @brief 检查日志级别是否启用
|
||||
*/
|
||||
virtual bool enabled(LogLevel lvl) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 记录日志(格式化)
|
||||
*/
|
||||
virtual void log(LogLevel lvl, const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief 记录日志(字符串)
|
||||
*/
|
||||
virtual void log(LogLevel lvl, const std::string &msg) = 0;
|
||||
|
||||
/**
|
||||
* @brief Trace级别日志
|
||||
*/
|
||||
virtual void trace(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Debug级别日志
|
||||
*/
|
||||
virtual void debug(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Info级别日志
|
||||
*/
|
||||
virtual void info(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Registry级别日志(用于模块/服务注册显示)
|
||||
*/
|
||||
virtual void registry(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Warn级别日志
|
||||
*/
|
||||
virtual void warn(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Error级别日志
|
||||
*/
|
||||
virtual void error(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief Fatal级别日志
|
||||
*/
|
||||
virtual void fatal(const char *fmt, ...) = 0;
|
||||
|
||||
/**
|
||||
* @brief 设置日志级别颜色
|
||||
* @param lvl 日志级别
|
||||
* @param c 颜色
|
||||
*/
|
||||
virtual void levelColor(LogLevel lvl, const LogColor &c) = 0;
|
||||
|
||||
/**
|
||||
* @brief 获取日志级别颜色
|
||||
* @param lvl 日志级别
|
||||
* @return 颜色
|
||||
*/
|
||||
virtual LogColor levelColor(LogLevel lvl) const = 0;
|
||||
|
||||
/**
|
||||
* @brief 启用/禁用颜色输出
|
||||
* @param on 是否启用
|
||||
*/
|
||||
virtual void colors(bool on) = 0;
|
||||
|
||||
/**
|
||||
* @brief 是否启用颜色输出
|
||||
*/
|
||||
virtual bool colors() const = 0;
|
||||
|
||||
ServiceInfo info() const override {
|
||||
ServiceInfo i;
|
||||
i.name = "Logger";
|
||||
i.priority = ServicePriority::Core;
|
||||
i.enabled = true;
|
||||
return i;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 控制台日志服务实现
|
||||
*/
|
||||
class ConsoleLogger : public ILogger {
|
||||
public:
|
||||
ConsoleLogger();
|
||||
~ConsoleLogger() override;
|
||||
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
|
||||
void level(LogLevel lvl) override;
|
||||
LogLevel level() const override;
|
||||
bool enabled(LogLevel lvl) const override;
|
||||
|
||||
void log(LogLevel lvl, const char *fmt, ...) override;
|
||||
void log(LogLevel lvl, const std::string &msg) override;
|
||||
|
||||
void trace(const char *fmt, ...) override;
|
||||
void debug(const char *fmt, ...) override;
|
||||
void info(const char *fmt, ...) override;
|
||||
void registry(const char *fmt, ...) override;
|
||||
void warn(const char *fmt, ...) override;
|
||||
void error(const char *fmt, ...) override;
|
||||
void fatal(const char *fmt, ...) override;
|
||||
|
||||
void levelColor(LogLevel lvl, const LogColor &c) override;
|
||||
LogColor levelColor(LogLevel lvl) const override;
|
||||
void colors(bool on) override;
|
||||
bool colors() const override;
|
||||
|
||||
private:
|
||||
void output(LogLevel lvl, const char *msg);
|
||||
const char *levelString(LogLevel lvl);
|
||||
std::string ansiColor(LogLevel lvl);
|
||||
|
||||
LogLevel level_;
|
||||
bool colors_;
|
||||
LogColor levelColors_[7];
|
||||
class Impl;
|
||||
Unique<Impl> impl_;
|
||||
|
||||
// 服务注册元数据
|
||||
E2D_AUTO_REGISTER_SERVICE(ILogger, ConsoleLogger);
|
||||
};
|
||||
|
||||
} // namespace extra2d
|
||||
|
||||
// 格式化辅助函数 - 将参数转换为字符串
|
||||
namespace extra2d {
|
||||
namespace detail {
|
||||
template <typename T> std::string to_string(T &&value) {
|
||||
using Decayed = std::decay_t<T>;
|
||||
if constexpr (std::is_same_v<Decayed, std::string>) {
|
||||
return value;
|
||||
} else if constexpr (std::is_same_v<Decayed, const char *>) {
|
||||
return value ? value : "(null)";
|
||||
} else if constexpr (std::is_arithmetic_v<Decayed>) {
|
||||
if constexpr (std::is_same_v<Decayed, bool>) {
|
||||
return value ? "true" : "false";
|
||||
} else if constexpr (std::is_floating_point_v<Decayed>) {
|
||||
return std::to_string(value);
|
||||
} else {
|
||||
return std::to_string(value);
|
||||
}
|
||||
} else {
|
||||
return "<?>";
|
||||
}
|
||||
}
|
||||
|
||||
inline void format_impl(std::string &result, const char *fmt) { result += fmt; }
|
||||
|
||||
template <typename T, typename... Args>
|
||||
void format_impl(std::string &result, const char *fmt, T &&value,
|
||||
Args &&...args) {
|
||||
const char *p = fmt;
|
||||
while (*p) {
|
||||
if (*p == '{' && *(p + 1) == '}') {
|
||||
result += to_string(std::forward<T>(value));
|
||||
format_impl(result, p + 2, std::forward<Args>(args)...);
|
||||
return;
|
||||
}
|
||||
result += *p++;
|
||||
}
|
||||
result += " ";
|
||||
result += to_string(std::forward<T>(value));
|
||||
format_impl(result, p, std::forward<Args>(args)...);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <typename... Args>
|
||||
std::string format_str(const char *fmt, Args &&...args) {
|
||||
if constexpr (sizeof...(args) == 0) {
|
||||
return std::string(fmt);
|
||||
} else {
|
||||
std::string result;
|
||||
detail::format_impl(result, fmt, std::forward<Args>(args)...);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} // namespace extra2d
|
||||
|
||||
// 便捷宏 - 自动获取日志服务
|
||||
#define E2D_LOG(lvl, ...) \
|
||||
do { \
|
||||
if (auto logService = ::extra2d::ServiceLocator::instance() \
|
||||
.tryGet<::extra2d::ILogger>()) { \
|
||||
if (logService->enabled(lvl)) { \
|
||||
logService->log(lvl, ::extra2d::format_str(__VA_ARGS__)); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define E2D_TRACE(...) E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__)
|
||||
#define E2D_DEBUG(...) E2D_LOG(::extra2d::LogLevel::Debug, __VA_ARGS__)
|
||||
#define E2D_INFO(...) E2D_LOG(::extra2d::LogLevel::Info, __VA_ARGS__)
|
||||
#define E2D_REGISTRY(...) E2D_LOG(::extra2d::LogLevel::Registry, __VA_ARGS__)
|
||||
#define E2D_WARN(...) E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__)
|
||||
#define E2D_ERROR(...) E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__)
|
||||
#define E2D_FATAL(...) E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__)
|
||||
|
||||
// 带颜色参数的日志宏
|
||||
#define E2D_LOG_COLOR(lvl, c, ...) \
|
||||
do { \
|
||||
if (auto logService = ::extra2d::ServiceLocator::instance() \
|
||||
.tryGet<::extra2d::ILogger>()) { \
|
||||
if (logService->enabled(lvl)) { \
|
||||
auto prevColor = logService->levelColor(lvl); \
|
||||
logService->levelColor(lvl, c); \
|
||||
logService->log(lvl, ::extra2d::format_str(__VA_ARGS__)); \
|
||||
logService->levelColor(lvl, prevColor); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define E2D_TRACE_COLOR(c, ...) \
|
||||
E2D_LOG_COLOR(::extra2d::LogLevel::Trace, c, __VA_ARGS__)
|
||||
#define E2D_DEBUG_COLOR(c, ...) \
|
||||
E2D_LOG_COLOR(::extra2d::LogLevel::Debug, c, __VA_ARGS__)
|
||||
#define E2D_INFO_COLOR(c, ...) \
|
||||
E2D_LOG_COLOR(::extra2d::LogLevel::Info, c, __VA_ARGS__)
|
||||
#define E2D_REGISTRY_COLOR(c, ...) \
|
||||
E2D_LOG_COLOR(::extra2d::LogLevel::Registry, c, __VA_ARGS__)
|
||||
#define E2D_WARN_COLOR(c, ...) \
|
||||
E2D_LOG_COLOR(::extra2d::LogLevel::Warn, c, __VA_ARGS__)
|
||||
#define E2D_ERROR_COLOR(c, ...) \
|
||||
E2D_LOG_COLOR(::extra2d::LogLevel::Error, c, __VA_ARGS__)
|
||||
#define E2D_FATAL_COLOR(c, ...) \
|
||||
E2D_LOG_COLOR(::extra2d::LogLevel::Fatal, c, __VA_ARGS__)
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <extra2d/core/service_interface.h>
|
||||
#include <extra2d/core/service_locator.h>
|
||||
#include <extra2d/utils/timer.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
/**
|
||||
* @brief 计时器服务接口
|
||||
*/
|
||||
class ITimerService : public IService {
|
||||
public:
|
||||
virtual ~ITimerService() = default;
|
||||
|
||||
virtual u32 add(f32 delay, Timer::Fn fn) = 0;
|
||||
virtual u32 addRepeat(f32 interval, Timer::Fn fn) = 0;
|
||||
virtual void cancel(u32 timerId) = 0;
|
||||
virtual void pauseTimer(u32 timerId) = 0;
|
||||
virtual void resumeTimer(u32 timerId) = 0;
|
||||
virtual void clear() = 0;
|
||||
virtual size_t count() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief 计时器服务实现
|
||||
*/
|
||||
class TimerService : public ITimerService {
|
||||
public:
|
||||
TimerService();
|
||||
~TimerService() override = default;
|
||||
|
||||
ServiceInfo info() const override;
|
||||
|
||||
bool init() override;
|
||||
void shutdown() override;
|
||||
void update(f32 dt) override;
|
||||
|
||||
u32 add(f32 delay, Timer::Fn fn) override;
|
||||
u32 addRepeat(f32 interval, Timer::Fn fn) override;
|
||||
void cancel(u32 timerId) override;
|
||||
void pauseTimer(u32 timerId) override;
|
||||
void resumeTimer(u32 timerId) override;
|
||||
void clear() override;
|
||||
size_t count() const override;
|
||||
|
||||
TimerManager& mgr() { return mgr_; }
|
||||
const TimerManager& mgr() const { return mgr_; }
|
||||
|
||||
private:
|
||||
TimerManager mgr_;
|
||||
|
||||
// 服务注册元数据
|
||||
E2D_AUTO_REGISTER_SERVICE(ITimerService, TimerService);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,169 @@
|
|||
#include <extra2d/2d/components/Sprite.h>
|
||||
#include <extra2d/core/scene-graph/Node.h>
|
||||
#include <extra2d/2d/renderer/Batcher2d.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Sprite::Sprite() : Component("Sprite") {
|
||||
}
|
||||
|
||||
Sprite::~Sprite() {
|
||||
if (drawInfo_) {
|
||||
delete drawInfo_;
|
||||
drawInfo_ = nullptr;
|
||||
}
|
||||
if (renderEntity_) {
|
||||
delete renderEntity_;
|
||||
renderEntity_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SpriteFrame* Sprite::getSpriteFrame() const {
|
||||
return spriteFrame_;
|
||||
}
|
||||
|
||||
void Sprite::setSpriteFrame(SpriteFrame* frame) {
|
||||
if (spriteFrame_ != frame) {
|
||||
spriteFrame_ = frame;
|
||||
dirty_ = true;
|
||||
|
||||
if (frame && !sizeSetByUser_) {
|
||||
size_ = Vec2{frame->getOriginalSize().x, frame->getOriginalSize().y};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vec4 Sprite::getColor() const {
|
||||
return color_;
|
||||
}
|
||||
|
||||
void Sprite::setColor(const Vec4& color) {
|
||||
color_ = color;
|
||||
dirty_ = true;
|
||||
}
|
||||
|
||||
void Sprite::setColor(float r, float g, float b) {
|
||||
setColor(r, g, b, color_.a);
|
||||
}
|
||||
|
||||
void Sprite::setColor(float r, float g, float b, float a) {
|
||||
color_ = Vec4{r, g, b, a};
|
||||
dirty_ = true;
|
||||
}
|
||||
|
||||
float Sprite::getWidth() const {
|
||||
return size_.x;
|
||||
}
|
||||
|
||||
void Sprite::setWidth(float width) {
|
||||
size_.x = width;
|
||||
sizeSetByUser_ = true;
|
||||
dirty_ = true;
|
||||
}
|
||||
|
||||
float Sprite::getHeight() const {
|
||||
return size_.y;
|
||||
}
|
||||
|
||||
void Sprite::setHeight(float height) {
|
||||
size_.y = height;
|
||||
sizeSetByUser_ = true;
|
||||
dirty_ = true;
|
||||
}
|
||||
|
||||
Vec2 Sprite::getSize() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
void Sprite::setSize(const Vec2& size) {
|
||||
size_ = size;
|
||||
sizeSetByUser_ = true;
|
||||
dirty_ = true;
|
||||
}
|
||||
|
||||
bool Sprite::isDirty() const {
|
||||
return dirty_;
|
||||
}
|
||||
|
||||
void Sprite::setDirty(bool dirty) {
|
||||
dirty_ = dirty;
|
||||
}
|
||||
|
||||
void Sprite::markForUpdate() {
|
||||
dirty_ = true;
|
||||
}
|
||||
|
||||
void Sprite::onAdd() {
|
||||
renderEntity_ = new RenderEntity();
|
||||
renderEntity_->setNode(getNode());
|
||||
|
||||
drawInfo_ = new RenderDrawInfo();
|
||||
drawInfo_->setDrawInfoType(RenderDrawInfoType::Component);
|
||||
renderEntity_->addDynamicRenderDrawInfo(drawInfo_);
|
||||
|
||||
dirty_ = true;
|
||||
}
|
||||
|
||||
void Sprite::onRemove() {
|
||||
if (drawInfo_) {
|
||||
delete drawInfo_;
|
||||
drawInfo_ = nullptr;
|
||||
}
|
||||
if (renderEntity_) {
|
||||
delete renderEntity_;
|
||||
renderEntity_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite::update(f32 dt) {
|
||||
if (dirty_) {
|
||||
updateGeometry();
|
||||
dirty_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite::render() {
|
||||
}
|
||||
|
||||
void Sprite::updateGeometry() {
|
||||
if (!spriteFrame_ || !drawInfo_) return;
|
||||
|
||||
updateRenderEntity();
|
||||
updateVertexData();
|
||||
}
|
||||
|
||||
void Sprite::updateRenderEntity() {
|
||||
if (!renderEntity_) return;
|
||||
|
||||
renderEntity_->setColor(
|
||||
static_cast<u8>(color_.r * 255),
|
||||
static_cast<u8>(color_.g * 255),
|
||||
static_cast<u8>(color_.b * 255),
|
||||
static_cast<u8>(color_.a * 255)
|
||||
);
|
||||
|
||||
if (getNode()) {
|
||||
renderEntity_->setPriority(static_cast<u32>(getNode()->getLayer()));
|
||||
}
|
||||
}
|
||||
|
||||
void Sprite::updateVertexData() {
|
||||
if (!spriteFrame_ || !drawInfo_) return;
|
||||
|
||||
auto* texture = spriteFrame_->getTexture();
|
||||
if (!texture) return;
|
||||
|
||||
drawInfo_->setTexture(texture->getGFXTexture());
|
||||
|
||||
float width = size_.x;
|
||||
float height = size_.y;
|
||||
|
||||
float halfW = width * 0.5f;
|
||||
float halfH = height * 0.5f;
|
||||
|
||||
drawInfo_->setVbCount(4);
|
||||
drawInfo_->setIbCount(6);
|
||||
drawInfo_->setVertDirty(true);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
#include <extra2d/2d/components/SpriteFrame.h>
|
||||
#include <extra2d/2d/components/Sprite.h>
|
||||
#include <extra2d/core/scene-graph/Node.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
SpriteFrame::SpriteFrame() : Asset("SpriteFrame") {
|
||||
}
|
||||
|
||||
SpriteFrame::~SpriteFrame() {
|
||||
}
|
||||
|
||||
Texture2D* SpriteFrame::getTexture() const {
|
||||
return texture_;
|
||||
}
|
||||
|
||||
void SpriteFrame::setTexture(Texture2D* texture) {
|
||||
texture_ = texture;
|
||||
if (texture && rect_.width == 0 && rect_.height == 0) {
|
||||
rect_.x = 0;
|
||||
rect_.y = 0;
|
||||
rect_.width = static_cast<float>(texture->getWidth());
|
||||
rect_.height = static_cast<float>(texture->getHeight());
|
||||
calculateUV();
|
||||
}
|
||||
}
|
||||
|
||||
Rect SpriteFrame::getRect() const {
|
||||
return rect_;
|
||||
}
|
||||
|
||||
void SpriteFrame::setRect(const Rect& rect) {
|
||||
rect_ = rect;
|
||||
calculateUV();
|
||||
}
|
||||
|
||||
Rect SpriteFrame::getUnrotatedRect() const {
|
||||
return unrotatedRect_;
|
||||
}
|
||||
|
||||
void SpriteFrame::setUnrotatedRect(const Rect& rect) {
|
||||
unrotatedRect_ = rect;
|
||||
}
|
||||
|
||||
bool SpriteFrame::isRotated() const {
|
||||
return rotated_;
|
||||
}
|
||||
|
||||
void SpriteFrame::setRotated(bool rotated) {
|
||||
rotated_ = rotated;
|
||||
calculateUV();
|
||||
}
|
||||
|
||||
Vec2 SpriteFrame::getOffset() const {
|
||||
return offset_;
|
||||
}
|
||||
|
||||
void SpriteFrame::setOffset(const Vec2& offset) {
|
||||
offset_ = offset;
|
||||
}
|
||||
|
||||
Vec2 SpriteFrame::getOriginalSize() const {
|
||||
return originalSize_;
|
||||
}
|
||||
|
||||
void SpriteFrame::setOriginalSize(const Vec2& size) {
|
||||
originalSize_ = size;
|
||||
}
|
||||
|
||||
std::array<Vec2, 4> SpriteFrame::getUVs() const {
|
||||
return uvs_;
|
||||
}
|
||||
|
||||
void SpriteFrame::calculateUV() {
|
||||
if (!texture_ || rect_.width == 0 || rect_.height == 0) {
|
||||
uvs_[0] = Vec2{0, 0};
|
||||
uvs_[1] = Vec2{1, 0};
|
||||
uvs_[2] = Vec2{1, 1};
|
||||
uvs_[3] = Vec2{0, 1};
|
||||
return;
|
||||
}
|
||||
|
||||
float texWidth = static_cast<float>(texture_->getWidth());
|
||||
float texHeight = static_cast<float>(texture_->getHeight());
|
||||
|
||||
float left = rect_.x / texWidth;
|
||||
float right = (rect_.x + rect_.width) / texWidth;
|
||||
float top = rect_.y / texHeight;
|
||||
float bottom = (rect_.y + rect_.height) / texHeight;
|
||||
|
||||
if (rotated_) {
|
||||
uvs_[0] = Vec2{left, top};
|
||||
uvs_[1] = Vec2{right, top};
|
||||
uvs_[2] = Vec2{right, bottom};
|
||||
uvs_[3] = Vec2{left, bottom};
|
||||
} else {
|
||||
uvs_[0] = Vec2{left, bottom};
|
||||
uvs_[1] = Vec2{right, bottom};
|
||||
uvs_[2] = Vec2{right, top};
|
||||
uvs_[3] = Vec2{left, top};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,228 @@
|
|||
#include <extra2d/2d/renderer/Batcher2d.h>
|
||||
#include <extra2d/core/scene-graph/Node.h>
|
||||
#include <extra2d/gfx/GFXDevice.h>
|
||||
#include <extra2d/gfx/GFXShader.h>
|
||||
#include <extra2d/gfx/GFXPipeline.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Batcher2d* Batcher2d::instance_ = nullptr;
|
||||
|
||||
Batcher2d::Batcher2d() {
|
||||
initDefaultAttributes();
|
||||
}
|
||||
|
||||
Batcher2d::~Batcher2d() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
Batcher2d* Batcher2d::getInstance() {
|
||||
if (!instance_) {
|
||||
instance_ = new Batcher2d();
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
bool Batcher2d::initialize(gfx::Device* device) {
|
||||
device_ = device;
|
||||
return device_ != nullptr;
|
||||
}
|
||||
|
||||
void Batcher2d::destroy() {
|
||||
reset();
|
||||
|
||||
for (auto* batch : batches_) {
|
||||
delete batch;
|
||||
}
|
||||
batches_.clear();
|
||||
|
||||
for (auto& pair : meshBuffersMap_) {
|
||||
for (auto* buffer : pair.second) {
|
||||
delete buffer;
|
||||
}
|
||||
}
|
||||
meshBuffersMap_.clear();
|
||||
|
||||
rootNodes_.clear();
|
||||
device_ = nullptr;
|
||||
}
|
||||
|
||||
void Batcher2d::update() {
|
||||
fillBuffersAndMergeBatches();
|
||||
uploadBuffers();
|
||||
}
|
||||
|
||||
void Batcher2d::uploadBuffers() {
|
||||
for (auto& pair : meshBuffersMap_) {
|
||||
for (auto* buffer : pair.second) {
|
||||
if (buffer && buffer->isDirty()) {
|
||||
buffer->uploadBuffers();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Batcher2d::reset() {
|
||||
for (auto* batch : batches_) {
|
||||
delete batch;
|
||||
}
|
||||
batches_.clear();
|
||||
|
||||
for (auto& pair : meshBuffersMap_) {
|
||||
for (auto* buffer : pair.second) {
|
||||
if (buffer) {
|
||||
buffer->reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
currEntity_ = nullptr;
|
||||
currDrawInfo_ = nullptr;
|
||||
currMeshBuffer_ = nullptr;
|
||||
currStencilStage_ = 0;
|
||||
currStencilValue_ = 0;
|
||||
}
|
||||
|
||||
void Batcher2d::fillBuffersAndMergeBatches() {
|
||||
reset();
|
||||
|
||||
for (auto* rootNode : rootNodes_) {
|
||||
if (rootNode && rootNode->isActiveInHierarchy()) {
|
||||
walk(rootNode, 1.0f, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Batcher2d::walk(Node* node, float parentOpacity, bool parentColorDirty) {
|
||||
if (!node || !node->isActive()) return;
|
||||
|
||||
float opacity = parentOpacity;
|
||||
bool colorDirty = parentColorDirty;
|
||||
|
||||
auto* renderEntity = static_cast<RenderEntity*>(node->getUserData());
|
||||
if (renderEntity) {
|
||||
currEntity_ = renderEntity;
|
||||
|
||||
u32 drawInfoCount = renderEntity->getRenderDrawInfoCount();
|
||||
for (u32 i = 0; i < drawInfoCount; ++i) {
|
||||
auto* drawInfo = renderEntity->getRenderDrawInfoAt(i);
|
||||
if (drawInfo) {
|
||||
handleDrawInfo(renderEntity, drawInfo, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto& children = node->getChildren();
|
||||
for (auto& child : children) {
|
||||
walk(child.get(), opacity, colorDirty);
|
||||
}
|
||||
}
|
||||
|
||||
void Batcher2d::handleDrawInfo(RenderEntity* entity, RenderDrawInfo* drawInfo, Node* node) {
|
||||
currDrawInfo_ = drawInfo;
|
||||
|
||||
auto type = drawInfo->getDrawInfoType();
|
||||
switch (type) {
|
||||
case RenderDrawInfoType::Component:
|
||||
case RenderDrawInfoType::Model:
|
||||
generateBatch(entity, drawInfo);
|
||||
break;
|
||||
case RenderDrawInfoType::Middleware:
|
||||
break;
|
||||
case RenderDrawInfoType::SubNode:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Batcher2d::generateBatch(RenderEntity* entity, RenderDrawInfo* drawInfo) {
|
||||
if (!drawInfo) return;
|
||||
|
||||
auto* meshBuffer = drawInfo->getMeshBuffer();
|
||||
if (!meshBuffer) {
|
||||
u16 accId = drawInfo->getAccId();
|
||||
u16 bufferId = drawInfo->getBufferId();
|
||||
meshBuffer = getMeshBuffer(accId, bufferId);
|
||||
drawInfo->setMeshBuffer(meshBuffer);
|
||||
}
|
||||
|
||||
if (!meshBuffer) return;
|
||||
|
||||
currMeshBuffer_ = meshBuffer;
|
||||
|
||||
DrawBatch2D* batch = new DrawBatch2D();
|
||||
batch->inputAssembler = meshBuffer->getInputAssembler();
|
||||
batch->vertexOffset = drawInfo->getVertexOffset();
|
||||
batch->indexOffset = drawInfo->getIndexOffset();
|
||||
batch->vbCount = drawInfo->getVbCount();
|
||||
batch->ibCount = drawInfo->getIbCount();
|
||||
batch->texture = drawInfo->getTexture();
|
||||
batch->priority = entity->getPriority();
|
||||
batch->stencilStage = currStencilStage_;
|
||||
batch->stencilValue = currStencilValue_;
|
||||
|
||||
auto* material = drawInfo->getMaterial();
|
||||
if (material) {
|
||||
}
|
||||
|
||||
batches_.push_back(batch);
|
||||
}
|
||||
|
||||
void Batcher2d::renderBatches() {
|
||||
if (!device_) return;
|
||||
|
||||
device_->beginRenderPass();
|
||||
|
||||
for (auto* batch : batches_) {
|
||||
if (!batch || !batch->inputAssembler) continue;
|
||||
|
||||
updateBatch(batch);
|
||||
|
||||
device_->bindInputAssembler(batch->inputAssembler);
|
||||
device_->drawIndexed(batch->indexOffset, batch->ibCount, 0);
|
||||
}
|
||||
|
||||
device_->endRenderPass();
|
||||
}
|
||||
|
||||
void Batcher2d::updateBatch(DrawBatch2D* batch) {
|
||||
(void)batch;
|
||||
}
|
||||
|
||||
UIMeshBuffer* Batcher2d::getMeshBuffer(u16 accId, u16 bufferId) {
|
||||
auto it = meshBuffersMap_.find(accId);
|
||||
if (it == meshBuffersMap_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const auto& buffers = it->second;
|
||||
if (bufferId >= buffers.size()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return buffers[bufferId];
|
||||
}
|
||||
|
||||
void Batcher2d::syncMeshBuffers(u16 accId, std::vector<UIMeshBuffer*>&& buffers) {
|
||||
meshBuffersMap_[accId] = std::move(buffers);
|
||||
}
|
||||
|
||||
void Batcher2d::syncRootNodes(std::vector<Node*>&& rootNodes) {
|
||||
rootNodes_ = std::move(rootNodes);
|
||||
}
|
||||
|
||||
gfx::Device* Batcher2d::getDevice() {
|
||||
return device_;
|
||||
}
|
||||
|
||||
const std::vector<gfx::Attribute>& Batcher2d::getDefaultAttributes() const {
|
||||
return defaultAttributes_;
|
||||
}
|
||||
|
||||
void Batcher2d::initDefaultAttributes() {
|
||||
defaultAttributes_.push_back({gfx::ATTR_NAME_POSITION, gfx::Format::RGB32F});
|
||||
defaultAttributes_.push_back({gfx::ATTR_NAME_TEX_COORD, gfx::Format::RG32F});
|
||||
defaultAttributes_.push_back({gfx::ATTR_NAME_COLOR, gfx::Format::RGBA32F});
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
#include <extra2d/2d/renderer/RenderDrawInfo.h>
|
||||
#include <extra2d/2d/renderer/UIMeshBuffer.h>
|
||||
#include <extra2d/gfx/GFXDevice.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
RenderDrawInfo::RenderDrawInfo() {
|
||||
}
|
||||
|
||||
RenderDrawInfo::~RenderDrawInfo() {
|
||||
if (inputAssembler_) {
|
||||
inputAssembler_->release();
|
||||
inputAssembler_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
RenderDrawInfoType RenderDrawInfo::getDrawInfoType() const {
|
||||
return attrs_.type;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setDrawInfoType(RenderDrawInfoType type) {
|
||||
attrs_.type = type;
|
||||
}
|
||||
|
||||
u16 RenderDrawInfo::getAccId() const {
|
||||
return attrs_.accId;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setAccId(u16 accId) {
|
||||
attrs_.accId = accId;
|
||||
}
|
||||
|
||||
u16 RenderDrawInfo::getBufferId() const {
|
||||
return attrs_.bufferId;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setBufferId(u16 bufferId) {
|
||||
attrs_.bufferId = bufferId;
|
||||
}
|
||||
|
||||
u32 RenderDrawInfo::getVertexOffset() const {
|
||||
return attrs_.vertexOffset;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setVertexOffset(u32 offset) {
|
||||
attrs_.vertexOffset = offset;
|
||||
}
|
||||
|
||||
u32 RenderDrawInfo::getIndexOffset() const {
|
||||
return attrs_.indexOffset;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setIndexOffset(u32 offset) {
|
||||
attrs_.indexOffset = offset;
|
||||
}
|
||||
|
||||
u32 RenderDrawInfo::getVbCount() const {
|
||||
return attrs_.vbCount;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setVbCount(u32 count) {
|
||||
attrs_.vbCount = count;
|
||||
}
|
||||
|
||||
u32 RenderDrawInfo::getIbCount() const {
|
||||
return attrs_.ibCount;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setIbCount(u32 count) {
|
||||
attrs_.ibCount = count;
|
||||
}
|
||||
|
||||
Material* RenderDrawInfo::getMaterial() const {
|
||||
return material_;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setMaterial(Material* material) {
|
||||
material_ = material;
|
||||
}
|
||||
|
||||
UIMeshBuffer* RenderDrawInfo::getMeshBuffer() const {
|
||||
return meshBuffer_;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setMeshBuffer(UIMeshBuffer* buffer) {
|
||||
meshBuffer_ = buffer;
|
||||
}
|
||||
|
||||
gfx::Texture* RenderDrawInfo::getTexture() const {
|
||||
return texture_;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setTexture(gfx::Texture* texture) {
|
||||
texture_ = texture;
|
||||
}
|
||||
|
||||
float* RenderDrawInfo::getVDataBuffer() const {
|
||||
return vData_;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setVDataBuffer(float* data) {
|
||||
vData_ = data;
|
||||
}
|
||||
|
||||
u16* RenderDrawInfo::getIDataBuffer() const {
|
||||
return iData_;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setIDataBuffer(u16* data) {
|
||||
iData_ = data;
|
||||
}
|
||||
|
||||
gfx::InputAssembler* RenderDrawInfo::requestIA(gfx::Device* device) {
|
||||
if (!inputAssembler_ && meshBuffer_) {
|
||||
inputAssembler_ = meshBuffer_->requireFreeIA(device);
|
||||
}
|
||||
return inputAssembler_;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::uploadBuffers() {
|
||||
if (meshBuffer_) {
|
||||
meshBuffer_->uploadBuffers();
|
||||
}
|
||||
}
|
||||
|
||||
bool RenderDrawInfo::isVertDirty() const {
|
||||
return attrs_.vertDirty;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setVertDirty(bool dirty) {
|
||||
attrs_.vertDirty = dirty;
|
||||
}
|
||||
|
||||
u64 RenderDrawInfo::getDataHash() const {
|
||||
return attrs_.dataHash;
|
||||
}
|
||||
|
||||
void RenderDrawInfo::setDataHash(u64 hash) {
|
||||
attrs_.dataHash = hash;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
#include <extra2d/2d/renderer/RenderEntity.h>
|
||||
#include <extra2d/2d/renderer/RenderDrawInfo.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
RenderEntity::RenderEntity() {
|
||||
attr_.enabledIndex = 0;
|
||||
attr_.useLocal = 1;
|
||||
attr_.fillColorType = FillColorType::Color;
|
||||
}
|
||||
|
||||
RenderEntity::~RenderEntity() {
|
||||
clearDynamicRenderDrawInfos();
|
||||
clearStaticRenderDrawInfos();
|
||||
}
|
||||
|
||||
void RenderEntity::addDynamicRenderDrawInfo(RenderDrawInfo* drawInfo) {
|
||||
if (entityType_ == RenderEntityType::Static) {
|
||||
clearStaticRenderDrawInfos();
|
||||
entityType_ = RenderEntityType::Dynamic;
|
||||
}
|
||||
dynamicDrawInfos_.push_back(drawInfo);
|
||||
drawInfoCount_ = static_cast<u32>(dynamicDrawInfos_.size());
|
||||
}
|
||||
|
||||
void RenderEntity::setDynamicRenderDrawInfo(RenderDrawInfo* drawInfo, u32 index) {
|
||||
if (entityType_ == RenderEntityType::Static) {
|
||||
clearStaticRenderDrawInfos();
|
||||
entityType_ = RenderEntityType::Dynamic;
|
||||
}
|
||||
if (index >= dynamicDrawInfos_.size()) {
|
||||
dynamicDrawInfos_.resize(index + 1, nullptr);
|
||||
}
|
||||
dynamicDrawInfos_[index] = drawInfo;
|
||||
drawInfoCount_ = static_cast<u32>(dynamicDrawInfos_.size());
|
||||
}
|
||||
|
||||
void RenderEntity::removeDynamicRenderDrawInfo() {
|
||||
if (entityType_ == RenderEntityType::Dynamic && !dynamicDrawInfos_.empty()) {
|
||||
dynamicDrawInfos_.pop_back();
|
||||
drawInfoCount_ = static_cast<u32>(dynamicDrawInfos_.size());
|
||||
}
|
||||
}
|
||||
|
||||
void RenderEntity::clearDynamicRenderDrawInfos() {
|
||||
for (auto* info : dynamicDrawInfos_) {
|
||||
if (info) {
|
||||
delete info;
|
||||
}
|
||||
}
|
||||
dynamicDrawInfos_.clear();
|
||||
drawInfoCount_ = 0;
|
||||
}
|
||||
|
||||
void RenderEntity::clearStaticRenderDrawInfos() {
|
||||
drawInfoCount_ = 0;
|
||||
}
|
||||
|
||||
RenderDrawInfo* RenderEntity::getRenderDrawInfoAt(u32 index) {
|
||||
if (index >= drawInfoCount_) {
|
||||
return nullptr;
|
||||
}
|
||||
if (entityType_ == RenderEntityType::Dynamic) {
|
||||
return dynamicDrawInfos_[index];
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
u32 RenderEntity::getRenderDrawInfoCount() const {
|
||||
return drawInfoCount_;
|
||||
}
|
||||
|
||||
Node* RenderEntity::getNode() const {
|
||||
return node_;
|
||||
}
|
||||
|
||||
void RenderEntity::setNode(Node* node) {
|
||||
node_ = node;
|
||||
}
|
||||
|
||||
RenderEntityType RenderEntity::getRenderEntityType() const {
|
||||
return entityType_;
|
||||
}
|
||||
|
||||
void RenderEntity::setRenderEntityType(RenderEntityType type) {
|
||||
entityType_ = type;
|
||||
}
|
||||
|
||||
u32 RenderEntity::getPriority() const {
|
||||
return attr_.priority;
|
||||
}
|
||||
|
||||
void RenderEntity::setPriority(u32 priority) {
|
||||
attr_.priority = priority;
|
||||
}
|
||||
|
||||
MaskMode RenderEntity::getMaskMode() const {
|
||||
return static_cast<MaskMode>(attr_.maskMode);
|
||||
}
|
||||
|
||||
void RenderEntity::setMaskMode(MaskMode mode) {
|
||||
attr_.maskMode = static_cast<u8>(mode);
|
||||
}
|
||||
|
||||
bool RenderEntity::isMask() const {
|
||||
return attr_.maskMode == static_cast<u8>(MaskMode::Mask) ||
|
||||
attr_.maskMode == static_cast<u8>(MaskMode::MaskInverted);
|
||||
}
|
||||
|
||||
bool RenderEntity::isSubMask() const {
|
||||
return attr_.maskMode == static_cast<u8>(MaskMode::MaskNode) ||
|
||||
attr_.maskMode == static_cast<u8>(MaskMode::MaskNodeInverted);
|
||||
}
|
||||
|
||||
bool RenderEntity::isMaskInverted() const {
|
||||
return attr_.maskMode == static_cast<u8>(MaskMode::MaskInverted) ||
|
||||
attr_.maskMode == static_cast<u8>(MaskMode::MaskNodeInverted);
|
||||
}
|
||||
|
||||
FillColorType RenderEntity::getFillColorType() const {
|
||||
return attr_.fillColorType;
|
||||
}
|
||||
|
||||
void RenderEntity::setFillColorType(FillColorType type) {
|
||||
attr_.fillColorType = type;
|
||||
}
|
||||
|
||||
Vec4 RenderEntity::getColor() const {
|
||||
return Vec4{
|
||||
static_cast<float>(attr_.colorR) / 255.0f,
|
||||
static_cast<float>(attr_.colorG) / 255.0f,
|
||||
static_cast<float>(attr_.colorB) / 255.0f,
|
||||
static_cast<float>(attr_.colorA) / 255.0f
|
||||
};
|
||||
}
|
||||
|
||||
void RenderEntity::setColor(u8 r, u8 g, u8 b, u8 a) {
|
||||
attr_.colorR = r;
|
||||
attr_.colorG = g;
|
||||
attr_.colorB = b;
|
||||
attr_.colorA = a;
|
||||
}
|
||||
|
||||
u32 RenderEntity::getStencilStage() const {
|
||||
return stencilStage_;
|
||||
}
|
||||
|
||||
void RenderEntity::setStencilStage(u32 stage) {
|
||||
stencilStage_ = stage;
|
||||
}
|
||||
|
||||
bool RenderEntity::useLocal() const {
|
||||
return attr_.useLocal != 0;
|
||||
}
|
||||
|
||||
void RenderEntity::setUseLocal(bool useLocal) {
|
||||
attr_.useLocal = useLocal ? 1 : 0;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
#include <extra2d/2d/renderer/StencilManager.h>
|
||||
#include <extra2d/2d/renderer/RenderEntity.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
StencilManager* StencilManager::instance_ = nullptr;
|
||||
|
||||
StencilManager* StencilManager::getInstance() {
|
||||
if (!instance_) {
|
||||
instance_ = new StencilManager();
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
StencilManager::~StencilManager() {
|
||||
for (auto& pair : cacheStateMap_) {
|
||||
delete pair.second;
|
||||
}
|
||||
cacheStateMap_.clear();
|
||||
|
||||
if (instance_ == this) {
|
||||
instance_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
gfx::DepthStencilState* StencilManager::getDepthStencilState(StencilStage stage) {
|
||||
u32 hash = getStencilHash(stage);
|
||||
|
||||
auto it = cacheStateMap_.find(hash);
|
||||
if (it != cacheStateMap_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
gfx::DepthStencilState* state = new gfx::DepthStencilState();
|
||||
|
||||
switch (stage) {
|
||||
case StencilStage::Disabled:
|
||||
state->depthTest = false;
|
||||
state->depthWrite = false;
|
||||
state->stencilTest = false;
|
||||
break;
|
||||
|
||||
case StencilStage::Clear:
|
||||
state->depthTest = false;
|
||||
state->depthWrite = false;
|
||||
state->stencilTest = true;
|
||||
state->stencilFuncFront = gfx::ComparisonFunc::Always;
|
||||
state->stencilFuncBack = gfx::ComparisonFunc::Always;
|
||||
state->stencilFailOpFront = gfx::StencilOp::Zero;
|
||||
state->stencilFailOpBack = gfx::StencilOp::Zero;
|
||||
state->stencilZFailOpFront = gfx::StencilOp::Zero;
|
||||
state->stencilZFailOpBack = gfx::StencilOp::Zero;
|
||||
state->stencilPassOpFront = gfx::StencilOp::Zero;
|
||||
state->stencilPassOpBack = gfx::StencilOp::Zero;
|
||||
state->stencilReadMask = 0xFF;
|
||||
state->stencilWriteMask = getWriteMask();
|
||||
state->stencilRef = 0;
|
||||
break;
|
||||
|
||||
case StencilStage::EnterLevel:
|
||||
state->depthTest = false;
|
||||
state->depthWrite = false;
|
||||
state->stencilTest = true;
|
||||
state->stencilFuncFront = gfx::ComparisonFunc::Equal;
|
||||
state->stencilFuncBack = gfx::ComparisonFunc::Equal;
|
||||
state->stencilFailOpFront = gfx::StencilOp::Keep;
|
||||
state->stencilFailOpBack = gfx::StencilOp::Keep;
|
||||
state->stencilZFailOpFront = gfx::StencilOp::Keep;
|
||||
state->stencilZFailOpBack = gfx::StencilOp::Keep;
|
||||
state->stencilPassOpFront = gfx::StencilOp::Incr;
|
||||
state->stencilPassOpBack = gfx::StencilOp::Incr;
|
||||
state->stencilReadMask = getWriteMask();
|
||||
state->stencilWriteMask = getWriteMask();
|
||||
state->stencilRef = getStencilRef();
|
||||
break;
|
||||
|
||||
case StencilStage::Enabled:
|
||||
state->depthTest = false;
|
||||
state->depthWrite = false;
|
||||
state->stencilTest = true;
|
||||
state->stencilFuncFront = gfx::ComparisonFunc::Equal;
|
||||
state->stencilFuncBack = gfx::ComparisonFunc::Equal;
|
||||
state->stencilFailOpFront = gfx::StencilOp::Keep;
|
||||
state->stencilFailOpBack = gfx::StencilOp::Keep;
|
||||
state->stencilZFailOpFront = gfx::StencilOp::Keep;
|
||||
state->stencilZFailOpBack = gfx::StencilOp::Keep;
|
||||
state->stencilPassOpFront = gfx::StencilOp::Keep;
|
||||
state->stencilPassOpBack = gfx::StencilOp::Keep;
|
||||
state->stencilReadMask = getWriteMask();
|
||||
state->stencilWriteMask = 0;
|
||||
state->stencilRef = getStencilRef();
|
||||
break;
|
||||
|
||||
case StencilStage::ExitLevel:
|
||||
state->depthTest = false;
|
||||
state->depthWrite = false;
|
||||
state->stencilTest = true;
|
||||
state->stencilFuncFront = gfx::ComparisonFunc::Equal;
|
||||
state->stencilFuncBack = gfx::ComparisonFunc::Equal;
|
||||
state->stencilFailOpFront = gfx::StencilOp::Keep;
|
||||
state->stencilFailOpBack = gfx::StencilOp::Keep;
|
||||
state->stencilZFailOpFront = gfx::StencilOp::Keep;
|
||||
state->stencilZFailOpBack = gfx::StencilOp::Keep;
|
||||
state->stencilPassOpFront = gfx::StencilOp::Decr;
|
||||
state->stencilPassOpBack = gfx::StencilOp::Decr;
|
||||
state->stencilReadMask = getExitWriteMask();
|
||||
state->stencilWriteMask = getExitWriteMask();
|
||||
state->stencilRef = getStencilRef();
|
||||
break;
|
||||
|
||||
case StencilStage::ClearInverted:
|
||||
state->depthTest = false;
|
||||
state->depthWrite = false;
|
||||
state->stencilTest = true;
|
||||
state->stencilFuncFront = gfx::ComparisonFunc::NotEqual;
|
||||
state->stencilFuncBack = gfx::ComparisonFunc::NotEqual;
|
||||
state->stencilFailOpFront = gfx::StencilOp::Zero;
|
||||
state->stencilFailOpBack = gfx::StencilOp::Zero;
|
||||
state->stencilZFailOpFront = gfx::StencilOp::Zero;
|
||||
state->stencilZFailOpBack = gfx::StencilOp::Zero;
|
||||
state->stencilPassOpFront = gfx::StencilOp::Zero;
|
||||
state->stencilPassOpBack = gfx::StencilOp::Zero;
|
||||
state->stencilReadMask = 0xFF;
|
||||
state->stencilWriteMask = getWriteMask();
|
||||
state->stencilRef = 0;
|
||||
break;
|
||||
|
||||
case StencilStage::EnterLevelInverted:
|
||||
state->depthTest = false;
|
||||
state->depthWrite = false;
|
||||
state->stencilTest = true;
|
||||
state->stencilFuncFront = gfx::ComparisonFunc::Equal;
|
||||
state->stencilFuncBack = gfx::ComparisonFunc::Equal;
|
||||
state->stencilFailOpFront = gfx::StencilOp::Keep;
|
||||
state->stencilFailOpBack = gfx::StencilOp::Keep;
|
||||
state->stencilZFailOpFront = gfx::StencilOp::Keep;
|
||||
state->stencilZFailOpBack = gfx::StencilOp::Keep;
|
||||
state->stencilPassOpFront = gfx::StencilOp::Decr;
|
||||
state->stencilPassOpBack = gfx::StencilOp::Decr;
|
||||
state->stencilReadMask = getWriteMask();
|
||||
state->stencilWriteMask = getWriteMask();
|
||||
state->stencilRef = getStencilRef();
|
||||
break;
|
||||
}
|
||||
|
||||
cacheStateMap_[hash] = state;
|
||||
return state;
|
||||
}
|
||||
|
||||
void StencilManager::setDepthStencilStateFromStage(StencilStage stage) {
|
||||
stage_ = stage;
|
||||
}
|
||||
|
||||
StencilStage StencilManager::clear(RenderEntity* entity) {
|
||||
if (maskStackSize_ == 0) {
|
||||
return StencilStage::Disabled;
|
||||
}
|
||||
|
||||
stage_ = StencilStage::Clear;
|
||||
return stage_;
|
||||
}
|
||||
|
||||
void StencilManager::enterLevel(RenderEntity* entity) {
|
||||
stage_ = StencilStage::EnterLevel;
|
||||
}
|
||||
|
||||
void StencilManager::setStencilStage(u32 stageIndex) {
|
||||
stage_ = static_cast<StencilStage>(stageIndex);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
#include <extra2d/2d/renderer/UIMeshBuffer.h>
|
||||
#include <extra2d/gfx/GFXDevice.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
UIMeshBuffer::UIMeshBuffer() {
|
||||
}
|
||||
|
||||
UIMeshBuffer::~UIMeshBuffer() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
bool UIMeshBuffer::initialize(gfx::Device* device,
|
||||
const std::vector<gfx::Attribute>& attrs,
|
||||
u32 vertexCapacity,
|
||||
u32 indexCapacity) {
|
||||
device_ = device;
|
||||
attributes_ = attrs;
|
||||
vertexCapacity_ = vertexCapacity;
|
||||
indexCapacity_ = indexCapacity;
|
||||
|
||||
vertexFormatBytes_ = 0;
|
||||
for (const auto& attr : attrs) {
|
||||
switch (attr.format) {
|
||||
case gfx::Format::R32F:
|
||||
vertexFormatBytes_ += 4;
|
||||
break;
|
||||
case gfx::Format::RG32F:
|
||||
vertexFormatBytes_ += 8;
|
||||
break;
|
||||
case gfx::Format::RGB32F:
|
||||
vertexFormatBytes_ += 12;
|
||||
break;
|
||||
case gfx::Format::RGBA32F:
|
||||
vertexFormatBytes_ += 16;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
vData_ = new float[vertexCapacity_ * vertexFormatBytes_ / sizeof(float)];
|
||||
iData_ = new u16[indexCapacity_];
|
||||
ownsData_ = true;
|
||||
|
||||
memset(vData_, 0, vertexCapacity_ * vertexFormatBytes_);
|
||||
memset(iData_, 0, indexCapacity_ * sizeof(u16));
|
||||
|
||||
return createBuffers();
|
||||
}
|
||||
|
||||
void UIMeshBuffer::reset() {
|
||||
byteOffset_ = 0;
|
||||
vertexOffset_ = 0;
|
||||
indexOffset_ = 0;
|
||||
dirty_ = false;
|
||||
|
||||
if (vData_) {
|
||||
memset(vData_, 0, vertexCapacity_ * vertexFormatBytes_);
|
||||
}
|
||||
if (iData_) {
|
||||
memset(iData_, 0, indexCapacity_ * sizeof(u16));
|
||||
}
|
||||
}
|
||||
|
||||
void UIMeshBuffer::destroy() {
|
||||
if (inputAssembler_) {
|
||||
inputAssembler_->release();
|
||||
inputAssembler_ = nullptr;
|
||||
}
|
||||
|
||||
if (vertexBuffer_) {
|
||||
vertexBuffer_->release();
|
||||
vertexBuffer_ = nullptr;
|
||||
}
|
||||
|
||||
if (indexBuffer_) {
|
||||
indexBuffer_->release();
|
||||
indexBuffer_ = nullptr;
|
||||
}
|
||||
|
||||
if (ownsData_) {
|
||||
delete[] vData_;
|
||||
delete[] iData_;
|
||||
}
|
||||
vData_ = nullptr;
|
||||
iData_ = nullptr;
|
||||
ownsData_ = false;
|
||||
}
|
||||
|
||||
float* UIMeshBuffer::getVData() const {
|
||||
return vData_;
|
||||
}
|
||||
|
||||
void UIMeshBuffer::setVData(float* vData) {
|
||||
if (ownsData_ && vData_) {
|
||||
delete[] vData_;
|
||||
}
|
||||
vData_ = vData;
|
||||
ownsData_ = false;
|
||||
}
|
||||
|
||||
u16* UIMeshBuffer::getIData() const {
|
||||
return iData_;
|
||||
}
|
||||
|
||||
void UIMeshBuffer::setIData(u16* iData) {
|
||||
if (ownsData_ && iData_) {
|
||||
delete[] iData_;
|
||||
}
|
||||
iData_ = iData;
|
||||
ownsData_ = false;
|
||||
}
|
||||
|
||||
u32 UIMeshBuffer::getByteOffset() const {
|
||||
return byteOffset_;
|
||||
}
|
||||
|
||||
void UIMeshBuffer::setByteOffset(u32 offset) {
|
||||
byteOffset_ = offset;
|
||||
}
|
||||
|
||||
u32 UIMeshBuffer::getVertexOffset() const {
|
||||
return vertexOffset_;
|
||||
}
|
||||
|
||||
void UIMeshBuffer::setVertexOffset(u32 offset) {
|
||||
vertexOffset_ = offset;
|
||||
}
|
||||
|
||||
u32 UIMeshBuffer::getIndexOffset() const {
|
||||
return indexOffset_;
|
||||
}
|
||||
|
||||
void UIMeshBuffer::setIndexOffset(u32 offset) {
|
||||
indexOffset_ = offset;
|
||||
}
|
||||
|
||||
bool UIMeshBuffer::isDirty() const {
|
||||
return dirty_;
|
||||
}
|
||||
|
||||
void UIMeshBuffer::setDirty(bool dirty) {
|
||||
dirty_ = dirty;
|
||||
}
|
||||
|
||||
u32 UIMeshBuffer::getVertexCapacity() const {
|
||||
return vertexCapacity_;
|
||||
}
|
||||
|
||||
u32 UIMeshBuffer::getIndexCapacity() const {
|
||||
return indexCapacity_;
|
||||
}
|
||||
|
||||
u32 UIMeshBuffer::getVertexFormatBytes() const {
|
||||
return vertexFormatBytes_;
|
||||
}
|
||||
|
||||
gfx::Buffer* UIMeshBuffer::getVertexBuffer() const {
|
||||
return vertexBuffer_;
|
||||
}
|
||||
|
||||
gfx::Buffer* UIMeshBuffer::getIndexBuffer() const {
|
||||
return indexBuffer_;
|
||||
}
|
||||
|
||||
gfx::InputAssembler* UIMeshBuffer::getInputAssembler() const {
|
||||
return inputAssembler_;
|
||||
}
|
||||
|
||||
gfx::InputAssembler* UIMeshBuffer::requireFreeIA(gfx::Device* device) {
|
||||
if (!inputAssembler_) {
|
||||
createInputAssembler();
|
||||
}
|
||||
return inputAssembler_;
|
||||
}
|
||||
|
||||
void UIMeshBuffer::uploadBuffers() {
|
||||
if (!dirty_ || !vData_ || !iData_) return;
|
||||
|
||||
if (vertexBuffer_) {
|
||||
vertexBuffer_->update(vData_, vertexCapacity_ * vertexFormatBytes_);
|
||||
}
|
||||
if (indexBuffer_) {
|
||||
indexBuffer_->update(iData_, indexCapacity_ * sizeof(u16));
|
||||
}
|
||||
|
||||
dirty_ = false;
|
||||
}
|
||||
|
||||
void UIMeshBuffer::resetIA() {
|
||||
if (inputAssembler_) {
|
||||
inputAssembler_->release();
|
||||
inputAssembler_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool UIMeshBuffer::createBuffers() {
|
||||
if (!device_) return false;
|
||||
|
||||
gfx::BufferInfo vbInfo;
|
||||
vbInfo.usage = gfx::BufferUsage::Vertex | gfx::BufferUsage::TransferDst;
|
||||
vbInfo.memUsage = gfx::MemoryUsage::Host;
|
||||
vbInfo.size = vertexCapacity_ * vertexFormatBytes_;
|
||||
vbInfo.stride = vertexFormatBytes_;
|
||||
|
||||
vertexBuffer_ = device_->createBuffer(vbInfo);
|
||||
if (!vertexBuffer_) return false;
|
||||
|
||||
gfx::BufferInfo ibInfo;
|
||||
ibInfo.usage = gfx::BufferUsage::Index | gfx::BufferUsage::TransferDst;
|
||||
ibInfo.memUsage = gfx::MemoryUsage::Host;
|
||||
ibInfo.size = indexCapacity_ * sizeof(u16);
|
||||
ibInfo.stride = sizeof(u16);
|
||||
|
||||
indexBuffer_ = device_->createBuffer(ibInfo);
|
||||
if (!indexBuffer_) {
|
||||
vertexBuffer_->release();
|
||||
vertexBuffer_ = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UIMeshBuffer::createInputAssembler() {
|
||||
if (!device_ || !vertexBuffer_ || !indexBuffer_) return false;
|
||||
|
||||
gfx::InputAssemblerInfo iaInfo;
|
||||
iaInfo.vertexStreams.push_back({vertexBuffer_, 0});
|
||||
iaInfo.indexBuffer = indexBuffer_;
|
||||
iaInfo.vertexCount = vertexCapacity_;
|
||||
iaInfo.indexCount = indexCapacity_;
|
||||
|
||||
inputAssembler_ = device_->createInputAssembler(iaInfo);
|
||||
return inputAssembler_ != nullptr;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,152 +0,0 @@
|
|||
#include <extra2d/app/application.h>
|
||||
#include <extra2d/core/registry.h>
|
||||
#include <extra2d/platform/glfw/glfw_window.h>
|
||||
#include <extra2d/platform/window_module.h>
|
||||
#include <extra2d/services/event_service.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <extra2d/services/timer_service.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
static f64 getTimeSeconds() {
|
||||
#ifdef __SWITCH__
|
||||
struct timespec ts;
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return static_cast<f64>(ts.tv_sec) +
|
||||
static_cast<f64>(ts.tv_nsec) / 1000000000.0;
|
||||
#else
|
||||
using namespace std::chrono;
|
||||
auto now = steady_clock::now();
|
||||
auto duration = now.time_since_epoch();
|
||||
return duration_cast<std::chrono::duration<f64>>(duration).count();
|
||||
#endif
|
||||
}
|
||||
|
||||
Application &Application::get() {
|
||||
static Application instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
Application::Application() { Registry::instance().setApp(this); }
|
||||
|
||||
Application::~Application() {
|
||||
if (initialized_) {
|
||||
shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
bool Application::init() {
|
||||
if (initialized_) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 初始化所有模块(拓扑排序)
|
||||
// 服务通过 E2D_AUTO_REGISTER_SERVICE 宏自动注册
|
||||
if (!Registry::instance().init()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 初始化所有服务
|
||||
ServiceLocator::instance().init();
|
||||
|
||||
initialized_ = true;
|
||||
running_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Application::shutdown() {
|
||||
if (!initialized_)
|
||||
return;
|
||||
|
||||
ServiceLocator::instance().shutdown();
|
||||
ServiceLocator::instance().clear();
|
||||
Registry::instance().shutdown();
|
||||
Registry::instance().clear();
|
||||
|
||||
initialized_ = false;
|
||||
running_ = false;
|
||||
}
|
||||
|
||||
void Application::run() {
|
||||
if (!initialized_)
|
||||
return;
|
||||
|
||||
auto *winMod = get<WindowModule>();
|
||||
if (!winMod || !winMod->win())
|
||||
return;
|
||||
|
||||
lastFrameTime_ = getTimeSeconds();
|
||||
|
||||
while (running_ && !winMod->win()->shouldClose()) {
|
||||
mainLoop();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::quit() {
|
||||
shouldQuit_ = true;
|
||||
running_ = false;
|
||||
}
|
||||
|
||||
void Application::pause() {
|
||||
if (!paused_) {
|
||||
paused_ = true;
|
||||
ServiceLocator::instance().pause();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::resume() {
|
||||
if (paused_) {
|
||||
paused_ = false;
|
||||
ServiceLocator::instance().resume();
|
||||
lastFrameTime_ = getTimeSeconds();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::mainLoop() {
|
||||
f64 currentTime = getTimeSeconds();
|
||||
dt_ = static_cast<f32>(currentTime - lastFrameTime_);
|
||||
lastFrameTime_ = currentTime;
|
||||
|
||||
totalTime_ += dt_;
|
||||
|
||||
frameCount_++;
|
||||
fpsTimer_ += dt_;
|
||||
if (fpsTimer_ >= 1.0f) {
|
||||
fps_ = frameCount_;
|
||||
frameCount_ = 0;
|
||||
fpsTimer_ -= 1.0f;
|
||||
}
|
||||
|
||||
auto *winMod = get<WindowModule>();
|
||||
if (winMod && winMod->win()) {
|
||||
winMod->win()->poll();
|
||||
}
|
||||
|
||||
auto eventService = ServiceLocator::instance().get<IEventService>();
|
||||
if (eventService) {
|
||||
eventService->process();
|
||||
}
|
||||
|
||||
if (!paused_) {
|
||||
update();
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
|
||||
void Application::update() { ServiceLocator::instance().update(dt_); }
|
||||
|
||||
void Application::render() {
|
||||
auto *winMod = get<WindowModule>();
|
||||
if (!winMod || !winMod->win())
|
||||
return;
|
||||
|
||||
winMod->win()->swap();
|
||||
}
|
||||
|
||||
GLFWWindow *Application::window() {
|
||||
auto *winMod = get<WindowModule>();
|
||||
return winMod ? winMod->win() : nullptr;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,183 @@
|
|||
#include <extra2d/application/Application.h>
|
||||
#include <extra2d/engine/Engine.h>
|
||||
#include <extra2d/platform/Window.h>
|
||||
#include <extra2d/platform/Input.h>
|
||||
#include <extra2d/platform/SDLHelper.h>
|
||||
#include <extra2d/core/event/EventBus.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Application* Application::instance_ = nullptr;
|
||||
|
||||
Application::Application() {}
|
||||
|
||||
Application::~Application() {
|
||||
close();
|
||||
}
|
||||
|
||||
Application* Application::getInstance() {
|
||||
if (!instance_) {
|
||||
instance_ = new Application();
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
i32 Application::init(const AppConfig& config) {
|
||||
if (initialized_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (SDLHelper::init() != 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
window_ = Window::getInstance();
|
||||
WindowConfig windowConfig;
|
||||
windowConfig.title = config.title;
|
||||
windowConfig.width = config.width;
|
||||
windowConfig.height = config.height;
|
||||
windowConfig.fullscreen = config.fullscreen;
|
||||
windowConfig.resizable = config.resizable;
|
||||
windowConfig.vsync = config.vsync;
|
||||
windowConfig.borderless = config.borderless;
|
||||
windowConfig.highDPI = config.highDPI;
|
||||
|
||||
if (window_->initialize(windowConfig) != 0) {
|
||||
SDLHelper::quit();
|
||||
return -2;
|
||||
}
|
||||
|
||||
engine_ = Engine::getInstance();
|
||||
if (engine_->init() != 0) {
|
||||
window_->shutdown();
|
||||
SDLHelper::quit();
|
||||
return -3;
|
||||
}
|
||||
|
||||
engine_->setPreferredFramesPerSecond(config.fps);
|
||||
|
||||
input_ = Input::getInstance();
|
||||
|
||||
initialized_ = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
i32 Application::run() {
|
||||
if (!initialized_) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
running_ = true;
|
||||
paused_ = false;
|
||||
|
||||
mainLoop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Application::pause() {
|
||||
if (!running_ || paused_) return;
|
||||
|
||||
paused_ = true;
|
||||
engine_->pause();
|
||||
}
|
||||
|
||||
void Application::resume() {
|
||||
if (!running_ || !paused_) return;
|
||||
|
||||
paused_ = false;
|
||||
engine_->resume();
|
||||
}
|
||||
|
||||
void Application::close() {
|
||||
if (!initialized_) return;
|
||||
|
||||
running_ = false;
|
||||
|
||||
if (engine_) {
|
||||
engine_->close();
|
||||
}
|
||||
|
||||
if (window_) {
|
||||
window_->shutdown();
|
||||
}
|
||||
|
||||
SDLHelper::quit();
|
||||
|
||||
initialized_ = false;
|
||||
}
|
||||
|
||||
bool Application::isRunning() const {
|
||||
return running_;
|
||||
}
|
||||
|
||||
bool Application::isPaused() const {
|
||||
return paused_;
|
||||
}
|
||||
|
||||
Engine* Application::getEngine() {
|
||||
return engine_;
|
||||
}
|
||||
|
||||
Window* Application::getWindow() {
|
||||
return window_;
|
||||
}
|
||||
|
||||
Input* Application::getInput() {
|
||||
return input_;
|
||||
}
|
||||
|
||||
const std::vector<std::string>& Application::getArguments() const {
|
||||
return arguments_;
|
||||
}
|
||||
|
||||
void Application::setArguments(i32 argc, char* argv[]) {
|
||||
arguments_.clear();
|
||||
for (i32 i = 0; i < argc; ++i) {
|
||||
arguments_.push_back(argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::setFrameCallback(const std::function<void(f32)>& callback) {
|
||||
frameCallback_ = callback;
|
||||
}
|
||||
|
||||
void Application::mainLoop() {
|
||||
auto lastTime = std::chrono::high_resolution_clock::now();
|
||||
i64 targetFrameTime = 1000000000LL / engine_->getPreferredFramesPerSecond();
|
||||
|
||||
while (running_) {
|
||||
processEvents();
|
||||
|
||||
if (!paused_) {
|
||||
engine_->tick();
|
||||
|
||||
if (frameCallback_) {
|
||||
frameCallback_(engine_->getDeltaTime());
|
||||
}
|
||||
|
||||
input_->update();
|
||||
|
||||
window_->swapBuffers();
|
||||
}
|
||||
|
||||
auto currentTime = std::chrono::high_resolution_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds>(currentTime - lastTime);
|
||||
|
||||
if (elapsed.count() < targetFrameTime) {
|
||||
auto sleepTime = targetFrameTime - elapsed.count();
|
||||
std::this_thread::sleep_for(std::chrono::nanoseconds(sleepTime));
|
||||
}
|
||||
|
||||
lastTime = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
}
|
||||
|
||||
void Application::processEvents() {
|
||||
SDLHelper::pollAndDispatchEvents();
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#include <extra2d/asset/asset.h>
|
||||
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include <stb/stb_truetype.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FontAsset::Impl - Pimpl 实现类
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
class FontAsset::Impl {
|
||||
public:
|
||||
stbtt_fontinfo info;
|
||||
bool initialized = false;
|
||||
};
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FontAsset 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
FontAsset::FontAsset() : impl_(ptr::makeUnique<Impl>()) {}
|
||||
|
||||
FontAsset::~FontAsset() = default;
|
||||
|
||||
bool FontAsset::loaded() const {
|
||||
return state_.load(std::memory_order_acquire) == AssetState::Loaded && impl_->initialized;
|
||||
}
|
||||
|
||||
float FontAsset::scaleForPixelHeight(float pixels) const {
|
||||
if (!impl_->initialized || data_.empty()) {
|
||||
return 0.0f;
|
||||
}
|
||||
return stbtt_ScaleForPixelHeight(&impl_->info, pixels);
|
||||
}
|
||||
|
||||
bool FontAsset::setData(std::vector<u8> data) {
|
||||
if (data.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
data_ = std::move(data);
|
||||
|
||||
if (!stbtt_InitFont(&impl_->info, data_.data(), 0)) {
|
||||
data_.clear();
|
||||
impl_->initialized = false;
|
||||
setState(AssetState::Failed);
|
||||
return false;
|
||||
}
|
||||
|
||||
impl_->initialized = true;
|
||||
setState(AssetState::Loaded);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FontAsset::release() {
|
||||
data_.clear();
|
||||
impl_->initialized = false;
|
||||
setState(AssetState::Unloaded);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
#include <extra2d/asset/asset_cache.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetCache 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
AssetCache::AssetCache(size_t limit)
|
||||
: limit_(limit) {
|
||||
stats_.limit = limit;
|
||||
}
|
||||
|
||||
bool AssetCache::has(const AssetID& id) const {
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
return entries_.find(id) != entries_.end();
|
||||
}
|
||||
|
||||
bool AssetCache::remove(const AssetID& id) {
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
|
||||
auto it = entries_.find(id);
|
||||
if (it == entries_.end()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bytes_ -= it->second.entry->asset->memSize();
|
||||
lruList_.erase(it->second.lruIterator);
|
||||
entries_.erase(it);
|
||||
--stats_.count;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssetCache::setLimit(size_t limit) {
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
limit_ = limit;
|
||||
stats_.limit = limit;
|
||||
|
||||
if (limit > 0 && bytes_ > limit) {
|
||||
evict();
|
||||
}
|
||||
}
|
||||
|
||||
size_t AssetCache::count() const {
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
return entries_.size();
|
||||
}
|
||||
|
||||
size_t AssetCache::purge() {
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
|
||||
size_t purged = 0;
|
||||
auto it = entries_.begin();
|
||||
|
||||
while (it != entries_.end()) {
|
||||
if (canEvict(*it->second.entry)) {
|
||||
bytes_ -= it->second.entry->asset->memSize();
|
||||
lruList_.erase(it->second.lruIterator);
|
||||
it = entries_.erase(it);
|
||||
++purged;
|
||||
--stats_.count;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
return purged;
|
||||
}
|
||||
|
||||
void AssetCache::clear() {
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
entries_.clear();
|
||||
lruList_.clear();
|
||||
bytes_ = 0;
|
||||
stats_.count = 0;
|
||||
stats_.bytes = 0;
|
||||
}
|
||||
|
||||
CacheStats AssetCache::stats() const {
|
||||
std::shared_lock<std::shared_mutex> lock(mutex_);
|
||||
stats_.bytes = bytes_;
|
||||
return stats_;
|
||||
}
|
||||
|
||||
void AssetCache::resetStats() {
|
||||
std::unique_lock<std::shared_mutex> lock(mutex_);
|
||||
stats_.hits = 0;
|
||||
stats_.misses = 0;
|
||||
}
|
||||
|
||||
void AssetCache::evict() {
|
||||
while (!lruList_.empty() && (limit_ > 0 && bytes_ > limit_)) {
|
||||
AssetID id = lruList_.front();
|
||||
|
||||
auto it = entries_.find(id);
|
||||
if (it != entries_.end()) {
|
||||
if (canEvict(*it->second.entry)) {
|
||||
bytes_ -= it->second.entry->asset->memSize();
|
||||
entries_.erase(it);
|
||||
lruList_.pop_front();
|
||||
--stats_.count;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
lruList_.pop_front();
|
||||
if (it != entries_.end()) {
|
||||
it->second.lruIterator = lruList_.insert(lruList_.end(), id);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool AssetCache::canEvict(const CacheEntry& entry) const {
|
||||
long useCount = entry.asset.use_count();
|
||||
return useCount <= 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,418 +0,0 @@
|
|||
#include <extra2d/asset/asset_loader.h>
|
||||
#include <extra2d/services/logger_service.h>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include <stb/stb_image.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 辅助函数
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* @brief 转换为小写
|
||||
*/
|
||||
std::string toLower(const std::string& str) {
|
||||
std::string result = str;
|
||||
std::transform(result.begin(), result.end(), result.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取文件扩展名
|
||||
*/
|
||||
std::string getExtension(const std::string& path) {
|
||||
size_t pos = path.rfind('.');
|
||||
if (pos == std::string::npos) {
|
||||
return "";
|
||||
}
|
||||
return toLower(path.substr(pos));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读取文件内容
|
||||
*/
|
||||
std::vector<u8> readFile(const std::string& path) {
|
||||
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||
if (!file) {
|
||||
return {};
|
||||
}
|
||||
|
||||
size_t size = static_cast<size_t>(file.tellg());
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<u8> data(size);
|
||||
if (!file.read(reinterpret_cast<char*>(data.data()), size)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// TextureLoader 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
TextureLoader::TextureLoader() = default;
|
||||
|
||||
TextureLoader::~TextureLoader() = default;
|
||||
|
||||
Ref<TextureAsset> TextureLoader::load(const std::string& path) {
|
||||
auto data = readFile(path);
|
||||
if (data.empty()) {
|
||||
E2D_ERROR("Failed to read texture file: {}", path);
|
||||
return nullptr;
|
||||
}
|
||||
return loadFromMemory(data.data(), data.size());
|
||||
}
|
||||
|
||||
Ref<TextureAsset> TextureLoader::loadFromMemory(const u8* data, size_t size) {
|
||||
if (!data || size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int width, height, channels;
|
||||
u8* pixels = stbi_load_from_memory(data, static_cast<int>(size),
|
||||
&width, &height, &channels,
|
||||
desiredChannels_);
|
||||
|
||||
if (!pixels) {
|
||||
E2D_ERROR("Failed to load texture from memory: {}", stbi_failure_reason());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int actualChannels = desiredChannels_ > 0 ? desiredChannels_ : channels;
|
||||
|
||||
auto asset = ptr::make<TextureAsset>();
|
||||
|
||||
Unique<u8[]> pixelData(new u8[static_cast<size_t>(width) * height * actualChannels]);
|
||||
std::memcpy(pixelData.get(), pixels,
|
||||
static_cast<size_t>(width) * height * actualChannels);
|
||||
|
||||
asset->setData(width, height, actualChannels, std::move(pixelData));
|
||||
|
||||
stbi_image_free(pixels);
|
||||
|
||||
return asset;
|
||||
}
|
||||
|
||||
bool TextureLoader::canLoad(const std::string& path) const {
|
||||
std::string ext = getExtension(path);
|
||||
auto exts = extensions();
|
||||
return std::find(exts.begin(), exts.end(), ext) != exts.end();
|
||||
}
|
||||
|
||||
std::vector<std::string> TextureLoader::extensions() const {
|
||||
return {".png", ".jpg", ".jpeg", ".bmp", ".tga", ".gif", ".psd", ".hdr", ".pic"};
|
||||
}
|
||||
|
||||
void TextureLoader::setDesiredChannels(int channels) {
|
||||
desiredChannels_ = std::clamp(channels, 0, 4);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// FontLoader 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Ref<FontAsset> FontLoader::load(const std::string& path) {
|
||||
auto data = readFile(path);
|
||||
if (data.empty()) {
|
||||
E2D_ERROR("Failed to read font file: {}", path);
|
||||
return nullptr;
|
||||
}
|
||||
return loadFromMemory(data.data(), data.size());
|
||||
}
|
||||
|
||||
Ref<FontAsset> FontLoader::loadFromMemory(const u8* data, size_t size) {
|
||||
if (!data || size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto asset = ptr::make<FontAsset>();
|
||||
|
||||
std::vector<u8> fontData(data, data + size);
|
||||
if (!asset->setData(std::move(fontData))) {
|
||||
E2D_ERROR("Failed to initialize font from memory");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return asset;
|
||||
}
|
||||
|
||||
bool FontLoader::canLoad(const std::string& path) const {
|
||||
std::string ext = getExtension(path);
|
||||
auto exts = extensions();
|
||||
return std::find(exts.begin(), exts.end(), ext) != exts.end();
|
||||
}
|
||||
|
||||
std::vector<std::string> FontLoader::extensions() const {
|
||||
return {".ttf", ".otf", ".ttc"};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// ShaderLoader 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Ref<ShaderAsset> ShaderLoader::load(const std::string& path) {
|
||||
auto data = readFile(path);
|
||||
if (data.empty()) {
|
||||
E2D_ERROR("Failed to read shader file: {}", path);
|
||||
return nullptr;
|
||||
}
|
||||
return loadFromMemory(data.data(), data.size());
|
||||
}
|
||||
|
||||
Ref<ShaderAsset> ShaderLoader::loadFromMemory(const u8* data, size_t size) {
|
||||
if (!data || size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string content(reinterpret_cast<const char*>(data), size);
|
||||
|
||||
std::string vertexSrc, fragmentSrc;
|
||||
|
||||
if (content.find(vertexMarker_) != std::string::npos) {
|
||||
if (!parseCombined(content, vertexSrc, fragmentSrc)) {
|
||||
E2D_ERROR("Failed to parse combined shader file");
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
vertexSrc = content;
|
||||
fragmentSrc = content;
|
||||
}
|
||||
|
||||
auto asset = ptr::make<ShaderAsset>();
|
||||
asset->setSource(std::move(vertexSrc), std::move(fragmentSrc));
|
||||
|
||||
return asset;
|
||||
}
|
||||
|
||||
bool ShaderLoader::canLoad(const std::string& path) const {
|
||||
std::string ext = getExtension(path);
|
||||
auto exts = extensions();
|
||||
return std::find(exts.begin(), exts.end(), ext) != exts.end();
|
||||
}
|
||||
|
||||
std::vector<std::string> ShaderLoader::extensions() const {
|
||||
return {".vert", ".frag", ".glsl", ".vs", ".fs"};
|
||||
}
|
||||
|
||||
bool ShaderLoader::parseCombined(const std::string& content,
|
||||
std::string& vertex,
|
||||
std::string& fragment) {
|
||||
size_t vertexPos = content.find(vertexMarker_);
|
||||
size_t fragmentPos = content.find(fragmentMarker_);
|
||||
|
||||
if (vertexPos == std::string::npos || fragmentPos == std::string::npos) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t vertexStart = vertexPos + vertexMarker_.length();
|
||||
|
||||
if (vertexPos < fragmentPos) {
|
||||
vertex = content.substr(vertexStart, fragmentPos - vertexStart);
|
||||
fragment = content.substr(fragmentPos + fragmentMarker_.length());
|
||||
} else {
|
||||
fragment = content.substr(fragmentPos + fragmentMarker_.length(),
|
||||
vertexPos - fragmentPos - fragmentMarker_.length());
|
||||
vertex = content.substr(vertexStart);
|
||||
}
|
||||
|
||||
auto trim = [](std::string& s) {
|
||||
size_t start = s.find_first_not_of(" \t\r\n");
|
||||
if (start == std::string::npos) {
|
||||
s.clear();
|
||||
return;
|
||||
}
|
||||
size_t end = s.find_last_not_of(" \t\r\n");
|
||||
s = s.substr(start, end - start + 1);
|
||||
};
|
||||
|
||||
trim(vertex);
|
||||
trim(fragment);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AudioLoader 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Ref<AudioAsset> AudioLoader::load(const std::string& path) {
|
||||
auto data = readFile(path);
|
||||
if (data.empty()) {
|
||||
E2D_ERROR("Failed to read audio file: {}", path);
|
||||
return nullptr;
|
||||
}
|
||||
return loadFromMemory(data.data(), data.size());
|
||||
}
|
||||
|
||||
Ref<AudioAsset> AudioLoader::loadFromMemory(const u8* data, size_t size) {
|
||||
if (!data || size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string ext = ".wav";
|
||||
if (size >= 4) {
|
||||
if (data[0] == 'R' && data[1] == 'I' && data[2] == 'F' && data[3] == 'F') {
|
||||
return loadWav(data, size);
|
||||
}
|
||||
}
|
||||
|
||||
E2D_ERROR("Unsupported audio format");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool AudioLoader::canLoad(const std::string& path) const {
|
||||
std::string ext = getExtension(path);
|
||||
auto exts = extensions();
|
||||
return std::find(exts.begin(), exts.end(), ext) != exts.end();
|
||||
}
|
||||
|
||||
std::vector<std::string> AudioLoader::extensions() const {
|
||||
return {".wav"};
|
||||
}
|
||||
|
||||
Ref<AudioAsset> AudioLoader::loadWav(const u8* data, size_t size) {
|
||||
if (size < 44) {
|
||||
E2D_ERROR("WAV file too small");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F') {
|
||||
E2D_ERROR("Invalid WAV file: missing RIFF header");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E') {
|
||||
E2D_ERROR("Invalid WAV file: missing WAVE format");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t pos = 12;
|
||||
u16 audioFormat = 0;
|
||||
u16 numChannels = 0;
|
||||
u32 sampleRate = 0;
|
||||
u16 bitsPerSample = 0;
|
||||
size_t dataSize = 0;
|
||||
const u8* audioData = nullptr;
|
||||
|
||||
while (pos < size) {
|
||||
u32 chunkId = *reinterpret_cast<const u32*>(data + pos);
|
||||
u32 chunkSize = *reinterpret_cast<const u32*>(data + pos + 4);
|
||||
|
||||
if (chunkId == 0x20746D66) { // "fmt "
|
||||
audioFormat = *reinterpret_cast<const u16*>(data + pos + 8);
|
||||
numChannels = *reinterpret_cast<const u16*>(data + pos + 10);
|
||||
sampleRate = *reinterpret_cast<const u32*>(data + pos + 12);
|
||||
bitsPerSample = *reinterpret_cast<const u16*>(data + pos + 22);
|
||||
} else if (chunkId == 0x61746164) { // "data"
|
||||
dataSize = chunkSize;
|
||||
audioData = data + pos + 8;
|
||||
break;
|
||||
}
|
||||
|
||||
pos += 8 + chunkSize;
|
||||
if (chunkSize % 2 == 1) {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!audioData || dataSize == 0) {
|
||||
E2D_ERROR("Invalid WAV file: missing data chunk");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (audioFormat != 1) {
|
||||
E2D_ERROR("Unsupported WAV format: only PCM supported");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto asset = ptr::make<AudioAsset>();
|
||||
|
||||
std::vector<u8> pcmData(audioData, audioData + dataSize);
|
||||
asset->setData(AudioFormat::PCM, numChannels, sampleRate, bitsPerSample,
|
||||
std::move(pcmData));
|
||||
|
||||
return asset;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DataLoader 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Ref<DataAsset> DataLoader::load(const std::string& path) {
|
||||
auto data = readFile(path);
|
||||
if (data.empty()) {
|
||||
E2D_ERROR("Failed to read data file: {}", path);
|
||||
return nullptr;
|
||||
}
|
||||
return loadFromMemory(data.data(), data.size());
|
||||
}
|
||||
|
||||
Ref<DataAsset> DataLoader::loadFromMemory(const u8* data, size_t size) {
|
||||
if (!data || size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto asset = ptr::make<DataAsset>();
|
||||
std::vector<u8> assetData(data, data + size);
|
||||
asset->setData(std::move(assetData));
|
||||
|
||||
return asset;
|
||||
}
|
||||
|
||||
bool DataLoader::canLoad(const std::string& path) const {
|
||||
return !path.empty();
|
||||
}
|
||||
|
||||
std::vector<std::string> DataLoader::extensions() const {
|
||||
return {".bin", ".dat"};
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetLoaderFactory 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
AssetType AssetLoaderFactory::getTypeByExtension(const std::string& extension) {
|
||||
std::string ext = toLower(extension);
|
||||
|
||||
if (ext == ".png" || ext == ".jpg" || ext == ".jpeg" ||
|
||||
ext == ".bmp" || ext == ".tga" || ext == ".gif" ||
|
||||
ext == ".psd" || ext == ".hdr" || ext == ".pic") {
|
||||
return AssetType::Texture;
|
||||
}
|
||||
|
||||
if (ext == ".ttf" || ext == ".otf" || ext == ".ttc") {
|
||||
return AssetType::Font;
|
||||
}
|
||||
|
||||
if (ext == ".vert" || ext == ".frag" || ext == ".glsl" ||
|
||||
ext == ".vs" || ext == ".fs") {
|
||||
return AssetType::Shader;
|
||||
}
|
||||
|
||||
if (ext == ".wav" || ext == ".mp3" || ext == ".ogg") {
|
||||
return AssetType::Audio;
|
||||
}
|
||||
|
||||
if (ext == ".bin" || ext == ".dat") {
|
||||
return AssetType::Data;
|
||||
}
|
||||
|
||||
return AssetType::Unknown;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,439 +0,0 @@
|
|||
#include "extra2d/asset/asset_pack.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetPack 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
AssetPack::AssetPack(AssetPack&& other) noexcept
|
||||
: path_(std::move(other.path_))
|
||||
, file_(std::move(other.file_))
|
||||
, header_(other.header_)
|
||||
, entries_(std::move(other.entries_))
|
||||
, pipe_(std::move(other.pipe_)) {
|
||||
other.header_ = AssetPackageHeader{};
|
||||
}
|
||||
|
||||
AssetPack& AssetPack::operator=(AssetPack&& other) noexcept {
|
||||
if (this != &other) {
|
||||
close();
|
||||
path_ = std::move(other.path_);
|
||||
file_ = std::move(other.file_);
|
||||
header_ = other.header_;
|
||||
entries_ = std::move(other.entries_);
|
||||
pipe_ = std::move(other.pipe_);
|
||||
other.header_ = AssetPackageHeader{};
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
AssetPack::~AssetPack() {
|
||||
close();
|
||||
}
|
||||
|
||||
bool AssetPack::open(const std::string& path) {
|
||||
close();
|
||||
|
||||
path_ = path;
|
||||
file_.open(path, std::ios::binary);
|
||||
|
||||
if (!file_.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!readHeader()) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!readIndex()) {
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AssetPack::close() {
|
||||
if (file_.is_open()) {
|
||||
file_.close();
|
||||
}
|
||||
entries_.clear();
|
||||
path_.clear();
|
||||
header_ = AssetPackageHeader{};
|
||||
}
|
||||
|
||||
bool AssetPack::has(const AssetID& id) const {
|
||||
return entries_.find(id) != entries_.end();
|
||||
}
|
||||
|
||||
bool AssetPack::has(const std::string& path) const {
|
||||
return has(AssetID(path));
|
||||
}
|
||||
|
||||
std::vector<u8> AssetPack::read(const AssetID& id) {
|
||||
auto raw = readRaw(id);
|
||||
if (raw.empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!pipe_.empty()) {
|
||||
return pipe_.process(raw);
|
||||
}
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
std::vector<u8> AssetPack::read(const std::string& path) {
|
||||
return read(AssetID(path));
|
||||
}
|
||||
|
||||
std::vector<u8> AssetPack::readRaw(const AssetID& id) {
|
||||
auto* entry = getEntry(id);
|
||||
if (!entry) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return readEntryData(*entry);
|
||||
}
|
||||
|
||||
std::vector<AssetID> AssetPack::assets() const {
|
||||
std::vector<AssetID> result;
|
||||
result.reserve(entries_.size());
|
||||
for (const auto& pair : entries_) {
|
||||
result.push_back(pair.first);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const AssetPackageEntry* AssetPack::getEntry(const AssetID& id) const {
|
||||
auto it = entries_.find(id);
|
||||
return it != entries_.end() ? &it->second : nullptr;
|
||||
}
|
||||
|
||||
bool AssetPack::readHeader() {
|
||||
file_.seekg(0, std::ios::beg);
|
||||
file_.read(reinterpret_cast<char*>(&header_), sizeof(header_));
|
||||
|
||||
if (!file_.good()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!header_.valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AssetPack::readIndex() {
|
||||
u32 entryCount = 0;
|
||||
file_.read(reinterpret_cast<char*>(&entryCount), sizeof(entryCount));
|
||||
|
||||
if (!file_.good()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
entries_.clear();
|
||||
entries_.reserve(entryCount);
|
||||
|
||||
for (u32 i = 0; i < entryCount; ++i) {
|
||||
u32 pathLen = 0;
|
||||
file_.read(reinterpret_cast<char*>(&pathLen), sizeof(pathLen));
|
||||
|
||||
if (!file_.good()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string path(pathLen, '\0');
|
||||
file_.read(path.data(), pathLen);
|
||||
|
||||
AssetPackageEntry entry;
|
||||
entry.id = AssetID(path);
|
||||
|
||||
file_.read(reinterpret_cast<char*>(&entry.offset), sizeof(entry.offset));
|
||||
file_.read(reinterpret_cast<char*>(&entry.size), sizeof(entry.size));
|
||||
file_.read(reinterpret_cast<char*>(&entry.originalSize), sizeof(entry.originalSize));
|
||||
file_.read(reinterpret_cast<char*>(&entry.compression), sizeof(entry.compression));
|
||||
file_.read(reinterpret_cast<char*>(&entry.flags), sizeof(entry.flags));
|
||||
|
||||
if (!file_.good()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
entries_[entry.id] = entry;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<u8> AssetPack::readEntryData(const AssetPackageEntry& entry) {
|
||||
std::vector<u8> data(entry.size);
|
||||
|
||||
file_.seekg(entry.offset, std::ios::beg);
|
||||
file_.read(reinterpret_cast<char*>(data.data()), entry.size);
|
||||
|
||||
if (!file_.good()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// PackManager 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
bool PackManager::mount(const std::string& path) {
|
||||
auto pack = std::make_unique<AssetPack>();
|
||||
if (!pack->open(path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
(void)defaultPipe_;
|
||||
|
||||
|
||||
packs_.push_back(std::move(pack));
|
||||
return true;
|
||||
}
|
||||
|
||||
void PackManager::unmount(const std::string& path) {
|
||||
packs_.erase(
|
||||
std::remove_if(packs_.begin(), packs_.end(),
|
||||
[&path](const Unique<AssetPack>& pack) {
|
||||
return pack->path() == path;
|
||||
}),
|
||||
packs_.end()
|
||||
);
|
||||
}
|
||||
|
||||
void PackManager::unmountAll() {
|
||||
packs_.clear();
|
||||
}
|
||||
|
||||
AssetPack* PackManager::find(const AssetID& id) {
|
||||
for (auto& pack : packs_) {
|
||||
if (pack->has(id)) {
|
||||
return pack.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AssetPack* PackManager::find(const std::string& path) {
|
||||
return find(AssetID(path));
|
||||
}
|
||||
|
||||
bool PackManager::has(const AssetID& id) const {
|
||||
for (const auto& pack : packs_) {
|
||||
if (pack->has(id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PackManager::has(const std::string& path) const {
|
||||
return has(AssetID(path));
|
||||
}
|
||||
|
||||
std::vector<u8> PackManager::read(const AssetID& id) {
|
||||
auto* pack = find(id);
|
||||
if (pack) {
|
||||
return pack->read(id);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<u8> PackManager::read(const std::string& path) {
|
||||
return read(AssetID(path));
|
||||
}
|
||||
|
||||
std::vector<AssetID> PackManager::allAssets() const {
|
||||
std::vector<AssetID> result;
|
||||
for (const auto& pack : packs_) {
|
||||
auto assets = pack->assets();
|
||||
result.insert(result.end(), assets.begin(), assets.end());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::string> PackManager::mountedPacks() const {
|
||||
std::vector<std::string> result;
|
||||
result.reserve(packs_.size());
|
||||
for (const auto& pack : packs_) {
|
||||
result.push_back(pack->path());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AssetPackBuilder 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
AssetPackBuilder::AssetPackBuilder(Compression compression, int level)
|
||||
: compression_(compression)
|
||||
, level_(level) {
|
||||
}
|
||||
|
||||
void AssetPackBuilder::add(const std::string& path, const std::vector<u8>& data) {
|
||||
BuilderEntry entry;
|
||||
entry.id = AssetID(path);
|
||||
entry.data = data;
|
||||
entry.compression = static_cast<u32>(compression_);
|
||||
entry.flags = encryptType_ != Decryptor::Type::None ? 1 : 0;
|
||||
|
||||
entry.compressedData = processData(entry.data);
|
||||
if (entry.compressedData.empty()) {
|
||||
entry.compressedData = entry.data;
|
||||
entry.compression = static_cast<u32>(Compression::None);
|
||||
}
|
||||
|
||||
entries_.push_back(std::move(entry));
|
||||
|
||||
totalOriginalSize_ += data.size();
|
||||
totalCompressedSize_ += entry.compressedData.size();
|
||||
}
|
||||
|
||||
void AssetPackBuilder::add(const std::string& path, std::vector<u8>&& data) {
|
||||
add(path, data);
|
||||
}
|
||||
|
||||
bool AssetPackBuilder::addFile(const std::string& filePath, const std::string& packPath) {
|
||||
std::ifstream file(filePath, std::ios::binary | std::ios::ate);
|
||||
if (!file.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<u8> data(size);
|
||||
file.read(reinterpret_cast<char*>(data.data()), size);
|
||||
|
||||
if (!file.good()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string ppath = packPath.empty()
|
||||
? std::filesystem::path(filePath).filename().string()
|
||||
: packPath;
|
||||
|
||||
add(ppath, std::move(data));
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t AssetPackBuilder::addDirectory(const std::string& dirPath, const std::string& prefix) {
|
||||
size_t count = 0;
|
||||
std::filesystem::path dir(dirPath);
|
||||
|
||||
if (!std::filesystem::exists(dir) || !std::filesystem::is_directory(dir)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (const auto& entry : std::filesystem::recursive_directory_iterator(dir)) {
|
||||
if (entry.is_regular_file()) {
|
||||
std::string relativePath = std::filesystem::relative(entry.path(), dir).string();
|
||||
std::string packPath = prefix.empty()
|
||||
? relativePath
|
||||
: prefix + "/" + relativePath;
|
||||
|
||||
if (addFile(entry.path().string(), packPath)) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void AssetPackBuilder::setEncryption(const std::string& key, Decryptor::Type type) {
|
||||
encryptKey_ = key;
|
||||
encryptType_ = type;
|
||||
}
|
||||
|
||||
bool AssetPackBuilder::build(const std::string& outputPath) {
|
||||
std::ofstream out(outputPath, std::ios::binary);
|
||||
if (!out.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
AssetPackageHeader header;
|
||||
header.magic = AssetPackageHeader::MAGIC;
|
||||
header.version = 1;
|
||||
header.compressionType = static_cast<u32>(compression_);
|
||||
header.encryptionType = static_cast<u32>(encryptType_);
|
||||
header.originalSize = totalOriginalSize_;
|
||||
|
||||
u64 dataOffset = sizeof(header) + sizeof(u32);
|
||||
for (const auto& entry : entries_) {
|
||||
dataOffset += sizeof(u32) + entry.id.path.size();
|
||||
dataOffset += sizeof(u64) * 3 + sizeof(u32) * 2;
|
||||
}
|
||||
|
||||
for (auto& entry : entries_) {
|
||||
entry.offset = dataOffset;
|
||||
dataOffset += entry.compressedData.size();
|
||||
}
|
||||
|
||||
header.compressedSize = dataOffset - sizeof(header) - sizeof(u32);
|
||||
for (const auto& entry : entries_) {
|
||||
header.compressedSize -= sizeof(u32) + entry.id.path.size();
|
||||
header.compressedSize -= sizeof(u64) * 3 + sizeof(u32) * 2;
|
||||
}
|
||||
|
||||
out.write(reinterpret_cast<const char*>(&header), sizeof(header));
|
||||
|
||||
u32 entryCount = static_cast<u32>(entries_.size());
|
||||
out.write(reinterpret_cast<const char*>(&entryCount), sizeof(entryCount));
|
||||
|
||||
for (const auto& entry : entries_) {
|
||||
u32 pathLen = static_cast<u32>(entry.id.path.size());
|
||||
out.write(reinterpret_cast<const char*>(&pathLen), sizeof(pathLen));
|
||||
out.write(entry.id.path.data(), pathLen);
|
||||
|
||||
u64 offset = entry.offset;
|
||||
u64 size = entry.compressedData.size();
|
||||
u64 originalSize = entry.data.size();
|
||||
|
||||
out.write(reinterpret_cast<const char*>(&offset), sizeof(offset));
|
||||
out.write(reinterpret_cast<const char*>(&size), sizeof(size));
|
||||
out.write(reinterpret_cast<const char*>(&originalSize), sizeof(originalSize));
|
||||
out.write(reinterpret_cast<const char*>(&entry.compression), sizeof(entry.compression));
|
||||
out.write(reinterpret_cast<const char*>(&entry.flags), sizeof(entry.flags));
|
||||
}
|
||||
|
||||
for (const auto& entry : entries_) {
|
||||
out.write(reinterpret_cast<const char*>(entry.compressedData.data()),
|
||||
entry.compressedData.size());
|
||||
}
|
||||
|
||||
return out.good();
|
||||
}
|
||||
|
||||
void AssetPackBuilder::clear() {
|
||||
entries_.clear();
|
||||
totalOriginalSize_ = 0;
|
||||
totalCompressedSize_ = 0;
|
||||
}
|
||||
|
||||
std::vector<u8> AssetPackBuilder::processData(const std::vector<u8>& data) {
|
||||
DataPipe pipe;
|
||||
|
||||
if (compression_ != Compression::None) {
|
||||
pipe.compress(compression_, level_);
|
||||
}
|
||||
|
||||
if (encryptType_ != Decryptor::Type::None && !encryptKey_.empty()) {
|
||||
pipe.encrypt(encryptKey_, encryptType_);
|
||||
}
|
||||
|
||||
return pipe.process(data);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,449 +0,0 @@
|
|||
#include "extra2d/asset/data_processor.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
|
||||
#ifdef E2D_USE_ZSTD
|
||||
#include <zstd.h>
|
||||
#endif
|
||||
|
||||
#ifdef E2D_USE_LZ4
|
||||
#include <lz4.h>
|
||||
#endif
|
||||
|
||||
#ifdef E2D_USE_ZLIB
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Decryptor 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Decryptor::Decryptor(const std::string& key, Type type)
|
||||
: key_(key), type_(type) {
|
||||
}
|
||||
|
||||
std::vector<u8> Decryptor::process(const std::vector<u8>& input) {
|
||||
if (input.empty() || type_ == Type::None) {
|
||||
return processNext(input);
|
||||
}
|
||||
|
||||
std::vector<u8> result;
|
||||
switch (type_) {
|
||||
case Type::XOR:
|
||||
result = decryptXOR(input);
|
||||
break;
|
||||
case Type::AES256:
|
||||
result = decryptAES256(input);
|
||||
break;
|
||||
default:
|
||||
result = input;
|
||||
break;
|
||||
}
|
||||
|
||||
return processNext(result);
|
||||
}
|
||||
|
||||
std::vector<u8> Decryptor::decryptXOR(const std::vector<u8>& input) {
|
||||
if (key_.empty()) {
|
||||
return input;
|
||||
}
|
||||
|
||||
std::vector<u8> result(input.size());
|
||||
const size_t keyLen = key_.size();
|
||||
|
||||
for (size_t i = 0; i < input.size(); ++i) {
|
||||
result[i] = input[i] ^ static_cast<u8>(key_[i % keyLen]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<u8> Decryptor::decryptAES256(const std::vector<u8>& input) {
|
||||
return decryptXOR(input);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Decompressor 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Decompressor::Decompressor(Compression algo)
|
||||
: algo_(algo) {
|
||||
}
|
||||
|
||||
std::vector<u8> Decompressor::process(const std::vector<u8>& input) {
|
||||
if (input.empty() || algo_ == Compression::None) {
|
||||
return processNext(input);
|
||||
}
|
||||
|
||||
std::vector<u8> result;
|
||||
switch (algo_) {
|
||||
case Compression::Zstd:
|
||||
result = decompressZstd(input);
|
||||
break;
|
||||
case Compression::LZ4:
|
||||
result = decompressLZ4(input);
|
||||
break;
|
||||
case Compression::Zlib:
|
||||
result = decompressZlib(input);
|
||||
break;
|
||||
default:
|
||||
result = input;
|
||||
break;
|
||||
}
|
||||
|
||||
return processNext(result);
|
||||
}
|
||||
|
||||
std::vector<u8> Decompressor::decompressZstd(const std::vector<u8>& input) {
|
||||
#ifdef E2D_USE_ZSTD
|
||||
unsigned long long const decompressedSize = ZSTD_getFrameContentSize(input.data(), input.size());
|
||||
|
||||
if (decompressedSize == ZSTD_CONTENTSIZE_ERROR ||
|
||||
decompressedSize == ZSTD_CONTENTSIZE_UNKNOWN) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<u8> result(static_cast<size_t>(decompressedSize));
|
||||
|
||||
size_t const actualSize = ZSTD_decompress(
|
||||
result.data(), result.size(),
|
||||
input.data(), input.size()
|
||||
);
|
||||
|
||||
if (ZSTD_isError(actualSize)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
result.resize(actualSize);
|
||||
return result;
|
||||
#else
|
||||
return input;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<u8> Decompressor::decompressLZ4(const std::vector<u8>& input) {
|
||||
#ifdef E2D_USE_LZ4
|
||||
if (input.size() < sizeof(u32)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
u32 originalSize;
|
||||
std::memcpy(&originalSize, input.data(), sizeof(u32));
|
||||
|
||||
std::vector<u8> result(originalSize);
|
||||
|
||||
int const decompressedSize = LZ4_decompress_safe(
|
||||
reinterpret_cast<const char*>(input.data() + sizeof(u32)),
|
||||
reinterpret_cast<char*>(result.data()),
|
||||
static_cast<int>(input.size() - sizeof(u32)),
|
||||
static_cast<int>(originalSize)
|
||||
);
|
||||
|
||||
if (decompressedSize < 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return result;
|
||||
#else
|
||||
return input;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<u8> Decompressor::decompressZlib(const std::vector<u8>& input) {
|
||||
#ifdef E2D_USE_ZLIB
|
||||
std::vector<u8> result;
|
||||
result.reserve(input.size() * 4);
|
||||
|
||||
const size_t CHUNK = 16384;
|
||||
u8 out[CHUNK];
|
||||
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
strm.avail_in = static_cast<uInt>(input.size());
|
||||
strm.next_in = const_cast<Bytef*>(input.data());
|
||||
|
||||
if (inflateInit(&strm) != Z_OK) {
|
||||
return {};
|
||||
}
|
||||
|
||||
int ret;
|
||||
do {
|
||||
strm.avail_out = CHUNK;
|
||||
strm.next_out = out;
|
||||
|
||||
ret = inflate(&strm, Z_NO_FLUSH);
|
||||
if (ret == Z_STREAM_ERROR || ret == Z_DATA_ERROR || ret == Z_MEM_ERROR) {
|
||||
inflateEnd(&strm);
|
||||
return {};
|
||||
}
|
||||
|
||||
result.insert(result.end(), out, out + CHUNK - strm.avail_out);
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
inflateEnd(&strm);
|
||||
return result;
|
||||
#else
|
||||
return input;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Encryptor 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Encryptor::Encryptor(const std::string& key, Decryptor::Type type)
|
||||
: key_(key), type_(type) {
|
||||
}
|
||||
|
||||
std::vector<u8> Encryptor::process(const std::vector<u8>& input) {
|
||||
if (input.empty() || type_ == Decryptor::Type::None) {
|
||||
return processNext(input);
|
||||
}
|
||||
|
||||
std::vector<u8> result;
|
||||
switch (type_) {
|
||||
case Decryptor::Type::XOR:
|
||||
result = encryptXOR(input);
|
||||
break;
|
||||
case Decryptor::Type::AES256:
|
||||
result = encryptAES256(input);
|
||||
break;
|
||||
default:
|
||||
result = input;
|
||||
break;
|
||||
}
|
||||
|
||||
return processNext(result);
|
||||
}
|
||||
|
||||
std::vector<u8> Encryptor::encryptXOR(const std::vector<u8>& input) {
|
||||
if (key_.empty()) {
|
||||
return input;
|
||||
}
|
||||
|
||||
std::vector<u8> result(input.size());
|
||||
const size_t keyLen = key_.size();
|
||||
|
||||
for (size_t i = 0; i < input.size(); ++i) {
|
||||
result[i] = input[i] ^ static_cast<u8>(key_[i % keyLen]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<u8> Encryptor::encryptAES256(const std::vector<u8>& input) {
|
||||
return encryptXOR(input);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Compressor 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
Compressor::Compressor(Compression algo, int level)
|
||||
: algo_(algo), level_(level) {
|
||||
}
|
||||
|
||||
std::vector<u8> Compressor::process(const std::vector<u8>& input) {
|
||||
if (input.empty() || algo_ == Compression::None) {
|
||||
return processNext(input);
|
||||
}
|
||||
|
||||
std::vector<u8> result;
|
||||
switch (algo_) {
|
||||
case Compression::Zstd:
|
||||
result = compressZstd(input);
|
||||
break;
|
||||
case Compression::LZ4:
|
||||
result = compressLZ4(input);
|
||||
break;
|
||||
case Compression::Zlib:
|
||||
result = compressZlib(input);
|
||||
break;
|
||||
default:
|
||||
result = input;
|
||||
break;
|
||||
}
|
||||
|
||||
return processNext(result);
|
||||
}
|
||||
|
||||
std::vector<u8> Compressor::compressZstd(const std::vector<u8>& input) {
|
||||
#ifdef E2D_USE_ZSTD
|
||||
size_t const bound = ZSTD_compressBound(input.size());
|
||||
std::vector<u8> result(bound);
|
||||
|
||||
size_t const compressedSize = ZSTD_compress(
|
||||
result.data(), result.size(),
|
||||
input.data(), input.size(),
|
||||
level_
|
||||
);
|
||||
|
||||
if (ZSTD_isError(compressedSize)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
result.resize(compressedSize);
|
||||
return result;
|
||||
#else
|
||||
return input;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<u8> Compressor::compressLZ4(const std::vector<u8>& input) {
|
||||
#ifdef E2D_USE_LZ4
|
||||
int const bound = LZ4_compressBound(static_cast<int>(input.size()));
|
||||
std::vector<u8> result(sizeof(u32) + bound);
|
||||
|
||||
u32 originalSize = static_cast<u32>(input.size());
|
||||
std::memcpy(result.data(), &originalSize, sizeof(u32));
|
||||
|
||||
int const compressedSize = LZ4_compress_default(
|
||||
reinterpret_cast<const char*>(input.data()),
|
||||
reinterpret_cast<char*>(result.data() + sizeof(u32)),
|
||||
static_cast<int>(input.size()),
|
||||
bound
|
||||
);
|
||||
|
||||
if (compressedSize <= 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
result.resize(sizeof(u32) + compressedSize);
|
||||
return result;
|
||||
#else
|
||||
return input;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<u8> Compressor::compressZlib(const std::vector<u8>& input) {
|
||||
#ifdef E2D_USE_ZLIB
|
||||
std::vector<u8> result;
|
||||
result.reserve(input.size() / 2);
|
||||
|
||||
const size_t CHUNK = 16384;
|
||||
u8 out[CHUNK];
|
||||
|
||||
z_stream strm;
|
||||
strm.zalloc = Z_NULL;
|
||||
strm.zfree = Z_NULL;
|
||||
strm.opaque = Z_NULL;
|
||||
|
||||
if (deflateInit(&strm, level_) != Z_OK) {
|
||||
return {};
|
||||
}
|
||||
|
||||
strm.avail_in = static_cast<uInt>(input.size());
|
||||
strm.next_in = const_cast<Bytef*>(input.data());
|
||||
|
||||
int ret;
|
||||
do {
|
||||
strm.avail_out = CHUNK;
|
||||
strm.next_out = out;
|
||||
|
||||
ret = deflate(&strm, Z_FINISH);
|
||||
if (ret == Z_STREAM_ERROR) {
|
||||
deflateEnd(&strm);
|
||||
return {};
|
||||
}
|
||||
|
||||
result.insert(result.end(), out, out + CHUNK - strm.avail_out);
|
||||
} while (strm.avail_out == 0);
|
||||
|
||||
deflateEnd(&strm);
|
||||
return result;
|
||||
#else
|
||||
return input;
|
||||
#endif
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DataPipe 实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
DataPipe& DataPipe::decrypt(const std::string& key, Decryptor::Type type) {
|
||||
processors_.push_back(std::make_unique<Decryptor>(key, type));
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataPipe& DataPipe::decompress(Compression algo) {
|
||||
processors_.push_back(std::make_unique<Decompressor>(algo));
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataPipe& DataPipe::encrypt(const std::string& key, Decryptor::Type type) {
|
||||
processors_.push_back(std::make_unique<Encryptor>(key, type));
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataPipe& DataPipe::compress(Compression algo, int level) {
|
||||
processors_.push_back(std::make_unique<Compressor>(algo, level));
|
||||
return *this;
|
||||
}
|
||||
|
||||
DataPipe& DataPipe::add(Unique<DataProcessor> processor) {
|
||||
processors_.push_back(std::move(processor));
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<u8> DataPipe::process(const std::vector<u8>& input) {
|
||||
if (processors_.empty()) {
|
||||
return input;
|
||||
}
|
||||
|
||||
std::vector<u8> result = input;
|
||||
for (auto& processor : processors_) {
|
||||
result = processor->process(result);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void DataPipe::clear() {
|
||||
processors_.clear();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 工具函数实现
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
std::vector<u8> computeChecksum(const std::vector<u8>& data) {
|
||||
std::vector<u8> result(32, 0);
|
||||
|
||||
u64 hash1 = 14695981039346656037ULL;
|
||||
u64 hash2 = 14695981039346656037ULL;
|
||||
|
||||
for (size_t i = 0; i < data.size(); ++i) {
|
||||
hash1 ^= static_cast<u64>(data[i]);
|
||||
hash1 *= 1099511628211ULL;
|
||||
|
||||
hash2 ^= static_cast<u64>(data[i]) << ((i % 8) * 8);
|
||||
hash2 *= 1099511628211ULL;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 8; ++i) {
|
||||
result[i] = static_cast<u8>((hash1 >> (i * 8)) & 0xFF);
|
||||
result[i + 8] = static_cast<u8>((hash2 >> (i * 8)) & 0xFF);
|
||||
}
|
||||
|
||||
for (size_t i = 16; i < 32; ++i) {
|
||||
result[i] = static_cast<u8>((hash1 ^ hash2) >> ((i - 16) * 8) & 0xFF);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool verifyChecksum(const std::vector<u8>& data, const std::vector<u8>& checksum) {
|
||||
if (checksum.size() != 32) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto computed = computeChecksum(data);
|
||||
return std::equal(computed.begin(), computed.end(), checksum.begin());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
#include <extra2d/base/AutoreleasePool.h>
|
||||
#include <extra2d/base/RefCounted.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
AutoreleasePool* AutoreleasePool::instance_ = nullptr;
|
||||
|
||||
AutoreleasePool::AutoreleasePool() {}
|
||||
|
||||
AutoreleasePool::~AutoreleasePool() {
|
||||
clear();
|
||||
}
|
||||
|
||||
AutoreleasePool* AutoreleasePool::getInstance() {
|
||||
if (!instance_) {
|
||||
instance_ = new AutoreleasePool();
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void AutoreleasePool::addObject(RefCounted* object) {
|
||||
if (object) {
|
||||
objects_.push_back(object);
|
||||
}
|
||||
}
|
||||
|
||||
void AutoreleasePool::clear() {
|
||||
for (auto* obj : objects_) {
|
||||
if (obj) {
|
||||
obj->release();
|
||||
}
|
||||
}
|
||||
objects_.clear();
|
||||
}
|
||||
|
||||
size_t AutoreleasePool::getObjectCount() const {
|
||||
return objects_.size();
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
#include <extra2d/base/RefCounted.h>
|
||||
#include <extra2d/base/AutoreleasePool.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
RefCounted::RefCounted() : refCount_(1), autorelease_(false) {}
|
||||
|
||||
RefCounted::~RefCounted() {}
|
||||
|
||||
void RefCounted::addRef() {
|
||||
refCount_.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
bool RefCounted::release() {
|
||||
u32 count = refCount_.fetch_sub(1, std::memory_order_acq_rel);
|
||||
if (count == 1) {
|
||||
delete this;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
u32 RefCounted::getRefCount() const {
|
||||
return refCount_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void RefCounted::autorelease() {
|
||||
autorelease_ = true;
|
||||
AutoreleasePool::getInstance()->addObject(this);
|
||||
}
|
||||
|
||||
bool RefCounted::isAutorelease() const {
|
||||
return autorelease_;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
#include <extra2d/base/Scheduler.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Scheduler::Scheduler() {}
|
||||
|
||||
Scheduler::~Scheduler() {
|
||||
unscheduleAll();
|
||||
}
|
||||
|
||||
void Scheduler::update(f32 dt) {
|
||||
dt *= timeScale_;
|
||||
|
||||
runFunctionsToBePerformed();
|
||||
|
||||
updateLocked_ = true;
|
||||
|
||||
for (auto& pair : targets_) {
|
||||
auto& entry = pair.second;
|
||||
if (entry.paused) continue;
|
||||
|
||||
for (auto it = entry.timers.begin(); it != entry.timers.end(); ) {
|
||||
Timer* timer = *it;
|
||||
|
||||
if (timer->paused) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (timer->delay > 0.0f) {
|
||||
timer->delay -= dt;
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
|
||||
timer->accumulator += dt;
|
||||
|
||||
while (timer->accumulator >= timer->interval) {
|
||||
timer->accumulator -= timer->interval;
|
||||
|
||||
if (timer->callback) {
|
||||
timer->callback(dt);
|
||||
}
|
||||
|
||||
if (timer->repeat > 0) {
|
||||
timer->elapsed++;
|
||||
if (timer->elapsed >= timer->repeat) {
|
||||
delete timer;
|
||||
it = entry.timers.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (it != entry.timers.end()) {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateLocked_ = false;
|
||||
}
|
||||
|
||||
void Scheduler::schedule(const SchedulerCallback& callback, void* target,
|
||||
f32 interval, u32 repeat, f32 delay,
|
||||
bool paused, const std::string& key) {
|
||||
Timer* timer = new Timer();
|
||||
timer->callback = callback;
|
||||
timer->interval = interval;
|
||||
timer->repeat = repeat;
|
||||
timer->delay = delay;
|
||||
timer->paused = paused;
|
||||
timer->key = key;
|
||||
timer->target = target;
|
||||
timer->elapsed = 0;
|
||||
timer->accumulator = 0.0f;
|
||||
|
||||
auto& entry = targets_[target];
|
||||
entry.timers.push_back(timer);
|
||||
}
|
||||
|
||||
void Scheduler::schedule(const SchedulerCallback& callback, void* target,
|
||||
f32 interval, bool paused, const std::string& key) {
|
||||
schedule(callback, target, interval, 0, 0.0f, paused, key);
|
||||
}
|
||||
|
||||
void Scheduler::scheduleOnce(const SchedulerCallback& callback, void* target,
|
||||
f32 delay, const std::string& key) {
|
||||
schedule(callback, target, 0.0f, 1, delay, false, key);
|
||||
}
|
||||
|
||||
void Scheduler::unschedule(const std::string& key, void* target) {
|
||||
auto it = targets_.find(target);
|
||||
if (it == targets_.end()) return;
|
||||
|
||||
auto& timers = it->second.timers;
|
||||
for (auto timerIt = timers.begin(); timerIt != timers.end(); ++timerIt) {
|
||||
if ((*timerIt)->key == key) {
|
||||
delete *timerIt;
|
||||
timers.erase(timerIt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::unscheduleAllForTarget(void* target) {
|
||||
auto it = targets_.find(target);
|
||||
if (it == targets_.end()) return;
|
||||
|
||||
for (auto* timer : it->second.timers) {
|
||||
delete timer;
|
||||
}
|
||||
targets_.erase(it);
|
||||
}
|
||||
|
||||
void Scheduler::unscheduleAll() {
|
||||
for (auto& pair : targets_) {
|
||||
for (auto* timer : pair.second.timers) {
|
||||
delete timer;
|
||||
}
|
||||
}
|
||||
targets_.clear();
|
||||
}
|
||||
|
||||
bool Scheduler::isScheduled(const std::string& key, void* target) {
|
||||
auto it = targets_.find(target);
|
||||
if (it == targets_.end()) return false;
|
||||
|
||||
for (const auto* timer : it->second.timers) {
|
||||
if (timer->key == key) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Scheduler::pauseTarget(void* target) {
|
||||
auto it = targets_.find(target);
|
||||
if (it != targets_.end()) {
|
||||
it->second.paused = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::resumeTarget(void* target) {
|
||||
auto it = targets_.find(target);
|
||||
if (it != targets_.end()) {
|
||||
it->second.paused = false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Scheduler::isTargetPaused(void* target) {
|
||||
auto it = targets_.find(target);
|
||||
if (it != targets_.end()) {
|
||||
return it->second.paused;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Scheduler::performFunctionInMainThread(const std::function<void()>& func) {
|
||||
std::lock_guard<std::mutex> lock(performMutex_);
|
||||
functionsToPerform_.push_back(func);
|
||||
}
|
||||
|
||||
void Scheduler::runFunctionsToBePerformed() {
|
||||
std::vector<std::function<void()>> functions;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(performMutex_);
|
||||
functions.swap(functionsToPerform_);
|
||||
}
|
||||
|
||||
for (const auto& func : functions) {
|
||||
func();
|
||||
}
|
||||
}
|
||||
|
||||
f32 Scheduler::getTimeScale() const {
|
||||
return timeScale_;
|
||||
}
|
||||
|
||||
void Scheduler::setTimeScale(f32 scale) {
|
||||
timeScale_ = scale;
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#include <extra2d/core/Object.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
u64 Object::nextObjectId_ = 1;
|
||||
|
||||
Object::Object(const std::string& name)
|
||||
: name_(name)
|
||||
, objectId_(nextObjectId_++) {
|
||||
}
|
||||
|
||||
Object::~Object() {
|
||||
if (!hasFlag(ObjectFlags::Destroyed)) {
|
||||
onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& Object::getName() const {
|
||||
return name_;
|
||||
}
|
||||
|
||||
void Object::setName(const std::string& name) {
|
||||
name_ = name;
|
||||
}
|
||||
|
||||
bool Object::destroy() {
|
||||
if (hasFlag(ObjectFlags::Destroyed)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
onDestroy();
|
||||
flags_ |= ObjectFlags::Destroyed;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Object::isValid() const {
|
||||
return !hasFlag(ObjectFlags::Destroyed);
|
||||
}
|
||||
|
||||
bool Object::isDestroyed() const {
|
||||
return hasFlag(ObjectFlags::Destroyed);
|
||||
}
|
||||
|
||||
ObjectFlags Object::getFlags() const {
|
||||
return flags_;
|
||||
}
|
||||
|
||||
void Object::setFlags(ObjectFlags flags) {
|
||||
flags_ = flags;
|
||||
}
|
||||
|
||||
void Object::addFlag(ObjectFlags flag) {
|
||||
flags_ |= flag;
|
||||
}
|
||||
|
||||
void Object::removeFlag(ObjectFlags flag) {
|
||||
flags_ &= ~flag;
|
||||
}
|
||||
|
||||
bool Object::hasFlag(ObjectFlags flag) const {
|
||||
return extra2d::hasFlag(flags_, flag);
|
||||
}
|
||||
|
||||
u64 Object::getObjectId() const {
|
||||
return objectId_;
|
||||
}
|
||||
|
||||
std::string Object::toString() const {
|
||||
return "Object[" + name_ + "]";
|
||||
}
|
||||
|
||||
void Object::onDestroy() {
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
#include <extra2d/core/assets/Asset.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Asset::Asset(const std::string& name) : Object(name) {
|
||||
}
|
||||
|
||||
Asset::~Asset() {
|
||||
}
|
||||
|
||||
const std::string& Asset::getUuid() const {
|
||||
return uuid_;
|
||||
}
|
||||
|
||||
void Asset::setUuid(const std::string& uuid) {
|
||||
uuid_ = uuid;
|
||||
}
|
||||
|
||||
const std::string& Asset::getNativeUrl() const {
|
||||
return nativeUrl_;
|
||||
}
|
||||
|
||||
void Asset::setNativeUrl(const std::string& url) {
|
||||
nativeUrl_ = url;
|
||||
}
|
||||
|
||||
void Asset::addAssetRef() {
|
||||
++assetRefCount_;
|
||||
}
|
||||
|
||||
void Asset::decAssetRef(bool autoRelease) {
|
||||
if (assetRefCount_ > 0) {
|
||||
--assetRefCount_;
|
||||
}
|
||||
|
||||
if (autoRelease && assetRefCount_ == 0 && getRefCount() == 1) {
|
||||
release();
|
||||
}
|
||||
}
|
||||
|
||||
u32 Asset::getAssetRefCount() const {
|
||||
return assetRefCount_;
|
||||
}
|
||||
|
||||
bool Asset::isLoaded() const {
|
||||
return loaded_;
|
||||
}
|
||||
|
||||
void Asset::setLoaded(bool loaded) {
|
||||
loaded_ = loaded;
|
||||
}
|
||||
|
||||
void Asset::onLoaded() {
|
||||
loaded_ = true;
|
||||
}
|
||||
|
||||
bool Asset::isDefault() const {
|
||||
return isDefault_;
|
||||
}
|
||||
|
||||
void Asset::initDefault(const std::string& uuid) {
|
||||
isDefault_ = true;
|
||||
if (!uuid.empty()) {
|
||||
uuid_ = uuid;
|
||||
}
|
||||
}
|
||||
|
||||
bool Asset::validate() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Asset::destroy() {
|
||||
if (isDestroyed()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Object::destroy();
|
||||
}
|
||||
|
||||
void Asset::onDestroy() {
|
||||
Object::onDestroy();
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
#include <extra2d/core/assets/AssetManager.h>
|
||||
#include <extra2d/core/assets/Texture2D.h>
|
||||
#include <extra2d/gfx/GFXDevice.h>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
AssetManager::AssetManager() {
|
||||
}
|
||||
|
||||
AssetManager::~AssetManager() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
AssetManager* AssetManager::getInstance() {
|
||||
static AssetManager instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
bool AssetManager::initialize(gfx::Device* device) {
|
||||
device_ = device;
|
||||
initDefaultAssets();
|
||||
return true;
|
||||
}
|
||||
void AssetManager::destroy() {
|
||||
releaseAll();
|
||||
assets_.clear();
|
||||
defaultTextures_.clear();
|
||||
}
|
||||
Asset* AssetManager::load(const std::string& path, const std::string& type,
|
||||
const AssetLoadOptions& options) {
|
||||
auto it = assets_.find(path);
|
||||
if (it != assets_.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
void AssetManager::loadAsync(const std::string& path, const std::string& type,
|
||||
AssetLoadProgress progress, AssetLoadComplete complete,
|
||||
AssetLoadError error,
|
||||
const AssetLoadOptions& options) {
|
||||
}
|
||||
Asset* AssetManager::getAsset(const std::string& path) {
|
||||
auto it = assets_.find(path);
|
||||
if (it != assets_.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
Asset* AssetManager::getAssetByUuid(const std::string& uuid) {
|
||||
for (auto& pair : assets_) {
|
||||
if (pair.second->getUuid() == uuid) {
|
||||
return pair.second.get();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void AssetManager::releaseAsset(const std::string& path) {
|
||||
auto it = assets_.find(path);
|
||||
if (it != assets_.end()) {
|
||||
assets_.erase(it);
|
||||
}
|
||||
}
|
||||
void AssetManager::releaseAll() {
|
||||
assets_.clear();
|
||||
}
|
||||
void AssetManager::addAsset(const std::string& path, Asset* asset) {
|
||||
if (asset) {
|
||||
assets_[path] = asset;
|
||||
}
|
||||
}
|
||||
void AssetManager::removeAsset(const std::string& path) {
|
||||
assets_.erase(path);
|
||||
}
|
||||
bool AssetManager::hasAsset(const std::string& path) {
|
||||
return assets_.find(path) != assets_.end();
|
||||
}
|
||||
size_t AssetManager::getAssetCount() const {
|
||||
return assets_.size();
|
||||
}
|
||||
void AssetManager::setDefaultTexture(const std::string& name, Texture2D* texture) {
|
||||
if (texture) {
|
||||
defaultTextures_[name] = texture;
|
||||
assets_[name] = texture;
|
||||
}
|
||||
}
|
||||
Texture2D* AssetManager::getDefaultTexture(const std::string& name) {
|
||||
auto it = defaultTextures_.find(name);
|
||||
if (it != defaultTextures_.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
std::vector<std::string> AssetManager::getDefaultTextureNames() const {
|
||||
std::vector<std::string> names;
|
||||
for (const auto& pair : defaultTextures_) {
|
||||
names.push_back(pair.first);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
void AssetManager::initDefaultAssets() {
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
#include <extra2d/core/assets/ImageAsset.h>
|
||||
#include <cstring>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
ImageAsset::ImageAsset() : Asset("ImageAsset") {
|
||||
}
|
||||
|
||||
ImageAsset::~ImageAsset() {
|
||||
if (ownsData_ && data_) {
|
||||
delete[] data_;
|
||||
data_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
const u8* ImageAsset::getData() const {
|
||||
return data_;
|
||||
}
|
||||
|
||||
u32 ImageAsset::getDataSize() const {
|
||||
return dataSize_;
|
||||
}
|
||||
|
||||
void ImageAsset::setData(u8* data, u32 size, bool ownsData) {
|
||||
if (ownsData_ && data_) {
|
||||
delete[] data_;
|
||||
}
|
||||
|
||||
data_ = data;
|
||||
dataSize_ = size;
|
||||
ownsData_ = ownsData;
|
||||
}
|
||||
|
||||
u32 ImageAsset::getWidth() const {
|
||||
return width_;
|
||||
}
|
||||
|
||||
void ImageAsset::setWidth(u32 width) {
|
||||
width_ = width;
|
||||
}
|
||||
|
||||
u32 ImageAsset::getHeight() const {
|
||||
return height_;
|
||||
}
|
||||
|
||||
void ImageAsset::setHeight(u32 height) {
|
||||
height_ = height;
|
||||
}
|
||||
|
||||
PixelFormat ImageAsset::getFormat() const {
|
||||
return format_;
|
||||
}
|
||||
|
||||
void ImageAsset::setFormat(PixelFormat format) {
|
||||
format_ = format;
|
||||
}
|
||||
|
||||
bool ImageAsset::isCompressed() const {
|
||||
return compressed_;
|
||||
}
|
||||
|
||||
void ImageAsset::setCompressed(bool compressed) {
|
||||
compressed_ = compressed;
|
||||
}
|
||||
|
||||
const std::vector<u32>& ImageAsset::getMipmapLevelDataSize() const {
|
||||
return mipmapLevelDataSize_;
|
||||
}
|
||||
|
||||
void ImageAsset::setMipmapLevelDataSize(const std::vector<u32>& sizes) {
|
||||
mipmapLevelDataSize_ = sizes;
|
||||
}
|
||||
|
||||
u32 ImageAsset::getBytesPerPixel() const {
|
||||
switch (format_) {
|
||||
case PixelFormat::A8:
|
||||
case PixelFormat::I8:
|
||||
case PixelFormat::R8:
|
||||
return 1;
|
||||
case PixelFormat::RGB565:
|
||||
case PixelFormat::RGB5A1:
|
||||
case PixelFormat::RGBA4444:
|
||||
case PixelFormat::AI8:
|
||||
case PixelFormat::RG8:
|
||||
case PixelFormat::R16F:
|
||||
return 2;
|
||||
case PixelFormat::RGB888:
|
||||
return 3;
|
||||
case PixelFormat::RGBA8888:
|
||||
case PixelFormat::RG16F:
|
||||
case PixelFormat::R32F:
|
||||
return 4;
|
||||
case PixelFormat::RGB32F:
|
||||
return 12;
|
||||
case PixelFormat::RGBA32F:
|
||||
case PixelFormat::RGBA16F:
|
||||
return 16;
|
||||
default:
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
gfx::Format ImageAsset::getGFXFormat() const {
|
||||
switch (format_) {
|
||||
case PixelFormat::R8:
|
||||
return gfx::Format::R8;
|
||||
case PixelFormat::RG8:
|
||||
return gfx::Format::RG8;
|
||||
case PixelFormat::RGB888:
|
||||
return gfx::Format::RGBA8;
|
||||
case PixelFormat::RGBA8888:
|
||||
return gfx::Format::RGBA8;
|
||||
case PixelFormat::R16F:
|
||||
return gfx::Format::R16F;
|
||||
case PixelFormat::RG16F:
|
||||
return gfx::Format::RG16F;
|
||||
case PixelFormat::RGBA16F:
|
||||
return gfx::Format::RGBA16F;
|
||||
case PixelFormat::R32F:
|
||||
return gfx::Format::R32F;
|
||||
case PixelFormat::RG32F:
|
||||
return gfx::Format::RG32F;
|
||||
case PixelFormat::RGBA32F:
|
||||
return gfx::Format::RGBA32F;
|
||||
case PixelFormat::Depth:
|
||||
return gfx::Format::Depth;
|
||||
case PixelFormat::DepthStencil:
|
||||
return gfx::Format::DepthStencil;
|
||||
default:
|
||||
return gfx::Format::RGBA8;
|
||||
}
|
||||
}
|
||||
|
||||
bool ImageAsset::validate() const {
|
||||
return data_ != nullptr && width_ > 0 && height_ > 0;
|
||||
}
|
||||
|
||||
void ImageAsset::onDestroy() {
|
||||
if (ownsData_ && data_) {
|
||||
delete[] data_;
|
||||
data_ = nullptr;
|
||||
dataSize_ = 0;
|
||||
}
|
||||
Asset::onDestroy();
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,284 @@
|
|||
#include <extra2d/core/assets/Material.h>
|
||||
#include <extra2d/gfx/GFXShader.h>
|
||||
#include <extra2d/gfx/GFXPipeline.h>
|
||||
#include <functional>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
// ==================== Pass 实现 ====================
|
||||
|
||||
Pass::Pass() {
|
||||
}
|
||||
|
||||
Pass::~Pass() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
bool Pass::initialize(gfx::Shader* shader) {
|
||||
shader_ = shader;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Pass::destroy() {
|
||||
shader_ = nullptr;
|
||||
if (pipelineState_) {
|
||||
delete pipelineState_;
|
||||
pipelineState_ = nullptr;
|
||||
}
|
||||
uniforms_.clear();
|
||||
}
|
||||
|
||||
void Pass::setUniform(const std::string& name, const MaterialPropertyVariant& value) {
|
||||
uniforms_[name] = value;
|
||||
}
|
||||
|
||||
const MaterialPropertyVariant* Pass::getUniform(const std::string& name) const {
|
||||
auto it = uniforms_.find(name);
|
||||
if (it != uniforms_.end()) {
|
||||
return &it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Pass::setPipelineState(gfx::PipelineState* state) {
|
||||
pipelineState_ = state;
|
||||
}
|
||||
|
||||
gfx::PipelineState* Pass::getPipelineState() const {
|
||||
return pipelineState_;
|
||||
}
|
||||
|
||||
gfx::Shader* Pass::getShader() const {
|
||||
return shader_;
|
||||
}
|
||||
|
||||
void Pass::setBlendState(const gfx::BlendState& state) {
|
||||
blendState_ = state;
|
||||
}
|
||||
|
||||
const gfx::BlendState& Pass::getBlendState() const {
|
||||
return blendState_;
|
||||
}
|
||||
|
||||
void Pass::setDepthStencilState(const gfx::DepthStencilState& state) {
|
||||
depthStencilState_ = state;
|
||||
}
|
||||
|
||||
const gfx::DepthStencilState& Pass::getDepthStencilState() const {
|
||||
return depthStencilState_;
|
||||
}
|
||||
|
||||
void Pass::setRasterizerState(const gfx::RasterizerState& state) {
|
||||
rasterizerState_ = state;
|
||||
}
|
||||
|
||||
const gfx::RasterizerState& Pass::getRasterizerState() const {
|
||||
return rasterizerState_;
|
||||
}
|
||||
|
||||
size_t Pass::getHash() const {
|
||||
return hash_;
|
||||
}
|
||||
|
||||
// ==================== Material 实现 ====================
|
||||
|
||||
Ref<Material> Material::create() {
|
||||
Ref<Material> material = new Material();
|
||||
return material;
|
||||
}
|
||||
|
||||
Material::Material() : Asset() {
|
||||
}
|
||||
|
||||
Material::~Material() {
|
||||
destroy();
|
||||
}
|
||||
|
||||
bool Material::initialize(const MaterialInfo& info) {
|
||||
effectName_ = info.effectName;
|
||||
techniqueIndex_ = info.technique;
|
||||
defines_.push_back(info.defines);
|
||||
states_.push_back(info.states);
|
||||
|
||||
updateHash();
|
||||
return true;
|
||||
}
|
||||
|
||||
void Material::reset(const MaterialInfo& info) {
|
||||
passes_.clear();
|
||||
defines_.clear();
|
||||
states_.clear();
|
||||
properties_.clear();
|
||||
|
||||
initialize(info);
|
||||
}
|
||||
|
||||
bool Material::destroy() {
|
||||
passes_.clear();
|
||||
defines_.clear();
|
||||
states_.clear();
|
||||
properties_.clear();
|
||||
return Asset::destroy();
|
||||
}
|
||||
|
||||
void Material::setProperty(const std::string& name, const MaterialPropertyVariant& value, i32 passIdx) {
|
||||
properties_[name] = value;
|
||||
|
||||
if (passIdx >= 0 && static_cast<size_t>(passIdx) < passes_.size()) {
|
||||
passes_[passIdx]->setUniform(name, value);
|
||||
} else {
|
||||
for (auto& pass : passes_) {
|
||||
pass->setUniform(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Material::setPropertyFloat(const std::string& name, float value, i32 passIdx) {
|
||||
setProperty(name, MaterialPropertyVariant(value), passIdx);
|
||||
}
|
||||
|
||||
void Material::setPropertyInt(const std::string& name, i32 value, i32 passIdx) {
|
||||
setProperty(name, MaterialPropertyVariant(value), passIdx);
|
||||
}
|
||||
|
||||
void Material::setPropertyVec2(const std::string& name, const Vec2& value, i32 passIdx) {
|
||||
setProperty(name, MaterialPropertyVariant(value), passIdx);
|
||||
}
|
||||
|
||||
void Material::setPropertyVec3(const std::string& name, const Vec3& value, i32 passIdx) {
|
||||
setProperty(name, MaterialPropertyVariant(value), passIdx);
|
||||
}
|
||||
|
||||
void Material::setPropertyVec4(const std::string& name, const Vec4& value, i32 passIdx) {
|
||||
setProperty(name, MaterialPropertyVariant(value), passIdx);
|
||||
}
|
||||
|
||||
void Material::setPropertyColor(const std::string& name, const Color& value, i32 passIdx) {
|
||||
setProperty(name, MaterialPropertyVariant(value), passIdx);
|
||||
}
|
||||
|
||||
const MaterialPropertyVariant* Material::getProperty(const std::string& name, i32 passIdx) const {
|
||||
auto it = properties_.find(name);
|
||||
if (it != properties_.end()) {
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
if (passIdx >= 0 && static_cast<size_t>(passIdx) < passes_.size()) {
|
||||
return passes_[passIdx]->getUniform(name);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t Material::getPassCount() const {
|
||||
return passes_.size();
|
||||
}
|
||||
|
||||
Pass* Material::getPass(size_t index) {
|
||||
if (index < passes_.size()) {
|
||||
return passes_[index].get();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<Ref<Pass>>& Material::getPasses() const {
|
||||
return passes_;
|
||||
}
|
||||
|
||||
void Material::addPass(Ref<Pass> pass) {
|
||||
passes_.push_back(pass);
|
||||
updateHash();
|
||||
}
|
||||
|
||||
void Material::overridePipelineStates(const PassOverrides& overrides, i32 passIdx) {
|
||||
if (passIdx >= 0 && static_cast<size_t>(passIdx) < passes_.size()) {
|
||||
passes_[passIdx]->setBlendState(overrides.blendState);
|
||||
passes_[passIdx]->setDepthStencilState(overrides.depthStencilState);
|
||||
passes_[passIdx]->setRasterizerState(overrides.rasterizerState);
|
||||
} else {
|
||||
for (auto& pass : passes_) {
|
||||
pass->setBlendState(overrides.blendState);
|
||||
pass->setDepthStencilState(overrides.depthStencilState);
|
||||
pass->setRasterizerState(overrides.rasterizerState);
|
||||
}
|
||||
}
|
||||
updateHash();
|
||||
}
|
||||
|
||||
void Material::recompileShaders(const MacroRecord& defines, i32 passIdx) {
|
||||
if (passIdx >= 0 && static_cast<size_t>(passIdx) < defines_.size()) {
|
||||
for (const auto& pair : defines) {
|
||||
defines_[passIdx][pair.first] = pair.second;
|
||||
}
|
||||
} else {
|
||||
for (auto& def : defines_) {
|
||||
for (const auto& pair : defines) {
|
||||
def[pair.first] = pair.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
updateHash();
|
||||
}
|
||||
|
||||
void Material::setEffectName(const std::string& name) {
|
||||
effectName_ = name;
|
||||
}
|
||||
|
||||
const std::string& Material::getEffectName() const {
|
||||
return effectName_;
|
||||
}
|
||||
|
||||
void Material::setTechniqueIndex(u32 index) {
|
||||
techniqueIndex_ = index;
|
||||
}
|
||||
|
||||
u32 Material::getTechniqueIndex() const {
|
||||
return techniqueIndex_;
|
||||
}
|
||||
|
||||
size_t Material::getHash() const {
|
||||
return hash_;
|
||||
}
|
||||
|
||||
void Material::copy(const Material* other) {
|
||||
if (!other) return;
|
||||
|
||||
effectName_ = other->effectName_;
|
||||
techniqueIndex_ = other->techniqueIndex_;
|
||||
defines_ = other->defines_;
|
||||
states_ = other->states_;
|
||||
properties_ = other->properties_;
|
||||
|
||||
passes_.clear();
|
||||
for (const auto& pass : other->passes_) {
|
||||
Ref<Pass> newPass = new Pass();
|
||||
newPass->initialize(pass->getShader());
|
||||
newPass->setBlendState(pass->getBlendState());
|
||||
newPass->setDepthStencilState(pass->getDepthStencilState());
|
||||
newPass->setRasterizerState(pass->getRasterizerState());
|
||||
passes_.push_back(newPass);
|
||||
}
|
||||
|
||||
updateHash();
|
||||
}
|
||||
|
||||
void Material::resetUniforms() {
|
||||
properties_.clear();
|
||||
for (auto& pass : passes_) {
|
||||
pass->setUniform("u_color", MaterialPropertyVariant(Color::White));
|
||||
}
|
||||
}
|
||||
|
||||
void Material::updateHash() {
|
||||
std::hash<std::string> stringHash;
|
||||
std::hash<u32> intHash;
|
||||
|
||||
hash_ = stringHash(effectName_);
|
||||
hash_ ^= intHash(techniqueIndex_) + 0x9e3779b9 + (hash_ << 6) + (hash_ >> 2);
|
||||
|
||||
for (const auto& pass : passes_) {
|
||||
hash_ ^= pass->getHash() + 0x9e3779b9 + (hash_ << 6) + (hash_ >> 2);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
#include <extra2d/core/assets/Texture2D.h>
|
||||
#include <extra2d/gfx/GFXDevice.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
Texture2D::Texture2D() : Asset("Texture2D") {
|
||||
}
|
||||
|
||||
Texture2D::~Texture2D() {
|
||||
if (gfxTexture_) {
|
||||
gfxTexture_->release();
|
||||
gfxTexture_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool Texture2D::initialize(gfx::Device* device, const Texture2DCreateInfo& info) {
|
||||
device_ = device;
|
||||
width_ = info.width;
|
||||
height_ = info.height;
|
||||
format_ = info.format;
|
||||
mipmapLevel_ = info.mipmapLevel;
|
||||
|
||||
createGFXTexture();
|
||||
|
||||
return gfxTexture_ != nullptr;
|
||||
}
|
||||
|
||||
void Texture2D::reset(const Texture2DCreateInfo& info) {
|
||||
width_ = info.width;
|
||||
height_ = info.height;
|
||||
format_ = info.format;
|
||||
mipmapLevel_ = info.mipmapLevel;
|
||||
|
||||
if (gfxTexture_) {
|
||||
gfxTexture_->resize(width_, height_);
|
||||
} else {
|
||||
createGFXTexture();
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<Ref<ImageAsset>>& Texture2D::getMipmaps() const {
|
||||
return mipmaps_;
|
||||
}
|
||||
|
||||
void Texture2D::setMipmaps(const std::vector<Ref<ImageAsset>>& mipmaps) {
|
||||
mipmaps_ = mipmaps;
|
||||
}
|
||||
|
||||
ImageAsset* Texture2D::getImage() const {
|
||||
if (mipmaps_.empty()) return nullptr;
|
||||
return mipmaps_[0].get();
|
||||
}
|
||||
|
||||
void Texture2D::setImage(ImageAsset* image) {
|
||||
if (mipmaps_.empty()) {
|
||||
mipmaps_.push_back(Ref<ImageAsset>(image));
|
||||
} else {
|
||||
mipmaps_[0] = Ref<ImageAsset>(image);
|
||||
}
|
||||
|
||||
if (image) {
|
||||
width_ = image->getWidth();
|
||||
height_ = image->getHeight();
|
||||
format_ = image->getFormat();
|
||||
}
|
||||
}
|
||||
|
||||
u32 Texture2D::getWidth() const {
|
||||
return width_;
|
||||
}
|
||||
|
||||
u32 Texture2D::getHeight() const {
|
||||
return height_;
|
||||
}
|
||||
|
||||
PixelFormat Texture2D::getFormat() const {
|
||||
return format_;
|
||||
}
|
||||
|
||||
u32 Texture2D::getMipmapLevel() const {
|
||||
return mipmapLevel_;
|
||||
}
|
||||
|
||||
Filter Texture2D::getMinFilter() const {
|
||||
return minFilter_;
|
||||
}
|
||||
|
||||
void Texture2D::setMinFilter(Filter filter) {
|
||||
minFilter_ = filter;
|
||||
}
|
||||
|
||||
Filter Texture2D::getMagFilter() const {
|
||||
return magFilter_;
|
||||
}
|
||||
|
||||
void Texture2D::setMagFilter(Filter filter) {
|
||||
magFilter_ = filter;
|
||||
}
|
||||
|
||||
WrapMode Texture2D::getWrapModeS() const {
|
||||
return wrapModeS_;
|
||||
}
|
||||
|
||||
void Texture2D::setWrapModeS(WrapMode mode) {
|
||||
wrapModeS_ = mode;
|
||||
}
|
||||
|
||||
WrapMode Texture2D::getWrapModeT() const {
|
||||
return wrapModeT_;
|
||||
}
|
||||
|
||||
void Texture2D::setWrapModeT(WrapMode mode) {
|
||||
wrapModeT_ = mode;
|
||||
}
|
||||
|
||||
gfx::Texture* Texture2D::getGFXTexture() const {
|
||||
return gfxTexture_;
|
||||
}
|
||||
|
||||
void Texture2D::updateImage() {
|
||||
if (!gfxTexture_ || mipmaps_.empty()) return;
|
||||
|
||||
auto* image = mipmaps_[0].get();
|
||||
if (!image || !image->getData()) return;
|
||||
|
||||
uploadData(image->getData(), 0);
|
||||
}
|
||||
|
||||
void Texture2D::uploadData(const u8* data, u32 level) {
|
||||
if (!gfxTexture_ || !data) return;
|
||||
|
||||
gfx::BufferTextureCopy region;
|
||||
region.texExtentX = width_;
|
||||
region.texExtentY = height_;
|
||||
region.texExtentZ = 1;
|
||||
region.texBaseLevel = level;
|
||||
region.texLevelCount = 1;
|
||||
|
||||
device_->copyBuffersToTexture(&data, gfxTexture_, ®ion, 1);
|
||||
}
|
||||
|
||||
void Texture2D::onLoaded() {
|
||||
Asset::onLoaded();
|
||||
|
||||
if (!gfxTexture_ && width_ > 0 && height_ > 0) {
|
||||
createGFXTexture();
|
||||
}
|
||||
|
||||
updateImage();
|
||||
}
|
||||
|
||||
bool Texture2D::validate() const {
|
||||
return gfxTexture_ != nullptr && width_ > 0 && height_ > 0;
|
||||
}
|
||||
|
||||
void Texture2D::onDestroy() {
|
||||
mipmaps_.clear();
|
||||
|
||||
if (gfxTexture_) {
|
||||
gfxTexture_->release();
|
||||
gfxTexture_ = nullptr;
|
||||
}
|
||||
|
||||
Asset::onDestroy();
|
||||
}
|
||||
|
||||
void Texture2D::createGFXTexture() {
|
||||
if (!device_ || width_ == 0 || height_ == 0) return;
|
||||
|
||||
gfx::TextureInfo info;
|
||||
info.type = gfx::TextureType::Tex2D;
|
||||
info.usage = gfx::TextureUsage::Sampled | gfx::TextureUsage::TransferDst;
|
||||
info.width = width_;
|
||||
info.height = height_;
|
||||
info.mipLevels = mipmapLevel_;
|
||||
|
||||
ImageAsset tempImage;
|
||||
tempImage.setFormat(format_);
|
||||
info.format = tempImage.getGFXFormat();
|
||||
|
||||
gfxTexture_ = device_->createTexture(info);
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#include <extra2d/core/event/EventBus.h>
|
||||
|
||||
namespace extra2d {
|
||||
|
||||
EventBus* EventBus::instance_ = nullptr;
|
||||
|
||||
EventBus::EventBus() {}
|
||||
|
||||
EventBus::~EventBus() {
|
||||
clear();
|
||||
}
|
||||
|
||||
EventBus* EventBus::getInstance() {
|
||||
if (!instance_) {
|
||||
instance_ = new EventBus();
|
||||
}
|
||||
return instance_;
|
||||
}
|
||||
|
||||
void EventBus::clear() {
|
||||
for (auto& pair : listeners_) {
|
||||
for (auto& entry : pair.second) {
|
||||
delete entry.listener;
|
||||
}
|
||||
}
|
||||
listeners_.clear();
|
||||
}
|
||||
|
||||
} // namespace extra2d
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue