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:
ChestnutYueyue 2026-02-25 04:57:36 +08:00
parent 3e00b290d0
commit 5fddc4a209
144 changed files with 15860 additions and 11665 deletions

View File

@ -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** - 初始版本
- 资源加载和缓存
- 资源打包和加密
- 异步加载支持
- 多种压缩算法

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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_;
};
}

View File

@ -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>)>;
}

View File

@ -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-40
*/
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);
};
}

View File

@ -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);
};
}

View File

@ -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);
}
};
}

View File

@ -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
*
*
* ZstdLZ4 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);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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_;
};

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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 truefalse
*/
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 truefalse
*/
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 truefalse
*/
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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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); \
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
};
}

View File

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

View File

@ -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);
};
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
}
}

View File

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

View File

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

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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() {
}

View File

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

View File

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

View File

@ -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_, &region, 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

View File

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