510 lines
18 KiB
C++
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
|