Files
XCEngine/engine/Runtime/Rendering/Materials/RenderMaterialResolve.h

510 lines
18 KiB
C++

#pragma once
#include <XCEngine/Components/MeshRendererComponent.h>
#include <XCEngine/Core/Types.h>
#include <XCEngine/RHI/RHIBuffer.h>
#include <XCEngine/RHI/RHIEnums.h>
#include <XCEngine/RHI/RHITypes.h>
#include <XCEngine/Resources/Material/Material.h>
#include <XCEngine/Resources/Mesh/Mesh.h>
#include <XCEngine/Rendering/Builtin/BuiltinPassMetadataUtils.h>
#include <XCEngine/Rendering/FrameData/VisibleRenderItem.h>
#include <cstdlib>
#include <cstddef>
namespace XCEngine {
namespace Rendering {
enum class BuiltinSkyboxTextureMode : Core::uint8 {
None = 0,
Panoramic = 1,
Cubemap = 2
};
struct BuiltinSkyboxMaterialData {
Math::Vector4 tint = Math::Vector4::One();
float exposure = 1.0f;
float rotationDegrees = 0.0f;
BuiltinSkyboxTextureMode textureMode = BuiltinSkyboxTextureMode::None;
};
struct BuiltinDepthStyleMaterialConstants {
Math::Vector4 baseColorFactor = Math::Vector4::One();
Math::Vector4 alphaCutoffParams = Math::Vector4(0.5f, 0.0f, 0.0f, 0.0f);
};
struct MaterialConstantLayoutView {
const Resources::MaterialConstantFieldDesc* fields = nullptr;
size_t count = 0;
size_t size = 0;
bool IsValid() const {
return fields != nullptr && count > 0 && size > 0;
}
};
struct MaterialConstantPayloadView {
const void* data = nullptr;
size_t size = 0;
MaterialConstantLayoutView layout = {};
bool IsValid() const {
return data != nullptr && size > 0 && layout.IsValid() && layout.size == size;
}
};
struct MaterialBufferResourceView {
RHI::RHIBuffer* buffer = nullptr;
RHI::ResourceViewType viewType = RHI::ResourceViewType::ShaderResource;
RHI::ResourceViewDesc viewDesc = {};
bool IsValid() const {
return buffer != nullptr &&
(viewType == RHI::ResourceViewType::ShaderResource ||
viewType == RHI::ResourceViewType::UnorderedAccess) &&
viewDesc.dimension != RHI::ResourceViewDimension::Unknown;
}
};
inline bool IsMaterialBufferResourceType(Resources::ShaderResourceType type) {
switch (type) {
case Resources::ShaderResourceType::StructuredBuffer:
case Resources::ShaderResourceType::RawBuffer:
case Resources::ShaderResourceType::RWStructuredBuffer:
case Resources::ShaderResourceType::RWRawBuffer:
return true;
default:
return false;
}
}
inline const Resources::ShaderPropertyDesc* FindShaderPropertyBySemantic(
const Resources::Material* material,
const Containers::String& semantic) {
if (material == nullptr || material->GetShader() == nullptr) {
return nullptr;
}
const Containers::String normalizedSemantic = NormalizeBuiltinPassMetadataValue(semantic);
for (const Resources::ShaderPropertyDesc& property : material->GetShader()->GetProperties()) {
if (NormalizeBuiltinPassMetadataValue(property.semantic) == normalizedSemantic) {
return &property;
}
}
return nullptr;
}
inline Math::Vector4 ResolveBuiltinBaseColorFactor(const Resources::Material* material) {
if (material == nullptr) {
return Math::Vector4::One();
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "BaseColor")) {
if (material->HasProperty(property->name) &&
(property->type == Resources::ShaderPropertyType::Color ||
property->type == Resources::ShaderPropertyType::Vector)) {
return material->GetFloat4(property->name);
}
}
return Math::Vector4::One();
}
inline const Resources::Texture* ResolveBuiltinBaseColorTexture(const Resources::Material* material) {
if (material == nullptr) {
return nullptr;
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "BaseColorTexture")) {
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
if (textureHandle.Get() != nullptr && textureHandle->IsValid()) {
return textureHandle.Get();
}
}
return nullptr;
}
inline float ResolveBuiltinAlphaCutoff(const Resources::Material* material) {
if (material == nullptr) {
return 0.5f;
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "AlphaCutoff")) {
if (material->HasProperty(property->name) &&
(property->type == Resources::ShaderPropertyType::Float ||
property->type == Resources::ShaderPropertyType::Range)) {
return material->GetFloat(property->name);
}
}
return 0.5f;
}
inline bool IsCubemapSkyboxTextureType(Resources::TextureType type) {
return type == Resources::TextureType::TextureCube ||
type == Resources::TextureType::TextureCubeArray;
}
inline bool IsPanoramicSkyboxTextureType(Resources::TextureType type) {
return type == Resources::TextureType::Texture2D ||
type == Resources::TextureType::Texture2DArray;
}
inline const Resources::Texture* ResolveSkyboxPanoramicTexture(const Resources::Material* material) {
if (material == nullptr) {
return nullptr;
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "SkyboxPanoramicTexture")) {
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
if (textureHandle.Get() != nullptr &&
textureHandle->IsValid() &&
IsPanoramicSkyboxTextureType(textureHandle->GetTextureType())) {
return textureHandle.Get();
}
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "SkyboxTexture")) {
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
if (textureHandle.Get() != nullptr &&
textureHandle->IsValid() &&
IsPanoramicSkyboxTextureType(textureHandle->GetTextureType())) {
return textureHandle.Get();
}
}
return nullptr;
}
inline const Resources::Texture* ResolveSkyboxCubemapTexture(const Resources::Material* material) {
if (material == nullptr) {
return nullptr;
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "SkyboxTexture")) {
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
if (textureHandle.Get() != nullptr &&
textureHandle->IsValid() &&
IsCubemapSkyboxTextureType(textureHandle->GetTextureType())) {
return textureHandle.Get();
}
}
return nullptr;
}
inline BuiltinSkyboxTextureMode ResolveSkyboxTextureMode(const Resources::Material* material) {
if (ResolveSkyboxPanoramicTexture(material) != nullptr) {
return BuiltinSkyboxTextureMode::Panoramic;
}
if (ResolveSkyboxCubemapTexture(material) != nullptr) {
return BuiltinSkyboxTextureMode::Cubemap;
}
return BuiltinSkyboxTextureMode::None;
}
inline Math::Vector4 ResolveSkyboxTint(const Resources::Material* material) {
if (material == nullptr) {
return Math::Vector4::One();
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "Tint")) {
if (material->HasProperty(property->name) &&
(property->type == Resources::ShaderPropertyType::Color ||
property->type == Resources::ShaderPropertyType::Vector)) {
return material->GetFloat4(property->name);
}
}
return Math::Vector4::One();
}
inline float ResolveSkyboxExposure(const Resources::Material* material) {
if (material == nullptr) {
return 1.0f;
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "Exposure")) {
if (material->HasProperty(property->name) &&
(property->type == Resources::ShaderPropertyType::Float ||
property->type == Resources::ShaderPropertyType::Range)) {
return material->GetFloat(property->name);
}
}
return 1.0f;
}
inline float ResolveSkyboxRotationDegrees(const Resources::Material* material) {
if (material == nullptr) {
return 0.0f;
}
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "Rotation")) {
if (material->HasProperty(property->name) &&
(property->type == Resources::ShaderPropertyType::Float ||
property->type == Resources::ShaderPropertyType::Range)) {
return material->GetFloat(property->name);
}
}
return 0.0f;
}
inline BuiltinSkyboxMaterialData BuildBuiltinSkyboxMaterialData(const Resources::Material* material) {
BuiltinSkyboxMaterialData data = {};
data.tint = ResolveSkyboxTint(material);
data.exposure = ResolveSkyboxExposure(material);
data.rotationDegrees = ResolveSkyboxRotationDegrees(material);
data.textureMode = ResolveSkyboxTextureMode(material);
return data;
}
inline MaterialConstantPayloadView ResolveSchemaMaterialConstantPayload(const Resources::Material* material) {
if (material == nullptr || material->GetShader() == nullptr) {
return {};
}
const Containers::Array<Resources::MaterialConstantFieldDesc>& constantLayout = material->GetConstantLayout();
const Containers::Array<Core::uint8>& constantBufferData = material->GetConstantBufferData();
if (constantLayout.Empty() || constantBufferData.Empty()) {
return {};
}
MaterialConstantLayoutView layoutView = {};
layoutView.fields = constantLayout.Data();
layoutView.count = constantLayout.Size();
layoutView.size = constantBufferData.Size();
return { constantBufferData.Data(), constantBufferData.Size(), layoutView };
}
inline BuiltinDepthStyleMaterialConstants BuildBuiltinDepthStyleMaterialConstants(
const Resources::Material* material) {
BuiltinDepthStyleMaterialConstants constants = {};
constants.baseColorFactor = ResolveBuiltinBaseColorFactor(material);
constants.alphaCutoffParams = Math::Vector4(ResolveBuiltinAlphaCutoff(material), 0.0f, 0.0f, 0.0f);
return constants;
}
inline MaterialConstantPayloadView ResolveBuiltinDepthStyleMaterialConstantPayload(
const Resources::Material* material,
BuiltinDepthStyleMaterialConstants& outConstants,
Resources::MaterialConstantFieldDesc (&outLayout)[2]) {
outConstants = BuildBuiltinDepthStyleMaterialConstants(material);
outLayout[0].name = "gBaseColorFactor";
outLayout[0].type = Resources::MaterialPropertyType::Float4;
outLayout[0].offset = 0u;
outLayout[0].size = static_cast<Core::uint32>(sizeof(Math::Vector4));
outLayout[0].alignedSize = static_cast<Core::uint32>(sizeof(Math::Vector4));
outLayout[1].name = "gAlphaCutoffParams";
outLayout[1].type = Resources::MaterialPropertyType::Float4;
outLayout[1].offset = static_cast<Core::uint32>(sizeof(Math::Vector4));
outLayout[1].size = static_cast<Core::uint32>(sizeof(Math::Vector4));
outLayout[1].alignedSize = static_cast<Core::uint32>(sizeof(Math::Vector4));
MaterialConstantLayoutView layoutView = {};
layoutView.fields = outLayout;
layoutView.count = 2u;
layoutView.size = sizeof(BuiltinDepthStyleMaterialConstants);
return { &outConstants, sizeof(BuiltinDepthStyleMaterialConstants), layoutView };
}
inline bool TryResolveMaterialBufferResourceView(
const Resources::Material* material,
const BuiltinPassResourceBindingDesc& binding,
MaterialBufferResourceView& outView) {
outView = {};
if (material == nullptr || !IsMaterialBufferResourceType(binding.resourceType)) {
return false;
}
const Resources::MaterialBufferBinding* materialBinding = material->FindBufferBinding(binding.name);
if (materialBinding == nullptr || materialBinding->buffer == nullptr) {
return false;
}
outView.buffer = materialBinding->buffer;
outView.viewDesc.firstElement = materialBinding->viewDesc.firstElement;
outView.viewDesc.elementCount = materialBinding->viewDesc.elementCount;
switch (binding.resourceType) {
case Resources::ShaderResourceType::StructuredBuffer:
outView.viewType = RHI::ResourceViewType::ShaderResource;
outView.viewDesc.dimension = RHI::ResourceViewDimension::StructuredBuffer;
outView.viewDesc.structureByteStride =
materialBinding->viewDesc.structureByteStride > 0
? materialBinding->viewDesc.structureByteStride
: materialBinding->buffer->GetStride();
break;
case Resources::ShaderResourceType::RawBuffer:
outView.viewType = RHI::ResourceViewType::ShaderResource;
outView.viewDesc.dimension = RHI::ResourceViewDimension::RawBuffer;
break;
case Resources::ShaderResourceType::RWStructuredBuffer:
outView.viewType = RHI::ResourceViewType::UnorderedAccess;
outView.viewDesc.dimension = RHI::ResourceViewDimension::StructuredBuffer;
outView.viewDesc.structureByteStride =
materialBinding->viewDesc.structureByteStride > 0
? materialBinding->viewDesc.structureByteStride
: materialBinding->buffer->GetStride();
break;
case Resources::ShaderResourceType::RWRawBuffer:
outView.viewType = RHI::ResourceViewType::UnorderedAccess;
outView.viewDesc.dimension = RHI::ResourceViewDimension::RawBuffer;
break;
default:
return false;
}
if (outView.viewDesc.dimension == RHI::ResourceViewDimension::StructuredBuffer &&
outView.viewDesc.structureByteStride == 0) {
outView = {};
return false;
}
return outView.IsValid();
}
inline const Resources::Material* ResolveMaterial(
const Components::MeshRendererComponent* meshRenderer,
const Resources::Mesh* mesh,
Core::uint32 materialIndex) {
if (meshRenderer != nullptr && materialIndex < meshRenderer->GetMaterialCount()) {
if (const Resources::Material* material = meshRenderer->GetMaterial(materialIndex)) {
return material;
}
}
if (mesh != nullptr && materialIndex < mesh->GetMaterials().Size()) {
if (const Resources::Material* material = mesh->GetMaterials()[materialIndex]) {
return material;
}
}
if (meshRenderer != nullptr && meshRenderer->GetMaterialCount() > 0) {
if (const Resources::Material* material = meshRenderer->GetMaterial(0)) {
return material;
}
}
if (mesh != nullptr && mesh->GetMaterials().Size() > 0) {
return mesh->GetMaterials()[0];
}
return nullptr;
}
inline const Resources::Material* ResolveMaterial(const VisibleRenderItem& visibleItem) {
if (visibleItem.material != nullptr) {
return visibleItem.material;
}
return ResolveMaterial(visibleItem.meshRenderer, visibleItem.mesh, visibleItem.materialIndex);
}
inline bool TryResolveRenderQueueTagValue(
const Containers::String& queueValue,
Core::int32& outRenderQueue) {
const Containers::String normalized = NormalizeBuiltinPassMetadataValue(queueValue);
if (normalized.Empty()) {
return false;
}
if (normalized == Containers::String("background")) {
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Background);
return true;
}
if (normalized == Containers::String("geometry")) {
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Geometry);
return true;
}
if (normalized == Containers::String("alphatest")) {
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::AlphaTest);
return true;
}
if (normalized == Containers::String("transparent")) {
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Transparent);
return true;
}
if (normalized == Containers::String("overlay")) {
outRenderQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Overlay);
return true;
}
char* end = nullptr;
const long parsedValue = std::strtol(normalized.CStr(), &end, 10);
if (end != nullptr && *end == '\0') {
outRenderQueue = static_cast<Core::int32>(parsedValue);
return true;
}
return false;
}
inline bool TryResolveShaderPassRenderQueue(const Resources::ShaderPass& shaderPass, Core::int32& outRenderQueue) {
for (const Resources::ShaderPassTagEntry& tag : shaderPass.tags) {
if (NormalizeBuiltinPassMetadataValue(tag.name) == Containers::String("queue") &&
TryResolveRenderQueueTagValue(tag.value, outRenderQueue)) {
return true;
}
}
return false;
}
inline Core::int32 ResolveMaterialRenderQueue(const Resources::Material* material) {
const Core::int32 defaultQueue = static_cast<Core::int32>(Resources::MaterialRenderQueue::Geometry);
if (material == nullptr) {
return defaultQueue;
}
const Core::int32 materialQueue = material->GetRenderQueue();
if (materialQueue != defaultQueue) {
return materialQueue;
}
if (const Resources::Shader* shader = material->GetShader()) {
for (const Resources::ShaderPass& pass : shader->GetPasses()) {
Core::int32 shaderQueue = defaultQueue;
if (TryResolveShaderPassRenderQueue(pass, shaderQueue)) {
return shaderQueue;
}
}
}
return materialQueue;
}
inline bool IsTransparentRenderQueue(Core::int32 renderQueue) {
return renderQueue >= static_cast<Core::int32>(Resources::MaterialRenderQueue::Transparent);
}
inline bool MatchesBuiltinPass(const Resources::Material* material, BuiltinMaterialPass pass) {
if (material == nullptr) {
return false;
}
const Resources::Shader* shader = material->GetShader();
if (shader == nullptr) {
return false;
}
for (const Resources::ShaderPass& shaderPassEntry : shader->GetPasses()) {
if (ShaderPassMatchesBuiltinPass(shaderPassEntry, pass)) {
return true;
}
}
return false;
}
} // namespace Rendering
} // namespace XCEngine