diff --git a/CHANGELOG.MD b/CHANGELOG.MD index eb5682c02..cd877d48c 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,5 +1,21 @@ # Change Log +## Version 2.0.13 +- Improved interactivity with Interactive Renders. +- Some issues with texture compression present in version 3.0.1 are fixed. +- Issues with normal maps (related to texture compression) are also fixed. +- Fixed issues with .rpr file export: + - Fixed the AOV names in config.json file. + - Fixed the contour rendering option in config.json file + - Export of .rpr file is made faster and textures are reused between animation frames. +- Users no longer need to manually restart the render when switching render mode in Houdini. +- Time-to-first-pixel is made faster when not using denoisers. +- Added an ID parameter to Render Geometry Settings Node in Houdini for setting an explicit Object ID. +- Add a Houdini Node RPR Material Properties for explicitly setting the Material ID to be used in the corresponding AOV. +- Single and two-channel textures are handled correctly now. +- Fixed USDPreviewSurface Normal Maps. + + ## Version 2.0 ### New Features: - The new plug-in version incorporates version 2.0 of our Radeon™ ProRender system and brings about the significant changes and enhancements: diff --git a/CMakeLists.txt b/CMakeLists.txt index fd610a8c6..a24caf4d1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,3 +36,10 @@ add_subdirectory(pxr/imaging) install(FILES README.md DESTINATION .) install(FILES INSTALL.md DESTINATION .) install(FILES LICENSE.md DESTINATION .) + +install( + CODE + "FILE(WRITE \"${CMAKE_INSTALL_PREFIX}/version\" +\"core:${RPR_VERSION_STRING} +plugin:${HD_RPR_MAJOR_VERSION}.${HD_RPR_MINOR_VERSION}.${HD_RPR_PATCH_VERSION} +\")") diff --git a/cmake/defaults/ProjectDefaults.cmake b/cmake/defaults/ProjectDefaults.cmake index 6205a72ce..530bda4f8 100644 --- a/cmake/defaults/ProjectDefaults.cmake +++ b/cmake/defaults/ProjectDefaults.cmake @@ -50,6 +50,8 @@ if (PXR_BUILD_TESTS) enable_testing() endif() +include(parseVersion) + if(NOT DEFINED RPR_SDK_PLATFORM) include(PlatformIntrospection) DETERMINE_PLATFORM(RPR_SDK_PLATFORM) diff --git a/cmake/defaults/Version.cmake b/cmake/defaults/Version.cmake index 3507907d5..cc30f1035 100644 --- a/cmake/defaults/Version.cmake +++ b/cmake/defaults/Version.cmake @@ -24,4 +24,4 @@ # Versioning information set(HD_RPR_MAJOR_VERSION "2") set(HD_RPR_MINOR_VERSION "0") -set(HD_RPR_PATCH_VERSION "1") +set(HD_RPR_PATCH_VERSION "13") diff --git a/cmake/macros/parseVersion.cmake b/cmake/macros/parseVersion.cmake new file mode 100644 index 000000000..edeb95f8a --- /dev/null +++ b/cmake/macros/parseVersion.cmake @@ -0,0 +1,21 @@ +macro(parseVersion version_file prefix) + if(NOT EXISTS ${version_file}) + message(FATAL_ERROR "Invalid ${prefix} SDK: missing ${version_file} file") + endif() + + file(STRINGS "${version_file}" _major_version_str + REGEX "^#define[\t ]+${prefix}_VERSION_MAJOR[\t ]+.*") + file(STRINGS "${version_file}" _minor_version_str + REGEX "^#define[\t ]+${prefix}_VERSION_MINOR[\t ]+.*") + file(STRINGS "${version_file}" _revision_version_str + REGEX "^#define[\t ]+${prefix}_VERSION_REVISION[\t ]+.*") + + string(REGEX REPLACE "^.*MAJOR[\t ]+([0-9]*).*$" "\\1" + ${prefix}_MAJOR_VERSION "${_major_version_str}") + string(REGEX REPLACE "^.*MINOR[\t ]+([0-9]*).*$" "\\1" + ${prefix}_MINOR_VERSION "${_minor_version_str}") + string(REGEX REPLACE "^.*REVISION[\t ]+([0-9]*).*$" "\\1" + ${prefix}_REVISION_VERSION "${_revision_version_str}") + + set(${prefix}_VERSION_STRING "${${prefix}_MAJOR_VERSION}.${${prefix}_MINOR_VERSION}.${${prefix}_REVISION_VERSION}") +endmacro() \ No newline at end of file diff --git a/cmake/modules/FindRif.cmake b/cmake/modules/FindRif.cmake index 27972b7c1..4c5befe4e 100644 --- a/cmake/modules/FindRif.cmake +++ b/cmake/modules/FindRif.cmake @@ -53,32 +53,13 @@ if(NOT DEFINED RIF_MODELS_DIR) set(RIF_MODELS_DIR "${RIF_LOCATION}/models") endif() -set(RIF_VERSION_FILE "${RIF_LOCATION_INCLUDE}/RadeonImageFilters_version.h") -if(NOT EXISTS ${RIF_VERSION_FILE}) - message(FATAL_ERROR "Invalid RIF SDK: missing ${RIF_VERSION_FILE} file") -endif() - -file(STRINGS "${RIF_VERSION_FILE}" _rif_major_version_str - REGEX "^#define[\t ]+RIF_VERSION_MAJOR[\t ]+.*") -file(STRINGS "${RIF_VERSION_FILE}" _rif_minor_version_str - REGEX "^#define[\t ]+RIF_VERSION_MINOR[\t ]+.*") -file(STRINGS "${RIF_VERSION_FILE}" _rif_revision_version_str - REGEX "^#define[\t ]+RIF_VERSION_REVISION[\t ]+.*") - -string(REGEX REPLACE "^.*MAJOR[\t ]+([0-9]*).*$" "\\1" - RIF_MAJOR_VERSION "${_rif_major_version_str}") -string(REGEX REPLACE "^.*MINOR[\t ]+([0-9]*).*$" "\\1" - RIF_MINOR_VERSION "${_rif_minor_version_str}") -string(REGEX REPLACE "^.*REVISION[\t ]+([0-9]*).*$" "\\1" - RIF_REVISION_VERSION "${_rif_revision_version_str}") - -set(RIF_VERSION_STRING "${RIF_MAJOR_VERSION}.${RIF_MINOR_VERSION}.${RIF_REVISION_VERSION}") +parseVersion("${RIF_LOCATION_INCLUDE}/RadeonImageFilters_version.h" RIF) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Rif REQUIRED_VARS - RIF_VERSION_STRING RIF_LOCATION_INCLUDE + RIF_VERSION_STRING RIF_LIBRARY ) diff --git a/cmake/modules/FindRpr.cmake b/cmake/modules/FindRpr.cmake index 3f9a14791..7b593564c 100644 --- a/cmake/modules/FindRpr.cmake +++ b/cmake/modules/FindRpr.cmake @@ -81,11 +81,14 @@ if(NOT RPR_PLUGINS) message(FATAL_ERROR "At least one RPR plugin required") endif() +parseVersion("${RPR_LOCATION_INCLUDE}/RadeonProRender.h" RPR) + include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Rpr REQUIRED_VARS RPR_LOCATION_INCLUDE + RPR_VERSION_STRING RPR_LOADSTORE_LIBRARY RPR_LIBRARY ) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 42de65f06..1e14b577f 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -50,7 +50,7 @@ add_library(cpprpr STATIC ${RPR_CPP_WRAPPER_LOCATION}/RadeonProRenderCpp.cpp ${RPR_CPP_WRAPPER_LOCATION}/tinyxml2.cpp) set_target_properties(cpprpr PROPERTIES POSITION_INDEPENDENT_CODE ON) -target_include_directories(cpprpr PUBLIC ${RPR_CPP_WRAPPER_LOCATION} ${RPRMTLXLOADER_LOCATION}) +target_include_directories(cpprpr PUBLIC ${RPRMTLXLOADER_LOCATION} ${RPR_CPP_WRAPPER_LOCATION}) target_link_libraries(cpprpr PUBLIC rpr MaterialXCore MaterialXFormat) target_compile_definitions(cpprpr PUBLIC RPR_CPPWRAPER_DISABLE_MUTEXLOCK diff --git a/deps/RPR b/deps/RPR index 3bc01d29f..015becd5b 160000 --- a/deps/RPR +++ b/deps/RPR @@ -1 +1 @@ -Subproject commit 3bc01d29f8dee0068023a897e63699c82e5ce43d +Subproject commit 015becd5b81c772c5edfa07b5469e32ecef5e6be diff --git a/deps/rprMtlxLoader/rprMtlxLoader.cpp b/deps/rprMtlxLoader/rprMtlxLoader.cpp index 700796655..d9b10d570 100644 --- a/deps/rprMtlxLoader/rprMtlxLoader.cpp +++ b/deps/rprMtlxLoader/rprMtlxLoader.cpp @@ -367,6 +367,7 @@ struct RprMappedNode : public RprNode { struct RprImageNode : public RprMappedNode { // TODO: support frame ranges + std::string type; std::string file; std::string layer; mx::ValuePtr defaultValue; @@ -379,7 +380,7 @@ struct RprImageNode : public RprMappedNode { // TODO: can we support this in RPR? //std::string filtertype; - RprImageNode(LoaderContext* context); + RprImageNode(std::string const& type, LoaderContext* context); ~RprImageNode() override = default; rpr_status SetInput(mx::Element* inputElement, std::string const& value, std::string const& valueType, LoaderContext* context) override; @@ -824,7 +825,7 @@ Node::Ptr Node::Create(mx::Node* mtlxNode, LoaderContext* context) { }; rprNodeMapping = &s_sqrtMapping; } else if (mtlxNode->getCategory() == "image") { - return std::make_unique(context); + return std::make_unique(mtlxNode->getType(), context); } else if (mtlxNode->getCategory() == "swizzle") { // TODO: implement healthy man swizzle @@ -1371,7 +1372,7 @@ rpr_status RprMappedNode::SetInput(mx::Element* inputElement, std::string const& return RprNode::SetInput(inputIt->second, valueString, valueType, context); } -RprImageNode::RprImageNode(LoaderContext* context) +RprImageNode::RprImageNode(std::string const& type, LoaderContext* context) : RprMappedNode( [context]() { rpr_material_node node = nullptr; @@ -1386,7 +1387,8 @@ RprImageNode::RprImageNode(LoaderContext* context) }; return &s_imageMapping; }() - ) { + ) + , type(type) { } @@ -1995,6 +1997,7 @@ RPRMtlxLoader::Result RPRMtlxLoader::Load( outImageNodes.emplace_back(); auto& outImageNode = outImageNodes.back(); + std::swap(outImageNode.type, imageNode->type); std::swap(outImageNode.file, imageNode->file); std::swap(outImageNode.layer, imageNode->layer); std::swap(outImageNode.defaultValue, imageNode->defaultValue); diff --git a/deps/rprMtlxLoader/rprMtlxLoader.h b/deps/rprMtlxLoader/rprMtlxLoader.h index 9afc5ec59..2e3be5501 100644 --- a/deps/rprMtlxLoader/rprMtlxLoader.h +++ b/deps/rprMtlxLoader/rprMtlxLoader.h @@ -41,6 +41,9 @@ class RPRMtlxLoader { static const size_t kInvalidRootNodeIndex = size_t(-1); struct ImageNode { + /// MaterialX type + std::string type; + /// The URI of an image file. /// It's responsibility of the loader user to setup rpr_image and bind it to rprNode std::string file; diff --git a/pxr/imaging/plugin/hdRpr/CMakeLists.txt b/pxr/imaging/plugin/hdRpr/CMakeLists.txt index 896aebd5c..c3f7d8f09 100644 --- a/pxr/imaging/plugin/hdRpr/CMakeLists.txt +++ b/pxr/imaging/plugin/hdRpr/CMakeLists.txt @@ -29,7 +29,7 @@ endif(HoudiniUSD_FOUND) set(GEN_SCRIPT_PYTHON ${PYTHON_EXECUTABLE}) set(GENERATION_SCRIPTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/python) set(GEN_SCRIPT ${GENERATION_SCRIPTS_DIR}/generateFiles.py) -set(GEN_SCRIPT_ARGS \"${GENERATION_SCRIPTS_DIR}\" \"${CMAKE_CURRENT_BINARY_DIR}\") +set(GEN_SCRIPT_ARGS \"${CMAKE_CURRENT_BINARY_DIR}\") set(GENERATED_FILES ${CMAKE_CURRENT_BINARY_DIR}/config.h ${CMAKE_CURRENT_BINARY_DIR}/config.cpp) @@ -39,7 +39,7 @@ set(GENERATION_DEPENDENT_FILES ${GEN_SCRIPT} ${GENERATION_SCRIPTS_DIR}/generateRenderSettingFiles.py ${GENERATION_SCRIPTS_DIR}/generateGeometrySettingFiles.py) if(HoudiniUSD_FOUND) - set(GEN_SCRIPT_ARGS --houdini_root \"${HOUDINI_ROOT}\" ${GEN_SCRIPT_ARGS}) + set(GEN_SCRIPT_ARGS --for_houdini ${GEN_SCRIPT_ARGS}) set(GENERATED_FILES ${GENERATED_FILES} ${CMAKE_CURRENT_BINARY_DIR}/HdRprPlugin_Light.ds ${CMAKE_CURRENT_BINARY_DIR}/HdRprPlugin_Global.ds @@ -62,8 +62,6 @@ if(HoudiniUSD_FOUND) }\")") endif() -set(GEN_SCRIPT_ARGS --python_exe "\"${GEN_SCRIPT_PYTHON}\"" ${GEN_SCRIPT_ARGS}) - add_custom_command( COMMAND ${GEN_SCRIPT_PYTHON} ${GEN_SCRIPT} ${GEN_SCRIPT_ARGS} DEPENDS ${GENERATION_DEPENDENT_FILES} diff --git a/pxr/imaging/plugin/hdRpr/aovDescriptor.cpp b/pxr/imaging/plugin/hdRpr/aovDescriptor.cpp index 97e58c478..ec4295253 100644 --- a/pxr/imaging/plugin/hdRpr/aovDescriptor.cpp +++ b/pxr/imaging/plugin/hdRpr/aovDescriptor.cpp @@ -25,7 +25,7 @@ TF_INSTANTIATE_SINGLETON(HdRprAovRegistry); TF_DEFINE_PUBLIC_TOKENS(HdRprAovTokens, HDRPR_AOV_TOKENS); HdRprAovRegistry::HdRprAovRegistry() { - const auto rprAovMax = RPR_AOV_LPE_8 + 1; + const auto rprAovMax = RPR_AOV_CRYPTOMATTE_OBJ2 + 1; const GfVec4f idClearValue(255.0f, 255.0f, 255.0f, 0.0f); m_aovDescriptors.resize(rprAovMax); @@ -61,7 +61,13 @@ HdRprAovRegistry::HdRprAovRegistry() { RPR_AOV_LPE_5, RPR_AOV_LPE_6, RPR_AOV_LPE_7, - RPR_AOV_LPE_8 + RPR_AOV_LPE_8, + RPR_AOV_CRYPTOMATTE_MAT0, + RPR_AOV_CRYPTOMATTE_MAT1, + RPR_AOV_CRYPTOMATTE_MAT2, + RPR_AOV_CRYPTOMATTE_OBJ0, + RPR_AOV_CRYPTOMATTE_OBJ1, + RPR_AOV_CRYPTOMATTE_OBJ2, }) { m_aovDescriptors[rprAovId] = HdRprAovDescriptor(rprAovId); } @@ -78,6 +84,7 @@ HdRprAovRegistry::HdRprAovRegistry() { m_aovDescriptors[RPR_AOV_BACKGROUND] = HdRprAovDescriptor(RPR_AOV_BACKGROUND, false); m_aovDescriptors[RPR_AOV_VELOCITY] = HdRprAovDescriptor(RPR_AOV_VELOCITY, false); m_aovDescriptors[RPR_AOV_VIEW_SHADING_NORMAL] = HdRprAovDescriptor(RPR_AOV_VIEW_SHADING_NORMAL, false); + m_aovDescriptors[RPR_AOV_CAMERA_NORMAL] = HdRprAovDescriptor(RPR_AOV_CAMERA_NORMAL, false); m_computedAovDescriptors.resize(kComputedAovsCount); m_computedAovDescriptors[kNdcDepth] = HdRprAovDescriptor(kNdcDepth, false, HdFormatFloat32, GfVec4f(std::numeric_limits::infinity()), true); @@ -135,6 +142,13 @@ HdRprAovRegistry::HdRprAovRegistry() { addAovNameLookup(HdRprAovTokens->lpe6, m_aovDescriptors[RPR_AOV_LPE_6]); addAovNameLookup(HdRprAovTokens->lpe7, m_aovDescriptors[RPR_AOV_LPE_7]); addAovNameLookup(HdRprAovTokens->lpe8, m_aovDescriptors[RPR_AOV_LPE_8]); + addAovNameLookup(HdRprAovTokens->cameraNormal, m_aovDescriptors[RPR_AOV_CAMERA_NORMAL]); + addAovNameLookup(HdRprAovTokens->cryptomatteMat0, m_aovDescriptors[RPR_AOV_CRYPTOMATTE_MAT0]); + addAovNameLookup(HdRprAovTokens->cryptomatteMat1, m_aovDescriptors[RPR_AOV_CRYPTOMATTE_MAT1]); + addAovNameLookup(HdRprAovTokens->cryptomatteMat2, m_aovDescriptors[RPR_AOV_CRYPTOMATTE_MAT2]); + addAovNameLookup(HdRprAovTokens->cryptomatteObj0, m_aovDescriptors[RPR_AOV_CRYPTOMATTE_OBJ0]); + addAovNameLookup(HdRprAovTokens->cryptomatteObj1, m_aovDescriptors[RPR_AOV_CRYPTOMATTE_OBJ1]); + addAovNameLookup(HdRprAovTokens->cryptomatteObj2, m_aovDescriptors[RPR_AOV_CRYPTOMATTE_OBJ2]); } HdRprAovDescriptor const& HdRprAovRegistry::GetAovDesc(TfToken const& name) { diff --git a/pxr/imaging/plugin/hdRpr/aovDescriptor.h b/pxr/imaging/plugin/hdRpr/aovDescriptor.h index 20e9695dc..21068bda0 100644 --- a/pxr/imaging/plugin/hdRpr/aovDescriptor.h +++ b/pxr/imaging/plugin/hdRpr/aovDescriptor.h @@ -65,6 +65,13 @@ PXR_NAMESPACE_OPEN_SCOPE (lpe6) \ (lpe7) \ (lpe8) \ + (cameraNormal) \ + (cryptomatteMat0) \ + (cryptomatteMat1) \ + (cryptomatteMat2) \ + (cryptomatteObj0) \ + (cryptomatteObj1) \ + (cryptomatteObj2) \ TF_DECLARE_PUBLIC_TOKENS(HdRprAovTokens, HDRPR_AOV_TOKENS); diff --git a/pxr/imaging/plugin/hdRpr/mesh.cpp b/pxr/imaging/plugin/hdRpr/mesh.cpp index 780dfa816..ffcab2162 100644 --- a/pxr/imaging/plugin/hdRpr/mesh.cpp +++ b/pxr/imaging/plugin/hdRpr/mesh.cpp @@ -360,7 +360,9 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } } + bool isIgnoreContourDirty = false; bool isVisibilityMaskDirty = false; + bool isIdDirty = false; if (*dirtyBits & HdChangeTracker::DirtyPrimvar) { HdRprGeometrySettings geomSettings = {}; geomSettings.visibilityMask = kVisibleAll; @@ -376,6 +378,16 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, m_visibilityMask = geomSettings.visibilityMask; isVisibilityMaskDirty = true; } + + if (m_id != geomSettings.id) { + m_id = geomSettings.id; + isIdDirty = true; + } + + if (m_ignoreContour != geomSettings.ignoreContour) { + m_ignoreContour = geomSettings.ignoreContour; + isIgnoreContourDirty = true; + } } m_smoothNormals = m_displayStyle.flatShadingEnabled; @@ -420,7 +432,6 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, if (m_geomSubsets.empty()) { if (auto rprMesh = rprApi->CreateMesh(m_points, m_faceVertexIndices, m_normals, m_normalIndices, m_uvs, m_uvIndices, m_faceVertexCounts, m_topology.GetOrientation())) { - rprApi->SetMeshId(rprMesh, GetPrimId()); m_rprMeshes.push_back(rprMesh); } } else { @@ -524,7 +535,6 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } if (auto rprMesh = rprApi->CreateMesh(subsetPoints, subsetIndexes, subsetNormals, subsetNormalIndices, subsetUv, subsetUvIndices, subsetVertexPerFace, m_topology.GetOrientation())) { - rprApi->SetMeshId(rprMesh, GetPrimId()); m_rprMeshes.push_back(rprMesh); ++it; } else { @@ -664,7 +674,6 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, int32_t meshId = GetPrimId(); for (int j = meshInstances.size(); j < newNumInstances; ++j) { meshInstances.push_back(rprApi->CreateMeshInstance(m_rprMeshes[i])); - rprApi->SetMeshId(meshInstances.back(), meshId); } } } @@ -696,6 +705,24 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } } + if (newMesh || isIdDirty) { + uint32_t id = m_id >= 0 ? uint32_t(m_id) : GetPrimId(); + for (auto& rprMesh : m_rprMeshes) { + rprApi->SetMeshId(rprMesh, id); + } + for (auto& instances : m_rprMeshInstances) { + for (auto& rprMesh : instances) { + rprApi->SetMeshId(rprMesh, id); + } + } + } + + if (newMesh || isIgnoreContourDirty) { + for (auto& rprMesh : m_rprMeshes) { + rprApi->SetMeshIgnoreContour(rprMesh, m_ignoreContour); + } + } + if (updateTransform) { for (auto& rprMesh : m_rprMeshes) { rprApi->SetTransform(rprMesh, m_transformSamples.count, m_transformSamples.times.data(), m_transformSamples.values.data()); diff --git a/pxr/imaging/plugin/hdRpr/mesh.h b/pxr/imaging/plugin/hdRpr/mesh.h index 212216766..e45262a24 100644 --- a/pxr/imaging/plugin/hdRpr/mesh.h +++ b/pxr/imaging/plugin/hdRpr/mesh.h @@ -96,7 +96,9 @@ class HdRprMesh final : public HdRprBaseRprim { HdDisplayStyle m_displayStyle; int m_refineLevel = 0; - uint32_t m_visibilityMask; + int m_id = -1; + uint32_t m_visibilityMask = 0; + bool m_ignoreContour; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/primvarUtil.cpp b/pxr/imaging/plugin/hdRpr/primvarUtil.cpp index c08d1c393..b8b3f28f5 100644 --- a/pxr/imaging/plugin/hdRpr/primvarUtil.cpp +++ b/pxr/imaging/plugin/hdRpr/primvarUtil.cpp @@ -17,7 +17,9 @@ limitations under the License. PXR_NAMESPACE_OPEN_SCOPE TF_DEFINE_PRIVATE_TOKENS(HdRprGeometryPrimvarTokens, + ((id, "rpr:id")) ((subdivisionLevel, "rpr:subdivisionLevel")) + ((ignoreContour, "rpr:ignoreContour")) ((visibilityPrimary, "rpr:visibilityPrimary")) ((visibilityShadow, "rpr:visibilityShadow")) ((visibilityReflection, "rpr:visibilityReflection")) @@ -49,11 +51,15 @@ void HdRprParseGeometrySettings( }; for (auto& desc : constantPrimvarDescs) { - if (desc.name == HdRprGeometryPrimvarTokens->subdivisionLevel) { + if (desc.name == HdRprGeometryPrimvarTokens->id) { + HdRprGetConstantPrimvar(HdRprGeometryPrimvarTokens->id, sceneDelegate, id, &geomSettings->id); + } else if (desc.name == HdRprGeometryPrimvarTokens->subdivisionLevel) { int subdivisionLevel; if (HdRprGetConstantPrimvar(HdRprGeometryPrimvarTokens->subdivisionLevel, sceneDelegate, id, &subdivisionLevel)) { geomSettings->subdivisionLevel = std::max(0, std::min(subdivisionLevel, 7)); } + } else if (desc.name == HdRprGeometryPrimvarTokens->ignoreContour) { + HdRprGetConstantPrimvar(HdRprGeometryPrimvarTokens->ignoreContour, sceneDelegate, id, &geomSettings->ignoreContour); } else if (desc.name == HdRprGeometryPrimvarTokens->visibilityPrimary) { setVisibilityFlag(HdRprGeometryPrimvarTokens->visibilityPrimary, kVisiblePrimary); } else if (desc.name == HdRprGeometryPrimvarTokens->visibilityShadow) { diff --git a/pxr/imaging/plugin/hdRpr/primvarUtil.h b/pxr/imaging/plugin/hdRpr/primvarUtil.h index a92d41ef0..5eaba948d 100644 --- a/pxr/imaging/plugin/hdRpr/primvarUtil.h +++ b/pxr/imaging/plugin/hdRpr/primvarUtil.h @@ -36,8 +36,10 @@ bool HdRprIsValidPrimvarSize( size_t vertexInterpolationSize); struct HdRprGeometrySettings { - uint32_t visibilityMask = 0; + int id = -1; int subdivisionLevel = 0; + uint32_t visibilityMask = 0; + bool ignoreContour = false; }; void HdRprParseGeometrySettings( diff --git a/pxr/imaging/plugin/hdRpr/python/commonSettings.py b/pxr/imaging/plugin/hdRpr/python/commonSettings.py index c0cd479f8..7b2d614ff 100644 --- a/pxr/imaging/plugin/hdRpr/python/commonSettings.py +++ b/pxr/imaging/plugin/hdRpr/python/commonSettings.py @@ -75,10 +75,10 @@ ] class SettingValue(object): - def __init__(self, key, ui_name=None, disabled_platform=None): + def __init__(self, key, ui_name=None, enable_py_condition=None): self._key = key self._ui_name = ui_name - self.disabled_platform = disabled_platform + self.enable_py_condition = enable_py_condition def __eq__(self, obj): return self._key == obj diff --git a/pxr/imaging/plugin/hdRpr/python/generateFiles.py b/pxr/imaging/plugin/hdRpr/python/generateFiles.py index 3a36eee70..0a4f37279 100644 --- a/pxr/imaging/plugin/hdRpr/python/generateFiles.py +++ b/pxr/imaging/plugin/hdRpr/python/generateFiles.py @@ -9,22 +9,19 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import subprocess import argparse import os +import generateGeometrySettingFiles +import generateRenderSettingFiles +import generateLightSettingFiles + if __name__ == "__main__": p = argparse.ArgumentParser() - p.add_argument('scripts_dir', help="Directory with scripts") p.add_argument("install", help="The install root for generated files.") - p.add_argument("--houdini_root", help="The install root for generated files.") - p.add_argument('--python_exe', help="Directory with scripts") + p.add_argument("--for_houdini", action="store_true") args = p.parse_args() - generate_ds_files = [] - if args.houdini_root: - generate_ds_files = ['--generate_ds_files'] - - subprocess.check_call([args.python_exe, os.path.join(args.scripts_dir, 'generateLightSettingFiles.py'), args.install] + generate_ds_files) - subprocess.check_call([args.python_exe, os.path.join(args.scripts_dir, 'generateGeometrySettingFiles.py'), args.install] + generate_ds_files) - subprocess.check_call([args.python_exe, os.path.join(args.scripts_dir, 'generateRenderSettingFiles.py'), args.install] + generate_ds_files) + generateGeometrySettingFiles.generate(args.install, args.for_houdini) + generateRenderSettingFiles.generate(args.install, args.for_houdini) + generateLightSettingFiles.generate(args.install, args.for_houdini) diff --git a/pxr/imaging/plugin/hdRpr/python/generateGeometrySettingFiles.py b/pxr/imaging/plugin/hdRpr/python/generateGeometrySettingFiles.py index 743ec86fa..2234374b7 100644 --- a/pxr/imaging/plugin/hdRpr/python/generateGeometrySettingFiles.py +++ b/pxr/imaging/plugin/hdRpr/python/generateGeometrySettingFiles.py @@ -18,22 +18,34 @@ { 'name': 'Mesh', 'settings': [ + { + 'name': 'primvars:rpr:id', + 'ui_name': 'ID', + 'defaultValue': 0, + 'minValue': 0, + 'maxValue': 1 ** 16 + }, { 'name': 'primvars:rpr:subdivisionLevel', 'ui_name': 'Subidivision Level', 'defaultValue': 0, 'minValue': 0, 'maxValue': 7 + }, + { + 'name': 'primvars:rpr:ignoreContour', + 'ui_name': 'Ignore Contour', + 'defaultValue': False, + 'help': 'Whether to extract contour for a mesh or not' + }, + { + 'folder': 'Visibility Settings', + 'settings': visibility_flag_settings } - ] + visibility_flag_settings + ] } ] -if __name__ == "__main__": - p = argparse.ArgumentParser() - p.add_argument("install", help="The install root for generated files.") - p.add_argument("--generate_ds_files", default=False, action='store_true') - args = p.parse_args() - - if args.generate_ds_files: - generate_houdini_ds(args.install, 'Geometry', geometry_settings) +def generate(install, generate_ds_files): + if generate_ds_files: + generate_houdini_ds(install, 'Geometry', geometry_settings) diff --git a/pxr/imaging/plugin/hdRpr/python/generateLightSettingFiles.py b/pxr/imaging/plugin/hdRpr/python/generateLightSettingFiles.py index 50ddfcf7d..3174de482 100644 --- a/pxr/imaging/plugin/hdRpr/python/generateLightSettingFiles.py +++ b/pxr/imaging/plugin/hdRpr/python/generateLightSettingFiles.py @@ -28,11 +28,6 @@ } ] -if __name__ == "__main__": - p = argparse.ArgumentParser() - p.add_argument("install", help="The install root for generated files.") - p.add_argument("--generate_ds_files", default=False, action='store_true') - args = p.parse_args() - - if args.generate_ds_files: - generate_houdini_ds(args.install, 'Light', light_settings) +def generate(install, generate_ds_files): + if generate_ds_files: + generate_houdini_ds(install, 'Light', light_settings) diff --git a/pxr/imaging/plugin/hdRpr/python/generateRenderSettingFiles.py b/pxr/imaging/plugin/hdRpr/python/generateRenderSettingFiles.py index e460333ba..c930c8952 100644 --- a/pxr/imaging/plugin/hdRpr/python/generateRenderSettingFiles.py +++ b/pxr/imaging/plugin/hdRpr/python/generateRenderSettingFiles.py @@ -49,7 +49,11 @@ def hidewhen_hybrid(render_setting_categories): def hidewhen_not_northstar(render_setting_categories): return hidewhen_render_quality('!=', 'Northstar', render_setting_categories) -HYBRID_DISABLED_PLATFORM = 'Darwin' +def hidewhen_not_tahoe(render_setting_categories): + return hidewhen_render_quality('!=', 'Full', render_setting_categories) + +HYBRID_IS_AVAILABLE_PY_CONDITION = 'platform.system() != "Darwin"' +NORTHSTAR_ENABLED_PY_CONDITION = 'hou.pwd().parm("renderQuality").evalAsString() == "Northstar"' render_setting_categories = [ { @@ -61,9 +65,9 @@ def hidewhen_not_northstar(render_setting_categories): 'help': 'Render restart might be required', 'defaultValue': 'Northstar', 'values': [ - SettingValue('Low', disabled_platform=HYBRID_DISABLED_PLATFORM), - SettingValue('Medium', disabled_platform=HYBRID_DISABLED_PLATFORM), - SettingValue('High', disabled_platform=HYBRID_DISABLED_PLATFORM), + SettingValue('Low', enable_py_condition=HYBRID_IS_AVAILABLE_PY_CONDITION), + SettingValue('Medium', enable_py_condition=HYBRID_IS_AVAILABLE_PY_CONDITION), + SettingValue('High', enable_py_condition=HYBRID_IS_AVAILABLE_PY_CONDITION), SettingValue('Full', 'Full (Legacy)'), SettingValue('Northstar', 'Full') ] @@ -86,7 +90,8 @@ def hidewhen_not_northstar(render_setting_categories): SettingValue('Normal'), SettingValue('Texcoord'), SettingValue('Ambient Occlusion'), - SettingValue('Diffuse') + SettingValue('Diffuse'), + SettingValue('Contour', enable_py_condition=NORTHSTAR_ENABLED_PY_CONDITION), ] }, { @@ -98,6 +103,111 @@ def hidewhen_not_northstar(render_setting_categories): 'houdini': { 'hidewhen': 'renderMode != "AmbientOcclusion"' } + }, + { + 'folder': 'Contour Settings', + 'houdini': { + 'hidewhen': 'renderMode != "Contour"' + }, + 'settings': [ + { + 'name': 'contourAntialiasing', + 'ui_name': 'Antialiasing', + 'defaultValue': 1.0, + 'minValue': 0.0, + 'maxValue': 1.0, + 'houdini': { + 'hidewhen': 'renderMode != "Contour"' + } + }, + { + 'name': 'contourUseNormal', + 'ui_name': 'Use Normal', + 'defaultValue': True, + 'help': 'Whether to use geometry normals for edge detection or not', + 'houdini': { + 'hidewhen': 'renderMode != "Contour"' + } + }, + { + 'name': 'contourLinewidthNormal', + 'ui_name': 'Linewidth Normal', + 'defaultValue': 1.0, + 'minValue': 0.0, + 'maxValue': 100.0, + 'help': 'Linewidth of edges detected via normals', + 'houdini': { + 'hidewhen': ['renderMode != "Contour"', 'contourUseNormal == 0'] + } + }, + { + 'name': 'contourNormalThreshold', + 'ui_name': 'Normal Threshold', + 'defaultValue': 45.0, + 'minValue': 0.0, + 'maxValue': 180.0, + 'houdini': { + 'hidewhen': ['renderMode != "Contour"', 'contourUseNormal == 0'] + } + }, + { + 'name': 'contourUsePrimId', + 'ui_name': 'Use Primitive Id', + 'defaultValue': True, + 'help': 'Whether to use primitive Id for edge detection or not', + 'houdini': { + 'hidewhen': 'renderMode != "Contour"' + } + }, + { + 'name': 'contourLinewidthPrimId', + 'ui_name': 'Linewidth Primitive Id', + 'defaultValue': 1.0, + 'minValue': 0.0, + 'maxValue': 100.0, + 'help': 'Linewidth of edges detected via primitive Id', + 'houdini': { + 'hidewhen': ['renderMode != "Contour"', 'contourUsePrimId == 0'] + } + }, + { + 'name': 'contourUseMaterialId', + 'ui_name': 'Use Material Id', + 'defaultValue': True, + 'help': 'Whether to use material Id for edge detection or not', + 'houdini': { + 'hidewhen': 'renderMode != "Contour"' + } + }, + { + 'name': 'contourLinewidthMaterialId', + 'ui_name': 'Linewidth Material Id', + 'defaultValue': 1.0, + 'minValue': 0.0, + 'maxValue': 100.0, + 'help': 'Linewidth of edges detected via material Id', + 'houdini': { + 'hidewhen': ['renderMode != "Contour"', 'contourUseMaterialId == 0'] + } + }, + { + 'name': 'contourDebug', + 'ui_name': 'Debug', + 'defaultValue': False, + 'help': 'Whether to show colored outlines according to used features or not.\\n' + 'Colors legend:\\n' + ' * red - primitive Id\\n' + ' * green - material Id\\n' + ' * blue - normal\\n' + ' * yellow - primitive Id + material Id\\n' + ' * magenta - primitive Id + normal\\n' + ' * cyan - material Id + normal\\n' + ' * black - all', + 'houdini': { + 'hidewhen': 'renderMode != "Contour"' + } + } + ] } ], 'houdini': { @@ -138,6 +248,30 @@ def hidewhen_not_northstar(render_setting_categories): '"uiicon" VIEW_display_denoise' ] } + }, + { + 'folder': 'Denoise Settings', + 'houdini': { + 'hidewhen': 'enableDenoising == 0' + }, + 'settings': [ + { + 'name': 'denoiseMinIter', + 'ui_name': 'Denoise Min Iteration', + 'defaultValue': 4, + 'minValue': 1, + 'maxValue': 2 ** 16, + 'help': 'The first iteration on which denoising should be applied.' + }, + { + 'name': 'denoiseIterStep', + 'ui_name': 'Denoise Iteration Step', + 'defaultValue': 32, + 'minValue': 1, + 'maxValue': 2 ** 16, + 'help': 'Denoise use frequency. To denoise on each iteration, set to 1.' + } + ] } ] }, @@ -160,7 +294,7 @@ def hidewhen_not_northstar(render_setting_categories): { 'name': 'AdaptiveSampling', 'houdini': { - 'hidewhen': hidewhen_hybrid + 'hidewhen': hidewhen_not_tahoe }, 'settings': [ { @@ -267,7 +401,10 @@ def hidewhen_not_northstar(render_setting_categories): 'help': 'Controls value of \'Max Ray Depth\' in interactive mode.', 'defaultValue': 2, 'minValue': 1, - 'maxValue': 50 + 'maxValue': 50, + 'houdini': { + 'hidewhen': hidewhen_hybrid + } }, { 'name': 'interactiveResolutionDownscale', @@ -279,6 +416,15 @@ def hidewhen_not_northstar(render_setting_categories): 'houdini': { 'hidewhen': hidewhen_not_northstar } + }, + { + 'name': 'interactiveEnableDownscale', + 'ui_name': 'Downscale Resolution When Interactive', + 'help': 'Controls whether in interactive mode resolution should be downscaled or no.', + 'defaultValue': True, + 'houdini': { + 'hidewhen': hidewhen_not_tahoe + } } ] }, @@ -343,10 +489,7 @@ def hidewhen_not_northstar(render_setting_categories): { 'name': 'enableAlpha', 'ui_name': 'Enable Color Alpha', - 'defaultValue': True, - 'houdini': { - 'hidewhen': hidewhen_hybrid - } + 'defaultValue': True } ] }, @@ -397,7 +540,7 @@ def hidewhen_not_northstar(render_setting_categories): 'ui_name': 'Use Uniform Seed', 'defaultValue': True, 'houdini': { - 'hidewhen': 'renderQuality < 3' + 'hidewhen': hidewhen_hybrid } } ] @@ -422,6 +565,14 @@ def hidewhen_not_northstar(render_setting_categories): 'name': 'rprExportPath', 'defaultValue': '', 'c_type': 'std::string' + }, + { + 'name': 'rprExportAsSingleFile', + 'defaultValue': False + }, + { + 'name': 'rprExportUseImageCache', + 'defaultValue': False } ] } @@ -602,29 +753,29 @@ class HdRprConfig {{ dirty_flags_offset = 1 - rs_public_token_definitions = '' - rs_tokens_declaration = '#define HDRPR_RENDER_SETTINGS_TOKENS \\\n' - rs_category_dirty_flags = '' - rs_get_set_method_declarations = '' - rs_variables_declaration = '' - rs_mapped_values_enum = '' - rs_range_definitions = '' - rs_list_initialization = '' - rs_sync = '' - rs_get_set_method_definitions = '' - rs_set_default_values = '' - rs_validate_values = '' + rs_public_token_definitions = [] + rs_tokens_declaration = ['#define HDRPR_RENDER_SETTINGS_TOKENS \\\n'] + rs_category_dirty_flags = [] + rs_get_set_method_declarations = [] + rs_variables_declaration = [] + rs_mapped_values_enum = [] + rs_range_definitions = [] + rs_list_initialization = [] + rs_sync = [] + rs_get_set_method_definitions = [] + rs_set_default_values = [] + rs_validate_values = [] for category in render_setting_categories: disabled_category = False category_name = category['name'] dirty_flag = 'Dirty{}'.format(category_name) - rs_category_dirty_flags += ' {} = 1 << {},\n'.format(dirty_flag, dirty_flags_offset) + rs_category_dirty_flags.append(' {} = 1 << {},\n'.format(dirty_flag, dirty_flags_offset)) dirty_flags_offset += 1 - for setting in category['settings']: + def process_setting(setting): name = setting['name'] - rs_tokens_declaration += ' ({}) \\\n'.format(name) + rs_tokens_declaration.append(' ({}) \\\n'.format(name)) name_title = camel_case_capitalize(name) @@ -642,62 +793,62 @@ class HdRprConfig {{ value_tokens_list_name = '__{}Tokens'.format(name_title) value_tokens_name = 'HdRpr{}Tokens'.format(name_title) - rs_mapped_values_enum += '#define ' + value_tokens_list_name + rs_mapped_values_enum.append('#define ' + value_tokens_list_name) for value in setting['values']: - rs_mapped_values_enum += ' ({})'.format(value.get_key()) - rs_mapped_values_enum += '\n' + rs_mapped_values_enum.append(' ({})'.format(value.get_key())) + rs_mapped_values_enum.append('\n') - rs_mapped_values_enum += 'TF_DECLARE_PUBLIC_TOKENS({}, {});\n\n'.format(value_tokens_name, value_tokens_list_name) - rs_public_token_definitions += 'TF_DEFINE_PUBLIC_TOKENS({}, {});\n'.format(value_tokens_name, value_tokens_list_name) + rs_mapped_values_enum.append('TF_DECLARE_PUBLIC_TOKENS({}, {});\n\n'.format(value_tokens_name, value_tokens_list_name)) + rs_public_token_definitions.append('TF_DEFINE_PUBLIC_TOKENS({}, {});\n'.format(value_tokens_name, value_tokens_list_name)) type_str = 'TfToken' c_type_str = type_str default_value = next(value for value in setting['values'] if value == default_value) - rs_get_set_method_declarations += ' void Set{}({} {});\n'.format(name_title, c_type_str, name) - rs_get_set_method_declarations += ' {} const& Get{}() const {{ return m_prefData.{}; }}\n\n'.format(type_str, name_title, name) + rs_get_set_method_declarations.append(' void Set{}({} {});\n'.format(name_title, c_type_str, name)) + rs_get_set_method_declarations.append(' {} const& Get{}() const {{ return m_prefData.{}; }}\n\n'.format(type_str, name_title, name)) - rs_variables_declaration += ' {} {};\n'.format(type_str, name) + rs_variables_declaration.append(' {} {};\n'.format(type_str, name)) if isinstance(default_value, bool): - rs_sync += ' Set{name_title}(getBoolSetting(HdRprRenderSettingsTokens->{name}, k{name_title}Default));\n'.format(name_title=name_title, name=name) + rs_sync.append(' Set{name_title}(getBoolSetting(HdRprRenderSettingsTokens->{name}, k{name_title}Default));\n'.format(name_title=name_title, name=name)) else: - rs_sync += ' Set{name_title}(renderDelegate->GetRenderSetting(HdRprRenderSettingsTokens->{name}, k{name_title}Default));\n'.format(name_title=name_title, name=name) + rs_sync.append(' Set{name_title}(renderDelegate->GetRenderSetting(HdRprRenderSettingsTokens->{name}, k{name_title}Default));\n'.format(name_title=name_title, name=name)) if 'values' in setting: - rs_range_definitions += '#define k{name_title}Default {value_tokens_name}->{value}'.format(name_title=name_title, value_tokens_name=value_tokens_name, value=default_value.get_key()) + rs_range_definitions.append('#define k{name_title}Default {value_tokens_name}->{value}'.format(name_title=name_title, value_tokens_name=value_tokens_name, value=default_value.get_key())) else: value_str = str(default_value) if isinstance(default_value, bool): value_str = value_str.lower() - rs_range_definitions += 'const {type} k{name_title}Default = {type}({value});\n'.format(type=type_str, name_title=name_title, value=value_str) + rs_range_definitions.append('const {type} k{name_title}Default = {type}({value});\n'.format(type=type_str, name_title=name_title, value=value_str)) set_validation = '' if 'minValue' in setting or 'maxValue' in setting: - rs_validate_values += ' ' + rs_validate_values.append(' ') if 'minValue' in setting: - rs_range_definitions += 'const {type} k{name_title}Min = {type}({value});\n'.format(type=type_str, name_title=name_title, value=setting['minValue']) + rs_range_definitions.append('const {type} k{name_title}Min = {type}({value});\n'.format(type=type_str, name_title=name_title, value=setting['minValue'])) set_validation += ' if ({name} < k{name_title}Min) {{ return; }}\n'.format(name=name, name_title=name_title) - rs_validate_values += '&& {name} < k{name_title}Min'.format(name=name, name_title=name_title) + rs_validate_values.append('&& {name} < k{name_title}Min'.format(name=name, name_title=name_title)) if 'maxValue' in setting: - rs_range_definitions += 'const {type} k{name_title}Max = {type}({value});\n'.format(type=type_str, name_title=name_title, value=setting['maxValue']) + rs_range_definitions.append('const {type} k{name_title}Max = {type}({value});\n'.format(type=type_str, name_title=name_title, value=setting['maxValue'])) set_validation += ' if ({name} > k{name_title}Max) {{ return; }}\n'.format(name=name, name_title=name_title) - rs_validate_values += '&& {name} > k{name_title}Max'.format(name=name, name_title=name_title) + rs_validate_values.append('&& {name} > k{name_title}Max'.format(name=name, name_title=name_title)) if 'minValue' in setting or 'maxValue' in setting: - rs_validate_values += '\n' - rs_range_definitions += '\n' + rs_validate_values.append('\n') + rs_range_definitions.append('\n') if 'values' in setting: value_range = value_tokens_name + '->allTokens' set_validation += ' if (std::find({range}.begin(), {range}.end(), {name}) == {range}.end()) return;\n'.format(range=value_range, name=name) if 'ui_name' in setting: - rs_list_initialization += ' settingDescs.push_back({{"{}", HdRprRenderSettingsTokens->{}, VtValue(k{}Default)}});\n'.format(setting['ui_name'], name, name_title) + rs_list_initialization.append(' settingDescs.push_back({{"{}", HdRprRenderSettingsTokens->{}, VtValue(k{}Default)}});\n'.format(setting['ui_name'], name, name_title)) if disabled_category: - rs_get_set_method_definitions += 'void HdRprConfig::Set{name_title}({c_type} {name}) {{ /* Platform no-op */ }}'.format(name_title=name_title, c_type=c_type_str, name=name) + rs_get_set_method_definitions.append('void HdRprConfig::Set{name_title}({c_type} {name}) {{ /* Platform no-op */ }}'.format(name_title=name_title, c_type=c_type_str, name=name)) else: - rs_get_set_method_definitions += ( + rs_get_set_method_definitions.append(( ''' void HdRprConfig::Set{name_title}({c_type} {name}) {{ {set_validation} @@ -706,40 +857,42 @@ class HdRprConfig {{ m_dirtyFlags |= {dirty_flag}; }} }} -''').format(name_title=name_title, c_type=c_type_str, name=name, dirty_flag=dirty_flag, set_validation=set_validation) +''').format(name_title=name_title, c_type=c_type_str, name=name, dirty_flag=dirty_flag, set_validation=set_validation)) - rs_set_default_values += ' {name} = k{name_title}Default;\n'.format(name=name, name_title=name_title) + rs_set_default_values.append(' {name} = k{name_title}Default;\n'.format(name=name, name_title=name_title)) - rs_tokens_declaration += '\nTF_DECLARE_PUBLIC_TOKENS(HdRprRenderSettingsTokens, HDRPR_RENDER_SETTINGS_TOKENS);\n' + for setting in category['settings']: + if 'folder' in setting: + for sub_setting in setting['settings']: + process_setting(sub_setting) + else: + process_setting(setting) + + rs_tokens_declaration.append('\nTF_DECLARE_PUBLIC_TOKENS(HdRprRenderSettingsTokens, HDRPR_RENDER_SETTINGS_TOKENS);\n') header_dst_path = os.path.join(install_path, 'config.h') header_file = open(header_dst_path, 'w') header_file.write(header_template.format( - rs_tokens_declaration=rs_tokens_declaration, - rs_category_dirty_flags=rs_category_dirty_flags, - rs_get_set_method_declarations=rs_get_set_method_declarations, - rs_variables_declaration=rs_variables_declaration, - rs_mapped_values_enum=rs_mapped_values_enum)) + rs_tokens_declaration=''.join(rs_tokens_declaration), + rs_category_dirty_flags=''.join(rs_category_dirty_flags), + rs_get_set_method_declarations=''.join(rs_get_set_method_declarations), + rs_variables_declaration=''.join(rs_variables_declaration), + rs_mapped_values_enum=''.join(rs_mapped_values_enum))) cpp_dst_path = os.path.join(install_path, 'config.cpp') cpp_file = open(cpp_dst_path, 'w') cpp_file.write(cpp_template.format( - rs_public_token_definitions=rs_public_token_definitions, - rs_range_definitions=rs_range_definitions, - rs_list_initialization=rs_list_initialization, - rs_sync=rs_sync, - rs_get_set_method_definitions=rs_get_set_method_definitions, - rs_set_default_values=rs_set_default_values, - rs_validate_values=rs_validate_values)) + rs_public_token_definitions=''.join(rs_public_token_definitions), + rs_range_definitions=''.join(rs_range_definitions), + rs_list_initialization=''.join(rs_list_initialization), + rs_sync=''.join(rs_sync), + rs_get_set_method_definitions=''.join(rs_get_set_method_definitions), + rs_set_default_values=''.join(rs_set_default_values), + rs_validate_values=''.join(rs_validate_values))) if generate_ds_files: generate_houdini_ds(install_path, 'Global', render_setting_categories) -if __name__ == "__main__": - p = argparse.ArgumentParser() - p.add_argument("install", help="The install root for generated files.") - p.add_argument("--generate_ds_files", default=False, action='store_true') - args = p.parse_args() - - generate_render_setting_files(args.install, args.generate_ds_files) +def generate(install, generate_ds_files): + generate_render_setting_files(install, generate_ds_files) diff --git a/pxr/imaging/plugin/hdRpr/python/houdiniDsGenerator.py b/pxr/imaging/plugin/hdRpr/python/houdiniDsGenerator.py index 76236d15c..d55edb5ad 100644 --- a/pxr/imaging/plugin/hdRpr/python/houdiniDsGenerator.py +++ b/pxr/imaging/plugin/hdRpr/python/houdiniDsGenerator.py @@ -44,143 +44,170 @@ ''' ) -def generate_houdini_ds(install_path, ds_name, settings): - import hou +def _get_valid_houdini_param_name(name): + if all(c.isalnum() or c == '_' for c in name): + return name + else: + import hou + return hou.encode(name) + +def _get_houdini_hidewhen_string(conditions, settings): + houdini_hidewhen_conditions = [] + for condition in conditions: + if condition and callable(condition): + condition = condition(settings) + if condition: + if isinstance(condition, str): + houdini_hidewhen_conditions.append(condition) + elif isinstance(condition, list): + houdini_hidewhen_conditions.extend(condition); + + houdini_hidewhen = '' + if houdini_hidewhen_conditions: + houdini_hidewhen += 'hidewhen "' + for condition in houdini_hidewhen_conditions: + houdini_hidewhen += '{{ {} }} '.format(condition) + houdini_hidewhen += '"' + return houdini_hidewhen + +def _generate_ds_setting(setting, spare_category, global_hidewhen, settings): + if not 'ui_name' in setting: + return '' + + houdini_settings = setting.get('houdini', {}) + houdini_hidewhen = _get_houdini_hidewhen_string((houdini_settings.get('hidewhen'), global_hidewhen), settings) + + def CreateHoudiniParam(name, label, htype, default, values=[], tags=[], disablewhen_conditions=[], size=None, valid_range=None, help_msg=None): + param = 'parm {\n' + param += ' name "{}"\n'.format(_get_valid_houdini_param_name(name)) + param += ' label "{}"\n'.format(label) + param += ' type {}\n'.format(htype) + if size: param += ' size {}\n'.format(size) + param += ' default {{ {} }}\n'.format(default) + for tag in tags: + param += ' parmtag {{ {} }}\n'.format(tag) + if values: + param += ' menu {\n' + param += ' ' + values + '\n' + param += ' }\n' + if houdini_hidewhen: + param += ' {}\n'.format(houdini_hidewhen) + if disablewhen_conditions: + param += ' disablewhen "' + for condition in disablewhen_conditions: + param += '{{ {} }} '.format(condition) + param += '"\n' + if valid_range: + param += ' range {{ {}! {} }}\n'.format(valid_range[0], valid_range[1]) + if help_msg: + param += ' help "{}"\n'.format(help_msg) + param += '}\n' + + return param + + name = setting['name'] + + control_param_name = _get_valid_houdini_param_name(name + '_control') + + render_param_values = None + default_value = setting['defaultValue'] + c_type_str = type(default_value).__name__ + controlled_type = c_type_str + if c_type_str == 'str': + c_type_str = 'string' + controlled_type = 'string' + render_param_type = c_type_str + render_param_default = default_value + if isinstance(default_value, bool): + render_param_type = 'toggle' + render_param_default = 1 if default_value else 0 + elif 'values' in setting: + default_value = next(value for value in setting['values'] if value == default_value) + render_param_default = '"{}"'.format(default_value.get_key()) + render_param_type = 'string' + c_type_str = 'token' + + is_values_constant = True + for value in setting['values']: + if value.enable_py_condition: + is_values_constant = False + break + + render_param_values = '' + if is_values_constant: + for value in setting['values']: + render_param_values += '"{}" "{}"\n'.format(value.get_key(), value.get_ui_name()) + else: + render_param_values += '[ "import platform" ]\n' + render_param_values += '[ "menu_values = []" ]\n' + for value in setting['values']: + expression = 'menu_values.extend([\\"{}\\", \\"{}\\"])'.format(value.get_key(), value.get_ui_name()) + + if value.enable_py_condition: + enable_condition = value.enable_py_condition.replace('"', '\\"') + expression = 'if {}: {}'.format(enable_condition, expression) + + render_param_values += '[ "{}" ]\n'.format(expression) + render_param_values += '[ "return menu_values" ]\n'.format(expression) + render_param_values += 'language python\n' + + if 'type' in houdini_settings: + render_param_type = houdini_settings['type'] + + render_param_range = None + if 'minValue' in setting and 'maxValue' in setting and not 'values' in setting: + render_param_range = (setting['minValue'], setting['maxValue']) + + houdini_param_label = setting['ui_name'] + + houdini_params = control_param_template.format( + name=control_param_name, + label=houdini_param_label, + controlled_type=controlled_type, + hidewhen=houdini_hidewhen) + houdini_params += CreateHoudiniParam(name, houdini_param_label, render_param_type, render_param_default, + values=render_param_values, + tags=[ + '"spare_category" "{}"'.format(spare_category), + '"uiscope" "viewport"', + '"usdvaluetype" "{}"'.format(c_type_str) + ] + houdini_settings.get('custom_tags', []), + disablewhen_conditions=[ + control_param_name + ' == block', + control_param_name + ' == none', + ], + size=1, + valid_range=render_param_range, + help_msg=setting.get('help', None)) + + return houdini_params +def generate_houdini_ds(install_path, ds_name, settings): houdini_params = '' for category in settings: - disabled_category = False - category_name = category['name'] + category_hidewhen = None + if 'houdini' in category: + category_hidewhen = category['houdini'].get('hidewhen') for setting in category['settings']: - if not 'ui_name' in setting: - continue - - houdini_hidewhen_conditions = [] - def add_hidewhen_condition(condition): - if condition and callable(condition): - condition = condition(settings) - if condition: - if isinstance(condition, str): - houdini_hidewhen_conditions.append(condition) - elif isinstance(condition, list): - houdini_hidewhen_conditions.extend(condition); - - if 'houdini' in category: - add_hidewhen_condition(category['houdini'].get('hidewhen')) - - houdini_settings = setting.get('houdini', {}) - houdini_param_label = setting['ui_name'] - add_hidewhen_condition(houdini_settings.get('hidewhen')) - - houdini_hidewhen = '' - if houdini_hidewhen_conditions: - houdini_hidewhen += 'hidewhen "' - for condition in houdini_hidewhen_conditions: - houdini_hidewhen += '{{ {} }} '.format(condition) - houdini_hidewhen += '"' - - def CreateHoudiniParam(name, label, htype, default, values=[], tags=[], disablewhen_conditions=[], size=None, valid_range=None, help_msg=None): - param = 'parm {\n' - param += ' name "{}"\n'.format(hou.encode(name)) - param += ' label "{}"\n'.format(label) - param += ' type {}\n'.format(htype) - if size: param += ' size {}\n'.format(size) - param += ' default {{ {} }}\n'.format(default) - for tag in tags: - param += ' parmtag {{ {} }}\n'.format(tag) - if values: - param += ' menu {\n' - param += ' ' + values + '\n' - param += ' }\n' - if disabled_category: - param += ' invisible\n' + if 'folder' in setting: + houdini_params += 'groupcollapsible {\n' + houdini_params += ' name "{}"\n'.format(setting['folder'].replace(' ', '')) + houdini_params += ' label "{}"\n'.format(setting['folder']) + + houdini_settings = setting.get('houdini', {}) + houdini_hidewhen = _get_houdini_hidewhen_string((houdini_settings.get('hidewhen'), category_hidewhen), settings) if houdini_hidewhen: - param += ' {}\n'.format(houdini_hidewhen) - if disablewhen_conditions: - param += ' disablewhen "' - for condition in disablewhen_conditions: - param += '{{ {} }} '.format(condition) - param += '"\n' - if valid_range: - param += ' range {{ {}! {} }}\n'.format(valid_range[0], valid_range[1]) - if help_msg: - param += ' help "{}"\n'.format(help_msg) - param += '}\n' - - return param - - name = setting['name'] - - control_param_name = hou.encode(name + '_control') - - render_param_values = None - default_value = setting['defaultValue'] - c_type_str = type(default_value).__name__ - controlled_type = c_type_str - if c_type_str == 'str': - c_type_str = 'string' - controlled_type = 'string' - render_param_type = c_type_str - render_param_default = default_value - if isinstance(default_value, bool): - render_param_type = 'toggle' - render_param_default = 1 if default_value else 0 - elif 'values' in setting: - default_value = next(value for value in setting['values'] if value == default_value) - render_param_default = '"{}"'.format(default_value.get_key()) - render_param_type = 'string' - c_type_str = 'token' - - is_values_constant = True - for value in setting['values']: - if value.disabled_platform: - is_values_constant = False - break - - render_param_values = '' - if is_values_constant: - for value in setting['values']: - render_param_values += '"{}" "{}"\n'.format(value.get_key(), value.get_ui_name()) - else: - render_param_values += '[ "import platform" ]\n' - render_param_values += '[ "menu_values = []" ]\n' - for value in setting['values']: - expression = 'menu_values.extend([\\"{}\\", \\"{}\\"])'.format(value.get_key(), value.get_ui_name()) - if value.disabled_platform: - expression = 'if platform.system() != \\"{}\\": {}'.format(value.disabled_platform, expression) - render_param_values += '[ "{}" ]\n'.format(expression) - render_param_values += '[ "return menu_values" ]\n'.format(expression) - render_param_values += 'language python\n' - - if 'type' in houdini_settings: - render_param_type = houdini_settings['type'] - - render_param_range = None - if 'minValue' in setting and 'maxValue' in setting and not 'values' in setting: - render_param_range = (setting['minValue'], setting['maxValue']) - - houdini_params += control_param_template.format( - name=control_param_name, - label=houdini_param_label, - controlled_type=controlled_type, - hidewhen=houdini_hidewhen) - houdini_params += CreateHoudiniParam(name, houdini_param_label, render_param_type, render_param_default, - values=render_param_values, - tags=[ - '"spare_category" "{}"'.format(category_name), - '"uiscope" "viewport"', - '"usdvaluetype" "{}"'.format(c_type_str) - ] + houdini_settings.get('custom_tags', []), - disablewhen_conditions=[ - control_param_name + ' == block', - control_param_name + ' == none', - ], - size=1, - valid_range=render_param_range, - help_msg=setting.get('help', None)) + houdini_params += ' {}\n'.format(houdini_hidewhen) + + + for sub_setting in setting['settings']: + houdini_params += _generate_ds_setting(sub_setting, category_name, category_hidewhen, settings) + houdini_params += '}\n' + else: + houdini_params += _generate_ds_setting(setting, category_name, category_hidewhen, settings) if houdini_params: houdini_ds_dst_path = os.path.join(install_path, 'HdRprPlugin_{}.ds'.format(ds_name)) diff --git a/pxr/imaging/plugin/hdRpr/rprApi.cpp b/pxr/imaging/plugin/hdRpr/rprApi.cpp index 0eca02e52..51e7fdea9 100644 --- a/pxr/imaging/plugin/hdRpr/rprApi.cpp +++ b/pxr/imaging/plugin/hdRpr/rprApi.cpp @@ -276,6 +276,13 @@ class HdRprApiImpl { try { InitRpr(); InitRif(); + + { + HdRprConfig* config; + auto configInstanceLock = HdRprConfig::GetInstance(&config); + UpdateSettings(*config, true); + } + InitScene(); InitCamera(); InitAovs(); @@ -560,6 +567,16 @@ class HdRprApiImpl { RPR_ERROR_CHECK(mesh->SetObjectID(id), "Failed to set mesh id"); } + void SetMeshIgnoreContour(rpr::Shape* mesh, bool ignoreContour) { + if (m_rprContextMetadata.pluginType == kPluginNorthstar) { + LockGuard rprLock(m_rprContext->GetMutex()); + // TODO: update C++ wrapper + RPR_ERROR_CHECK(rprShapeSetContourIgnore(rpr::GetRprObject(mesh), ignoreContour), "Failed to set shape contour ignore"); + + m_dirtyFlags |= ChangeTracker::DirtyScene; + } + } + rpr::Curve* CreateCurve(VtVec3fArray const& points, VtIntArray const& indices, VtFloatArray const& radiuses, VtVec2fArray const& uvs, VtIntArray const& segmentPerCurve) { if (!m_rprContext) { return nullptr; @@ -804,7 +821,7 @@ class HdRprApiImpl { LockGuard rprLock(m_rprContext->GetMutex()); - auto image = std::unique_ptr(RprUsdCoreImage::Create(m_rprContext.get(), path)); + auto image = std::unique_ptr(RprUsdCoreImage::Create(m_rprContext.get(), path, 0)); if (!image) { return nullptr; } @@ -1373,6 +1390,9 @@ class HdRprApiImpl { enableDenoise.isDirty = config->IsDirty(HdRprConfig::DirtyDenoise); if (enableDenoise.isDirty) { enableDenoise.value = config->GetEnableDenoising(); + + m_denoiseMinIter = config->GetDenoiseMinIter(); + m_denoiseIterStep = config->GetDenoiseIterStep(); } tonemap.isDirty = config->IsDirty(HdRprConfig::DirtyTonemapping); @@ -1391,6 +1411,8 @@ class HdRprApiImpl { if (config->IsDirty(HdRprConfig::DirtyRprExport)) { m_rprSceneExportPath = config->GetRprExportPath(); + m_rprExportAsSingleFile = config->GetRprExportAsSingleFile(); + m_rprExportUseImageCache = config->GetRprExportUseImageCache(); } if (config->IsDirty(HdRprConfig::DirtyRenderQuality)) { @@ -1469,6 +1491,56 @@ class HdRprApiImpl { return it->second; } + void UpdateRenderMode(HdRprConfig const& preferences, bool force) { + if (!preferences.IsDirty(HdRprConfig::DirtyRenderMode) && !force) { + return; + } + m_dirtyFlags |= ChangeTracker::DirtyScene; + + auto& renderMode = preferences.GetRenderMode(); + + if (m_rprContextMetadata.pluginType == kPluginNorthstar) { + if (renderMode == HdRprRenderModeTokens->Contour) { + if (!m_contourAovs) { + m_contourAovs = std::make_unique(); + m_contourAovs->normal = CreateAov(HdAovTokens->normal); + m_contourAovs->primId = CreateAov(HdAovTokens->primId); + m_contourAovs->materialId = CreateAov(HdRprAovTokens->materialId); + } + + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_CONTOUR_DEBUG_ENABLED, preferences.GetContourDebug()), "Failed to set contour debug"); + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_CONTOUR_ANTIALIASING, preferences.GetContourAntialiasing()), "Failed to set contour antialiasing"); + + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_CONTOUR_USE_NORMAL, int(preferences.GetContourUseNormal())), "Failed to set contour use normal"); + if (preferences.GetContourUseNormal()) { + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_CONTOUR_LINEWIDTH_NORMAL, preferences.GetContourLinewidthNormal()), "Failed to set contour normal linewidth"); + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_CONTOUR_NORMAL_THRESHOLD, preferences.GetContourNormalThreshold()), "Failed to set contour normal threshold"); + } + + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_CONTOUR_USE_OBJECTID, int(preferences.GetContourUsePrimId())), "Failed to set contour use primId"); + if (preferences.GetContourUsePrimId()) { + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_CONTOUR_LINEWIDTH_OBJECTID, preferences.GetContourLinewidthPrimId()), "Failed to set contour primId linewidth"); + } + + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_CONTOUR_USE_MATERIALID, int(preferences.GetContourUseMaterialId())), "Failed to set contour use materialId"); + if (preferences.GetContourUseMaterialId()) { + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_CONTOUR_LINEWIDTH_MATERIALID, preferences.GetContourLinewidthMaterialId()), "Failed to set contour materialId linewidth"); + } + + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_GPUINTEGRATOR, "gpucontour"), "Failed to set gpuintegrator"); + return; + } else { + m_contourAovs = nullptr; + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_GPUINTEGRATOR, "gpusimple"), "Failed to set gpuintegrator"); + } + } + + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_RENDER_MODE, GetRprRenderMode(renderMode)), "Failed to set render mode"); + if (renderMode == HdRprRenderModeTokens->AmbientOcclusion) { + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_AO_RAY_LENGTH, preferences.GetAoRadius()), "Failed to set ambient occlusion radius"); + } + } + void UpdateTahoeSettings(HdRprConfig const& preferences, bool force) { if (preferences.IsDirty(HdRprConfig::DirtyAdaptiveSampling) || force) { m_varianceThreshold = preferences.GetVarianceThreshold(); @@ -1478,7 +1550,7 @@ class HdRprApiImpl { if (IsAdaptiveSamplingEnabled()) { if (!m_internalAovs.count(HdRprAovTokens->variance)) { - if (auto aov = CreateAov(HdRprAovTokens->variance, m_viewportSize[0], m_viewportSize[1])) { + if (auto aov = CreateAov(HdRprAovTokens->variance)) { m_internalAovs.emplace(HdRprAovTokens->variance, std::move(aov)); } else { TF_RUNTIME_ERROR("Failed to create variance AOV, adaptive sampling will not work"); @@ -1519,7 +1591,8 @@ class HdRprApiImpl { } RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_PREVIEW, uint32_t(downscale)), "Failed to set preview mode"); } else { - RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_PREVIEW, uint32_t(m_isInteractive)), "Failed to set preview mode"); + bool enableDownscale = m_isInteractive && preferences.GetInteractiveEnableDownscale(); + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_PREVIEW, uint32_t(enableDownscale)), "Failed to set preview mode"); } if (preferences.IsDirty(HdRprConfig::DirtyInteractiveMode) || m_isInteractive) { @@ -1527,14 +1600,7 @@ class HdRprApiImpl { } } - if (preferences.IsDirty(HdRprConfig::DirtyRenderMode) || force) { - auto& renderMode = preferences.GetRenderMode(); - RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_RENDER_MODE, GetRprRenderMode(renderMode)), "Failed to set render mode"); - if (renderMode == HdRprRenderModeTokens->AmbientOcclusion) { - RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_AO_RAY_LENGTH, preferences.GetAoRadius()), "Failed to set ambient occlusion radius"); - } - m_dirtyFlags |= ChangeTracker::DirtyScene; - } + UpdateRenderMode(preferences, force); if (preferences.IsDirty(HdRprConfig::DirtySeed) || force) { m_isUniformSeed = preferences.GetUniformSeed(); @@ -1594,18 +1660,19 @@ class HdRprApiImpl { } } - if (preferences.IsDirty(HdRprConfig::DirtyAlpha) || force) { - m_isAlphaEnabled = preferences.GetEnableAlpha(); - - UpdateColorAlpha(); - } - if (m_rprContextMetadata.pluginType == kPluginTahoe || m_rprContextMetadata.pluginType == kPluginNorthstar) { UpdateTahoeSettings(preferences, force); } else if (m_rprContextMetadata.pluginType == kPluginHybrid) { UpdateHybridSettings(preferences, force); } + + if (preferences.IsDirty(HdRprConfig::DirtyAlpha) || force || + (m_rprContextMetadata.pluginType == kPluginNorthstar && preferences.IsDirty(HdRprConfig::DirtyRenderMode))) { + m_isAlphaEnabled = preferences.GetEnableAlpha(); + + UpdateColorAlpha(); + } } void UpdateCamera(RenderSetting const& aspectRatioPolicy, RenderSetting const& instantaneousShutter) { @@ -1797,6 +1864,15 @@ class HdRprApiImpl { } void UpdateAovs(HdRprRenderParam* rprRenderParam, RenderSetting enableDenoise, RenderSetting tonemap, bool clearAovs) { + auto colorAov = GetColorAov(); + if (colorAov) { + UpdateDenoising(enableDenoise, colorAov); + + if (tonemap.isDirty) { + colorAov->SetTonemap(tonemap.value); + } + } + if (m_dirtyFlags & (ChangeTracker::DirtyAOVBindings | ChangeTracker::DirtyAOVRegistry)) { m_resolveData.rawAovs.clear(); m_resolveData.computedAovs.clear(); @@ -1844,15 +1920,6 @@ class HdRprApiImpl { clearAovs = true; } - auto colorAov = GetColorAov(); - if (colorAov) { - UpdateDenoising(enableDenoise, colorAov); - - if (tonemap.isDirty) { - colorAov->SetTonemap(tonemap.value); - } - } - auto rprApi = rprRenderParam->GetRprApi(); m_resolveData.ForAllAovs([=](ResolveData::AovEntry const& e) { e.aov->Update(rprApi, m_rifContext.get()); @@ -1880,12 +1947,14 @@ class HdRprApiImpl { return; } - if (!enableDenoise.isDirty) { + if (!enableDenoise.isDirty || + m_isDenoiseEnabled == enableDenoise.value) { return; } - if (!enableDenoise.value) { - colorAov->DisableDenoise(m_rifContext.get()); + m_isDenoiseEnabled = enableDenoise.value; + if (!m_isDenoiseEnabled) { + colorAov->DeinitDenoise(m_rifContext.get()); return; } @@ -1895,15 +1964,15 @@ class HdRprApiImpl { } if (filterType == rif::FilterType::EawDenoise) { - colorAov->EnableEAWDenoise(m_internalAovs.at(HdRprAovTokens->albedo), - m_internalAovs.at(HdAovTokens->normal), - m_internalAovs.at(HdRprGetCameraDepthAovName()), - m_internalAovs.at(HdAovTokens->primId), - m_internalAovs.at(HdRprAovTokens->worldCoordinate)); + colorAov->InitEAWDenoise(CreateAov(HdRprAovTokens->albedo), + CreateAov(HdAovTokens->normal), + CreateAov(HdRprGetCameraDepthAovName()), + CreateAov(HdAovTokens->primId), + CreateAov(HdRprAovTokens->worldCoordinate)); } else { - colorAov->EnableAIDenoise(m_internalAovs.at(HdRprAovTokens->albedo), - m_internalAovs.at(HdAovTokens->normal), - m_internalAovs.at(HdRprGetCameraDepthAovName())); + colorAov->InitAIDenoise(CreateAov(HdRprAovTokens->albedo), + CreateAov(HdAovTokens->normal), + CreateAov(HdRprGetCameraDepthAovName())); } } @@ -1969,6 +2038,15 @@ class HdRprApiImpl { bool CommonRenderImplPrologue() { if (m_numSamples == 0) { + // Default resolve mode + m_resolveMode = kResolveAfterRender; + + // When we want to have a uniform seed across all frames, + // we need to make sure that RPR_CONTEXT_FRAMECOUNT sequence is the same for all of them + if (m_isUniformSeed) { + m_frameCount = 0; + } + // Disable aborting on the very first sample // // Ideally, aborting the first sample should not be the problem. @@ -1981,15 +2059,6 @@ class HdRprApiImpl { } m_abortRender.store(false); - // Default resolve mode - m_resolveMode = kResolveAfterRender; - - // When we want to have a uniform seed across all frames, - // we need to make sure that RPR_CONTEXT_FRAMECOUNT sequence is the same for all of them - if (m_isUniformSeed && m_numSamples == 0) { - m_frameCount = 0; - } - // If the changes that were made by the user did not reset our AOVs, // we can just resolve them to the current render buffers and we are done with the rendering if (IsConverged()) { @@ -2037,11 +2106,29 @@ class HdRprApiImpl { } } + if (m_contourAovs) { + // In contour rendering mode we must render with RPR_CONTEXT_ITERATIONS=1 + if (isMaximizingContextIterations) { + isMaximizingContextIterations = false; + if (m_isRenderUpdateCallbackEnabled) { + m_isRenderUpdateCallbackEnabled = false; + RPR_ERROR_CHECK_THROW(m_rprContext->SetParameter(RPR_CONTEXT_RENDER_UPDATE_CALLBACK_FUNC, (void*)nullptr), "Failed to disable RUC func"); + } + } + } + // Though if adaptive sampling is enabled in a batch session we first render m_minSamples samples // and after that render 1 sample at a time because we want to query the current amount of // active pixels as often as possible const bool isAdaptiveSamplingEnabled = IsAdaptiveSamplingEnabled(); + // In a batch session, we do denoise once at the end + auto rprApi = static_cast(m_delegate->GetRenderParam())->GetRprApi(); + auto colorAov = GetColorAov(); + if (colorAov && m_isDenoiseEnabled) { + colorAov->SetDenoise(false, rprApi, m_rifContext.get()); + } + while (!IsConverged()) { if (renderThread->IsStopRequested()) { break; @@ -2123,6 +2210,10 @@ class HdRprApiImpl { } } + if (m_isDenoiseEnabled) { + colorAov->SetDenoise(true, rprApi, m_rifContext.get()); + } + ResolveFramebuffers(); } @@ -2153,6 +2244,20 @@ class HdRprApiImpl { EnableRenderUpdateCallback(RenderUpdateCallback); + const bool progressivelyIncreaseSamplesPerIter = + // Progressively increasing RPR_CONTEXT_ITERATIONS makes sense only for Northstar + // because, first, it highly improves its performance (internal optimization) + // and, second, it supports render update callback that allows us too resolve intermediate results + m_rprContextMetadata.pluginType == kPluginNorthstar && + // in interactive mode we want to be able to abort ASAP + !m_isInteractive && + // in contour rendering mode we must render only with RPR_CONTEXT_ITERATIONS=1 + !m_contourAovs; + + auto rprApi = static_cast(m_delegate->GetRenderParam())->GetRprApi(); + auto colorAov = GetColorAov(); + int iteration = 0; + while (!IsConverged()) { // In interactive mode, always render at least one frame, otherwise // disturbing full-screen-flickering will be visible or @@ -2166,6 +2271,27 @@ class HdRprApiImpl { IncrementFrameCount(IsAdaptiveSamplingEnabled()); + if (progressivelyIncreaseSamplesPerIter) { + // 1, 1, 2, 4, 8, ... + int numSamplesPerIter = std::max(int(pow(2, int(log2(m_numSamples)))), 1); + + // Make sure we will not oversample the image + int numSamplesLeft = std::min(numSamplesPerIter, m_maxSamples - m_numSamples); + numSamplesPerIter = numSamplesLeft > 0 ? numSamplesLeft : numSamplesPerIter; + if (m_numSamplesPerIter != numSamplesPerIter) { + m_numSamplesPerIter = numSamplesPerIter; + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_ITERATIONS, m_numSamplesPerIter), "Failed to set context iterations"); + + if (m_isRenderUpdateCallbackEnabled) { + // Enable resolves in the render update callback after RPR_CONTEXT_ITERATIONS gets high enough + // to get interactive updates even when RPR_CONTEXT_ITERATIONS huge + if (m_numSamplesPerIter >= 32) { + m_resolveMode = kResolveInRenderUpdateCallback; + } + } + } + } + auto startTime = std::chrono::high_resolution_clock::now(); m_rucData.previousProgress = -1.0f; @@ -2186,7 +2312,29 @@ class HdRprApiImpl { break; } - if (m_resolveMode == kResolveAfterRender) { + bool doDenoisedResolve = false; + if (colorAov) { + if (m_isDenoiseEnabled) { + ++iteration; + if (iteration >= m_denoiseMinIter) { + int relativeIter = iteration - m_denoiseMinIter; + if (relativeIter % m_denoiseIterStep == 0) { + doDenoisedResolve = true; + } + } + + // Always force denoise on the last sample because it's quite hard to match + // the max amount of samples and denoise controls (min iter and iter step) + if (m_numSamples + m_numSamplesPerIter == m_maxSamples) { + doDenoisedResolve = true; + } + } + + colorAov->SetDenoise(doDenoisedResolve, rprApi, m_rifContext.get()); + } + + if (m_resolveMode == kResolveAfterRender || + doDenoisedResolve) { ResolveFramebuffers(); } @@ -2199,23 +2347,6 @@ class HdRprApiImpl { m_isAbortingEnabled.store(true); m_numSamples += m_numSamplesPerIter; - - if (!m_isInteractive && m_rprContextMetadata.pluginType == kPluginNorthstar && m_numSamples > 1) { - // Progressively increase RPR_CONTEXT_ITERATIONS because it highly improves Northstar's performance - m_numSamplesPerIter *= 2; - - // Make sure we will not oversample the image - int numSamplesLeft = m_maxSamples - m_numSamples; - m_numSamplesPerIter = std::min(m_numSamplesPerIter, numSamplesLeft); - if (m_numSamplesPerIter > 0) { - RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_ITERATIONS, m_numSamplesPerIter), "Failed to set context iterations"); - } - - // Enable resolves in the render update callback after RPR_CONTEXT_ITERATIONS gets high enough - if (m_numSamplesPerIter >= 32) { - m_resolveMode = kResolveInRenderUpdateCallback; - } - } } } @@ -2246,8 +2377,7 @@ class HdRprApiImpl { } void RenderFrame(HdRprRenderThread* renderThread) { - if (!m_rprContext || - m_aovRegistry.empty()) { + if (!m_rprContext) { return; } @@ -2266,6 +2396,10 @@ class HdRprApiImpl { return ExportRpr(); } + if (m_aovRegistry.empty()) { + return; + } + if (m_state == kStateRender) { try { if (m_delegate->IsBatch()) { @@ -2326,9 +2460,17 @@ Don't show this message again? RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_Y_FLIP, currentYFlip), "Failed to set context Y FLIP parameter"); } + unsigned int rprsFlags = 0; + if (!m_rprExportAsSingleFile) { + rprsFlags |= RPRLOADSTORE_EXPORTFLAG_EXTERNALFILES; + } + if (m_rprExportUseImageCache) { + rprsFlags |= RPRLOADSTORE_EXPORTFLAG_USE_IMAGE_CACHE; + } + auto rprContextHandle = rpr::GetRprObject(m_rprContext.get()); auto rprSceneHandle = rpr::GetRprObject(m_scene.get()); - if (RPR_ERROR_CHECK(rprsExport(m_rprSceneExportPath.c_str(), rprContextHandle, rprSceneHandle, 0, nullptr, nullptr, 0, nullptr, nullptr, 0), "Failed to export .rpr file")) { + if (RPR_ERROR_CHECK(rprsExport(m_rprSceneExportPath.c_str(), rprContextHandle, rprSceneHandle, 0, nullptr, nullptr, 0, nullptr, nullptr, rprsFlags), "Failed to export .rpr file")) { return; } @@ -2395,11 +2537,11 @@ Don't show this message again? /* RPR_AOV_WORLD_COORDINATE = */ "world.coordinate", /* RPR_AOV_UV = */ "uv", /* RPR_AOV_MATERIAL_ID = */ "material.id", - /* RPR_AOV_GEOMETRIC_NORMAL = */ "geometric.normal", - /* RPR_AOV_SHADING_NORMAL = */ "shading.normal", + /* RPR_AOV_GEOMETRIC_NORMAL = */ "normal.geom", + /* RPR_AOV_SHADING_NORMAL = */ "normal", /* RPR_AOV_DEPTH = */ "depth", /* RPR_AOV_OBJECT_ID = */ "object.id", - /* RPR_AOV_OBJECT_GROUP_ID = */ "object.group.id", + /* RPR_AOV_OBJECT_GROUP_ID = */ "group.id", /* RPR_AOV_SHADOW_CATCHER = */ "shadow.catcher", /* RPR_AOV_BACKGROUND = */ "background", /* RPR_AOV_EMISSION = */ "emission", @@ -2417,7 +2559,7 @@ Don't show this message again? /* RPR_AOV_LIGHT_GROUP1 = */ "light.group1", /* RPR_AOV_LIGHT_GROUP2 = */ "light.group2", /* RPR_AOV_LIGHT_GROUP3 = */ "light.group3", - /* RPR_AOV_DIFFUSE_ALBEDO = */ "diffuse.albedo", + /* RPR_AOV_DIFFUSE_ALBEDO = */ "albedo.diffuse", /* RPR_AOV_VARIANCE = */ "variance", /* RPR_AOV_VIEW_SHADING_NORMAL = */ "view.shading.normal", /* RPR_AOV_REFLECTION_CATCHER = */ "reflection.catcher", @@ -2431,6 +2573,13 @@ Don't show this message again? /* RPR_AOV_LPE_6 = */ "lpe6", /* RPR_AOV_LPE_7 = */ "lpe7", /* RPR_AOV_LPE_8 = */ "lpe8", + /* RPR_AOV_CAMERA_NORMAL = */ "camera.normal", + /* RPR_AOV_CRYPTOMATTE_MAT0 = */ "CryptoMaterial00", + /* RPR_AOV_CRYPTOMATTE_MAT1 = */ "CryptoMaterial01", + /* RPR_AOV_CRYPTOMATTE_MAT2 = */ "CryptoMaterial02", + /* RPR_AOV_CRYPTOMATTE_OBJ0 = */ "CryptoObject00", + /* RPR_AOV_CRYPTOMATTE_OBJ1 = */ "CryptoObject01", + /* RPR_AOV_CRYPTOMATTE_OBJ2 = */ "CryptoObject02", }; static const size_t kNumRprsAovNames = sizeof(kRprsAovNames) / sizeof(kRprsAovNames[0]); @@ -2473,6 +2622,24 @@ Don't show this message again? } config["aovs"] = configAovs; + if (m_contourAovs) { + HdRprConfig* rprConfig; + auto configInstanceLock = HdRprConfig::GetInstance(&rprConfig); + + json contour; + contour["object.id"] = int(rprConfig->GetContourUsePrimId()); + contour["material.id"] = int(rprConfig->GetContourUseMaterialId()); + contour["normal"] = int(rprConfig->GetContourUseNormal()); + contour["threshold.normal"] = rprConfig->GetContourNormalThreshold(); + contour["linewidth.objid"] = rprConfig->GetContourLinewidthPrimId(); + contour["linewidth.matid"] = rprConfig->GetContourLinewidthMaterialId(); + contour["linewidth.normal"] = rprConfig->GetContourLinewidthNormal(); + contour["antialiasing"] = rprConfig->GetContourAntialiasing(); + contour["debug"] = int(rprConfig->GetContourDebug()); + + config["contour"] = contour; + } + configFile << config; } catch (json::exception& e) { fprintf(stderr, "Failed to fill config file: %s\n", configFilename.c_str()); @@ -2761,18 +2928,12 @@ Don't show this message again? // * convert float4 texture to uchar4 using RIF // * reinterpret uchar4 data as int32_t (works on little-endian CPU only) m_rprContext->SetAOVindexLookup(rpr_int(i), - float((i >> 0) & 0xFF) / 255.0f, - float((i >> 8) & 0xFF) / 255.0f, + float(((i + 1) >> 0) & 0xFF) / 255.0f, + float(((i + 1) >> 8) & 0xFF) / 255.0f, 0.0f, 0.0f); } } - { - HdRprConfig* config; - auto configInstanceLock = HdRprConfig::GetInstance(&config); - UpdateSettings(*config, true); - } - m_imageCache.reset(new RprUsdImageCache(m_rprContext.get())); m_isAbortingEnabled.store(false); @@ -2805,30 +2966,6 @@ Don't show this message again? } void InitAovs() { - auto initInternalAov = [this](TfToken const& name) { - auto& aovDesc = HdRprAovRegistry::GetInstance().GetAovDesc(name); - if (auto aov = CreateAov(name, 0, 0, aovDesc.format)) { - m_internalAovs.emplace(name, std::move(aov)); - } - }; - - // We create separate AOVs needed for denoising ASAP - // In such a way, when user enables denoising it will not require to rerender - // but it requires more memory, obviously, it should be taken into an account - rif::FilterType filterType = rif::FilterType::EawDenoise; - if (m_rprContextMetadata.renderDeviceType == RprUsdRenderDeviceType::GPU) { - filterType = rif::FilterType::AIDenoise; - } - - initInternalAov(HdRprGetCameraDepthAovName()); - initInternalAov(HdRprAovTokens->albedo); - initInternalAov(HdAovTokens->color); - initInternalAov(HdAovTokens->normal); - if (filterType == rif::FilterType::EawDenoise) { - initInternalAov(HdAovTokens->primId); - initInternalAov(HdRprAovTokens->worldCoordinate); - } - m_lpeAovPool.clear(); m_lpeAovPool.insert(m_lpeAovPool.begin(), { HdRprAovTokens->lpe0, HdRprAovTokens->lpe1, HdRprAovTokens->lpe2, @@ -3049,6 +3186,26 @@ Don't show this message again? if (!colorAov) return; } + // Force disable alpha for some render modes when we render with Northstar + if (m_rprContextMetadata.pluginType == kPluginNorthstar) { + // Contour rendering should not have an alpha, + // it might cause missed contours (just for the user, not in actual data) + if (m_contourAovs) { + m_isAlphaEnabled = false; + } else { + auto currentRenderMode = RprUsdGetInfo(m_rprContext.get(), RPR_CONTEXT_RENDER_MODE); + + // XXX (RPRNEXT-343): opacity is always zero when such render modes are active: + if (currentRenderMode == RPR_RENDER_MODE_NORMAL || + currentRenderMode == RPR_RENDER_MODE_POSITION || + currentRenderMode == RPR_RENDER_MODE_TEXCOORD || + currentRenderMode == RPR_RENDER_MODE_WIREFRAME || + currentRenderMode == RPR_RENDER_MODE_MATERIAL_INDEX) { + m_isAlphaEnabled = false; + } + } + } + if (m_isAlphaEnabled) { auto opacityAov = GetAov(HdRprAovTokens->opacity, m_viewportSize[0], m_viewportSize[1], HdFormatFloat32Vec4); if (opacityAov) { @@ -3072,7 +3229,7 @@ Don't show this message again? return aov; } - std::shared_ptr CreateAov(TfToken const& aovName, int width = 0, int height = 0, HdFormat format = HdFormatFloat32Vec4) { + std::shared_ptr CreateAov(TfToken const& aovName, int width, int height, HdFormat format) { if (!m_rprContext || width < 0 || height < 0 || format == HdFormatInvalid || HdDataSizeOfFormat(format) == 0) { @@ -3095,6 +3252,9 @@ Don't show this message again? try { if (!aov) { + HdRprApiAov* newAov = nullptr; + std::function aovCustomDestructor; + if (aovName == HdAovTokens->color) { auto rawColorAov = GetAov(HdRprAovTokens->rawColor, width, height, HdFormatFloat32Vec4); if (!rawColorAov) { @@ -3102,12 +3262,12 @@ Don't show this message again? return nullptr; } - auto colorAov = std::make_shared(format, std::move(rawColorAov), m_rprContext.get(), m_rprContextMetadata); - UpdateColorAlpha(colorAov.get()); + auto colorAov = new HdRprApiColorAov(format, std::move(rawColorAov), m_rprContext.get(), m_rprContextMetadata); + UpdateColorAlpha(colorAov); - aov = colorAov; + newAov = colorAov; } else if (aovName == HdAovTokens->normal) { - aov = std::make_shared(width, height, format, m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); + newAov = new HdRprApiNormalAov(width, height, format, m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); } else if (aovName == HdAovTokens->depth) { auto worldCoordinateAov = GetAov(HdRprAovTokens->worldCoordinate, width, height, HdFormatFloat32Vec4); if (!worldCoordinateAov) { @@ -3115,24 +3275,36 @@ Don't show this message again? return nullptr; } - aov = std::make_shared(format, std::move(worldCoordinateAov), m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); + newAov = new HdRprApiDepthAov(format, std::move(worldCoordinateAov), m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); } else if (TfStringStartsWith(aovName.GetString(), "lpe")) { - auto aovPtr = new HdRprApiAov(rpr::Aov(aovDesc.id), width, height, format, m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); - aov = std::shared_ptr(aovPtr, [this](HdRprApiAov* aov) { + newAov = new HdRprApiAov(rpr::Aov(aovDesc.id), width, height, format, m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); + aovCustomDestructor = [this](HdRprApiAov* aov) { // Each LPE AOV reserves RPR's LPE AOV id (RPR_AOV_LPE_0, ...) // As soon as LPE AOV is released we want to return reserved id to the pool m_lpeAovPool.push_back(GetRprLpeAovName(aov->GetAovFb()->GetAovId())); + m_dirtyFlags |= ChangeTracker::DirtyAOVRegistry; delete aov; - }); + }; } else { if (!aovDesc.computed) { - aov = std::make_shared(rpr::Aov(aovDesc.id), width, height, format, m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); + newAov = new HdRprApiAov(rpr::Aov(aovDesc.id), width, height, format, m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); } else { TF_CODING_ERROR("Failed to create %s AOV: unprocessed computed AOV", aovName.GetText()); - return nullptr; } } + if (!newAov) { + return nullptr; + } + + if (!aovCustomDestructor) { + aovCustomDestructor = [this](HdRprApiAov* aov) { + m_dirtyFlags |= ChangeTracker::DirtyAOVRegistry; + delete aov; + }; + } + + aov = std::shared_ptr(newAov, std::move(aovCustomDestructor)); m_aovRegistry[aovName] = aov; m_dirtyFlags |= ChangeTracker::DirtyAOVRegistry; } else { @@ -3145,6 +3317,17 @@ Don't show this message again? return aov; } + std::shared_ptr CreateAov(TfToken const& aovName) { + auto iter = m_aovRegistry.find(aovName); + if (iter != m_aovRegistry.end()) { + if (auto aov = iter->second.lock()) { + return aov; + } + } + + return CreateAov(aovName, m_viewportSize[0], m_viewportSize[1], HdFormatFloat32Vec4); + } + HdRprApiColorAov* GetColorAov() { std::shared_ptr retainedAov; auto colorAovIter = m_aovRegistry.find(HdAovTokens->color); @@ -3347,6 +3530,13 @@ Don't show this message again? HdRenderPassAovBindingVector m_aovBindings; std::vector m_lpeAovPool; + struct ContourRenderModeAovs { + std::shared_ptr normal; + std::shared_ptr primId; + std::shared_ptr materialId; + }; + std::unique_ptr m_contourAovs; + struct OutputRenderBuffer { HdRenderPassAovBinding const* aovBinding; TfToken aovName; @@ -3428,6 +3618,10 @@ Don't show this message again? } m_resolveMode = kResolveAfterRender; bool m_isFirstSample = true; + bool m_isDenoiseEnabled = false; + int m_denoiseMinIter; + int m_denoiseIterStep; + GfVec2i m_viewportSize = GfVec2i(0); GfMatrix4d m_cameraProjectionMatrix = GfMatrix4d(1.f); HdRprCamera const* m_hdCamera = nullptr; @@ -3471,6 +3665,8 @@ Don't show this message again? std::atomic m_abortRender; std::string m_rprSceneExportPath; + bool m_rprExportAsSingleFile; + bool m_rprExportUseImageCache; std::condition_variable* m_presentedConditionVariable = nullptr; bool* m_presentedCondition = nullptr; @@ -3634,6 +3830,10 @@ void HdRprApi::SetMeshId(rpr::Shape* mesh, uint32_t id) { m_impl->SetMeshId(mesh, id); } +void HdRprApi::SetMeshIgnoreContour(rpr::Shape* mesh, bool ignoreContour) { + m_impl->SetMeshIgnoreContour(mesh, ignoreContour); +} + void HdRprApi::SetCurveMaterial(rpr::Curve* curve, RprUsdMaterial const* material) { m_impl->SetCurveMaterial(curve, material); } diff --git a/pxr/imaging/plugin/hdRpr/rprApi.h b/pxr/imaging/plugin/hdRpr/rprApi.h index e2023ad4c..605ca672e 100644 --- a/pxr/imaging/plugin/hdRpr/rprApi.h +++ b/pxr/imaging/plugin/hdRpr/rprApi.h @@ -121,6 +121,7 @@ class HdRprApi final { void SetMeshMaterial(rpr::Shape* mesh, RprUsdMaterial const* material, bool displacementEnabled); void SetMeshVisibility(rpr::Shape* mesh, uint32_t visibilityMask); void SetMeshId(rpr::Shape* mesh, uint32_t id); + void SetMeshIgnoreContour(rpr::Shape* mesh, bool ignoreContour); void Release(rpr::Shape* shape); rpr::Curve* CreateCurve(VtVec3fArray const& points, VtIntArray const& indices, VtFloatArray const& radiuses, VtVec2fArray const& uvs, VtIntArray const& segmentPerCurve); diff --git a/pxr/imaging/plugin/hdRpr/rprApiAov.cpp b/pxr/imaging/plugin/hdRpr/rprApiAov.cpp index 6f1e14f40..d9662cf25 100644 --- a/pxr/imaging/plugin/hdRpr/rprApiAov.cpp +++ b/pxr/imaging/plugin/hdRpr/rprApiAov.cpp @@ -131,7 +131,7 @@ bool HdRprApiAov::GetData(void* dstBuffer, size_t dstBufferSize) { // That's why we interpret the value as int and filling the alpha channel with zeros auto primIdData = reinterpret_cast(dstBuffer); for (size_t i = 0; i < dstBufferSize / sizeof(int); ++i) { - primIdData[i] &= 0xFFFFFF; + primIdData[i] = (primIdData[i] & 0xFFFFFF) - 1; } } @@ -220,7 +220,7 @@ void HdRprApiColorAov::SetOpacityAov(std::shared_ptr opacity) { } } -void HdRprApiColorAov::EnableAIDenoise( +void HdRprApiColorAov::InitAIDenoise( std::shared_ptr albedo, std::shared_ptr normal, std::shared_ptr linearDepth) { @@ -239,11 +239,10 @@ void HdRprApiColorAov::EnableAIDenoise( m_retainedDenoiseInputs[rif::LinearDepth] = linearDepth; m_retainedDenoiseInputs[rif::Albedo] = albedo; - SetFilter(kFilterAIDenoise, true); - SetFilter(kFilterEAWDenoise, false); + m_denoiseFilterType = kFilterAIDenoise; } -void HdRprApiColorAov::EnableEAWDenoise( +void HdRprApiColorAov::InitEAWDenoise( std::shared_ptr albedo, std::shared_ptr normal, std::shared_ptr linearDepth, @@ -266,18 +265,29 @@ void HdRprApiColorAov::EnableEAWDenoise( m_retainedDenoiseInputs[rif::Albedo] = albedo; m_retainedDenoiseInputs[rif::WorldCoordinate] = worldCoordinate; - SetFilter(kFilterEAWDenoise, true); - SetFilter(kFilterAIDenoise, false); + m_denoiseFilterType = kFilterEAWDenoise; } -void HdRprApiColorAov::DisableDenoise(rif::Context* rifContext) { - SetFilter(kFilterEAWDenoise, false); - SetFilter(kFilterAIDenoise, false); - SetFilter(kFilterResample, m_format != HdFormatFloat32Vec4); - +void HdRprApiColorAov::DeinitDenoise(rif::Context* rifContext) { for (auto& retainedInput : m_retainedDenoiseInputs) { retainedInput = nullptr; } + + m_denoiseFilterType = kFilterNone; +} + +void HdRprApiColorAov::SetDenoise(bool enable, HdRprApi const* rprApi, rif::Context* rifContext) { + if (m_denoiseFilterType != kFilterNone) { + SetFilter(m_denoiseFilterType, enable); + SetFilter(m_denoiseFilterType == kFilterAIDenoise ? kFilterEAWDenoise : kFilterAIDenoise, false); + } else { + SetFilter(kFilterAIDenoise, false); + SetFilter(kFilterEAWDenoise, false); + } + + SetFilter(kFilterResample, m_format != HdFormatFloat32Vec4); + + Update(rprApi, rifContext); } void HdRprApiColorAov::SetTonemap(TonemapParams const& params) { @@ -338,15 +348,27 @@ void HdRprApiColorAov::Update(HdRprApi const* rprApi, rif::Context* rifContext) m_enabledFilters = kFilterNone; } - m_filter = nullptr; - m_auxFilters.clear(); + // Reuse the previously created filters + std::vector>> filterPool = std::move(m_auxFilters); + if (m_filter) { + filterPool.emplace_back(m_mainFilterType, std::move(m_filter)); + } if ((m_enabledFilters & kFilterAIDenoise) || (m_enabledFilters & kFilterEAWDenoise) || (m_enabledFilters & kFilterComposeOpacity) || (m_enabledFilters & kFilterTonemap)) { - auto addFilter = [this](Filter type, std::unique_ptr filter) { + auto addFilter = [this, &filterPool](Filter type, std::function()> filterCreator) { + std::unique_ptr filter; + + auto it = std::find_if(filterPool.begin(), filterPool.end(), [type](auto& entry) { return type == entry.first; }); + if (it != filterPool.end()) { + filter = std::move(it->second); + } else { + filter = filterCreator(); + } + if (m_filter) { m_auxFilters.emplace_back(m_mainFilterType, std::move(m_filter)); } @@ -356,30 +378,40 @@ void HdRprApiColorAov::Update(HdRprApi const* rprApi, rif::Context* rifContext) }; if (m_enabledFilters & kFilterTonemap) { - addFilter(kFilterTonemap, rif::Filter::CreateCustom(RIF_IMAGE_FILTER_PHOTO_LINEAR_TONEMAP, rifContext)); + addFilter(kFilterTonemap, + [rifContext]() { + return rif::Filter::CreateCustom(RIF_IMAGE_FILTER_PHOTO_LINEAR_TONEMAP, rifContext); + } + ); } if ((m_enabledFilters & kFilterAIDenoise) || (m_enabledFilters & kFilterEAWDenoise)) { - auto denoiseFilterType = (m_enabledFilters & kFilterAIDenoise) ? rif::FilterType::AIDenoise : rif::FilterType::EawDenoise; - auto fbDesc = m_retainedRawColor->GetAovFb()->GetDesc(); - auto type = (m_enabledFilters & kFilterAIDenoise) ? kFilterAIDenoise : kFilterEAWDenoise; - auto filter = rif::Filter::Create(denoiseFilterType, rifContext, fbDesc.fb_width, fbDesc.fb_height); - addFilter(type, std::move(filter)); + addFilter(type, + [this, rifContext]() { + auto denoiseFilterType = (m_enabledFilters & kFilterAIDenoise) ? rif::FilterType::AIDenoise : rif::FilterType::EawDenoise; + auto fbDesc = m_retainedRawColor->GetAovFb()->GetDesc(); + return rif::Filter::Create(denoiseFilterType, rifContext, fbDesc.fb_width, fbDesc.fb_height); + } + ); } if (m_enabledFilters & kFilterComposeOpacity) { - auto filter = rif::Filter::CreateCustom(RIF_IMAGE_FILTER_USER_DEFINED, rifContext); - auto opacityComposingKernelCode = std::string(R"( - int2 coord; - GET_COORD_OR_RETURN(coord, GET_BUFFER_SIZE(inputImage)); - vec4 alpha = ReadPixelTyped(alphaImage, coord.x, coord.y); - vec4 color = ReadPixelTyped(inputImage, coord.x, coord.y) * alpha.x; - WritePixelTyped(outputImage, coord.x, coord.y, make_vec4(color.x, color.y, color.z, alpha.x)); - )"); - filter->SetParam("code", opacityComposingKernelCode); - addFilter(kFilterComposeOpacity, std::move(filter)); + addFilter(kFilterComposeOpacity, + [rifContext]() { + auto filter = rif::Filter::CreateCustom(RIF_IMAGE_FILTER_USER_DEFINED, rifContext); + auto opacityComposingKernelCode = std::string(R"( + int2 coord; + GET_COORD_OR_RETURN(coord, GET_BUFFER_SIZE(inputImage)); + vec4 alpha = ReadPixelTyped(alphaImage, coord.x, coord.y); + vec4 color = ReadPixelTyped(inputImage, coord.x, coord.y) * alpha.x; + WritePixelTyped(outputImage, coord.x, coord.y, make_vec4(color.x, color.y, color.z, alpha.x)); + )"); + filter->SetParam("code", opacityComposingKernelCode); + return filter; + } + ); } } else if (m_enabledFilters & kFilterResample) { m_filter = rif::Filter::CreateCustom(RIF_IMAGE_FILTER_RESAMPLE, rifContext); diff --git a/pxr/imaging/plugin/hdRpr/rprApiAov.h b/pxr/imaging/plugin/hdRpr/rprApiAov.h index ed461aa48..ff2111db8 100644 --- a/pxr/imaging/plugin/hdRpr/rprApiAov.h +++ b/pxr/imaging/plugin/hdRpr/rprApiAov.h @@ -85,15 +85,16 @@ class HdRprApiColorAov : public HdRprApiAov { void SetOpacityAov(std::shared_ptr opacity); - void EnableAIDenoise(std::shared_ptr albedo, + void InitAIDenoise(std::shared_ptr albedo, std::shared_ptr normal, std::shared_ptr linearDepth); - void EnableEAWDenoise(std::shared_ptr albedo, + void InitEAWDenoise(std::shared_ptr albedo, std::shared_ptr normal, std::shared_ptr linearDepth, std::shared_ptr objectId, std::shared_ptr worldCoordinate); - void DisableDenoise(rif::Context* rifContext); + void DeinitDenoise(rif::Context* rifContext); + void SetDenoise(bool enable, HdRprApi const* rprApi, rif::Context* rifContext); struct TonemapParams { bool enable; @@ -140,6 +141,7 @@ class HdRprApiColorAov : public HdRprApiAov { std::shared_ptr m_retainedRawColor; std::shared_ptr m_retainedOpacity; std::shared_ptr m_retainedDenoiseInputs[rif::MaxInput]; + Filter m_denoiseFilterType = kFilterNone; Filter m_mainFilterType = kFilterNone; std::vector>> m_auxFilters; diff --git a/pxr/imaging/plugin/rprHoudini/CMakeLists.txt b/pxr/imaging/plugin/rprHoudini/CMakeLists.txt index b4e1f4678..b3b94121f 100644 --- a/pxr/imaging/plugin/rprHoudini/CMakeLists.txt +++ b/pxr/imaging/plugin/rprHoudini/CMakeLists.txt @@ -6,6 +6,8 @@ add_library(RPR_for_Houdini SHARED plugin.cpp VOP_RPRMaterial.h VOP_RPRMaterial.cpp + LOP_RPRMaterialProperties.h + LOP_RPRMaterialProperties.cpp LOP_RPRExportHelper.h LOP_RPRExportHelper.cpp) @@ -13,6 +15,7 @@ target_link_libraries(RPR_for_Houdini arch usd usdGeom + usdShade usdRender rprUsd Houdini) @@ -49,5 +52,6 @@ install( install( FILES ui/MainMenuCommon.xml + UsdRenderers.json DESTINATION houdini) diff --git a/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.cpp b/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.cpp index aa85a11e7..078c97a6c 100644 --- a/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.cpp +++ b/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.cpp @@ -20,9 +20,11 @@ limitations under the License. #include #include +#include #include #include +#include #include #include #include @@ -30,11 +32,21 @@ limitations under the License. PXR_NAMESPACE_OPEN_SCOPE +TF_DEFINE_PRIVATE_TOKENS(_tokens, + (rprExportPath) + (rprExportAsSingleFile) + (rprExportUseImageCache) +); + static PRM_Name g_exportPathName("exportPath", "Export Path"); +static PRM_Name g_exportAsSingleFileName("exportAsSingleFile", "Export As Single File"); +static PRM_Name g_exportUseImageCacheName("exportUseImageCache", "Use Image Cache"); static PRM_Name g_renderSettingsName("renderSettings", "Render Settings"); static PRM_Template g_templateList[] = { PRM_Template(PRM_FILE, 1, &g_exportPathName), + PRM_Template(PRM_TOGGLE_E, 1, &g_exportAsSingleFileName), + PRM_Template(PRM_TOGGLE_E, 1, &g_exportUseImageCacheName), PRM_Template(PRM_STRING_E, 1, &g_renderSettingsName), PRM_Template(), }; @@ -59,13 +71,39 @@ LOP_RPRExportHelper::LOP_RPRExportHelper(OP_Network *net, const char *name, OP_O } +template +bool LOP_RPRExportHelper::SetRenderSetting(UsdPrim* prim, TfToken const& name, SdfValueTypeName const& sdfType, T const& value, bool timeDependent) { + if (auto exportPathAttr = prim->CreateAttribute(name, sdfType, true)) { + auto timeCode = timeDependent ? HUSDgetCurrentUsdTimeCode() : UsdTimeCode::Default(); + if (exportPathAttr.Set(value, timeCode)) { + return true; + } + + addError(LOP_MESSAGE, TfStringPrintf("Failed to set %s:%s", prim->GetPath().GetText(), name.GetText()).c_str()); + } else { + addError(LOP_MESSAGE, TfStringPrintf("Failed to create %s attribute", name.GetText()).c_str()); + } + + return false; +} + OP_ERROR LOP_RPRExportHelper::cookMyLop(OP_Context &context) { if (cookModifyInput(context) >= UT_ERROR_FATAL) { return error(); } + auto getParm = [&](const UT_StringHolder &parmname) -> PRM_Parm* { + int index = getParmList()->getParmIndex(parmname); + if (index == -1) { + return nullptr; + } + return getParmList()->getParmPtr(index); + }; + + auto exportPathParm = getParm(g_exportPathName.getToken()); + UT_String exportPath; - evalString(exportPath, g_exportPathName.getToken(), 0, context.getTime()); + exportPathParm->getValue(context.getTime(), exportPath, 0, true, context.getThread()); if (!exportPath.isstring()) { return error(); @@ -76,6 +114,9 @@ OP_ERROR LOP_RPRExportHelper::cookMyLop(OP_Context &context) { exportPath += ".rpr"; } + bool exportAsSingleFile = bool(evalInt(g_exportAsSingleFileName.getToken(), 0, context.getTime())); + bool exportUseImageCache = bool(evalInt(g_exportUseImageCacheName.getToken(), 0, context.getTime())); + UT_String renderSettingsPath; evalString(renderSettingsPath, g_renderSettingsName.getToken(), 0, context.getTime()); @@ -86,21 +127,12 @@ OP_ERROR LOP_RPRExportHelper::cookMyLop(OP_Context &context) { // Insert export file into each UsdRenderSetting primitive, // or if no UsdRenderSetting primitives exist create new one - auto modifyRenderSettings = [this, &exportPath](UsdRenderSettings& renderSettings) { + auto modifyRenderSettings = [&](UsdRenderSettings& renderSettings) { auto prim = renderSettings.GetPrim(); - static TfToken rprExportPath("rprExportPath", TfToken::Immortal); - if (auto exportPathAttr = prim.CreateAttribute(rprExportPath, SdfValueTypeNames->String, true)) { - if (!exportPathAttr.Set(exportPath.toStdString())) { - addError(LOP_MESSAGE, TfStringPrintf("Failed to set %s:%s", prim.GetPath().GetText(), rprExportPath.GetText()).c_str()); - return false; - } - } else { - addError(LOP_MESSAGE, TfStringPrintf("Failed to create %s attribute", rprExportPath.GetText()).c_str()); - return false; - } - - return true; + return SetRenderSetting(&prim, _tokens->rprExportPath, SdfValueTypeNames->String, exportPath.toStdString(), exportPathParm->isTimeDependent()) && + SetRenderSetting(&prim, _tokens->rprExportAsSingleFile, SdfValueTypeNames->Bool, exportAsSingleFile, false) && + SetRenderSetting(&prim, _tokens->rprExportUseImageCache, SdfValueTypeNames->Bool, exportUseImageCache, false); }; // Use explicitly specified render settings primitive if any diff --git a/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.h b/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.h index 1cf47155a..50122d9d6 100644 --- a/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.h +++ b/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.h @@ -15,6 +15,7 @@ limitations under the License. #define RPR_LOP_RPREXPORTHELPER_H #include "pxr/pxr.h" +#include "pxr/usd/usd/prim.h" #include @@ -31,6 +32,10 @@ class LOP_RPRExportHelper : public LOP_Node { protected: OP_ERROR cookMyLop(OP_Context &context) override; + +private: + template + bool SetRenderSetting(UsdPrim* prim, TfToken const& name, SdfValueTypeName const& sdfType, T const& value, bool timeDependent); }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/rprHoudini/LOP_RPRMaterialProperties.cpp b/pxr/imaging/plugin/rprHoudini/LOP_RPRMaterialProperties.cpp new file mode 100644 index 000000000..06e3c5f02 --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/LOP_RPRMaterialProperties.cpp @@ -0,0 +1,105 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "LOP_RPRMaterialProperties.h" + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +static PRM_Name g_materialPath("materialPath", "Material Path"); +static PRM_Name g_id("id", "ID"); + +static PRM_Template g_templateList[] = { + PRM_Template(PRM_STRING_E, 1, &g_materialPath), + PRM_Template(PRM_INT, 1, &g_id, nullptr, nullptr, nullptr, nullptr, nullptr, 1, + "some help"), + PRM_Template() +}; + +void LOP_RPRMaterialProperties::Register(OP_OperatorTable* table) { + auto opOperator = new OP_Operator( + "rpr_LOP_RPRMaterialProperties", + "RPR Material Properties", + [](OP_Network *net, const char *name, OP_Operator *op) -> OP_Node* { + return new LOP_RPRMaterialProperties(net, name, op); + }, + g_templateList, + 0u, + (unsigned)1); + opOperator->setIconName("RPR"); + + table->addOperator(opOperator); +} + +LOP_RPRMaterialProperties::LOP_RPRMaterialProperties(OP_Network *net, const char *name, OP_Operator *op) + : LOP_Node(net, name, op) { + +} + +OP_ERROR LOP_RPRMaterialProperties::cookMyLop(OP_Context &context) { + if (cookModifyInput(context) >= UT_ERROR_FATAL) { + return error(); + } + + UT_String materialPath; + evalString(materialPath, g_materialPath.getToken(), 0, context.getTime()); + HUSDmakeValidUsdPath(materialPath, true); + + int id = evalInt(g_id.getToken(), 0, context.getTime()); + + if (!materialPath.isstring()) { + return error(); + } + SdfPath materialSdfPath(HUSDgetSdfPath(materialPath)); + + HUSD_AutoWriteLock writelock(editableDataHandle()); + HUSD_AutoLayerLock layerlock(writelock); + + UsdStageRefPtr stage = writelock.data()->stage(); + + UsdPrim materialPrim = stage->GetPrimAtPath(materialSdfPath); + if (!materialPrim) { + addError(LOP_MESSAGE, TfStringPrintf("Material with %s path does not exist", materialPath.c_str()).c_str()); + return error(); + } + + UsdShadeMaterial material(materialPrim); + if (!material) { + addError(LOP_MESSAGE, TfStringPrintf("Specified path does not point to a material: %s", materialPath.c_str()).c_str()); + return error(); + } + + UsdShadeShader surfaceSource = material.ComputeSurfaceSource(RprUsdTokens->rpr); + if (surfaceSource) { + surfaceSource.CreateInput(RprUsdTokens->id, SdfValueTypeNames->Int).Set(id); + } + + return error(); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/rprHoudini/LOP_RPRMaterialProperties.h b/pxr/imaging/plugin/rprHoudini/LOP_RPRMaterialProperties.h new file mode 100644 index 000000000..211002d67 --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/LOP_RPRMaterialProperties.h @@ -0,0 +1,37 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPR_LOP_RPRMATERIALPROPERTIES_H +#define RPR_LOP_RPRMATERIALPROPERTIES_H + +#include "pxr/pxr.h" + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +/// This node allows to set RPR specific properties on materials +class LOP_RPRMaterialProperties : public LOP_Node { +public: + LOP_RPRMaterialProperties(OP_Network *net, const char *name, OP_Operator *op); + ~LOP_RPRMaterialProperties() override = default; + + static void Register(OP_OperatorTable *table); + +protected: + OP_ERROR cookMyLop(OP_Context &context) override; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPR_LOP_RPRMATERIALPROPERTIES_H diff --git a/pxr/imaging/plugin/rprHoudini/UsdRenderers.json b/pxr/imaging/plugin/rprHoudini/UsdRenderers.json new file mode 100644 index 000000000..77e42526a --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/UsdRenderers.json @@ -0,0 +1,8 @@ +{ + "HdRprPlugin" : { + "valid" : true, + "menulabel" : "RPR", + "menupriority" : 0, + "restartrendersettings" : ["renderQuality", "renderDevice"] + } +} diff --git a/pxr/imaging/plugin/rprHoudini/activateHoudiniPlugin.cpp.in b/pxr/imaging/plugin/rprHoudini/activateHoudiniPlugin.cpp.in index d43595eca..c33a4bade 100644 --- a/pxr/imaging/plugin/rprHoudini/activateHoudiniPlugin.cpp.in +++ b/pxr/imaging/plugin/rprHoudini/activateHoudiniPlugin.cpp.in @@ -214,12 +214,12 @@ int ActivateHoudiniPlugin() { return EXIT_FAILURE; } - std::map env = { - {"RPR", rprPath}, - {"HOUDINI_PATH", "$RPR/houdini"}, - {"PYTHONPATH", "$RPR/lib/python"}, + std::vector> env = { + {"RPR", ""}, + {"HOUDINI_PATH", "/houdini"}, + {"PYTHONPATH", "/lib/python"}, #if defined(_WIN32) || defined(_WIN64) - {"PATH", "$RPR/lib"} + {"PATH", "/lib"} #endif }; @@ -231,15 +231,9 @@ int ActivateHoudiniPlugin() { packageJson << '{'; packageJson << '\"' << it->first << '\"'; packageJson << ':'; -#if defined(_WIN32) || defined(_WIN64) packageJson << '\"'; - auto path = it->second.string(); - std::replace(path.begin(), path.end(), '\\', '/'); - packageJson << path; + packageJson << rprPath << it->second; packageJson << '\"'; -#else - packageJson << it->second; -#endif packageJson << '}'; if (std::next(it) != env.end()) { packageJson << ','; diff --git a/pxr/imaging/plugin/rprHoudini/hda/rpr_exportRpr1.hda b/pxr/imaging/plugin/rprHoudini/hda/rpr_exportRpr1.hda index 7fbba1679..9b0114400 100644 Binary files a/pxr/imaging/plugin/rprHoudini/hda/rpr_exportRpr1.hda and b/pxr/imaging/plugin/rprHoudini/hda/rpr_exportRpr1.hda differ diff --git a/pxr/imaging/plugin/rprHoudini/hda/rpr_standard_rendervars.hda b/pxr/imaging/plugin/rprHoudini/hda/rpr_standard_rendervars.hda index f95e261fc..665cc51ee 100644 Binary files a/pxr/imaging/plugin/rprHoudini/hda/rpr_standard_rendervars.hda and b/pxr/imaging/plugin/rprHoudini/hda/rpr_standard_rendervars.hda differ diff --git a/pxr/imaging/plugin/rprHoudini/plugin.cpp b/pxr/imaging/plugin/rprHoudini/plugin.cpp index 382ec71fb..ad8505c02 100644 --- a/pxr/imaging/plugin/rprHoudini/plugin.cpp +++ b/pxr/imaging/plugin/rprHoudini/plugin.cpp @@ -13,6 +13,7 @@ limitations under the License. #include "VOP_RPRMaterial.h" #include "LOP_RPRExportHelper.h" +#include "LOP_RPRMaterialProperties.h" #include "pxr/imaging/rprUsd/materialRegistry.h" #include @@ -34,4 +35,5 @@ void newVopOperator(OP_OperatorTable* io_table) { void newLopOperator(OP_OperatorTable *table) { PXR_NAMESPACE_USING_DIRECTIVE LOP_RPRExportHelper::Register(table); + LOP_RPRMaterialProperties::Register(table); } diff --git a/pxr/imaging/rprUsd/CMakeLists.txt b/pxr/imaging/rprUsd/CMakeLists.txt index a29e21570..38b9b5ec1 100644 --- a/pxr/imaging/rprUsd/CMakeLists.txt +++ b/pxr/imaging/rprUsd/CMakeLists.txt @@ -19,6 +19,7 @@ pxr_library(rprUsd PUBLIC_CLASSES util config + tokens contextHelpers coreImage debugCodes diff --git a/pxr/imaging/rprUsd/coreImage.cpp b/pxr/imaging/rprUsd/coreImage.cpp index 89824c0c4..6e1d49e3f 100644 --- a/pxr/imaging/rprUsd/coreImage.cpp +++ b/pxr/imaging/rprUsd/coreImage.cpp @@ -38,7 +38,98 @@ rpr::ImageDesc GetRprImageDesc(rpr::ImageFormat format, uint32_t width, uint32_t return desc; } -rpr::Image* CreateRprImage(rpr::Context* context, GlfUVTextureData* textureData) { +template +std::unique_ptr _ConvertTexture(GlfUVTextureData* textureData, rpr::ImageFormat const& srcFormat, uint32_t dstNumComponents, PixelConverterFunc&& converter) { + uint8_t* src = textureData->GetRawBuffer(); + + size_t srcPixelStride = srcFormat.num_components * sizeof(ComponentT); + size_t dstPixelStride = dstNumComponents * sizeof(ComponentT); + + size_t numPixels = size_t(textureData->ResizedWidth()) * textureData->ResizedHeight(); + auto dstData = std::make_unique(numPixels * dstPixelStride); + uint8_t* dst = dstData.get(); + + for (size_t i = 0; i < numPixels; ++i) { + converter((ComponentT*)(dst + i * dstPixelStride), (ComponentT*)(src + i * srcPixelStride)); + } + + return dstData; +} + +template +struct WhiteColor { + const T value = static_cast(1); +}; + +template <> struct WhiteColor { + const uint8_t value = 255u; +}; + +template +std::unique_ptr ConvertTexture(GlfUVTextureData* textureData, rpr::ImageFormat const& format, uint32_t dstNumComponents) { + if (dstNumComponents < format.num_components) { + // Trim excessive channels + return _ConvertTexture(textureData, format, dstNumComponents, + [=](ComponentT* dst, ComponentT* src) { + for (size_t i = 0; i < dstNumComponents; ++i) { + dst[i] = src[i]; + } + } + ); + } + + if (format.num_components == 1) { + // Expand to a required amount of channels. Example: greyscale texture that is stored as single-channel. + if (dstNumComponents == 4) { + // r -> rrr1 + return _ConvertTexture(textureData, format, dstNumComponents, + [](ComponentT* dst, ComponentT* src) { + dst[0] = dst[1] = dst[2] = src[0]; + dst[3] = WhiteColor{}.value; + } + ); + } else { + return _ConvertTexture(textureData, format, dstNumComponents, + [=](ComponentT* dst, ComponentT* src) { + for (size_t i = 0; i < dstNumComponents; ++i) { + dst[i] = src[0]; + } + } + ); + } + } else if (format.num_components == 2) { + if (dstNumComponents == 4) { + // rg -> rrrg + return _ConvertTexture(textureData, format, dstNumComponents, + [](ComponentT* dst, ComponentT* src) { + dst[0] = dst[1] = dst[2] = src[0]; + dst[3] = src[1]; + } + ); + } else { + // rg -> rrr + return _ConvertTexture(textureData, format, dstNumComponents, + [](ComponentT* dst, ComponentT* src) { + dst[0] = dst[1] = dst[2] = src[0]; + } + ); + } + } else if (format.num_components == 3) { + // rgb -> rgb1 + return _ConvertTexture(textureData, format, dstNumComponents, + [](ComponentT* dst, ComponentT* src) { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + dst[3] = WhiteColor{}.value; + } + ); + } + + return nullptr; +} + +rpr::Image* CreateRprImage(rpr::Context* context, GlfUVTextureData* textureData, uint32_t numComponentsRequired) { rpr::ImageFormat format = {}; #if PXR_VERSION >= 2011 @@ -81,8 +172,28 @@ rpr::Image* CreateRprImage(rpr::Context* context, GlfUVTextureData* textureData) } rpr::ImageDesc desc = GetRprImageDesc(format, textureData->ResizedWidth(), textureData->ResizedHeight()); + auto textureBuffer = textureData->GetRawBuffer(); + + std::unique_ptr convertedData; + if (numComponentsRequired != 0 && + numComponentsRequired != format.num_components) { + if (format.type == RPR_COMPONENT_TYPE_UINT8) { + convertedData = ConvertTexture(textureData, format, numComponentsRequired); + } else if (format.type == RPR_COMPONENT_TYPE_FLOAT16) { + convertedData = ConvertTexture(textureData, format, numComponentsRequired); + } else if (format.type == RPR_COMPONENT_TYPE_FLOAT32) { + convertedData = ConvertTexture(textureData, format, numComponentsRequired); + } + + if (convertedData) { + textureBuffer = convertedData.get(); + format.num_components = numComponentsRequired; + desc = GetRprImageDesc(format, textureData->ResizedWidth(), textureData->ResizedHeight()); + } + } + rpr::Status status; - auto rprImage = context->CreateImage(format, desc, textureData->GetRawBuffer(), &status); + auto rprImage = context->CreateImage(format, desc, textureBuffer, &status); if (!rprImage) { RPR_ERROR_CHECK(status, "Failed to create image from data", context); return nullptr; @@ -93,13 +204,13 @@ rpr::Image* CreateRprImage(rpr::Context* context, GlfUVTextureData* textureData) } // namespace anonymous -RprUsdCoreImage* RprUsdCoreImage::Create(rpr::Context* context, std::string const& path) { +RprUsdCoreImage* RprUsdCoreImage::Create(rpr::Context* context, std::string const& path, uint32_t numComponentsRequired) { auto textureData = GlfUVTextureData::New(path, INT_MAX, 0, 0, 0, 0); if (!textureData || !textureData->Read(0, false)) { return nullptr; } - return Create(context, {{0, textureData.operator->()}}); + return Create(context, {{0, textureData.operator->()}}, numComponentsRequired); } RprUsdCoreImage* RprUsdCoreImage::Create(rpr::Context* context, uint32_t width, uint32_t height, rpr::ImageFormat format, void const* data, rpr::Status* status) { @@ -113,7 +224,8 @@ RprUsdCoreImage* RprUsdCoreImage::Create(rpr::Context* context, uint32_t width, RprUsdCoreImage* RprUsdCoreImage::Create( rpr::Context* context, - std::vector const& tiles) { + std::vector const& tiles, + uint32_t numComponentsRequired) { if (tiles.empty()) { return nullptr; @@ -121,7 +233,7 @@ RprUsdCoreImage* RprUsdCoreImage::Create( if (tiles.size() == 1 && tiles[0].id == 0) { // Single non-UDIM tile - auto rprImage = CreateRprImage(context, tiles[0].textureData); + auto rprImage = CreateRprImage(context, tiles[0].textureData, numComponentsRequired); if (!rprImage) { return nullptr; } @@ -137,7 +249,7 @@ RprUsdCoreImage* RprUsdCoreImage::Create( continue; } - auto rprImage = CreateRprImage(context, tile.textureData); + auto rprImage = CreateRprImage(context, tile.textureData, numComponentsRequired); if (!rprImage) { continue; } diff --git a/pxr/imaging/rprUsd/coreImage.h b/pxr/imaging/rprUsd/coreImage.h index 7711f2a84..ae64eb6d8 100644 --- a/pxr/imaging/rprUsd/coreImage.h +++ b/pxr/imaging/rprUsd/coreImage.h @@ -26,7 +26,7 @@ PXR_NAMESPACE_OPEN_SCOPE class RprUsdCoreImage { public: RPRUSD_API - static RprUsdCoreImage* Create(rpr::Context* context, std::string const& path); + static RprUsdCoreImage* Create(rpr::Context* context, std::string const& path, uint32_t numComponentsRequired); struct UDIMTile { uint32_t id; @@ -35,7 +35,7 @@ class RprUsdCoreImage { UDIMTile(uint32_t id, GlfUVTextureData* textureData) : id(id), textureData(textureData) {} }; RPRUSD_API - static RprUsdCoreImage* Create(rpr::Context* context, std::vector const& textureData); + static RprUsdCoreImage* Create(rpr::Context* context, std::vector const& textureData, uint32_t numComponentsRequired); RPRUSD_API static RprUsdCoreImage* Create(rpr::Context* context, uint32_t width, uint32_t height, rpr::ImageFormat format, void const* data, rpr::Status* status = nullptr); diff --git a/pxr/imaging/rprUsd/imageCache.cpp b/pxr/imaging/rprUsd/imageCache.cpp index c6bbe58ad..f1b989483 100644 --- a/pxr/imaging/rprUsd/imageCache.cpp +++ b/pxr/imaging/rprUsd/imageCache.cpp @@ -42,7 +42,8 @@ RprUsdImageCache::GetImage( std::string const& path, std::string const& colorspace, rpr::ImageWrapType wrapType, - std::vector const& tiles) { + std::vector const& tiles, + uint32_t numComponentsRequired) { if (!wrapType) { wrapType = RPR_IMAGE_WRAP_TYPE_REPEAT; } @@ -64,7 +65,7 @@ RprUsdImageCache::GetImage( } } - auto coreImage = RprUsdCoreImage::Create(m_context, tiles); + auto coreImage = RprUsdCoreImage::Create(m_context, tiles, numComponentsRequired); if (!coreImage) { return nullptr; } diff --git a/pxr/imaging/rprUsd/imageCache.h b/pxr/imaging/rprUsd/imageCache.h index 7b18f1e93..c4919cb7d 100644 --- a/pxr/imaging/rprUsd/imageCache.h +++ b/pxr/imaging/rprUsd/imageCache.h @@ -32,7 +32,8 @@ class RprUsdImageCache { std::string const& path, std::string const& colorspace, rpr::ImageWrapType wrapType, - std::vector const& data = {}); + std::vector const& data, + uint32_t numComponentsRequired); private: rpr::Context* m_context; diff --git a/pxr/imaging/rprUsd/materialNodes/materialNode.h b/pxr/imaging/rprUsd/materialNodes/materialNode.h index 71170915d..9f0c0e836 100644 --- a/pxr/imaging/rprUsd/materialNodes/materialNode.h +++ b/pxr/imaging/rprUsd/materialNodes/materialNode.h @@ -24,11 +24,30 @@ PXR_NAMESPACE_OPEN_SCOPE class RprUsdImageCache; +struct RprUsd_MaterialNetwork { + struct Connection { + SdfPath upstreamNode; + TfToken upstreamOutputName; + }; + + struct Node { + TfToken nodeTypeId; + std::map parameters; + std::map inputConnections; + }; + + std::map nodes; + std::map terminals; +}; + struct RprUsd_MaterialBuilderContext { + RprUsd_MaterialNetwork const* hdMaterialNetwork; + SdfPath const* currentNodePath; + rpr::Context* rprContext; RprUsdImageCache* imageCache; - TfToken uvPrimvarName; + std::string uvPrimvarName; bool isShadowCatcher; bool isReflectionCatcher; diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.cpp index 6a69f2913..a05094cde 100644 --- a/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.cpp +++ b/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.cpp @@ -36,6 +36,20 @@ static rpr_material_node ReleaseOutputNodeOwnership(RPRMtlxLoader::Result* mtlx, return ret; } +template +static bool ReadInput(TfToken const& inputId, VtValue const& inputValue, T* dst) { + if (inputValue.IsHolding()) { + *dst = inputValue.UncheckedGet(); + return true; + } else { + TF_RUNTIME_ERROR("[%s] %s input should be of %s type: %s", + RprUsdRprMaterialXNodeTokens->rpr_materialx_node.GetText(), + inputId.GetText(), ArchGetDemangled().c_str(), + inputValue.GetTypeName().c_str()); + return false; + } +} + class RprUsd_RprMaterialXNode : public RprUsd_MaterialNode { public: RprUsd_RprMaterialXNode(RprUsd_MaterialBuilderContext* ctx) @@ -81,19 +95,20 @@ class RprUsd_RprMaterialXNode : public RprUsd_MaterialNode { RprUsdRprMaterialXNodeTokens->rpr_materialx_node.GetText(), value.GetTypeName().c_str()); return false; } + } else if (inputId == RprUsdRprMaterialXNodeTokens->string) { + bool ret = ReadInput(inputId, value, &m_mtlxString); + if (ret) { ResetNodeOutput(); } + return ret; + } else if (inputId == RprUsdRprMaterialXNodeTokens->basePath) { + bool ret = ReadInput(inputId, value, &m_mtlxBasePath); + if (ret) { ResetNodeOutput(); } + return ret; } else if (inputId == RprUsdRprMaterialXNodeTokens->surfaceElement) { return SetRenderElement(RPRMtlxLoader::kOutputSurface, value); } else if (inputId == RprUsdRprMaterialXNodeTokens->displacementElement) { return SetRenderElement(RPRMtlxLoader::kOutputDisplacement, value); } else if (inputId == RprUsdRprMaterialXNodeTokens->stPrimvarName) { - if (value.IsHolding()) { - m_ctx->uvPrimvarName = TfToken(value.UncheckedGet()); - return true; - } else { - TF_RUNTIME_ERROR("[%s] file input should be of string type: %s", - RprUsdRprMaterialXNodeTokens->rpr_materialx_node.GetText(), value.GetTypeName().c_str()); - return false; - } + return ReadInput(inputId, value, &m_ctx->uvPrimvarName); } TF_RUNTIME_ERROR("[%s] Unknown input %s", @@ -125,13 +140,21 @@ class RprUsd_RprMaterialXNode : public RprUsd_MaterialNode { } bool UpdateNodeOutput() { - auto basePath = TfGetPathName(m_mtlxFilepath); + auto basePath = !m_mtlxBasePath.empty() ? m_mtlxBasePath : TfGetPathName(m_mtlxFilepath); + if (basePath.empty()) { + TF_WARN("[rpr_materialx_node] no base path specified, image loading might be broken"); + } if (m_ctx->mtlxLoader) { RPRMtlxLoader::Result mtlx; try { auto mtlxDoc = MaterialX::createDocument(); - MaterialX::readFromXmlFile(mtlxDoc, m_mtlxFilepath); + if (!m_mtlxFilepath.empty()) { + MaterialX::readFromXmlFile(mtlxDoc, m_mtlxFilepath); + } + if (!m_mtlxString.empty()) { + MaterialX::readFromXmlString(mtlxDoc, m_mtlxString); + } mtlxDoc->importLibrary(m_ctx->mtlxLoader->GetStdlib()); rpr_material_system matSys; @@ -212,7 +235,7 @@ class RprUsd_RprMaterialXNode : public RprUsd_MaterialNode { } else { // Find the only existing output - RPRMtlxLoader::OutputType outputType; + RPRMtlxLoader::OutputType outputType = RPRMtlxLoader::kOutputNone; for (int i = 0; i < RPRMtlxLoader::kOutputsTotal; ++i) { if (mtlx.rootNodeIndices[i] != RPRMtlxLoader::Result::kInvalidRootNodeIndex) { outputType = RPRMtlxLoader::OutputType(i); @@ -268,6 +291,18 @@ class RprUsd_RprMaterialXNode : public RprUsd_MaterialNode { } } + if (mtlxImageNode.type == "float") { + textureCommit.numComponentsRequired = 1; + } else if (mtlxImageNode.type == "vector2" || mtlxImageNode.type == "color2") { + textureCommit.numComponentsRequired = 2; + } else if (mtlxImageNode.type == "vector3" || mtlxImageNode.type == "color3") { + textureCommit.numComponentsRequired = 3; + } else if (mtlxImageNode.type == "vector4" || mtlxImageNode.type == "color4") { + textureCommit.numComponentsRequired = 4; + } else { + TF_WARN("Invalid image materialX type: %s", mtlxImageNode.type.c_str()); + } + rpr_material_node rprImageNode = mtlxImageNode.rprNode; textureCommit.setTextureCallback = [retainedImagesPtr, rprImageNode](std::shared_ptr const& image) { if (!image) return; @@ -289,25 +324,29 @@ class RprUsd_RprMaterialXNode : public RprUsd_MaterialNode { return m_surfaceNode || m_displacementNode; } - std::ifstream mtlxFile(m_mtlxFilepath); - if (!mtlxFile.good()) { - TF_RUNTIME_ERROR("Failed to open \"%s\" file", m_mtlxFilepath.c_str()); - return false; - } + rpr::Status status; + if (!m_mtlxString.empty()) { + m_surfaceNode.reset(m_ctx->rprContext->CreateMaterialXNode(m_mtlxString.c_str(), basePath.c_str(), 0, nullptr, nullptr, &status)); + } else { + std::ifstream mtlxFile(m_mtlxFilepath); + if (!mtlxFile.good()) { + TF_RUNTIME_ERROR("Failed to open \"%s\" file", m_mtlxFilepath.c_str()); + return false; + } - mtlxFile.seekg(0, std::ios::end); - auto fileSize = mtlxFile.tellg(); - if (fileSize == 0) { - TF_RUNTIME_ERROR("Empty file: \"%s\"", m_mtlxFilepath.c_str()); - return false; - } + mtlxFile.seekg(0, std::ios::end); + auto fileSize = mtlxFile.tellg(); + if (fileSize == 0) { + TF_RUNTIME_ERROR("Empty file: \"%s\"", m_mtlxFilepath.c_str()); + return false; + } - auto xmlData = std::make_unique(fileSize); - mtlxFile.seekg(0); - mtlxFile.read(&xmlData[0], fileSize); + auto xmlData = std::make_unique(fileSize); + mtlxFile.seekg(0); + mtlxFile.read(&xmlData[0], fileSize); - rpr::Status status; - m_surfaceNode.reset(m_ctx->rprContext->CreateMaterialXNode(xmlData.get(), basePath.c_str(), 0, nullptr, nullptr, &status)); + m_surfaceNode.reset(m_ctx->rprContext->CreateMaterialXNode(xmlData.get(), basePath.c_str(), 0, nullptr, nullptr, &status)); + } if (!m_surfaceNode) { RPR_ERROR_CHECK(status, "Failed to create materialX node", m_ctx->rprContext); @@ -328,6 +367,16 @@ class RprUsd_RprMaterialXNode : public RprUsd_MaterialNode { fileInput.uiName = "File"; nodeInfo.inputs.push_back(fileInput); + RprUsd_RprNodeInput stringInput(RprUsdMaterialNodeElement::kString); + stringInput.name = RprUsdRprMaterialXNodeTokens->string.GetText(); + stringInput.uiName = ""; // hide from UI + nodeInfo.inputs.push_back(stringInput); + + RprUsd_RprNodeInput basePathInput(RprUsdMaterialNodeElement::kString); + basePathInput.name = RprUsdRprMaterialXNodeTokens->basePath.GetText(); + basePathInput.uiName = ""; // hide from UI + nodeInfo.inputs.push_back(basePathInput); + RprUsd_RprNodeInput stPrimvarNameInput(RprUsdMaterialNodeElement::kString); stPrimvarNameInput.name = RprUsdRprMaterialXNodeTokens->stPrimvarName.GetText(); stPrimvarNameInput.uiName = "UV Primvar Name"; @@ -358,6 +407,8 @@ class RprUsd_RprMaterialXNode : public RprUsd_MaterialNode { private: RprUsd_MaterialBuilderContext* m_ctx; std::string m_mtlxFilepath; + std::string m_mtlxString; + std::string m_mtlxBasePath; std::string m_selectedRenderElements[RPRMtlxLoader::kOutputsTotal]; bool m_isDirty = true; diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.h b/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.h index 90e1f61d5..21539e124 100644 --- a/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.h +++ b/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.h @@ -22,6 +22,8 @@ PXR_NAMESPACE_OPEN_SCOPE #define RPRUSD_RPR_MATERIALX_NODE_TOKENS \ (rpr_materialx_node) \ (file) \ + (string) \ + (basePath) \ (surfaceElement) \ (displacementElement) \ (stPrimvarName) diff --git a/pxr/imaging/rprUsd/materialNodes/usdNode.cpp b/pxr/imaging/rprUsd/materialNodes/usdNode.cpp index ff84b8322..5d7d09677 100644 --- a/pxr/imaging/rprUsd/materialNodes/usdNode.cpp +++ b/pxr/imaging/rprUsd/materialNodes/usdNode.cpp @@ -145,8 +145,17 @@ bool RprUsd_UsdPreviewSurface::SetInput( if (value.IsHolding()) { if (!m_normalMapNode) { m_normalMapNode.reset(new RprUsd_BaseRuntimeNode(RPR_MATERIAL_NODE_NORMAL_MAP, m_ctx)); + m_normalMapScaleNode = RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_MUL, m_ctx); + m_normalMapBiasNode = RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_ADD, m_ctx); } - m_normalMapNode->SetInput(RPR_MATERIAL_INPUT_COLOR, value); + + m_normalMapScaleNode->SetInput(0, value); + m_normalMapScaleNode->SetInput(1, VtValue(GfVec4f(0.5f))); + + m_normalMapBiasNode->SetInput(0, m_normalMapScaleNode->GetOutput()); + m_normalMapBiasNode->SetInput(1, VtValue(GfVec4f(0.5f))); + + m_normalMapNode->SetInput(RPR_MATERIAL_INPUT_COLOR, m_normalMapBiasNode->GetOutput()); auto normalMapOutput = m_normalMapNode->GetOutput(TfToken()); return (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_DIFFUSE_NORMAL, normalMapOutput) == RPR_SUCCESS) && @@ -282,6 +291,38 @@ RprUsd_UsdUVTexture::RprUsd_UsdUVTexture( } }; + // Analyze material graph and find out the minimum required amount of components required + textureCommit.numComponentsRequired = 0; + for (auto& entry : m_ctx->hdMaterialNetwork->nodes) { + for (auto& connection : entry.second.inputConnections) { + if (connection.second.upstreamNode == *m_ctx->currentNodePath) { + uint32_t numComponentsRequired = 0; + if (connection.second.upstreamOutputName == RprUsd_UsdUVTextureTokens->rgba) { + numComponentsRequired = 4; + } else if (connection.second.upstreamOutputName == RprUsd_UsdUVTextureTokens->rgb) { + numComponentsRequired = 3; + } else if (connection.second.upstreamOutputName == RprUsd_UsdUVTextureTokens->r) { + numComponentsRequired = 1; + } else if (connection.second.upstreamOutputName == RprUsd_UsdUVTextureTokens->g) { + numComponentsRequired = 2; + } else if (connection.second.upstreamOutputName == RprUsd_UsdUVTextureTokens->b) { + numComponentsRequired = 3; + } else if (connection.second.upstreamOutputName == RprUsd_UsdUVTextureTokens->a) { + numComponentsRequired = 4; + } + textureCommit.numComponentsRequired = std::max(textureCommit.numComponentsRequired, numComponentsRequired); + + if (textureCommit.numComponentsRequired == 4) { + break; + } + } + } + + if (textureCommit.numComponentsRequired == 4) { + break; + } + } + // Texture loading is postponed to allow multi-threading loading. // RprUsdMaterialRegistry::GetInstance().CommitTexture(std::move(textureCommit)); @@ -402,11 +443,17 @@ RprUsd_UsdPrimvarReader::RprUsd_UsdPrimvarReader( // it outputs actual UVs so that the user can manipulate it in any way. auto varnameNameIt = hydraParameters.find(UsdPrimvarReaderTokens->varname); - if (varnameNameIt != hydraParameters.end() && - varnameNameIt->second.IsHolding()) { - auto& varname = varnameNameIt->second.UncheckedGet(); - if (!varname.IsEmpty()) { - ctx->uvPrimvarName = varname; + if (varnameNameIt != hydraParameters.end()) { + if (varnameNameIt->second.IsHolding()) { + auto& varname = varnameNameIt->second.UncheckedGet(); + if (!varname.IsEmpty()) { + ctx->uvPrimvarName = varname.GetString(); + } + } else if (varnameNameIt->second.IsHolding()) { + auto& varname = varnameNameIt->second.UncheckedGet(); + if (!varname.empty()) { + ctx->uvPrimvarName = varname; + } } } diff --git a/pxr/imaging/rprUsd/materialNodes/usdNode.h b/pxr/imaging/rprUsd/materialNodes/usdNode.h index cd10b5503..5089d51e2 100644 --- a/pxr/imaging/rprUsd/materialNodes/usdNode.h +++ b/pxr/imaging/rprUsd/materialNodes/usdNode.h @@ -41,6 +41,9 @@ class RprUsd_UsdPreviewSurface : public RprUsd_BaseRuntimeNode { std::unique_ptr m_emissiveWeightNode; std::unique_ptr m_refractionWeightNode; + + std::unique_ptr m_normalMapScaleNode; + std::unique_ptr m_normalMapBiasNode; std::unique_ptr m_normalMapNode; std::shared_ptr m_displaceNode; diff --git a/pxr/imaging/rprUsd/materialRegistry.cpp b/pxr/imaging/rprUsd/materialRegistry.cpp index 33ae6f5b6..707a5cfc0 100644 --- a/pxr/imaging/rprUsd/materialRegistry.cpp +++ b/pxr/imaging/rprUsd/materialRegistry.cpp @@ -18,6 +18,8 @@ limitations under the License. #include "pxr/imaging/rprUsd/imageCache.h" #include "pxr/imaging/rprUsd/debugCodes.h" #include "pxr/imaging/rprUsd/material.h" +#include "pxr/imaging/rprUsd/tokens.h" +#include "pxr/imaging/rprUsd/error.h" #include "pxr/imaging/rprUsd/util.h" #include "pxr/base/tf/instantiateSingleton.h" #include "pxr/base/tf/staticTokens.h" @@ -144,7 +146,7 @@ void RprUsdMaterialRegistry::CommitResources( std::string formatString; for (size_t i = 0; i < m_textureCommits.size(); ++i) { auto& commit = m_textureCommits[i]; - if (auto rprImage = imageCache->GetImage(commit.filepath, commit.colorspace, commit.wrapType)) { + if (auto rprImage = imageCache->GetImage(commit.filepath, commit.colorspace, commit.wrapType, {}, 0)) { commit.setTextureCallback(rprImage); continue; } @@ -198,7 +200,7 @@ void RprUsdMaterialRegistry::CommitResources( } auto& commit = m_textureCommits[i]; - auto coreImage = imageCache->GetImage(commit.filepath, commit.colorspace, commit.wrapType, tiles); + auto coreImage = imageCache->GetImage(commit.filepath, commit.colorspace, commit.wrapType, tiles, commit.numComponentsRequired); commit.setTextureCallback(coreImage); } @@ -300,24 +302,6 @@ void DumpMaterialNetwork(HdMaterialNetworkMap const& networkMap) { } } -// Structures are taken from hdSt/materialNetwork.cpp - -struct RprUsd_MaterialNetwork { - struct Connection { - SdfPath upstreamNode; - TfToken upstreamOutputName; - }; - - struct Node { - TfToken nodeTypeId; - std::map parameters; - std::map inputConnections; - }; - - std::map nodes; - std::map terminals; -}; - void ConvertLegacyHdMaterialNetwork( HdMaterialNetworkMap const& hdNetworkMap, RprUsd_MaterialNetwork *result) { @@ -383,6 +367,7 @@ RprUsdMaterial* RprUsdMaterialRegistry::CreateMaterial( ConvertLegacyHdMaterialNetwork(legacyNetworkMap, &network); RprUsd_MaterialBuilderContext context = {}; + context.hdMaterialNetwork = &network; context.rprContext = rprContext; context.imageCache = imageCache; context.mtlxLoader = m_mtlxLoader.get(); @@ -394,7 +379,8 @@ RprUsdMaterial* RprUsdMaterialRegistry::CreateMaterial( bool Finalize(RprUsd_MaterialBuilderContext& context, VtValue const& surfaceOutput, VtValue const& displacementOutput, - VtValue const& volumeOutput) { + VtValue const& volumeOutput, + int materialId) { auto getTerminalRprNode = [](VtValue const& terminalOutput) -> rpr::MaterialNode* { if (!terminalOutput.IsEmpty()) { @@ -414,9 +400,15 @@ RprUsdMaterial* RprUsdMaterialRegistry::CreateMaterial( m_isShadowCatcher = context.isShadowCatcher; m_isReflectionCatcher = context.isReflectionCatcher; - m_uvPrimvarName = std::move(context.uvPrimvarName); + m_uvPrimvarName = TfToken(context.uvPrimvarName); m_displacementScale = std::move(context.displacementScale); + if (m_surfaceNode && materialId >= 0) { + // TODO: add C++ wrapper + auto apiHandle = rpr::GetRprObject(m_surfaceNode); + RPR_ERROR_CHECK(rprMaterialNodeSetID(apiHandle, rpr_uint(materialId)), "Failed to set material node id"); + } + return m_volumeNode || m_surfaceNode || m_displacementNode; } }; @@ -435,6 +427,7 @@ RprUsdMaterial* RprUsdMaterialRegistry::CreateMaterial( for (auto& entry : network.nodes) { auto& nodePath = entry.first; auto& node = entry.second; + context.currentNodePath = &nodePath; try { // Check if we have registered node that match nodeTypeId @@ -525,7 +518,28 @@ RprUsdMaterial* RprUsdMaterialRegistry::CreateMaterial( auto surfaceOutput = getTerminalOutput(HdMaterialTerminalTokens->surface); auto displacementOutput = getTerminalOutput(HdMaterialTerminalTokens->displacement); - return out->Finalize(context, surfaceOutput, displacementOutput, volumeOutput) ? out.release() : nullptr; + int materialId = -1; + + auto surfaceTerminalIt = network.terminals.find(HdMaterialTerminalTokens->surface); + if (surfaceTerminalIt != network.terminals.end()) { + auto& surfaceNodePath = surfaceTerminalIt->second.upstreamNode; + + auto surfaceNodeIt = network.nodes.find(surfaceNodePath); + if (surfaceNodeIt != network.nodes.end()) { + auto& parameters = surfaceNodeIt->second.parameters; + + auto idIt = parameters.find(RprUsdTokens->id); + if (idIt != parameters.end()) { + auto& value = idIt->second; + + if (value.IsHolding()) { + materialId = value.UncheckedGet(); + } + } + } + } + + return out->Finalize(context, surfaceOutput, displacementOutput, volumeOutput, materialId) ? out.release() : nullptr; } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialRegistry.h b/pxr/imaging/rprUsd/materialRegistry.h index 737efb608..3ecb83e75 100644 --- a/pxr/imaging/rprUsd/materialRegistry.h +++ b/pxr/imaging/rprUsd/materialRegistry.h @@ -86,6 +86,7 @@ class RprUsdMaterialRegistry { std::string filepath; std::string colorspace; rpr::ImageWrapType wrapType; + uint32_t numComponentsRequired = 0; std::function const&)> setTextureCallback; }; diff --git a/pxr/imaging/rprUsd/tokens.cpp b/pxr/imaging/rprUsd/tokens.cpp new file mode 100644 index 000000000..c1a3d1464 --- /dev/null +++ b/pxr/imaging/rprUsd/tokens.cpp @@ -0,0 +1,21 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "pxr/imaging/rprUsd/tokens.h" + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEFINE_PUBLIC_TOKENS(RprUsdTokens, RPRUSD_TOKENS); + +PXR_NAMESPACE_CLOSE_SCOPE + diff --git a/pxr/imaging/rprUsd/tokens.h b/pxr/imaging/rprUsd/tokens.h new file mode 100644 index 000000000..053776dd6 --- /dev/null +++ b/pxr/imaging/rprUsd/tokens.h @@ -0,0 +1,33 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef PXR_IMAGING_RPRUSD_TOKENS_H +#define PXR_IMAGING_RPRUSD_TOKENS_H + +#include "pxr/pxr.h" +#include "pxr/imaging/rprUsd/api.h" +#include "pxr/base/tf/staticTokens.h" + +PXR_NAMESPACE_OPEN_SCOPE + +#define RPRUSD_TOKENS \ + (rpr) \ + /* UsdShadeShader */ \ + ((id, "rpr:id")) + + +TF_DECLARE_PUBLIC_TOKENS(RprUsdTokens, RPRUSD_API, RPRUSD_TOKENS); + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif //PXR_IMAGING_RPRUSD_TOKENS_H