Extra2D/src/renderer/material.cpp

247 lines
6.0 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 &params_[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