feat(asset/render): 添加材质系统和资源类实现
实现新的材质系统包括Material、MaterialInstance和MaterialPropertyBlock类 添加多种资源类实现:TextureAsset、FontAsset、ShaderAsset、AudioAsset等 重构SpriteRenderer以支持材质系统 添加UniformValue和UniformInfo类用于材质参数传递 实现MSDF字体渲染器和文本渲染功能
This commit is contained in:
parent
174d7327ef
commit
70c2806c77
|
|
@ -6,10 +6,16 @@
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
// 前向声明
|
||||||
|
class TextureAsset;
|
||||||
|
class FontAsset;
|
||||||
|
class ShaderAsset;
|
||||||
|
class AudioAsset;
|
||||||
|
class DataAsset;
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Asset - 资源基类
|
// Asset - 资源基类
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
@ -95,383 +101,4 @@ protected:
|
||||||
friend class AssetService;
|
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
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/asset/asset.h>
|
||||||
#include <extra2d/asset/asset_types.h>
|
#include <extra2d/asset/asset_types.h>
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,13 @@
|
||||||
|
|
||||||
#include <extra2d/asset/asset.h>
|
#include <extra2d/asset/asset.h>
|
||||||
#include <extra2d/asset/asset_types.h>
|
#include <extra2d/asset/asset_types.h>
|
||||||
|
#include <extra2d/asset/texture_asset.h>
|
||||||
|
#include <extra2d/asset/font_asset.h>
|
||||||
|
#include <extra2d/asset/shader_asset.h>
|
||||||
|
#include <extra2d/asset/audio_asset.h>
|
||||||
|
#include <extra2d/asset/data_asset.h>
|
||||||
|
#include <extra2d/render/uniform_value.h>
|
||||||
|
#include <extra2d/render/render_types.h>
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -160,6 +167,7 @@ public:
|
||||||
* @brief 着色器加载器
|
* @brief 着色器加载器
|
||||||
*
|
*
|
||||||
* 加载着色器源文件,支持以下格式:
|
* 加载着色器源文件,支持以下格式:
|
||||||
|
* - .json: JSON 配置文件(推荐)
|
||||||
* - .vert/.frag: 分离的顶点/片段着色器
|
* - .vert/.frag: 分离的顶点/片段着色器
|
||||||
* - .glsl: 合并的着色器文件(使用标记分隔)
|
* - .glsl: 合并的着色器文件(使用标记分隔)
|
||||||
*/
|
*/
|
||||||
|
|
@ -185,19 +193,67 @@ public:
|
||||||
fragmentMarker_ = marker;
|
fragmentMarker_ = marker;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置资源根目录
|
||||||
|
* @param root 根目录路径
|
||||||
|
*/
|
||||||
|
void setRoot(const std::string &root) { root_ = root; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string vertexMarker_ = "[VERTEX]";
|
std::string vertexMarker_ = "[VERTEX]";
|
||||||
std::string fragmentMarker_ = "[FRAGMENT]";
|
std::string fragmentMarker_ = "[FRAGMENT]";
|
||||||
|
std::string root_;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 解析合并的着色器文件
|
* @brief 解析合并的着色器文件
|
||||||
* @param content 文件内容
|
|
||||||
* @param vertex 输出顶点着色器源码
|
|
||||||
* @param fragment 输出片段着色器源码
|
|
||||||
* @return 成功返回 true
|
|
||||||
*/
|
*/
|
||||||
bool parseCombined(const std::string &content, std::string &vertex,
|
bool parseCombined(const std::string &content, std::string &vertex,
|
||||||
std::string &fragment);
|
std::string &fragment);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 JSON 配置加载
|
||||||
|
*/
|
||||||
|
Ref<ShaderAsset> loadFromJson(const std::string &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 加载着色器源码
|
||||||
|
*/
|
||||||
|
std::string loadSource(const std::string &path);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 编译着色器
|
||||||
|
*/
|
||||||
|
GLuint compileShader(const std::string &source, GLenum type);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 链接程序
|
||||||
|
*/
|
||||||
|
GLuint linkProgram(GLuint vertexShader, GLuint fragmentShader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 提取 Uniform 信息
|
||||||
|
*/
|
||||||
|
void extractUniforms(GLuint program, ShaderAsset *asset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解析混合状态
|
||||||
|
*/
|
||||||
|
BlendState parseBlendState(const std::string &json);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解析深度状态
|
||||||
|
*/
|
||||||
|
DepthState parseDepthState(const std::string &json);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 类型字符串转 UniformType
|
||||||
|
*/
|
||||||
|
UniformType parseUniformType(const std::string &typeStr);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解析默认值
|
||||||
|
*/
|
||||||
|
UniformValue parseDefaultValue(const std::string &json, UniformType type);
|
||||||
};
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/asset/asset.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 释放音频数据
|
||||||
|
*/
|
||||||
|
void release();
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/asset/asset.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 释放数据
|
||||||
|
*/
|
||||||
|
void release();
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<u8> data_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/asset/asset.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,184 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/asset/asset.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 字符信息
|
||||||
|
*/
|
||||||
|
struct GlyphInfo {
|
||||||
|
char32_t codepoint = 0; ///< Unicode 码点
|
||||||
|
glm::vec2 uvMin; ///< UV 左下角
|
||||||
|
glm::vec2 uvMax; ///< UV 右上角
|
||||||
|
glm::vec2 size; ///< 字符尺寸(像素)
|
||||||
|
glm::vec2 bearing; ///< 基线偏移
|
||||||
|
float advance = 0.0f; ///< 前进宽度
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MSDF 字体资源
|
||||||
|
*
|
||||||
|
* 加载内嵌元数据的 MSDF PNG 图集
|
||||||
|
*/
|
||||||
|
class MSDFFontAsset : public Asset {
|
||||||
|
public:
|
||||||
|
MSDFFontAsset();
|
||||||
|
~MSDFFontAsset() override;
|
||||||
|
|
||||||
|
AssetType type() const override { return AssetType::Font; }
|
||||||
|
bool loaded() const override;
|
||||||
|
size_t memSize() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取图集纹理 ID
|
||||||
|
* @return OpenGL 纹理 ID
|
||||||
|
*/
|
||||||
|
GLuint textureId() const { return textureId_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取字符信息
|
||||||
|
* @param codepoint Unicode 码点
|
||||||
|
* @return 字符信息指针,不存在返回 nullptr
|
||||||
|
*/
|
||||||
|
const GlyphInfo* getGlyph(char32_t codepoint) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否有字符
|
||||||
|
* @param codepoint Unicode 码点
|
||||||
|
* @return 存在返回 true
|
||||||
|
*/
|
||||||
|
bool hasGlyph(char32_t codepoint) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取缺失的字符列表
|
||||||
|
* @param text 文本
|
||||||
|
* @return 缺失字符列表
|
||||||
|
*/
|
||||||
|
std::vector<char32_t> getMissingChars(const std::u32string& text) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查文本是否可渲染
|
||||||
|
* @param text 文本
|
||||||
|
* @return 可渲染返回 true
|
||||||
|
*/
|
||||||
|
bool canRender(const std::u32string& text) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 计算文本尺寸
|
||||||
|
* @param text 文本
|
||||||
|
* @param scale 缩放比例
|
||||||
|
* @return 文本尺寸
|
||||||
|
*/
|
||||||
|
glm::vec2 measureText(const std::u32string& text, float scale = 1.0f) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取字体大小
|
||||||
|
* @return 字体大小(像素)
|
||||||
|
*/
|
||||||
|
int fontSize() const { return fontSize_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取像素范围
|
||||||
|
* @return 像素范围
|
||||||
|
*/
|
||||||
|
float pxRange() const { return pxRange_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取图集宽度
|
||||||
|
* @return 宽度
|
||||||
|
*/
|
||||||
|
int atlasWidth() const { return atlasWidth_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取图集高度
|
||||||
|
* @return 高度
|
||||||
|
*/
|
||||||
|
int atlasHeight() const { return atlasHeight_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取行高
|
||||||
|
* @return 行高
|
||||||
|
*/
|
||||||
|
int lineHeight() const { return lineHeight_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取基线
|
||||||
|
* @return 基线
|
||||||
|
*/
|
||||||
|
int baseline() const { return baseline_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置纹理 ID
|
||||||
|
* @param id 纹理 ID
|
||||||
|
*/
|
||||||
|
void setTextureId(GLuint id) { textureId_ = id; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置字体大小
|
||||||
|
* @param size 字体大小
|
||||||
|
*/
|
||||||
|
void setFontSize(int size) { fontSize_ = size; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置像素范围
|
||||||
|
* @param range 像素范围
|
||||||
|
*/
|
||||||
|
void setPxRange(float range) { pxRange_ = range; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置图集尺寸
|
||||||
|
* @param width 宽度
|
||||||
|
* @param height 高度
|
||||||
|
*/
|
||||||
|
void setAtlasSize(int width, int height) {
|
||||||
|
atlasWidth_ = width;
|
||||||
|
atlasHeight_ = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置行高
|
||||||
|
* @param height 行高
|
||||||
|
*/
|
||||||
|
void setLineHeight(int height) { lineHeight_ = height; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置基线
|
||||||
|
* @param baseline 基线
|
||||||
|
*/
|
||||||
|
void setBaseline(int baseline) { baseline_ = baseline; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 添加字符信息
|
||||||
|
* @param glyph 字符信息
|
||||||
|
*/
|
||||||
|
void addGlyph(const GlyphInfo& glyph);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 标记为已加载
|
||||||
|
*/
|
||||||
|
void markLoaded();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 释放资源
|
||||||
|
*/
|
||||||
|
void release();
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint textureId_ = 0;
|
||||||
|
int fontSize_ = 48;
|
||||||
|
float pxRange_ = 4.0f;
|
||||||
|
int atlasWidth_ = 2048;
|
||||||
|
int atlasHeight_ = 2048;
|
||||||
|
int lineHeight_ = 60;
|
||||||
|
int baseline_ = 12;
|
||||||
|
std::unordered_map<char32_t, GlyphInfo> glyphs_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/asset/asset_loader.h>
|
||||||
|
#include <extra2d/asset/msdf_font_asset.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MSDF 字体加载器
|
||||||
|
*
|
||||||
|
* 加载内嵌元数据的 MSDF PNG 图集
|
||||||
|
*/
|
||||||
|
class MSDFFontLoader : public AssetLoader<MSDFFontAsset> {
|
||||||
|
public:
|
||||||
|
Ref<MSDFFontAsset> load(const std::string& path) override;
|
||||||
|
Ref<MSDFFontAsset> 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;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief 解析 PNG 内嵌元数据
|
||||||
|
*/
|
||||||
|
bool parseEmbeddedMetadata(const std::string& path, MSDFFontAsset* asset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 tEXt chunk 读取数据
|
||||||
|
*/
|
||||||
|
bool readTextChunk(const std::vector<u8>& pngData,
|
||||||
|
const std::string& keyword,
|
||||||
|
std::vector<u8>& output);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解析 JSON 元数据
|
||||||
|
*/
|
||||||
|
bool parseJsonMetadata(const std::string& json, MSDFFontAsset* asset);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,158 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/asset/asset.h>
|
||||||
|
#include <extra2d/render/uniform_value.h>
|
||||||
|
#include <extra2d/render/uniform_info.h>
|
||||||
|
#include <extra2d/render/render_types.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// 前向声明
|
||||||
|
class Material;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 着色器资源(增强版)
|
||||||
|
*
|
||||||
|
* 管理 OpenGL 着色器程序,支持:
|
||||||
|
* - 顶点和片段着色器编译
|
||||||
|
* - Uniform 信息提取
|
||||||
|
* - 默认渲染状态
|
||||||
|
* - 材质模板创建
|
||||||
|
*/
|
||||||
|
class ShaderAsset : public Asset {
|
||||||
|
public:
|
||||||
|
ShaderAsset();
|
||||||
|
~ShaderAsset() override;
|
||||||
|
|
||||||
|
AssetType type() const override { return AssetType::Shader; }
|
||||||
|
bool loaded() const override;
|
||||||
|
size_t memSize() const override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 OpenGL 程序 ID
|
||||||
|
* @return 程序 ID
|
||||||
|
*/
|
||||||
|
GLuint programId() const { return programId_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 Uniform 信息
|
||||||
|
* @param name Uniform 名称
|
||||||
|
* @return Uniform 信息指针,不存在返回 nullptr
|
||||||
|
*/
|
||||||
|
const UniformInfo* getUniformInfo(const std::string& name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 Uniform 位置
|
||||||
|
* @param name Uniform 名称
|
||||||
|
* @return Uniform 位置
|
||||||
|
*/
|
||||||
|
GLint getUniformLocation(const std::string& name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取所有 uniforms
|
||||||
|
* @return Uniform 映射表
|
||||||
|
*/
|
||||||
|
const std::unordered_map<std::string, UniformInfo>& uniforms() const {
|
||||||
|
return uniforms_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取默认混合状态
|
||||||
|
* @return 混合状态
|
||||||
|
*/
|
||||||
|
const BlendState& blendState() const { return blendState_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取默认深度状态
|
||||||
|
* @return 深度状态
|
||||||
|
*/
|
||||||
|
const DepthState& depthState() const { return depthState_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用渲染状态
|
||||||
|
*/
|
||||||
|
void applyStates() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 使用着色器
|
||||||
|
*/
|
||||||
|
void bind() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 解除着色器绑定
|
||||||
|
*/
|
||||||
|
void unbind() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建材质模板
|
||||||
|
* @return 材质引用
|
||||||
|
*/
|
||||||
|
Ref<Material> createMaterial();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 OpenGL 程序(由 ShaderLoader 调用)
|
||||||
|
* @param programId 程序 ID
|
||||||
|
*/
|
||||||
|
void setProgram(GLuint programId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 添加 Uniform 信息(由 ShaderLoader 调用)
|
||||||
|
* @param info Uniform 信息
|
||||||
|
*/
|
||||||
|
void addUniform(const UniformInfo& info);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置混合状态
|
||||||
|
* @param state 混合状态
|
||||||
|
*/
|
||||||
|
void setBlendState(const BlendState& state) { blendState_ = state; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置深度状态
|
||||||
|
* @param state 深度状态
|
||||||
|
*/
|
||||||
|
void setDepthState(const DepthState& state) { depthState_ = state; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 标记为已加载
|
||||||
|
*/
|
||||||
|
void markLoaded();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 释放资源
|
||||||
|
*/
|
||||||
|
void release();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
private:
|
||||||
|
GLuint programId_ = 0;
|
||||||
|
std::unordered_map<std::string, UniformInfo> uniforms_;
|
||||||
|
std::unordered_map<std::string, GLint> uniformLocations_;
|
||||||
|
BlendState blendState_;
|
||||||
|
DepthState depthState_;
|
||||||
|
std::string vertexSrc_;
|
||||||
|
std::string fragmentSrc_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/asset/asset.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 释放纹理数据
|
||||||
|
*/
|
||||||
|
void release();
|
||||||
|
|
||||||
|
private:
|
||||||
|
int width_ = 0;
|
||||||
|
int height_ = 0;
|
||||||
|
int channels_ = 0;
|
||||||
|
Unique<u8[]> data_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/asset/shader_asset.h>
|
||||||
|
#include <extra2d/render/uniform_value.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// 前向声明
|
||||||
|
class MaterialInstance;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 材质模板
|
||||||
|
*
|
||||||
|
* ShaderAsset 的参数配置,可被多个对象共享。
|
||||||
|
* 定义了着色器的默认参数值。
|
||||||
|
*/
|
||||||
|
class Material : public std::enable_shared_from_this<Material> {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 构造函数
|
||||||
|
* @param shader 着色器资源
|
||||||
|
*/
|
||||||
|
explicit Material(Ref<ShaderAsset> shader);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 float 属性
|
||||||
|
*/
|
||||||
|
void setFloat(const std::string& name, float value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 vec2 属性
|
||||||
|
*/
|
||||||
|
void setVec2(const std::string& name, const glm::vec2& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 vec3 属性
|
||||||
|
*/
|
||||||
|
void setVec3(const std::string& name, const glm::vec3& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 vec4 属性
|
||||||
|
*/
|
||||||
|
void setVec4(const std::string& name, const glm::vec4& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 mat4 属性
|
||||||
|
*/
|
||||||
|
void setMat4(const std::string& name, const glm::mat4& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 int 属性
|
||||||
|
*/
|
||||||
|
void setInt(const std::string& name, int value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 bool 属性
|
||||||
|
*/
|
||||||
|
void setBool(const std::string& name, bool value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置纹理
|
||||||
|
*/
|
||||||
|
void setTexture(const std::string& name, GLuint textureId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置颜色
|
||||||
|
*/
|
||||||
|
void setColor(const std::string& name, const Color& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取属性
|
||||||
|
* @param name 属性名
|
||||||
|
* @return 属性值指针,不存在返回 nullptr
|
||||||
|
*/
|
||||||
|
const UniformValue* getProperty(const std::string& name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否有属性
|
||||||
|
* @param name 属性名
|
||||||
|
* @return 存在返回 true
|
||||||
|
*/
|
||||||
|
bool hasProperty(const std::string& name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取着色器
|
||||||
|
* @return 着色器指针
|
||||||
|
*/
|
||||||
|
ShaderAsset* shader() const { return shader_.get(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取程序 ID
|
||||||
|
* @return OpenGL 程序 ID
|
||||||
|
*/
|
||||||
|
GLuint programId() const { return shader_ ? shader_->programId() : 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 创建实例
|
||||||
|
* @return 材质实例
|
||||||
|
*/
|
||||||
|
Ref<MaterialInstance> createInstance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用材质(设置所有属性到着色器)
|
||||||
|
*/
|
||||||
|
void apply() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取所有属性
|
||||||
|
* @return 属性映射表
|
||||||
|
*/
|
||||||
|
const std::unordered_map<std::string, UniformValue>& properties() const {
|
||||||
|
return properties_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ref<ShaderAsset> shader_;
|
||||||
|
std::unordered_map<std::string, UniformValue> properties_;
|
||||||
|
std::unordered_map<std::string, GLuint> textures_;
|
||||||
|
mutable std::unordered_map<std::string, GLint> cachedLocations_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 Uniform 位置
|
||||||
|
*/
|
||||||
|
GLint getLocation(const std::string& name) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,161 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/render/material.h>
|
||||||
|
#include <extra2d/render/uniform_value.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 材质实例
|
||||||
|
*
|
||||||
|
* Material 的实例,可覆盖属性值。
|
||||||
|
* 类似 Unity 的 MaterialPropertyBlock。
|
||||||
|
*/
|
||||||
|
class MaterialInstance {
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief 构造函数
|
||||||
|
* @param material 材质模板
|
||||||
|
*/
|
||||||
|
explicit MaterialInstance(Ref<Material> material);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 覆盖 float 属性
|
||||||
|
*/
|
||||||
|
void setFloat(const std::string& name, float value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 覆盖 vec2 属性
|
||||||
|
*/
|
||||||
|
void setVec2(const std::string& name, const glm::vec2& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 覆盖 vec3 属性
|
||||||
|
*/
|
||||||
|
void setVec3(const std::string& name, const glm::vec3& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 覆盖 vec4 属性
|
||||||
|
*/
|
||||||
|
void setVec4(const std::string& name, const glm::vec4& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 覆盖 mat4 属性
|
||||||
|
*/
|
||||||
|
void setMat4(const std::string& name, const glm::mat4& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 覆盖 int 属性
|
||||||
|
*/
|
||||||
|
void setInt(const std::string& name, int value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 覆盖 bool 属性
|
||||||
|
*/
|
||||||
|
void setBool(const std::string& name, bool value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 覆盖纹理
|
||||||
|
*/
|
||||||
|
void setTexture(const std::string& name, GLuint textureId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 覆盖颜色
|
||||||
|
*/
|
||||||
|
void setColor(const std::string& name, const Color& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置模型矩阵
|
||||||
|
*/
|
||||||
|
void setModelMatrix(const glm::mat4& model);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置视图矩阵
|
||||||
|
*/
|
||||||
|
void setViewMatrix(const glm::mat4& view);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置投影矩阵
|
||||||
|
*/
|
||||||
|
void setProjectionMatrix(const glm::mat4& projection);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 MVP 矩阵
|
||||||
|
*/
|
||||||
|
void setMVP(const glm::mat4& mvp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置颜色(快捷方法)
|
||||||
|
*/
|
||||||
|
void setColor(const Color& color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取覆盖值
|
||||||
|
* @param name 属性名
|
||||||
|
* @return 覆盖值指针,不存在返回 nullptr
|
||||||
|
*/
|
||||||
|
const UniformValue* getOverride(const std::string& name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否有覆盖
|
||||||
|
* @param name 属性名
|
||||||
|
* @return 存在返回 true
|
||||||
|
*/
|
||||||
|
bool hasOverride(const std::string& name) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清除覆盖
|
||||||
|
* @param name 属性名
|
||||||
|
*/
|
||||||
|
void clearOverride(const std::string& name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清除所有覆盖
|
||||||
|
*/
|
||||||
|
void clearAllOverrides();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取材质
|
||||||
|
* @return 材质指针
|
||||||
|
*/
|
||||||
|
Material* material() const { return material_.get(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取着色器
|
||||||
|
* @return 着色器指针
|
||||||
|
*/
|
||||||
|
ShaderAsset* shader() const { return material_ ? material_->shader() : nullptr; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取程序 ID
|
||||||
|
* @return OpenGL 程序 ID
|
||||||
|
*/
|
||||||
|
GLuint programId() const { return material_ ? material_->programId() : 0; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用实例(先应用材质,再应用覆盖)
|
||||||
|
*/
|
||||||
|
void apply() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否有覆盖
|
||||||
|
* @return 有覆盖返回 true
|
||||||
|
*/
|
||||||
|
bool hasOverrides() const { return !overrides_.empty(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ref<Material> material_;
|
||||||
|
std::unordered_map<std::string, UniformValue> overrides_;
|
||||||
|
std::unordered_map<std::string, GLuint> textureOverrides_;
|
||||||
|
mutable std::unordered_map<std::string, GLint> cachedLocations_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 Uniform 位置
|
||||||
|
*/
|
||||||
|
GLint getLocation(const std::string& name) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,152 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/render/uniform_value.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
// 前向声明
|
||||||
|
class ShaderAsset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 材质属性块
|
||||||
|
*
|
||||||
|
* 用于快速设置每对象属性,避免创建 MaterialInstance。
|
||||||
|
* 类似 Unity 的 MaterialPropertyBlock。
|
||||||
|
*
|
||||||
|
* 使用场景:
|
||||||
|
* - 批量渲染时设置每实例不同的属性
|
||||||
|
* - 临时修改属性而不影响材质
|
||||||
|
*/
|
||||||
|
class MaterialPropertyBlock {
|
||||||
|
public:
|
||||||
|
MaterialPropertyBlock() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 float 属性
|
||||||
|
*/
|
||||||
|
void setFloat(const std::string& name, float value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 vec2 属性
|
||||||
|
*/
|
||||||
|
void setVec2(const std::string& name, const glm::vec2& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 vec3 属性
|
||||||
|
*/
|
||||||
|
void setVec3(const std::string& name, const glm::vec3& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 vec4 属性
|
||||||
|
*/
|
||||||
|
void setVec4(const std::string& name, const glm::vec4& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 mat4 属性
|
||||||
|
*/
|
||||||
|
void setMat4(const std::string& name, const glm::mat4& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 int 属性
|
||||||
|
*/
|
||||||
|
void setInt(const std::string& name, int value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 bool 属性
|
||||||
|
*/
|
||||||
|
void setBool(const std::string& name, bool value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置纹理
|
||||||
|
* @param name 属性名
|
||||||
|
* @param textureId 纹理 ID
|
||||||
|
* @param unit 纹理单元(-1 表示自动分配)
|
||||||
|
*/
|
||||||
|
void setTexture(const std::string& name, GLuint textureId, int unit = -1);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置颜色
|
||||||
|
*/
|
||||||
|
void setColor(const std::string& name, const Color& value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置模型矩阵
|
||||||
|
*/
|
||||||
|
void setModelMatrix(const glm::mat4& model);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置视图矩阵
|
||||||
|
*/
|
||||||
|
void setViewMatrix(const glm::mat4& view);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置投影矩阵
|
||||||
|
*/
|
||||||
|
void setProjectionMatrix(const glm::mat4& projection);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置 MVP 矩阵
|
||||||
|
*/
|
||||||
|
void setMVP(const glm::mat4& mvp);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置颜色(快捷方法)
|
||||||
|
*/
|
||||||
|
void setColor(const Color& color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用到着色器
|
||||||
|
* @param shader 着色器资源
|
||||||
|
*/
|
||||||
|
void apply(ShaderAsset* shader) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用到着色器程序
|
||||||
|
* @param programId 程序 ID
|
||||||
|
*/
|
||||||
|
void apply(GLuint programId) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 清除所有属性
|
||||||
|
*/
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否为空
|
||||||
|
* @return 为空返回 true
|
||||||
|
*/
|
||||||
|
bool isEmpty() const { return properties_.empty() && textures_.empty(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取属性数量
|
||||||
|
*/
|
||||||
|
size_t propertyCount() const { return properties_.size(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取纹理数量
|
||||||
|
*/
|
||||||
|
size_t textureCount() const { return textures_.size(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct TextureBinding {
|
||||||
|
std::string name;
|
||||||
|
GLuint textureId;
|
||||||
|
int unit;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unordered_map<std::string, UniformValue> properties_;
|
||||||
|
std::vector<TextureBinding> textures_;
|
||||||
|
mutable std::unordered_map<std::string, GLint> cachedLocations_;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取 Uniform 位置
|
||||||
|
*/
|
||||||
|
GLint getLocation(GLuint programId, const std::string& name) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/asset/msdf_font_asset.h>
|
||||||
|
#include <extra2d/render/material.h>
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief MSDF 文本渲染器
|
||||||
|
*
|
||||||
|
* 支持任意大小的字体渲染
|
||||||
|
*/
|
||||||
|
class MSDFTextRenderer {
|
||||||
|
public:
|
||||||
|
MSDFTextRenderer();
|
||||||
|
~MSDFTextRenderer();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 初始化
|
||||||
|
* @return 成功返回 true
|
||||||
|
*/
|
||||||
|
bool init();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 关闭
|
||||||
|
*/
|
||||||
|
void shutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置字体
|
||||||
|
* @param font 字体资源
|
||||||
|
*/
|
||||||
|
void setFont(Ref<MSDFFontAsset> font) { font_ = font; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取字体
|
||||||
|
* @return 字体资源
|
||||||
|
*/
|
||||||
|
Ref<MSDFFontAsset> font() const { return font_; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置文本
|
||||||
|
* @param text UTF-32 文本
|
||||||
|
*/
|
||||||
|
void setText(const std::u32string& text) { text_ = text; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置文本(UTF-8)
|
||||||
|
* @param utf8Text UTF-8 文本
|
||||||
|
*/
|
||||||
|
void setText(const std::string& utf8Text);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置位置
|
||||||
|
* @param pos 位置
|
||||||
|
*/
|
||||||
|
void setPosition(const glm::vec2& pos) { position_ = pos; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置字体大小
|
||||||
|
* @param size 目标像素高度
|
||||||
|
*/
|
||||||
|
void setFontSize(float size);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置颜色
|
||||||
|
* @param color 颜色
|
||||||
|
*/
|
||||||
|
void setColor(const Color& color) { color_ = color; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置材质
|
||||||
|
* @param material 材质实例
|
||||||
|
*/
|
||||||
|
void setMaterial(Ref<MaterialInstance> material) { material_ = material; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取文本尺寸
|
||||||
|
* @return 尺寸
|
||||||
|
*/
|
||||||
|
glm::vec2 getSize() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 渲染
|
||||||
|
* @param projection 投影矩阵
|
||||||
|
*/
|
||||||
|
void render(const glm::mat4& projection);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ref<MSDFFontAsset> font_;
|
||||||
|
std::u32string text_;
|
||||||
|
glm::vec2 position_ = glm::vec2(0.0f);
|
||||||
|
float fontSize_ = 48.0f;
|
||||||
|
float scale_ = 1.0f;
|
||||||
|
Color color_ = Colors::White;
|
||||||
|
Ref<MaterialInstance> material_;
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
glm::vec2 position;
|
||||||
|
glm::vec2 texCoord;
|
||||||
|
};
|
||||||
|
std::vector<Vertex> vertices_;
|
||||||
|
std::vector<uint32_t> indices_;
|
||||||
|
|
||||||
|
GLuint vao_ = 0;
|
||||||
|
GLuint vbo_ = 0;
|
||||||
|
GLuint ibo_ = 0;
|
||||||
|
GLuint shader_ = 0;
|
||||||
|
|
||||||
|
void updateGeometry();
|
||||||
|
void draw();
|
||||||
|
bool createShader();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <extra2d/render/render_types.h>
|
|
||||||
#include <extra2d/render/render_context.h>
|
|
||||||
#include <extra2d/render/buffer.h>
|
#include <extra2d/render/buffer.h>
|
||||||
|
#include <extra2d/render/material.h>
|
||||||
|
#include <extra2d/render/material_instance.h>
|
||||||
|
#include <extra2d/render/material_property_block.h>
|
||||||
|
#include <extra2d/render/render_context.h>
|
||||||
|
#include <extra2d/render/render_types.h>
|
||||||
#include <extra2d/render/vao.h>
|
#include <extra2d/render/vao.h>
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
#include <glm/mat4x4.hpp>
|
#include <glm/mat4x4.hpp>
|
||||||
|
|
@ -37,7 +40,7 @@ struct SpriteData {
|
||||||
/**
|
/**
|
||||||
* @brief 精灵渲染器
|
* @brief 精灵渲染器
|
||||||
*
|
*
|
||||||
* 高性能批量精灵渲染器
|
* 高性能批量精灵渲染器,支持 Material 系统
|
||||||
*/
|
*/
|
||||||
class SpriteRenderer {
|
class SpriteRenderer {
|
||||||
public:
|
public:
|
||||||
|
|
@ -60,16 +63,47 @@ public:
|
||||||
*/
|
*/
|
||||||
void shutdown();
|
void shutdown();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 设置材质
|
||||||
|
* @param material 材质模板
|
||||||
|
*/
|
||||||
|
void setMaterial(Ref<Material> material) { material_ = material; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取材质
|
||||||
|
* @return 材质模板
|
||||||
|
*/
|
||||||
|
Ref<Material> material() const { return material_; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 开始批量渲染
|
* @brief 开始批量渲染
|
||||||
*/
|
*/
|
||||||
void begin(const glm::mat4 &viewProjection);
|
void begin(const glm::mat4 &viewProjection);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 绘制精灵
|
* @brief 绘制精灵(使用默认材质)
|
||||||
|
* @param texture 纹理 ID
|
||||||
|
* @param data 精灵数据
|
||||||
*/
|
*/
|
||||||
void draw(GLuint texture, const SpriteData &data);
|
void draw(GLuint texture, const SpriteData &data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绘制精灵(使用材质实例)
|
||||||
|
* @param texture 纹理 ID
|
||||||
|
* @param data 精灵数据
|
||||||
|
* @param instance 材质实例
|
||||||
|
*/
|
||||||
|
void draw(GLuint texture, const SpriteData &data, MaterialInstance *instance);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 绘制精灵(使用属性块)
|
||||||
|
* @param texture 纹理 ID
|
||||||
|
* @param data 精灵数据
|
||||||
|
* @param block 属性块
|
||||||
|
*/
|
||||||
|
void draw(GLuint texture, const SpriteData &data,
|
||||||
|
const MaterialPropertyBlock &block);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief 结束批量渲染
|
* @brief 结束批量渲染
|
||||||
*/
|
*/
|
||||||
|
|
@ -93,11 +127,15 @@ private:
|
||||||
GLuint currentTexture_ = 0;
|
GLuint currentTexture_ = 0;
|
||||||
glm::mat4 viewProjection_;
|
glm::mat4 viewProjection_;
|
||||||
|
|
||||||
|
Ref<Material> material_;
|
||||||
|
MaterialInstance *currentInstance_ = nullptr;
|
||||||
|
|
||||||
uint32 drawCalls_ = 0;
|
uint32 drawCalls_ = 0;
|
||||||
uint32 spriteCount_ = 0;
|
uint32 spriteCount_ = 0;
|
||||||
|
|
||||||
bool createShader();
|
bool createShader();
|
||||||
void flush();
|
void flush();
|
||||||
|
void flushWithMaterial();
|
||||||
void addQuad(const SpriteData &data, GLuint texture);
|
void addQuad(const SpriteData &data, GLuint texture);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/render/uniform_value.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Uniform 元信息
|
||||||
|
*
|
||||||
|
* 描述着色器中的 uniform 变量信息
|
||||||
|
*/
|
||||||
|
struct UniformInfo {
|
||||||
|
std::string name; ///< Uniform 名称
|
||||||
|
UniformType type; ///< 数据类型
|
||||||
|
GLint location = -1; ///< OpenGL 位置
|
||||||
|
UniformValue defaultValue; ///< 默认值
|
||||||
|
bool isArray = false; ///< 是否为数组
|
||||||
|
size_t arraySize = 1; ///< 数组大小
|
||||||
|
|
||||||
|
UniformInfo() : type(UniformType::None) {}
|
||||||
|
|
||||||
|
UniformInfo(const std::string& n, UniformType t, GLint loc = -1)
|
||||||
|
: name(n), type(t), location(loc) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,171 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <extra2d/core/types.h>
|
||||||
|
#include <extra2d/core/color.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <variant>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Uniform 值类型枚举
|
||||||
|
*/
|
||||||
|
enum class UniformType : u8 {
|
||||||
|
None = 0,
|
||||||
|
Float,
|
||||||
|
Vec2,
|
||||||
|
Vec3,
|
||||||
|
Vec4,
|
||||||
|
Mat2,
|
||||||
|
Mat3,
|
||||||
|
Mat4,
|
||||||
|
Int,
|
||||||
|
IVec2,
|
||||||
|
IVec3,
|
||||||
|
IVec4,
|
||||||
|
Bool,
|
||||||
|
Sampler2D,
|
||||||
|
SamplerCube
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Uniform 值变体类型
|
||||||
|
*/
|
||||||
|
using UniformValueVariant = std::variant<
|
||||||
|
std::monostate,
|
||||||
|
float,
|
||||||
|
glm::vec2,
|
||||||
|
glm::vec3,
|
||||||
|
glm::vec4,
|
||||||
|
glm::mat2,
|
||||||
|
glm::mat3,
|
||||||
|
glm::mat4,
|
||||||
|
int,
|
||||||
|
glm::ivec2,
|
||||||
|
glm::ivec3,
|
||||||
|
glm::ivec4,
|
||||||
|
bool,
|
||||||
|
GLuint
|
||||||
|
>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Uniform 值封装类
|
||||||
|
*
|
||||||
|
* 支持多种数据类型的统一封装,用于材质参数传递
|
||||||
|
*/
|
||||||
|
class UniformValue {
|
||||||
|
public:
|
||||||
|
UniformValue() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 float 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(float v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 vec2 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(const glm::vec2& v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 vec3 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(const glm::vec3& v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 vec4 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(const glm::vec4& v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 mat2 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(const glm::mat2& v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 mat3 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(const glm::mat3& v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 mat4 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(const glm::mat4& v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 int 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(int v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 ivec2 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(const glm::ivec2& v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 ivec3 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(const glm::ivec3& v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 ivec4 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(const glm::ivec4& v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 bool 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(bool v);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从纹理 ID 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(GLuint textureId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 从 Color 构造
|
||||||
|
*/
|
||||||
|
explicit UniformValue(const Color& color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取类型
|
||||||
|
* @return Uniform 类型
|
||||||
|
*/
|
||||||
|
UniformType type() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取值指针
|
||||||
|
* @tparam T 值类型
|
||||||
|
* @return 值指针,类型不匹配返回 nullptr
|
||||||
|
*/
|
||||||
|
template<typename T>
|
||||||
|
const T* get() const {
|
||||||
|
return std::get_if<T>(&value_);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 应用到着色器
|
||||||
|
* @param location Uniform 位置
|
||||||
|
*/
|
||||||
|
void apply(GLint location) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 检查是否为空
|
||||||
|
* @return 为空返回 true
|
||||||
|
*/
|
||||||
|
bool isEmpty() const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief 获取类型名称
|
||||||
|
* @param type Uniform 类型
|
||||||
|
* @return 类型名称字符串
|
||||||
|
*/
|
||||||
|
static const char* typeName(UniformType type);
|
||||||
|
|
||||||
|
private:
|
||||||
|
UniformValueVariant value_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -8,6 +8,11 @@
|
||||||
#include <extra2d/asset/asset_pack.h>
|
#include <extra2d/asset/asset_pack.h>
|
||||||
#include <extra2d/asset/asset_types.h>
|
#include <extra2d/asset/asset_types.h>
|
||||||
#include <extra2d/asset/data_processor.h>
|
#include <extra2d/asset/data_processor.h>
|
||||||
|
#include <extra2d/asset/texture_asset.h>
|
||||||
|
#include <extra2d/asset/font_asset.h>
|
||||||
|
#include <extra2d/asset/shader_asset.h>
|
||||||
|
#include <extra2d/asset/audio_asset.h>
|
||||||
|
#include <extra2d/asset/data_asset.h>
|
||||||
#include <extra2d/core/service_interface.h>
|
#include <extra2d/core/service_interface.h>
|
||||||
#include <extra2d/core/types.h>
|
#include <extra2d/core/types.h>
|
||||||
#include <extra2d/services/logger_service.h>
|
#include <extra2d/services/logger_service.h>
|
||||||
|
|
|
||||||
|
|
@ -1,63 +1,8 @@
|
||||||
#include <extra2d/asset/asset.h>
|
#include <extra2d/asset/asset.h>
|
||||||
|
|
||||||
#define STB_TRUETYPE_IMPLEMENTATION
|
|
||||||
#include <stb/stb_truetype.h>
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// Asset 基类目前没有需要单独实现的方法
|
||||||
// FontAsset::Impl - Pimpl 实现类
|
// 所有资源类型的实现都在各自的文件中
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
class FontAsset::Impl {
|
|
||||||
public:
|
|
||||||
stbtt_fontinfo info;
|
|
||||||
bool initialized = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
// FontAsset 实现
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
FontAsset::FontAsset() : impl_(ptr::unique<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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
#include <extra2d/asset/asset_loader.h>
|
#include <extra2d/asset/asset_loader.h>
|
||||||
#include <extra2d/services/logger_service.h>
|
#include <extra2d/services/logger_service.h>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <json/json.hpp>
|
||||||
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include <stb/stb_image.h>
|
#include <stb/stb_image.h>
|
||||||
|
|
@ -166,6 +167,12 @@ std::vector<std::string> FontLoader::extensions() const {
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
Ref<ShaderAsset> ShaderLoader::load(const std::string &path) {
|
Ref<ShaderAsset> ShaderLoader::load(const std::string &path) {
|
||||||
|
std::string ext = getExtension(path);
|
||||||
|
|
||||||
|
if (ext == ".json") {
|
||||||
|
return loadFromJson(path);
|
||||||
|
}
|
||||||
|
|
||||||
auto data = readFile(path);
|
auto data = readFile(path);
|
||||||
if (data.empty()) {
|
if (data.empty()) {
|
||||||
E2D_ERROR(CAT_ASSET, "Failed to read shader file: {}", path);
|
E2D_ERROR(CAT_ASSET, "Failed to read shader file: {}", path);
|
||||||
|
|
@ -174,6 +181,247 @@ Ref<ShaderAsset> ShaderLoader::load(const std::string &path) {
|
||||||
return loadFromMemory(data.data(), data.size());
|
return loadFromMemory(data.data(), data.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ref<ShaderAsset> ShaderLoader::loadFromJson(const std::string &path) {
|
||||||
|
auto data = readFile(path);
|
||||||
|
if (data.empty()) {
|
||||||
|
E2D_ERROR(CAT_ASSET, "Failed to read shader config: {}", path);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string content(reinterpret_cast<const char*>(data.data()), data.size());
|
||||||
|
|
||||||
|
try {
|
||||||
|
auto json = nlohmann::json::parse(content);
|
||||||
|
|
||||||
|
std::string vertexPath = json.value("vertex", "");
|
||||||
|
std::string fragmentPath = json.value("fragment", "");
|
||||||
|
|
||||||
|
if (vertexPath.empty() || fragmentPath.empty()) {
|
||||||
|
E2D_ERROR(CAT_ASSET, "Shader config missing vertex or fragment path");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string basePath;
|
||||||
|
size_t lastSlash = path.rfind('/');
|
||||||
|
if (lastSlash != std::string::npos) {
|
||||||
|
basePath = path.substr(0, lastSlash + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string vertexSrc = loadSource(basePath + vertexPath);
|
||||||
|
std::string fragmentSrc = loadSource(basePath + fragmentPath);
|
||||||
|
|
||||||
|
if (vertexSrc.empty() || fragmentSrc.empty()) {
|
||||||
|
E2D_ERROR(CAT_ASSET, "Failed to load shader sources");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint vertexShader = compileShader(vertexSrc, GL_VERTEX_SHADER);
|
||||||
|
GLuint fragmentShader = compileShader(fragmentSrc, GL_FRAGMENT_SHADER);
|
||||||
|
|
||||||
|
if (vertexShader == 0 || fragmentShader == 0) {
|
||||||
|
if (vertexShader) glDeleteShader(vertexShader);
|
||||||
|
if (fragmentShader) glDeleteShader(fragmentShader);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint program = linkProgram(vertexShader, fragmentShader);
|
||||||
|
|
||||||
|
glDeleteShader(vertexShader);
|
||||||
|
glDeleteShader(fragmentShader);
|
||||||
|
|
||||||
|
if (program == 0) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto asset = ptr::make<ShaderAsset>();
|
||||||
|
asset->setProgram(program);
|
||||||
|
asset->setSource(std::move(vertexSrc), std::move(fragmentSrc));
|
||||||
|
|
||||||
|
extractUniforms(program, asset.get());
|
||||||
|
|
||||||
|
if (json.contains("states")) {
|
||||||
|
auto states = json["states"];
|
||||||
|
if (states.contains("blend")) {
|
||||||
|
asset->setBlendState(parseBlendState(states["blend"].dump()));
|
||||||
|
}
|
||||||
|
if (states.contains("depth")) {
|
||||||
|
asset->setDepthState(parseDepthState(states["depth"].dump()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
asset->markLoaded();
|
||||||
|
return asset;
|
||||||
|
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
E2D_ERROR(CAT_ASSET, "Failed to parse shader config: {}", e.what());
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ShaderLoader::loadSource(const std::string &path) {
|
||||||
|
auto data = readFile(path);
|
||||||
|
if (data.empty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return std::string(reinterpret_cast<const char*>(data.data()), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint ShaderLoader::compileShader(const std::string &source, GLenum type) {
|
||||||
|
GLuint shader = glCreateShader(type);
|
||||||
|
const char* src = source.c_str();
|
||||||
|
glShaderSource(shader, 1, &src, nullptr);
|
||||||
|
glCompileShader(shader);
|
||||||
|
|
||||||
|
GLint success;
|
||||||
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
char infoLog[512];
|
||||||
|
glGetShaderInfoLog(shader, sizeof(infoLog), nullptr, infoLog);
|
||||||
|
E2D_ERROR(CAT_ASSET, "Shader compilation failed: {}", infoLog);
|
||||||
|
glDeleteShader(shader);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return shader;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint ShaderLoader::linkProgram(GLuint vertexShader, GLuint fragmentShader) {
|
||||||
|
GLuint program = glCreateProgram();
|
||||||
|
glAttachShader(program, vertexShader);
|
||||||
|
glAttachShader(program, fragmentShader);
|
||||||
|
glLinkProgram(program);
|
||||||
|
|
||||||
|
GLint success;
|
||||||
|
glGetProgramiv(program, GL_LINK_STATUS, &success);
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
char infoLog[512];
|
||||||
|
glGetProgramInfoLog(program, sizeof(infoLog), nullptr, infoLog);
|
||||||
|
E2D_ERROR(CAT_ASSET, "Shader program linking failed: {}", infoLog);
|
||||||
|
glDeleteProgram(program);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return program;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderLoader::extractUniforms(GLuint program, ShaderAsset *asset) {
|
||||||
|
GLint uniformCount = 0;
|
||||||
|
glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &uniformCount);
|
||||||
|
|
||||||
|
for (GLint i = 0; i < uniformCount; ++i) {
|
||||||
|
char name[256];
|
||||||
|
GLsizei nameLen = 0;
|
||||||
|
GLint size = 0;
|
||||||
|
GLenum type = 0;
|
||||||
|
|
||||||
|
glGetActiveUniform(program, i, sizeof(name), &nameLen, &size, &type, name);
|
||||||
|
|
||||||
|
UniformInfo info;
|
||||||
|
info.name = std::string(name, nameLen);
|
||||||
|
info.location = glGetUniformLocation(program, name);
|
||||||
|
info.isArray = (size > 1);
|
||||||
|
info.arraySize = static_cast<size_t>(size);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case GL_FLOAT: info.type = UniformType::Float; break;
|
||||||
|
case GL_FLOAT_VEC2: info.type = UniformType::Vec2; break;
|
||||||
|
case GL_FLOAT_VEC3: info.type = UniformType::Vec3; break;
|
||||||
|
case GL_FLOAT_VEC4: info.type = UniformType::Vec4; break;
|
||||||
|
case GL_FLOAT_MAT2: info.type = UniformType::Mat2; break;
|
||||||
|
case GL_FLOAT_MAT3: info.type = UniformType::Mat3; break;
|
||||||
|
case GL_FLOAT_MAT4: info.type = UniformType::Mat4; break;
|
||||||
|
case GL_INT: info.type = UniformType::Int; break;
|
||||||
|
case GL_INT_VEC2: info.type = UniformType::IVec2; break;
|
||||||
|
case GL_INT_VEC3: info.type = UniformType::IVec3; break;
|
||||||
|
case GL_INT_VEC4: info.type = UniformType::IVec4; break;
|
||||||
|
case GL_BOOL: info.type = UniformType::Bool; break;
|
||||||
|
case GL_SAMPLER_2D: info.type = UniformType::Sampler2D; break;
|
||||||
|
case GL_SAMPLER_CUBE: info.type = UniformType::SamplerCube; break;
|
||||||
|
default: info.type = UniformType::None; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
asset->addUniform(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BlendState ShaderLoader::parseBlendState(const std::string &json) {
|
||||||
|
BlendState state;
|
||||||
|
try {
|
||||||
|
auto j = nlohmann::json::parse(json);
|
||||||
|
state.enabled = j.value("enabled", false);
|
||||||
|
|
||||||
|
auto parseBlendFactor = [](const std::string &s) -> BlendFactor {
|
||||||
|
if (s == "ZERO") return BlendFactor::Zero;
|
||||||
|
if (s == "ONE") return BlendFactor::One;
|
||||||
|
if (s == "SRC_COLOR") return BlendFactor::SrcColor;
|
||||||
|
if (s == "ONE_MINUS_SRC_COLOR") return BlendFactor::OneMinusSrcColor;
|
||||||
|
if (s == "DST_COLOR") return BlendFactor::DstColor;
|
||||||
|
if (s == "ONE_MINUS_DST_COLOR") return BlendFactor::OneMinusDstColor;
|
||||||
|
if (s == "SRC_ALPHA") return BlendFactor::SrcAlpha;
|
||||||
|
if (s == "ONE_MINUS_SRC_ALPHA") return BlendFactor::OneMinusSrcAlpha;
|
||||||
|
return BlendFactor::SrcAlpha;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (j.contains("src")) state.srcRGB = parseBlendFactor(j["src"]);
|
||||||
|
if (j.contains("dst")) state.dstRGB = parseBlendFactor(j["dst"]);
|
||||||
|
|
||||||
|
} catch (...) {}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
DepthState ShaderLoader::parseDepthState(const std::string &json) {
|
||||||
|
DepthState state;
|
||||||
|
try {
|
||||||
|
auto j = nlohmann::json::parse(json);
|
||||||
|
state.testEnabled = j.value("enabled", false);
|
||||||
|
state.writeEnabled = j.value("write", true);
|
||||||
|
} catch (...) {}
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniformType ShaderLoader::parseUniformType(const std::string &typeStr) {
|
||||||
|
if (typeStr == "float") return UniformType::Float;
|
||||||
|
if (typeStr == "vec2") return UniformType::Vec2;
|
||||||
|
if (typeStr == "vec3") return UniformType::Vec3;
|
||||||
|
if (typeStr == "vec4") return UniformType::Vec4;
|
||||||
|
if (typeStr == "mat2") return UniformType::Mat2;
|
||||||
|
if (typeStr == "mat3") return UniformType::Mat3;
|
||||||
|
if (typeStr == "mat4") return UniformType::Mat4;
|
||||||
|
if (typeStr == "int") return UniformType::Int;
|
||||||
|
if (typeStr == "ivec2") return UniformType::IVec2;
|
||||||
|
if (typeStr == "ivec3") return UniformType::IVec3;
|
||||||
|
if (typeStr == "ivec4") return UniformType::IVec4;
|
||||||
|
if (typeStr == "bool") return UniformType::Bool;
|
||||||
|
if (typeStr == "sampler2D") return UniformType::Sampler2D;
|
||||||
|
if (typeStr == "samplerCube") return UniformType::SamplerCube;
|
||||||
|
return UniformType::None;
|
||||||
|
}
|
||||||
|
UniformValue ShaderLoader::parseDefaultValue(const std::string &json, UniformType type) {
|
||||||
|
try {
|
||||||
|
auto j = nlohmann::json::parse(json);
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case UniformType::Float:
|
||||||
|
return UniformValue(j.get<float>());
|
||||||
|
case UniformType::Vec2:
|
||||||
|
return UniformValue(glm::vec2(j[0].get<float>(), j[1].get<float>()));
|
||||||
|
case UniformType::Vec3:
|
||||||
|
return UniformValue(glm::vec3(j[0].get<float>(), j[1].get<float>(), j[2].get<float>()));
|
||||||
|
case UniformType::Vec4:
|
||||||
|
return UniformValue(glm::vec4(j[0].get<float>(), j[1].get<float>(), j[2].get<float>(), j[3].get<float>()));
|
||||||
|
case UniformType::Int:
|
||||||
|
return UniformValue(j.get<int>());
|
||||||
|
case UniformType::Bool:
|
||||||
|
return UniformValue(j.get<bool>());
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (...) {}
|
||||||
|
return UniformValue();
|
||||||
|
}
|
||||||
|
|
||||||
Ref<ShaderAsset> ShaderLoader::loadFromMemory(const u8 *data, size_t size) {
|
Ref<ShaderAsset> ShaderLoader::loadFromMemory(const u8 *data, size_t size) {
|
||||||
if (!data || size == 0) {
|
if (!data || size == 0) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
@ -204,9 +452,8 @@ bool ShaderLoader::canLoad(const std::string &path) const {
|
||||||
auto exts = extensions();
|
auto exts = extensions();
|
||||||
return std::find(exts.begin(), exts.end(), ext) != exts.end();
|
return std::find(exts.begin(), exts.end(), ext) != exts.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> ShaderLoader::extensions() const {
|
std::vector<std::string> ShaderLoader::extensions() const {
|
||||||
return {".vert", ".frag", ".glsl", ".vs", ".fs"};
|
return {".json", ".vert", ".frag", ".glsl", ".vs", ".fs"};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ShaderLoader::parseCombined(const std::string &content,
|
bool ShaderLoader::parseCombined(const std::string &content,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
#include <extra2d/asset/audio_asset.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
void AudioAsset::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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioAsset::release() {
|
||||||
|
data_.clear();
|
||||||
|
format_ = AudioFormat::PCM;
|
||||||
|
channels_ = 0;
|
||||||
|
sampleRate_ = 0;
|
||||||
|
bitsPerSample_ = 0;
|
||||||
|
duration_ = 0.0f;
|
||||||
|
streaming_ = false;
|
||||||
|
setState(AssetState::Unloaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
#include <extra2d/asset/data_asset.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
void DataAsset::setData(std::vector<u8> data) {
|
||||||
|
data_ = std::move(data);
|
||||||
|
setState(AssetState::Loaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataAsset::release() {
|
||||||
|
data_.clear();
|
||||||
|
setState(AssetState::Unloaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
#include <extra2d/asset/font_asset.h>
|
||||||
|
|
||||||
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
#include <stb/stb_truetype.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
class FontAsset::Impl {
|
||||||
|
public:
|
||||||
|
stbtt_fontinfo info;
|
||||||
|
bool initialized = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
FontAsset::FontAsset() : impl_(ptr::unique<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);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
#include <extra2d/asset/msdf_font_asset.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
MSDFFontAsset::MSDFFontAsset() = default;
|
||||||
|
|
||||||
|
MSDFFontAsset::~MSDFFontAsset() {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MSDFFontAsset::loaded() const {
|
||||||
|
return state_.load(std::memory_order_acquire) == AssetState::Loaded &&
|
||||||
|
textureId_ != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t MSDFFontAsset::memSize() const {
|
||||||
|
return glyphs_.size() * sizeof(GlyphInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
const GlyphInfo* MSDFFontAsset::getGlyph(char32_t codepoint) const {
|
||||||
|
auto it = glyphs_.find(codepoint);
|
||||||
|
if (it != glyphs_.end()) {
|
||||||
|
return &it->second;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MSDFFontAsset::hasGlyph(char32_t codepoint) const {
|
||||||
|
return glyphs_.find(codepoint) != glyphs_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<char32_t> MSDFFontAsset::getMissingChars(const std::u32string& text) const {
|
||||||
|
std::vector<char32_t> missing;
|
||||||
|
for (char32_t c : text) {
|
||||||
|
if (!hasGlyph(c)) {
|
||||||
|
missing.push_back(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MSDFFontAsset::canRender(const std::u32string& text) const {
|
||||||
|
for (char32_t c : text) {
|
||||||
|
if (!hasGlyph(c)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 MSDFFontAsset::measureText(const std::u32string& text, float scale) const {
|
||||||
|
if (text.empty()) return glm::vec2(0.0f);
|
||||||
|
|
||||||
|
float width = 0.0f;
|
||||||
|
float maxAscent = 0.0f;
|
||||||
|
float maxDescent = 0.0f;
|
||||||
|
|
||||||
|
for (char32_t c : text) {
|
||||||
|
const GlyphInfo* glyph = getGlyph(c);
|
||||||
|
if (glyph) {
|
||||||
|
width += glyph->advance * scale;
|
||||||
|
maxAscent = glm::max(maxAscent, glyph->bearing.y * scale);
|
||||||
|
maxDescent = glm::max(maxDescent, (glyph->size.y - glyph->bearing.y) * scale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return glm::vec2(width, maxAscent + maxDescent);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MSDFFontAsset::addGlyph(const GlyphInfo& glyph) {
|
||||||
|
glyphs_[glyph.codepoint] = glyph;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MSDFFontAsset::markLoaded() {
|
||||||
|
setState(AssetState::Loaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MSDFFontAsset::release() {
|
||||||
|
if (textureId_ != 0) {
|
||||||
|
glDeleteTextures(1, &textureId_);
|
||||||
|
textureId_ = 0;
|
||||||
|
}
|
||||||
|
glyphs_.clear();
|
||||||
|
setState(AssetState::Unloaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,183 @@
|
||||||
|
#include <extra2d/asset/msdf_font_loader.h>
|
||||||
|
#include <extra2d/services/logger_service.h>
|
||||||
|
#include <json/json.hpp>
|
||||||
|
#include <fstream>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getExtension(const std::string& path) {
|
||||||
|
size_t pos = path.rfind('.');
|
||||||
|
if (pos == std::string::npos) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
std::string ext = path.substr(pos);
|
||||||
|
std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
|
||||||
|
return ext;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<MSDFFontAsset> MSDFFontLoader::load(const std::string& path) {
|
||||||
|
auto asset = ptr::make<MSDFFontAsset>();
|
||||||
|
|
||||||
|
if (!parseEmbeddedMetadata(path, asset.get())) {
|
||||||
|
E2D_ERROR(CAT_ASSET, "Failed to parse MSDF font metadata: {}", path);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
asset->markLoaded();
|
||||||
|
return asset;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<MSDFFontAsset> MSDFFontLoader::loadFromMemory(const u8* data, size_t size) {
|
||||||
|
E2D_ERROR(CAT_ASSET, "MSDF font loading from memory not supported");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MSDFFontLoader::canLoad(const std::string& path) const {
|
||||||
|
std::string ext = getExtension(path);
|
||||||
|
return ext == ".msdf" || ext == ".png";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> MSDFFontLoader::extensions() const {
|
||||||
|
return {".msdf", ".png"};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MSDFFontLoader::parseEmbeddedMetadata(const std::string& path, MSDFFontAsset* asset) {
|
||||||
|
auto pngData = readFile(path);
|
||||||
|
if (pngData.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<u8> metadataJson;
|
||||||
|
if (!readTextChunk(pngData, "msdf", metadataJson)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string jsonStr(metadataJson.begin(), metadataJson.end());
|
||||||
|
if (!parseJsonMetadata(jsonStr, asset)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint texture;
|
||||||
|
glGenTextures(1, &texture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
|
||||||
|
asset->setTextureId(texture);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MSDFFontLoader::readTextChunk(const std::vector<u8>& pngData,
|
||||||
|
const std::string& keyword,
|
||||||
|
std::vector<u8>& output) {
|
||||||
|
if (pngData.size() < 8) return false;
|
||||||
|
|
||||||
|
if (pngData[0] != 0x89 || pngData[1] != 'P' || pngData[2] != 'N' || pngData[3] != 'G') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pos = 8;
|
||||||
|
while (pos < pngData.size()) {
|
||||||
|
if (pos + 8 > pngData.size()) break;
|
||||||
|
|
||||||
|
u32 length = (static_cast<u32>(pngData[pos]) << 24) |
|
||||||
|
(static_cast<u32>(pngData[pos + 1]) << 16) |
|
||||||
|
(static_cast<u32>(pngData[pos + 2]) << 8) |
|
||||||
|
static_cast<u32>(pngData[pos + 3]);
|
||||||
|
|
||||||
|
std::string chunkType(pngData.begin() + pos + 4, pngData.begin() + pos + 8);
|
||||||
|
|
||||||
|
if (chunkType == "tEXt") {
|
||||||
|
size_t dataStart = pos + 8;
|
||||||
|
size_t nullPos = dataStart;
|
||||||
|
while (nullPos < pngData.size() && pngData[nullPos] != 0) {
|
||||||
|
nullPos++;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string foundKeyword(pngData.begin() + dataStart, pngData.begin() + nullPos);
|
||||||
|
if (foundKeyword == keyword) {
|
||||||
|
output.assign(pngData.begin() + nullPos + 1, pngData.begin() + pos + 8 + length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pos += 12 + length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MSDFFontLoader::parseJsonMetadata(const std::string& json, MSDFFontAsset* asset) {
|
||||||
|
try {
|
||||||
|
auto j = nlohmann::json::parse(json);
|
||||||
|
|
||||||
|
asset->setFontSize(j.value("size", 48));
|
||||||
|
asset->setPxRange(j.value("pxRange", 4.0f));
|
||||||
|
asset->setLineHeight(j.value("lineHeight", 60));
|
||||||
|
asset->setBaseline(j.value("baseline", 12));
|
||||||
|
|
||||||
|
if (j.contains("atlas")) {
|
||||||
|
auto atlas = j["atlas"];
|
||||||
|
asset->setAtlasSize(atlas.value("width", 2048), atlas.value("height", 2048));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (j.contains("glyphs")) {
|
||||||
|
for (const auto& glyphJson : j["glyphs"]) {
|
||||||
|
GlyphInfo glyph;
|
||||||
|
glyph.codepoint = glyphJson.value("unicode", 0u);
|
||||||
|
|
||||||
|
if (glyphJson.contains("atlasBounds")) {
|
||||||
|
auto bounds = glyphJson["atlasBounds"];
|
||||||
|
glyph.uvMin.x = bounds.value("left", 0.0f) / asset->atlasWidth();
|
||||||
|
glyph.uvMin.y = bounds.value("top", 0.0f) / asset->atlasHeight();
|
||||||
|
glyph.uvMax.x = bounds.value("right", 0.0f) / asset->atlasWidth();
|
||||||
|
glyph.uvMax.y = bounds.value("bottom", 0.0f) / asset->atlasHeight();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glyphJson.contains("planeBounds")) {
|
||||||
|
auto plane = glyphJson["planeBounds"];
|
||||||
|
glyph.size.x = plane.value("right", 0.0f) - plane.value("left", 0.0f);
|
||||||
|
glyph.size.y = plane.value("top", 0.0f) - plane.value("bottom", 0.0f);
|
||||||
|
glyph.bearing.x = plane.value("left", 0.0f);
|
||||||
|
glyph.bearing.y = plane.value("top", 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
glyph.advance = glyphJson.value("advance", 0.0f);
|
||||||
|
|
||||||
|
asset->addGlyph(glyph);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
E2D_ERROR(CAT_ASSET, "Failed to parse MSDF metadata: {}", e.what());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,116 @@
|
||||||
|
#include <extra2d/asset/shader_asset.h>
|
||||||
|
#include <extra2d/render/material.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
ShaderAsset::ShaderAsset() = default;
|
||||||
|
|
||||||
|
ShaderAsset::~ShaderAsset() {
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ShaderAsset::loaded() const {
|
||||||
|
return state_.load(std::memory_order_acquire) == AssetState::Loaded &&
|
||||||
|
programId_ != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ShaderAsset::memSize() const {
|
||||||
|
return vertexSrc_.size() + fragmentSrc_.size() + uniforms_.size() * sizeof(UniformInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
const UniformInfo* ShaderAsset::getUniformInfo(const std::string& name) const {
|
||||||
|
auto it = uniforms_.find(name);
|
||||||
|
if (it != uniforms_.end()) {
|
||||||
|
return &it->second;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint ShaderAsset::getUniformLocation(const std::string& name) const {
|
||||||
|
auto it = uniformLocations_.find(name);
|
||||||
|
if (it != uniformLocations_.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
if (programId_ != 0) {
|
||||||
|
GLint loc = glGetUniformLocation(programId_, name.c_str());
|
||||||
|
const_cast<ShaderAsset*>(this)->uniformLocations_[name] = loc;
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAsset::applyStates() const {
|
||||||
|
if (blendState_.enabled) {
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFuncSeparate(
|
||||||
|
static_cast<GLenum>(blendState_.srcRGB),
|
||||||
|
static_cast<GLenum>(blendState_.dstRGB),
|
||||||
|
static_cast<GLenum>(blendState_.srcAlpha),
|
||||||
|
static_cast<GLenum>(blendState_.dstAlpha)
|
||||||
|
);
|
||||||
|
glBlendEquationSeparate(
|
||||||
|
static_cast<GLenum>(blendState_.opRGB),
|
||||||
|
static_cast<GLenum>(blendState_.opAlpha)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (depthState_.testEnabled) {
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glDepthMask(depthState_.writeEnabled ? GL_TRUE : GL_FALSE);
|
||||||
|
glDepthFunc(static_cast<GLenum>(depthState_.compareFunc));
|
||||||
|
} else {
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAsset::bind() const {
|
||||||
|
if (programId_ != 0) {
|
||||||
|
glUseProgram(programId_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAsset::unbind() const {
|
||||||
|
glUseProgram(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<Material> ShaderAsset::createMaterial() {
|
||||||
|
return ptr::make<Material>(std::static_pointer_cast<ShaderAsset>(shared_from_this()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAsset::setProgram(GLuint programId) {
|
||||||
|
if (programId_ != 0 && programId_ != programId) {
|
||||||
|
glDeleteProgram(programId_);
|
||||||
|
}
|
||||||
|
programId_ = programId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAsset::addUniform(const UniformInfo& info) {
|
||||||
|
uniforms_[info.name] = info;
|
||||||
|
uniformLocations_[info.name] = info.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAsset::markLoaded() {
|
||||||
|
setState(AssetState::Loaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAsset::release() {
|
||||||
|
if (programId_ != 0) {
|
||||||
|
glDeleteProgram(programId_);
|
||||||
|
programId_ = 0;
|
||||||
|
}
|
||||||
|
uniforms_.clear();
|
||||||
|
uniformLocations_.clear();
|
||||||
|
vertexSrc_.clear();
|
||||||
|
fragmentSrc_.clear();
|
||||||
|
setState(AssetState::Unloaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShaderAsset::setSource(std::string vertex, std::string fragment) {
|
||||||
|
vertexSrc_ = std::move(vertex);
|
||||||
|
fragmentSrc_ = std::move(fragment);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#include <extra2d/asset/texture_asset.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
void TextureAsset::setData(int width, int height, int channels, Unique<u8[]> data) {
|
||||||
|
width_ = width;
|
||||||
|
height_ = height;
|
||||||
|
channels_ = channels;
|
||||||
|
data_ = std::move(data);
|
||||||
|
setState(AssetState::Loaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureAsset::release() {
|
||||||
|
data_.reset();
|
||||||
|
width_ = 0;
|
||||||
|
height_ = 0;
|
||||||
|
channels_ = 0;
|
||||||
|
setState(AssetState::Unloaded);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
#include <extra2d/render/material.h>
|
||||||
|
#include <extra2d/render/material_instance.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
Material::Material(Ref<ShaderAsset> shader) : shader_(std::move(shader)) {}
|
||||||
|
|
||||||
|
void Material::setFloat(const std::string& name, float value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::setVec2(const std::string& name, const glm::vec2& value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::setVec3(const std::string& name, const glm::vec3& value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::setVec4(const std::string& name, const glm::vec4& value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::setMat4(const std::string& name, const glm::mat4& value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::setInt(const std::string& name, int value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::setBool(const std::string& name, bool value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::setTexture(const std::string& name, GLuint textureId) {
|
||||||
|
textures_[name] = textureId;
|
||||||
|
properties_[name] = UniformValue(textureId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::setColor(const std::string& name, const Color& value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const UniformValue* Material::getProperty(const std::string& name) const {
|
||||||
|
auto it = properties_.find(name);
|
||||||
|
if (it != properties_.end()) {
|
||||||
|
return &it->second;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Material::hasProperty(const std::string& name) const {
|
||||||
|
return properties_.find(name) != properties_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ref<MaterialInstance> Material::createInstance() {
|
||||||
|
return ptr::make<MaterialInstance>(std::static_pointer_cast<Material>(shared_from_this()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Material::apply() const {
|
||||||
|
if (!shader_ || shader_->programId() == 0) return;
|
||||||
|
|
||||||
|
shader_->bind();
|
||||||
|
shader_->applyStates();
|
||||||
|
|
||||||
|
int textureUnit = 0;
|
||||||
|
for (const auto& [name, value] : properties_) {
|
||||||
|
GLint location = getLocation(name);
|
||||||
|
if (location < 0) continue;
|
||||||
|
|
||||||
|
if (value.type() == UniformType::Sampler2D) {
|
||||||
|
const GLuint* texId = value.get<GLuint>();
|
||||||
|
if (texId && *texId != 0) {
|
||||||
|
glActiveTexture(GL_TEXTURE0 + textureUnit);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, *texId);
|
||||||
|
glUniform1i(location, textureUnit);
|
||||||
|
textureUnit++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
value.apply(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint Material::getLocation(const std::string& name) const {
|
||||||
|
auto it = cachedLocations_.find(name);
|
||||||
|
if (it != cachedLocations_.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint location = -1;
|
||||||
|
if (shader_) {
|
||||||
|
location = shader_->getUniformLocation(name);
|
||||||
|
}
|
||||||
|
cachedLocations_[name] = location;
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
#include <extra2d/render/material_instance.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
MaterialInstance::MaterialInstance(Ref<Material> material)
|
||||||
|
: material_(std::move(material)) {}
|
||||||
|
|
||||||
|
void MaterialInstance::setFloat(const std::string& name, float value) {
|
||||||
|
overrides_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setVec2(const std::string& name, const glm::vec2& value) {
|
||||||
|
overrides_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setVec3(const std::string& name, const glm::vec3& value) {
|
||||||
|
overrides_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setVec4(const std::string& name, const glm::vec4& value) {
|
||||||
|
overrides_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setMat4(const std::string& name, const glm::mat4& value) {
|
||||||
|
overrides_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setInt(const std::string& name, int value) {
|
||||||
|
overrides_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setBool(const std::string& name, bool value) {
|
||||||
|
overrides_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setTexture(const std::string& name, GLuint textureId) {
|
||||||
|
textureOverrides_[name] = textureId;
|
||||||
|
overrides_[name] = UniformValue(textureId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setColor(const std::string& name, const Color& value) {
|
||||||
|
overrides_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setModelMatrix(const glm::mat4& model) {
|
||||||
|
overrides_["u_model"] = UniformValue(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setViewMatrix(const glm::mat4& view) {
|
||||||
|
overrides_["u_view"] = UniformValue(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setProjectionMatrix(const glm::mat4& projection) {
|
||||||
|
overrides_["u_projection"] = UniformValue(projection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setMVP(const glm::mat4& mvp) {
|
||||||
|
overrides_["u_mvp"] = UniformValue(mvp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::setColor(const Color& color) {
|
||||||
|
overrides_["u_color"] = UniformValue(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
const UniformValue* MaterialInstance::getOverride(const std::string& name) const {
|
||||||
|
auto it = overrides_.find(name);
|
||||||
|
if (it != overrides_.end()) {
|
||||||
|
return &it->second;
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MaterialInstance::hasOverride(const std::string& name) const {
|
||||||
|
return overrides_.find(name) != overrides_.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::clearOverride(const std::string& name) {
|
||||||
|
overrides_.erase(name);
|
||||||
|
textureOverrides_.erase(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::clearAllOverrides() {
|
||||||
|
overrides_.clear();
|
||||||
|
textureOverrides_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialInstance::apply() const {
|
||||||
|
if (!material_) return;
|
||||||
|
|
||||||
|
material_->apply();
|
||||||
|
|
||||||
|
int textureUnit = 0;
|
||||||
|
for (const auto& [name, texId] : textureOverrides_) {
|
||||||
|
GLint location = getLocation(name);
|
||||||
|
if (location >= 0 && texId != 0) {
|
||||||
|
glActiveTexture(GL_TEXTURE0 + textureUnit);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texId);
|
||||||
|
glUniform1i(location, textureUnit);
|
||||||
|
textureUnit++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [name, value] : overrides_) {
|
||||||
|
if (textureOverrides_.find(name) != textureOverrides_.end()) continue;
|
||||||
|
|
||||||
|
GLint location = getLocation(name);
|
||||||
|
if (location >= 0 && value.type() != UniformType::Sampler2D) {
|
||||||
|
value.apply(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint MaterialInstance::getLocation(const std::string& name) const {
|
||||||
|
auto it = cachedLocations_.find(name);
|
||||||
|
if (it != cachedLocations_.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint location = -1;
|
||||||
|
if (material_ && material_->shader()) {
|
||||||
|
location = material_->shader()->getUniformLocation(name);
|
||||||
|
}
|
||||||
|
cachedLocations_[name] = location;
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
#include <extra2d/render/material_property_block.h>
|
||||||
|
#include <extra2d/asset/shader_asset.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setFloat(const std::string& name, float value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setVec2(const std::string& name, const glm::vec2& value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setVec3(const std::string& name, const glm::vec3& value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setVec4(const std::string& name, const glm::vec4& value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setMat4(const std::string& name, const glm::mat4& value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setInt(const std::string& name, int value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setBool(const std::string& name, bool value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setTexture(const std::string& name, GLuint textureId, int unit) {
|
||||||
|
for (auto& tex : textures_) {
|
||||||
|
if (tex.name == name) {
|
||||||
|
tex.textureId = textureId;
|
||||||
|
tex.unit = unit;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
textures_.push_back({name, textureId, unit});
|
||||||
|
properties_[name] = UniformValue(textureId);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setColor(const std::string& name, const Color& value) {
|
||||||
|
properties_[name] = UniformValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setModelMatrix(const glm::mat4& model) {
|
||||||
|
properties_["u_model"] = UniformValue(model);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setViewMatrix(const glm::mat4& view) {
|
||||||
|
properties_["u_view"] = UniformValue(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setProjectionMatrix(const glm::mat4& projection) {
|
||||||
|
properties_["u_projection"] = UniformValue(projection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setMVP(const glm::mat4& mvp) {
|
||||||
|
properties_["u_mvp"] = UniformValue(mvp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::setColor(const Color& color) {
|
||||||
|
properties_["u_color"] = UniformValue(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::apply(ShaderAsset* shader) const {
|
||||||
|
if (shader) {
|
||||||
|
apply(shader->programId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::apply(GLuint programId) const {
|
||||||
|
if (programId == 0) return;
|
||||||
|
|
||||||
|
int nextUnit = 0;
|
||||||
|
for (const auto& tex : textures_) {
|
||||||
|
int unit = (tex.unit >= 0) ? tex.unit : nextUnit++;
|
||||||
|
if (tex.textureId != 0) {
|
||||||
|
glActiveTexture(GL_TEXTURE0 + unit);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, tex.textureId);
|
||||||
|
|
||||||
|
GLint location = getLocation(programId, tex.name);
|
||||||
|
if (location >= 0) {
|
||||||
|
glUniform1i(location, unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [name, value] : properties_) {
|
||||||
|
bool isTexture = false;
|
||||||
|
for (const auto& tex : textures_) {
|
||||||
|
if (tex.name == name) {
|
||||||
|
isTexture = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isTexture) {
|
||||||
|
GLint location = getLocation(programId, name);
|
||||||
|
if (location >= 0) {
|
||||||
|
value.apply(location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MaterialPropertyBlock::clear() {
|
||||||
|
properties_.clear();
|
||||||
|
textures_.clear();
|
||||||
|
cachedLocations_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint MaterialPropertyBlock::getLocation(GLuint programId, const std::string& name) const {
|
||||||
|
auto it = cachedLocations_.find(name);
|
||||||
|
if (it != cachedLocations_.end()) {
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint location = glGetUniformLocation(programId, name.c_str());
|
||||||
|
cachedLocations_[name] = location;
|
||||||
|
return location;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -0,0 +1,217 @@
|
||||||
|
#include <extra2d/render/msdf_text_renderer.h>
|
||||||
|
#include <glad/glad.h>
|
||||||
|
#include <simdutf/simdutf.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
static const char* MSDF_VERTEX_SHADER = R"(
|
||||||
|
#version 450 core
|
||||||
|
layout(location = 0) in vec2 a_position;
|
||||||
|
layout(location = 1) in vec2 a_texCoord;
|
||||||
|
|
||||||
|
uniform mat4 u_projection;
|
||||||
|
|
||||||
|
out vec2 v_texCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = u_projection * vec4(a_position, 0.0, 1.0);
|
||||||
|
v_texCoord = a_texCoord;
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
static const char* MSDF_FRAGMENT_SHADER = R"(
|
||||||
|
#version 450 core
|
||||||
|
in vec2 v_texCoord;
|
||||||
|
|
||||||
|
uniform sampler2D u_texture;
|
||||||
|
uniform float u_pxRange;
|
||||||
|
uniform vec4 u_color;
|
||||||
|
|
||||||
|
out vec4 fragColor;
|
||||||
|
|
||||||
|
float median(float r, float g, float b) {
|
||||||
|
return max(min(r, g), min(max(r, g), b));
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 msdf = texture(u_texture, v_texCoord).rgb;
|
||||||
|
float sigDist = median(msdf.r, msdf.g, msdf.b);
|
||||||
|
|
||||||
|
float pxRange = u_pxRange;
|
||||||
|
float alpha = smoothstep(0.5 - 0.5/pxRange, 0.5 + 0.5/pxRange, sigDist);
|
||||||
|
|
||||||
|
fragColor = u_color * vec4(1.0, 1.0, 1.0, alpha);
|
||||||
|
}
|
||||||
|
)";
|
||||||
|
|
||||||
|
MSDFTextRenderer::MSDFTextRenderer() = default;
|
||||||
|
|
||||||
|
MSDFTextRenderer::~MSDFTextRenderer() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MSDFTextRenderer::init() {
|
||||||
|
return createShader();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MSDFTextRenderer::shutdown() {
|
||||||
|
if (shader_) {
|
||||||
|
glDeleteProgram(shader_);
|
||||||
|
shader_ = 0;
|
||||||
|
}
|
||||||
|
if (vao_) {
|
||||||
|
glDeleteVertexArrays(1, &vao_);
|
||||||
|
vao_ = 0;
|
||||||
|
}
|
||||||
|
if (vbo_) {
|
||||||
|
glDeleteBuffers(1, &vbo_);
|
||||||
|
vbo_ = 0;
|
||||||
|
}
|
||||||
|
if (ibo_) {
|
||||||
|
glDeleteBuffers(1, &ibo_);
|
||||||
|
ibo_ = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MSDFTextRenderer::setText(const std::string& utf8Text) {
|
||||||
|
size_t len = simdutf::utf32_length_from_utf8(utf8Text.data(), utf8Text.size());
|
||||||
|
text_.resize(len);
|
||||||
|
simdutf::convert_utf8_to_utf32(utf8Text.data(), utf8Text.size(), text_.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MSDFTextRenderer::setFontSize(float size) {
|
||||||
|
fontSize_ = size;
|
||||||
|
if (font_) {
|
||||||
|
scale_ = size / static_cast<float>(font_->fontSize());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 MSDFTextRenderer::getSize() const {
|
||||||
|
if (font_) {
|
||||||
|
return font_->measureText(text_, scale_);
|
||||||
|
}
|
||||||
|
return glm::vec2(0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MSDFTextRenderer::render(const glm::mat4& projection) {
|
||||||
|
if (!font_ || text_.empty()) return;
|
||||||
|
|
||||||
|
updateGeometry();
|
||||||
|
|
||||||
|
glUseProgram(shader_);
|
||||||
|
|
||||||
|
GLint projLoc = glGetUniformLocation(shader_, "u_projection");
|
||||||
|
glUniformMatrix4fv(projLoc, 1, GL_FALSE, &projection[0][0]);
|
||||||
|
|
||||||
|
GLint texLoc = glGetUniformLocation(shader_, "u_texture");
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, font_->textureId());
|
||||||
|
glUniform1i(texLoc, 0);
|
||||||
|
|
||||||
|
GLint pxRangeLoc = glGetUniformLocation(shader_, "u_pxRange");
|
||||||
|
glUniform1f(pxRangeLoc, font_->pxRange() * scale_);
|
||||||
|
|
||||||
|
GLint colorLoc = glGetUniformLocation(shader_, "u_color");
|
||||||
|
glUniform4f(colorLoc, color_.r, color_.g, color_.b, color_.a);
|
||||||
|
|
||||||
|
draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MSDFTextRenderer::updateGeometry() {
|
||||||
|
vertices_.clear();
|
||||||
|
indices_.clear();
|
||||||
|
|
||||||
|
float x = position_.x;
|
||||||
|
float y = position_.y;
|
||||||
|
|
||||||
|
for (char32_t c : text_) {
|
||||||
|
const GlyphInfo* glyph = font_->getGlyph(c);
|
||||||
|
if (!glyph) continue;
|
||||||
|
|
||||||
|
float x0 = x + glyph->bearing.x * scale_;
|
||||||
|
float y0 = y - glyph->bearing.y * scale_;
|
||||||
|
float x1 = x0 + glyph->size.x * scale_;
|
||||||
|
float y1 = y0 + glyph->size.y * scale_;
|
||||||
|
|
||||||
|
uint32_t base = static_cast<uint32_t>(vertices_.size());
|
||||||
|
|
||||||
|
vertices_.push_back({{x0, y0}, {glyph->uvMin.x, glyph->uvMin.y}});
|
||||||
|
vertices_.push_back({{x1, y0}, {glyph->uvMax.x, glyph->uvMin.y}});
|
||||||
|
vertices_.push_back({{x1, y1}, {glyph->uvMax.x, glyph->uvMax.y}});
|
||||||
|
vertices_.push_back({{x0, y1}, {glyph->uvMin.x, glyph->uvMax.y}});
|
||||||
|
|
||||||
|
indices_.push_back(base + 0);
|
||||||
|
indices_.push_back(base + 1);
|
||||||
|
indices_.push_back(base + 2);
|
||||||
|
indices_.push_back(base + 0);
|
||||||
|
indices_.push_back(base + 2);
|
||||||
|
indices_.push_back(base + 3);
|
||||||
|
|
||||||
|
x += glyph->advance * scale_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MSDFTextRenderer::draw() {
|
||||||
|
if (vertices_.empty()) return;
|
||||||
|
|
||||||
|
if (!vao_) {
|
||||||
|
glGenVertexArrays(1, &vao_);
|
||||||
|
glGenBuffers(1, &vbo_);
|
||||||
|
glGenBuffers(1, &ibo_);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindVertexArray(vao_);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, vbo_);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, vertices_.size() * sizeof(Vertex), vertices_.data(), GL_DYNAMIC_DRAW);
|
||||||
|
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo_);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_.size() * sizeof(uint32_t), indices_.data(), GL_DYNAMIC_DRAW);
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
|
||||||
|
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, texCoord));
|
||||||
|
|
||||||
|
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indices_.size()), GL_UNSIGNED_INT, nullptr);
|
||||||
|
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MSDFTextRenderer::createShader() {
|
||||||
|
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
|
||||||
|
glShaderSource(vs, 1, &MSDF_VERTEX_SHADER, nullptr);
|
||||||
|
glCompileShader(vs);
|
||||||
|
|
||||||
|
GLint success;
|
||||||
|
glGetShaderiv(vs, GL_COMPILE_STATUS, &success);
|
||||||
|
if (!success) {
|
||||||
|
glDeleteShader(vs);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
|
glShaderSource(fs, 1, &MSDF_FRAGMENT_SHADER, nullptr);
|
||||||
|
glCompileShader(fs);
|
||||||
|
|
||||||
|
glGetShaderiv(fs, GL_COMPILE_STATUS, &success);
|
||||||
|
if (!success) {
|
||||||
|
glDeleteShader(vs);
|
||||||
|
glDeleteShader(fs);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
shader_ = glCreateProgram();
|
||||||
|
glAttachShader(shader_, vs);
|
||||||
|
glAttachShader(shader_, fs);
|
||||||
|
glLinkProgram(shader_);
|
||||||
|
|
||||||
|
glGetProgramiv(shader_, GL_LINK_STATUS, &success);
|
||||||
|
glDeleteShader(vs);
|
||||||
|
glDeleteShader(fs);
|
||||||
|
|
||||||
|
return success == GL_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -94,12 +94,14 @@ void SpriteRenderer::shutdown() {
|
||||||
vao_.reset();
|
vao_.reset();
|
||||||
vbo_.reset();
|
vbo_.reset();
|
||||||
ibo_.reset();
|
ibo_.reset();
|
||||||
|
material_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpriteRenderer::begin(const glm::mat4& viewProjection) {
|
void SpriteRenderer::begin(const glm::mat4& viewProjection) {
|
||||||
viewProjection_ = viewProjection;
|
viewProjection_ = viewProjection;
|
||||||
vertexCount_ = 0;
|
vertexCount_ = 0;
|
||||||
currentTexture_ = 0;
|
currentTexture_ = 0;
|
||||||
|
currentInstance_ = nullptr;
|
||||||
drawCalls_ = 0;
|
drawCalls_ = 0;
|
||||||
spriteCount_ = 0;
|
spriteCount_ = 0;
|
||||||
}
|
}
|
||||||
|
|
@ -114,8 +116,41 @@ void SpriteRenderer::draw(GLuint texture, const SpriteData& data) {
|
||||||
spriteCount_++;
|
spriteCount_++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SpriteRenderer::end() {
|
void SpriteRenderer::draw(GLuint texture, const SpriteData& data, MaterialInstance* instance) {
|
||||||
|
if (material_ && instance) {
|
||||||
|
if (instance != currentInstance_ || texture != currentTexture_ ||
|
||||||
|
vertexCount_ + VERTICES_PER_SPRITE > MAX_VERTICES) {
|
||||||
|
flushWithMaterial();
|
||||||
|
currentInstance_ = instance;
|
||||||
|
currentTexture_ = texture;
|
||||||
|
}
|
||||||
|
addQuad(data, texture);
|
||||||
|
spriteCount_++;
|
||||||
|
} else {
|
||||||
|
draw(texture, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpriteRenderer::draw(GLuint texture, const SpriteData& data, const MaterialPropertyBlock& block) {
|
||||||
flush();
|
flush();
|
||||||
|
currentTexture_ = texture;
|
||||||
|
addQuad(data, texture);
|
||||||
|
spriteCount_++;
|
||||||
|
|
||||||
|
flush();
|
||||||
|
|
||||||
|
if (material_ && material_->shader()) {
|
||||||
|
material_->shader()->bind();
|
||||||
|
block.apply(material_->shader());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpriteRenderer::end() {
|
||||||
|
if (material_ && currentInstance_) {
|
||||||
|
flushWithMaterial();
|
||||||
|
} else {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SpriteRenderer::createShader() {
|
bool SpriteRenderer::createShader() {
|
||||||
|
|
@ -158,6 +193,22 @@ void SpriteRenderer::flush() {
|
||||||
|
|
||||||
vbo_->update(0, vertexCount_ * sizeof(SpriteVertex), vertices_.data());
|
vbo_->update(0, vertexCount_ * sizeof(SpriteVertex), vertices_.data());
|
||||||
|
|
||||||
|
if (material_ && material_->shader()) {
|
||||||
|
material_->shader()->bind();
|
||||||
|
material_->shader()->applyStates();
|
||||||
|
|
||||||
|
GLint vpLoc = material_->shader()->getUniformLocation("u_viewProjection");
|
||||||
|
if (vpLoc >= 0) {
|
||||||
|
glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint texLoc = material_->shader()->getUniformLocation("u_texture");
|
||||||
|
if (texLoc >= 0) {
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, currentTexture_);
|
||||||
|
glUniform1i(texLoc, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
glUseProgram(shader_);
|
glUseProgram(shader_);
|
||||||
GLint vpLoc = glGetUniformLocation(shader_, "u_viewProjection");
|
GLint vpLoc = glGetUniformLocation(shader_, "u_viewProjection");
|
||||||
glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]);
|
glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]);
|
||||||
|
|
@ -166,6 +217,42 @@ void SpriteRenderer::flush() {
|
||||||
glUniform1i(texLoc, 0);
|
glUniform1i(texLoc, 0);
|
||||||
|
|
||||||
glBindTextureUnit(0, currentTexture_);
|
glBindTextureUnit(0, currentTexture_);
|
||||||
|
}
|
||||||
|
|
||||||
|
vao_->bind();
|
||||||
|
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(vertexCount_ / VERTICES_PER_SPRITE * INDICES_PER_SPRITE),
|
||||||
|
GL_UNSIGNED_SHORT, nullptr);
|
||||||
|
|
||||||
|
E2D_RENDER_STATS().addDrawCall(static_cast<uint32>(vertexCount_),
|
||||||
|
static_cast<uint32>(vertexCount_ / 3));
|
||||||
|
|
||||||
|
drawCalls_++;
|
||||||
|
vertexCount_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpriteRenderer::flushWithMaterial() {
|
||||||
|
if (vertexCount_ == 0) return;
|
||||||
|
|
||||||
|
vbo_->update(0, vertexCount_ * sizeof(SpriteVertex), vertices_.data());
|
||||||
|
|
||||||
|
if (currentInstance_) {
|
||||||
|
currentInstance_->apply();
|
||||||
|
|
||||||
|
ShaderAsset* shader = currentInstance_->shader();
|
||||||
|
if (shader) {
|
||||||
|
GLint vpLoc = shader->getUniformLocation("u_viewProjection");
|
||||||
|
if (vpLoc >= 0) {
|
||||||
|
glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
GLint texLoc = shader->getUniformLocation("u_texture");
|
||||||
|
if (texLoc >= 0) {
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, currentTexture_);
|
||||||
|
glUniform1i(texLoc, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
vao_->bind();
|
vao_->bind();
|
||||||
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(vertexCount_ / VERTICES_PER_SPRITE * INDICES_PER_SPRITE),
|
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(vertexCount_ / VERTICES_PER_SPRITE * INDICES_PER_SPRITE),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,128 @@
|
||||||
|
#include <extra2d/render/uniform_value.h>
|
||||||
|
|
||||||
|
namespace extra2d {
|
||||||
|
|
||||||
|
UniformValue::UniformValue(float v) : value_(v) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(const glm::vec2& v) : value_(v) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(const glm::vec3& v) : value_(v) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(const glm::vec4& v) : value_(v) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(const glm::mat2& v) : value_(v) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(const glm::mat3& v) : value_(v) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(const glm::mat4& v) : value_(v) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(int v) : value_(v) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(const glm::ivec2& v) : value_(v) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(const glm::ivec3& v) : value_(v) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(const glm::ivec4& v) : value_(v) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(bool v) : value_(v) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(GLuint textureId) : value_(textureId) {}
|
||||||
|
|
||||||
|
UniformValue::UniformValue(const Color& color)
|
||||||
|
: value_(glm::vec4(color.r, color.g, color.b, color.a)) {}
|
||||||
|
|
||||||
|
UniformType UniformValue::type() const {
|
||||||
|
if (std::holds_alternative<std::monostate>(value_)) {
|
||||||
|
return UniformType::None;
|
||||||
|
} else if (std::holds_alternative<float>(value_)) {
|
||||||
|
return UniformType::Float;
|
||||||
|
} else if (std::holds_alternative<glm::vec2>(value_)) {
|
||||||
|
return UniformType::Vec2;
|
||||||
|
} else if (std::holds_alternative<glm::vec3>(value_)) {
|
||||||
|
return UniformType::Vec3;
|
||||||
|
} else if (std::holds_alternative<glm::vec4>(value_)) {
|
||||||
|
return UniformType::Vec4;
|
||||||
|
} else if (std::holds_alternative<glm::mat2>(value_)) {
|
||||||
|
return UniformType::Mat2;
|
||||||
|
} else if (std::holds_alternative<glm::mat3>(value_)) {
|
||||||
|
return UniformType::Mat3;
|
||||||
|
} else if (std::holds_alternative<glm::mat4>(value_)) {
|
||||||
|
return UniformType::Mat4;
|
||||||
|
} else if (std::holds_alternative<int>(value_)) {
|
||||||
|
return UniformType::Int;
|
||||||
|
} else if (std::holds_alternative<glm::ivec2>(value_)) {
|
||||||
|
return UniformType::IVec2;
|
||||||
|
} else if (std::holds_alternative<glm::ivec3>(value_)) {
|
||||||
|
return UniformType::IVec3;
|
||||||
|
} else if (std::holds_alternative<glm::ivec4>(value_)) {
|
||||||
|
return UniformType::IVec4;
|
||||||
|
} else if (std::holds_alternative<bool>(value_)) {
|
||||||
|
return UniformType::Bool;
|
||||||
|
} else if (std::holds_alternative<GLuint>(value_)) {
|
||||||
|
return UniformType::Sampler2D;
|
||||||
|
}
|
||||||
|
return UniformType::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UniformValue::apply(GLint location) const {
|
||||||
|
if (location < 0) return;
|
||||||
|
|
||||||
|
std::visit([&](auto&& arg) {
|
||||||
|
using T = std::decay_t<decltype(arg)>;
|
||||||
|
|
||||||
|
if constexpr (std::is_same_v<T, float>) {
|
||||||
|
glUniform1f(location, arg);
|
||||||
|
} else if constexpr (std::is_same_v<T, glm::vec2>) {
|
||||||
|
glUniform2fv(location, 1, &arg[0]);
|
||||||
|
} else if constexpr (std::is_same_v<T, glm::vec3>) {
|
||||||
|
glUniform3fv(location, 1, &arg[0]);
|
||||||
|
} else if constexpr (std::is_same_v<T, glm::vec4>) {
|
||||||
|
glUniform4fv(location, 1, &arg[0]);
|
||||||
|
} else if constexpr (std::is_same_v<T, glm::mat2>) {
|
||||||
|
glUniformMatrix2fv(location, 1, GL_FALSE, &arg[0][0]);
|
||||||
|
} else if constexpr (std::is_same_v<T, glm::mat3>) {
|
||||||
|
glUniformMatrix3fv(location, 1, GL_FALSE, &arg[0][0]);
|
||||||
|
} else if constexpr (std::is_same_v<T, glm::mat4>) {
|
||||||
|
glUniformMatrix4fv(location, 1, GL_FALSE, &arg[0][0]);
|
||||||
|
} else if constexpr (std::is_same_v<T, int>) {
|
||||||
|
glUniform1i(location, arg);
|
||||||
|
} else if constexpr (std::is_same_v<T, glm::ivec2>) {
|
||||||
|
glUniform2iv(location, 1, &arg[0]);
|
||||||
|
} else if constexpr (std::is_same_v<T, glm::ivec3>) {
|
||||||
|
glUniform3iv(location, 1, &arg[0]);
|
||||||
|
} else if constexpr (std::is_same_v<T, glm::ivec4>) {
|
||||||
|
glUniform4iv(location, 1, &arg[0]);
|
||||||
|
} else if constexpr (std::is_same_v<T, bool>) {
|
||||||
|
glUniform1i(location, arg ? 1 : 0);
|
||||||
|
} else if constexpr (std::is_same_v<T, GLuint>) {
|
||||||
|
glUniform1i(location, static_cast<GLint>(arg));
|
||||||
|
}
|
||||||
|
}, value_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UniformValue::isEmpty() const {
|
||||||
|
return std::holds_alternative<std::monostate>(value_);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* UniformValue::typeName(UniformType type) {
|
||||||
|
switch (type) {
|
||||||
|
case UniformType::None: return "None";
|
||||||
|
case UniformType::Float: return "Float";
|
||||||
|
case UniformType::Vec2: return "Vec2";
|
||||||
|
case UniformType::Vec3: return "Vec3";
|
||||||
|
case UniformType::Vec4: return "Vec4";
|
||||||
|
case UniformType::Mat2: return "Mat2";
|
||||||
|
case UniformType::Mat3: return "Mat3";
|
||||||
|
case UniformType::Mat4: return "Mat4";
|
||||||
|
case UniformType::Int: return "Int";
|
||||||
|
case UniformType::IVec2: return "IVec2";
|
||||||
|
case UniformType::IVec3: return "IVec3";
|
||||||
|
case UniformType::IVec4: return "IVec4";
|
||||||
|
case UniformType::Bool: return "Bool";
|
||||||
|
case UniformType::Sampler2D: return "Sampler2D";
|
||||||
|
case UniformType::SamplerCube: return "SamplerCube";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace extra2d
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
#include <extra2d/window/event_converter.h>
|
#include <extra2d/window/event_converter.h>
|
||||||
|
#include <extra2d/window/keys.h>
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
@ -102,79 +103,79 @@ Event EventConverter::convertTouch(const SDL_TouchFingerEvent& e, EventType type
|
||||||
|
|
||||||
i32 EventConverter::mapKey(SDL_Keycode key) {
|
i32 EventConverter::mapKey(SDL_Keycode key) {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case SDLK_a: return 0;
|
case SDLK_a: return static_cast<i32>(Key::A);
|
||||||
case SDLK_b: return 1;
|
case SDLK_b: return static_cast<i32>(Key::B);
|
||||||
case SDLK_c: return 2;
|
case SDLK_c: return static_cast<i32>(Key::C);
|
||||||
case SDLK_d: return 3;
|
case SDLK_d: return static_cast<i32>(Key::D);
|
||||||
case SDLK_e: return 4;
|
case SDLK_e: return static_cast<i32>(Key::E);
|
||||||
case SDLK_f: return 5;
|
case SDLK_f: return static_cast<i32>(Key::F);
|
||||||
case SDLK_g: return 6;
|
case SDLK_g: return static_cast<i32>(Key::G);
|
||||||
case SDLK_h: return 7;
|
case SDLK_h: return static_cast<i32>(Key::H);
|
||||||
case SDLK_i: return 8;
|
case SDLK_i: return static_cast<i32>(Key::I);
|
||||||
case SDLK_j: return 9;
|
case SDLK_j: return static_cast<i32>(Key::J);
|
||||||
case SDLK_k: return 10;
|
case SDLK_k: return static_cast<i32>(Key::K);
|
||||||
case SDLK_l: return 11;
|
case SDLK_l: return static_cast<i32>(Key::L);
|
||||||
case SDLK_m: return 12;
|
case SDLK_m: return static_cast<i32>(Key::M);
|
||||||
case SDLK_n: return 13;
|
case SDLK_n: return static_cast<i32>(Key::N);
|
||||||
case SDLK_o: return 14;
|
case SDLK_o: return static_cast<i32>(Key::O);
|
||||||
case SDLK_p: return 15;
|
case SDLK_p: return static_cast<i32>(Key::P);
|
||||||
case SDLK_q: return 16;
|
case SDLK_q: return static_cast<i32>(Key::Q);
|
||||||
case SDLK_r: return 17;
|
case SDLK_r: return static_cast<i32>(Key::R);
|
||||||
case SDLK_s: return 18;
|
case SDLK_s: return static_cast<i32>(Key::S);
|
||||||
case SDLK_t: return 19;
|
case SDLK_t: return static_cast<i32>(Key::T);
|
||||||
case SDLK_u: return 20;
|
case SDLK_u: return static_cast<i32>(Key::U);
|
||||||
case SDLK_v: return 21;
|
case SDLK_v: return static_cast<i32>(Key::V);
|
||||||
case SDLK_w: return 22;
|
case SDLK_w: return static_cast<i32>(Key::W);
|
||||||
case SDLK_x: return 23;
|
case SDLK_x: return static_cast<i32>(Key::X);
|
||||||
case SDLK_y: return 24;
|
case SDLK_y: return static_cast<i32>(Key::Y);
|
||||||
case SDLK_z: return 25;
|
case SDLK_z: return static_cast<i32>(Key::Z);
|
||||||
case SDLK_0: return 26;
|
case SDLK_0: return static_cast<i32>(Key::Num0);
|
||||||
case SDLK_1: return 27;
|
case SDLK_1: return static_cast<i32>(Key::Num1);
|
||||||
case SDLK_2: return 28;
|
case SDLK_2: return static_cast<i32>(Key::Num2);
|
||||||
case SDLK_3: return 29;
|
case SDLK_3: return static_cast<i32>(Key::Num3);
|
||||||
case SDLK_4: return 30;
|
case SDLK_4: return static_cast<i32>(Key::Num4);
|
||||||
case SDLK_5: return 31;
|
case SDLK_5: return static_cast<i32>(Key::Num5);
|
||||||
case SDLK_6: return 32;
|
case SDLK_6: return static_cast<i32>(Key::Num6);
|
||||||
case SDLK_7: return 33;
|
case SDLK_7: return static_cast<i32>(Key::Num7);
|
||||||
case SDLK_8: return 34;
|
case SDLK_8: return static_cast<i32>(Key::Num8);
|
||||||
case SDLK_9: return 35;
|
case SDLK_9: return static_cast<i32>(Key::Num9);
|
||||||
case SDLK_F1: return 36;
|
case SDLK_F1: return static_cast<i32>(Key::F1);
|
||||||
case SDLK_F2: return 37;
|
case SDLK_F2: return static_cast<i32>(Key::F2);
|
||||||
case SDLK_F3: return 38;
|
case SDLK_F3: return static_cast<i32>(Key::F3);
|
||||||
case SDLK_F4: return 39;
|
case SDLK_F4: return static_cast<i32>(Key::F4);
|
||||||
case SDLK_F5: return 40;
|
case SDLK_F5: return static_cast<i32>(Key::F5);
|
||||||
case SDLK_F6: return 41;
|
case SDLK_F6: return static_cast<i32>(Key::F6);
|
||||||
case SDLK_F7: return 42;
|
case SDLK_F7: return static_cast<i32>(Key::F7);
|
||||||
case SDLK_F8: return 43;
|
case SDLK_F8: return static_cast<i32>(Key::F8);
|
||||||
case SDLK_F9: return 44;
|
case SDLK_F9: return static_cast<i32>(Key::F9);
|
||||||
case SDLK_F10: return 45;
|
case SDLK_F10: return static_cast<i32>(Key::F10);
|
||||||
case SDLK_F11: return 46;
|
case SDLK_F11: return static_cast<i32>(Key::F11);
|
||||||
case SDLK_F12: return 47;
|
case SDLK_F12: return static_cast<i32>(Key::F12);
|
||||||
case SDLK_SPACE: return 48;
|
case SDLK_SPACE: return static_cast<i32>(Key::Space);
|
||||||
case SDLK_RETURN: return 49;
|
case SDLK_RETURN: return static_cast<i32>(Key::Enter);
|
||||||
case SDLK_ESCAPE: return 50;
|
case SDLK_ESCAPE: return static_cast<i32>(Key::Escape);
|
||||||
case SDLK_TAB: return 51;
|
case SDLK_TAB: return static_cast<i32>(Key::Tab);
|
||||||
case SDLK_BACKSPACE: return 52;
|
case SDLK_BACKSPACE: return static_cast<i32>(Key::Backspace);
|
||||||
case SDLK_INSERT: return 53;
|
case SDLK_INSERT: return static_cast<i32>(Key::Insert);
|
||||||
case SDLK_DELETE: return 54;
|
case SDLK_DELETE: return static_cast<i32>(Key::Delete);
|
||||||
case SDLK_HOME: return 55;
|
case SDLK_HOME: return static_cast<i32>(Key::Home);
|
||||||
case SDLK_END: return 56;
|
case SDLK_END: return static_cast<i32>(Key::End);
|
||||||
case SDLK_PAGEUP: return 57;
|
case SDLK_PAGEUP: return static_cast<i32>(Key::PageUp);
|
||||||
case SDLK_PAGEDOWN: return 58;
|
case SDLK_PAGEDOWN: return static_cast<i32>(Key::PageDown);
|
||||||
case SDLK_UP: return 59;
|
case SDLK_UP: return static_cast<i32>(Key::Up);
|
||||||
case SDLK_DOWN: return 60;
|
case SDLK_DOWN: return static_cast<i32>(Key::Down);
|
||||||
case SDLK_LEFT: return 61;
|
case SDLK_LEFT: return static_cast<i32>(Key::Left);
|
||||||
case SDLK_RIGHT: return 62;
|
case SDLK_RIGHT: return static_cast<i32>(Key::Right);
|
||||||
case SDLK_LSHIFT: return 63;
|
case SDLK_LSHIFT: return static_cast<i32>(Key::LShift);
|
||||||
case SDLK_RSHIFT: return 64;
|
case SDLK_RSHIFT: return static_cast<i32>(Key::RShift);
|
||||||
case SDLK_LCTRL: return 65;
|
case SDLK_LCTRL: return static_cast<i32>(Key::LCtrl);
|
||||||
case SDLK_RCTRL: return 66;
|
case SDLK_RCTRL: return static_cast<i32>(Key::RCtrl);
|
||||||
case SDLK_LALT: return 67;
|
case SDLK_LALT: return static_cast<i32>(Key::LAlt);
|
||||||
case SDLK_RALT: return 68;
|
case SDLK_RALT: return static_cast<i32>(Key::RAlt);
|
||||||
case SDLK_CAPSLOCK: return 69;
|
case SDLK_CAPSLOCK: return static_cast<i32>(Key::CapsLock);
|
||||||
case SDLK_NUMLOCKCLEAR: return 70;
|
case SDLK_NUMLOCKCLEAR: return static_cast<i32>(Key::NumLock);
|
||||||
case SDLK_SCROLLLOCK: return 71;
|
case SDLK_SCROLLLOCK: return static_cast<i32>(Key::ScrollLock);
|
||||||
default: return -1;
|
default: return static_cast<i32>(Key::None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue