diff --git a/.gitmodules b/.gitmodules index 185f0a949..43c4683e3 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,6 @@ [submodule "deps/ghc_filesystem"] path = deps/ghc_filesystem url = https://github.com/gulrak/filesystem +[submodule "deps/murmurhash"] + path = deps/murmurhash + url = https://github.com/aappleby/smhasher diff --git a/CHANGELOG.MD b/CHANGELOG.MD index f315517ad..40210f107 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,5 +1,44 @@ # Change Log +## Version 2.1 +### New Features: +- Subsurface Scattering and Volume shaders now work in RPR 2.0. This allows the rendering of organic materials, such as skin, which absorb light into their interior. +- Deformation motion blur gives accurate motion blur to objects which are being deformed, for example, a flag flapping in the wind or a ball squashing. Besides, motion blur export has been optimized, and a setting for disabling deformation motion blur has been added. +- The new RPR Toon Shader has been added. This enables cartoon style shading for a non-photorealistic look. Toon shaders can be used in a “simple” mode for just setting a color or a gradient of different colors for shadow vs lit areas of the object. +- Support for USD 21.05 has been added. +- Cryptomatte AOVs can now be exported from Houdini. + + +### Issues Fixed: +- Overbright edges of objects with Uber shaders in metalness mode ― fixed. +- Shaders with high roughness could have artifacts with reflection or refraction ― fixed. +- Users can now override the environment light background color in Houdini. +- The render’s “percent done” statistics could be incorrect if the minimum number of samples had not yet been reached ― fixed. +- An issue with the visibility of distant lights not being respected has been fixed. +- A crash was occurring on macOS in some cases when using the RPR Material Library for the first time ― fixed. +- RPR Materials nodes were not present in Houdini on Linux ― fixed. +- The speed of rendering via Houdini’s Husk utility with multiple render products has been increased. +- .rpr export fixes: + - .rpr export now exports USD depth AOV correctly; + - An issue with creating temporary directories when exporting .rpr files has been fixed; + - The $OS variable can now be used in the output path to change the path based on the operating system. +- Developer changes to hdRPR: + - The hdRPR render delegate can now override the kernel cache path with an environment variable; + - The batch or interactive render mode can now be switched on dynamically. +- MaterialX fixes: + - Logging with the MaterialX loader has been improved; + - Support for the Tangent node has been added; + - Support for the View Direction node has been added; + - Support for the RPR Emissive node has been added; + - The displacement scale has been fixed; + - An issue with the use of the mix node to mix surface shaders has been fixed. + +### Known Issues: +- In RPR 2.0, heterogenous volumes, smoke and fire simulations or VDB files are not yet supported. +- Subsurface scattering and volume shader are currently disabled on macOS due to long compile times. +- Some AOVs may have artifacts on AMD cards with drivers earlier than 21.6.1 + + ## Version 2.0.27 ### New Features: - Support for AMD Radeon™ RX 6700 XT graphics cards has been added. diff --git a/README.md b/README.md index 0c2c71745..a7244543a 100644 --- a/README.md +++ b/README.md @@ -110,3 +110,12 @@ Launch either usdview or Houdini's Solaris viewport and select RPR as the render * `HDRPR_TRACING_DIR` To change the default directory, add the environment variable `HDRPR_TRACING_DIR` pointing to the location in which you wish the trace files to be recorded. For example, set `HDRPR_TRACING_DIR=C:\folder\` to activate the tracing in `C:\folder\`. + +Houdini +----------------------------- + +##### RPR Material Library + +1. Download [.mtlx version of RPR Material Library](https://drive.google.com/file/d/1i5jdYGS7gmrxw_Y0y7uotx4gxXVr8cMB/view?usp=sharing). + +2. Follow instructions from INSTALL.md shipped with the material library. diff --git a/cmake/defaults/Options.cmake b/cmake/defaults/Options.cmake index 4bf39b6f2..0cd79bb5c 100644 --- a/cmake/defaults/Options.cmake +++ b/cmake/defaults/Options.cmake @@ -77,4 +77,7 @@ set(PXR_LIB_PREFIX ${CMAKE_SHARED_LIBRARY_PREFIX} ) option(BUILD_SHARED_LIBS "Build shared libraries." ON) -option(PXR_BUILD_MONOLITHIC "Build a monolithic library." OFF) \ No newline at end of file +option(PXR_BUILD_MONOLITHIC "Build a monolithic library." OFF) + +option(MATERIALX_BUILD_PYTHON "Build the MaterialX Python package from C++ bindings. Requires Python 2.7 or greater." OFF) +option(MATERIALX_INSTALL_PYTHON "Install the MaterialX Python package as a third-party library when the install target is built." OFF) \ No newline at end of file diff --git a/cmake/defaults/Packages.cmake b/cmake/defaults/Packages.cmake index 7471a62c9..1b086ca3e 100644 --- a/cmake/defaults/Packages.cmake +++ b/cmake/defaults/Packages.cmake @@ -99,20 +99,32 @@ else() find_package(OpenVDB QUIET) endif() -if(OpenVDB_FOUND) - if(HoudiniUSD_FOUND) - find_package(OpenEXR QUIET COMPONENTS Half_sidefx) - endif() - +macro(find_exr) if(NOT OpenEXR_FOUND) - find_package(OpenEXR QUIET COMPONENTS Half) - endif() + set(SIDEFX_COMPONENTS ${ARGV}) + list(TRANSFORM SIDEFX_COMPONENTS APPEND "_sidefx") - if(NOT OpenEXR_FOUND) - message(FATAL_ERROR "Failed to find Half library") + if(HoudiniUSD_FOUND) + find_package(OpenEXR QUIET COMPONENTS ${SIDEFX_COMPONENTS}) + endif() + + if(NOT OpenEXR_FOUND) + find_package(OpenEXR QUIET COMPONENTS ${ARGV}) + endif() endif() -else() - message(STATUS "Skipping OpenVDB support") +endmacro() + +find_exr(Half IlmImf Iex) + +set(RPR_EXR_EXPORT_ENABLED TRUE) +if(NOT OpenEXR_FOUND) + set(RPR_EXR_EXPORT_ENABLED FALSE) +endif() + +find_exr(Half) + +if(NOT OpenEXR_FOUND) + message(FATAL_ERROR "Failed to find Half library") endif() # ---------------------------------------------- diff --git a/cmake/defaults/Version.cmake b/cmake/defaults/Version.cmake index 7052024b0..4ea9235d4 100644 --- a/cmake/defaults/Version.cmake +++ b/cmake/defaults/Version.cmake @@ -23,5 +23,5 @@ # # Versioning information set(HD_RPR_MAJOR_VERSION "2") -set(HD_RPR_MINOR_VERSION "0") -set(HD_RPR_PATCH_VERSION "27") +set(HD_RPR_MINOR_VERSION "2") +set(HD_RPR_PATCH_VERSION "0") diff --git a/cmake/modules/FindRif.cmake b/cmake/modules/FindRif.cmake index 51c4ccc52..e063da458 100644 --- a/cmake/modules/FindRif.cmake +++ b/cmake/modules/FindRif.cmake @@ -34,6 +34,7 @@ find_library(RIF_LIBRARY if(WIN32) set(RIF_BINARIES + ${RIF_LOCATION_LIB}/dxcompiler.dll ${RIF_LOCATION_LIB}/MIOpen.dll ${RIF_LOCATION_LIB}/RadeonImageFilters.dll ${RIF_LOCATION_LIB}/RadeonML_MIOpen.dll diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 1e14b577f..ad9c09bab 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -5,12 +5,16 @@ if(NOT MaterialX_FOUND) # If MaterialX was not explicitly provided, use the one from a submodule set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) set(MATERIALX_PYTHON_LTO OFF) - set(MATERIALX_INSTALL_PYTHON OFF) set(MATERIALX_BUILD_RENDER OFF) set(MATERIALX_BUILD_GEN_GLSL OFF) set(MATERIALX_BUILD_GEN_OSL OFF) set(MATERIALX_BUILD_TESTS OFF) - add_subdirectory(MaterialX EXCLUDE_FROM_ALL) # EXCLUDE_FROM_ALL allows us to skip installation of mtlx static libraries + + if(MATERIALX_BUILD_PYTHON AND MATERIALX_INSTALL_PYTHON) + add_subdirectory(MaterialX) + else() + add_subdirectory(MaterialX EXCLUDE_FROM_ALL) # EXCLUDE_FROM_ALL allows us to skip installation of mtlx static libraries + endif() install( FILES @@ -70,4 +74,13 @@ if(HoudiniUSD_FOUND) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ghc_filesystem) endif() +# Murmurhash +# ---------------------------------------------- + +set(MURMURHASH_DIR ${CMAKE_CURRENT_SOURCE_DIR}/murmurhash/src) +add_library(murmurhash STATIC + ${MURMURHASH_DIR}/MurmurHash3.h + ${MURMURHASH_DIR}/MurmurHash3.cpp) +target_include_directories(murmurhash PUBLIC ${MURMURHASH_DIR}) + # ---------------------------------------------- diff --git a/deps/RPR b/deps/RPR index 5ca51cc43..7afdadba3 160000 --- a/deps/RPR +++ b/deps/RPR @@ -1 +1 @@ -Subproject commit 5ca51cc43b41f04e812b393ffcb402f9a6b56f9f +Subproject commit 7afdadba35695f36054a314ccbfec646291fbe39 diff --git a/deps/murmurhash b/deps/murmurhash new file mode 160000 index 000000000..61a0530f2 --- /dev/null +++ b/deps/murmurhash @@ -0,0 +1 @@ +Subproject commit 61a0530f28277f2e850bfc39600ce61d02b518de diff --git a/deps/rprMtlxLoader/rprMtlxLoader.cpp b/deps/rprMtlxLoader/rprMtlxLoader.cpp index 504bd6ff9..debe11c6e 100644 --- a/deps/rprMtlxLoader/rprMtlxLoader.cpp +++ b/deps/rprMtlxLoader/rprMtlxLoader.cpp @@ -164,11 +164,6 @@ struct Mtlx2Rpr { {"lumacoeffs", RPR_MATERIAL_INPUT_LUMACOEFF}, } }; - nodes["convert"] = { - RPR_MATERIAL_NODE_MATX_CONVERT, { - {"in", RPR_MATERIAL_INPUT_0}, - } - }; nodes["rotate3d"] = { RPR_MATERIAL_NODE_MATX_ROTATE3D, { {"in", RPR_MATERIAL_INPUT_0}, @@ -208,6 +203,12 @@ struct Mtlx2Rpr { }; nodes["position"] = {RPR_MATERIAL_NODE_MATX_POSITION, {}}; + nodes["rpr_emissive"] = { + RPR_MATERIAL_NODE_EMISSIVE, { + {"color", RPR_MATERIAL_INPUT_COLOR} + } + }; + auto addArithmeticNode = [this](const char* name, rpr_material_node_arithmetic_operation op, int numArgs) { auto& mapping = nodes[name]; mapping.id = RPR_MATERIAL_NODE_ARITHMETIC; @@ -339,11 +340,10 @@ struct LoaderContext { static const int kGlobalLogDepth = -1; int logDepth = kGlobalLogDepth; LogScope logScope = LSGlobal; - bool logEnabled = true; - void Log(const char* fmt, ...); + RPRMtlxLoader::LogLevel logLevel; - void LogError(size_t line, const char* fmt, ...); + void Log(RPRMtlxLoader::LogLevel level, size_t line, const char* fmt, ...); struct ScopeGuard { LoaderContext* ctx; @@ -356,11 +356,16 @@ struct LoaderContext { ScopeGuard EnterScope(LogScope logScope, mx::Element const* scopeElement); std::string ResolveFile(std::string const& filename); - std::string FindFile(std::string const& filename); }; #define LOG_ERROR(ctx, fmt, ...) \ - (ctx)->LogError(__LINE__, fmt, ##__VA_ARGS__) + (ctx)->Log(RPRMtlxLoader::LogLevel::Error, __LINE__, fmt, ##__VA_ARGS__) + +#define LOG_WARNING(ctx, fmt, ...) \ + (ctx)->Log(RPRMtlxLoader::LogLevel::Warning, __LINE__, fmt, ##__VA_ARGS__) + +#define LOG(ctx, fmt, ...) \ + (ctx)->Log(RPRMtlxLoader::LogLevel::Info, __LINE__, fmt, ##__VA_ARGS__) //------------------------------------------------------------------------------ // Node declarations @@ -417,6 +422,86 @@ struct RprNode : public Node { size_t MoveRprApiHandles(rpr_material_node* dst) override; }; +// The passthrough node transfers input unaltered +// +struct PassthroughNode : public RprNode { + std::string inputName; + PassthroughNode(std::string inputName) : RprNode(nullptr, false), inputName(std::move(inputName)) {} + ~PassthroughNode() override = default; + + rpr_status SetInput(mx::TypedElement* downstreamElement, mx::Element* upstreamElement, rpr_material_node upstreamRprNode, LoaderContext* context) override { + if (downstreamElement->getName() == inputName) { + rprNode = upstreamRprNode; + isOwningRprNode = false; + return RPR_SUCCESS; + } else { + LOG(context, "Unsupported input: %s", downstreamElement->getName().c_str()); + return RPR_ERROR_UNSUPPORTED; + } + } + + rpr_status SetInput(mx::TypedElement* downstreamElement, mx::ValueElement* upstreamValueElement, LoaderContext* context) override { + if (downstreamElement->getName() == inputName) { + if (rprNode && !isOwningRprNode) { + LOG(context, "Unsupported input: %s", downstreamElement->getName().c_str()); + return RPR_ERROR_UNSUPPORTED; + } + + if (!rprNode) { + auto status = rprMaterialSystemCreateNode(context->rprMatSys, RPR_MATERIAL_NODE_CONSTANT_TEXTURE, &rprNode); + if (status != RPR_SUCCESS) { + return status; + } + isOwningRprNode = true; + } + + return RprNode::SetInput(downstreamElement, RPR_MATERIAL_INPUT_VALUE, upstreamValueElement, context); + } else { + LOG(context, "Unsupported input: %s", downstreamElement->getName().c_str()); + return RPR_ERROR_UNSUPPORTED; + } + } +}; + +struct DisplacementNode : public RprNode { + DisplacementNode(LoaderContext* context) : RprNode(nullptr, true) { + rprMaterialSystemCreateNode(context->rprMatSys, RPR_MATERIAL_NODE_ARITHMETIC, &rprNode); + rprMaterialNodeSetInputUByKey(rprNode, RPR_MATERIAL_INPUT_OP, RPR_MATERIAL_NODE_OP_MUL); + } + ~DisplacementNode() override = default; + + rpr_status SetInput(mx::TypedElement* downstreamElement, mx::Element* upstreamElement, rpr_material_node upstreamRprNode, LoaderContext* context) override { + if (downstreamElement->getName() == "displacement") { + if (downstreamElement->getType() != "float") { + LOG_ERROR(context, "Only scalar displacement is supported"); + return RPR_ERROR_UNSUPPORTED; + } + + return rprMaterialNodeSetInputNByKey(rprNode, RPR_MATERIAL_INPUT_COLOR0, upstreamRprNode); + } else if (downstreamElement->getName() == "scale") { + return rprMaterialNodeSetInputNByKey(rprNode, RPR_MATERIAL_INPUT_COLOR1, upstreamRprNode); + } else { + LOG(context, "Unsupported input: %s", downstreamElement->getName().c_str()); + return RPR_ERROR_UNSUPPORTED; + } + } + rpr_status SetInput(mx::TypedElement* downstreamElement, mx::ValueElement* upstreamValueElement, LoaderContext* context) override { + if (downstreamElement->getName() == "displacement") { + if (downstreamElement->getType() != "float") { + LOG_ERROR(context, "Only scalar displacement is supported"); + return RPR_ERROR_UNSUPPORTED; + } + + return RprNode::SetInput(downstreamElement, RPR_MATERIAL_INPUT_COLOR0, upstreamValueElement, context); + } else if (downstreamElement->getName() == "scale") { + return RprNode::SetInput(downstreamElement, RPR_MATERIAL_INPUT_COLOR1, upstreamValueElement, context); + } else { + LOG(context, "Unsupported input: %s", downstreamElement->getName().c_str()); + return RPR_ERROR_UNSUPPORTED; + } + } +}; + /// The node that can be mapped (fully or partially) to MaterialX standard node /// struct RprMappedNode : public RprNode { @@ -519,7 +604,7 @@ struct MtlxNodeGraphNode : public Node { mx::NodePtr downstreamNode; mx::TypedElementPtr downstreamInput; }; - Node* _CreateSubNode(mx::NodePtr const& mtlxNode, std::vector* pendingConnections, LoaderContext* context); + Node* _CreateSubNode(mx::NodePtr const& mtlxNode, LoaderContext* context); struct InterfaceSocket { mx::NodePtr subNode; @@ -534,7 +619,7 @@ struct MtlxNodeGraphNode : public Node { for (auto& socket : *_sockets) { auto nodeIt = _nodeGraphNode->subNodes.find(socket.subNode->getName()); if (nodeIt != _nodeGraphNode->subNodes.end()) { - context->Log(" %s:%s\n", nodeIt->first.c_str(), socket.input->getName().c_str()); + LOG(context, " %s:%s", nodeIt->first.c_str(), socket.input->getName().c_str()); f(nodeIt->second.get(), socket.input.get()); } @@ -585,13 +670,13 @@ mx::OutputPtr GetOutput(mx::InterfaceElement const* interfaceElement, mx::PortEl if (interfaceElement->getType() == mx::MULTI_OUTPUT_TYPE_STRING) { auto& targetOutputName = portElement->getOutputString(); if (targetOutputName.empty()) { - LOG_ERROR(context, "invalid port element structure: output should be specified when connecting to multioutput element - port: %s, interface: %s\n", portElement->asString().c_str(), interfaceElement->asString().c_str()); + LOG_ERROR(context, "invalid port element structure: output should be specified when connecting to multioutput element - port: %s, interface: %s", portElement->asString().c_str(), interfaceElement->asString().c_str()); return nullptr; } auto output = interfaceElement->getOutput(targetOutputName); if (!output) { - LOG_ERROR(context, "invalid connection: cannot determine output - %s\n", portElement->asString().c_str()); + LOG_ERROR(context, "invalid connection: cannot determine output - %s", portElement->asString().c_str()); } return output; @@ -609,11 +694,20 @@ size_t GetHash(T const& value) { // Loader context implementation //------------------------------------------------------------------------------ -void LoaderContext::Log(const char* fmt, ...) { - if (!logEnabled) { +const char* const kLogLevelStr[int(RPRMtlxLoader::LogLevel::Info) + 1] = { + "", + "ERROR", + "WARNING", + "INFO", +}; + +void LoaderContext::Log(RPRMtlxLoader::LogLevel level, size_t line, const char* fmt, ...) { + if (level > logLevel) { return; } + printf("[MTLXLOADER %s] ", kLogLevelStr[int(level)]); + if (logScope != LSGlobal) { int padding = 0; if (logDepth > 0) { @@ -631,15 +725,10 @@ void LoaderContext::Log(const char* fmt, ...) { va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); -} - -void LoaderContext::LogError(size_t line, const char* fmt, ...) { - printf("RPRMtlxLoader error (%zu): ", line); - va_list ap; - va_start(ap, fmt); - vprintf(fmt, ap); - va_end(ap); + if (line) { + printf(" (%zu)\n", line); + } } LoaderContext::ScopeGuard::ScopeGuard(LoaderContext* ctx, LogScope logScope, mx::Element const* scopeElement) @@ -650,7 +739,7 @@ LoaderContext::ScopeGuard::ScopeGuard(LoaderContext* ctx, LogScope logScope, mx: ctx->logScope = logScope; if (logScope == LSGlobal) { ctx->logDepth = kGlobalLogDepth; - } else if (logScope == LSNested) { + } else if (logScope == LSNested || logScope == LSNode) { ctx->logDepth++; } } @@ -711,7 +800,7 @@ bool LoaderContext::ConnectToGlobalOutput(T* input, Node* node) { if (auto nodeGraph = mtlxDocument->getNodeGraph(nodeGraphName)) { if (auto nodeGraphOutput = nodeGraph->getOutput(outputName)) { if (auto freeStandingNodeGraphNode = GetFreeStandingNodeGraph(nodeGraph)) { - Log("Bindinput %s: %s:%s (nodegraph)\n", input->getName().c_str(), nodeGraphName.c_str(), outputName.c_str()); + LOG(this, "Bindinput %s: %s:%s (nodegraph)", input->getName().c_str(), nodeGraphName.c_str(), outputName.c_str()); return freeStandingNodeGraphNode->Connect(nodeGraphOutput->getName(), node, input, this) == RPR_SUCCESS; } @@ -727,7 +816,7 @@ bool LoaderContext::ConnectToGlobalOutput(T* input, Node* node) { if (auto mxtlGlobalNodeDef = mtlxGlobalNode->getNodeDef()) { if (auto mtlxGlobalNodeOutput = GetOutput(mxtlGlobalNodeDef.get(), globalOutput.get(), this)) { if (auto globalNode = GetGlobalNode(mtlxGlobalNode.get())) { - Log("Bindinput %s: %s (output)\n", input->getName().c_str(), outputName.c_str()); + LOG(this, "Bindinput %s: %s (output)", input->getName().c_str(), outputName.c_str()); return globalNode->Connect(mtlxGlobalNodeOutput->getName(), node, input, this) == RPR_SUCCESS; } @@ -749,56 +838,70 @@ Node* LoaderContext::GetGeomNode(mx::GeomPropDef* geomPropDef) { auto& geomProp = geomPropDef->getGeomProp(); auto& type = geomPropDef->getAttribute("type"); if (geomProp.empty() || type.empty()) { - LOG_ERROR(this, "Invalid geomPropDef: %s\n", geomPropDef->asString().c_str()); + LOG_ERROR(this, "Invalid geomPropDef: %s", geomPropDef->asString().c_str()); return nullptr; } - const auto kInvalidLookupValue = static_cast(-1); - rpr_material_node_lookup_value lookupValue = kInvalidLookupValue; - - if (geomProp == "texcoord") { - if (type != "vector2") { - LOG_ERROR(this, "Unexpected type for texcoord geomProp: %s\n", type.c_str()); - } + rpr_material_node apiHandle = nullptr; - auto& index = geomPropDef->getIndex(); - if (index.empty() || index == "0") { - lookupValue = RPR_MATERIAL_NODE_LOOKUP_UV; - } else if (index == "1") { - lookupValue = RPR_MATERIAL_NODE_LOOKUP_UV1; - } - } else if (geomProp == "normal") { + if (geomProp == "tangent") { auto& space = geomPropDef->getSpace(); if (space == "world") { - lookupValue = RPR_MATERIAL_NODE_LOOKUP_N; - } else { - LOG_ERROR(this, "Unsupported normal space: \"%s\"\n", space.c_str()); - } - } else if (geomProp == "position") { - auto& space = geomPropDef->getSpace(); - if (space == "world") { - lookupValue = RPR_MATERIAL_NODE_LOOKUP_P; + auto status = rprMaterialSystemCreateNode(rprMatSys, RPR_MATERIAL_NODE_MATX_TANGENT, &apiHandle); + if (!apiHandle) { + LOG_ERROR(this, "Failed to create matx tangent node: %d", status); + } } else { - lookupValue = RPR_MATERIAL_NODE_LOOKUP_P_LOCAL; + LOG_ERROR(this, "Unsupported tangent space: \"%s\"", space.c_str()); } - } - // TODO: handle tangent, bitangent, geomcolor, geompropvalue (primvar) + } else { + const auto kInvalidLookupValue = static_cast(-1); + rpr_material_node_lookup_value lookupValue = kInvalidLookupValue; - if (lookupValue != kInvalidLookupValue) { - rpr_material_node apiHandle; - auto status = rprMaterialSystemCreateNode(rprMatSys, RPR_MATERIAL_NODE_INPUT_LOOKUP, &apiHandle); - if (apiHandle) { - rprMaterialNodeSetInputUByKey(apiHandle, RPR_MATERIAL_INPUT_VALUE, lookupValue); + if (geomProp == "texcoord") { + if (type != "vector2") { + LOG_ERROR(this, "Unexpected type for texcoord geomProp: %s", type.c_str()); + } - auto geomNodeIt = geomNodes.emplace(geomPropDef->getName(), std::make_unique(apiHandle, true)).first; - return geomNodeIt->second.get(); - } else { - LOG_ERROR(this, "Failed to create RPR_MATERIAL_NODE_INPUT_LOOKUP node: %d\n", status); - return nullptr; + auto& index = geomPropDef->getIndex(); + if (index.empty() || index == "0") { + lookupValue = RPR_MATERIAL_NODE_LOOKUP_UV; + } else if (index == "1") { + lookupValue = RPR_MATERIAL_NODE_LOOKUP_UV1; + } + } else if (geomProp == "normal") { + auto& space = geomPropDef->getSpace(); + if (space == "world") { + lookupValue = RPR_MATERIAL_NODE_LOOKUP_N; + } else { + LOG_ERROR(this, "Unsupported normal space: \"%s\"", space.c_str()); + } + } else if (geomProp == "position") { + auto& space = geomPropDef->getSpace(); + if (space == "world") { + lookupValue = RPR_MATERIAL_NODE_LOOKUP_P; + } else { + lookupValue = RPR_MATERIAL_NODE_LOOKUP_P_LOCAL; + } + } + // TODO: handle bitangent, geomcolor, geompropvalue (primvar) + + if (lookupValue != kInvalidLookupValue) { + auto status = rprMaterialSystemCreateNode(rprMatSys, RPR_MATERIAL_NODE_INPUT_LOOKUP, &apiHandle); + if (apiHandle) { + rprMaterialNodeSetInputUByKey(apiHandle, RPR_MATERIAL_INPUT_VALUE, lookupValue); + } else { + LOG_ERROR(this, "Failed to create RPR_MATERIAL_NODE_INPUT_LOOKUP node: %d", status); + } } } - LOG_ERROR(this, "Unsupported geom node: %s\n", geomPropDef->asString().c_str()); + if (apiHandle) { + auto geomNodeIt = geomNodes.emplace(geomPropDef->getName(), std::make_unique(apiHandle, true)).first; + return geomNodeIt->second.get(); + } + + LOG_ERROR(this, "Unsupported geom node: %s", geomPropDef->asString().c_str()); return nullptr; } @@ -1167,7 +1270,7 @@ Node::Ptr Node::Create(mx::Node* mtlxNode, LoaderContext* context) { rprNode = upstreamRprNode; return RPR_SUCCESS; } else { - context->Log("Unsupported surface input: %s\n", downstreamElement->getName().c_str()); + LOG(context, "Unsupported surface input: %s", downstreamElement->getName().c_str()); return RPR_ERROR_UNSUPPORTED; } } @@ -1175,54 +1278,18 @@ Node::Ptr Node::Create(mx::Node* mtlxNode, LoaderContext* context) { return std::make_unique(); } else if (mtlxNode->getCategory() == "displacement") { - // The displacement node is passthrough node - it transfers input unaltered - // - struct DisplacementNode : public RprNode { - DisplacementNode() : RprNode(nullptr, false) {} - - rpr_status SetInput(mx::TypedElement* downstreamElement, mx::Element* upstreamElement, rpr_material_node upstreamRprNode, LoaderContext* context) override { - if (downstreamElement->getName() == "displacement") { - rprNode = upstreamRprNode; - isOwningRprNode = false; - return RPR_SUCCESS; - } else { - context->Log("Unsupported displacement input: %s\n", downstreamElement->getName().c_str()); - return RPR_ERROR_UNSUPPORTED; - } - } - - rpr_status SetInput(mx::TypedElement* downstreamElement, mx::ValueElement* upstreamValueElement, LoaderContext* context) override { - if (downstreamElement->getName() == "displacement") { - if (rprNode && isOwningRprNode) { - rprObjectDelete(rprNode); - } - - rprNode = nullptr; - auto status = rprMaterialSystemCreateNode(context->rprMatSys, RPR_MATERIAL_NODE_CONSTANT_TEXTURE, &rprNode); - if (status == RPR_SUCCESS) { - status = RprNode::SetInput(downstreamElement, RPR_MATERIAL_INPUT_VALUE, upstreamValueElement, context); - if (status == RPR_SUCCESS) { - isOwningRprNode = true; - } else { - rprObjectDelete(rprNode); - rprNode = nullptr; - } - } - return status; - } else { - context->Log("Unsupported displacement input: %s\n", downstreamElement->getName().c_str()); - return RPR_ERROR_UNSUPPORTED; - } - } - }; - - return std::make_unique(); + return std::make_unique(context); + } else if (mtlxNode->getCategory() == "convert") { + return std::make_unique("in"); } else if (mtlxNode->getCategory() == "texcoord") { rprMaterialSystemCreateNode(context->rprMatSys, RPR_MATERIAL_NODE_INPUT_LOOKUP, &rprNode); rprMaterialNodeSetInputUByKey(rprNode, RPR_MATERIAL_INPUT_VALUE, RPR_MATERIAL_NODE_LOOKUP_UV); } else if (mtlxNode->getCategory() == "normal") { rprMaterialSystemCreateNode(context->rprMatSys, RPR_MATERIAL_NODE_INPUT_LOOKUP, &rprNode); rprMaterialNodeSetInputUByKey(rprNode, RPR_MATERIAL_INPUT_VALUE, RPR_MATERIAL_NODE_LOOKUP_N); + } else if (mtlxNode->getCategory() == "viewdirection") { + rprMaterialSystemCreateNode(context->rprMatSys, RPR_MATERIAL_NODE_INPUT_LOOKUP, &rprNode); + rprMaterialNodeSetInputUByKey(rprNode, RPR_MATERIAL_INPUT_VALUE, RPR_MATERIAL_NODE_LOOKUP_INVEC); } else if (mtlxNode->getCategory() == "sqrt") { rprMaterialSystemCreateNode(context->rprMatSys, RPR_MATERIAL_NODE_ARITHMETIC, &rprNode); rprMaterialNodeSetInputUByKey(rprNode, RPR_MATERIAL_INPUT_OP, RPR_MATERIAL_NODE_OP_POW); @@ -1273,7 +1340,7 @@ Node::Ptr Node::Create(mx::Node* mtlxNode, LoaderContext* context) { } else { // Some of the materialX standard nodes map to RPR differently depending on the node return type // - if (mtlxNode->getCategory() == "mix" && mtlxNode->getType() == "BSDF") { + if (mtlxNode->getCategory() == "mix" && (mtlxNode->getType() == "BSDF" || mtlxNode->getType() == "surfaceshader")) { // In RPR, mixing of two BSDFs can be done with RPR_MATERIAL_NODE_BLEND // static Mtlx2Rpr::Node bsdfMix = { @@ -1304,7 +1371,7 @@ Node::Ptr Node::Create(mx::Node* mtlxNode, LoaderContext* context) { // TODO: code generation required - context->Log("Unsupported node: %s (%s)\n", mtlxNode->getName().c_str(), mtlxNode->getCategory().c_str()); + LOG(context, "Unsupported node: %s (%s)", mtlxNode->getName().c_str(), mtlxNode->getCategory().c_str()); } } } @@ -1312,7 +1379,7 @@ Node::Ptr Node::Create(mx::Node* mtlxNode, LoaderContext* context) { if (!rprNode && rprNodeMapping) { auto status = rprMaterialSystemCreateNode(context->rprMatSys, rprNodeMapping->id, &rprNode); if (status != RPR_SUCCESS) { - LOG_ERROR(context, "failed to create %s (%s) node: %d\n", mtlxNode->getName().c_str(), mtlxNode->getCategory().c_str(), status); + LOG_ERROR(context, "failed to create %s (%s) node: %d", mtlxNode->getName().c_str(), mtlxNode->getCategory().c_str(), status); return nullptr; } @@ -1379,12 +1446,12 @@ MtlxNodeGraphNode::MtlxNodeGraphNode( LoaderContext* context) : mtlxGraph(graph) { - context->Log("NodeGraph: %s\n", mtlxGraph->getName().c_str()); + LOG(context, "NodeGraph: %s", mtlxGraph->getName().c_str()); auto graphScope = context->EnterScope(LSGraph, mtlxGraph.get()); bool hasAnyOutputNode = false; for (auto& output : requiredOutputs) { - context->Log("Output: %s -> %s \n", output->getName().c_str(), output->getNodeName().c_str()); + LOG(context, "Output: %s -> %s ", output->getName().c_str(), output->getNodeName().c_str()); // An output of a node graph must have a `nodename` attribute // @@ -1392,7 +1459,7 @@ MtlxNodeGraphNode::MtlxNodeGraphNode( if (subNode) { hasAnyOutputNode = true; } else { - LOG_ERROR(context, "Failed to create node %s in %s\n", output->getNodeName().c_str(), mtlxGraph->getName().c_str()); + LOG_ERROR(context, "Failed to create node %s in %s", output->getNodeName().c_str(), mtlxGraph->getName().c_str()); } } @@ -1409,45 +1476,15 @@ Node* MtlxNodeGraphNode::GetSubNode(std::string const& nodename, LoaderContext* auto mtlxNode = mtlxGraph->getNode(nodename); if (!mtlxNode) { - LOG_ERROR(context, "No node with such name: %s\n", nodename.c_str()); + LOG_ERROR(context, "No node with such name: %s", nodename.c_str()); return nullptr; } - // To avoid recursion, we postpone the connection of nodes until the whole subgraph is built - // - std::vector pendingConnections; - pendingConnections.emplace_back(); - pendingConnections.back().downstreamNode = mtlxNode; - - Node* retNode = nullptr; - - while (!pendingConnections.empty()) { - auto connection = std::move(pendingConnections.back()); - pendingConnections.pop_back(); - - Node* downstreamNode = _CreateSubNode(connection.downstreamNode, &pendingConnections, context); - Node* upstreamNode = _CreateSubNode(connection.upstreamNode, &pendingConnections, context); - - if (downstreamNode && upstreamNode) { - auto& outputName = connection.upstreamNodeOutput ? connection.upstreamNodeOutput->getName() : mx::EMPTY_STRING; - auto status = upstreamNode->Connect(outputName, downstreamNode, connection.downstreamInput.get(), context); - - if (status == RPR_SUCCESS) { - context->Log("Connected %s to %s\n", connection.upstreamNode->getName().c_str(), connection.downstreamNode->getName().c_str()); - } - } - - if (connection.downstreamNode == mtlxNode) { - retNode = downstreamNode; - } - } - - return retNode; + return _CreateSubNode(mtlxNode, context); } Node* MtlxNodeGraphNode::_CreateSubNode( mx::NodePtr const& mtlxNode, - std::vector* pendingConnections, LoaderContext* context) { if (!mtlxNode) { return nullptr; @@ -1458,7 +1495,7 @@ Node* MtlxNodeGraphNode::_CreateSubNode( return subNodeIt->second.get(); } - context->Log("Node: %s (%s)\n", mtlxNode->getName().c_str(), mtlxNode->getCategory().c_str()); + LOG(context, "Node: %s (%s)", mtlxNode->getName().c_str(), mtlxNode->getCategory().c_str()); auto nodeScope = context->EnterScope(LSNode, mtlxNode.get()); auto nodeHandle = Node::Create(mtlxNode.get(), context); @@ -1471,7 +1508,7 @@ Node* MtlxNodeGraphNode::_CreateSubNode( auto nodeDef = mtlxNode->getNodeDef(); if (!nodeDef) { - LOG_ERROR(context, "Failed to get mtlxNode definition: %s\n", mtlxNode->asString().c_str()); + LOG_ERROR(context, "Failed to get mtlxNode definition: %s", mtlxNode->asString().c_str()); return node; } @@ -1489,7 +1526,7 @@ Node* MtlxNodeGraphNode::_CreateSubNode( continue; } - context->Log("%s %s\n", inputElement->getCategory().c_str(), inputElement->getName().c_str()); + LOG(context, "%s %s", inputElement->getCategory().c_str(), inputElement->getName().c_str()); auto inputScope = context->EnterScope(LSInput, inputElement.get()); // An element that provides a value for the current input @@ -1533,11 +1570,11 @@ Node* MtlxNodeGraphNode::_CreateSubNode( // auto& nodeName = input->getNodeName(); if (!nodeName.empty()) { - context->Log("nodename: %s\n", nodeName.c_str()); + LOG(context, "nodename: %s", nodeName.c_str()); auto mtlxUpstreamNode = mtlxGraph->getNode(nodeName); if (!mtlxUpstreamNode) { - LOG_ERROR(context, "Node \"%s\" cannot be found in \"%s\"\n", nodeName.c_str(), mtlxGraph->getName().c_str()); + LOG_ERROR(context, "Node \"%s\" cannot be found in \"%s\"", nodeName.c_str(), mtlxGraph->getName().c_str()); continue; } @@ -1546,20 +1583,17 @@ Node* MtlxNodeGraphNode::_CreateSubNode( continue; } - // If upstream node is already created, connect output of upstream node to input of downstream node - // - auto upstreamNodeIt = subNodes.find(mtlxUpstreamNode->getName()); - if (upstreamNodeIt != subNodes.end()) { - status = upstreamNodeIt->second->Connect(mtlxUpstreamNodeOutput->getName(), node, inputElement.get(), context); + Node* upstreamNode = _CreateSubNode(mtlxUpstreamNode, context); + if (upstreamNode) { + auto& outputName = mtlxUpstreamNodeOutput ? mtlxUpstreamNodeOutput->getName() : mx::EMPTY_STRING; + auto status = upstreamNode->Connect(outputName, node, inputElement.get(), context); + + if (status == RPR_SUCCESS) { + LOG(context, "Connected %s to %s", mtlxUpstreamNode->getName().c_str(), mtlxNode->getName().c_str()); + } } else { - // Otherwise, postpone the connection process until the upstream node is created - // - pendingConnections->emplace_back(); - auto& pendingConnection = pendingConnections->back(); - pendingConnection.downstreamNode = mtlxNode; - pendingConnection.downstreamInput = std::move(input); - pendingConnection.upstreamNode = std::move(mtlxUpstreamNode); - pendingConnection.upstreamNodeOutput = std::move(mtlxUpstreamNodeOutput); + LOG(context, "Failed to connect %s to %s", mtlxUpstreamNode->getName().c_str(), mtlxNode->getName().c_str()); + status = RPR_ERROR_INVALID_OBJECT; } if (status == RPR_SUCCESS) { @@ -1582,7 +1616,7 @@ Node* MtlxNodeGraphNode::_CreateSubNode( status = geomNode->Connect(mx::EMPTY_STRING, node, inputElement.get(), context); } } else { - LOG_ERROR(context, "Unkown defaultgeomprop: %s\n", defaultGeomProp.c_str()); + LOG_ERROR(context, "Unkown defaultgeomprop: %s", defaultGeomProp.c_str()); } continue; @@ -1593,7 +1627,7 @@ Node* MtlxNodeGraphNode::_CreateSubNode( // auto& valueStr = valueElement->getValueString(); if (!valueStr.empty()) { - context->Log("%s\n", valueStr.c_str()); + LOG(context, "%s", valueStr.c_str()); status = node->SetInput(inputElement.get(), valueElement.get(), context); if (status == RPR_SUCCESS) { @@ -1658,7 +1692,7 @@ rpr_status MtlxNodeGraphNode::SetInput(mx::TypedElement* downstreamElement, mx:: return status; } - LOG_ERROR(context, "failed to set %s input for %s: no such interface socket\n", downstreamElement->getName().c_str(), mtlxGraph->getName().c_str()); + LOG_ERROR(context, "failed to set %s input for %s: no such interface socket", downstreamElement->getName().c_str(), mtlxGraph->getName().c_str()); return RPR_ERROR_INVALID_PARAMETER; } @@ -1676,7 +1710,7 @@ rpr_status MtlxNodeGraphNode::SetInput(mx::TypedElement* downstreamElement, mx:: return status; } - LOG_ERROR(context, "failed to set %s input for %s: no such interface socket\n", downstreamElement->getName().c_str(), mtlxGraph->getName().c_str()); + LOG_ERROR(context, "failed to set %s input for %s: no such interface socket", downstreamElement->getName().c_str(), mtlxGraph->getName().c_str()); return RPR_ERROR_INVALID_PARAMETER; } @@ -1774,10 +1808,10 @@ rpr_status RprNode::SetInput(mx::TypedElement* downstreamElement, rpr_material_n auto value = static_cast(mx::fromValueString(valueString)); return rprMaterialNodeSetInputFByKey(rprNode, downstreamRprId, value, value, value, 0.0f); } else { - LOG_ERROR(context, "failed to parse %s value: unsupported type - %s\n", valueString.c_str(), valueType.c_str()); + LOG_WARNING(context, "failed to parse %s value: unsupported type - %s", valueString.c_str(), valueType.c_str()); } } catch (mx::ExceptionTypeError& e) { - LOG_ERROR(context, "failed to parse %s value: %s\n", valueString.c_str(), e.what()); + LOG_ERROR(context, "failed to parse %s value: %s", valueString.c_str(), e.what()); } return RPR_ERROR_INVALID_PARAMETER; @@ -1810,7 +1844,7 @@ RprMappedNode::RprMappedNode(rpr_material_node node, Mtlx2Rpr::Node const* nodeM rpr_status RprMappedNode::SetInput(mx::TypedElement* downstreamElement, mx::Element* upstreamElement, rpr_material_node upstreamRprNode, LoaderContext* context) { auto inputIt = rprNodeMapping->inputs.find(downstreamElement->getName()); if (inputIt == rprNodeMapping->inputs.end()) { - LOG_ERROR(context, "unknown input: %s\n", downstreamElement->getName().c_str()); + LOG_ERROR(context, "unknown input: %s", downstreamElement->getName().c_str()); return RPR_ERROR_INVALID_PARAMETER; } @@ -1841,7 +1875,7 @@ rpr_status RprMappedNode::SetInput(mx::TypedElement* downstreamElement, mx::Elem rpr_status RprMappedNode::SetInput(mx::TypedElement* downstreamElement, mx::ValueElement* valueElement, LoaderContext* context) { auto inputIt = rprNodeMapping->inputs.find(downstreamElement->getName()); if (inputIt == rprNodeMapping->inputs.end()) { - LOG_ERROR(context, "unknown input: %s\n", downstreamElement->getName().c_str()); + LOG_ERROR(context, "unknown input: %s", downstreamElement->getName().c_str()); return RPR_ERROR_INVALID_PARAMETER; } @@ -1956,7 +1990,7 @@ rpr_status RprImageNode::SetInput(mx::TypedElement* downstreamElement, mx::Value } if (status != RPR_SUCCESS) { - LOG_ERROR(context, "Invalid input for image node %s (%s %s): unknown input or invalid type\n", + LOG_ERROR(context, "Invalid input for image node %s (%s %s): unknown input or invalid type", downstreamElement->getName().c_str(), value.c_str(), valueType.c_str()); } return status; @@ -2065,7 +2099,7 @@ rpr_status RprUberNode::SetInput(mx::TypedElement* downstreamElement, mx::ValueE if (status != RPR_SUCCESS && status != RPR_ERROR_UNSUPPORTED) { - LOG_ERROR(context, "Invalid input for uber node %s (%s %s): unknown input or invalid type\n", + LOG_ERROR(context, "Invalid input for uber node %s (%s %s): unknown input or invalid type", downstreamElement->getName().c_str(), value.c_str(), valueType.c_str()); } return status; @@ -2371,7 +2405,7 @@ RPRMtlxLoader::Result RPRMtlxLoader::Load( rpr_material_system rprMatSys) { LoaderContext ctx = {}; - ctx.logEnabled = _loggingEnabled; + ctx.logLevel = _logLevel; ctx.mtlxDocument = mtlxDocument; ctx.rprMatSys = rprMatSys; ctx.searchPath = searchPath; @@ -2515,7 +2549,7 @@ RPRMtlxLoader::Result RPRMtlxLoader::Load( } if (renderableElements.IsEmpty()) { - LOG_ERROR(&ctx, "No renderable elements in %s\n", mtlxDocument->getSourceUri().c_str()); + LOG_ERROR(&ctx, "No renderable elements in %s", mtlxDocument->getSourceUri().c_str()); return {}; } @@ -2575,7 +2609,7 @@ RPRMtlxLoader::Result RPRMtlxLoader::Load( if (!valueStr.empty()) { auto& type = bindInput->getType(); - ctx.Log("Bindinput %s: %s (%s)\n", bindInput->getName().c_str(), valueStr.c_str(), type.c_str()); + LOG(&ctx, "Bindinput %s: %s (%s)", bindInput->getName().c_str(), valueStr.c_str(), type.c_str()); node->SetInput(bindInput.get(), bindInput.get(), &ctx); } diff --git a/deps/rprMtlxLoader/rprMtlxLoader.h b/deps/rprMtlxLoader/rprMtlxLoader.h index c4a09e2ec..8e0359323 100644 --- a/deps/rprMtlxLoader/rprMtlxLoader.h +++ b/deps/rprMtlxLoader/rprMtlxLoader.h @@ -13,7 +13,14 @@ class RPRMtlxLoader { void SetupStdlib(MaterialX::FilePathVec const& libraryNames, MaterialX::FileSearchPath const& searchPath); MaterialX::ConstDocumentPtr GetStdlib() const { return _stdlib; } - void SetLogging(bool enable) { _loggingEnabled = enable; } + enum class LogLevel : int { + None, + Error, + Warning, + Info, + }; + void SetLogging(LogLevel level) { _logLevel = level; } + void SetSceneDistanceUnit(std::string const& unit) { _sceneDistanceUnit = unit; } enum OutputType { @@ -115,7 +122,7 @@ class RPRMtlxLoader { private: MaterialX::DocumentPtr _stdlib; MaterialX::FileSearchPath _stdSearchPath; - bool _loggingEnabled = false; + LogLevel _logLevel = LogLevel::Error; std::string _sceneDistanceUnit = "meter"; }; diff --git a/pxr/imaging/plugin/hdRpr/CMakeLists.txt b/pxr/imaging/plugin/hdRpr/CMakeLists.txt index c3f7d8f09..aae09e236 100644 --- a/pxr/imaging/plugin/hdRpr/CMakeLists.txt +++ b/pxr/imaging/plugin/hdRpr/CMakeLists.txt @@ -98,6 +98,7 @@ pxr_plugin(hdRpr cameraUtil rprUsd json + murmurhash ${RIF_LIBRARY} ${OPENEXR_LIBRARIES} @@ -151,6 +152,10 @@ pxr_plugin(hdRpr ndrParserPlugin.cpp ) +if(RPR_EXR_EXPORT_ENABLED) + target_compile_definitions(hdRpr PRIVATE -DRPR_EXR_EXPORT_ENABLED) +endif() + target_sources(hdRpr PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/notify/message.h) if(APPLE) diff --git a/pxr/imaging/plugin/hdRpr/distantLight.cpp b/pxr/imaging/plugin/hdRpr/distantLight.cpp index 276a9d71c..c5e05058a 100644 --- a/pxr/imaging/plugin/hdRpr/distantLight.cpp +++ b/pxr/imaging/plugin/hdRpr/distantLight.cpp @@ -47,6 +47,17 @@ void HdRprDistantLight::Sync(HdSceneDelegate* sceneDelegate, bool newLight = false; if (bits & HdLight::DirtyParams) { + bool isVisible = sceneDelegate->GetVisible(id); + if (!isVisible) { + if (m_rprLight) { + rprApi->Release(m_rprLight); + m_rprLight = nullptr; + } + + *dirtyBits = HdLight::Clean; + return; + } + float intensity = sceneDelegate->GetLightParamValue(id, HdLightTokens->intensity).Get(); float exposure = sceneDelegate->GetLightParamValue(id, HdLightTokens->exposure).Get(); float computedIntensity = computeLightIntensity(intensity, exposure); diff --git a/pxr/imaging/plugin/hdRpr/domeLight.cpp b/pxr/imaging/plugin/hdRpr/domeLight.cpp index a57bcd31a..967471efd 100644 --- a/pxr/imaging/plugin/hdRpr/domeLight.cpp +++ b/pxr/imaging/plugin/hdRpr/domeLight.cpp @@ -26,6 +26,10 @@ limitations under the License. PXR_NAMESPACE_OPEN_SCOPE +TF_DEFINE_PRIVATE_TOKENS(_tokens, + ((backgroundOverride, "rpr:backgroundOverride")) +); + static void removeFirstSlash(std::string& string) { // Don't need this for *nix/Mac #ifdef _WIN32 @@ -63,14 +67,12 @@ void HdRprDomeLight::Sync(HdSceneDelegate* sceneDelegate, bool isVisible = sceneDelegate->GetVisible(id); if (!isVisible) { - // Invisible light does not produces any emission on a scene. - // So we simply keep light primitive empty in that case. - // We can do it in such a way because Hydra releases light object - // whenever it changed and creates it from scratch *dirtyBits = HdLight::Clean; return; } + VtValue const& backgroundOverride = sceneDelegate->GetLightParamValue(id, _tokens->backgroundOverride); + float intensity = sceneDelegate->GetLightParamValue(id, HdLightTokens->intensity).Get(); float exposure = sceneDelegate->GetLightParamValue(id, HdLightTokens->exposure).Get(); float computedIntensity = computeLightIntensity(intensity, exposure); @@ -100,9 +102,9 @@ void HdRprDomeLight::Sync(HdSceneDelegate* sceneDelegate, color[2] *= temperatureColor[2]; } - m_rprLight = rprApi->CreateEnvironmentLight(color, computedIntensity); + m_rprLight = rprApi->CreateEnvironmentLight(color, computedIntensity, backgroundOverride); } else { - m_rprLight = rprApi->CreateEnvironmentLight(texturePath, computedIntensity); + m_rprLight = rprApi->CreateEnvironmentLight(texturePath, computedIntensity, backgroundOverride); } if (m_rprLight) { diff --git a/pxr/imaging/plugin/hdRpr/light.cpp b/pxr/imaging/plugin/hdRpr/light.cpp index 97aa33829..319918071 100644 --- a/pxr/imaging/plugin/hdRpr/light.cpp +++ b/pxr/imaging/plugin/hdRpr/light.cpp @@ -31,6 +31,16 @@ limitations under the License. PXR_NAMESPACE_OPEN_SCOPE +#if PXR_VERSION >= 2105 +#define USD_LUX_TOKEN_SHAPING_IES_FILE UsdLuxTokens->inputsShapingIesFile +#define USD_LUX_TOKEN_SHAPING_CONE_ANGLE UsdLuxTokens->inputsShapingConeAngle +#define USD_LUX_TOKEN_SHAPING_CONE_SOFTNESS UsdLuxTokens->inputsShapingConeSoftness +#else +#define USD_LUX_TOKEN_SHAPING_IES_FILE UsdLuxTokens->shapingIesFile +#define USD_LUX_TOKEN_SHAPING_CONE_ANGLE UsdLuxTokens->shapingConeAngle +#define USD_LUX_TOKEN_SHAPING_CONE_SOFTNESS UsdLuxTokens->shapingConeSoftness +#endif + namespace { float GetDiskLightNormalization(GfMatrix4f const& transform, float radius) { @@ -387,7 +397,11 @@ void HdRprLight::Sync(HdSceneDelegate* sceneDelegate, HdDirtyBits bits = *dirtyBits; if (bits & DirtyBits::DirtyTransform) { +#if PXR_VERSION >= 2011 + m_transform = GfMatrix4f(sceneDelegate->GetTransform(id)); +#else m_transform = GfMatrix4f(sceneDelegate->GetLightParamValue(id, HdTokens->transform).Get()); +#endif } if (bits & DirtyParams) { @@ -396,16 +410,12 @@ void HdRprLight::Sync(HdSceneDelegate* sceneDelegate, bool isVisible = sceneDelegate->GetVisible(id); if (!isVisible) { - // Invisible light does not produces any emission on a scene. - // So we simply keep light primitive empty in that case. - // We can do it in such a way because Hydra releases light object - // whenever it changed and creates it from scratch *dirtyBits = DirtyBits::Clean; return; } bool newLight = false; - auto iesFile = sceneDelegate->GetLightParamValue(id, UsdLuxTokens->shapingIesFile); + auto iesFile = sceneDelegate->GetLightParamValue(id, USD_LUX_TOKEN_SHAPING_IES_FILE); if (iesFile.IsHolding()) { auto& path = iesFile.UncheckedGet(); if (!path.GetResolvedPath().empty()) { @@ -415,8 +425,8 @@ void HdRprLight::Sync(HdSceneDelegate* sceneDelegate, } } } else { - auto coneAngle = sceneDelegate->GetLightParamValue(id, UsdLuxTokens->shapingConeAngle); - auto coneSoftness = sceneDelegate->GetLightParamValue(id, UsdLuxTokens->shapingConeSoftness); + auto coneAngle = sceneDelegate->GetLightParamValue(id, USD_LUX_TOKEN_SHAPING_CONE_ANGLE); + auto coneSoftness = sceneDelegate->GetLightParamValue(id, USD_LUX_TOKEN_SHAPING_CONE_SOFTNESS); if (coneAngle.IsHolding() && coneSoftness.IsHolding()) { if (auto light = rprApi->CreateSpotLight(coneAngle.UncheckedGet(), coneSoftness.UncheckedGet())) { m_light = light; diff --git a/pxr/imaging/plugin/hdRpr/material.cpp b/pxr/imaging/plugin/hdRpr/material.cpp index 3e20c7592..3b0c01264 100644 --- a/pxr/imaging/plugin/hdRpr/material.cpp +++ b/pxr/imaging/plugin/hdRpr/material.cpp @@ -41,7 +41,7 @@ void HdRprMaterial::Sync(HdSceneDelegate* sceneDelegate, VtValue vtMat = sceneDelegate->GetMaterialResource(GetId()); if (vtMat.IsHolding()) { auto& networkMap = vtMat.UncheckedGet(); - m_rprMaterial = rprApi->CreateMaterial(sceneDelegate, networkMap); + m_rprMaterial = rprApi->CreateMaterial(GetId(), sceneDelegate, networkMap); } if (!m_rprMaterial) { @@ -69,7 +69,7 @@ void HdRprMaterial::Sync(HdSceneDelegate* sceneDelegate, networkMap.map[HdMaterialTerminalTokens->displacement] = network; networkMap.terminals.push_back(mtlxNode.path); - m_rprMaterial = rprApi->CreateMaterial(sceneDelegate, networkMap); + m_rprMaterial = rprApi->CreateMaterial(GetId(), sceneDelegate, networkMap); } } } diff --git a/pxr/imaging/plugin/hdRpr/mesh.cpp b/pxr/imaging/plugin/hdRpr/mesh.cpp index d399a3a9d..8a3f81152 100644 --- a/pxr/imaging/plugin/hdRpr/mesh.cpp +++ b/pxr/imaging/plugin/hdRpr/mesh.cpp @@ -73,43 +73,6 @@ void HdRprMesh::_InitRepr(TfToken const& reprName, // No-op } -template -bool HdRprMesh::GetPrimvarData(TfToken const& name, - HdSceneDelegate* sceneDelegate, - std::map const& primvarDescsPerInterpolation, - VtArray& out_data, - VtIntArray& out_indices) { - out_data.clear(); - out_indices.clear(); - - for (auto& primvarDescsEntry : primvarDescsPerInterpolation) { - for (auto& pv : primvarDescsEntry.second) { - if (pv.name == name) { - auto value = GetPrimvar(sceneDelegate, name); - if (value.IsHolding>()) { - out_data = value.UncheckedGet>(); - if (primvarDescsEntry.first == HdInterpolationFaceVarying) { - out_indices.reserve(m_faceVertexIndices.size()); - for (int i = 0; i < m_faceVertexIndices.size(); ++i) { - out_indices.push_back(i); - } - } else if (primvarDescsEntry.first == HdInterpolationConstant) { - out_indices = VtIntArray(m_faceVertexIndices.size(), 0); - } - return true; - } - - TF_RUNTIME_ERROR("Failed to load %s primvar data: unexpected underlying type - %s", name.GetText(), value.GetTypeName().c_str()); - return false; - } - } - } - - return false; -} -template bool HdRprMesh::GetPrimvarData(TfToken const&, HdSceneDelegate*, std::map const&, VtArray&, VtIntArray&); -template bool HdRprMesh::GetPrimvarData(TfToken const&, HdSceneDelegate*, std::map const&, VtArray&, VtIntArray&); - RprUsdMaterial const* HdRprMesh::GetFallbackMaterial( HdSceneDelegate* sceneDelegate, HdRprApi* rprApi, @@ -140,10 +103,7 @@ RprUsdMaterial const* HdRprMesh::GetFallbackMaterial( } m_fallbackMaterial = rprApi->CreateDiffuseMaterial(color); - - if (RprUsdIsLeakCheckEnabled()) { - rprApi->SetName(m_fallbackMaterial, GetId().GetText()); - } + rprApi->SetName(m_fallbackMaterial, GetId().GetText()); } return m_fallbackMaterial; @@ -166,6 +126,56 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, bool newMesh = false; + std::map primvarDescsPerInterpolation; + + bool isRefineLevelDirty = false; + if (*dirtyBits & HdChangeTracker::DirtyDisplayStyle) { + m_displayStyle = sceneDelegate->GetDisplayStyle(id); + if (m_refineLevel != m_displayStyle.refineLevel) { + isRefineLevelDirty = true; + m_refineLevel = m_displayStyle.refineLevel; + } + } + + bool isIgnoreContourDirty = false; + bool isVisibilityMaskDirty = false; + bool isIdDirty = false; + if (*dirtyBits & HdChangeTracker::DirtyPrimvar) { + HdRprGeometrySettings geomSettings = {}; + geomSettings.visibilityMask = kVisibleAll; + HdRprFillPrimvarDescsPerInterpolation(sceneDelegate, id, &primvarDescsPerInterpolation); + HdRprParseGeometrySettings(sceneDelegate, id, primvarDescsPerInterpolation, &geomSettings); + + if (m_refineLevel != geomSettings.subdivisionLevel) { + m_refineLevel = geomSettings.subdivisionLevel; + isRefineLevelDirty = true; + } + + if (m_visibilityMask != geomSettings.visibilityMask) { + 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; + } + + if (m_cryptomatteName != geomSettings.cryptomatteName) { + m_cryptomatteName = geomSettings.cryptomatteName; + } + + if (m_numGeometrySamples != geomSettings.numGeometrySamples) { + m_numGeometrySamples = geomSettings.numGeometrySamples; + *dirtyBits |= HdChangeTracker::DirtyPoints | HdChangeTracker::DirtyNormals; + } + } + bool pointsIsComputed = false; auto extComputationDescs = sceneDelegate->GetExtComputationPrimvarDescriptors(id, HdInterpolationVertex); for (auto& desc : extComputationDescs) { @@ -174,15 +184,47 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, desc.name)) { + m_pointSamples.clear(); + +#if PXR_VERSION >= 2105 + HdExtComputationUtils::SampledValueStore<2> valueStore; + HdExtComputationUtils::SampleComputedPrimvarValues({desc}, sceneDelegate, m_numGeometrySamples, &valueStore); + auto pointValueIt = valueStore.find(desc.name); + if (pointValueIt != valueStore.end()) { + auto& sampleValues = pointValueIt->second.values; + VtArray newPointSamples; + newPointSamples.reserve(sampleValues.size()); + for (auto& sampleValue : sampleValues) { + if (sampleValue.IsHolding()) { + newPointSamples.push_back(sampleValue.UncheckedGet()); + } else { + newPointSamples.clear(); + break; + } + } + + if (!newPointSamples.empty()) { + m_pointSamples = std::move(newPointSamples); + m_normalsValid = false; + pointsIsComputed = true; + + newMesh = true; + } + } +#else // PXR_VERSION < 2105 + if (m_numGeometrySamples != 1) { + TF_WARN("UsdSkel deformation motion blur is supported only in USD 21.05+ (current version %d.%d)", PXR_MINOR_VERSION, PXR_PATCH_VERSION); + } auto valueStore = HdExtComputationUtils::GetComputedPrimvarValues({desc}, sceneDelegate); auto pointValueIt = valueStore.find(desc.name); if (pointValueIt != valueStore.end()) { - m_points = pointValueIt->second.Get(); + m_pointSamples = {pointValueIt->second.Get()}; m_normalsValid = false; pointsIsComputed = true; newMesh = true; } +#endif // PXR_VERSION >= 2105 } break; @@ -190,10 +232,11 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, if (!pointsIsComputed && HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->points)) { - VtValue pointsValue = sceneDelegate->Get(id, HdTokens->points); - m_points = pointsValue.Get(); - m_normalsValid = false; + if (!HdRprSamplePrimvar(id, HdTokens->points, sceneDelegate, m_numGeometrySamples, &m_pointSamples)) { + m_pointSamples.clear(); + } + m_normalsValid = false; newMesh = true; } @@ -282,11 +325,16 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, newMesh = true; } - std::map primvarDescsPerInterpolation; - if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, HdTokens->normals)) { HdRprFillPrimvarDescsPerInterpolation(sceneDelegate, id, &primvarDescsPerInterpolation); - m_authoredNormals = GetPrimvarData(HdTokens->normals, sceneDelegate, primvarDescsPerInterpolation, m_normals, m_normalIndices); + HdInterpolation interpolation; + m_authoredNormals = HdRprSamplePrimvar(id, HdTokens->normals, sceneDelegate, primvarDescsPerInterpolation, m_numGeometrySamples, &m_normalSamples, &interpolation); + if (m_authoredNormals) { + HdRprGetPrimvarIndices(interpolation, m_faceVertexIndices, &m_normalIndices); + } else { + m_normalSamples.clear(); + m_normalIndices.clear(); + } newMesh = true; } @@ -326,7 +374,14 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, *uvPrimvarName)) { HdRprFillPrimvarDescsPerInterpolation(sceneDelegate, id, &primvarDescsPerInterpolation); - GetPrimvarData(*uvPrimvarName, sceneDelegate, primvarDescsPerInterpolation, m_uvs, m_uvIndices); + + HdInterpolation interpolation; + if (HdRprSamplePrimvar(id, *uvPrimvarName, sceneDelegate, primvarDescsPerInterpolation, m_numGeometrySamples, &m_uvSamples, &interpolation)) { + HdRprGetPrimvarIndices(interpolation, m_faceVertexIndices, &m_uvIndices); + } else { + m_uvSamples.clear(); + m_uvIndices.clear(); + } newMesh = true; } @@ -339,45 +394,6 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, //////////////////////////////////////////////////////////////////////// // 2. Resolve drawstyles - bool isRefineLevelDirty = false; - if (*dirtyBits & HdChangeTracker::DirtyDisplayStyle) { - m_displayStyle = sceneDelegate->GetDisplayStyle(id); - if (m_refineLevel != m_displayStyle.refineLevel) { - isRefineLevelDirty = true; - m_refineLevel = m_displayStyle.refineLevel; - } - } - - bool isIgnoreContourDirty = false; - bool isVisibilityMaskDirty = false; - bool isIdDirty = false; - if (*dirtyBits & HdChangeTracker::DirtyPrimvar) { - HdRprGeometrySettings geomSettings = {}; - geomSettings.visibilityMask = kVisibleAll; - HdRprFillPrimvarDescsPerInterpolation(sceneDelegate, id, &primvarDescsPerInterpolation); - HdRprParseGeometrySettings(sceneDelegate, id, primvarDescsPerInterpolation, &geomSettings); - - if (m_refineLevel != geomSettings.subdivisionLevel) { - m_refineLevel = geomSettings.subdivisionLevel; - isRefineLevelDirty = true; - } - - if (m_visibilityMask != geomSettings.visibilityMask) { - 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; // Don't compute smooth normals on a refined mesh. They are implicitly smooth. if (m_enableSubdiv && m_refineLevel != 0) { @@ -392,7 +408,10 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } if (!m_normalsValid) { - m_normals = Hd_SmoothNormals::ComputeSmoothNormals(&m_adjacency, m_points.size(), m_points.cdata()); + m_normalSamples.clear(); + for (auto& points : m_pointSamples) { + m_normalSamples.push_back(Hd_SmoothNormals::ComputeSmoothNormals(&m_adjacency, points.size(), points.cdata())); + } m_normalsValid = true; newMesh = true; @@ -421,7 +440,7 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, m_rprMeshes.clear(); 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())) { + if (auto rprMesh = rprApi->CreateMesh(m_pointSamples, m_faceVertexIndices, m_normalSamples, m_normalIndices, m_uvSamples, m_uvIndices, m_faceVertexCounts, m_topology.GetOrientation())) { m_rprMeshes.push_back(rprMesh); } } else { @@ -447,24 +466,24 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, continue; } - VtVec3fArray subsetPoints; - VtVec3fArray subsetNormals; - VtVec2fArray subsetUv; + VtArray subsetPointSamples(m_pointSamples.size()); + VtArray subsetNormalSamples(m_normalSamples.size()); + VtArray subsetUvSamples(m_uvSamples.size()); VtIntArray subsetNormalIndices; VtIntArray subsetUvIndices; VtIntArray subsetIndexes; VtIntArray subsetVertexPerFace; subsetVertexPerFace.reserve(subset.indices.size()); - vertexIndexRemapping.reserve(m_points.size()); - std::fill(vertexIndexRemapping.begin(), vertexIndexRemapping.begin() + m_points.size(), -1); + vertexIndexRemapping.reserve(m_pointSamples.front().size()); + std::fill(vertexIndexRemapping.begin(), vertexIndexRemapping.begin() + m_pointSamples.front().size(), -1); if (!m_normalIndices.empty()) { - normalIndexRemapping.reserve(m_normals.size()); - std::fill(normalIndexRemapping.begin(), normalIndexRemapping.begin() + m_normals.size(), -1); + normalIndexRemapping.reserve(m_normalSamples.front().size()); + std::fill(normalIndexRemapping.begin(), normalIndexRemapping.begin() + m_normalSamples.front().size(), -1); } if (!m_uvIndices.empty()) { - uvIndexRemapping.reserve(m_uvs.size()); - std::fill(uvIndexRemapping.begin(), uvIndexRemapping.begin() + m_uvs.size(), -1); + uvIndexRemapping.reserve(m_uvSamples.front().size()); + std::fill(uvIndexRemapping.begin(), uvIndexRemapping.begin() + m_uvSamples.front().size(), -1); } for (auto faceIndex : subset.indices) { @@ -479,44 +498,54 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, bool newPoint = subsetPointIndex == -1; if (newPoint) { - subsetPointIndex = static_cast(subsetPoints.size()); + subsetPointIndex = static_cast(subsetPointSamples.front().size()); vertexIndexRemapping[pointIndex] = subsetPointIndex; - subsetPoints.push_back(m_points[pointIndex]); + for (int sampleIndex = 0; sampleIndex < m_pointSamples.size(); ++sampleIndex) { + subsetPointSamples[sampleIndex].push_back(m_pointSamples[sampleIndex][pointIndex]); + } } subsetIndexes.push_back(subsetPointIndex); - if (!m_normals.empty()) { + if (!m_normalSamples.empty()) { if (m_normalIndices.empty()) { if (newPoint) { - subsetNormals.push_back(m_normals[pointIndex]); + for (int sampleIndex = 0; sampleIndex < m_normalSamples.size(); ++sampleIndex) { + subsetNormalSamples[sampleIndex].push_back(m_normalSamples[sampleIndex][pointIndex]); + } } } else { const int normalIndex = m_normalIndices[faceIndexesOffset + i]; int subsetNormalIndex = normalIndexRemapping[normalIndex]; if (subsetNormalIndex == -1) { - subsetNormalIndex = static_cast(subsetNormals.size()); + subsetNormalIndex = static_cast(subsetNormalSamples.front().size()); normalIndexRemapping[normalIndex] = subsetNormalIndex; - subsetNormals.push_back(m_normals[normalIndex]); + for (int sampleIndex = 0; sampleIndex < m_normalSamples.size(); ++sampleIndex) { + subsetNormalSamples[sampleIndex].push_back(m_normalSamples[sampleIndex][normalIndex]); + } } subsetNormalIndices.push_back(subsetNormalIndex); } } - if (!m_uvs.empty()) { + if (!m_uvSamples.empty()) { if (m_uvIndices.empty()) { if (newPoint) { - subsetUv.push_back(m_uvs[pointIndex]); + for (int sampleIndex = 0; sampleIndex < m_uvSamples.size(); ++sampleIndex) { + subsetUvSamples[sampleIndex].push_back(m_uvSamples[sampleIndex][pointIndex]); + } } } else { const int uvIndex = m_uvIndices[faceIndexesOffset + i]; int subsetuvIndex = uvIndexRemapping[uvIndex]; if (subsetuvIndex == -1) { - subsetuvIndex = static_cast(subsetUv.size()); + subsetuvIndex = static_cast(subsetUvSamples.front().size()); uvIndexRemapping[uvIndex] = subsetuvIndex; - subsetUv.push_back(m_uvs[uvIndex]); + for (int sampleIndex = 0; sampleIndex < m_uvSamples.size(); ++sampleIndex) { + subsetUvSamples[sampleIndex].push_back(m_uvSamples[sampleIndex][uvIndex]); + } } subsetUvIndices.push_back(subsetuvIndex); } @@ -524,7 +553,7 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } } - if (auto rprMesh = rprApi->CreateMesh(subsetPoints, subsetIndexes, subsetNormals, subsetNormalIndices, subsetUv, subsetUvIndices, subsetVertexPerFace, m_topology.GetOrientation())) { + if (auto rprMesh = rprApi->CreateMesh(subsetPointSamples, subsetIndexes, subsetNormalSamples, subsetNormalIndices, subsetUvSamples, subsetUvIndices, subsetVertexPerFace, m_topology.GetOrientation())) { m_rprMeshes.push_back(rprMesh); ++it; } else { @@ -535,11 +564,9 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } if (!m_rprMeshes.empty()) { - if (newMesh && RprUsdIsLeakCheckEnabled()) { - auto name = id.GetText(); - for (auto& rprMesh : m_rprMeshes) { - rprApi->SetName(rprMesh, name); - } + auto name = m_cryptomatteName.empty() ? id.GetText() : m_cryptomatteName.c_str(); + for (auto& rprMesh : m_rprMeshes) { + rprApi->SetName(rprMesh, name); } if (newMesh || (*dirtyBits & HdChangeTracker::DirtySubdivTags)) { diff --git a/pxr/imaging/plugin/hdRpr/mesh.h b/pxr/imaging/plugin/hdRpr/mesh.h index 3513525d1..51b9b268a 100644 --- a/pxr/imaging/plugin/hdRpr/mesh.h +++ b/pxr/imaging/plugin/hdRpr/mesh.h @@ -51,13 +51,6 @@ class HdRprMesh final : public HdRprBaseRprim { void _InitRepr(TfToken const& reprName, HdDirtyBits* dirtyBits) override; private: - template - bool GetPrimvarData(TfToken const& name, - HdSceneDelegate* sceneDelegate, - std::map const& primvarDescsPerInterpolation, - VtArray& out_data, - VtIntArray& out_indices); - RprUsdMaterial const* GetFallbackMaterial( HdSceneDelegate* sceneDelegate, HdRprApi* rprApi, @@ -74,7 +67,7 @@ class HdRprMesh final : public HdRprBaseRprim { HdMeshTopology m_topology; HdGeomSubsets m_geomSubsets; - VtVec3fArray m_points; + VtArray m_pointSamples; VtIntArray m_faceVertexCounts; VtIntArray m_faceVertexIndices; bool m_enableSubdiv = false; @@ -82,13 +75,13 @@ class HdRprMesh final : public HdRprBaseRprim { Hd_VertexAdjacency m_adjacency; bool m_adjacencyValid = false; - VtVec3fArray m_normals; + VtArray m_normalSamples; VtIntArray m_normalIndices; bool m_normalsValid = false; bool m_authoredNormals = false; bool m_smoothNormals = false; - VtVec2fArray m_uvs; + VtArray m_uvSamples; VtIntArray m_uvIndices; HdDisplayStyle m_displayStyle; @@ -96,6 +89,8 @@ class HdRprMesh final : public HdRprBaseRprim { int m_id = -1; bool m_ignoreContour; + std::string m_cryptomatteName; + size_t m_numGeometrySamples = 1; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/primvarUtil.cpp b/pxr/imaging/plugin/hdRpr/primvarUtil.cpp index b8b3f28f5..3f8ad890f 100644 --- a/pxr/imaging/plugin/hdRpr/primvarUtil.cpp +++ b/pxr/imaging/plugin/hdRpr/primvarUtil.cpp @@ -20,6 +20,8 @@ TF_DEFINE_PRIVATE_TOKENS(HdRprGeometryPrimvarTokens, ((id, "rpr:id")) ((subdivisionLevel, "rpr:subdivisionLevel")) ((ignoreContour, "rpr:ignoreContour")) + ((cryptomatteName, "rpr:cryptomatteName")) + ((geomSamples, "rpr:geomSamples")) ((visibilityPrimary, "rpr:visibilityPrimary")) ((visibilityShadow, "rpr:visibilityShadow")) ((visibilityReflection, "rpr:visibilityReflection")) @@ -60,6 +62,11 @@ void HdRprParseGeometrySettings( } } else if (desc.name == HdRprGeometryPrimvarTokens->ignoreContour) { HdRprGetConstantPrimvar(HdRprGeometryPrimvarTokens->ignoreContour, sceneDelegate, id, &geomSettings->ignoreContour); + } else if (desc.name == HdRprGeometryPrimvarTokens->cryptomatteName) { + HdRprGetConstantPrimvar(HdRprGeometryPrimvarTokens->cryptomatteName, sceneDelegate, id, &geomSettings->cryptomatteName); + } else if (desc.name == HdRprGeometryPrimvarTokens->geomSamples) { + HdRprGetConstantPrimvar(HdRprGeometryPrimvarTokens->geomSamples, sceneDelegate, id, &geomSettings->numGeometrySamples); + geomSettings->numGeometrySamples = std::max(1, geomSettings->numGeometrySamples); } 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 5eaba948d..c3ba792b4 100644 --- a/pxr/imaging/plugin/hdRpr/primvarUtil.h +++ b/pxr/imaging/plugin/hdRpr/primvarUtil.h @@ -40,6 +40,8 @@ struct HdRprGeometrySettings { int subdivisionLevel = 0; uint32_t visibilityMask = 0; bool ignoreContour = false; + std::string cryptomatteName; + int numGeometrySamples = 1; }; void HdRprParseGeometrySettings( @@ -72,6 +74,99 @@ bool HdRprGetConstantPrimvar(TfToken const& name, HdSceneDelegate* sceneDelegate return false; } +template +bool HdRprSamplePrimvar( + SdfPath const& id, + TfToken const& key, + HdSceneDelegate* sceneDelegate, + size_t maxSampleCount, + VtArray* sampleValuesPtr) { + std::vector sampleTimes(maxSampleCount); + std::vector sampleVtValues(maxSampleCount); + + size_t authoredSampleCount = sceneDelegate->SamplePrimvar(id, key, maxSampleCount, sampleTimes.data(), sampleVtValues.data()); + if (!authoredSampleCount) { + return false; + } + + if (authoredSampleCount < maxSampleCount) { + sampleTimes.resize(authoredSampleCount); + sampleVtValues.resize(authoredSampleCount); + } + + if (sampleTimes.size() > 1) { + float baselineTimeStep = sampleTimes[1] - sampleTimes[0]; + for (size_t i = 1; i < sampleTimes.size() - 1; ++i) { + float timeStep = sampleTimes[i + 1] - sampleTimes[i]; + if (std::abs(baselineTimeStep - timeStep) > 1e-6f) { + // Definitely an issue but we can at least use such data with the current API, so just log a warning + TF_WARN("[%s] RPR does not support non-linear in time sub-frame primvar samples", id.GetText()); + break; + } + } + } + + size_t baselineSize = 0; + + auto& sampleValues = *sampleValuesPtr; + sampleValues.resize(sampleVtValues.size()); + for (size_t i = 0; i < sampleVtValues.size(); ++i) { + if (sampleVtValues[i].IsHolding()) { + sampleValues[i] = sampleVtValues[i].UncheckedGet(); + + if (i == 0) { + baselineSize = sampleValues[i].size(); + } else if (baselineSize != sampleValues[i].size()) { + TF_RUNTIME_ERROR("[%s] RPR does not support non-uniform sub-frame samples - %s", id.GetText(), key.GetText()); + return false; + } + } else { + TF_RUNTIME_ERROR("[%s] Failed to sample %s primvar data: unexpected underlying type - %s", id.GetText(), key.GetText(), sampleVtValues[i].GetTypeName().c_str()); + return false; + } + } + + return true; +} + +template +bool HdRprSamplePrimvar( + SdfPath const& id, + TfToken const& key, + HdSceneDelegate* sceneDelegate, + std::map const& primvarDescsPerInterpolation, + size_t maxSampleCount, + VtArray* sampleValues, + HdInterpolation* interpolation) { + + for (auto& primvarDescsEntry : primvarDescsPerInterpolation) { + for (auto& pv : primvarDescsEntry.second) { + if (pv.name == key) { + if (!HdRprSamplePrimvar(id, key, sceneDelegate, maxSampleCount, sampleValues)) { + return false; + } + + *interpolation = primvarDescsEntry.first; + return true; + } + } + } + + return false; +} + +inline void HdRprGetPrimvarIndices(HdInterpolation interpolation, VtIntArray const& faceIndices, VtIntArray* out_indices) { + out_indices->clear(); + if (interpolation == HdInterpolationFaceVarying) { + out_indices->reserve(faceIndices.size()); + for (int i = 0; i < faceIndices.size(); ++i) { + out_indices->push_back(i); + } + } else if (interpolation == HdInterpolationConstant) { + *out_indices = VtIntArray(faceIndices.size(), 0); + } +} + PXR_NAMESPACE_CLOSE_SCOPE #endif // HDRPR_PRIMVAR_UTIL_H diff --git a/pxr/imaging/plugin/hdRpr/python/generateGeometrySettingFiles.py b/pxr/imaging/plugin/hdRpr/python/generateGeometrySettingFiles.py index 2234374b7..9ce5b174f 100644 --- a/pxr/imaging/plugin/hdRpr/python/generateGeometrySettingFiles.py +++ b/pxr/imaging/plugin/hdRpr/python/generateGeometrySettingFiles.py @@ -38,6 +38,21 @@ 'defaultValue': False, 'help': 'Whether to extract contour for a mesh or not' }, + { + 'name': 'primvars:rpr:cryptomatteName', + 'ui_name': 'Cryptomatte Name', + 'defaultValue': '', + 'c_type': 'std::string', + 'help': 'String used to generate cryptomatte ID. If not specified, the path to a primitive used.' + }, + { + 'name': 'primvars:rpr:geomSamples', + 'ui_name': 'Geometry Time Samples', + 'defaultValue': 1, + 'minValue': 1, + 'maxValue': 1 ** 16, + 'help': 'The number of sub-frame samples to compute when rendering deformation motion blur over the shutter open time. The default is 1 (sample only at the start of the shutter time), giving no deformation blur by default. If you want rapidly deforming geometry to blur properly, you must increase this value to 2 or more. Note that this value is limited by the number of sub-samples available in the USD file being rendered.' + }, { 'folder': 'Visibility Settings', 'settings': visibility_flag_settings diff --git a/pxr/imaging/plugin/hdRpr/python/generateRenderSettingFiles.py b/pxr/imaging/plugin/hdRpr/python/generateRenderSettingFiles.py index c930c8952..1188087e5 100644 --- a/pxr/imaging/plugin/hdRpr/python/generateRenderSettingFiles.py +++ b/pxr/imaging/plugin/hdRpr/python/generateRenderSettingFiles.py @@ -78,7 +78,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'name': 'RenderMode', 'settings': [ { - 'name': 'renderMode', + 'name': 'coreRenderMode', 'ui_name': 'Render Mode', 'defaultValue': 'Global Illumination', 'values': [ @@ -101,13 +101,13 @@ def hidewhen_not_tahoe(render_setting_categories): 'minValue': 0.0, 'maxValue': 100.0, 'houdini': { - 'hidewhen': 'renderMode != "AmbientOcclusion"' + 'hidewhen': 'coreRenderMode != "AmbientOcclusion"' } }, { 'folder': 'Contour Settings', 'houdini': { - 'hidewhen': 'renderMode != "Contour"' + 'hidewhen': 'coreRenderMode != "Contour"' }, 'settings': [ { @@ -117,7 +117,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'minValue': 0.0, 'maxValue': 1.0, 'houdini': { - 'hidewhen': 'renderMode != "Contour"' + 'hidewhen': 'coreRenderMode != "Contour"' } }, { @@ -126,7 +126,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'defaultValue': True, 'help': 'Whether to use geometry normals for edge detection or not', 'houdini': { - 'hidewhen': 'renderMode != "Contour"' + 'hidewhen': 'coreRenderMode != "Contour"' } }, { @@ -137,7 +137,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'maxValue': 100.0, 'help': 'Linewidth of edges detected via normals', 'houdini': { - 'hidewhen': ['renderMode != "Contour"', 'contourUseNormal == 0'] + 'hidewhen': ['coreRenderMode != "Contour"', 'contourUseNormal == 0'] } }, { @@ -147,7 +147,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'minValue': 0.0, 'maxValue': 180.0, 'houdini': { - 'hidewhen': ['renderMode != "Contour"', 'contourUseNormal == 0'] + 'hidewhen': ['coreRenderMode != "Contour"', 'contourUseNormal == 0'] } }, { @@ -156,7 +156,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'defaultValue': True, 'help': 'Whether to use primitive Id for edge detection or not', 'houdini': { - 'hidewhen': 'renderMode != "Contour"' + 'hidewhen': 'coreRenderMode != "Contour"' } }, { @@ -167,7 +167,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'maxValue': 100.0, 'help': 'Linewidth of edges detected via primitive Id', 'houdini': { - 'hidewhen': ['renderMode != "Contour"', 'contourUsePrimId == 0'] + 'hidewhen': ['coreRenderMode != "Contour"', 'contourUsePrimId == 0'] } }, { @@ -176,7 +176,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'defaultValue': True, 'help': 'Whether to use material Id for edge detection or not', 'houdini': { - 'hidewhen': 'renderMode != "Contour"' + 'hidewhen': 'coreRenderMode != "Contour"' } }, { @@ -187,7 +187,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'maxValue': 100.0, 'help': 'Linewidth of edges detected via material Id', 'houdini': { - 'hidewhen': ['renderMode != "Contour"', 'contourUseMaterialId == 0'] + 'hidewhen': ['coreRenderMode != "Contour"', 'contourUseMaterialId == 0'] } }, { @@ -204,7 +204,7 @@ def hidewhen_not_tahoe(render_setting_categories): ' * cyan - material Id + normal\\n' ' * black - all', 'houdini': { - 'hidewhen': 'renderMode != "Contour"' + 'hidewhen': 'coreRenderMode != "Contour"' } } ] @@ -241,7 +241,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'settings': [ { 'name': 'enableDenoising', - 'ui_name': 'Enable Denoising', + 'ui_name': 'Enable AI Denoising', 'defaultValue': False, 'houdini': { 'custom_tags': [ @@ -283,7 +283,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'settings': [ { 'name': 'maxSamples', - 'ui_name': 'Max Pixel Samples', + 'ui_name': 'Max Samples', 'help': 'Maximum number of samples to render for each pixel.', 'defaultValue': 256, 'minValue': 1, @@ -299,7 +299,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'settings': [ { 'name': 'minAdaptiveSamples', - 'ui_name': 'Min Pixel Samples', + 'ui_name': 'Min Samples', 'help': 'Minimum number of samples to render for each pixel. After this, adaptive sampling will stop sampling pixels where noise is less than \'Variance Threshold\'.', 'defaultValue': 64, 'minValue': 1, @@ -307,7 +307,7 @@ def hidewhen_not_tahoe(render_setting_categories): }, { 'name': 'varianceThreshold', - 'ui_name': 'Variance Threshold', + 'ui_name': 'Noise Threshold', 'help': 'Cutoff for adaptive sampling. Once pixels are below this amount of noise, no more samples are added. Set to 0 for no cutoff.', 'defaultValue': 0.0, 'minValue': 0.0, @@ -373,18 +373,18 @@ def hidewhen_not_tahoe(render_setting_categories): 'name': 'raycastEpsilon', 'ui_name': 'Ray Cast Epsilon', 'help': 'Determines an offset used to move light rays away from the geometry for ray-surface intersection calculations.', - 'defaultValue': 2e-5, + 'defaultValue': 2e-3, 'minValue': 1e-6, 'maxValue': 1.0 }, { 'name': 'enableRadianceClamping', - 'ui_name': 'Enable Clamp Radiance', + 'ui_name': 'Clamp Fireflies', 'defaultValue': False, }, { 'name': 'radianceClamping', - 'ui_name': 'Clamp Radiance', + 'ui_name': 'Max Radiance', 'help': 'Limits the intensity, or the maximum brightness, of samples in the scene. Greater clamp radiance values produce more brightness.', 'defaultValue': 0.0, 'minValue': 0.0, @@ -439,7 +439,7 @@ def hidewhen_not_tahoe(render_setting_categories): }, { 'name': 'tonemapExposureTime', - 'ui_name': 'Tone Mapping Exposure Time', + 'ui_name': 'Film Exposure Time (sec)', 'help': 'Film exposure time', 'defaultValue': 0.125, 'minValue': 0.0, @@ -450,7 +450,7 @@ def hidewhen_not_tahoe(render_setting_categories): }, { 'name': 'tonemapSensitivity', - 'ui_name': 'Tone Mapping Sensitivity', + 'ui_name': 'Film Sensitivity', 'help': 'Luminance of the scene (in candela per m^2)', 'defaultValue': 1.0, 'minValue': 0.0, @@ -461,7 +461,7 @@ def hidewhen_not_tahoe(render_setting_categories): }, { 'name': 'tonemapFstop', - 'ui_name': 'Tone Mapping Fstop', + 'ui_name': 'Fstop', 'help': 'Aperture f-number', 'defaultValue': 1.0, 'minValue': 0.0, @@ -498,7 +498,7 @@ def hidewhen_not_tahoe(render_setting_categories): 'settings': [ { 'name': 'enableBeautyMotionBlur', - 'ui_name': 'Enable Beaty Motion Blur', + 'ui_name': 'Enable Beauty Motion Blur', 'defaultValue': True, 'help': 'If disabled, only velocity AOV will store information about movement on the scene. Required for motion blur that is generated in post-processing.', 'houdini': { @@ -545,6 +545,47 @@ def hidewhen_not_tahoe(render_setting_categories): } ] }, + { + 'name': 'Cryptomatte', + 'settings': [ + { + 'name': 'cryptomatteOutputPath', + 'ui_name': 'Cryptomatte Output Path', + 'defaultValue': '', + 'c_type': 'std::string', + 'help': 'Controls where cryptomatte should be saved. Use \'Cryptomatte Output Mode\' to control when cryptomatte is saved.', + 'houdini': { + 'type': 'file' + } + }, + { + 'name': 'cryptomatteOutputMode', + 'ui_name': 'Cryptomatte Output Mode', + 'defaultValue': 'Batch', + 'values': [ + SettingValue('Batch'), + SettingValue('Interactive') + ], + 'help': 'Batch - save cryptomatte only in the batch rendering mode (USD Render ROP, husk). Interactive - same as the Batch but also save cryptomatte in the non-batch rendering mode. Cryptomatte always saved after \'Max Samples\' is reached.', + 'houdini': { + 'hidewhen': 'cryptomatteOutputPath == ""', + } + }, + { + 'name': 'cryptomattePreviewLayer', + 'ui_name': 'Cryptomatte Add Preview Layer', + 'defaultValue': False, + 'help': 'Whether to generate cryptomatte preview layer or not. Whether you need it depends on the software you are planning to use cryptomatte in. For example, Houdini\'s COP Cryptomatte requires it, Nuke, on contrary, does not.', + 'houdini': { + 'hidewhen': 'cryptomatteOutputPath == ""', + } + + } + ], + 'houdini': { + 'hidewhen': hidewhen_not_northstar + } + }, { 'name': 'UsdNativeCamera', 'settings': [ @@ -575,6 +616,23 @@ def hidewhen_not_tahoe(render_setting_categories): 'defaultValue': False } ] + }, + { + 'name': 'Session', + 'settings': [ + { + 'name': 'renderMode', + 'defaultValue': 'interactive', + 'values': [ + SettingValue('batch'), + SettingValue('interactive') + ] + }, + { + 'name': 'progressive', + 'defaultValue': True + } + ] } ] @@ -619,6 +677,8 @@ class HdRprConfig {{ void CleanDirtyFlag(ChangeTracker dirtyFlag); void ResetDirty(); + void ResetRenderSettingsVersion(); + private: HdRprConfig() = default; @@ -732,6 +792,10 @@ class HdRprConfig {{ m_dirtyFlags = Clean; }} +void HdRprConfig::ResetRenderSettingsVersion() {{ + m_lastRenderSettingsVersion = -1; +}} + HdRprConfig::PrefData::PrefData() {{ SetDefault(); }} diff --git a/pxr/imaging/plugin/hdRpr/renderBuffer.cpp b/pxr/imaging/plugin/hdRpr/renderBuffer.cpp index 606956284..cc54f0a7c 100644 --- a/pxr/imaging/plugin/hdRpr/renderBuffer.cpp +++ b/pxr/imaging/plugin/hdRpr/renderBuffer.cpp @@ -92,7 +92,7 @@ void HdRprRenderBuffer::_Deallocate() { } void* HdRprRenderBuffer::Map() { - m_rprApi->Resolve(); + m_rprApi->Resolve(GetId()); #ifdef ENABLE_MULTITHREADED_RENDER_BUFFER std::unique_lock lock(m_mapMutex); diff --git a/pxr/imaging/plugin/hdRpr/renderDelegate.cpp b/pxr/imaging/plugin/hdRpr/renderDelegate.cpp index 8d9a02aa3..791055459 100644 --- a/pxr/imaging/plugin/hdRpr/renderDelegate.cpp +++ b/pxr/imaging/plugin/hdRpr/renderDelegate.cpp @@ -118,9 +118,6 @@ class HdRprDiagnosticMgrDelegate : public TfDiagnosticMgr::Delegate { TF_DEFINE_PRIVATE_TOKENS(_tokens, (openvdbAsset) \ (percentDone) \ - (renderMode) \ - (batch) \ - (progressive) \ (RPR) ); @@ -157,8 +154,6 @@ HdRprDelegate::HdRprDelegate(HdRenderSettingsMap const& renderSettings) { SetRenderSetting(entry.first, entry.second); } - m_isBatch = GetRenderSetting(_tokens->renderMode) == _tokens->batch; - m_isProgressive = GetRenderSetting(_tokens->progressive).GetWithDefault(true); m_rprApi.reset(new HdRprApi(this)); g_rprApi = m_rprApi.get(); @@ -189,6 +184,12 @@ HdRprDelegate::HdRprDelegate(HdRenderSettingsMap const& renderSettings) { } HdRprDelegate::~HdRprDelegate() { + // Render settings version reset is required for valid recreation of HdRprDelgate + // Config singleton persists in memory after delegate destruction, therefore version must be invalidated + HdRprConfig* config; + auto configInstanceLock = HdRprConfig::GetInstance(&config); + config->ResetRenderSettingsVersion(); + g_rprApi = nullptr; } diff --git a/pxr/imaging/plugin/hdRpr/renderDelegate.h b/pxr/imaging/plugin/hdRpr/renderDelegate.h index c0b1f3edd..7ac069e52 100644 --- a/pxr/imaging/plugin/hdRpr/renderDelegate.h +++ b/pxr/imaging/plugin/hdRpr/renderDelegate.h @@ -94,17 +94,11 @@ class HdRprDelegate final : public HdRenderDelegate { void SetDrivers(HdDriverVector const& drivers) override; #endif // PXR_VERSION >= 2005 - bool IsBatch() const { return m_isBatch; } - bool IsProgressive() const { return m_isProgressive; } - private: static const TfTokenVector SUPPORTED_RPRIM_TYPES; static const TfTokenVector SUPPORTED_SPRIM_TYPES; static const TfTokenVector SUPPORTED_BPRIM_TYPES; - bool m_isBatch; - bool m_isProgressive; - std::unique_ptr m_rprApi; std::unique_ptr m_renderParam; HdRenderSettingDescriptorList m_settingDescriptors; diff --git a/pxr/imaging/plugin/hdRpr/rprApi.cpp b/pxr/imaging/plugin/hdRpr/rprApi.cpp index 1e1489039..d7c957942 100644 --- a/pxr/imaging/plugin/hdRpr/rprApi.cpp +++ b/pxr/imaging/plugin/hdRpr/rprApi.cpp @@ -30,8 +30,6 @@ using json = nlohmann::json; #include "renderParam.h" #include "pxr/imaging/rprUsd/util.h" -#include "pxr/imaging/glf/uvTextureData.h" - #include "pxr/imaging/rprUsd/config.h" #include "pxr/imaging/rprUsd/error.h" #include "pxr/imaging/rprUsd/helpers.h" @@ -52,13 +50,22 @@ using json = nlohmann::json; #include "pxr/usd/usdRender/tokens.h" #include "pxr/usd/usdGeom/tokens.h" #include "pxr/base/tf/envSetting.h" +#include "pxr/base/tf/fileUtils.h" #include "pxr/base/tf/getenv.h" +#include "pxr/base/work/loops.h" #include "notify/message.h" #include #include +#ifdef RPR_EXR_EXPORT_ENABLED +#include +#include +#include +#include +#endif // RPR_EXR_EXPORT_ENABLED + #ifdef BUILD_AS_HOUDINI_PLUGIN #include #endif // BUILD_AS_HOUDINI_PLUGIN @@ -75,6 +82,12 @@ TF_DEFINE_ENV_SETTING(HDRPR_RENDER_QUALITY_OVERRIDE, "", namespace { +TF_DEFINE_PRIVATE_TOKENS(_tokens, + (batch) + (renderMode) + (progressive) +); + TfToken GetRenderQuality(HdRprConfig const& config) { std::string renderQualityOverride = TfGetEnvSetting(HDRPR_RENDER_QUALITY_OVERRIDE); @@ -108,6 +121,67 @@ RprUsdRenderDeviceType ToRprUsd(TfToken const& configDeviceType) { } } +bool CreateIntermediateDirectories(std::string const& filePath) { + auto dir = TfGetPathName(filePath); + if (!dir.empty()) { + return TfMakeDirs(dir, -1, true); + } + return true; +} + +template +void UnalignedRead(void const* src, size_t* offset, T* dst) { + std::memcpy(dst, (uint8_t const*)src + *offset, sizeof(T)); + *offset += sizeof(T); +} + +GfVec4f ColorizeId(uint32_t id) { + return { + (float)(id & 0xFF) / 0xFF, + (float)((id & 0xFF00) >> 8) / 0xFF, + (float)((id & 0xFF0000) >> 16) / 0xFF, + 1.0f + }; +} + +template +std::unique_ptr MergeSamples(VtArray>* samples, size_t requiredNumSamples, rpr_float const** rprData, size_t* rprDataSize) { + if (samples->empty()) { + *rprData = nullptr; + *rprDataSize = 0; + return nullptr; + } + + if (samples->size() != requiredNumSamples) { + samples->resize(requiredNumSamples, [backElem=samples->back()](auto begin, auto end) { + for (auto it = begin; it != end; ++it) { + *it = backElem; + } + }); + } + + const size_t numSampleValues = samples->cdata()[0].size(); + auto mergedSamples = std::make_unique(requiredNumSamples * numSampleValues); + + for (size_t i = 0; i < requiredNumSamples; ++i) { + size_t offset = i * numSampleValues; + VtArray const& values = samples->cdata()[i]; + std::copy(values.cbegin(), values.cend(), mergedSamples.get() + offset); + + if (values.size() != numSampleValues) { + TF_RUNTIME_ERROR("Non-uniform size between samples: %zu vs %zu", samples->size(), numSampleValues); + *rprData = nullptr; + *rprDataSize = 0; + return nullptr; + } + } + + *rprData = (rpr_float const*)mergedSamples.get(); + *rprDataSize = requiredNumSamples * numSampleValues; + + return mergedSamples; +} + } // namespace anonymous TfToken GetRprLpeAovName(rpr::Aov aov) { @@ -242,6 +316,9 @@ struct HdRprApiEnvironmentLight { std::unique_ptr light; std::unique_ptr image; + std::unique_ptr backgroundOverrideLight; + std::unique_ptr backgroundOverrideImage; + enum { kDetached, kAttachedAsLight, @@ -294,33 +371,51 @@ class HdRprApiImpl { } } - rpr::Shape* CreateMesh(const VtVec3fArray& points, const VtIntArray& pointIndexes, - VtVec3fArray normals, const VtIntArray& normalIndexes, - VtVec2fArray uvs, const VtIntArray& uvIndexes, - const VtIntArray& vpf, TfToken const& polygonWinding = HdTokens->rightHanded) { + rpr::Shape* CreateMesh(VtVec3fArray const& points, VtIntArray const& pointIndices, + VtVec3fArray const& normals, VtIntArray const& normalIndices, + VtVec2fArray const& uvs, VtIntArray const& uvIndices, + VtIntArray const& vpf, TfToken const& polygonWinding = HdTokens->rightHanded) { + VtArray pointSamples; + VtArray normalSamples; + VtArray uvSamples; + + if (!points.empty()) pointSamples.push_back(points); + if (!normals.empty()) normalSamples.push_back(normals); + if (!uvs.empty()) uvSamples.push_back(uvs); + + return CreateMesh(pointSamples, pointIndices, normalSamples, normalIndices, uvSamples, uvIndices, vpf, polygonWinding); + } + + rpr::Shape* CreateMesh(VtArray pointSamples, VtIntArray const& pointIndices, + VtArray normalSamples, VtIntArray const& normalIndices, + VtArray uvSamples, VtIntArray const& uvIndices, + VtIntArray const& vpf, TfToken const& polygonWinding) { if (!m_rprContext) { return nullptr; } - VtIntArray newIndexes, newVpf; - SplitPolygons(pointIndexes, vpf, newIndexes, newVpf); - ConvertIndices(&newIndexes, newVpf, polygonWinding); + VtIntArray newIndices, newVpf; + SplitPolygons(pointIndices, vpf, newIndices, newVpf); + ConvertIndices(&newIndices, newVpf, polygonWinding); - VtIntArray newNormalIndexes; - if (normals.empty()) { + VtIntArray newNormalIndices; + if (normalSamples.empty()) { if (m_rprContextMetadata.pluginType == kPluginHybrid) { // XXX (Hybrid): we need to generate geometry normals by ourself + VtVec3fArray normals; normals.reserve(newVpf.size()); - newNormalIndexes.clear(); - newNormalIndexes.reserve(newIndexes.size()); + newNormalIndices.clear(); + newNormalIndices.reserve(newIndices.size()); + + auto& points = pointSamples[0]; size_t indicesOffset = 0u; for (auto numVerticesPerFace : newVpf) { for (int i = 0; i < numVerticesPerFace; ++i) { - newNormalIndexes.push_back(normals.size()); + newNormalIndices.push_back(normals.size()); } - auto indices = &newIndexes[indicesOffset]; + auto indices = &newIndices[indicesOffset]; indicesOffset += numVerticesPerFace; auto p0 = points[indices[0]]; @@ -334,52 +429,106 @@ class HdRprApiImpl { GfNormalize(&normal); normals.push_back(normal); } + + normalSamples.push_back(normals); } } else { - if (!normalIndexes.empty()) { - SplitPolygons(normalIndexes, vpf, newNormalIndexes); - ConvertIndices(&newNormalIndexes, newVpf, polygonWinding); + if (!normalIndices.empty()) { + SplitPolygons(normalIndices, vpf, newNormalIndices); + ConvertIndices(&newNormalIndices, newVpf, polygonWinding); } else { - newNormalIndexes = newIndexes; + newNormalIndices = newIndices; } } - VtIntArray newUvIndexes; - if (uvs.empty()) { + VtIntArray newUvIndices; + if (uvSamples.empty()) { if (m_rprContextMetadata.pluginType == kPluginHybrid) { - newUvIndexes = newIndexes; - uvs = VtVec2fArray(points.size(), GfVec2f(0.0f)); + newUvIndices = newIndices; + VtVec2fArray uvs(pointSamples[0].size(), GfVec2f(0.0f)); + uvSamples.push_back(uvs); } } else { - if (!uvIndexes.empty()) { - SplitPolygons(uvIndexes, vpf, newUvIndexes); - ConvertIndices(&newUvIndexes, newVpf, polygonWinding); + if (!uvIndices.empty()) { + SplitPolygons(uvIndices, vpf, newUvIndices); + ConvertIndices(&newUvIndices, newVpf, polygonWinding); } else { - newUvIndexes = newIndexes; + newUvIndices = newIndices; } } - auto normalIndicesData = !newNormalIndexes.empty() ? newNormalIndexes.data() : newIndexes.data(); - if (normals.empty()) { + auto normalIndicesData = !newNormalIndices.empty() ? newNormalIndices.data() : newIndices.data(); + if (normalSamples.empty()) { normalIndicesData = nullptr; } - auto uvIndicesData = !newUvIndexes.empty() ? newUvIndexes.data() : uvIndexes.data(); - if (uvs.empty()) { + auto uvIndicesData = !newUvIndices.empty() ? newUvIndices.data() : uvIndices.data(); + if (uvSamples.empty()) { uvIndicesData = nullptr; } + rpr_float const* pointsData = nullptr; + rpr_float const* normalsData = nullptr; + rpr_float const* uvsData = nullptr; + size_t numPoints = 0; + size_t numNormals = 0; + size_t numUvs = 0; + + std::vector meshProperties(1, rpr_mesh_info(0)); + + std::unique_ptr mergedPoints; + std::unique_ptr mergedNormals; + std::unique_ptr mergedUvs; + + size_t numMeshSamples = std::max(std::max(pointSamples.size(), normalSamples.size()), uvSamples.size()); + + // XXX (RPR): Only Northstar supports deformation motion blur. Use only the first sample for all other plugins. + if (m_rprContextMetadata.pluginType != kPluginNorthstar) { + numMeshSamples = 1; + } + + if (numMeshSamples > 1) { + meshProperties[0] = (rpr_mesh_info)RPR_MESH_MOTION_DIMENSION; + meshProperties[1] = (rpr_mesh_info)numMeshSamples; + meshProperties[2] = (rpr_mesh_info)0; + + mergedPoints = MergeSamples(&pointSamples, numMeshSamples, &pointsData, &numPoints); + mergedNormals = MergeSamples(&normalSamples, numMeshSamples, &normalsData, &numNormals); + mergedUvs = MergeSamples(&uvSamples, numMeshSamples, &uvsData, &numUvs); + + } else { + if (pointSamples.empty()) { + return nullptr; + } + pointsData = (rpr_float const*)pointSamples[0].cdata(); + numPoints = pointSamples[0].size(); + + if (!normalSamples.empty()) { + normalsData = (rpr_float const*)normalSamples[0].data(); + numNormals = normalSamples[0].size(); + } + + if (!uvSamples.empty()) { + uvsData = (rpr_float const*)uvSamples[0].data(); + numUvs = uvSamples[0].size(); + } + } + + rpr_int texCoordStride = sizeof(GfVec2f); + rpr_int texCoordIdxStride = sizeof(rpr_int); + LockGuard rprLock(m_rprContext->GetMutex()); rpr::Status status; auto mesh = m_rprContext->CreateShape( - (rpr_float const*)points.data(), points.size(), sizeof(GfVec3f), - (rpr_float const*)(normals.data()), normals.size(), sizeof(GfVec3f), - (rpr_float const*)(uvs.data()), uvs.size(), sizeof(GfVec2f), - newIndexes.data(), sizeof(rpr_int), + pointsData, numPoints, sizeof(GfVec3f), + normalsData, numNormals, sizeof(GfVec3f), + nullptr, 0, 0, + 1, &uvsData, &numUvs, &texCoordStride, + newIndices.data(), sizeof(rpr_int), normalIndicesData, sizeof(rpr_int), - uvIndicesData, sizeof(rpr_int), - newVpf.data(), newVpf.size(), &status); + &uvIndicesData, &texCoordIdxStride, + newVpf.data(), newVpf.size(), meshProperties.data(), &status); if (!mesh) { RPR_ERROR_CHECK(status, "Failed to create mesh"); return nullptr; @@ -752,7 +901,7 @@ class HdRprApiImpl { } } - HdRprApiEnvironmentLight* CreateEnvironmentLight(std::unique_ptr&& image, float intensity) { + HdRprApiEnvironmentLight* CreateEnvironmentLight(std::unique_ptr&& image, float intensity, VtValue const& backgroundOverride) { // XXX (RPR): default environment light should be removed before creating a new one - RPR limitation RemoveDefaultLight(); @@ -785,6 +934,20 @@ class HdRprApiImpl { return nullptr; } + if (backgroundOverride.IsHolding()) { + GfVec3f const& backgroundOverrideColor = backgroundOverride.UncheckedGet(); + envLight->backgroundOverrideLight.reset(m_rprContext->CreateEnvironmentLight(&status)); + envLight->backgroundOverrideImage = CreateConstantColorImage(backgroundOverrideColor.data()); + + if (!envLight->backgroundOverrideLight || + !envLight->backgroundOverrideImage || + RPR_ERROR_CHECK(envLight->backgroundOverrideLight->SetImage(envLight->backgroundOverrideImage->GetRootImage()), "Failed to set background override env light image", m_rprContext.get()) || + RPR_ERROR_CHECK(envLight->light->SetEnvironmentLightOverride(RPR_ENVIRONMENT_LIGHT_OVERRIDE_BACKGROUND, envLight->backgroundOverrideLight.get()), "Failed to set env light background override")) { + envLight->backgroundOverrideLight = nullptr; + envLight->backgroundOverrideImage = nullptr; + } + } + m_dirtyFlags |= ChangeTracker::DirtyScene; m_numLights++; return envLight; @@ -814,7 +977,7 @@ class HdRprApiImpl { } } - HdRprApiEnvironmentLight* CreateEnvironmentLight(const std::string& path, float intensity) { + HdRprApiEnvironmentLight* CreateEnvironmentLight(const std::string& path, float intensity, VtValue const& backgroundOverride) { if (!m_rprContext || path.empty()) { return nullptr; } @@ -826,29 +989,22 @@ class HdRprApiImpl { return nullptr; } - return CreateEnvironmentLight(std::move(image), intensity); + return CreateEnvironmentLight(std::move(image), intensity, backgroundOverride); } - HdRprApiEnvironmentLight* CreateEnvironmentLight(GfVec3f color, float intensity) { + HdRprApiEnvironmentLight* CreateEnvironmentLight(GfVec3f color, float intensity, VtValue const& backgroundOverride) { if (!m_rprContext) { return nullptr; } - std::array backgroundColor = {color[0], color[1], color[2]}; - rpr_image_format format = {3, RPR_COMPONENT_TYPE_FLOAT32}; - rpr_uint imageSize = m_rprContextMetadata.pluginType == kPluginHybrid ? 64 : 1; - std::vector> imageData(imageSize * imageSize, backgroundColor); - LockGuard rprLock(m_rprContext->GetMutex()); - rpr::Status status; - auto image = std::unique_ptr(RprUsdCoreImage::Create(m_rprContext.get(), imageSize, imageSize, format, imageData.data(), &status)); - if (!image) { - RPR_ERROR_CHECK(status, "Failed to create image", m_rprContext.get()); + auto backgroundImage = CreateConstantColorImage(color.data()); + if (!backgroundImage) { return nullptr; } - return CreateEnvironmentLight(std::move(image), intensity); + return CreateEnvironmentLight(std::move(backgroundImage), intensity, backgroundOverride); } void SetTransform(rpr::SceneObject* object, GfMatrix4f const& transform) { @@ -994,13 +1150,13 @@ class HdRprApiImpl { m_dirtyFlags |= ChangeTracker::DirtyScene; } - RprUsdMaterial* CreateMaterial(HdSceneDelegate* sceneDelegate, HdMaterialNetworkMap const& materialNetwork) { + RprUsdMaterial* CreateMaterial(SdfPath const& materialId, HdSceneDelegate* sceneDelegate, HdMaterialNetworkMap const& materialNetwork) { if (!m_rprContext) { return nullptr; } LockGuard rprLock(m_rprContext->GetMutex()); - return RprUsdMaterialRegistry::GetInstance().CreateMaterial(sceneDelegate, materialNetwork, m_rprContext.get(), m_imageCache.get()); + return RprUsdMaterialRegistry::GetInstance().CreateMaterial(materialId, sceneDelegate, materialNetwork, m_rprContext.get(), m_imageCache.get()); } RprUsdMaterial* CreatePointsMaterial(VtVec3fArray const& colors) { @@ -1475,15 +1631,15 @@ class HdRprApiImpl { rpr_uint GetRprRenderMode(TfToken const& mode) { static std::map s_mapping = { - {HdRprRenderModeTokens->GlobalIllumination, RPR_RENDER_MODE_GLOBAL_ILLUMINATION}, - {HdRprRenderModeTokens->DirectIllumination, RPR_RENDER_MODE_DIRECT_ILLUMINATION}, - {HdRprRenderModeTokens->Wireframe, RPR_RENDER_MODE_WIREFRAME}, - {HdRprRenderModeTokens->MaterialIndex, RPR_RENDER_MODE_MATERIAL_INDEX}, - {HdRprRenderModeTokens->Position, RPR_RENDER_MODE_POSITION}, - {HdRprRenderModeTokens->Normal, RPR_RENDER_MODE_NORMAL}, - {HdRprRenderModeTokens->Texcoord, RPR_RENDER_MODE_TEXCOORD}, - {HdRprRenderModeTokens->AmbientOcclusion, RPR_RENDER_MODE_AMBIENT_OCCLUSION}, - {HdRprRenderModeTokens->Diffuse, RPR_RENDER_MODE_DIFFUSE}, + {HdRprCoreRenderModeTokens->GlobalIllumination, RPR_RENDER_MODE_GLOBAL_ILLUMINATION}, + {HdRprCoreRenderModeTokens->DirectIllumination, RPR_RENDER_MODE_DIRECT_ILLUMINATION}, + {HdRprCoreRenderModeTokens->Wireframe, RPR_RENDER_MODE_WIREFRAME}, + {HdRprCoreRenderModeTokens->MaterialIndex, RPR_RENDER_MODE_MATERIAL_INDEX}, + {HdRprCoreRenderModeTokens->Position, RPR_RENDER_MODE_POSITION}, + {HdRprCoreRenderModeTokens->Normal, RPR_RENDER_MODE_NORMAL}, + {HdRprCoreRenderModeTokens->Texcoord, RPR_RENDER_MODE_TEXCOORD}, + {HdRprCoreRenderModeTokens->AmbientOcclusion, RPR_RENDER_MODE_AMBIENT_OCCLUSION}, + {HdRprCoreRenderModeTokens->Diffuse, RPR_RENDER_MODE_DIFFUSE}, }; auto it = s_mapping.find(mode); @@ -1497,10 +1653,10 @@ class HdRprApiImpl { } m_dirtyFlags |= ChangeTracker::DirtyScene; - auto& renderMode = preferences.GetRenderMode(); + auto& renderMode = preferences.GetCoreRenderMode(); if (m_rprContextMetadata.pluginType == kPluginNorthstar) { - if (renderMode == HdRprRenderModeTokens->Contour) { + if (renderMode == HdRprCoreRenderModeTokens->Contour) { if (!m_contourAovs) { m_contourAovs = std::make_unique(); m_contourAovs->normal = CreateAov(HdAovTokens->normal); @@ -1536,7 +1692,7 @@ class HdRprApiImpl { } RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_RENDER_MODE, GetRprRenderMode(renderMode)), "Failed to set render mode"); - if (renderMode == HdRprRenderModeTokens->AmbientOcclusion) { + if (renderMode == HdRprCoreRenderModeTokens->AmbientOcclusion) { RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_AO_RAY_LENGTH, preferences.GetAoRadius()), "Failed to set ambient occlusion radius"); } } @@ -1631,6 +1787,17 @@ class HdRprApiImpl { RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_OCIO_RENDERING_COLOR_SPACE, preferences.GetOcioRenderingColorSpace().c_str()), "Faled to set OCIO rendering color space"); m_dirtyFlags |= ChangeTracker::DirtyScene; } + + if (preferences.IsDirty(HdRprConfig::DirtyCryptomatte) || force) { +#ifdef RPR_EXR_EXPORT_ENABLED + m_cryptomatteOutputPath = preferences.GetCryptomatteOutputPath(); + m_cryptomattePreviewLayer = preferences.GetCryptomattePreviewLayer(); +#else + if (!preferences.GetCryptomatteOutputPath().empty()) { + fprintf(stderr, "Cryptomatte export is not supported: hdRpr compiled without .exr support\n"); + } +#endif // RPR_EXR_EXPORT_ENABLED + } } } @@ -1673,6 +1840,42 @@ class HdRprApiImpl { UpdateColorAlpha(m_colorAov.get()); } + + if (preferences.IsDirty(HdRprConfig::DirtySession) || force) { + m_isProgressive = preferences.GetProgressive(); + bool isBatch = preferences.GetRenderMode() == HdRprRenderModeTokens->batch; + if (m_isBatch != isBatch) { + m_isBatch = isBatch; + m_batchREM = isBatch ? std::make_unique() : nullptr; + m_dirtyFlags |= ChangeTracker::DirtyScene; + } + } + + if (preferences.IsDirty(HdRprConfig::DirtySession) || preferences.IsDirty(HdRprConfig::DirtyCryptomatte) || force) { + if (!m_cryptomatteOutputPath.empty() && + (m_isBatch || preferences.GetCryptomatteOutputMode() == HdRprCryptomatteOutputModeTokens->Interactive) && + m_rprContextMetadata.pluginType == kPluginNorthstar) { + if (!m_cryptomatteAovs) { + auto cryptomatteAovs = std::make_unique(); + cryptomatteAovs->obj.aov[0] = CreateAov(HdRprAovTokens->cryptomatteObj0); + cryptomatteAovs->obj.aov[1] = CreateAov(HdRprAovTokens->cryptomatteObj1); + cryptomatteAovs->obj.aov[2] = CreateAov(HdRprAovTokens->cryptomatteObj2); + cryptomatteAovs->mat.aov[0] = CreateAov(HdRprAovTokens->cryptomatteMat0); + cryptomatteAovs->mat.aov[1] = CreateAov(HdRprAovTokens->cryptomatteMat1); + cryptomatteAovs->mat.aov[2] = CreateAov(HdRprAovTokens->cryptomatteMat2); + for (int i = 0; i < 3; ++i) { + if (!cryptomatteAovs->obj.aov[i] || + !cryptomatteAovs->mat.aov[i]) { + cryptomatteAovs = nullptr; + break; + } + } + m_cryptomatteAovs = std::move(cryptomatteAovs); + } + } else { + m_cryptomatteAovs = nullptr; + } + } } void UpdateCamera(RenderSetting const& aspectRatioPolicy, RenderSetting const& instantaneousShutter) { @@ -1781,7 +1984,9 @@ class HdRprApiImpl { float focusDistance = 1.0f; m_hdCamera->GetFocusDistance(&focusDistance); - RPR_ERROR_CHECK(m_camera->SetFocusDistance(focusDistance), "Failed to set camera focus distance"); + if (focusDistance > 0.0f) { + RPR_ERROR_CHECK(m_camera->SetFocusDistance(focusDistance), "Failed to set camera focus distance"); + } float fstop = 0.0f; m_hdCamera->GetFStop(&fstop); @@ -2066,28 +2271,206 @@ class HdRprApiImpl { return true; } + uint32_t cryptomatte_avoid_bad_float_hash(uint32_t hash) { + // from Cryptomatte Specification version 1.2.0 + // This is for avoiding nan, inf, subnormals + // + // if all exponent bits are 0 (subnormals, +zero, -zero) set exponent to 1 + // if all exponent bits are 1 (NaNs, +inf, -inf) set exponent to 254 + uint32_t exponent = hash >> 23 & 255; // extract exponent (8 bits) + if (exponent == 0 || exponent == 255) { + hash ^= 1 << 23; // toggle bit + } + return hash; + } + std::string cryptomatte_hash_name(const char* name, size_t size) { + uint32_t m3hash = 0; + MurmurHash3_x86_32(name, size, 0, &m3hash); + m3hash = cryptomatte_avoid_bad_float_hash(m3hash); + return TfStringPrintf("%08x", m3hash); + } + template + std::string cryptomatte_hash_name(char (&name)[size]) { + return cryptomatte_hash_name(name, size); + } + std::string cryptomatte_hash_name(std::string const& name) { + return cryptomatte_hash_name(name.c_str(), name.size()); + } + + void SaveCryptomatte() { +#ifdef RPR_EXR_EXPORT_ENABLED + if (m_cryptomatteAovs && + m_numSamples == m_maxSamples) { + + std::string outputPath = m_cryptomatteOutputPath; + std::string filename = TfGetBaseName(outputPath); + if (filename.empty()) { + TF_WARN("Cryptomatte output path should be a path to .exr file"); + outputPath = TfStringCatPaths(outputPath, "cryptomatte.exr"); + } else if (!TfStringEndsWith(outputPath, ".exr")) { + TF_WARN("Cryptomatte output path should be a path to .exr file"); + outputPath += ".exr"; + } + + if (!CreateIntermediateDirectories(outputPath)) { + fprintf(stderr, "Failed to save cryptomatte aov: cannot create intermediate directories - %s\n", outputPath.c_str()); + return; + } + + // Generate manifests + std::string objectManifestEncoded; + std::string materialManifestEncoded; + + if (auto shapes = RprUsdGetListInfo(m_rprContext.get(), RPR_CONTEXT_LIST_CREATED_SHAPES)) { + json objectManifest; + json materialManifest; + for (size_t iShape = 0; iShape < shapes.size; ++iShape) { + auto shape = RprUsdGetRprObject(shapes.data[iShape]); + if (auto name = RprUsdGetListInfo(shape, RPR_SHAPE_NAME)) { + objectManifest[name.data.get()] = cryptomatte_hash_name(name.data.get(), name.size - 1); + } + + if (rpr_material_node rprMaterialNode = RprUsdGetInfo(shape, RPR_SHAPE_MATERIAL)) { + auto name = RprUsdGetListInfo( + [rprMaterialNode](size_t size, void* data, size_t* size_ret) { + return rprMaterialNodeGetInfo(rprMaterialNode, RPR_MATERIAL_NODE_NAME, size, data, size_ret); + } + ); + if (name) { + materialManifest[name.data.get()] = cryptomatte_hash_name(name.data.get(), name.size - 1); + } + } + } + objectManifestEncoded = objectManifest.dump(); + materialManifestEncoded = materialManifest.dump(); + } + + try { + namespace exr = OPENEXR_IMF_NAMESPACE; + exr::FrameBuffer exrFb; + exr::Header exrHeader(m_viewportSize[0], m_viewportSize[1]); + exrHeader.compression() = exr::ZIPS_COMPRESSION; + + const int kNumComponents = 4; + const char* kComponentNames[kNumComponents] = {".R", ".G", ".B", ".A"}; + + size_t linesize = m_viewportSize[0] * kNumComponents * sizeof(float); + size_t layersize = m_viewportSize[1] * linesize; + std::unique_ptr flipBuffer; + if (m_isOutputFlipped) { + flipBuffer = std::make_unique(layersize); + } + + std::vector> cryptomatteLayers; + std::vector> previewLayers; + + auto addCryptomatte = [&](const char* name, CryptomatteAov const& cryptomatte, std::string const& manifest) { + std::string nameHash = cryptomatte_hash_name(name).substr(0, 7); + std::string cryptoPrefix = "cryptomatte/" + nameHash + "/"; + exrHeader.insert(cryptoPrefix + "name", exr::StringAttribute(name)); + exrHeader.insert(cryptoPrefix + "hash", exr::StringAttribute("MurmurHash3_32")); + exrHeader.insert(cryptoPrefix + "conversion", exr::StringAttribute("uint32_to_float32")); + exrHeader.insert(cryptoPrefix + "manifest", exr::StringAttribute(manifest)); + + auto addLayer = [&](const char* layerName, char* basePtr) { + for (int iComponent = 0; iComponent < kNumComponents; ++iComponent) { + std::string channelName = TfStringPrintf("%s%s", layerName, kComponentNames[iComponent]); + exrHeader.channels().insert(channelName.c_str(), exr::Channel(exr::FLOAT)); + + char* dataPtr = &basePtr[iComponent * sizeof(float)]; + size_t xStride = kNumComponents * sizeof(float); + size_t yStride = xStride * m_viewportSize[0]; + exrFb.insert(channelName.c_str(), exr::Slice(exr::FLOAT, dataPtr, xStride, yStride)); + } + }; + + for (int i = 0; i < 3; ++i) { + auto rprLayer = cryptomatte.aov[i]->GetResolvedFb(); + cryptomatteLayers.push_back(std::make_unique(layersize)); + if (!rprLayer->GetData(cryptomatteLayers.back().get(), layersize)) { + throw std::runtime_error("failed to get RPR fb data"); + } + + if (m_isOutputFlipped) { + for (int y = 0; y < m_viewportSize[1]; ++y) { + void* src = &cryptomatteLayers.back()[y * linesize]; + void* dst = &flipBuffer[(m_viewportSize[1] - y - 1) * linesize]; + std::memcpy(dst, src, linesize); + } + std::swap(flipBuffer, cryptomatteLayers.back()); + } + + std::string layerName = TfStringPrintf("%s0%d", name, i); + addLayer(layerName.c_str(), cryptomatteLayers.back().get()); + } + + if (m_cryptomattePreviewLayer) { + size_t numPixels = m_viewportSize[0] * m_viewportSize[1]; + previewLayers.push_back(std::make_unique(numPixels)); + std::memset(previewLayers.back().get(), 0, numPixels * sizeof(GfVec4f)); + + size_t channelOffset = cryptomatteLayers.size() - 3; + + GfVec4f* previewLayer = previewLayers.back().get(); + WorkParallelForN(numPixels, + [&](size_t begin, size_t end) { + for (size_t iChannel = 0; iChannel < 3; ++iChannel) { + auto channelData = cryptomatteLayers[channelOffset + iChannel].get(); + for (size_t iPixel = begin; iPixel < end; ++iPixel) { + size_t offset = iPixel * kNumComponents * sizeof(float); + + uint32_t id; + float contrib; + UnalignedRead(channelData, &offset, &id); + UnalignedRead(channelData, &offset, &contrib); + GfVec4f color = ColorizeId(id) * contrib; + + UnalignedRead(channelData, &offset, &id); + UnalignedRead(channelData, &offset, &contrib); + color += ColorizeId(id) * contrib; + + previewLayer[iPixel] += color; + } + } + } + ); + + addLayer(name, (char*)previewLayer); + } + }; + + addCryptomatte("CryptoObject", m_cryptomatteAovs->obj, objectManifestEncoded); + addCryptomatte("CryptoMaterial", m_cryptomatteAovs->mat, materialManifestEncoded); + + ArchUnlinkFile(outputPath.c_str()); + exr::OutputFile exrFile(outputPath.c_str(), exrHeader); + exrFile.setFrameBuffer(exrFb); + exrFile.writePixels(m_viewportSize[1]); + } catch (std::exception& e) { + fprintf(stderr, "Failed to save cryptomatte: %s", e.what()); + } + } +#endif // RPR_EXR_EXPORT_ENABLED + } + void BatchRenderImpl(HdRprRenderThread* renderThread) { if (!CommonRenderImplPrologue()) { return; } - if (!m_batchREM) { - m_batchREM = std::make_unique(); - } auto renderScope = m_batchREM->EnterRenderScope(); EnableRenderUpdateCallback(BatchRenderUpdateCallback); + // Also, we try to maximize the number of samples rendered with one rprContextRender call. + bool isMaximizingContextIterations = false; + // In a batch session, we disable resolving of all samples except the first and last. // Also, if the user will keep progressive mode on, we support snapshoting (resolving intermediate renders) - + // // User might decide to completely disable progress updates to maximize performance. // In this case, snapshoting is not available. - const bool isProgressive = m_delegate->IsProgressive(); - - // Also, we try to maximize the number of samples rendered with one rprContextRender call. - bool isMaximizingContextIterations = false; - if (isProgressive) { + if (m_isProgressive) { // We can keep the ability to log progress and do snapshots // while maximizing RPR_CONTEXT_ITERATIONS, only if RUC is supported. if (m_isRenderUpdateCallbackEnabled) { @@ -2197,7 +2580,7 @@ class HdRprApiImpl { } } - if (isAdaptiveSamplingEnabled && + if (isAdaptiveSamplingEnabled && m_numSamples >= m_minSamples && RPR_ERROR_CHECK(m_rprContext->GetInfo(RPR_CONTEXT_ACTIVE_PIXEL_COUNT, sizeof(m_activePixels), &m_activePixels, NULL), "Failed to query active pixels")) { m_activePixels = -1; } @@ -2327,7 +2710,7 @@ class HdRprApiImpl { ResolveFramebuffers(); } - if (IsAdaptiveSamplingEnabled() && + if (IsAdaptiveSamplingEnabled() && m_numSamples >= m_minSamples && RPR_ERROR_CHECK(m_rprContext->GetInfo(RPR_CONTEXT_ACTIVE_PIXEL_COUNT, sizeof(m_activePixels), &m_activePixels, NULL), "Failed to query active pixels")) { m_activePixels = -1; } @@ -2391,7 +2774,7 @@ class HdRprApiImpl { if (m_state == kStateRender) { try { - if (m_delegate->IsBatch()) { + if (m_isBatch) { BatchRenderImpl(renderThread); } else { if (m_rprContextMetadata.pluginType == RprUsdPluginType::kPluginHybrid && @@ -2401,6 +2784,7 @@ class HdRprApiImpl { RenderImpl(renderThread); } } + SaveCryptomatte(); } catch (std::runtime_error const& e) { TF_RUNTIME_ERROR("Failed to render frame: %s", e.what()); } @@ -2441,6 +2825,10 @@ Don't show this message again? } #endif // BUILD_AS_HOUDINI_PLUGIN + if (!CreateIntermediateDirectories(m_rprSceneExportPath)) { + fprintf(stderr, "Failed to create .rpr export output directory\n"); + } + uint32_t currentYFlip; if (m_isOutputFlipped) { currentYFlip = RprUsdGetInfo(m_rprContext.get(), RPR_CONTEXT_Y_FLIP); @@ -2579,15 +2967,16 @@ Don't show this message again? if (aovDesc->id == kColorAlpha) { aovDesc = &HdRprAovRegistry::GetInstance().GetAovDesc(RPR_AOV_COLOR, false); } else if (aovDesc->id == kNdcDepth) { - // RprsRender does not support it...yet? - continue; + // XXX: RprsRender does not support it but for the users, it is more expected if we map it to the linear depth instead of ignoring it at all + aovDesc = &HdRprAovRegistry::GetInstance().GetAovDesc(RPR_AOV_DEPTH, false); } else { fprintf(stderr, "Unprocessed computed AOV: %u\n", aovDesc->id); continue; } } - if (TF_VERIFY(aovDesc->id < kNumRprsAovNames) && + if (TF_VERIFY(aovDesc->id != kAovNone) && + TF_VERIFY(aovDesc->id < kNumRprsAovNames) && TF_VERIFY(!aovDesc->computed)) { auto aovName = kRprsAovNames[aovDesc->id]; if (aovDesc->id >= RPR_AOV_LPE_0 && aovDesc->id <= RPR_AOV_LPE_8) { @@ -2643,7 +3032,7 @@ Don't show this message again? RprUsdMaterialRegistry::GetInstance().CommitResources(m_imageCache.get()); } - void Resolve() { + void Resolve(SdfPath const& aovId) { // hdRpr's rendering is implemented asynchronously - rprContextRender spins in the background thread. // // Resolve works differently depending on the current rendering session type: @@ -2652,9 +3041,8 @@ Don't show this message again? // * In an interactive session, it simply does nothing because we always resolve // data to HdRenderBuffer as fast as possible. It might change in the future though. // - if (m_batchREM) { - m_batchREM->OnResolveRequest(); - m_batchREM->WaitUntilNextRenderEvent(); + if (m_isBatch) { + m_batchREM->WaitForResolve(aovId); } } @@ -2990,7 +3378,7 @@ Don't show this message again? void AddDefaultLight() { if (!m_defaultLightObject) { const GfVec3f k_defaultLightColor(0.5f, 0.5f, 0.5f); - m_defaultLightObject = CreateEnvironmentLight(k_defaultLightColor, 1.f); + m_defaultLightObject = CreateEnvironmentLight(k_defaultLightColor, 1.f, VtValue()); if (RprUsdIsLeakCheckEnabled()) { m_defaultLightObject->light->SetName("defaultLight"); @@ -3012,6 +3400,22 @@ Don't show this message again? } } + std::unique_ptr CreateConstantColorImage(float const* color) { + std::array colorArray = {color[0], color[1], color[2]}; + rpr_image_format format = {3, RPR_COMPONENT_TYPE_FLOAT32}; + rpr_uint imageSize = m_rprContextMetadata.pluginType == kPluginHybrid ? 64 : 1; + std::vector> imageData(imageSize * imageSize, colorArray); + + rpr::Status status; + auto image = std::unique_ptr(RprUsdCoreImage::Create(m_rprContext.get(), imageSize, imageSize, format, imageData.data(), &status)); + if (!image) { + RPR_ERROR_CHECK(status, "Failed to create const color image", m_rprContext.get()); + return nullptr; + } + + return image; + } + void SplitPolygons(const VtIntArray& indexes, const VtIntArray& vpf, VtIntArray& out_newIndexes, VtIntArray& out_newVpf) { out_newIndexes.clear(); out_newVpf.clear(); @@ -3358,16 +3762,16 @@ Don't show this message again? return false; } - auto textureData = GlfUVTextureData::New(path, INT_MAX, 0, 0, 0, 0); - if (textureData && textureData->Read(0, false)) { + auto textureData = RprUsdTextureData::New(path); + if (textureData) { rif_image_desc imageDesc = {}; - imageDesc.image_width = textureData->ResizedWidth(); - imageDesc.image_height = textureData->ResizedHeight(); + imageDesc.image_width = textureData->GetWidth(); + imageDesc.image_height = textureData->GetHeight(); imageDesc.image_depth = 1; imageDesc.image_row_pitch = 0; imageDesc.image_slice_pitch = 0; - auto textureMetadata = RprUsdGetGlfTextureMetadata(&(*textureData)); + auto textureMetadata = textureData->GetGLMetadata(); uint8_t bytesPerComponent; if (textureMetadata.glType == GL_UNSIGNED_BYTE) { @@ -3402,7 +3806,7 @@ Don't show this message again? return false; } size_t imageSize = bytesPerComponent * imageDesc.num_components * imageDesc.image_width * imageDesc.image_height; - std::memcpy(mappedData, textureData->GetRawBuffer(), imageSize); + std::memcpy(mappedData, textureData->GetData(), imageSize); RIF_ERROR_CHECK(rifImageUnmap(rifImage->GetHandle(), mappedData), "Failed to unmap rif image"); auto colorRb = static_cast(colorOutputRb->aovBinding->renderBuffer); @@ -3541,32 +3945,37 @@ Don't show this message again? class BatchRenderEventManager { public: - void WaitUntilNextRenderEvent() { + void WaitForResolve(SdfPath const& aovId) { + if (m_signalingAovId.IsEmpty()) { + m_signalingAovId = aovId; + } else if (m_signalingAovId != aovId) { + // When there is more than one bound aov, + // we want to avoid blocking resolve for each of them separately. + // Instead, block only on the first one only. + return; + } + + m_isResolveRequested.store(true); std::unique_lock lock(m_renderEventMutex); m_renderEventCV.wait(lock, [this]() -> bool { return !m_isRenderInProgress || !m_isResolveRequested; }); } class RenderScopeGuard { public: - RenderScopeGuard(std::atomic* isRenderInProgress, std::condition_variable* renderEventCV) - : m_isRenderInProgress(isRenderInProgress), m_renderEventCV(renderEventCV) { - m_isRenderInProgress->store(true); - m_renderEventCV->notify_one(); + RenderScopeGuard(BatchRenderEventManager* rem) : m_rem(rem) { + m_rem->m_signalingAovId = SdfPath::EmptyPath(); + m_rem->m_isRenderInProgress.store(true); + m_rem->m_renderEventCV.notify_one(); } ~RenderScopeGuard() { - m_isRenderInProgress->store(false); - m_renderEventCV->notify_one(); + m_rem->m_isRenderInProgress.store(false); + m_rem->m_renderEventCV.notify_one(); } private: - std::atomic* m_isRenderInProgress; - std::condition_variable* m_renderEventCV; + BatchRenderEventManager* m_rem; }; - RenderScopeGuard EnterRenderScope() { return RenderScopeGuard(&m_isRenderInProgress, &m_renderEventCV); } - - void OnResolveRequest() { - m_isResolveRequested.store(true); - } + RenderScopeGuard EnterRenderScope() { return RenderScopeGuard(this); } bool IsResolveRequested() const { return m_isResolveRequested; @@ -3582,8 +3991,11 @@ Don't show this message again? std::condition_variable m_renderEventCV; std::atomic m_isRenderInProgress{false}; std::atomic m_isResolveRequested{false}; + SdfPath m_signalingAovId; }; std::unique_ptr m_batchREM; + std::atomic m_isBatch{false}; + bool m_isProgressive; struct ResolveData { struct AovEntry { @@ -3657,6 +4069,17 @@ Don't show this message again? bool m_rprExportAsSingleFile; bool m_rprExportUseImageCache; + std::string m_cryptomatteOutputPath; + bool m_cryptomattePreviewLayer; + struct CryptomatteAov { + std::shared_ptr aov[3]; + }; + struct CryptomatteAovs { + CryptomatteAov mat; + CryptomatteAov obj; + }; + std::unique_ptr m_cryptomatteAovs; + std::condition_variable* m_presentedConditionVariable = nullptr; bool* m_presentedCondition = nullptr; rprContextFlushFrameBuffers_func m_rprContextFlushFrameBuffers = nullptr; @@ -3670,9 +4093,14 @@ HdRprApi::~HdRprApi() { delete m_impl; } -rpr::Shape* HdRprApi::CreateMesh(const VtVec3fArray& points, const VtIntArray& pointIndexes, const VtVec3fArray& normals, const VtIntArray& normalIndexes, const VtVec2fArray& uv, const VtIntArray& uvIndexes, const VtIntArray& vpf, TfToken const& polygonWinding) { +rpr::Shape* HdRprApi::CreateMesh(VtArray const& pointSamples, VtIntArray const& pointIndexes, VtArray const& normalSamples, VtIntArray const& normalIndexes, VtArray const& uvSamples, VtIntArray const& uvIndexes, VtIntArray const& vpf, TfToken const& polygonWinding) { + m_impl->InitIfNeeded(); + return m_impl->CreateMesh(pointSamples, pointIndexes, normalSamples, normalIndexes, uvSamples, uvIndexes, vpf, polygonWinding); +} + +rpr::Shape* HdRprApi::CreateMesh(VtVec3fArray const& points, VtIntArray const& pointIndexes, VtVec3fArray const& normals, VtIntArray const& normalIndexes, VtVec2fArray const& uvs, VtIntArray const& uvIndexes, VtIntArray const& vpf, TfToken const& polygonWinding) { m_impl->InitIfNeeded(); - return m_impl->CreateMesh(points, pointIndexes, normals, normalIndexes, uv, uvIndexes, vpf, polygonWinding); + return m_impl->CreateMesh(points, pointIndexes, normals, normalIndexes, uvs, uvIndexes, vpf, polygonWinding); } rpr::Curve* HdRprApi::CreateCurve(VtVec3fArray const& points, VtIntArray const& indices, VtFloatArray const& radiuses, VtVec2fArray const& uvs, VtIntArray const& segmentPerCurve) { @@ -3684,14 +4112,14 @@ rpr::Shape* HdRprApi::CreateMeshInstance(rpr::Shape* prototypeMesh) { return m_impl->CreateMeshInstance(prototypeMesh); } -HdRprApiEnvironmentLight* HdRprApi::CreateEnvironmentLight(GfVec3f color, float intensity) { +HdRprApiEnvironmentLight* HdRprApi::CreateEnvironmentLight(GfVec3f color, float intensity, VtValue const& backgroundOverride) { m_impl->InitIfNeeded(); - return m_impl->CreateEnvironmentLight(color, intensity); + return m_impl->CreateEnvironmentLight(color, intensity, backgroundOverride); } -HdRprApiEnvironmentLight* HdRprApi::CreateEnvironmentLight(const std::string& prthTotexture, float intensity) { +HdRprApiEnvironmentLight* HdRprApi::CreateEnvironmentLight(const std::string& prthTotexture, float intensity, VtValue const& backgroundOverride) { m_impl->InitIfNeeded(); - return m_impl->CreateEnvironmentLight(prthTotexture, intensity); + return m_impl->CreateEnvironmentLight(prthTotexture, intensity, backgroundOverride); } void HdRprApi::SetTransform(HdRprApiEnvironmentLight* envLight, GfMatrix4f const& transform) { @@ -3781,9 +4209,9 @@ HdRprApiVolume* HdRprApi::CreateVolume( gridSize, voxelSize, gridBBLow, materialParams); } -RprUsdMaterial* HdRprApi::CreateMaterial(HdSceneDelegate* sceneDelegate, HdMaterialNetworkMap const& materialNetwork) { +RprUsdMaterial* HdRprApi::CreateMaterial(SdfPath const& materialId, HdSceneDelegate* sceneDelegate, HdMaterialNetworkMap const& materialNetwork) { m_impl->InitIfNeeded(); - return m_impl->CreateMaterial(sceneDelegate, materialNetwork); + return m_impl->CreateMaterial(materialId, sceneDelegate, materialNetwork); } RprUsdMaterial* HdRprApi::CreatePointsMaterial(VtVec3fArray const& colors) { @@ -3904,8 +4332,8 @@ void HdRprApi::CommitResources() { m_impl->CommitResources(); } -void HdRprApi::Resolve() { - m_impl->Resolve(); +void HdRprApi::Resolve(SdfPath const& aovId) { + m_impl->Resolve(aovId); } void HdRprApi::Render(HdRprRenderThread* renderThread) { diff --git a/pxr/imaging/plugin/hdRpr/rprApi.h b/pxr/imaging/plugin/hdRpr/rprApi.h index 605ca672e..68d1b2f44 100644 --- a/pxr/imaging/plugin/hdRpr/rprApi.h +++ b/pxr/imaging/plugin/hdRpr/rprApi.h @@ -71,8 +71,8 @@ class HdRprApi final { HdRprApi(HdRprDelegate* delegate); ~HdRprApi(); - HdRprApiEnvironmentLight* CreateEnvironmentLight(const std::string& pathTotexture, float intensity); - HdRprApiEnvironmentLight* CreateEnvironmentLight(GfVec3f color, float intensity); + HdRprApiEnvironmentLight* CreateEnvironmentLight(const std::string& pathTotexture, float intensity, VtValue const& backgroundOverride); + HdRprApiEnvironmentLight* CreateEnvironmentLight(GfVec3f color, float intensity, VtValue const& backgroundOverride); void SetTransform(HdRprApiEnvironmentLight* envLight, GfMatrix4f const& transform); void Release(HdRprApiEnvironmentLight* envLight); @@ -109,12 +109,13 @@ class HdRprApi final { void SetTransform(HdRprApiVolume* volume, GfMatrix4f const& transform); void Release(HdRprApiVolume* volume); - RprUsdMaterial* CreateMaterial(HdSceneDelegate* sceneDelegate, HdMaterialNetworkMap const& materialNetwork); + RprUsdMaterial* CreateMaterial(SdfPath const& materialId, HdSceneDelegate* sceneDelegate, HdMaterialNetworkMap const& materialNetwork); RprUsdMaterial* CreatePointsMaterial(VtVec3fArray const& colors); RprUsdMaterial* CreateDiffuseMaterial(GfVec3f const& color); void Release(RprUsdMaterial* material); - rpr::Shape* CreateMesh(const VtVec3fArray& points, const VtIntArray& pointIndexes, const VtVec3fArray& normals, const VtIntArray& normalIndexes, const VtVec2fArray& uv, const VtIntArray& uvIndexes, const VtIntArray& vpf, TfToken const& polygonWinding); + rpr::Shape* CreateMesh(VtVec3fArray const& points, VtIntArray const& pointIndexes, VtVec3fArray const& normals, VtIntArray const& normalIndexes, VtVec2fArray const& uvs, VtIntArray const& uvIndexes, VtIntArray const& vpf, TfToken const& polygonWinding); + rpr::Shape* CreateMesh(VtArray const& pointSamples, VtIntArray const& pointIndexes, VtArray const& normalSamples, VtIntArray const& normalIndexes, VtArray const& uvSamples, VtIntArray const& uvIndexes, VtIntArray const& vpf, TfToken const& polygonWinding); rpr::Shape* CreateMeshInstance(rpr::Shape* prototypeMesh); void SetMeshRefineLevel(rpr::Shape* mesh, int level); void SetMeshVertexInterpolationRule(rpr::Shape* mesh, TfToken boundaryInterpolation); @@ -158,7 +159,7 @@ class HdRprApi final { RenderStats GetRenderStats() const; void CommitResources(); - void Resolve(); + void Resolve(SdfPath const& aovId); void Render(HdRprRenderThread* renderThread); void AbortRender(); diff --git a/pxr/imaging/plugin/rprHoudini/CMakeLists.txt b/pxr/imaging/plugin/rprHoudini/CMakeLists.txt index 29f9889c1..d37ec04e0 100644 --- a/pxr/imaging/plugin/rprHoudini/CMakeLists.txt +++ b/pxr/imaging/plugin/rprHoudini/CMakeLists.txt @@ -20,19 +20,41 @@ target_link_libraries(RPR_for_Houdini rprUsd Houdini) +_pxr_init_rpath(rpath "houdini/dso") +_pxr_add_rpath(rpath "${CMAKE_INSTALL_PREFIX}/lib") +_pxr_install_rpath(rpath RPR_for_Houdini) + GroupSources(RPR_for_Houdini) -houdini_configure_target(RPR_for_Houdini "INSTDIR" "${CMAKE_INSTALL_PREFIX}/houdini/dso") +set_target_properties(RPR_for_Houdini PROPERTIES PREFIX "") +install(TARGETS RPR_for_Houdini + RUNTIME DESTINATION "houdini/dso" + LIBRARY DESTINATION "houdini/dso") set(HOUDINI_MAJOR_MINOR_VERSION "${Houdini_VERSION_MAJOR}.${Houdini_VERSION_MINOR}") -configure_file(activateHoudiniPlugin.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/activateHoudiniPlugin.cpp) -add_executable(activateHoudiniPlugin ${CMAKE_CURRENT_BINARY_DIR}/activateHoudiniPlugin.cpp) -set_property(TARGET activateHoudiniPlugin PROPERTY CXX_STANDARD 11) -target_link_libraries(activateHoudiniPlugin PRIVATE ghc_filesystem) -target_compile_definitions(activateHoudiniPlugin PRIVATE GHC_WIN_WSTRING_STRING_TYPE) -install( - TARGETS activateHoudiniPlugin - RUNTIME DESTINATION .) +configure_file(houdiniPluginActivator.cpp.in ${CMAKE_CURRENT_BINARY_DIR}/houdiniPluginActivator.cpp) + +function(add_plugin_activator name) + add_executable(${name} ${name}.cpp) + set_property(TARGET ${name} PROPERTY CXX_STANDARD 11) + target_link_libraries(${name} PRIVATE ghc_filesystem) + target_include_directories(${name} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) + target_compile_definitions(${name} PRIVATE GHC_WIN_WSTRING_STRING_TYPE) +endfunction() + +add_plugin_activator(activateHoudiniPlugin) +install(TARGETS activateHoudiniPlugin RUNTIME DESTINATION .) + +if(RPR_INSTALL_MATLIB_PACKAGE) + add_plugin_activator(activateMatlibPlugin) + install(TARGETS activateMatlibPlugin RUNTIME DESTINATION MaterialLibrary) + + install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/MatLib_INSTALL.md + DESTINATION MaterialLibrary + RENAME INSTALL.md) +endif() + install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/hda/rpr_exportRpr1.hda diff --git a/pxr/imaging/plugin/rprHoudini/LOP_RPRMaterialProperties.cpp b/pxr/imaging/plugin/rprHoudini/LOP_RPRMaterialProperties.cpp index 79e0b191d..1e8ecc996 100644 --- a/pxr/imaging/plugin/rprHoudini/LOP_RPRMaterialProperties.cpp +++ b/pxr/imaging/plugin/rprHoudini/LOP_RPRMaterialProperties.cpp @@ -33,11 +33,14 @@ PXR_NAMESPACE_OPEN_SCOPE static PRM_Name g_materialPath("materialPath", "Material Path"); static PRM_Name g_id("id", "ID"); +static PRM_Name g_cryptomatteName("cryptomatteName", "Cryptomatte Name"); 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"), + "The ID that corresponds to ID on materialId AOV."), + PRM_Template(PRM_STRING_E, 1, &g_cryptomatteName, nullptr, nullptr, nullptr, nullptr, nullptr, 1, + "String used to generate cryptomatte ID. If not specified, the path to a primitive used."), PRM_Template() }; @@ -70,13 +73,16 @@ OP_ERROR LOP_RPRMaterialProperties::cookMyLop(OP_Context &context) { 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)); + int id = evalInt(g_id.getToken(), 0, context.getTime()); + + UT_String cryptomatteName; + evalString(cryptomatteName, g_cryptomatteName.getToken(), 0, context.getTime()); + HUSD_AutoWriteLock writelock(editableDataHandle()); HUSD_AutoLayerLock layerlock(writelock); @@ -103,6 +109,9 @@ OP_ERROR LOP_RPRMaterialProperties::cookMyLop(OP_Context &context) { UsdShadeShader surfaceSource = material.ComputeSurfaceSource(RprUsdTokens->rpr); if (surfaceSource) { surfaceSource.CreateInput(RprUsdTokens->id, SdfValueTypeNames->Int).Set(id); + surfaceSource.CreateInput(RprUsdTokens->cryptomatteName, SdfValueTypeNames->String).Set(VtValue(std::string(cryptomatteName))); + } else { + addWarning(LOP_MESSAGE, "Material has no surface source"); } return error(); diff --git a/pxr/imaging/plugin/rprHoudini/MatLib_INSTALL.md b/pxr/imaging/plugin/rprHoudini/MatLib_INSTALL.md new file mode 100644 index 000000000..69342595a --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/MatLib_INSTALL.md @@ -0,0 +1,25 @@ +## Installation + +### Automatic + +Run `activateMatlibPlugin`. It will find your houdini preference dir and add material library package that will point to the current directory. + +### Manual + +Add a new Houdini package with such configuration json: +``` +{ + "env":[ + { + "RPR_MTLX_MATERIAL_LIBRARY_PATH":"path-to-the-package" + } + ] +} +``` +where `path-to-the-package` depends on where do you unzip the material library package and should point to the directory that contains INSTALL.md (this file) + +More info here https://www.sidefx.com/docs/houdini/ref/plugins.html + +## Feedback + +In case you encounter any issues or would like to make a feature request, please post an "issue" on our official [GitHub repository](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/issues) diff --git a/pxr/imaging/plugin/rprHoudini/VOP_RPRMaterial.cpp b/pxr/imaging/plugin/rprHoudini/VOP_RPRMaterial.cpp index e68f6b339..b611c7522 100644 --- a/pxr/imaging/plugin/rprHoudini/VOP_RPRMaterial.cpp +++ b/pxr/imaging/plugin/rprHoudini/VOP_RPRMaterial.cpp @@ -653,6 +653,8 @@ static const char* GetUIName(RprUsdMaterialNodeInfo const* shaderInfo) { } VOP_RPRMaterialOperator* VOP_RPRMaterialOperator::Create(RprUsdMaterialNodeInfo const* shaderInfo) { + TF_DEBUG(RPR_USD_DEBUG_MATERIAL_REGISTRY).Msg("Creating \"%s\" VOP node\n", shaderInfo->GetName()); + if (shaderInfo->GetName()) { if (std::strcmp("rpr_materialx_node", shaderInfo->GetName()) == 0) { return VOP_RPRMaterialOperator::_Create(shaderInfo); diff --git a/pxr/imaging/plugin/rprHoudini/activateHoudiniPlugin.cpp b/pxr/imaging/plugin/rprHoudini/activateHoudiniPlugin.cpp new file mode 100644 index 000000000..989531d5c --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/activateHoudiniPlugin.cpp @@ -0,0 +1,18 @@ +#include "houdiniPluginActivator.cpp" + +int main() { + int exitCode = ActivateHoudiniPlugin("RPR_for_Houdini", { + {"RPR", ""}, + {"HOUDINI_PATH", "/houdini"}, + {"PYTHONPATH", "/lib/python"}, +#if defined(_WIN32) || defined(_WIN64) + {"PATH", "/lib"} +#endif + }); + +#if defined(_WIN32) || defined(_WIN64) + system("pause"); +#endif + + return exitCode; +} diff --git a/pxr/imaging/plugin/rprHoudini/activateMatlibPlugin.cpp b/pxr/imaging/plugin/rprHoudini/activateMatlibPlugin.cpp new file mode 100644 index 000000000..9553726c0 --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/activateMatlibPlugin.cpp @@ -0,0 +1,13 @@ +#include "houdiniPluginActivator.cpp" + +int main() { + int exitCode = ActivateHoudiniPlugin("RPR_MaterialLibrary", { + {"RPR_MTLX_MATERIAL_LIBRARY_PATH", ""} + }); + +#if defined(_WIN32) || defined(_WIN64) + system("pause"); +#endif + + return exitCode; +} diff --git a/pxr/imaging/plugin/rprHoudini/hda/rpr_exportRpr1.hda b/pxr/imaging/plugin/rprHoudini/hda/rpr_exportRpr1.hda index 9b0114400..c63bbca3b 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/activateHoudiniPlugin.cpp.in b/pxr/imaging/plugin/rprHoudini/houdiniPluginActivator.cpp.in similarity index 90% rename from pxr/imaging/plugin/rprHoudini/activateHoudiniPlugin.cpp.in rename to pxr/imaging/plugin/rprHoudini/houdiniPluginActivator.cpp.in index c33a4bade..2a7f9d1f0 100644 --- a/pxr/imaging/plugin/rprHoudini/activateHoudiniPlugin.cpp.in +++ b/pxr/imaging/plugin/rprHoudini/houdiniPluginActivator.cpp.in @@ -186,7 +186,7 @@ fs::path GetHoudiniUserPrefDir(const char* hver) { return {}; } -int ActivateHoudiniPlugin() { +int ActivateHoudiniPlugin(std::string const& pluginName, std::vector> const& env) { auto houdiniUserPrefDir = GetHoudiniUserPrefDir("@HOUDINI_MAJOR_MINOR_VERSION@"); if (houdiniUserPrefDir.empty()) { std::cout << "Can not determine HOUDINI_USER_PREF_DIR. Please check your environment and specify HOUDINI_USER_PREF_DIR" << std::endl; @@ -200,29 +200,20 @@ int ActivateHoudiniPlugin() { return EXIT_FAILURE; } - auto rprPath = executablePath.parent_path().string(); - std::replace(rprPath.begin(), rprPath.end(), '\\', '/'); - std::cout << "RPR path: " << rprPath << std::endl; + auto package = executablePath.parent_path().lexically_normal().string(); + std::replace(package.begin(), package.end(), '\\', '/'); + std::cout << "Package path: " << package << std::endl; auto packagesDir = houdiniUserPrefDir / "packages"; fs::create_directories(packagesDir); - auto packageJsonFilepath = packagesDir / "RPR_for_Houdini.json"; + auto packageJsonFilepath = packagesDir / (pluginName + ".json"); std::ofstream packageJson(packageJsonFilepath); if (!packageJson.is_open()) { std::cout << "Failed to open output file: " << packageJsonFilepath << std::endl; return EXIT_FAILURE; } - std::vector> env = { - {"RPR", ""}, - {"HOUDINI_PATH", "/houdini"}, - {"PYTHONPATH", "/lib/python"}, -#if defined(_WIN32) || defined(_WIN64) - {"PATH", "/lib"} -#endif - }; - packageJson << '{'; packageJson << '\"' << "env" << '\"'; packageJson << ':'; @@ -232,7 +223,7 @@ int ActivateHoudiniPlugin() { packageJson << '\"' << it->first << '\"'; packageJson << ':'; packageJson << '\"'; - packageJson << rprPath << it->second; + packageJson << package << it->second; packageJson << '\"'; packageJson << '}'; if (std::next(it) != env.end()) { @@ -247,16 +238,6 @@ int ActivateHoudiniPlugin() { return EXIT_FAILURE; } - std::cout << "Successfully activated RPR plugin" << std::endl; + std::cout << "Successfully activated the plugin" << std::endl; return EXIT_SUCCESS; } - -int main() { - int exitCode = ActivateHoudiniPlugin(); - -#if defined(_WIN32) || defined(_WIN64) - system("pause"); -#endif - - return exitCode; -} diff --git a/pxr/imaging/plugin/rprHoudini/scripts/python/houRpr/materialLibrary.py b/pxr/imaging/plugin/rprHoudini/scripts/python/houRpr/materialLibrary.py index 71ea3a6f1..bd5095fbc 100644 --- a/pxr/imaging/plugin/rprHoudini/scripts/python/houRpr/materialLibrary.py +++ b/pxr/imaging/plugin/rprHoudini/scripts/python/houRpr/materialLibrary.py @@ -8,35 +8,6 @@ material_library=None -HOWTO_INSTALL_MD = ''' -### How to install -1. Download MaterialX version of "Radeon ProRender Material Library" - [link](https://drive.google.com/file/d/1e2Qys1UMi9pu_x3wW5ctm9b8_FIohLKG/view?usp=sharing) (TODO: host somewhere) -2. Unzip to any directory - this will be our `INSTALL_DIR`. -3. Add `RPR_MTLX_MATERIAL_LIBRARY_PATH` environment variable value of which is `INSTALL_DIR`. - This can be done in a few ways: - * by modifying houdini.env file. [More info](https://www.sidefx.com/docs/houdini/basics/config_env.html) at "Setting environment variables" - * by modifying `HOUDINI_USER_PREF_DIR/packages/RPR_for_Houdini.json` package file. [More info](https://www.sidefx.com/docs/houdini/ref/plugins.html) - * by setting environment variable globally. [More info](https://superuser.com/questions/284342/what-are-path-and-other-environment-variables-and-how-can-i-set-or-use-them) -4. Restart Houdini -''' - -# Generated from HOWTO_INSTALL_MD -HOWTO_INSTALL_HTML = ''' -

How to install

-
    -
  1. Download MaterialX version of "Radeon ProRender Material Library" - link (TODO: host somewhere)
  2. -
  3. Unzip to any directory - this will be our INSTALL_DIR.
  4. -
  5. Add RPR_MTLX_MATERIAL_LIBRARY_PATH environment variable value of which is INSTALL_DIR. - This can be done in a few ways:
      -
    • by modifying houdini.env file. More info at "Setting environment variables"
    • -
    • by modifying HOUDINI_USER_PREF_DIR/packages/RPR_for_Houdini.json package file. More info
    • -
    • by setting environment variable globally. More info
    • -
    -
  6. -
  7. Restart Houdini
  8. -
-''' - HELP_TEXT = ''' To import a material click on a corresponding swatch. The material is always imported as a separate "Material Library" LOP node. @@ -54,10 +25,9 @@ def recursive_mkdir(path): class MaterialLibrary: class InitError(Exception): - def __init__(self, brief_msg, full_msg): - self.brief_msg = brief_msg - self.full_msg = full_msg - super(MaterialLibrary.InitError, self).__init__(brief_msg) + def __init__(self, msg): + self.msg = msg + super(MaterialLibrary.InitError, self).__init__(msg) class MaterialData: @@ -73,9 +43,7 @@ def __init__(self): self.path = os.environ.get('RPR_MTLX_MATERIAL_LIBRARY_PATH') if not self.path: - raise MaterialLibrary.InitError( - 'Material Library is not installed.', - HOWTO_INSTALL_HTML) + raise MaterialLibrary.InitError('Material Library is not installed: install guide') materials_json_file = os.path.join(self.path, 'materials.json') try: @@ -109,13 +77,9 @@ def __init__(self): self.material_groups.sort(key=lambda entry: entry[0]) except IOError as e: - raise MaterialLibrary.InitError( - 'Corrupted Material Library.', - 'materials.json could not be read.') + raise MaterialLibrary.InitError('Corrupted Material Library: missing materials.json.') except ValueError: - raise MaterialLibrary.InitError( - 'Corrupted Material Library', - 'Invalid materials.json.') + raise MaterialLibrary.InitError('Corrupted Material Librar: invalid materials.json.') MATERIAL_LIBRARY_TAG='hdrpr_material_library_generated' @@ -623,9 +587,8 @@ def import_material(): except MaterialLibrary.InitError as e: msg = QtWidgets.QMessageBox(hou.qt.mainWindow()) msg.setIcon(QtWidgets.QMessageBox.Critical) - msg.setText(e.brief_msg) - msg.setInformativeText(e.full_msg) - msg.setWindowTitle("Error") + msg.setText(e.msg) + msg.setWindowTitle("Material Library initialization failed") msg.show() return diff --git a/pxr/imaging/rprUsd/CMakeLists.txt b/pxr/imaging/rprUsd/CMakeLists.txt index 38b9b5ec1..066c95da5 100644 --- a/pxr/imaging/rprUsd/CMakeLists.txt +++ b/pxr/imaging/rprUsd/CMakeLists.txt @@ -61,6 +61,7 @@ target_sources(rprUsd PRIVATE materialNodes/rpr/baseNode.h materialNodes/rpr/baseNode.cpp materialNodes/rpr/nodeInfo.h + materialNodes/rpr/toonNode.cpp materialNodes/rpr/catcherNode.cpp materialNodes/rpr/displaceNode.cpp materialNodes/rpr/materialXNode.cpp diff --git a/pxr/imaging/rprUsd/config.cpp b/pxr/imaging/rprUsd/config.cpp index 4f6e6cab9..8e38cea17 100644 --- a/pxr/imaging/rprUsd/config.cpp +++ b/pxr/imaging/rprUsd/config.cpp @@ -3,6 +3,7 @@ #include "pxr/base/arch/fileSystem.h" #include "pxr/base/tf/tf.h" #include "pxr/base/tf/instantiateSingleton.h" +#include "pxr/base/tf/envSetting.h" #include using json = nlohmann::json; @@ -19,6 +20,9 @@ using json = nlohmann::json; PXR_NAMESPACE_OPEN_SCOPE +TF_DEFINE_ENV_SETTING(HDRPR_CACHE_PATH_OVERRIDE, "", + "Set this to override shaders cache path"); + namespace { bool ArchCreateDirectory(const char* path) { @@ -29,6 +33,22 @@ bool ArchCreateDirectory(const char* path) { #endif } +bool ArchDirectoryExists(const char* path) { +#ifdef WIN32 + DWORD ftyp = GetFileAttributesA(path); + if (ftyp == INVALID_FILE_ATTRIBUTES) + return false; //something is wrong with your path! + + if (ftyp & FILE_ATTRIBUTE_DIRECTORY) + return true; // this is a directory! + + return false; // this is not a directory! +#else + throw std::runtime_error("ArchDirectoryExists not implemented for this platform"); + return false; +#endif +} + std::string GetAppDataPath() { #ifdef WIN32 char appDataPath[MAX_PATH]; @@ -60,6 +80,23 @@ std::string GetAppDataPath() { } std::string GetDefaultCacheDir(const char* cacheType) { + // Return HDRPR_CACHE_PATH_OVERRIDE if provided + auto overriddenCacheDir = TfGetEnvSetting(HDRPR_CACHE_PATH_OVERRIDE); + if (!overriddenCacheDir.empty()) { + overriddenCacheDir = overriddenCacheDir + ARCH_PATH_SEP + cacheType; + + bool directoryExists = ArchDirectoryExists(overriddenCacheDir.c_str()); + if (!directoryExists) { + bool succeeded = ArchCreateDirectory(overriddenCacheDir.c_str()); + if (!succeeded) + { + TF_RUNTIME_ERROR("Can't create shader cache directory at: %s", overriddenCacheDir.c_str()); + } + } + + return overriddenCacheDir; + } + auto cacheDir = ArchGetEnv("RPR"); if (cacheDir.empty()) { // Fallback to AppData diff --git a/pxr/imaging/rprUsd/coreImage.cpp b/pxr/imaging/rprUsd/coreImage.cpp index a2723c0e1..c9ce21783 100644 --- a/pxr/imaging/rprUsd/coreImage.cpp +++ b/pxr/imaging/rprUsd/coreImage.cpp @@ -39,13 +39,13 @@ rpr::ImageDesc GetRprImageDesc(rpr::ImageFormat format, uint32_t width, uint32_t } template -std::unique_ptr _ConvertTexture(GlfUVTextureData* textureData, rpr::ImageFormat const& srcFormat, uint32_t dstNumComponents, PixelConverterFunc&& converter) { - uint8_t* src = textureData->GetRawBuffer(); +std::unique_ptr _ConvertTexture(RprUsdTextureData* textureData, rpr::ImageFormat const& srcFormat, uint32_t dstNumComponents, PixelConverterFunc&& converter) { + uint8_t* src = textureData->GetData(); size_t srcPixelStride = srcFormat.num_components * sizeof(ComponentT); size_t dstPixelStride = dstNumComponents * sizeof(ComponentT); - size_t numPixels = size_t(textureData->ResizedWidth()) * textureData->ResizedHeight(); + size_t numPixels = size_t(textureData->GetWidth()) * textureData->GetHeight(); auto dstData = std::make_unique(numPixels * dstPixelStride); uint8_t* dst = dstData.get(); @@ -66,7 +66,7 @@ template <> struct WhiteColor { }; template -std::unique_ptr ConvertTexture(GlfUVTextureData* textureData, rpr::ImageFormat const& format, uint32_t dstNumComponents) { +std::unique_ptr ConvertTexture(RprUsdTextureData* textureData, rpr::ImageFormat const& format, uint32_t dstNumComponents) { if (dstNumComponents < format.num_components) { // Trim excessive channels return _ConvertTexture(textureData, format, dstNumComponents, @@ -129,10 +129,10 @@ std::unique_ptr ConvertTexture(GlfUVTextureData* textureData, rpr::Im return nullptr; } -rpr::Image* CreateRprImage(rpr::Context* context, GlfUVTextureData* textureData, uint32_t numComponentsRequired) { +rpr::Image* CreateRprImage(rpr::Context* context, RprUsdTextureData* textureData, uint32_t numComponentsRequired) { rpr::ImageFormat format = {}; - auto imageMetadata = RprUsdGetGlfTextureMetadata(textureData); + auto imageMetadata = textureData->GetGLMetadata(); switch (imageMetadata.glType) { case GL_UNSIGNED_BYTE: @@ -163,9 +163,9 @@ rpr::Image* CreateRprImage(rpr::Context* context, GlfUVTextureData* textureData, TF_RUNTIME_ERROR("Unsupported pixel data GLformat: %#x", imageMetadata.glFormat); return nullptr; } - rpr::ImageDesc desc = GetRprImageDesc(format, textureData->ResizedWidth(), textureData->ResizedHeight()); + rpr::ImageDesc desc = GetRprImageDesc(format, textureData->GetWidth(), textureData->GetHeight()); - auto textureBuffer = textureData->GetRawBuffer(); + auto textureBuffer = textureData->GetData(); std::unique_ptr convertedData; if (numComponentsRequired != 0 && @@ -181,7 +181,7 @@ rpr::Image* CreateRprImage(rpr::Context* context, GlfUVTextureData* textureData, if (convertedData) { textureBuffer = convertedData.get(); format.num_components = numComponentsRequired; - desc = GetRprImageDesc(format, textureData->ResizedWidth(), textureData->ResizedHeight()); + desc = GetRprImageDesc(format, textureData->GetWidth(), textureData->GetHeight()); } } @@ -198,12 +198,12 @@ rpr::Image* CreateRprImage(rpr::Context* context, GlfUVTextureData* textureData, } // namespace anonymous 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)) { + auto textureData = RprUsdTextureData::New(path); + if (!textureData) { return nullptr; } - return Create(context, {{0, textureData.operator->()}}, numComponentsRequired); + return Create(context, {{0, textureData.get()}}, numComponentsRequired); } RprUsdCoreImage* RprUsdCoreImage::Create(rpr::Context* context, uint32_t width, uint32_t height, rpr::ImageFormat format, void const* data, rpr::Status* status) { diff --git a/pxr/imaging/rprUsd/coreImage.h b/pxr/imaging/rprUsd/coreImage.h index ae64eb6d8..d8064bddf 100644 --- a/pxr/imaging/rprUsd/coreImage.h +++ b/pxr/imaging/rprUsd/coreImage.h @@ -15,7 +15,7 @@ limitations under the License. #define PXR_IMAGING_RPR_USD_CORE_IMAGE_H #include "pxr/imaging/rprUsd/api.h" -#include "pxr/imaging/glf/uvTextureData.h" +#include "pxr/imaging/rprUsd/util.h" #include @@ -30,9 +30,9 @@ class RprUsdCoreImage { struct UDIMTile { uint32_t id; - GlfUVTextureData* textureData; + RprUsdTextureData* textureData; - UDIMTile(uint32_t id, GlfUVTextureData* textureData) : id(id), textureData(textureData) {} + UDIMTile(uint32_t id, RprUsdTextureData* textureData) : id(id), textureData(textureData) {} }; RPRUSD_API static RprUsdCoreImage* Create(rpr::Context* context, std::vector const& textureData, uint32_t numComponentsRequired); diff --git a/pxr/imaging/rprUsd/debugCodes.cpp b/pxr/imaging/rprUsd/debugCodes.cpp index b32860920..c1f9043fc 100644 --- a/pxr/imaging/rprUsd/debugCodes.cpp +++ b/pxr/imaging/rprUsd/debugCodes.cpp @@ -22,6 +22,7 @@ TF_REGISTRY_FUNCTION(TfDebug) { TF_DEBUG_ENVIRONMENT_SYMBOL(RPR_USD_DEBUG_CORE_UNSUPPORTED_ERROR, "signal about unsupported errors"); TF_DEBUG_ENVIRONMENT_SYMBOL(RPR_USD_DEBUG_DUMP_MATERIALS, "Dump material networks to the files in the current working directory") TF_DEBUG_ENVIRONMENT_SYMBOL(RPR_USD_DEBUG_LEAKS, "signal about rpr_context leaks"); + TF_DEBUG_ENVIRONMENT_SYMBOL(RPR_USD_DEBUG_MATERIAL_REGISTRY, "Print debug info about material registry"); } bool RprUsdIsLeakCheckEnabled() { diff --git a/pxr/imaging/rprUsd/debugCodes.h b/pxr/imaging/rprUsd/debugCodes.h index 339fe0024..39ebd7a0f 100644 --- a/pxr/imaging/rprUsd/debugCodes.h +++ b/pxr/imaging/rprUsd/debugCodes.h @@ -23,7 +23,8 @@ PXR_NAMESPACE_OPEN_SCOPE TF_DEBUG_CODES( RPR_USD_DEBUG_CORE_UNSUPPORTED_ERROR, RPR_USD_DEBUG_DUMP_MATERIALS, - RPR_USD_DEBUG_LEAKS + RPR_USD_DEBUG_LEAKS, + RPR_USD_DEBUG_MATERIAL_REGISTRY ); RPRUSD_API diff --git a/pxr/imaging/rprUsd/helpers.h b/pxr/imaging/rprUsd/helpers.h index 416faf891..86381199e 100644 --- a/pxr/imaging/rprUsd/helpers.h +++ b/pxr/imaging/rprUsd/helpers.h @@ -23,27 +23,58 @@ PXR_NAMESPACE_OPEN_SCOPE template T RprUsdGetInfo(U* object, R info) { T value = {}; - size_t dummy; - RPR_ERROR_CHECK_THROW(object->GetInfo(info, sizeof(value), &value, &dummy), "Failed to get object info"); + RPR_ERROR_CHECK_THROW(object->GetInfo(info, sizeof(value), &value, nullptr), "Failed to get object info"); return value; } -template -std::string RprUsdGetStringInfo(U* object, R info) { +template +struct Buffer { + std::unique_ptr data; + size_t size; + + explicit operator bool() const { return data && size; } +}; + +template +Buffer RprUsdGetListInfo(GetInfoFunc&& getInfoFunc) { size_t size = 0; - RPR_ERROR_CHECK_THROW(object->GetInfo(info, sizeof(size), nullptr, &size), "Failed to get object info"); + RPR_ERROR_CHECK_THROW(getInfoFunc(sizeof(size), nullptr, &size), "Failed to get object info"); if (size <= 1) { return {}; } - auto buffer = std::make_unique(size); - RPR_ERROR_CHECK_THROW(object->GetInfo(info, size, buffer.get(), &size), "Failed to get object info"); + size_t numElements = size / sizeof(T); + auto buffer = std::make_unique(numElements); + RPR_ERROR_CHECK_THROW(getInfoFunc(size, buffer.get(), nullptr), "Failed to get object info"); - // discard null-terminator - --size; + return {std::move(buffer), numElements}; +} + +template +Buffer RprUsdGetListInfo(U* object, R info) { + return RprUsdGetListInfo([object, info](size_t size, void* data, size_t* size_ret) { return object->GetInfo(info, size, data, size_ret); }); +} - return std::string(buffer.get(), size); +template +std::string RprUsdGetStringInfo(U* object, R info) { + if (auto strBuffer = RprUsdGetListInfo(object, info)) { + // discard null-terminator + --strBuffer.size; + + return std::string(strBuffer.data.get(), strBuffer.size); + } + return {}; +} + +template +Wrapper* RprUsdGetRprObject(typename rpr::RprApiTypeOf::value rprApiObject) { + const void* customPtr = nullptr; + if (rprObjectGetCustomPointer(rprApiObject, &customPtr) != RPR_SUCCESS) { + return nullptr; + } + assert(dynamic_cast(static_cast(customPtr))); + return const_cast(static_cast(customPtr)); } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/imageCache.cpp b/pxr/imaging/rprUsd/imageCache.cpp index 9c633a749..f287852fd 100644 --- a/pxr/imaging/rprUsd/imageCache.cpp +++ b/pxr/imaging/rprUsd/imageCache.cpp @@ -81,7 +81,7 @@ RprUsdImageCache::GetImage( // Assume that all tiles have the same colorspace // auto data = tiles[0].textureData; - GLenum internalFormat = RprUsdGetGlfTextureMetadata(data).internalFormat; + GLenum internalFormat = data->GetGLMetadata().internalFormat; if (internalFormat == GL_SRGB || internalFormat == GL_SRGB8 || internalFormat == GL_SRGB_ALPHA || diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_blend.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_blend.mtlx index ea9bf069b..09af5a612 100644 --- a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_blend.mtlx +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_blend.mtlx @@ -4,7 +4,7 @@ - + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_orennayar.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_orennayar.mtlx index 3d5e46c12..628179625 100644 --- a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_orennayar.mtlx +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_orennayar.mtlx @@ -1,6 +1,6 @@ - + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_refraction.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_refraction.mtlx index dee6efb86..08fefaf6f 100644 --- a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_refraction.mtlx +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_refraction.mtlx @@ -1,6 +1,6 @@ - + diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/arithmeticNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/arithmeticNode.cpp index ca9d21568..0897933f3 100644 --- a/pxr/imaging/rprUsd/materialNodes/rpr/arithmeticNode.cpp +++ b/pxr/imaging/rprUsd/materialNodes/rpr/arithmeticNode.cpp @@ -9,6 +9,13 @@ PXR_NAMESPACE_OPEN_SCOPE +TF_DEFINE_PRIVATE_TOKENS(_tokens, + (color0) + (color1) + (color2) + (color3) +); + template RprUsd_MaterialNode* RprUsd_CreateRprNode( RprUsd_MaterialBuilderContext* ctx, @@ -33,7 +40,7 @@ RprUsd_RprNodeInfo* GetNodeInfo() { for (int i = 0; i < Node::kArity; ++i) { RprUsd_RprNodeInput input(RprUsd_RprNodeInput::kColor3); - input.name = TfStringPrintf("color%d", i); + input.name = _tokens->allTokens[i]; input.uiName = TfStringPrintf("Color %d", i); input.valueString = "0,0,0"; input.uiSoftMin = "0"; diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/catcherNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/catcherNode.cpp index 5ba58719f..7b92b250e 100644 --- a/pxr/imaging/rprUsd/materialNodes/rpr/catcherNode.cpp +++ b/pxr/imaging/rprUsd/materialNodes/rpr/catcherNode.cpp @@ -20,7 +20,7 @@ limitations under the License. PXR_NAMESPACE_OPEN_SCOPE -TF_DEFINE_PRIVATE_TOKENS(RprUsdRprCatcherNodeTokens, +TF_DEFINE_PRIVATE_TOKENS(_tokens, (in) (enable) ); @@ -49,10 +49,10 @@ class RprUsd_RprCatcherNode : public RprUsd_MaterialNode { bool SetInput( TfToken const& inputId, VtValue const& value) override { - if (inputId == RprUsdRprCatcherNodeTokens->in) { + if (inputId == _tokens->in) { m_output = value; return true; - } else if (inputId == RprUsdRprCatcherNodeTokens->enable) { + } else if (inputId == _tokens->enable) { if (value.IsHolding()) { *m_catcherToggle = value.UncheckedGet() != 0; return true; @@ -72,11 +72,11 @@ class RprUsd_RprCatcherNode : public RprUsd_MaterialNode { nodeInfo.name = "rpr_" + catcherType + "_catcher"; RprUsd_RprNodeInput in(RprUsdMaterialNodeElement::kSurfaceShader); - in.name = "in"; + in.name = _tokens->in; nodeInfo.inputs.push_back(in); RprUsd_RprNodeInput enable(RprUsdMaterialNodeElement::kBoolean); - enable.name = "enable"; + enable.name = _tokens->enable; enable.uiName = "Enable"; enable.valueString = "true"; nodeInfo.inputs.push_back(enable); diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/combineShadersNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/combineShadersNode.cpp index 7335443a0..9b67ce54f 100644 --- a/pxr/imaging/rprUsd/materialNodes/rpr/combineShadersNode.cpp +++ b/pxr/imaging/rprUsd/materialNodes/rpr/combineShadersNode.cpp @@ -72,17 +72,17 @@ class RprUsd_RprCombineShadersNode : public RprUsd_MaterialNode { nodeInfo.uiFolder = "Shaders"; RprUsd_RprNodeInput surfaceInput(RprUsdMaterialNodeElement::kSurfaceShader); - surfaceInput.name = "surface"; + surfaceInput.name = HdMaterialTerminalTokens->surface; surfaceInput.uiName = "Surface Shader"; nodeInfo.inputs.push_back(surfaceInput); RprUsd_RprNodeInput displacementInput(RprUsdMaterialNodeElement::kDisplacementShader); - displacementInput.name = "displacement"; + displacementInput.name = HdMaterialTerminalTokens->displacement; displacementInput.uiName = "Displacement Shader"; nodeInfo.inputs.push_back(displacementInput); RprUsd_RprNodeInput volumeInput(RprUsdMaterialNodeElement::kVolumeShader); - volumeInput.name = "volume"; + volumeInput.name = HdMaterialTerminalTokens->volume; volumeInput.uiName = "Volume Shader"; nodeInfo.inputs.push_back(volumeInput); diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/displaceNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/displaceNode.cpp index 79c2867f1..61014b53a 100644 --- a/pxr/imaging/rprUsd/materialNodes/rpr/displaceNode.cpp +++ b/pxr/imaging/rprUsd/materialNodes/rpr/displaceNode.cpp @@ -20,7 +20,7 @@ limitations under the License. PXR_NAMESPACE_OPEN_SCOPE -TF_DEFINE_PRIVATE_TOKENS(RprUsdRprDisplaceNodeTokens, +TF_DEFINE_PRIVATE_TOKENS(_tokens, (minscale) (maxscale) (in) @@ -54,7 +54,7 @@ class RprUsd_RprDisplaceNode : public RprUsd_MaterialNode { bool SetInput( TfToken const& inputId, VtValue const& value) override { - if (inputId == RprUsdRprDisplaceNodeTokens->minscale) { + if (inputId == _tokens->minscale) { if (value.IsHolding()) { m_displacementScale[0] = value.UncheckedGet(); } else { @@ -62,7 +62,7 @@ class RprUsd_RprDisplaceNode : public RprUsd_MaterialNode { m_displacementScale[0] = 0.0f; return false; } - } else if (inputId == RprUsdRprDisplaceNodeTokens->maxscale) { + } else if (inputId == _tokens->maxscale) { if (value.IsHolding()) { m_displacementScale[1] = value.UncheckedGet(); } else { @@ -70,7 +70,7 @@ class RprUsd_RprDisplaceNode : public RprUsd_MaterialNode { m_displacementScale[1] = 1.0f; return false; } - } else if (inputId == RprUsdRprDisplaceNodeTokens->in) { + } else if (inputId == _tokens->in) { if (value.IsHolding>()) { m_output = value; } else { @@ -104,16 +104,16 @@ class RprUsd_RprDisplaceNode : public RprUsd_MaterialNode { input.uiSoftMin = "0"; input.uiSoftMax = "1"; - input.name = "in"; + input.name = _tokens->in; input.uiName = "Displacement"; input.valueString = "0"; nodeInfo.inputs.push_back(input); - input.name = "minscale"; + input.name = _tokens->minscale; input.uiName = "Minimum Scale"; nodeInfo.inputs.push_back(input); - input.name = "maxscale"; + input.name = _tokens->maxscale; input.uiName = "Maximum Scale"; input.valueString = "1"; nodeInfo.inputs.push_back(input); diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.cpp index 453b280cd..11d090ce2 100644 --- a/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.cpp +++ b/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.cpp @@ -367,33 +367,33 @@ class RprUsd_RprMaterialXNode : public RprUsd_MaterialNode { nodeInfo.uiFolder = "Shaders"; RprUsd_RprNodeInput fileInput(RprUsdMaterialNodeElement::kFilepath); - fileInput.name = RprUsdRprMaterialXNodeTokens->file.GetText(); + fileInput.name = RprUsdRprMaterialXNodeTokens->file; fileInput.uiName = "File"; nodeInfo.inputs.push_back(fileInput); RprUsd_RprNodeInput stringInput(RprUsdMaterialNodeElement::kString); - stringInput.name = RprUsdRprMaterialXNodeTokens->string.GetText(); + stringInput.name = RprUsdRprMaterialXNodeTokens->string; stringInput.uiName = ""; // hide from UI nodeInfo.inputs.push_back(stringInput); RprUsd_RprNodeInput basePathInput(RprUsdMaterialNodeElement::kString); - basePathInput.name = RprUsdRprMaterialXNodeTokens->basePath.GetText(); + basePathInput.name = RprUsdRprMaterialXNodeTokens->basePath; basePathInput.uiName = ""; // hide from UI nodeInfo.inputs.push_back(basePathInput); RprUsd_RprNodeInput stPrimvarNameInput(RprUsdMaterialNodeElement::kString); - stPrimvarNameInput.name = RprUsdRprMaterialXNodeTokens->stPrimvarName.GetText(); + stPrimvarNameInput.name = RprUsdRprMaterialXNodeTokens->stPrimvarName; stPrimvarNameInput.uiName = "UV Primvar Name"; stPrimvarNameInput.valueString = "st"; nodeInfo.inputs.push_back(stPrimvarNameInput); RprUsd_RprNodeInput surfaceElementInput(RprUsdMaterialNodeElement::kString); - surfaceElementInput.name = RprUsdRprMaterialXNodeTokens->surfaceElement.GetText(); + surfaceElementInput.name = RprUsdRprMaterialXNodeTokens->surfaceElement; surfaceElementInput.uiName = "Surface Element"; nodeInfo.inputs.push_back(surfaceElementInput); RprUsd_RprNodeInput displacementElementInput(RprUsdMaterialNodeElement::kString); - displacementElementInput.name = RprUsdRprMaterialXNodeTokens->displacementElement.GetText(); + displacementElementInput.name = RprUsdRprMaterialXNodeTokens->displacementElement; displacementElementInput.uiName = "Displacement Element"; nodeInfo.inputs.push_back(displacementElementInput); diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/nodeInfo.h b/pxr/imaging/rprUsd/materialNodes/rpr/nodeInfo.h index 5364a904a..f2ec82cc5 100644 --- a/pxr/imaging/rprUsd/materialNodes/rpr/nodeInfo.h +++ b/pxr/imaging/rprUsd/materialNodes/rpr/nodeInfo.h @@ -15,9 +15,44 @@ limitations under the License. #define RPRUSD_MATERIAL_NODES_RPR_NODE_INFO_H #include "pxr/imaging/rprUsd/materialRegistry.h" +#include "pxr/base/gf/vec2f.h" + +namespace std { + +PXR_NAMESPACE_USING_DIRECTIVE + +inline std::string to_string(GfVec2f const& v) { + return TfStringPrintf("%f,%f", v[0], v[1]); +} + +inline std::string to_string(GfVec3f const& v) { + return TfStringPrintf("%f,%f,%f", v[0], v[1], v[2]); +} + +inline std::string to_string(TfToken const& v) { + return v.GetString(); +} + +} // namespace std PXR_NAMESPACE_OPEN_SCOPE +template +struct ToRprUsdMaterialNodeInputType; + +#define DEFINE_TYPE_CONVERSION(c_type, material_type) \ + template <> \ + struct ToRprUsdMaterialNodeInputType { \ + static constexpr RprUsdMaterialNodeInput::Type value = RprUsdMaterialNodeInput::k##material_type; \ + }; + +DEFINE_TYPE_CONVERSION(bool, Boolean); +DEFINE_TYPE_CONVERSION(int, Integer); +DEFINE_TYPE_CONVERSION(float, Float); +DEFINE_TYPE_CONVERSION(GfVec2f, Vector2); +DEFINE_TYPE_CONVERSION(GfVec3f, Color3); +DEFINE_TYPE_CONVERSION(TfToken, Token); + struct RprUsd_RprNodeInput : public RprUsdMaterialNodeInput { RprUsd_RprNodeInput(RprUsdMaterialNodeInput::Type type) : RprUsdMaterialNodeInput(type) {} const char* GetName() const override { return GetCStr(name); } @@ -29,9 +64,26 @@ struct RprUsd_RprNodeInput : public RprUsdMaterialNodeInput { const char* GetUIFolder() const override { return GetCStr(uiFolder); } const char* GetDocString() const override { return GetCStr(docString); } const char* GetValueString() const override { return GetCStr(valueString); } - std::vector const& GetTokenValues() const override { return m_tokenValues; } + std::vector const& GetTokenValues() const override { return tokenValues; } - std::string name; + template + RprUsd_RprNodeInput(TfToken const& name, T defaultValue, RprUsdMaterialNodeInput::Type type = RprUsdMaterialNodeInput::kInvalid, const char* uiName = nullptr) + : RprUsdMaterialNodeInput(type != RprUsdMaterialNodeInput::kInvalid ? type : ToRprUsdMaterialNodeInputType::value) + , name(name) + , uiSoftMin("0") + , uiSoftMax("1") + , value(VtValue(defaultValue)) + , valueString(std::to_string(defaultValue)) { + if (!uiName) { + this->uiName = name.GetString(); + this->uiName[0] = ::toupper(this->uiName[0]); + } else { + this->uiName = uiName; + } + + } + + TfToken name; std::string uiName; std::string uiMin; std::string uiSoftMin; @@ -39,8 +91,9 @@ struct RprUsd_RprNodeInput : public RprUsdMaterialNodeInput { std::string uiSoftMax; std::string uiFolder; std::string docString; + VtValue value; std::string valueString; - std::vector m_tokenValues; + std::vector tokenValues; }; struct RprUsd_RprNodeOutput : public RprUsdMaterialNodeElement { diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/toonNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/toonNode.cpp new file mode 100644 index 000000000..a88230024 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/rpr/toonNode.cpp @@ -0,0 +1,180 @@ +/************************************************************************ +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 "baseNode.h" +#include "nodeInfo.h" + +#include "pxr/imaging/rprUsd/materialHelpers.h" +#include "pxr/base/arch/attributes.h" +#include "pxr/base/gf/vec3f.h" + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEFINE_PRIVATE_TOKENS(_tokens, + (color) + (roughness) + (normal) + (shadowTint) + (midLevel) + (midLevelMix) + (midTint) + (highlightLevel) + (highlightLevelMix) + (highlightTint) + (interpolationMode) + (Linear) + (None) +); + +template +bool ProcessInput(TfToken const& inputId, VtValue const& inputValue, SmartPtr const& rprNode, rpr::MaterialNodeInput rprInput) { + if (inputValue.IsHolding() || + inputValue.IsHolding()) { + return SetRprInput(rprNode.get(), rprInput, inputValue) == RPR_SUCCESS; + } + TF_RUNTIME_ERROR("Input `%s` has invalid type: %s, expected - %s", inputId.GetText(), inputValue.GetTypeName().c_str(), TfType::Find().GetTypeName().c_str()); + return false; +} + +/// \class RprUsd_RprToonNode +/// +/// The node that wraps RPR nodes required to setup correct RPR toon shader. +/// +class RprUsd_RprToonNode : public RprUsd_MaterialNode { +public: + RprUsd_RprToonNode(RprUsd_MaterialBuilderContext* ctx) { + + rpr::Status status; + m_toonClosureNode.reset(ctx->rprContext->CreateMaterialNode(RPR_MATERIAL_NODE_TOON_CLOSURE, &status)); + if (!m_toonClosureNode) { + throw RprUsd_NodeError(RPR_GET_ERROR_MESSAGE(status, "Failed to create toon closure node", ctx->rprContext)); + } + m_rampNode.reset(ctx->rprContext->CreateMaterialNode(RPR_MATERIAL_NODE_TOON_RAMP, &status)); + if (!m_rampNode) { + throw RprUsd_NodeError(RPR_GET_ERROR_MESSAGE(status, "Failed to create toon ramp node", ctx->rprContext)); + } + status = m_toonClosureNode->SetInput(RPR_MATERIAL_INPUT_DIFFUSE_RAMP, m_rampNode.get()); + if (status != RPR_SUCCESS) { + throw RprUsd_NodeError(RPR_GET_ERROR_MESSAGE(status, "Failed to set ramp node input of closure node", ctx->rprContext)); + } + } + ~RprUsd_RprToonNode() override = default; + + VtValue GetOutput(TfToken const& outputId) override { + return VtValue(m_toonClosureNode); + } + + bool SetInput( + TfToken const& id, + VtValue const& value) override { + if (id == _tokens->shadowTint) { + return ProcessInput(id, value, m_rampNode, RPR_MATERIAL_INPUT_SHADOW); + } else if (id == _tokens->midLevel) { + return ProcessInput(id, value, m_rampNode, RPR_MATERIAL_INPUT_POSITION1); + } else if (id == _tokens->midLevelMix) { + return ProcessInput(id, value, m_rampNode, RPR_MATERIAL_INPUT_RANGE1); + } else if (id == _tokens->midTint) { + return ProcessInput(id, value, m_rampNode, RPR_MATERIAL_INPUT_MID); + } else if (id == _tokens->highlightLevel) { + return ProcessInput(id, value, m_rampNode, RPR_MATERIAL_INPUT_POSITION2); + } else if (id == _tokens->highlightLevelMix) { + return ProcessInput(id, value, m_rampNode, RPR_MATERIAL_INPUT_RANGE2); + } else if (id == _tokens->highlightTint) { + return ProcessInput(id, value, m_rampNode, RPR_MATERIAL_INPUT_HIGHLIGHT); + } else if (id == _tokens->interpolationMode) { + if (value.IsHolding()) { + int interpolationModeInt = value.UncheckedGet(); + auto interpolationMode = !interpolationModeInt ? RPR_INTERPOLATION_MODE_NONE : RPR_INTERPOLATION_MODE_LINEAR; + return m_rampNode->SetInput(RPR_MATERIAL_INPUT_INTERPOLATION, interpolationMode) == RPR_SUCCESS; + } + TF_RUNTIME_ERROR("Input `%s` has invalid type: %s, expected - `Token`", id.GetText(), value.GetTypeName().c_str()); + return false; + } else if (id == _tokens->color) { + return ProcessInput(id, value, m_toonClosureNode, RPR_MATERIAL_INPUT_COLOR); + } else if (id == _tokens->roughness) { + return ProcessInput(id, value, m_toonClosureNode, RPR_MATERIAL_INPUT_ROUGHNESS); + } else if (id == _tokens->normal) { + if (value.IsHolding() && + value.UncheckedGet() == GfVec3f(0.0f)) { + return m_toonClosureNode->SetInput(RPR_MATERIAL_INPUT_NORMAL, (rpr::MaterialNode*)nullptr) == RPR_SUCCESS; + } + + return ProcessInput(id, value, m_toonClosureNode, RPR_MATERIAL_INPUT_NORMAL); + } + + TF_RUNTIME_ERROR("Unknown input `%s` for RPR Toon node", id.GetText()); + return false; + } + + static RprUsd_RprNodeInfo* GetInfo() { + static RprUsd_RprNodeInfo* ret = nullptr; + if (ret) { + return ret; + } + + ret = new RprUsd_RprNodeInfo; + auto& nodeInfo = *ret; + + nodeInfo.name = "rpr_toon"; + nodeInfo.uiName = "RPR Toon"; + nodeInfo.uiFolder = "Shaders"; + + nodeInfo.inputs.emplace_back(_tokens->color, GfVec3f(1.0f)); + nodeInfo.inputs.emplace_back(_tokens->shadowTint, GfVec3f(0.0f)); + nodeInfo.inputs.emplace_back(_tokens->midLevel, 0.5f); + nodeInfo.inputs.emplace_back(_tokens->midLevelMix, 0.05f); + nodeInfo.inputs.emplace_back(_tokens->midTint, GfVec3f(0.4f)); + nodeInfo.inputs.emplace_back(_tokens->highlightLevel, 0.8f); + nodeInfo.inputs.emplace_back(_tokens->highlightLevelMix, 0.05f); + nodeInfo.inputs.emplace_back(_tokens->highlightTint, GfVec3f(0.8f)); + + RprUsd_RprNodeInput interpolationModeInput(_tokens->interpolationMode, _tokens->None); + interpolationModeInput.value = VtValue(0); + interpolationModeInput.tokenValues = {_tokens->None, _tokens->Linear}; + nodeInfo.inputs.push_back(interpolationModeInput); + + nodeInfo.inputs.emplace_back(_tokens->roughness, 1.0f); + nodeInfo.inputs.emplace_back(_tokens->normal, GfVec3f(0.0f), RprUsdMaterialNodeElement::kVector3, ""); + + RprUsd_RprNodeOutput output(RprUsdMaterialNodeElement::kSurfaceShader); + output.name = "surface"; + nodeInfo.outputs.push_back(output); + + return ret; + } + +private: + std::unique_ptr m_rampNode; + std::shared_ptr m_toonClosureNode; +}; + +ARCH_CONSTRUCTOR(RprUsd_InitDisplaceNode, 255, void) { + auto nodeInfo = RprUsd_RprToonNode::GetInfo(); + RprUsdMaterialRegistry::GetInstance().Register( + TfToken(nodeInfo->name, TfToken::Immortal), + [](RprUsd_MaterialBuilderContext* context, std::map const& parameters) { + auto node = new RprUsd_RprToonNode(context); + for (auto& input : RprUsd_RprToonNode::GetInfo()->inputs) { + auto it = parameters.find(input.name); + if (it == parameters.end()) { + node->SetInput(input.name, input.value); + } else { + node->SetInput(input.name, it->second); + } + } + return node; + }, + nodeInfo); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialRegistry.cpp b/pxr/imaging/rprUsd/materialRegistry.cpp index 0a85b5520..cdb9c005e 100644 --- a/pxr/imaging/rprUsd/materialRegistry.cpp +++ b/pxr/imaging/rprUsd/materialRegistry.cpp @@ -12,7 +12,6 @@ limitations under the License. ************************************************************************/ #include "pxr/imaging/rprUsd/util.h" -#include "pxr/imaging/glf/uvTextureData.h" #include "pxr/imaging/rprUsd/materialRegistry.h" #include "pxr/imaging/rprUsd/imageCache.h" #include "pxr/imaging/rprUsd/debugCodes.h" @@ -41,8 +40,8 @@ TF_DEFINE_ENV_SETTING(RPRUSD_MATERIAL_NETWORK_SELECTOR, "rpr", "Material network selector to be used in hdRpr"); TF_DEFINE_ENV_SETTING(RPRUSD_USE_RPRMTLXLOADER, true, "Whether to use RPRMtlxLoader or rprLoadMateriaX"); -TF_DEFINE_ENV_SETTING(RPRUSD_RPRMTLXLOADER_ENABLE_LOGGING, false, - "Enable logging of RPRMtlxLoader"); +TF_DEFINE_ENV_SETTING(RPRUSD_RPRMTLXLOADER_LOG_LEVEL, int(RPRMtlxLoader::LogLevel::Error), + "Set logging level of RPRMtlxLoader"); RprUsdMaterialRegistry::RprUsdMaterialRegistry() : m_materialNetworkSelector(TfGetEnvSetting(RPRUSD_MATERIAL_NETWORK_SELECTOR)) { @@ -61,13 +60,20 @@ RprUsdMaterialRegistry::GetRegisteredNodes() { TF_WARN("RPR environment variable is not set"); return m_registeredNodes; } + TF_DEBUG(RPR_USD_DEBUG_MATERIAL_REGISTRY).Msg("RPR: %s\n", RPR.c_str()); if (TfGetEnvSetting(RPRUSD_USE_RPRMTLXLOADER)) { MaterialX::FilePathVec libraryNames = {"libraries", "materials"}; MaterialX::FileSearchPath searchPath = RPR; m_mtlxLoader = std::make_unique(); m_mtlxLoader->SetupStdlib(libraryNames, searchPath); - m_mtlxLoader->SetLogging(TfGetEnvSetting(RPRUSD_RPRMTLXLOADER_ENABLE_LOGGING)); + + auto logLevel = RPRMtlxLoader::LogLevel(TfGetEnvSetting(RPRUSD_RPRMTLXLOADER_LOG_LEVEL)); + if (logLevel < RPRMtlxLoader::LogLevel::None || + logLevel > RPRMtlxLoader::LogLevel::Info) { + logLevel = RPRMtlxLoader::LogLevel::Error; + } + m_mtlxLoader->SetLogging(logLevel); } auto rprMaterialsPath = TfAbsPath(TfNormPath(RPR + "/materials")); @@ -78,6 +84,8 @@ RprUsdMaterialRegistry::GetRegisteredNodes() { } for (auto& file : materialFiles) { + TF_DEBUG(RPR_USD_DEBUG_MATERIAL_REGISTRY).Msg("Processing material: \"%s\"\n", file.c_str()); + // UI Folder corresponds to subsections on UI // e.g. $RPR/Patterns/material.mtlx corresponds to Pattern UI folder auto uiFolder = file.substr(rprMaterialsPath.size() + 1); @@ -124,7 +132,7 @@ void RprUsdMaterialRegistry::CommitResources( std::string path; uint32_t udimTileId; - GlfUVTextureDataRefPtr data; + RprUsdTextureDataRefPtr data; UniqueTextureInfo(std::string const& path, uint32_t udimTileId) : path(path), udimTileId(udimTileId), data(nullptr) {} @@ -171,8 +179,7 @@ void RprUsdMaterialRegistry::CommitResources( WorkParallelForN(uniqueTextures.size(), [&uniqueTextures](size_t begin, size_t end) { for (size_t i = begin; i < end; ++i) { - auto textureData = GlfUVTextureData::New(uniqueTextures[i].path, INT_MAX, 0, 0, 0, 0); - if (textureData && textureData->Read(0, false)) { + if (auto textureData = RprUsdTextureData::New(uniqueTextures[i].path)) { uniqueTextures[i].data = textureData; } else { TF_RUNTIME_ERROR("Failed to load %s texture", uniqueTextures[i].path.c_str()); @@ -349,6 +356,7 @@ void ConvertLegacyHdMaterialNetwork( } // namespace anonymous RprUsdMaterial* RprUsdMaterialRegistry::CreateMaterial( + SdfPath const& materialId, HdSceneDelegate* sceneDelegate, HdMaterialNetworkMap const& legacyNetworkMap, rpr::Context* rprContext, @@ -378,6 +386,7 @@ RprUsdMaterial* RprUsdMaterialRegistry::CreateMaterial( VtValue const& surfaceOutput, VtValue const& displacementOutput, VtValue const& volumeOutput, + const char* cryptomatteName, int materialId) { auto getTerminalRprNode = [](VtValue const& terminalOutput) -> rpr::MaterialNode* { @@ -401,10 +410,14 @@ RprUsdMaterial* RprUsdMaterialRegistry::CreateMaterial( 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"); + if (m_surfaceNode) { + if (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"); + } + + RPR_ERROR_CHECK(m_surfaceNode->SetName(cryptomatteName), "Failed to set material name"); } return m_volumeNode || m_surfaceNode || m_displacementNode; @@ -516,7 +529,8 @@ RprUsdMaterial* RprUsdMaterialRegistry::CreateMaterial( auto surfaceOutput = getTerminalOutput(HdMaterialTerminalTokens->surface); auto displacementOutput = getTerminalOutput(HdMaterialTerminalTokens->displacement); - int materialId = -1; + int materialRprId = -1; + std::string const* cryptomatteName = nullptr; auto surfaceTerminalIt = network.terminals.find(HdMaterialTerminalTokens->surface); if (surfaceTerminalIt != network.terminals.end()) { @@ -531,13 +545,26 @@ RprUsdMaterial* RprUsdMaterialRegistry::CreateMaterial( auto& value = idIt->second; if (value.IsHolding()) { - materialId = value.UncheckedGet(); + materialRprId = value.UncheckedGet(); + } + } + + auto cryptomatteNameIt = parameters.find(RprUsdTokens->cryptomatteName); + if (cryptomatteNameIt != parameters.end()) { + auto& value = cryptomatteNameIt->second; + + if (value.IsHolding()) { + cryptomatteName = &value.UncheckedGet(); } } } } - return out->Finalize(context, surfaceOutput, displacementOutput, volumeOutput, materialId) ? out.release() : nullptr; + if (!cryptomatteName) { + cryptomatteName = &materialId.GetString(); + } + + return out->Finalize(context, surfaceOutput, displacementOutput, volumeOutput, cryptomatteName->c_str(), materialRprId) ? out.release() : nullptr; } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialRegistry.h b/pxr/imaging/rprUsd/materialRegistry.h index 3ecb83e75..a1f35ead4 100644 --- a/pxr/imaging/rprUsd/materialRegistry.h +++ b/pxr/imaging/rprUsd/materialRegistry.h @@ -15,6 +15,7 @@ limitations under the License. #define RPRUSD_MATERIAL_REGISTRY_H #include "pxr/imaging/rprUsd/api.h" +#include "pxr/imaging/rprUsd/debugCodes.h" #include "pxr/imaging/hd/material.h" #include "pxr/base/arch/demangle.h" #include "pxr/base/tf/singleton.h" @@ -61,6 +62,7 @@ class RprUsdMaterialRegistry { RPRUSD_API RprUsdMaterial* CreateMaterial( + SdfPath const& materialId, HdSceneDelegate* sceneDelegate, HdMaterialNetworkMap const& networkMap, rpr::Context* rprContext, @@ -197,6 +199,8 @@ inline void RprUsdMaterialRegistry::Register( TfToken const& id, RprUsdMaterialNodeFactoryFnc factory, RprUsdMaterialNodeInfo const* info) { + TF_DEBUG(RPR_USD_DEBUG_MATERIAL_REGISTRY).Msg("Registering material node with id \"%s\"\n", id.GetText()); + RprUsdMaterialNodeDesc desc = {}; desc.factory = std::move(factory); desc.info = info; diff --git a/pxr/imaging/rprUsd/tokens.h b/pxr/imaging/rprUsd/tokens.h index 053776dd6..d5881bdb7 100644 --- a/pxr/imaging/rprUsd/tokens.h +++ b/pxr/imaging/rprUsd/tokens.h @@ -23,7 +23,8 @@ PXR_NAMESPACE_OPEN_SCOPE #define RPRUSD_TOKENS \ (rpr) \ /* UsdShadeShader */ \ - ((id, "rpr:id")) + ((id, "rpr:id")) \ + ((cryptomatteName, "rpr:cryptomatteName")) TF_DECLARE_PUBLIC_TOKENS(RprUsdTokens, RPRUSD_API, RPRUSD_TOKENS); diff --git a/pxr/imaging/rprUsd/util.cpp b/pxr/imaging/rprUsd/util.cpp index d9e754931..a4638486f 100644 --- a/pxr/imaging/rprUsd/util.cpp +++ b/pxr/imaging/rprUsd/util.cpp @@ -47,23 +47,163 @@ bool RprUsdInitGLApi() { #endif } -RprUsdGlfTextureMetadata RprUsdGetGlfTextureMetadata(GlfUVTextureData* uvTextureData) { - RprUsdGlfTextureMetadata ret = {}; #if PXR_VERSION >= 2011 -# if PXR_VERSION >= 2102 - auto hioFormat = uvTextureData->GetFormat(); -# else - auto hioFormat = uvTextureData->GetHioFormat(); -# endif - ret.glType = GlfGetGLType(hioFormat); - ret.glFormat = GlfGetGLFormat(hioFormat); - ret.internalFormat = GlfGetGLInternalFormat(hioFormat); -#else - ret.internalFormat = uvTextureData->GLInternalFormat(); - ret.glType = uvTextureData->GLType(); - ret.glFormat = uvTextureData->GLFormat(); -#endif + +static const RprUsdTextureData::GLMetadata g_GLMetadata[HioFormatCount] = +{ + // glFormat, glType, glInternatFormat // HioFormat + {GL_RED, GL_UNSIGNED_BYTE, GL_R8 }, // UNorm8 + {GL_RG, GL_UNSIGNED_BYTE, GL_RG8 }, // UNorm8Vec2 + {GL_RGB, GL_UNSIGNED_BYTE, GL_RGB8 }, // UNorm8Vec3 + {GL_RGBA, GL_UNSIGNED_BYTE, GL_RGBA8 }, // UNorm8Vec4 + + {GL_RED, GL_BYTE, GL_R8_SNORM }, // SNorm8 + {GL_RG, GL_BYTE, GL_RG8_SNORM }, // SNorm8Vec2 + {GL_RGB, GL_BYTE, GL_RGB8_SNORM }, // SNorm8Vec3 + {GL_RGBA, GL_BYTE, GL_RGBA8_SNORM }, // SNorm8Vec4 + + {GL_RED, GL_HALF_FLOAT, GL_R16F }, // Float16 + {GL_RG, GL_HALF_FLOAT, GL_RG16F }, // Float16Vec2 + {GL_RGB, GL_HALF_FLOAT, GL_RGB16F }, // Float16Vec3 + {GL_RGBA, GL_HALF_FLOAT, GL_RGBA16F }, // Float16Vec4 + + {GL_RED, GL_FLOAT, GL_R32F }, // Float32 + {GL_RG, GL_FLOAT, GL_RG32F }, // Float32Vec2 + {GL_RGB, GL_FLOAT, GL_RGB32F }, // Float32Vec3 + {GL_RGBA, GL_FLOAT, GL_RGBA32F }, // Float32Vec4 + + {GL_RED, GL_DOUBLE, GL_RED }, // Double64 + {GL_RG, GL_DOUBLE, GL_RG }, // Double64Vec2 + {GL_RGB, GL_DOUBLE, GL_RGB }, // Double64Vec3 + {GL_RGBA, GL_DOUBLE, GL_RGBA }, // Double64Vec4 + + {GL_RED, GL_UNSIGNED_SHORT,GL_R16UI }, // UInt16 + {GL_RG, GL_UNSIGNED_SHORT,GL_RG16UI }, // UInt16Vec2 + {GL_RGB, GL_UNSIGNED_SHORT,GL_RGB16UI }, // UInt16Vec3 + {GL_RGBA, GL_UNSIGNED_SHORT,GL_RGBA16UI }, // UInt16Vec4 + + {GL_RED, GL_SHORT, GL_R16I }, // Int16 + {GL_RG, GL_SHORT, GL_RG16I }, // Int16Vec2 + {GL_RGB, GL_SHORT, GL_RGB16I }, // Int16Vec3 + {GL_RGBA, GL_SHORT, GL_RGBA16I }, // Int16Vec4 + + {GL_RED, GL_UNSIGNED_INT, GL_R32UI }, // UInt32 + {GL_RG, GL_UNSIGNED_INT, GL_RG32UI }, // UInt32Vec2 + {GL_RGB, GL_UNSIGNED_INT, GL_RGB32UI }, // UInt32Vec3 + {GL_RGBA, GL_UNSIGNED_INT, GL_RGBA32UI }, // UInt32Vec4 + + {GL_RED, GL_INT, GL_R32I }, // Int32 + {GL_RG, GL_INT, GL_RG32I }, // Int32Vec2 + {GL_RGB, GL_INT, GL_RGB32I }, // Int32Vec3 + {GL_RGBA, GL_INT, GL_RGBA32I }, // Int32Vec4 + + {GL_NONE, GL_NONE, GL_NONE }, // UNorm8srgb - not supported by OpenGL + {GL_NONE, GL_NONE, GL_NONE }, // UNorm8Vec2srgb - not supported by OpenGL + {GL_RGB, GL_UNSIGNED_BYTE, GL_SRGB8, }, // UNorm8Vec3srgb + {GL_RGBA, GL_UNSIGNED_BYTE, GL_SRGB8_ALPHA8 }, // UNorm8Vec4sRGB + + {GL_RGB, GL_FLOAT, + GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT }, // BC6FloatVec3 + {GL_RGB, GL_FLOAT, + GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT }, // BC6UFloatVec3 + {GL_RGBA, GL_UNSIGNED_BYTE, + GL_COMPRESSED_RGBA_BPTC_UNORM }, // BC7UNorm8Vec4 + {GL_RGBA, GL_UNSIGNED_BYTE, + GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM }, // BC7UNorm8Vec4srgb + {GL_RGBA, GL_UNSIGNED_BYTE, + GL_COMPRESSED_RGBA_S3TC_DXT1_EXT }, // BC1UNorm8Vec4 + {GL_RGBA, GL_UNSIGNED_BYTE, + GL_COMPRESSED_RGBA_S3TC_DXT5_EXT }, // BC3UNorm8Vec4 +}; + +#endif // PXR_VERSION >= 2011 + +#if PXR_VERSION >= 2105 + +std::shared_ptr RprUsdTextureData::New(std::string const& filepath) { + auto ret = std::make_unique(); + auto hioImage = HioImage::OpenForReading(filepath); + if (!hioImage) { + return nullptr; + } + + ret->_hioStorageSpec.width = hioImage->GetWidth(); + ret->_hioStorageSpec.height = hioImage->GetHeight(); + ret->_hioStorageSpec.depth = 1; + ret->_hioStorageSpec.format = hioImage->GetFormat(); + ret->_hioStorageSpec.flipped = false; + + size_t dataSize = ret->_hioStorageSpec.width * ret->_hioStorageSpec.height * HioGetDataSizeOfFormat(ret->_hioStorageSpec.format); + ret->_data = std::make_unique(dataSize); + ret->_hioStorageSpec.data = ret->_data.get(); + + if (!hioImage->Read(ret->_hioStorageSpec)) { + return nullptr; + } + + return ret; +} + +uint8_t* RprUsdTextureData::GetData() const { + return _data.get(); +} + +int RprUsdTextureData::GetWidth() const { + return _hioStorageSpec.width; +} + +int RprUsdTextureData::GetHeight() const { + return _hioStorageSpec.height; +} + +RprUsdTextureData::GLMetadata RprUsdTextureData::GetGLMetadata() const { + return g_GLMetadata[_hioStorageSpec.format]; +} + +#else // PXR_VERSION < 2105 + +std::shared_ptr RprUsdTextureData::New(std::string const& filepath) { + auto ret = std::make_unique(); + + ret->_uvTextureData = GlfUVTextureData::New(filepath, INT_MAX, 0, 0, 0, 0); + if (!ret->_uvTextureData || !ret->_uvTextureData->Read(0, false)) { + return nullptr; + } + return ret; } +uint8_t* RprUsdTextureData::GetData() const { + return _uvTextureData->GetRawBuffer(); +} + +int RprUsdTextureData::GetWidth() const { + return _uvTextureData->ResizedWidth(); +} + +int RprUsdTextureData::GetHeight() const { + return _uvTextureData->ResizedHeight(); +} + +RprUsdTextureData::GLMetadata RprUsdTextureData::GetGLMetadata() const { +#if PXR_VERSION >= 2011 + +# if PXR_VERSION >= 2102 + HioFormat hioFormat = _uvTextureData->GetFormat(); +# else // PXR_VERSION < 2102 + HioFormat hioFormat = _uvTextureData->GetHioFormat(); +# endif // PXR_VERSION >= 2102 + return g_GLMetadata[hioFormat]; + +#else // PXR_VERSION < 2011 + RprUsdTextureData::GLMetadata ret; + ret.internalFormat = _uvTextureData->GLInternalFormat(); + ret.glType = _uvTextureData->GLType(); + ret.glFormat = _uvTextureData->GLFormat(); + return ret; +#endif // PXR_VERSION >= 2011 +} + +#endif // PXR_VERSION >= 2105 + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/util.h b/pxr/imaging/rprUsd/util.h index d9b912140..7f7130213 100644 --- a/pxr/imaging/rprUsd/util.h +++ b/pxr/imaging/rprUsd/util.h @@ -22,26 +22,47 @@ limitations under the License. #include "pxr/imaging/glf/glew.h" #endif +#if PXR_VERSION >= 2105 +#include "pxr/imaging/hio/image.h" +#else #include "pxr/imaging/glf/uvTextureData.h" +#endif #include PXR_NAMESPACE_OPEN_SCOPE -RPRUSD_API -bool RprUsdGetUDIMFormatString(std::string const& filepath, std::string* out_formatString); +class RPRUSD_API RprUsdTextureData { +public: + static std::shared_ptr New(std::string const& filepath); -RPRUSD_API -bool RprUsdInitGLApi(); + uint8_t* GetData() const; + int GetWidth() const; + int GetHeight() const; + + struct GLMetadata { + GLenum glFormat; + GLenum glType; + GLenum internalFormat; + }; + GLMetadata GetGLMetadata() const; -struct RprUsdGlfTextureMetadata { - GLenum glType; - GLenum glFormat; - GLenum internalFormat; +private: +#if PXR_VERSION >= 2105 + HioImage::StorageSpec _hioStorageSpec; + std::unique_ptr _data; +#else // PXR_VERSION < 2105 + GlfUVTextureDataRefPtr _uvTextureData; +#endif }; +using RprUsdTextureDataRefPtr = std::shared_ptr; + +RPRUSD_API +bool RprUsdGetUDIMFormatString(std::string const& filepath, std::string* out_formatString); + RPRUSD_API -RprUsdGlfTextureMetadata RprUsdGetGlfTextureMetadata(GlfUVTextureData* uvTextureData); +bool RprUsdInitGLApi(); PXR_NAMESPACE_CLOSE_SCOPE