Extra2D/src/renderer/material.cpp

159 lines
4.5 KiB
C++
Raw Normal View History

#include <renderer/material.h>
#include <utils/logger.h>
#include <cstring>
namespace extra2d {
// MaterialLayout 实现
MaterialLayout::MaterialLayout() = default;
void MaterialLayout::addParam(const std::string& name, MaterialParamType type) {
if (finalized_) {
E2D_LOG_WARN("Cannot add param to finalized MaterialLayout");
return;
}
MaterialParamInfo info;
info.type = type;
info.size = getMaterialParamSize(type);
info.offset = 0; // 将在 finalize 时计算
params_[name] = info;
}
void MaterialLayout::finalize() {
if (finalized_) return;
// 计算 std140 布局的偏移
// std140 规则:
// - 标量和向量:偏移必须是其大小的倍数
// - 数组和结构体:偏移必须是 16 的倍数
// - vec3 后面需要填充到 16 字节边界
uint32_t offset = 0;
for (auto& pair : params_) {
auto& info = pair.second;
// 对齐到参数大小的倍数
uint32_t alignment = info.size;
if (info.type == MaterialParamType::Mat4) {
alignment = 16; // mat4 需要 16 字节对齐
} else if (info.type == MaterialParamType::Vec3) {
alignment = 16; // vec3 在 std140 中占 16 字节
} else if (alignment < 4) {
alignment = 4; // 最小 4 字节对齐
}
// 对齐偏移
offset = (offset + alignment - 1) & ~(alignment - 1);
info.offset = offset;
offset += info.size;
}
// 最终大小对齐到 16 字节
bufferSize_ = (offset + 15) & ~15;
finalized_ = true;
}
const MaterialParamInfo* MaterialLayout::getParam(const std::string& name) const {
auto it = params_.find(name);
if (it != params_.end()) {
return &it->second;
}
return nullptr;
}
// Material 实现
Material::Material() = default;
void Material::setLayout(Ptr<MaterialLayout> layout) {
layout_ = layout;
if (layout_ && layout_->isFinalized()) {
data_.resize(layout_->getBufferSize(), 0);
}
}
void Material::setShader(Ptr<Shader> shader) {
shader_ = shader;
}
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));
}
}
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);
}
}
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);
}
}
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);
}
}
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);
}
}
void Material::setTexture(const std::string& name, TextureHandle texture, uint32_t slot) {
// 纹理存储在单独的列表中
textures_.push_back({texture, slot});
}
void Material::apply(UniformBufferManager& uboManager) {
if (!shader_) return;
// 绑定着色器
shader_->bind();
// 上传材质数据到 UBO
if (!data_.empty()) {
auto* ubo = uboManager.acquireMaterialUBO(static_cast<uint32_t>(data_.size()));
if (ubo) {
ubo->update(data_.data(), static_cast<uint32_t>(data_.size()));
ubo->bind(MATERIAL_UBO_BINDING);
}
}
// 设置 Uniform Block 绑定(如果着色器支持)
shader_->setUniformBlock("MaterialUBO", MATERIAL_UBO_BINDING);
// TODO: 绑定纹理
// for (const auto& [texture, slot] : textures_) {
// // 通过纹理句柄获取纹理并绑定
// }
}
} // namespace extra2d