diff --git a/Hydrogent/interface/HnMesh.hpp b/Hydrogent/interface/HnMesh.hpp index 144dbf17..cc7b72cd 100644 --- a/Hydrogent/interface/HnMesh.hpp +++ b/Hydrogent/interface/HnMesh.hpp @@ -160,8 +160,10 @@ class HnMesh final : public pxr::HdMesh // The total number of supported primvars Uint32 Count = 0; - // Dirty primvars - pxr::HdPrimvarDescriptorVector Dirty; + // Dirty primvars arranged by name. + // Typically, the name is the same as the primvar descriptor name, + // but it may be different if the primvar is found using the role. + std::unordered_map Dirty; // Computation primvars pxr::HdExtComputationPrimvarDescriptorVector ExtComp; diff --git a/Hydrogent/interface/HnRenderPass.hpp b/Hydrogent/interface/HnRenderPass.hpp index a3176963..90dec6af 100644 --- a/Hydrogent/interface/HnRenderPass.hpp +++ b/Hydrogent/interface/HnRenderPass.hpp @@ -88,8 +88,9 @@ class HnRenderPass final : public pxr::HdRenderPass return m_Params.Name; } - using SupportedVertexInputsSetType = std::unordered_set; - static SupportedVertexInputsSetType GetSupportedVertexInputs(const HnMaterial* Material); + // A mapping from the primvar name to its role (e.g. "points" -> "point", "normals" -> "normal", "st0" -> "textureCoordinate", etc.) + using SupportedVertexInputsMapType = std::unordered_map; + static SupportedVertexInputsMapType GetSupportedVertexInputs(const HnMaterial* Material); static PBR_Renderer::PSO_FLAGS GetMaterialPSOFlags(const HnMaterial& Material); enum DRAW_LIST_ITEM_DIRTY_FLAGS : Uint32 diff --git a/Hydrogent/src/HnMaterial.cpp b/Hydrogent/src/HnMaterial.cpp index 695563b2..a8091caf 100644 --- a/Hydrogent/src/HnMaterial.cpp +++ b/Hydrogent/src/HnMaterial.cpp @@ -61,6 +61,7 @@ TF_DEFINE_PRIVATE_TOKENS( (whiteRgba8) (blackRgba8) (whiteR8) + (st0) ); // clang-format on @@ -548,37 +549,36 @@ void HnMaterial::AllocateTextures(const HnMaterialNetwork& Network, HnTextureReg m_Textures[TexDescriptor.Name] = std::move(pTex); // Find texture coordinate - size_t TexCoordIdx = ~size_t{0}; if (const HnMaterialParameter* Param = Network.GetParameter(HnMaterialParameter::ParamType::Texture, TexDescriptor.Name)) { - if (!Param->SamplerCoords.empty()) + const pxr::TfToken& TexCoordName = !Param->SamplerCoords.empty() ? Param->SamplerCoords[0] : HnMaterialPrivateTokens->st0; + if (Param->SamplerCoords.empty()) { - if (Param->SamplerCoords.size() > 1) - LOG_WARNING_MESSAGE("Texture '", TexDescriptor.Name, "' has ", Param->SamplerCoords.size(), " texture coordinates. Only the first set will be used"); - const pxr::TfToken& TexCoordName = Param->SamplerCoords[0]; - - // Check if the texture coordinate set primvar (e.g. "st0") has already been allocated - auto it_inserted = TexCoordPrimvarMapping.emplace(TexCoordName, m_TexCoords.size()); - TexCoordIdx = it_inserted.first->second; - if (it_inserted.second) - { - // Add new texture coordinate set - VERIFY_EXPR(TexCoordIdx == m_TexCoords.size()); - m_TexCoords.resize(TexCoordIdx + 1); - m_TexCoords[TexCoordIdx] = {TexCoordName}; - } - - TexNameToCoordSetMap[TexDescriptor.Name] = TexCoordIdx; + LOG_WARNING_MESSAGE("Texture '", TexDescriptor.Name, "' in material '", GetId(), "' has no texture coordinates. Using ", TexCoordName, " as fallback."); } - else + else if (Param->SamplerCoords.size() > 1) { - LOG_ERROR_MESSAGE("Texture '", TexDescriptor.Name, "' in material '", GetId(), "' has no texture coordinates"); + LOG_WARNING_MESSAGE("Texture '", TexDescriptor.Name, "' has ", Param->SamplerCoords.size(), " texture coordinates. Using the first one (", TexCoordName, ")."); } - } - if (TexCoordIdx == ~size_t{0}) + // Check if the texture coordinate set primvar (e.g. "st0") has already been allocated + auto it_inserted = TexCoordPrimvarMapping.emplace(TexCoordName, m_TexCoords.size()); + size_t TexCoordIdx = it_inserted.first->second; + if (it_inserted.second) + { + // Add new texture coordinate set + VERIFY_EXPR(TexCoordIdx == m_TexCoords.size()); + m_TexCoords.resize(TexCoordIdx + 1); + m_TexCoords[TexCoordIdx] = {TexCoordName}; + } + + TexNameToCoordSetMap[TexDescriptor.Name] = TexCoordIdx; + } + else { - LOG_ERROR_MESSAGE("Failed to find texture coordinates for texture '", TexDescriptor.Name, "' in material '", GetId(), "'"); + UNEXPECTED("Texture parameter '", TexDescriptor.Name, + "' is not found in the material network. This looks like a bug since we obtained the texture descriptor from the network, " + "and all textures in the network should have a corresponding parameter."); } } } diff --git a/Hydrogent/src/HnMesh.cpp b/Hydrogent/src/HnMesh.cpp index a160e96f..57e285ba 100644 --- a/Hydrogent/src/HnMesh.cpp +++ b/Hydrogent/src/HnMesh.cpp @@ -48,6 +48,7 @@ #include "pxr/imaging/hd/vtBufferSource.h" #include "pxr/imaging/hd/vertexAdjacency.h" #include "pxr/imaging/hd/smoothNormals.h" +#include "pxr/imaging/hd/primvarSchema.h" namespace Diligent { @@ -570,19 +571,19 @@ bool HnMesh::AddStagingBufferSourceForPrimvar(StagingVertexData& StagingVerts, return true; } -static HnRenderPass::SupportedVertexInputsSetType GetSupportedPrimvars( +static HnRenderPass::SupportedVertexInputsMapType GetSupportedPrimvars( const pxr::HdRenderIndex& RenderIndex, const pxr::SdfPath& MaterialId, const pxr::HdMeshTopology& Topology) { - HnRenderPass::SupportedVertexInputsSetType SupportedPrimvars = + HnRenderPass::SupportedVertexInputsMapType SupportedPrimvars = HnRenderPass::GetSupportedVertexInputs(static_cast(RenderIndex.GetSprim(pxr::HdPrimTypeTokens->material, MaterialId))); { const pxr::HdGeomSubsets GeomSubsets = Topology.GetGeomSubsets(); for (const pxr::HdGeomSubset& Subset : GeomSubsets) { - HnRenderPass::SupportedVertexInputsSetType SubsetPrimvars = + HnRenderPass::SupportedVertexInputsMapType SubsetPrimvars = HnRenderPass::GetSupportedVertexInputs(static_cast(RenderIndex.GetSprim(pxr::HdPrimTypeTokens->material, Subset.materialId))); SupportedPrimvars.insert(SubsetPrimvars.begin(), SubsetPrimvars.end()); } @@ -672,21 +673,52 @@ void HnMesh::GetPrimvarsInfo(pxr::HdSceneDelegate& SceneDelegate, { const pxr::SdfPath& Id = GetId(); - const HnRenderPass::SupportedVertexInputsSetType SupportedPrimvars = GetSupportedPrimvars(SceneDelegate.GetRenderIndex(), GetMaterialId(), m_Topology); + const HnRenderPass::SupportedVertexInputsMapType SupportedPrimvars = GetSupportedPrimvars(SceneDelegate.GetRenderIndex(), GetMaterialId(), m_Topology); + + HnRenderPass::SupportedVertexInputsMapType SupportedPrimvarsByRole; + for (const auto& it : SupportedPrimvars) + { + const pxr::TfToken& Role = it.second; + if (Role == pxr::HdPrimvarSchemaTokens->point || + Role == pxr::HdPrimvarSchemaTokens->normal || + Role == pxr::HdPrimvarSchemaTokens->textureCoordinate) + { + SupportedPrimvarsByRole[Role] = it.first; + } + } auto UpdatePrimvarsInfo = [&](const pxr::HdInterpolation Interpolation, PrimvarsInfo& PrimvarsInfo) { pxr::HdPrimvarDescriptorVector PrimVarDescs = GetPrimvarDescriptors(&SceneDelegate, Interpolation); for (const pxr::HdPrimvarDescriptor& PrimDesc : PrimVarDescs) { - if (SupportedPrimvars.find(PrimDesc.name) == SupportedPrimvars.end()) - continue; + pxr::TfToken Name = PrimDesc.name; + if (SupportedPrimvars.find(Name) == SupportedPrimvars.end()) + { + const auto RoleIt = SupportedPrimvarsByRole.find(PrimDesc.role); + if (RoleIt != SupportedPrimvarsByRole.end()) + { + LOG_WARNING_MESSAGE("Primvar '", Name, "' in mesh ", Id, " is not recognized by the material, but matches primvar '", + RoleIt->second, "' by role '", PrimDesc.role, "'."); + Name = RoleIt->second; + } + else + { + continue; + } + } ++PrimvarsInfo.Count; - if (pxr::HdChangeTracker::IsPrimvarDirty(DirtyBits, Id, PrimDesc.name)) + if (pxr::HdChangeTracker::IsPrimvarDirty(DirtyBits, Id, Name)) { - PrimvarsInfo.Dirty.push_back(PrimDesc); + auto it_inserted = PrimvarsInfo.Dirty.emplace(Name, PrimDesc); + if (!it_inserted.second && Name == PrimDesc.name) + { + // We previously found a primvar with a different name but same role. + // Now we found a primvar with the same name, so we need to update the descriptor. + it_inserted.first->second = PrimDesc; + } } } }; @@ -848,16 +880,19 @@ void HnMesh::UpdateVertexAndVaryingPrimvars(pxr::HdSceneDelegate& SceneDelegate, const PrimvarsInfo& VertexPrimvarsInfo, StagingVertexData& StagingVerts) { - for (const pxr::HdPrimvarDescriptor& PrimDesc : VertexPrimvarsInfo.Dirty) + for (const auto& it : VertexPrimvarsInfo.Dirty) { + const pxr::TfToken& Name = it.first; + const pxr::HdPrimvarDescriptor& PrimDesc = it.second; + pxr::VtValue PrimValue = GetPrimvar(&SceneDelegate, PrimDesc.name); if (PrimValue.IsEmpty()) - return; + continue; if (PrimDesc.name == pxr::HdTokens->points) StagingVerts.Points = PrimValue; - AddStagingBufferSourceForPrimvar(StagingVerts, PrimDesc.name, std::move(PrimValue), PrimDesc.interpolation); + AddStagingBufferSourceForPrimvar(StagingVerts, Name, std::move(PrimValue), PrimDesc.interpolation); } for (const pxr::HdExtComputationPrimvarDescriptor& ExtCompPrimDesc : VertexPrimvarsInfo.ExtComp) @@ -877,10 +912,13 @@ void HnMesh::UpdateFaceVaryingPrimvars(pxr::HdSceneDelegate& SceneDelegate, const PrimvarsInfo& FacePrimvarsInfo, StagingVertexData& StagingVerts) { - for (const pxr::HdPrimvarDescriptor& PrimDesc : FacePrimvarsInfo.Dirty) + for (const auto& it : FacePrimvarsInfo.Dirty) { + const pxr::TfToken& Name = it.first; + const pxr::HdPrimvarDescriptor& PrimDesc = it.second; + pxr::VtValue PrimValue = GetPrimvar(&SceneDelegate, PrimDesc.name); - AddStagingBufferSourceForPrimvar(StagingVerts, PrimDesc.name, std::move(PrimValue), PrimDesc.interpolation); + AddStagingBufferSourceForPrimvar(StagingVerts, Name, std::move(PrimValue), PrimDesc.interpolation); } } diff --git a/Hydrogent/src/HnRenderPass.cpp b/Hydrogent/src/HnRenderPass.cpp index 7ed4aedf..7af5f3b1 100644 --- a/Hydrogent/src/HnRenderPass.cpp +++ b/Hydrogent/src/HnRenderPass.cpp @@ -37,6 +37,7 @@ #include #include "pxr/imaging/hd/renderIndex.h" +#include "pxr/imaging/hd/primvarSchema.h" #include "USD_Renderer.hpp" #include "GLTF_PBR_Renderer.hpp" @@ -937,16 +938,20 @@ void HnRenderPass::UpdateDrawListGPUResources(RenderState& State) m_DrawListItemsDirtyFlags = DRAW_LIST_ITEM_DIRTY_FLAG_NONE; } -HnRenderPass::SupportedVertexInputsSetType HnRenderPass::GetSupportedVertexInputs(const HnMaterial* Material) +HnRenderPass::SupportedVertexInputsMapType HnRenderPass::GetSupportedVertexInputs(const HnMaterial* Material) { - SupportedVertexInputsSetType SupportedInputs{{pxr::HdTokens->points, pxr::HdTokens->normals, pxr::HdTokens->displayColor}}; + SupportedVertexInputsMapType SupportedInputs{ + {pxr::HdTokens->points, pxr::HdPrimvarSchemaTokens->point}, + {pxr::HdTokens->normals, pxr::HdPrimvarSchemaTokens->normal}, + {pxr::HdTokens->displayColor, pxr::HdPrimvarSchemaTokens->color}, + }; if (Material != nullptr) { const auto& TexCoordSets = Material->GetTextureCoordinateSets(); for (const auto& TexCoordSet : TexCoordSets) { if (!TexCoordSet.PrimVarName.IsEmpty()) - SupportedInputs.emplace(TexCoordSet.PrimVarName); + SupportedInputs.emplace(TexCoordSet.PrimVarName, pxr::HdPrimvarSchemaTokens->textureCoordinate); } }