merging from main

This commit is contained in:
greerdv
2021-04-15 08:18:11 +01:00
2503 changed files with 45147 additions and 527864 deletions
@@ -40,6 +40,7 @@
#include <SceneAPI/SceneCore/DataTypes/Rules/ICoordinateSystemRule.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/ILodRule.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/ISkinRule.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IClothRule.h>
#include <SceneAPI/SceneCore/Events/ExportEventContext.h>
#include <SceneAPI/SceneCore/Utilities/SceneGraphSelector.h>
#include <SceneAPI/SceneCore/Utilities/Reporting.h>
@@ -93,6 +94,11 @@ namespace
const AZ::RHI::Format MorphTargetPositionDeltaFormat = AZ::RHI::Format::R16_UINT; // 16-bit integer per compressed position delta component
const char* ShaderSemanticName_MorphTargetNormalDeltas = "MORPHTARGET_NORMALDELTAS";
const AZ::RHI::Format MorphTargetNormalDeltaFormat = AZ::RHI::Format::R8_UINT; // 8-bit integer per compressed normal delta component
// Cloth data
const char* const ShaderSemanticName_ClothData = "CLOTH_DATA";
const uint32_t ClothDataFloatsPerVert = 4;
const AZ::RHI::Format ClothDataFormat = AZ::RHI::Format::R32G32B32A32_FLOAT;
}
namespace AZ
@@ -108,11 +114,12 @@ namespace AZ
if (auto* serialize = azrtti_cast<SerializeContext*>(context))
{
serialize->Class<ModelAssetBuilderComponent, SceneAPI::SceneCore::ExportingComponent>()
->Version(25); // [ATOM-14876]
->Version(26); // [ATOM-14992]
}
}
ModelAssetBuilderComponent::ModelAssetBuilderComponent()
: m_numSkinJointInfluencesPerVertex(DefaultSkinInfluencesPerVert)
{
BindToCall(&ModelAssetBuilderComponent::BuildModel);
}
@@ -331,6 +338,12 @@ namespace AZ
sourceMesh.m_isMorphed = GetIsMorphed(sceneGraph, node);
// Get the cloth data (only for full mesh LOD 0).
sourceMesh.m_meshClothData = (lodIndex == 0)
? SceneAPI::DataTypes::IClothRule::FindClothData(
sceneGraph, node, sourceMesh.m_meshData->GetVertexCount(), context.m_group.GetRuleContainerConst())
: AZStd::vector<AZ::Color>{};
// We've traversed this node and all its children that hold
// relevant data We can move it into the list of content for this lod
sourceMeshContentList.emplace_back(AZStd::move(sourceMesh));
@@ -362,6 +375,8 @@ namespace AZ
{
ProductMeshContentList lodMeshes = SourceMeshListToProductMeshList(context, sourceMeshContentList, jointNameToIndexMap, morphTargetMetaCreator);
PadVerticesForSkinning(lodMeshes);
// By default, we merge meshes that share the same material
bool canMergeMeshes = true;
@@ -371,17 +386,19 @@ namespace AZ
// If the merge meshes option is disabled in the advanced mesh rule, don't merge meshes
canMergeMeshes = false;
}
for (const SourceMeshContent& sourceMesh : sourceMeshContentList)
else
{
if (sourceMesh.m_isMorphed)
for (const SourceMeshContent& sourceMesh : sourceMeshContentList)
{
// Merging meshes shuffles around the order of the vertices, but morph targets rely on having an index that tell them which vertices to morph
// We do not merge morphed meshes so that this index is preserved and correct.
// If we keep track of the ordering changes in MergeMeshesByMaterialUid and then re-mapped the MORPHTARGET_VERTEXINDICES buffer
// we could potentially enable merging meshes that are morphed. But for now, disable merging.
canMergeMeshes = false;
break;
if (sourceMesh.m_isMorphed)
{
// Merging meshes shuffles around the order of the vertices, but morph targets rely on having an index that tell them which vertices to morph
// We do not merge morphed meshes so that this index is preserved and correct.
// If we keep track of the ordering changes in MergeMeshesByMaterialUid and then re-mapped the MORPHTARGET_VERTEXINDICES buffer
// we could potentially enable merging meshes that are morphed. But for now, disable merging.
canMergeMeshes = false;
break;
}
}
}
@@ -396,12 +413,12 @@ namespace AZ
ProductMeshViewList lodMeshViews;
ProductMeshContent mergedMesh;
MergeMeshesToCommonBuffers(context, lodMeshes, mergedMesh, lodMeshViews);
MergeMeshesToCommonBuffers(lodMeshes, mergedMesh, lodMeshViews);
BufferAssetView indexBuffer;
AZStd::vector<ModelLodAsset::Mesh::StreamBufferInfo> streamBuffers;
if (!CreateModelLodBuffers(context, mergedMesh, indexBuffer, streamBuffers, lodAssetCreator))
if (!CreateModelLodBuffers(mergedMesh, indexBuffer, streamBuffers, lodAssetCreator))
{
return AZ::SceneAPI::Events::ProcessingResult::Failure;
}
@@ -417,7 +434,7 @@ namespace AZ
uint32_t meshIndex = 0;
for (const ProductMeshContent& mesh : lodMeshes)
{
const ProductMeshView meshView = CreateViewToEntireMesh(context, mesh);
const ProductMeshView meshView = CreateViewToEntireMesh(mesh);
BufferAssetView indexBuffer;
AZStd::vector<ModelLodAsset::Mesh::StreamBufferInfo> streamBuffers;
@@ -425,7 +442,7 @@ namespace AZ
// Mesh name in ProductMeshContent could be duplicated so generate unique mesh name using index
m_meshName = AZStd::string::format("mesh%d", meshIndex++);
if (!CreateModelLodBuffers(context, mesh, indexBuffer, streamBuffers, lodAssetCreator))
if (!CreateModelLodBuffers(mesh, indexBuffer, streamBuffers, lodAssetCreator))
{
return AZ::SceneAPI::Events::ProcessingResult::Failure;
}
@@ -614,7 +631,11 @@ namespace AZ
productMeshList.reserve(productMeshCount);
// Get the skin rule
AZStd::shared_ptr<const SceneAPI::DataTypes::ISkinRule> skinRule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneAPI::DataTypes::ISkinRule>();
if (const auto* skinRule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneAPI::DataTypes::ISkinRule>().get())
{
m_numSkinJointInfluencesPerVertex = skinRule->GetMaxWeightsPerVertex();
m_skinWeightThreshold = skinRule->GetWeightThreshold();
}
uint32_t totalVertexCount = 0;
for (size_t i = 0; i < productList.size(); ++i)
@@ -632,6 +653,7 @@ namespace AZ
const auto& colorContentCollection = sourceMesh.m_meshColorData;
const size_t colorSetCount = colorContentCollection.size();
bool processedMorphTargets = false;
bool warnedExcessOfSkinInfluences = false;
for (const auto& it : productsByMaterialUid)
{
@@ -677,6 +699,7 @@ namespace AZ
AZStd::vector<AZ::Name>& uvNames = productMesh.m_uvCustomNames;
AZStd::vector<AZStd::vector<float>>& colorSets = productMesh.m_colorSets;
AZStd::vector<AZ::Name>& colorNames = productMesh.m_colorCustomNames;
AZStd::vector<float>& clothData = productMesh.m_clothData;
const size_t vertexCount = oldToNewIndices.size();
positions.reserve(vertexCount * PositionFloatsPerVert);
@@ -716,7 +739,16 @@ namespace AZ
colorSet.reserve(vertexCount * ColorFloatsPerVert);
}
if (!sourceMesh.m_skinData.empty())
const bool hasClothData = !sourceMesh.m_meshClothData.empty();
if (hasClothData)
{
AZ_Assert(sourceMesh.m_meshClothData.size() == vertexCount,
"Vertex Count %d does not match mesh cloth data size %d", vertexCount, sourceMesh.m_meshClothData.size());
clothData.reserve(vertexCount * ClothDataFloatsPerVert);
}
const bool hasSkinData = !sourceMesh.m_skinData.empty();
if (hasSkinData)
{
// Skinned meshes require that positions, normals, tangents, bitangents, all exist and have the same number
// of total elements. Pad buffers with missing data to make them align with positions and normals
@@ -817,8 +849,23 @@ namespace AZ
colors.push_back(color.alpha);
}
// Gather Cloth Data
if (hasClothData)
{
const AZ::Color& vertexClothData = sourceMesh.m_meshClothData[oldIndex];
clothData.push_back(vertexClothData.GetR());
clothData.push_back(vertexClothData.GetG());
clothData.push_back(vertexClothData.GetB());
clothData.push_back(vertexClothData.GetA());
}
// Gather skinning influences
GatherSkinningInfluences(context, sourceMesh, productMesh, jointNameToIndexMap, oldIndex);
if (hasSkinData)
{
// Warn about excess of skin influences once per-source mesh.
GatherVertexSkinningInfluences(sourceMesh, productMesh, jointNameToIndexMap, oldIndex, warnedExcessOfSkinInfluences);
}
}
if(!processedMorphTargets)
@@ -836,19 +883,71 @@ namespace AZ
return productMeshList;
}
void ModelAssetBuilderComponent::GatherSkinningInfluences(
const ModelAssetBuilderContext& context,
void ModelAssetBuilderComponent::PadVerticesForSkinning(ProductMeshContentList& productMeshList)
{
// Check if this is a skinned mesh
if (!productMeshList.empty() && !productMeshList[0].m_skinWeights.empty())
{
// First, do a pass to see if any mesh has morphed colors
bool hasMorphedColors = false;
for (ProductMeshContent& productMesh : productMeshList)
{
if (productMesh.m_hasMorphedColors)
{
hasMorphedColors = true;
break;
}
}
for (ProductMeshContent& productMesh : productMeshList)
{
size_t vertexCount = productMesh.m_positions.size() / PositionFloatsPerVert;
// Skinned meshes require that positions, normals, tangents, bitangents, all exist and have the same number
// of total elements. Pad buffers with missing data to make them align with positions and normals
if (productMesh.m_tangents.empty())
{
productMesh.m_tangents.resize(vertexCount * TangentFloatsPerVert, 1.0f);
AZ_Warning(s_builderName, false, "Mesh '%s' is missing tangents and no defaults were generated. Skinned meshes require tangents. Dummy tangents will be inserted, which may result in rendering artifacts.", productMesh.m_name.GetCStr());
}
if (productMesh.m_bitangents.empty())
{
productMesh.m_bitangents.resize(vertexCount * BitangentFloatsPerVert, 1.0f);
AZ_Warning(s_builderName, false, "Mesh '%s' is missing bitangents and no defaults were generated. Skinned meshes require bitangents. Dummy bitangents will be inserted, which may result in rendering artifacts.", productMesh.m_name.GetCStr());
}
// If any of the meshes have morphed colors, padd all the meshes so that the color stream is aligned with the other skinned streams
if (hasMorphedColors)
{
if (productMesh.m_colorCustomNames.empty())
{
productMesh.m_colorCustomNames.push_back(Name{ "COLOR" });
}
if (productMesh.m_colorSets.empty())
{
productMesh.m_colorSets.resize(1);
}
if (productMesh.m_colorSets[0].empty())
{
productMesh.m_colorSets[0].resize(vertexCount * ColorFloatsPerVert, 0.0f);
}
}
}
}
}
void ModelAssetBuilderComponent::GatherVertexSkinningInfluences(
const SourceMeshContent& sourceMesh,
ProductMeshContent& productMesh,
AZStd::unordered_map<AZStd::string, uint16_t>& jointNameToIndexMap,
size_t vertexIndex) const
size_t vertexIndex,
bool& warnedExcessOfSkinInfluences) const
{
AZStd::vector<uint16_t>& skinJointIndices = productMesh.m_skinJointIndices;
AZStd::vector<float>& skinWeights = productMesh.m_skinWeights;
const auto& sourceMeshData = sourceMesh.m_meshData;
const SceneAPI::DataTypes::ISkinRule* skinRule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneAPI::DataTypes::ISkinRule>().get();
const size_t maxNumInfluences = ExtractMaxNumInfluencesPerVertex(skinRule);
const float weightThreshold = skinRule ? skinRule->GetWeightThreshold() : 0.0f;
size_t numInfluencesAdded = 0;
for (const auto& skinData : sourceMesh.m_skinData)
@@ -857,8 +956,9 @@ namespace AZ
const AZ::u32 controlPointIndex = sourceMeshData->GetControlPointIndex(vertexIndex);
const size_t numSkinInfluences = skinData->GetLinkCount(controlPointIndex);
const size_t numInfluencesToAdd = AZStd::min<size_t>(numSkinInfluences, maxNumInfluences - numInfluencesAdded);
for (size_t influenceIndex = 0; influenceIndex < numInfluencesToAdd; ++influenceIndex)
size_t numInfluencesExcess = 0;
for (size_t influenceIndex = 0; influenceIndex < numSkinInfluences; ++influenceIndex)
{
const AZ::SceneAPI::DataTypes::ISkinWeightData::Link& link = skinData->GetLink(controlPointIndex, influenceIndex);
@@ -874,30 +974,37 @@ namespace AZ
const AZ::u16 jointIndex = jointNameToIndexMap[boneName];
// Add skin influence
if (weight > weightThreshold)
if (weight > m_skinWeightThreshold)
{
skinJointIndices.push_back(jointIndex);
skinWeights.push_back(weight);
numInfluencesAdded++;
if (numInfluencesAdded < m_numSkinJointInfluencesPerVertex)
{
skinJointIndices.push_back(jointIndex);
skinWeights.push_back(weight);
numInfluencesAdded++;
}
else
{
numInfluencesExcess++;
}
}
}
if (numInfluencesAdded > maxNumInfluences)
if (numInfluencesExcess > 0)
{
AZ_WarningOnce(s_builderName, false, "More skin influences (%d) on data than supported (%d). Skinning influences won't be normalized.",
numSkinInfluences, maxNumInfluences);
AZ_Warning(s_builderName, warnedExcessOfSkinInfluences,
"Mesh %s has more skin influences (%d) than the maximum (%d). Skinning influences won't be normalized. Maximum number of skin influences can be increased with a Skin Modifier in FBX Settings.",
sourceMesh.m_name.GetCStr(),
m_numSkinJointInfluencesPerVertex + numInfluencesExcess,
m_numSkinJointInfluencesPerVertex);
warnedExcessOfSkinInfluences = true;
break;
}
}
if (!sourceMesh.m_skinData.empty() &&
numInfluencesAdded < maxNumInfluences)
for (size_t influenceIndex = numInfluencesAdded; influenceIndex < m_numSkinJointInfluencesPerVertex; ++influenceIndex)
{
for (size_t influenceIndex = numInfluencesAdded; influenceIndex < maxNumInfluences; ++influenceIndex)
{
skinJointIndices.push_back(0);
skinWeights.push_back(0.0f);
}
skinJointIndices.push_back(0);
skinWeights.push_back(0.0f);
}
}
@@ -914,7 +1021,6 @@ namespace AZ
for (const ProductMeshContent& mesh : productMeshList)
{
// Disable mesh merging whenever a mesh is morphed.
if (mesh.CanBeMerged())
{
meshCountByMatUid[mesh.m_materialUid]++;
@@ -970,9 +1076,11 @@ namespace AZ
}
template<typename T>
void ModelAssetBuilderComponent::ValidateStreamSize([[maybe_unused]] size_t expectedVertexCount, const AZStd::vector<T>& bufferData, AZ::RHI::Format format, [[maybe_unused]] const char* streamName) const
void ModelAssetBuilderComponent::ValidateStreamSize([[maybe_unused]] size_t expectedVertexCount, [[maybe_unused]] const AZStd::vector<T>& bufferData, [[maybe_unused]] AZ::RHI::Format format, [[maybe_unused]] const char* streamName) const
{
#if defined(AZ_ENABLE_TRACING)
size_t actualVertexCount = (bufferData.size() * sizeof(T)) / RHI::GetFormatSize(format);
#endif
AZ_Error(s_builderName, expectedVertexCount == actualVertexCount, "VertexStream '%s' does not match the expected vertex count. This typically means multiple sub-meshes have mis-matched vertex stream layouts (such as one having more uv sets than the other) but are assigned the same material in the dcc tool so they were merged.", streamName);
}
@@ -999,9 +1107,21 @@ namespace AZ
{
ValidateStreamSize(expectedVertexCount, mesh.m_colorSets[i], ColorFormat, mesh.m_colorCustomNames[i].GetCStr());
}
if (!mesh.m_clothData.empty())
{
ValidateStreamSize(expectedVertexCount, mesh.m_clothData, ClothDataFormat, ShaderSemanticName_ClothData);
}
if (!mesh.m_skinJointIndices.empty())
{
ValidateStreamSize(expectedVertexCount * m_numSkinJointInfluencesPerVertex, mesh.m_skinJointIndices, AZ::RHI::Format::R16_UINT, ShaderSemanticName_SkinJointIndices);
}
if (!mesh.m_skinWeights.empty())
{
ValidateStreamSize(expectedVertexCount * m_numSkinJointInfluencesPerVertex, mesh.m_skinWeights, SkinWeightFormat, ShaderSemanticName_SkinWeights);
}
}
ModelAssetBuilderComponent::ProductMeshView ModelAssetBuilderComponent::CreateViewToEntireMesh(const ModelAssetBuilderContext& context, const ProductMeshContent& mesh)
ModelAssetBuilderComponent::ProductMeshView ModelAssetBuilderComponent::CreateViewToEntireMesh(const ProductMeshContent& mesh)
{
ProductMeshView meshView;
meshView.m_name = mesh.m_name.GetStringView();
@@ -1059,16 +1179,13 @@ namespace AZ
if (!mesh.m_skinJointIndices.empty() && !mesh.m_skinWeights.empty())
{
const SceneAPI::DataTypes::ISkinRule* skinRule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneAPI::DataTypes::ISkinRule>().get();
const AZ::u32 maxNumSkinInfluencesPerVertex = ExtractMaxNumInfluencesPerVertex(skinRule);
AZ_Assert(mesh.m_skinJointIndices.size() == mesh.m_skinWeights.size(),
"Number of skin influence joint indices (%d) should match the number of weights (%d).",
mesh.m_skinJointIndices.size(), mesh.m_skinWeights.size());
AZ_Assert(mesh.m_skinWeights.size() % maxNumSkinInfluencesPerVertex == 0,
AZ_Assert(mesh.m_skinWeights.size() % m_numSkinJointInfluencesPerVertex == 0,
"The number of skin influences per vertex (%d) is not a multiple of the total number of skinning weights (%d). This means that not every vertex has exactly (%d) skinning weights and invalidates the data.",
mesh.m_skinWeights.size(), maxNumSkinInfluencesPerVertex, maxNumSkinInfluencesPerVertex);
mesh.m_skinWeights.size(), m_numSkinJointInfluencesPerVertex, m_numSkinJointInfluencesPerVertex);
const size_t numSkinInfluences = mesh.m_skinWeights.size();
uint32_t jointIndicesSizeInBytes = numSkinInfluences * sizeof(uint16_t);
@@ -1082,13 +1199,25 @@ namespace AZ
meshView.m_morphTargetVertexDataView = RHI::BufferViewDescriptor::CreateStructured(0, numTotalVertices, sizeof(PackedCompressedMorphTargetDelta));
}
if (!mesh.m_clothData.empty())
{
auto meshClothDataFloatCount = static_cast<uint32_t>(mesh.m_clothData.size());
AZ_Assert((meshClothDataFloatCount % ClothDataFloatsPerVert) == 0,
"Unexpected number of cloth data elements (%d), it should contain a multiple of %d elements.", meshClothDataFloatCount, ClothDataFloatsPerVert);
auto meshClothDataCount = meshClothDataFloatCount / ClothDataFloatsPerVert;
AZ_Assert(meshClothDataCount == meshPositionCount,
"Number of cloth data elements (%d) does not match the number of positions (%d) in the mesh", meshClothDataCount, meshPositionCount);
meshView.m_clothDataView = RHI::BufferViewDescriptor::CreateTyped(0, meshClothDataCount, ClothDataFormat);
}
meshView.m_materialUid = mesh.m_materialUid;
return meshView;
}
void ModelAssetBuilderComponent::MergeMeshesToCommonBuffers(
const ModelAssetBuilderContext& context,
const ProductMeshContentList& lodMeshList,
ProductMeshContent& lodMeshContent,
ProductMeshViewList& meshViews)
@@ -1120,6 +1249,7 @@ namespace AZ
auto meshNormalsFloatCount = static_cast<uint32_t>(mesh.m_normals.size());
auto meshTangentsFloatCount = static_cast<uint32_t>(mesh.m_tangents.size());
auto meshBitangentsFloatCount = static_cast<uint32_t>(mesh.m_bitangents.size());
auto meshClothDataFloatCount = static_cast<uint32_t>(mesh.m_clothData.size());
// For each element we need to:
// record the offset for the view
@@ -1199,20 +1329,24 @@ namespace AZ
}
}
if (!mesh.m_clothData.empty())
{
const uint32_t elementOffset = static_cast<uint32_t>(lodBufferInfo.m_clothDataFloatCount) / ClothDataFloatsPerVert;
meshView.m_clothDataView = RHI::BufferViewDescriptor::CreateTyped(elementOffset, meshVertexCount, ClothDataFormat);
lodBufferInfo.m_clothDataFloatCount += meshClothDataFloatCount;
}
meshView.m_materialUid = mesh.m_materialUid;
if (!mesh.m_skinJointIndices.empty() && !mesh.m_skinWeights.empty())
{
const SceneAPI::DataTypes::ISkinRule* skinRule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneAPI::DataTypes::ISkinRule>().get();
const AZ::u32 maxNumSkinInfluencesPerVertex = ExtractMaxNumInfluencesPerVertex(skinRule);
AZ_Assert(mesh.m_skinJointIndices.size() == mesh.m_skinWeights.size(),
"Number of skin influence joint indices (%d) should match the number of weights (%d).",
mesh.m_skinJointIndices.size(), mesh.m_skinWeights.size());
AZ_Assert(mesh.m_skinWeights.size() % maxNumSkinInfluencesPerVertex == 0,
AZ_Assert(mesh.m_skinWeights.size() % m_numSkinJointInfluencesPerVertex == 0,
"The number of skin influences per vertex (%d) is not a multiple of the total number of skinning weights (%d). This means that not every vertex has exactly (%d) skinning weights and invalidates the data.",
mesh.m_skinWeights.size(), maxNumSkinInfluencesPerVertex, maxNumSkinInfluencesPerVertex);
mesh.m_skinWeights.size(), m_numSkinJointInfluencesPerVertex, m_numSkinJointInfluencesPerVertex);
const size_t numPrevSkinInfluences = lodBufferInfo.m_skinInfluencesCount;
const size_t numNewSkinInfluences = mesh.m_skinWeights.size();
@@ -1253,6 +1387,7 @@ namespace AZ
size_t normalCount = 0;
size_t tangentCount = 0;
size_t bitangentCount = 0;
size_t clothDataCount = 0;
AZStd::vector<size_t> uvSetCounts;
AZStd::vector<size_t> colorSetCounts;
@@ -1263,6 +1398,7 @@ namespace AZ
normalCount += mesh.m_normals.size();
tangentCount += mesh.m_tangents.size();
bitangentCount += mesh.m_bitangents.size();
clothDataCount += mesh.m_clothData.size();
if (mesh.m_uvSets.size() > uvSetCounts.size())
{
@@ -1290,6 +1426,7 @@ namespace AZ
mergedMesh.m_normals.reserve(normalCount);
mergedMesh.m_tangents.reserve(tangentCount);
mergedMesh.m_bitangents.reserve(bitangentCount);
mergedMesh.m_clothData.reserve(clothDataCount);
mergedMesh.m_uvCustomNames.resize(uvSetCounts.size());
for (auto& mesh : productMeshList)
@@ -1424,6 +1561,12 @@ namespace AZ
auto& mergedMorphTargetData = mergedMesh.m_morphTargetVertexData;
mergedMorphTargetData.insert(mergedMorphTargetData.end(), sourceMorphTargetData.begin(), sourceMorphTargetData.end());
}
if (!mesh.m_clothData.empty())
{
mergedMesh.m_clothData.insert(
mergedMesh.m_clothData.end(), mesh.m_clothData.begin(), mesh.m_clothData.end());
}
}
return mergedMesh;
@@ -1522,7 +1665,6 @@ namespace AZ
};
bool ModelAssetBuilderComponent::CreateModelLodBuffers(
const ModelAssetBuilderContext& context,
const ProductMeshContent& lodBufferContent,
BufferAssetView& outIndexBuffer,
AZStd::vector<ModelLodAsset::Mesh::StreamBufferInfo>& outStreamBuffers,
@@ -1537,6 +1679,7 @@ namespace AZ
const AZStd::vector<AZ::Name>& uvCustomNames = lodBufferContent.m_uvCustomNames;
const AZStd::vector<AZStd::vector<float>>& colorSets = lodBufferContent.m_colorSets;
const AZStd::vector<AZ::Name>& colorCustomNames = lodBufferContent.m_colorCustomNames;
const AZStd::vector<float>& clothData = lodBufferContent.m_clothData;
// Build Index Buffer ...
{
@@ -1598,10 +1741,8 @@ namespace AZ
const AZStd::vector<float>& skinWeights = lodBufferContent.m_skinWeights;
if (!skinJointIndices.empty() && !skinWeights.empty())
{
const SceneAPI::DataTypes::ISkinRule* skinRule = context.m_group.GetRuleContainerConst().FindFirstByType<SceneAPI::DataTypes::ISkinRule>().get();
const AZ::u32 maxNumSkinInfluencesPerVertex = ExtractMaxNumInfluencesPerVertex(skinRule);
const size_t vertexCount = positions.size() / PositionFloatsPerVert;
const size_t numSkinInfluences = vertexCount * maxNumSkinInfluencesPerVertex;
const size_t numSkinInfluences = vertexCount * m_numSkinJointInfluencesPerVertex;
if (!BuildRawStreamBuffer<uint16_t>(outStreamBuffers, skinJointIndices, RHI::ShaderSemantic{ShaderSemanticName_SkinJointIndices}))
{
@@ -1625,6 +1766,14 @@ namespace AZ
}
}
if (!clothData.empty())
{
if (!BuildTypedStreamBuffer<float>(outStreamBuffers, clothData, ClothDataFormat, RHI::ShaderSemantic{ ShaderSemanticName_ClothData }))
{
return false;
}
}
lodAssetCreator.SetLodIndexBuffer(outIndexBuffer.GetBufferAsset());
for (const auto& streamBufferInfo : outStreamBuffers)
@@ -1758,6 +1907,15 @@ namespace AZ
}
}
// Set cloth data buffer
if (meshView.m_clothDataView.m_elementCount > 0)
{
if (!SetMeshStreamBufferById(RHI::ShaderSemantic{ ShaderSemanticName_ClothData }, AZ::Name(), meshView.m_clothDataView, lodStreamBuffers, lodAssetCreator))
{
return false;
}
}
lodAssetCreator.EndMesh();
return true;
@@ -2014,15 +2172,5 @@ namespace AZ
return transform;
}
AZ::u32 ModelAssetBuilderComponent::ExtractMaxNumInfluencesPerVertex(const SceneAPI::DataTypes::ISkinRule* skinRule) const
{
if (skinRule)
{
return skinRule->GetMaxWeightsPerVertex();
}
return DefaultSkinInfluencesPerVert;
}
} // namespace RPI
} // namespace AZ
@@ -90,6 +90,7 @@ namespace AZ
AZStd::vector<AZStd::shared_ptr<const UVData>> m_meshUVData;
AZStd::vector<AZStd::shared_ptr<const ColorData>> m_meshColorData;
AZStd::vector<AZStd::shared_ptr<const SkinData>> m_skinData;
AZStd::vector<AZ::Color> m_meshClothData;
AZStd::vector<MaterialUid> m_materials;
bool m_isMorphed = false;
@@ -110,6 +111,7 @@ namespace AZ
AZStd::vector<AZ::Name> m_uvCustomNames;
AZStd::vector<AZStd::vector<float>> m_colorSets;
AZStd::vector<AZ::Name> m_colorCustomNames;
AZStd::vector<float> m_clothData;
//! Joint index per vertex in range [0, numJoints].
//! Note: The joint indices have to match the used skeleton when applying skinning.
@@ -121,7 +123,8 @@ namespace AZ
AZStd::vector<RPI::PackedCompressedMorphTargetDelta> m_morphTargetVertexData;
MaterialUid m_materialUid;
bool CanBeMerged() const { return true; }
bool CanBeMerged() const { return m_clothData.empty(); }
bool m_hasMorphedColors = false;
};
using ProductMeshContentList = AZStd::vector<ProductMeshContent>;
@@ -143,6 +146,7 @@ namespace AZ
size_t m_normalsFloatCount = 0;
size_t m_tangentsFloatCount = 0;
size_t m_bitangentsFloatCount = 0;
size_t m_clothDataFloatCount = 0;
AZStd::vector<size_t> m_uvSetFloatCounts;
AZStd::vector<size_t> m_colorSetFloatCounts;
size_t m_skinInfluencesCount = 0;
@@ -173,6 +177,8 @@ namespace AZ
RHI::BufferViewDescriptor m_morphTargetVertexDataView;
RHI::BufferViewDescriptor m_clothDataView;
MaterialUid m_materialUid;
};
using ProductMeshViewList = AZStd::vector<ProductMeshView>;
@@ -192,18 +198,23 @@ namespace AZ
AZStd::unordered_map<AZStd::string, uint16_t>& jointNameToIndexMap,
MorphTargetMetaAssetCreator& morphTargetMetaCreator);
//! Checks if this is a skinned mesh and if soe,
//! adds some extra padding to make vertex streams align for skinning
//! Skinning is applied on an entire lod at once, so it presumes that
//! Each vertex stream that is modified by skinning is the same length
void PadVerticesForSkinning(ProductMeshContentList& productMeshList);
//! Takes in a ProductMeshContentList and merges all elements that share the same MaterialUid.
ProductMeshContentList MergeMeshesByMaterialUid(
const ProductMeshContentList& productMeshList);
//! Simple helper to create a MeshView that views an entire given ProductMeshContent object as one mesh.
ProductMeshView CreateViewToEntireMesh(const ModelAssetBuilderContext& context, const ProductMeshContent& mesh);
ProductMeshView CreateViewToEntireMesh(const ProductMeshContent& mesh);
//! Takes a ProductMeshContentList and merges all elements into a single ProductMeshContent object.
//! This also produces a ProductMeshViewList that contains views to all
//! the original meshes described in the lodMeshList collection.
void MergeMeshesToCommonBuffers(
const ModelAssetBuilderContext& context,
const ProductMeshContentList& lodMeshList,
ProductMeshContent& lodMeshContent,
ProductMeshViewList& meshViewsPerLodBuffer);
@@ -274,7 +285,6 @@ namespace AZ
//!
//! Returns false if an error occurs
bool CreateModelLodBuffers(
const ModelAssetBuilderContext& context,
const ProductMeshContent& lodBufferContent,
BufferAssetView& outIndexBuffer,
AZStd::vector<ModelLodAsset::Mesh::StreamBufferInfo>& outStreamBuffers,
@@ -356,6 +366,9 @@ namespace AZ
AZStd::string m_lodName;
AZStd::string m_meshName;
size_t m_numSkinJointInfluencesPerVertex = 0;
float m_skinWeightThreshold = 0.0f;
AZStd::set<uint32_t> m_createdSubId;
// NOTE: This is explicitly fetched from a filename. In the future, this should be fetched from the RPI system
@@ -366,15 +379,13 @@ namespace AZ
SceneAPI::DataTypes::MatrixType GetWorldTransform(const SceneAPI::Containers::SceneGraph& sceneGraph, SceneAPI::Containers::SceneGraph::NodeIndex node);
private:
//! Collects skinning influences from the SceneAPI source mesh and fills them in the resulting mesh
void GatherSkinningInfluences(
const ModelAssetBuilderContext& context,
//! Collects skinning influences of a vertex from the SceneAPI source mesh and fills them in the resulting mesh
void GatherVertexSkinningInfluences(
const SourceMeshContent& sourceMesh,
ProductMeshContent& productMesh,
AZStd::unordered_map<AZStd::string, uint16_t>& jointNameToIndexMap,
size_t vertexIndex) const;
AZ::u32 ExtractMaxNumInfluencesPerVertex(const SceneAPI::DataTypes::ISkinRule* skinRule) const;
size_t vertexIndex,
bool& warnedExcessOfSkinInfluences) const;
};
} // namespace RPI
} // namespace AZ
@@ -104,8 +104,10 @@ namespace AZ::RPI
AZ_Assert(blendShapeData, "Node is expected to be a blend shape.");
if (blendShapeData)
{
#if defined(AZ_ENABLE_TRACING)
const Containers::SceneGraph::NodeIndex morphMeshParentIndex = sceneGraph.GetNodeParent(sceneNodeIndex);
const char* meshNodeName = sceneGraph.GetNodeName(morphMeshParentIndex).GetName();
#endif
AZ_Assert(AZ::StringFunc::Equal(sourceMesh.m_name.GetCStr(), meshNodeName, /*bCaseSensitive=*/true),
"Scene graph mesh node (%s) has a different name than the product mesh (%s).",
@@ -230,25 +232,35 @@ namespace AZ::RPI
const AZ::Vector3 deltaNormal = targetNormal - neutralNormal;
currentDelta.m_normalX = Compress<uint8_t>(deltaNormal.GetX(), -2.0f, 2.0f);
currentDelta.m_normalY = Compress<uint8_t>(deltaNormal.GetY(), -2.0f, 2.0f);
currentDelta.m_normalZ = Compress<uint8_t>(deltaNormal.GetZ(), -2.0f, 2.0f);
currentDelta.m_normalX = Compress<uint8_t>(deltaNormal.GetX(), MorphTargetDeltaConstants::s_tangentSpaceDeltaMin, MorphTargetDeltaConstants::s_tangentSpaceDeltaMax);
currentDelta.m_normalY = Compress<uint8_t>(deltaNormal.GetY(), MorphTargetDeltaConstants::s_tangentSpaceDeltaMin, MorphTargetDeltaConstants::s_tangentSpaceDeltaMax);
currentDelta.m_normalZ = Compress<uint8_t>(deltaNormal.GetZ(), MorphTargetDeltaConstants::s_tangentSpaceDeltaMin, MorphTargetDeltaConstants::s_tangentSpaceDeltaMax);
}
// Tangent
{
// Insert zero-delta until morphed tangents are supported in SceneAPI
currentDelta.m_tangentX = Compress<uint8_t>(0.0f, -2.0f, 2.0f);
currentDelta.m_tangentY = Compress<uint8_t>(0.0f, -2.0f, 2.0f);
currentDelta.m_tangentZ = Compress<uint8_t>(0.0f, -2.0f, 2.0f);
currentDelta.m_tangentX = Compress<uint8_t>(0.0f, MorphTargetDeltaConstants::s_tangentSpaceDeltaMin, MorphTargetDeltaConstants::s_tangentSpaceDeltaMax);
currentDelta.m_tangentY = Compress<uint8_t>(0.0f, MorphTargetDeltaConstants::s_tangentSpaceDeltaMin, MorphTargetDeltaConstants::s_tangentSpaceDeltaMax);
currentDelta.m_tangentZ = Compress<uint8_t>(0.0f, MorphTargetDeltaConstants::s_tangentSpaceDeltaMin, MorphTargetDeltaConstants::s_tangentSpaceDeltaMax);
}
// Bitangent
{
// Insert zero-delta until morphed bitangents are supported in SceneAPI
currentDelta.m_bitangentX = Compress<uint8_t>(0.0f, -2.0f, 2.0f);
currentDelta.m_bitangentY = Compress<uint8_t>(0.0f, -2.0f, 2.0f);
currentDelta.m_bitangentZ = Compress<uint8_t>(0.0f, -2.0f, 2.0f);
currentDelta.m_bitangentX = Compress<uint8_t>(0.0f, MorphTargetDeltaConstants::s_tangentSpaceDeltaMin, MorphTargetDeltaConstants::s_tangentSpaceDeltaMax);
currentDelta.m_bitangentY = Compress<uint8_t>(0.0f, MorphTargetDeltaConstants::s_tangentSpaceDeltaMin, MorphTargetDeltaConstants::s_tangentSpaceDeltaMax);
currentDelta.m_bitangentZ = Compress<uint8_t>(0.0f, MorphTargetDeltaConstants::s_tangentSpaceDeltaMin, MorphTargetDeltaConstants::s_tangentSpaceDeltaMax);
}
// Color
{
metaData.m_hasColorDeltas = true;
productMesh.m_hasMorphedColors = true;
currentDelta.m_colorR = Compress<uint8_t>(0.0f, MorphTargetDeltaConstants::s_colorDeltaMin, MorphTargetDeltaConstants::s_colorDeltaMax);
currentDelta.m_colorG = Compress<uint8_t>(0.0f, MorphTargetDeltaConstants::s_colorDeltaMin, MorphTargetDeltaConstants::s_colorDeltaMax);
currentDelta.m_colorB = Compress<uint8_t>(0.0f, MorphTargetDeltaConstants::s_colorDeltaMin, MorphTargetDeltaConstants::s_colorDeltaMax);
currentDelta.m_colorA = Compress<uint8_t>(0.0f, MorphTargetDeltaConstants::s_colorDeltaMin, MorphTargetDeltaConstants::s_colorDeltaMax);
}
}
}
@@ -50,7 +50,6 @@ namespace AZ
return false;
}
MaterialPropertyDataType dataType = propertyDescriptor->GetDataType();
AZ::TypeId typeId = propertyDescriptor->GetStorageDataTypeId();
auto iter = m_possibleValues.find(typeId);
if (iter != m_possibleValues.end())
@@ -60,7 +59,7 @@ namespace AZ
if (!m_resolvedValue.IsValid())
{
AZ_Error("MaterialPropertyValueSourceData", false, "Value for material property '%s' is invalid. %s is required.", materialPropertyName.GetCStr(), ToString(dataType));
AZ_Error("MaterialPropertyValueSourceData", false, "Value for material property '%s' is invalid. %s is required.", materialPropertyName.GetCStr(), ToString(propertyDescriptor->GetDataType()));
return false;
}
@@ -237,25 +237,23 @@ namespace AZ
}
}
void CullingSystem::RegisterOrUpdateCullable(Cullable& cullable)
void CullingScene::RegisterOrUpdateCullable(Cullable& cullable)
{
// [GFX TODO][ATOM-15036] Remove lock from CullingSystem visibility updates
m_mutex.lock();
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->InsertOrUpdateEntry(cullable.m_cullData.m_visibilityEntry);
m_mutex.unlock();
m_cullDataConcurrencyCheck.soft_lock();
m_visScene->InsertOrUpdateEntry(cullable.m_cullData.m_visibilityEntry);
m_cullDataConcurrencyCheck.soft_unlock();
}
void CullingSystem::UnregisterCullable(Cullable& cullable)
void CullingScene::UnregisterCullable(Cullable& cullable)
{
// [GFX TODO][ATOM-15036] Remove lock from CullingSystem visibility updates
m_mutex.lock();
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->RemoveEntry(cullable.m_cullData.m_visibilityEntry);
m_mutex.unlock();
m_cullDataConcurrencyCheck.soft_lock();
m_visScene->RemoveEntry(cullable.m_cullData.m_visibilityEntry);
m_cullDataConcurrencyCheck.soft_unlock();
}
uint32_t CullingSystem::GetNumCullables() const
uint32_t CullingScene::GetNumCullables() const
{
return AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetEntryCount();
return m_visScene->GetEntryCount();
}
class AddObjectsToViewJob final
@@ -269,10 +267,10 @@ namespace AZ
const Scene* m_scene;
View* m_view;
Frustum m_frustum;
CullingSystem::WorkListType m_worklist;
CullingScene::WorkListType m_worklist;
public:
AddObjectsToViewJob(CullingDebugContext& debugCtx, const Scene& scene, View& view, Frustum& frustum, CullingSystem::WorkListType& worklist)
AddObjectsToViewJob(CullingDebugContext& debugCtx, const Scene& scene, View& view, Frustum& frustum, CullingScene::WorkListType& worklist)
: Job(true, nullptr) //auto-deletes, no JobContext
, m_debugCtx(&debugCtx)
, m_scene(&scene)
@@ -292,7 +290,7 @@ namespace AZ
uint32_t numDrawPackets = 0;
uint32_t numVisibleCullables = 0;
for (const AzFramework::IVisibilitySystem::NodeData& nodeData : m_worklist)
for (const AzFramework::IVisibilityScene::NodeData& nodeData : m_worklist)
{
//If a node is entirely contained within the frustum, then we can skip the fine grained culling.
bool nodeIsContainedInFrustum = ShapeIntersection::Contains(m_frustum, nodeData.m_bounds);
@@ -415,9 +413,9 @@ namespace AZ
}
};
void CullingSystem::ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob)
void CullingScene::ProcessCullables(const Scene& scene, View& view, AZ::Job& parentJob)
{
AZ_PROFILE_SCOPE_DYNAMIC(Debug::ProfileCategory::AzRender, "CullingSystem::ProcessCullables() - %s", view.GetName().GetCStr());
AZ_PROFILE_SCOPE_DYNAMIC(Debug::ProfileCategory::AzRender, "CullingScene::ProcessCullables() - %s", view.GetName().GetCStr());
const Matrix4x4& worldToClip = view.GetWorldToClipMatrix();
Frustum frustum = Frustum::CreateFromMatrixColumnMajor(worldToClip);
@@ -447,7 +445,7 @@ namespace AZ
}
WorkListType worklist;
auto nodeVisitorLambda = [this, &scene, &view, &parentJob, &frustum, &worklist](const AzFramework::IVisibilitySystem::NodeData& nodeData) -> void
auto nodeVisitorLambda = [this, &scene, &view, &parentJob, &frustum, &worklist](const AzFramework::IVisibilityScene::NodeData& nodeData) -> void
{
AZ_PROFILE_SCOPE(Debug::ProfileCategory::AzRender, "nodeVisitorLambda()");
AZ_Assert(nodeData.m_entries.size() > 0, "should not get called with 0 entries");
@@ -469,11 +467,11 @@ namespace AZ
if (m_debugCtx.m_enableFrustumCulling)
{
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->Enumerate(frustum, nodeVisitorLambda);
m_visScene->Enumerate(frustum, nodeVisitorLambda);
}
else
{
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->EnumerateNoCull(nodeVisitorLambda);
m_visScene->EnumerateNoCull(nodeVisitorLambda);
}
if (worklist.size() > 0)
@@ -534,23 +532,34 @@ namespace AZ
return numVisibleDrawPackets;
}
void CullingSystem::Activate(const Scene* parentScene)
void CullingScene::Activate(const Scene* parentScene)
{
m_parentScene = parentScene;
AZ_Assert(m_visScene == nullptr, "IVisibilityScene already created for this RPI::Scene");
char sceneIdBuf[40] = "";
m_parentScene->GetId().ToString(sceneIdBuf);
AZ::Name visSceneName(AZStd::string::format("RenderCullScene[%s]", sceneIdBuf));
m_visScene = AZ::Interface<AzFramework::IVisibilitySystem>::Get()->CreateVisibilityScene(visSceneName);
#ifdef AZ_CULL_DEBUG_ENABLED
AZ_Assert(CountObjectsInScene() == 0, "The culling system should start with 0 entries in this scene.");
#endif
}
void CullingSystem::Deactivate()
void CullingScene::Deactivate()
{
#ifdef AZ_CULL_DEBUG_ENABLED
AZ_Assert(CountObjectsInScene() == 0, "All culling entries must be removed from the scene before shutdown.");
#endif
if (m_visScene)
{
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->DestroyVisibilityScene(m_visScene);
m_visScene = nullptr;
}
}
void CullingSystem::BeginCulling(const AZStd::vector<ViewPtr>& views)
void CullingScene::BeginCulling(const AZStd::vector<ViewPtr>& views)
{
m_cullDataConcurrencyCheck.soft_lock();
@@ -591,16 +600,16 @@ namespace AZ
}
}
void CullingSystem::EndCulling()
void CullingScene::EndCulling()
{
m_cullDataConcurrencyCheck.soft_unlock();
}
size_t CullingSystem::CountObjectsInScene()
size_t CullingScene::CountObjectsInScene()
{
size_t numObjects = 0;
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->EnumerateNoCull(
[this, &numObjects](const AzFramework::IVisibilitySystem::NodeData& nodeData)
m_visScene->EnumerateNoCull(
[this, &numObjects](const AzFramework::IVisibilityScene::NodeData& nodeData)
{
for (AzFramework::VisibilityEntry* visibleEntry : nodeData.m_entries)
{
@@ -79,7 +79,7 @@ namespace AZ
}
// Tell the FrameGraph which RHI QueryPool, and which RHI Queries need to be used.
RHI::ResultCode resultCode = frameGraph.UseQueryPool(m_queryPool->m_rhiQueryPool, rhiQueryIndices.value(), m_attachmentType, m_attachmentAccess);
[[maybe_unused]] RHI::ResultCode resultCode = frameGraph.UseQueryPool(m_queryPool->m_rhiQueryPool, rhiQueryIndices.value(), m_attachmentType, m_attachmentAccess);
AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to add the queries to the scope builder");
// Invalidate the ScopeId.
@@ -108,7 +108,7 @@ namespace AZ
return QueryResultCode::Fail;
}
RHI::ResultCode resultCode = m_queryPool->BeginQueryInternal(rhiQueryIndices.value(), *context.GetCommandList());
[[maybe_unused]] RHI::ResultCode resultCode = m_queryPool->BeginQueryInternal(rhiQueryIndices.value(), *context.GetCommandList());
AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to begin recording the query");
m_cachedScopeId = context.GetScopeId();
@@ -144,7 +144,7 @@ namespace AZ
return QueryResultCode::Fail;
}
RHI::ResultCode resultCode = m_queryPool->EndQueryInternal(rhiQueryIndices.value(), *context.GetCommandList());
[[maybe_unused]] RHI::ResultCode resultCode = m_queryPool->EndQueryInternal(rhiQueryIndices.value(), *context.GetCommandList());
AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to end recording the query");
return QueryResultCode::Success;
@@ -57,7 +57,7 @@ namespace AZ
queryPoolDesc.m_pipelineStatisticsMask = m_statisticsFlags;
m_rhiQueryPool = RHI::Factory::Get().CreateQueryPool();
auto result = m_rhiQueryPool->Init(*device, queryPoolDesc);
[[maybe_unused]] auto result = m_rhiQueryPool->Init(*device, queryPoolDesc);
AZ_Assert(result == RHI::ResultCode::Success, "Failed to create the query pool");
}
@@ -504,7 +504,7 @@ namespace AZ
// Re-initialize the image.
Shutdown();
RHI::ResultCode resultCode = Init(*imageAsset);
[[maybe_unused]] RHI::ResultCode resultCode = Init(*imageAsset);
AZ_Assert(resultCode == RHI::ResultCode::Success, "Failed to re-initialize streaming image");
}
@@ -137,7 +137,7 @@ namespace AZ
return m_modelAsset;
}
bool Model::LocalRayIntersection(const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distance) const
bool Model::LocalRayIntersection(const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distance, AZ::Vector3& normal) const
{
AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender);
float firstHit;
@@ -150,7 +150,7 @@ namespace AZ
AZ::Debug::Timer timer;
timer.Stamp();
#endif
const bool hit = modelAssetPtr->LocalRayIntersectionAgainstModel(rayStart, dir, distance);
const bool hit = modelAssetPtr->LocalRayIntersectionAgainstModel(rayStart, dir, distance, normal);
#if defined(AZ_RPI_PROFILE_RAYCASTING_AGAINST_MODELS)
if (hit)
{
@@ -164,7 +164,7 @@ namespace AZ
return false;
}
bool Model::RayIntersection(const AZ::Transform& modelTransform, const AZ::Vector3& nonUniformScale, const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distanceFactor) const
bool Model::RayIntersection(const AZ::Transform& modelTransform, const AZ::Vector3& nonUniformScale, const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distanceFactor, AZ::Vector3& normal) const
{
AZ_PROFILE_FUNCTION(Debug::ProfileCategory::AzRender);
const AZ::Transform inverseTM = modelTransform.GetInverse();
@@ -175,7 +175,9 @@ namespace AZ
const AZ::Vector3 rayDestLocal = inverseTM.TransformPoint(rayDest) / nonUniformScale;
const AZ::Vector3 rayDirLocal = rayDestLocal - raySrcLocal;
return LocalRayIntersection(raySrcLocal, rayDirLocal, distanceFactor);
bool result = LocalRayIntersection(raySrcLocal, rayDirLocal, distanceFactor, normal);
normal = (normal * nonUniformScale).GetNormalized();
return result;
}
const AZStd::unordered_set<AZ::Name>& Model::GetUvNames() const
@@ -92,7 +92,7 @@ namespace AZ
AZ_Assert(pass->m_parent == this, "Trying to remove a pass of which we are not the parent.");
// Find child and move it to the end of the list
auto it = AZStd::remove(m_children.begin(), m_children.end(), pass);
[[maybe_unused]] auto it = AZStd::remove(m_children.begin(), m_children.end(), pass);
AZ_Assert((it + 1) == m_children.end(), "Pass::RemoveChild found more than one Ptr<Pass> in m_children, which is not allowed.");
// Delete the child that is now at the end of the list
@@ -220,7 +220,7 @@ namespace AZ
TemplateEntry* entry = GetEntry(pass->m_template->m_name);
if (entry)
{
auto iter = AZStd::remove(entry->m_passes.begin(), entry->m_passes.end(), pass);
[[maybe_unused]] auto iter = AZStd::remove(entry->m_passes.begin(), entry->m_passes.end(), pass);
AZ_Assert((iter + 1) == entry->m_passes.end(),
"Pass [%s] is being deleted but was not registered with it's PassTemlate [%s] in the PassLibrary.",
@@ -372,7 +372,6 @@ namespace AZ
{
RHI::Format format = imageAttachment.m_imageDescriptor.m_format;
AZStd::string formatLocation = AZStd::string::format("PassAttachmentDesc [%s] on PassTemplate [%s]", imageAttachment.m_name.GetCStr(), passTemplate->m_name.GetCStr());
RHI::FormatCapabilities capabilities = RHI::FormatCapabilities::Sample;
imageAttachment.m_imageDescriptor.m_format = RHI::ValidateFormat(format, formatLocation.c_str(), imageAttachment.m_formatFallbacks);
}
@@ -136,15 +136,16 @@ namespace AZ
// -- View & DrawList --
const AZStd::vector<ViewPtr>& views = m_pipeline->GetViews(GetPipelineViewTag());
m_drawListView = {};
for (const ViewPtr& view : views)
if (!views.empty())
{
const ViewPtr& view = views.front();
// Assert the view has our draw list (the view's DrawlistTags are collected from passes using its viewTag)
AZ_Assert(view->HasDrawListTag(m_drawListTag), "View's DrawListTags out of sync with pass'. ");
// Draw List
m_drawListView = view->GetDrawList(m_drawListTag);
break;
}
RenderPass::FrameBeginInternal(params);
@@ -82,7 +82,7 @@ namespace AZ
}
RHI::RenderAttachmentLayout layout;
RHI::ResultCode result = builder.End(layout);
[[maybe_unused]] RHI::ResultCode result = builder.End(layout);
AZ_Assert(result == RHI::ResultCode::Success, "RenderPass [%s] failed to create render attachment layout", GetPathName().GetCStr());
return RHI::RenderAttachmentConfiguration{ layout, 0 };
}
@@ -151,7 +151,6 @@ namespace AZ
{
if (passAttachment == binding.m_attachment)
{
RHI::AttachmentType type = binding.m_attachment->GetAttachmentType();
RHI::AttachmentId attachmentId = binding.m_attachment->GetAttachmentId();
// Append slot index and pass name so the read back's name won't be same as the attachment used in other passes.
@@ -496,6 +496,11 @@ namespace AZ
m_renderMode = RenderMode::NoRender;
m_rootPass->SetEnabled(false);
}
RenderPipeline::RenderMode RenderPipeline::GetRenderMode() const
{
return m_renderMode;
}
bool RenderPipeline::NeedsRender() const
{
+10 -10
View File
@@ -85,7 +85,7 @@ namespace AZ
Scene::Scene()
{
m_id = Uuid::CreateRandom();
m_cullingSystem = aznew CullingSystem();
m_cullingScene = aznew CullingScene();
SceneRequestBus::Handler::BusConnect(m_id);
}
@@ -105,7 +105,7 @@ namespace AZ
m_pipelines.clear();
AZ::RPI::PassSystemInterface::Get()->ProcessQueuedChanges();
delete m_cullingSystem;
delete m_cullingScene;
}
void Scene::Activate()
@@ -114,7 +114,7 @@ namespace AZ
m_activated = true;
m_cullingSystem->Activate(this);
m_cullingScene->Activate(this);
// We have to tick the PassSystem in order for all the pass attachments to get created.
// This has to be done before FeatureProcessors are activated, because they may try to
@@ -139,7 +139,7 @@ namespace AZ
fp->Deactivate();
}
m_cullingSystem->Deactivate();
m_cullingScene->Deactivate();
m_activated = false;
m_pipelineStatesLookup.clear();
@@ -424,8 +424,8 @@ namespace AZ
// Init render packet
m_renderPacket.m_views.clear();
AZ_Assert(m_cullingSystem, "Culling System is not initialized");
m_renderPacket.m_cullingSystem = m_cullingSystem;
AZ_Assert(m_cullingScene, "m_cullingScene is not initialized");
m_renderPacket.m_cullingScene = m_cullingScene;
m_renderPacket.m_jobPolicy = jobPolicy;
@@ -486,15 +486,15 @@ namespace AZ
}
// Launch CullingSystem::ProcessCullables() jobs (will run concurrently with FeatureProcessor::Render() jobs)
m_cullingSystem->BeginCulling(m_renderPacket.m_views);
m_cullingScene->BeginCulling(m_renderPacket.m_views);
for (ViewPtr& viewPtr : m_renderPacket.m_views)
{
AZ::Job* processCullablesJob = AZ::CreateJobFunction([this, &viewPtr](AZ::Job& thisJob)
{
m_cullingSystem->ProcessCullables(*this, *viewPtr, thisJob);
m_cullingScene->ProcessCullables(*this, *viewPtr, thisJob);
},
true, nullptr); //auto-deletes
if (m_cullingSystem->GetDebugContext().m_parallelOctreeTraversal)
if (m_cullingScene->GetDebugContext().m_parallelOctreeTraversal)
{
processCullablesJob->SetDependent(collectDrawPacketsCompletion);
processCullablesJob->Start();
@@ -507,7 +507,7 @@ namespace AZ
WaitAndCleanCompletionJob(collectDrawPacketsCompletion);
m_cullingSystem->EndCulling();
m_cullingScene->EndCulling();
// Add dynamic draw data for all the views
if (m_dynamicDrawSystem)
@@ -288,6 +288,11 @@ namespace AZ
return false;
}
bool ShaderResourceGroup::SetImageViewUnboundedArray(RHI::ShaderInputImageUnboundedArrayIndex inputIndex, AZStd::array_view<const RHI::ImageView*> imageViews)
{
return m_data.SetImageViewUnboundedArray(inputIndex, imageViews);
}
bool ShaderResourceGroup::SetBufferView(RHI::ShaderInputNameIndex& inputIndex, const RHI::BufferView* bufferView, uint32_t arrayIndex)
{
if (inputIndex.ValidateOrFindBufferIndex(GetLayout()))
@@ -334,6 +339,11 @@ namespace AZ
return false;
}
bool ShaderResourceGroup::SetBufferViewUnboundedArray(RHI::ShaderInputBufferUnboundedArrayIndex inputIndex, AZStd::array_view<const RHI::BufferView*> bufferViews)
{
return m_data.SetBufferViewUnboundedArray(inputIndex, bufferViews);
}
bool ShaderResourceGroup::SetSampler(RHI::ShaderInputNameIndex& inputIndex, const RHI::SamplerState& sampler, uint32_t arrayIndex)
{
if (inputIndex.ValidateOrFindSamplerIndex(GetLayout()))
@@ -103,7 +103,6 @@ namespace AZ
pairItor->m_shaderAsset->GetShaderOptionGroupLayout(), pairItor->m_shaderVariantId);
if (searchResult.IsRoot())
{
AZ_Error(LogName, false, "Searching for a variant should never yield the root variant: %s", shaderVariantTreeAsset.GetHint().c_str());
pairItor = newShaderVariantPendingRequests.erase(pairItor);
continue;
}
@@ -101,8 +101,11 @@ namespace AZ
void ViewportContext::RenderTick()
{
if (m_currentPipeline)
// add the current pipeline to next render tick if it's not already added.
if (m_currentPipeline && m_currentPipeline->GetRenderMode() != RenderPipeline::RenderMode::RenderOnce)
{
ViewportContextNotificationBus::Event(GetName(), &ViewportContextNotificationBus::Events::OnRenderTick);
ViewportContextIdNotificationBus::Event(GetId(), &ViewportContextIdNotificationBus::Events::OnRenderTick);
m_currentPipeline->AddToRenderTickOnce();
}
}
@@ -23,6 +23,7 @@ namespace AZ
ViewportContextManager::ViewportContextManager()
{
AZ::Interface<ViewportContextRequestsInterface>::Register(this);
m_defaultViewportContextName = AZ::Name(s_defaultViewportContextName);
}
ViewportContextManager::~ViewportContextManager()
@@ -187,7 +188,12 @@ namespace AZ
AZ::Name ViewportContextManager::GetDefaultViewportContextName() const
{
return AZ::Name(s_defaultViewportContextName);
return m_defaultViewportContextName;
}
ViewportContextPtr ViewportContextManager::GetDefaultViewportContext() const
{
return GetViewportContextByName(m_defaultViewportContextName);
}
void ViewportContextManager::PushView(const Name& context, ViewPtr view)
@@ -63,8 +63,7 @@ namespace AZ
}
else
{
const Name& shaderOptionName = layout->GetShaderOption(optionIndex).GetName();
AZ_Error("MaterialFunctor", false, "Shader option '%s' is not owned by this material.", shaderOptionName.GetCStr());
AZ_Error("MaterialFunctor", false, "Shader option '%s' is not owned by this material.", layout->GetShaderOption(optionIndex).GetName().GetCStr());
}
return false;
@@ -395,11 +394,13 @@ namespace AZ
template const Color& MaterialFunctor::EditorContext::GetMaterialPropertyValue<Color> (const MaterialPropertyIndex& index) const;
template const Data::Instance<Image>& MaterialFunctor::EditorContext::GetMaterialPropertyValue<Data::Instance<Image>> (const MaterialPropertyIndex& index) const;
void CheckPropertyAccess(const MaterialPropertyIndex& index, const MaterialPropertyFlags& materialPropertyDependencies, const MaterialPropertiesLayout& materialPropertiesLayout)
void CheckPropertyAccess(const MaterialPropertyIndex& index, const MaterialPropertyFlags& materialPropertyDependencies, [[maybe_unused]] const MaterialPropertiesLayout& materialPropertiesLayout)
{
if (!materialPropertyDependencies.test(index.GetIndex()))
{
#if defined(AZ_ENABLE_TRACING)
const MaterialPropertyDescriptor* propertyDescriptor = materialPropertiesLayout.GetPropertyDescriptor(index);
#endif
AZ_Error("MaterialFunctor", false, "Material functor accessing an unregistered material property '%s'.",
propertyDescriptor ? propertyDescriptor->GetName().GetCStr() : "<unknown>");
}
@@ -75,7 +75,7 @@ namespace AZ
m_status = Data::AssetData::AssetStatus::Ready;
}
bool ModelAsset::LocalRayIntersectionAgainstModel(const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distance) const
bool ModelAsset::LocalRayIntersectionAgainstModel(const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distance, AZ::Vector3& normal) const
{
AZ_PROFILE_FUNCTION(AZ::Debug::ProfileCategory::AzRender);
@@ -97,11 +97,11 @@ namespace AZ
}
else
{
return m_kdTree->RayIntersection(rayStart, dir, distance);
return m_kdTree->RayIntersection(rayStart, dir, distance, normal);
}
}
return BruteForceRayIntersect(rayStart, dir, distance);
return BruteForceRayIntersect(rayStart, dir, distance, normal);
}
void ModelAsset::BuildKdTree() const
@@ -136,7 +136,7 @@ namespace AZ
}
}
bool ModelAsset::BruteForceRayIntersect(const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distance) const
bool ModelAsset::BruteForceRayIntersect(const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distance, AZ::Vector3& normal) const
{
// brute force - check every triangle
if (GetLodAssets().empty() == false)
@@ -147,12 +147,18 @@ namespace AZ
float shortestDistance = std::numeric_limits<float>::max();
bool anyHit = false;
AZ::Vector3 intersectionNormal;
for (const ModelLodAsset::Mesh& mesh : loadAssetPtr->GetMeshes())
{
if (LocalRayIntersectionAgainstMesh(mesh, rayStart, dir, distance))
if (LocalRayIntersectionAgainstMesh(mesh, rayStart, dir, distance, intersectionNormal))
{
anyHit = true;
shortestDistance = AZ::GetMin(distance, shortestDistance);
if (distance < shortestDistance)
{
normal = intersectionNormal;
shortestDistance = distance;
}
}
}
@@ -168,7 +174,7 @@ namespace AZ
return false;
}
bool ModelAsset::LocalRayIntersectionAgainstMesh(const ModelLodAsset::Mesh& mesh, const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distance) const
bool ModelAsset::LocalRayIntersectionAgainstMesh(const ModelLodAsset::Mesh& mesh, const AZ::Vector3& rayStart, const AZ::Vector3& dir, float& distance, AZ::Vector3& normal) const
{
const BufferAssetView& indexBufferView = mesh.GetIndexBufferAssetView();
const AZStd::array_view<ModelLodAsset::Mesh::StreamBufferInfo>& streamBufferList = mesh.GetStreamBufferInfoList();
@@ -216,7 +222,7 @@ namespace AZ
const AZ::Vector3 rayEnd = rayStart + dir * distance;
AZ::Vector3 a, b, c;
AZ::Vector3 normal;
AZ::Vector3 intersectionNormal;
float normalizedDistance = 1.f;
const AZ::u32* indexPtr = reinterpret_cast<const AZ::u32*>(indexRawBuffer.data());
@@ -241,9 +247,13 @@ namespace AZ
p = reinterpret_cast<const float*>(&positionRawBuffer[index2 * positionElementSize]);
c.Set(const_cast<float*>(p));
if (AZ::Intersect::IntersectSegmentTriangleCCW(rayStart, rayEnd, a, b, c, normal, normalizedDistance))
if (AZ::Intersect::IntersectSegmentTriangleCCW(rayStart, rayEnd, a, b, c, intersectionNormal, normalizedDistance))
{
closestNormalizedDistance = AZ::GetMin(closestNormalizedDistance, normalizedDistance);
if (normalizedDistance < closestNormalizedDistance)
{
normal = intersectionNormal;
closestNormalizedDistance = normalizedDistance;
}
anyHit = true;
}
}
@@ -138,31 +138,14 @@ namespace AZ
AZStd::array_view<float> ModelKdTree::GetPositionsBuffer(const ModelLodAsset::Mesh& mesh)
{
const BufferAssetView* positionBufferAssetView = mesh.GetSemanticBufferAssetView(AZ::Name{"POSITION"});
if (positionBufferAssetView)
{
const AZStd::array_view<uint8_t> positionRawBuffer = positionBufferAssetView->GetBufferAsset()->GetBuffer();
const auto size = positionBufferAssetView->GetBufferViewDescriptor().m_elementSize;
return {
reinterpret_cast<const float*>(positionRawBuffer.data() + positionBufferAssetView->GetBufferViewDescriptor().m_elementOffset * size),
positionBufferAssetView->GetBufferViewDescriptor().m_elementCount * size / sizeof(float)
};
}
AZ_Warning("ModelKdTree", false, "Could not find position buffers in a mesh");
return {};
AZStd::array_view<float> positionBuffer = mesh.GetSemanticBufferTyped<float>(AZ::Name{"POSITION"});
AZ_Warning("ModelKdTree", !positionBuffer.empty(), "Could not find position buffers in a mesh");
return positionBuffer;
}
AZStd::array_view<ModelKdTree::TriangleIndices> ModelKdTree::GetIndexBuffer(const ModelLodAsset::Mesh& mesh)
{
const BufferAssetView& indexBufferAssetView = mesh.GetIndexBufferAssetView();
const AZStd::array_view<uint8_t> indexRawBuffer = indexBufferAssetView.GetBufferAsset()->GetBuffer();
const auto size = indexBufferAssetView.GetBufferViewDescriptor().m_elementSize;
static_assert(sizeof(TriangleIndices) == 3 * sizeof(uint32_t));
return {
reinterpret_cast<const TriangleIndices*>(indexRawBuffer.data() + indexBufferAssetView.GetBufferViewDescriptor().m_elementOffset * size),
indexBufferAssetView.GetBufferViewDescriptor().m_elementCount * size / sizeof(TriangleIndices)
};
return mesh.GetIndexBufferTyped<ModelKdTree::TriangleIndices>();
}
void ModelKdTree::BuildRecursively(ModelKdTreeNode* pNode, const AZ::Aabb& boundbox, AZStd::vector<ObjectIdTriangleIndices>& indices)
@@ -221,12 +204,12 @@ namespace AZ
}
}
bool ModelKdTree::RayIntersection(const AZ::Vector3& raySrc, const AZ::Vector3& rayDir, float& distance) const
bool ModelKdTree::RayIntersection(const AZ::Vector3& raySrc, const AZ::Vector3& rayDir, float& distance, AZ::Vector3& normal) const
{
return RayIntersectionRecursively(m_pRootNode.get(), raySrc, rayDir, distance);
return RayIntersectionRecursively(m_pRootNode.get(), raySrc, rayDir, distance, normal);
}
bool ModelKdTree::RayIntersectionRecursively(ModelKdTreeNode* pNode, const AZ::Vector3& raySrc, const AZ::Vector3& rayDir, float& distance) const
bool ModelKdTree::RayIntersectionRecursively(ModelKdTreeNode* pNode, const AZ::Vector3& raySrc, const AZ::Vector3& rayDir, float& distance, AZ::Vector3& normal) const
{
if (!pNode)
{
@@ -252,7 +235,7 @@ namespace AZ
return false;
}
AZ::Vector3 ignoreNormal;
AZ::Vector3 intersectionNormal;
float hitDistanceNormalized;
const float maxDist(FLT_MAX);
float nearestDist = maxDist;
@@ -278,9 +261,15 @@ namespace AZ
const AZ::Vector3 rayEnd = raySrc + rayDir * distance;
if (AZ::Intersect::IntersectSegmentTriangleCCW(raySrc, rayEnd, trianglePoints[0], trianglePoints[1], trianglePoints[2],
ignoreNormal, hitDistanceNormalized) != Intersect::ISECT_RAY_AABB_NONE)
intersectionNormal, hitDistanceNormalized) != Intersect::ISECT_RAY_AABB_NONE)
{
float hitDistance = hitDistanceNormalized * distance;
if (nearestDist > hitDistance)
{
normal = intersectionNormal;
}
nearestDist = AZStd::GetMin(nearestDist, hitDistance);
}
}
@@ -295,8 +284,8 @@ namespace AZ
}
// running both sides to find the closest intersection
const bool bFoundChild0 = RayIntersectionRecursively(pNode->GetChild(0), raySrc, rayDir, distance);
const bool bFoundChild1 = RayIntersectionRecursively(pNode->GetChild(1), raySrc, rayDir, distance);
const bool bFoundChild0 = RayIntersectionRecursively(pNode->GetChild(0), raySrc, rayDir, distance, normal);
const bool bFoundChild1 = RayIntersectionRecursively(pNode->GetChild(1), raySrc, rayDir, distance, normal);
return bFoundChild0 || bFoundChild1;
}
@@ -138,19 +138,6 @@ namespace AZ
return nullptr;
}
AZStd::array_view<uint8_t> ModelLodAsset::Mesh::GetSemanticBuffer(const AZ::Name& semantic) const
{
if (const BufferAssetView* bufferAssetView = GetSemanticBufferAssetView(semantic))
{
if (const BufferAsset* bufferAsset = bufferAssetView->GetBufferAsset().Get())
{
return bufferAsset->GetBuffer();
}
}
return {};
}
void ModelLodAsset::SetReady()
{
m_status = Data::AssetData::AssetStatus::Ready;
@@ -18,7 +18,7 @@ namespace AZ::RPI
PackedCompressedMorphTargetDelta PackMorphTargetDelta(const CompressedMorphTargetDelta& compressedDelta)
{
PackedCompressedMorphTargetDelta packedDelta{ 0,0,0,0,0,{0,0,0} };
PackedCompressedMorphTargetDelta packedDelta{ 0,0,0,0,0,0,{0,0} };
packedDelta.m_morphedVertexIndex = compressedDelta.m_morphedVertexIndex;
// Position x is in the most significant 16 bits, y is in the least significant 16 bits
@@ -45,6 +45,12 @@ namespace AZ::RPI
packedDelta.m_padBitangentXYZ |= static_cast<uint32_t>(compressedDelta.m_bitangentY) << 8;
packedDelta.m_padBitangentXYZ |= static_cast<uint32_t>(compressedDelta.m_bitangentZ);
// Colors are in the least significant 24 bits (8 bits per channel)
packedDelta.m_colorRGBA |= static_cast<uint32_t>(compressedDelta.m_colorR) << 24;
packedDelta.m_colorRGBA |= static_cast<uint32_t>(compressedDelta.m_colorG) << 16;
packedDelta.m_colorRGBA |= static_cast<uint32_t>(compressedDelta.m_colorB) << 8;
packedDelta.m_colorRGBA |= static_cast<uint32_t>(compressedDelta.m_colorA);
return packedDelta;
}
@@ -77,6 +83,12 @@ namespace AZ::RPI
compressedDelta.m_bitangentY = (packedDelta.m_padBitangentXYZ >> 8 ) & 0x000000FF;
compressedDelta.m_bitangentZ = packedDelta.m_padBitangentXYZ & 0x000000FF;
// Colors are 4 channels, 8 bits per channel
compressedDelta.m_colorR = (packedDelta.m_colorRGBA >> 24) & 0x000000FF;
compressedDelta.m_colorG = (packedDelta.m_colorRGBA >> 16) & 0x000000FF;
compressedDelta.m_colorB = (packedDelta.m_colorRGBA >> 8) & 0x000000FF;
compressedDelta.m_colorA = packedDelta.m_colorRGBA & 0x000000FF;
return compressedDelta;
}
} // namespace AZ::RPI
@@ -28,6 +28,7 @@ namespace AZ::RPI
->Field("numVertices", &MorphTargetMetaAsset::MorphTarget::m_numVertices)
->Field("minPositionDelta", &MorphTargetMetaAsset::MorphTarget::m_minPositionDelta)
->Field("maxPositionDelta", &MorphTargetMetaAsset::MorphTarget::m_maxPositionDelta)
->Field("hasColorDeltas", &MorphTargetMetaAsset::MorphTarget::m_hasColorDeltas)
;
}
}
@@ -39,6 +39,7 @@ namespace AZ
serializeContext->Class<PrecompiledShaderAssetSourceData>()
->Version(0)
->Field("ShaderAssetFileName", &PrecompiledShaderAssetSourceData::m_shaderAssetFileName)
->Field("PlatformIdentifiers", &PrecompiledShaderAssetSourceData::m_platformIdentifiers)
->Field("ShaderResourceGroupAssets", &PrecompiledShaderAssetSourceData::m_srgAssetFileNames)
->Field("RootShaderVariantAssets", &PrecompiledShaderAssetSourceData::m_rootShaderVariantAssets)
;
@@ -264,7 +264,7 @@ namespace AZ
if (!foundVariantAsset)
{
ReportError("Failed to find variant asset for API [%d]", perAPIShaderData.m_APIType);
ReportWarning("Failed to find variant asset for API [%d]", perAPIShaderData.m_APIType);
}
m_asset->m_perAPIShaderData.push_back(perAPIShaderData);
@@ -80,6 +80,22 @@ namespace AZ
}
}
void ShaderResourceGroupAssetCreator::AddShaderInput(const RHI::ShaderInputBufferUnboundedArrayDescriptor& shaderInputBufferUnboundedArray)
{
if (ValidateIsReady())
{
m_shaderResourceGroupLayout->AddShaderInput(shaderInputBufferUnboundedArray);
}
}
void ShaderResourceGroupAssetCreator::AddShaderInput(const RHI::ShaderInputImageUnboundedArrayDescriptor& shaderInputImageUnboundedArray)
{
if (ValidateIsReady())
{
m_shaderResourceGroupLayout->AddShaderInput(shaderInputImageUnboundedArray);
}
}
void ShaderResourceGroupAssetCreator::AddShaderInput(const RHI::ShaderInputSamplerDescriptor& shaderInputSampler)
{
if (ValidateIsReady())
@@ -150,4 +166,4 @@ namespace AZ
return true;
}
} // namespace RPI
} // namespace AZ
} // namespace AZ