diff --git a/pxr/imaging/hd/material.cpp b/pxr/imaging/hd/material.cpp index ef4d23920b8..8fc97fbb069 100644 --- a/pxr/imaging/hd/material.cpp +++ b/pxr/imaging/hd/material.cpp @@ -103,7 +103,10 @@ HdConvertToHdMaterialNetwork2( } // Transfer primvars: - result.primvars = hdNetwork.primvars; + result.primvars.insert( + result.primvars.end(), + hdNetwork.primvars.begin(), + hdNetwork.primvars.end()); } // Transfer config dictionary diff --git a/pxr/imaging/hd/materialNetworkSchema.cpp b/pxr/imaging/hd/materialNetworkSchema.cpp index 5cebbda72dd..716bdc9982b 100644 --- a/pxr/imaging/hd/materialNetworkSchema.cpp +++ b/pxr/imaging/hd/materialNetworkSchema.cpp @@ -60,17 +60,25 @@ HdMaterialNetworkSchema::GetConfig() const HdMaterialNetworkSchemaTokens->config)); } +HdTokenVectorMapDataSourceHandle +HdMaterialNetworkSchema::GetPrimvars() const +{ + return _GetTypedDataSource( + HdMaterialNetworkSchemaTokens->primvars); +} + /*static*/ HdContainerDataSourceHandle HdMaterialNetworkSchema::BuildRetained( const HdContainerDataSourceHandle &nodes, const HdContainerDataSourceHandle &terminals, const HdContainerDataSourceHandle &interfaceMappings, - const HdContainerDataSourceHandle &config + const HdContainerDataSourceHandle &config, + const HdTokenVectorMapDataSourceHandle &primvarsMap ) { - TfToken _names[4]; - HdDataSourceBaseHandle _values[4]; + TfToken _names[5]; + HdDataSourceBaseHandle _values[5]; size_t _count = 0; @@ -93,6 +101,11 @@ HdMaterialNetworkSchema::BuildRetained( _names[_count] = HdMaterialNetworkSchemaTokens->config; _values[_count++] = config; } + + if (primvarsMap) { + _names[_count] = HdMaterialNetworkSchemaTokens->primvars; + _values[_count++] = primvarsMap; + } return HdRetainedContainerDataSource::New(_count, _names, _values); } @@ -128,6 +141,14 @@ HdMaterialNetworkSchema::Builder::SetConfig( return *this; } +HdMaterialNetworkSchema::Builder & +HdMaterialNetworkSchema::Builder::SetPrimvars( + const HdTokenVectorMapDataSourceHandle &primvarsMap) +{ + _primvarsMap = primvarsMap; + return *this; +} + HdContainerDataSourceHandle HdMaterialNetworkSchema::Builder::Build() { @@ -135,7 +156,8 @@ HdMaterialNetworkSchema::Builder::Build() _nodes, _terminals, _interfaceMappings, - _config + _config, + _primvarsMap ); } diff --git a/pxr/imaging/hd/materialNetworkSchema.h b/pxr/imaging/hd/materialNetworkSchema.h index 00268355bc2..caa5aebd4a9 100644 --- a/pxr/imaging/hd/materialNetworkSchema.h +++ b/pxr/imaging/hd/materialNetworkSchema.h @@ -31,6 +31,11 @@ PXR_NAMESPACE_OPEN_SCOPE // --(BEGIN CUSTOM CODE: Declares)-- + +using HdTokenVectorMap = std::map; +using HdTokenVectorMapDataSource = HdTypedSampledDataSource; +using HdTokenVectorMapDataSourceHandle = HdTokenVectorMapDataSource::Handle; + // --(END CUSTOM CODE: Declares)-- #define HD_MATERIAL_NETWORK_SCHEMA_TOKENS \ @@ -38,6 +43,7 @@ PXR_NAMESPACE_OPEN_SCOPE (terminals) \ (interfaceMappings) \ (config) \ + (primvars) \ TF_DECLARE_PUBLIC_TOKENS(HdMaterialNetworkSchemaTokens, HD_API, HD_MATERIAL_NETWORK_SCHEMA_TOKENS); @@ -113,6 +119,9 @@ class HdMaterialNetworkSchema : public HdSchema HD_API HdSampledDataSourceContainerSchema GetConfig() const; + HD_API + HdTokenVectorMapDataSourceHandle GetPrimvars() const; + /// @} /// \name Schema construction @@ -131,7 +140,8 @@ class HdMaterialNetworkSchema : public HdSchema const HdContainerDataSourceHandle &nodes, const HdContainerDataSourceHandle &terminals, const HdContainerDataSourceHandle &interfaceMappings, - const HdContainerDataSourceHandle &config + const HdContainerDataSourceHandle &config, + const HdTokenVectorMapDataSourceHandle &primvarsMap ); /// \class HdMaterialNetworkSchema::Builder @@ -155,6 +165,9 @@ class HdMaterialNetworkSchema : public HdSchema HD_API Builder &SetConfig( const HdContainerDataSourceHandle &config); + HD_API + Builder &SetPrimvars( + const HdTokenVectorMapDataSourceHandle &primvarsMap); /// Returns a container data source containing the members set thus far. HD_API @@ -165,6 +178,7 @@ class HdMaterialNetworkSchema : public HdSchema HdContainerDataSourceHandle _terminals; HdContainerDataSourceHandle _interfaceMappings; HdContainerDataSourceHandle _config; + HdTokenVectorMapDataSourceHandle _primvarsMap; }; diff --git a/pxr/imaging/hd/sceneIndexAdapterSceneDelegate.cpp b/pxr/imaging/hd/sceneIndexAdapterSceneDelegate.cpp index f16d45fc95f..21727ea84e1 100644 --- a/pxr/imaging/hd/sceneIndexAdapterSceneDelegate.cpp +++ b/pxr/imaging/hd/sceneIndexAdapterSceneDelegate.cpp @@ -1275,6 +1275,13 @@ _ToMaterialNetworkMap( matHd.config = _ToDictionary(config); } + HdTokenVectorMap primvarsMap; + if (const HdTokenVectorMapDataSourceHandle primvarsMapDs = + netSchema.GetPrimvars()) + { + primvarsMap = primvarsMapDs->GetTypedValue(0); + } + for (const auto & name : names) { visitedNodes.clear(); @@ -1304,6 +1311,16 @@ _ToMaterialNetworkMap( nodesSchema, renderContexts, &visitedNodes, &netHd); } } + + // Extra primvars. + if (const auto &primvarsIter = primvarsMap.find(name); + primvarsIter != primvarsMap.end()) + { + netHd.primvars.insert( + netHd.primvars.end(), + primvarsIter->second.begin(), + primvarsIter->second.end()); + } } return matHd; diff --git a/pxr/imaging/hd/testenv/testHdUtils.cpp b/pxr/imaging/hd/testenv/testHdUtils.cpp index c55d263ee34..1631d748dbd 100644 --- a/pxr/imaging/hd/testenv/testHdUtils.cpp +++ b/pxr/imaging/hd/testenv/testHdUtils.cpp @@ -6,6 +6,7 @@ // #include "pxr/pxr.h" +#include "pxr/base/gf/vec3f.h" #include "pxr/imaging/hd/dataSource.h" #include "pxr/imaging/hd/utils.h" #include "pxr/base/tf/errorMark.h" @@ -22,7 +23,7 @@ BasicTest() { // Create a representation of a material network HdMaterialNetwork materialNetwork; - materialNetwork.nodes.reserve(3); + materialNetwork.nodes.reserve(4); const SdfPath materialPath("/Asset/Looks/Material"); @@ -43,6 +44,16 @@ BasicTest() standInNode.identifier = TfToken("PbsNetworkMaterialStandIn_3"); materialNetwork.nodes.push_back(standInNode); + const TfToken primvarToken("Primvar_0"); + materialNetwork.primvars.push_back(primvarToken); + + HdMaterialNode primvarNode; + primvarNode.path = SdfPath("/Asset/Looks/Material/Primvar_0Reader"); + primvarNode.identifier = TfToken("PrimvarReader_float3"); + primvarNode.parameters[TfToken("varname")] = primvarToken; + primvarNode.parameters[TfToken("fallback")] = VtValue(GfVec3f(1.0f, 1.0f, 1.0f)); + materialNetwork.nodes.push_back(primvarNode); + // Connect the nodes HdMaterialRelationship textureMaterialLayerRel; textureMaterialLayerRel.inputId = textureNode.path; @@ -58,6 +69,13 @@ BasicTest() materialLayerStandInRel.outputName = TfToken("multiMaterialIn"); materialNetwork.relationships.push_back(materialLayerStandInRel); + pxr::HdMaterialRelationship primvarRel; + primvarRel.inputId = primvarNode.path; + primvarRel.inputName = TfToken("result"); + primvarRel.outputId = materialPath; + primvarRel.outputName = primvarToken; + materialNetwork.relationships.push_back(primvarRel); + HdMaterialNetworkMap networkMap; networkMap.map[TfToken("surface")] = materialNetwork; diff --git a/pxr/imaging/hd/testenv/testHdUtils/baseline/testHdUtils_material.txt b/pxr/imaging/hd/testenv/testHdUtils/baseline/testHdUtils_material.txt index 18ff01b2745..afa43dc2c0f 100644 --- a/pxr/imaging/hd/testenv/testHdUtils/baseline/testHdUtils_material.txt +++ b/pxr/imaging/hd/testenv/testHdUtils/baseline/testHdUtils_material.txt @@ -12,6 +12,17 @@ [nodeIdentifier] MaterialLayer_3 [parameters] + [/Asset/Looks/Material/Primvar_0Reader] + [inputConnections] + [nodeIdentifier] + PrimvarReader_float3 + [parameters] + [fallback] + [value] + (1, 1, 1) + [varname] + [value] + Primvar_0 [/Asset/Looks/Material/StandIn] [inputConnections] [multiMaterialIn] @@ -31,6 +42,7 @@ [inputs:filename] [value] studio/patterns/checkerboard/checkerboard.tex + [primvars] [terminals] [surface] [upstreamNodeOutputName] diff --git a/pxr/imaging/hd/utils.cpp b/pxr/imaging/hd/utils.cpp index a1252cb1500..58dfac44ad4 100644 --- a/pxr/imaging/hd/utils.cpp +++ b/pxr/imaging/hd/utils.cpp @@ -144,6 +144,7 @@ ConvertHdMaterialNetworkToHdMaterialNetworkSchema( std::vector terminalsValues; std::vector nodeNames; std::vector nodeValues; + HdTokenVectorMap primvarsMap; struct ParamData { VtValue value; @@ -160,6 +161,7 @@ ConvertHdMaterialNetworkToHdMaterialNetworkSchema( } terminalsNames.push_back(terminalName); + primvarsMap[terminalName] = hdNetwork.primvars; // Transfer over individual nodes. // Note that the same nodes may be shared by multiple terminals. @@ -307,6 +309,8 @@ ConvertHdMaterialNetworkToHdMaterialNetworkSchema( .SetNodes(nodesDefaultContext) .SetTerminals(terminalsDefaultContext) .SetConfig(configDefaultContext) + .SetPrimvars( + HdRetainedTypedSampledDataSource::New(primvarsMap)) .Build(); } diff --git a/pxr/imaging/hdSt/CMakeLists.txt b/pxr/imaging/hdSt/CMakeLists.txt index 7b4624cdb4d..02146f9a0a9 100644 --- a/pxr/imaging/hdSt/CMakeLists.txt +++ b/pxr/imaging/hdSt/CMakeLists.txt @@ -3363,4 +3363,31 @@ pxr_register_test(testHdStShaders ENV TF_DEBUG=HD_SAFE_MODE ) + +pxr_build_test(testHdStShaderPrimvars + LIBRARIES + hdSt + hd + sdr + tf + gf + CPPFILES + testenv/testHdStShaderPrimvars.cpp +) +pxr_install_test_dir( + SRC testenv/testHdStShaderPrimvars + DEST testHdStShaderPrimvars +) +pxr_register_test(testHdStShaderPrimvars + COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testHdStShaderPrimvars --offscreen --outputFilePrefix testHdStShaderPrimvars" + EXPECTED_RETURN_CODE 0 + IMAGE_DIFF_COMPARE + testHdStShaderPrimvars.png + FAIL 1 + FAIL_PERCENT 1 + PERCEPTUAL + TESTENV testHdStShaderPrimvars + ENV + TF_DEBUG=HD_SAFE_MODE +) endif() # TARGET shared_libs diff --git a/pxr/imaging/hdSt/testenv/testHdStShaderPrimvars.cpp b/pxr/imaging/hdSt/testenv/testHdStShaderPrimvars.cpp new file mode 100644 index 00000000000..948ea7cd9ba --- /dev/null +++ b/pxr/imaging/hdSt/testenv/testHdStShaderPrimvars.cpp @@ -0,0 +1,233 @@ +// +// Copyright 2025 Pixar +// +// Licensed under the terms set forth in the LICENSE.txt file available at +// https://openusd.org/license. +// +#include "pxr/pxr.h" + +#include "pxr/base/gf/vec3f.h" +#include "pxr/base/gf/vec4f.h" +#include "pxr/base/tf/errorMark.h" +#include "pxr/imaging/hd/tokens.h" +#include "pxr/imaging/hd/unitTestDelegate.h" +#include "pxr/imaging/hdSt/material.h" +#include "pxr/imaging/hdSt/unitTestGLDrawing.h" +#include "pxr/imaging/hdSt/unitTestHelper.h" +#include "pxr/usd/sdr/registry.h" + +#include +#include + +PXR_NAMESPACE_USING_DIRECTIVE + +class My_TestGLDrawing : public HdSt_UnitTestGLDrawing { +public: + My_TestGLDrawing() { + SetCameraRotate(60.0f, 0.0f); + SetCameraTranslate(GfVec3f(0, 0, -10.0f)); + + _reprName = HdReprTokens->hull; + _refineLevel = 0; + _cullStyle = HdCullStyleNothing; + } + + // HdSt_UnitTestGLDrawing overrides + void InitTest() override; + void DrawTest() override; + void OffscreenTest() override; + void Present(uint32_t framebuffer) override; + +protected: + void ParseArgs(int argc, char* argv[]) override; + +private: + HdSt_TestDriverUniquePtr _driver; + + HdMaterialNetworkMap _networkMap; + SdfPath _materialPath; + + TfToken _reprName; + int _refineLevel; + HdCullStyle _cullStyle; + std::string _outputFilePrefix; +}; + +/* virtual */ +void +My_TestGLDrawing::InitTest() +{ + // Initialize driver. + _driver = std::make_unique(_reprName); + _driver->SetClearColor(GfVec4f(0.1f, 0.1f, 0.1f, 1.0f)); + _driver->SetClearDepth(1.0f); + _driver->SetupAovs(GetWidth(), GetHeight()); + + HdUnitTestDelegate& delegate = _driver->GetDelegate(); + delegate.SetRefineLevel(_refineLevel); + + // ------------------------------------------------------------------------ + // Process raw shader with Sdr. + std::string const surfaceShaderSource( + "-- glslfx version 0.1 \n" + "-- configuration \n" + "{\n" + "\"techniques\": {\n" + " \"default\": {\n" + " \"surfaceShader\": {\n" + " \"source\": [ \"testHdStShaderPrimvars.Surface\" ]\n" + " }\n" + " }\n" + "}\n\n" + "}\n" + + "-- glsl testHdStShaderPrimvars.Surface \n\n" + "vec4 surfaceShader(vec4 Peye, vec3 Neye, vec4 color, vec4 patchCoord) {\n" + "#if defined(HD_HAS_Primvar_0) \n" + " return HdGet_Primvar_0();\n" + "#else\n" + " return vec4(0.0);\n" + "#endif\n" + "}\n" + ); + + SdrRegistry& shaderReg = SdrRegistry::GetInstance(); + SdrShaderNodeConstPtr sdrSurfaceNode = + shaderReg.GetShaderNodeFromSourceCode( + surfaceShaderSource, + HioGlslfxTokens->glslfx, + SdrTokenMap()); // metadata + + // ------------------------------------------------------------------------ + // Create a material network. + _materialPath = SdfPath("/Material0"); + HdMaterialNetwork materialNetwork; + + HdMaterialNode materialNode; + materialNode.path = _materialPath.AppendChild(pxr::TfToken("SurfaceShader")); + materialNode.identifier = sdrSurfaceNode->GetIdentifier();; + materialNetwork.nodes.push_back(materialNode); + + const TfToken primvarToken("Primvar_0"); + materialNetwork.primvars.push_back(primvarToken); + + _networkMap.terminals.push_back(materialNode.path); + _networkMap.map[HdMaterialTerminalTokens->surface] = materialNetwork; + delegate.AddMaterialResource(_materialPath, VtValue(_networkMap)); + + // ------------------------------------------------------------------------ + // Create rprims with corresponding primvars and add to the scene. + GfMatrix4d dmat; + + const SdfPath grid0Path("/Grid0"); + dmat.SetTranslate(GfVec3d(-4.5, 0.0, 0.0)); + delegate.AddGrid(grid0Path, 10, 10, GfMatrix4f(dmat)); + delegate.AddPrimvar(grid0Path, primvarToken, + VtValue(GfVec4f(1.0f, 0.0f, 0.0f, 1.0f)), HdInterpolationConstant, + HdPrimvarRoleTokens->none); + delegate.BindMaterial(grid0Path, _materialPath); + + const SdfPath grid1Path("/Grid1"); + dmat.SetTranslate(GfVec3d(-1.5, 0.0, 0.0)); + delegate.AddGrid(grid1Path, 10, 10, GfMatrix4f(dmat)); + delegate.AddPrimvar(grid1Path, primvarToken, + VtValue(GfVec4f(0.0f, 1.0f, 0.0f, 1.0f)), HdInterpolationConstant, + HdPrimvarRoleTokens->none); + delegate.BindMaterial(grid1Path, _materialPath); + + const SdfPath grid2Path("/Grid2"); + dmat.SetTranslate(GfVec3d(1.5, 0.0, 0.0)); + delegate.AddGrid(grid2Path, 10, 10, GfMatrix4f(dmat)); + delegate.AddPrimvar(grid2Path, primvarToken, + VtValue(GfVec4f(0.0f, 0.0f, 1.0f, 1.0f)), HdInterpolationConstant, + HdPrimvarRoleTokens->none); + delegate.BindMaterial(grid2Path, _materialPath); + + const SdfPath grid3Path("/Grid3"); + dmat.SetTranslate(GfVec3d(4.5, 0.0, 0.0)); + delegate.AddGrid(grid3Path, 10, 10, GfMatrix4f(dmat)); + delegate.AddPrimvar(grid3Path, primvarToken, + VtValue(GfVec4f(1.0f, 1.0f, 0.0f, 1.0f)), HdInterpolationConstant, + HdPrimvarRoleTokens->none); + delegate.BindMaterial(grid3Path, _materialPath); +} + +/* virtual */ +void +My_TestGLDrawing::DrawTest() +{ + int width = GetWidth(), height = GetHeight(); + GfMatrix4d viewMatrix = GetViewMatrix(); + GfMatrix4d projMatrix = GetProjectionMatrix(); + + _driver->SetCullStyle(_cullStyle); + _driver->SetCamera( + viewMatrix, + projMatrix, + CameraUtilFraming( + GfRect2i(GfVec2i(0, 0), width, height))); + _driver->UpdateAovDimensions(width, height); + + _driver->Draw(); + + // Check the material network. + // NOTE: By default, the SceneIndex emulation will be used, in which case both + // ConvertHdMaterialNetworkToHdMaterialSchema & HdConvertToHdMaterialNetwork2 can + // be tested. + HdUnitTestDelegate& delegate = _driver->GetDelegate(); + HdMaterialNetworkMap const& resultNetworkMap = + delegate.GetMaterialResource(_materialPath).Get(); + TF_VERIFY(resultNetworkMap == _networkMap); +} + +/* virtual */ +void +My_TestGLDrawing::OffscreenTest() +{ + if (!TF_VERIFY(!_outputFilePrefix.empty())) return; + + DrawTest(); + _driver->WriteToFile("color", _outputFilePrefix + ".png"); +} + +/* virtual */ +void +My_TestGLDrawing::Present(uint32_t framebuffer) +{ + _driver->Present(GetWidth(), GetHeight(), framebuffer); +} + +/* virtual */ +void +My_TestGLDrawing::ParseArgs(int argc, char* argv[]) +{ + for (int i = 0; i < argc; ++i) { + std::string arg(argv[i]); + if (arg == "--outputFilePrefix") { + _outputFilePrefix = argv[++i]; + } + } +} + +void +ShaderPrimvarsTest(int argc, char* argv[]) +{ + My_TestGLDrawing driver; + + driver.RunTest(argc, argv); +} + +int main(int argc, char* argv[]) +{ + TfErrorMark mark; + + ShaderPrimvarsTest(argc, argv); + + if (mark.IsClean()) { + std::cout << "OK" << std::endl; + return EXIT_SUCCESS; + } else { + std::cout << "FAILED" << std::endl; + return EXIT_FAILURE; + } +} diff --git a/pxr/imaging/hdSt/testenv/testHdStShaderPrimvars/baseline/testHdStShaderPrimvars.png b/pxr/imaging/hdSt/testenv/testHdStShaderPrimvars/baseline/testHdStShaderPrimvars.png new file mode 100644 index 00000000000..3f3674529ea Binary files /dev/null and b/pxr/imaging/hdSt/testenv/testHdStShaderPrimvars/baseline/testHdStShaderPrimvars.png differ