247 lines
6.0 KiB
C++
247 lines
6.0 KiB
C++
#include <cstring>
|
||
#include <renderer/material.h>
|
||
#include <utils/logger.h>
|
||
|
||
namespace extra2d {
|
||
|
||
// ========================================
|
||
// MaterialLayout 实现
|
||
// ========================================
|
||
|
||
MaterialLayout::MaterialLayout() = default;
|
||
|
||
/**
|
||
* @brief 添加参数到布局
|
||
* @param name 参数名称
|
||
* @param type 参数类型
|
||
*/
|
||
void MaterialLayout::addParam(const std::string &name, MaterialParamType type) {
|
||
if (finalized_) {
|
||
E2D_WARN("无法向已完成的材质布局添加参数");
|
||
return;
|
||
}
|
||
|
||
// 检查是否已存在
|
||
if (paramIndexMap_.find(name) != paramIndexMap_.end()) {
|
||
E2D_WARN("参数 '{}' 已存在于材质布局中", name);
|
||
return;
|
||
}
|
||
|
||
MaterialParamInfo info;
|
||
info.type = type;
|
||
info.size = getMaterialParamSize(type);
|
||
info.offset = 0; // 将在 finalize 时计算
|
||
|
||
size_t index = params_.size();
|
||
params_.emplace_back(name, info);
|
||
paramIndexMap_[name] = index;
|
||
}
|
||
|
||
/**
|
||
* @brief 完成布局定义,计算 std140 布局偏移
|
||
*
|
||
* std140 布局规则:
|
||
* - 标量和向量:偏移必须是其大小的倍数
|
||
* - 数组和结构体:偏移必须是 16 的倍数
|
||
* - vec3 后面需要填充到 16 字节边界
|
||
* - 整个结构体大小必须是16的倍数
|
||
*/
|
||
void MaterialLayout::finalize() {
|
||
if (finalized_)
|
||
return;
|
||
|
||
uint32_t offset = 0;
|
||
for (auto &pair : params_) {
|
||
auto &info = pair.second;
|
||
|
||
// 计算对齐要求 - std140规则
|
||
uint32_t alignment = 4; // 默认 4 字节对齐
|
||
|
||
switch (info.type) {
|
||
case MaterialParamType::Float:
|
||
alignment = 4; // float: 4 字节对齐
|
||
break;
|
||
case MaterialParamType::Vec2:
|
||
alignment = 8; // vec2: 8 字节对齐
|
||
break;
|
||
case MaterialParamType::Vec3:
|
||
case MaterialParamType::Vec4:
|
||
case MaterialParamType::Color:
|
||
alignment = 16; // vec3/vec4/color: 16 字节对齐(std140 规则)
|
||
break;
|
||
case MaterialParamType::Mat4:
|
||
alignment = 16; // mat4: 16 字节对齐,每列vec4对齐到16字节
|
||
break;
|
||
default:
|
||
alignment = 4;
|
||
break;
|
||
}
|
||
|
||
// 对齐偏移到alignment的倍数
|
||
offset = (offset + alignment - 1) & ~(alignment - 1);
|
||
|
||
info.offset = offset;
|
||
offset += info.size;
|
||
}
|
||
|
||
// 最终大小对齐到 16 字节(std140要求结构体总大小是16的倍数)
|
||
bufferSize_ = (offset + 15) & ~15;
|
||
finalized_ = true;
|
||
}
|
||
|
||
/**
|
||
* @brief 获取参数信息
|
||
* @param name 参数名称
|
||
* @return 参数信息指针,不存在返回 nullptr
|
||
*/
|
||
const MaterialParamInfo *
|
||
MaterialLayout::getParam(const std::string &name) const {
|
||
auto mapIt = paramIndexMap_.find(name);
|
||
if (mapIt != paramIndexMap_.end()) {
|
||
return ¶ms_[mapIt->second].second;
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
// ========================================
|
||
// Material 实现
|
||
// ========================================
|
||
|
||
Material::Material() = default;
|
||
|
||
/**
|
||
* @brief 设置材质布局
|
||
* @param layout 材质布局
|
||
*/
|
||
void Material::setLayout(Ptr<MaterialLayout> layout) {
|
||
layout_ = layout;
|
||
if (layout_ && layout_->isFinalized()) {
|
||
data_.resize(layout_->getBufferSize(), 0);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 设置着色器
|
||
* @param shader 着色器
|
||
*/
|
||
void Material::setShader(Ptr<Shader> shader) { shader_ = shader; }
|
||
|
||
/**
|
||
* @brief 获取 RHI 管线句柄
|
||
* @return RHI 管线句柄
|
||
*/
|
||
PipelineHandle Material::getPipeline() const {
|
||
if (shader_) {
|
||
return shader_->getPipeline();
|
||
}
|
||
return PipelineHandle{};
|
||
}
|
||
|
||
/**
|
||
* @brief 设置 float 参数
|
||
* @param name 参数名称
|
||
* @param value 值
|
||
*/
|
||
void Material::setFloat(const std::string &name, float value) {
|
||
if (!layout_)
|
||
return;
|
||
|
||
const auto *param = layout_->getParam(name);
|
||
if (param && param->type == MaterialParamType::Float) {
|
||
std::memcpy(data_.data() + param->offset, &value, sizeof(float));
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 设置 vec2 参数
|
||
* @param name 参数名称
|
||
* @param value 值
|
||
*/
|
||
void Material::setVec2(const std::string &name, const Vec2 &value) {
|
||
if (!layout_)
|
||
return;
|
||
|
||
const auto *param = layout_->getParam(name);
|
||
if (param && param->type == MaterialParamType::Vec2) {
|
||
std::memcpy(data_.data() + param->offset, &value.x, sizeof(float) * 2);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 设置 vec4 参数
|
||
* @param name 参数名称
|
||
* @param x X 分量
|
||
* @param y Y 分量
|
||
* @param z Z 分量
|
||
* @param w W 分量
|
||
*/
|
||
void Material::setVec4(const std::string &name, float x, float y, float z,
|
||
float w) {
|
||
if (!layout_)
|
||
return;
|
||
|
||
const auto *param = layout_->getParam(name);
|
||
if (param && param->type == MaterialParamType::Vec4) {
|
||
float values[4] = {x, y, z, w};
|
||
std::memcpy(data_.data() + param->offset, values, sizeof(float) * 4);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 设置颜色参数
|
||
* @param name 参数名称
|
||
* @param value 颜色值
|
||
*/
|
||
void Material::setColor(const std::string &name, const Color &value) {
|
||
if (!layout_)
|
||
return;
|
||
|
||
const auto *param = layout_->getParam(name);
|
||
if (param && param->type == MaterialParamType::Color) {
|
||
std::memcpy(data_.data() + param->offset, &value.r, sizeof(float) * 4);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 设置 mat4 参数
|
||
* @param name 参数名称
|
||
* @param value 矩阵数据指针
|
||
*/
|
||
void Material::setMat4(const std::string &name, const float *value) {
|
||
if (!layout_)
|
||
return;
|
||
|
||
const auto *param = layout_->getParam(name);
|
||
if (param && param->type == MaterialParamType::Mat4) {
|
||
std::memcpy(data_.data() + param->offset, value, sizeof(float) * 16);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @brief 设置纹理
|
||
* @param uniformName 着色器中的采样器 uniform 名称
|
||
* @param texture 纹理
|
||
* @param slot 纹理槽位(0-15)
|
||
*/
|
||
void Material::setTexture(const std::string &uniformName, Ptr<Texture> texture,
|
||
uint32_t slot) {
|
||
// 查找是否已存在相同名称的纹理
|
||
for (auto &texSlot : textures_) {
|
||
if (texSlot.uniformName == uniformName) {
|
||
texSlot.texture = texture;
|
||
texSlot.slot = slot;
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 添加新的纹理槽位
|
||
textures_.push_back({texture, slot, uniformName});
|
||
}
|
||
|
||
/**
|
||
* @brief 清除所有纹理
|
||
*/
|
||
void Material::clearTextures() { textures_.clear(); }
|
||
|
||
} // namespace extra2d
|