159 lines
4.5 KiB
C++
159 lines
4.5 KiB
C++
|
|
#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
|