diff --git a/.gitignore b/.gitignore index a007feab0..e4eee0ca1 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ build/* +pxr/imaging/plugin/hdRpr/python/__pycache__/ \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index 0327b6f18..185f0a949 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,9 @@ [submodule "deps/RPR"] path = deps/RPR url = https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderSDK +[submodule "deps/MaterialX"] + path = deps/MaterialX + url = https://github.com/materialx/MaterialX +[submodule "deps/ghc_filesystem"] + path = deps/ghc_filesystem + url = https://github.com/gulrak/filesystem diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 99d3ee944..eb5682c02 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,4 +1,77 @@ # Change Log + +## Version 2.0 +### New Features: +- The new plug-in version incorporates version 2.0 of our Radeon™ ProRender system and brings about the significant changes and enhancements: +Hardware-accelerated ray tracing on AMD Radeon™ RX 6000 series GPUs. + - Better scaling across multiple devices: the use of two or more GPUs gives a better performance improvement with Radeon ProRender 2.0 than with Radeon ProRender 1.0 in most cases. + - Less noise for a given amount of render samples. Using the same number of samples as with Radeon ProRender 1.0 may be slower in some scenes, but noise will be significantly lower. + - RPR 2.0 is the default render mode called “Full”. Users who wish to use RPR 1.0 can set the render quality mode to “Legacy”. + - A new setting for texture cache has been added to the RPR menu in Houdini. The specified folder will cache textures for rendering, and can be cleaned up by the user if it becomes too large. + - MaterialX networks can now be attached to an object. To use them, we have added an RPR Material Library LOP node. Add this node and select a MaterialX file and specify the surface shader to use. + - User customizable LPE outputs have been added. + - OpenColorIO support has been added, allowing to control textures colorspace and the rendering colorspace. +- Support of Houdini 18.5 and USD 20.11 has been added. +- The installer for Houdini is now an executable `activateHoudiniPlugin`. +- A rendering setting has been added to change noise “seed” to vary noise across frames. +- Support of Varying or Vertex primitive variables has been added. +- An “RPR Standard Render Vars” node in Houdini for AOVs has been added in the tab menu for nodes. + + +### Issues Fixed: +- Using the Uber shader with “Metalness” reflection mode now matches the Disney shader PBR standard more closely. Among other improvements, the USD Preview Surface node respects the “Specular” parameter better. +- Motion Blur, particularly for rotation, is now more correct. +- Depth of field was not respecting camera sensor size and focal length correctly — fixed. +- UVs with USD meshes using geomSubset — fixed. +- Some issues with textures embedded in USDZ files have been fixed. +- The visibility of mesh instances has been fixed. +- A mesh primitive was not always marked as dirty when the material changes or the geomSubset changes — fixed. +- .rpr export node to work with non-ASCII characters — fixed. +- An issue of locking with no lights in the scene has been fixed. +- Various issues with checking and preventing memory leaks have been fixed. +- Selection of instances in the viewport has been fixed: the objectID AOV is correct now. +- A crash when AOVs are resized in Houdini has been fixed. +- A crash when getting volume data directly from Houdini has been fixed. +- .rpr file export has been improved, and a HDA node to enable it in Houdini has been added. +- Rendered .rpr files exported from Houdini were flipped vertically — fixed. +- Issues have been fixed to compile on Ubuntu 20.04. +- Motion Blur was changed in USD 19.11 leading to incorrect (to little) motion blur effect. This is fixed to match the USD behavior. + +### Known Issues: +- RPR 2.0 has some forthcoming features. If these are needed, use the Legacy render mode: + - Heterogenous volumes; + - Adaptive sampling; + - Adaptive subdivision. +- The first render on macOS® with RPR 2.0 can take a few minutes to start, while the kernels are being compiled. + + +## 1.3 Release + +hdRpr installation scheme was changed - there is no more need to copy hdRpr package directly into Houdini. More info [here](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/285). + +### New features +* Parallelize texture loading ([PR #307](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/307)) +* Expose RPR-native materials ([PR #301](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/301)) +* Add RPR 2.0 (Northstar) support ([PR #297](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/297)) +* Add UsdRenderVar support (raw AOVs only) ([PR #300](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/300)) +* Add UDIM support (Northstar only) ([PR #298](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/298)) +* Improve AOV system ([PR #295](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/295)) +* Expose shadow and reflection catcher controls ([PR #294](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/294)) +* Expose caustics control ([PR #291](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/291)) +* Add render mode render setting ([PR #289](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/289)) +* Optimize batch rendering ([PR #288](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/288)) +* BasisCurves bezier support ([PR #282](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/282)) +* Speed up basis curves creation ([PR #302](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/302)) +* Add RIF-based tone mapping filter ([PR #283](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/283)) + +### Fixes +* Fixed depth of field ([PR #319](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/319)) +* Fixed normal input of AI denoise filter ([PR #305](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/305)) +* Fixed dome light visibility ([PR #292](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/292)) +* Now UV primvar name is queried from the bound material ([PR #296](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/296)) +* Use specular workflow on refractive materials for UsdPreviewSurface ([PR #299](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/299)) + + ## 1.2 Release ### New features - Volume rework ([PR #268](https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD/pull/268)) @@ -63,4 +136,4 @@ ## 1.0 Release - Denoiser support - Full Spectrum Rendering modes on Windows and Ubuntu -- Support for USDPreviewSurface, USD lights, and Volumes \ No newline at end of file +- Support for USDPreviewSurface, USD lights, and Volumes diff --git a/CMakeLists.txt b/CMakeLists.txt index 419afb8ca..fd610a8c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,11 +7,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/defaults ${CMAKE_SOURCE_DIR}/cmake/macros) include(Options) - -if(RPR_BUILD_AS_HOUDINI_PLUGIN) - include(Houdini) -endif(RPR_BUILD_AS_HOUDINI_PLUGIN) - include(ProjectDefaults) include(Packages) @@ -34,12 +29,10 @@ include(Public) set(CMAKE_CXX_STANDARD 14) -include_directories(${USD_INCLUDE_DIR}) -link_directories(${USD_LIBRARY_DIR}) -include_directories(${RPR_INCLUDE_DIR}) -link_directories(${RPR_LIBRARY_DIR}) -add_subdirectory(pxr/imaging/plugin/hdRpr) +add_subdirectory(deps) +add_subdirectory(pxr/imaging) -# install(FILES README.md DESTINATION .) -# install(FILES LICENSE.md DESTINATION .) +install(FILES README.md DESTINATION .) +install(FILES INSTALL.md DESTINATION .) +install(FILES LICENSE.md DESTINATION .) diff --git a/INSTALL.md b/INSTALL.md index a4a9ce848..c57e0f1f7 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -1,37 +1,66 @@ ## Installation from a package -### Automatic installation +### Houdini plugin -Installed [**Python**](https://www.python.org/downloads/) with PATH environment variable setup is required for automatic installation. +#### Manual -> If you do not want to install python, but you have already installed Houdini with a working license, you can use hython from the Houdini package. Follow instructions under [Others](#others) subsection. +Add a new Houdini package with such configuration json: +``` +{ + "env":[ + { + "RPR":"path-to-the-package" + }, + { + "HOUDINI_PATH":"$RPR/houdini" + }, + { + "PATH":"$RPR/lib" + }, + { + "PYTHONPATH":"$RPR/lib/python" + } + ] +} +``` +where `path-to-the-package` depends on where do you unzip hdRpr package and should point to the directory that contains INSTALL.md (this file) -#### Windows +More info here https://www.sidefx.com/docs/houdini/ref/plugins.html -Run **install.bat** +#### Automatic -#### Others +`activateHoudiniPlugin` executable can do the same for you automatically - it will try to find your houdini preference dir and add hdRpr package that will point to the current directory. -Use the installer script **install.py**. From a terminal run: -``` -python install.py -``` -And follow the instructions from the script +### Usdview plugin + +Here and next, HDRPR_PACKAGE_DIR is a path to the root of the package. + +* Set `RPR` environment variable to `HDRPR_PACKAGE_DIR` +* Add `HDRPR_PACKAGE_DIR/plugin` path entry to the `PXR_PLUGINPATH_NAME` environment variable +* Add `HDRPR_PACKAGE_DIR/lib/python` path entry to the `PYTHONPATH` environment variable +* Windows only: add the `HDRPR_PACKAGE_DIR/lib` path entry to the `PATH` environment variable -### Manual +OR -Copy content of hdRpr*.tar.gz to houdini installation directory. -Default destination path looks like: -* **Windows** `C:\Program Files\Side Effects Software\Houdini 18.0.287` -* **Ubuntu** `/opt/hfs18.0` -* **macOS** `/Applications/Houdini/Current/Frameworks/Houdini.framework/Versions/Current` +You can copy hdRpr package directories (lib and plugin) directly to the root of your USD package. ## Installation from sources -Run following command in configured cmake project: +1. Configure cmake project (more info about it in README.md) + +``` +cd RadeonProRenderUSD +mkdir build +cd build +cmake -DCMAKE_INSTALL_PREFIX=package .. ``` -cmake --build . --config Release --target INSTALL + +2. Build cmake project ``` +cmake --build . --config Release --target install +``` + +3. Follow instructions from "Installation from package" for either "Houdini plugin" or "Usdview plugin" depending on how you configured the project on step 1. ## Feedback diff --git a/README.md b/README.md index 4c8f329c2..9c7aaec44 100644 --- a/README.md +++ b/README.md @@ -42,29 +42,22 @@ Cloning into 'RadeonProRenderUSD'... ##### Required Components -##### UsdView plugin Components +##### USD Component -As many USD users get the USD libraries from different places, or compile their own, we tried to keep this as flexible as possible. You can download USD to build yourself from [GitHub](https://www.github.com/PixarAnimationStudios/USD). UsdView plugin is build by default (```RPR_BUILD_AS_HOUDINI_PLUGIN=FALSE```). +Provide USD in one of two ways: -| Dependency Name | Description | Version | -| ------------------ |----------------------------------------------------------------------- | ------- | -| USD_ROOT | USD directory with include and lib dirs | 20.02 | - -##### Houdini plugin Components +* An installation of USD. Define pxr_DIR to point to it when running cmake, if required. You can download USD to build yourself from [GitHub](https://www.github.com/PixarAnimationStudios/USD). +* The USD which is provided with Houdini. The HFS environment variable should point to the Houdini installation (the correct way is to run cmake from Houdini's `Command Line Tools` or by sourcing `houdini_setup`). You can download Houdini installer from [Downloads | SideFX](https://www.sidefx.com/download). -To build houdini plugin set cmake flag ```RPR_BUILD_AS_HOUDINI_PLUGIN=TRUE```. `HOUDINI_ROOT` is the directory containing the `houdini_setup` file. You can download Houdini installer from [Daily Builds | SideFX](https://www.sidefx.com/download/daily-builds/#category-gold). +##### MaterialX Component -| Dependency Name | Description | Version | -| ------------------ |----------------------------------------------------------------------- | ------- | -| HOUDINI_ROOT | Houdini toolkit directory | 18 | +By default, MaterialX library will be compiled from the sources located under a MaterialX submodule `deps/MaterialX`. +You can override this behavior by providing a complete build of MaterialX to cmake. Please note, on Linux for Houdini plugin, MaterialX should be compiled with `-D_GLIBCXX_USE_CXX11_ABI=0` definition as it is required by Houdini. ##### Optional Components ##### OpenVDB -Support for OpenVDB is disabled by default, and can optionally be enabled by -specifying the cmake flag ```RPR_ENABLE_OPENVDB_SUPPORT=TRUE```. - **Following dependency required only for usdview plugin, houdini is shipped with own build of openvdb** | Dependency Name | Description | Version | @@ -83,9 +76,8 @@ then you need to specify ```RPR_SDK_PLATFORM=centos6``` cmake flag ``` mkdir build cd build -cmake -DUSD_ROOT=/data/usd_build -DCMAKE_INSTALL_PREFIX=/data/usd_build .. -make -make install +cmake -Dpxr_DIR=/data/usd_build -DCMAKE_INSTALL_PREFIX=/data/usd_build .. +cmake --build . --config Release --target install ``` Supported Platforms @@ -97,21 +89,11 @@ Supported Platforms Try it out ----------------------------- -Set the environment variables specified by the script when it finishes and -launch ```usdview``` with a sample asset. - -``` -> usdview extras/usd/tutorials/convertingLayerFormats/Sphere.usda -``` - -And select RPR as the render delegate. +Follow instruction from INSTALL.md to activate the plugin. +Launch either usdview or Houdini's Solaris viewport and select RPR as the render delegate. #### Environment Variables -* `PXR_PLUGINPATH_NAME` - - If you want the RPR menu added to USDView (allows selecting device, and render quality). Set it to ``` PXR_PLUGINPATH_NAME=${USD_ROOT}/lib/python/rpr ```, where USD_ROOT is your USD install directory. - * `HDRPR_ENABLE_TRACING` Instruct Radeon ProRender to generate trace files for debugging purposes. The tracing will record all RPR commands with a memory dump of the data used. By default, RPR tracing is disabled. To enable it set `HDRPR_ENABLE_TRACING` to 1. diff --git a/cmake/defaults/Houdini.cmake b/cmake/defaults/Houdini.cmake deleted file mode 100644 index e19c8c8ee..000000000 --- a/cmake/defaults/Houdini.cmake +++ /dev/null @@ -1,40 +0,0 @@ -if(NOT DEFINED HOUDINI_ROOT) - if (NOT DEFINED ENV{HOUDINI_ROOT}) - message(FATAL_ERROR "Specify HOUDINI_ROOT") - endif() - set(HOUDINI_ROOT $ENV{HOUDINI_ROOT}) -endif() - -get_filename_component(HOUDINI_ROOT "${HOUDINI_ROOT}" REALPATH) - -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - message(STATUS "Setting CMAKE_INSTALL_PREFIX to HOUDINI_ROOT - ${HOUDINI_ROOT}") - set(HOUDINI_INSTALL_PREFIX ${HOUDINI_ROOT}) - if(APPLE) - set(HOUDINI_INSTALL_PREFIX ${HOUDINI_ROOT}/..) - endif() - set(CMAKE_INSTALL_PREFIX "${HOUDINI_INSTALL_PREFIX}" CACHE PATH "..." FORCE) -endif() - -find_program(HYTHON_EXECUTABLE hython - PATHS ${HOUDINI_ROOT}/bin) -if(NOT HYTHON_EXECUTABLE) - message(FATAL "Could not find hython executable. Please check HOUDINI_ROOT: ${HOUDINI_ROOT}") -endif() - -set(HOUDINI_INCLUDE_DIR ${HOUDINI_ROOT}/toolkit/include) -set(HOUDINI_RESOURCE_DIR_RELPATH ".") -if(WIN32) - set(HOUDINI_PLUGIN_INSTALL_RELPATH bin) - set(HOUDINI_LIB ${HOUDINI_ROOT}/custom/houdini/dsolib) -elseif(APPLE) - set(HOUDINI_PLUGIN_INSTALL_RELPATH Libraries) - set(HOUDINI_LIB ${HOUDINI_ROOT}/../Libraries) - set(HOUDINI_RESOURCE_DIR_RELPATH "Resources") -else() - set(HOUDINI_PLUGIN_INSTALL_RELPATH dsolib) - set(HOUDINI_LIB ${HOUDINI_ROOT}/dsolib) -endif() -add_definitions(-DBUILD_AS_HOUDINI_PLUGIN) - -list(APPEND CMAKE_PREFIX_PATH ${HOUDINI_LIB} ${HOUDINI_INCLUDE_DIR}) diff --git a/cmake/defaults/Options.cmake b/cmake/defaults/Options.cmake index ac77a61b2..4bf39b6f2 100644 --- a/cmake/defaults/Options.cmake +++ b/cmake/defaults/Options.cmake @@ -27,10 +27,9 @@ option(PXR_ENABLE_PYTHON_SUPPORT "Enable Python based components for USD" ON) option(PXR_USE_PYTHON_3 "Build Python bindings for Python 3" OFF) option(PXR_ENABLE_NAMESPACES "Enable C++ namespaces." ON) -option(PXR_SYMLINK_HEADER_FILES "Symlink the header files from, ie, pxr/base/lib/tf to CMAKE_DIR/pxr/base/tf, instead of copying; ensures that you may edit the header file in either location, and improves experience in IDEs which find normally the \"copied\" header, ie, CLion; has no effect on windows" OFF) +option(RPR_ENABLE_VULKAN_INTEROP_SUPPORT OFF "Build hdRpr with Vulkan interop support. Requires Vulkan cmake package") -option(RPR_BUILD_AS_HOUDINI_PLUGIN "Build RadeonProRender Houdini plugin" OFF) -option(RPR_ENABLE_OPENVDB_SUPPORT "Enable OpenVDB" ${RPR_BUILD_AS_HOUDINI_PLUGIN}) +option(PXR_SYMLINK_HEADER_FILES "Symlink the header files from, ie, pxr/base/lib/tf to CMAKE_DIR/pxr/base/tf, instead of copying; ensures that you may edit the header file in either location, and improves experience in IDEs which find normally the \"copied\" header, ie, CLion; has no effect on windows" OFF) # Precompiled headers are a win on Windows, not on gcc. set(pxr_enable_pch "OFF") diff --git a/cmake/defaults/Packages.cmake b/cmake/defaults/Packages.cmake index 38ff40e75..7471a62c9 100644 --- a/cmake/defaults/Packages.cmake +++ b/cmake/defaults/Packages.cmake @@ -30,7 +30,30 @@ set(build_shared_libs "${BUILD_SHARED_LIBS}") # USD Arnold Requirements # ---------------------------------------------- -find_package(USD REQUIRED) +# Try to find monolithic USD +find_package(USDMonolithic QUIET) + +if(NOT USDMonolithic_FOUND) + find_package(pxr CONFIG) + + if(NOT pxr_FOUND) + # Try to find USD as part of Houdini. + find_package(HoudiniUSD) + + if(HoudiniUSD_FOUND) + message(STATUS "Configuring Houdini plugin") + endif() + else() + message(STATUS "Configuring usdview plugin") + endif() +else() + message(STATUS "Configuring usdview plugin: monolithic USD") +endif() + +if(NOT pxr_FOUND AND NOT HoudiniUSD_FOUND AND NOT USDMonolithic_FOUND) + message(FATAL_ERROR "Required: USD install or Houdini with included USD.") +endif() + find_package(Rpr REQUIRED) find_package(Rif REQUIRED) @@ -44,91 +67,52 @@ set(CMAKE_THREAD_PREFER_PTHREAD TRUE) find_package(Threads REQUIRED) set(PXR_THREAD_LIBS "${CMAKE_THREAD_LIBS_INIT}") -if(RPR_BUILD_AS_HOUDINI_PLUGIN) +if(HoudiniUSD_FOUND) + set(HOUDINI_ROOT "$ENV{HFS}" CACHE PATH "Houdini installation dir") find_package(Houdini REQUIRED CONFIG PATHS ${HOUDINI_ROOT}/toolkit/cmake) - if(WIN32) - set(PYTHON_INCLUDE_DIRS ${HOUDINI_ROOT}/python27/include) - set(PYTHON_LIBRARY ${HOUDINI_ROOT}/python27/libs/python27.lib) - endif() - - find_package(PythonLibs 2.7 REQUIRED) + set(OPENEXR_LOCATION ${Houdini_USD_INCLUDE_DIR}) + set(OPENEXR_LIB_LOCATION ${Houdini_LIB_DIR}) +else() + # We are using python to generate source files find_package(PythonInterp 2.7 REQUIRED) - - set(TBB_INCLUDE_DIR ${HOUDINI_INCLUDE_DIR}) - set(TBB_LIBRARY ${HOUDINI_LIB}) - - set(GLEW_LOCATION ${HOUDINI_LIB}) - set(GLEW_INCLUDE_DIR ${HOUDINI_INCLUDE_DIR}) - - set(Boost_INCLUDE_DIRS ${HOUDINI_INCLUDE_DIR}) - find_library(Boost_LIBRARIES - NAMES libhboost_python libhboost_python-mt hboost_python hboost_python-mt - PATHS "${HOUDINI_LIB}" - NO_DEFAULT_PATH - NO_SYSTEM_ENVIRONMENT_PATH) -else(RPR_BUILD_AS_HOUDINI_PLUGIN) - # --Python. - if(PXR_USE_PYTHON_3) - find_package(PythonInterp 3.0 REQUIRED) - find_package(PythonLibs 3.0 REQUIRED) - else() - find_package(PythonInterp 2.7 REQUIRED) - find_package(PythonLibs 2.7 REQUIRED) - endif() - - # Set up a version string for comparisons. This is available - # as Boost_VERSION_STRING in CMake 3.14+ - set(boost_version_string "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") - - if (((${boost_version_string} VERSION_GREATER_EQUAL "1.67") AND - (${boost_version_string} VERSION_LESS "1.70")) OR - ((${boost_version_string} VERSION_GREATER_EQUAL "1.70") AND - Boost_NO_BOOST_CMAKE)) - # As of boost 1.67 the boost_python component name includes the - # associated Python version (e.g. python27, python36). After boost 1.70 - # the built-in cmake files will deal with this. If we are using boost - # that does not have working cmake files, or we are using a new boost - # and not using cmake's boost files, we need to do the below. - # - # Find the component under the versioned name and then set the generic - # Boost_PYTHON_LIBRARY variable so that we don't have to duplicate this - # logic in each library's CMakeLists.txt. - set(python_version_nodot "${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}") - find_package(Boost - COMPONENTS - python${python_version_nodot} - REQUIRED - ) - set(Boost_PYTHON_LIBRARY "${Boost_PYTHON${python_version_nodot}_LIBRARY}") - else() - find_package(Boost - COMPONENTS - python - REQUIRED - ) - endif() endif() -find_package(TBB REQUIRED COMPONENTS tbb) -add_definitions(${TBB_DEFINITIONS}) - -find_package(OpenGL REQUIRED) -find_package(GLEW REQUIRED) - if (NOT PXR_MALLOC_LIBRARY) if (NOT WIN32) message(STATUS "Using default system allocator because PXR_MALLOC_LIBRARY is unspecified") endif() endif() +find_package(MaterialX QUIET) + +if(RPR_ENABLE_VULKAN_INTEROP_SUPPORT) + find_package(Vulkan REQUIRED) +endif() # Third Party Plugin Package Requirements # ---------------------------------------------- -if(RPR_ENABLE_OPENVDB_SUPPORT) - find_package(OpenVDB REQUIRED) - find_package(OpenEXR REQUIRED COMPONENTS Half) +if(HoudiniUSD_FOUND) + find_package(OpenVDB REQUIRED) +else() + find_package(OpenVDB QUIET) +endif() + +if(OpenVDB_FOUND) + if(HoudiniUSD_FOUND) + find_package(OpenEXR QUIET COMPONENTS Half_sidefx) + endif() + + if(NOT OpenEXR_FOUND) + find_package(OpenEXR QUIET COMPONENTS Half) + endif() + + if(NOT OpenEXR_FOUND) + message(FATAL_ERROR "Failed to find Half library") + endif() +else() + message(STATUS "Skipping OpenVDB support") endif() # ---------------------------------------------- diff --git a/cmake/defaults/ProjectDefaults.cmake b/cmake/defaults/ProjectDefaults.cmake index 06cd70741..6205a72ce 100644 --- a/cmake/defaults/ProjectDefaults.cmake +++ b/cmake/defaults/ProjectDefaults.cmake @@ -67,3 +67,8 @@ execute_process( WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} OUTPUT_STRIP_TRAILING_WHITESPACE) add_definitions(-DRPR_GIT_SHORT_HASH="${RPR_GIT_SHORT_HASH}") + +if(MSVC) + # Allow running INSTALL target + set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +endif() \ No newline at end of file diff --git a/cmake/defaults/Version.cmake b/cmake/defaults/Version.cmake index 5b3e8c2b5..3507907d5 100644 --- a/cmake/defaults/Version.cmake +++ b/cmake/defaults/Version.cmake @@ -22,6 +22,6 @@ # language governing permissions and limitations under the Apache License. # # Versioning information -set(HD_RPR_MAJOR_VERSION "1") -set(HD_RPR_MINOR_VERSION "2") -set(HD_RPR_PATCH_VERSION "3") +set(HD_RPR_MAJOR_VERSION "2") +set(HD_RPR_MINOR_VERSION "0") +set(HD_RPR_PATCH_VERSION "1") diff --git a/cmake/macros/Private.cmake b/cmake/macros/Private.cmake index 330d545dd..c0c93304e 100644 --- a/cmake/macros/Private.cmake +++ b/cmake/macros/Private.cmake @@ -51,7 +51,7 @@ function(_copy_headers LIBRARY_NAME) OUTPUT ${outfile} COMMAND ${CMAKE_COMMAND} -E make_directory "${dir_to_create}" COMMAND ${CMAKE_COMMAND} -Dinfile="${infile}" -Doutfile="${outfile}" -P "${PROJECT_SOURCE_DIR}/cmake/macros/copyHeaderForBuild.cmake" - MAIN_DEPENDENCY "${infile}" + DEPENDS "${infile}" COMMENT "Copying ${f} ..." VERBATIM ) @@ -127,7 +127,7 @@ function(_install_python LIBRARY_NAME) foreach(file ${ip_FILES}) set(filesToInstall "") set(installDest - "${libPythonPrefix}/pxr/${LIBRARY_INSTALLNAME}") + "${libPythonPrefix}/rpr/${LIBRARY_INSTALLNAME}") # Only attempt to compile .py files. Files like plugInfo.json may also # be in this list @@ -163,10 +163,6 @@ function(_install_python LIBRARY_NAME) message(FATAL_ERROR "Cannot have non-Python file ${file} in PYTHON_FILES.") endif() - # Note that we always install under lib/python/pxr, even if we are in - # the third_party project. This means the import will always look like - # 'from pxr import X'. We need to do this per-loop iteration because - # the installDest may be different due to the presence of subdirs. install( FILES ${filesToInstall} @@ -273,7 +269,7 @@ function(_install_pyside_ui_files LIBRARY_NAME) install( FILES ${uiFiles} - DESTINATION "${libPythonPrefix}/pxr/${LIBRARY_INSTALLNAME}" + DESTINATION "${libPythonPrefix}/rpr/${LIBRARY_INSTALLNAME}" ) endfunction() # _install_pyside_ui_files @@ -619,29 +615,32 @@ function(_pxr_add_rpath rpathRef target) endfunction() function(_pxr_install_rpath rpathRef NAME) - if(APPLE) - set(final "@loader_path/.") - else() - # Get and remove the origin. - list(GET ${rpathRef} 0 origin) - set(rpath ${${rpathRef}}) - list(REMOVE_AT rpath 0) - - set(final "") - # Canonicalize and uniquify paths. - foreach(path ${rpath}) - # Strip trailing slashes. - string(REGEX REPLACE "/+$" "" path "${path}") - - # Ignore paths we already have. - if (NOT ";${final};" MATCHES ";${path};") - list(APPEND final "${path}") + # Get and remove the origin. + list(GET ${rpathRef} 0 origin) + set(rpath ${${rpathRef}}) + list(REMOVE_AT rpath 0) + + # Canonicalize and uniquify paths. + set(final "") + foreach(path ${rpath}) + if(APPLE) + if("${path}/" MATCHES "^[$]ORIGIN/") + # Replace with origin path. + string(REPLACE "$ORIGIN/" "@loader_path/" path "${path}/") endif() - endforeach() - endif() + endif() + + # Strip trailing slashes. + string(REGEX REPLACE "/+$" "" path "${path}") + + # Ignore paths we already have. + if (NOT ";${final};" MATCHES ";${path};") + list(APPEND final "${path}") + endif() + endforeach() set_target_properties(${NAME} - PROPERTIES + PROPERTIES INSTALL_RPATH_USE_LINK_PATH FALSE INSTALL_RPATH "${final}" ) @@ -942,12 +941,7 @@ function(_pxr_python_module NAME) APPEND PROPERTY PXR_PYTHON_MODULES ${pyModuleName} ) - # Always install under the 'pxr' module, rather than base on the - # project name. This makes importing consistent, e.g. - # 'from pxr import X'. Additionally, python libraries always install - # into the default lib install, not into the third_party subdirectory - # or similar. - set(libInstallPrefix "lib/python/pxr/${pyModuleName}") + set(libInstallPrefix "lib/python/rpr/${pyModuleName}") # Python modules need to be able to access their corresponding # wrapped library and the install lib directory. @@ -1142,15 +1136,11 @@ function(_pxr_library NAME) _get_install_dir("include/${PXR_PREFIX}/${NAME}" headerInstallPrefix) _get_install_dir("lib" libInstallPrefix) if(isPlugin) - if(RPR_BUILD_AS_HOUDINI_PLUGIN) - _get_install_dir("${HOUDINI_PLUGIN_INSTALL_RELPATH}" pluginInstallPrefix) - else(RPR_BUILD_AS_HOUDINI_PLUGIN) - _get_install_dir("plugin" pluginInstallPrefix) - if(NOT PXR_INSTALL_SUBDIR) - # XXX -- Why this difference? - _get_install_dir("plugin/usd" pluginInstallPrefix) - endif() - endif(RPR_BUILD_AS_HOUDINI_PLUGIN) + _get_install_dir("plugin" pluginInstallPrefix) + if(NOT PXR_INSTALL_SUBDIR) + # XXX -- Why this difference? + _get_install_dir("plugin/usd" pluginInstallPrefix) + endif() if(NOT isObject) # A plugin embedded in the monolithic library is found in @@ -1260,10 +1250,9 @@ function(_pxr_library NAME) ${PXR_PREFIX} ) target_include_directories(${NAME} - PRIVATE + PUBLIC "${CMAKE_BINARY_DIR}/include" "${CMAKE_BINARY_DIR}/${PXR_INSTALL_SUBDIR}/include" - PUBLIC ${args_INCLUDE_DIRS} ) @@ -1271,11 +1260,7 @@ function(_pxr_library NAME) _pxr_target_link_libraries(${NAME} ${args_LIBRARIES}) _pxr_init_rpath(rpath "${libInstallPrefix}") - if(RPR_BUILD_AS_HOUDINI_PLUGIN) - _pxr_add_rpath(rpath "${HOUDINI_LIB}") - else() - _pxr_add_rpath(rpath "${CMAKE_INSTALL_PREFIX}/lib") - endif() + _pxr_add_rpath(rpath "${CMAKE_INSTALL_PREFIX}/lib") _pxr_install_rpath(rpath ${NAME}) # diff --git a/cmake/macros/Public.cmake b/cmake/macros/Public.cmake index 1cc9e1556..ee5c0fef8 100644 --- a/cmake/macros/Public.cmake +++ b/cmake/macros/Public.cmake @@ -23,6 +23,24 @@ # include(Private) +function(GroupSources target) + get_target_property(${target}_SOURCES ${target} SOURCES) + foreach(FILE ${${target}_SOURCES}) + get_filename_component(ABS_FILE ${FILE} ABSOLUTE) + file(RELATIVE_PATH relPath ${CMAKE_CURRENT_SOURCE_DIR} ${ABS_FILE}) + + string(FIND "${relPath}" ".." out) + if("${out}" EQUAL 0) + source_group("external" FILES "${FILE}") + else() + get_filename_component(PARENT_DIR "${FILE}" DIRECTORY) + string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" "" GROUP "${PARENT_DIR}") + string(REPLACE "/" "\\" GROUP "${GROUP}") + source_group("${GROUP}" FILES "${FILE}") + endif() + endforeach() +endfunction() + macro(GetAppDataPath retVal) if(WIN32) if(DEFINED ENV{PROGRAMDATA}) @@ -274,10 +292,6 @@ function(pxr_library NAME) set(prefix "") set(suffix ${CMAKE_SHARED_LIBRARY_SUFFIX}) - if(RPR_BUILD_AS_HOUDINI_PLUGIN) - set(subdir "usd_plugins") - endif() - # Katana plugins install into a specific sub directory structure. # In particular, shared objects install into plugin/Libs if(args_KATANA_PLUGIN) @@ -372,8 +386,8 @@ function(pxr_setup_python) # Join these with a ', ' string(REPLACE ";" ", " pyModulesStr "${converted}") - # Install a pxr __init__.py with an appropriate __all__ - _get_install_dir(lib/python/pxr installPrefix) + # Install a rpr __init__.py with an appropriate __all__ + _get_install_dir(lib/python/rpr installPrefix) file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/generated_modules_init.py" "__all__ = [${pyModulesStr}]\n") diff --git a/cmake/macros/compilePython.py b/cmake/macros/compilePython.py index 58ab77ca2..d1b561c49 100755 --- a/cmake/macros/compilePython.py +++ b/cmake/macros/compilePython.py @@ -32,11 +32,13 @@ # file.py - the installed location of the file # file.pyc - the precompiled python file +from __future__ import print_function + import sys import py_compile if len(sys.argv) < 4: - print "Usage: %s src.py file.py file.pyc" % sys.argv[0] + print("Usage: {} src.py file.py file.pyc".format(sys.argv[0])) sys.exit(1) try: @@ -53,14 +55,14 @@ try: linenumber = exc_value[1][1] line = exc_value[1][3] - print '%s:%s: %s: "%s"' % (sys.argv[1], linenumber, error, line) + print('{}:{}: {}: "{}"'.format(sys.argv[1], linenumber, error, line)) except IndexError: - print '%s: Syntax error: "%s"' % (sys.argv[1], error) + print('{}: Syntax error: "{}"'.format(sys.argv[1], error)) else: - print "%s: Unhandled compile error: (%s) %s" % ( - sys.argv[1], compileError.exc_type_name, exc_value) + print("{}: Unhandled compile error: ({}) {}".format( + sys.argv[1], compileError.exc_type_name, exc_value)) sys.exit(1) except: exc_type, exc_value, exc_traceback = sys.exc_info() - print "%s: Unhandled exception: %s" % (sys.argv[1], exc_value) + print("{}: Unhandled exception: {}".format(sys.argv[1], exc_value)) sys.exit(1) diff --git a/cmake/modules/FindGLEW.cmake b/cmake/modules/FindGLEW.cmake deleted file mode 100644 index bf2d41e91..000000000 --- a/cmake/modules/FindGLEW.cmake +++ /dev/null @@ -1,132 +0,0 @@ -# -# Copyright 2016 Pixar -# -# Licensed under the Apache License, Version 2.0 (the "Apache License") -# with the following modification; you may not use this file except in -# compliance with the Apache License and the following modification to it: -# Section 6. Trademarks. is deleted and replaced with: -# -# 6. Trademarks. This License does not grant permission to use the trade -# names, trademarks, service marks, or product names of the Licensor -# and its affiliates, except as required to comply with Section 4(c) of -# the License and to reproduce the content of the NOTICE file. -# -# You may obtain a copy of the Apache License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the Apache License with the above modification is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the Apache License for the specific -# language governing permissions and limitations under the Apache License. -# -# Try to find GLEW library and include path. -# Once done this will define -# -# GLEW_FOUND -# GLEW_INCLUDE_DIR -# GLEW_LIBRARY -# - -include(FindPackageHandleStandardArgs) - -if (WIN32) - find_path(GLEW_INCLUDE_DIR - NAMES - GL/glew.h - HINTS - "${GLEW_LOCATION}/include" - "$ENV{GLEW_LOCATION}/include" - "${GLEW_INCLUDEDIR}" - PATHS - "$ENV{PROGRAMFILES}/GLEW/include" - "${PROJECT_SOURCE_DIR}/extern/glew/include" - DOC "The directory where GL/glew.h resides" ) - - if ("${CMAKE_GENERATOR}" MATCHES "[Ww]in64") - set(ARCH x64) - else() - set(ARCH x86) - endif() - - find_library(GLEW_LIBRARY - NAMES - glew GLEW glew32 glew32s - HINTS - "${GLEW_LOCATION}/lib" - "$ENV{GLEW_LOCATION}/lib" - PATHS - "$ENV{PROGRAMFILES}/GLEW/lib" - "${PROJECT_SOURCE_DIR}/extern/glew/bin" - "${PROJECT_SOURCE_DIR}/extern/glew/lib" - PATH_SUFFIXES - Release/${ARCH} - DOC "The GLEW library") -endif () - -if (${CMAKE_HOST_UNIX}) - find_path( GLEW_INCLUDE_DIR - NAMES - GL/glew.h - HINTS - "${GLEW_LOCATION}/include" - "$ENV{GLEW_LOCATION}/include" - "${GLEW_INCLUDEDIR}" - PATHS - /usr/include - /usr/local/include - /sw/include - /opt/local/include - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH - DOC "The directory where GL/glew.h resides" - ) - find_library( GLEW_LIBRARY - NAMES - GLEW glew - HINTS - "${GLEW_LOCATION}/lib" - "$ENV{GLEW_LOCATION}/lib" - PATHS - "${GLEW_LOCATION}/lib" - /usr/lib64 - /usr/lib - /usr/lib/${CMAKE_LIBRARY_ARCHITECTURE} - /usr/local/lib64 - /usr/local/lib - /sw/lib - /opt/local/lib - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH - DOC "The GLEW library") -endif () - - -if (GLEW_INCLUDE_DIR AND EXISTS "${GLEW_INCLUDE_DIR}/GL/glew.h") - - file(STRINGS "${GLEW_INCLUDE_DIR}/GL/glew.h" GLEW_4_2 REGEX "^#define GL_VERSION_4_2.*$") - if (GLEW_4_2) - set(OPENGL_4_2_FOUND TRUE) - else () - message(WARNING - "glew-1.7.0 or newer needed for supporting OpenGL 4.2 dependent features" - ) - endif () - - file(STRINGS "${GLEW_INCLUDE_DIR}/GL/glew.h" GLEW_4_3 REGEX "^#define GL_VERSION_4_3.*$") - if (GLEW_4_3) - SET(OPENGL_4_3_FOUND TRUE) - else () - message(WARNING - "glew-1.9.0 or newer needed for supporting OpenGL 4.3 dependent features" - ) - endif () - -endif () - -find_package_handle_standard_args(GLEW - REQUIRED_VARS - GLEW_INCLUDE_DIR - GLEW_LIBRARY -) diff --git a/cmake/modules/FindHoudiniUSD.cmake b/cmake/modules/FindHoudiniUSD.cmake new file mode 100644 index 000000000..d367dd178 --- /dev/null +++ b/cmake/modules/FindHoudiniUSD.cmake @@ -0,0 +1,178 @@ +# This finds Pixar USD which is part of a Houdini installation. + +set(HOUDINI_ROOT $ENV{HFS} CACHE PATH "Houdini installation directory") + +set(HUSD_REQ_VARS "") + +find_path(Houdini_USD_INCLUDE_DIR + "pxr/pxr.h" + PATHS ${HOUDINI_ROOT} + PATH_SUFFIXES "toolkit/include" + NO_DEFAULT_PATH) +list(APPEND HUSD_REQ_VARS "Houdini_USD_INCLUDE_DIR") + +find_path(Houdini_USD_LIB_DIR + "libpxr_tf${CMAKE_SHARED_LIBRARY_SUFFIX}" + PATHS ${HOUDINI_ROOT} + PATH_SUFFIXES + "dsolib" # Linux and Windows + "../Libraries" # macOS + "bin" # Windows (dll) + NO_DEFAULT_PATH) +list(APPEND HUSD_REQ_VARS "Houdini_USD_LIB_DIR") + +if(WIN32) + find_path(Houdini_USD_IMPLIB_DIR + "libpxr_tf.lib" + PATHS ${HOUDINI_ROOT} + PATH_SUFFIXES "custom/houdini/dsolib" # Windows (import lib) + NO_DEFAULT_PATH) + list(APPEND HUSD_REQ_VARS "Houdini_USD_IMPLIB_DIR") +endif() + +if(APPLE) + find_library(Houdini_TBB_LIB + NAMES tbb + PATHS ${HOUDINI_ROOT} + PATH_SUFFIXES "../Libraries" + NO_DEFAULT_PATH) +endif(APPLE) + +set(Houdini_Python_VARS Houdini_Python_INCLUDE_DIR Houdini_Python_LIB Houdini_Boostpython_LIB) +list(APPEND HUSD_REQ_VARS ${Houdini_Python_VARS}) + +foreach(python_major_minor "2;7" "3;7") + list(GET python_major_minor 0 py_major) + list(GET python_major_minor 1 py_minor) + + foreach(var ${Houdini_Python_VARS}) + set(${var} ${var}-NOTFOUND) + endforeach() + + find_path( + Houdini_Python_INCLUDE_DIR + "pyconfig.h" + PATHS ${HOUDINI_ROOT}/toolkit/include + PATH_SUFFIXES + "python${py_major}.${py_minor}" + "python${py_major}.${py_minor}m" # macOS Houdini 18.5 + NO_DEFAULT_PATH) + + find_file( + Houdini_Python_LIB + NAMES + "libpython${py_major}.${py_minor}${CMAKE_SHARED_LIBRARY_SUFFIX}" # Unix + "libpython${py_major}.${py_minor}m${CMAKE_SHARED_LIBRARY_SUFFIX}" # Unix Houdini 18.5 + "python${py_major}${py_minor}.lib" # Windows (import lib) + PATHS ${HOUDINI_ROOT} + PATH_SUFFIXES + "python/lib" # Linux + "../../../../Python.framework/Versions/Current/lib" # macOS + "python${py_major}${py_minor}/libs" # Windows (import lib) + NO_DEFAULT_PATH) + + find_file( + Houdini_Boostpython_LIB + "libhboost_python-mt${CMAKE_SHARED_LIBRARY_SUFFIX}" # Unix + "libhboost_python${py_major}${py_minor}-mt-x64${CMAKE_SHARED_LIBRARY_SUFFIX}" # Unix + "hboost_python-mt.lib" # Windows (import lib) + "hboost_python${py_major}${py_minor}-mt-x64.lib" # Windows Houdini 18.5 + PATHS ${HOUDINI_ROOT} + PATH_SUFFIXES + "dsolib" # Linux + "../Libraries" # macOS + "custom/houdini/dsolib" # Windows (import lib) + NO_DEFAULT_PATH) + + set(Houdini_Python_FOUND TRUE) + foreach(var ${Houdini_Python_VARS}) + if(NOT ${var}) + set(Houdini_Python_FOUND FALSE) + endif() + endforeach() + + if(Houdini_Python_FOUND) + break() + endif() +endforeach() + +find_program(HYTHON_EXECUTABLE hython + PATHS ${HOUDINI_ROOT} + PATH_SUFFIXES bin) +if(NOT HYTHON_EXECUTABLE) + message(FATAL "Could not find hython executable") +endif() +list(APPEND HUSD_REQ_VARS "HYTHON_EXECUTABLE") +set(PYTHON_EXECUTABLE ${HYTHON_EXECUTABLE}) + +if(WIN32) + set(Houdini_LIB_DIR ${HOUDINI_ROOT}/custom/houdini/dsolib) +elseif(APPLE) + set(Houdini_LIB_DIR ${HOUDINI_ROOT}/../Libraries) +else() + set(Houdini_LIB_DIR ${HOUDINI_ROOT}/dsolib) +endif() + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args( + "HoudiniUSD" + REQUIRED_VARS ${HUSD_REQ_VARS}) + +if(HoudiniUSD_FOUND) + message(STATUS " Houdini USD includes: ${Houdini_USD_INCLUDE_DIR}") + message(STATUS " Houdini USD libs: ${Houdini_USD_LIB_DIR}") + message(STATUS " Houdini Python includes: ${Houdini_Python_INCLUDE_DIR}") + message(STATUS " Houdini Python lib: ${Houdini_Python_LIB}") + message(STATUS " Houdini Boost.Python lib: ${Houdini_Boostpython_LIB}") +endif() + +if(HoudiniUSD_FOUND AND NOT TARGET hd) + # Generic creation of the usd targets. This is not meant to be perfect. The + # criteria is "does it work for our use". Also, these names match the ones + # of the USD distribution so we can use either without many conditions. + # We're missing the interlib dependencies here so the plugin has to link a + # bit more stuff explicitly. + foreach(targetName + arch tf gf js trace work plug vt ar kind sdf ndr sdr pcp usd usdGeom + usdVol usdLux usdMedia usdShade usdRender usdHydra usdRi usdSkel usdUI + usdUtils garch hf hio cameraUtil pxOsd glf hgi hgiGL hd hdSt hdx + usdImaging usdImagingGL usdRiImaging usdSkelImaging usdVolImaging + usdAppUtils usdviewq) + add_library(${targetName} SHARED IMPORTED) + set_target_properties(${targetName} PROPERTIES + IMPORTED_LOCATION "${Houdini_USD_LIB_DIR}/libpxr_${targetName}${CMAKE_SHARED_LIBRARY_SUFFIX}" + INTERFACE_INCLUDE_DIRECTORIES ${Houdini_USD_INCLUDE_DIR}) + if(MSVC) + set_target_properties(${targetName} PROPERTIES + IMPORTED_IMPLIB "${Houdini_USD_IMPLIB_DIR}/libpxr_${targetName}.lib") + endif() + if(WIN32) + # Shut up compiler about warnings from USD. + target_compile_options(${targetName} INTERFACE + "/wd4506" "/wd4244" "/wd4305" "/wd4267") + # For automatically linked libraries (python, tbb) + target_link_directories(${targetName} + INTERFACE "${HOUDINI_ROOT}/custom/houdini/dsolib") + endif() + endforeach() + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Houdini builds with the old ABI. We need to match. + add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0) + endif() + # Add python to tf, usdImagingGL targets. + target_include_directories(tf INTERFACE ${Houdini_Python_INCLUDE_DIR}) + target_link_libraries(tf INTERFACE ${Houdini_Python_LIB}) + target_include_directories(usdImagingGL INTERFACE + ${Houdini_Python_INCLUDE_DIR}) + target_link_libraries(usdImagingGL INTERFACE ${Houdini_Python_LIB}) + # Add Boost.Python to tf, vt. Should actually be in many more but that's + # more than enough to get it linked in for us. + target_link_libraries(tf INTERFACE ${Houdini_Boostpython_LIB}) + target_link_libraries(vt INTERFACE ${Houdini_Boostpython_LIB}) + if(APPLE) + target_link_libraries(tf INTERFACE ${Houdini_TBB_LIB}) + endif(APPLE) + # By default Boost links libraries implicitly for the user via pragma's, we do not want this + target_compile_definitions(tf INTERFACE -DHBOOST_ALL_NO_LIB) +endif() diff --git a/cmake/modules/FindOpenEXR.cmake b/cmake/modules/FindOpenEXR.cmake index 4454e69aa..595edfb4c 100644 --- a/cmake/modules/FindOpenEXR.cmake +++ b/cmake/modules/FindOpenEXR.cmake @@ -76,6 +76,7 @@ foreach(OPENEXR_LIB ${OpenEXR_FIND_COMPONENTS}) HINTS "${OPENEXR_LOCATION}" "$ENV{OPENEXR_LOCATION}" + "${OPENEXR_LIB_LOCATION}" PATH_SUFFIXES lib/ DOC diff --git a/cmake/modules/FindOpenVDB.cmake b/cmake/modules/FindOpenVDB.cmake index 208219716..b53ad296c 100644 --- a/cmake/modules/FindOpenVDB.cmake +++ b/cmake/modules/FindOpenVDB.cmake @@ -15,10 +15,10 @@ FIND_PACKAGE( PackageHandleStandardArgs ) set(OPENVDB_VERSION_FILE_RELPATH include/openvdb/version.h) set(OPENVDB_FIND_PATH "${OPENVDB_ROOT}" "$ENV{OPENVDB_ROOT}") -if(RPR_BUILD_AS_HOUDINI_PLUGIN) +if(HoudiniUSD_FOUND) set(OPENVDB_VERSION_FILE_RELPATH openvdb/version.h) - set(OPENVDB_FIND_PATH ${HOUDINI_INCLUDE_DIR}) -endif(RPR_BUILD_AS_HOUDINI_PLUGIN) + set(OPENVDB_FIND_PATH ${Houdini_USD_INCLUDE_DIR}) +endif(HoudiniUSD_FOUND) FIND_PATH( OPENVDB_LOCATION ${OPENVDB_VERSION_FILE_RELPATH} ${OPENVDB_FIND_PATH} @@ -26,15 +26,15 @@ FIND_PATH( OPENVDB_LOCATION ${OPENVDB_VERSION_FILE_RELPATH} NO_SYSTEM_ENVIRONMENT_PATH ) -if(RPR_BUILD_AS_HOUDINI_PLUGIN) - set(OpenVDB_INCLUDE_DIR ${HOUDINI_INCLUDE_DIR}) - set(OpenVDB_LIBRARY_DIR ${HOUDINI_LIB}) +if(HoudiniUSD_FOUND) + set(OpenVDB_INCLUDE_DIR ${Houdini_USD_INCLUDE_DIR}) + set(OpenVDB_LIBRARY_DIR ${Houdini_LIB_DIR}) set(OPENVDB_LIBRARY_NAME openvdb_sesi) -else() +else(HoudiniUSD_FOUND) set(OpenVDB_INCLUDE_DIR ${OPENVDB_LOCATION}/include) set(OpenVDB_LIBRARY_DIR ${OPENVDB_LOCATION}/lib) set(OPENVDB_LIBRARY_NAME openvdb) -endif(RPR_BUILD_AS_HOUDINI_PLUGIN) +endif(HoudiniUSD_FOUND) FIND_LIBRARY( OpenVDB_OPENVDB_LIBRARY ${OPENVDB_LIBRARY_NAME} PATHS "${OpenVDB_LIBRARY_DIR}" @@ -42,32 +42,34 @@ FIND_LIBRARY( OpenVDB_OPENVDB_LIBRARY ${OPENVDB_LIBRARY_NAME} NO_SYSTEM_ENVIRONMENT_PATH ) +FIND_PACKAGE_HANDLE_STANDARD_ARGS( OpenVDB + REQUIRED_VARS OPENVDB_LOCATION OpenVDB_OPENVDB_LIBRARY + ) + SET( OpenVDB_LIBRARIES "") LIST( APPEND OpenVDB_LIBRARIES "${OpenVDB_OPENVDB_LIBRARY}" ) -SET( OPENVDB_VERSION_FILE "${OpenVDB_INCLUDE_DIR}/openvdb/version.h" ) +if(OpenVDB_FOUND) + SET( OPENVDB_VERSION_FILE "${OpenVDB_INCLUDE_DIR}/openvdb/version.h" ) -FILE( STRINGS "${OPENVDB_VERSION_FILE}" openvdb_major_version_str - REGEX "^#define[\t ]+OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER[\t ]+.*") -FILE( STRINGS "${OPENVDB_VERSION_FILE}" openvdb_minor_version_str - REGEX "^#define[\t ]+OPENVDB_LIBRARY_MINOR_VERSION_NUMBER[\t ]+.*") -FILE( STRINGS "${OPENVDB_VERSION_FILE}" openvdb_patch_version_str - REGEX "^#define[\t ]+OPENVDB_LIBRARY_PATCH_VERSION_NUMBER[\t ]+.*") + FILE( STRINGS "${OPENVDB_VERSION_FILE}" openvdb_major_version_str + REGEX "^#define[\t ]+OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER[\t ]+.*") + FILE( STRINGS "${OPENVDB_VERSION_FILE}" openvdb_minor_version_str + REGEX "^#define[\t ]+OPENVDB_LIBRARY_MINOR_VERSION_NUMBER[\t ]+.*") + FILE( STRINGS "${OPENVDB_VERSION_FILE}" openvdb_patch_version_str + REGEX "^#define[\t ]+OPENVDB_LIBRARY_PATCH_VERSION_NUMBER[\t ]+.*") -STRING( REGEX REPLACE "^.*OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER[\t ]+([0-9]*).*$" "\\1" - _openvdb_major_version_number "${openvdb_major_version_str}") -STRING( REGEX REPLACE "^.*OPENVDB_LIBRARY_MINOR_VERSION_NUMBER[\t ]+([0-9]*).*$" "\\1" - _openvdb_minor_version_number "${openvdb_minor_version_str}") -STRING( REGEX REPLACE "^.*OPENVDB_LIBRARY_PATCH_VERSION_NUMBER[\t ]+([0-9]*).*$" "\\1" - _openvdb_patch_version_number "${openvdb_patch_version_str}") + STRING( REGEX REPLACE "^.*OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER[\t ]+([0-9]*).*$" "\\1" + _openvdb_major_version_number "${openvdb_major_version_str}") + STRING( REGEX REPLACE "^.*OPENVDB_LIBRARY_MINOR_VERSION_NUMBER[\t ]+([0-9]*).*$" "\\1" + _openvdb_minor_version_number "${openvdb_minor_version_str}") + STRING( REGEX REPLACE "^.*OPENVDB_LIBRARY_PATCH_VERSION_NUMBER[\t ]+([0-9]*).*$" "\\1" + _openvdb_patch_version_number "${openvdb_patch_version_str}") -SET( OpenVDB_MAJOR_VERSION ${_openvdb_major_version_number} - CACHE STRING "OpenVDB major version number" ) -SET( OpenVDB_MINOR_VERSION ${_openvdb_minor_version_number} - CACHE STRING "OpenVDB minor version number" ) -SET( OpenVDB_PATCH_VERSION ${_openvdb_patch_version_number} - CACHE STRING "OpenVDB patch version number" ) - -FIND_PACKAGE_HANDLE_STANDARD_ARGS( OpenVDB - REQUIRED_VARS OPENVDB_LOCATION OpenVDB_OPENVDB_LIBRARY - ) \ No newline at end of file + SET( OpenVDB_MAJOR_VERSION ${_openvdb_major_version_number} + CACHE STRING "OpenVDB major version number" ) + SET( OpenVDB_MINOR_VERSION ${_openvdb_minor_version_number} + CACHE STRING "OpenVDB minor version number" ) + SET( OpenVDB_PATCH_VERSION ${_openvdb_patch_version_number} + CACHE STRING "OpenVDB patch version number" ) +endif() diff --git a/cmake/modules/FindRif.cmake b/cmake/modules/FindRif.cmake index 55ac8294a..27972b7c1 100644 --- a/cmake/modules/FindRif.cmake +++ b/cmake/modules/FindRif.cmake @@ -18,10 +18,8 @@ elseif(WIN32) SET_RIF_VARIABLES(Windows) elseif(RPR_SDK_PLATFORM STREQUAL "ubuntu18.04") SET_RIF_VARIABLES(Ubuntu18) -elseif(RPR_SDK_PLATFORM STREQUAL "centos7") - SET_RIF_VARIABLES(CentOS7) else() - message(FATAL_ERROR "Unknown platform: ${RPR_SDK_PLATFORM}") + SET_RIF_VARIABLES(CentOS7) endif() find_library(RIF_LIBRARY @@ -38,16 +36,16 @@ if(WIN32) set(RIF_BINARIES ${RIF_LOCATION_LIB}/MIOpen.dll ${RIF_LOCATION_LIB}/RadeonImageFilters.dll - ${RIF_LOCATION_LIB}/RadeonML-MIOpen.dll - ${RIF_LOCATION_LIB}/RadeonML-DirectML.dll) + ${RIF_LOCATION_LIB}/RadeonML_MIOpen.dll + ${RIF_LOCATION_LIB}/RadeonML_DirectML.dll) else(WIN32) if(APPLE) set(RIF_DEPENDENCY_LIBRARIES - ${RIF_LOCATION_LIB}/libRadeonML-MPS.dylib) + ${RIF_LOCATION_LIB}/libRadeonML_MPS.dylib) else() set(RIF_DEPENDENCY_LIBRARIES ${RIF_LOCATION_LIB}/libMIOpen.so - ${RIF_LOCATION_LIB}/libRadeonML-MIOpen.so) + ${RIF_LOCATION_LIB}/libRadeonML_MIOpen.so) endif(APPLE) endif(WIN32) @@ -55,7 +53,7 @@ if(NOT DEFINED RIF_MODELS_DIR) set(RIF_MODELS_DIR "${RIF_LOCATION}/models") endif() -set(RIF_VERSION_FILE "${RIF_LOCATION_INCLUDE}/version.h") +set(RIF_VERSION_FILE "${RIF_LOCATION_INCLUDE}/RadeonImageFilters_version.h") if(NOT EXISTS ${RIF_VERSION_FILE}) message(FATAL_ERROR "Invalid RIF SDK: missing ${RIF_VERSION_FILE} file") endif() diff --git a/cmake/modules/FindRpr.cmake b/cmake/modules/FindRpr.cmake index f8da296c2..3f9a14791 100644 --- a/cmake/modules/FindRpr.cmake +++ b/cmake/modules/FindRpr.cmake @@ -1,3 +1,7 @@ +if(TARGET rpr) + return() +endif() + if(NOT RPR_LOCATION) set(RPR_LOCATION ${PROJECT_SOURCE_DIR}/deps/RPR/RadeonProRender) endif() @@ -16,12 +20,13 @@ if(APPLE) SET_RPR_VARIABLES(binMacOS) elseif(WIN32) SET_RPR_VARIABLES(libWin64) + if(NOT RPR_BIN_LOCATION) + set(RPR_BIN_LOCATION ${RPR_LOCATION}/binWin64) + endif() elseif(RPR_SDK_PLATFORM STREQUAL "ubuntu18.04") SET_RPR_VARIABLES(binUbuntu18) -elseif(RPR_SDK_PLATFORM STREQUAL "centos7") - SET_RPR_VARIABLES(binCentOS7) else() - message(FATAL_ERROR "Unknown platform: ${RPR_SDK_PLATFORM}") + SET_RPR_VARIABLES(binCentOS7) endif() find_library(RPR_LIBRARY @@ -44,60 +49,38 @@ find_library(RPR_LOADSTORE_LIBRARY NO_SYSTEM_ENVIRONMENT_PATH ) -if(WIN32) - if(NOT RPR_BIN_LOCATION) - set(RPR_BIN_LOCATION ${RPR_LOCATION}/binWin64) - endif() - - if (EXISTS "${RPR_BIN_LOCATION}/Tahoe64.dll") - set(RPR_TAHOE_BINARY ${RPR_BIN_LOCATION}/Tahoe64.dll) - endif() +foreach(entry "Tahoe64;TAHOE" "Northstar64;NORTHSTAR" "Hybrid;HYBRID") + list(GET entry 0 libName) + list(GET entry 1 libId) - if (EXISTS "${RPR_BIN_LOCATION}/Hybrid.dll") - set(RPR_HYBRID_BINARY ${RPR_BIN_LOCATION}/Hybrid.dll) + if(WIN32) + set(libPath ${RPR_BIN_LOCATION}/${libName}.dll) + if (EXISTS "${libPath}") + set(RPR_${libId}_BINARY ${libPath}) + endif() + else() + find_library(RPR_${libId}_BINARY + NAMES + ${libName} + ${libName}${CMAKE_SHARED_LIBRARY_SUFFIX} + PATHS + "${RPR_LOCATION_LIB}" + DOC + "Radeon ProRender ${libName} library path" + NO_DEFAULT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + ) endif() - set(RPR_BINARIES - ${RPR_BIN_LOCATION}/RadeonProRender64.dll - ${RPR_BIN_LOCATION}/RprLoadStore64.dll - ${RPR_TAHOE_BINARY} - ${RPR_HYBRID_BINARY}) -else() - find_library(RPR_HYBRID_BINARY - NAMES Hybrid${CMAKE_SHARED_LIBRARY_SUFFIX} - PATHS - "${RPR_LOCATION_LIB}" - DOC - "Radeon ProRender hybrid library path" - NO_DEFAULT_PATH - NO_SYSTEM_ENVIRONMENT_PATH - ) - if(RPR_HYBRID_BINARY) - set(RPR_PLUGIN_LIBRARIES ${RPR_HYBRID_BINARY}) + if(RPR_${libId}_BINARY) + set(RPR_PLUGINS ${RPR_PLUGINS} ${RPR_${libId}_BINARY}) endif() +endforeach() - find_library(RPR_TAHOE_BINARY - NAMES libTahoe64 Tahoe64 - PATHS - "${RPR_LOCATION_LIB}" - DOC - "Radeon ProRender tahoe library path" - NO_DEFAULT_PATH - NO_SYSTEM_ENVIRONMENT_PATH - ) - if(RPR_TAHOE_BINARY) - set(RPR_PLUGIN_LIBRARIES ${RPR_PLUGIN_LIBRARIES} ${RPR_TAHOE_BINARY}) - endif() -endif(WIN32) - -if(NOT RPR_TAHOE_BINARY AND NOT RPR_HYBRID_BINARY) +if(NOT RPR_PLUGINS) message(FATAL_ERROR "At least one RPR plugin required") endif() -if(NOT DEFINED RPR_TOOLS_LOCATION) - set(RPR_TOOLS_LOCATION ${RPR_LOCATION_INCLUDE}/../rprTools) -endif() - include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Rpr @@ -106,3 +89,7 @@ find_package_handle_standard_args(Rpr RPR_LOADSTORE_LIBRARY RPR_LIBRARY ) + +add_library(rpr INTERFACE) +target_include_directories(rpr INTERFACE ${RPR_LOCATION_INCLUDE}) +target_link_libraries(rpr INTERFACE ${RPR_LIBRARY} ${RPR_LOADSTORE_LIBRARY}) diff --git a/cmake/modules/FindTBB.cmake b/cmake/modules/FindTBB.cmake deleted file mode 100644 index 4c2d7f1ff..000000000 --- a/cmake/modules/FindTBB.cmake +++ /dev/null @@ -1,219 +0,0 @@ -# The MIT License (MIT) -# -# Copyright (c) 2015 Justus Calvin -# -# Permission is hereby granted, free of charge, to any person obtaining a copy -# of this software and associated documentation files (the "Software"), to deal -# in the Software without restriction, including without limitation the rights -# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -# copies of the Software, and to permit persons to whom the Software is -# furnished to do so, subject to the following conditions: -# -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# -# FindTBB -# ------- -# -# Find TBB include directories and libraries. -# -# Usage: -# -# find_package(TBB [major[.minor]] [EXACT] -# [QUIET] [REQUIRED] -# [[COMPONENTS] [components...]] -# [OPTIONAL_COMPONENTS components...]) -# -# where the allowed components are tbbmalloc and tbb_preview. Users may modify -# the behavior of this module with the following variables: -# -# * TBB_ROOT_DIR - The base directory the of TBB installation. -# * TBB_INCLUDE_DIR - The directory that contains the TBB headers files. -# * TBB_LIBRARY - The directory that contains the TBB library files. -# * TBB__LIBRARY - The path of the TBB the corresponding TBB library. -# These libraries, if specified, override the -# corresponding library search results, where -# may be tbb, tbb_debug, tbbmalloc, tbbmalloc_debug, -# tbb_preview, or tbb_preview_debug. -# * TBB_USE_DEBUG_BUILD - The debug version of tbb libraries, if present, will -# be used instead of the release version. -# -# Users may modify the behavior of this module with the following environment -# variables: -# -# * TBB_INSTALL_DIR -# * TBBROOT -# * LIBRARY_PATH -# -# This module will set the following variables: -# -# * TBB_FOUND - Set to false, or undefined, if we haven’t found, or -# don’t want to use TBB. -# * TBB__FOUND - If False, optional part of TBB sytem is -# not available. -# * TBB_VERSION - The full version string -# * TBB_VERSION_MAJOR - The major version -# * TBB_VERSION_MINOR - The minor version -# * TBB_INTERFACE_VERSION - The interface version number defined in -# tbb/tbb_stddef.h. -# * TBB__LIBRARY_RELEASE - The path of the TBB release version of -# , where may be tbb, tbb_debug, -# tbbmalloc, tbbmalloc_debug, tbb_preview, or -# tbb_preview_debug. -# * TBB__LIBRARY_DEGUG - The path of the TBB release version of -# , where may be tbb, tbb_debug, -# tbbmalloc, tbbmalloc_debug, tbb_preview, or -# tbb_preview_debug. -# -# The following varibles should be used to build and link with TBB: -# -# * TBB_INCLUDE_DIRS - The include directory for TBB. -# * TBB_LIBRARIES - The libraries to link against to use TBB. -# * TBB_DEFINITIONS - Definitions to use when compiling code that uses TBB. - -include(FindPackageHandleStandardArgs) - -if(NOT TBB_FOUND) - - ################################## - # Check the build type - ################################## - - if(NOT DEFINED TBB_USE_DEBUG_BUILD) - if(CMAKE_BUILD_TYPE MATCHES "Debug|DEBUG|debug") - set(TBB_USE_DEBUG_BUILD TRUE) - else() - set(TBB_USE_DEBUG_BUILD FALSE) - endif() - endif() - - ################################## - # Set the TBB search directories - ################################## - - # Define search paths based on user input and environment variables - set(TBB_SEARCH_DIR ${TBB_ROOT_DIR} $ENV{TBB_INSTALL_DIR} $ENV{TBBROOT}) - - # Define the search directories based on the current platform - if(CMAKE_SYSTEM_NAME STREQUAL "Windows") - set(TBB_DEFAULT_SEARCH_DIR "C:/Program Files/Intel/TBB" - "C:/Program Files (x86)/Intel/TBB") - # TODO: Set the proper suffix paths based on compiler introspection. - - elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - # OS X - set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") - - # TODO: Check to see which C++ library is being used by the compiler. - if(NOT ${CMAKE_SYSTEM_VERSION} VERSION_LESS 13.0) - # The default C++ library on OS X 10.9 and later is libc++ - set(TBB_LIB_PATH_SUFFIX "lib/libc++") - else() - set(TBB_LIB_PATH_SUFFIX "lib") - endif() - elseif(CMAKE_SYSTEM_NAME STREQUAL "Linux") - # Linux - set(TBB_DEFAULT_SEARCH_DIR "/opt/intel/tbb") - - # TODO: Check compiler version to see the suffix should be /gcc4.1 or - # /gcc4.1. For now, assume that the compiler is more recent than - # gcc 4.4.x or later. - if(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") - set(TBB_LIB_PATH_SUFFIX "lib/intel64/gcc4.4") - elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^i.86$") - set(TBB_LIB_PATH_SUFFIX "lib/ia32/gcc4.4") - endif() - endif() - - ################################## - # Find the TBB include dir - ################################## - - find_path(TBB_INCLUDE_DIRS tbb/tbb.h - HINTS ${TBB_INCLUDE_DIR} ${TBB_SEARCH_DIR} - PATHS ${TBB_DEFAULT_SEARCH_DIR} - PATH_SUFFIXES include) - - ################################## - # Find TBB components - ################################## - - # Find each component - foreach(_comp tbb_preview tbbmalloc tbb) - # Search for the libraries - find_library(TBB_${_comp}_LIBRARY_RELEASE ${_comp} - HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} - PATHS ${TBB_DEFAULT_SEARCH_DIR} - PATH_SUFFIXES "${TBB_LIB_PATH_SUFFIX}") - - find_library(TBB_${_comp}_LIBRARY_DEBUG ${_comp}_debug - HINTS ${TBB_LIBRARY} ${TBB_SEARCH_DIR} - PATHS ${TBB_DEFAULT_SEARCH_DIR} ENV LIBRARY_PATH - PATH_SUFFIXES "${TBB_LIB_PATH_SUFFIX}") - - - # Set the library to be used for the component - if(NOT TBB_${_comp}_LIBRARY) - if(TBB_USE_DEBUG_BUILD AND TBB_${_comp}_LIBRARY_DEBUG) - set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_DEBUG}") - elseif(TBB_${_comp}_LIBRARY_RELEASE) - set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_RELEASE}") - elseif(TBB_${_comp}_LIBRARY_DEBUG) - set(TBB_${_comp}_LIBRARY "${TBB_${_comp}_LIBRARY_DEBUG}") - endif() - endif() - - # Set the TBB library list and component found variables - if(TBB_${_comp}_LIBRARY) - list(APPEND TBB_LIBRARIES "${TBB_${_comp}_LIBRARY}") - set(TBB_${_comp}_FOUND TRUE) - else() - set(TBB_${_comp}_FOUND FALSE) - endif() - - mark_as_advanced(TBB_${_comp}_LIBRARY_RELEASE) - mark_as_advanced(TBB_${_comp}_LIBRARY_DEBUG) - mark_as_advanced(TBB_${_comp}_LIBRARY) - - endforeach() - - ################################## - # Set compile flags - ################################## - - if(TBB_tbb_LIBRARY MATCHES "debug") - set(TBB_DEFINITIONS "-DTBB_USE_DEBUG=1") - endif() - - ################################## - # Set version strings - ################################## - - if(TBB_INCLUDE_DIRS) - file(READ "${TBB_INCLUDE_DIRS}/tbb/tbb_stddef.h" _tbb_version_file) - string(REGEX REPLACE ".*#define TBB_VERSION_MAJOR ([0-9]+).*" "\\1" - TBB_VERSION_MAJOR "${_tbb_version_file}") - string(REGEX REPLACE ".*#define TBB_VERSION_MINOR ([0-9]+).*" "\\1" - TBB_VERSION_MINOR "${_tbb_version_file}") - string(REGEX REPLACE ".*#define TBB_INTERFACE_VERSION ([0-9]+).*" "\\1" - TBB_INTERFACE_VERSION "${_tbb_version_file}") - set(TBB_VERSION "${TBB_VERSION_MAJOR}.${TBB_VERSION_MINOR}") - endif() - - find_package_handle_standard_args(TBB - REQUIRED_VARS TBB_INCLUDE_DIRS TBB_LIBRARIES - HANDLE_COMPONENTS - VERSION_VAR TBB_VERSION) - - mark_as_advanced(TBB_INCLUDE_DIRS TBB_LIBRARIES) - -endif() diff --git a/cmake/modules/FindUSD.cmake b/cmake/modules/FindUSD.cmake deleted file mode 100644 index 2c3f9dc67..000000000 --- a/cmake/modules/FindUSD.cmake +++ /dev/null @@ -1,59 +0,0 @@ -# Simple module to find USD. - -if(RPR_BUILD_AS_HOUDINI_PLUGIN) - set(USD_INCLUDE_DIR ${HOUDINI_INCLUDE_DIR}) - set(USD_LIBRARY_DIR ${HOUDINI_LIB}) - set(PXR_LIB_PREFIX pxr_) - if(WIN32) - set(PXR_LIB_PREFIX libpxr_) - endif() - set(USD_LIBRARY_MONOLITHIC FALSE) -else(RPR_BUILD_AS_HOUDINI_PLUGIN) - find_path(USD_INCLUDE_DIR pxr/pxr.h - PATHS ${USD_ROOT}/include - $ENV{USD_ROOT}/include - DOC "USD Include directory" - NO_DEFAULT_PATH - NO_SYSTEM_ENVIRONMENT_PATH) - - find_path(USD_LIBRARY_DIR - NAMES "${PXR_LIB_PREFIX}usd${CMAKE_SHARED_LIBRARY_SUFFIX}" - PATHS ${USD_ROOT}/lib - $ENV{USD_ROOT}/lib - DOC "USD Libraries directory" - NO_DEFAULT_PATH - NO_SYSTEM_ENVIRONMENT_PATH) - set(USD_LIBRARY_MONOLITHIC FALSE) - - if(NOT USD_LIBRARY_DIR) - find_path(USD_LIBRARY_DIR - NAMES "${PXR_LIB_PREFIX}usd_ms${CMAKE_SHARED_LIBRARY_SUFFIX}" - PATHS ${USD_ROOT}/lib - $ENV{USD_ROOT}/lib - DOC "USD Libraries directory" - NO_DEFAULT_PATH - NO_SYSTEM_ENVIRONMENT_PATH) - set(USD_LIBRARY_MONOLITHIC TRUE) - endif() -endif(RPR_BUILD_AS_HOUDINI_PLUGIN) - -if(USD_INCLUDE_DIR AND EXISTS "${USD_INCLUDE_DIR}/pxr/pxr.h") - foreach(_usd_comp MAJOR MINOR PATCH) - file(STRINGS - "${USD_INCLUDE_DIR}/pxr/pxr.h" - _usd_tmp - REGEX "#define PXR_${_usd_comp}_VERSION .*$") - string(REGEX MATCHALL "[0-9]+" USD_${_usd_comp}_VERSION ${_usd_tmp}) - endforeach() - set(USD_VERSION ${USD_MAJOR_VERSION}.${USD_MINOR_VERSION}.${USD_PATCH_VERSION}) -endif() - -include(FindPackageHandleStandardArgs) - -find_package_handle_standard_args( - USD - REQUIRED_VARS - USD_INCLUDE_DIR - USD_LIBRARY_DIR - VERSION_VAR - USD_VERSION) diff --git a/cmake/modules/FindUSDMonolithic.cmake b/cmake/modules/FindUSDMonolithic.cmake new file mode 100644 index 000000000..1d8078eea --- /dev/null +++ b/cmake/modules/FindUSDMonolithic.cmake @@ -0,0 +1,104 @@ +# USD 20.05 does not generate cmake config for the monolithic library as it does for the default mode +# So we have to handle monolithic USD in a special way + +find_path(USD_INCLUDE_DIR pxr/pxr.h + PATHS ${pxr_DIR}/include + $ENV{pxr_DIR}/include + DOC "USD Include directory" + NO_DEFAULT_PATH + NO_SYSTEM_ENVIRONMENT_PATH) + +find_path(USD_LIBRARY_DIR + NAMES "${PXR_LIB_PREFIX}usd_ms${CMAKE_SHARED_LIBRARY_SUFFIX}" + PATHS ${pxr_DIR}/lib + $ENV{pxr_DIR}/lib + DOC "USD Libraries directory" + NO_DEFAULT_PATH + NO_SYSTEM_ENVIRONMENT_PATH) + +find_library(USD_MONOLITHIC_LIBRARY + NAMES ${PXR_LIB_PREFIX}usd_ms + PATHS ${USD_LIBRARY_DIR} + NO_DEFAULT_PATH + NO_SYSTEM_ENVIRONMENT_PATH) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(USDMonolithic + USD_INCLUDE_DIR + USD_LIBRARY_DIR + USD_MONOLITHIC_LIBRARY) + +if(USDMonolithic_FOUND) + list(APPEND CMAKE_PREFIX_PATH ${pxr_DIR}) + + # --Python. + if(PXR_USE_PYTHON_3) + find_package(PythonInterp 3.0 REQUIRED) + find_package(PythonLibs 3.0 REQUIRED) + else() + find_package(PythonInterp 2.7 REQUIRED) + find_package(PythonLibs 2.7 REQUIRED) + endif() + + # Set up a version string for comparisons. This is available + # as Boost_VERSION_STRING in CMake 3.14+ + set(boost_version_string "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}") + + if (((${boost_version_string} VERSION_GREATER_EQUAL "1.67") AND + (${boost_version_string} VERSION_LESS "1.70")) OR + ((${boost_version_string} VERSION_GREATER_EQUAL "1.70") AND + Boost_NO_BOOST_CMAKE)) + # As of boost 1.67 the boost_python component name includes the + # associated Python version (e.g. python27, python36). After boost 1.70 + # the built-in cmake files will deal with this. If we are using boost + # that does not have working cmake files, or we are using a new boost + # and not using cmake's boost files, we need to do the below. + # + # Find the component under the versioned name and then set the generic + # Boost_PYTHON_LIBRARY variable so that we don't have to duplicate this + # logic in each library's CMakeLists.txt. + set(python_version_nodot "${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}") + find_package(Boost + COMPONENTS + python${python_version_nodot} + REQUIRED + ) + set(Boost_PYTHON_LIBRARY "${Boost_PYTHON${python_version_nodot}_LIBRARY}") + else() + find_package(Boost + COMPONENTS + python + REQUIRED + ) + endif() + + add_library(usd_ms SHARED IMPORTED) + set_property(TARGET usd_ms APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) + + target_compile_definitions(usd_ms INTERFACE + -DPXR_PYTHON_ENABLED=1) + target_link_libraries(usd_ms INTERFACE + ${USD_MONOLITHIC_LIBRARY} + ${Boost_LIBRARIES} + ${PYTHON_LIBRARIES}) + target_link_directories(usd_ms INTERFACE + ${USD_LIBRARY_DIR}) + target_include_directories(usd_ms INTERFACE + ${USD_INCLUDE_DIR} + ${Boost_INCLUDE_DIRS}) + set_target_properties(usd_ms PROPERTIES + IMPORTED_IMPLIB_RELEASE "${USD_MONOLITHIC_LIBRARY}" + IMPORTED_LOCATION_RELEASE "${USD_LIBRARY_DIR}/${PXR_LIB_PREFIX}usd_ms${CMAKE_SHARED_LIBRARY_SUFFIX}" + ) + + foreach(targetName + arch tf gf js trace work plug vt ar kind sdf ndr sdr pcp usd usdGeom + usdVol usdLux usdMedia usdShade usdRender usdHydra usdRi usdSkel usdUI + usdUtils garch hf hio cameraUtil pxOsd glf hgi hgiGL hd hdSt hdx + usdImaging usdImagingGL usdRiImaging usdSkelImaging usdVolImaging + usdAppUtils usdviewq) + add_library(${targetName} INTERFACE) + target_link_libraries(${targetName} INTERFACE usd_ms) + endforeach() +endif() diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt new file mode 100644 index 000000000..42de65f06 --- /dev/null +++ b/deps/CMakeLists.txt @@ -0,0 +1,73 @@ +# MaterialX +# ---------------------------------------------- + +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 + + install( + FILES + MaterialX/libraries/bxdf/standard_surface.mtlx + MaterialX/libraries/bxdf/usd_preview_surface.mtlx + DESTINATION + libraries/bxdf) + foreach(mtlx_lib stdlib pbrlib) + install( + FILES + MaterialX/libraries/${mtlx_lib}/${mtlx_lib}_ng.mtlx + MaterialX/libraries/${mtlx_lib}/${mtlx_lib}_defs.mtlx + DESTINATION + libraries/${mtlx_lib}) + endforeach() +endif() + +# RPR C++ wrapper +# ---------------------------------------------- + +if(NOT RPR_TOOLS_LOCATION) + set(RPR_TOOLS_LOCATION ${RPR_LOCATION_INCLUDE}/../rprTools) +endif() + +if(NOT RPRMTLXLOADER_LOCATION) + set(RPRMTLXLOADER_LOCATION rprMtlxLoader) +endif() + +if(NOT RPR_CPP_WRAPPER_LOCATION) + set(RPR_CPP_WRAPPER_LOCATION ${RPR_TOOLS_LOCATION}) +endif() + +add_library(cpprpr STATIC + ${RPRMTLXLOADER_LOCATION}/rprMtlxLoader.h + ${RPRMTLXLOADER_LOCATION}/rprMtlxLoader.cpp + ${RPR_CPP_WRAPPER_LOCATION}/RadeonProRender.hpp + ${RPR_CPP_WRAPPER_LOCATION}/RadeonProRenderCpp.cpp + ${RPR_CPP_WRAPPER_LOCATION}/tinyxml2.cpp) +set_target_properties(cpprpr PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_include_directories(cpprpr PUBLIC ${RPR_CPP_WRAPPER_LOCATION} ${RPRMTLXLOADER_LOCATION}) +target_link_libraries(cpprpr PUBLIC rpr MaterialXCore MaterialXFormat) +target_compile_definitions(cpprpr PUBLIC + RPR_CPPWRAPER_DISABLE_MUTEXLOCK + RPR_API_USE_HEADER_V2) + +# json +# ---------------------------------------------- + +add_library(json INTERFACE) +target_include_directories(json INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/json) + +# filesystem +# ---------------------------------------------- + +if(HoudiniUSD_FOUND) + # Currently we use it only in activateHoudiniPlugin utility + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/ghc_filesystem) +endif() + +# ---------------------------------------------- diff --git a/deps/MaterialX b/deps/MaterialX new file mode 160000 index 000000000..d17e7926c --- /dev/null +++ b/deps/MaterialX @@ -0,0 +1 @@ +Subproject commit d17e7926c4278968c3ffde7da966da1821618ac1 diff --git a/deps/RIF b/deps/RIF index cfc91b070..32643c48c 160000 --- a/deps/RIF +++ b/deps/RIF @@ -1 +1 @@ -Subproject commit cfc91b0709b96ee300060a94f1efca7bb144f225 +Subproject commit 32643c48c4d3bb21793c135efb5d532fac94fdd2 diff --git a/deps/RPR b/deps/RPR index f67815953..3bc01d29f 160000 --- a/deps/RPR +++ b/deps/RPR @@ -1 +1 @@ -Subproject commit f678159530ca64f9dd90783bd3a83b10ee346fa6 +Subproject commit 3bc01d29f8dee0068023a897e63699c82e5ce43d diff --git a/deps/ghc_filesystem b/deps/ghc_filesystem new file mode 160000 index 000000000..d8abf146a --- /dev/null +++ b/deps/ghc_filesystem @@ -0,0 +1 @@ +Subproject commit d8abf146a43ea12d28b09dfa71ece3bad4168185 diff --git a/pxr/imaging/plugin/hdRpr/thirdparty/json/json.hpp b/deps/json/json.hpp similarity index 100% rename from pxr/imaging/plugin/hdRpr/thirdparty/json/json.hpp rename to deps/json/json.hpp diff --git a/deps/rprMtlxLoader/rprMtlxLoader.cpp b/deps/rprMtlxLoader/rprMtlxLoader.cpp new file mode 100644 index 000000000..700796655 --- /dev/null +++ b/deps/rprMtlxLoader/rprMtlxLoader.cpp @@ -0,0 +1,2046 @@ +#include "rprMtlxLoader.h" + +#include // mx::loadLibraries + +#include +#include +#include + +namespace mx = MaterialX; + +namespace { + +//------------------------------------------------------------------------------ +// Direct mappings of standard mtlx nodes to RPR nodes +//------------------------------------------------------------------------------ + +struct Mtlx2Rpr { + struct Node { + rpr_material_node_type id; + std::map inputs; + using InputsValueType = decltype(inputs)::value_type; + + Node() = default; + Node(rpr_material_node_type id, std::initializer_list&& inputs) + : id(id), inputs(std::move(inputs)) { + } + }; + + std::map nodes; + std::map arithmeticOps; + + Mtlx2Rpr() { + nodes["diffuse_brdf"] = { + RPR_MATERIAL_NODE_MATX_DIFFUSE_BRDF, { + {"color", RPR_MATERIAL_INPUT_COLOR}, + {"weight", RPR_MATERIAL_INPUT_WEIGHT}, + {"roughness", RPR_MATERIAL_INPUT_ROUGHNESS}, + {"normal", RPR_MATERIAL_INPUT_NORMAL}, + } + }; + nodes["dielectric_brdf"] = { + RPR_MATERIAL_NODE_MATX_DIELECTRIC_BRDF, { + {"weight", RPR_MATERIAL_INPUT_WEIGHT}, + {"tint", RPR_MATERIAL_INPUT_TINT}, + {"ior", RPR_MATERIAL_INPUT_IOR}, + {"roughness", RPR_MATERIAL_INPUT_ROUGHNESS}, + {"normal", RPR_MATERIAL_INPUT_NORMAL}, + {"tangent", RPR_MATERIAL_INPUT_TANGENT}, + {"distribution", RPR_MATERIAL_INPUT_DISTRIBUTION}, + {"base", RPR_MATERIAL_INPUT_BASE}, + } + }; + nodes["generalized_schlick_brdf"] = { + RPR_MATERIAL_NODE_MATX_GENERALIZED_SCHLICK_BRDF, { + {"color0", RPR_MATERIAL_INPUT_COLOR0}, + {"color90", RPR_MATERIAL_INPUT_COLOR1}, + {"exponent", RPR_MATERIAL_INPUT_EXPONENT}, + {"roughness", RPR_MATERIAL_INPUT_ROUGHNESS}, + {"weight", RPR_MATERIAL_INPUT_WEIGHT}, + {"normal", RPR_MATERIAL_INPUT_NORMAL}, + {"tangent", RPR_MATERIAL_INPUT_TANGENT}, + {"distribution", RPR_MATERIAL_INPUT_DISTRIBUTION}, + {"base", RPR_MATERIAL_INPUT_BASE}, + } + }; + nodes["dielectric_btdf"] = { + RPR_MATERIAL_NODE_MATX_DIELECTRIC_BTDF, { + {"weight", RPR_MATERIAL_INPUT_WEIGHT}, + {"tint", RPR_MATERIAL_INPUT_COLOR}, + {"ior", RPR_MATERIAL_INPUT_IOR}, + {"roughness", RPR_MATERIAL_INPUT_ROUGHNESS}, + {"normal", RPR_MATERIAL_INPUT_NORMAL}, + {"tangent", RPR_MATERIAL_INPUT_TANGENT}, + {"distribution", RPR_MATERIAL_INPUT_DISTRIBUTION}, + {"interior", RPR_MATERIAL_INPUT_INTERIOR}, + } + }; + nodes["sheen_brdf"] = { + RPR_MATERIAL_NODE_MATX_SHEEN_BRDF, { + {"weight", RPR_MATERIAL_INPUT_WEIGHT}, + {"color", RPR_MATERIAL_INPUT_COLOR}, + {"roughness", RPR_MATERIAL_INPUT_ROUGHNESS}, + {"normal", RPR_MATERIAL_INPUT_NORMAL}, + {"base", RPR_MATERIAL_INPUT_BASE}, + } + }; + nodes["subsurface_brdf"] = { + RPR_MATERIAL_NODE_MATX_SUBSURFACE_BRDF, { + {"weight", RPR_MATERIAL_INPUT_WEIGHT}, + {"color", RPR_MATERIAL_INPUT_COLOR}, + {"radius", RPR_MATERIAL_INPUT_RADIUS}, + {"anisotropy", RPR_MATERIAL_INPUT_ANISOTROPIC}, + {"normal", RPR_MATERIAL_INPUT_NORMAL}, + } + }; + nodes["diffuse_btdf"] = { + RPR_MATERIAL_NODE_MATX_DIFFUSE_BTDF, { + {"weight", RPR_MATERIAL_INPUT_WEIGHT}, + {"color", RPR_MATERIAL_INPUT_COLOR}, + {"normal", RPR_MATERIAL_INPUT_NORMAL}, + } + }; + nodes["conductor_brdf"] = { + RPR_MATERIAL_NODE_MATX_CONDUCTOR_BRDF, { + {"weight", RPR_MATERIAL_INPUT_WEIGHT}, + {"reflectivity", RPR_MATERIAL_INPUT_REFLECTIVITY}, + {"edge_color", RPR_MATERIAL_INPUT_EDGE_COLOR}, + {"roughness", RPR_MATERIAL_INPUT_ROUGHNESS}, + {"normal", RPR_MATERIAL_INPUT_NORMAL}, + {"tangent", RPR_MATERIAL_INPUT_TANGENT}, + {"distribution", RPR_MATERIAL_INPUT_DISTRIBUTION}, + } + }; + nodes["fresnel"] = { + RPR_MATERIAL_NODE_MATX_FRESNEL, { + {"ior", RPR_MATERIAL_INPUT_IOR}, + {"normal", RPR_MATERIAL_INPUT_NORMAL}, + {"viewdirection", RPR_MATERIAL_INPUT_VIEW_DIRECTION}, + } + }; + nodes["constant"] = { + RPR_MATERIAL_NODE_CONSTANT_TEXTURE, { + {"value", RPR_MATERIAL_INPUT_VALUE}, + } + }; + nodes["mix"] = { + RPR_MATERIAL_NODE_BLEND_VALUE, { + {"fg", RPR_MATERIAL_INPUT_COLOR1}, + {"bg", RPR_MATERIAL_INPUT_COLOR0}, + {"mix", RPR_MATERIAL_INPUT_WEIGHT}, + } + }; + nodes["ifgreater"] = { + RPR_MATERIAL_NODE_MATX_IFGREATER, { + {"value1", RPR_MATERIAL_INPUT_0}, + {"value2", RPR_MATERIAL_INPUT_1}, + {"in1", RPR_MATERIAL_INPUT_COLOR0}, + {"in2", RPR_MATERIAL_INPUT_COLOR1}, + } + }; + nodes["normalize"] = { + RPR_MATERIAL_NODE_MATX_NORMALIZE, { + {"in", RPR_MATERIAL_INPUT_COLOR}, + } + }; + nodes["luminance"] = { + RPR_MATERIAL_NODE_MATX_LUMINANCE, { + {"in", RPR_MATERIAL_INPUT_0}, + {"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}, + {"amount", RPR_MATERIAL_INPUT_AMOUNT}, + {"axis", RPR_MATERIAL_INPUT_AXIS}, + } + }; + nodes["roughness_anisotropy"] = { + RPR_MATERIAL_NODE_MATX_ROUGHNESS_ANISOTROPY, { + {"roughness", RPR_MATERIAL_INPUT_ROUGHNESS}, + {"anisotropy", RPR_MATERIAL_INPUT_ANISOTROPIC}, + } + }; + nodes["noise3d"] = { + RPR_MATERIAL_NODE_MATX_NOISE3D, { + {"amplitude", RPR_MATERIAL_INPUT_AMPLITUDE}, + {"pivot", RPR_MATERIAL_INPUT_PIVOT}, + {"position", RPR_MATERIAL_INPUT_POSITION}, + } + }; + nodes["normalmap"] = { + RPR_MATERIAL_NODE_NORMAL_MAP, { + {"in", RPR_MATERIAL_INPUT_COLOR}, + {"scale", RPR_MATERIAL_INPUT_SCALE}, + } + }; + nodes["normalize"] = { + RPR_MATERIAL_NODE_MATX_NORMALIZE, { + {"in", RPR_MATERIAL_INPUT_COLOR}, + } + }; + nodes["position"] = {RPR_MATERIAL_NODE_MATX_POSITION, {}}; + + auto addArithmeticNode = [this](const char* name, rpr_material_node_arithmetic_operation op, int numArgs) { + auto& mapping = nodes[name]; + mapping.id = RPR_MATERIAL_NODE_ARITHMETIC; + + arithmeticOps[name] = op; + + if (numArgs == 1) { + mapping.inputs["in"] = RPR_MATERIAL_INPUT_COLOR0; + } else { + mapping.inputs["in1"] = RPR_MATERIAL_INPUT_COLOR0; + mapping.inputs["in2"] = RPR_MATERIAL_INPUT_COLOR1; + + if (numArgs > 2) mapping.inputs["in3"] = RPR_MATERIAL_INPUT_COLOR2; + if (numArgs > 3) mapping.inputs["in4"] = RPR_MATERIAL_INPUT_COLOR3; + } + }; + + addArithmeticNode("sin", RPR_MATERIAL_NODE_OP_SIN, 1); + addArithmeticNode("cos", RPR_MATERIAL_NODE_OP_COS, 1); + addArithmeticNode("tan", RPR_MATERIAL_NODE_OP_TAN, 1); + addArithmeticNode("asin", RPR_MATERIAL_NODE_OP_ASIN, 1); + addArithmeticNode("acos", RPR_MATERIAL_NODE_OP_ACOS, 1); + addArithmeticNode("absval", RPR_MATERIAL_NODE_OP_ABS, 1); + addArithmeticNode("floor", RPR_MATERIAL_NODE_OP_FLOOR, 1); + addArithmeticNode("ln", RPR_MATERIAL_NODE_OP_LOG, 1); + addArithmeticNode("normalize", RPR_MATERIAL_NODE_OP_NORMALIZE3, 1); + addArithmeticNode("add", RPR_MATERIAL_NODE_OP_ADD, 2); + addArithmeticNode("subtract", RPR_MATERIAL_NODE_OP_SUB, 2); + addArithmeticNode("multiply", RPR_MATERIAL_NODE_OP_MUL, 2); + addArithmeticNode("divide", RPR_MATERIAL_NODE_OP_DIV, 2); + addArithmeticNode("power", RPR_MATERIAL_NODE_OP_POW, 2); + addArithmeticNode("min", RPR_MATERIAL_NODE_OP_MIN, 2); + addArithmeticNode("max", RPR_MATERIAL_NODE_OP_MAX, 2); + addArithmeticNode("dotproduct", RPR_MATERIAL_NODE_OP_DOT3, 2); + addArithmeticNode("crossproduct", RPR_MATERIAL_NODE_OP_CROSS3, 2); + addArithmeticNode("modulo", RPR_MATERIAL_NODE_OP_MOD, 2); + + arithmeticOps["invert"] = RPR_MATERIAL_NODE_OP_SUB; + nodes["invert"] = { + RPR_MATERIAL_NODE_ARITHMETIC, { + {"amount", RPR_MATERIAL_INPUT_COLOR0}, + {"in", RPR_MATERIAL_INPUT_COLOR1}, + } + }; + + // TODO: add custom implementations + arithmeticOps["clamp"] = RPR_MATERIAL_NODE_OP_MAX; + nodes["clamp"] = { + RPR_MATERIAL_NODE_ARITHMETIC, { + {"in", RPR_MATERIAL_INPUT_COLOR0}, + {"low", RPR_MATERIAL_INPUT_COLOR1}, + } + }; + } +}; + +Mtlx2Rpr const& GetMtlx2Rpr() { + static Mtlx2Rpr s_mtlx2rpr; + return s_mtlx2rpr; +} + +//------------------------------------------------------------------------------ +// Loader context declarations +//------------------------------------------------------------------------------ + +struct Node; +struct MtlxNodeGraphNode; + +enum LogScope { + LSGlobal = -1, + LSGraph, + LSNested = LSGraph, + LSNode, + LSInput, + LogScopeMax +}; + +struct LoaderContext { + mx::Document const* mtlxDocument; + rpr_material_system rprMatSys; + + std::unique_ptr globalNodeGraph; + Node* GetGlobalNode(mx::Node* mtlxNode); + + std::map> freeStandingNodeGraphs; + MtlxNodeGraphNode* GetFreeStandingNodeGraph(mx::NodeGraphPtr const& nodeGraph); + + std::map> geomNodes; + Node* GetGeomNode(mx::GeomPropDef* geomPropDef); + + template + bool ConnectToGlobalOutput(T* input, Node* node); + + mx::FileSearchPath searchPath; + std::string fileprefix; + + static const int kGlobalLogDepth = -1; + int logDepth = kGlobalLogDepth; + LogScope logScope = LSGlobal; + bool logEnabled = true; + + void Log(const char* fmt, ...); + + void LogError(size_t line, const char* fmt, ...); + + struct ScopeGuard { + LoaderContext* ctx; + int previousLogDepth; + LogScope previousLogScope; + + bool isFileprefixOverridden; + std::string previousFileprefix; + + ScopeGuard(LoaderContext* ctx, LogScope logScope, mx::Element const* scopeElement); + ~ScopeGuard(); + }; + 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__) + +//------------------------------------------------------------------------------ +// Node declarations +//------------------------------------------------------------------------------ + +struct Node { + using Ptr = std::unique_ptr; + + virtual ~Node() = default; + + /// Connect the current (upstream) node to the \p downstreamNode + virtual rpr_status Connect(mx::Element* outputElement, Node* downstreamNode, mx::Element* inputElement, LoaderContext* context) = 0; + virtual rpr_status SetInput(mx::Element* inputElement, rpr_material_node inputNode, LoaderContext* context) = 0; + virtual rpr_status SetInput(mx::Element* inputElement, std::string const& value, std::string const& valueType, LoaderContext* context) = 0; + + template + T* AsA() { + return dynamic_cast(this); + } + + static Node::Ptr Create(mx::Node* mtlxNode, LoaderContext* context); +}; + +/// The node that wraps rpr_material_node +/// +struct RprNode : public Node { + bool isOwningRprNode; + rpr_material_node rprNode; + + /// \p retainNode controls whether RprNode owns \p node + RprNode(rpr_material_node node, bool retainNode); + ~RprNode() override; + + rpr_status Connect(mx::Element* outputElement, Node* downstreamNode, mx::Element* inputElement, LoaderContext* context) override; + rpr_status SetInput(mx::Element* inputElement, rpr_material_node inputNode, LoaderContext* context) override; + rpr_status SetInput(mx::Element* inputElement, std::string const& value, std::string const& valueType, LoaderContext* context) override; + + rpr_status SetInput(rpr_material_node_input inputId, std::string const& valueString, std::string const& valueType, LoaderContext* context); +}; + +/// The node that can be mapped (fully or partially) to MaterialX standard node +/// +struct RprMappedNode : public RprNode { + Mtlx2Rpr::Node const* rprNodeMapping; + + /// RprMappedNode takes ownership over \p node + RprMappedNode(rpr_material_node node, Mtlx2Rpr::Node const* nodeMapping); + ~RprMappedNode() override = default; + + rpr_status SetInput(mx::Element* inputElement, rpr_material_node inputNode, LoaderContext* context) override; + rpr_status SetInput(mx::Element* inputElement, std::string const& value, std::string const& valueType, LoaderContext* context) override; +}; + +struct RprImageNode : public RprMappedNode { + // TODO: support frame ranges + + std::string file; + std::string layer; + mx::ValuePtr defaultValue; + + /// Possible values: "constant", "clamp", "periodic", "mirror" + std::string uaddressmode; + std::string vaddressmode; + + /// Possible values: "closest", "linear", "cubic" + // TODO: can we support this in RPR? + //std::string filtertype; + + RprImageNode(LoaderContext* context); + ~RprImageNode() override = default; + + rpr_status SetInput(mx::Element* inputElement, std::string const& value, std::string const& valueType, LoaderContext* context) override; +}; + +/// The node that wraps RprNode so that the latter one can be used as a surface material. +/// +struct RprWrapNode : public RprNode { + using Ptr = std::unique_ptr; + + struct Error : public std::exception {}; + struct RprApiError : public Error {}; + struct UnknownType : public Error {}; + + /// Wraps \p node of \p outputType type. + /// Throws UnknownType if \p outputType cannot be wrapped. + /// Throws RprApiError on critical RPR API error. + RprWrapNode(rpr_material_node node, std::string const& outputType, LoaderContext* ctx); + ~RprWrapNode() override = default; + + static bool IsOutputTypeSupported(std::string const& outputType); +}; + +/// The node that wraps mx::GraphElement. +/// This node handles all type of connections possible in MaterialX graphs. +/// +struct MtlxNodeGraphNode : public Node { + mx::ConstGraphElementPtr mtlxGraph; + std::map subNodes; + + using Ptr = std::unique_ptr; + + struct NoOutputsError : public std::exception {}; + + /// \p mtlxGraph may be either NodeGraph or Document, + /// \p requiredOutputs specifies outputs that should be processed. + /// Throws NoOutputsError exception if mtlxGraph has no outputs + MtlxNodeGraphNode(mx::ConstGraphElementPtr const& mtlxGraph, std::vector const& requiredOutputs, LoaderContext* context); + + /// Overload, requiredOutputs equals mtlxGraph->getOutputs(). + /// Throws NoOutputsError exception if mtlxGraph has no outputs + MtlxNodeGraphNode(mx::ConstGraphElementPtr const& mtlxGraph, LoaderContext* context); + + enum LazyInit { LAZY_INIT }; + /// Constructs MtlxNodeGraphNode in which subNodes created on demand, \see GetSubNode + MtlxNodeGraphNode(mx::ConstGraphElementPtr const& mtlxGraph, LazyInit); + + ~MtlxNodeGraphNode() override = default; + + Node* GetSubNode(std::string const& nodename, LoaderContext* context); + + rpr_status Connect(mx::Element* outputElement, Node* downstreamNode, mx::Element* inputElement, LoaderContext* context) override; + rpr_status SetInput(mx::Element* inputElement, rpr_material_node inputNode, LoaderContext* context) override; + rpr_status SetInput(mx::Element* inputElement, std::string const& value, std::string const& valueType, LoaderContext* context) override; + +private: + struct PendingConnection { + mx::NodePtr upstreamNode; + mx::OutputPtr upstreamNodeOutput; + + mx::NodePtr downstreamNode; + mx::ElementPtr downstreamInput; + }; + Node* _CreateSubNode(mx::NodePtr const& mtlxNode, std::vector* pendingConnections, LoaderContext* context); + + struct InterfaceSocket { + mx::NodePtr subNode; + mx::ElementPtr input; + }; + std::map> _interfaceSockets; + + struct InterfaceSocketsQuery { + template + void ForEach(LoaderContext* context, F&& f) { + if (_sockets) { + 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()); + + f(nodeIt->second.get(), socket.input.get()); + } + } + } + } + + explicit operator bool() const { return _sockets; } + + MtlxNodeGraphNode* _nodeGraphNode; + std::vector* _sockets; + }; + + InterfaceSocketsQuery _GetInterfaceSockets(mx::Element* inputElement); + InterfaceSocketsQuery _GetInterfaceSockets(std::string const& interfaceName); +}; + +//------------------------------------------------------------------------------ +// Utilities +//------------------------------------------------------------------------------ + +template +std::shared_ptr GetFirst(mx::Element const* element) { + for (auto& child : element->getChildren()) { + if (auto object = child->asA()) { + return object; + } + } + return nullptr; +} + +mx::OutputPtr GetOutput(mx::InterfaceElement const* interfaceElement, mx::PortElement* portElement, LoaderContext* context) { + // If the interface element has a few outputs, the port element must specify a target output + // + 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()); + return nullptr; + } + + auto output = interfaceElement->getOutput(targetOutputName); + if (!output) { + LOG_ERROR(context, "invalid connection: cannot determine output - %s\n", portElement->asString().c_str()); + } + + return output; + } + + return GetFirst(interfaceElement); +} + +template +size_t GetHash(T const& value) { + return std::hash{}(value); +} + +//------------------------------------------------------------------------------ +// Loader context implementation +//------------------------------------------------------------------------------ + +void LoaderContext::Log(const char* fmt, ...) { + if (!logEnabled) { + return; + } + + if (logScope != LSGlobal) { + int padding = 0; + if (logDepth > 0) { + padding += logDepth * LogScopeMax; + } + padding += logScope; + + if (padding > 0) { + printf("%*s", padding, ""); + } + printf("- "); + } + + va_list ap; + 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); +} + +LoaderContext::ScopeGuard::ScopeGuard(LoaderContext* ctx, LogScope logScope, mx::Element const* scopeElement) + : ctx(ctx) + , previousLogDepth(ctx->logDepth) + , previousLogScope(ctx->logScope) + , previousFileprefix(ctx->fileprefix) { + + ctx->logScope = logScope; + if (logScope == LSGlobal) { + ctx->logDepth = kGlobalLogDepth; + } else if (logScope == LSNested) { + ctx->logDepth++; + } + + isFileprefixOverridden = false; + if (scopeElement->hasFilePrefix()) { + isFileprefixOverridden = true; + previousFileprefix = std::move(ctx->fileprefix); + ctx->fileprefix = scopeElement->getFilePrefix(); + } +} + +LoaderContext::ScopeGuard::~ScopeGuard() { + ctx->logScope = previousLogScope; + ctx->logDepth = previousLogDepth; + if (isFileprefixOverridden) { + ctx->fileprefix = previousFileprefix; + } +} + +LoaderContext::ScopeGuard LoaderContext::EnterScope(LogScope logScope, mx::Element const* scopeElement) { + return ScopeGuard(this, logScope, scopeElement); +} + +std::string LoaderContext::ResolveFile(std::string const& filename) { + if (fileprefix.empty()) { + return FindFile(filename); + } else { + return FindFile(fileprefix + filename); + } +} + +std::string LoaderContext::FindFile(std::string const& filename) { + mx::FilePath filepath = searchPath.find(filename); + if (filepath.isEmpty()) { + return std::string(); + } + return filepath.asString(); +} + +Node* LoaderContext::GetGlobalNode(mx::Node* node) { + if (!globalNodeGraph) { + auto docGraph = mtlxDocument->getSelf()->asA(); + globalNodeGraph = std::make_unique(docGraph, MtlxNodeGraphNode::LAZY_INIT); + } + + auto scope = EnterScope(LSGlobal, mtlxDocument); + return globalNodeGraph->GetSubNode(node->getName(), this); +} + +MtlxNodeGraphNode* LoaderContext::GetFreeStandingNodeGraph(mx::NodeGraphPtr const& nodeGraph) { + auto it = freeStandingNodeGraphs.find(nodeGraph->getName()); + if (it == freeStandingNodeGraphs.end()) { + MtlxNodeGraphNode::Ptr nodeGraphNode; + try { + nodeGraphNode = std::make_unique(nodeGraph, this); + } catch (MtlxNodeGraphNode::NoOutputsError& e) { + // Store nullptr nodeGraph in freeStandingNodeGraphs to not fall into the same failure + } + it = freeStandingNodeGraphs.emplace(nodeGraph->getName(), std::move(nodeGraphNode)).first; + } + + return it->second.get(); +} + +template +bool LoaderContext::ConnectToGlobalOutput(T* input, Node* node) { + auto& outputName = input->getOutputString(); + if (outputName.empty()) { + return false; + } + + // The output attribute may point to the output of some free-standing node graph or free-standing output + // + auto& nodeGraphName = input->getAttribute(mx::PortElement::NODE_GRAPH_ATTRIBUTE); + if (!nodeGraphName.empty()) { + 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()); + + return freeStandingNodeGraphNode->Connect(nodeGraphOutput.get(), node, input, this) == RPR_SUCCESS; + } + } + } + } else { + // A global output is an output that is defined globally in mtlxDocument + // + if (auto globalOutput = mtlxDocument->getOutput(outputName)) { + // Such outputs point to a globally instantiated nodes + // + if (auto mtlxGlobalNode = globalOutput->getConnectedNode()) { + 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()); + + return globalNode->Connect(mtlxGlobalNodeOutput.get(), node, input, this) == RPR_SUCCESS; + } + } + } + } + } + } + + return false; +} + +Node* LoaderContext::GetGeomNode(mx::GeomPropDef* geomPropDef) { + auto geomNodeIt = geomNodes.find(geomPropDef->getName()); + if (geomNodeIt != geomNodes.end()) { + return geomNodeIt->second.get(); + } + + 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()); + 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()); + } + + 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\"\n", 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 tangent, bitangent, geomcolor, geompropvalue (primvar) + + 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); + + 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; + } + } + + LOG_ERROR(this, "Unsupported geom node: %s\n", geomPropDef->asString().c_str()); + return nullptr; +} + +//------------------------------------------------------------------------------ +// Node implementation +//------------------------------------------------------------------------------ + +// TODO: add error handling +Node::Ptr Node::Create(mx::Node* mtlxNode, LoaderContext* context) { + rpr_material_node rprNode = nullptr; + Mtlx2Rpr::Node const* rprNodeMapping = nullptr; + + // Check for nodes with special handling first + // + if (mtlxNode->getCategory() == "surface") { + auto surfaceDef = mtlxNode->getNodeDef(); + // The surface node has 3 inputs: bsdf, edf and opacity. + // Right now we can not implement bsdf and edf blending and, + // as a workaround, our surface node simply transfers bsdf node further along connections + // + struct SurfaceNode : public RprNode { + SurfaceNode() : RprNode(nullptr, false) {} + + rpr_status SetInput(mx::Element* inputElement, rpr_material_node inputNode, LoaderContext* context) override { + if (inputElement->getName() == "bsdf") { + rprNode = inputNode; + return RPR_SUCCESS; + } else { + context->Log("Unsupported surface input: %s\n", inputElement->getName().c_str()); + return RPR_ERROR_UNSUPPORTED; + } + } + }; + + 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::Element* inputElement, rpr_material_node inputNode, LoaderContext* context) override { + if (inputElement->getName() == "displacement") { + rprNode = inputNode; + isOwningRprNode = false; + return RPR_SUCCESS; + } else { + context->Log("Unsupported displacement input: %s\n", inputElement->getName().c_str()); + return RPR_ERROR_UNSUPPORTED; + } + } + + rpr_status SetInput(mx::Element* inputElement, std::string const& value, std::string const& valueType, LoaderContext* context) override { + if (inputElement->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(RPR_MATERIAL_INPUT_VALUE, value, valueType, context); + if (status == RPR_SUCCESS) { + isOwningRprNode = true; + } else { + rprObjectDelete(rprNode); + rprNode = nullptr; + } + } + return status; + } else { + context->Log("Unsupported displacement input: %s\n", inputElement->getName().c_str()); + return RPR_ERROR_UNSUPPORTED; + } + } + }; + + return std::make_unique(); + } 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() == "sqrt") { + rprMaterialSystemCreateNode(context->rprMatSys, RPR_MATERIAL_NODE_ARITHMETIC, &rprNode); + rprMaterialNodeSetInputUByKey(rprNode, RPR_MATERIAL_INPUT_OP, RPR_MATERIAL_NODE_OP_POW); + rprMaterialNodeSetInputFByKey(rprNode, RPR_MATERIAL_INPUT_COLOR1, 0.5f, 0.5f, 0.5f, 1.0f); + static Mtlx2Rpr::Node s_sqrtMapping = { + RPR_MATERIAL_NODE_ARITHMETIC, { + {"in", RPR_MATERIAL_INPUT_COLOR0} + } + }; + rprNodeMapping = &s_sqrtMapping; + } else if (mtlxNode->getCategory() == "image") { + return std::make_unique(context); + } else if (mtlxNode->getCategory() == "swizzle") { + // TODO: implement healthy man swizzle + + std::string channels; + if (auto channelsParam = mtlxNode->getActiveParameter("channels")) { + auto& valueString = channelsParam->getValueString(); + if (channelsParam->getType() == "string" && + !valueString.empty()) { + channels = valueString; + } + } + + rpr_material_node_arithmetic_operation op; + if (channels == "x") { + op = RPR_MATERIAL_NODE_OP_SELECT_X; + } else if (channels == "y") { + op = RPR_MATERIAL_NODE_OP_SELECT_Y; + } else { + return nullptr; + } + + rprMaterialSystemCreateNode(context->rprMatSys, RPR_MATERIAL_NODE_ARITHMETIC, &rprNode); + rprMaterialNodeSetInputUByKey(rprNode, RPR_MATERIAL_INPUT_OP, op); + + static Mtlx2Rpr::Node s_swizzleMapping = { + RPR_MATERIAL_NODE_ARITHMETIC, { + {"in", RPR_MATERIAL_INPUT_COLOR0} + } + }; + rprNodeMapping = &s_swizzleMapping; + + // Then process standard nodes that can be mapped directly to RPR nodes + // + } else { + // Some of the materialX standard nodes map to RPR differently depending on the node return type + // + if (mtlxNode->getCategory() == "mix" && mtlxNode->getType() == "BSDF") { + // In RPR, mixing of two BSDFs can be done with RPR_MATERIAL_NODE_BLEND + // + static Mtlx2Rpr::Node bsdfMix = { + RPR_MATERIAL_NODE_BLEND, { + {"fg", RPR_MATERIAL_INPUT_COLOR1}, + {"bg", RPR_MATERIAL_INPUT_COLOR0}, + {"mix", RPR_MATERIAL_INPUT_WEIGHT}, + } + }; + rprNodeMapping = &bsdfMix; + } else { + auto rprNodeMappingIt = GetMtlx2Rpr().nodes.find(mtlxNode->getCategory()); + if (rprNodeMappingIt != GetMtlx2Rpr().nodes.end()) { + rprNodeMapping = &rprNodeMappingIt->second; + } else { + // But direct mapping might not have been implemented yet or + // this node might be of a custom definition + // + if (auto nodeDef = mtlxNode->getNodeDef()) { + if (auto implementation = nodeDef->getImplementation()) { + if (auto nodeGraph = implementation->asA()) { + try { + return std::make_unique(std::move(nodeGraph), context); + } catch (MtlxNodeGraphNode::NoOutputsError& e) { + // no-op + } + } + } + } + + // TODO: code generation required + + context->Log("Unsupported node: %s (%s)\n", mtlxNode->getName().c_str(), mtlxNode->getCategory().c_str()); + } + } + } + + 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); + return nullptr; + } + + // For arithmetic nodes, we also must not forget to set operation + // + if (rprNodeMapping->id == RPR_MATERIAL_NODE_ARITHMETIC) { + auto it = GetMtlx2Rpr().arithmeticOps.find(mtlxNode->getCategory()); + if (it != GetMtlx2Rpr().arithmeticOps.end()) { + rprMaterialNodeSetInputUByKey(rprNode, RPR_MATERIAL_INPUT_OP, it->second); + } else { + LOG_ERROR(context, "unknown arithmetic node: %s (%s)", mtlxNode->getName().c_str(), mtlxNode->getCategory().c_str()); + } + } + } + + if (!rprNode) { + return nullptr; + } + rprObjectSetName(rprNode, mtlxNode->getName().c_str()); + + if (rprNodeMapping) { + return std::make_unique(rprNode, rprNodeMapping); + } else { + return std::make_unique(rprNode, true); + } +} + +//------------------------------------------------------------------------------ +// RprWrapNode implementation +//------------------------------------------------------------------------------ + +bool RprWrapNode::IsOutputTypeSupported(std::string const& outputType) { + return outputType == "color3" || outputType == "color2" || + outputType == "vector3" || outputType == "vector2" || + outputType == "boolean" || outputType == "float"; +} + +RprWrapNode::RprWrapNode(rpr_material_node nodeToWrap, std::string const& outputType, LoaderContext* ctx) + : RprNode(nullptr, true) { + auto& wrapRprNode = rprNode; + + auto status = rprMaterialSystemCreateNode(ctx->rprMatSys, RPR_MATERIAL_NODE_PASSTHROUGH, &wrapRprNode); + if (!wrapRprNode) { + LOG_ERROR(ctx, "Failed to create RPR_MATERIAL_NODE_PASSTHROUGH node: %d\n", status); + throw RprApiError(); + } + + if (outputType == "color3" || outputType == "color2" || + outputType == "vector3" || outputType == "vector2" || outputType == "boolean" || // Should we apply here any conversions? + outputType == "float") { // XXX: how to create rrr float3 from 1 float? Right now, it works okay because of how RprNode::SetInput implemented + status = rprMaterialNodeSetInputNByKey(wrapRprNode, RPR_MATERIAL_INPUT_COLOR, nodeToWrap); + if (status != RPR_SUCCESS) { + LOG_ERROR(ctx, "Failed to create set RPR_MATERIAL_NODE_PASSTHROUGH node color input: %d\n", status); + throw RprApiError(); + } + } else { + LOG_ERROR(ctx, "Failed to wrap node: unknown type - %s\n", outputType.c_str()); + throw UnknownType(); + } +} + +//------------------------------------------------------------------------------ +// MtlxNodeGraphNode implementation +//------------------------------------------------------------------------------ + +MtlxNodeGraphNode::MtlxNodeGraphNode(mx::ConstGraphElementPtr const& mtlxGraph, LazyInit) + : mtlxGraph(mtlxGraph) { + +} + +MtlxNodeGraphNode::MtlxNodeGraphNode(mx::ConstGraphElementPtr const& mtlxGraph, LoaderContext* context) + : MtlxNodeGraphNode(mtlxGraph, mtlxGraph->getOutputs(), context) { + +} + +MtlxNodeGraphNode::MtlxNodeGraphNode( + mx::ConstGraphElementPtr const& graph, + std::vector const& requiredOutputs, + LoaderContext* context) + : mtlxGraph(graph) { + + context->Log("NodeGraph: %s\n", 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()); + + // An output of a node graph must have a `nodename` attribute + // + auto subNode = GetSubNode(output->getNodeName(), context); + if (subNode) { + hasAnyOutputNode = true; + } else { + LOG_ERROR(context, "Failed to create node %s in %s\n", output->getNodeName().c_str(), mtlxGraph->getName().c_str()); + } + } + + if (!hasAnyOutputNode) { + throw NoOutputsError(); + } +} + +Node* MtlxNodeGraphNode::GetSubNode(std::string const& nodename, LoaderContext* context) { + auto subNodeIt = subNodes.find(nodename); + if (subNodeIt != subNodes.end()) { + return subNodeIt->second.get(); + } + + auto mtlxNode = mtlxGraph->getNode(nodename); + if (!mtlxNode) { + LOG_ERROR(context, "No node with such name: %s\n", 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 status = upstreamNode->Connect(connection.upstreamNodeOutput.get(), 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; +} + +Node* MtlxNodeGraphNode::_CreateSubNode( + mx::NodePtr const& mtlxNode, + std::vector* pendingConnections, + LoaderContext* context) { + if (!mtlxNode) { + return nullptr; + } + + auto subNodeIt = subNodes.find(mtlxNode->getName()); + if (subNodeIt != subNodes.end()) { + return subNodeIt->second.get(); + } + + context->Log("Node: %s (%s)\n", mtlxNode->getName().c_str(), mtlxNode->getCategory().c_str()); + auto nodeScope = context->EnterScope(LSNode, mtlxNode.get()); + + auto nodeHandle = Node::Create(mtlxNode.get(), context); + if (!nodeHandle) { + return nullptr; + } + + auto node = nodeHandle.get(); + subNodes.emplace(mtlxNode->getName(), std::move(nodeHandle)); + + auto nodeDef = mtlxNode->getNodeDef(); + if (!nodeDef) { + LOG_ERROR(context, "Failed to get mtlxNode definition: %s\n", mtlxNode->asString().c_str()); + return node; + } + + // Iterate over nodeDef's inputs/parameters and apply them + // + for (auto& nodeDefChild : nodeDef->getChildren()) { + if (nodeDefChild->getCategory() == "output") { + continue; + } + + // ValueElement is a common base type + // + auto inputElement = nodeDefChild->asA(); + if (!inputElement) { + continue; + } + + context->Log("%s %s\n", 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 + // + auto valueElement = inputElement; + + // mtlxNode may override one of the inputs/parameters defined in nodeDef. + // + if (auto mtlxNodeChild = mtlxNode->getChild(valueElement->getName())) { + if (auto mtlxNodeInputElement = mtlxNodeChild->asA()) { + auto& interfaceName = mtlxNodeInputElement->getInterfaceName(); + if (!interfaceName.empty()) { + // If this input has an interface name then the instantiator of this node might override this value later, + // this input will be referenced by the interface name, so we map this name to a particular input socket + // + _interfaceSockets[interfaceName].push_back({mtlxNode, valueElement}); + // + // For now, get value from a nodeDef of the current nodeGraph + // + if (auto nodeGraph = mtlxGraph->asA()) { + if (auto nodeGraphDef = nodeGraph->getNodeDef()) { + if (auto nodeGraphDefInput = nodeGraphDef->getInput(interfaceName)) { + valueElement = nodeGraphDefInput; + } + } + } + } else { + valueElement = std::move(mtlxNodeInputElement); + } + } + } + + rpr_status status = RPR_SUCCESS; + + // A input element may be of different types: Input or Parameter (removed in 1.38). + // + // If an input element is of Input type, it can specify its value through: + // + if (auto input = valueElement->asA()) { + // `nodename` attribute - an input connection to the node of the current graph + // + auto& nodeName = input->getNodeName(); + if (!nodeName.empty()) { + context->Log("nodename: %s\n", 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()); + continue; + } + + auto mtlxUpstreamNodeOutput = GetOutput(mtlxUpstreamNode.get(), input.get(), context); + if (!mtlxUpstreamNodeOutput && mtlxUpstreamNode->getType() == mx::MULTI_OUTPUT_TYPE_STRING) { + 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.get(), node, inputElement.get(), context); + } 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(inputElement); + pendingConnection.upstreamNode = std::move(mtlxUpstreamNode); + pendingConnection.upstreamNodeOutput = std::move(mtlxUpstreamNodeOutput); + } + + if (status == RPR_SUCCESS) { + continue; + } + } + + // `output`[&`nodegraph`] attribute[s] - an input connection to a freestanding output[/nodegraph] + // + if (context->ConnectToGlobalOutput(input.get(), node)) { + continue; + } + + // `defaultgeomprop` attribute - an intrinsic geometric property + // + auto& defaultGeomProp = input->getDefaultGeomPropString(); + if (!defaultGeomProp.empty()) { + if (auto geomPropDef = context->mtlxDocument->getGeomPropDef(defaultGeomProp)) { + if (auto geomNode = context->GetGeomNode(geomPropDef.get())) { + status = geomNode->Connect(nullptr, node, inputElement.get(), context); + } + } else { + LOG_ERROR(context, "Unkown defaultgeomprop: %s\n", defaultGeomProp.c_str()); + } + + continue; + } + } + + // `value` attribute - a uniform value + // + auto& valueStr = valueElement->getValueString(); + if (!valueStr.empty()) { + context->Log("%s\n", valueStr.c_str()); + + status = node->SetInput(inputElement.get(), valueStr, valueElement->getType(), context); + if (status == RPR_SUCCESS) { + continue; + } + } + } + + return node; +} + +rpr_status MtlxNodeGraphNode::Connect(mx::Element* outputElement, Node* downstreamNode, mx::Element* inputElement, LoaderContext* context) { + mx::OutputPtr output; + if (outputElement) { + output = mtlxGraph->getOutput(outputElement->getName()); + } else { + output = GetFirst(mtlxGraph.get()); + } + + if (output && !output->getNodeName().empty()) { + auto nodeIt = subNodes.find(output->getNodeName()); + if (nodeIt != subNodes.end()) { + return nodeIt->second->Connect(nullptr, downstreamNode, inputElement, context); + } + } + + return RPR_ERROR_INVALID_PARAMETER; +} + +MtlxNodeGraphNode::InterfaceSocketsQuery MtlxNodeGraphNode::_GetInterfaceSockets(mx::Element* inputElement) { + if (auto valueElement = inputElement->asA()) { + auto& interfaceName = valueElement->getInterfaceName(); + if (!interfaceName.empty()) { + if (auto query = _GetInterfaceSockets(interfaceName)) { + return query; + } + } + } + + return _GetInterfaceSockets(inputElement->getName()); +} + +MtlxNodeGraphNode::InterfaceSocketsQuery MtlxNodeGraphNode::_GetInterfaceSockets(std::string const& interfaceName) { + auto it = _interfaceSockets.find(interfaceName); + if (it == _interfaceSockets.end()) { + return {}; + } + return {this, &it->second}; +} + +rpr_status MtlxNodeGraphNode::SetInput(mx::Element* inputElement, rpr_material_node inputNode, LoaderContext* context) { + if (auto sockets = _GetInterfaceSockets(inputElement)) { + rpr_status status = RPR_SUCCESS; + sockets.ForEach(context, + [&](Node* socketNode, mx::Element* socketInputElement) { + auto setInputStatus = socketNode->SetInput(socketInputElement, inputNode, context); + if (setInputStatus != RPR_SUCCESS) { + status = setInputStatus; + } + } + ); + return status; + } + + LOG_ERROR(context, "failed to set %s input for %s: no such interface socket\n", inputElement->getName().c_str(), mtlxGraph->getName().c_str()); + return RPR_ERROR_INVALID_PARAMETER; +} + +rpr_status MtlxNodeGraphNode::SetInput(mx::Element* inputElement, std::string const& value, std::string const& valueType, LoaderContext* context) { + if (auto sockets = _GetInterfaceSockets(inputElement)) { + rpr_status status = RPR_SUCCESS; + sockets.ForEach(context, + [&](Node* socketNode, mx::Element* socketInputElement) { + auto setInputStatus = socketNode->SetInput(socketInputElement, value, valueType, context); + if (setInputStatus != RPR_SUCCESS) { + status = setInputStatus; + } + } + ); + return status; + } + + LOG_ERROR(context, "failed to set %s input for %s: no such interface socket\n", inputElement->getName().c_str(), mtlxGraph->getName().c_str()); + return RPR_ERROR_INVALID_PARAMETER; +} + +//------------------------------------------------------------------------------ +// RprNode implementation +//------------------------------------------------------------------------------ + +RprNode::RprNode(rpr_material_node node, bool retainNode) + : isOwningRprNode(retainNode), rprNode(node) { + +} + +RprNode::~RprNode() { + if (rprNode && isOwningRprNode) { + rprObjectDelete(rprNode); + } +} + +rpr_status RprNode::Connect(mx::Element* outputElement, Node* downstreamNode, mx::Element* inputElement, LoaderContext* context) { + // Ignoring outputElement because rprNode is the only possible output + // + return downstreamNode->SetInput(inputElement, rprNode, context); +} + +rpr_status RprNode::SetInput(mx::Element* inputElement, rpr_material_node inputNode, LoaderContext* context) { + return RPR_ERROR_UNSUPPORTED; +} + +rpr_status RprNode::SetInput(mx::Element* inputElement, std::string const& value, std::string const& valueType, LoaderContext* context) { + return RPR_ERROR_UNSUPPORTED; +} + +rpr_status RprNode::SetInput(rpr_material_node_input inputId, std::string const& valueString, std::string const& valueType, LoaderContext* context) { + try { + if (valueType == "float") { + auto value = mx::fromValueString(valueString); + return rprMaterialNodeSetInputFByKey(rprNode, inputId, value, value, value, 0.0f); + } else if ( + valueType == "color2" || + valueType == "vector2") { + auto value = mx::fromValueString(valueString); + return rprMaterialNodeSetInputFByKey(rprNode, inputId, value[0], value[1], 0.0f, 0.0f); + } else if ( + valueType == "color3" || + valueType == "vector3") { + auto value = mx::fromValueString(valueString); + return rprMaterialNodeSetInputFByKey(rprNode, inputId, value[0], value[1], value[2], 0.0f); + } else if ( + valueType == "boolean") { + auto value = static_cast(mx::fromValueString(valueString)); + return rprMaterialNodeSetInputFByKey(rprNode, inputId, value, value, value, 0.0f); + } else if ( + valueType == "integer") { + auto value = static_cast(mx::fromValueString(valueString)); + return rprMaterialNodeSetInputFByKey(rprNode, inputId, value, value, value, 0.0f); + } else { + LOG_ERROR(context, "failed to parse %s value: unsupported type - %s\n", 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()); + } + + return RPR_ERROR_INVALID_PARAMETER; +} + +RprMappedNode::RprMappedNode(rpr_material_node node, Mtlx2Rpr::Node const* nodeMapping) + : RprNode(node, true), rprNodeMapping(nodeMapping) { + +} + +rpr_status RprMappedNode::SetInput(mx::Element* inputElement, rpr_material_node inputNode, LoaderContext* context) { + auto inputIt = rprNodeMapping->inputs.find(inputElement->getName()); + if (inputIt == rprNodeMapping->inputs.end()) { + LOG_ERROR(context, "unknown input: %s\n", inputElement->getName().c_str()); + return RPR_ERROR_INVALID_PARAMETER; + } + + return rprMaterialNodeSetInputNByKey(rprNode, inputIt->second, inputNode); +} + +rpr_status RprMappedNode::SetInput(mx::Element* inputElement, std::string const& valueString, std::string const& valueType, LoaderContext* context) { + auto inputIt = rprNodeMapping->inputs.find(inputElement->getName()); + if (inputIt == rprNodeMapping->inputs.end()) { + LOG_ERROR(context, "unknown input: %s\n", inputElement->getName().c_str()); + return RPR_ERROR_INVALID_PARAMETER; + } + + return RprNode::SetInput(inputIt->second, valueString, valueType, context); +} + +RprImageNode::RprImageNode(LoaderContext* context) + : RprMappedNode( + [context]() { + rpr_material_node node = nullptr; + rprMaterialSystemCreateNode(context->rprMatSys, RPR_MATERIAL_NODE_IMAGE_TEXTURE, &node); + return node; + }(), + []() { + static Mtlx2Rpr::Node s_imageMapping = { + RPR_MATERIAL_NODE_IMAGE_TEXTURE, { + {"texcoord", RPR_MATERIAL_INPUT_UV} + } + }; + return &s_imageMapping; + }() + ) { + +} + +rpr_status RprImageNode::SetInput(mx::Element* inputElement, std::string const& value, std::string const& valueType, LoaderContext* context) { + rpr_status status = RPR_SUCCESS; + if (valueType == "string") { + if (inputElement->getName() == "layer") { + layer = value; + } else if (inputElement->getName() == "uaddressmode") { + uaddressmode = value; + } else if (inputElement->getName() == "vaddressmode") { + vaddressmode = value; + } else if (inputElement->getName() == "filtertype" || + inputElement->getName() == "framerange" || + inputElement->getName() == "frameendaction") { + // unprocessed for now + } else { + status = RPR_ERROR_INVALID_PARAMETER; + } + } else if (valueType == "filename") { + if (inputElement->getName() == "file") { + file = context->ResolveFile(value); + } else { + status = RPR_ERROR_INVALID_PARAMETER; + } + } else if (inputElement->getName() == "frameoffset" && valueType == "integer") { + // unprocessed for now + } else if (inputElement->getName() == "default") { + defaultValue = mx::Value::createValueFromStrings(value, valueType); + } else { + status = RprMappedNode::SetInput(inputElement, value, valueType, context); + } + + if (status != RPR_SUCCESS) { + LOG_ERROR(context, "Invalid input for image node %s (%s %s): unknown input or invalid type\n", + inputElement->getName().c_str(), value.c_str(), valueType.c_str()); + } + return status; +} + +//------------------------------------------------------------------------------ +// +//------------------------------------------------------------------------------ + +bool IsSupportedTarget(std::string const& target) { + // We currently support only universal nodes + return target.empty(); +} + +RPRMtlxLoader::OutputType ToOutputType(std::string const& type) { + if (type == "surfaceshader" || + RprWrapNode::IsOutputTypeSupported(type)) { + return RPRMtlxLoader::kOutputSurface; + } else if (type == "displacementshader") { + return RPRMtlxLoader::kOutputDisplacement; + } else { + return RPRMtlxLoader::kOutputNone; + } +} + +bool IsRenderableType(std::string const& mtlxTypeString) { + auto mtlxType = ToOutputType(mtlxTypeString); + return mtlxType != RPRMtlxLoader::kOutputNone; +} + +// F must be a function with bool(mx::OutputPtr const&, mx::ShaderRefPtr const& shaderRef) signature. +// Returned boolean controls whether ForEachOutput should continue traversing +template +void ForEachOutput(mx::ElementPtr const& element, F&& func) { + if (auto material = element->asA()) { + for (auto& shaderRef : material->getShaderRefs()) { + if (auto nodeDef = shaderRef->getNodeDef()) { + if (IsSupportedTarget(nodeDef->getTarget())) { + if (auto impl = nodeDef->getImplementation()) { + if (auto nodeGraph = impl->asA()) { + for (auto& child : nodeGraph->getChildren()) { + if (auto output = child->asA()) { + if (!func(output, shaderRef)) { + return; + } + } + } + } + } + } + } + } + } else if (auto nodeGraph = element->asA()) { + for (auto& child : nodeGraph->getChildren()) { + if (auto output = child->asA()) { + if (!func(output, nullptr)) { + return; + } + } + } + } else if (auto output = element->asA()) { + func(output, nullptr); + } else if (auto node = element->asA()) { + auto processShaderNode = [&func](mx::NodePtr const& shaderNode) { + auto referenceOutput = std::make_shared(shaderNode->getParent(), "out"); + referenceOutput->setNodeName(shaderNode->getName()); + referenceOutput->setType(shaderNode->getType()); + return func(referenceOutput, nullptr); + }; + + auto& nodeType = node->getType(); + if (nodeType == "material") { + for (auto& shaderType : {"surfaceshader", "displacementshader"}) { + if (auto shader = node->getInput(shaderType)) { + if (mx::ElementPtr shaderNodeElement = element->getParent()->getChild(shader->getNodeName())) { + if (auto shaderNode = shaderNodeElement->asA()) { + if (!processShaderNode(shaderNode)) { + return; + } + } + } + } + } + } else if ( + nodeType == "surfaceshader" || + nodeType == "displacementshader") { + if (!processShaderNode(node)) { + return; + } + } + } +} + +bool IsMaterialHasRenderableOutputs(mx::MaterialPtr const& material) { + bool hasRenderableOutput = false; + ForEachOutput(material, + [&hasRenderableOutput](mx::OutputPtr const& output, mx::ShaderRefPtr const&) { + if (IsRenderableType(output->getType())) { + hasRenderableOutput = true; + } + // Keep iterating untill we find a renderable output + return !hasRenderableOutput; + }); + return hasRenderableOutput; +} + +// Alternative to mx::Element::getChildrenOfType. This function avoids using std::vector. +// F must be a function with void(std::shared_ptr) signature +template +void ForEachChildrenOfType(mx::Element const* element, S&& shouldStop, F&& func) { + if (shouldStop()) { return; } + + for (auto& child : element->getChildren()) { + if (auto typedChild = child->asA()) { + func(typedChild); + + if (shouldStop()) { return; } + } + } +} + +// F must be a function with void(mx::ElementPtr const&) signature. +template +void ForEachRenderableElement(mx::Document const* mtlxDocument, S&& shouldStop, F&& func) { + // We are not using looks for what they are designed (the assignment of material to geometry) + // because RPRMtlxLoader does not deal with geometry. + // Though, we use it to get hints from mtlx file about available materials + ForEachChildrenOfType(mtlxDocument, shouldStop, [&](auto const& look) { + ForEachChildrenOfType(look.get(), shouldStop, + [&](auto const& materialAssign) { + if (auto material = mtlxDocument->getChild(materialAssign->getMaterial())) { + func(material); + } + } + ); + }); + + ForEachChildrenOfType(mtlxDocument, shouldStop, + [&func](auto const& material) { + if (IsMaterialHasRenderableOutputs(material)) { + func(material); + } + } + ); + + auto mtlxVersion = mtlxDocument->getVersionIntegers(); + if (mtlxVersion.first >= 1 && mtlxVersion.second >= 38) { + // Iterate over all global shader nodes + // + ForEachChildrenOfType(mtlxDocument, shouldStop, + [&func, &mtlxDocument](auto const& node) { + if (!node->hasSourceUri()) { + if (auto typeDef = mtlxDocument->getTypeDef(node->getType())) { + if (typeDef->getSemantic() == mx::SHADER_SEMANTIC) { + func(node); + } + } + } + } + ); + } + + ForEachChildrenOfType(mtlxDocument, shouldStop, + [&func](auto const& nodeGraph) { + // Skip anything from an include file including libraries. + // Skip any nodegraph which is a definition + // + if (nodeGraph->hasSourceUri() || + nodeGraph->hasAttribute(mx::InterfaceElement::NODE_DEF_ATTRIBUTE)) { + return; + } + + bool stop = false; + ForEachChildrenOfType(nodeGraph.get(), + [&stop]() { return stop; }, + [&](auto const& output) { + if (ToOutputType(output->getType()) != RPRMtlxLoader::kOutputNone) { + func(nodeGraph); + stop = true; + } + } + ); + } + ); + + // Iterate over all global outputs + // + ForEachChildrenOfType(mtlxDocument, shouldStop, + [&func](auto const& output) { + if (!output->hasSourceUri()) { + if (ToOutputType(output->getType()) != RPRMtlxLoader::kOutputNone) { + func(output); + } + } + } + ); +} + +template +void TraverseNode(Node* node, F&& cb) { + cb(node); + if (auto mtlxGraphNode = node->AsA()) { + for (auto& entry : mtlxGraphNode->subNodes) { + TraverseNode(entry.second.get(), cb); + } + } +} + +struct GraphNodesKey { + mx::GraphElement const* nodeGraph; + mx::ShaderRef const* shaderRef; + + struct Hash { + size_t operator()(GraphNodesKey const& key) const { + return GetHash(key.nodeGraph) ^ GetHash(key.shaderRef); + } + }; + + bool operator==(GraphNodesKey const& rhs) const { + return nodeGraph == rhs.nodeGraph && + shaderRef == rhs.shaderRef; + } +}; +struct GraphNodesValue { + std::vector outputTypes; + MtlxNodeGraphNode::Ptr node; + RprWrapNode::Ptr wrapNode; +}; +using GraphNodes = std::unordered_map; + +template +void TraverseGraphNodes(GraphNodes const& graphNodes, LoaderContext* ctx, F&& cb) { + for (auto& entry : graphNodes) { + if (entry.second.node) { + TraverseNode(entry.second.node.get(), cb); + } + if (entry.second.wrapNode) { + TraverseNode(entry.second.wrapNode.get(), cb); + } + } + if (ctx->globalNodeGraph) { + TraverseNode(ctx->globalNodeGraph.get(), cb); + } + for (auto& entry : ctx->geomNodes) { + TraverseNode(entry.second.get(), cb); + } + for (auto& entry : ctx->freeStandingNodeGraphs) { + if (entry.second) { + TraverseNode(entry.second.get(), cb); + } + } +} + +} // namespace anonymous + +//------------------------------------------------------------------------------ +// Loader +//------------------------------------------------------------------------------ + +RPRMtlxLoader::RPRMtlxLoader() + : _stdSearchPath(mx::getEnvironmentPath()) { + +} + +RPRMtlxLoader::RenderableElements RPRMtlxLoader::GetRenderableElements(mx::Document const* mtlxDocument) { + std::unordered_set processedElements; + RenderableElements elements; + ForEachRenderableElement(mtlxDocument, + []() { return false; }, + [&](mx::ElementPtr const& mtlxElement) { + if (processedElements.count(mtlxElement.get())) { + return; + } + processedElements.insert(mtlxElement.get()); + + bool elementOutputTypes[kOutputsTotal] = {}; + ForEachOutput(mtlxElement, [&elementOutputTypes](mx::OutputPtr const& output, mx::ShaderRefPtr const&) { + auto outputType = ToOutputType(output->getType()); + if (outputType != kOutputNone) { + elementOutputTypes[outputType] = true; + } + + return true; + }); + + std::string elementNamePath; + for (int i = 0; i < kOutputsTotal; ++i) { + if (elementOutputTypes[i]) { + if (elementNamePath.empty()) { + elementNamePath = mtlxElement->getNamePath(); + } + elements.namePaths[i].push_back(elementNamePath); + } + } + } + ); + return elements; +} + +RPRMtlxLoader::Result RPRMtlxLoader::Load( + MaterialX::Document const* mtlxDocument, + const std::string inputRenderableElements[kOutputsTotal], + MaterialX::FileSearchPath const& searchPath, + rpr_material_system rprMatSys) { + + LoaderContext ctx = {}; + ctx.logEnabled = _loggingEnabled; + ctx.mtlxDocument = mtlxDocument; + ctx.rprMatSys = rprMatSys; + ctx.searchPath = searchPath; + ctx.searchPath.append(_stdSearchPath); + auto globalScope = ctx.EnterScope(LSGlobal, mtlxDocument); + + class MtlxRenderableElements { + public: + void Add(RPRMtlxLoader::OutputType outputType, mx::ElementPtr const& element, LoaderContext* ctx) { + if (Exists(outputType)) { + return; + } + + ForEachOutput(element, [this, outputType](mx::OutputPtr const& output, mx::ShaderRefPtr const& shaderRef) { + _Add(outputType, output, shaderRef); + + // Keep iterating until we find renderable element + return !Exists(outputType); + }); + } + + void Disable(RPRMtlxLoader::OutputType outputType) { + _activeElementsBits |= 1u << outputType; + _elements[outputType] = {}; + } + + bool IsEmpty() const { + return _activeElementsBits == 0; + } + + bool IsFull() const { + return _activeElementsBits == kAllElementsBits; + } + + bool Exists(RPRMtlxLoader::OutputType outputType) { + if (outputType == RPRMtlxLoader::kOutputAny) { + return IsFull(); + } + return _activeElementsBits & (1u << outputType); + } + + struct Element { + mx::OutputPtr output; + mx::ShaderRefPtr shaderRef; + }; + Element const& Get(RPRMtlxLoader::OutputType type) const { return _elements[type]; } + + private: + void _Add(RPRMtlxLoader::OutputType outputType, mx::OutputPtr const& output, mx::ShaderRefPtr const& shaderRef) { + // Validate output type + // + auto actualOutputType = ToOutputType(output->getType()); + if (actualOutputType != RPRMtlxLoader::kOutputNone) { + + // If we are looking for a particular output type, + // discard all outputs with a different type + // + if (outputType != RPRMtlxLoader::kOutputAny && + outputType != actualOutputType) { + return; + } + + uint8_t outputTypeBit = 1u << actualOutputType; + if ((_activeElementsBits & outputTypeBit) == 0) { + _elements[actualOutputType] = {output, shaderRef}; + _activeElementsBits |= outputTypeBit; + } + } + } + + private: + Element _elements[RPRMtlxLoader::kOutputsTotal]; + + uint8_t _activeElementsBits = 0u; + enum { kAllElementsBits = (1u << RPRMtlxLoader::kOutputsTotal) - 1 }; + }; + MtlxRenderableElements renderableElements; + + // Process user's renderable elements + // + if (inputRenderableElements) { + for (int i = 0; i < kOutputsTotal; ++i) { + auto outputType = static_cast(i); + + auto& namePath = inputRenderableElements[i]; + if (namePath.empty()) { + renderableElements.Disable(outputType); + } else { + // XXX: getDescendant does not change the object, so it should be marked const + // + auto doc = const_cast(mtlxDocument); + + if (auto element = doc->getDescendant(namePath)) { + renderableElements.Add(outputType, element, &ctx); + } + } + } + } + + // If no renderable elements were specified or if they are invalid, + // use first available renderable elements in the mtlxDocument + // + if (renderableElements.IsEmpty()) { + ForEachRenderableElement(mtlxDocument, + [&]() { return renderableElements.IsFull(); }, + [&](mx::ElementPtr const& element) { renderableElements.Add(kOutputAny, element, &ctx); } + ); + } + + if (renderableElements.IsEmpty()) { + LOG_ERROR(&ctx, "No renderable elements in %s\n", mtlxDocument->getSourceUri().c_str()); + return {}; + } + + // A few renderable elements may base on the same nodeGraph-shaderRef pair, + // in this case, we want to create appropriate MtlxNodeGraphNode only once + // + GraphNodes graphNodes; + + for (int i = 0; i < kOutputsTotal; ++i) { + auto outputType = static_cast(i); + + auto& element = renderableElements.Get(outputType); + if (!element.output) { + continue; + } + + mx::ConstGraphElementPtr nodeGraph; + if (element.shaderRef) { + if (auto nodeDef = element.shaderRef->getNodeDef()) { + if (auto impl = nodeDef->getImplementation()) { + nodeGraph = impl->asA(); + } + } + } else { + nodeGraph = element.output->getParent()->asA(); + } + if (!nodeGraph) { + continue; + } + + GraphNodesKey graphNodesKey{nodeGraph.get(), element.shaderRef.get()}; + graphNodes[graphNodesKey].outputTypes.push_back(outputType); + } + + // Create MtlxNodeGraphNode for each nodeGraph-shaderRef pair + // + bool hasAnyOutput = false; + RprNode* rprOutputs[kOutputsTotal] = {}; + for (auto& entry : graphNodes) { + std::vector requiredOutputs; + for (auto outputType : entry.second.outputTypes) { + requiredOutputs.push_back(renderableElements.Get(outputType).output); + } + + try { + auto nodeGraph = entry.first.nodeGraph->getSelf()->asA(); + + entry.second.node = std::make_unique(nodeGraph, requiredOutputs, &ctx); + + if (entry.first.shaderRef) { + ForEachChildrenOfType(entry.first.shaderRef, + []() { return false; }, + [&ctx, node=entry.second.node.get()](mx::BindInputPtr const& bindInput) { + if (ctx.ConnectToGlobalOutput(bindInput.get(), node)) { + return; + } + + auto& valueStr = bindInput->getValueString(); + if (!valueStr.empty()) { + auto& type = bindInput->getType(); + + ctx.Log("Bindinput %s: %s (%s)\n", bindInput->getName().c_str(), valueStr.c_str(), type.c_str()); + + node->SetInput(bindInput.get(), valueStr, type, &ctx); + } + } + ); + } + } catch (MtlxNodeGraphNode::NoOutputsError&) { + continue; + } + + for (size_t i = 0; i < requiredOutputs.size(); ++i) { + auto& output = requiredOutputs[i]; + + auto it = entry.second.node->subNodes.find(output->getNodeName()); + if (it != entry.second.node->subNodes.end()) { + RprNode* rprOutput = nullptr; + if (auto rprNode = it->second->AsA()) { + rprOutput = rprNode; + } else if (auto nodeGraphNode = it->second->AsA()) { + if (auto nodeGraphOutput = GetOutput(nodeGraphNode->mtlxGraph.get(), output.get(), &ctx)) { + if (auto subNode = nodeGraphNode->GetSubNode(nodeGraphOutput->getNodeName(), &ctx)) { + rprOutput = subNode->AsA(); + } + } + } + + // Nodes with non-shader type cannot be used directly as a surface output + // + if (entry.second.outputTypes[i] == kOutputSurface && + rprOutput && rprOutput->rprNode) { + + auto& outputType = output->getType(); + if (outputType != "surfaceshader") { + try { + entry.second.wrapNode = std::make_unique(rprOutput->rprNode, outputType, &ctx); + rprOutput = entry.second.wrapNode.get(); + } catch (RprWrapNode::Error& e) { + continue; + } + } + } + + if (rprOutput && rprOutput->rprNode) { + auto outputType = entry.second.outputTypes[i]; + rprOutputs[outputType] = rprOutput; + hasAnyOutput = true; + } + } + } + } + if (!hasAnyOutput) { + return {}; + } + + // Convert graphNodes to Result + + Result ret = {}; + + // Calculate the total number of rpr nodes + // + TraverseGraphNodes(graphNodes, &ctx, [&ret](Node* node) { + if (auto rprNode = node->AsA()) { + if (rprNode->isOwningRprNode) { + ret.numNodes++; + } + } + }); + + ret.nodes = new rpr_material_node[ret.numNodes]; + + // Map rpr_material_node handles to output type. We use it to fill rootNodeIndices + // + std::unordered_map targetOutputs; + for (int i = 0; i < kOutputsTotal; ++i) { + if (rprOutputs[i] && rprOutputs[i]->rprNode) { + targetOutputs.emplace(rprOutputs[i]->rprNode, static_cast(i)); + } + } + + // Move all rpr nodes from subnodes into the return array + // + std::vector outImageNodes; + size_t nodeIdx = 0; + TraverseGraphNodes(graphNodes, &ctx, [&ret, &nodeIdx, &outImageNodes](Node* node) { + if (auto rprNode = node->AsA()) { + + // Export ImageNode + // + if (auto imageNode = node->AsA()) { + if (!imageNode->file.empty()) { + outImageNodes.emplace_back(); + auto& outImageNode = outImageNodes.back(); + + std::swap(outImageNode.file, imageNode->file); + std::swap(outImageNode.layer, imageNode->layer); + std::swap(outImageNode.defaultValue, imageNode->defaultValue); + std::swap(outImageNode.uaddressmode, imageNode->uaddressmode); + std::swap(outImageNode.vaddressmode, imageNode->vaddressmode); + outImageNode.rprNode = rprNode->rprNode; + } + } + + // Move only unique nodes + // + if (rprNode->isOwningRprNode) { + ret.nodes[nodeIdx++] = rprNode->rprNode; + rprNode->rprNode = nullptr; + } + } + }); + + // Fill root node indices + // + for (auto& index : ret.rootNodeIndices) { + index = Result::kInvalidRootNodeIndex; + } + for (size_t i = 0; i < ret.numNodes; ++i) { + auto it = targetOutputs.find(ret.nodes[i]); + if (it != targetOutputs.end()) { + ret.rootNodeIndices[it->second] = i; + targetOutputs.erase(it); + } + } + + if (!outImageNodes.empty()) { + ret.numImageNodes = outImageNodes.size(); + ret.imageNodes = new Result::ImageNode[ret.numImageNodes]; + for (size_t i = 0; i < ret.numImageNodes; ++i) { + ret.imageNodes[i] = outImageNodes[i]; + } + } + + return ret; +} + +void RPRMtlxLoader::SetupStdlib(mx::FilePathVec const& libraryNames, mx::FileSearchPath const& searchPath) { + _stdlib = mx::createDocument(); + auto includes = mx::loadLibraries(libraryNames, searchPath, _stdlib); + if (!includes.empty()) { + _stdSearchPath.append(searchPath); + } +} diff --git a/deps/rprMtlxLoader/rprMtlxLoader.h b/deps/rprMtlxLoader/rprMtlxLoader.h new file mode 100644 index 000000000..9afc5ec59 --- /dev/null +++ b/deps/rprMtlxLoader/rprMtlxLoader.h @@ -0,0 +1,112 @@ +#ifndef RPRTOOLS_MTLX_LOADER_H +#define RPRTOOLS_MTLX_LOADER_H + +#include + +#include +#include + +class RPRMtlxLoader { +public: + RPRMtlxLoader(); + + void SetupStdlib(MaterialX::FilePathVec const& libraryNames, MaterialX::FileSearchPath const& searchPath); + MaterialX::ConstDocumentPtr GetStdlib() const { return _stdlib; } + + void SetLogging(bool enable) { _loggingEnabled = enable; } + + enum OutputType { + kOutputNone = -1, + kOutputSurface, + kOutputDisplacement, + kOutputsTotal, + kOutputAny = kOutputsTotal + }; + + struct RenderableElements { + /// A range of name paths to renderable elements per OutputType, + /// an actual MaterialX::Element may be resolved with MaterialX::Document::getDescendant + std::vector namePaths[kOutputsTotal]; + }; + static RenderableElements GetRenderableElements(MaterialX::Document const* mtlxDocument); + + struct Result { + /// All rpr nodes that form a material graph + rpr_material_node* nodes = nullptr; + + /// Number of elements in nodes array + size_t numNodes; + + size_t rootNodeIndices[kOutputsTotal]; + static const size_t kInvalidRootNodeIndex = size_t(-1); + + struct ImageNode { + /// The URI of an image file. + /// It's responsibility of the loader user to setup rpr_image and bind it to rprNode + std::string file; + + /// The name of the layer to extract from a multi-layer input file + std::string layer; + + /// A default value to use if the file reference can not be resolved + MaterialX::ValuePtr defaultValue; + + /// Determines how U/V coordinates outside the 0-1 range are processed. + /// Possible values: "constant", "clamp", "periodic", "mirror" + std::string uaddressmode; + std::string vaddressmode; + + /// The target image texture node + rpr_material_node rprNode; + }; + + ImageNode* imageNodes = nullptr; + size_t numImageNodes; + }; + + /// \ref Load parses provided \p mtlxDocument; + /// + /// \p renderableElements controls what element from \p mtlxDocument to use for each OutputType. + /// \p renderableElements may be null, in this case, loader will use first available ones in the mtlxDocument. + /// To disable a particular output type element, pass empty string. + /// If any renderable element (non-empty string) does not exist in mtlxDocument, + /// the loader will auto-select a renderable element with a corresponding output type. + /// + /// All parameters of `filename` type is resolved by the loader, + /// part of the resolve process consists of searching file in a particular directories, + /// RPRMtlxLoader's default search paths: + /// * value of MATERIALX_SEARCH_PATH env.var. + /// * stdlib search paths passed to RPRMtlxLoader::SetupStdlib + /// \p searchPath is appended to these paths + Result Load( + MaterialX::Document const* mtlxDocument, + const std::string inputRenderableElements[kOutputsTotal], + MaterialX::FileSearchPath const& searchPath, + rpr_material_system rprMatSys); + + /// Reference function on how properly to release RPRMtlxLoader::Result + static void Release(Result* result) { + if (!result || !result->nodes) { + return; + } + + for (size_t i = 0; i < result->numNodes; ++i) { + if (result->nodes[i]) { + rprObjectDelete(result->nodes[i]); + } + } + delete[] result->nodes; + if (result->imageNodes) { + delete[] result->imageNodes; + } + + *result = Result{}; + } + +private: + MaterialX::DocumentPtr _stdlib; + MaterialX::FileSearchPath _stdSearchPath; + bool _loggingEnabled = false; +}; + +#endif // RPRTOOLS_MTLX_LOADER_H diff --git a/pxr/imaging/CMakeLists.txt b/pxr/imaging/CMakeLists.txt new file mode 100644 index 000000000..6a3b169e6 --- /dev/null +++ b/pxr/imaging/CMakeLists.txt @@ -0,0 +1,7 @@ +add_custom_target(shared_libs) +add_custom_target(python ALL) + +add_subdirectory(rprUsd) +add_subdirectory(plugin) + +pxr_setup_python() diff --git a/pxr/imaging/plugin/CMakeLists.txt b/pxr/imaging/plugin/CMakeLists.txt new file mode 100644 index 000000000..6c787ec1e --- /dev/null +++ b/pxr/imaging/plugin/CMakeLists.txt @@ -0,0 +1,3 @@ +add_subdirectory(hdRpr) +add_subdirectory(glfRatImage) +add_subdirectory(rprHoudini) diff --git a/pxr/imaging/plugin/glfRatImage/CMakeLists.txt b/pxr/imaging/plugin/glfRatImage/CMakeLists.txt new file mode 100644 index 000000000..03354266b --- /dev/null +++ b/pxr/imaging/plugin/glfRatImage/CMakeLists.txt @@ -0,0 +1,23 @@ +if(NOT TARGET Houdini) + return() +endif() + +set(PXR_PREFIX pxr/imaging) +set(PXR_PACKAGE glfRatImage) + +pxr_plugin(glfRatImage + DISABLE_PRECOMPILED_HEADERS + LIBRARIES + tf + glf + arch + Houdini + + CPPFILES + glfRatImage.cpp + + RESOURCE_FILES + plugInfo.json +) + +GroupSources(glfRatImage) diff --git a/pxr/imaging/plugin/glfRatImage/glfRatImage.cpp b/pxr/imaging/plugin/glfRatImage/glfRatImage.cpp new file mode 100644 index 000000000..7745ec5d7 --- /dev/null +++ b/pxr/imaging/plugin/glfRatImage/glfRatImage.cpp @@ -0,0 +1,336 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "pxr/imaging/glf/glew.h" +#include "pxr/imaging/glf/image.h" +#include "pxr/imaging/glf/utils.h" + +#include +#include +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +class Glf_RatImage : public GlfImage { +public: + typedef GlfImage Base; + + Glf_RatImage(); + ~Glf_RatImage() override = default; + + std::string const& GetFilename() const override { return m_filename; } + int GetWidth() const override { return m_width; } + int GetHeight() const override { return m_height; } + GLenum GetFormat() const override; + GLenum GetType() const override { return m_outputType; } + int GetBytesPerPixel() const override; + int GetNumMipLevels() const override; + + bool IsColorSpaceSRGB() const override; + + bool GetMetadata(TfToken const& key, VtValue * value) const override { /* TODO */ return false; } + bool GetSamplerMetadata(GLenum pname, VtValue * param) const override { /* TODO */ return false; } + + bool Read(StorageSpec const& storage) override; + bool ReadCropped(int const cropTop, + int const cropBottom, + int const cropLeft, + int const cropRight, + StorageSpec const& storage) override; + + bool Write(StorageSpec const& storage, + VtDictionary const& metadata) override { /* TODO */ return false; } + +protected: + bool _OpenForReading(std::string const& filename, int subimage, + int mip, bool suppressErrors) override; + bool _OpenForWriting(std::string const& filename) override { /* TODO */ return false; } + +private: + bool _IsValidCrop(int cropTop, int cropBottom, int cropLeft, int cropRight); + bool _CropAndResize(void const *sourceData, int const cropTop, + int const cropBottom, + int const cropLeft, + int const cropRight, + bool resizeNeeded, + StorageSpec const& storage); + + std::string m_filename; + int m_width; + int m_height; + float m_gamma; + + //GL_UNSIGNED_BYTE, GL_FLOAT + GLenum m_outputType; + + int m_nchannels; +}; + +TF_REGISTRY_FUNCTION(TfType) { + TfType t = TfType::Define>(); + t.SetFactory>(); +} + +/// Returns the bpc (bits per channel) based on the GLType stored in storage +static int +_GetBytesPerChannelFromType(GLenum const& type) { + switch(type) { + case GL_UNSIGNED_BYTE: + return 1; + case GL_FLOAT: + return 4; + default: + TF_CODING_ERROR("Unsupported type"); + return 4; + } +} + +static int +_GetNumElements(GLenum format) { + switch (format) { + case GL_DEPTH_COMPONENT: + case GL_COLOR_INDEX: + case GL_ALPHA: + case GL_LUMINANCE: + case GL_RED: + return 1; + case GL_LUMINANCE_ALPHA: + case GL_RG: + return 2; + case GL_RGB: + return 3; + case GL_RGBA: + return 4; + default: + TF_CODING_ERROR("Unsupported format"); + return 1; + } +} + +bool Glf_RatImage::_IsValidCrop(int cropTop, int cropBottom, int cropLeft, int cropRight) { + int cropImageWidth = m_width - (cropLeft + cropRight); + int cropImageHeight = m_height - (cropTop + cropBottom); + return (cropTop >= 0 && + cropBottom >= 0 && + cropLeft >= 0 && + cropRight >= 0 && + cropImageWidth > 0 && + cropImageHeight > 0); +} + + Glf_RatImage::Glf_RatImage() + : m_width(0) + , m_height(0) + , m_gamma(0.0f) + , m_nchannels(0) { + +} + +GLenum Glf_RatImage::GetFormat() const { + switch (m_nchannels) { + case 1: + return GL_RED; + case 2: + return GL_RG; + case 3: + return GL_RGB; + case 4: + return GL_RGBA; + default: + TF_CODING_ERROR("Unsupported numComponents"); + return 1; + } +} + +int Glf_RatImage::GetBytesPerPixel() const { + return _GetBytesPerChannelFromType(m_outputType) * m_nchannels; +} + +bool Glf_RatImage::IsColorSpaceSRGB() const { + const float gamma_epsilon = 0.1f; + + // If we found gamma in the texture, use it to decide if we are sRGB + bool isSRGB = ( fabs(m_gamma-0.45455f) < gamma_epsilon); + if (isSRGB) { + return true; + } + + bool isLinear = ( fabs(m_gamma-1) < gamma_epsilon); + if (isLinear) { + return false; + } + + if (m_gamma > 0) { + TF_WARN("Unsupported gamma encoding in: %s", m_filename.c_str()); + } + + // Texture had no (recognized) gamma hint, make a reasonable guess + return ((m_nchannels == 3 || m_nchannels == 4) && + GetType() == GL_UNSIGNED_BYTE); +} + +int Glf_RatImage::GetNumMipLevels() const { + return 1; +} + +bool Glf_RatImage::_OpenForReading( + std::string const& filename, int subimage, + int mip, bool suppressErrors) { + if (mip != 0 || subimage != 0) { + /* TODO */ + return false; + } + + m_filename = filename; + + auto imageFile = std::unique_ptr(IMG_File::open(m_filename.c_str())); + if (!imageFile) { + return false; + } + + auto& stat = imageFile->getStat(); + + if (stat.getNumPlanes() < 1) { + return false; + } + auto plane = stat.getPlane(); + + m_width = stat.getXres(); + m_height = stat.getYres(); + if (m_width <= 0 || m_height <= 0) { + return false; + } + + m_nchannels = stat.getComponentCount(); + if (m_nchannels == 0) { + return false; + } + + m_gamma = plane->getColorSpaceGamma(); + + if (plane->getDataType() == IMG_UCHAR) { + m_outputType = GL_UNSIGNED_BYTE; + } else if (plane->getDataType() == IMG_HALF) { + m_outputType = GL_HALF_FLOAT; + } else if (plane->getDataType() == IMG_FLOAT) { + m_outputType = GL_FLOAT; + } else { + return false; + } + + return true; +} + +bool Glf_RatImage::Read(StorageSpec const& storage) { + return ReadCropped(0, 0, 0, 0, storage); +} + +bool Glf_RatImage::ReadCropped( + int const cropTop, + int const cropBottom, + int const cropLeft, + int const cropRight, + StorageSpec const& storage) { + if (cropTop || cropBottom || cropLeft || cropRight) { + // TODO: implement cropping + return false; + } + + std::unique_ptr imageFile(IMG_File::open(m_filename.c_str())); + if (!imageFile) { + return false; + } + + UT_Array rasters; + if (!imageFile->readImages(rasters) || rasters.isEmpty()) { + return false; + } + + // TODO: find out what to do with other images + std::unique_ptr raster(rasters[0]); + if (rasters.size() > 1) { + TF_WARN("Using only first raster from %s", m_filename.c_str()); + for (size_t i = 1; i < rasters.size(); ++i) { + delete rasters[i]; + } + } + + int numChannels; + if (raster->getPacking() == PACK_SINGLE) { + numChannels = 1; + } else if (raster->getPacking() == PACK_DUAL) { + numChannels = 2; + } else if (raster->getPacking() == PACK_RGB) { + numChannels = 3; + } else if (raster->getPacking() == PACK_RGBA) { + numChannels = 4; + } else { + TF_RUNTIME_ERROR("Failed to load image %s: unsupported RAT packing - %u", m_filename.c_str(), raster->getPacking()); + return false; + } + + if (numChannels != _GetNumElements(storage.format)) { + TF_RUNTIME_ERROR("Failed to load image %s: number of channels do not match - expected=%d, got=%d", m_filename.c_str(), m_nchannels, numChannels); + return false; + } + + GLenum format; + int bytesPerChannel; + if (raster->getFormat() == PXL_INT8) { + format = GL_UNSIGNED_BYTE; + bytesPerChannel = 1; + } else if (raster->getFormat() == PXL_FLOAT16) { + format = GL_HALF_FLOAT; + bytesPerChannel = 2; + } else if (raster->getFormat() == PXL_FLOAT32) { + format = GL_FLOAT; + bytesPerChannel = 4; + } else { + TF_RUNTIME_ERROR("Failed to load image %s: unsupported RAT format - %u", m_filename.c_str(), raster->getFormat()); + return false; + } + + if (format != m_outputType) { + TF_RUNTIME_ERROR("Failed to load image %s: format do not match - expected=%#x, got=%#x", m_filename.c_str(), m_outputType, format); + return false; + } + + if (raster->getXres() != storage.width || + raster->getYres() != storage.height) { + TF_RUNTIME_ERROR("Failed to load image %s: resolution do not match - expected=%dx%d, got=%dx%d", + m_filename.c_str(), m_width, m_height, int(raster->getXres()), int(raster->getYres())); + return false; + } + + auto srcPixels = reinterpret_cast(raster->getPixels()); + auto srcStride = raster->getStride(); + auto dstPixels = reinterpret_cast(storage.data); + auto dstStride = storage.width * numChannels * bytesPerChannel; + for (int y = 0; y < storage.height; ++y) { + int lineIndex = y; + if (!storage.flipped) { + // RAT image is flipped in Y axis so we flip the data when storage requires no flip + lineIndex = storage.height - 1 - y; + } + + auto dstData = dstPixels + dstStride * lineIndex; + auto srcData = srcPixels + srcStride * y; + + std::memcpy(dstData, srcData, dstStride); + } + + return true; +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/glfRatImage/plugInfo.json b/pxr/imaging/plugin/glfRatImage/plugInfo.json new file mode 100644 index 000000000..b6cb18572 --- /dev/null +++ b/pxr/imaging/plugin/glfRatImage/plugInfo.json @@ -0,0 +1,20 @@ +{ + "Plugins": [ + { + "Info": { + "Types": { + "Glf_RatImage" : { + "bases": ["GlfImage"], + "imageTypes": ["rat"], + "precedence": 0 + } + } + }, + "LibraryPath": "@PLUG_INFO_LIBRARY_PATH@", + "Name": "ratImage", + "ResourcePath": "@PLUG_INFO_RESOURCE_PATH@", + "Root": "@PLUG_INFO_ROOT@", + "Type": "library" + } + ] +} diff --git a/pxr/imaging/plugin/hdRpr/CMakeLists.txt b/pxr/imaging/plugin/hdRpr/CMakeLists.txt index eea3d0ead..896aebd5c 100644 --- a/pxr/imaging/plugin/hdRpr/CMakeLists.txt +++ b/pxr/imaging/plugin/hdRpr/CMakeLists.txt @@ -1,71 +1,30 @@ set(PXR_PREFIX pxr/imaging) set(PXR_PACKAGE hdRpr) -add_custom_target(shared_libs) set(OptLibs ${ARGN}) set(OptBin ${ARGN}) set(OptIncludeDir ${ARGN}) set(OptClass${ARGN}) -if(RPR_ENABLE_OPENVDB_SUPPORT) +if(OpenVDB_FOUND) add_definitions(-DUSE_VOLUME -DOPENVDB_DLL) set(OptLibs ${OptLibs} ${OpenVDB_LIBRARIES}) set(OptBin ${OptBin} ${OpenVDB_BINARIES}) set(OptIncludeDir ${OptIncludeDir} ${OpenVDB_INCLUDE_DIR}) set(OptClass ${OptClass} field volume) -endif(RPR_ENABLE_OPENVDB_SUPPORT) - -set(USD_LIBRARIES - ar - arch - sdf - trace - plug - tf - vt - gf - glf - work - hf - hd - hdSt - hdx - usdVol - usdLux - usdUtils - usdRender - usdGeom - usdImaging - pxOsd - cameraUtil) -if(${USD_LIBRARY_MONOLITHIC}) - set(USD_LIBRARIES usd_ms) -endif() - -set(_libPrefix ${PXR_LIB_PREFIX}) -# UNIX compilers adding "lib" prefix implicitly -if(NOT WIN32) - string(REGEX REPLACE "^lib" "" _libPrefix "${PXR_LIB_PREFIX}") -endif() - -set(_prefixedUsdLibraries "") -foreach(name ${USD_LIBRARIES}) - list(APPEND _prefixedUsdLibraries "${_libPrefix}${name}") -endforeach() -set(USD_LIBRARIES "${_prefixedUsdLibraries}") +endif(OpenVDB_FOUND) set(_sep ${PXR_RESOURCE_FILE_SRC_DST_SEPARATOR}) -if(RPR_BUILD_AS_HOUDINI_PLUGIN) +if(HoudiniUSD_FOUND) list(APPEND OptLibs Houdini) - add_definitions(-DENABLE_RAT) + add_definitions(-DENABLE_MULTITHREADED_RENDER_BUFFER) add_definitions(-DHDRPR_DEFAULT_MATERIAL_NETWORK_SELECTOR="karma") set(RESTART_REQUIRED_RESOURCE_FILE images/restartRequired_Houdini.png${_sep}images/restartRequired.png) -else(RPR_BUILD_AS_HOUDINI_PLUGIN) - add_definitions(-DENABLE_PREFERENCES_FILE) +else(HoudiniUSD_FOUND) add_definitions(-DHDRPR_DEFAULT_MATERIAL_NETWORK_SELECTOR="rpr") set(RESTART_REQUIRED_RESOURCE_FILE images/restartRequired_Usdview.png${_sep}images/restartRequired.png) -endif(RPR_BUILD_AS_HOUDINI_PLUGIN) +endif(HoudiniUSD_FOUND) set(GEN_SCRIPT_PYTHON ${PYTHON_EXECUTABLE}) set(GENERATION_SCRIPTS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/python) @@ -79,8 +38,7 @@ set(GENERATION_DEPENDENT_FILES ${GEN_SCRIPT} ${GENERATION_SCRIPTS_DIR}/generateLightSettingFiles.py ${GENERATION_SCRIPTS_DIR}/generateRenderSettingFiles.py ${GENERATION_SCRIPTS_DIR}/generateGeometrySettingFiles.py) -if(RPR_BUILD_AS_HOUDINI_PLUGIN) - set(GEN_SCRIPT_PYTHON ${HYTHON_EXECUTABLE}) +if(HoudiniUSD_FOUND) set(GEN_SCRIPT_ARGS --houdini_root \"${HOUDINI_ROOT}\" ${GEN_SCRIPT_ARGS}) set(GENERATED_FILES ${GENERATED_FILES} ${CMAKE_CURRENT_BINARY_DIR}/HdRprPlugin_Light.ds @@ -88,8 +46,24 @@ if(RPR_BUILD_AS_HOUDINI_PLUGIN) ${CMAKE_CURRENT_BINARY_DIR}/HdRprPlugin_Geometry.ds) set(GENERATION_DEPENDENT_FILES ${GENERATION_DEPENDENT_FILES} ${GENERATION_SCRIPTS_DIR}/houdiniDsGenerator.py) + + install( + FILES + ${CMAKE_CURRENT_BINARY_DIR}/HdRprPlugin_Light.ds + ${CMAKE_CURRENT_BINARY_DIR}/HdRprPlugin_Global.ds + ${CMAKE_CURRENT_BINARY_DIR}/HdRprPlugin_Geometry.ds + DESTINATION "houdini/soho/parameters") + + install( + CODE + "FILE(WRITE \"${CMAKE_INSTALL_PREFIX}/houdini/dso/usd_plugins/plugInfo.json\" + \"{ + \\\"Includes\\\": [ \\\"../../../plugin/\\\" ] + }\")") endif() +set(GEN_SCRIPT_ARGS --python_exe "\"${GEN_SCRIPT_PYTHON}\"" ${GEN_SCRIPT_ARGS}) + add_custom_command( COMMAND ${GEN_SCRIPT_PYTHON} ${GEN_SCRIPT} ${GEN_SCRIPT_ARGS} DEPENDS ${GENERATION_DEPENDENT_FILES} @@ -105,32 +79,34 @@ file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/rif_models.version ${RIF_VERSION_STRING}) list(APPEND RIF_MODEL_RESOURCE_FILES "${CMAKE_CURRENT_BINARY_DIR}/rif_models.version${_sep}rif_models/rif_models.version") -if(NOT DEFINED RPR_CPP_WRAPPER_LOCATION) - set(RPR_CPP_WRAPPER_LOCATION ${RPR_TOOLS_LOCATION}) -endif() - pxr_plugin(hdRpr - LIBRARIES - ${USD_LIBRARIES} - ${RPR_LIBRARY} - ${RPR_LOADSTORE_LIBRARY} + DISABLE_PRECOMPILED_HEADERS + + LIBRARIES + ar + trace + plug + tf + gf + hf + ndr + usdVol + usdLux + usdUtils + usdRender + usdGeom + usdImaging + pxOsd + cameraUtil + rprUsd + json ${RIF_LIBRARY} - ${Boost_LIBRARIES} - ${TBB_tbb_LIBRARY} - ${GLEW_LIBRARY} - ${OPENGL_LIBRARIES} - ${PYTHON_LIBRARIES} ${OPENEXR_LIBRARIES} ${OptLibs} INCLUDE_DIRS - ${RPR_LOCATION_INCLUDE} - ${RPR_CPP_WRAPPER_LOCATION} ${RIF_LOCATION_INCLUDE} - ${Boost_INCLUDE_DIRS} - ${TBB_INCLUDE_DIRS} - ${GLEW_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/thirdparty ${OPENEXR_INCLUDE_DIRS} @@ -138,6 +114,7 @@ pxr_plugin(hdRpr ${OptIncludeDir} PRIVATE_CLASSES + aovDescriptor rendererPlugin renderDelegate renderPass @@ -149,14 +126,11 @@ pxr_plugin(hdRpr mesh instancer material - materialFactory - materialAdapter domeLight distantLight light renderBuffer basisCurves - imageCache camera debugCodes primvarUtil @@ -165,7 +139,7 @@ pxr_plugin(hdRpr ${OptClass} PRIVATE_HEADERS - boostIncludePath.h + baseRprim.h api.h RESOURCE_FILES @@ -175,6 +149,8 @@ pxr_plugin(hdRpr CPPFILES ${CMAKE_CURRENT_BINARY_DIR}/config.cpp + ndrDiscoveryPlugin.cpp + ndrParserPlugin.cpp ) target_sources(hdRpr PRIVATE @@ -190,80 +166,32 @@ else() ${CMAKE_CURRENT_SOURCE_DIR}/notify/message.cpp) endif() -target_compile_definitions(hdRpr PRIVATE - RPR_CPPWRAPER_DISABLE_MUTEXLOCK - RPR_API_USE_HEADER_V2) - -target_sources(hdRpr PRIVATE - ${RPR_CPP_WRAPPER_LOCATION}/RadeonProRender.hpp - ${RPR_CPP_WRAPPER_LOCATION}/RadeonProRenderCpp.cpp) - -add_subdirectory(rpr) add_subdirectory(rifcpp) add_subdirectory(houdini) +if(NOT HoudiniUSD_FOUND) + add_subdirectory(usdviewMenu) +endif() -get_target_property(hdRpr_SOURCES hdRpr SOURCES) -foreach(FILE ${hdRpr_SOURCES}) - get_filename_component(ABS_FILE ${FILE} ABSOLUTE) - file(RELATIVE_PATH relPath ${CMAKE_CURRENT_SOURCE_DIR} ${ABS_FILE}) - - string(FIND "${relPath}" ".." out) - if("${out}" EQUAL 0) - source_group("external" FILES "${FILE}") - else() - get_filename_component(PARENT_DIR "${FILE}" DIRECTORY) - string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}" "" GROUP "${PARENT_DIR}") - string(REPLACE "/" "\\" GROUP "${GROUP}") - - source_group("${GROUP}" FILES "${FILE}") - endif() -endforeach() - -if(RPR_BUILD_AS_HOUDINI_PLUGIN) - install( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/HdRprPlugin_Light.ds - ${CMAKE_CURRENT_BINARY_DIR}/HdRprPlugin_Global.ds - ${CMAKE_CURRENT_BINARY_DIR}/HdRprPlugin_Geometry.ds - DESTINATION "${HOUDINI_RESOURCE_DIR_RELPATH}/houdini/soho/parameters") - install( - FILES ${CMAKE_CURRENT_SOURCE_DIR}/houdini/rpr_exportRpr1.hda - DESTINATION "${HOUDINI_RESOURCE_DIR_RELPATH}/houdini/otls") -else(RPR_BUILD_AS_HOUDINI_PLUGIN) - _get_install_dir(lib/python/rpr installPrefix) - - install( - FILES ${CMAKE_CURRENT_SOURCE_DIR}/python/rpr.py - DESTINATION ${installPrefix} - RENAME "__init__.py") +GroupSources(hdRpr) - install( - FILES ${CMAKE_CURRENT_SOURCE_DIR}/python/plugInfo.json - DESTINATION ${installPrefix}) - install( - CODE - "FILE(WRITE \"${CMAKE_INSTALL_PREFIX}/plugin/usd/plugInfo.json\" - \" - { - \\\"Includes\\\": [ \\\"*/resources/\\\" ] - } - \")") -endif(RPR_BUILD_AS_HOUDINI_PLUGIN) +install( + CODE + "FILE(WRITE \"${CMAKE_INSTALL_PREFIX}/plugin/plugInfo.json\" + \"{ + \\\"Includes\\\": [ \\\"usd/*/resources/\\\" ] +}\")") if(WIN32) + set(RPR_BINARIES + ${RPR_BIN_LOCATION}/RadeonProRender64.dll + ${RPR_BIN_LOCATION}/RprLoadStore64.dll) install( - FILES ${RPR_BINARIES} ${RIF_BINARIES} ${OptBin} - DESTINATION bin) + FILES ${RPR_BINARIES} ${RPR_PLUGINS} ${RIF_BINARIES} ${OptBin} + DESTINATION lib) else(WIN32) - if(RPR_BUILD_AS_HOUDINI_PLUGIN) - _get_install_dir("${HOUDINI_PLUGIN_INSTALL_RELPATH}" installPrefix) - else() - _get_install_dir("lib" installPrefix) - endif() - # install() does not follow symlinks, so we do it manually set(RESOLVED_LIBRARIES "") - foreach (file ${RPR_LIBRARY} ${RPR_LOADSTORE_LIBRARY} ${RPR_PLUGIN_LIBRARIES} ${RIF_LIBRARY} ${RIF_DEPENDENCY_LIBRARIES}) + foreach (file ${RPR_LIBRARY} ${RPR_LOADSTORE_LIBRARY} ${RPR_PLUGINS} ${RIF_LIBRARY} ${RIF_DEPENDENCY_LIBRARIES}) while(IS_SYMLINK ${file}) file(READ_SYMLINK ${file} symfile) if(NOT IS_ABSOLUTE "${symfile}") @@ -278,7 +206,7 @@ else(WIN32) install( FILES ${RESOLVED_LIBRARIES} - DESTINATION ${installPrefix}) + DESTINATION lib) endif(WIN32) add_subdirectory(package) diff --git a/pxr/imaging/plugin/hdRpr/aovDescriptor.cpp b/pxr/imaging/plugin/hdRpr/aovDescriptor.cpp new file mode 100644 index 000000000..97e58c478 --- /dev/null +++ b/pxr/imaging/plugin/hdRpr/aovDescriptor.cpp @@ -0,0 +1,171 @@ +/************************************************************************ +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 "aovDescriptor.h" + +#include "pxr/base/tf/instantiateSingleton.h" + +#include "pxr/imaging/hd/tokens.h" + +PXR_NAMESPACE_OPEN_SCOPE + +const HdRprAovDescriptor kInvalidDesc; + +TF_INSTANTIATE_SINGLETON(HdRprAovRegistry); +TF_DEFINE_PUBLIC_TOKENS(HdRprAovTokens, HDRPR_AOV_TOKENS); + +HdRprAovRegistry::HdRprAovRegistry() { + const auto rprAovMax = RPR_AOV_LPE_8 + 1; + const GfVec4f idClearValue(255.0f, 255.0f, 255.0f, 0.0f); + + m_aovDescriptors.resize(rprAovMax); + + // multisampled vec4f AOVs + for (auto rprAovId : { + RPR_AOV_COLOR, + RPR_AOV_DIFFUSE_ALBEDO, // XXX: RPR's albedo can be noisy in some cases, so we left it as multisampled + RPR_AOV_VARIANCE, + RPR_AOV_OPACITY, + RPR_AOV_EMISSION, + RPR_AOV_DIRECT_ILLUMINATION, + RPR_AOV_INDIRECT_ILLUMINATION, + RPR_AOV_AO, + RPR_AOV_DIRECT_DIFFUSE, + RPR_AOV_DIRECT_REFLECT, + RPR_AOV_INDIRECT_DIFFUSE, + RPR_AOV_INDIRECT_REFLECT, + RPR_AOV_REFRACT, + RPR_AOV_VOLUME, + RPR_AOV_LIGHT_GROUP0, + RPR_AOV_LIGHT_GROUP1, + RPR_AOV_LIGHT_GROUP2, + RPR_AOV_LIGHT_GROUP3, + RPR_AOV_COLOR_RIGHT, + RPR_AOV_SHADOW_CATCHER, + RPR_AOV_REFLECTION_CATCHER, + RPR_AOV_LPE_0, + RPR_AOV_LPE_1, + RPR_AOV_LPE_2, + RPR_AOV_LPE_3, + RPR_AOV_LPE_4, + RPR_AOV_LPE_5, + RPR_AOV_LPE_6, + RPR_AOV_LPE_7, + RPR_AOV_LPE_8 + }) { + m_aovDescriptors[rprAovId] = HdRprAovDescriptor(rprAovId); + } + + // singlesampled AOVs + m_aovDescriptors[RPR_AOV_DEPTH] = HdRprAovDescriptor(RPR_AOV_DEPTH, false, HdFormatFloat32, GfVec4f(std::numeric_limits::infinity())); + m_aovDescriptors[RPR_AOV_UV] = HdRprAovDescriptor(RPR_AOV_UV, false, HdFormatFloat32Vec3); + m_aovDescriptors[RPR_AOV_SHADING_NORMAL] = HdRprAovDescriptor(RPR_AOV_SHADING_NORMAL, false, HdFormatFloat32Vec3); + m_aovDescriptors[RPR_AOV_GEOMETRIC_NORMAL] = HdRprAovDescriptor(RPR_AOV_GEOMETRIC_NORMAL, false); + m_aovDescriptors[RPR_AOV_OBJECT_ID] = HdRprAovDescriptor(RPR_AOV_OBJECT_ID, false, HdFormatInt32, idClearValue); + m_aovDescriptors[RPR_AOV_MATERIAL_ID] = HdRprAovDescriptor(RPR_AOV_MATERIAL_ID, false, HdFormatInt32, idClearValue); + m_aovDescriptors[RPR_AOV_OBJECT_GROUP_ID] = HdRprAovDescriptor(RPR_AOV_OBJECT_GROUP_ID, false, HdFormatInt32, idClearValue); + m_aovDescriptors[RPR_AOV_WORLD_COORDINATE] = HdRprAovDescriptor(RPR_AOV_WORLD_COORDINATE, false); + m_aovDescriptors[RPR_AOV_BACKGROUND] = HdRprAovDescriptor(RPR_AOV_BACKGROUND, false); + m_aovDescriptors[RPR_AOV_VELOCITY] = HdRprAovDescriptor(RPR_AOV_VELOCITY, false); + m_aovDescriptors[RPR_AOV_VIEW_SHADING_NORMAL] = HdRprAovDescriptor(RPR_AOV_VIEW_SHADING_NORMAL, false); + + m_computedAovDescriptors.resize(kComputedAovsCount); + m_computedAovDescriptors[kNdcDepth] = HdRprAovDescriptor(kNdcDepth, false, HdFormatFloat32, GfVec4f(std::numeric_limits::infinity()), true); + m_computedAovDescriptors[kColorAlpha] = HdRprAovDescriptor(kColorAlpha, true, HdFormatFloat32Vec4, GfVec4f(0.0f), true); + + auto addAovNameLookup = [this](TfToken const& name, HdRprAovDescriptor const& descriptor) { + auto status = m_aovNameLookup.emplace(name, AovNameLookupValue(descriptor.id, descriptor.computed)); + if (!status.second) { + TF_CODING_ERROR("AOV lookup name should be unique"); + } + }; + + addAovNameLookup(HdAovTokens->color, m_computedAovDescriptors[kColorAlpha]); + addAovNameLookup(HdAovTokens->normal, m_aovDescriptors[RPR_AOV_SHADING_NORMAL]); + addAovNameLookup(HdAovTokens->primId, m_aovDescriptors[RPR_AOV_OBJECT_ID]); + addAovNameLookup(HdAovTokens->Neye, m_aovDescriptors[RPR_AOV_VIEW_SHADING_NORMAL]); + addAovNameLookup(HdAovTokens->depth, m_computedAovDescriptors[kNdcDepth]); + addAovNameLookup(HdRprGetCameraDepthAovName(), m_aovDescriptors[RPR_AOV_DEPTH]); + + addAovNameLookup(HdRprAovTokens->rawColor, m_aovDescriptors[RPR_AOV_COLOR]); + addAovNameLookup(HdRprAovTokens->albedo, m_aovDescriptors[RPR_AOV_DIFFUSE_ALBEDO]); + addAovNameLookup(HdRprAovTokens->variance, m_aovDescriptors[RPR_AOV_VARIANCE]); + addAovNameLookup(HdRprAovTokens->opacity, m_aovDescriptors[RPR_AOV_OPACITY]); + addAovNameLookup(HdRprAovTokens->emission, m_aovDescriptors[RPR_AOV_EMISSION]); + addAovNameLookup(HdRprAovTokens->directIllumination, m_aovDescriptors[RPR_AOV_DIRECT_ILLUMINATION]); + addAovNameLookup(HdRprAovTokens->indirectIllumination, m_aovDescriptors[RPR_AOV_INDIRECT_ILLUMINATION]); + addAovNameLookup(HdRprAovTokens->ao, m_aovDescriptors[RPR_AOV_AO]); + addAovNameLookup(HdRprAovTokens->directDiffuse, m_aovDescriptors[RPR_AOV_DIRECT_DIFFUSE]); + addAovNameLookup(HdRprAovTokens->directReflect, m_aovDescriptors[RPR_AOV_DIRECT_REFLECT]); + addAovNameLookup(HdRprAovTokens->indirectDiffuse, m_aovDescriptors[RPR_AOV_INDIRECT_DIFFUSE]); + addAovNameLookup(HdRprAovTokens->indirectReflect, m_aovDescriptors[RPR_AOV_INDIRECT_REFLECT]); + addAovNameLookup(HdRprAovTokens->refract, m_aovDescriptors[RPR_AOV_REFRACT]); + addAovNameLookup(HdRprAovTokens->volume, m_aovDescriptors[RPR_AOV_VOLUME]); + addAovNameLookup(HdRprAovTokens->lightGroup0, m_aovDescriptors[RPR_AOV_LIGHT_GROUP0]); + addAovNameLookup(HdRprAovTokens->lightGroup1, m_aovDescriptors[RPR_AOV_LIGHT_GROUP1]); + addAovNameLookup(HdRprAovTokens->lightGroup2, m_aovDescriptors[RPR_AOV_LIGHT_GROUP2]); + addAovNameLookup(HdRprAovTokens->lightGroup3, m_aovDescriptors[RPR_AOV_LIGHT_GROUP3]); + addAovNameLookup(HdRprAovTokens->colorRight, m_aovDescriptors[RPR_AOV_COLOR_RIGHT]); + addAovNameLookup(HdRprAovTokens->materialId, m_aovDescriptors[RPR_AOV_MATERIAL_ID]); + addAovNameLookup(HdRprAovTokens->objectGroupId, m_aovDescriptors[RPR_AOV_OBJECT_GROUP_ID]); + addAovNameLookup(HdRprAovTokens->geometricNormal, m_aovDescriptors[RPR_AOV_GEOMETRIC_NORMAL]); + addAovNameLookup(HdRprAovTokens->worldCoordinate, m_aovDescriptors[RPR_AOV_WORLD_COORDINATE]); + addAovNameLookup(HdRprAovTokens->primvarsSt, m_aovDescriptors[RPR_AOV_UV]); + addAovNameLookup(HdRprAovTokens->shadowCatcher, m_aovDescriptors[RPR_AOV_SHADOW_CATCHER]); + addAovNameLookup(HdRprAovTokens->reflectionCatcher, m_aovDescriptors[RPR_AOV_REFLECTION_CATCHER]); + addAovNameLookup(HdRprAovTokens->background, m_aovDescriptors[RPR_AOV_BACKGROUND]); + addAovNameLookup(HdRprAovTokens->velocity, m_aovDescriptors[RPR_AOV_VELOCITY]); + addAovNameLookup(HdRprAovTokens->viewShadingNormal, m_aovDescriptors[RPR_AOV_VIEW_SHADING_NORMAL]); + addAovNameLookup(HdRprAovTokens->lpe0, m_aovDescriptors[RPR_AOV_LPE_0]); + addAovNameLookup(HdRprAovTokens->lpe1, m_aovDescriptors[RPR_AOV_LPE_1]); + addAovNameLookup(HdRprAovTokens->lpe2, m_aovDescriptors[RPR_AOV_LPE_2]); + addAovNameLookup(HdRprAovTokens->lpe3, m_aovDescriptors[RPR_AOV_LPE_3]); + addAovNameLookup(HdRprAovTokens->lpe4, m_aovDescriptors[RPR_AOV_LPE_4]); + addAovNameLookup(HdRprAovTokens->lpe5, m_aovDescriptors[RPR_AOV_LPE_5]); + addAovNameLookup(HdRprAovTokens->lpe6, m_aovDescriptors[RPR_AOV_LPE_6]); + addAovNameLookup(HdRprAovTokens->lpe7, m_aovDescriptors[RPR_AOV_LPE_7]); + addAovNameLookup(HdRprAovTokens->lpe8, m_aovDescriptors[RPR_AOV_LPE_8]); +} + +HdRprAovDescriptor const& HdRprAovRegistry::GetAovDesc(TfToken const& name) { + auto it = m_aovNameLookup.find(name); + if (it == m_aovNameLookup.end()) { + return kInvalidDesc; + } + + return GetAovDesc(it->second.id, it->second.isComputed); +} + +HdRprAovDescriptor const& HdRprAovRegistry::GetAovDesc(uint32_t id, bool computed) { + size_t descsSize = computed ? m_computedAovDescriptors.size() : m_aovDescriptors.size(); + if (id >= descsSize) { + TF_RUNTIME_ERROR("Invalid arguments: %#x (computed=%d)", id, int(computed)); + return kInvalidDesc; + } + + if (computed) { + return m_computedAovDescriptors[id]; + } else { + return m_aovDescriptors[id]; + } +} + +TfToken const& HdRprGetCameraDepthAovName() { +#if PXR_VERSION < 2002 + return HdAovTokens->linearDepth; +#else + return HdAovTokens->cameraDepth; +#endif +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/aovDescriptor.h b/pxr/imaging/plugin/hdRpr/aovDescriptor.h new file mode 100644 index 000000000..20e9695dc --- /dev/null +++ b/pxr/imaging/plugin/hdRpr/aovDescriptor.h @@ -0,0 +1,132 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef HDRPR_AOV_DESCRIPTOR_H +#define HDRPR_AOV_DESCRIPTOR_H + +#include "pxr/base/tf/singleton.h" +#include "pxr/base/tf/staticTokens.h" +#include "pxr/base/gf/vec4f.h" + +#include "pxr/imaging/hd/types.h" + +#include + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +#define HDRPR_AOV_TOKENS \ + (rawColor) \ + (albedo) \ + (variance) \ + (worldCoordinate) \ + (opacity) \ + ((primvarsSt, "primvars:st")) \ + (materialId) \ + (geometricNormal) \ + (objectGroupId) \ + (shadowCatcher) \ + (background) \ + (emission) \ + (velocity) \ + (directIllumination) \ + (indirectIllumination) \ + (ao) \ + (directDiffuse) \ + (directReflect) \ + (indirectDiffuse) \ + (indirectReflect) \ + (refract) \ + (volume) \ + (lightGroup0) \ + (lightGroup1) \ + (lightGroup2) \ + (lightGroup3) \ + (viewShadingNormal) \ + (reflectionCatcher) \ + (colorRight) \ + (lpe0) \ + (lpe1) \ + (lpe2) \ + (lpe3) \ + (lpe4) \ + (lpe5) \ + (lpe6) \ + (lpe7) \ + (lpe8) \ + +TF_DECLARE_PUBLIC_TOKENS(HdRprAovTokens, HDRPR_AOV_TOKENS); + +const rpr::Aov kAovNone = static_cast(-1); + +enum ComputedAovs { + kNdcDepth = 0, + kColorAlpha, + kComputedAovsCount +}; + +struct HdRprAovDescriptor { + uint32_t id; + HdFormat format; + bool multiSampled; + bool computed; + GfVec4f clearValue; + + HdRprAovDescriptor(uint32_t id = kAovNone, bool multiSampled = true, HdFormat format = HdFormatFloat32Vec4, GfVec4f clearValue = GfVec4f(0.0f), bool computed = false) + : id(id), format(format), multiSampled(multiSampled), computed(computed), clearValue(clearValue) { + + } +}; + +class HdRprAovRegistry { +public: + static HdRprAovRegistry& GetInstance() { + return TfSingleton::GetInstance(); + } + + HdRprAovDescriptor const& GetAovDesc(TfToken const& name); + HdRprAovDescriptor const& GetAovDesc(uint32_t id, bool computed); + + HdRprAovRegistry(HdRprAovRegistry const&) = delete; + HdRprAovRegistry& operator=(HdRprAovRegistry const&) = delete; + HdRprAovRegistry(HdRprAovRegistry&&) = delete; + HdRprAovRegistry& operator=(HdRprAovRegistry&&) = delete; + +private: + HdRprAovRegistry(); + ~HdRprAovRegistry() = default; + + friend class TfSingleton; + +private: + struct AovNameLookupValue { + uint32_t id; + bool isComputed; + + AovNameLookupValue(uint32_t id, bool isComputed = false) + : id(id), isComputed(isComputed) { + + } + }; + std::map m_aovNameLookup; + + std::vector m_aovDescriptors; + std::vector m_computedAovDescriptors; +}; + +TfToken const& HdRprGetCameraDepthAovName(); + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // HDRPR_AOV_DESCRIPTOR_H diff --git a/pxr/imaging/plugin/hdRpr/baseRprim.h b/pxr/imaging/plugin/hdRpr/baseRprim.h new file mode 100644 index 000000000..54b84506a --- /dev/null +++ b/pxr/imaging/plugin/hdRpr/baseRprim.h @@ -0,0 +1,62 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef HDRPR_BASE_RPRIM_H +#define HDRPR_BASE_RPRIM_H + +#include "renderParam.h" + +#include "pxr/imaging/hd/sceneDelegate.h" + +PXR_NAMESPACE_OPEN_SCOPE + +template +class HdRprBaseRprim : public Base { +public: + HdRprBaseRprim( + SdfPath const& id, + SdfPath const& instancerId) + : Base(id, instancerId) { + + } + ~HdRprBaseRprim() override = default; + + void Finalize(HdRenderParam* renderParam) override { + if (!m_materialId.IsEmpty()) { + auto rprRenderParam = static_cast(renderParam); + rprRenderParam->UnsubscribeFromMaterialUpdates(m_materialId, Base::GetId()); + } + + Base::Finalize(renderParam); + } + +protected: + void UpdateMaterialId(HdSceneDelegate* sceneDelegate, HdRprRenderParam* renderParam) { + auto newMaterialId = sceneDelegate->GetMaterialId(Base::GetId()); + if (m_materialId != newMaterialId) { + if (!m_materialId.IsEmpty()) { + renderParam->UnsubscribeFromMaterialUpdates(m_materialId, Base::GetId()); + } + renderParam->SubscribeForMaterialUpdates(newMaterialId, Base::GetId()); + + m_materialId = newMaterialId; + } + } + +protected: + SdfPath m_materialId; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // HDRPR_BASE_RPRIM_H diff --git a/pxr/imaging/plugin/hdRpr/basisCurves.cpp b/pxr/imaging/plugin/hdRpr/basisCurves.cpp index da9643615..88a142811 100644 --- a/pxr/imaging/plugin/hdRpr/basisCurves.cpp +++ b/pxr/imaging/plugin/hdRpr/basisCurves.cpp @@ -12,19 +12,19 @@ limitations under the License. ************************************************************************/ #include "basisCurves.h" -#include "materialAdapter.h" #include "material.h" #include "renderParam.h" #include "primvarUtil.h" #include "rprApi.h" -#include "pxr/usd/usdUtils/pipeline.h" +#include "pxr/imaging/rprUsd/material.h" +#include "pxr/imaging/rprUsd/debugCodes.h" PXR_NAMESPACE_OPEN_SCOPE HdRprBasisCurves::HdRprBasisCurves(SdfPath const& id, SdfPath const& instancerId) - : HdBasisCurves(id, instancerId) + : HdRprBaseRprim(id, instancerId) , m_visibilityMask(kVisibleAll) { } @@ -94,12 +94,28 @@ void HdRprBasisCurves::Sync(HdSceneDelegate* sceneDelegate, newCurve = true; } + if (*dirtyBits & HdChangeTracker::DirtyMaterialId) { + UpdateMaterialId(sceneDelegate, rprRenderParam); + } + + auto material = static_cast( + sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, m_materialId) + ); + bool isVisibilityMaskDirty = false; if (*dirtyBits & HdChangeTracker::DirtyPrimvar) { HdRprFillPrimvarDescsPerInterpolation(sceneDelegate, id, &primvarDescsPerInterpolation); - auto stToken = UsdUtilsGetPrimaryUVSetName(); - if (HdRprIsPrimvarExists(stToken, primvarDescsPerInterpolation, &m_uvsInterpolation)) { - m_uvs = sceneDelegate->Get(id, stToken).Get(); + + static TfToken st("st", TfToken::Immortal); + TfToken const* uvPrimvarName = &st; + if (material) { + if (auto rprMaterial = material->GetRprMaterialObject()) { + uvPrimvarName = &rprMaterial->GetUvPrimvarName(); + } + } + + if (HdRprIsPrimvarExists(*uvPrimvarName, primvarDescsPerInterpolation, &m_uvsInterpolation)) { + m_uvs = sceneDelegate->Get(id, *uvPrimvarName).Get(); } else { m_uvs = VtVec2fArray(); } @@ -107,7 +123,7 @@ void HdRprBasisCurves::Sync(HdSceneDelegate* sceneDelegate, HdRprGeometrySettings geomSettings = {}; geomSettings.visibilityMask = kVisibleAll; - HdRprParseGeometrySettings(sceneDelegate, id, primvarDescsPerInterpolation.at(HdInterpolationConstant), &geomSettings); + HdRprParseGeometrySettings(sceneDelegate, id, primvarDescsPerInterpolation, &geomSettings); if (m_visibilityMask != geomSettings.visibilityMask) { m_visibilityMask = geomSettings.visibilityMask; @@ -120,16 +136,15 @@ void HdRprBasisCurves::Sync(HdSceneDelegate* sceneDelegate, newCurve = true; } - if (*dirtyBits & HdChangeTracker::DirtyMaterialId) { - m_cachedMaterial = static_cast(sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, sceneDelegate->GetMaterialId(id))); - } - if (*dirtyBits & HdChangeTracker::DirtyVisibility) { _sharedData.visible = sceneDelegate->GetVisible(id); } if (newCurve) { - m_rprCurve = nullptr; + if (m_rprCurve) { + rprApi->Release(m_rprCurve); + m_rprCurve = nullptr; + } if (m_points.empty()) { TF_RUNTIME_ERROR("[%s] Curve could not be created: missing points", id.GetText()); @@ -171,13 +186,17 @@ void HdRprBasisCurves::Sync(HdSceneDelegate* sceneDelegate, m_topology.GetCurveBasis() == HdTokens->bezier) { m_rprCurve = CreateBezierRprCurve(rprApi); } + + if (m_rprCurve && RprUsdIsLeakCheckEnabled()) { + rprApi->SetName(m_rprCurve, id.GetText()); + } } } if (m_rprCurve) { if (newCurve || (*dirtyBits & HdChangeTracker::DirtyMaterialId)) { - if (m_cachedMaterial && m_cachedMaterial->GetRprMaterialObject()) { - rprApi->SetCurveMaterial(m_rprCurve, m_cachedMaterial->GetRprMaterialObject()); + if (material && material->GetRprMaterialObject()) { + rprApi->SetCurveMaterial(m_rprCurve, material->GetRprMaterialObject()); } else { GfVec3f color(0.18f); @@ -191,10 +210,12 @@ void HdRprBasisCurves::Sync(HdSceneDelegate* sceneDelegate, } } - MaterialAdapter matAdapter(EMaterialType::COLOR, MaterialParams{{HdRprMaterialTokens->color, VtValue(color)}}); - m_fallbackMaterial = rprApi->CreateMaterial(matAdapter); - + m_fallbackMaterial = rprApi->CreateDiffuseMaterial(color); rprApi->SetCurveMaterial(m_rprCurve, m_fallbackMaterial); + + if (RprUsdIsLeakCheckEnabled()) { + rprApi->SetName(m_fallbackMaterial, id.GetText()); + } } } @@ -259,6 +280,10 @@ rpr::Curve* HdRprBasisCurves::CreateLinearRprCurve(HdRprApi* rprApi) { auto& curveCounts = m_topology.GetCurveVertexCounts(); rprSegmentPerCurve.reserve(curveCounts.size()); + // Validate Hydra curve data and calculate amount of required memory. + // + size_t numRadiuses = 0; + size_t numIndices = 0; int curveSegmentOffset = 0; int curveIndicesOffset = 0; for (size_t iCurve = 0; iCurve < curveCounts.size(); ++iCurve) { @@ -281,12 +306,41 @@ rpr::Curve* HdRprBasisCurves::CreateLinearRprCurve(HdRprApi* rprApi) { return nullptr; } - int numNewRprIndices = numSegments * kNumPointsPerSegment; if (isCurveTapered) { - rprRadiuses.reserve(rprRadiuses.size() + numNewRprIndices); - numNewRprIndices *= 2; + numRadiuses += numSegments * 2; + numIndices += numSegments * 4; + } else { + if (m_widthsInterpolation == HdInterpolationUniform || + m_widthsInterpolation == HdInterpolationConstant) { + ++numRadiuses; + } + numIndices += numSegments * 2; + + // RPR requires curves to consist only of segments of kRprNumPointsPerSegment length + auto numPointsInCurve = (numVertices - 1) * 2; + auto numTrailingPoints = numPointsInCurve % kRprNumPointsPerSegment; + if (numTrailingPoints > 0) { + numIndices += kRprNumPointsPerSegment - numTrailingPoints; + } + } + + curveSegmentOffset += numSegments; + curveIndicesOffset += numVertices; + } + rprRadiuses.reserve(numRadiuses); + rprIndices.reserve(numIndices); + + // Convert Hydra curve data to RPR data. + // + curveIndicesOffset = 0; + for (size_t iCurve = 0; iCurve < curveCounts.size(); ++iCurve) { + auto numVertices = curveCounts[iCurve]; + if (numVertices < 2) { + continue; } - rprIndices.reserve(rprIndices.size() + numNewRprIndices); + + int numSegments = (numVertices - (kNumPointsPerSegment - kVstep)) / kVstep; + if (periodic) numSegments++; if (isCurveTapered) { rprSegmentPerCurve.push_back(numVertices - 1); @@ -336,7 +390,6 @@ rpr::Curve* HdRprBasisCurves::CreateLinearRprCurve(HdRprApi* rprApi) { rprSegmentPerCurve.push_back((numPointsInCurve + extraPoints) / kRprNumPointsPerSegment); } - curveSegmentOffset += numSegments; curveIndicesOffset += numVertices; } @@ -391,6 +444,10 @@ rpr::Curve* HdRprBasisCurves::CreateBezierRprCurve(HdRprApi* rprApi) { indexSampler = [this](int idx) { return m_indices.cdata()[idx]; }; } + // Validate Hydra curve data and calculate amount of required memory. + // + size_t numRadiuses = 0; + size_t numIndices = 0; int curveSegmentOffset = 0; int curveIndicesOffset = 0; for (size_t iCurve = 0; iCurve < curveCounts.size(); ++iCurve) { @@ -406,7 +463,6 @@ rpr::Curve* HdRprBasisCurves::CreateBezierRprCurve(HdRprApi* rprApi) { return nullptr; } - int numSegments = (numVertices - (kNumPointsPerSegment - kVstep)) / kVstep; if (periodic) numSegments++; @@ -416,11 +472,34 @@ rpr::Curve* HdRprBasisCurves::CreateBezierRprCurve(HdRprApi* rprApi) { return nullptr; } - rprIndices.reserve(rprIndices.size() + numSegments * kNumPointsPerSegment); + numIndices += numSegments * kNumPointsPerSegment; if (isCurveTapered) { - rprRadiuses.reserve(rprRadiuses.size() + numSegments * 2); + numRadiuses += numSegments * 2; + } else { + if (m_widthsInterpolation == HdInterpolationUniform || + m_widthsInterpolation == HdInterpolationConstant) { + numRadiuses++; + } } + curveSegmentOffset += numSegments; + curveIndicesOffset += numVertices; + } + rprRadiuses.reserve(numRadiuses); + rprIndices.reserve(numIndices); + + // Convert Hydra curve data to RPR data. + // + curveIndicesOffset = 0; + for (size_t iCurve = 0; iCurve < curveCounts.size(); ++iCurve) { + auto numVertices = curveCounts[iCurve]; + if (numVertices < kNumPointsPerSegment) { + continue; + } + + int numSegments = (numVertices - (kNumPointsPerSegment - kVstep)) / kVstep; + if (periodic) numSegments++; + rprSegmentPerCurve.push_back(numSegments); for (int iSegment = 0; iSegment < numSegments; ++iSegment) { @@ -452,7 +531,6 @@ rpr::Curve* HdRprBasisCurves::CreateBezierRprCurve(HdRprApi* rprApi) { } } - curveSegmentOffset += numSegments; curveIndicesOffset += numVertices; } @@ -476,7 +554,7 @@ void HdRprBasisCurves::Finalize(HdRenderParam* renderParam) { rprApi->Release(m_fallbackMaterial); m_fallbackMaterial = nullptr; - HdBasisCurves::Finalize(renderParam); + HdRprBaseRprim::Finalize(renderParam); } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/basisCurves.h b/pxr/imaging/plugin/hdRpr/basisCurves.h index 8f82c6ef1..cb850f4ac 100644 --- a/pxr/imaging/plugin/hdRpr/basisCurves.h +++ b/pxr/imaging/plugin/hdRpr/basisCurves.h @@ -14,6 +14,8 @@ limitations under the License. #ifndef HDRPR_BASIS_CURVES_H #define HDRPR_BASIS_CURVES_H +#include "baseRprim.h" + #include "pxr/imaging/hd/basisCurves.h" #include "pxr/base/gf/matrix4f.h" #include "pxr/base/gf/vec2f.h" @@ -23,11 +25,11 @@ namespace rpr { class Curve; } PXR_NAMESPACE_OPEN_SCOPE class HdRprApi; -struct HdRprApiMaterial; +class RprUsdMaterial; class HdRprMaterial; -class HdRprBasisCurves : public HdBasisCurves { +class HdRprBasisCurves : public HdRprBaseRprim { public: HdRprBasisCurves(SdfPath const& id, @@ -56,9 +58,7 @@ class HdRprBasisCurves : public HdBasisCurves { private: rpr::Curve* m_rprCurve = nullptr; - HdRprApiMaterial* m_fallbackMaterial = nullptr; - - HdRprMaterial const* m_cachedMaterial; + RprUsdMaterial* m_fallbackMaterial = nullptr; HdBasisCurvesTopology m_topology; VtIntArray m_indices; diff --git a/pxr/imaging/plugin/hdRpr/camera.cpp b/pxr/imaging/plugin/hdRpr/camera.cpp index 3082b273c..d5415d8ff 100644 --- a/pxr/imaging/plugin/hdRpr/camera.cpp +++ b/pxr/imaging/plugin/hdRpr/camera.cpp @@ -18,6 +18,10 @@ limitations under the License. PXR_NAMESPACE_OPEN_SCOPE +TF_DEFINE_PRIVATE_TOKENS(HdRprCameraTokens, + (apertureBlades) +); + namespace { template @@ -60,6 +64,7 @@ HdRprCamera::HdRprCamera(SdfPath const& id) m_focalLength(std::numeric_limits::quiet_NaN()), m_fStop(std::numeric_limits::quiet_NaN()), m_focusDistance(std::numeric_limits::quiet_NaN()), + m_apertureBlades(0), m_shutterOpen(std::numeric_limits::quiet_NaN()), m_shutterClose(std::numeric_limits::quiet_NaN()), m_clippingRange(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN()) { @@ -95,6 +100,8 @@ void HdRprCamera::Sync(HdSceneDelegate* sceneDelegate, EvalCameraParam(&m_clippingRange, HdCameraTokens->clippingRange, sceneDelegate, id, GfRange1f(std::numeric_limits::quiet_NaN(), std::numeric_limits::quiet_NaN())); EvalCameraParam(&m_projectionType, UsdGeomTokens->projection, sceneDelegate, id, TfToken()); + + EvalCameraParam(&m_apertureBlades, HdRprCameraTokens->apertureBlades, sceneDelegate, id, 16); } if (*dirtyBits & HdCamera::DirtyViewMatrix) { diff --git a/pxr/imaging/plugin/hdRpr/camera.h b/pxr/imaging/plugin/hdRpr/camera.h index d06ffbd6f..db498d74f 100644 --- a/pxr/imaging/plugin/hdRpr/camera.h +++ b/pxr/imaging/plugin/hdRpr/camera.h @@ -45,6 +45,7 @@ class HdRprCamera : public HdCamera { bool GetClippingRange(GfRange1f* value) const; bool GetProjectionType(TfToken* value) const; HdTimeSampleArray const& GetTransformSamples() const { return m_transform; } + int GetApertureBlades() const { return m_apertureBlades; } HdDirtyBits GetDirtyBits() const { return m_rprDirtyBits; } void CleanDirtyBits() const { m_rprDirtyBits = HdCamera::Clean; } @@ -57,6 +58,7 @@ class HdRprCamera : public HdCamera { float m_focalLength; float m_fStop; float m_focusDistance; + int m_apertureBlades; double m_shutterOpen; double m_shutterClose; GfRange1f m_clippingRange; diff --git a/pxr/imaging/plugin/hdRpr/distantLight.cpp b/pxr/imaging/plugin/hdRpr/distantLight.cpp index 86460da2e..249a0aa36 100644 --- a/pxr/imaging/plugin/hdRpr/distantLight.cpp +++ b/pxr/imaging/plugin/hdRpr/distantLight.cpp @@ -15,10 +15,12 @@ limitations under the License. #include "renderParam.h" #include "rprApi.h" +#include "pxr/imaging/rprUsd/debugCodes.h" #include "pxr/imaging/hd/light.h" #include "pxr/imaging/hd/sceneDelegate.h" #include "pxr/usd/usdLux/blackbody.h" #include "pxr/usd/usdLux/tokens.h" +#include "pxr/base/gf/matrix4d.h" PXR_NAMESPACE_OPEN_SCOPE @@ -37,7 +39,7 @@ void HdRprDistantLight::Sync(HdSceneDelegate* sceneDelegate, auto& id = GetId(); if (bits & HdLight::DirtyTransform) { - m_transform = GfMatrix4f(sceneDelegate->GetLightParamValue(id, HdLightTokens->transform).Get()); + m_transform = GfMatrix4f(sceneDelegate->GetLightParamValue(id, HdTokens->transform).Get()); } bool newLight = false; @@ -61,7 +63,10 @@ void HdRprDistantLight::Sync(HdSceneDelegate* sceneDelegate, *dirtyBits = HdLight::Clean; return; } - rprRenderParam->AddLight(); + + if (RprUsdIsLeakCheckEnabled()) { + rprApi->SetName(m_rprLight, id.GetText()); + } } float angle = sceneDelegate->GetLightParamValue(id, UsdLuxTokens->angle).Get(); @@ -88,8 +93,6 @@ void HdRprDistantLight::Finalize(HdRenderParam* renderParam) { auto rprRenderParam = static_cast(renderParam); rprRenderParam->AcquireRprApiForEdit()->Release(m_rprLight); m_rprLight = nullptr; - - rprRenderParam->RemoveLight(); } HdSprim::Finalize(renderParam); diff --git a/pxr/imaging/plugin/hdRpr/domeLight.cpp b/pxr/imaging/plugin/hdRpr/domeLight.cpp index 1e788fe29..a57bcd31a 100644 --- a/pxr/imaging/plugin/hdRpr/domeLight.cpp +++ b/pxr/imaging/plugin/hdRpr/domeLight.cpp @@ -15,19 +15,17 @@ limitations under the License. #include "renderParam.h" #include "rprApi.h" +#include "pxr/imaging/rprUsd/debugCodes.h" + #include "pxr/usd/ar/resolver.h" #include "pxr/imaging/hd/light.h" #include "pxr/imaging/hd/sceneDelegate.h" #include "pxr/usd/sdf/assetPath.h" #include "pxr/usd/usdLux/blackbody.h" -#include "pxr/base/tf/envSetting.h" +#include "pxr/base/gf/matrix4d.h" PXR_NAMESPACE_OPEN_SCOPE -TF_DEFINE_ENV_SETTING(HDRPR_INVERT_DOME_LIGHT_Z_AXIS, true, - "In Houdini 18.0.287 we needed to invert X-axis of dome light to match with Karma," - "but in Houdini 18.0.311 this behavior is changed so now we need to invert Z-axis"); - static void removeFirstSlash(std::string& string) { // Don't need this for *nix/Mac #ifdef _WIN32 @@ -52,18 +50,26 @@ void HdRprDomeLight::Sync(HdSceneDelegate* sceneDelegate, HdDirtyBits bits = *dirtyBits; if (bits & HdLight::DirtyTransform) { - m_transform = GfMatrix4f(sceneDelegate->GetLightParamValue(id, HdLightTokens->transform).Get()); - // XXX: Required to match orientation with Houdini's Karma - if (TfGetEnvSetting(HDRPR_INVERT_DOME_LIGHT_Z_AXIS)) { - m_transform *= GfMatrix4f(1.0).SetScale(GfVec3f(1.0f, 1.0f, -1.0f)); - } else { - m_transform *= GfMatrix4f(1.0).SetScale(GfVec3f(-1.0f, 1.0f, 1.0f)); - } + m_transform = GfMatrix4f(sceneDelegate->GetLightParamValue(id, HdTokens->transform).Get()); + m_transform *= GfMatrix4f(1.0).SetScale(GfVec3f(1.0f, 1.0f, -1.0f)); } bool newLight = false; if (bits & HdLight::DirtyParams) { - m_rprLight = nullptr; + if (m_rprLight) { + rprApi->Release(m_rprLight); + m_rprLight = nullptr; + } + + 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; + } float intensity = sceneDelegate->GetLightParamValue(id, HdLightTokens->intensity).Get(); float exposure = sceneDelegate->GetLightParamValue(id, HdLightTokens->exposure).Get(); @@ -101,6 +107,10 @@ void HdRprDomeLight::Sync(HdSceneDelegate* sceneDelegate, if (m_rprLight) { newLight = true; + + if (RprUsdIsLeakCheckEnabled()) { + rprApi->SetName(m_rprLight, id.GetText()); + } } } @@ -108,11 +118,6 @@ void HdRprDomeLight::Sync(HdSceneDelegate* sceneDelegate, rprApi->SetTransform(m_rprLight, m_transform); } - if (newLight && !m_created) { - m_created = true; - rprRenderParam->AddLight(); - } - *dirtyBits = HdLight::Clean; } @@ -128,11 +133,6 @@ void HdRprDomeLight::Finalize(HdRenderParam* renderParam) { m_rprLight = nullptr; } - if (m_created) { - rprRenderParam->RemoveLight(); - m_created = false; - } - HdSprim::Finalize(renderParam); } diff --git a/pxr/imaging/plugin/hdRpr/domeLight.h b/pxr/imaging/plugin/hdRpr/domeLight.h index 4398ced5e..1218b8937 100644 --- a/pxr/imaging/plugin/hdRpr/domeLight.h +++ b/pxr/imaging/plugin/hdRpr/domeLight.h @@ -42,7 +42,6 @@ class HdRprDomeLight : public HdSprim { protected: HdRprApiEnvironmentLight* m_rprLight = nullptr; GfMatrix4f m_transform; - bool m_created = false; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/houdini/CMakeLists.txt b/pxr/imaging/plugin/hdRpr/houdini/CMakeLists.txt index 4022a3f00..be4c23dab 100644 --- a/pxr/imaging/plugin/hdRpr/houdini/CMakeLists.txt +++ b/pxr/imaging/plugin/hdRpr/houdini/CMakeLists.txt @@ -1,4 +1,4 @@ -if(RPR_ENABLE_OPENVDB_SUPPORT) +if(OpenVDB_FOUND) set(HOUDINI_OPENVDB_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/openvdb.h ${CMAKE_CURRENT_SOURCE_DIR}/openvdb.cpp) diff --git a/pxr/imaging/plugin/hdRpr/houdini/rpr_exportRpr1.hda b/pxr/imaging/plugin/hdRpr/houdini/rpr_exportRpr1.hda deleted file mode 100644 index 5c5f813a2..000000000 Binary files a/pxr/imaging/plugin/hdRpr/houdini/rpr_exportRpr1.hda and /dev/null differ diff --git a/pxr/imaging/plugin/hdRpr/imageCache.cpp b/pxr/imaging/plugin/hdRpr/imageCache.cpp deleted file mode 100644 index 1d7057a82..000000000 --- a/pxr/imaging/plugin/hdRpr/imageCache.cpp +++ /dev/null @@ -1,103 +0,0 @@ -/************************************************************************ -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 "imageCache.h" -#include "rpr/helpers.h" -#include "rpr/imageHelpers.h" - -#include "pxr/base/arch/fileSystem.h" - -PXR_NAMESPACE_OPEN_SCOPE - -ImageCache::ImageCache(rpr::Context* context) - : m_context(context) { - -} - -std::shared_ptr ImageCache::GetImage(std::string const& path, bool forceLinearSpace) { - ImageMetadata md(path); - - auto cacheKey = path; - - static const char* kForceLinearSpaceCacheKeySuffix = "?l"; - if (forceLinearSpace) { - cacheKey += kForceLinearSpaceCacheKeySuffix; - } - - auto it = m_cache.find(cacheKey); - if (it != m_cache.end() && it->second.IsMetadataEqual(md)) { - if (auto image = it->second.handle.lock()) { - return image; - } - } - - auto image = std::shared_ptr(rpr::CreateImage(m_context, path.c_str(), forceLinearSpace)); - if (image) { - md.handle = image; - m_cache.emplace(cacheKey, md); - - auto gammaFromFile = rpr::GetInfo(image.get(), RPR_IMAGE_GAMMA_FROM_FILE); - if (std::abs(gammaFromFile - 1.0f) < 0.01f) { - // Image is in linear space, we can cache the same image for both variants of forceLinearSpace - if (forceLinearSpace) { - m_cache.emplace(path, md); - } else { - m_cache.emplace(path + kForceLinearSpaceCacheKeySuffix, md); - } - } - } - return image; -} - -void ImageCache::RequireGarbageCollection() { - m_garbageCollectionRequired = true; -} - -void ImageCache::GarbageCollectIfNeeded() { - if (!m_garbageCollectionRequired) { - return; - } - - auto it = m_cache.begin(); - while (it != m_cache.end()) { - if (it->second.handle.expired()) { - it = m_cache.erase(it); - } else { - ++it; - } - } - - m_garbageCollectionRequired = false; -} - -ImageCache::ImageMetadata::ImageMetadata(std::string const& path) { - double time; - if (!ArchGetModificationTime(path.c_str(), &time)) { - return; - } - - int64_t size = ArchGetFileLength(path.c_str()); - if (size == -1) { - return; - } - - m_modificationTime = time; - m_size = static_cast(size); -} - -bool ImageCache::ImageMetadata::IsMetadataEqual(ImageMetadata const& md) { - return m_modificationTime == md.m_modificationTime && - m_size == md.m_size; -} - -PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/imageCache.h b/pxr/imaging/plugin/hdRpr/imageCache.h deleted file mode 100644 index d6ca96715..000000000 --- a/pxr/imaging/plugin/hdRpr/imageCache.h +++ /dev/null @@ -1,67 +0,0 @@ -/************************************************************************ -Copyright 2020 Advanced Micro Devices, Inc -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -************************************************************************/ - -#ifndef HDRPR_IMAGE_CACHE_H -#define HDRPR_IMAGE_CACHE_H - -#include "pxr/pxr.h" - -#include -#include -#include - -namespace rpr { - -class Context; -class Image; - -} // namespace rpr - -PXR_NAMESPACE_OPEN_SCOPE - -class ImageCache { -public: - ImageCache(rpr::Context* context); - - std::shared_ptr GetImage(std::string const& path, bool forceLinearSpace = false); - - void RequireGarbageCollection(); - void GarbageCollectIfNeeded(); - - rpr::Context* GetContext() { return m_context; } - -private: - class ImageMetadata { - public: - ImageMetadata() = default; - ImageMetadata(std::string const& path); - - bool IsMetadataEqual(ImageMetadata const& md); - - public: - std::weak_ptr handle; - - private: - size_t m_size = 0u; - double m_modificationTime = 0.0; - }; - -private: - rpr::Context* m_context; - std::unordered_map m_cache; - bool m_garbageCollectionRequired = false; -}; - -PXR_NAMESPACE_CLOSE_SCOPE - -#endif // HDRPR_IMAGE_CACHE_H diff --git a/pxr/imaging/plugin/hdRpr/light.cpp b/pxr/imaging/plugin/hdRpr/light.cpp index aaa8a35af..97aa33829 100644 --- a/pxr/imaging/plugin/hdRpr/light.cpp +++ b/pxr/imaging/plugin/hdRpr/light.cpp @@ -13,12 +13,14 @@ limitations under the License. #include "light.h" #include "renderParam.h" -#include "materialAdapter.h" #include "primvarUtil.h" #include "rprApi.h" +#include "pxr/imaging/rprUsd/debugCodes.h" + #include "pxr/base/tf/envSetting.h" #include "pxr/base/gf/rotation.h" +#include "pxr/base/gf/matrix4d.h" #include "pxr/imaging/hd/sceneDelegate.h" #include "pxr/usd/usdLux/blackbody.h" #include "pxr/usd/usdLux/tokens.h" @@ -184,14 +186,14 @@ rpr::Shape* HdRprLight::CreateCylinderLightMesh(HdRprApi* rprApi) { return rprApi->CreateMesh(points, topology.GetFaceVertexIndices(), VtVec3fArray(), VtIntArray(), VtVec2fArray(), VtIntArray(), topology.GetFaceVertexCounts(), topology.GetOrientation()); } -void HdRprLight::SyncAreaLightGeomParams(AreaLight* light, HdSceneDelegate* sceneDelegate, float* intensity) { +void HdRprLight::SyncAreaLightGeomParams(HdSceneDelegate* sceneDelegate, float* intensity) { bool normalizeIntensity = sceneDelegate->GetLightParamValue(GetId(), HdLightTokens->normalize).Get(); if (m_lightType == HdPrimTypeTokens->diskLight || m_lightType == HdPrimTypeTokens->sphereLight) { float radius = std::abs(sceneDelegate->GetLightParamValue(GetId(), HdLightTokens->radius).Get()); - light->localTransform = GfMatrix4f(1.0f).SetScale(GfVec3f(radius * 2.0f)); + m_localTransform = GfMatrix4f(1.0f).SetScale(GfVec3f(radius * 2.0f)); if (normalizeIntensity) { if (m_lightType == HdPrimTypeTokens->diskLight) { @@ -204,7 +206,7 @@ void HdRprLight::SyncAreaLightGeomParams(AreaLight* light, HdSceneDelegate* scen float width = std::abs(sceneDelegate->GetLightParamValue(GetId(), HdLightTokens->width).Get()); float height = std::abs(sceneDelegate->GetLightParamValue(GetId(), HdLightTokens->height).Get()); - light->localTransform = GfMatrix4f(1.0f).SetScale(GfVec3f(width, height, 1.0f)); + m_localTransform = GfMatrix4f(1.0f).SetScale(GfVec3f(width, height, 1.0f)); if (normalizeIntensity) { (*intensity) /= GetRectLightNormalization(m_transform, width, height); @@ -213,7 +215,7 @@ void HdRprLight::SyncAreaLightGeomParams(AreaLight* light, HdSceneDelegate* scen float radius = std::abs(sceneDelegate->GetLightParamValue(GetId(), HdLightTokens->radius).Get()); float length = std::abs(sceneDelegate->GetLightParamValue(GetId(), HdLightTokens->length).Get()); - light->localTransform = GfMatrix4f(1.0f).SetRotate(GfRotation(GfVec3d(0.0, 1.0, 0.0), 90.0)) * GfMatrix4f(1.0f).SetScale(GfVec3f(length, radius * 2.0f, radius * 2.0f)); + m_localTransform = GfMatrix4f(1.0f).SetRotate(GfRotation(GfVec3d(0.0, 1.0, 0.0), 90.0)) * GfMatrix4f(1.0f).SetScale(GfVec3f(length, radius * 2.0f, radius * 2.0f)); if (normalizeIntensity) { (*intensity) /= GetCylinderLightNormalization(m_transform, length, radius); @@ -300,6 +302,81 @@ void HdRprLight::CreateAreaLightMesh(HdRprApi* rprApi, HdSceneDelegate* sceneDel m_light = light; } +struct HdRprLight::LightParameterSetter : public BOOST_NS::static_visitor { + HdRprApi* rprApi; + GfVec3f const& emissionColor; + bool emissionColorIsDirty; + + LightParameterSetter(HdRprApi* rprApi, GfVec3f const& emissionColor, bool emissionColorIsDirty) + : rprApi(rprApi), emissionColor(emissionColor), emissionColorIsDirty(emissionColorIsDirty) { + + } + + void operator()(LightVariantEmpty) const { /*no-op*/ } + void operator()(AreaLight* light) const { + if (emissionColorIsDirty || !light->material) { + if (light->material) { + rprApi->ReleaseGeometryLightMaterial(light->material); + } + light->material = rprApi->CreateGeometryLightMaterial(emissionColor); + } + + if (light->material) { + for (auto& mesh : light->meshes) { + rprApi->SetMeshMaterial(mesh, light->material, false); + } + } + } + + template + void operator()(T* light) const { + if (emissionColorIsDirty) { rprApi->SetLightColor(light, emissionColor); } + } +}; + +struct HdRprLight::LightNameSetter : public BOOST_NS::static_visitor { + HdRprApi* rprApi; + const char* name; + + LightNameSetter(HdRprApi* rprApi, const char* name) + : rprApi(rprApi), name(name) { + + } + + void operator()(LightVariantEmpty) const { /*no-op*/ } + void operator()(AreaLight* light) const { + rprApi->SetName(light->material, name); + for (auto& mesh : light->meshes) { + rprApi->SetName(mesh, name); + } + } + + template + void operator()(T* light) const { + rprApi->SetName(light, name); + } +}; + +struct HdRprLight::LightTransformSetter : public BOOST_NS::static_visitor<> { + HdRprApi* rprApi; + GfMatrix4f const& transform; + + LightTransformSetter(HdRprApi* rprApi, GfMatrix4f const& transform) + : rprApi(rprApi), transform(transform) { + + } + + void operator()(LightVariantEmpty) const {} + void operator()(AreaLight* light) const { + for (auto& mesh : light->meshes) { + rprApi->SetTransform(mesh, transform); + } + } + + template + void operator()(T* light) const { rprApi->SetTransform(light, transform); } +}; + void HdRprLight::Sync(HdSceneDelegate* sceneDelegate, HdRenderParam* renderParam, HdDirtyBits* dirtyBits) { @@ -310,10 +387,11 @@ void HdRprLight::Sync(HdSceneDelegate* sceneDelegate, HdDirtyBits bits = *dirtyBits; if (bits & DirtyBits::DirtyTransform) { - m_transform = GfMatrix4f(sceneDelegate->GetLightParamValue(id, HdLightTokens->transform).Get()); + m_transform = GfMatrix4f(sceneDelegate->GetLightParamValue(id, HdTokens->transform).Get()); } if (bits & DirtyParams) { + m_localTransform = GfMatrix4f(1.0f); ReleaseLight(rprApi); bool isVisible = sceneDelegate->GetVisible(id); @@ -326,12 +404,14 @@ void HdRprLight::Sync(HdSceneDelegate* sceneDelegate, return; } + bool newLight = false; auto iesFile = sceneDelegate->GetLightParamValue(id, UsdLuxTokens->shapingIesFile); if (iesFile.IsHolding()) { auto& path = iesFile.UncheckedGet(); if (!path.GetResolvedPath().empty()) { if (auto light = rprApi->CreateIESLight(path.GetResolvedPath())) { m_light = light; + newLight = true; } } } else { @@ -340,17 +420,43 @@ void HdRprLight::Sync(HdSceneDelegate* sceneDelegate, if (coneAngle.IsHolding() && coneSoftness.IsHolding()) { if (auto light = rprApi->CreateSpotLight(coneAngle.UncheckedGet(), coneSoftness.UncheckedGet())) { m_light = light; + newLight = true; } } else if (sceneDelegate->GetLightParamValue(id, UsdLuxTokens->treatAsPoint).GetWithDefault(false)) { if (auto light = rprApi->CreatePointLight()) { m_light = light; + newLight = true; } } else { - CreateAreaLightMesh(rprApi, sceneDelegate); + if (rprApi->IsSphereAndDiskLightSupported() && + (m_lightType == HdPrimTypeTokens->sphereLight || + m_lightType == HdPrimTypeTokens->diskLight)) { + + if (m_lightType == HdPrimTypeTokens->sphereLight) { + if (auto light = rprApi->CreateSphereLight()) { + rprApi->SetLightRadius(light, 0.5f); + + m_light = light; + newLight = true; + } + } else { + if (auto light = rprApi->CreateDiskLight()) { + rprApi->SetLightRadius(light, 0.5f); + rprApi->SetLightAngle(light, float(M_PI_2)); + + m_light = light; + newLight = true; + } + } + + } else { + CreateAreaLightMesh(rprApi, sceneDelegate); + newLight = true; + } } } - if (m_light.which() == kLightTypeNone) { + if (m_light.type() == typeid(LightVariantEmpty)) { *dirtyBits = DirtyBits::Clean; return; } @@ -367,85 +473,25 @@ void HdRprLight::Sync(HdSceneDelegate* sceneDelegate, color[2] *= temperatureColor[2]; } - if (m_light.which() == kLightTypeArea) { - SyncAreaLightGeomParams(BOOST_NS::get(m_light), sceneDelegate, &intensity); + if (m_light.type() == typeid(AreaLight*) || + m_light.type() == typeid(rpr::SphereLight*) || + m_light.type() == typeid(rpr::DiskLight*)) { + SyncAreaLightGeomParams(sceneDelegate, &intensity); } auto emissionColor = color * intensity; - bool isEmissionColorDirty = m_emisionColor != emissionColor; + bool isEmissionColorDirty = newLight || m_emisionColor != emissionColor; if (isEmissionColorDirty) { m_emisionColor = emissionColor; } - struct LightParameterSetter : public BOOST_NS::static_visitor { - HdRprApi* rprApi; - GfVec3f const& emissionColor; - bool emissionColorIsDirty; + BOOST_NS::apply_visitor(LightParameterSetter{rprApi, emissionColor, isEmissionColorDirty}, m_light); - LightParameterSetter(HdRprApi* rprApi, GfVec3f const& emissionColor, bool emissionColorIsDirty) - : rprApi(rprApi), emissionColor(emissionColor), emissionColorIsDirty(emissionColorIsDirty) { - - } - - bool operator()(LightVariantEmpty) const { return false; } - bool operator()(AreaLight* light) const { - if (emissionColorIsDirty || !light->material) { - MaterialAdapter matAdapter(EMaterialType::EMISSIVE, MaterialParams{{HdLightTokens->color, VtValue(emissionColor)}}); - light->material = std::move(rprApi->CreateMaterial(matAdapter)); - } - - if (light->material) { - for (auto& mesh : light->meshes) { - rprApi->SetMeshMaterial(mesh, light->material, false, false); - } - return true; - } - - return false; - } - - bool operator()(rpr::SpotLight* light) const { - if (emissionColorIsDirty) { rprApi->SetLightColor(light, emissionColor); } - return true; - } - - bool operator()(rpr::PointLight* light) const { - if (emissionColorIsDirty) { rprApi->SetLightColor(light, emissionColor); } - return true; - } - - bool operator()(rpr::IESLight* light) const { - if (emissionColorIsDirty) { rprApi->SetLightColor(light, emissionColor); } - return true; - } - }; - - if (BOOST_NS::apply_visitor(LightParameterSetter{rprApi, emissionColor, isEmissionColorDirty}, m_light) && !m_created) { - m_created = true; - rprRenderParam->AddLight(); + if (newLight && RprUsdIsLeakCheckEnabled()) { + BOOST_NS::apply_visitor(LightNameSetter{rprApi, id.GetText()}, m_light); } } if (bits & (DirtyTransform | DirtyParams)) { - struct LightTransformSetter : public BOOST_NS::static_visitor<> { - HdRprApi* rprApi; - GfMatrix4f const& transform; - - LightTransformSetter(HdRprApi* rprApi, GfMatrix4f const& transform) - : rprApi(rprApi), transform(transform) { - - } - - void operator()(LightVariantEmpty) const {} - void operator()(AreaLight* light) const { - auto modelTransform = light->localTransform * transform; - for (auto& mesh : light->meshes) { - rprApi->SetTransform(mesh, modelTransform); - } - } - void operator()(rpr::PointLight* light) const { rprApi->SetTransform(light, transform); } - void operator()(rpr::SpotLight* light) const { rprApi->SetTransform(light, transform); } - void operator()(rpr::IESLight* light) const { rprApi->SetTransform(light, transform); } - }; - BOOST_NS::apply_visitor(LightTransformSetter{rprApi, m_transform}, m_light); + BOOST_NS::apply_visitor(LightTransformSetter{rprApi, m_localTransform * m_transform}, m_light); } *dirtyBits = DirtyBits::Clean; @@ -457,38 +503,31 @@ HdDirtyBits HdRprLight::GetInitialDirtyBitsMask() const { | DirtyBits::DirtyParams; } -void HdRprLight::ReleaseLight(HdRprApi* rprApi) { - struct LightReleaser : public BOOST_NS::static_visitor<> { - HdRprApi* rprApi; - - LightReleaser(HdRprApi* rprApi) : rprApi(rprApi) {} +struct HdRprLight::LightReleaser : public BOOST_NS::static_visitor<> { + HdRprApi* rprApi; - void operator()(LightVariantEmpty) const { /*no-op*/ } - void operator()(rpr::PointLight* light) const { rprApi->Release(light); } - void operator()(rpr::SpotLight* light) const { rprApi->Release(light); } - void operator()(rpr::IESLight* light) const { rprApi->Release(light); } + LightReleaser(HdRprApi* rprApi) : rprApi(rprApi) {} - void operator()(AreaLight* light) const { - for (auto& mesh : light->meshes) { - rprApi->Release(mesh); - } - rprApi->Release(light->material); - delete light; + void operator()(LightVariantEmpty) const { /*no-op*/ } + void operator()(AreaLight* light) const { + for (auto& mesh : light->meshes) { + rprApi->Release(mesh); } - }; + rprApi->ReleaseGeometryLightMaterial(light->material); + delete light; + } + template + void operator()(T* light) const { rprApi->Release(light); } +}; + +void HdRprLight::ReleaseLight(HdRprApi* rprApi) { BOOST_NS::apply_visitor(LightReleaser{rprApi}, m_light); m_light = LightVariantEmpty{}; } void HdRprLight::Finalize(HdRenderParam* renderParam) { - auto rprRenderParam = static_cast(renderParam); - if (m_created) { - m_created = false; - rprRenderParam->RemoveLight(); - } - - auto rprApi = rprRenderParam->AcquireRprApiForEdit(); + auto rprApi = static_cast(renderParam)->AcquireRprApiForEdit(); ReleaseLight(rprApi); HdLight::Finalize(renderParam); diff --git a/pxr/imaging/plugin/hdRpr/light.h b/pxr/imaging/plugin/hdRpr/light.h index b9bb26a02..f84d8a6a3 100644 --- a/pxr/imaging/plugin/hdRpr/light.h +++ b/pxr/imaging/plugin/hdRpr/light.h @@ -18,15 +18,15 @@ limitations under the License. #include "pxr/base/gf/matrix4f.h" #include "pxr/imaging/hd/light.h" -#include "boostIncludePath.h" +#include "pxr/imaging/rprUsd/boostIncludePath.h" #include BOOST_INCLUDE_PATH(variant.hpp) -namespace rpr { class Shape; class PointLight; class SpotLight; class IESLight; } +namespace rpr { class Shape; class PointLight; class SpotLight; class IESLight; class DiskLight; class SphereLight; } PXR_NAMESPACE_OPEN_SCOPE class HdRprApi; -struct HdRprApiMaterial; +class RprUsdMaterial; class HdRprLight : public HdLight { public: @@ -55,7 +55,7 @@ class HdRprLight : public HdLight { rpr::Shape* CreateCylinderLightMesh(HdRprApi* rprApi); struct AreaLight; - void SyncAreaLightGeomParams(AreaLight* light, HdSceneDelegate* sceneDelegate, float* intensity); + void SyncAreaLightGeomParams(HdSceneDelegate* sceneDelegate, float* intensity); void ReleaseLight(HdRprApi* rprApi); @@ -63,26 +63,22 @@ class HdRprLight : public HdLight { const TfToken m_lightType; struct AreaLight { - HdRprApiMaterial* material = nullptr; + RprUsdMaterial* material = nullptr; std::vector meshes; - GfMatrix4f localTransform; }; struct LightVariantEmpty {}; - using Light = BOOST_NS::variant; - enum LightType { - kLightTypeNone, - kLightTypePoint, - kLightTypeSpot, - kLightIES, - kLightTypeArea - }; + using Light = BOOST_NS::variant; Light m_light; + struct LightParameterSetter; + struct LightTransformSetter; + struct LightNameSetter; + struct LightReleaser; + GfVec3f m_emisionColor = GfVec3f(0.0f); GfMatrix4f m_transform; - - bool m_created = false; + GfMatrix4f m_localTransform; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/material.cpp b/pxr/imaging/plugin/hdRpr/material.cpp index 2b85bb25c..3e20c7592 100644 --- a/pxr/imaging/plugin/hdRpr/material.cpp +++ b/pxr/imaging/plugin/hdRpr/material.cpp @@ -12,7 +12,7 @@ limitations under the License. ************************************************************************/ #include "material.h" -#include "materialAdapter.h" +#include "pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.h" #include "renderParam.h" #include "rprApi.h" @@ -21,57 +21,6 @@ limitations under the License. PXR_NAMESPACE_OPEN_SCOPE -TF_DEFINE_PRIVATE_TOKENS(_tokens, - ((infoSourceAsset, "info:sourceAsset")) \ - ((infoImplementationSource, "info:implementationSource")) \ - (sourceAsset) -); - -static bool GetMaterialNetwork( - TfToken const& terminal, HdSceneDelegate* delegate, HdMaterialNetworkMap const& networkMap, HdRprRenderParam const& renderParam, - EMaterialType* out_materialType, HdMaterialNetwork const** out_network) { - auto mapIt = networkMap.map.find(terminal); - if (mapIt == networkMap.map.end()) { - return false; - } - - auto& network = mapIt->second; - if (network.nodes.empty()) { - return false; - } - - *out_network = &network; - - for (auto& node : network.nodes) { - if (node.identifier == HdRprMaterialTokens->UsdPreviewSurface) { - *out_materialType = EMaterialType::USD_PREVIEW_SURFACE; - return true; - } else { - if (renderParam.GetMaterialNetworkSelector() == HdRprMaterialNetworkSelectorTokens->karma) { - auto implementationSource = delegate->Get(node.path, _tokens->infoImplementationSource); - if (implementationSource.IsHolding() && - implementationSource.UncheckedGet() == _tokens->sourceAsset) { - auto nodeAsset = delegate->Get(node.path, _tokens->infoSourceAsset); - if (nodeAsset.IsHolding()) { - auto& asset = nodeAsset.UncheckedGet(); - if (!asset.GetAssetPath().empty()) { - static const std::string kPrincipledShaderDef = "opdef:/Vop/principledshader::2.0"; - if (asset.GetAssetPath().compare(0, kPrincipledShaderDef.size(), kPrincipledShaderDef.c_str())) { - return false; - } - - *out_materialType = EMaterialType::HOUDINI_PRINCIPLED_SHADER; - return true; - } - } - } - } - } - } - - return false; -} - HdRprMaterial::HdRprMaterial(SdfPath const& id) : HdMaterial(id) { } @@ -84,29 +33,48 @@ void HdRprMaterial::Sync(HdSceneDelegate* sceneDelegate, auto rprApi = rprRenderParam->AcquireRprApiForEdit(); if (*dirtyBits & HdMaterial::DirtyResource) { + if (m_rprMaterial) { + rprApi->Release(m_rprMaterial); + m_rprMaterial = nullptr; + } + VtValue vtMat = sceneDelegate->GetMaterialResource(GetId()); if (vtMat.IsHolding()) { auto& networkMap = vtMat.UncheckedGet(); + m_rprMaterial = rprApi->CreateMaterial(sceneDelegate, networkMap); + } - EMaterialType surfaceType = EMaterialType::NONE; - HdMaterialNetwork const* surface = nullptr; - - EMaterialType displacementType = EMaterialType::NONE; - HdMaterialNetwork const* displacement = nullptr; - - if (GetMaterialNetwork(HdMaterialTerminalTokens->surface, sceneDelegate, networkMap, *rprRenderParam, &surfaceType, &surface)) { - if (GetMaterialNetwork(HdMaterialTerminalTokens->displacement, sceneDelegate, networkMap, *rprRenderParam, &displacementType, &displacement)) { - if (displacementType != surfaceType) { - displacement = nullptr; - } + if (!m_rprMaterial) { + // Autodesk's Hydra Scene delegate may give us a mtlx file path directly, + // to reuse existing material processing code, we create HdMaterialNetworkMap + // that holds rpr_materialx_node + // + static TfToken materialXFilenameToken("MaterialXFilename", TfToken::Immortal); + auto materialXFilename = sceneDelegate->Get(GetId(), materialXFilenameToken); + if (materialXFilename.IsHolding()) { + auto& mtlxAssetPath = materialXFilename.UncheckedGet(); + auto& mtlxPath = mtlxAssetPath.GetResolvedPath(); + if (!mtlxPath.empty()) { + HdMaterialNetwork network; + network.nodes.emplace_back(); + HdMaterialNode& mtlxNode = network.nodes.back(); + mtlxNode.identifier = RprUsdRprMaterialXNodeTokens->rpr_materialx_node; + mtlxNode.parameters.emplace(RprUsdRprMaterialXNodeTokens->file, materialXFilename); + + // Use the same network for both surface and displacement terminals, + // RprUsdMaterialRegistry handles automatically shared nodes between terminal networks + // + HdMaterialNetworkMap networkMap; + networkMap.map[HdMaterialTerminalTokens->surface] = network; + networkMap.map[HdMaterialTerminalTokens->displacement] = network; + networkMap.terminals.push_back(mtlxNode.path); + + m_rprMaterial = rprApi->CreateMaterial(sceneDelegate, networkMap); } - - MaterialAdapter matAdapter(surfaceType, *surface, displacement ? *displacement : HdMaterialNetwork{}); - m_rprMaterial = rprApi->CreateMaterial(matAdapter); - } else { - TF_CODING_WARNING("Material type not supported"); } } + + rprRenderParam->MaterialDidChange(sceneDelegate, GetId()); } *dirtyBits = Clean; @@ -117,7 +85,7 @@ HdDirtyBits HdRprMaterial::GetInitialDirtyBitsMask() const { } void HdRprMaterial::Reload() { - // no-op + // possibly we can use it to reload .mtlx definition if it's changed but I don't know when and how Reload is actually called } void HdRprMaterial::Finalize(HdRenderParam* renderParam) { @@ -127,7 +95,7 @@ void HdRprMaterial::Finalize(HdRenderParam* renderParam) { HdMaterial::Finalize(renderParam); } -HdRprApiMaterial const* HdRprMaterial::GetRprMaterialObject() const { +RprUsdMaterial const* HdRprMaterial::GetRprMaterialObject() const { return m_rprMaterial; } diff --git a/pxr/imaging/plugin/hdRpr/material.h b/pxr/imaging/plugin/hdRpr/material.h index 38c44d757..db1b8447c 100644 --- a/pxr/imaging/plugin/hdRpr/material.h +++ b/pxr/imaging/plugin/hdRpr/material.h @@ -18,7 +18,7 @@ limitations under the License. PXR_NAMESPACE_OPEN_SCOPE -struct HdRprApiMaterial; +class RprUsdMaterial; class HdRprMaterial final : public HdMaterial { public: @@ -32,15 +32,15 @@ class HdRprMaterial final : public HdMaterial { HdDirtyBits GetInitialDirtyBitsMask() const override; - void Reload() override; + void Reload(); void Finalize(HdRenderParam* renderParam) override; /// Get pointer to RPR material /// In case material сreation failure return nullptr - HdRprApiMaterial const* GetRprMaterialObject() const; + RprUsdMaterial const* GetRprMaterialObject() const; private: - HdRprApiMaterial* m_rprMaterial = nullptr; + RprUsdMaterial* m_rprMaterial = nullptr; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/materialAdapter.cpp b/pxr/imaging/plugin/hdRpr/materialAdapter.cpp deleted file mode 100644 index f318f902e..000000000 --- a/pxr/imaging/plugin/hdRpr/materialAdapter.cpp +++ /dev/null @@ -1,895 +0,0 @@ -/************************************************************************ -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 "materialAdapter.h" - -#include "pxr/imaging/hd/tokens.h" -#include "pxr/imaging/hd/material.h" - -#include "pxr/base/gf/vec3f.h" -#include "pxr/base/gf/vec2f.h" -#include "pxr/usd/sdf/assetPath.h" -#include "pxr/usd/ar/resolver.h" -#include "pxr/usd/usdUtils/pipeline.h" - -#include -#include - -PXR_NAMESPACE_OPEN_SCOPE - -TF_DEFINE_PUBLIC_TOKENS(HdRprMaterialTokens, HDRPR_MATERIAL_TOKENS); - -TF_DEFINE_PRIVATE_TOKENS(HdRprTextureChannelToken, - (rgba) - (rgb) - (r) - (g) - (b) - (a) -); - -GfVec4f VtValToVec4f(const VtValue val) { - if (val.IsHolding()) { - return GfVec4f(val.Get()); - } else if (val.IsHolding()) { - GfVec3f temp = val.Get(); - return GfVec4f(temp[0], temp[1], temp[2], 1.0f); - } if (val.IsHolding()) { - return GfVec4f(val.Get()); - } else { - return val.Get(); - } -} - -bool IsColorBlack(GfVec4f color) { - return color[0] <= FLT_EPSILON && color[1] <= FLT_EPSILON && color[2] <= FLT_EPSILON; -} - -bool GetNode(const TfToken& type, const HdMaterialNetwork& materialNetwork, HdMaterialNode& out_node) { - TF_FOR_ALL(it, materialNetwork.nodes) { - if (it->identifier == type) { - out_node = *it; - return true; - } - } - - return false; -} - -bool GetParam(const TfToken& type, const HdMaterialNode& node, VtValue& out_param) { - out_param = VtValue(); - - auto& params = node.parameters; - - auto finded = params.find(type); - - if (finded == params.end()) { - return false; - } - - out_param = finded->second; - return true; -} - -EColorChannel GetChannel(TfToken channel) { - if (HdRprTextureChannelToken->rgba == channel) { - return EColorChannel::RGBA; - } else if (HdRprTextureChannelToken->rgb == channel) { - return EColorChannel::RGB; - } else if (HdRprTextureChannelToken->r == channel) { - return EColorChannel::R; - } else if (HdRprTextureChannelToken->g == channel) { - return EColorChannel::G; - } else if (HdRprTextureChannelToken->b == channel) { - return EColorChannel::B; - } else if (HdRprTextureChannelToken->a == channel) { - return EColorChannel::A; - } - - return EColorChannel::NONE; -} - -EWrapMode GetWrapMode(const TfToken type, const HdMaterialNode& node) { - VtValue param; - GetParam(type, node, param); - if (!param.IsHolding()) { - return EWrapMode::NONE; - } - - TfToken WrapModeType = param.Get(); - if (WrapModeType == HdRprMaterialTokens->black) { - return EWrapMode::BLACK; - } else if (WrapModeType == HdRprMaterialTokens->clamp) { - return EWrapMode::CLAMP; - } else if (WrapModeType == HdRprMaterialTokens->mirror) { - return EWrapMode::MIRROR; - } else if (WrapModeType == HdRprMaterialTokens->repeat) { - return EWrapMode::REPEAT; - } - - return EWrapMode::NONE; -} - -void GetParameters(const HdMaterialNetwork& materialNetwork, const HdMaterialNode& previewNode, MaterialParams& out_materialParams) { - out_materialParams.clear(); - out_materialParams.insert(previewNode.parameters.begin(), previewNode.parameters.end()); -} - -void GetTextures(const HdMaterialNetwork& materialNetwork, MaterialTextures& out_materialTextures) { - out_materialTextures.clear(); - - auto stToken = UsdUtilsGetPrimaryUVSetName(); - - for (auto& textureRel : materialNetwork.relationships) { - auto nodeIter = std::find_if(materialNetwork.nodes.begin(), materialNetwork.nodes.end(), - [&textureRel](HdMaterialNode const& node) { - return node.path == textureRel.inputId; - }); - if (nodeIter == materialNetwork.nodes.end()) { - TF_RUNTIME_ERROR("Invalid material network. Relationship %s does not match to any node", textureRel.outputName.GetText()); - continue; - } - if (nodeIter->identifier != HdRprMaterialTokens->UsdUVTexture) { - continue; - } - - MaterialTexture materialNode; - VtValue param; - - // Find out which node produces UV for look up - for (auto& stRel : materialNetwork.relationships) { - if (stRel.outputName != stToken || - stRel.outputId != textureRel.inputId) { - continue; - } - - auto stNodeIter = std::find_if(materialNetwork.nodes.begin(), materialNetwork.nodes.end(), - [&stRel](HdMaterialNode const& node) { - return node.path == stRel.inputId; - }); - if (stNodeIter == materialNetwork.nodes.end()) { - TF_RUNTIME_ERROR("Invalid material network. Relationship %s does not match to any node", stRel.outputName.GetText()); - continue; - } - // Actually some much more complex node graph could exists - // But we support only direct UsdUvTexture<->UsdTransform2d relationship for now - if (stNodeIter->identifier == HdRprMaterialTokens->UsdTransform2d) { - float rotationDegrees = 0.0f; - GfVec2f scale(1.0f); - GfVec2f translation(0.0f); - - GetParam(HdRprMaterialTokens->rotation, *stNodeIter, param); - if (param.IsHolding()) { - rotationDegrees = param.UncheckedGet(); - } - - GetParam(HdRprMaterialTokens->scale, *stNodeIter, param); - if (param.IsHolding()) { - scale = param.UncheckedGet(); - } - - GetParam(HdRprMaterialTokens->translation, *stNodeIter, param); - if (param.IsHolding()) { - translation = param.UncheckedGet(); - } - - float rotation = GfDegreesToRadians(rotationDegrees); - float rotCos = std::cos(rotation); - float rotSin = std::sin(rotation); - - // XXX (Houdini): Proposal of UsdPreviewSurface states that rotation is - // "Counter-clockwise rotation in degrees around the origin", - // by default, the origin is the zero point on the UV coordinate system - // but Houdini's Karma uses origin = (0.5, 0.5). We stick with it right now - GfVec2f origin(0.5f); - - materialNode.uvTransform = GfMatrix3f(1.0, 0.0, -origin[0], - 0.0, 1.0, -origin[1], - 0.0, 0.0, 1.0); - - materialNode.uvTransform = GfMatrix3f(scale[0], 0.0, 0.0, - 0.0, scale[1], 0.0, - 0.0, 0.0, 1.0) * materialNode.uvTransform; - materialNode.uvTransform = GfMatrix3f(rotCos, -rotSin, 0.0, - rotSin, rotCos, 0.0, - 0.0f, 0.0f, 1.0f) * materialNode.uvTransform; - materialNode.uvTransform[0][2] += translation[0] + origin[0]; - materialNode.uvTransform[1][2] += translation[1] + origin[1]; - } - break; - } - - auto& node = *nodeIter; - - GetParam(HdRprMaterialTokens->file, node, param); - - // Get image path - if (param.IsHolding()) { - auto& assetPath = param.UncheckedGet(); - if (assetPath.GetResolvedPath().empty()) { - materialNode.path = ArGetResolver().Resolve(assetPath.GetAssetPath()); - } else { - materialNode.path = assetPath.GetResolvedPath(); - } - } else { - continue; - } - - // Get channel - // The input name descripe channel(s) required - materialNode.channel = GetChannel(textureRel.inputName); - - // Get Wrap Modes - auto wrapS = GetWrapMode(HdRprMaterialTokens->wrapS, node); - auto wrapT = GetWrapMode(HdRprMaterialTokens->wrapT, node); - if (wrapS != wrapT) { - TF_RUNTIME_ERROR("RPR renderer does not support different WrapS and WrapT modes"); - } - materialNode.wrapMode = wrapS; - - // Get Scale - GetParam(HdRprMaterialTokens->scale, node, param); - if (param.IsHolding()) { - materialNode.scale = param.Get(); - } - - // Get Bias - GetParam(HdRprMaterialTokens->bias, node, param); - if (param.IsHolding()) { - materialNode.bias = param.Get(); - } - - out_materialTextures[textureRel.outputName] = materialNode; - } -} - -MaterialAdapter::MaterialAdapter(EMaterialType type, const MaterialParams& params) : m_type(type) { - switch (type) { - case EMaterialType::COLOR: { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR] = GfVec4f(0.18f); - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_DIFFUSE_WEIGHT] = GfVec4f(1.0f); - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFLECTION_WEIGHT] = GfVec4f(0.0f); - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFRACTION_WEIGHT] = GfVec4f(0.0f); - - for (auto& entry : params) { - if (entry.first == HdRprMaterialTokens->color) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR] = VtValToVec4f(entry.second); - } - } - break; - } - case EMaterialType::EMISSIVE: - PopulateEmissive(params); - break; - case EMaterialType::TRANSPERENT: - PopulateTransparent(params); - break; - case EMaterialType::USD_PREVIEW_SURFACE: - PopulateUsdPreviewSurface(params, {}); - break; - default: - break; - } -} - -MaterialAdapter::MaterialAdapter(EMaterialType type, const HdMaterialNetwork& surfaceNetwork, HdMaterialNetwork const& displacementNetwork) - : m_type(type), m_doublesided(true) { - switch (type) { - case EMaterialType::USD_PREVIEW_SURFACE: { - HdMaterialNode previewNode; - if (!GetNode(HdRprMaterialTokens->UsdPreviewSurface, surfaceNetwork, previewNode)) { - break; - } - - MaterialParams materialParameters; - GetParameters(surfaceNetwork, previewNode, materialParameters); - - auto setFallbackValue = [&materialParameters](TfToken const& name, VtValue value) { - // TODO: change to try_emplace when it will be available - materialParameters.emplace(name, value); - }; - setFallbackValue(HdRprMaterialTokens->diffuseColor, VtValue(GfVec3f(0.18f))); - setFallbackValue(HdRprMaterialTokens->emissiveColor, VtValue(GfVec3f(0.0f))); - setFallbackValue(HdRprMaterialTokens->useSpecularWorkflow, VtValue(0)); - setFallbackValue(HdRprMaterialTokens->specularColor, VtValue(GfVec3f(0.0f))); - setFallbackValue(HdRprMaterialTokens->metallic, VtValue(0.0f)); - setFallbackValue(HdRprMaterialTokens->roughness, VtValue(0.5f)); - setFallbackValue(HdRprMaterialTokens->clearcoat, VtValue(0.0f)); - setFallbackValue(HdRprMaterialTokens->clearcoatRoughness, VtValue(0.01f)); - setFallbackValue(HdRprMaterialTokens->opacity, VtValue(1.0f)); - setFallbackValue(HdRprMaterialTokens->opacityThreshold, VtValue(0.0f)); - setFallbackValue(HdRprMaterialTokens->ior, VtValue(1.5f)); - setFallbackValue(HdRprMaterialTokens->opacityThreshold, VtValue(0.0f)); - - MaterialTextures materialTextures; - GetTextures(surfaceNetwork, materialTextures); - - PopulateUsdPreviewSurface(materialParameters, materialTextures); - break; - } - case EMaterialType::HOUDINI_PRINCIPLED_SHADER: { - PopulateHoudiniPrincipledShader(surfaceNetwork, displacementNetwork); - break; - } - default: - break; - } -} - -void MaterialAdapter::PopulateRprColor(const MaterialParams& params) { - for (MaterialParams::const_iterator param = params.begin(); param != params.end(); ++param) { - const TfToken& paramName = param->first; - const VtValue& paramValue = param->second; - - if (paramName == HdRprMaterialTokens->color) { - m_vec4fRprParams.insert({RPR_MATERIAL_INPUT_COLOR, VtValToVec4f(paramValue)}); - } - } -} - -void MaterialAdapter::PopulateEmissive(const MaterialParams& params) { - PopulateRprColor(params); -} - -void MaterialAdapter::PopulateTransparent(const MaterialParams& params) { - PopulateRprColor(params); -} - -void MaterialAdapter::PopulateUsdPreviewSurface(const MaterialParams& params, const MaterialTextures& textures) { - // initial params - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFLECTION_WEIGHT] = GfVec4f(1.0f); - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFRACTION_COLOR] = GfVec4f(1.0f); - - int useSpecular = 0; - GfVec4f albedoColor = GfVec4f(1.0f); - MaterialTexture albedoTex; - bool isAlbedoTexture = false; - - GfVec4f reflectionColor = GfVec4f(1.0f); - MaterialTexture reflectionTex; - bool isReflectionTexture = false; - - for (MaterialParams::const_iterator param = params.begin(); param != params.end(); ++param) { - const TfToken& paramName = param->first; - const VtValue& paramValue = param->second; - - if (paramName == HdRprMaterialTokens->diffuseColor) { - albedoColor = VtValToVec4f(paramValue); - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR] = albedoColor; - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFRACTION_COLOR] = albedoColor; - } else if (paramName == HdRprMaterialTokens->emissiveColor) { - GfVec4f emmisionColor = VtValToVec4f(paramValue); - if (!IsColorBlack(emmisionColor)) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_EMISSION_WEIGHT] = GfVec4f(1.0f); - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_EMISSION_COLOR] = emmisionColor; - } - } else if (paramName == HdRprMaterialTokens->useSpecularWorkflow) { - useSpecular = paramValue.Get(); - } else if (paramName == HdRprMaterialTokens->specularColor) { - reflectionColor = VtValToVec4f(paramValue); - } else if (paramName == HdRprMaterialTokens->metallic) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFLECTION_METALNESS] = VtValToVec4f(paramValue); - } else if (paramName == HdRprMaterialTokens->roughness) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_DIFFUSE_ROUGHNESS] = VtValToVec4f(paramValue); - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFLECTION_ROUGHNESS] = VtValToVec4f(paramValue); - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFRACTION_ROUGHNESS] = VtValToVec4f(paramValue); - } else if (paramName == HdRprMaterialTokens->clearcoat) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_COATING_WEIGHT] = VtValToVec4f(paramValue); - } else if (paramName == HdRprMaterialTokens->clearcoatRoughness) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_COATING_ROUGHNESS] = VtValToVec4f(paramValue); - } else if (paramName == HdRprMaterialTokens->ior) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFRACTION_IOR] = VtValToVec4f(paramValue); - } else if (paramName == HdRprMaterialTokens->opacity) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_DIFFUSE_WEIGHT] = VtValToVec4f(paramValue); - auto refractionWeight = GfVec4f(1.0f) - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_DIFFUSE_WEIGHT]; - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFRACTION_WEIGHT] = refractionWeight; - - if (refractionWeight[0] != 0.0f || refractionWeight[1] != 0.0f || refractionWeight[2] != 0.0f) { - m_doublesided = false; - } - } - } - - for (auto textureEntry : textures) { - auto const& paramName = textureEntry.first; - auto& materialTexture = textureEntry.second; - if (paramName == HdRprMaterialTokens->diffuseColor) { - isAlbedoTexture = true; - albedoTex = materialTexture; - m_texRpr[RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR] = materialTexture; - } else if (paramName == HdRprMaterialTokens->emissiveColor) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_EMISSION_WEIGHT] = GfVec4f(1.0f); - m_texRpr[RPR_MATERIAL_INPUT_UBER_EMISSION_COLOR] = materialTexture; - } else if (paramName == HdRprMaterialTokens->specularColor) { - isReflectionTexture = true; - reflectionTex = materialTexture; - } else if (paramName == HdRprMaterialTokens->metallic) { - m_texRpr[RPR_MATERIAL_INPUT_UBER_REFLECTION_METALNESS] = materialTexture; - } else if (paramName == HdRprMaterialTokens->roughness) { - m_texRpr[RPR_MATERIAL_INPUT_UBER_DIFFUSE_ROUGHNESS] = materialTexture; - m_texRpr[RPR_MATERIAL_INPUT_UBER_REFLECTION_ROUGHNESS] = materialTexture; - m_texRpr[RPR_MATERIAL_INPUT_UBER_REFRACTION_ROUGHNESS] = materialTexture; - } else if (paramName == HdRprMaterialTokens->clearcoat) { - m_texRpr[RPR_MATERIAL_INPUT_UBER_COATING_WEIGHT] = materialTexture; - } else if (paramName == HdRprMaterialTokens->clearcoatRoughness) { - m_texRpr[RPR_MATERIAL_INPUT_UBER_COATING_ROUGHNESS] = materialTexture; - } else if (paramName == HdRprMaterialTokens->ior) { - m_texRpr[RPR_MATERIAL_INPUT_UBER_REFRACTION_IOR] = materialTexture; - } else if (paramName == HdRprMaterialTokens->opacity) { - m_texRpr[RPR_MATERIAL_INPUT_UBER_DIFFUSE_WEIGHT] = materialTexture; - - // refractionWeight == 1 - diffuseWeight - // UsdUvTexture has scale and bias: color = scale * textureValue + bias - // We use it to inverse diffuse weight, so refractionWeight = 1 - diffuseWeightColor = 1 - (scale * textureValue + bias) = - // = (1 - bias) + (-1 * scale) * textureValue, where (1 - bias) = newBias, (-1 * scale) = newScale - materialTexture.bias = GfVec4f(1.0f) - materialTexture.bias; - materialTexture.scale *= -1.0f; - m_texRpr[RPR_MATERIAL_INPUT_UBER_REFRACTION_WEIGHT] = materialTexture; - - m_doublesided = false; - } else if (paramName == HdRprMaterialTokens->normal) { - NormalMapParam param; - param.texture = materialTexture; - param.texture.forceLinearSpace = true; - m_normalMapParams.emplace_back(std::vector{RPR_MATERIAL_INPUT_UBER_DIFFUSE_NORMAL, RPR_MATERIAL_INPUT_UBER_REFLECTION_NORMAL}, param); - } else if (paramName == HdRprMaterialTokens->displacement) { - m_displacementTexture = materialTexture; - } - - } - - if (useSpecular) { - m_uRprParams[RPR_MATERIAL_INPUT_UBER_REFLECTION_MODE] = RPR_UBER_MATERIAL_IOR_MODE_PBR; - - if (isReflectionTexture) { - m_texRpr[RPR_MATERIAL_INPUT_UBER_REFLECTION_COLOR] = reflectionTex; - } else { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFLECTION_COLOR] = reflectionColor; - } - - } else { - m_uRprParams[RPR_MATERIAL_INPUT_UBER_REFLECTION_MODE] = RPR_UBER_MATERIAL_IOR_MODE_METALNESS; - - if (isAlbedoTexture) { - m_texRpr[RPR_MATERIAL_INPUT_UBER_REFLECTION_COLOR] = albedoTex; - } else { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFLECTION_COLOR] = albedoColor; - } - } -} - -TF_DEFINE_PRIVATE_TOKENS(HoudiniPrincipledShaderTokens, - (basecolor) \ - (albedomult) \ - (ior) \ - ((roughness, "rough")) \ - ((anisotropy, "aniso")) \ - ((anisotropyDirection, "anisodir")) \ - (metallic) \ - ((reflectivity, "reflect")) \ - ((reflectTint, "reflecttint")) \ - (coat) \ - ((coatRoughness, "coatrough")) \ - (transparency) \ - ((transmissionColor, "transcolor")) \ - ((transmissionDistance, "transdist")) \ - ((subsurface, "sss")) \ - ((subsurfaceDistance, "sssdist")) \ - ((subsurfaceModel, "sssmodel")) \ - ((subsurfaceColor, "ssscolor")) \ - ((subsurfacePhase, "sssphase")) \ - (sheen) \ - ((sheenTint, "sheentint")) \ - ((emissionColor, "emitcolor")) \ - ((emissionIntensity, "emitint")) \ - ((opacity, "opac")) \ - ((opacityColor, "opaccolor")) \ - (baseNormal) \ - ((baseNormalScale, "baseNormal_scale")) \ - (coatNormal) \ - ((coatNormalScale, "coatNormal_scale")) \ - ((baseNormalEnable, "baseBumpAndNormal_enable")) \ - ((baseNormalType, "baseBumpAndNormal_type")) \ - (separateCoatNormals) \ - ((doubleSided, "frontface")) \ - ((displacementEnable, "dispTex_enable")) \ - ((displacementTexture, "dispTex_texture")) \ - ((displacementOffset, "dispTex_offset")) \ - ((displacementScale, "dispTex_scale")) \ - ((displacementColorSpace, "dispTex_colorSpace")) \ - ((displacementChannel, "dispTex_channel")) \ - ((displacementWrap, "dispTex_wrap")) \ - ((displacementType, "dispTex_type")) -); - -std::map g_houdiniPrincipledShaderParameterDefaultValues = { - {HoudiniPrincipledShaderTokens->basecolor, VtValue(0.2f)}, - {HoudiniPrincipledShaderTokens->ior, VtValue(1.5f)}, - {HoudiniPrincipledShaderTokens->roughness, VtValue(0.3f)}, - {HoudiniPrincipledShaderTokens->anisotropy, VtValue(0.0f)}, - {HoudiniPrincipledShaderTokens->anisotropyDirection, VtValue(0.0f)}, - {HoudiniPrincipledShaderTokens->metallic, VtValue(0.0f)}, - {HoudiniPrincipledShaderTokens->reflectivity, VtValue(1.0f)}, - {HoudiniPrincipledShaderTokens->reflectTint, VtValue(0.0f)}, - {HoudiniPrincipledShaderTokens->coat, VtValue(0.0f)}, - {HoudiniPrincipledShaderTokens->coatRoughness, VtValue(0.0f)}, - {HoudiniPrincipledShaderTokens->transparency, VtValue(0.0f)}, - {HoudiniPrincipledShaderTokens->transmissionColor, VtValue(1.0f)}, - {HoudiniPrincipledShaderTokens->transmissionDistance, VtValue(0.1f)}, - {HoudiniPrincipledShaderTokens->subsurface, VtValue(0.0f)}, - {HoudiniPrincipledShaderTokens->subsurfaceDistance, VtValue(0.1f)}, - {HoudiniPrincipledShaderTokens->subsurfaceColor, VtValue(1.0f)}, - {HoudiniPrincipledShaderTokens->sheen, VtValue(0.0f)}, - {HoudiniPrincipledShaderTokens->sheenTint, VtValue(0.0f)}, - {HoudiniPrincipledShaderTokens->emissionColor, VtValue(0.0f)}, - {HoudiniPrincipledShaderTokens->opacityColor, VtValue(1.0f)}, -}; - -template -T GetParameter(TfToken const& name, std::map const& parameters, T defaultValue = T()) { - auto parameterIt = parameters.find(name); - if (parameterIt != parameters.end() && - parameterIt->second.IsHolding()) { - return parameterIt->second.UncheckedGet(); - } - - return defaultValue; -} - -EColorChannel HoudiniChannelToRpr(int channel, EColorChannel rprDefault) { - if (channel == 1) { - return EColorChannel::R; - } else if (channel == 2) { - return EColorChannel::G; - } else if (channel == 3) { - return EColorChannel::B; - } else { - return rprDefault; - } -} - -EWrapMode HoudiniWrapModeToRpr(std::string const& wrapMode) { - if (wrapMode == "streak") { - return EWrapMode::CLAMP; - } else if (wrapMode == "decal") { - return EWrapMode::BLACK; - } else { - return EWrapMode::REPEAT; - } -} - -void MaterialAdapter::PopulateHoudiniPrincipledShader(HdMaterialNetwork const& surfaceNetwork, HdMaterialNetwork const& displacementNetwork) { - auto& params = surfaceNetwork.nodes[0].parameters; - - m_doublesided = GetParameter(HoudiniPrincipledShaderTokens->doubleSided, params, 1) == 1; - - // XXX: unused parameters: - // reflectTint - // reflectivity - - auto albedoMultiplier = GetParameter(HoudiniPrincipledShaderTokens->albedomult, params, 1.0f); - auto opacity = GetParameter(HoudiniPrincipledShaderTokens->opacity, params, 1.0f); - auto emissionIntensity = GetParameter(HoudiniPrincipledShaderTokens->emissionIntensity, params, 1.0f); - auto subsurfaceModel = GetParameter(HoudiniPrincipledShaderTokens->subsurfaceModel, params, std::string("full")); - auto iorMode = RPR_UBER_MATERIAL_IOR_MODE_PBR; - bool useBaseColorTextureAlpha = false; - - auto getMaterialTexture = [&](TfToken const& baseParameter) -> MaterialTexture { - MaterialTexture texture; - texture.wrapMode = EWrapMode::REPEAT; - - // Each parameter (e.g. basecolor) may have set of properties in the form: - // paramName_propertyName (e.g. basecolor_texture) - // but parameter itself may be missing in input params - - bool useTexture = false; - for (auto it = params.lower_bound(baseParameter); it != params.end(); ++it) { - // check that this property corresponds to our base parameter - if (it->first.GetString().compare(0, baseParameter.size(), baseParameter.GetText())) { - break; - } - - // skip paramName - auto propertyName = it->first.GetText() + baseParameter.size(); - - if (*propertyName != '_') { - continue; - } - ++propertyName; - - if (std::strncmp(propertyName, "texture", sizeof("texture") - 1) == 0) { - auto texturePropertyName = propertyName + (sizeof("texture") - 1); - if (*texturePropertyName == '\0') { - if (it->second.IsHolding()) { - auto& assetPath = it->second.UncheckedGet(); - texture.path = assetPath.GetResolvedPath(); - } - } else if (std::strcmp(texturePropertyName, "Intensity") == 0) { - if (it->second.IsHolding()) { - texture.scale = GfVec4f(it->second.UncheckedGet()); - } - } else if (std::strcmp(texturePropertyName, "ColorSpace") == 0) { - if (it->second.IsHolding()) { - texture.forceLinearSpace = it->second.UncheckedGet() == "linear"; - } - } else if (std::strcmp(texturePropertyName, "Wrap") == 0) { - if (it->second.IsHolding()) { - texture.wrapMode = HoudiniWrapModeToRpr(it->second.UncheckedGet()); - } - } - } else if (std::strncmp(propertyName, "useTexture", sizeof("useTexture") - 1) == 0) { - auto useTexturePropertyName = propertyName + sizeof("useTexture") - 1; - if (*useTexturePropertyName == '\0') { - if (it->second.IsHolding()) { - useTexture = static_cast(it->second.UncheckedGet()); - if (!useTexture) { - return MaterialTexture{}; - } - } - } else if (std::strcmp(useTexturePropertyName, "Alpha") == 0) { - if (it->second.IsHolding() && - it->second.UncheckedGet() == 1) { - useBaseColorTextureAlpha = true; - } - } - } else if (std::strcmp(propertyName, "monoChannel") == 0) { - if (it->second.IsHolding()) { - texture.channel = HoudiniChannelToRpr(it->second.UncheckedGet(), EColorChannel::NONE); - } - } - } - - if (baseParameter == HoudiniPrincipledShaderTokens->baseNormal) { - // baseNormal's texture enabled by baseBumpAndNormal_enable parameter unlike all other textures by *_useTexture - useTexture = true; - } - - if (useTexture) { - if (baseParameter != HoudiniPrincipledShaderTokens->basecolor && - baseParameter != HoudiniPrincipledShaderTokens->transmissionColor && - baseParameter != HoudiniPrincipledShaderTokens->subsurfaceColor && - baseParameter != HoudiniPrincipledShaderTokens->baseNormal && - baseParameter != HoudiniPrincipledShaderTokens->coatNormal && - texture.channel == EColorChannel::NONE) { - texture.channel = EColorChannel::LUMINANCE; - } - - return texture; - } - - return MaterialTexture(); - }; - - auto setMaterialTexture = [&](rpr::MaterialNodeInput rprInput, MaterialTexture const& texture) { - if (rprInput == RPR_MATERIAL_INPUT_UBER_EMISSION_COLOR) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_EMISSION_WEIGHT] = GfVec4f(1.0f); - - auto emissionTexture = texture; - emissionTexture.scale = GfVec4f(emissionIntensity); - m_texRpr[rprInput] = emissionTexture; - } else if (rprInput == RPR_MATERIAL_INPUT_UBER_TRANSPARENCY) { - auto transparencyTex = texture; - transparencyTex.bias = GfVec4f(1.0f) - transparencyTex.bias; - transparencyTex.scale *= -1.0f * opacity; - m_texRpr[rprInput] = transparencyTex; - m_doublesided = false; - } else if (rprInput == RPR_MATERIAL_INPUT_UBER_REFRACTION_WEIGHT) { - m_texRpr[RPR_MATERIAL_INPUT_UBER_REFRACTION_WEIGHT] = texture; - m_uRprParams[RPR_MATERIAL_INPUT_UBER_REFRACTION_CAUSTICS] = 1; - - auto diffuseWeightTex = texture; - // Inverse logic of UsdPreviewSurface opacity texture - diffuseWeightTex.bias = GfVec4f(1.0f) - diffuseWeightTex.bias; - diffuseWeightTex.scale *= -1.0f; - m_texRpr[RPR_MATERIAL_INPUT_UBER_DIFFUSE_WEIGHT] = diffuseWeightTex; - - m_doublesided = false; - } else if (albedoMultiplier != 1.0f && - (rprInput == RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR || - rprInput == RPR_MATERIAL_INPUT_UBER_COATING_COLOR || - rprInput == RPR_MATERIAL_INPUT_UBER_COATING_TRANSMISSION_COLOR || - rprInput == RPR_MATERIAL_INPUT_UBER_SHEEN)) { - auto premultipliedTexture = texture; - premultipliedTexture.scale *= albedoMultiplier; - m_texRpr[rprInput] = texture; - } else { - if (rprInput == RPR_MATERIAL_INPUT_UBER_REFLECTION_METALNESS) { - iorMode = RPR_UBER_MATERIAL_IOR_MODE_METALNESS; - } else if (rprInput == RPR_MATERIAL_INPUT_UBER_COATING_THICKNESS) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_COATING_WEIGHT] = GfVec4f(1.0f); - } else if (rprInput == RPR_MATERIAL_INPUT_UBER_SSS_WEIGHT) { - m_texRpr[RPR_MATERIAL_INPUT_UBER_BACKSCATTER_WEIGHT] = texture; - } - - m_texRpr[rprInput] = texture; - } - }; - - auto populateRprParameter = [&](std::vector rprInputs, TfToken const& paramName) -> bool { - bool isAuthored = false; - - VtValue value; - - auto parameterIt = params.find(paramName); - if (parameterIt != params.end()) { - value = parameterIt->second; - isAuthored = true; - } else { - parameterIt = g_houdiniPrincipledShaderParameterDefaultValues.find(paramName); - if (parameterIt == g_houdiniPrincipledShaderParameterDefaultValues.end()) { - return isAuthored; - } - - value = parameterIt->second; - } - - auto texture = getMaterialTexture(paramName); - if (!texture.path.empty()) { - for (auto rprInput : rprInputs) { - setMaterialTexture(rprInput, texture); - } - - return true; - } - - auto vec = VtValToVec4f(value); - for (auto rprInput : rprInputs) { - if (rprInput == RPR_MATERIAL_INPUT_UBER_EMISSION_COLOR) { - if (IsColorBlack(vec * emissionIntensity)) { - continue; - } - - m_vec4fRprParams[rprInput] = vec * emissionIntensity; - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_EMISSION_WEIGHT] = GfVec4f(1.0f); - } else if (rprInput == RPR_MATERIAL_INPUT_UBER_TRANSPARENCY) { - auto transparency = GfVec4f(1.0f) - vec * opacity; - m_vec4fRprParams[rprInput] = transparency; - } else if (rprInput == RPR_MATERIAL_INPUT_UBER_REFRACTION_WEIGHT) { - m_vec4fRprParams[rprInput] = vec; - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_DIFFUSE_WEIGHT] = GfVec4f(1.0f) - vec; - - if (!IsColorBlack(vec)) { - m_uRprParams[RPR_MATERIAL_INPUT_UBER_REFRACTION_CAUSTICS] = 1; - m_doublesided = false; - } - } else { - if (rprInput == RPR_MATERIAL_INPUT_UBER_REFLECTION_METALNESS && - !IsColorBlack(vec)) { - iorMode = RPR_UBER_MATERIAL_IOR_MODE_METALNESS; - } else if (rprInput == RPR_MATERIAL_INPUT_UBER_COATING_THICKNESS && - !IsColorBlack(vec)) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_COATING_WEIGHT] = GfVec4f(1.0f); - } else if (rprInput == RPR_MATERIAL_INPUT_UBER_SSS_WEIGHT) { - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_BACKSCATTER_WEIGHT] = vec; - } else if (rprInput == RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR || - rprInput == RPR_MATERIAL_INPUT_UBER_COATING_COLOR || - rprInput == RPR_MATERIAL_INPUT_UBER_COATING_TRANSMISSION_COLOR || - rprInput == RPR_MATERIAL_INPUT_UBER_SHEEN) { - vec *= albedoMultiplier; - } - - m_vec4fRprParams[rprInput] = vec; - } - } - - return isAuthored; - }; - - populateRprParameter({RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR, RPR_MATERIAL_INPUT_UBER_REFLECTION_COLOR, RPR_MATERIAL_INPUT_UBER_COATING_COLOR, RPR_MATERIAL_INPUT_UBER_COATING_TRANSMISSION_COLOR, RPR_MATERIAL_INPUT_UBER_SHEEN}, HoudiniPrincipledShaderTokens->basecolor); - populateRprParameter({RPR_MATERIAL_INPUT_UBER_REFRACTION_IOR, RPR_MATERIAL_INPUT_UBER_COATING_IOR}, HoudiniPrincipledShaderTokens->ior); - populateRprParameter({RPR_MATERIAL_INPUT_UBER_DIFFUSE_ROUGHNESS, RPR_MATERIAL_INPUT_UBER_REFLECTION_ROUGHNESS, RPR_MATERIAL_INPUT_UBER_REFRACTION_ROUGHNESS}, HoudiniPrincipledShaderTokens->roughness); - - populateRprParameter({RPR_MATERIAL_INPUT_UBER_REFLECTION_ANISOTROPY}, HoudiniPrincipledShaderTokens->anisotropy); - populateRprParameter({RPR_MATERIAL_INPUT_UBER_REFLECTION_ANISOTROPY_ROTATION}, HoudiniPrincipledShaderTokens->anisotropyDirection); - - bool hasTransparency = false; - hasTransparency |= populateRprParameter({RPR_MATERIAL_INPUT_UBER_REFRACTION_WEIGHT}, HoudiniPrincipledShaderTokens->transparency); - if (useBaseColorTextureAlpha) { - auto baseColorTextureIt = m_texRpr.find(RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR); - if (baseColorTextureIt != m_texRpr.end()) { - MaterialTexture opacityTexture = baseColorTextureIt->second; - opacityTexture.channel = EColorChannel::A; - opacityTexture.scale = GfVec4f(1.0f); - opacityTexture.bias = GfVec4f(0.0f); - opacityTexture.forceLinearSpace = true; - - setMaterialTexture(RPR_MATERIAL_INPUT_UBER_TRANSPARENCY, opacityTexture); - hasTransparency = true; - } else { - useBaseColorTextureAlpha = false; - } - } - - if (!useBaseColorTextureAlpha) { - hasTransparency |= populateRprParameter({RPR_MATERIAL_INPUT_UBER_TRANSPARENCY}, HoudiniPrincipledShaderTokens->opacityColor); - } - - if (!hasTransparency) { - populateRprParameter({RPR_MATERIAL_INPUT_UBER_REFLECTION_METALNESS, RPR_MATERIAL_INPUT_UBER_COATING_METALNESS}, HoudiniPrincipledShaderTokens->metallic); - } - - populateRprParameter({RPR_MATERIAL_INPUT_UBER_COATING_THICKNESS}, HoudiniPrincipledShaderTokens->coat); - populateRprParameter({RPR_MATERIAL_INPUT_UBER_COATING_ROUGHNESS}, HoudiniPrincipledShaderTokens->coatRoughness); - - populateRprParameter({RPR_MATERIAL_INPUT_UBER_REFRACTION_WEIGHT}, HoudiniPrincipledShaderTokens->transparency); - populateRprParameter({RPR_MATERIAL_INPUT_UBER_REFRACTION_COLOR, RPR_MATERIAL_INPUT_UBER_REFRACTION_ABSORPTION_COLOR}, HoudiniPrincipledShaderTokens->transmissionColor); - populateRprParameter({RPR_MATERIAL_INPUT_UBER_REFRACTION_ABSORPTION_DISTANCE}, HoudiniPrincipledShaderTokens->transmissionDistance); - - populateRprParameter({RPR_MATERIAL_INPUT_UBER_SSS_WEIGHT}, HoudiniPrincipledShaderTokens->subsurface); - populateRprParameter({RPR_MATERIAL_INPUT_UBER_SSS_SCATTER_DISTANCE}, HoudiniPrincipledShaderTokens->subsurfaceDistance); - populateRprParameter({RPR_MATERIAL_INPUT_UBER_SSS_SCATTER_COLOR, RPR_MATERIAL_INPUT_UBER_BACKSCATTER_COLOR}, HoudiniPrincipledShaderTokens->subsurfaceColor); - if (subsurfaceModel == "full") { - m_uRprParams[RPR_MATERIAL_INPUT_UBER_SSS_MULTISCATTER] = 1; - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_SSS_SCATTER_DIRECTION] = GfVec4f(0.0); - } else { - m_uRprParams[RPR_MATERIAL_INPUT_UBER_SSS_MULTISCATTER] = 0; - populateRprParameter({RPR_MATERIAL_INPUT_UBER_SSS_SCATTER_DIRECTION}, HoudiniPrincipledShaderTokens->subsurfacePhase); - } - - populateRprParameter({RPR_MATERIAL_INPUT_UBER_SHEEN_WEIGHT}, HoudiniPrincipledShaderTokens->sheen); - populateRprParameter({RPR_MATERIAL_INPUT_UBER_SHEEN_TINT}, HoudiniPrincipledShaderTokens->sheenTint); - - populateRprParameter({RPR_MATERIAL_INPUT_UBER_EMISSION_COLOR}, HoudiniPrincipledShaderTokens->emissionColor); - - if (GetParameter(HoudiniPrincipledShaderTokens->baseNormalEnable, params, 0)) { - NormalMapParam baseNormalMapParam; - baseNormalMapParam.texture = getMaterialTexture(HoudiniPrincipledShaderTokens->baseNormal); - - std::vector rprInputs = {RPR_MATERIAL_INPUT_UBER_DIFFUSE_NORMAL, RPR_MATERIAL_INPUT_UBER_REFLECTION_NORMAL, RPR_MATERIAL_INPUT_UBER_REFRACTION_NORMAL}; - - if (GetParameter(HoudiniPrincipledShaderTokens->separateCoatNormals, params, 0)) { - NormalMapParam coatNormalMapParam; - coatNormalMapParam.texture = getMaterialTexture(HoudiniPrincipledShaderTokens->coatNormal); - if (!coatNormalMapParam.texture.path.empty()) { - coatNormalMapParam.effectScale = GetParameter(HoudiniPrincipledShaderTokens->coatNormalScale, params, 1.0f); - coatNormalMapParam.texture.forceLinearSpace = true; - m_normalMapParams.emplace_back(std::vector{RPR_MATERIAL_INPUT_UBER_COATING_NORMAL}, coatNormalMapParam); - } - } else { - rprInputs.push_back(RPR_MATERIAL_INPUT_UBER_COATING_NORMAL); - } - - if (!baseNormalMapParam.texture.path.empty()) { - baseNormalMapParam.effectScale = GetParameter(HoudiniPrincipledShaderTokens->baseNormalScale, params, 1.0f); - baseNormalMapParam.texture.forceLinearSpace = true; - m_normalMapParams.emplace_back(rprInputs, baseNormalMapParam); - } - } - - m_vec4fRprParams[RPR_MATERIAL_INPUT_UBER_REFLECTION_WEIGHT] = GfVec4f(1.0f); - - m_uRprParams[RPR_MATERIAL_INPUT_UBER_REFLECTION_MODE] = iorMode; - - if (!displacementNetwork.nodes.empty()) { - auto& dispParams = displacementNetwork.nodes[0].parameters; - - if (GetParameter(HoudiniPrincipledShaderTokens->displacementEnable, dispParams, 0)) { - auto dispType = GetParameter(HoudiniPrincipledShaderTokens->displacementType, dispParams); - if (dispType == "vectordisp") { - TF_RUNTIME_ERROR("Vector displacement unsupported"); - } else { - auto dispTexturePath = GetParameter(HoudiniPrincipledShaderTokens->displacementTexture, dispParams); - if (!dispTexturePath.GetResolvedPath().empty()) { - m_displacementTexture.path = dispTexturePath.GetResolvedPath(); - m_displacementTexture.scale = GfVec4f(GetParameter(HoudiniPrincipledShaderTokens->displacementScale, dispParams, 0.05f)); - m_displacementTexture.bias = GfVec4f(GetParameter(HoudiniPrincipledShaderTokens->displacementOffset, dispParams, -0.5f)); - m_displacementTexture.wrapMode = HoudiniWrapModeToRpr(GetParameter(HoudiniPrincipledShaderTokens->displacementWrap, dispParams)); - m_displacementTexture.channel = HoudiniChannelToRpr(GetParameter(HoudiniPrincipledShaderTokens->displacementChannel, dispParams, 0), EColorChannel::LUMINANCE); - m_displacementTexture.forceLinearSpace = GetParameter(HoudiniPrincipledShaderTokens->displacementColorSpace, dispParams, std::string("linear")) == "linear"; - } - } - } - } -} - -PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/materialAdapter.h b/pxr/imaging/plugin/hdRpr/materialAdapter.h deleted file mode 100644 index 5a3a3c1ee..000000000 --- a/pxr/imaging/plugin/hdRpr/materialAdapter.h +++ /dev/null @@ -1,171 +0,0 @@ -/************************************************************************ -Copyright 2020 Advanced Micro Devices, Inc -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -************************************************************************/ - -#ifndef HDRPR_MATERIAL_ADAPTER_H -#define HDRPR_MATERIAL_ADAPTER_H - -#include "pxr/base/tf/token.h" -#include "pxr/base/vt/value.h" -#include "pxr/base/gf/vec4f.h" -#include "pxr/base/gf/matrix3f.h" -#include "pxr/imaging/hd/material.h" - -#include - -PXR_NAMESPACE_OPEN_SCOPE - -#define HDRPR_MATERIAL_TOKENS \ - (bxdf) \ - (file) \ - (scale) \ - (bias) \ - (wrapS) \ - (wrapT) \ - (black) \ - (clamp) \ - (repeat) \ - (mirror) \ - (UsdPreviewSurface) \ - (UsdUVTexture) \ - (UsdTransform2d) \ - (color) \ - (diffuseColor) \ - (emissiveColor) \ - (useSpecularWorkflow) \ - (specularColor) \ - (metallic) \ - (roughness) \ - (clearcoat) \ - (clearcoatRoughness) \ - (opacity) \ - (opacityThreshold) \ - (ior) \ - (normal) \ - (displacement) \ - (transparency) \ - (rotation) \ - (translation) - -TF_DECLARE_PUBLIC_TOKENS(HdRprMaterialTokens, HDRPR_MATERIAL_TOKENS); - -enum class EWrapMode { - NONE = -1 - , BLACK - , CLAMP - , REPEAT - , MIRROR -}; - -enum class EColorChannel { - NONE = -1 - , RGBA - , RGB - , R - , G - , B - , A - , LUMINANCE -}; - -struct MaterialTexture { - std::string path; - - EColorChannel channel = EColorChannel::NONE; - - EWrapMode wrapMode = EWrapMode::NONE; - - GfVec4f scale = GfVec4f(1.0f); - GfVec4f bias = GfVec4f(0.0f); - - GfMatrix3f uvTransform = GfMatrix3f(1.0f); - - bool forceLinearSpace = false; -}; - -typedef std::map MaterialParams; -typedef std::map MaterialTextures; - -typedef std::map MaterialRprParamsVec4f; -typedef std::map MaterialRprParamsU; -typedef std::map MaterialRprParamsTexture; - -struct NormalMapParam { - MaterialTexture texture; - float effectScale = 1.0f; -}; -using MaterialRprParamsNormalMap = std::vector, NormalMapParam>>; - -enum class EMaterialType : int32_t { - NONE = -1 - , COLOR = 0 - , EMISSIVE - , TRANSPERENT - , USD_PREVIEW_SURFACE - , HOUDINI_PRINCIPLED_SHADER -}; - -class MaterialAdapter { -public: - MaterialAdapter(EMaterialType type, MaterialParams const& params); - - MaterialAdapter(EMaterialType type, HdMaterialNetwork const& surfaceNetwork, HdMaterialNetwork const& displacementNetwork); - - EMaterialType GetType() const { - return m_type; - } - - const MaterialRprParamsVec4f& GetVec4fRprParams() const { - return m_vec4fRprParams; - } - - const MaterialRprParamsU& GetURprParams() const { - return m_uRprParams; - } - - const MaterialRprParamsTexture& GetTexRprParams() const { - return m_texRpr; - } - - const MaterialTexture& GetDisplacementTexture() const { - return m_displacementTexture; - } - - const MaterialRprParamsNormalMap& GetNormalMapParams() const { - return m_normalMapParams; - } - - bool IsDoublesided() const { - return m_doublesided; - } - -private: - void PopulateRprColor(const MaterialParams& params); - void PopulateEmissive(const MaterialParams& params); - void PopulateTransparent(const MaterialParams& params); - void PopulateUsdPreviewSurface(const MaterialParams& params, const MaterialTextures& textures); - void PopulateHoudiniPrincipledShader(HdMaterialNetwork const& surfaceNetwork, HdMaterialNetwork const& displacementNetwork); - - EMaterialType m_type; - - MaterialRprParamsVec4f m_vec4fRprParams; - MaterialRprParamsU m_uRprParams; - MaterialRprParamsTexture m_texRpr; - MaterialRprParamsNormalMap m_normalMapParams; - - MaterialTexture m_displacementTexture; - bool m_doublesided = false; -}; - -PXR_NAMESPACE_CLOSE_SCOPE - -#endif // HDRPR_MATERIAL_ADAPTER_H diff --git a/pxr/imaging/plugin/hdRpr/materialFactory.cpp b/pxr/imaging/plugin/hdRpr/materialFactory.cpp deleted file mode 100644 index fdf493628..000000000 --- a/pxr/imaging/plugin/hdRpr/materialFactory.cpp +++ /dev/null @@ -1,463 +0,0 @@ -/************************************************************************ -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 "materialFactory.h" -#include "imageCache.h" - -#include "rpr/error.h" - -#include - -PXR_NAMESPACE_OPEN_SCOPE - -namespace { - -bool GfIsEqual(GfVec4f const& v1, GfVec4f const& v2, float tolerance = 1e-5f) { - return std::abs(v1[0] - v2[0]) <= tolerance && - std::abs(v1[1] - v2[1]) <= tolerance && - std::abs(v1[2] - v2[2]) <= tolerance && - std::abs(v1[3] - v2[3]) <= tolerance; -} - -bool GfIsEqual(GfMatrix3f const& m1, GfMatrix3f const& m2, float tolerance = 1e-5f) { - for (int i = 0; i < 3; ++i) { - for (int j = 0; j < 3; ++j) { - if (std::abs(m1[i][j] - m2[i][j]) >= tolerance) { - return false; - } - } - } - return true; -} - -bool GetWrapType(const EWrapMode& wrapMode, rpr_image_wrap_type& rprWrapType) { - switch (wrapMode) { - case EWrapMode::BLACK: - rprWrapType = RPR_IMAGE_WRAP_TYPE_CLAMP_ZERO; - return true; - case EWrapMode::CLAMP: - rprWrapType = RPR_IMAGE_WRAP_TYPE_CLAMP_TO_EDGE; - return true; - case EWrapMode::MIRROR: - rprWrapType = RPR_IMAGE_WRAP_TYPE_MIRRORED_REPEAT; - return true; - case EWrapMode::REPEAT: - rprWrapType = RPR_IMAGE_WRAP_TYPE_REPEAT; - return true; - default: - break; - } - return false; -} - -bool GetSelectedChannel(const EColorChannel& colorChannel, rpr_int& out_selectedChannel) { - switch (colorChannel) { - case EColorChannel::R: - out_selectedChannel = RPR_MATERIAL_NODE_OP_SELECT_X; - return true; - case EColorChannel::G: - out_selectedChannel = RPR_MATERIAL_NODE_OP_SELECT_Y; - return true; - case EColorChannel::B: - out_selectedChannel = RPR_MATERIAL_NODE_OP_SELECT_Z; - return true; - case EColorChannel::A: - out_selectedChannel = RPR_MATERIAL_NODE_OP_SELECT_W; - return true; - default: - break; - } - return false; -} - -} // namespace anonymous - -RprMaterialFactory::RprMaterialFactory(ImageCache* imageCache) - : m_imageCache(imageCache) { - -} - -HdRprApiMaterial* RprMaterialFactory::CreatePointsMaterial(VtVec3fArray const& colors) { - auto context = m_imageCache->GetContext(); - - auto setupPointsMaterial = [&colors, context](HdRprApiMaterial* material) -> bool { - rpr::Status status; - auto rootMaterialNode = context->CreateMaterialNode(RPR_MATERIAL_NODE_UBERV2, &status); - if (!rootMaterialNode) { - return !RPR_ERROR_CHECK(status, "Failed to create material node"); - } - material->rootMaterial = rootMaterialNode; - - rpr::BufferDesc bufferDesc; - bufferDesc.nb_element = colors.size(); - bufferDesc.element_type = RPR_BUFFER_ELEMENT_TYPE_FLOAT32; - bufferDesc.element_channel_size = 3; - - auto colorsBuffer = context->CreateBuffer(bufferDesc, colors.data(), &status); - if (!colorsBuffer) { - return !RPR_ERROR_CHECK(status, "Failed to create colors buffer"); - } - material->auxiliaryObjects.push_back(colorsBuffer); - - auto lookupIndex = context->CreateMaterialNode(RPR_MATERIAL_NODE_INPUT_LOOKUP, &status); - if (!lookupIndex) { - return !RPR_ERROR_CHECK(status, "Failed to create input lookup node"); - } - material->auxiliaryObjects.push_back(lookupIndex); - if (RPR_ERROR_CHECK(lookupIndex->SetInput(RPR_MATERIAL_INPUT_VALUE, RPR_MATERIAL_NODE_LOOKUP_OBJECT_ID), "Failed to set lookup node input value")) { - return false; - } - - auto bufferSampler = context->CreateMaterialNode(RPR_MATERIAL_NODE_BUFFER_SAMPLER, &status); - if (!bufferSampler) { - return !RPR_ERROR_CHECK(status, "Failed to create buffer sampler node"); - } - material->auxiliaryObjects.push_back(bufferSampler); - - if (RPR_ERROR_CHECK(bufferSampler->SetInput(RPR_MATERIAL_INPUT_DATA, colorsBuffer), "Failed to set buffer sampler node input data") || - RPR_ERROR_CHECK(bufferSampler->SetInput(RPR_MATERIAL_INPUT_UV, lookupIndex), "Failed to set buffer sampler node input uv") || - RPR_ERROR_CHECK(rootMaterialNode->SetInput(RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR, bufferSampler), "Failed to set root material diffuse color")) { - return false; - } - - return true; - }; - - auto material = new HdRprApiMaterial; - if (!setupPointsMaterial(material)) { - Release(material); - material = nullptr; - } - - return material; -} - -HdRprApiMaterial* RprMaterialFactory::CreateMaterial(EMaterialType type, const MaterialAdapter& materialAdapter) { - rpr::MaterialNodeType materialType; - - switch (type) { - case EMaterialType::EMISSIVE: - materialType = RPR_MATERIAL_NODE_EMISSIVE; - break; - case EMaterialType::TRANSPERENT: - materialType = RPR_MATERIAL_NODE_TRANSPARENT; - break; - case EMaterialType::COLOR: - case EMaterialType::USD_PREVIEW_SURFACE: - case EMaterialType::HOUDINI_PRINCIPLED_SHADER: - materialType = RPR_MATERIAL_NODE_UBERV2; - break; - default: - return nullptr; - } - - auto context = m_imageCache->GetContext(); - - rpr::Status status; - auto rootMaterialNode = context->CreateMaterialNode(materialType, &status); - if (!rootMaterialNode) { - RPR_ERROR_CHECK(status, "Failed to create material node"); - return nullptr; - } - - auto material = new HdRprApiMaterial; - material->rootMaterial = rootMaterialNode; - - if (materialAdapter.IsDoublesided()) { - material->twosidedNode = context->CreateMaterialNode(RPR_MATERIAL_NODE_TWOSIDED, &status); - if (!material->twosidedNode) { - RPR_ERROR_CHECK(status, "Failed to create twosided node"); - } else { - RPR_ERROR_CHECK(material->twosidedNode->SetInput(RPR_MATERIAL_INPUT_FRONTFACE, rootMaterialNode), "Failed to set front face input of twosided node"); - } - } - - for (auto const& param : materialAdapter.GetVec4fRprParams()) { - auto& paramId = param.first; - auto& paramValue = param.second; - - if (materialAdapter.GetTexRprParams().count(paramId)) { - continue; - } - RPR_ERROR_CHECK(material->rootMaterial->SetInput(paramId, paramValue[0], paramValue[1], paramValue[2], paramValue[3]), "Failed to set material node vec4 input"); - } - - for (auto param : materialAdapter.GetURprParams()) { - auto& paramId = param.first; - auto& paramValue = param.second; - - RPR_ERROR_CHECK(material->rootMaterial->SetInput(paramId, paramValue), "Failed to set material node uint input"); - } - - auto getTextureMaterialNode = [&material](ImageCache* imageCache, MaterialTexture const& matTex) -> rpr::MaterialNode* { - if (matTex.path.empty()) { - return nullptr; - } - - auto image = imageCache->GetImage(matTex.path, matTex.forceLinearSpace); - if (!image) { - return nullptr; - } - auto rprImage = image.get(); - material->materialImages.push_back(std::move(image)); - - rpr::ImageWrapType rprWrapType; - if (GetWrapType(matTex.wrapMode, rprWrapType)) { - RPR_ERROR_CHECK(rprImage->SetWrap(rprWrapType), "Failed to set image wrap mode"); - } - - rpr::Status status; - auto context = imageCache->GetContext(); - - rpr::MaterialNode* materialNode = context->CreateMaterialNode(RPR_MATERIAL_NODE_IMAGE_TEXTURE, &status); - if (!materialNode) { - RPR_ERROR_CHECK(status, "Failed to create image texture material node"); - return nullptr; - } - - RPR_ERROR_CHECK(materialNode->SetInput(RPR_MATERIAL_INPUT_DATA, rprImage), "Failed to set material node image data input"); - material->auxiliaryObjects.push_back(materialNode); - - if (!GfIsEqual(matTex.uvTransform, GfMatrix3f(1.0f))) { - rpr::MaterialNode* uvLookupNode = context->CreateMaterialNode(RPR_MATERIAL_NODE_INPUT_LOOKUP, &status); - if (uvLookupNode) { - RPR_ERROR_CHECK(uvLookupNode->SetInput(RPR_MATERIAL_INPUT_VALUE, rpr_uint(RPR_MATERIAL_NODE_LOOKUP_UV)), "Failed to set material node uint input"); - - rpr::MaterialNode* transformUvNode = context->CreateMaterialNode(RPR_MATERIAL_NODE_ARITHMETIC, &status); - if (transformUvNode) { - // XXX (RPR): due to missing functionality to set explicitly third component of UV vector to 1 - // third component set to 1 using addition - rpr::MaterialNode* setZtoOneNode = context->CreateMaterialNode(RPR_MATERIAL_NODE_ARITHMETIC, &status); - if (setZtoOneNode) { - RPR_ERROR_CHECK(setZtoOneNode->SetInput(RPR_MATERIAL_INPUT_OP, rpr_uint(RPR_MATERIAL_NODE_OP_ADD)), "Failed to set material node uint input"); - RPR_ERROR_CHECK(setZtoOneNode->SetInput(RPR_MATERIAL_INPUT_COLOR0, 0.0f, 0.0f, 1.0f, 0.0f), "Failed to set material node vec4 input"); - RPR_ERROR_CHECK(setZtoOneNode->SetInput(RPR_MATERIAL_INPUT_COLOR1, uvLookupNode), "Failed to set material node node input"); - - RPR_ERROR_CHECK(transformUvNode->SetInput(RPR_MATERIAL_INPUT_OP, RPR_MATERIAL_NODE_OP_MAT_MUL), "Failed to set material node uint input"); - RPR_ERROR_CHECK(transformUvNode->SetInput(RPR_MATERIAL_INPUT_COLOR0, matTex.uvTransform[0][0], matTex.uvTransform[0][1], matTex.uvTransform[0][2], 0.0f), "Failed to set material node vec4 input"); - RPR_ERROR_CHECK(transformUvNode->SetInput(RPR_MATERIAL_INPUT_COLOR1, matTex.uvTransform[1][0], matTex.uvTransform[1][1], matTex.uvTransform[1][2], 0.0f), "Failed to set material node vec4 input"); - RPR_ERROR_CHECK(transformUvNode->SetInput(RPR_MATERIAL_INPUT_COLOR2, matTex.uvTransform[2][0], matTex.uvTransform[2][1], matTex.uvTransform[2][2], 0.0f), "Failed to set material node vec4 input"); - RPR_ERROR_CHECK(transformUvNode->SetInput(RPR_MATERIAL_INPUT_COLOR3, setZtoOneNode), "Failed to set material node node input"); - - RPR_ERROR_CHECK(materialNode->SetInput(RPR_MATERIAL_INPUT_UV, transformUvNode), "Failed to set material node node input"); - - material->auxiliaryObjects.push_back(transformUvNode); - material->auxiliaryObjects.push_back(setZtoOneNode); - material->auxiliaryObjects.push_back(uvLookupNode); - } else { - RPR_ERROR_CHECK(status, "Failed to create arithmetic material node"); - delete uvLookupNode; - delete transformUvNode; - } - } else { - RPR_ERROR_CHECK(status, "Failed to create arithmetic material node"); - delete uvLookupNode; - } - } else { - RPR_ERROR_CHECK(status, "Failed to create uv lookup material node"); - } - } - - if (!GfIsEqual(matTex.scale, GfVec4f(1.0f))) { - rpr::MaterialNode* arithmetic = context->CreateMaterialNode(RPR_MATERIAL_NODE_ARITHMETIC, &status); - if (arithmetic) { - RPR_ERROR_CHECK(arithmetic->SetInput(RPR_MATERIAL_INPUT_OP, RPR_MATERIAL_NODE_OP_MUL), "Failed to set material node uint input"); - RPR_ERROR_CHECK(arithmetic->SetInput(RPR_MATERIAL_INPUT_COLOR0, materialNode), "Failed to set material node node input"); - RPR_ERROR_CHECK(arithmetic->SetInput(RPR_MATERIAL_INPUT_COLOR1, matTex.scale[0], matTex.scale[1], matTex.scale[2], matTex.scale[3]), "Failed to set material node vec4 input"); - material->auxiliaryObjects.push_back(arithmetic); - - materialNode = arithmetic; - } else { - RPR_ERROR_CHECK(status, "Failed to set material node vec4 input"); - } - } - - if (!GfIsEqual(matTex.bias, GfVec4f(0.0f))) { - rpr::MaterialNode* arithmetic = context->CreateMaterialNode(RPR_MATERIAL_NODE_ARITHMETIC, &status); - if (arithmetic) { - RPR_ERROR_CHECK(arithmetic->SetInput(RPR_MATERIAL_INPUT_OP, rpr_uint(RPR_MATERIAL_NODE_OP_ADD)), "Failed to set material node uint input"); - RPR_ERROR_CHECK(arithmetic->SetInput(RPR_MATERIAL_INPUT_COLOR0, materialNode), "Failed to set material node node input"); - RPR_ERROR_CHECK(arithmetic->SetInput(RPR_MATERIAL_INPUT_COLOR1, matTex.bias[0], matTex.bias[1], matTex.bias[2], matTex.bias[3]), "Failed to set material node vec4 input"); - material->auxiliaryObjects.push_back(arithmetic); - - materialNode = arithmetic; - } else { - RPR_ERROR_CHECK(status, "Failed to create arithmetic material node"); - - } - } - - rpr::MaterialNode* outTexture = nullptr; - if (matTex.channel != EColorChannel::NONE) { - rpr_int selectedChannel = 0; - - if (GetSelectedChannel(matTex.channel, selectedChannel)) { - rpr::MaterialNode* arithmetic = context->CreateMaterialNode(RPR_MATERIAL_NODE_ARITHMETIC, &status); - if (arithmetic) { - RPR_ERROR_CHECK(arithmetic->SetInput(RPR_MATERIAL_INPUT_COLOR0, materialNode), "Failed to set material node node input"); - RPR_ERROR_CHECK(arithmetic->SetInput(RPR_MATERIAL_INPUT_COLOR1, 0.0, 0.0, 0.0, 0.0), "Failed to set material node vec4 input"); - RPR_ERROR_CHECK(arithmetic->SetInput(RPR_MATERIAL_INPUT_OP, selectedChannel), "Failed to set material node uint input"); - material->auxiliaryObjects.push_back(arithmetic); - - outTexture = arithmetic; - } else { - RPR_ERROR_CHECK(status, "Failed to create arithmetic material node"); - } - } else if (matTex.channel == EColorChannel::LUMINANCE) { - rpr::MaterialNode* arithmetic = context->CreateMaterialNode(RPR_MATERIAL_NODE_ARITHMETIC, &status); - if (arithmetic) { - RPR_ERROR_CHECK(arithmetic->SetInput(RPR_MATERIAL_INPUT_COLOR0, materialNode), "Failed to set material node node input"); - RPR_ERROR_CHECK(arithmetic->SetInput(RPR_MATERIAL_INPUT_COLOR1, 0.2126, 0.7152, 0.0722, 0.0), "Failed to set material node vec4 input"); - RPR_ERROR_CHECK(arithmetic->SetInput(RPR_MATERIAL_INPUT_OP, RPR_MATERIAL_NODE_OP_DOT3), "Failed to set material node uint input"); - material->auxiliaryObjects.push_back(arithmetic); - - outTexture = arithmetic; - } else { - RPR_ERROR_CHECK(status, "Failed to create arithmetic material node"); - } - } else { - outTexture = materialNode; - } - } else { - outTexture = materialNode; - } - - return outTexture; - }; - - rpr::MaterialNode* emissionColorNode = nullptr; - - for (auto const& texParam : materialAdapter.GetTexRprParams()) { - auto& paramId = texParam.first; - auto& matTex = texParam.second; - - auto outNode = getTextureMaterialNode(m_imageCache, matTex); - if (!outNode) { - continue; - } - - if (paramId == RPR_MATERIAL_INPUT_UBER_EMISSION_COLOR) { - emissionColorNode = outNode; - } - - material->rootMaterial->SetInput(paramId, outNode); - } - - for (auto const& normalMapParam : materialAdapter.GetNormalMapParams()) { - auto textureNode = getTextureMaterialNode(m_imageCache, normalMapParam.second.texture); - if (!textureNode) { - continue; - } - - auto normalMapNode = context->CreateMaterialNode(RPR_MATERIAL_NODE_NORMAL_MAP, &status); - if (normalMapNode) { - material->auxiliaryObjects.push_back(normalMapNode); - RPR_ERROR_CHECK(normalMapNode->SetInput(RPR_MATERIAL_INPUT_COLOR, textureNode), "Failed to set material node node input"); - - auto s = normalMapParam.second.effectScale; - RPR_ERROR_CHECK(normalMapNode->SetInput(RPR_MATERIAL_INPUT_SCALE, s, s, s, s), "Failed to set material node node input"); - - for (auto paramId : normalMapParam.first) { - RPR_ERROR_CHECK(material->rootMaterial->SetInput(paramId, normalMapNode), "Failed to set normal map node"); - } - } else { - RPR_ERROR_CHECK(status, "Failed to create normal map material node"); - } - } - - if (emissionColorNode) { - auto averageNode = context->CreateMaterialNode(RPR_MATERIAL_NODE_ARITHMETIC, &status); - if (averageNode) { - averageNode->SetInput(RPR_MATERIAL_INPUT_OP, RPR_MATERIAL_NODE_OP_AVERAGE_XYZ); - averageNode->SetInput(RPR_MATERIAL_INPUT_COLOR0, emissionColorNode); - - auto isBlackColorNode = context->CreateMaterialNode(RPR_MATERIAL_NODE_ARITHMETIC, &status); - if (isBlackColorNode) { - material->auxiliaryObjects.push_back(averageNode); - material->auxiliaryObjects.push_back(isBlackColorNode); - - isBlackColorNode->SetInput(RPR_MATERIAL_INPUT_OP, RPR_MATERIAL_NODE_OP_GREATER); - isBlackColorNode->SetInput(RPR_MATERIAL_INPUT_COLOR0, averageNode); - isBlackColorNode->SetInput(RPR_MATERIAL_INPUT_COLOR1, 0.0f, 0.0f, 0.0f, 0.0f); - - material->rootMaterial->SetInput(RPR_MATERIAL_INPUT_UBER_EMISSION_WEIGHT, isBlackColorNode); - } else { - RPR_ERROR_CHECK(status, "Failed to create isBlackColor node"); - delete averageNode; - } - } else { - RPR_ERROR_CHECK(status, "Failed to create averaging node"); - } - } - - material->displacementMaterial = getTextureMaterialNode(m_imageCache, materialAdapter.GetDisplacementTexture()); - - return material; -} - -void RprMaterialFactory::Release(HdRprApiMaterial* material) { - if (!material) { - return; - } - - if (!material->materialImages.empty()) { - m_imageCache->RequireGarbageCollection(); - } - - delete material->rootMaterial; - delete material->twosidedNode; - for (auto node : material->auxiliaryObjects) { - delete node; - } - delete material; -} - -void RprMaterialFactory::AttachMaterial(rpr::Shape* mesh, HdRprApiMaterial const* material, bool doublesided, bool displacementEnabled) { - if (material) { - if (material->twosidedNode) { - RPR_ERROR_CHECK(material->twosidedNode->SetInput(RPR_MATERIAL_INPUT_BACKFACE, doublesided ? material->rootMaterial : nullptr), "Failed to set back face input of twosided node"); - RPR_ERROR_CHECK(mesh->SetMaterial(material->twosidedNode), "Failed to set shape material"); - } else { - RPR_ERROR_CHECK(mesh->SetMaterial(material->rootMaterial), "Failed to set shape material"); - } - - if (displacementEnabled && material->displacementMaterial) { - size_t dummy; - int subdFactor; - if (RPR_ERROR_CHECK(mesh->GetInfo(RPR_SHAPE_SUBDIVISION_FACTOR, sizeof(subdFactor), &subdFactor, &dummy), "Failed to query mesh subdivision factor")) { - subdFactor = 0; - } - - if (subdFactor == 0) { - TF_WARN("Displacement material requires subdivision to be enabled. The subdivision will be enabled with refine level of 1"); - if (!RPR_ERROR_CHECK(mesh->SetSubdivisionFactor(1), "Failed to set mesh subdividion")) { - subdFactor = 1; - } - } - if (subdFactor > 0) { - RPR_ERROR_CHECK(mesh->SetDisplacementMaterial(material->displacementMaterial), "Failed to set shape displacement material"); - } - } else { - RPR_ERROR_CHECK(mesh->SetDisplacementMaterial(nullptr), "Failed to unset shape displacement material"); - } - } else { - RPR_ERROR_CHECK(mesh->SetMaterial(nullptr), "Failed to unset shape material"); - RPR_ERROR_CHECK(mesh->SetDisplacementMaterial(nullptr), "Failed to unset shape displacement material"); - } -} - -void RprMaterialFactory::AttachMaterial(rpr::Curve* curve, HdRprApiMaterial const* material) { - RPR_ERROR_CHECK(curve->SetMaterial(material ? material->rootMaterial : nullptr), "Failed to set curve material"); -} - -PXR_NAMESPACE_CLOSE_SCOPE \ No newline at end of file diff --git a/pxr/imaging/plugin/hdRpr/materialFactory.h b/pxr/imaging/plugin/hdRpr/materialFactory.h deleted file mode 100644 index 5d9efb6e7..000000000 --- a/pxr/imaging/plugin/hdRpr/materialFactory.h +++ /dev/null @@ -1,53 +0,0 @@ -/************************************************************************ -Copyright 2020 Advanced Micro Devices, Inc -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -************************************************************************/ - -#ifndef HDRPR_MATERIAL_FACTORY_H -#define HDRPR_MATERIAL_FACTORY_H - -#include "pxr/pxr.h" -#include "materialAdapter.h" - -#include - -namespace rpr { class MaterialNode; class Image; class Shape; class Curve; } - -PXR_NAMESPACE_OPEN_SCOPE - -struct HdRprApiMaterial { - rpr::MaterialNode* rootMaterial = nullptr; - rpr::MaterialNode* twosidedNode = nullptr; - rpr::MaterialNode* displacementMaterial = nullptr; - std::vector auxiliaryObjects; - std::vector> materialImages; -}; - -class ImageCache; - -class RprMaterialFactory { -public: - RprMaterialFactory(ImageCache* imageCache); - - HdRprApiMaterial* CreateMaterial(EMaterialType type, MaterialAdapter const& materialAdapter); - HdRprApiMaterial* CreatePointsMaterial(VtVec3fArray const& colors); - void Release(HdRprApiMaterial* material); - - void AttachMaterial(rpr::Shape* mesh, HdRprApiMaterial const* material, bool doublesided, bool displacementEnabled); - void AttachMaterial(rpr::Curve* mesh, HdRprApiMaterial const* material); - -private: - ImageCache* m_imageCache; -}; - -PXR_NAMESPACE_CLOSE_SCOPE - -#endif // HDRPR_MATERIAL_FACTORY_H diff --git a/pxr/imaging/plugin/hdRpr/mesh.cpp b/pxr/imaging/plugin/hdRpr/mesh.cpp index 34d78049d..780dfa816 100644 --- a/pxr/imaging/plugin/hdRpr/mesh.cpp +++ b/pxr/imaging/plugin/hdRpr/mesh.cpp @@ -15,10 +15,12 @@ limitations under the License. #include "instancer.h" #include "renderParam.h" #include "material.h" -#include "materialAdapter.h" #include "primvarUtil.h" #include "rprApi.h" +#include "pxr/imaging/rprUsd/material.h" +#include "pxr/imaging/rprUsd/debugCodes.h" + #include "pxr/imaging/pxOsd/tokens.h" #include "pxr/imaging/pxOsd/subdivTags.h" @@ -30,12 +32,10 @@ limitations under the License. #include "pxr/base/gf/matrix4f.h" #include "pxr/base/gf/vec4f.h" -#include "pxr/usd/usdUtils/pipeline.h" - PXR_NAMESPACE_OPEN_SCOPE HdRprMesh::HdRprMesh(SdfPath const& id, SdfPath const& instancerId) - : HdMesh(id, instancerId) + : HdRprBaseRprim(id, instancerId) , m_visibilityMask(kVisibleAll) { } @@ -77,7 +77,7 @@ void HdRprMesh::_InitRepr(TfToken const& reprName, template bool HdRprMesh::GetPrimvarData(TfToken const& name, HdSceneDelegate* sceneDelegate, - std::map primvarDescsPerInterpolation, + std::map const& primvarDescsPerInterpolation, VtArray& out_data, VtIntArray& out_indices) { out_data.clear(); @@ -97,6 +97,8 @@ bool HdRprMesh::GetPrimvarData(TfToken const& name, } return true; } + + TF_RUNTIME_ERROR("Failed to load %s primvar data: unexpected underlying type - %s", name.GetText(), value.GetTypeName().c_str()); return false; } } @@ -104,10 +106,14 @@ bool HdRprMesh::GetPrimvarData(TfToken const& name, return false; } -template bool HdRprMesh::GetPrimvarData(TfToken const&, HdSceneDelegate*, std::map, VtArray&, VtIntArray&); -template bool HdRprMesh::GetPrimvarData(TfToken const&, HdSceneDelegate*, std::map, VtArray&, VtIntArray&); - -HdRprApiMaterial const* HdRprMesh::GetFallbackMaterial(HdSceneDelegate* sceneDelegate, HdRprApi* rprApi, HdDirtyBits dirtyBits) { +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, + HdDirtyBits dirtyBits, + std::map const& primvarDescsPerInterpolation) { if (m_fallbackMaterial && (dirtyBits & HdChangeTracker::DirtyPrimvar)) { rprApi->Release(m_fallbackMaterial); m_fallbackMaterial = nullptr; @@ -120,27 +126,41 @@ HdRprApiMaterial const* HdRprMesh::GetFallbackMaterial(HdSceneDelegate* sceneDel GfVec3f color(0.18f); - HdPrimvarDescriptorVector primvars = sceneDelegate->GetPrimvarDescriptors(GetId(), HdInterpolationConstant); - for (auto& pv : primvars) { - if (pv.name == HdTokens->displayColor) { - VtValue val = sceneDelegate->Get(GetId(), HdTokens->displayColor); - if (val.IsHolding()) { - auto colors = val.UncheckedGet(); - if (!colors.empty()) { - color = colors[0]; + auto constantPrimvarsIt = primvarDescsPerInterpolation.find(HdInterpolationConstant); + if (constantPrimvarsIt != primvarDescsPerInterpolation.end()) { + for (auto& pv : constantPrimvarsIt->second) { + if (pv.name == HdTokens->displayColor) { + VtValue val = sceneDelegate->Get(GetId(), HdTokens->displayColor); + if (val.IsHolding()) { + auto colors = val.UncheckedGet(); + if (!colors.empty()) { + color = colors[0]; + } + break; } - break; } } } - auto matAdapter = MaterialAdapter(EMaterialType::COLOR, MaterialParams{ {HdRprMaterialTokens->color, VtValue(color)} }); - m_fallbackMaterial = rprApi->CreateMaterial(matAdapter); + m_fallbackMaterial = rprApi->CreateDiffuseMaterial(color); + + if (RprUsdIsLeakCheckEnabled()) { + rprApi->SetName(m_fallbackMaterial, GetId().GetText()); + } } return m_fallbackMaterial; } +uint32_t HdRprMesh::GetVisibilityMask() const { + if (!_sharedData.visible) { + // If mesh is explicitly made invisible, ignore custom visibility mask + return kInvisible; + } + + return m_visibilityMask; +} + void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, HdRenderParam* renderParam, HdDirtyBits* dirtyBits, @@ -190,6 +210,12 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } if (HdChangeTracker::IsTopologyDirty(*dirtyBits, id)) { + for (auto& oldGeomSubset : m_geomSubsets) { + if (!oldGeomSubset.materialId.IsEmpty()) { + rprRenderParam->UnsubscribeFromMaterialUpdates(oldGeomSubset.materialId, id); + } + } + m_topology = GetMeshTopology(sceneDelegate); m_faceVertexCounts = m_topology.GetFaceVertexCounts(); m_faceVertexIndices = m_topology.GetFaceVertexIndices(); @@ -198,41 +224,130 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, m_normalsValid = false; m_enableSubdiv = m_topology.GetScheme() == PxOsdOpenSubdivTokens->catmullClark; + m_geomSubsets = m_topology.GetGeomSubsets(); + + // GeomSubset data is directly transfered from USD into Hydra topology. + // This data should be validated and preprocessed before using it: + // 1) merge subsets with the same material + // + std::map materialToSubsetMapping; + for (size_t i = 0; i < m_geomSubsets.size();) { + auto& subset = m_geomSubsets[i]; + auto it = materialToSubsetMapping.find(subset.materialId); + if (it == materialToSubsetMapping.end()) { + materialToSubsetMapping.emplace(subset.materialId, i); + ++i; + } else { + auto& baseSubset = m_geomSubsets[it->second]; + + // Append indices to the base subset + baseSubset.indices.reserve(baseSubset.indices.size() + subset.indices.size()); + for (auto index : subset.indices) { + baseSubset.indices.push_back(index); + } + + // Erase the current subset + if (i + 1 != m_geomSubsets.size()) { + std::swap(m_geomSubsets[i], m_geomSubsets.back()); + } + m_geomSubsets.pop_back(); + } + } + // + // 2) create a new geomSubset that consists of unused faces + // + if (!m_geomSubsets.empty()) { + auto numFaces = m_faceVertexCounts.size(); + std::vector faceIsUnused(numFaces, true); + size_t numUnusedFaces = faceIsUnused.size(); + for (auto const& subset : m_geomSubsets) { + for (int index : subset.indices) { + if (TF_VERIFY(index < numFaces) && faceIsUnused[index]) { + faceIsUnused[index] = false; + numUnusedFaces--; + } + } + } + if (numUnusedFaces) { + m_geomSubsets.push_back(HdGeomSubset()); + HdGeomSubset& unusedSubset = m_geomSubsets.back(); + unusedSubset.type = HdGeomSubset::TypeFaceSet; + unusedSubset.id = id; + unusedSubset.materialId = m_materialId; + unusedSubset.indices.resize(numUnusedFaces); + size_t count = 0; + for (size_t i = 0; i < faceIsUnused.size() && count < numUnusedFaces; ++i) { + if (faceIsUnused[i]) { + unusedSubset.indices[count] = i; + count++; + } + } + } + } + + for (auto& newGeomSubset : m_geomSubsets) { + if (!newGeomSubset.materialId.IsEmpty()) { + rprRenderParam->SubscribeForMaterialUpdates(newGeomSubset.materialId, id); + } + } newMesh = true; } - std::map primvarDescsPerInterpolation = { - {HdInterpolationFaceVarying, sceneDelegate->GetPrimvarDescriptors(id, HdInterpolationFaceVarying)}, - {HdInterpolationVertex, sceneDelegate->GetPrimvarDescriptors(id, HdInterpolationVertex)}, - {HdInterpolationConstant, sceneDelegate->GetPrimvarDescriptors(id, HdInterpolationConstant)}, - }; + 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); newMesh = true; } - auto stToken = UsdUtilsGetPrimaryUVSetName(); - if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, stToken)) { - GetPrimvarData(stToken, sceneDelegate, primvarDescsPerInterpolation, m_uvs, m_uvIndices); + if (*dirtyBits & HdChangeTracker::DirtyMaterialId) { + UpdateMaterialId(sceneDelegate, rprRenderParam); + } - newMesh = true; + // We are loading mesh UVs only when it has material + auto material = static_cast( + sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, m_materialId) + ); + + // Check all materials, including those from geomSubsets + if (!material || !material->GetRprMaterialObject()) { + for (auto& subset : m_geomSubsets) { + if (subset.type == HdGeomSubset::TypeFaceSet && + !subset.materialId.IsEmpty()) { + material = static_cast( + sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, subset.materialId) + ); + if (material && material->GetRprMaterialObject()) { + break; + } + } + } } - if (*dirtyBits & HdChangeTracker::DirtyMaterialId) { - m_cachedMaterialId = sceneDelegate->GetMaterialId(id); + if (material && material->GetRprMaterialObject()) { + auto rprMaterial = material->GetRprMaterialObject(); + + auto uvPrimvarName = &rprMaterial->GetUvPrimvarName(); + if (uvPrimvarName->IsEmpty()) { + static TfToken st("st", TfToken::Immortal); + uvPrimvarName = &st; + } + + if (HdChangeTracker::IsPrimvarDirty(*dirtyBits, id, *uvPrimvarName)) { + HdRprFillPrimvarDescsPerInterpolation(sceneDelegate, id, &primvarDescsPerInterpolation); + GetPrimvarData(*uvPrimvarName, sceneDelegate, primvarDescsPerInterpolation, m_uvs, m_uvIndices); + + newMesh = true; + } } if (*dirtyBits & HdChangeTracker::DirtyVisibility) { _sharedData.visible = sceneDelegate->GetVisible(id); } - if (*dirtyBits & HdChangeTracker::DirtyDoubleSided) { - m_doublesided = sceneDelegate->GetDoubleSided(id); - } - //////////////////////////////////////////////////////////////////////// // 2. Resolve drawstyles @@ -249,7 +364,8 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, if (*dirtyBits & HdChangeTracker::DirtyPrimvar) { HdRprGeometrySettings geomSettings = {}; geomSettings.visibilityMask = kVisibleAll; - HdRprParseGeometrySettings(sceneDelegate, id, primvarDescsPerInterpolation.at(HdInterpolationConstant), &geomSettings); + HdRprFillPrimvarDescsPerInterpolation(sceneDelegate, id, &primvarDescsPerInterpolation); + HdRprParseGeometrySettings(sceneDelegate, id, primvarDescsPerInterpolation, &geomSettings); if (m_refineLevel != geomSettings.subdivisionLevel) { m_refineLevel = geomSettings.subdivisionLevel; @@ -302,41 +418,12 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, m_rprMeshInstances.clear(); m_rprMeshes.clear(); - m_geomSubsets = m_topology.GetGeomSubsets(); if (m_geomSubsets.empty()) { if (auto rprMesh = rprApi->CreateMesh(m_points, m_faceVertexIndices, m_normals, m_normalIndices, m_uvs, m_uvIndices, m_faceVertexCounts, m_topology.GetOrientation())) { + rprApi->SetMeshId(rprMesh, GetPrimId()); m_rprMeshes.push_back(rprMesh); } } else { - auto numFaces = m_faceVertexCounts.size(); - std::vector faceIsUnused(numFaces, true); - size_t numUnusedFaces = faceIsUnused.size(); - for (auto const& subset : m_geomSubsets) { - for (int index : subset.indices) { - if (TF_VERIFY(index < numFaces) && faceIsUnused[index]) { - faceIsUnused[index] = false; - numUnusedFaces--; - } - } - } - // If we found any unused faces, build a final subset with those faces. - // Use the material bound to the parent mesh. - if (numUnusedFaces) { - m_geomSubsets.push_back(HdGeomSubset()); - HdGeomSubset& unusedSubset = m_geomSubsets.back(); - unusedSubset.type = HdGeomSubset::TypeFaceSet; - unusedSubset.id = id; - unusedSubset.materialId = m_cachedMaterialId; - unusedSubset.indices.resize(numUnusedFaces); - size_t count = 0; - for (size_t i = 0; i < faceIsUnused.size() && count < numUnusedFaces; ++i) { - if (faceIsUnused[i]) { - unusedSubset.indices[count] = i; - count++; - } - } - } - // GeomSubset may reference face subset in any given order so we need to be able to // randomly lookup face indexes but each face may be of an arbitrary number of vertices std::vector indexesOffsetPrefixSum; @@ -388,7 +475,9 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, for (int i = 0; i < numVerticesInFace; ++i) { const int pointIndex = m_faceVertexIndices[faceIndexesOffset + i]; int subsetPointIndex = vertexIndexRemapping[pointIndex]; - if (subsetPointIndex == -1) { + + bool newPoint = subsetPointIndex == -1; + if (newPoint) { subsetPointIndex = static_cast(subsetPoints.size()); vertexIndexRemapping[pointIndex] = subsetPointIndex; @@ -398,7 +487,9 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, if (!m_normals.empty()) { if (m_normalIndices.empty()) { - subsetNormals.push_back(m_normals[pointIndex]); + if (newPoint) { + subsetNormals.push_back(m_normals[pointIndex]); + } } else { const int normalIndex = m_normalIndices[faceIndexesOffset + i]; int subsetNormalIndex = normalIndexRemapping[normalIndex]; @@ -414,7 +505,9 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, if (!m_uvs.empty()) { if (m_uvIndices.empty()) { - subsetUv.push_back(m_uvs[pointIndex]); + if (newPoint) { + subsetUv.push_back(m_uvs[pointIndex]); + } } else { const int uvIndex = m_uvIndices[faceIndexesOffset + i]; int subsetuvIndex = uvIndexRemapping[uvIndex]; @@ -431,6 +524,7 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } if (auto rprMesh = rprApi->CreateMesh(subsetPoints, subsetIndexes, subsetNormals, subsetNormalIndices, subsetUv, subsetUvIndices, subsetVertexPerFace, m_topology.GetOrientation())) { + rprApi->SetMeshId(rprMesh, GetPrimId()); m_rprMeshes.push_back(rprMesh); ++it; } else { @@ -441,6 +535,13 @@ 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); + } + } + if (newMesh || (*dirtyBits & HdChangeTracker::DirtySubdivTags)) { PxOsdSubdivTags subdivTags = sceneDelegate->GetSubdivTags(id); @@ -471,39 +572,29 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } } - if (newMesh || ((*dirtyBits & HdChangeTracker::DirtyVisibility) || isVisibilityMaskDirty)) { - auto visibilityMask = m_visibilityMask; - if (!_sharedData.visible) { - // Override m_visibilityMask - visibilityMask = 0; - } - for (auto& rprMesh : m_rprMeshes) { - rprApi->SetMeshVisibility(rprMesh, visibilityMask); - } - } - if (newMesh || (*dirtyBits & HdChangeTracker::DirtyMaterialId) || (*dirtyBits & HdChangeTracker::DirtyDoubleSided) || // update twosided material node (*dirtyBits & HdChangeTracker::DirtyDisplayStyle) || isRefineLevelDirty) { // update displacement material - auto getMeshMaterial = [sceneDelegate, rprApi, dirtyBits, this](SdfPath const& materialId) { + auto getMeshMaterial = [sceneDelegate, rprApi, dirtyBits, &primvarDescsPerInterpolation, this](SdfPath const& materialId) { auto material = static_cast(sceneDelegate->GetRenderIndex().GetSprim(HdPrimTypeTokens->material, materialId)); if (material && material->GetRprMaterialObject()) { return material->GetRprMaterialObject(); } else { - return GetFallbackMaterial(sceneDelegate, rprApi, *dirtyBits); + HdRprFillPrimvarDescsPerInterpolation(sceneDelegate, GetId(), &primvarDescsPerInterpolation); + return GetFallbackMaterial(sceneDelegate, rprApi, *dirtyBits, primvarDescsPerInterpolation); } }; if (m_geomSubsets.empty()) { - auto material = getMeshMaterial(m_cachedMaterialId); + auto material = getMeshMaterial(m_materialId); for (auto& mesh : m_rprMeshes) { - rprApi->SetMeshMaterial(mesh, material, m_doublesided, m_displayStyle.displacementEnabled); + rprApi->SetMeshMaterial(mesh, material, m_displayStyle.displacementEnabled); } } else { if (m_geomSubsets.size() == m_rprMeshes.size()) { for (int i = 0; i < m_rprMeshes.size(); ++i) { auto material = getMeshMaterial(m_geomSubsets[i].materialId); - rprApi->SetMeshMaterial(m_rprMeshes[i], material, m_doublesided, m_displayStyle.displacementEnabled); + rprApi->SetMeshMaterial(m_rprMeshes[i], material, m_displayStyle.displacementEnabled); } } else { TF_CODING_ERROR("Unexpected number of meshes"); @@ -524,8 +615,9 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } m_rprMeshInstances.clear(); + auto visibilityMask = GetVisibilityMask(); for (int i = 0; i < m_rprMeshes.size(); ++i) { - rprApi->SetMeshVisibility(m_rprMeshes[i], _sharedData.visible); + rprApi->SetMeshVisibility(m_rprMeshes[i], visibilityMask); } } else { updateTransform = false; @@ -569,8 +661,10 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } meshInstances.resize(newNumInstances); } else { + int32_t meshId = GetPrimId(); for (int j = meshInstances.size(); j < newNumInstances; ++j) { meshInstances.push_back(rprApi->CreateMeshInstance(m_rprMeshes[i])); + rprApi->SetMeshId(meshInstances.back(), meshId); } } } @@ -580,7 +674,23 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } // Hide prototype - rprApi->SetMeshVisibility(m_rprMeshes[i], false); + rprApi->SetMeshVisibility(m_rprMeshes[i], kInvisible); + } + } + } + } + + if (newMesh || ((*dirtyBits & HdChangeTracker::DirtyVisibility) || isVisibilityMaskDirty)) { + auto visibilityMask = GetVisibilityMask(); + if (m_rprMeshInstances.empty()) { + for (auto& rprMesh : m_rprMeshes) { + rprApi->SetMeshVisibility(rprMesh, visibilityMask); + } + } else { + // Do not touch prototype meshes (m_rprMeshes), set visibility for instances only + for (auto& instances : m_rprMeshInstances) { + for (auto& rprMesh : instances) { + rprApi->SetMeshVisibility(rprMesh, visibilityMask); } } } @@ -597,7 +707,8 @@ void HdRprMesh::Sync(HdSceneDelegate* sceneDelegate, } void HdRprMesh::Finalize(HdRenderParam* renderParam) { - auto rprApi = static_cast(renderParam)->AcquireRprApiForEdit(); + auto rprRenderParam = static_cast(renderParam); + auto rprApi = rprRenderParam->AcquireRprApiForEdit(); for (auto mesh : m_rprMeshes) { rprApi->Release(mesh); @@ -613,7 +724,13 @@ void HdRprMesh::Finalize(HdRenderParam* renderParam) { rprApi->Release(m_fallbackMaterial); m_fallbackMaterial = nullptr; - HdMesh::Finalize(renderParam); + for (auto& oldGeomSubset : m_geomSubsets) { + if (!oldGeomSubset.materialId.IsEmpty()) { + rprRenderParam->UnsubscribeFromMaterialUpdates(oldGeomSubset.materialId, GetId()); + } + } + + HdRprBaseRprim::Finalize(renderParam); } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/mesh.h b/pxr/imaging/plugin/hdRpr/mesh.h index a551d61a8..212216766 100644 --- a/pxr/imaging/plugin/hdRpr/mesh.h +++ b/pxr/imaging/plugin/hdRpr/mesh.h @@ -14,6 +14,8 @@ limitations under the License. #ifndef HDRPR_MESH_H #define HDRPR_MESH_H +#include "baseRprim.h" + #include "pxr/imaging/hd/mesh.h" #include "pxr/imaging/hd/vertexAdjacency.h" #include "pxr/base/vt/array.h" @@ -25,9 +27,9 @@ namespace rpr { class Shape; } PXR_NAMESPACE_OPEN_SCOPE class HdRprApi; -struct HdRprApiMaterial; +class RprUsdMaterial; -class HdRprMesh final : public HdMesh { +class HdRprMesh final : public HdRprBaseRprim { public: HF_MALLOC_TAG_NEW("new HdRprMesh"); @@ -52,18 +54,23 @@ class HdRprMesh final : public HdMesh { template bool GetPrimvarData(TfToken const& name, HdSceneDelegate* sceneDelegate, - std::map primvarDescsPerInterpolation, + std::map const& primvarDescsPerInterpolation, VtArray& out_data, VtIntArray& out_indices); - HdRprApiMaterial const* GetFallbackMaterial(HdSceneDelegate* sceneDelegate, HdRprApi* rprApi, HdDirtyBits dirtyBits); + RprUsdMaterial const* GetFallbackMaterial( + HdSceneDelegate* sceneDelegate, + HdRprApi* rprApi, + HdDirtyBits dirtyBits, + std::map const& primvarDescsPerInterpolation); + + uint32_t GetVisibilityMask() const; private: std::vector m_rprMeshes; std::vector> m_rprMeshInstances; - HdRprApiMaterial* m_fallbackMaterial = nullptr; + RprUsdMaterial* m_fallbackMaterial = nullptr; - SdfPath m_cachedMaterialId; static constexpr int kDefaultNumTimeSamples = 2; HdTimeSampleArray m_transformSamples; @@ -88,7 +95,6 @@ class HdRprMesh final : public HdMesh { HdDisplayStyle m_displayStyle; int m_refineLevel = 0; - bool m_doublesided = false; uint32_t m_visibilityMask; }; diff --git a/pxr/imaging/plugin/hdRpr/ndrDiscoveryPlugin.cpp b/pxr/imaging/plugin/hdRpr/ndrDiscoveryPlugin.cpp new file mode 100644 index 000000000..f33ea34e5 --- /dev/null +++ b/pxr/imaging/plugin/hdRpr/ndrDiscoveryPlugin.cpp @@ -0,0 +1,57 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "pxr/usd/ndr/discoveryPlugin.h" +#include "pxr/imaging/rprUsd/materialRegistry.h" + +PXR_NAMESPACE_OPEN_SCOPE + +/// \class HdRprNdrDiscoveryPlugin +/// +/// Enumerates materials from RprUsdMaterialRegistry. +/// +class HdRprNdrDiscoveryPlugin final : public NdrDiscoveryPlugin { +public: + NdrNodeDiscoveryResultVec DiscoverNodes(const Context& ctx) override { + static TfToken rpr("rpr", TfToken::Immortal); + + NdrNodeDiscoveryResultVec ret; + for (auto& nodeDesc : RprUsdMaterialRegistry::GetInstance().GetRegisteredNodes()) { + if (!nodeDesc.info) continue; + + ret.emplace_back( + /* identifier = */ TfToken(nodeDesc.info->GetName()), + /* version = */ NdrVersion(1), + /* name = */ nodeDesc.info->GetName(), + /* family = */ TfToken(nodeDesc.info->GetUIFolder()), + /* discoveryType = */ rpr, + /* sourceType = */ rpr, + /* uri = */ std::string(), + /* resolvedUri = */ std::string(), + /* sourceCode = */ std::string(), + /* metadata = */ NdrTokenMap(), + /* blindData = */ std::string() + ); + } + return ret; + } + + const NdrStringVec& GetSearchURIs() const override { + static NdrStringVec s_searchURIs; + return s_searchURIs; + } +}; + +NDR_REGISTER_DISCOVERY_PLUGIN(HdRprNdrDiscoveryPlugin); + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/ndrParserPlugin.cpp b/pxr/imaging/plugin/hdRpr/ndrParserPlugin.cpp new file mode 100644 index 000000000..bc08e4fe9 --- /dev/null +++ b/pxr/imaging/plugin/hdRpr/ndrParserPlugin.cpp @@ -0,0 +1,60 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "pxr/usd/ndr/node.h" +#include "pxr/usd/ndr/parserPlugin.h" +#include "pxr/usd/ndr/nodeDiscoveryResult.h" + +#include "pxr/base/tf/staticTokens.h" + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEFINE_PRIVATE_TOKENS(_tokens, (rpr)); + +/// \class HdRprNdrParserPlugin +/// +/// This does the minimal amount of work so Hydra will let us have our shaders. +/// +class HdRprNdrParserPlugin final : public NdrParserPlugin { +public: + NdrNodeUniquePtr Parse(const NdrNodeDiscoveryResult& discoveryResult) override { + return std::make_unique( + /* identifier = */ discoveryResult.identifier, + /* version = */ discoveryResult.version, + /* name = */ discoveryResult.name, + /* family = */ discoveryResult.family, + /* context = */ _tokens->rpr, + /* sourceType = */ _tokens->rpr, + /* uri = */ discoveryResult.uri, +#if PXR_VERSION > 1911 + /* resolvedUri = */ discoveryResult.resolvedUri, +#endif + /* properties = */ NdrPropertyUniquePtrVec{}, + /* metadata = */ discoveryResult.metadata, + /* sourceCode = */ discoveryResult.sourceCode + ); + } + + const NdrTokenVec& GetDiscoveryTypes() const override { + static NdrTokenVec s_discoveryTypes{_tokens->rpr}; + return s_discoveryTypes; + } + + const TfToken& GetSourceType() const override { + return _tokens->rpr; + } +}; + +NDR_REGISTER_PARSER_PLUGIN(HdRprNdrParserPlugin); + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/package/CMakeLists.txt b/pxr/imaging/plugin/hdRpr/package/CMakeLists.txt index 556b6be43..81ae0c9d1 100644 --- a/pxr/imaging/plugin/hdRpr/package/CMakeLists.txt +++ b/pxr/imaging/plugin/hdRpr/package/CMakeLists.txt @@ -1,31 +1,45 @@ -set(HOMEPAGE "https://github.com/GPUOpen-LibrariesAndSDKs/RadeonProRenderUSD") - -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Radeon ProRender USD Hydra rendering delegate") -set(CPACK_PACKAGE_VENDOR "AMD") -set(CPACK_RESOURCE_FILE_LICENSE "${PROJECT_SOURCE_DIR}/LICENSE.md") -if(RPR_BUILD_AS_HOUDINI_PLUGIN) - set(CPACK_INSTALL_TARGET "Houdini-${Houdini_VERSION}") -else() - set(CPACK_INSTALL_TARGET "USD-${USD_MINOR_VERSION}.${USD_PATCH_VERSION}") -endif() -set(CPACK_PACKAGE_NAME hdRpr) -if(NOT CMAKE_BUILD_TYPE STREQUAL "Release") - set(CPACK_PACKAGE_NAME ${CPACK_PACKAGE_NAME}-${CMAKE_BUILD_TYPE}) +set(USD_MINOR_VERSION "0") +set(USD_MAJOR_VERSION "0") +get_target_property(USD_INCLUDE_DIRS ar INTERFACE_INCLUDE_DIRECTORIES) +if(USD_INCLUDE_DIRS) + foreach(dir ${USD_INCLUDE_DIRS}) + if(EXISTS "${dir}/pxr/pxr.h") + foreach(_usd_comp MAJOR MINOR PATCH) + file(STRINGS + "${dir}/pxr/pxr.h" + _usd_tmp + REGEX "#define PXR_${_usd_comp}_VERSION .*$") + string(REGEX MATCHALL "[0-9]+" USD_${_usd_comp}_VERSION ${_usd_tmp}) + endforeach() + break() + endif() + endforeach() endif() -set(CPACK_PACKAGE_VERSION "${HD_RPR_MAJOR_VERSION}.${HD_RPR_MINOR_VERSION}.${HD_RPR_PATCH_VERSION}") -set(CPACK_GENERATOR "TGZ") +if(DUMP_PACKAGE_FILE_NAME) + if(APPLE) + set(PACKAGE_PLATFORM "macOS") + elseif(WIN32) + set(PACKAGE_PLATFORM "Windows") + else() + set(PACKAGE_PLATFORM "${RPR_SDK_PLATFORM}") + endif() -set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY OFF) + set(PACKAGE_NAME hdRpr) + if(NOT CMAKE_BUILD_TYPE STREQUAL "Release") + set(PACKAGE_NAME ${PACKAGE_NAME}-${CMAKE_BUILD_TYPE}) + endif() -if(APPLE) - set(CPACK_SYSTEM_NAME "macOS") -elseif(WIN32) - set(CPACK_SYSTEM_NAME "Windows") -else() - set(CPACK_SYSTEM_NAME "${RPR_SDK_PLATFORM}") -endif() + set(PACKAGE_VERSION "${HD_RPR_MAJOR_VERSION}.${HD_RPR_MINOR_VERSION}.${HD_RPR_PATCH_VERSION}") -set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_INSTALL_TARGET}-${CPACK_SYSTEM_NAME}") + if(HoudiniUSD_FOUND) + set(TARGET "Houdini-${Houdini_VERSION}-py${_houdini_python_version}") -include(CPack) + else() + set(TARGET "USD-${USD_MINOR_VERSION}.${USD_PATCH_VERSION}") + endif() + + set(PACKAGE_FILE_NAME "${PACKAGE_NAME}-${PACKAGE_VERSION}-${TARGET}-${PACKAGE_PLATFORM}") + + message(STATUS "PACKAGE_FILE_NAME: ${PACKAGE_FILE_NAME}") +endif() diff --git a/pxr/imaging/plugin/hdRpr/package/generatePackage.py b/pxr/imaging/plugin/hdRpr/package/generatePackage.py index bcc92930e..2b44a3f84 100644 --- a/pxr/imaging/plugin/hdRpr/package/generatePackage.py +++ b/pxr/imaging/plugin/hdRpr/package/generatePackage.py @@ -12,7 +12,9 @@ import os import re import sys +import shlex import shutil +import tarfile import platform import argparse import subprocess @@ -42,67 +44,51 @@ def current_working_directory(dir): try: yield finally: os.chdir(curdir) -def get_package_path(build_dir, config): - with current_working_directory(build_dir): - subprocess.call(['cmake', '--build', '.', '--config', config, '--', format_multi_procs(get_cpu_count())]) - output = subprocess.check_output(['cpack', '-C', config]) - print(output) - - pattern = re.compile(r'CPack:\ -\ package:\ (?P.*?)\ generated.', re.VERBOSE) - for line in str(output).split('\n'): - match = pattern.match(line.rstrip()) - if match: - package_path = match.group('package_path') - return package_path - self_dir = os.path.abspath(self_path()) hdrpr_root_path = os.path.abspath(os.path.join(self_path(), '../../../../..')) parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter) -parser.add_argument('-b', '--build_dir', type=str, default='{}/build'.format(hdrpr_root_path)) -parser.add_argument('-o', '--output_package_path', type=str, default=None) +parser.add_argument('-i', '--src_dir', type=str, default=hdrpr_root_path) +parser.add_argument('-o', '--output_dir', type=str, default='.') parser.add_argument('-c', '--config', type=str, default='Release') +parser.add_argument('--cmake_options', type=str, default='') +parser.add_argument('--disable_auto_cleanup', default=False, action='store_true') args = parser.parse_args() -package_path = get_package_path(args.build_dir, args.config) -if package_path: - package_basename = os.path.basename(package_path) +output_dir = os.path.abspath(args.output_dir) + +package_dir = '_package' +cmake_configure_cmd = ['cmake', '-DCMAKE_INSTALL_PREFIX='+package_dir, '-DDUMP_PACKAGE_FILE_NAME=ON'] +cmake_configure_cmd += shlex.split(args.cmake_options) +if platform.system() == 'Windows': + cmake_configure_cmd += ['-G', 'Visual Studio 15 2017 Win64'] +cmake_configure_cmd += ['..'] + +build_dir = 'build_generatePackage_tmp_dir' +if not args.disable_auto_cleanup and os.path.isdir(build_dir): + shutil.rmtree(build_dir) - output_package_path = args.output_package_path - if not output_package_path: - package_name = package_basename - package_ext = '.tar.gz' - if package_basename.endswith(package_ext): - package_name = package_basename[:-len(package_ext)] - output_package_path = '{}-package.zip'.format(package_name) +os.makedirs(build_dir, exist_ok=True) - if not os.path.exists('tmp_dir'): - os.mkdir('tmp_dir') - with current_working_directory('tmp_dir'): - if platform.system() != 'Linux': - installer_content = 'python install.py\n' - if platform.system() == "Windows": - installer_ext = 'bat' - installer_content = 'pushd %~dp0\n' + installer_content - installer_content += 'pause\n' - if platform.system() == "Darwin": - installer_content = 'cd $(dirname $0)\n' + installer_content - installer_ext = 'command' - install_file_path = 'install.' + installer_ext - install_file = open(install_file_path, 'w') - install_file.write(installer_content) - install_file.close() +with current_working_directory(build_dir): + configure_output = subprocess.check_output(cmake_configure_cmd).decode() + print(configure_output) - if platform.system() == 'Darwin': - os.chmod(install_file_path, 0o755) + package_name = 'hdRpr' + for line in reversed(configure_output.splitlines()): + if line.startswith('-- PACKAGE_FILE_NAME'): + package_name = line[len('-- PACKAGE_FILE_NAME: '):] + break - shutil.copyfile(package_path, package_basename) - shutil.copyfile(self_dir + '/install.py', 'install.py') - shutil.copyfile(hdrpr_root_path + '/INSTALL.md', 'INSTALL.md') - shutil.copyfile(hdrpr_root_path + '/LICENSE.md', 'LICENSE.md') + return_code = subprocess.call(['cmake', '--build', '.', '--config', args.config, '--target', 'install', '--', format_multi_procs(get_cpu_count())]) + if return_code != 0: + exit(return_code) - shutil.make_archive('tmp_package', 'zip', 'tmp_dir', './') - shutil.copyfile('tmp_package.zip', output_package_path) + with tarfile.open('tmpPackage.tar.gz', 'w:gz') as tar: + tar.add(package_dir, package_name) + output_package = os.path.join(output_dir, package_name+'.tar.gz') + shutil.copyfile('tmpPackage.tar.gz', output_package) +print('{} has been created'.format(os.path.relpath(output_package))) - shutil.rmtree('tmp_dir') - os.remove('tmp_package.zip') +if not args.disable_auto_cleanup: + shutil.rmtree(build_dir) diff --git a/pxr/imaging/plugin/hdRpr/package/install.py b/pxr/imaging/plugin/hdRpr/package/install.py deleted file mode 100644 index de19ed28f..000000000 --- a/pxr/imaging/plugin/hdRpr/package/install.py +++ /dev/null @@ -1,183 +0,0 @@ -# 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. -# -#!/usr/bin/python2.7 -import os -import re -import glob -import platform -import argparse -import tarfile - -from distutils import util - -# handle input for pyton 2 vs 3 -try: - input = raw_input -except NameError: - input = input - -def windows(): - return platform.system() == "Windows" -def linux(): - return platform.system() == "Linux" -def macOS(): - return platform.system() == "Darwin" - -if windows(): - hfs_search_paths = ['C:\\Program Files\\Side Effects Software\\Houdini 18*'] -elif linux(): - hfs_search_paths = ['/opt/hfs18*'] -else: - hfs_search_paths = ['/Applications/Houdini/Houdini18*/Frameworks/Houdini.framework/Versions/Current/Resources'] - -def is_valid_install_dir(path): - if not os.path.isdir(path): - return False - - setup_script_path = os.path.join(path, 'houdini_setup') - if not os.path.isfile(setup_script_path): - return False - - test_dir = os.path.join(path, 'create_access_test') - try: - os.mkdir(test_dir) - except OSError: - print('No permission to create directories in {}. Run with root privileges to install here.'.format(path)) - return False - else: - os.rmdir(test_dir) - return True - -def query_agreement(install_dir): - print('hdRpr will be installed to "{}"'.format(install_dir)) - print('Do you agree? [y/n]') - while True: - try: - return util.strtobool(input()) - except ValueError: - print('yes or no') - -def install_package(install_dir, package, force=False): - if force or query_agreement(install_dir): - try: - tar_file = tarfile.open(package[0], 'r:gz') - tar_file.extractall(install_dir) - for file in tar_file.getnames(): - path = '{install_dir}/{file}'.format(install_dir=install_dir, file=file) - if not os.path.isdir(path): - print('-- Installing: ' + path) - print('Successfully installed') - except Exception as e: - print(e) - print('Could not install package. Try running with root privileges') - -def install_to_houdini_dir(hfs, package, force=False): - if macOS(): - hfs = os.path.abspath(os.path.join(hfs, os.pardir)) - install_package(hfs, package, force) - -def install_to_custom_dir(package): - print('Please enter Houdini\'s path (e.g. {}):'.format(hfs_search_paths[0])) - while True: - path = input() - if is_valid_install_dir(path): - install_to_houdini_dir(path, package, force=True) - break - print('Invalid path. Please try again') - -valid_targets = ['Houdini'] -package_pattern = re.compile(r'hdRpr-.*-(?P.*?)-.*-(?P.*?).tar.gz', re.VERBOSE) -def get_package_from_path(path): - match = package_pattern.match(path) - - target = match.group('target') - if target not in valid_targets: - print('"{}": unknown target. Skipping.'.format(path)) - return None - return (path, target) - -script_description = ( -''' -The script allows to easily install hdRpr across Windows, Ubuntu, and macOS. -To install hdRpr into specific location pass install_dir. -''') - -parser = argparse.ArgumentParser( - description=script_description, - formatter_class=argparse.RawDescriptionHelpFormatter) - -parser.add_argument('-i', '--install_dir', type=str, - help='Directory where hdRpr will be installed. Typically it\'s going to be Houdini installation directory') -parser.add_argument('-p', '--package_path', type=str, - help='Path to hdRpr package. If not set, the package will be automatically found in a script folder') - -args = parser.parse_args() - -if args.package_path: - package = get_package_from_path(args.package_path) - if not package: - exit() -else: - packages = [] - - for path in glob.glob('hdRpr*.tar.gz'): - if os.path.isfile(path): - package = get_package_from_path(path) - if package: - packages.append(package) - - num_packages = len(packages) - if num_packages == 0: - print('Could not find any packages to install') - exit() - else: - if num_packages > 1: - print('Found few packages: {}'.format(packages)) - package = packages[0] - -print('Installing "{}"'.format(package[0])) - -if args.install_dir: - install_package(args.install_dir, package) -elif 'HFS' in os.environ: - install_to_houdini_dir(os.environ['HFS'], package) -else: - install_variants = set() - for search_path in hfs_search_paths: - for path in glob.glob(search_path): - if os.path.islink(path): - path = os.path.realpath(path) - if is_valid_install_dir(path): - install_variants.add(path) - - install_variants = list(install_variants) - - if len(install_variants) == 0: - print('Could not find any Houdini installation directories.') - install_to_custom_dir(package) - elif len(install_variants) == 1: - install_to_houdini_dir(install_variants[0], package) - else: - print('Few Houdini installation has been found. Select the desired one.') - for i, path in enumerate(install_variants, start=1): - print('{}. "{}"'.format(i, path)) - custom_path_idx = len(install_variants) - print('{}. Custom path'.format(custom_path_idx)) - print('Enter number.') - try: - idx = int(input()) - 1 - if idx == custom_path_idx: - install_to_custom_dir(package) - else: - install_to_houdini_dir(install_variants[idx], package, force=True) - except (ValueError, IndexError) as e: - print('Installation canceled: incorrect number.') diff --git a/pxr/imaging/plugin/hdRpr/pch.h b/pxr/imaging/plugin/hdRpr/pch.h deleted file mode 100644 index 3eca6f958..000000000 --- a/pxr/imaging/plugin/hdRpr/pch.h +++ /dev/null @@ -1,136 +0,0 @@ -/************************************************************************ -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. -************************************************************************/ - -// -// Copyright 2017 Pixar -// -// Licensed under the Apache License, Version 2.0 (the "Apache License") -// with the following modification; you may not use this file except in -// compliance with the Apache License and the following modification to it: -// Section 6. Trademarks. is deleted and replaced with: -// -// 6. Trademarks. This License does not grant permission to use the trade -// names, trademarks, service marks, or product names of the Licensor -// and its affiliates, except as required to comply with Section 4(c) of -// the License and to reproduce the content of the NOTICE file. -// -// You may obtain a copy of the Apache License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the Apache License with the above modification is -// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the Apache License for the specific -// language governing permissions and limitations under the Apache License. -// -// WARNING: THIS FILE IS GENERATED. DO NOT EDIT. -// - -#define TF_MAX_ARITY 7 -#include "pxr/pxr.h" -#include "pxr/base/arch/defines.h" -#include "boostIncludePath.h" - -#if defined(ARCH_OS_WINDOWS) -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN -# endif - -# include BOOST_INCLUDE_PATH(preprocessor/variadic/size.hpp) -# include BOOST_INCLUDE_PATH(vmd/is_empty.hpp) -# include BOOST_INCLUDE_PATH(vmd/is_tuple.hpp) - -#endif // defined(ARCH_OS_WINDOWS) - -#include BOOST_INCLUDE_PATH(any.hpp) -#include BOOST_INCLUDE_PATH(call_traits.hpp) -#include BOOST_INCLUDE_PATH(function.hpp) -#include BOOST_INCLUDE_PATH(functional/hash_fwd.hpp) -#include BOOST_INCLUDE_PATH(intrusive_ptr.hpp) -#include BOOST_INCLUDE_PATH(mpl/empty.hpp) -#include BOOST_INCLUDE_PATH(mpl/front.hpp) -#include BOOST_INCLUDE_PATH(mpl/if.hpp) -#include BOOST_INCLUDE_PATH(mpl/pop_front.hpp) -#include BOOST_INCLUDE_PATH(mpl/remove.hpp) -#include BOOST_INCLUDE_PATH(mpl/vector.hpp) -#include BOOST_INCLUDE_PATH(noncopyable.hpp) -#include BOOST_INCLUDE_PATH(operators.hpp) -#include BOOST_INCLUDE_PATH(optional.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/arithmetic/add.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/arithmetic/inc.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/arithmetic/sub.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/cat.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/comparison/equal.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/control/expr_iif.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/control/iif.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/facilities/expand.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/logical/and.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/logical/not.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/punctuation/comma.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/punctuation/comma_if.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/punctuation/paren.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/repetition/repeat.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/seq/filter.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/seq/for_each.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/seq/for_each_i.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/seq/push_back.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/seq/size.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/stringize.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/tuple/eat.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/tuple/elem.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/tuple/to_list.hpp) -#include BOOST_INCLUDE_PATH(preprocessor/tuple/to_seq.hpp) -#include BOOST_INCLUDE_PATH(scoped_ptr.hpp) -#include BOOST_INCLUDE_PATH(shared_ptr.hpp) -#include BOOST_INCLUDE_PATH(type_traits/is_base_of.hpp) -#include BOOST_INCLUDE_PATH(type_traits/is_const.hpp) -#include BOOST_INCLUDE_PATH(type_traits/is_convertible.hpp) -#include BOOST_INCLUDE_PATH(type_traits/is_enum.hpp) -#include BOOST_INCLUDE_PATH(type_traits/is_same.hpp) -#include BOOST_INCLUDE_PATH(unordered_map.hpp) -#include BOOST_INCLUDE_PATH(utility/enable_if.hpp) -#include BOOST_INCLUDE_PATH(weak_ptr.hpp) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include diff --git a/pxr/imaging/plugin/hdRpr/plugInfo.json b/pxr/imaging/plugin/hdRpr/plugInfo.json index 2811c833e..5fae003f0 100644 --- a/pxr/imaging/plugin/hdRpr/plugInfo.json +++ b/pxr/imaging/plugin/hdRpr/plugInfo.json @@ -9,6 +9,14 @@ ], "displayName": "RPR", "priority": 99 + }, + "HdRprNdrDiscoveryPlugin": { + "bases": ["NdrDiscoveryPlugin"], + "displayName": "RPR Node Discovery" + }, + "HdRprNdrParserPlugin": { + "bases": ["NdrParserPlugin"], + "displayName": "RPR Node Parser" } } }, diff --git a/pxr/imaging/plugin/hdRpr/points.cpp b/pxr/imaging/plugin/hdRpr/points.cpp index df10f2211..fdcd7166f 100644 --- a/pxr/imaging/plugin/hdRpr/points.cpp +++ b/pxr/imaging/plugin/hdRpr/points.cpp @@ -15,8 +15,8 @@ limitations under the License. #include "rprApi.h" #include "primvarUtil.h" #include "renderParam.h" -#include "materialAdapter.h" +#include "pxr/imaging/rprUsd/debugCodes.h" #include "pxr/imaging/hd/extComputationUtils.h" #include "pxr/usdImaging/usdImaging/implicitSurfaceMeshUtils.h" @@ -106,7 +106,7 @@ void HdRprPoints::Sync( HdRprGeometrySettings geomSettings; geomSettings.visibilityMask = kVisibleAll; HdRprFillPrimvarDescsPerInterpolation(sceneDelegate, id, &primvarDescsPerInterpolation); - HdRprParseGeometrySettings(sceneDelegate, id, primvarDescsPerInterpolation[HdInterpolationConstant], &geomSettings); + HdRprParseGeometrySettings(sceneDelegate, id, primvarDescsPerInterpolation, &geomSettings); if (m_subdivisionLevel != geomSettings.subdivisionLevel) { m_subdivisionLevel = geomSettings.subdivisionLevel; @@ -128,8 +128,11 @@ void HdRprPoints::Sync( if (m_colorsInterpolation == HdInterpolationVertex) { m_material = rprApi->CreatePointsMaterial(m_colors); } else if (!m_colors.empty()) { - auto matAdapter = MaterialAdapter(EMaterialType::COLOR, MaterialParams{{HdRprMaterialTokens->color, VtValue(m_colors[0])}}); - m_material = rprApi->CreateMaterial(matAdapter); + m_material = rprApi->CreateDiffuseMaterial(m_colors[0]); + } + + if (m_material && RprUsdIsLeakCheckEnabled()) { + rprApi->SetName(m_material, id.GetText()); } } @@ -148,6 +151,10 @@ void HdRprPoints::Sync( rprApi->SetMeshRefineLevel(m_prototypeMesh, m_subdivisionLevel); dirtyPrototypeMesh = true; + + if (RprUsdIsLeakCheckEnabled()) { + rprApi->SetName(m_prototypeMesh, id.GetText()); + } } if (m_instances.size() > m_points.size()) { @@ -161,6 +168,10 @@ void HdRprPoints::Sync( if (auto instance = rprApi->CreateMeshInstance(m_prototypeMesh)) { rprApi->SetMeshId(instance, i); m_instances.push_back(instance); + + if (RprUsdIsLeakCheckEnabled()) { + rprApi->SetName(instance, id.GetText()); + } } } @@ -199,7 +210,7 @@ void HdRprPoints::Sync( if (dirtyDisplayColors || dirtyInstances) { for (size_t i = 0; i < m_instances.size(); ++i) { - rprApi->SetMeshMaterial(m_instances[i], m_material, false, false); + rprApi->SetMeshMaterial(m_instances[i], m_material, false); } } diff --git a/pxr/imaging/plugin/hdRpr/points.h b/pxr/imaging/plugin/hdRpr/points.h index 8ef6a304f..032a98f43 100644 --- a/pxr/imaging/plugin/hdRpr/points.h +++ b/pxr/imaging/plugin/hdRpr/points.h @@ -22,7 +22,7 @@ namespace rpr { class Shape; } PXR_NAMESPACE_OPEN_SCOPE -struct HdRprApiMaterial; +class RprUsdMaterial; class HdRprPoints : public HdPoints { public: @@ -48,7 +48,7 @@ class HdRprPoints : public HdPoints { private: rpr::Shape* m_prototypeMesh = nullptr; std::vector m_instances; - HdRprApiMaterial* m_material = nullptr; + RprUsdMaterial* m_material = nullptr; GfMatrix4f m_transform; diff --git a/pxr/imaging/plugin/hdRpr/primvarUtil.cpp b/pxr/imaging/plugin/hdRpr/primvarUtil.cpp index c02871f6d..c08d1c393 100644 --- a/pxr/imaging/plugin/hdRpr/primvarUtil.cpp +++ b/pxr/imaging/plugin/hdRpr/primvarUtil.cpp @@ -93,7 +93,17 @@ void HdRprFillPrimvarDescsPerInterpolation( HdInterpolationInstance, }; for (auto& interpolation : interpolations) { - primvarDescsPerInterpolation->emplace(interpolation, sceneDelegate->GetPrimvarDescriptors(id, interpolation)); + auto primvarDescs = sceneDelegate->GetPrimvarDescriptors(id, interpolation); + if (!primvarDescs.empty()) { + primvarDescsPerInterpolation->emplace(interpolation, std::move(primvarDescs)); + } + } + + // If primitive has no primvars, + // insert dummy entry so that the next time user calls this function, + // it will not rerun sceneDelegate->GetPrimvarDescriptors which is quite costly + if (primvarDescsPerInterpolation->empty()) { + primvarDescsPerInterpolation->emplace(HdInterpolationCount, HdPrimvarDescriptorVector{}); } } diff --git a/pxr/imaging/plugin/hdRpr/primvarUtil.h b/pxr/imaging/plugin/hdRpr/primvarUtil.h index d6b5fcab9..a92d41ef0 100644 --- a/pxr/imaging/plugin/hdRpr/primvarUtil.h +++ b/pxr/imaging/plugin/hdRpr/primvarUtil.h @@ -45,6 +45,18 @@ void HdRprParseGeometrySettings( HdPrimvarDescriptorVector const& constantPrimvarDescs, HdRprGeometrySettings* geomSettings); +inline void HdRprParseGeometrySettings( + HdSceneDelegate* sceneDelegate, SdfPath const& id, + std::map const& primvarDescsPerInterpolation, + HdRprGeometrySettings* geomSettings) { + auto constantPrimvarDescIt = primvarDescsPerInterpolation.find(HdInterpolationConstant); + if (constantPrimvarDescIt == primvarDescsPerInterpolation.end()) { + return; + } + + HdRprParseGeometrySettings(sceneDelegate, id, constantPrimvarDescIt->second, geomSettings); +} + template bool HdRprGetConstantPrimvar(TfToken const& name, HdSceneDelegate* sceneDelegate, SdfPath const& id, T* out_value) { auto value = sceneDelegate->Get(id, name); diff --git a/pxr/imaging/plugin/hdRpr/python/commonSettings.py b/pxr/imaging/plugin/hdRpr/python/commonSettings.py index 03cca88b5..c0cd479f8 100644 --- a/pxr/imaging/plugin/hdRpr/python/commonSettings.py +++ b/pxr/imaging/plugin/hdRpr/python/commonSettings.py @@ -73,3 +73,22 @@ 'defaultValue': True } ] + +class SettingValue(object): + def __init__(self, key, ui_name=None, disabled_platform=None): + self._key = key + self._ui_name = ui_name + self.disabled_platform = disabled_platform + + def __eq__(self, obj): + return self._key == obj + + def __ne__(self, obj): + return not self == obj + + def get_ui_name(self): + return self._ui_name if self._ui_name else self._key + + def get_key(self): + return self._key.replace(' ', '') + diff --git a/pxr/imaging/plugin/hdRpr/python/generateFiles.py b/pxr/imaging/plugin/hdRpr/python/generateFiles.py index 6f45a97ae..3a36eee70 100644 --- a/pxr/imaging/plugin/hdRpr/python/generateFiles.py +++ b/pxr/imaging/plugin/hdRpr/python/generateFiles.py @@ -11,7 +11,6 @@ # import subprocess import argparse -import sys import os if __name__ == "__main__": @@ -19,15 +18,13 @@ p.add_argument('scripts_dir', help="Directory with scripts") p.add_argument("install", help="The install root for generated files.") p.add_argument("--houdini_root", help="The install root for generated files.") + p.add_argument('--python_exe', help="Directory with scripts") args = p.parse_args() generate_ds_files = [] if args.houdini_root: - # Generator of DialogScript files requires access to houdini's python modules generate_ds_files = ['--generate_ds_files'] - os.environ['PATH'] = os.environ.get('PATH', '') + os.pathsep + os.path.join(args.houdini_root, 'bin') - os.environ['PYTHONPATH'] = os.environ.get('PYTHONPATH', '') + os.pathsep + os.path.join(os.path.join(args.houdini_root, 'houdini'), 'python2.7libs') - subprocess.check_call([sys.executable, os.path.join(args.scripts_dir, 'generateLightSettingFiles.py'), args.install] + generate_ds_files) - subprocess.check_call([sys.executable, os.path.join(args.scripts_dir, 'generateRenderSettingFiles.py'), args.install] + generate_ds_files) - subprocess.check_call([sys.executable, os.path.join(args.scripts_dir, 'generateGeometrySettingFiles.py'), args.install] + generate_ds_files) + subprocess.check_call([args.python_exe, os.path.join(args.scripts_dir, 'generateLightSettingFiles.py'), args.install] + generate_ds_files) + subprocess.check_call([args.python_exe, os.path.join(args.scripts_dir, 'generateGeometrySettingFiles.py'), args.install] + generate_ds_files) + subprocess.check_call([args.python_exe, os.path.join(args.scripts_dir, 'generateRenderSettingFiles.py'), args.install] + generate_ds_files) diff --git a/pxr/imaging/plugin/hdRpr/python/generateRenderSettingFiles.py b/pxr/imaging/plugin/hdRpr/python/generateRenderSettingFiles.py index 8f2016970..e460333ba 100644 --- a/pxr/imaging/plugin/hdRpr/python/generateRenderSettingFiles.py +++ b/pxr/imaging/plugin/hdRpr/python/generateRenderSettingFiles.py @@ -15,41 +15,110 @@ import platform from houdiniDsGenerator import generate_houdini_ds +from commonSettings import SettingValue + +def get_render_setting(render_setting_categories, category_name, name): + for category in render_setting_categories: + if category['name'] != category_name: + continue + + for setting in category['settings']: + if setting['name'] == name: + return setting + +def hidewhen_render_quality(operator, quality, render_setting_categories=None): + if operator in ('==', '!='): + return 'renderQuality {} "{}"'.format(operator, quality) + elif operator == '<': + render_quality = get_render_setting(render_setting_categories, 'RenderQuality', 'renderQuality') + values = render_quality['values'] + + hidewhen = [] + for value in values: + if value == quality: + break + hidewhen.append(hidewhen_render_quality('==', value.get_key())) + + return hidewhen + else: + raise ValueError('Operator "{}" not implemented'.format(operator)) + +def hidewhen_hybrid(render_setting_categories): + return hidewhen_render_quality('<', 'Full', render_setting_categories) + +def hidewhen_not_northstar(render_setting_categories): + return hidewhen_render_quality('!=', 'Northstar', render_setting_categories) + +HYBRID_DISABLED_PLATFORM = 'Darwin' render_setting_categories = [ { 'name': 'RenderQuality', - 'disabled_platform': ['Darwin'], 'settings': [ { 'name': 'renderQuality', 'ui_name': 'Render Quality', 'help': 'Render restart might be required', - 'defaultValue': 3, + 'defaultValue': 'Northstar', 'values': [ - "Low", - "Medium", - "High", - "Full" + SettingValue('Low', disabled_platform=HYBRID_DISABLED_PLATFORM), + SettingValue('Medium', disabled_platform=HYBRID_DISABLED_PLATFORM), + SettingValue('High', disabled_platform=HYBRID_DISABLED_PLATFORM), + SettingValue('Full', 'Full (Legacy)'), + SettingValue('Northstar', 'Full') ] } ] }, + { + 'name': 'RenderMode', + 'settings': [ + { + 'name': 'renderMode', + 'ui_name': 'Render Mode', + 'defaultValue': 'Global Illumination', + 'values': [ + SettingValue('Global Illumination'), + SettingValue('Direct Illumination'), + SettingValue('Wireframe'), + SettingValue('Material Index'), + SettingValue('Position'), + SettingValue('Normal'), + SettingValue('Texcoord'), + SettingValue('Ambient Occlusion'), + SettingValue('Diffuse') + ] + }, + { + 'name': 'aoRadius', + 'ui_name': 'Ambient Occlusion Radius', + 'defaultValue': 1.0, + 'minValue': 0.0, + 'maxValue': 100.0, + 'houdini': { + 'hidewhen': 'renderMode != "AmbientOcclusion"' + } + } + ], + 'houdini': { + 'hidewhen': hidewhen_hybrid + } + }, { 'name': 'Device', 'houdini': { - 'hidewhen': 'renderQuality != 3' + 'hidewhen': hidewhen_hybrid }, 'settings': [ { 'name': 'renderDevice', 'ui_name': 'Render Device', 'help': 'Restart required.', - 'defaultValue': 1, + 'defaultValue': 'GPU', 'values': [ - "CPU", - "GPU", - # "CPU+GPU" + SettingValue('CPU'), + SettingValue('GPU'), + # SettingValue('CPU+GPU') ] } ] @@ -57,7 +126,7 @@ { 'name': 'Denoise', 'houdini': { - 'hidewhen': 'renderQuality < 2' + 'hidewhen': lambda settings: hidewhen_render_quality('<', 'High', settings) }, 'settings': [ { @@ -75,7 +144,7 @@ { 'name': 'Sampling', 'houdini': { - 'hidewhen': 'renderQuality == 0' + 'hidewhen': lambda settings: hidewhen_render_quality('==', 'Low', settings) }, 'settings': [ { @@ -91,7 +160,7 @@ { 'name': 'AdaptiveSampling', 'houdini': { - 'hidewhen': 'renderQuality != 3' + 'hidewhen': hidewhen_hybrid }, 'settings': [ { @@ -115,7 +184,7 @@ { 'name': 'Quality', 'houdini': { - 'hidewhen': 'renderQuality != 3' + 'hidewhen': hidewhen_hybrid }, 'settings': [ { @@ -186,7 +255,12 @@ 'defaultValue': 0.0, 'minValue': 0.0, 'maxValue': 1e6 - }, + } + ] + }, + { + 'name': 'InteractiveQuality', + 'settings': [ { 'name': 'interactiveMaxRayDepth', 'ui_name': 'Interactive Max Ray Depth', @@ -194,6 +268,17 @@ 'defaultValue': 2, 'minValue': 1, 'maxValue': 50 + }, + { + 'name': 'interactiveResolutionDownscale', + 'ui_name': 'Interactive Resolution Downscale', + 'help': 'Controls how much rendering resolution is downscaled in interactive mode. Formula: resolution / (2 ^ downscale). E.g. downscale==2 will give you 4 times smaller rendering resolution.', + 'defaultValue': 3, + 'minValue': 0, + 'maxValue': 10, + 'houdini': { + 'hidewhen': hidewhen_not_northstar + } } ] }, @@ -207,8 +292,8 @@ 'defaultValue': False }, { - 'name': 'tonemapExposure', - 'ui_name': 'Tone Mapping Exposure', + 'name': 'tonemapExposureTime', + 'ui_name': 'Tone Mapping Exposure Time', 'help': 'Film exposure time', 'defaultValue': 0.125, 'minValue': 0.0, @@ -252,6 +337,71 @@ } ] }, + { + 'name': 'Alpha', + 'settings': [ + { + 'name': 'enableAlpha', + 'ui_name': 'Enable Color Alpha', + 'defaultValue': True, + 'houdini': { + 'hidewhen': hidewhen_hybrid + } + } + ] + }, + { + 'name': 'MotionBlur', + 'settings': [ + { + 'name': 'enableBeautyMotionBlur', + 'ui_name': 'Enable Beaty 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': { + 'hidewhen': hidewhen_not_northstar + } + } + ] + }, + { + 'name': 'OCIO', + 'settings': [ + { + 'name': 'ocioConfigPath', + 'ui_name': 'OpenColorIO Config Path', + 'defaultValue': '', + 'c_type': 'std::string', + 'help': 'The file path of the OpenColorIO config file to be used. Overrides any value specified in OCIO environment variable.', + 'houdini': { + 'type': 'file', + 'hidewhen': hidewhen_not_northstar + } + }, + { + 'name': 'ocioRenderingColorSpace', + 'ui_name': 'OpenColorIO Rendering Color Space', + 'defaultValue': '', + 'c_type': 'std::string', + 'houdini': { + 'hidewhen': hidewhen_not_northstar + } + } + ] + }, + { + 'name': 'Seed', + 'settings': [ + { + 'name': 'uniformSeed', + 'ui_name': 'Use Uniform Seed', + 'defaultValue': True, + 'houdini': { + 'hidewhen': 'renderQuality < 3' + } + } + ] + }, { 'name': 'UsdNativeCamera', 'settings': [ @@ -264,6 +414,16 @@ 'defaultValue': False, }, ] + }, + { + 'name': 'RprExport', + 'settings': [ + { + 'name': 'rprExportPath', + 'defaultValue': '', + 'c_type': 'std::string' + } + ] } ] @@ -317,21 +477,15 @@ class HdRprConfig {{ {rs_variables_declaration} PrefData(); - ~PrefData(); void SetDefault(); - bool Load(); - void Save(); - bool IsValid(); }}; PrefData m_prefData; uint32_t m_dirtyFlags = DirtyAll; int m_lastRenderSettingsVersion = -1; - - constexpr static const char* k_rprPreferenceFilename = "hdRprPreferences.dat"; }}; PXR_NAMESPACE_CLOSE_SCOPE @@ -351,8 +505,11 @@ class HdRprConfig {{ TF_DEFINE_PUBLIC_TOKENS(HdRprRenderSettingsTokens, HDRPR_RENDER_SETTINGS_TOKENS); TF_DEFINE_PRIVATE_TOKENS(_tokens, ((houdiniInteractive, "houdini:interactive")) + ((rprInteractive, "rpr:interactive")) ); +{rs_public_token_definitions} + namespace {{ {rs_range_definitions} @@ -387,8 +544,13 @@ class HdRprConfig {{ return defaultValue; }}; - auto interactiveMode = renderDelegate->GetRenderSetting(_tokens->houdiniInteractive, "normal"); - SetInteractiveMode(interactiveMode != "normal"); + bool interactiveMode = getBoolSetting(_tokens->rprInteractive, false); + + if (renderDelegate->GetRenderSetting(_tokens->houdiniInteractive, "normal") != "normal") {{ + interactiveMode = true; + }} + + SetInteractiveMode(interactiveMode); {rs_sync} }} @@ -397,7 +559,6 @@ class HdRprConfig {{ void HdRprConfig::SetInteractiveMode(bool enable) {{ if (m_prefData.enableInteractive != enable) {{ m_prefData.enableInteractive = enable; - m_prefData.Save(); m_dirtyFlags |= DirtyInteractiveMode; }} }} @@ -420,45 +581,8 @@ class HdRprConfig {{ m_dirtyFlags = Clean; }} -bool HdRprConfig::PrefData::Load() {{ -#ifdef ENABLE_PREFERENCES_FILE - std::string appDataDir = HdRprApi::GetAppDataPath(); - std::string rprPreferencePath = (appDataDir.empty()) ? k_rprPreferenceFilename : (appDataDir + ARCH_PATH_SEP) + k_rprPreferenceFilename; - - if (FILE* f = fopen(rprPreferencePath.c_str(), "rb")) {{ - if (!fread(this, sizeof(PrefData), 1, f)) {{ - TF_CODING_ERROR("Fail to read rpr preferences dat file"); - }} - fclose(f); - return IsValid(); - }} -#endif // ENABLE_PREFERENCES_FILE - - return false; -}} - -void HdRprConfig::PrefData::Save() {{ -#ifdef ENABLE_PREFERENCES_FILE - std::string appDataDir = HdRprApi::GetAppDataPath(); - std::string rprPreferencePath = (appDataDir.empty()) ? k_rprPreferenceFilename : (appDataDir + ARCH_PATH_SEP) + k_rprPreferenceFilename; - - if (FILE* f = fopen(rprPreferencePath.c_str(), "wb")) {{ - if (!fwrite(this, sizeof(PrefData), 1, f)) {{ - TF_CODING_ERROR("Fail to write rpr preferences dat file"); - }} - fclose(f); - }} -#endif // ENABLE_PREFERENCES_FILE -}} - HdRprConfig::PrefData::PrefData() {{ - if (!Load()) {{ - SetDefault(); - }} -}} - -HdRprConfig::PrefData::~PrefData() {{ - Save(); + SetDefault(); }} void HdRprConfig::PrefData::SetDefault() {{ @@ -478,6 +602,7 @@ class HdRprConfig {{ dirty_flags_offset = 1 + rs_public_token_definitions = '' rs_tokens_declaration = '#define HDRPR_RENDER_SETTINGS_TOKENS \\\n' rs_category_dirty_flags = '' rs_get_set_method_declarations = '' @@ -491,9 +616,6 @@ class HdRprConfig {{ rs_validate_values = '' for category in render_setting_categories: disabled_category = False - if 'disabled_platform' in category: - if platform.system() in category['disabled_platform']: - disabled_category = True category_name = category['name'] dirty_flag = 'Dirty{}'.format(category_name) @@ -508,33 +630,48 @@ class HdRprConfig {{ default_value = setting['defaultValue'] - c_type_str = type(default_value).__name__ - if c_type_str == 'str': - c_type_str = 'TfToken' + if 'c_type' in setting: + c_type_str = setting['c_type'] + else: + c_type_str = type(default_value).__name__ + if c_type_str == 'str': + c_type_str = 'TfToken' type_str = c_type_str if 'values' in setting: - setting['minValue'] = 0 - setting['maxValue'] = len(setting['values']) - 1 - rs_mapped_values_enum += 'enum {name_title}Type {{\n'.format(name_title=name_title) + value_tokens_list_name = '__{}Tokens'.format(name_title) + value_tokens_name = 'HdRpr{}Tokens'.format(name_title) + + rs_mapped_values_enum += '#define ' + value_tokens_list_name for value in setting['values']: - rs_mapped_values_enum += ' k{name_title}{value},\n'.format(name_title=name_title, value=value) - rs_mapped_values_enum += '};\n' - type_str = '{name_title}Type'.format(name_title=name_title) + rs_mapped_values_enum += ' ({})'.format(value.get_key()) + rs_mapped_values_enum += '\n' + + rs_mapped_values_enum += 'TF_DECLARE_PUBLIC_TOKENS({}, {});\n\n'.format(value_tokens_name, value_tokens_list_name) + rs_public_token_definitions += 'TF_DEFINE_PUBLIC_TOKENS({}, {});\n'.format(value_tokens_name, value_tokens_list_name) + + type_str = 'TfToken' + c_type_str = type_str + default_value = next(value for value in setting['values'] if value == default_value) rs_get_set_method_declarations += ' void Set{}({} {});\n'.format(name_title, c_type_str, name) - rs_get_set_method_declarations += ' {} Get{}() const {{ return m_prefData.{}; }}\n\n'.format(type_str, name_title, name) + rs_get_set_method_declarations += ' {} const& Get{}() const {{ return m_prefData.{}; }}\n\n'.format(type_str, name_title, name) rs_variables_declaration += ' {} {};\n'.format(type_str, name) - value_str = str(default_value) if isinstance(default_value, bool): - value_str = value_str.lower() rs_sync += ' Set{name_title}(getBoolSetting(HdRprRenderSettingsTokens->{name}, k{name_title}Default));\n'.format(name_title=name_title, name=name) else: - rs_sync += ' Set{name_title}(renderDelegate->GetRenderSetting(HdRprRenderSettingsTokens->{name}, {type}(k{name_title}Default)));\n'.format(name_title=name_title, name=name, type=c_type_str) + rs_sync += ' Set{name_title}(renderDelegate->GetRenderSetting(HdRprRenderSettingsTokens->{name}, k{name_title}Default));\n'.format(name_title=name_title, name=name) + + if 'values' in setting: + rs_range_definitions += '#define k{name_title}Default {value_tokens_name}->{value}'.format(name_title=name_title, value_tokens_name=value_tokens_name, value=default_value.get_key()) + else: + value_str = str(default_value) + if isinstance(default_value, bool): + value_str = value_str.lower() + rs_range_definitions += 'const {type} k{name_title}Default = {type}({value});\n'.format(type=type_str, name_title=name_title, value=value_str) - rs_range_definitions += 'const {type} k{name_title}Default = {type}({value});\n'.format(type=type_str, name_title=name_title, value=value_str) set_validation = '' if 'minValue' in setting or 'maxValue' in setting: rs_validate_values += ' ' @@ -550,6 +687,10 @@ class HdRprConfig {{ rs_validate_values += '\n' rs_range_definitions += '\n' + if 'values' in setting: + value_range = value_tokens_name + '->allTokens' + set_validation += ' if (std::find({range}.begin(), {range}.end(), {name}) == {range}.end()) return;\n'.format(range=value_range, name=name) + if 'ui_name' in setting: rs_list_initialization += ' settingDescs.push_back({{"{}", HdRprRenderSettingsTokens->{}, VtValue(k{}Default)}});\n'.format(setting['ui_name'], name, name_title) @@ -561,16 +702,15 @@ class HdRprConfig {{ void HdRprConfig::Set{name_title}({c_type} {name}) {{ {set_validation} if (m_prefData.{name} != {name}) {{ - m_prefData.{name} = {type}({name}); - m_prefData.Save(); + m_prefData.{name} = {name}; m_dirtyFlags |= {dirty_flag}; }} }} -''').format(name_title=name_title, c_type=c_type_str, type=type_str, name=name, dirty_flag=dirty_flag, set_validation=set_validation) +''').format(name_title=name_title, c_type=c_type_str, name=name, dirty_flag=dirty_flag, set_validation=set_validation) rs_set_default_values += ' {name} = k{name_title}Default;\n'.format(name=name, name_title=name_title) - rs_tokens_declaration += '\nTF_DECLARE_PUBLIC_TOKENS(HdRprRenderSettingsTokens, HDRPR_RENDER_SETTINGS_TOKENS);' + rs_tokens_declaration += '\nTF_DECLARE_PUBLIC_TOKENS(HdRprRenderSettingsTokens, HDRPR_RENDER_SETTINGS_TOKENS);\n' header_dst_path = os.path.join(install_path, 'config.h') header_file = open(header_dst_path, 'w') @@ -584,6 +724,7 @@ class HdRprConfig {{ cpp_dst_path = os.path.join(install_path, 'config.cpp') cpp_file = open(cpp_dst_path, 'w') cpp_file.write(cpp_template.format( + rs_public_token_definitions=rs_public_token_definitions, rs_range_definitions=rs_range_definitions, rs_list_initialization=rs_list_initialization, rs_sync=rs_sync, diff --git a/pxr/imaging/plugin/hdRpr/python/houdiniDsGenerator.py b/pxr/imaging/plugin/hdRpr/python/houdiniDsGenerator.py index 4d1e48d65..76236d15c 100644 --- a/pxr/imaging/plugin/hdRpr/python/houdiniDsGenerator.py +++ b/pxr/imaging/plugin/hdRpr/python/houdiniDsGenerator.py @@ -51,9 +51,6 @@ def generate_houdini_ds(install_path, ds_name, settings): for category in settings: disabled_category = False - if 'disabled_platform' in category: - if platform.system() in category['disabled_platform']: - disabled_category = True category_name = category['name'] @@ -61,15 +58,23 @@ def generate_houdini_ds(install_path, ds_name, settings): if not 'ui_name' in setting: continue - category_hidewhen = None + houdini_hidewhen_conditions = [] + def add_hidewhen_condition(condition): + if condition and callable(condition): + condition = condition(settings) + if condition: + if isinstance(condition, str): + houdini_hidewhen_conditions.append(condition) + elif isinstance(condition, list): + houdini_hidewhen_conditions.extend(condition); + if 'houdini' in category: - category_hidewhen = category['houdini'].get('hidewhen') + add_hidewhen_condition(category['houdini'].get('hidewhen')) houdini_settings = setting.get('houdini', {}) houdini_param_label = setting['ui_name'] - houdini_hidewhen_conditions = [] - if 'hidewhen' in houdini_settings or category_hidewhen: - houdini_hidewhen_conditions.append(houdini_settings.get('hidewhen', category_hidewhen)) + add_hidewhen_condition(houdini_settings.get('hidewhen')) + houdini_hidewhen = '' if houdini_hidewhen_conditions: houdini_hidewhen += 'hidewhen "' @@ -77,7 +82,7 @@ def generate_houdini_ds(install_path, ds_name, settings): houdini_hidewhen += '{{ {} }} '.format(condition) houdini_hidewhen += '"' - def CreateHoudiniParam(name, label, htype, default, values=[], hints=[], tags=[], disablewhen_conditions=[], size=None, valid_range=None, help_msg=None): + def CreateHoudiniParam(name, label, htype, default, values=[], tags=[], disablewhen_conditions=[], size=None, valid_range=None, help_msg=None): param = 'parm {\n' param += ' name "{}"\n'.format(hou.encode(name)) param += ' label "{}"\n'.format(label) @@ -88,13 +93,7 @@ def CreateHoudiniParam(name, label, htype, default, values=[], hints=[], tags=[] param += ' parmtag {{ {} }}\n'.format(tag) if values: param += ' menu {\n' - for value in values: - param += ' "{}" "{}"\n'.format(value[0], value[1]) - param += ' }\n' - if hints: - param += ' menureplace {\n' - for hint in hints: - param += ' R"({})" "{}"\n'.format(hint[0], hint[1]) + param += ' ' + values + '\n' param += ' }\n' if disabled_category: param += ' invisible\n' @@ -117,12 +116,12 @@ def CreateHoudiniParam(name, label, htype, default, values=[], hints=[], tags=[] control_param_name = hou.encode(name + '_control') - render_param_values = [] + render_param_values = None default_value = setting['defaultValue'] c_type_str = type(default_value).__name__ controlled_type = c_type_str if c_type_str == 'str': - c_type_str = 'TfToken' + c_type_str = 'string' controlled_type = 'string' render_param_type = c_type_str render_param_default = default_value @@ -130,9 +129,34 @@ def CreateHoudiniParam(name, label, htype, default, values=[], hints=[], tags=[] render_param_type = 'toggle' render_param_default = 1 if default_value else 0 elif 'values' in setting: - render_param_type = 'ordinal' + default_value = next(value for value in setting['values'] if value == default_value) + render_param_default = '"{}"'.format(default_value.get_key()) + render_param_type = 'string' + c_type_str = 'token' + + is_values_constant = True for value in setting['values']: - render_param_values.append((len(render_param_values), value)) + if value.disabled_platform: + is_values_constant = False + break + + render_param_values = '' + if is_values_constant: + for value in setting['values']: + render_param_values += '"{}" "{}"\n'.format(value.get_key(), value.get_ui_name()) + else: + render_param_values += '[ "import platform" ]\n' + render_param_values += '[ "menu_values = []" ]\n' + for value in setting['values']: + expression = 'menu_values.extend([\\"{}\\", \\"{}\\"])'.format(value.get_key(), value.get_ui_name()) + if value.disabled_platform: + expression = 'if platform.system() != \\"{}\\": {}'.format(value.disabled_platform, expression) + render_param_values += '[ "{}" ]\n'.format(expression) + render_param_values += '[ "return menu_values" ]\n'.format(expression) + render_param_values += 'language python\n' + + if 'type' in houdini_settings: + render_param_type = houdini_settings['type'] render_param_range = None if 'minValue' in setting and 'maxValue' in setting and not 'values' in setting: @@ -145,7 +169,6 @@ def CreateHoudiniParam(name, label, htype, default, values=[], hints=[], tags=[] hidewhen=houdini_hidewhen) houdini_params += CreateHoudiniParam(name, houdini_param_label, render_param_type, render_param_default, values=render_param_values, - hints=setting['hints'] if 'hints' in setting else [], tags=[ '"spare_category" "{}"'.format(category_name), '"uiscope" "viewport"', diff --git a/pxr/imaging/plugin/hdRpr/python/rpr.py b/pxr/imaging/plugin/hdRpr/python/rpr.py deleted file mode 100644 index 9ffb2fd42..000000000 --- a/pxr/imaging/plugin/hdRpr/python/rpr.py +++ /dev/null @@ -1,119 +0,0 @@ -# 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. -# -from pxr import Tf -from pxr.Plug import Registry -from pxr.Usdviewq.plugin import PluginContainer - -from ctypes import cdll, c_int -from ctypes.util import find_library - -import os - -def getRprPath(_pathCache=[None]): - if _pathCache[0]: - return _pathCache[0] - - rprPluginType = Registry.FindTypeByName('HdRprPlugin') - plugin = Registry().GetPluginForType(rprPluginType) - if plugin and plugin.path: - _pathCache[0] = plugin.path - return _pathCache[0] - -def reemitStage(usdviewApi): - usdviewApi._UsdviewApi__appController._reopenStage() - usdviewApi._UsdviewApi__appController._rendererPluginChanged('HdRprPlugin') - -def setRenderDevice(usdviewApi, renderDeviceId): - rprPath = getRprPath() - if rprPath is not None: - lib = cdll.LoadLibrary(rprPath) - lib.SetHdRprRenderDevice(renderDeviceId) - reemitStage(usdviewApi) - -def setRenderQuality(usdviewApi, quality): - rprPath = getRprPath() - if rprPath is not None: - lib = cdll.LoadLibrary(rprPath) - lib.GetHdRprRenderQuality.restype = c_int - currentQuality = lib.GetHdRprRenderQuality() - lib.SetHdRprRenderQuality(quality) - if (currentQuality == 3 and quality < 3) or \ - (currentQuality < 3 and quality == 3): - reemitStage(usdviewApi) - -def renderDeviceCPU(usdviewApi): - setRenderDevice(usdviewApi, 0) - -def renderDeviceGPU(usdviewApi): - setRenderDevice(usdviewApi, 1) - -def SetRenderLowQuality(usdviewApi): - setRenderQuality(usdviewApi, 0) -def SetRenderMediumQuality(usdviewApi): - setRenderQuality(usdviewApi, 1) -def SetRenderHighQuality(usdviewApi): - setRenderQuality(usdviewApi, 2) -def SetRenderFullQuality(usdviewApi): - setRenderQuality(usdviewApi, 3) - -class RprPluginContainer(PluginContainer): - - def registerPlugins(self, plugRegistry, usdviewApi): - self.rDeviceCpu = plugRegistry.registerCommandPlugin( - "RprPluginContainer.renderDeviceCPU", - "CPU", - renderDeviceCPU) - self.rDeviceGpu = plugRegistry.registerCommandPlugin( - "RprPluginContainer.renderDeviceGPU", - "GPU", - renderDeviceGPU) - - self.setRenderLowQuality = plugRegistry.registerCommandPlugin( - "RprPluginContainer.setRenderLowQuality", - "Low", - SetRenderLowQuality) - self.setRenderMediumQuality = plugRegistry.registerCommandPlugin( - "RprPluginContainer.setRenderMediumQuality", - "Medium", - SetRenderMediumQuality) - self.setRenderHighQuality = plugRegistry.registerCommandPlugin( - "RprPluginContainer.setRenderHighQuality", - "High", - SetRenderHighQuality) - self.setRenderFullQuality = plugRegistry.registerCommandPlugin( - "RprPluginContainer.setRenderFullQuality", - "Full", - SetRenderFullQuality) - - self.restartAction = plugRegistry.registerCommandPlugin( - "RprPluginContainer.restartAction", - "Restart", - reemitStage) - - - def configureView(self, plugRegistry, plugUIBuilder): - - rprMenu = plugUIBuilder.findOrCreateMenu("RPR") - - renderDeviceSubMenu = rprMenu.findOrCreateSubmenu("Render Device") - renderDeviceSubMenu.addItem(self.rDeviceCpu) - renderDeviceSubMenu.addItem(self.rDeviceGpu) - - renderQualityMenu = rprMenu.findOrCreateSubmenu("Render Quality") - renderQualityMenu.addItem(self.setRenderLowQuality) - renderQualityMenu.addItem(self.setRenderMediumQuality) - renderQualityMenu.addItem(self.setRenderHighQuality) - renderQualityMenu.addItem(self.setRenderFullQuality) - - rprMenu.addItem(self.restartAction) - -Tf.Type.Define(RprPluginContainer) diff --git a/pxr/imaging/plugin/hdRpr/renderBuffer.cpp b/pxr/imaging/plugin/hdRpr/renderBuffer.cpp index 4ea2be946..606956284 100644 --- a/pxr/imaging/plugin/hdRpr/renderBuffer.cpp +++ b/pxr/imaging/plugin/hdRpr/renderBuffer.cpp @@ -19,10 +19,11 @@ limitations under the License. PXR_NAMESPACE_OPEN_SCOPE -HdRprRenderBuffer::HdRprRenderBuffer(SdfPath const& id) +HdRprRenderBuffer::HdRprRenderBuffer(SdfPath const& id, HdRprApi* api) : HdRenderBuffer(id) , m_numMappers(0) - , m_isConverged(false) { + , m_isConverged(false) + , m_rprApi(api) { } @@ -49,52 +50,94 @@ void HdRprRenderBuffer::Finalize(HdRenderParam* renderParam) { bool HdRprRenderBuffer::Allocate(GfVec3i const& dimensions, HdFormat format, bool multiSampled) { - TF_VERIFY(!IsMapped()); - TF_UNUSED(multiSampled); - if (dimensions[2] != 1) { TF_WARN("HdRprRenderBuffer supports 2D buffers only"); return false; } - _Deallocate(); +#ifdef ENABLE_MULTITHREADED_RENDER_BUFFER + std::unique_lock lock(m_mapMutex); + m_mapConditionVar.wait(lock, [this]() { return m_numMappers == 0; }); +#endif // ENABLE_MULTITHREADED_RENDER_BUFFER m_width = dimensions[0]; m_height = dimensions[1]; m_format = format; + m_multiSampled = multiSampled; + m_isConverged.store(false); + size_t dataByteSize = m_width * m_height * HdDataSizeOfFormat(m_format); - m_mappedBuffer.resize(dataByteSize, 0); + if (dataByteSize) { + m_mappedBuffer.reserve(dataByteSize); + std::memset(m_mappedBuffer.data(), 0, dataByteSize); + } else { + m_mappedBuffer = std::vector(); + } - return false; + return true; } void HdRprRenderBuffer::_Deallocate() { - TF_VERIFY(!IsMapped()); + +#ifdef ENABLE_MULTITHREADED_RENDER_BUFFER + std::unique_lock lock(m_mapMutex); + m_mapConditionVar.wait(lock, [this]() { return m_numMappers == 0; }); +#endif // ENABLE_MULTITHREADED_RENDER_BUFFER m_width = 0u; m_height = 0u; m_format = HdFormatInvalid; m_isConverged.store(false); - m_numMappers.store(0); - m_mappedBuffer.resize(0); + m_mappedBuffer = std::vector(); } void* HdRprRenderBuffer::Map() { + m_rprApi->Resolve(); + +#ifdef ENABLE_MULTITHREADED_RENDER_BUFFER + std::unique_lock lock(m_mapMutex); +#endif // ENABLE_MULTITHREADED_RENDER_BUFFER + ++m_numMappers; return m_mappedBuffer.data(); } void HdRprRenderBuffer::Unmap() { + +#ifdef ENABLE_MULTITHREADED_RENDER_BUFFER + bool isLastMapper; + { + std::unique_lock lock(m_mapMutex); + if (!TF_VERIFY(m_numMappers)) { + TF_CODING_ERROR("Invalid HdRenderBuffer usage detected. Over-use of Unmap."); + return; + } +#endif // ENABLE_MULTITHREADED_RENDER_BUFFER + + --m_numMappers; + TF_VERIFY(m_numMappers >= 0); + +#ifdef ENABLE_MULTITHREADED_RENDER_BUFFER + isLastMapper = m_numMappers == 0; + } + + if (isLastMapper) { + m_mapConditionVar.notify_one(); + } +#endif // ENABLE_MULTITHREADED_RENDER_BUFFER + // XXX We could consider clearing _mappedBuffer here to free RAM. // For now we assume that Map() will be called frequently so we prefer // to avoid the cost of clearing the buffer over memory savings. - // m_mappedBuffer.clear(); - // m_mappedBuffer.shrink_to_fit(); - --m_numMappers; + //if (m_numMappers == 0) { + // m_mappedBuffer = std::vector(); + //} } bool HdRprRenderBuffer::IsMapped() const { - return m_numMappers.load() != 0; + // There is no point to lock this read because HdRenderBuffer user has no idea about internal synchronization. + // Calling this function to check if they need to unmap a render buffer is just wrong and should not happen. + return m_numMappers != 0; } void HdRprRenderBuffer::Resolve() { @@ -109,4 +152,21 @@ void HdRprRenderBuffer::SetConverged(bool converged) { return m_isConverged.store(converged); } +VtValue HdRprRenderBuffer::GetResource(bool multiSampled) const { + if ("aov_color" == GetId().GetElementString()) { + rpr::FrameBuffer* color = m_rprApi->GetRawColorFramebuffer(); + // RPR framebuffer not created yet + if (!color) { + return VtValue(); + } + + VtDictionary dictionary; + dictionary["isVulkanInteropEnabled"] = m_rprApi->IsVulkanInteropEnabled(); + dictionary["framebuffer"] = color; + + return VtValue(dictionary); + } + return VtValue(); +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/renderBuffer.h b/pxr/imaging/plugin/hdRpr/renderBuffer.h index edd9ad2ae..f97357632 100644 --- a/pxr/imaging/plugin/hdRpr/renderBuffer.h +++ b/pxr/imaging/plugin/hdRpr/renderBuffer.h @@ -16,11 +16,15 @@ limitations under the License. #include "pxr/imaging/hd/renderBuffer.h" +#include + PXR_NAMESPACE_OPEN_SCOPE +class HdRprApi; + class HdRprRenderBuffer final : public HdRenderBuffer { public: - HdRprRenderBuffer(SdfPath const& id); + HdRprRenderBuffer(SdfPath const& id, HdRprApi* api = nullptr); ~HdRprRenderBuffer() override = default; void Sync(HdSceneDelegate* sceneDelegate, @@ -41,7 +45,7 @@ class HdRprRenderBuffer final : public HdRenderBuffer { HdFormat GetFormat() const override { return m_format; } - bool IsMultiSampled() const override { return false; } + bool IsMultiSampled() const override { return m_multiSampled; } void* Map() override; @@ -55,17 +59,32 @@ class HdRprRenderBuffer final : public HdRenderBuffer { void SetConverged(bool converged); + void* GetPointerForWriting() { return m_mappedBuffer.data(); } + + // HdRprRenderBuffer should hold actual framebuffer + // But for now just take it from HdRprApi in order to provide valid API + VtValue GetResource(bool multiSampled) const; + protected: void _Deallocate() override; private: + std::vector m_mappedBuffer; uint32_t m_width = 0u; uint32_t m_height = 0u; HdFormat m_format = HdFormat::HdFormatInvalid; + bool m_multiSampled = false; - std::vector m_mappedBuffer; - std::atomic m_numMappers; std::atomic m_isConverged; + + HdRprApi* m_rprApi = nullptr; + +#ifdef ENABLE_MULTITHREADED_RENDER_BUFFER + std::mutex m_mapMutex; + std::condition_variable m_mapConditionVar; +#endif // ENABLE_MULTITHREADED_RENDER_BUFFER + + int m_numMappers; }; PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/renderDelegate.cpp b/pxr/imaging/plugin/hdRpr/renderDelegate.cpp index a5396e3b6..71044f5de 100644 --- a/pxr/imaging/plugin/hdRpr/renderDelegate.cpp +++ b/pxr/imaging/plugin/hdRpr/renderDelegate.cpp @@ -12,9 +12,10 @@ limitations under the License. ************************************************************************/ #include "renderDelegate.h" +#include "aovDescriptor.h" -#include"pxr/imaging/hd/extComputation.h" - +#include "pxr/imaging/rprUsd/materialRegistry.h" +#include "pxr/imaging/hd/extComputation.h" #include "pxr/base/tf/diagnosticMgr.h" #include "pxr/base/tf/getenv.h" @@ -116,7 +117,11 @@ class HdRprDiagnosticMgrDelegate : public TfDiagnosticMgr::Delegate { TF_DEFINE_PRIVATE_TOKENS(_tokens, (openvdbAsset) \ - (percentDone) + (percentDone) \ + (renderMode) \ + (batch) \ + (progressive) \ + (RPR) ); const TfTokenVector HdRprDelegate::SUPPORTED_RPRIM_TYPES = { @@ -147,7 +152,14 @@ const TfTokenVector HdRprDelegate::SUPPORTED_BPRIM_TYPES = { HdPrimTypeTokens->renderBuffer }; -HdRprDelegate::HdRprDelegate() { +HdRprDelegate::HdRprDelegate(HdRenderSettingsMap const& renderSettings) { + for (auto& entry : 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(); @@ -187,11 +199,11 @@ HdRenderParam* HdRprDelegate::GetRenderParam() const { void HdRprDelegate::CommitResources(HdChangeTracker* tracker) { // CommitResources() is called after prim sync has finished, but before any // tasks (such as draw tasks) have run. - + m_rprApi->CommitResources(); } TfToken HdRprDelegate::GetMaterialNetworkSelector() const { - return m_renderParam->GetMaterialNetworkSelector(); + return RprUsdMaterialRegistry::GetInstance().GetMaterialNetworkSelector(); } TfTokenVector const& HdRprDelegate::GetSupportedRprimTypes() const { @@ -303,7 +315,7 @@ void HdRprDelegate::DestroySprim(HdSprim* sPrim) { HdBprim* HdRprDelegate::CreateBprim(TfToken const& typeId, SdfPath const& bprimId) { if (typeId == HdPrimTypeTokens->renderBuffer) { - return new HdRprRenderBuffer(bprimId); + return new HdRprRenderBuffer(bprimId, m_rprApi.get()); } #ifdef USE_VOLUME else if (typeId == _tokens->openvdbAsset) { @@ -324,42 +336,14 @@ void HdRprDelegate::DestroyBprim(HdBprim* bPrim) { } HdAovDescriptor HdRprDelegate::GetDefaultAovDescriptor(TfToken const& name) const { - HdParsedAovToken aovId(name); - if (name != HdAovTokens->color && - name != HdAovTokens->normal && - name != HdAovTokens->primId && - name != HdAovTokens->depth && - name != HdRprUtilsGetCameraDepthName() && - !(aovId.isPrimvar && aovId.name == "st")) { - // TODO: implement support for instanceId and elementId aov - return HdAovDescriptor(); - } + auto& rprAovDesc = HdRprAovRegistry::GetInstance().GetAovDesc(name); - if (!m_rprApi->IsAovFormatConversionAvailable()) { - if (name == HdAovTokens->primId) { - // Integer images required, no way to support it - return HdAovDescriptor(); - } - // Only native RPR format can be used for AOVs when there is no support for AOV format conversion - return HdAovDescriptor(HdFormatFloat32Vec4, false, VtValue(GfVec4f(0.0f))); - } - - HdFormat format = HdFormatInvalid; - - float clearColorValue = 0.0f; - if (name == HdAovTokens->depth || - name == HdRprUtilsGetCameraDepthName()) { - clearColorValue = name == HdRprUtilsGetCameraDepthName() ? 0.0f : 1.0f; - format = HdFormatFloat32; - } else if (name == HdAovTokens->color) { - format = HdFormatFloat32Vec4; - } else if (name == HdAovTokens->primId) { - format = HdFormatInt32; - } else { - format = HdFormatFloat32Vec3; - } + HdAovDescriptor hdAovDesc; + hdAovDesc.format = rprAovDesc.format; + hdAovDesc.multiSampled = rprAovDesc.multiSampled; + hdAovDesc.clearValue = VtValue(rprAovDesc.clearValue); - return HdAovDescriptor(format, false, VtValue(GfVec4f(clearColorValue))); + return hdAovDesc; } HdRenderSettingDescriptorList HdRprDelegate::GetRenderSettingDescriptors() const { @@ -367,23 +351,12 @@ HdRenderSettingDescriptorList HdRprDelegate::GetRenderSettingDescriptors() const } VtDictionary HdRprDelegate::GetRenderStats() const { + auto rprStats = m_rprApi->GetRenderStats(); + VtDictionary stats; - int numCompletedSamples = m_rprApi->GetNumCompletedSamples(); - stats[HdPerfTokens->numCompletedSamples.GetString()] = numCompletedSamples; - - double percentDone = 0.0; - { - HdRprConfig* config; - auto configInstanceLock = HdRprConfig::GetInstance(&config); - percentDone = double(numCompletedSamples) / config->GetMaxSamples(); - } - int numActivePixels = m_rprApi->GetNumActivePixels(); - if (numActivePixels != -1) { - auto size = m_rprApi->GetViewportSize(); - int numPixels = size[0] * size[1]; - percentDone = std::max(percentDone, double(numPixels - numActivePixels) / numPixels); - } - stats[_tokens->percentDone.GetString()] = 100.0 * percentDone; + stats[_tokens->percentDone.GetString()] = rprStats.percentDone; + stats["averageRenderTimePerSample"] = rprStats.averageRenderTimePerSample; + stats["averageResolveTimePerSample"] = rprStats.averageResolveTimePerSample; return stats; } @@ -418,41 +391,65 @@ bool HdRprDelegate::Restart() { return true; } -#endif // PXR_VERSION >= 2005 - -TfToken const& HdRprUtilsGetCameraDepthName() { -#if PXR_VERSION < 2002 - return HdAovTokens->linearDepth; -#else - return HdAovTokens->cameraDepth; -#endif +void HdRprDelegate::SetDrivers(HdDriverVector const& drivers) { + for (HdDriver* hdDriver : drivers) { + if (hdDriver->name == _tokens->RPR && hdDriver->driver.IsHolding()) { + VtDictionary dictionary = hdDriver->driver.UncheckedGet(); + + // Interop info is used to create context + void* interopInfo = dictionary["interop_info"].Get(); + + // Condition variable is used to prevent this issue: + // [Plugin] Render_Frame_1 & Flush_Frame_1 + // [Plugin] Render_Frame_2 & Flush_Frame_2 + // [Client] Present frame + // Hybrid correct usage prohibit flushing next frame before previous was presented + // Render thread would wait on next flush till previous frame would be presented, example: + // [Plugin] Render_Frame_1 & Flush_Frame_1 + // [Plugin] Render_Frame_2 & [Wait for present] <- Here frame wasn't presented yet + // [Client] Present Frame_1 + // [Plugin] [Wake up] Flush Frame_2, continue work + std::condition_variable* presentedConditionVariable = dictionary["presented_condition_variable"].Get(); + bool* presentedCondition = dictionary["presented_condition"].Get(); + + // Set condition to true to render first frame + *presentedCondition = true; + + m_rprApi->SetInteropInfo(interopInfo, presentedConditionVariable, presentedCondition); + break; + } + } } +#endif // PXR_VERSION >= 2005 + PXR_NAMESPACE_CLOSE_SCOPE -void SetHdRprRenderDevice(int renderDevice) { +void HdRprSetRenderDevice(const char* renderDevice) { PXR_INTERNAL_NS::HdRprConfig* config; auto configInstanceLock = PXR_INTERNAL_NS::HdRprConfig::GetInstance(&config); - config->SetRenderDevice(renderDevice); + config->SetRenderDevice(PXR_INTERNAL_NS::TfToken(renderDevice)); } -void SetHdRprRenderQuality(int quality) { +void HdRprSetRenderQuality(const char* quality) { PXR_INTERNAL_NS::HdRprConfig* config; auto configInstanceLock = PXR_INTERNAL_NS::HdRprConfig::GetInstance(&config); - config->SetRenderQuality(quality); + config->SetRenderQuality(PXR_INTERNAL_NS::TfToken(quality)); } -int GetHdRprRenderQuality() { +char* HdRprGetRenderQuality() { if (!PXR_INTERNAL_NS::g_rprApi) { - return -1; + return nullptr; } - return PXR_INTERNAL_NS::g_rprApi->GetCurrentRenderQuality(); + auto currentRenderQuality = PXR_INTERNAL_NS::g_rprApi->GetCurrentRenderQuality().GetText(); + + auto len = std::strlen(currentRenderQuality); + auto copy = (char*)malloc(len + 1); + copy[len] = '\0'; + std::strncpy(copy, currentRenderQuality, len); + return copy; } -int HdRprExportRprSceneOnNextRender(const char* exportPath) { - if (!PXR_INTERNAL_NS::g_rprApi) { - return -1; - } - PXR_INTERNAL_NS::g_rprApi->ExportRprSceneOnNextRender(exportPath); - return 0; +void HdRprFree(void* ptr) { + free(ptr); } diff --git a/pxr/imaging/plugin/hdRpr/renderDelegate.h b/pxr/imaging/plugin/hdRpr/renderDelegate.h index 1037562cb..9855a0b3e 100644 --- a/pxr/imaging/plugin/hdRpr/renderDelegate.h +++ b/pxr/imaging/plugin/hdRpr/renderDelegate.h @@ -28,7 +28,7 @@ class HdRprApi; class HdRprDelegate final : public HdRenderDelegate { public: - HdRprDelegate(); + HdRprDelegate(HdRenderSettingsMap const& renderSettings); ~HdRprDelegate() override; HdRprDelegate(const HdRprDelegate&) = delete; @@ -83,13 +83,20 @@ class HdRprDelegate final : public HdRenderDelegate { bool IsStopSupported() const override; bool Stop() override; bool Restart() override; + 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; @@ -99,19 +106,19 @@ class HdRprDelegate final : public HdRenderDelegate { DiagnostMgrDelegatePtr m_diagnosticMgrDelegate; }; -TfToken const& HdRprUtilsGetCameraDepthName(); PXR_NAMESPACE_CLOSE_SCOPE extern "C" { -HDRPR_API void SetHdRprRenderDevice(int renderDevice); +HDRPR_API void HdRprSetRenderDevice(const char* renderDevice); -HDRPR_API void SetHdRprRenderQuality(int quality); +HDRPR_API void HdRprSetRenderQuality(const char* quality); -HDRPR_API int GetHdRprRenderQuality(); +// Returned pointer should be released by the caller with HdRprFree +HDRPR_API char* HdRprGetRenderQuality(); -HDRPR_API int HdRprExportRprSceneOnNextRender(const char* exportPath); +HDRPR_API void HdRprFree(void* ptr); } // extern "C" diff --git a/pxr/imaging/plugin/hdRpr/renderParam.cpp b/pxr/imaging/plugin/hdRpr/renderParam.cpp index f682aaa2f..8be43b6e1 100644 --- a/pxr/imaging/plugin/hdRpr/renderParam.cpp +++ b/pxr/imaging/plugin/hdRpr/renderParam.cpp @@ -14,21 +14,10 @@ limitations under the License. #include "renderParam.h" #include "volume.h" -#include "pxr/base/tf/envSetting.h" - #include "pxr/imaging/hd/sceneDelegate.h" PXR_NAMESPACE_OPEN_SCOPE -TF_DEFINE_PUBLIC_TOKENS(HdRprMaterialNetworkSelectorTokens, HDRPR_MATERIAL_NETWORK_SELECTOR_TOKENS); - -TF_DEFINE_ENV_SETTING(HDRPR_MATERIAL_NETWORK_SELECTOR, HDRPR_DEFAULT_MATERIAL_NETWORK_SELECTOR, - "Material network selector to be used in hdRpr"); - -void HdRprRenderParam::InitializeEnvParameters() { - m_materialNetworkSelector = TfToken(TfGetEnvSetting(HDRPR_MATERIAL_NETWORK_SELECTOR)); -} - HdRprVolumeFieldSubscription HdRprRenderParam::SubscribeVolumeForFieldUpdates( HdRprVolume* volume, SdfPath const& fieldId) { auto sub = HdRprVolumeFieldSubscription(volume, [](HdRprVolume* volume) {}); @@ -66,4 +55,32 @@ void HdRprRenderParam::NotifyVolumesAboutFieldChange(HdSceneDelegate* sceneDeleg } } +void HdRprRenderParam::SubscribeForMaterialUpdates(SdfPath const& materialId, SdfPath const& rPrimId) { + std::lock_guard lock(m_materialSubscriptionsMutex); + m_materialSubscriptions[materialId].insert(rPrimId); +} + +void HdRprRenderParam::UnsubscribeFromMaterialUpdates(SdfPath const& materialId, SdfPath const& rPrimId) { + std::lock_guard lock(m_materialSubscriptionsMutex); + auto subscriptionsIt = m_materialSubscriptions.find(materialId); + if (TF_VERIFY(subscriptionsIt != m_materialSubscriptions.end())) { + subscriptionsIt->second.erase(rPrimId); + if (subscriptionsIt->second.empty()) { + m_materialSubscriptions.erase(subscriptionsIt); + } + } +} + +void HdRprRenderParam::MaterialDidChange(HdSceneDelegate* sceneDelegate, SdfPath const materialId) { + std::lock_guard lock(m_materialSubscriptionsMutex); + auto subscriptionsIt = m_materialSubscriptions.find(materialId); + if (subscriptionsIt != m_materialSubscriptions.end()) { + HdChangeTracker& changeTracker = sceneDelegate->GetRenderIndex().GetChangeTracker(); + for (auto& rPrimId : subscriptionsIt->second) { + changeTracker.MarkRprimDirty(rPrimId, HdChangeTracker::DirtyMaterialId); + } + } +} + + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/renderParam.h b/pxr/imaging/plugin/hdRpr/renderParam.h index 1e3590dc2..0c67df57b 100644 --- a/pxr/imaging/plugin/hdRpr/renderParam.h +++ b/pxr/imaging/plugin/hdRpr/renderParam.h @@ -20,12 +20,6 @@ limitations under the License. PXR_NAMESPACE_OPEN_SCOPE -#define HDRPR_MATERIAL_NETWORK_SELECTOR_TOKENS \ - (rpr) \ - (karma) - -TF_DECLARE_PUBLIC_TOKENS(HdRprMaterialNetworkSelectorTokens, HDRPR_MATERIAL_NETWORK_SELECTOR_TOKENS); - class HdRprApi; class HdRprVolume; @@ -36,10 +30,7 @@ class HdRprRenderParam final : public HdRenderParam { public: HdRprRenderParam(HdRprApi* rprApi, HdRprRenderThread* renderThread) : m_rprApi(rprApi) - , m_renderThread(renderThread) { - m_numLights.store(0); - InitializeEnvParameters(); - } + , m_renderThread(renderThread) {} ~HdRprRenderParam() override = default; HdRprApi const* GetRprApi() const { return m_rprApi; } @@ -50,34 +41,32 @@ class HdRprRenderParam final : public HdRenderParam { HdRprRenderThread* GetRenderThread() { return m_renderThread; } - void AddLight() { ++m_numLights; } - void RemoveLight() { --m_numLights; } - bool HasLights() const { return m_numLights != 0; } - - TfToken const& GetMaterialNetworkSelector() const { return m_materialNetworkSelector; } - // Hydra does not mark HdVolume as changed if HdField used by it is changed // We implement this volume-to-field dependency by ourself until it's implemented in Hydra // More info: https://groups.google.com/forum/#!topic/usd-interest/pabUE0B_5X4 HdRprVolumeFieldSubscription SubscribeVolumeForFieldUpdates(HdRprVolume* volume, SdfPath const& fieldId); void NotifyVolumesAboutFieldChange(HdSceneDelegate* sceneDelegate, SdfPath const& fieldId); + // Hydra does not always mark HdRprim as changed if HdMaterial used by it has been changed. + // HdStorm marks all existing rprims as dirty when a material is changed. + // We instead mark only those rprims that use the changed material. + void SubscribeForMaterialUpdates(SdfPath const& materialId, SdfPath const& rPrimId); + void UnsubscribeFromMaterialUpdates(SdfPath const& materialId, SdfPath const& rPrimId); + void MaterialDidChange(HdSceneDelegate* sceneDelegate, SdfPath const materialId); + void RestartRender() { m_restartRender.store(true); } bool IsRenderShouldBeRestarted() { return m_restartRender.exchange(false); } private: - void InitializeEnvParameters(); - HdRprApi* m_rprApi; HdRprRenderThread* m_renderThread; - std::atomic m_numLights; - - TfToken m_materialNetworkSelector; - std::mutex m_subscribedVolumesMutex; std::map> m_subscribedVolumes; + std::mutex m_materialSubscriptionsMutex; + std::map> m_materialSubscriptions; + std::atomic m_restartRender; }; diff --git a/pxr/imaging/plugin/hdRpr/renderPass.cpp b/pxr/imaging/plugin/hdRpr/renderPass.cpp index 64ef3b923..fa3601ee8 100644 --- a/pxr/imaging/plugin/hdRpr/renderPass.cpp +++ b/pxr/imaging/plugin/hdRpr/renderPass.cpp @@ -33,6 +33,10 @@ HdRprRenderPass::HdRprRenderPass(HdRenderIndex* index, } +HdRprRenderPass::~HdRprRenderPass() { + m_renderParam->GetRenderThread()->StopRender(); +} + void HdRprRenderPass::_Execute(HdRenderPassStateSharedPtr const& renderPassState, TfTokenVector const& renderTags) { // To avoid potential deadlock: // main thread locks config instance and requests render stop and diff --git a/pxr/imaging/plugin/hdRpr/renderPass.h b/pxr/imaging/plugin/hdRpr/renderPass.h index 9e783ff8c..0f0719192 100644 --- a/pxr/imaging/plugin/hdRpr/renderPass.h +++ b/pxr/imaging/plugin/hdRpr/renderPass.h @@ -28,7 +28,7 @@ class HdRprRenderPass final : public HdRenderPass { HdRprimCollection const& collection, HdRprRenderParam* renderParam); - ~HdRprRenderPass() override = default; + ~HdRprRenderPass() override; bool IsConverged() const override; diff --git a/pxr/imaging/plugin/hdRpr/rendererPlugin.cpp b/pxr/imaging/plugin/hdRpr/rendererPlugin.cpp index 84a1d124d..18d27e747 100644 --- a/pxr/imaging/plugin/hdRpr/rendererPlugin.cpp +++ b/pxr/imaging/plugin/hdRpr/rendererPlugin.cpp @@ -46,15 +46,11 @@ TF_REGISTRY_FUNCTION(TfType) { } HdRenderDelegate* HdRprPlugin::CreateRenderDelegate() { - return new HdRprDelegate(); + return new HdRprDelegate(HdRenderSettingsMap()); } HdRenderDelegate* HdRprPlugin::CreateRenderDelegate(HdRenderSettingsMap const& settingsMap) { - auto renderDelegate = new HdRprDelegate(); - for (auto& entry : settingsMap) { - renderDelegate->SetRenderSetting(entry.first, entry.second); - } - return renderDelegate; + return new HdRprDelegate(settingsMap); } void HdRprPlugin::DeleteRenderDelegate(HdRenderDelegate* renderDelegate) { diff --git a/pxr/imaging/plugin/hdRpr/rifcpp/rifContext.cpp b/pxr/imaging/plugin/hdRpr/rifcpp/rifContext.cpp index 7ec8e2ff0..1e1ed51c9 100644 --- a/pxr/imaging/plugin/hdRpr/rifcpp/rifContext.cpp +++ b/pxr/imaging/plugin/hdRpr/rifcpp/rifContext.cpp @@ -15,8 +15,8 @@ limitations under the License. #include "rifError.h" #include "rprApiFramebuffer.h" -#include "rpr/contextMetadata.h" -#include "rpr/helpers.h" +#include "pxr/imaging/rprUsd/contextMetadata.h" +#include "pxr/imaging/rprUsd/helpers.h" #include #include @@ -146,7 +146,7 @@ std::unique_ptr ContextOpenCL::CreateImage(HdRprApiFramebuffer* rprFrameB } auto rifImageDesc = GetRifImageDesc(rprFrameBuffer); - RIF_ERROR_CHECK_THROW(rifContextCreateImageFromOpenClMemory(m_context, &rifImageDesc, clMem, false, &rifImage), "Failed to create RIF image from OpenCL memory"); + RIF_ERROR_CHECK_THROW(rifContextCreateImageFromOpenClMemory(m_context, &rifImageDesc, clMem, &rifImage), "Failed to create RIF image from OpenCL memory"); return std::unique_ptr(new Image(rifImage)); } @@ -268,6 +268,7 @@ ContextMetal::ContextMetal(rpr::Context* rprContext, std::string const& modelPat } std::unique_ptr ContextMetal::CreateImage(HdRprApiFramebuffer* rprFrameBuffer) { +#ifdef __APPLE__ if (!rprFrameBuffer) { return nullptr; } @@ -296,6 +297,9 @@ std::unique_ptr ContextMetal::CreateImage(HdRprApiFramebuffer* rprFrameBu RIF_ERROR_CHECK_THROW(rifContextCreateImageFromMetalMemory(m_context, &desc, clMem, size, &rifImage), "Failed to create RIF image from metal memory"); return std::unique_ptr(new Image(rifImage)); +#else + return nullptr; +#endif } bool HasGpuContext(rpr_creation_flags contextFlags) { @@ -312,7 +316,7 @@ bool HasGpuContext(rpr_creation_flags contextFlags) { } // namespace anonymous -std::unique_ptr Context::Create(rpr::Context* rprContext, rpr::ContextMetadata const& rprContextMetadata, std::string const& modelPath) { +std::unique_ptr Context::Create(rpr::Context* rprContext, RprUsdContextMetadata const& rprContextMetadata, std::string const& modelPath) { if (!rprContext) { return nullptr; } @@ -325,7 +329,7 @@ std::unique_ptr Context::Create(rpr::Context* rprContext, rpr::ContextM try { std::unique_ptr rifContext; if (HasGpuContext(contextFlags) && - rprContextMetadata.pluginType != rpr::kPluginHybrid && + rprContextMetadata.pluginType == kPluginTahoe && !(contextFlags & RPR_CREATION_FLAGS_ENABLE_METAL)) { rifContext.reset(new ContextOpenCL(rprContext, modelPath)); } else { diff --git a/pxr/imaging/plugin/hdRpr/rifcpp/rifContext.h b/pxr/imaging/plugin/hdRpr/rifcpp/rifContext.h index 0d1634ac3..e0990d01f 100644 --- a/pxr/imaging/plugin/hdRpr/rifcpp/rifContext.h +++ b/pxr/imaging/plugin/hdRpr/rifcpp/rifContext.h @@ -19,22 +19,18 @@ limitations under the License. #include #include -namespace rpr { - -class Context; -struct ContextMetadata; - -} // namespace rpr +namespace rpr { class Context; } PXR_NAMESPACE_OPEN_SCOPE +struct RprUsdContextMetadata; class HdRprApiFramebuffer; namespace rif { class Context { public: - static std::unique_ptr Create(rpr::Context* rprContext, rpr::ContextMetadata const& rprContextMetadata, std::string const& modelPath); + static std::unique_ptr Create(rpr::Context* rprContext, RprUsdContextMetadata const& rprContextMetadata, std::string const& modelPath); virtual ~Context(); diff --git a/pxr/imaging/plugin/hdRpr/rifcpp/rifFilter.cpp b/pxr/imaging/plugin/hdRpr/rifcpp/rifFilter.cpp index 29f071060..75a1da8ef 100644 --- a/pxr/imaging/plugin/hdRpr/rifcpp/rifFilter.cpp +++ b/pxr/imaging/plugin/hdRpr/rifcpp/rifFilter.cpp @@ -23,10 +23,12 @@ namespace { class FilterAIDenoise final : public Filter { enum { RemapDepthFilter, + RemapNormalFilter, AuxFilterMax }; enum { RemappedDepthImage, + RemappedNormalImage, AuxImageMax }; public: @@ -82,26 +84,31 @@ FilterAIDenoise::FilterAIDenoise(Context* rifContext, std::uint32_t width, std:: // auxillary filters m_auxFilters.resize(AuxFilterMax, nullptr); m_auxFilters[RemapDepthFilter] = rifContext->CreateImageFilter(RIF_IMAGE_FILTER_REMAP_RANGE); + m_auxFilters[RemapNormalFilter] = rifContext->CreateImageFilter(RIF_IMAGE_FILTER_REMAP_RANGE); // setup remapping filters RIF_ERROR_CHECK_THROW(rifImageFilterSetParameter1f(m_auxFilters[RemapDepthFilter], "dstLo", 0.0f), "Failed to set filter parameter"); RIF_ERROR_CHECK_THROW(rifImageFilterSetParameter1f(m_auxFilters[RemapDepthFilter], "dstHi", 1.0f), "Failed to set filter parameter"); + RIF_ERROR_CHECK_THROW(rifImageFilterSetParameter1f(m_auxFilters[RemapNormalFilter], "dstLo", 0.0f), "Failed to set filter parameter"); + RIF_ERROR_CHECK_THROW(rifImageFilterSetParameter1f(m_auxFilters[RemapNormalFilter], "dstHi", 1.0f), "Failed to set filter parameter"); // auxillary rif images auto desc = Image::GetDesc(width, height, HdFormatFloat32Vec4); m_auxImages.resize(AuxImageMax); m_auxImages[RemappedDepthImage] = rifContext->CreateImage(desc); + m_auxImages[RemappedNormalImage] = rifContext->CreateImage(desc); } void FilterAIDenoise::AttachFilter(rif_image inputImage) { // setup inputs - RIF_ERROR_CHECK_THROW(rifImageFilterSetParameterImage(m_rifFilter, "normalsImg", m_inputs.at(Normal).rifImage), "Failed to set filter parameter"); + RIF_ERROR_CHECK_THROW(rifImageFilterSetParameterImage(m_rifFilter, "normalsImg", m_auxImages[RemappedNormalImage]->GetHandle()), "Failed to set filter parameter"); RIF_ERROR_CHECK_THROW(rifImageFilterSetParameterImage(m_rifFilter, "depthImg", m_auxImages[RemappedDepthImage]->GetHandle()), "Failed to set filter parameter"); RIF_ERROR_CHECK_THROW(rifImageFilterSetParameterImage(m_rifFilter, "colorImg", m_inputs.at(Color).rifImage), "Failed to set filter parameter"); RIF_ERROR_CHECK_THROW(rifImageFilterSetParameterImage(m_rifFilter, "albedoImg", m_inputs.at(Albedo).rifImage), "Failed to set filter parameter"); m_rifContext->AttachFilter(m_auxFilters[RemapDepthFilter], m_inputs.at(LinearDepth).rifImage, m_auxImages[RemappedDepthImage]->GetHandle()); + m_rifContext->AttachFilter(m_auxFilters[RemapNormalFilter], m_inputs.at(Normal).rifImage, m_auxImages[RemappedNormalImage]->GetHandle()); Filter::AttachFilter(inputImage); } diff --git a/pxr/imaging/plugin/hdRpr/rifcpp/rifFilter.h b/pxr/imaging/plugin/hdRpr/rifcpp/rifFilter.h index 41bd63732..aaa5a6df0 100644 --- a/pxr/imaging/plugin/hdRpr/rifcpp/rifFilter.h +++ b/pxr/imaging/plugin/hdRpr/rifcpp/rifFilter.h @@ -17,7 +17,7 @@ limitations under the License. #include "rifContext.h" #include "rifImage.h" -#include "boostIncludePath.h" +#include "pxr/imaging/rprUsd/boostIncludePath.h" #include BOOST_INCLUDE_PATH(variant.hpp) #include "pxr/base/gf/matrix4f.h" diff --git a/pxr/imaging/plugin/hdRpr/rpr/CMakeLists.txt b/pxr/imaging/plugin/hdRpr/rpr/CMakeLists.txt deleted file mode 100644 index 39e9b9fc2..000000000 --- a/pxr/imaging/plugin/hdRpr/rpr/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -target_sources(hdRpr PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR}/error.h - ${CMAKE_CURRENT_SOURCE_DIR}/contextMetadata.h - ${CMAKE_CURRENT_SOURCE_DIR}/contextHelpers.h - ${CMAKE_CURRENT_SOURCE_DIR}/contextHelpers.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/imageHelpers.h - ${CMAKE_CURRENT_SOURCE_DIR}/imageHelpers.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/helpers.h) \ No newline at end of file diff --git a/pxr/imaging/plugin/hdRpr/rpr/imageHelpers.cpp b/pxr/imaging/plugin/hdRpr/rpr/imageHelpers.cpp deleted file mode 100644 index 1630e72dc..000000000 --- a/pxr/imaging/plugin/hdRpr/rpr/imageHelpers.cpp +++ /dev/null @@ -1,205 +0,0 @@ -/************************************************************************ -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 "imageHelpers.h" -#include "helpers.h" - -#include "pxr/imaging/glf/glew.h" -#include "pxr/imaging/glf/uvTextureData.h" -#include "pxr/imaging/glf/image.h" -#include "pxr/base/arch/fileSystem.h" - -#ifdef ENABLE_RAT -#include -#include -#endif - -#include -#include -#include - -namespace rpr { - -namespace { - -ImageDesc GetRprImageDesc(ImageFormat format, uint32_t width, uint32_t height, uint32_t depth = 1) { - int bytesPerComponent = 1; - if (format.type == RPR_COMPONENT_TYPE_FLOAT16) { - bytesPerComponent = 2; - } else if (format.type == RPR_COMPONENT_TYPE_FLOAT32) { - bytesPerComponent = 4; - } - - ImageDesc desc = {}; - desc.image_width = width; - desc.image_height = height; - desc.image_depth = depth; - desc.image_row_pitch = width * format.num_components * bytesPerComponent; - desc.image_slice_pitch = desc.image_row_pitch * height; - - return desc; -} - -} // namespace anonymous - -Image* CreateImage(Context* context, uint32_t width, uint32_t height, ImageFormat format, void const* data, rpr::Status* status) { - return context->CreateImage(format, GetRprImageDesc(format, width, height), data, status); -} - -Image* CreateImage(Context* context, char const* path, bool forceLinearSpace) { - PXR_NAMESPACE_USING_DIRECTIVE - -#ifdef ENABLE_RAT - auto dot = strrchr(path, '.'); - if (dot && strcmp(dot, ".rat") == 0) { - auto ratImage = std::unique_ptr(IMG_File::open(path)); - if (!ratImage) { - TF_RUNTIME_ERROR("Failed to load image %s", path); - } - - UT_Array images; - std::shared_ptr deferImagesRelease(nullptr, [&images](...) { - for (auto image : images) { - delete image; - } - }); - if (!ratImage->readImages(images) || - images.isEmpty()) { - TF_RUNTIME_ERROR("Failed to load image %s", path); - } - - // XXX: use the only first image, find out what to do with other images - auto image = images[0]; - - rpr_image_format format = {}; - if (image->getPacking() == PACK_SINGLE) { - format.num_components = 1; - } else if (image->getPacking() == PACK_DUAL) { - format.num_components = 2; - } else if (image->getPacking() == PACK_RGB) { - format.num_components = 3; - } else if (image->getPacking() == PACK_RGBA) { - format.num_components = 4; - } else { - TF_RUNTIME_ERROR("Failed to load image %s: unsupported RAT packing", path); - } - - if (image->getFormat() == PXL_INT8) { - format.type = RPR_COMPONENT_TYPE_UINT8; - } else if (image->getFormat() == PXL_FLOAT16) { - format.type = RPR_COMPONENT_TYPE_FLOAT16; - } else if (image->getFormat() == PXL_FLOAT32) { - format.type = RPR_COMPONENT_TYPE_FLOAT32; - } else { - TF_RUNTIME_ERROR("Failed to load image %s: unsupported RAT format", path); - } - - ImageDesc desc = GetRprImageDesc(format, image->getXres(), image->getYres()); - if (desc.image_height < 1 || - desc.image_width < 1) { - TF_RUNTIME_ERROR("Failed to load image %s: incorrect dimensions", path); - } - - // RAT image is flipped in Y axis - std::vector flippedImage; - flippedImage.reserve(image->getStride() * desc.image_height); - for (int y = 0; y < desc.image_height; ++y) { - auto srcData = reinterpret_cast(image->getPixels()) + image->getStride() * y; - auto dstData = &flippedImage[image->getStride() * (desc.image_height - 1 - y)]; - std::memcpy(dstData, srcData, image->getStride()); - } - - rpr::Status status; - auto rprImage = context->CreateImage(format, desc, flippedImage.data(), &status); - if (!rprImage) { - RPR_ERROR_CHECK(status, "Failed to create image from data", context); - return nullptr; - } - - if (!forceLinearSpace && - (image->getColorSpace() == PXL_CS_LINEAR || - image->getColorSpace() == PXL_CS_GAMMA2_2 || - image->getColorSpace() == PXL_CS_CUSTOM_GAMMA)) { - RPR_ERROR_CHECK(rprImage->SetGamma(image->getColorSpaceGamma()), "Failed to set image gamma", context); - } - - return rprImage; - } -#endif - - if (GlfImage::IsSupportedImageFile(path)) { - auto textureData = GlfUVTextureData::New(path, INT_MAX, 0, 0, 0, 0); - if (textureData && textureData->Read(0, false)) { - ImageFormat format = {}; - switch (textureData->GLType()) { - case GL_UNSIGNED_BYTE: - format.type = RPR_COMPONENT_TYPE_UINT8; - break; - case GL_HALF_FLOAT: - format.type = RPR_COMPONENT_TYPE_FLOAT16; - break; - case GL_FLOAT: - format.type = RPR_COMPONENT_TYPE_FLOAT32; - break; - default: - TF_RUNTIME_ERROR("Failed to create image %s. Unsupported pixel data GLtype: %#x", path, textureData->GLType()); - } - - switch (textureData->GLFormat()) { - case GL_RED: - format.num_components = 1; - break; - case GL_RGB: - format.num_components = 3; - break; - case GL_RGBA: - format.num_components = 4; - break; - default: - TF_RUNTIME_ERROR("Failed to create image %s. Unsupported pixel data GLformat: %#x", path, textureData->GLFormat()); - } - ImageDesc desc = GetRprImageDesc(format, textureData->ResizedWidth(), textureData->ResizedHeight()); - - rpr::Status status; - auto rprImage = context->CreateImage(format, desc, textureData->GetRawBuffer(), &status); - if (!rprImage) { - RPR_ERROR_CHECK(status, "Failed to create image from data", context); - return nullptr; - } - - auto internalFormat = textureData->GLInternalFormat(); - if (!forceLinearSpace && - (internalFormat == GL_SRGB || - internalFormat == GL_SRGB8 || - internalFormat == GL_SRGB_ALPHA || - internalFormat == GL_SRGB8_ALPHA8)) { - // XXX(RPR): sRGB formula is different from straight pow decoding, but it's the best we can do right now - RPR_ERROR_CHECK(rprImage->SetGamma(2.2f), "Failed to set image gamma"); - } - - return rprImage; - } - } - - return context->CreateImageFromFile(path); -} - -ImageFormat GetImageFormat(Image* image) { - return GetInfo(image, RPR_IMAGE_FORMAT); -} - -ImageDesc GetImageDesc(Image* image) { - return GetInfo(image, RPR_IMAGE_DESC); -} - -} // namespace rpr diff --git a/pxr/imaging/plugin/hdRpr/rprApi.cpp b/pxr/imaging/plugin/hdRpr/rprApi.cpp index fd6db4cd7..0eca02e52 100644 --- a/pxr/imaging/plugin/hdRpr/rprApi.cpp +++ b/pxr/imaging/plugin/hdRpr/rprApi.cpp @@ -11,9 +11,12 @@ See the License for the specific language governing permissions and limitations under the License. ************************************************************************/ +#include +using json = nlohmann::json; + #include "rprApi.h" #include "rprApiAov.h" -#include "materialFactory.h" +#include "aovDescriptor.h" #include "rifcpp/rifFilter.h" #include "rifcpp/rifImage.h" @@ -21,12 +24,24 @@ limitations under the License. #include "config.h" #include "camera.h" -#include "imageCache.h" -#include "materialAdapter.h" +#include "debugCodes.h" #include "renderDelegate.h" #include "renderBuffer.h" #include "renderParam.h" +#include "pxr/imaging/glf/glew.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" +#include "pxr/imaging/rprUsd/coreImage.h" +#include "pxr/imaging/rprUsd/imageCache.h" +#include "pxr/imaging/rprUsd/material.h" +#include "pxr/imaging/rprUsd/materialRegistry.h" +#include "pxr/imaging/rprUsd/contextMetadata.h" +#include "pxr/imaging/rprUsd/contextHelpers.h" + #include "pxr/base/gf/math.h" #include "pxr/base/gf/vec2f.h" #include "pxr/base/gf/rotation.h" @@ -34,67 +49,184 @@ limitations under the License. #include "pxr/base/plug/plugin.h" #include "pxr/base/plug/thisPlugin.h" #include "pxr/imaging/pxOsd/tokens.h" -#include "pxr/imaging/glf/glew.h" -#include "pxr/imaging/glf/uvTextureData.h" #include "pxr/usd/usdRender/tokens.h" #include "pxr/usd/usdGeom/tokens.h" #include "pxr/base/tf/envSetting.h" - -#include "rpr/contextHelpers.h" -#include "rpr/imageHelpers.h" -#include "rpr/error.h" +#include "pxr/base/tf/getenv.h" #include "notify/message.h" #include #include +#ifdef BUILD_AS_HOUDINI_PLUGIN +#include +#endif // BUILD_AS_HOUDINI_PLUGIN + #include +#include #include #include -#ifdef WIN32 -#include -#pragma comment(lib,"Shell32.lib") -#elif defined(__linux__) -#include -#include -#endif // __APPLE__ - PXR_NAMESPACE_OPEN_SCOPE -TF_DEFINE_ENV_SETTING(HDRPR_DISABLE_ALPHA, false, - "Disable alpha in color AOV. All alpha values would be 1.0"); - -TF_DEFINE_PRIVATE_TOKENS(HdRprAovTokens, - (albedo) \ - (variance) \ - (worldCoordinate) \ - (opacity) \ - ((primvarsSt, "primvars:st")) -); +TF_DEFINE_ENV_SETTING(HDRPR_RENDER_QUALITY_OVERRIDE, "", + "Set this to override render quality coming from the render settings"); namespace { -using RecursiveLockGuard = std::lock_guard; -std::recursive_mutex g_rprAccessMutex; +TfToken GetRenderQuality(HdRprConfig const& config) { + std::string renderQualityOverride = TfGetEnvSetting(HDRPR_RENDER_QUALITY_OVERRIDE); + + auto& tokens = HdRprRenderQualityTokens->allTokens; + if (std::find(tokens.begin(), tokens.end(), renderQualityOverride) != tokens.end()) { + return TfToken(renderQualityOverride); + } -bool ArchCreateDirectory(const char* path) { -#ifdef WIN32 - return CreateDirectory(path, NULL) == TRUE; -#else - return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0; -#endif + return config.GetRenderQuality(); } +using LockGuard = std::lock_guard; + template struct RenderSetting { T value; bool isDirty; }; +GfVec4f ToVec4(GfVec3f const& vec, float w) { + return GfVec4f(vec[0], vec[1], vec[2], w); +} + +RprUsdRenderDeviceType ToRprUsd(TfToken const& configDeviceType) { + if (configDeviceType == HdRprRenderDeviceTokens->CPU) { + return RprUsdRenderDeviceType::CPU; + } else if (configDeviceType == HdRprRenderDeviceTokens->GPU) { + return RprUsdRenderDeviceType::GPU; + } else { + return RprUsdRenderDeviceType::Invalid; + } +} + } // namespace anonymous +TfToken GetRprLpeAovName(rpr::Aov aov) { + switch (aov) { + case RPR_AOV_LPE_0: + return HdRprAovTokens->lpe0; + case RPR_AOV_LPE_1: + return HdRprAovTokens->lpe1; + case RPR_AOV_LPE_2: + return HdRprAovTokens->lpe2; + case RPR_AOV_LPE_3: + return HdRprAovTokens->lpe3; + case RPR_AOV_LPE_4: + return HdRprAovTokens->lpe4; + case RPR_AOV_LPE_5: + return HdRprAovTokens->lpe5; + case RPR_AOV_LPE_6: + return HdRprAovTokens->lpe6; + case RPR_AOV_LPE_7: + return HdRprAovTokens->lpe7; + case RPR_AOV_LPE_8: + return HdRprAovTokens->lpe8; + default: + return TfToken(); + } +} + +HdFormat ConvertUsdRenderVarDataType(TfToken const& format) { + static std::map s_mapping = []() { + std::map ret; + + auto addMappingEntry = [&ret](std::string name, HdFormat format) { + ret[TfToken(name, TfToken::Immortal)] = format; + }; + + addMappingEntry("int", HdFormatInt32); + addMappingEntry("half", HdFormatFloat16); + addMappingEntry("half3", HdFormatFloat16Vec3); + addMappingEntry("half4", HdFormatFloat16Vec4); + addMappingEntry("float", HdFormatFloat32); + addMappingEntry("float3", HdFormatFloat32Vec3); + addMappingEntry("float4", HdFormatFloat32Vec4); + addMappingEntry("point3f", HdFormatFloat32Vec3); + addMappingEntry("vector3f", HdFormatFloat32Vec3); + addMappingEntry("normal3f", HdFormatFloat32Vec3); + addMappingEntry("color2f", HdFormatFloat32Vec2); + addMappingEntry("color3f", HdFormatFloat32Vec3); + addMappingEntry("color4f", HdFormatFloat32Vec4); + addMappingEntry("float2", HdFormatFloat32Vec2); + addMappingEntry("float16", HdFormatFloat16); + addMappingEntry("color2h", HdFormatFloat16Vec2); + addMappingEntry("color3h", HdFormatFloat16Vec3); + addMappingEntry("color4h", HdFormatFloat16Vec4); + addMappingEntry("half2", HdFormatFloat16Vec2); + addMappingEntry("u8", HdFormatUNorm8); + addMappingEntry("uint8", HdFormatUNorm8); + addMappingEntry("color2u8", HdFormatUNorm8Vec2); + addMappingEntry("color3u8", HdFormatUNorm8Vec3); + addMappingEntry("color4u8", HdFormatUNorm8Vec4); + + return ret; + }(); + static std::string s_supportedFormats = []() { + std::string ret; + auto it = s_mapping.begin(); + for (size_t i = 0; i < s_mapping.size(); ++i, ++it) { + ret += it->first.GetString(); + if (i + 1 != s_mapping.size()) { + ret += ", "; + } + } + return ret; + }(); + + auto it = s_mapping.find(format); + if (it == s_mapping.end()) { + TF_RUNTIME_ERROR("Unsupported UsdRenderVar format. Supported formats: %s", s_supportedFormats.c_str()); + return HdFormatInvalid; + } + return it->second; +} + +class HdRprApiRawMaterial : public RprUsdMaterial { +public: + static HdRprApiRawMaterial* Create( + rpr::Context* rprContext, + rpr::MaterialNodeType nodeType, + std::vector> const& inputs) { + + rpr::Status status; + auto rprNode = rprContext->CreateMaterialNode(nodeType, &status); + if (!rprNode) { + RPR_ERROR_CHECK(status, "Failed to create material node", rprContext); + return nullptr; + } + + for (auto& input : inputs) { + auto& c = input.second; + if (RPR_ERROR_CHECK(rprNode->SetInput(input.first, c[0], c[1], c[2], c[3]), "Failed to set material input")) { + delete rprNode; + return nullptr; + } + }; + + return new HdRprApiRawMaterial(rprNode); + } + + ~HdRprApiRawMaterial() final = default; + +private: + HdRprApiRawMaterial(rpr::MaterialNode* surfaceNode) + : m_retainedSurfaceNode(surfaceNode) { + m_surfaceNode = surfaceNode; + } + +private: + std::unique_ptr m_retainedSurfaceNode; +}; + struct HdRprApiVolume { std::unique_ptr heteroVolume; std::unique_ptr volumeMaterial; @@ -102,13 +234,13 @@ struct HdRprApiVolume { std::unique_ptr densityGrid; std::unique_ptr emissionGrid; std::unique_ptr cubeMesh; - std::unique_ptr cubeMeshMaterial; + std::unique_ptr cubeMeshMaterial; GfMatrix4f voxelsTransform; }; struct HdRprApiEnvironmentLight { std::unique_ptr light; - std::unique_ptr image; + std::unique_ptr image; enum { kDetached, @@ -117,33 +249,25 @@ struct HdRprApiEnvironmentLight { } state = kDetached; }; -static const std::map kAovTokenToRprAov = { - {HdAovTokens->color, RPR_AOV_COLOR}, - {HdAovTokens->depth, RPR_AOV_DEPTH}, - {HdAovTokens->primId, RPR_AOV_OBJECT_ID}, - {HdAovTokens->normal, RPR_AOV_SHADING_NORMAL}, - {HdRprUtilsGetCameraDepthName(), RPR_AOV_DEPTH}, - {HdRprAovTokens->albedo, RPR_AOV_DIFFUSE_ALBEDO}, - {HdRprAovTokens->variance, RPR_AOV_VARIANCE}, - {HdRprAovTokens->worldCoordinate, RPR_AOV_WORLD_COORDINATE}, - {HdRprAovTokens->primvarsSt, RPR_AOV_UV}, - {HdRprAovTokens->opacity, RPR_AOV_OPACITY}, -}; - class HdRprApiImpl { public: - HdRprApiImpl(HdRenderDelegate* delegate) + HdRprApiImpl(HdRprDelegate* delegate) : m_delegate(delegate) { // Postpone initialization as further as possible to allow Hydra user to set custom render settings before creating a context //InitIfNeeded(); } + ~HdRprApiImpl() { + RemoveDefaultLight(); + } + void InitIfNeeded() { if (m_state != kStateUninitialized) { return; } - RecursiveLockGuard rprLock(g_rprAccessMutex); + static std::mutex s_rprInitMutex; + LockGuard lock(s_rprInitMutex); if (m_state != kStateUninitialized) { return; @@ -154,17 +278,13 @@ class HdRprApiImpl { InitRif(); InitScene(); InitCamera(); + InitAovs(); m_state = kStateRender; - } catch (rpr::Error& e) { - TF_RUNTIME_ERROR("%s", e.what()); - m_state = kStateInvalid; - } catch (rif::Error& e) { + } catch (RprUsdError& e) { TF_RUNTIME_ERROR("%s", e.what()); m_state = kStateInvalid; } - - UpdateRestartRequiredMessageStatus(); } rpr::Shape* CreateMesh(const VtVec3fArray& points, const VtIntArray& pointIndexes, @@ -181,7 +301,7 @@ class HdRprApiImpl { VtIntArray newNormalIndexes; if (normals.empty()) { - if (m_rprContextMetadata.pluginType == rpr::kPluginHybrid) { + if (m_rprContextMetadata.pluginType == kPluginHybrid) { // XXX (Hybrid): we need to generate geometry normals by ourself normals.reserve(newVpf.size()); newNormalIndexes.clear(); @@ -219,7 +339,7 @@ class HdRprApiImpl { VtIntArray newUvIndexes; if (uvs.empty()) { - if (m_rprContextMetadata.pluginType == rpr::kPluginHybrid) { + if (m_rprContextMetadata.pluginType == kPluginHybrid) { newUvIndexes = newIndexes; uvs = VtVec2fArray(points.size(), GfVec2f(0.0f)); } @@ -242,7 +362,7 @@ class HdRprApiImpl { uvIndicesData = nullptr; } - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); rpr::Status status; auto mesh = m_rprContext->CreateShape( @@ -271,7 +391,7 @@ class HdRprApiImpl { return nullptr; } - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); rpr::Status status; auto mesh = m_rprContext->CreateShapeInstance(prototype, &status); @@ -293,12 +413,12 @@ class HdRprApiImpl { return; } - if (m_rprContextMetadata.pluginType == rpr::kPluginHybrid) { + if (m_rprContextMetadata.pluginType == kPluginHybrid) { // Not supported return; } - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); bool dirty = true; @@ -319,7 +439,7 @@ class HdRprApiImpl { return; } - if (m_rprContextMetadata.pluginType == rpr::kPluginHybrid) { + if (m_rprContextMetadata.pluginType == kPluginHybrid) { // Not supported return; } @@ -328,7 +448,7 @@ class HdRprApiImpl { RPR_SUBDIV_BOUNDARY_INTERFOP_TYPE_EDGE_AND_CORNER : RPR_SUBDIV_BOUNDARY_INTERFOP_TYPE_EDGE_ONLY; - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); bool dirty = true; @@ -344,21 +464,29 @@ class HdRprApiImpl { } } - void SetMeshMaterial(rpr::Shape* mesh, HdRprApiMaterial const* material, bool doublesided, bool displacementEnabled) { - RecursiveLockGuard rprLock(g_rprAccessMutex); - m_materialFactory->AttachMaterial(mesh, material, doublesided, displacementEnabled); + void SetMeshMaterial(rpr::Shape* mesh, RprUsdMaterial const* material, bool displacementEnabled) { + LockGuard rprLock(m_rprContext->GetMutex()); + if (material) { + material->AttachTo(mesh, displacementEnabled); + } else { + RprUsdMaterial::DetachFrom(mesh); + } m_dirtyFlags |= ChangeTracker::DirtyScene; } - void SetCurveMaterial(rpr::Curve* curve, HdRprApiMaterial const* material) { - RecursiveLockGuard rprLock(g_rprAccessMutex); - m_materialFactory->AttachMaterial(curve, material); + void SetCurveMaterial(rpr::Curve* curve, RprUsdMaterial const* material) { + LockGuard rprLock(m_rprContext->GetMutex()); + if (material) { + material->AttachTo(curve); + } else { + RprUsdMaterial::DetachFrom(curve); + } m_dirtyFlags |= ChangeTracker::DirtyScene; } void SetCurveVisibility(rpr::Curve* curve, uint32_t visibilityMask) { - RecursiveLockGuard rprLock(g_rprAccessMutex); - if (m_rprContextMetadata.pluginType == rpr::kPluginHybrid) { + LockGuard rprLock(m_rprContext->GetMutex()); + if (m_rprContextMetadata.pluginType == kPluginHybrid) { // XXX (Hybrid): rprCurveSetVisibility not supported, emulate visibility using attach/detach if (visibilityMask) { m_scene->Attach(curve); @@ -382,7 +510,7 @@ class HdRprApiImpl { void Release(rpr::Curve* curve) { if (curve) { - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); if (!RPR_ERROR_CHECK(m_scene->Detach(curve), "Failed to detach curve from scene")) { m_dirtyFlags |= ChangeTracker::DirtyScene; @@ -393,7 +521,7 @@ class HdRprApiImpl { void Release(rpr::Shape* shape) { if (shape) { - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); if (!RPR_ERROR_CHECK(m_scene->Detach(shape), "Failed to detach mesh from scene")) { m_dirtyFlags |= ChangeTracker::DirtyScene; @@ -403,8 +531,8 @@ class HdRprApiImpl { } void SetMeshVisibility(rpr::Shape* mesh, uint32_t visibilityMask) { - RecursiveLockGuard rprLock(g_rprAccessMutex); - if (m_rprContextMetadata.pluginType == rpr::kPluginHybrid) { + LockGuard rprLock(m_rprContext->GetMutex()); + if (m_rprContextMetadata.pluginType == kPluginHybrid) { // XXX (Hybrid): rprShapeSetVisibility not supported, emulate visibility using attach/detach if (visibilityMask) { m_scene->Attach(mesh); @@ -428,7 +556,7 @@ class HdRprApiImpl { } void SetMeshId(rpr::Shape* mesh, uint32_t id) { - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); RPR_ERROR_CHECK(mesh->SetObjectID(id), "Failed to set mesh id"); } @@ -454,7 +582,7 @@ class HdRprApiImpl { creationFlags |= rpr::kCurveCreationFlagTapered; } - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); rpr::Status status; auto curve = m_rprContext->CreateCurve( @@ -480,7 +608,7 @@ class HdRprApiImpl { return nullptr; } - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); rpr::Status status; auto light = creator(&status); @@ -495,6 +623,7 @@ class HdRprApiImpl { } m_dirtyFlags |= ChangeTracker::DirtyScene; + m_numLights++; return light; } @@ -522,6 +651,18 @@ class HdRprApiImpl { }); } + rpr::DiskLight* CreateDiskLight() { + return CreateLight([this](rpr::Status* status) { + return m_rprContext->CreateDiskLight(status); + }); + } + + rpr::SphereLight* CreateSphereLight() { + return CreateLight([this](rpr::Status* status) { + return m_rprContext->CreateSphereLight(status); + }); + } + rpr::IESLight* CreateIESLight(std::string const& iesFilepath) { return CreateLight([this, &iesFilepath](rpr::Status* status) { auto light = m_rprContext->CreateIESLight(status); @@ -540,31 +681,64 @@ class HdRprApiImpl { } void SetDirectionalLightAttributes(rpr::DirectionalLight* light, GfVec3f const& color, float shadowSoftnessAngle) { - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); RPR_ERROR_CHECK(light->SetRadiantPower(color[0], color[1], color[2]), "Failed to set directional light color"); RPR_ERROR_CHECK(light->SetShadowSoftnessAngle(GfClamp(shadowSoftnessAngle, 0.0f, float(M_PI_4))), "Failed to set directional light color"); } template - void SetLightColor(Light* light, GfVec3f const& color) { - RecursiveLockGuard rprLock(g_rprAccessMutex); + void SetLightRadius(Light* light, float radius) { + LockGuard rprLock(m_rprContext->GetMutex()); + + RPR_ERROR_CHECK(light->SetRadius(radius), "Failed to set light radius"); + } + + void SetLightAngle(rpr::DiskLight* light, float angle) { + LockGuard rprLock(m_rprContext->GetMutex()); + + RPR_ERROR_CHECK(light->SetAngle(angle), "Failed to set light angle"); + } + + void SetLightColor(rpr::RadiantLight* light, GfVec3f const& color) { + LockGuard rprLock(m_rprContext->GetMutex()); RPR_ERROR_CHECK(light->SetRadiantPower(color[0], color[1], color[2]), "Failed to set light color"); } void Release(rpr::Light* light) { if (light) { - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); if (!RPR_ERROR_CHECK(m_scene->Detach(light), "Failed to detach light from scene")) { m_dirtyFlags |= ChangeTracker::DirtyScene; } delete light; + m_numLights--; + } + } + + RprUsdMaterial* CreateGeometryLightMaterial(GfVec3f const& emissionColor) { + LockGuard rprLock(m_rprContext->GetMutex()); + + auto material = HdRprApiRawMaterial::Create(m_rprContext.get(), RPR_MATERIAL_NODE_EMISSIVE, { + {RPR_MATERIAL_INPUT_COLOR, ToVec4(emissionColor, 1.0f)} + }); + if (material) m_numLights++; + return material; + } + + void ReleaseGeometryLightMaterial(RprUsdMaterial* material) { + if (material) { + m_numLights--; + Release(material); } } - HdRprApiEnvironmentLight* CreateEnvironmentLight(std::unique_ptr&& image, float intensity) { + HdRprApiEnvironmentLight* CreateEnvironmentLight(std::unique_ptr&& image, float intensity) { + // XXX (RPR): default environment light should be removed before creating a new one - RPR limitation + RemoveDefaultLight(); + auto envLight = new HdRprApiEnvironmentLight; rpr::Status status; @@ -572,14 +746,14 @@ class HdRprApiImpl { envLight->image = std::move(image); if (!envLight || - RPR_ERROR_CHECK(envLight->light->SetImage(envLight->image.get()), "Failed to set env light image", m_rprContext.get()) || + RPR_ERROR_CHECK(envLight->light->SetImage(envLight->image->GetRootImage()), "Failed to set env light image", m_rprContext.get()) || RPR_ERROR_CHECK(envLight->light->SetIntensityScale(intensity), "Failed to set env light intensity", m_rprContext.get())) { RPR_ERROR_CHECK(status, "Failed to create environment light"); delete envLight; return nullptr; } - if (m_rprContextMetadata.pluginType == rpr::kPluginHybrid) { + if (m_rprContextMetadata.pluginType == kPluginHybrid) { if ((status = m_scene->SetEnvironmentLight(envLight->light.get())) == RPR_SUCCESS) { envLight->state = HdRprApiEnvironmentLight::kAttachedAsEnvLight; } @@ -595,13 +769,12 @@ class HdRprApiImpl { } m_dirtyFlags |= ChangeTracker::DirtyScene; + m_numLights++; return envLight; } - void Release(HdRprApiEnvironmentLight* envLight) { + void ReleaseImpl(HdRprApiEnvironmentLight* envLight) { if (envLight) { - RecursiveLockGuard rprLock(g_rprAccessMutex); - rpr::Status status; if (envLight->state == HdRprApiEnvironmentLight::kAttachedAsEnvLight) { status = m_scene->SetEnvironmentLight(nullptr); @@ -613,6 +786,14 @@ class HdRprApiImpl { m_dirtyFlags |= ChangeTracker::DirtyScene; } delete envLight; + m_numLights--; + } + } + + void Release(HdRprApiEnvironmentLight* envLight) { + if (envLight) { + LockGuard rprLock(m_rprContext->GetMutex()); + ReleaseImpl(envLight); } } @@ -621,9 +802,9 @@ class HdRprApiImpl { return nullptr; } - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); - auto image = std::unique_ptr(rpr::CreateImage(m_rprContext.get(), path.c_str())); + auto image = std::unique_ptr(RprUsdCoreImage::Create(m_rprContext.get(), path)); if (!image) { return nullptr; } @@ -638,11 +819,13 @@ class HdRprApiImpl { std::array backgroundColor = {color[0], color[1], color[2]}; rpr_image_format format = {3, RPR_COMPONENT_TYPE_FLOAT32}; - rpr_uint imageSize = m_rprContextMetadata.pluginType == rpr::kPluginHybrid ? 64 : 1; + 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(rpr::CreateImage(m_rprContext.get(), imageSize, imageSize, format, imageData.data(), &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()); return nullptr; @@ -652,7 +835,7 @@ class HdRprApiImpl { } void SetTransform(rpr::SceneObject* object, GfMatrix4f const& transform) { - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); if (!RPR_ERROR_CHECK(object->SetTransform(transform.GetArray(), false), "Fail set object transform")) { m_dirtyFlags |= ChangeTracker::DirtyScene; } @@ -757,51 +940,132 @@ class HdRprApiImpl { } void SetTransform(rpr::Shape* shape, size_t numSamples, float* timeSamples, GfMatrix4d* transformSamples) { + // TODO: Implement C++ wrapper methods + auto rprShapeHandle = rpr::GetRprObject(shape); + if (numSamples == 1) { + RPR_ERROR_CHECK(rprShapeSetMotionTransformCount(rprShapeHandle, 0), "Failed to set shape motion transform count"); + return SetTransform(shape, GfMatrix4f(transformSamples[0])); } - // XXX (RPR): there is no way to sample all transforms via current RPR API + // XXX (RPR): for the moment, RPR supports only 1 motion matrix auto& startTransform = transformSamples[0]; auto& endTransform = transformSamples[numSamples - 1]; - GfVec3f linearMotion, scaleMotion, rotateAxis; - float rotateAngle; - GetMotion(startTransform, endTransform, &linearMotion, &scaleMotion, &rotateAxis, &rotateAngle); - auto rprStartTransform = GfMatrix4f(startTransform); - RecursiveLockGuard rprLock(g_rprAccessMutex); + if (m_rprContextMetadata.pluginType == kPluginNorthstar) { + LockGuard rprLock(m_rprContext->GetMutex()); + RPR_ERROR_CHECK(shape->SetTransform(rprStartTransform.GetArray(), false), "Fail set shape transform"); - RPR_ERROR_CHECK(shape->SetTransform(rprStartTransform.GetArray(), false), "Fail set shape transform"); - RPR_ERROR_CHECK(shape->SetLinearMotion(linearMotion[0], linearMotion[1], linearMotion[2]), "Fail to set shape linear motion"); - RPR_ERROR_CHECK(shape->SetScaleMotion(scaleMotion[0], scaleMotion[1], scaleMotion[2]), "Fail to set shape scale motion"); - RPR_ERROR_CHECK(shape->SetAngularMotion(rotateAxis[0], rotateAxis[1], rotateAxis[2], rotateAngle), "Fail to set shape angular motion"); + auto rprEndTransform = GfMatrix4f(endTransform); + RPR_ERROR_CHECK(rprShapeSetMotionTransformCount(rprShapeHandle, 1), "Failed to set shape motion transform count"); + RPR_ERROR_CHECK(rprShapeSetMotionTransform(rprShapeHandle, false, rprEndTransform.GetArray(), 1), "Failed to set shape motion transform count"); + } else { + GfVec3f linearMotion, scaleMotion, rotateAxis; + float rotateAngle; + GetMotion(startTransform, endTransform, &linearMotion, &scaleMotion, &rotateAxis, &rotateAngle); + + LockGuard rprLock(m_rprContext->GetMutex()); + + RPR_ERROR_CHECK(shape->SetTransform(rprStartTransform.GetArray(), false), "Fail set shape transform"); + RPR_ERROR_CHECK(shape->SetLinearMotion(linearMotion[0], linearMotion[1], linearMotion[2]), "Fail to set shape linear motion"); + RPR_ERROR_CHECK(shape->SetScaleMotion(scaleMotion[0], scaleMotion[1], scaleMotion[2]), "Fail to set shape scale motion"); + RPR_ERROR_CHECK(shape->SetAngularMotion(rotateAxis[0], rotateAxis[1], rotateAxis[2], rotateAngle), "Fail to set shape angular motion"); + } m_dirtyFlags |= ChangeTracker::DirtyScene; } - HdRprApiMaterial* CreateMaterial(const MaterialAdapter& MaterialAdapter) { + RprUsdMaterial* CreateMaterial(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()); + } + + RprUsdMaterial* CreatePointsMaterial(VtVec3fArray const& colors) { if (!m_rprContext) { return nullptr; } - RecursiveLockGuard rprLock(g_rprAccessMutex); - return m_materialFactory->CreateMaterial(MaterialAdapter.GetType(), MaterialAdapter); + LockGuard rprLock(m_rprContext->GetMutex()); + + class HdRprApiPointsMaterial : public RprUsdMaterial { + public: + HdRprApiPointsMaterial(VtVec3fArray const& colors, rpr::Context* context) { + rpr::Status status; + + rpr::BufferDesc bufferDesc; + bufferDesc.nb_element = colors.size(); + bufferDesc.element_type = RPR_BUFFER_ELEMENT_TYPE_FLOAT32; + bufferDesc.element_channel_size = 3; + + m_colorsBuffer.reset(context->CreateBuffer(bufferDesc, colors.data(), &status)); + if (!m_colorsBuffer) { + RPR_ERROR_CHECK_THROW(status, "Failed to create colors buffer"); + } + + m_objectIdLookupNode.reset(context->CreateMaterialNode(RPR_MATERIAL_NODE_INPUT_LOOKUP, &status)); + if (!m_objectIdLookupNode) { + RPR_ERROR_CHECK_THROW(status, "Failed to create objectId lookup node"); + } + + status = m_objectIdLookupNode->SetInput(RPR_MATERIAL_INPUT_VALUE, RPR_MATERIAL_NODE_LOOKUP_OBJECT_ID); + if (status != RPR_SUCCESS) { + RPR_ERROR_CHECK_THROW(status, "Failed to set lookup node input value"); + } + + m_bufferSamplerNode.reset(context->CreateMaterialNode(RPR_MATERIAL_NODE_BUFFER_SAMPLER, &status)); + if (!m_bufferSamplerNode) { + RPR_ERROR_CHECK_THROW(status, "Failed to create buffer sampler node"); + } + RPR_ERROR_CHECK_THROW(m_bufferSamplerNode->SetInput(RPR_MATERIAL_INPUT_DATA, m_colorsBuffer.get()), "Failed to set buffer sampler node input data"); + RPR_ERROR_CHECK_THROW(m_bufferSamplerNode->SetInput(RPR_MATERIAL_INPUT_UV, m_objectIdLookupNode.get()), "Failed to set buffer sampler node input uv"); + + m_uberNode.reset(context->CreateMaterialNode(RPR_MATERIAL_NODE_UBERV2, &status)); + if (!m_uberNode) { + RPR_ERROR_CHECK_THROW(status, "Failed to create uber node"); + } + RPR_ERROR_CHECK_THROW(m_uberNode->SetInput(RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR, m_bufferSamplerNode.get()), "Failed to set root material diffuse color"); + + m_surfaceNode = m_uberNode.get(); + } + + ~HdRprApiPointsMaterial() final = default; + + private: + std::unique_ptr m_colorsBuffer; + std::unique_ptr m_objectIdLookupNode; + std::unique_ptr m_bufferSamplerNode; + std::unique_ptr m_uberNode; + }; + + try { + return new HdRprApiPointsMaterial(colors, m_rprContext.get()); + } catch (RprUsdError& e) { + TF_RUNTIME_ERROR("Failed to create points material: %s", e.what()); + return nullptr; + } } - HdRprApiMaterial* CreatePointsMaterial(VtVec3fArray const& colors) { + RprUsdMaterial* CreateRawMaterial( + rpr::MaterialNodeType nodeType, + std::vector> const& inputs) { if (!m_rprContext) { return nullptr; } - RecursiveLockGuard rprLock(g_rprAccessMutex); - return m_materialFactory->CreatePointsMaterial(colors); + LockGuard rprLock(m_rprContext->GetMutex()); + return HdRprApiRawMaterial::Create(m_rprContext.get(), nodeType, inputs); } - void Release(HdRprApiMaterial* material) { + void Release(RprUsdMaterial* material) { if (material) { - RecursiveLockGuard rprLock(g_rprAccessMutex); - m_materialFactory->Release(material); + LockGuard rprLock(m_rprContext->GetMutex()); + delete material; } } @@ -813,14 +1077,19 @@ class HdRprApiImpl { return nullptr; } - RecursiveLockGuard rprLock(g_rprAccessMutex); + auto cubeMesh = CreateCubeMesh(1.0f, 1.0f, 1.0f); + if (!cubeMesh) { + return nullptr; + } + + LockGuard rprLock(m_rprContext->GetMutex()); auto rprApiVolume = new HdRprApiVolume; - MaterialAdapter matAdapter(EMaterialType::TRANSPERENT, - MaterialParams{{HdPrimvarRoleTokens->color, VtValue(GfVec4f(1.0f))}}); - rprApiVolume->cubeMeshMaterial.reset(CreateMaterial(matAdapter)); - rprApiVolume->cubeMesh.reset(CreateCubeMesh(1.0f, 1.0f, 1.0f)); + rprApiVolume->cubeMeshMaterial.reset(HdRprApiRawMaterial::Create(m_rprContext.get(), RPR_MATERIAL_NODE_TRANSPARENT, { + {RPR_MATERIAL_INPUT_COLOR, GfVec4f(1.0f)} + })); + rprApiVolume->cubeMesh.reset(cubeMesh); rpr::Status densityGridStatus; rprApiVolume->densityGrid.reset(m_rprContext->CreateGrid(gridSize[0], gridSize[1], gridSize[2], @@ -863,7 +1132,10 @@ class HdRprApiImpl { RPR_ERROR_CHECK(albedoGridStatus, "Failed to create albedo grid"); RPR_ERROR_CHECK(emissionGridStatus, "Failed to create emission grid"); RPR_ERROR_CHECK(status, "Failed to create hetero volume"); + + m_scene->Detach(rprApiVolume->heteroVolume.get()); delete rprApiVolume; + return nullptr; } @@ -892,7 +1164,7 @@ class HdRprApiImpl { if (rprApiVolume->volumeMaterial) { RPR_ERROR_CHECK(rprApiVolume->cubeMesh->SetVolumeMaterial(rprApiVolume->volumeMaterial.get()), "Failed to set volume material"); } - SetMeshMaterial(rprApiVolume->cubeMesh.get(), rprApiVolume->cubeMeshMaterial.get(), true, false); + rprApiVolume->cubeMeshMaterial->AttachTo(rprApiVolume->cubeMesh.get(), false); rprApiVolume->voxelsTransform = GfMatrix4f(1.0f); rprApiVolume->voxelsTransform.SetScale(GfCompMult(voxelSize, gridSize)); @@ -904,7 +1176,7 @@ class HdRprApiImpl { void SetTransform(HdRprApiVolume* volume, GfMatrix4f const& transform) { auto t = transform * volume->voxelsTransform; - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); RPR_ERROR_CHECK(volume->cubeMesh->SetTransform(t.data(), false), "Failed to set cubeMesh transform"); RPR_ERROR_CHECK(volume->heteroVolume->SetTransform(t.data(), false), "Failed to set heteroVolume transform"); m_dirtyFlags |= ChangeTracker::DirtyScene; @@ -912,7 +1184,7 @@ class HdRprApiImpl { void Release(HdRprApiVolume* volume) { if (volume) { - RecursiveLockGuard rprLock(g_rprAccessMutex); + LockGuard rprLock(m_rprContext->GetMutex()); m_scene->Detach(volume->heteroVolume.get()); delete volume; @@ -921,6 +1193,22 @@ class HdRprApiImpl { } } + void SetName(rpr::ContextObject* object, const char* name) { + LockGuard rprLock(m_rprContext->GetMutex()); + object->SetName(name); + } + + void SetName(RprUsdMaterial* object, const char* name) { + LockGuard rprLock(m_rprContext->GetMutex()); + object->SetName(name); + } + + void SetName(HdRprApiEnvironmentLight* object, const char* name) { + LockGuard rprLock(m_rprContext->GetMutex()); + object->light->SetName(name); + object->image->SetName(name); + } + void SetCamera(HdCamera const* camera) { auto hdRprCamera = dynamic_cast(camera); if (!hdRprCamera) { @@ -928,8 +1216,6 @@ class HdRprApiImpl { return; } - RecursiveLockGuard rprLock(g_rprAccessMutex); - if (m_hdCamera != hdRprCamera) { m_hdCamera = hdRprCamera; m_dirtyFlags |= ChangeTracker::DirtyHdCamera; @@ -953,60 +1239,126 @@ class HdRprApiImpl { } void SetViewportSize(GfVec2i const& size) { - RecursiveLockGuard rprLock(g_rprAccessMutex); - m_viewportSize = size; m_dirtyFlags |= ChangeTracker::DirtyViewport; } void SetAovBindings(HdRenderPassAovBindingVector const& aovBindings) { - RecursiveLockGuard rprLock(g_rprAccessMutex); - m_aovBindings = aovBindings; m_dirtyFlags |= ChangeTracker::DirtyAOVBindings; + + auto retainedOutputRenderBuffers = std::move(m_outputRenderBuffers); + auto registerAovBinding = [&retainedOutputRenderBuffers, this](HdRenderPassAovBinding const& aovBinding) -> OutputRenderBuffer* { + OutputRenderBuffer outRb; + outRb.aovBinding = &aovBinding; + + HdFormat aovFormat; + if (!GetAovBindingInfo(aovBinding, &outRb.aovName, &outRb.lpe, &aovFormat)) { + return nullptr; + } + + decltype(retainedOutputRenderBuffers.begin()) outputRenderBufferIt; + if (outRb.lpe.empty()) { + outputRenderBufferIt = std::find_if(retainedOutputRenderBuffers.begin(), retainedOutputRenderBuffers.end(), + [&outRb](OutputRenderBuffer const& buffer) { return buffer.aovName == outRb.aovName; }); + } else { + outputRenderBufferIt = std::find_if(retainedOutputRenderBuffers.begin(), retainedOutputRenderBuffers.end(), + [&outRb](OutputRenderBuffer const& buffer) { return buffer.lpe == outRb.lpe; }); + } + + auto rprRenderBuffer = static_cast(aovBinding.renderBuffer); + if (outputRenderBufferIt == retainedOutputRenderBuffers.end()) { + if (!outRb.lpe.empty()) { + // We can bind limited amount of LPE AOVs with RPR. + // lpeAovPool controls available AOV binding slots. + if (m_lpeAovPool.empty()) { + TF_RUNTIME_ERROR("Cannot create \"%s\" LPE AOV: exceeded the number of LPE AOVs at the same time - %d", + outRb.lpe.c_str(), +RPR_AOV_LPE_8 - +RPR_AOV_LPE_0 + 1); + return nullptr; + } + + outRb.aovName = m_lpeAovPool.back(); + m_lpeAovPool.pop_back(); + } + + // Create new RPR AOV + outRb.rprAov = CreateAov(outRb.aovName, rprRenderBuffer->GetWidth(), rprRenderBuffer->GetHeight(), aovFormat); + } else if (outputRenderBufferIt->rprAov) { + // Reuse previously created RPR AOV + std::swap(outRb.rprAov, outputRenderBufferIt->rprAov); + // Update underlying format if needed + outRb.rprAov->Resize(rprRenderBuffer->GetWidth(), rprRenderBuffer->GetHeight(), aovFormat); + } + + if (!outRb.rprAov) return nullptr; + + if (!outRb.lpe.empty() && + RPR_ERROR_CHECK(outRb.rprAov->GetAovFb()->GetRprObject()->SetLPE(outRb.lpe.c_str()), "Failed to set LPE")) { + return nullptr; + } + + m_outputRenderBuffers.push_back(std::move(outRb)); + return &m_outputRenderBuffers.back(); + }; + + for (auto& aovBinding : m_aovBindings) { + if (!aovBinding.renderBuffer) { + continue; + } + + if (auto outputRb = registerAovBinding(aovBinding)) { + auto rprRenderBuffer = static_cast(aovBinding.renderBuffer); + outputRb->isMultiSampled = rprRenderBuffer->IsMultiSampled(); + outputRb->mappedData = rprRenderBuffer->GetPointerForWriting(); + outputRb->mappedDataSize = HdDataSizeOfFormat(rprRenderBuffer->GetFormat()) * rprRenderBuffer->GetWidth() * rprRenderBuffer->GetHeight(); + } + } } HdRenderPassAovBindingVector const& GetAovBindings() const { return m_aovBindings; } - void ResolveFramebuffers(std::vector> const& outputRenderBuffers) { - for (auto& aovEntry : m_aovRegistry) { - auto aov = aovEntry.second.lock(); - if (TF_VERIFY(aov)) { - aov->Resolve(); + void ResolveFramebuffers() { + auto startTime = std::chrono::high_resolution_clock::now(); + + m_resolveData.ForAllAovs([&](ResolveData::AovEntry& e) { + if (m_isFirstSample || e.isMultiSampled) { + e.aov->Resolve(); } - } + }); if (m_rifContext) { m_rifContext->ExecuteCommandQueue(); } - for (int i = 0; i < m_aovBindings.size(); ++i) { - if (outputRenderBuffers[i].first) { - auto aovIter = m_boundAovs.find(m_aovBindings[i].aovName); - if (aovIter != m_boundAovs.end()) { - aovIter->second->GetData(outputRenderBuffers[i].first, outputRenderBuffers[i].second); - } + for (auto& outRb : m_outputRenderBuffers) { + if (outRb.mappedData && (m_isFirstSample || outRb.isMultiSampled)) { + outRb.rprAov->GetData(outRb.mappedData, outRb.mappedDataSize); } } - } - void Update() { - RecursiveLockGuard rprLock(g_rprAccessMutex); + m_isFirstSample = false; + + auto resolveTime = std::chrono::high_resolution_clock::now() - startTime; + m_frameResolveTotalTime += resolveTime; - m_imageCache->GarbageCollectIfNeeded(); + if (m_resolveMode == kResolveInRenderUpdateCallback) { + // When RUC is enabled, we do resolves in between of rendering on the same thread + // TODO (optimization): move resolves in a background thread (makes sense for non-interactive, GPU-only rendering) + // + m_frameRenderTotalTime -= resolveTime; + } + } + void Update() { auto rprRenderParam = static_cast(m_delegate->GetRenderParam()); // In case there is no Lights in scene - create default - if (!rprRenderParam->HasLights()) { - if (!m_defaultLightObject) { - const GfVec3f k_defaultLightColor(0.5f, 0.5f, 0.5f); - m_defaultLightObject.reset(CreateEnvironmentLight(k_defaultLightColor, 1.f)); - } + if (m_numLights == 0) { + AddDefaultLight(); } else { - m_defaultLightObject = nullptr; + RemoveDefaultLight(); } bool clearAovs = false; @@ -1026,7 +1378,7 @@ class HdRprApiImpl { tonemap.isDirty = config->IsDirty(HdRprConfig::DirtyTonemapping); if (tonemap.isDirty) { tonemap.value.enable = config->GetEnableTonemap(); - tonemap.value.exposure = config->GetTonemapExposure(); + tonemap.value.exposureTime = config->GetTonemapExposureTime(); tonemap.value.sensitivity = config->GetTonemapSensitivity(); tonemap.value.fstop = config->GetTonemapFstop(); tonemap.value.gamma = config->GetTonemapGamma(); @@ -1037,21 +1389,27 @@ class HdRprApiImpl { instantaneousShutter.isDirty = config->IsDirty(HdRprConfig::DirtyUsdNativeCamera); instantaneousShutter.value = config->GetInstantaneousShutter(); + if (config->IsDirty(HdRprConfig::DirtyRprExport)) { + m_rprSceneExportPath = config->GetRprExportPath(); + } + + if (config->IsDirty(HdRprConfig::DirtyRenderQuality)) { + m_currentRenderQuality = GetRenderQuality(*config); + } + if (config->IsDirty(HdRprConfig::DirtyDevice) || config->IsDirty(HdRprConfig::DirtyRenderQuality)) { bool restartRequired = false; if (config->IsDirty(HdRprConfig::DirtyDevice)) { - if (int(m_rprContextMetadata.renderDeviceType) != config->GetRenderDevice()) { + if (m_rprContextMetadata.renderDeviceType != ToRprUsd(config->GetRenderDevice())) { restartRequired = true; } } if (config->IsDirty(HdRprConfig::DirtyRenderQuality)) { - auto quality = config->GetRenderQuality(); - + auto newPlugin = GetPluginType(m_currentRenderQuality); auto activePlugin = m_rprContextMetadata.pluginType; - if ((activePlugin == rpr::kPluginTahoe && quality < kRenderQualityFull) || - (activePlugin == rpr::kPluginHybrid && quality == kRenderQualityFull)) { + if (newPlugin != activePlugin) { restartRequired = true; } } @@ -1060,17 +1418,25 @@ class HdRprApiImpl { } if (m_state == kStateRender && config->IsDirty(HdRprConfig::DirtyRenderQuality)) { - RenderQualityType currentRenderQuality; - if (m_rprContextMetadata.pluginType == rpr::kPluginTahoe) { - currentRenderQuality = kRenderQualityFull; + TfToken activeRenderQuality; + if (m_rprContextMetadata.pluginType == kPluginTahoe) { + activeRenderQuality = HdRprRenderQualityTokens->Full; + } else if (m_rprContextMetadata.pluginType == kPluginNorthstar) { + activeRenderQuality = HdRprRenderQualityTokens->Northstar; } else { rpr_uint currentHybridQuality = RPR_RENDER_QUALITY_HIGH; size_t dummy; RPR_ERROR_CHECK(m_rprContext->GetInfo(rpr::ContextInfo(RPR_CONTEXT_RENDER_QUALITY), sizeof(currentHybridQuality), ¤tHybridQuality, &dummy), "Failed to query current render quality"); - currentRenderQuality = static_cast(currentHybridQuality); + if (currentHybridQuality == RPR_RENDER_QUALITY_LOW) { + activeRenderQuality = HdRprRenderQualityTokens->Low; + } else if (currentHybridQuality == RPR_RENDER_QUALITY_MEDIUM) { + activeRenderQuality = HdRprRenderQualityTokens->Medium; + } else { + activeRenderQuality = HdRprRenderQualityTokens->High; + } } - clearAovs = currentRenderQuality != config->GetRenderQuality(); + clearAovs = activeRenderQuality != m_currentRenderQuality; } UpdateSettings(*config); @@ -1085,13 +1451,32 @@ 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}, + }; + + auto it = s_mapping.find(mode); + if (it == s_mapping.end()) return RPR_RENDER_MODE_GLOBAL_ILLUMINATION; + return it->second; + } + void UpdateTahoeSettings(HdRprConfig const& preferences, bool force) { if (preferences.IsDirty(HdRprConfig::DirtyAdaptiveSampling) || force) { m_varianceThreshold = preferences.GetVarianceThreshold(); + m_minSamples = preferences.GetMinAdaptiveSamples(); RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_ADAPTIVE_SAMPLING_THRESHOLD, m_varianceThreshold), "Failed to set as.threshold"); - RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_ADAPTIVE_SAMPLING_MIN_SPP, preferences.GetMinAdaptiveSamples()), "Failed to set as.minspp"); + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_ADAPTIVE_SAMPLING_MIN_SPP, m_minSamples), "Failed to set as.minspp"); - if (m_varianceThreshold > 0.0f) { + if (IsAdaptiveSamplingEnabled()) { if (!m_internalAovs.count(HdRprAovTokens->variance)) { if (auto aov = CreateAov(HdRprAovTokens->variance, m_viewportSize[0], m_viewportSize[1])) { m_internalAovs.emplace(HdRprAovTokens->variance, std::move(aov)); @@ -1121,21 +1506,81 @@ class HdRprApiImpl { m_dirtyFlags |= ChangeTracker::DirtyScene; } - if (preferences.IsDirty(HdRprConfig::DirtyInteractiveMode)) { - bool is_interactive = preferences.GetInteractiveMode(); - auto maxRayDepth = is_interactive ? preferences.GetInteractiveMaxRayDepth() : preferences.GetMaxRayDepth(); + if ((preferences.IsDirty(HdRprConfig::DirtyInteractiveMode) || + preferences.IsDirty(HdRprConfig::DirtyInteractiveQuality)) || force) { + m_isInteractive = preferences.GetInteractiveMode(); + auto maxRayDepth = m_isInteractive ? preferences.GetInteractiveMaxRayDepth() : preferences.GetMaxRayDepth(); RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_MAX_RECURSION, maxRayDepth), "Failed to set max recursion"); - RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_PREVIEW, int(is_interactive)), "Failed to set preview mode"); + if (m_rprContextMetadata.pluginType == kPluginNorthstar) { + int downscale = 0; + if (m_isInteractive) { + downscale = preferences.GetInteractiveResolutionDownscale(); + } + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_PREVIEW, uint32_t(downscale)), "Failed to set preview mode"); + } else { + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_PREVIEW, uint32_t(m_isInteractive)), "Failed to set preview mode"); + } + + if (preferences.IsDirty(HdRprConfig::DirtyInteractiveMode) || m_isInteractive) { + m_dirtyFlags |= ChangeTracker::DirtyScene; + } + } + + if (preferences.IsDirty(HdRprConfig::DirtyRenderMode) || force) { + auto& renderMode = preferences.GetRenderMode(); + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_RENDER_MODE, GetRprRenderMode(renderMode)), "Failed to set render mode"); + if (renderMode == HdRprRenderModeTokens->AmbientOcclusion) { + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_AO_RAY_LENGTH, preferences.GetAoRadius()), "Failed to set ambient occlusion radius"); + } + m_dirtyFlags |= ChangeTracker::DirtyScene; + } + + if (preferences.IsDirty(HdRprConfig::DirtySeed) || force) { + m_isUniformSeed = preferences.GetUniformSeed(); + m_frameCount = 0; m_dirtyFlags |= ChangeTracker::DirtyScene; } + + if (m_rprContextMetadata.pluginType == kPluginNorthstar) { + if (preferences.IsDirty(HdRprConfig::DirtyMotionBlur) || force) { + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_BEAUTY_MOTION_BLUR, uint32_t(preferences.GetEnableBeautyMotionBlur())), "Failed to set beauty motion blur"); + m_dirtyFlags |= ChangeTracker::DirtyScene; + } + + if (preferences.IsDirty(HdRprConfig::DirtyOCIO) || force) { + std::string ocioConfigPath; + + // OpenColorIO doc recommends to use `OCIO` environment variable to + // globally define path to OCIO config. See the docs for details about the motivation for this. + // Houdini handles it in the same while leaving possibility to override it through UI. + // We allow the OCIO config path to be overridden through render settings. + if (!preferences.GetOcioConfigPath().empty()) { + ocioConfigPath = preferences.GetOcioConfigPath(); + } else { + ocioConfigPath = TfGetenv("OCIO"); + } + + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_OCIO_CONFIG_PATH, ocioConfigPath.c_str()), "Faled to set OCIO config path"); + 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; + } + } } void UpdateHybridSettings(HdRprConfig const& preferences, bool force) { if (preferences.IsDirty(HdRprConfig::DirtyRenderQuality) || force) { - auto quality = preferences.GetRenderQuality(); - if (quality < kRenderQualityFull) { - RPR_ERROR_CHECK(m_rprContext->SetParameter(rpr::ContextInfo(RPR_CONTEXT_RENDER_QUALITY), int(quality)), "Fail to set context hybrid render quality"); + rpr_uint hybridRenderQuality = -1; + if (m_currentRenderQuality == HdRprRenderQualityTokens->High) { + hybridRenderQuality = RPR_RENDER_QUALITY_HIGH; + } else if (m_currentRenderQuality == HdRprRenderQualityTokens->Medium) { + hybridRenderQuality = RPR_RENDER_QUALITY_MEDIUM; + } else if (m_currentRenderQuality == HdRprRenderQualityTokens->Low) { + hybridRenderQuality = RPR_RENDER_QUALITY_LOW; + } + + if (hybridRenderQuality != -1) { + RPR_ERROR_CHECK(m_rprContext->SetParameter(rpr::ContextInfo(RPR_CONTEXT_RENDER_QUALITY), hybridRenderQuality), "Fail to set context hybrid render quality"); } } } @@ -1143,17 +1588,22 @@ class HdRprApiImpl { void UpdateSettings(HdRprConfig const& preferences, bool force = false) { if (preferences.IsDirty(HdRprConfig::DirtySampling) || force) { m_maxSamples = preferences.GetMaxSamples(); - if (m_maxSamples < m_iter) { + if (m_maxSamples < m_numSamples) { // Force framebuffers clear to render required number of samples m_dirtyFlags |= ChangeTracker::DirtyScene; } } - m_currentRenderQuality = preferences.GetRenderQuality(); + if (preferences.IsDirty(HdRprConfig::DirtyAlpha) || force) { + m_isAlphaEnabled = preferences.GetEnableAlpha(); + + UpdateColorAlpha(); + } - if (m_rprContextMetadata.pluginType == rpr::kPluginTahoe) { + if (m_rprContextMetadata.pluginType == kPluginTahoe || + m_rprContextMetadata.pluginType == kPluginNorthstar) { UpdateTahoeSettings(preferences, force); - } else if (m_rprContextMetadata.pluginType == rpr::kPluginHybrid) { + } else if (m_rprContextMetadata.pluginType == kPluginHybrid) { UpdateHybridSettings(preferences, force); } } @@ -1185,7 +1635,11 @@ class HdRprApiImpl { m_hdCamera->GetShutterClose(&shutterClose); } double exposure = std::max(shutterClose - shutterOpen, 0.0); - RPR_ERROR_CHECK(m_camera->SetExposure(exposure), "Failed to set camera exposure"); + + // Hydra always sample transforms in such a way that + // starting transform matches shutterOpen and + // ending transform matches shutterClose + RPR_ERROR_CHECK(m_camera->SetExposure(1.0f), "Failed to set camera exposure"); auto setCameraLookAt = [this](GfMatrix4d const& viewMatrix, GfMatrix4d const& inverseViewMatrix) { auto& iwvm = inverseViewMatrix; @@ -1264,51 +1718,127 @@ class HdRprApiImpl { float fstop = 0.0f; m_hdCamera->GetFStop(&fstop); - if (fstop == 0.0f) { fstop = std::numeric_limits::max(); } + bool dofEnabled = fstop != 0.0f && fstop != std::numeric_limits::max(); + + if (dofEnabled) { + int apertureBlades = m_hdCamera->GetApertureBlades(); + if (apertureBlades < 4 || apertureBlades > 32) { + apertureBlades = 16; + } + RPR_ERROR_CHECK(m_camera->SetApertureBlades(apertureBlades), "Failed to set camera aperture blades"); + } else { + fstop = std::numeric_limits::max(); + } RPR_ERROR_CHECK(m_camera->SetFStop(fstop), "Failed to set camera FStop"); + // Convert to millimeters + focalLength *= 1e3f; + sensorWidth *= 1e3f; + sensorHeight *= 1e3f; + RPR_ERROR_CHECK(m_camera->SetFocalLength(focalLength), "Fail to set camera focal length"); RPR_ERROR_CHECK(m_camera->SetSensorSize(sensorWidth, sensorHeight), "Failed to set camera sensor size"); } } + VtValue GetAovSetting(TfToken const& settingName, HdRenderPassAovBinding const& aovBinding) { + auto settingsIt = aovBinding.aovSettings.find(settingName); + if (settingsIt == aovBinding.aovSettings.end()) { + return VtValue(); + } + return settingsIt->second; + } + + bool GetAovBindingInfo(HdRenderPassAovBinding const& aovBinding, TfToken* aovName, std::string* lpe, HdFormat* format) { + // Check for UsdRenderVar: take aovName from `sourceName`, format from `dataType` + auto sourceType = GetAovSetting(UsdRenderTokens->sourceType, aovBinding); + if (sourceType == UsdRenderTokens->raw) { + auto name = TfToken(GetAovSetting(UsdRenderTokens->sourceName, aovBinding).Get()); + auto& aovDesc = HdRprAovRegistry::GetInstance().GetAovDesc(name); + if (aovDesc.id != kAovNone && aovDesc.format != HdFormatInvalid) { + *aovName = name; + *format = ConvertUsdRenderVarDataType(GetAovSetting(UsdRenderTokens->dataType, aovBinding).Get()); + return *format != HdFormatInvalid; + } else { + TF_RUNTIME_ERROR("Unsupported UsdRenderVar sourceName: %s", name.GetText()); + } + } else if (sourceType == UsdRenderTokens->lpe) { + auto sourceName = GetAovSetting(UsdRenderTokens->sourceName, aovBinding).Get(); + if (sourceName.empty()) { + return false; + } + + *format = ConvertUsdRenderVarDataType(GetAovSetting(UsdRenderTokens->dataType, aovBinding).Get()); + + if (sourceName == "C.*") { + // We can use RPR_AOV_COLOR here instead of reserving one of the available LPE AOV slots + *aovName = HdAovTokens->color; + } else { + if (m_rprContextMetadata.pluginType != kPluginNorthstar) { + TF_RUNTIME_ERROR("LPE AOV is supported in Northstar only"); + return false; + } + + *lpe = sourceName; + } + + return *format != HdFormatInvalid; + } + + // Default AOV: aovName from `aovBinding.aovName`, format is controlled by HdRenderBuffer + auto& aovDesc = HdRprAovRegistry::GetInstance().GetAovDesc(aovBinding.aovName); + if (aovDesc.id != kAovNone && aovDesc.format != HdFormatInvalid) { + *aovName = aovBinding.aovName; + *format = static_cast(aovBinding.renderBuffer)->GetFormat(); + return true; + } + + return false; + } + void UpdateAovs(HdRprRenderParam* rprRenderParam, RenderSetting enableDenoise, RenderSetting tonemap, bool clearAovs) { - if (m_dirtyFlags & ChangeTracker::DirtyAOVBindings) { - auto retainedBoundAovs = std::move(m_boundAovs); - for (auto& aovBinding : m_aovBindings) { - if (auto rb = aovBinding.renderBuffer) { - auto boundAovIter = retainedBoundAovs.find(aovBinding.aovName); - if (boundAovIter == retainedBoundAovs.end()) { - if (auto aov = CreateAov(aovBinding.aovName, rb->GetWidth(), rb->GetHeight(), rb->GetFormat())) { - m_boundAovs[aovBinding.aovName] = aov; - } - } else { - m_boundAovs[aovBinding.aovName] = boundAovIter->second; + if (m_dirtyFlags & (ChangeTracker::DirtyAOVBindings | ChangeTracker::DirtyAOVRegistry)) { + m_resolveData.rawAovs.clear(); + m_resolveData.computedAovs.clear(); + for (auto it = m_aovRegistry.begin(); it != m_aovRegistry.end();) { + if (auto aov = it->second.lock()) { + bool isMultiSampled = aov->GetDesc().multiSampled; + + // An opinion of a user overrides an AOV descriptor opinion + if (auto outputRb = GetOutputRenderBuffer(it->first)) { + isMultiSampled = outputRb->isMultiSampled; + } - // Update underlying format if needed - boundAovIter->second->Resize(rb->GetWidth(), rb->GetHeight(), rb->GetFormat()); + if (aov->GetDesc().computed) { + m_resolveData.computedAovs.push_back({aov.get(), isMultiSampled}); + } else { + m_resolveData.rawAovs.push_back({aov.get(), isMultiSampled}); } + ++it; + } else { + it = m_aovRegistry.erase(it); } } } if (m_dirtyFlags & ChangeTracker::DirtyViewport) { - for (auto& aovEntry : m_internalAovs) { - aovEntry.second->Resize(m_viewportSize[0], m_viewportSize[1], aovEntry.second->GetFormat()); - } - for (auto& aovBinding : m_aovBindings) { - if (auto rb = aovBinding.renderBuffer) { - auto boundAovIter = m_boundAovs.find(aovBinding.aovName); - if (boundAovIter != m_boundAovs.end()) { - boundAovIter->second->Resize(rb->GetWidth(), rb->GetHeight(), rb->GetFormat()); - } + m_resolveData.ForAllAovs([this](ResolveData::AovEntry const& e) { + e.aov->Resize(m_viewportSize[0], m_viewportSize[1], e.aov->GetFormat()); + }); + + // If AOV bindings are dirty then we already committed HdRprRenderBuffers, see SetAovBindings + if ((m_dirtyFlags & ChangeTracker::DirtyAOVBindings) == 0) { + for (auto& outputRb : m_outputRenderBuffers) { + auto rprRenderBuffer = static_cast(outputRb.aovBinding->renderBuffer); + outputRb.mappedData = rprRenderBuffer->GetPointerForWriting(); + outputRb.mappedDataSize = HdDataSizeOfFormat(rprRenderBuffer->GetFormat()) * rprRenderBuffer->GetWidth() * rprRenderBuffer->GetHeight(); } } - // Size of bound AOVs controled by aovBinding's renderBuffer } if (m_dirtyFlags & ChangeTracker::DirtyScene || m_dirtyFlags & ChangeTracker::DirtyAOVRegistry || + m_dirtyFlags & ChangeTracker::DirtyAOVBindings || m_dirtyFlags & ChangeTracker::DirtyViewport || IsCameraChanged()) { clearAovs = true; @@ -1324,21 +1854,23 @@ class HdRprApiImpl { } auto rprApi = rprRenderParam->GetRprApi(); - for (auto it = m_aovRegistry.begin(); it != m_aovRegistry.end();) { - if (auto aov = it->second.lock()) { - aov->Update(rprApi, m_rifContext.get()); - if (clearAovs) { - aov->Clear(); - } - ++it; - } else { - it = m_aovRegistry.erase(it); + m_resolveData.ForAllAovs([=](ResolveData::AovEntry const& e) { + e.aov->Update(rprApi, m_rifContext.get()); + if (clearAovs) { + e.aov->Clear(); } - } + }); if (clearAovs) { - m_iter = 0; + m_numSamples = 0; m_activePixels = -1; + m_isFirstSample = true; + m_frameRenderTotalTime = {}; + m_frameResolveTotalTime = {}; + + // Always start from RPR_CONTEXT_ITERATIONS=1 to be able to resolve singlesampled AOVs correctly + m_numSamplesPerIter = 1; + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_ITERATIONS, m_numSamplesPerIter), "Failed to set context iterations"); } } @@ -1358,63 +1890,359 @@ class HdRprApiImpl { } rif::FilterType filterType = rif::FilterType::EawDenoise; - if (m_rprContextMetadata.renderDeviceType == rpr::kRenderDeviceGPU) { + if (m_rprContextMetadata.renderDeviceType == RprUsdRenderDeviceType::GPU) { filterType = rif::FilterType::AIDenoise; } if (filterType == rif::FilterType::EawDenoise) { colorAov->EnableEAWDenoise(m_internalAovs.at(HdRprAovTokens->albedo), m_internalAovs.at(HdAovTokens->normal), - m_internalAovs.at(HdRprUtilsGetCameraDepthName()), + m_internalAovs.at(HdRprGetCameraDepthAovName()), m_internalAovs.at(HdAovTokens->primId), m_internalAovs.at(HdRprAovTokens->worldCoordinate)); } else { colorAov->EnableAIDenoise(m_internalAovs.at(HdRprAovTokens->albedo), m_internalAovs.at(HdAovTokens->normal), - m_internalAovs.at(HdRprUtilsGetCameraDepthName())); + m_internalAovs.at(HdRprGetCameraDepthAovName())); } } - void RenderImpl(HdRprRenderThread* renderThread, std::vector> const& outputRenderBuffers) { - bool stopRequested = false; - while (!IsConverged() || stopRequested) { - renderThread->WaitUntilPaused(); - stopRequested = renderThread->IsStopRequested(); - if (stopRequested) { - break; + using RenderUpdateCallbackFunc = void (*)(float progress, void* userData); + void EnableRenderUpdateCallback(RenderUpdateCallbackFunc rucFunc) { + if (m_rprContextMetadata.pluginType != kPluginNorthstar) { + m_isRenderUpdateCallbackEnabled = false; + return; + } + + m_rucData.rprApi = this; + RPR_ERROR_CHECK_THROW(m_rprContext->SetParameter(RPR_CONTEXT_RENDER_UPDATE_CALLBACK_FUNC, (void*)rucFunc), "Failed to set northstar RUC func"); + RPR_ERROR_CHECK_THROW(m_rprContext->SetParameter(RPR_CONTEXT_RENDER_UPDATE_CALLBACK_DATA, &m_rucData), "Failed to set northstar RUC data"); + m_isRenderUpdateCallbackEnabled = true; + } + + bool EnableAborting() { + m_isAbortingEnabled.store(true); + + // If abort was called when it was disabled, abort now + if (m_abortRender) { + AbortRender(); + return true; + } + return false; + } + + void IncrementFrameCount(bool isAdaptiveSamplingEnabled) { + if (m_rprContextMetadata.pluginType == kPluginHybrid) { + return; + } + + uint32_t frameCount = m_frameCount++; + + // XXX: When adaptive sampling is enabled, + // Tahoe requires RPR_CONTEXT_FRAMECOUNT to be set to 0 on the very first sample, + // otherwise internal adaptive sampling buffers is never reset + if (m_rprContextMetadata.pluginType == kPluginTahoe && + isAdaptiveSamplingEnabled && m_numSamples == 0) { + frameCount = 0; + } + + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_FRAMECOUNT, frameCount), "Failed to set framecount"); + } + + static void BatchRenderUpdateCallback(float progress, void* dataPtr) { + auto data = static_cast(dataPtr); + + if (data->rprApi->m_numSamples == 0) { + data->rprApi->EnableAborting(); + } + + if (data->rprApi->m_resolveMode == kResolveInRenderUpdateCallback) { + // In batch, we do resolve from RUC only by request + if (data->rprApi->m_batchREM->IsResolveRequested()) { + data->rprApi->ResolveFramebuffers(); + data->rprApi->m_batchREM->OnResolve(); } + } + + data->previousProgress = progress; + } + + bool CommonRenderImplPrologue() { + if (m_numSamples == 0) { + // Disable aborting on the very first sample + // + // Ideally, aborting the first sample should not be the problem. + // We would like to be able to abort it: to reduce response time, + // to avoid doing calculations that may be discarded by the following changes. + // But in reality, aborting the very first sample may cause crashes or + // unwanted behavior when we will call rprContextRender next time. + // So until RPR core fixes these issues, we are not aborting the first sample. + m_isAbortingEnabled.store(false); + } + m_abortRender.store(false); + + // Default resolve mode + m_resolveMode = kResolveAfterRender; + + // When we want to have a uniform seed across all frames, + // we need to make sure that RPR_CONTEXT_FRAMECOUNT sequence is the same for all of them + if (m_isUniformSeed && m_numSamples == 0) { + m_frameCount = 0; + } + + // If the changes that were made by the user did not reset our AOVs, + // we can just resolve them to the current render buffers and we are done with the rendering + if (IsConverged()) { + ResolveFramebuffers(); + return false; + } + + return true; + } + + void BatchRenderImpl(HdRprRenderThread* renderThread) { + if (!CommonRenderImplPrologue()) { + return; + } + + if (!m_batchREM) { + m_batchREM = std::make_unique(); + } + auto renderScope = m_batchREM->EnterRenderScope(); + + EnableRenderUpdateCallback(BatchRenderUpdateCallback); - if (m_rprContextMetadata.pluginType != rpr::kPluginHybrid) { - RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_FRAMECOUNT, m_iter), "Failed to set framecount"); + // 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) { + // We can keep the ability to log progress and do snapshots + // while maximizing RPR_CONTEXT_ITERATIONS, only if RUC is supported. + if (m_isRenderUpdateCallbackEnabled) { + isMaximizingContextIterations = true; + } + } else { + // If the user is willing to sacrifice progress logging and snapshots, + // we minimize the amount of chore work needed to get renders. + isMaximizingContextIterations = true; + if (m_isRenderUpdateCallbackEnabled) { + m_isRenderUpdateCallbackEnabled = false; + RPR_ERROR_CHECK_THROW(m_rprContext->SetParameter(RPR_CONTEXT_RENDER_UPDATE_CALLBACK_FUNC, (void*)nullptr), "Failed to disable RUC func"); } + } + + // Though if adaptive sampling is enabled in a batch session we first render m_minSamples samples + // and after that render 1 sample at a time because we want to query the current amount of + // active pixels as often as possible + const bool isAdaptiveSamplingEnabled = IsAdaptiveSamplingEnabled(); + + while (!IsConverged()) { + if (renderThread->IsStopRequested()) { + break; + } + + IncrementFrameCount(isAdaptiveSamplingEnabled); + + auto startTime = std::chrono::high_resolution_clock::now(); + m_rucData.previousProgress = -1.0f; auto status = m_rprContext->Render(); + m_rucData.previousProgress = -1.0f; - if (status == RPR_ERROR_ABORTED || - RPR_ERROR_CHECK(status, "Fail contex render framebuffer")) { - stopRequested = true; + m_frameRenderTotalTime += std::chrono::high_resolution_clock::now() - startTime; + + if (status != RPR_SUCCESS && status != RPR_ERROR_ABORTED) { + RPR_ERROR_CHECK(status, "Failed to render", m_rprContext.get()); + break; + } + + // XXX(RPR): Northstar never returns RPR_ERROR_ABORTED, + // so we query whether render was aborted via m_abortRender (RPRNEXT-401) + bool isAborted = status == RPR_ERROR_ABORTED || m_abortRender.load(); + if (isAborted) { break; } - m_iter++; - if (m_varianceThreshold > 0.0f) { - if (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; + // As soon as the first sample has been rendered, we enable aborting + m_isAbortingEnabled.store(true); + + m_numSamples += m_numSamplesPerIter; + + if (m_resolveMode == kResolveAfterRender) { + // In batch mode resolve only the first and the last frames or + // when the host app requested it explicitly + if (m_isFirstSample || m_batchREM->IsResolveRequested()) { + ResolveFramebuffers(); + m_batchREM->OnResolve(); + } + } + + if (isMaximizingContextIterations) { + int oldNumSamplesPerIter = m_numSamplesPerIter; + + // When singlesampled AOVs already rendered, we can fire up rendering of as many samples as possible + if (m_numSamples == 1) { + // Render as many samples as possible per Render call + if (isAdaptiveSamplingEnabled) { + m_numSamplesPerIter = m_minSamples - m_numSamples; + } else { + m_numSamplesPerIter = m_maxSamples - m_numSamples; + } + + // And disable resolves after render if possible + if (m_isRenderUpdateCallbackEnabled) { + m_resolveMode = kResolveInRenderUpdateCallback; + } + } else { + // When adaptive sampling is enabled, after reaching m_minSamples we want query RPR_CONTEXT_ACTIVE_PIXEL_COUNT each sample + if (isAdaptiveSamplingEnabled && m_numSamples == m_minSamples) { + m_numSamplesPerIter = 1; + isMaximizingContextIterations = false; + } } + + if (m_numSamplesPerIter != oldNumSamplesPerIter) { + // Make sure we will not oversample the image + int numSamplesLeft = m_maxSamples - m_numSamples; + m_numSamplesPerIter = std::min(m_numSamplesPerIter, numSamplesLeft); + if (m_numSamplesPerIter > 0) { + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_ITERATIONS, m_numSamplesPerIter), "Failed to set context iterations"); + } + } + } + + if (isAdaptiveSamplingEnabled && + 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; + } + } + + ResolveFramebuffers(); + } + + static void RenderUpdateCallback(float progress, void* dataPtr) { + auto data = static_cast(dataPtr); + + if (data->rprApi->m_numSamples == 0 && + !data->rprApi->m_isInteractive) { + data->rprApi->EnableAborting(); + } + + if (data->rprApi->m_resolveMode == kResolveInRenderUpdateCallback) { + static const float kResolveFrequency = 0.1f; + int previousStep = static_cast(data->previousProgress / kResolveFrequency); + int currentStep = static_cast(progress / kResolveFrequency); + if (currentStep != previousStep) { + data->rprApi->ResolveFramebuffers(); + } + } + + data->previousProgress = progress; + } + + void RenderImpl(HdRprRenderThread* renderThread) { + if (!CommonRenderImplPrologue()) { + return; + } + + EnableRenderUpdateCallback(RenderUpdateCallback); + + while (!IsConverged()) { + // In interactive mode, always render at least one frame, otherwise + // disturbing full-screen-flickering will be visible or + // viewport will not update because of fast exit due to abort + const bool forceRender = m_isInteractive && m_numSamples == 0; + + renderThread->WaitUntilPaused(); + if (renderThread->IsStopRequested() && !forceRender) { + break; } - if (!IsConverged()) { - // Last framebuffer resolve will be called after "while" in case framebuffer is converged. - // We do not resolve framebuffers in case user requested render stop - ResolveFramebuffers(outputRenderBuffers); + IncrementFrameCount(IsAdaptiveSamplingEnabled()); + + auto startTime = std::chrono::high_resolution_clock::now(); + + m_rucData.previousProgress = -1.0f; + auto status = m_rprContext->Render(); + m_rucData.previousProgress = -1.0f; + + m_frameRenderTotalTime += std::chrono::high_resolution_clock::now() - startTime; + + if (status != RPR_SUCCESS && status != RPR_ERROR_ABORTED) { + RPR_ERROR_CHECK(status, "Failed to render", m_rprContext.get()); + break; + } + + // XXX(RPR): Northstar never returns RPR_ERROR_ABORTED, + // so we query whether render was aborted via m_abortRender (RPRNEXT-401) + bool isAborted = status == RPR_ERROR_ABORTED || m_abortRender.load(); + if (isAborted && !forceRender) { + break; } - stopRequested = renderThread->IsStopRequested(); + if (m_resolveMode == kResolveAfterRender) { + ResolveFramebuffers(); + } + + if (IsAdaptiveSamplingEnabled() && + 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; + } + + // As soon as the first sample has been rendered, we enable aborting + m_isAbortingEnabled.store(true); + + m_numSamples += m_numSamplesPerIter; + + if (!m_isInteractive && m_rprContextMetadata.pluginType == kPluginNorthstar && m_numSamples > 1) { + // Progressively increase RPR_CONTEXT_ITERATIONS because it highly improves Northstar's performance + m_numSamplesPerIter *= 2; + + // Make sure we will not oversample the image + int numSamplesLeft = m_maxSamples - m_numSamples; + m_numSamplesPerIter = std::min(m_numSamplesPerIter, numSamplesLeft); + if (m_numSamplesPerIter > 0) { + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_ITERATIONS, m_numSamplesPerIter), "Failed to set context iterations"); + } + + // Enable resolves in the render update callback after RPR_CONTEXT_ITERATIONS gets high enough + if (m_numSamplesPerIter >= 32) { + m_resolveMode = kResolveInRenderUpdateCallback; + } + } + } + } + + void InteropRenderImpl(HdRprRenderThread* renderThread) { + if (m_rprContextMetadata.pluginType != RprUsdPluginType::kPluginHybrid) { + TF_CODING_ERROR("InteropRenderImpl should be called for Hybrid plugin only"); + return; + } + + // For now render 5 frames before each present + for (int i = 0; i < 5; i++) { + rpr::Status status = m_rprContext->Render(); + if (status != rpr::Status::RPR_SUCCESS) { + TF_WARN("rprContextRender returns: %d", status); + } } - if (!stopRequested) { - ResolveFramebuffers(outputRenderBuffers); + // Next frame couldn't be flushed before previous was presented. We should wait for presenter + std::unique_lock lock(m_rprContext->GetMutex()); + m_presentedConditionVariable->wait(lock, [this] { return *m_presentedCondition == true; }); + + rpr_int status = m_rprContextFlushFrameBuffers(rpr::GetRprObject(m_rprContext.get())); + if (status != RPR_SUCCESS) { + TF_WARN("rprContextFlushFrameBuffers returns: %d", status); } + + *m_presentedCondition = false; } void RenderFrame(HdRprRenderThread* renderThread) { @@ -1435,53 +2263,41 @@ class HdRprApiImpl { } if (!m_rprSceneExportPath.empty()) { - std::string exportPath; - { - std::lock_guard lock(m_rprSceneExportPathMutex); - std::swap(m_rprSceneExportPath, exportPath); - } - if (!exportPath.empty()) { - auto rprContextHandle = rpr::GetRprObject(m_rprContext.get()); - auto rprSceneHandle = rpr::GetRprObject(m_scene.get()); - if (!RPR_ERROR_CHECK(rprsExport(exportPath.c_str(), rprContextHandle, rprSceneHandle, 0, nullptr, nullptr, 0, nullptr, nullptr, 0), "Failed to export .rpr file")) { - printf("Successfully exported \"%s\"\n", exportPath.c_str()); - } - } - } - - std::vector> outputRenderBuffers; - for (auto& aovBinding : m_aovBindings) { - if (auto rb = static_cast(aovBinding.renderBuffer)) { - if (rb->GetWidth() != m_viewportSize[0] || rb->GetHeight() != m_viewportSize[1]) { - TF_RUNTIME_ERROR("%s renderBuffer has inconsistent render buffer size: %ux%u. Expected: %dx%d", - aovBinding.aovName.GetText(), rb->GetWidth(), rb->GetHeight(), m_viewportSize[0], m_viewportSize[1]); - outputRenderBuffers.emplace_back(nullptr, 0u); - } else { - size_t size = rb->GetWidth() * rb->GetHeight() * HdDataSizeOfFormat(rb->GetFormat()); - outputRenderBuffers.emplace_back(rb->Map(), size); - } - } + return ExportRpr(); } if (m_state == kStateRender) { try { - RenderImpl(renderThread, outputRenderBuffers); + if (m_delegate->IsBatch()) { + BatchRenderImpl(renderThread); + } else { + if (m_rprContextMetadata.pluginType == RprUsdPluginType::kPluginHybrid && + m_rprContextMetadata.interopInfo) { + InteropRenderImpl(renderThread); + } else { + RenderImpl(renderThread); + } + } } catch (std::runtime_error const& e) { TF_RUNTIME_ERROR("Failed to render frame: %s", e.what()); } } else if (m_state == kStateRestartRequired) { - if (m_showRestartRequiredWarning) { + { + RprUsdConfig* config; + auto configLock = RprUsdConfig::GetInstance(&config); - std::string message = -R"(Restart required when you change "Render Device" or "Render Quality" (To "Full" or vice versa). + if (config->IsRestartWarningEnabled()) { + std::string message = + R"(Restart required when you change "Render Device" or "Render Quality" (To "Full" or vice versa). You can revert your changes now and you will not lose any rendering progress. You can restart renderer by pressing "RPR Persp" - "Restart Render". Don't show this message again? )"; - if (HdRprShowMessage("Restart required", message)) { - UpdateRestartRequiredMessageStatus(true); + if (HdRprShowMessage("Restart required", message)) { + config->SetRestartWarning(false); + } } } @@ -1492,11 +2308,197 @@ Don't show this message again? fprintf(stderr, "Please restart render\n"); } } + } - for (auto& aovBinding : m_aovBindings) { - if (auto rb = static_cast(aovBinding.renderBuffer)) { - rb->Unmap(); + void ExportRpr() { +#ifdef BUILD_AS_HOUDINI_PLUGIN + if (HOM().isApprentice()) { + fprintf(stderr, "Cannot export .rpr from Apprentice"); + return; + } +#endif // BUILD_AS_HOUDINI_PLUGIN + + uint32_t currentYFlip; + if (m_isOutputFlipped) { + currentYFlip = RprUsdGetInfo(m_rprContext.get(), RPR_CONTEXT_Y_FLIP); + + currentYFlip = !currentYFlip; + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_Y_FLIP, currentYFlip), "Failed to set context Y FLIP parameter"); + } + + auto rprContextHandle = rpr::GetRprObject(m_rprContext.get()); + auto rprSceneHandle = rpr::GetRprObject(m_scene.get()); + if (RPR_ERROR_CHECK(rprsExport(m_rprSceneExportPath.c_str(), rprContextHandle, rprSceneHandle, 0, nullptr, nullptr, 0, nullptr, nullptr, 0), "Failed to export .rpr file")) { + return; + } + + if (m_isOutputFlipped) { + currentYFlip = !currentYFlip; + RPR_ERROR_CHECK(m_rprContext->SetParameter(RPR_CONTEXT_Y_FLIP, currentYFlip), "Failed to set context Y FLIP parameter"); + } + + TfStringReplace(m_rprSceneExportPath, ".rpr", ".json"); + auto configFilename = m_rprSceneExportPath.substr(0, m_rprSceneExportPath.size() - 4) + ".json"; + std::ofstream configFile(configFilename); + if (!configFile.is_open()) { + fprintf(stderr, "Failed to create config file: %s\n", configFilename.c_str()); + return; + } + + try { + json config; + config["width"] = m_viewportSize[0]; + config["height"] = m_viewportSize[1]; + config["iterations"] = m_maxSamples; + config["batchsize"] = m_maxSamples; + + auto basename = TfStringGetBeforeSuffix(TfGetBaseName(m_rprSceneExportPath)); + config["output"] = basename + ".exr"; + config["output.json"] = basename + ".output.json"; + + static std::map kRprsContextFlags = { + {RPR_CREATION_FLAGS_ENABLE_CPU, "cpu"}, + {RPR_CREATION_FLAGS_ENABLE_DEBUG, "debug"}, + {RPR_CREATION_FLAGS_ENABLE_GPU0, "gpu0"}, + {RPR_CREATION_FLAGS_ENABLE_GPU1, "gpu1"}, + {RPR_CREATION_FLAGS_ENABLE_GPU2, "gpu2"}, + {RPR_CREATION_FLAGS_ENABLE_GPU3, "gpu3"}, + {RPR_CREATION_FLAGS_ENABLE_GPU4, "gpu4"}, + {RPR_CREATION_FLAGS_ENABLE_GPU5, "gpu5"}, + {RPR_CREATION_FLAGS_ENABLE_GPU6, "gpu6"}, + {RPR_CREATION_FLAGS_ENABLE_GPU7, "gpu7"}, + {RPR_CREATION_FLAGS_ENABLE_GPU8, "gpu8"}, + {RPR_CREATION_FLAGS_ENABLE_GPU9, "gpu9"}, + {RPR_CREATION_FLAGS_ENABLE_GPU10, "gpu10"}, + {RPR_CREATION_FLAGS_ENABLE_GPU11, "gpu11"}, + {RPR_CREATION_FLAGS_ENABLE_GPU12, "gpu12"}, + {RPR_CREATION_FLAGS_ENABLE_GPU13, "gpu13"}, + {RPR_CREATION_FLAGS_ENABLE_GPU14, "gpu14"}, + {RPR_CREATION_FLAGS_ENABLE_GPU15, "gpu15"}, + }; + + json context; + auto creationFlags = RprUsdGetInfo(m_rprContext.get(), RPR_CONTEXT_CREATION_FLAGS); + for (auto& entry : kRprsContextFlags) { + if (creationFlags & entry.first) { + context[entry.second] = 1; + } + } + if (creationFlags & RPR_CREATION_FLAGS_ENABLE_CPU) { + context["threads"] = RprUsdGetInfo(m_rprContext.get(), RPR_CONTEXT_CPU_THREAD_LIMIT); + } + config["context"] = context; + + static const char* kRprsAovNames[] = { + /* RPR_AOV_COLOR = */ "color", + /* RPR_AOV_OPACITY = */ "opacity", + /* RPR_AOV_WORLD_COORDINATE = */ "world.coordinate", + /* RPR_AOV_UV = */ "uv", + /* RPR_AOV_MATERIAL_ID = */ "material.id", + /* RPR_AOV_GEOMETRIC_NORMAL = */ "geometric.normal", + /* RPR_AOV_SHADING_NORMAL = */ "shading.normal", + /* RPR_AOV_DEPTH = */ "depth", + /* RPR_AOV_OBJECT_ID = */ "object.id", + /* RPR_AOV_OBJECT_GROUP_ID = */ "object.group.id", + /* RPR_AOV_SHADOW_CATCHER = */ "shadow.catcher", + /* RPR_AOV_BACKGROUND = */ "background", + /* RPR_AOV_EMISSION = */ "emission", + /* RPR_AOV_VELOCITY = */ "velocity", + /* RPR_AOV_DIRECT_ILLUMINATION = */ "direct.illumination", + /* RPR_AOV_INDIRECT_ILLUMINATION = */ "indirect.illumination", + /* RPR_AOV_AO = */ "ao", + /* RPR_AOV_DIRECT_DIFFUSE = */ "direct.diffuse", + /* RPR_AOV_DIRECT_REFLECT = */ "direct.reflect", + /* RPR_AOV_INDIRECT_DIFFUSE = */ "indirect.diffuse", + /* RPR_AOV_INDIRECT_REFLECT = */ "indirect.reflect", + /* RPR_AOV_REFRACT = */ "refract", + /* RPR_AOV_VOLUME = */ "volume", + /* RPR_AOV_LIGHT_GROUP0 = */ "light.group0", + /* RPR_AOV_LIGHT_GROUP1 = */ "light.group1", + /* RPR_AOV_LIGHT_GROUP2 = */ "light.group2", + /* RPR_AOV_LIGHT_GROUP3 = */ "light.group3", + /* RPR_AOV_DIFFUSE_ALBEDO = */ "diffuse.albedo", + /* RPR_AOV_VARIANCE = */ "variance", + /* RPR_AOV_VIEW_SHADING_NORMAL = */ "view.shading.normal", + /* RPR_AOV_REFLECTION_CATCHER = */ "reflection.catcher", + /* RPR_AOV_COLOR_RIGHT = */ "color.right", + /* RPR_AOV_LPE_0 = */ "lpe0", + /* RPR_AOV_LPE_1 = */ "lpe1", + /* RPR_AOV_LPE_2 = */ "lpe2", + /* RPR_AOV_LPE_3 = */ "lpe3", + /* RPR_AOV_LPE_4 = */ "lpe4", + /* RPR_AOV_LPE_5 = */ "lpe5", + /* RPR_AOV_LPE_6 = */ "lpe6", + /* RPR_AOV_LPE_7 = */ "lpe7", + /* RPR_AOV_LPE_8 = */ "lpe8", + }; + static const size_t kNumRprsAovNames = sizeof(kRprsAovNames) / sizeof(kRprsAovNames[0]); + + json configAovs; + for (auto& outputRb : m_outputRenderBuffers) { + auto aovDesc = &outputRb.rprAov->GetDesc(); + if (aovDesc->computed) { + if (aovDesc->id == kColorAlpha) { + aovDesc = &HdRprAovRegistry::GetInstance().GetAovDesc(RPR_AOV_COLOR, false); + } else if (aovDesc->id == kNdcDepth) { + // RprsRender does not support it...yet? + continue; + } else { + fprintf(stderr, "Unprocessed computed AOV: %u\n", aovDesc->id); + continue; + } + } + + if (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) { + try { + auto name = outputRb.aovBinding->aovName.GetText(); + if (!*name) { + name = aovName; + } + + json lpeAov; + lpeAov["output"] = TfStringPrintf("%s.%s.exr", basename.c_str(), name); + lpeAov["lpe"] = RprUsdGetStringInfo(outputRb.rprAov->GetAovFb()->GetRprObject(), RPR_FRAMEBUFFER_LPE); + configAovs[aovName] = lpeAov; + } catch (RprUsdError& e) { + fprintf(stderr, "Failed to export %s AOV: %s\n", aovName, e.what()); + } + } else { + configAovs[aovName] = TfStringPrintf("%s.%s.exr", basename.c_str(), aovName); + } + } } + config["aovs"] = configAovs; + + configFile << config; + } catch (json::exception& e) { + fprintf(stderr, "Failed to fill config file: %s\n", configFilename.c_str()); + } + } + + void CommitResources() { + if (!m_rprContext) { + return; + } + + RprUsdMaterialRegistry::GetInstance().CommitResources(m_imageCache.get()); + } + + void Resolve() { + // hdRpr's rendering is implemented asynchronously - rprContextRender spins in the background thread. + // + // Resolve works differently depending on the current rendering session type: + // * In a non-interactive (i.e. batch) session, it blocks execution until + // AOV framebuffer resolved to corresponding HdRenderBuffers. + // * 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(); } } @@ -1511,17 +2513,47 @@ Don't show this message again? } void AbortRender() { - if (m_rprContext) { + if (!m_rprContext) { + return; + } + + if (m_isAbortingEnabled) { RPR_ERROR_CHECK(m_rprContext->AbortRender(), "Failed to abort render"); } - } - int GetNumCompletedSamples() const { - return m_iter; + // XXX(RPRNEXT-401) + // In case aborting is disabled, we postpone abort until it's enabled + m_abortRender.store(true); } - int GetNumActivePixels() const { - return m_activePixels; + HdRprApi::RenderStats GetRenderStats() const { + HdRprApi::RenderStats stats = {}; + + // rprsExport has no progress callback + if (!m_rprSceneExportPath.empty()) { + return stats; + } + + double progress = double(m_numSamples) / m_maxSamples; + if (m_activePixels != -1) { + int numPixels = m_viewportSize[0] * m_viewportSize[1]; + progress = std::max(progress, double(numPixels - m_activePixels) / numPixels); + } else if (m_isRenderUpdateCallbackEnabled && m_rucData.previousProgress > 0.0f) { + progress += m_rucData.previousProgress * (double(m_numSamplesPerIter) / m_maxSamples); + } + stats.percentDone = 100.0 * progress; + + if (m_numSamples > 0) { + double numRenderedSamples = progress * m_maxSamples; + auto resolveTime = m_frameResolveTotalTime / numRenderedSamples; + auto renderTime = m_frameRenderTotalTime / numRenderedSamples; + + using FloatingPointSecond = std::chrono::duration; + stats.averageRenderTimePerSample = std::chrono::duration_cast(renderTime).count(); + stats.averageResolveTimePerSample = std::chrono::duration_cast(resolveTime).count(); + } + + return stats; } bool IsCameraChanged() const { @@ -1544,55 +2576,196 @@ Don't show this message again? } bool IsConverged() const { - if (m_currentRenderQuality < kRenderQualityHigh) { - return m_iter == 1; + if (m_currentRenderQuality == HdRprRenderQualityTokens->Low || + m_currentRenderQuality == HdRprRenderQualityTokens->Medium) { + return m_numSamples == 1; } - return m_iter >= m_maxSamples || m_activePixels == 0; + return m_numSamples >= m_maxSamples || m_activePixels == 0; + } + + bool IsAdaptiveSamplingEnabled() const { + return m_rprContext && m_rprContextMetadata.pluginType == kPluginTahoe && m_varianceThreshold > 0.0f; } bool IsGlInteropEnabled() const { return m_rprContext && m_rprContextMetadata.isGlInteropEnabled; } - bool IsAovFormatConversionAvailable() const { - return m_rifContext != nullptr; + bool IsVulkanInteropEnabled() const { + return m_rprContext && m_rprContextMetadata.pluginType == kPluginHybrid && m_rprContextMetadata.interopInfo; } bool IsArbitraryShapedLightSupported() const { - return m_rprContextMetadata.pluginType != rpr::kPluginHybrid; + return m_rprContextMetadata.pluginType != kPluginHybrid; } - int GetCurrentRenderQuality() const { + bool IsSphereAndDiskLightSupported() const { + return m_rprContextMetadata.pluginType == kPluginNorthstar; + } + + TfToken const& GetCurrentRenderQuality() const { return m_currentRenderQuality; } - void ExportRprSceneOnNextRender(const char* exportPath) { - std::lock_guard lock(m_rprSceneExportPathMutex); - m_rprSceneExportPath = exportPath; + void SetInteropInfo(void* interopInfo, std::condition_variable* presentedConditionVariable, bool* presentedCondition) { +#ifdef HDRPR_ENABLE_VULKAN_INTEROP_SUPPORT + m_rprContextMetadata.interopInfo = interopInfo; + m_presentedConditionVariable = presentedConditionVariable; + m_presentedCondition = presentedCondition; +#endif // HDRPR_ENABLE_VULKAN_INTEROP_SUPPORT + } + + rpr::FrameBuffer* GetRawColorFramebuffer() { + auto it = m_aovRegistry.find(HdRprAovTokens->rawColor); + if (it != m_aovRegistry.end()) { + if (auto aov = it->second.lock()) { + if (auto fb = aov->GetAovFb()) { + return fb->GetRprObject(); + } + } + } + + return nullptr; } private: + static RprUsdPluginType GetPluginType(TfToken const& renderQuality) { + if (renderQuality == HdRprRenderQualityTokens->Full) { + return kPluginTahoe; + } else if (renderQuality == HdRprRenderQualityTokens->Northstar) { + return kPluginNorthstar; + } else { + return kPluginHybrid; + } + } + + static void RprContextDeleter(rpr::Context* ctx) { + if (RprUsdIsLeakCheckEnabled()) { + typedef rpr::Status (GetInfoFnc)(void*, uint32_t, size_t, void*, size_t*); + + struct ListDescriptor { + rpr_context_info infoType; + const char* name; + GetInfoFnc* getInfo; + + ListDescriptor(rpr_context_info infoType, const char* name, void* getInfoFnc) + : infoType(infoType), name(name) + , getInfo(reinterpret_cast(getInfoFnc)) { + } + }; + std::vector lists = { + {RPR_CONTEXT_LIST_CREATED_CAMERAS, "cameras", (void*)rprCameraGetInfo}, + {RPR_CONTEXT_LIST_CREATED_MATERIALNODES, "materialnodes", (void*)rprMaterialNodeGetInfo}, + {RPR_CONTEXT_LIST_CREATED_LIGHTS, "lights", (void*)rprLightGetInfo}, + {RPR_CONTEXT_LIST_CREATED_SHAPES, "shapes", (void*)rprShapeGetInfo}, + {RPR_CONTEXT_LIST_CREATED_HETEROVOLUMES, "heterovolumes", (void*)rprHeteroVolumeGetInfo}, + {RPR_CONTEXT_LIST_CREATED_GRIDS, "grids", (void*)rprGridGetInfo}, + {RPR_CONTEXT_LIST_CREATED_BUFFERS, "buffers", (void*)rprBufferGetInfo}, + {RPR_CONTEXT_LIST_CREATED_IMAGES, "images", (void*)rprImageGetInfo}, + {RPR_CONTEXT_LIST_CREATED_FRAMEBUFFERS, "framebuffers", (void*)rprFrameBufferGetInfo}, + {RPR_CONTEXT_LIST_CREATED_SCENES, "scenes", (void*)rprSceneGetInfo}, + {RPR_CONTEXT_LIST_CREATED_CURVES, "curves", (void*)rprCurveGetInfo}, + }; + + bool hasLeaks = false; + std::vector nameBuffer; + + for (auto& list : lists) { + size_t sizeParam = 0; + if (!RPR_ERROR_CHECK(ctx->GetInfo(list.infoType, 0, nullptr, &sizeParam), "Failed to get context info", ctx)) { + size_t numObjects = sizeParam / sizeof(void*); + + if (numObjects > 0) { + if (!hasLeaks) { + hasLeaks = true; + fprintf(stderr, "hdRpr - rpr::Context leaks detected\n"); + } + + fprintf(stderr, "Leaked %zu %s: ", numObjects, list.name); + + std::vector objectPointers(numObjects, nullptr); + if (!RPR_ERROR_CHECK(ctx->GetInfo(list.infoType, sizeParam, objectPointers.data(), nullptr), "Failed to get context info", ctx)) { + fprintf(stderr, "{"); + for (size_t i = 0; i < numObjects; ++i) { + size_t size; + if (!RPR_ERROR_CHECK(list.getInfo(objectPointers[i], RPR_OBJECT_NAME, 0, nullptr, &size), "Failed to get object name size") && size > 0) { + if (size > nameBuffer.size()) { + nameBuffer.reserve(size); + } + + if (!RPR_ERROR_CHECK(list.getInfo(objectPointers[i], RPR_OBJECT_NAME, size, nameBuffer.data(), &size), "Failed to get object name")) { + fprintf(stderr, "\"%.*s\"", int(size), nameBuffer.data()); + if (i + 1 != numObjects) fprintf(stderr, ","); + continue; + } + } + + fprintf(stderr, "0x%p", objectPointers[i]); + if (i + 1 != numObjects) fprintf(stderr, ","); + } + fprintf(stderr, "}\n"); + } else { + fprintf(stderr, "failed to get object list\n"); + } + } + } + } + } + delete ctx; + } + void InitRpr() { - RenderQualityType renderQuality; { HdRprConfig* config; auto configInstanceLock = HdRprConfig::GetInstance(&config); // Force sync to catch up the latest render quality and render device config->Sync(m_delegate); - renderQuality = config->GetRenderQuality(); - m_rprContextMetadata.renderDeviceType = static_cast(config->GetRenderDevice()); + m_currentRenderQuality = GetRenderQuality(*config); + m_rprContextMetadata.renderDeviceType = ToRprUsd(config->GetRenderDevice()); } - m_rprContextMetadata.pluginType = renderQuality == kRenderQualityFull ? rpr::kPluginTahoe : rpr::kPluginHybrid; - auto cachePath = HdRprApi::GetCachePath(); - m_rprContext.reset(rpr::CreateContext(cachePath.c_str(), &m_rprContextMetadata)); + m_rprContextMetadata.pluginType = GetPluginType(m_currentRenderQuality); + m_rprContext = RprContextPtr(RprUsdCreateContext(&m_rprContextMetadata), RprContextDeleter); if (!m_rprContext) { RPR_THROW_ERROR_MSG("Failed to create RPR context"); } - RPR_ERROR_CHECK_THROW(m_rprContext->SetParameter(RPR_CONTEXT_Y_FLIP, 0), "Fail to set context Y FLIP parameter"); + uint32_t requiredYFlip = 0; + if (m_rprContextMetadata.pluginType == RprUsdPluginType::kPluginHybrid && m_rprContextMetadata.interopInfo) { + RPR_ERROR_CHECK_THROW(m_rprContext->GetFunctionPtr( + RPR_CONTEXT_FLUSH_FRAMEBUFFERS_FUNC_NAME, + (void**)(&m_rprContextFlushFrameBuffers) + ), "Fail to get rprContextFlushFramebuffers function"); + requiredYFlip = 1; + } + + m_isOutputFlipped = RprUsdGetInfo(m_rprContext.get(), RPR_CONTEXT_Y_FLIP) != requiredYFlip; + if (m_isOutputFlipped) { + RPR_ERROR_CHECK_THROW(m_rprContext->SetParameter(RPR_CONTEXT_Y_FLIP, requiredYFlip), "Failed to set context Y FLIP parameter"); + } + + m_isRenderUpdateCallbackEnabled = false; + + bool isTracingEnabled = RprUsdGetInfo(m_rprContext.get(), RPR_CONTEXT_TRACING_ENABLED); + if (!isTracingEnabled) { + // We need it for correct rendering of ID AOVs (e.g. RPR_AOV_OBJECT_ID) + // XXX: it takes approximately 32ms due to RPR API indirection, + // replace with rprContextSetAOVindexLookupRange when ready + // XXX: only up to 2^16 indices, internal LUT limit + for (uint32_t i = 0; i < (1 << 16); ++i) { + // Split uint32_t into 4 float values - every 8 bits correspond to one float. + // Such an encoding scheme simplifies the conversion of RPR ID texture (float4) to the int32 texture (as required by Hydra). + // Conversion is currently implemented like this: + // * convert float4 texture to uchar4 using RIF + // * reinterpret uchar4 data as int32_t (works on little-endian CPU only) + m_rprContext->SetAOVindexLookup(rpr_int(i), + float((i >> 0) & 0xFF) / 255.0f, + float((i >> 8) & 0xFF) / 255.0f, + 0.0f, 0.0f); + } + } { HdRprConfig* config; @@ -1600,8 +2773,9 @@ Don't show this message again? UpdateSettings(*config, true); } - m_imageCache.reset(new ImageCache(m_rprContext.get())); - m_materialFactory.reset(new RprMaterialFactory(m_imageCache.get())); + m_imageCache.reset(new RprUsdImageCache(m_rprContext.get())); + + m_isAbortingEnabled.store(false); } bool ValidateRifModels(std::string const& modelsPath) { @@ -1628,32 +2802,25 @@ Don't show this message again? } m_rifContext = rif::Context::Create(m_rprContext.get(), m_rprContextMetadata, modelsPath); - if (!m_rifContext) { - return; - } + } + void InitAovs() { auto initInternalAov = [this](TfToken const& name) { - if (auto aov = CreateAov(name)) { + auto& aovDesc = HdRprAovRegistry::GetInstance().GetAovDesc(name); + if (auto aov = CreateAov(name, 0, 0, aovDesc.format)) { m_internalAovs.emplace(name, std::move(aov)); } }; - // In case we have RIF we can use it to combine opacity and color AOVs - // into image that can be used for alpha compositing, - // without it color AOV always have 1.0 in alpha channel - if (!TfGetEnvSetting(HDRPR_DISABLE_ALPHA)) { - initInternalAov(HdRprAovTokens->opacity); - } - // We create separate AOVs needed for denoising ASAP // In such a way, when user enables denoising it will not require to rerender // but it requires more memory, obviously, it should be taken into an account rif::FilterType filterType = rif::FilterType::EawDenoise; - if (m_rprContextMetadata.renderDeviceType == rpr::kRenderDeviceGPU) { + if (m_rprContextMetadata.renderDeviceType == RprUsdRenderDeviceType::GPU) { filterType = rif::FilterType::AIDenoise; } - initInternalAov(HdRprUtilsGetCameraDepthName()); + initInternalAov(HdRprGetCameraDepthAovName()); initInternalAov(HdRprAovTokens->albedo); initInternalAov(HdAovTokens->color); initInternalAov(HdAovTokens->normal); @@ -1661,6 +2828,12 @@ Don't show this message again? initInternalAov(HdAovTokens->primId); initInternalAov(HdRprAovTokens->worldCoordinate); } + + m_lpeAovPool.clear(); + m_lpeAovPool.insert(m_lpeAovPool.begin(), { + HdRprAovTokens->lpe0, HdRprAovTokens->lpe1, HdRprAovTokens->lpe2, + HdRprAovTokens->lpe3, HdRprAovTokens->lpe4, HdRprAovTokens->lpe5, + HdRprAovTokens->lpe6, HdRprAovTokens->lpe7, HdRprAovTokens->lpe8}); } void InitScene() { @@ -1683,21 +2856,29 @@ Don't show this message again? RPR_ERROR_CHECK_THROW(m_scene->SetCamera(m_camera.get()), "Failed to to set scene camera"); } - void UpdateRestartRequiredMessageStatus(bool createIfMissing = false) { - auto statusFilePath = (HdRprApi::GetCachePath() + ARCH_PATH_SEP) + "dontShowRestartRequiredMessage"; + void AddDefaultLight() { + if (!m_defaultLightObject) { + const GfVec3f k_defaultLightColor(0.5f, 0.5f, 0.5f); + m_defaultLightObject = CreateEnvironmentLight(k_defaultLightColor, 1.f); - bool fileExists = false; - if (createIfMissing) { - auto f = fopen(statusFilePath.c_str(), "w"); - if (f) { - fileExists = true; - fclose(f); + if (RprUsdIsLeakCheckEnabled()) { + m_defaultLightObject->light->SetName("defaultLight"); + m_defaultLightObject->image->SetName("defaultLight"); } - } else { - fileExists = ArchFileAccess(statusFilePath.c_str(), F_OK) == 0; + + // Do not count default light object + m_numLights--; } + } - m_showRestartRequiredWarning = !fileExists; + void RemoveDefaultLight() { + if (m_defaultLightObject) { + ReleaseImpl(m_defaultLightObject); + m_defaultLightObject = nullptr; + + // Do not count default light object + m_numLights++; + } } void SplitPolygons(const VtIntArray& indexes, const VtIntArray& vpf, VtIntArray& out_newIndexes, VtIntArray& out_newVpf) { @@ -1862,6 +3043,35 @@ Don't show this message again? return CreateMesh(position, indexes, normals, VtIntArray(), VtVec2fArray(), VtIntArray(), vpf); } + void UpdateColorAlpha(HdRprApiColorAov* colorAov = nullptr) { + if (!colorAov) { + colorAov = GetColorAov(); + if (!colorAov) return; + } + + if (m_isAlphaEnabled) { + auto opacityAov = GetAov(HdRprAovTokens->opacity, m_viewportSize[0], m_viewportSize[1], HdFormatFloat32Vec4); + if (opacityAov) { + colorAov->SetOpacityAov(opacityAov); + } else { + TF_WARN("Cannot enable alpha"); + } + } else { + colorAov->SetOpacityAov(nullptr); + } + } + + std::shared_ptr GetAov(TfToken const& aovName, int width, int height, HdFormat format) { + std::shared_ptr aov; + auto aovIter = m_aovRegistry.find(aovName); + if (aovIter == m_aovRegistry.end() || + !(aov = aovIter->second.lock())) { + + aov = CreateAov(aovName, width, height, format); + } + return aov; + } + std::shared_ptr CreateAov(TfToken const& aovName, int width = 0, int height = 0, HdFormat format = HdFormatFloat32Vec4) { if (!m_rprContext || width < 0 || height < 0 || @@ -1869,8 +3079,9 @@ Don't show this message again? return nullptr; } - auto rprAovIt = kAovTokenToRprAov.find(aovName); - if (rprAovIt == kAovTokenToRprAov.end()) { + auto& aovDesc = HdRprAovRegistry::GetInstance().GetAovDesc(aovName); + if (aovDesc.id == kAovNone || + aovDesc.format == HdFormatInvalid) { TF_WARN("Unsupported aov type: %s", aovName.GetText()); return nullptr; } @@ -1885,31 +3096,41 @@ Don't show this message again? try { if (!aov) { if (aovName == HdAovTokens->color) { - auto colorAov = std::make_shared(width, height, format, m_rprContext.get(), m_rprContextMetadata); - - auto opacityAovIter = m_aovRegistry.find(HdRprAovTokens->opacity); - if (opacityAovIter != m_aovRegistry.end()) { - if (auto opacityAov = opacityAovIter->second.lock()) { - colorAov->SetOpacityAov(opacityAov); - } + auto rawColorAov = GetAov(HdRprAovTokens->rawColor, width, height, HdFormatFloat32Vec4); + if (!rawColorAov) { + TF_RUNTIME_ERROR("Failed to create color AOV: can't create rawColor AOV"); + return nullptr; } + auto colorAov = std::make_shared(format, std::move(rawColorAov), m_rprContext.get(), m_rprContextMetadata); + UpdateColorAlpha(colorAov.get()); + aov = colorAov; } else if (aovName == HdAovTokens->normal) { aov = std::make_shared(width, height, format, m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); } else if (aovName == HdAovTokens->depth) { - auto worldCoordinateAovIter = m_internalAovs.find(HdRprAovTokens->worldCoordinate); - if (worldCoordinateAovIter == m_internalAovs.end()) { - if (auto worldCoordinateAov = CreateAov(HdRprAovTokens->worldCoordinate, width, height, HdFormatFloat32Vec4)) { - worldCoordinateAovIter = m_internalAovs.emplace(HdRprAovTokens->worldCoordinate, worldCoordinateAov).first; - } else { - TF_CODING_ERROR("Failed to create depth AOV: can't create worldCoordinate AOV"); - return nullptr; - } + auto worldCoordinateAov = GetAov(HdRprAovTokens->worldCoordinate, width, height, HdFormatFloat32Vec4); + if (!worldCoordinateAov) { + TF_RUNTIME_ERROR("Failed to create depth AOV: can't create worldCoordinate AOV"); + return nullptr; } - aov = std::make_shared(format, worldCoordinateAovIter->second, m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); + + aov = std::make_shared(format, std::move(worldCoordinateAov), m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); + } else if (TfStringStartsWith(aovName.GetString(), "lpe")) { + auto aovPtr = new HdRprApiAov(rpr::Aov(aovDesc.id), width, height, format, m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); + aov = std::shared_ptr(aovPtr, [this](HdRprApiAov* aov) { + // Each LPE AOV reserves RPR's LPE AOV id (RPR_AOV_LPE_0, ...) + // As soon as LPE AOV is released we want to return reserved id to the pool + m_lpeAovPool.push_back(GetRprLpeAovName(aov->GetAovFb()->GetAovId())); + delete aov; + }); } else { - aov = std::make_shared(rprAovIt->second, width, height, format, m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); + if (!aovDesc.computed) { + aov = std::make_shared(rpr::Aov(aovDesc.id), width, height, format, m_rprContext.get(), m_rprContextMetadata, m_rifContext.get()); + } else { + TF_CODING_ERROR("Failed to create %s AOV: unprocessed computed AOV", aovName.GetText()); + return nullptr; + } } m_aovRegistry[aovName] = aov; @@ -1918,7 +3139,7 @@ Don't show this message again? aov->Resize(width, height, format); } } catch (std::runtime_error const& e) { - TF_CODING_ERROR("Failed to create %s AOV: %s", aovName.GetText(), e.what()); + TF_RUNTIME_ERROR("Failed to create %s AOV: %s", aovName.GetText(), e.what()); } return aov; @@ -1937,16 +3158,25 @@ Don't show this message again? return static_cast(aov); } + struct OutputRenderBuffer; + + OutputRenderBuffer* GetOutputRenderBuffer(TfToken const& aovName) { + auto it = std::find_if(m_outputRenderBuffers.begin(), m_outputRenderBuffers.end(), [&](OutputRenderBuffer const& outRb) { + return outRb.aovName == aovName; + }); + if (it == m_outputRenderBuffers.end()) { + return nullptr; + } + return &(*it); + } + bool RenderImage(std::string const& path) { if (!m_rifContext) { return false; } - auto colorAovBinding = std::find_if(m_aovBindings.begin(), m_aovBindings.end(), [](HdRenderPassAovBinding const& binding) { - return binding.aovName == HdAovTokens->color; - }); - if (colorAovBinding == m_aovBindings.end() || - !colorAovBinding->renderBuffer) { + auto colorOutputRb = GetOutputRenderBuffer(HdAovTokens->color); + if (!colorOutputRb) { return false; } @@ -1959,29 +3189,38 @@ Don't show this message again? imageDesc.image_row_pitch = 0; imageDesc.image_slice_pitch = 0; + #if PXR_VERSION >= 2011 + auto hioFormat = textureData->GetHioFormat(); + GLenum glType = GlfGetGLType(hioFormat); + GLenum glFormat = GlfGetGLFormat(hioFormat); + #else + GLenum glType = textureData->GLType(); + GLenum glFormat = textureData->GLFormat(); + #endif + uint8_t bytesPerComponent; - if (textureData->GLType() == GL_UNSIGNED_BYTE) { + if (glType == GL_UNSIGNED_BYTE) { imageDesc.type = RIF_COMPONENT_TYPE_UINT8; bytesPerComponent = 1; - } else if (textureData->GLType() == GL_HALF_FLOAT) { + } else if (glType == GL_HALF_FLOAT) { imageDesc.type = RIF_COMPONENT_TYPE_FLOAT16; bytesPerComponent = 2; - } else if (textureData->GLType() == GL_FLOAT) { + } else if (glType == GL_FLOAT) { imageDesc.type = RIF_COMPONENT_TYPE_FLOAT32; bytesPerComponent = 2; } else { - TF_RUNTIME_ERROR("\"%s\" image has unsupported pixel channel type: %#x", path.c_str(), textureData->GLType()); + TF_RUNTIME_ERROR("\"%s\" image has unsupported pixel channel type: %#x", path.c_str(), glType); return false; } - if (textureData->GLFormat() == GL_RGBA) { + if (glFormat == GL_RGBA) { imageDesc.num_components = 4; - } else if (textureData->GLFormat() == GL_RGB) { + } else if (glFormat == GL_RGB) { imageDesc.num_components = 3; - } else if (textureData->GLFormat() == GL_RED) { + } else if (glFormat == GL_RED) { imageDesc.num_components = 1; } else { - TF_RUNTIME_ERROR("\"%s\" image has unsupported pixel format: %#x", path.c_str(), textureData->GLFormat()); + TF_RUNTIME_ERROR("\"%s\" image has unsupported pixel format: %#x", path.c_str(), glFormat); return false; } @@ -1995,7 +3234,7 @@ Don't show this message again? std::memcpy(mappedData, textureData->GetRawBuffer(), imageSize); RIF_ERROR_CHECK(rifImageUnmap(rifImage->GetHandle(), mappedData), "Failed to unmap rif image"); - auto colorRb = colorAovBinding->renderBuffer; + auto colorRb = static_cast(colorOutputRb->aovBinding->renderBuffer); try { auto blitFilter = rif::Filter::CreateCustom(RIF_IMAGE_FILTER_USER_DEFINED, m_rifContext.get()); @@ -2035,8 +3274,9 @@ Don't show this message again? return false; } size_t size = HdDataSizeOfFormat(colorRb->GetFormat()) * colorRb->GetWidth() * colorRb->GetHeight(); - std::memcpy(colorRb->Map(), mappedData, size); - colorRb->Unmap(); + if (auto colorRbData = colorRb->GetPointerForWriting()) { + std::memcpy(colorRbData, mappedData, size); + } RIF_ERROR_CHECK(rifImageUnmap(blitFilter->GetOutput(), mappedData), "Failed to unmap rif image"); return true; @@ -2078,7 +3318,7 @@ Don't show this message again? } private: - HdRenderDelegate* m_delegate; + HdRprDelegate* m_delegate; enum ChangeTracker : uint32_t { Clean = 0, @@ -2091,32 +3331,133 @@ Don't show this message again? }; uint32_t m_dirtyFlags = ChangeTracker::AllDirty; - std::unique_ptr m_rprContext; - rpr::ContextMetadata m_rprContextMetadata; + using RprContextPtr = std::unique_ptr; + RprContextPtr m_rprContext{nullptr, RprContextDeleter}; + RprUsdContextMetadata m_rprContextMetadata; + bool m_isOutputFlipped; std::unique_ptr m_rifContext; std::unique_ptr m_scene; std::unique_ptr m_camera; - std::unique_ptr m_imageCache; - std::unique_ptr m_materialFactory; + std::unique_ptr m_imageCache; std::map> m_aovRegistry; - std::map> m_boundAovs; std::map> m_internalAovs; HdRenderPassAovBindingVector m_aovBindings; + std::vector m_lpeAovPool; + + struct OutputRenderBuffer { + HdRenderPassAovBinding const* aovBinding; + TfToken aovName; + std::string lpe; + + std::shared_ptr rprAov; + + bool isMultiSampled; + void* mappedData; + size_t mappedDataSize; + }; + std::vector m_outputRenderBuffers; + + class BatchRenderEventManager { + public: + void WaitUntilNextRenderEvent() { + 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() { + m_isRenderInProgress->store(false); + m_renderEventCV->notify_one(); + } + + private: + std::atomic* m_isRenderInProgress; + std::condition_variable* m_renderEventCV; + }; + RenderScopeGuard EnterRenderScope() { return RenderScopeGuard(&m_isRenderInProgress, &m_renderEventCV); } + + void OnResolveRequest() { + m_isResolveRequested.store(true); + } + + bool IsResolveRequested() const { + return m_isResolveRequested; + } + + void OnResolve() { + m_isResolveRequested.store(false); + m_renderEventCV.notify_one(); + } + + private: + std::mutex m_renderEventMutex; + std::condition_variable m_renderEventCV; + std::atomic m_isRenderInProgress{false}; + std::atomic m_isResolveRequested{false}; + }; + std::unique_ptr m_batchREM; + + struct ResolveData { + struct AovEntry { + HdRprApiAov* aov; + bool isMultiSampled; + }; + std::vector rawAovs; + std::vector computedAovs; + + template + void ForAllAovs(F&& f) { + for (auto& aov : rawAovs) { f(aov); } + for (auto& aov : computedAovs) { f(aov); } + } + }; + ResolveData m_resolveData; + + enum { + kResolveAfterRender, + kResolveInRenderUpdateCallback, + } m_resolveMode = kResolveAfterRender; + bool m_isFirstSample = true; GfVec2i m_viewportSize = GfVec2i(0); GfMatrix4d m_cameraProjectionMatrix = GfMatrix4d(1.f); - HdRprCamera const* m_hdCamera; + HdRprCamera const* m_hdCamera = nullptr; + bool m_isAlphaEnabled; + + std::atomic m_numLights{0}; + HdRprApiEnvironmentLight* m_defaultLightObject = nullptr; - std::unique_ptr m_defaultLightObject; + bool m_isUniformSeed = true; + uint32_t m_frameCount = 0; - int m_iter = 0; + bool m_isInteractive = false; + int m_numSamples = 0; + int m_numSamplesPerIter = 0; int m_activePixels = -1; int m_maxSamples = 0; + int m_minSamples = 0; float m_varianceThreshold = 0.0f; - RenderQualityType m_currentRenderQuality = kRenderQualityFull; + TfToken m_currentRenderQuality; + + using Duration = std::chrono::high_resolution_clock::duration; + Duration m_frameRenderTotalTime; + Duration m_frameResolveTotalTime; + + struct RenderUpdateCallbackData { + HdRprApiImpl* rprApi = nullptr; + float previousProgress = 0.0f; + }; + RenderUpdateCallbackData m_rucData; + bool m_isRenderUpdateCallbackEnabled; enum State { kStateUninitialized, @@ -2126,13 +3467,17 @@ Don't show this message again? }; State m_state = kStateUninitialized; - bool m_showRestartRequiredWarning = true; + std::atomic m_isAbortingEnabled; + std::atomic m_abortRender; - std::mutex m_rprSceneExportPathMutex; std::string m_rprSceneExportPath; + + std::condition_variable* m_presentedConditionVariable = nullptr; + bool* m_presentedCondition = nullptr; + rprContextFlushFrameBuffers_func m_rprContextFlushFrameBuffers = nullptr; }; -HdRprApi::HdRprApi(HdRenderDelegate* delegate) : m_impl(new HdRprApiImpl(delegate)) { +HdRprApi::HdRprApi(HdRprDelegate* delegate) : m_impl(new HdRprApiImpl(delegate)) { } @@ -2195,6 +3540,16 @@ rpr::PointLight* HdRprApi::CreatePointLight() { return m_impl->CreatePointLight(); } +rpr::DiskLight* HdRprApi::CreateDiskLight() { + m_impl->InitIfNeeded(); + return m_impl->CreateDiskLight(); +} + +rpr::SphereLight* HdRprApi::CreateSphereLight() { + m_impl->InitIfNeeded(); + return m_impl->CreateSphereLight(); +} + rpr::IESLight* HdRprApi::CreateIESLight(std::string const& iesFilepath) { m_impl->InitIfNeeded(); return m_impl->CreateIESLight(iesFilepath); @@ -2204,18 +3559,30 @@ void HdRprApi::SetDirectionalLightAttributes(rpr::DirectionalLight* directionalL m_impl->SetDirectionalLightAttributes(directionalLight, color, shadowSoftnessAngle); } -void HdRprApi::SetLightColor(rpr::SpotLight* light, GfVec3f const& color) { - m_impl->SetLightColor(light, color); +void HdRprApi::SetLightRadius(rpr::SphereLight* light, float radius) { + m_impl->SetLightRadius(light, radius); } -void HdRprApi::SetLightColor(rpr::PointLight* light, GfVec3f const& color) { - m_impl->SetLightColor(light, color); +void HdRprApi::SetLightRadius(rpr::DiskLight* light, float radius) { + m_impl->SetLightRadius(light, radius); } -void HdRprApi::SetLightColor(rpr::IESLight* light, GfVec3f const& color) { +void HdRprApi::SetLightAngle(rpr::DiskLight* light, float angle) { + m_impl->SetLightAngle(light, angle); +} + +void HdRprApi::SetLightColor(rpr::RadiantLight* light, GfVec3f const& color) { m_impl->SetLightColor(light, color); } +RprUsdMaterial* HdRprApi::CreateGeometryLightMaterial(GfVec3f const& emissionColor) { + return m_impl->CreateGeometryLightMaterial(emissionColor); +} + +void HdRprApi::ReleaseGeometryLightMaterial(RprUsdMaterial* material) { + return m_impl->ReleaseGeometryLightMaterial(material); +} + HdRprApiVolume* HdRprApi::CreateVolume( VtUIntArray const& densityCoords, VtFloatArray const& densityValues, VtVec3fArray const& densityLUT, float densityScale, VtUIntArray const& albedoCoords, VtFloatArray const& albedoValues, VtVec3fArray const& albedoLUT, float albedoScale, @@ -2229,16 +3596,24 @@ HdRprApiVolume* HdRprApi::CreateVolume( gridSize, voxelSize, gridBBLow, materialParams); } -HdRprApiMaterial* HdRprApi::CreateMaterial(MaterialAdapter& MaterialAdapter) { +RprUsdMaterial* HdRprApi::CreateMaterial(HdSceneDelegate* sceneDelegate, HdMaterialNetworkMap const& materialNetwork) { m_impl->InitIfNeeded(); - return m_impl->CreateMaterial(MaterialAdapter); + return m_impl->CreateMaterial(sceneDelegate, materialNetwork); } -HdRprApiMaterial* HdRprApi::CreatePointsMaterial(VtVec3fArray const& colors) { +RprUsdMaterial* HdRprApi::CreatePointsMaterial(VtVec3fArray const& colors) { m_impl->InitIfNeeded(); return m_impl->CreatePointsMaterial(colors); } +RprUsdMaterial* HdRprApi::CreateDiffuseMaterial(GfVec3f const& color) { + m_impl->InitIfNeeded(); + + return m_impl->CreateRawMaterial(RPR_MATERIAL_NODE_UBERV2, { + {RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR, ToVec4(color, 1.0f)} + }); +} + void HdRprApi::SetMeshRefineLevel(rpr::Shape* mesh, int level) { m_impl->SetMeshRefineLevel(mesh, level); } @@ -2247,8 +3622,8 @@ void HdRprApi::SetMeshVertexInterpolationRule(rpr::Shape* mesh, TfToken boundary m_impl->SetMeshVertexInterpolationRule(mesh, boundaryInterpolation); } -void HdRprApi::SetMeshMaterial(rpr::Shape* mesh, HdRprApiMaterial const* material, bool doublesided, bool displacementEnabled) { - m_impl->SetMeshMaterial(mesh, material, doublesided, displacementEnabled); +void HdRprApi::SetMeshMaterial(rpr::Shape* mesh, RprUsdMaterial const* material, bool displacementEnabled) { + m_impl->SetMeshMaterial(mesh, material, displacementEnabled); } void HdRprApi::SetMeshVisibility(rpr::Shape* mesh, uint32_t visibilityMask) { @@ -2259,7 +3634,7 @@ void HdRprApi::SetMeshId(rpr::Shape* mesh, uint32_t id) { m_impl->SetMeshId(mesh, id); } -void HdRprApi::SetCurveMaterial(rpr::Curve* curve, HdRprApiMaterial const* material) { +void HdRprApi::SetCurveMaterial(rpr::Curve* curve, RprUsdMaterial const* material) { m_impl->SetCurveMaterial(curve, material); } @@ -2271,7 +3646,7 @@ void HdRprApi::Release(HdRprApiEnvironmentLight* envLight) { m_impl->Release(envLight); } -void HdRprApi::Release(HdRprApiMaterial* material) { +void HdRprApi::Release(RprUsdMaterial* material) { m_impl->Release(material); } @@ -2291,6 +3666,18 @@ void HdRprApi::Release(rpr::Curve* curve) { m_impl->Release(curve); } +void HdRprApi::SetName(rpr::ContextObject* object, const char* name) { + m_impl->SetName(object, name); +} + +void HdRprApi::SetName(RprUsdMaterial* object, const char* name) { + m_impl->SetName(object, name); +} + +void HdRprApi::SetName(HdRprApiEnvironmentLight* object, const char* name) { + m_impl->SetName(object, name); +} + void HdRprApi::SetCamera(HdCamera const* camera) { m_impl->SetCamera(camera); } @@ -2324,6 +3711,14 @@ HdRenderPassAovBindingVector HdRprApi::GetAovBindings() const { return m_impl->GetAovBindings(); } +void HdRprApi::CommitResources() { + m_impl->CommitResources(); +} + +void HdRprApi::Resolve() { + m_impl->Resolve(); +} + void HdRprApi::Render(HdRprRenderThread* renderThread) { m_impl->Render(renderThread); } @@ -2336,20 +3731,16 @@ bool HdRprApi::IsChanged() const { return m_impl->IsChanged(); } -int HdRprApi::GetNumCompletedSamples() const { - return m_impl->GetNumCompletedSamples(); -} - -int HdRprApi::GetNumActivePixels() const { - return m_impl->GetNumActivePixels(); +HdRprApi::RenderStats HdRprApi::GetRenderStats() const { + return m_impl->GetRenderStats(); } bool HdRprApi::IsGlInteropEnabled() const { return m_impl->IsGlInteropEnabled(); } -bool HdRprApi::IsAovFormatConversionAvailable() const { - return m_impl->IsAovFormatConversionAvailable(); +bool HdRprApi::IsVulkanInteropEnabled() const { + return m_impl->IsVulkanInteropEnabled(); } bool HdRprApi::IsArbitraryShapedLightSupported() const { @@ -2357,58 +3748,24 @@ bool HdRprApi::IsArbitraryShapedLightSupported() const { return m_impl->IsArbitraryShapedLightSupported(); } -int HdRprApi::GetCurrentRenderQuality() const { - return m_impl->GetCurrentRenderQuality(); +bool HdRprApi::IsSphereAndDiskLightSupported() const { + m_impl->InitIfNeeded(); + return m_impl->IsSphereAndDiskLightSupported(); } -void HdRprApi::ExportRprSceneOnNextRender(const char* exportPath) { - m_impl->ExportRprSceneOnNextRender(exportPath); +TfToken const& HdRprApi::GetCurrentRenderQuality() const { + return m_impl->GetCurrentRenderQuality(); } -std::string HdRprApi::GetAppDataPath() { - auto appDataPath = []() -> std::string { -#ifdef WIN32 - char appDataPath[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, appDataPath))) { - return appDataPath + std::string("\\hdRpr"); - } -#elif defined(__linux__) - if (auto homeEnv = getenv("XDG_DATA_HOME")) { - if (homeEnv[0] == '/') { - return homeEnv + std::string("/hdRpr"); - } - } - - int uid = getuid(); - auto homeEnv = std::getenv("HOME"); - if (uid != 0 && homeEnv) { - return homeEnv + std::string("/.config/hdRpr"); - } - -#elif defined(__APPLE__) - if (auto homeEnv = getenv("HOME")) { - if (homeEnv[0] == '/') { - return homeEnv + std::string("/Library/Application Support/hdRPR"); - } - } -#else -#warning "Unknown platform" -#endif - return ""; - }(); - if (!appDataPath.empty()) { - ArchCreateDirectory(appDataPath.c_str()); - } - return appDataPath; +rpr::FrameBuffer* HdRprApi::GetRawColorFramebuffer() { + return m_impl->GetRawColorFramebuffer(); } -std::string HdRprApi::GetCachePath() { - auto path = GetAppDataPath(); - if (!path.empty()) { - path = (path + ARCH_PATH_SEP) + "cache"; - ArchCreateDirectory(path.c_str()); - } - return path; +void HdRprApi::SetInteropInfo(void* interopInfo, std::condition_variable* presentedConditionVariable, bool* presentedCondition) { + m_impl->SetInteropInfo(interopInfo, presentedConditionVariable, presentedCondition); + + // Temporary should be force inited here, because otherwise has issues with GPU synchronization + m_impl->InitIfNeeded(); } PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/rprApi.h b/pxr/imaging/plugin/hdRpr/rprApi.h index 0eac0e7e4..e2023ad4c 100644 --- a/pxr/imaging/plugin/hdRpr/rprApi.h +++ b/pxr/imaging/plugin/hdRpr/rprApi.h @@ -24,6 +24,7 @@ limitations under the License. #include "pxr/base/tf/staticTokens.h" #include "pxr/imaging/hd/types.h" #include "pxr/imaging/hd/camera.h" +#include "pxr/imaging/hd/material.h" #include "pxr/imaging/hd/renderPassState.h" #include "pxr/imaging/hd/renderDelegate.h" @@ -32,16 +33,18 @@ limitations under the License. #include #include #include +#include PXR_NAMESPACE_OPEN_SCOPE +class HdRprDelegate; class HdRprRenderThread; class MaterialAdapter; class HdRprApiImpl; +class RprUsdMaterial; struct HdRprApiVolume; -struct HdRprApiMaterial; struct HdRprApiEnvironmentLight; template @@ -65,7 +68,7 @@ const uint32_t kInvisible = 0u; class HdRprApi final { public: - HdRprApi(HdRenderDelegate* delegate); + HdRprApi(HdRprDelegate* delegate); ~HdRprApi(); HdRprApiEnvironmentLight* CreateEnvironmentLight(const std::string& pathTotexture, float intensity); @@ -77,14 +80,20 @@ class HdRprApi final { rpr::SpotLight* CreateSpotLight(float angle, float softness); rpr::IESLight* CreateIESLight(std::string const& iesFilepath); rpr::PointLight* CreatePointLight(); + rpr::DiskLight* CreateDiskLight(); + rpr::SphereLight* CreateSphereLight(); void SetDirectionalLightAttributes(rpr::DirectionalLight* light, GfVec3f const& color, float shadowSoftnessAngle); - void SetLightColor(rpr::SpotLight* light, GfVec3f const& color); - void SetLightColor(rpr::PointLight* light, GfVec3f const& color); - void SetLightColor(rpr::IESLight* light, GfVec3f const& color); + void SetLightRadius(rpr::SphereLight* light, float radius); + void SetLightRadius(rpr::DiskLight* light, float radius); + void SetLightAngle(rpr::DiskLight* light, float angle); + void SetLightColor(rpr::RadiantLight* light, GfVec3f const& color); void Release(rpr::Light* light); + RprUsdMaterial* CreateGeometryLightMaterial(GfVec3f const& emissionColor); + void ReleaseGeometryLightMaterial(RprUsdMaterial* material); + struct VolumeMaterialParameters { GfVec3f scatteringColor = GfVec3f(1.0f); GfVec3f transmissionColor = GfVec3f(1.0f); @@ -100,27 +109,32 @@ class HdRprApi final { void SetTransform(HdRprApiVolume* volume, GfMatrix4f const& transform); void Release(HdRprApiVolume* volume); - HdRprApiMaterial* CreateMaterial(MaterialAdapter& materialAdapter); - HdRprApiMaterial* CreatePointsMaterial(VtVec3fArray const& colors); - void Release(HdRprApiMaterial* material); + RprUsdMaterial* CreateMaterial(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* CreateMeshInstance(rpr::Shape* prototypeMesh); void SetMeshRefineLevel(rpr::Shape* mesh, int level); void SetMeshVertexInterpolationRule(rpr::Shape* mesh, TfToken boundaryInterpolation); - void SetMeshMaterial(rpr::Shape* mesh, HdRprApiMaterial const* material, bool doublesided, bool displacementEnabled); + void SetMeshMaterial(rpr::Shape* mesh, RprUsdMaterial const* material, bool displacementEnabled); void SetMeshVisibility(rpr::Shape* mesh, uint32_t visibilityMask); void SetMeshId(rpr::Shape* mesh, uint32_t id); void Release(rpr::Shape* shape); rpr::Curve* CreateCurve(VtVec3fArray const& points, VtIntArray const& indices, VtFloatArray const& radiuses, VtVec2fArray const& uvs, VtIntArray const& segmentPerCurve); - void SetCurveMaterial(rpr::Curve* curve, HdRprApiMaterial const* material); + void SetCurveMaterial(rpr::Curve* curve, RprUsdMaterial const* material); void SetCurveVisibility(rpr::Curve* curve, uint32_t visibilityMask); void Release(rpr::Curve* curve); void SetTransform(rpr::SceneObject* object, GfMatrix4f const& transform); void SetTransform(rpr::Shape* shape, size_t numSamples, float* timeSamples, GfMatrix4d* transformSamples); + void SetName(rpr::ContextObject* object, const char* name); + void SetName(RprUsdMaterial* object, const char* name); + void SetName(HdRprApiEnvironmentLight* object, const char* name); + GfMatrix4d GetCameraViewMatrix() const; const GfMatrix4d& GetCameraProjectionMatrix() const; @@ -133,22 +147,27 @@ class HdRprApi final { void SetAovBindings(HdRenderPassAovBindingVector const& aovBindings); HdRenderPassAovBindingVector GetAovBindings() const; - int GetNumCompletedSamples() const; - // returns -1 if adaptive sampling is not used - int GetNumActivePixels() const; + void SetInteropInfo(void* interopInfo, std::condition_variable* presentedConditionVariable, bool* presentedCondition); + struct RenderStats { + double percentDone; + double averageRenderTimePerSample; + double averageResolveTimePerSample; + }; + RenderStats GetRenderStats() const; + + void CommitResources(); + void Resolve(); void Render(HdRprRenderThread* renderThread); void AbortRender(); bool IsChanged() const; bool IsGlInteropEnabled() const; - bool IsAovFormatConversionAvailable() const; + bool IsVulkanInteropEnabled() const; bool IsArbitraryShapedLightSupported() const; - int GetCurrentRenderQuality() const; - void ExportRprSceneOnNextRender(const char* exportPath); - - static std::string GetAppDataPath(); - static std::string GetCachePath(); + bool IsSphereAndDiskLightSupported() const; + TfToken const& GetCurrentRenderQuality() const; + rpr::FrameBuffer* GetRawColorFramebuffer(); private: HdRprApiImpl* m_impl = nullptr; diff --git a/pxr/imaging/plugin/hdRpr/rprApiAov.cpp b/pxr/imaging/plugin/hdRpr/rprApiAov.cpp index 4f942f7cb..6f1e14f40 100644 --- a/pxr/imaging/plugin/hdRpr/rprApiAov.cpp +++ b/pxr/imaging/plugin/hdRpr/rprApiAov.cpp @@ -15,7 +15,9 @@ limitations under the License. #include "rprApi.h" #include "rprApiFramebuffer.h" #include "rifcpp/rifError.h" -#include "rpr/error.h" + +#include "pxr/imaging/rprUsd/contextMetadata.h" +#include "pxr/imaging/rprUsd/error.h" PXR_NAMESPACE_OPEN_SCOPE @@ -52,28 +54,25 @@ bool ReadRifImage(rif_image image, void* dstBuffer, size_t dstBufferSize) { } // namespace anonymous HdRprApiAov::HdRprApiAov(rpr_aov rprAovType, int width, int height, HdFormat format, - rpr::Context* rprContext, rpr::ContextMetadata const& rprContextMetadata, std::unique_ptr filter) - : m_format(format), - m_filter(std::move(filter)) { - auto componentType = HdGetComponentFormat(format); - if (componentType != HdFormatUNorm8 && - componentType != HdFormatFloat16 && - componentType != HdFormatFloat32) { - TF_CODING_ERROR("Unsupported component type: %d", componentType); - m_format = HdFormatFloat32Vec4; + rpr::Context* rprContext, RprUsdContextMetadata const& rprContextMetadata, std::unique_ptr filter) + : m_aovDescriptor(HdRprAovRegistry::GetInstance().GetAovDesc(rprAovType, false)) + , m_filter(std::move(filter)) + , m_format(format) { + if (rif::Image::GetDesc(0, 0, format).type == 0) { + RIF_THROW_ERROR_MSG("Unsupported format: " + TfEnum::GetName(format)); } m_aov = pxr::make_unique(rprContext, width, height); m_aov->AttachAs(rprAovType); // XXX (Hybrid): Hybrid plugin does not support framebuffer resolving (rprContextResolveFrameBuffer) - if (rprContextMetadata.pluginType != rpr::kPluginHybrid) { + if (rprContextMetadata.pluginType != kPluginHybrid) { m_resolved = pxr::make_unique(rprContext, width, height); } } HdRprApiAov::HdRprApiAov(rpr_aov rprAovType, int width, int height, HdFormat format, - rpr::Context* rprContext, rpr::ContextMetadata const& rprContextMetadata, rif::Context* rifContext) + rpr::Context* rprContext, RprUsdContextMetadata const& rprContextMetadata, rif::Context* rifContext) : HdRprApiAov(rprAovType, width, height, format, rprContext, rprContextMetadata, [format, rifContext]() -> std::unique_ptr { if (format == HdFormatFloat32Vec4) { // RPR framebuffers by default with such format @@ -81,9 +80,11 @@ HdRprApiAov::HdRprApiAov(rpr_aov rprAovType, int width, int height, HdFormat for } auto filter = rif::Filter::CreateCustom(RIF_IMAGE_FILTER_RESAMPLE, rifContext); - if (filter) { - filter->SetParam("interpOperator", RIF_IMAGE_INTERPOLATION_NEAREST); + if (!filter) { + RPR_THROW_ERROR_MSG("Failed to create resample filter"); } + + filter->SetParam("interpOperator", RIF_IMAGE_INTERPOLATION_NEAREST); return filter; }()) { @@ -101,7 +102,8 @@ void HdRprApiAov::Resolve() { void HdRprApiAov::Clear() { if (m_aov) { - m_aov->Clear(); + auto& v = m_aovDescriptor.clearValue; + m_aov->Clear(v[0], v[1], v[2], v[3]); } } @@ -193,8 +195,9 @@ void HdRprApiAov::OnSizeChange(rif::Context* rifContext) { } } -HdRprApiColorAov::HdRprApiColorAov(int width, int height, HdFormat format, rpr::Context* rprContext, rpr::ContextMetadata const& rprContextMetadata) - : HdRprApiAov(RPR_AOV_COLOR, width, height, format, rprContext, rprContextMetadata, nullptr) { +HdRprApiColorAov::HdRprApiColorAov(HdFormat format, std::shared_ptr rawColorAov, rpr::Context* rprContext, RprUsdContextMetadata const& rprContextMetadata) + : HdRprApiAov(HdRprAovRegistry::GetInstance().GetAovDesc(rpr::Aov(kColorAlpha), true), format) + , m_retainedRawColor(std::move(rawColorAov)) { } @@ -213,7 +216,7 @@ void HdRprApiColorAov::SetFilter(Filter filter, bool enable) { void HdRprApiColorAov::SetOpacityAov(std::shared_ptr opacity) { if (m_retainedOpacity != opacity) { m_retainedOpacity = opacity; - SetFilter(kFilterComposeOpacity, m_retainedOpacity != nullptr); + SetFilter(kFilterComposeOpacity, CanComposeAlpha()); } } @@ -302,12 +305,27 @@ void HdRprApiColorAov::SetTonemap(TonemapParams const& params) { } void HdRprApiColorAov::SetTonemapFilterParams(rif::Filter* filter) { - filter->SetParam("exposure", m_tonemap.exposure); + filter->SetParam("exposureTime", m_tonemap.exposureTime); filter->SetParam("sensitivity", m_tonemap.sensitivity); filter->SetParam("fstop", m_tonemap.fstop); filter->SetParam("gamma", m_tonemap.gamma); } +bool HdRprApiColorAov::CanComposeAlpha() { + // Compositing alpha into framebuffer with less than 4 components is a no-op + return HdGetComponentCount(m_format) == 4 && m_retainedOpacity; +} + +void HdRprApiColorAov::Resize(int width, int height, HdFormat format) { + if (m_width != width || m_height != height) { + m_width = width; + m_height = height; + m_dirtyBits |= ChangeTracker::DirtySize; + } + + HdRprApiAov::Resize(width, height, format); +} + void HdRprApiColorAov::Update(HdRprApi const* rprApi, rif::Context* rifContext) { if (m_dirtyBits & ChangeTracker::DirtyFormat) { OnFormatChange(rifContext); @@ -344,7 +362,7 @@ void HdRprApiColorAov::Update(HdRprApi const* rprApi, rif::Context* rifContext) if ((m_enabledFilters & kFilterAIDenoise) || (m_enabledFilters & kFilterEAWDenoise)) { auto denoiseFilterType = (m_enabledFilters & kFilterAIDenoise) ? rif::FilterType::AIDenoise : rif::FilterType::EawDenoise; - auto fbDesc = m_aov->GetDesc(); + auto fbDesc = m_retainedRawColor->GetAovFb()->GetDesc(); auto type = (m_enabledFilters & kFilterAIDenoise) ? kFilterAIDenoise : kFilterEAWDenoise; auto filter = rif::Filter::Create(denoiseFilterType, rifContext, fbDesc.fb_width, fbDesc.fb_height); @@ -366,6 +384,7 @@ void HdRprApiColorAov::Update(HdRprApi const* rprApi, rif::Context* rifContext) } else if (m_enabledFilters & kFilterResample) { m_filter = rif::Filter::CreateCustom(RIF_IMAGE_FILTER_RESAMPLE, rifContext); m_filter->SetParam("interpOperator", RIF_IMAGE_INTERPOLATION_NEAREST); + m_mainFilterType = kFilterResample; } // Signal to update inputs @@ -385,6 +404,18 @@ void HdRprApiColorAov::Update(HdRprApi const* rprApi, rif::Context* rifContext) } } +bool HdRprApiColorAov::GetData(void* dstBuffer, size_t dstBufferSize) { + if (!m_filter) { + if (auto resolvedRawColorFb = m_retainedRawColor->GetResolvedFb()) { + return resolvedRawColorFb->GetData(dstBuffer, dstBufferSize); + } else { + return false; + } + } else { + return HdRprApiAov::GetData(dstBuffer, dstBufferSize); + } +} + void HdRprApiColorAov::Resolve() { HdRprApiAov::Resolve(); @@ -395,6 +426,7 @@ void HdRprApiColorAov::Resolve() { void HdRprApiColorAov::OnFormatChange(rif::Context* rifContext) { SetFilter(kFilterResample, m_format != HdFormatFloat32Vec4); + SetFilter(kFilterComposeOpacity, CanComposeAlpha()); m_dirtyBits |= ChangeTracker::DirtySize; } @@ -424,15 +456,15 @@ void HdRprApiColorAov::OnSizeChange(rif::Context* rifContext) { return; } - auto fbDesc = m_aov->GetDesc(); + auto fbDesc = m_retainedRawColor->GetAovFb()->GetDesc(); if (m_auxFilters.empty()) { - ResizeFilter(fbDesc.fb_width, fbDesc.fb_height, m_mainFilterType, m_filter.get(), GetResolvedFb()); + ResizeFilter(fbDesc.fb_width, fbDesc.fb_height, m_mainFilterType, m_filter.get(), m_retainedRawColor->GetResolvedFb()); } else { // Ideally we would use "Filter combining" functionality, but it does not work with user-defined filter // So we attach each filter separately auto filter = m_auxFilters.front().second.get(); - ResizeFilter(fbDesc.fb_width, fbDesc.fb_height, m_auxFilters.front().first, filter, GetResolvedFb()); + ResizeFilter(fbDesc.fb_width, fbDesc.fb_height, m_auxFilters.front().first, filter, m_retainedRawColor->GetResolvedFb()); for (int i = 1; i < m_auxFilters.size(); ++i) { auto filterInput = m_auxFilters[i - 1].second->GetOutput(); ResizeFilter(fbDesc.fb_width, fbDesc.fb_height, m_auxFilters[i].first, m_auxFilters[i].second.get(), filterInput); @@ -443,7 +475,7 @@ void HdRprApiColorAov::OnSizeChange(rif::Context* rifContext) { HdRprApiNormalAov::HdRprApiNormalAov( int width, int height, HdFormat format, - rpr::Context* rprContext, rpr::ContextMetadata const& rprContextMetadata, rif::Context* rifContext) + rpr::Context* rprContext, RprUsdContextMetadata const& rprContextMetadata, rif::Context* rifContext) : HdRprApiAov(RPR_AOV_SHADING_NORMAL, width, height, format, rprContext, rprContextMetadata, rif::Filter::CreateCustom(RIF_IMAGE_FILTER_REMAP_RANGE, rifContext)) { if (!rifContext) { RPR_THROW_ERROR_MSG("Can not create normal AOV: RIF context required"); @@ -468,8 +500,9 @@ void HdRprApiNormalAov::OnSizeChange(rif::Context* rifContext) { HdRprApiDepthAov::HdRprApiDepthAov( HdFormat format, std::shared_ptr worldCoordinateAov, - rpr::Context* rprContext, rpr::ContextMetadata const& rprContextMetadata, rif::Context* rifContext) - : m_retainedWorldCoordinateAov(worldCoordinateAov) { + rpr::Context* rprContext, RprUsdContextMetadata const& rprContextMetadata, rif::Context* rifContext) + : HdRprApiAov(HdRprAovRegistry::GetInstance().GetAovDesc(rpr::Aov(kNdcDepth), true), format) + , m_retainedWorldCoordinateAov(worldCoordinateAov) { if (!rifContext) { RPR_THROW_ERROR_MSG("Can not create depth AOV: RIF context required"); } @@ -493,7 +526,6 @@ HdRprApiDepthAov::HdRprApiDepthAov( auto fbDesc = m_retainedWorldCoordinateAov->GetAovFb()->GetDesc(); m_width = fbDesc.fb_width; m_height = fbDesc.fb_height; - m_format = format; } void HdRprApiDepthAov::Update(HdRprApi const* rprApi, rif::Context* rifContext) { @@ -515,10 +547,10 @@ void HdRprApiDepthAov::Update(HdRprApi const* rprApi, rif::Context* rifContext) auto viewProjectionMatrix = rprApi->GetCameraViewMatrix() * rprApi->GetCameraProjectionMatrix(); m_ndcFilter->SetParam("viewProjMatrix", GfMatrix4f(viewProjectionMatrix.GetTranspose())); + m_ndcFilter->Update(); if (m_remapFilter) { m_remapFilter->Update(); } - m_ndcFilter->Update(); } void HdRprApiDepthAov::Resize(int width, int height, HdFormat format) { @@ -534,4 +566,13 @@ void HdRprApiDepthAov::Resize(int width, int height, HdFormat format) { } } +void HdRprApiDepthAov::Resolve() { + if (m_ndcFilter) { + m_ndcFilter->Resolve(); + } + if (m_remapFilter) { + m_remapFilter->Resolve(); + } +} + PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/rprApiAov.h b/pxr/imaging/plugin/hdRpr/rprApiAov.h index cb63020f3..ed461aa48 100644 --- a/pxr/imaging/plugin/hdRpr/rprApiAov.h +++ b/pxr/imaging/plugin/hdRpr/rprApiAov.h @@ -14,46 +14,52 @@ limitations under the License. #ifndef HDRPR_RPR_API_AOV_H #define HDRPR_RPR_API_AOV_H +#include "aovDescriptor.h" #include "rprApiFramebuffer.h" #include "rifcpp/rifFilter.h" -#include "rpr/contextMetadata.h" #include "pxr/base/gf/matrix4f.h" #include "pxr/imaging/hd/types.h" PXR_NAMESPACE_OPEN_SCOPE class HdRprApi; +struct RprUsdContextMetadata; class HdRprApiAov { public: HdRprApiAov(rpr_aov rprAovType, int width, int height, HdFormat format, - rpr::Context* rprContext, rpr::ContextMetadata const& rprContextMetadata, std::unique_ptr filter); + rpr::Context* rprContext, RprUsdContextMetadata const& rprContextMetadata, std::unique_ptr filter); HdRprApiAov(rpr_aov rprAovType, int width, int height, HdFormat format, - rpr::Context* rprContext, rpr::ContextMetadata const& rprContextMetadata, rif::Context* rifContext); + rpr::Context* rprContext, RprUsdContextMetadata const& rprContextMetadata, rif::Context* rifContext); virtual ~HdRprApiAov() = default; virtual void Resize(int width, int height, HdFormat format); virtual void Update(HdRprApi const* rprApi, rif::Context* rifContext); virtual void Resolve(); - bool GetData(void* dstBuffer, size_t dstBufferSize); + virtual bool GetData(void* dstBuffer, size_t dstBufferSize); void Clear(); HdFormat GetFormat() const { return m_format; } + HdRprAovDescriptor const& GetDesc() const { return m_aovDescriptor; } + HdRprApiFramebuffer* GetAovFb() { return m_aov.get(); }; HdRprApiFramebuffer* GetResolvedFb(); protected: - HdRprApiAov() = default; + HdRprApiAov(HdRprAovDescriptor const& aovDescriptor, HdFormat format) + : m_aovDescriptor(aovDescriptor), m_format(format) {}; virtual void OnFormatChange(rif::Context* rifContext); virtual void OnSizeChange(rif::Context* rifContext); protected: + HdRprAovDescriptor const& m_aovDescriptor; + HdFormat m_format; + std::unique_ptr m_aov; std::unique_ptr m_resolved; std::unique_ptr m_filter; - HdFormat m_format = HdFormatInvalid; enum ChangeTracker { Clean = 0, @@ -69,10 +75,12 @@ class HdRprApiAov { class HdRprApiColorAov : public HdRprApiAov { public: - HdRprApiColorAov(int width, int height, HdFormat format, rpr::Context* rprContext, rpr::ContextMetadata const& rprContextMetadata); + HdRprApiColorAov(HdFormat format, std::shared_ptr rawColorAov, rpr::Context* rprContext, RprUsdContextMetadata const& rprContextMetadata); ~HdRprApiColorAov() override = default; + void Resize(int width, int height, HdFormat format) override; void Update(HdRprApi const* rprApi, rif::Context* rifContext) override; + bool GetData(void* dstBuffer, size_t dstBufferSize) override; void Resolve() override; void SetOpacityAov(std::shared_ptr opacity); @@ -89,13 +97,13 @@ class HdRprApiColorAov : public HdRprApiAov { struct TonemapParams { bool enable; - float exposure; + float exposureTime; float sensitivity; float fstop; float gamma; bool operator==(TonemapParams const& lhs) { - return exposure == lhs.exposure && + return exposureTime == lhs.exposureTime && sensitivity == lhs.sensitivity && fstop == lhs.fstop && gamma == lhs.gamma; @@ -126,7 +134,10 @@ class HdRprApiColorAov : public HdRprApiAov { void SetTonemapFilterParams(rif::Filter* filter); + bool CanComposeAlpha(); + private: + std::shared_ptr m_retainedRawColor; std::shared_ptr m_retainedOpacity; std::shared_ptr m_retainedDenoiseInputs[rif::MaxInput]; @@ -137,12 +148,15 @@ class HdRprApiColorAov : public HdRprApiAov { bool m_isEnabledFiltersDirty = true; TonemapParams m_tonemap; + + int m_width = 0; + int m_height = 0; }; class HdRprApiNormalAov : public HdRprApiAov { public: HdRprApiNormalAov(int width, int height, HdFormat format, - rpr::Context* rprContext, rpr::ContextMetadata const& rprContextMetadata, rif::Context* rifContext); + rpr::Context* rprContext, RprUsdContextMetadata const& rprContextMetadata, rif::Context* rifContext); ~HdRprApiNormalAov() override = default; protected: void OnFormatChange(rif::Context* rifContext) override; @@ -153,11 +167,12 @@ class HdRprApiDepthAov : public HdRprApiAov { public: HdRprApiDepthAov(HdFormat format, std::shared_ptr worldCoordinateAov, - rpr::Context* rprContext, rpr::ContextMetadata const& rprContextMetadata, rif::Context* rifContext); + rpr::Context* rprContext, RprUsdContextMetadata const& rprContextMetadata, rif::Context* rifContext); ~HdRprApiDepthAov() override = default; void Update(HdRprApi const* rprApi, rif::Context* rifContext) override; void Resize(int width, int height, HdFormat format) override; + void Resolve() override; private: std::unique_ptr m_retainedFilter; diff --git a/pxr/imaging/plugin/hdRpr/rprApiFramebuffer.cpp b/pxr/imaging/plugin/hdRpr/rprApiFramebuffer.cpp index 9b80a8d4d..b5fbd2cc6 100644 --- a/pxr/imaging/plugin/hdRpr/rprApiFramebuffer.cpp +++ b/pxr/imaging/plugin/hdRpr/rprApiFramebuffer.cpp @@ -12,7 +12,9 @@ limitations under the License. ************************************************************************/ #include "rprApiFramebuffer.h" -#include "rpr/helpers.h" +#include "aovDescriptor.h" + +#include "pxr/imaging/rprUsd/helpers.h" PXR_NAMESPACE_OPEN_SCOPE @@ -63,11 +65,19 @@ void HdRprApiFramebuffer::AttachAs(rpr::Aov aov) { m_aov = aov; } -void HdRprApiFramebuffer::Clear() { +void HdRprApiFramebuffer::Clear(float r, float g, float b, float a) { if (m_width == 0 || m_height == 0) { return; } RPR_ERROR_CHECK(m_rprFb->Clear(), "Failed to clear framebuffer"); + + // XXX (FIR-1681): We can not rely on clear values because every AOV in RPR is multisampled, i.e. + // value of singlesampled AOV (any ID AOV, worldCoordinate, etc) is always equals to `clearValue + renderedValue` + /*if (r == 0.0f && g == 0.0f && b == 0.0f && a == 0.0f) { + RPR_ERROR_CHECK(m_rprFb->Clear(), "Failed to clear framebuffer"); + } else { + RPR_ERROR_CHECK(m_rprFb->FillWithColor(r, g, b, a), "Failed to clear framebuffer"); + }*/ } void HdRprApiFramebuffer::Resolve(HdRprApiFramebuffer* dstFrameBuffer) { @@ -123,7 +133,7 @@ rpr_cl_mem HdRprApiFramebuffer::GetCLMem() { return nullptr; } - return rpr::GetInfo(m_rprFb, rpr_framebuffer_info(RPR_CL_MEM_OBJECT)); + return RprUsdGetInfo(m_rprFb, rpr_framebuffer_info(RPR_CL_MEM_OBJECT)); } void HdRprApiFramebuffer::Create(uint32_t width, uint32_t height) { diff --git a/pxr/imaging/plugin/hdRpr/rprApiFramebuffer.h b/pxr/imaging/plugin/hdRpr/rprApiFramebuffer.h index 08af8e501..0fc63ad24 100644 --- a/pxr/imaging/plugin/hdRpr/rprApiFramebuffer.h +++ b/pxr/imaging/plugin/hdRpr/rprApiFramebuffer.h @@ -23,7 +23,6 @@ PXR_NAMESPACE_OPEN_SCOPE class HdRprApiFramebuffer { public: - static constexpr rpr::Aov kAovNone = static_cast(-1); static constexpr uint32_t kNumChannels = 4; HdRprApiFramebuffer(rpr::Context* context, uint32_t width, uint32_t height); @@ -33,7 +32,7 @@ class HdRprApiFramebuffer { HdRprApiFramebuffer& operator=(HdRprApiFramebuffer&& fb) noexcept; void AttachAs(rpr::Aov aov); - void Clear(); + void Clear(float r, float g, float b, float a); void Resolve(HdRprApiFramebuffer* dstFrameBuffer); /// Return true if framebuffer was actually resized bool Resize(uint32_t width, uint32_t height); @@ -41,6 +40,7 @@ class HdRprApiFramebuffer { bool GetData(void* dstBuffer, size_t dstBufferSize); size_t GetSize() const; rpr::FramebufferDesc GetDesc() const; + rpr::Aov GetAovId() const { return m_aov; } rpr_cl_mem GetCLMem(); rpr::FrameBuffer* GetRprObject() { return m_rprFb; } diff --git a/pxr/imaging/plugin/hdRpr/usdviewMenu/CMakeLists.txt b/pxr/imaging/plugin/hdRpr/usdviewMenu/CMakeLists.txt new file mode 100644 index 000000000..25312e6fe --- /dev/null +++ b/pxr/imaging/plugin/hdRpr/usdviewMenu/CMakeLists.txt @@ -0,0 +1,7 @@ +install( + FILES rpr.py + DESTINATION lib/python/rprUsdviewMenu + RENAME "__init__.py") +install( + FILES plugInfo.json + DESTINATION plugin/usd/rprUsdviewMenu/resources) diff --git a/pxr/imaging/plugin/hdRpr/python/plugInfo.json b/pxr/imaging/plugin/hdRpr/usdviewMenu/plugInfo.json similarity index 74% rename from pxr/imaging/plugin/hdRpr/python/plugInfo.json rename to pxr/imaging/plugin/hdRpr/usdviewMenu/plugInfo.json index 0c1712079..798ab4432 100644 --- a/pxr/imaging/plugin/hdRpr/python/plugInfo.json +++ b/pxr/imaging/plugin/hdRpr/usdviewMenu/plugInfo.json @@ -2,12 +2,12 @@ "Plugins": [ { "Type": "python", - "Name": "rpr", + "Name": "rprUsdviewMenu", "Info": { "Types": { - "rpr.RprPluginContainer": { + "rprUsdviewMenu.RprPluginContainer": { "bases": ["pxr.Usdviewq.plugin.PluginContainer"], - "displayName": "Usdview Radeon ProRender Plugin" + "displayName": "Usdview Radeon ProRender Plugin Menu" } } } diff --git a/pxr/imaging/plugin/hdRpr/usdviewMenu/rpr.py b/pxr/imaging/plugin/hdRpr/usdviewMenu/rpr.py new file mode 100644 index 000000000..3310c5bcd --- /dev/null +++ b/pxr/imaging/plugin/hdRpr/usdviewMenu/rpr.py @@ -0,0 +1,198 @@ +# 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. +# +from pxr import Tf +from pxr.Plug import Registry +from pxr.Usdviewq.plugin import PluginContainer +from pxr.Usdviewq.qt import QtWidgets + +from ctypes import cdll, c_void_p, c_char_p, cast +from ctypes.util import find_library + +import os +import glob + +from rpr import RprUsd + +def setCacheDir(usdviewApi, type, startDirectory, setter): + directory = QtWidgets.QFileDialog.getExistingDirectory( + usdviewApi._UsdviewApi__appController._mainWindow, + caption='RPR {} Cache Directory'.format(type), + dir=startDirectory) + + if directory: + setter(directory) + +def SetTextureCacheDir(usdviewApi): + setCacheDir(usdviewApi, 'Texture', RprUsd.Config.GetTextureCacheDir(), RprUsd.Config.SetTextureCacheDir) +def SetKernelCacheDir(usdviewApi): + setCacheDir(usdviewApi, 'Kernel', RprUsd.Config.GetKernelCacheDir(), RprUsd.Config.SetKernelCacheDir) + +def clearCache(cache_dir): + num_files_removed = 0 + for pattern in ('*.bin.check', '*.bin', '*.cache'): + for cache_file in glob.iglob(os.path.join(cache_dir, pattern)): + os.remove(cache_file) + num_files_removed += 1 + print('RPR: removed {} cache files'.format(num_files_removed)) + +def ClearTextureCache(usdviewApi): + clearCache(RprUsd.Config.GetTextureCacheDir()) +def ClearKernelCache(usdviewApi): + clearCache(RprUsd.Config.GetKernelCacheDir()) + +def getRprPath(_pathCache=[None]): + if _pathCache[0]: + return _pathCache[0] + + rprPluginType = Registry.FindTypeByName('HdRprPlugin') + plugin = Registry().GetPluginForType(rprPluginType) + if plugin and plugin.path: + _pathCache[0] = plugin.path + return _pathCache[0] + +def reemitStage(usdviewApi): + usdviewApi._UsdviewApi__appController._reopenStage() + usdviewApi._UsdviewApi__appController._rendererPluginChanged('HdRprPlugin') + +def setRenderDevice(usdviewApi, renderDeviceId): + rprPath = getRprPath() + if rprPath is not None: + lib = cdll.LoadLibrary(rprPath) + lib.HdRprSetRenderDevice(renderDeviceId) + reemitStage(usdviewApi) + +def setRenderQuality(usdviewApi, quality): + rprPath = getRprPath() + if rprPath is not None: + lib = cdll.LoadLibrary(rprPath) + lib.HdRprGetRenderQuality.restype = c_void_p + lib.HdRprFree.argtypes = [c_void_p] + + currentQualityPtr = lib.HdRprGetRenderQuality() + if not currentQualityPtr: + # Enable RPR plugin if it was not done yet + reemitStage(usdviewApi) + + currentQuality = cast(currentQualityPtr, c_char_p).value + lib.HdRprFree(currentQualityPtr) + + if quality == currentQuality: + return + lib.HdRprSetRenderQuality(quality) + + def getPluginName(quality): + if quality == b'Full': + return 'Tahoe' + elif quality == b'Northstar': + return 'Northstar' + else: + return 'Hybrid' + + if getPluginName(quality) != getPluginName(currentQuality): + reemitStage(usdviewApi) + +def SetRenderDeviceCPU(usdviewApi): + setRenderDevice(usdviewApi, b'CPU') +def SetRenderDeviceGPU(usdviewApi): + setRenderDevice(usdviewApi, b'GPU') + +def SetRenderLowQuality(usdviewApi): + setRenderQuality(usdviewApi, b'Low') +def SetRenderMediumQuality(usdviewApi): + setRenderQuality(usdviewApi, b'Medium') +def SetRenderHighQuality(usdviewApi): + setRenderQuality(usdviewApi, b'High') +def SetRenderFullQuality(usdviewApi): + setRenderQuality(usdviewApi, b'Full') +def SetRenderNorthstarQuality(usdviewApi): + setRenderQuality(usdviewApi, b'Northstar') + +class RprPluginContainer(PluginContainer): + + def registerPlugins(self, plugRegistry, usdviewApi): + self.rDeviceCpu = plugRegistry.registerCommandPlugin( + "RprPluginContainer.renderDeviceCPU", + "CPU", + SetRenderDeviceCPU) + self.rDeviceGpu = plugRegistry.registerCommandPlugin( + "RprPluginContainer.renderDeviceGPU", + "GPU", + SetRenderDeviceGPU) + + self.setRenderLowQuality = plugRegistry.registerCommandPlugin( + "RprPluginContainer.setRenderLowQuality", + "Low", + SetRenderLowQuality) + self.setRenderMediumQuality = plugRegistry.registerCommandPlugin( + "RprPluginContainer.setRenderMediumQuality", + "Medium", + SetRenderMediumQuality) + self.setRenderHighQuality = plugRegistry.registerCommandPlugin( + "RprPluginContainer.setRenderHighQuality", + "High", + SetRenderHighQuality) + self.setRenderFullQuality = plugRegistry.registerCommandPlugin( + "RprPluginContainer.setRenderFullQuality", + "Full", + SetRenderFullQuality) + self.setRenderNorthstarQuality = plugRegistry.registerCommandPlugin( + "RprPluginContainer.setRenderNorthstarQuality", + "Full 2.0 (Beta)", + SetRenderNorthstarQuality) + + self.setTextureCacheDir = plugRegistry.registerCommandPlugin( + "RprPluginContainer.setTextureCacheDir", + "Set Texture Cache Directory", + SetTextureCacheDir) + self.setKernelCacheDir = plugRegistry.registerCommandPlugin( + "RprPluginContainer.setKernelCacheDir", + "Set Kernel Cache Directory", + SetKernelCacheDir) + self.clearTextureCache = plugRegistry.registerCommandPlugin( + "RprPluginContainer.clearTextureCache", + "Clear Texture Cache", + ClearTextureCache) + self.clearKernelCache = plugRegistry.registerCommandPlugin( + "RprPluginContainer.clearKernelCache", + "Clear Kernel Cache", + ClearKernelCache) + + self.restartAction = plugRegistry.registerCommandPlugin( + "RprPluginContainer.restartAction", + "Restart", + reemitStage) + + + def configureView(self, plugRegistry, plugUIBuilder): + + rprMenu = plugUIBuilder.findOrCreateMenu("RPR") + + renderDeviceSubMenu = rprMenu.findOrCreateSubmenu("Render Device") + renderDeviceSubMenu.addItem(self.rDeviceCpu) + renderDeviceSubMenu.addItem(self.rDeviceGpu) + + renderQualityMenu = rprMenu.findOrCreateSubmenu("Render Quality") + renderQualityMenu.addItem(self.setRenderLowQuality) + renderQualityMenu.addItem(self.setRenderMediumQuality) + renderQualityMenu.addItem(self.setRenderHighQuality) + renderQualityMenu.addItem(self.setRenderFullQuality) + renderQualityMenu.addItem(self.setRenderNorthstarQuality) + + cacheMenu = rprMenu.findOrCreateSubmenu('Cache') + cacheMenu.addItem(self.setTextureCacheDir) + cacheMenu.addItem(self.setKernelCacheDir) + cacheMenu.addItem(self.clearTextureCache) + cacheMenu.addItem(self.clearKernelCache) + + rprMenu.addItem(self.restartAction) + +Tf.Type.Define(RprPluginContainer) diff --git a/pxr/imaging/plugin/hdRpr/volume.cpp b/pxr/imaging/plugin/hdRpr/volume.cpp index f3f8af628..7d936738b 100644 --- a/pxr/imaging/plugin/hdRpr/volume.cpp +++ b/pxr/imaging/plugin/hdRpr/volume.cpp @@ -11,7 +11,7 @@ See the License for the specific language governing permissions and limitations under the License. ************************************************************************/ -#include +#include using json = nlohmann::json; #include "volume.h" @@ -381,7 +381,7 @@ void HdRprVolume::Sync( decltype(m_fieldSubscriptions) activeFieldSubscriptions; auto volumeFieldDescriptorVector = sceneDelegate->GetVolumeFieldDescriptors(GetId()); - for (auto const& desc : sceneDelegate->GetVolumeFieldDescriptors(GetId())) { + for (auto const& desc : volumeFieldDescriptorVector) { GridInfo* targetInfo; if (desc.fieldName == HdRprVolumeTokens->density) { targetInfo = &densityGridInfo; diff --git a/pxr/imaging/plugin/rprHoudini/CMakeLists.txt b/pxr/imaging/plugin/rprHoudini/CMakeLists.txt new file mode 100644 index 000000000..b4e1f4678 --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/CMakeLists.txt @@ -0,0 +1,53 @@ +if(NOT TARGET Houdini) + return() +endif() + +add_library(RPR_for_Houdini SHARED + plugin.cpp + VOP_RPRMaterial.h + VOP_RPRMaterial.cpp + LOP_RPRExportHelper.h + LOP_RPRExportHelper.cpp) + +target_link_libraries(RPR_for_Houdini + arch + usd + usdGeom + usdRender + rprUsd + Houdini) + +GroupSources(RPR_for_Houdini) + +houdini_configure_target(RPR_for_Houdini "INSTDIR" "${CMAKE_INSTALL_PREFIX}/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 .) +install( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/hda/rpr_exportRpr1.hda + ${CMAKE_CURRENT_SOURCE_DIR}/hda/rpr_standard_rendervars.hda + DESTINATION "houdini/otls") +install( + FILES + ${CMAKE_CURRENT_SOURCE_DIR}/scripts/python/houRpr/__init__.py + ${CMAKE_CURRENT_SOURCE_DIR}/scripts/python/houRpr/cache.py + DESTINATION + houdini/scripts/python/houRpr) +install( + FILES + ui/RPR.svg + DESTINATION + houdini/config/Icons) +install( + FILES + ui/MainMenuCommon.xml + DESTINATION + houdini) diff --git a/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.cpp b/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.cpp new file mode 100644 index 000000000..aa85a11e7 --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.cpp @@ -0,0 +1,147 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "LOP_RPRExportHelper.h" + +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +static PRM_Name g_exportPathName("exportPath", "Export Path"); +static PRM_Name g_renderSettingsName("renderSettings", "Render Settings"); + +static PRM_Template g_templateList[] = { + PRM_Template(PRM_FILE, 1, &g_exportPathName), + PRM_Template(PRM_STRING_E, 1, &g_renderSettingsName), + PRM_Template(), +}; + +void LOP_RPRExportHelper::Register(OP_OperatorTable* table) { + auto opOperator = new OP_Operator( + "rpr_lop_rprExportHelper", + "RPR Export Helper", + [](OP_Network *net, const char *name, OP_Operator *op) -> OP_Node* { + return new LOP_RPRExportHelper(net, name, op); + }, + g_templateList, + 0u, + (unsigned)1); + opOperator->setIconName("RPR"); + + table->addOperator(opOperator); +} + +LOP_RPRExportHelper::LOP_RPRExportHelper(OP_Network *net, const char *name, OP_Operator *op) + : LOP_Node(net, name, op) { + +} + +OP_ERROR LOP_RPRExportHelper::cookMyLop(OP_Context &context) { + if (cookModifyInput(context) >= UT_ERROR_FATAL) { + return error(); + } + + UT_String exportPath; + evalString(exportPath, g_exportPathName.getToken(), 0, context.getTime()); + + if (!exportPath.isstring()) { + return error(); + } + + if (!exportPath.endsWith(".rpr")) { + addWarning(LOP_MESSAGE, "Export path must end with .rpr"); + exportPath += ".rpr"; + } + + UT_String renderSettingsPath; + evalString(renderSettingsPath, g_renderSettingsName.getToken(), 0, context.getTime()); + + HUSD_AutoWriteLock writelock(editableDataHandle()); + HUSD_AutoLayerLock layerlock(writelock); + + UsdStageRefPtr stage = writelock.data()->stage(); + + // Insert export file into each UsdRenderSetting primitive, + // or if no UsdRenderSetting primitives exist create new one + auto modifyRenderSettings = [this, &exportPath](UsdRenderSettings& renderSettings) { + auto prim = renderSettings.GetPrim(); + + static TfToken rprExportPath("rprExportPath", TfToken::Immortal); + if (auto exportPathAttr = prim.CreateAttribute(rprExportPath, SdfValueTypeNames->String, true)) { + if (!exportPathAttr.Set(exportPath.toStdString())) { + addError(LOP_MESSAGE, TfStringPrintf("Failed to set %s:%s", prim.GetPath().GetText(), rprExportPath.GetText()).c_str()); + return false; + } + } else { + addError(LOP_MESSAGE, TfStringPrintf("Failed to create %s attribute", rprExportPath.GetText()).c_str()); + return false; + } + + return true; + }; + + // Use explicitly specified render settings primitive if any + // + UsdRenderSettings renderSettings; + if (renderSettingsPath.isstring()) { + renderSettings = UsdRenderSettings(stage->GetPrimAtPath(HUSDgetSdfPath(renderSettingsPath))); + } + + if (renderSettings) { + modifyRenderSettings(renderSettings); + } else { + // If no valid render settings primitive was specified, + // modify all available render settings on the stage + // because we don't know which one will be selected implicitly + // + bool hasAnyRenderSettingsPrims = false; + if (auto renderPrim = stage->GetPrimAtPath(SdfPath("/Render"))) { + for (auto const& prim : renderPrim.GetDescendants()) { + if (auto renderSettings = UsdRenderSettings(prim)) { + if (modifyRenderSettings(renderSettings)) { + hasAnyRenderSettingsPrims = true; + } + } + } + } + + // But if there are no render settings primitives, create a new one + // + if (!hasAnyRenderSettingsPrims) { + SdfPath renderScopePath("/Render"); + if (auto renderScope = UsdGeomScope::Define(stage, renderScopePath)) { + auto renderSettingsPath = renderScopePath.AppendElementString("rprExportRenderSettings"); + if (auto renderSettings = UsdRenderSettings::Define(stage, renderSettingsPath)) { + modifyRenderSettings(renderSettings); + } + } + } + } + + return error(); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.h b/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.h new file mode 100644 index 000000000..1cf47155a --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/LOP_RPRExportHelper.h @@ -0,0 +1,38 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPR_LOP_RPREXPORTHELPER_H +#define RPR_LOP_RPREXPORTHELPER_H + +#include "pxr/pxr.h" + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +/// This node sets rprExportPath render settings to a particular UsdRenderSettings primitive. +/// It's impossible to implement needed functionality in a custom HDA, that's why this node was implemented. +class LOP_RPRExportHelper : public LOP_Node { +public: + LOP_RPRExportHelper(OP_Network *net, const char *name, OP_Operator *op); + ~LOP_RPRExportHelper() override = default; + + static void Register(OP_OperatorTable *table); + +protected: + OP_ERROR cookMyLop(OP_Context &context) override; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPR_LOP_RPREXPORTHELPER_H diff --git a/pxr/imaging/plugin/rprHoudini/VOP_RPRMaterial.cpp b/pxr/imaging/plugin/rprHoudini/VOP_RPRMaterial.cpp new file mode 100644 index 000000000..3b76f1c27 --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/VOP_RPRMaterial.cpp @@ -0,0 +1,706 @@ +/************************************************************************ +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 "VOP_RPRMaterial.h" + +#include +#include + +#include +#include + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +/** + Simple identity macro that does nothing but helps to identify allocations of + memory that will never be deleted, until we find a way to do so. +*/ +#define LEAKED(ptr) ptr + +static VOP_Type GetVOPType(RprUsdMaterialNodeInput::Type rprType) { + switch (rprType) { + case RprUsdMaterialNodeElement::kAngle: + case RprUsdMaterialNodeElement::kFloat: + return VOP_TYPE_FLOAT; + case RprUsdMaterialNodeElement::kVector3: + return VOP_TYPE_VECTOR; + case RprUsdMaterialNodeElement::kVector2: + return VOP_TYPE_VECTOR2; + case RprUsdMaterialNodeElement::kColor3: + return VOP_TYPE_COLOR; + case RprUsdMaterialNodeElement::kNormal: + return VOP_TYPE_NORMAL; + case RprUsdMaterialNodeElement::kBoolean: + case RprUsdMaterialNodeElement::kInteger: + return VOP_TYPE_INTEGER; + case RprUsdMaterialNodeElement::kToken: + case RprUsdMaterialNodeElement::kString: + case RprUsdMaterialNodeElement::kFilepath: + return VOP_TYPE_STRING; + case RprUsdMaterialNodeElement::kVolumeShader: + return VOP_ATMOSPHERE_SHADER; + case RprUsdMaterialNodeElement::kSurfaceShader: + return VOP_SURFACE_SHADER; + case RprUsdMaterialNodeElement::kDisplacementShader: + return VOP_DISPLACEMENT_SHADER; + default: + return VOP_TYPE_UNDEF; + } +} + +static PRM_Type const& GetPRMType(RprUsdMaterialNodeInput::Type rprType) { + switch (rprType) { + case RprUsdMaterialNodeElement::kFloat: + return PRM_FLT; + case RprUsdMaterialNodeElement::kAngle: + return PRM_ANGLE; + case RprUsdMaterialNodeElement::kVector2: + case RprUsdMaterialNodeElement::kVector3: + case RprUsdMaterialNodeElement::kNormal: + return PRM_XYZ; + case RprUsdMaterialNodeElement::kColor3: + return PRM_RGB; + case RprUsdMaterialNodeElement::kInteger: + return PRM_INT; + case RprUsdMaterialNodeElement::kBoolean: + return PRM_TOGGLE; + case RprUsdMaterialNodeElement::kToken: + return PRM_ORD_E; + case RprUsdMaterialNodeElement::kString: + return PRM_STRING_E; + case RprUsdMaterialNodeElement::kFilepath: + return PRM_FILE; + default: + return PRM_LIST_TERMINATOR; + } +} + +static PRM_Range* NewPRMRange(RprUsdMaterialNodeInput const* input) { + if (input->GetType() == RprUsdMaterialNodeElement::kFloat || + input->GetType() == RprUsdMaterialNodeElement::kInteger) { + bool isRangeSet = false; + float min = std::numeric_limits::lowest(); + float max = std::numeric_limits::max(); + auto minFlag = PRM_RANGE_UI; + auto maxFlag = PRM_RANGE_UI; + + auto uiMinString = input->GetUIMin(); + auto uiMinSoftString = input->GetUISoftMin(); + + auto uiMaxString = input->GetUIMax(); + auto uiMaxSoftString = input->GetUISoftMax(); + + if (uiMinString) { + min = std::stof(uiMinString); + minFlag = PRM_RANGE_RESTRICTED; + isRangeSet = true; + } else if (uiMinSoftString) { + min = std::stof(uiMinSoftString); + isRangeSet = true; + } + + if (uiMaxString) { + max = std::stof(uiMaxString); + maxFlag = PRM_RANGE_RESTRICTED; + isRangeSet = true; + } else if (uiMaxSoftString) { + max = std::stof(uiMaxSoftString); + isRangeSet = true; + } + + if (isRangeSet) { + return new PRM_Range(minFlag, min, maxFlag, max); + } + } + + return nullptr; +} + +static PRM_Default* NewPRMDefault( + RprUsdMaterialNodeInput const* input, + unsigned i_nb_defaults, + PRM_ChoiceList** choiceListPtr) { + auto valueStr = input->GetValueString(); + if (!valueStr) { + return nullptr; + } + + std::vector values; + + if (input->GetType() == RprUsdMaterialNodeElement::kBoolean) { + if (std::strcmp(valueStr, "true") == 0) { + values.push_back(1.0f); + } else if (std::strcmp(valueStr, "false") == 0) { + values.push_back(0.0f); + } + } else if (input->GetType() == RprUsdMaterialNodeElement::kFloat || + input->GetType() == RprUsdMaterialNodeElement::kAngle || + input->GetType() == RprUsdMaterialNodeElement::kInteger || + input->GetType() == RprUsdMaterialNodeElement::kVector3 || + input->GetType() == RprUsdMaterialNodeElement::kVector2 || + input->GetType() == RprUsdMaterialNodeElement::kColor3 || + input->GetType() == RprUsdMaterialNodeElement::kNormal) { + auto valueStrings = TfStringTokenize(valueStr, ", \t"); + if (valueStrings.size() == i_nb_defaults) { + for (size_t i = 0; i < valueStrings.size(); ++i) { + values.push_back(std::stof(valueStrings[i])); + } + } + } else if (input->GetType() == RprUsdMaterialNodeElement::kToken) { + auto defau1t = LEAKED(new PRM_Default(0, LEAKED(strdup(valueStr)))); + auto items = LEAKED(new std::vector); + + for (auto& value : input->GetTokenValues()) { + items->push_back(PRM_Item(LEAKED(strdup(value.GetText())))); + } + items->push_back(PRM_Item()); + + auto& choiceList = *choiceListPtr; + auto choiceListType = PRM_ChoiceListType(PRM_CHOICELIST_SINGLE | PRM_CHOICELIST_USE_TOKEN); + choiceList = LEAKED(new PRM_ChoiceList(choiceListType, items->data())); + + return defau1t; + } else if (input->GetType() == RprUsdMaterialNodeElement::kString) { + return LEAKED(new PRM_Default(0, LEAKED(strdup(valueStr)))); + } + + if (values.size() == i_nb_defaults) { + auto def = new PRM_Default[i_nb_defaults]; + for (unsigned i = 0; i < i_nb_defaults; ++i) { + def[i] = PRM_Default(values[i]); + } + return def; + } + + return nullptr; +} + +static unsigned GetNumChannels(RprUsdMaterialNodeInput const* input) { + if (input->GetType() == RprUsdMaterialNodeElement::kColor3 || + input->GetType() == RprUsdMaterialNodeElement::kVector3 || + input->GetType() == RprUsdMaterialNodeElement::kNormal) { + return 3; + } else if (input->GetType() == RprUsdMaterialNodeElement::kVector2) { + return 2; + } + + return 1; +} + +static std::vector* GetShaderTemplates(RprUsdMaterialNodeInfo const* shaderInfo) { + /* + The templates and their components (names and such) are dynamically + allocated here but never deleted, since they're expected to be valid for + the duration of the process. It's not that different from allocating + them as static variables, which is the usual way to do this when only + one type of operator is defined. Allocating them dynamically allows + multiple operator types, each with an arbitrary number of parameters, to + be created. + It could be interesting to keep them around in VOP_RPRMaterialOperator + and delete them when its destructor is called. Unfortunately, even + VOP_Operator's destructor doesn't seem to even be called, so we simply + use the LEAKED macro to mark them until we find out what we should do. + */ + auto templates = LEAKED(new std::vector); + + // Scan inputs for UI folders, we create tab for each of them + std::vector tabNames; + std::map> inputsPerTab; + for (size_t i = 0; i < shaderInfo->GetNumInputs(); ++i) { + auto input = shaderInfo->GetInput(i); + + std::string uiFolder; + if (input->GetUIFolder()) { + uiFolder = input->GetUIFolder(); + } + + auto tabIt = inputsPerTab.find(uiFolder); + if (tabIt == inputsPerTab.end()) { + tabNames.push_back(uiFolder); + inputsPerTab[uiFolder] = {input}; + } else { + tabIt->second.push_back(input); + } + } + + auto tabs = LEAKED(new std::vector); + for (auto& tabName : tabNames) { + if (tabName.empty()) { + continue; + } + auto inputsIt = inputsPerTab.find(tabName); + auto numInputsInTab = static_cast(inputsIt->second.size()); + tabs->push_back(PRM_Default(numInputsInTab, LEAKED(strdup(tabName.c_str())))); + } + + if (!tabs->empty()) { + static PRM_Name tabsName("tabs"); + templates->push_back(PRM_Template(PRM_SWITCHER, tabs->size(), &tabsName, tabs->data())); + } + + for (auto& tabName : tabNames) { + auto inputsIt = inputsPerTab.find(tabName); + for (auto input : inputsIt->second) { + auto numChannels = GetNumChannels(input); + auto uiName = input->GetUIName(); + if (!uiName) { + continue; + } + + auto docString = input->GetDocString(); + const char* doc = docString ? LEAKED(strdup(docString)) : nullptr; + + auto name = LEAKED(new PRM_Name(PRM_Name::COPY, input->GetName(), uiName)); + auto& type = GetPRMType(input->GetType()); + auto range = LEAKED(NewPRMRange(input)); + PRM_ChoiceList* choiceList = nullptr; + auto defau1t = LEAKED(NewPRMDefault(input, numChannels, &choiceList)); + + PRM_ConditionalBase* conditionalBase = nullptr; + PRM_SpareData* spareData = nullptr; + PRM_Callback callback = nullptr; + int paramGroup = 1; + + templates->push_back(PRM_Template(type, numChannels, name, defau1t, choiceList, range, callback, spareData, paramGroup, doc, conditionalBase)); + } + } + + templates->push_back(PRM_Template()); + return templates; +} + +PRM_Template* VOP_RPRMaterial::GetTemplates(RprUsdMaterialNodeInfo const* shaderInfo) { + return &GetShaderTemplates(shaderInfo)->at(0); +} + +VOP_RPRMaterial::VOP_RPRMaterial(OP_Network* parent, const char* name, OP_Operator* entry) + : VOP_Node(parent, name, entry) { + + auto rpr_entry = dynamic_cast(entry); + m_shaderInfo = rpr_entry->shaderInfo; + + for (size_t i = 0; i < m_shaderInfo->GetNumOutputs(); ++i) { + auto output = m_shaderInfo->GetOutput(i); + + if (output->GetType() == RprUsdMaterialNodeElement::kVolumeShader || + output->GetType() == RprUsdMaterialNodeElement::kDisplacementShader || + output->GetType() == RprUsdMaterialNodeElement::kSurfaceShader) { + + if (output->GetType() == RprUsdMaterialNodeElement::kVolumeShader) { + m_shaderType = VOP_ATMOSPHERE_SHADER; + } else if (output->GetType() == RprUsdMaterialNodeElement::kSurfaceShader) { + m_shaderType = VOP_SURFACE_SHADER; + } else if (output->GetType() == RprUsdMaterialNodeElement::kDisplacementShader) { + m_shaderType = VOP_DISPLACEMENT_SHADER; + } + + setMaterialFlag(true); + break; + } + } +} + +const char* VOP_RPRMaterial::inputLabel(unsigned i_idx) const { + return m_shaderInfo->GetInput(i_idx)->GetName(); +} + +const char* VOP_RPRMaterial::outputLabel(unsigned i_idx) const { + return m_shaderInfo->GetOutput(i_idx)->GetName(); +} + +unsigned VOP_RPRMaterial::minInputs() const { + return 0; +} + +unsigned VOP_RPRMaterial::getNumVisibleInputs() const { + return m_shaderInfo->GetNumInputs(); +} + +unsigned VOP_RPRMaterial::orderedInputs() const { + return m_shaderInfo->GetNumInputs(); +} + +#if HDK_API_VERSION >= 18000000 + +UT_StringHolder VOP_RPRMaterial::getShaderName( + VOP_ShaderNameStyle style, + VOP_Type shader_type) const { + /* This name is what becomes the shader id in USD land. Our Hydra plugin + will use it to find the correct shader. */ + if (style == VOP_ShaderNameStyle::PLAIN) { + return m_shaderInfo->GetName(); + } + + return VOP_Node::getShaderName(style, shader_type); +} + +VOP_Type VOP_RPRMaterial::getShaderType() const { + return m_shaderType; +} + +#endif + +void VOP_RPRMaterial::getInputNameSubclass(UT_String &in, int i_idx) const { + in = inputLabel(i_idx); +} + +int VOP_RPRMaterial::getInputFromNameSubclass(const UT_String &in) const { + for (size_t idx = 0; idx < m_shaderInfo->GetNumInputs(); ++idx) { + if (m_shaderInfo->GetInput(idx)->GetName() == in) { + return static_cast(idx); + } + } + return -1; +} + +void VOP_RPRMaterial::getInputTypeInfoSubclass( + VOP_TypeInfo &o_type_info, + int i_idx) { + o_type_info.setType(GetVOPType(m_shaderInfo->GetInput(i_idx)->GetType())); +} + +void VOP_RPRMaterial::getAllowedInputTypeInfosSubclass( + unsigned i_idx, + VOP_VopTypeInfoArray &o_type_infos) { + VOP_TypeInfo info; + getInputTypeInfoSubclass(info, i_idx); + o_type_infos.append(info); +} + +void VOP_RPRMaterial::getOutputNameSubclass(UT_String &out, int i_idx) const { + out = outputLabel(i_idx); +} + +void VOP_RPRMaterial::getOutputTypeInfoSubclass( + VOP_TypeInfo &o_type_info, + int i_idx) { + o_type_info.setType(GetVOPType(m_shaderInfo->GetOutput(i_idx)->GetType())); +} + +VOP_MaterialX::VOP_MaterialX(OP_Network* parent, const char* name, OP_Operator* entry) + : VOP_RPRMaterial(parent, name, entry) { + +} + +PRM_Template* VOP_MaterialX::GetTemplates(RprUsdMaterialNodeInfo const* shaderInfo) { + auto templates = GetShaderTemplates(shaderInfo); + + for (auto& prm : *templates) { + if (prm.getType() == PRM_STRING_E) { + if (std::strcmp("surfaceElement", prm.getToken()) == 0 || + std::strcmp("displacementElement", prm.getToken()) == 0) { + auto choiceListType = PRM_ChoiceListType(PRM_CHOICELIST_SINGLE | PRM_CHOICELIST_USE_TOKEN); + auto choiceList = LEAKED(new PRM_ChoiceList(choiceListType, &VOP_MaterialX::ElementChoiceGenFunc)); + prm.setChoiceListPtr(choiceList); + } + } + } + + templates->pop_back(); + + // Add error message parm. + // It will be displayed if the selected mtlx have no renderable elements + // + auto msgName = LEAKED(new PRM_Name("msg")); + auto msgDefault = LEAKED(new PRM_Default(0.0, "No renderable elements")); + templates->emplace_back(PRM_LABEL, PRM_TYPE_NO_LABEL, 1, msgName, msgDefault); + + // Reload button + + // Currently, the only way to reload a material in Houdini is to modify the material node itself. + // So we add dummy parameter that will be changed on reload button press + // + static const char* dummyParmName = "reloadDummy"; + templates->emplace_back(PRM_INT_E, 1, LEAKED(new PRM_Name(dummyParmName))); + templates->back().setInvisible(true); + + // Add reload button parm + // + auto separatorName = LEAKED(new PRM_Name("reloadSeparator")); + templates->emplace_back(PRM_SEPARATOR, 1, separatorName); + auto buttonName = LEAKED(new PRM_Name("reload", "Reload")); + PRM_Callback buttonCallback([](void* data, int, float time, const PRM_Template*) -> int { + auto vop = static_cast(CAST_VOPNODE((OP_Node*)data)); + if (vop->m_file.isstring()) { + if (auto reloadDummy = vop->getParmPtr(dummyParmName)) { + double modificationTime; + if (ArchGetModificationTime(vop->m_file, &modificationTime)) { + if (vop->m_fileModificationTime != modificationTime) { + + // Update UI + // + vop->opChanged(OP_PARM_CHANGED, (void*)vop->getParmIndex("file")); + + // Force Hydra material reload + // + reloadDummy->setValue(time, ++vop->m_reloadDummy); + + return 1; + } + } + } + } + + return 0; + }); + templates->emplace_back(PRM_CALLBACK_NOREFRESH, 1, buttonName, nullptr, nullptr, nullptr, buttonCallback); + + templates->emplace_back(); + + return &templates->at(0); +} + +void VOP_MaterialX::ElementChoiceGenFunc( + void* op, + PRM_Name* choices, int maxChoicesSize, + const PRM_SpareData* spare, const PRM_Parm* parm) { + if (maxChoicesSize <= 0) { + return; + } + + auto parmToken = parm->getToken(); + RPRMtlxLoader::OutputType outputType; + if (std::strcmp("surfaceElement", parmToken) == 0) { + outputType = RPRMtlxLoader::kOutputSurface; + } else if (std::strcmp("displacementElement", parmToken) == 0) { + outputType = RPRMtlxLoader::kOutputDisplacement; + } else { + return; + } + + auto vop = static_cast(CAST_VOPNODE((OP_Node*)op)); + auto renderElementPaths = vop->m_renderableElements.namePaths[outputType]; + size_t choiceCount = size_t(maxChoicesSize - 1); // -1 for sentinel PRM_Name + size_t iChoice = 0; + if (!renderElementPaths.empty()) { + size_t maxNumElementPaths = choiceCount - 1; // -1 for None choice + size_t namePathCount = std::min(maxNumElementPaths, renderElementPaths.size()); + for (; iChoice < namePathCount; ++iChoice) { + auto path = renderElementPaths[iChoice].c_str(); + choices[iChoice].setToken(path); + choices[iChoice].setLabel(path); + } + } + + if (iChoice < choiceCount) { + choices[iChoice].setToken(""); + choices[iChoice].setLabel("None"); + ++iChoice; + } + + choices[iChoice] = PRM_Name(); +} + +void VOP_MaterialX::opChanged(OP_EventType reason, void* data) { + VOP_RPRMaterial::opChanged(reason, data); + + if (reason == OP_PARM_CHANGED) { + int parmIndex = int(reinterpret_cast(data)); + auto& changedParm = getParm(parmIndex); + if (std::strcmp(changedParm.getToken(), "file") == 0) { + bool keepSelections = false; + + UT_String newFile; + double newFileModificationTime; + changedParm.getValue(0.0, newFile, 0, true, 0); + if (!ArchGetModificationTime(newFile, &newFileModificationTime)) { + newFileModificationTime = 0.0; + } + + // Keep selected renderable elements if file was edited + // + if (m_file == newFile) { + if (m_fileModificationTime != newFileModificationTime && + m_fileModificationTime != 0.0) { + keepSelections = true; + } + } else { + m_file = std::move(newFile); + } + m_fileModificationTime = newFileModificationTime; + + // Rebuild renderable elements cache + // + m_renderableElements = {}; + if (auto mtlxLoader = RprUsdMaterialRegistry::GetInstance().GetMtlxLoader()) { + try { + auto mtlxDoc = MaterialX::createDocument(); + MaterialX::readFromXmlFile(mtlxDoc, m_file.toStdString()); + mtlxDoc->importLibrary(mtlxLoader->GetStdlib()); + m_renderableElements = mtlxLoader->GetRenderableElements(mtlxDoc.get()); + } catch (MaterialX::Exception& e) { + // no-op + } + } + + // Hide everything but file parm when a file is not specified + // + bool isUiVisible = bool(m_file); + + // Reset element parameters if previously selected ones not valid + // + bool hasAnyElements = false; + const char* parmNames[RPRMtlxLoader::kOutputsTotal] = { + "surfaceElement", "displacementElement" + }; + for (int i = 0; i < RPRMtlxLoader::kOutputsTotal; ++i) { + if (auto parm = getParmPtr(parmNames[i])) { + auto& namePaths = m_renderableElements.namePaths[i]; + bool parmVisible = false; + if (!namePaths.empty()) { + parmVisible = true; + hasAnyElements = true; + } + parm->setVisibleState(isUiVisible && parmVisible); + + if (keepSelections) { + UT_String prevNamePath; + parm->getValue(0.0, prevNamePath, 0, true, 0); + + // Keep renderable element disabled + // + if (!prevNamePath.isstring()) { + continue; + } + + // Or if this renderable element is still available + // + auto namePathIt = std::find_if(namePaths.begin(), namePaths.end(), + [&prevNamePath](std::string const& namePath) { + return prevNamePath == namePath.c_str(); + } + ); + if (namePathIt != namePaths.end()) { + continue; + } + } + + parm->setValue(0.0, namePaths.empty() ? "" : namePaths[0].c_str(), CH_STRING_LITERAL); + } + } + + if (auto msgParm = getParmPtr("msg")) { + msgParm->setVisibleState(isUiVisible && !hasAnyElements); + } + + if (auto buttonParm = getParmPtr("reload")) { + buttonParm->setVisibleState(isUiVisible); + if (auto separatorParm = getParmPtr("reloadSeparator")) { + separatorParm->setVisibleState(isUiVisible); + } + } + } + } +} + +bool VOP_MaterialX::runCreateScript() { + int numParms = getNumParms(); + for (int i = getParmIndex("stPrimvarName") + 1; i < numParms; ++i) { + getParm(i).setVisibleState(false); + } + + return true; +} + +static const char* GetUIName(RprUsdMaterialNodeInfo const* shaderInfo) { + if (shaderInfo->GetUIName()) return shaderInfo->GetUIName(); + + // If UI name is not given, try to use name + std::string name = shaderInfo->GetName(); + + std::string rprPrefix("rpr_"); + auto rprPos = name.find(rprPrefix); + if (TfStringStartsWith(name, rprPrefix)) { + for (size_t i = 0; i < rprPrefix.size() - 1; ++i) { + name[rprPos + i] = std::toupper(name[rprPos + i]); + } + } + + bool isWord = false; + for (size_t i = 0; i < name.size(); ++i) { + if (std::isalpha(name[i])) { + if (!isWord) { + isWord = true; + name[i] = std::toupper(name[i]); + } + } else { + isWord = false; + } + + if (strchr("_", name[i])) { + name[i] = ' '; + } + } + + return LEAKED(strdup(name.c_str())); +} + +VOP_RPRMaterialOperator* VOP_RPRMaterialOperator::Create(RprUsdMaterialNodeInfo const* shaderInfo) { + if (shaderInfo->GetName()) { + if (std::strcmp("rpr_materialx_node", shaderInfo->GetName()) == 0) { + return VOP_RPRMaterialOperator::_Create(shaderInfo); + } + } + + return VOP_RPRMaterialOperator::_Create(shaderInfo); +} + +template +VOP_RPRMaterialOperator* VOP_RPRMaterialOperator::_Create(RprUsdMaterialNodeInfo const* shaderInfo) { + auto templates = VOP::GetTemplates(shaderInfo); + auto construct = [](OP_Network* parent, const char* name, OP_Operator* entry) -> OP_Node* { + return new VOP(parent, name, entry); + }; + return new VOP_RPRMaterialOperator(shaderInfo, construct, templates); +} + +VOP_RPRMaterialOperator::VOP_RPRMaterialOperator( + RprUsdMaterialNodeInfo const* shaderInfo, + OP_Constructor construct, + PRM_Template* templates) + : VOP_Operator( + TfStringPrintf("RPR::%s", shaderInfo->GetName()).c_str(), + GetUIName(shaderInfo), + construct, + templates, + VOP_Node::theChildTableName, + shaderInfo->GetNumInputs(), shaderInfo->GetNumInputs(), + // Put rpr here so Houdini's Material Builder won't see our VOPs + "rpr", + nullptr, + OP_FLAG_OUTPUT, + shaderInfo->GetNumOutputs()) + , shaderInfo(shaderInfo) { + + std::string subMenuPath("RPR"); + if (shaderInfo->GetUIFolder()) { + subMenuPath += "/"; + subMenuPath += shaderInfo->GetUIFolder(); + } + setOpTabSubMenuPath(subMenuPath.c_str()); + + setIconName("RPR"); + + /* + The RenderMask is what ends up being the MaterialNetworkSelector in + Hydra. If we don't set it, the default translator will not provide + networks at all. And if it does not match the Hydra plugin, we won't + see the networks there. + */ + auto vop_info = static_cast(getOpSpecificData()); + vop_info->setRenderMask("rpr"); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/rprHoudini/VOP_RPRMaterial.h b/pxr/imaging/plugin/rprHoudini/VOP_RPRMaterial.h new file mode 100644 index 000000000..2f035c5e9 --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/VOP_RPRMaterial.h @@ -0,0 +1,125 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPR_VOPS_MATERIAL_H +#define RPR_VOPS_MATERIAL_H + +#include "pxr/pxr.h" +#include "pxr/imaging/rprUsd/materialRegistry.h" + +#include + +#include +#include +#include + +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +class RprUsdMaterialNodeInfo; + +struct VOP_RPRMaterialOperator : public VOP_Operator { + RprUsdMaterialNodeInfo const* shaderInfo; + + static VOP_RPRMaterialOperator* Create(RprUsdMaterialNodeInfo const* shaderInfo); + +private: + template + static VOP_RPRMaterialOperator* _Create(RprUsdMaterialNodeInfo const* shaderInfo); + + VOP_RPRMaterialOperator(RprUsdMaterialNodeInfo const* shaderInfo, OP_Constructor construct, PRM_Template* templates); +}; + +class VOP_RPRMaterial : public VOP_Node { +public: + /// Returns the templates for the shader's input parameters + static PRM_Template* GetTemplates(RprUsdMaterialNodeInfo const* shaderInfo); + + VOP_RPRMaterial(OP_Network* parent, const char* name, OP_Operator* entry); + + /// Returns the label for input port at index i_idx + const char* inputLabel(unsigned i_idx) const override; + /// Returns the label for output port at index i_idx + const char* outputLabel(unsigned i_idx) const override; + + /// Minimum inputs that must be connected to a node for it to cook. + unsigned minInputs() const override; + + /// Returns the number input ports that should be visible + unsigned getNumVisibleInputs() const override; + + /// Returns the number of ordered (ie : non-indexed) inputs + unsigned orderedInputs() const override; + +#if HDK_API_VERSION >= 18000000 + /// From VOP_Node + UT_StringHolder getShaderName( + VOP_ShaderNameStyle style, + VOP_Type shader_type) const override; + + VOP_Type getShaderType() const override; +#endif + +protected: + /// Returns the internal name of an input parameter. + void getInputNameSubclass(UT_String &in, int i_idx) const override; + /// Returns the index of the named input + int getInputFromNameSubclass(const UT_String &in) const override; + /// Fills the info about the source of an input parameter + void getInputTypeInfoSubclass( + VOP_TypeInfo &o_type_info, + int i_idx) override; + /** + Fills the info about the acceptable types for the source of an input + parameter. + */ + void getAllowedInputTypeInfosSubclass( + unsigned i_idx, + VOP_VopTypeInfoArray &o_type_infos) override; + + /// Returns the internal name of an output parameter. + void getOutputNameSubclass(UT_String &out, int i_idx) const override; + /// Fills the info about an output parameter + void getOutputTypeInfoSubclass( + VOP_TypeInfo &o_type_info, + int i_idx) override; + +private: + RprUsdMaterialNodeInfo const* m_shaderInfo; + VOP_Type m_shaderType = VOP_SURFACE_SHADER; +}; + +class VOP_MaterialX : public VOP_RPRMaterial { +public: + VOP_MaterialX(OP_Network* parent, const char* name, OP_Operator* entry); + + static PRM_Template* GetTemplates(RprUsdMaterialNodeInfo const* shaderInfo); + + void opChanged(OP_EventType reason, void* data) override; + bool runCreateScript() override; + +private: + static void ElementChoiceGenFunc(void* op, PRM_Name* choices, int maxChoicesSize, const PRM_SpareData*, const PRM_Parm*); + +private: + UT_String m_file; + int m_reloadDummy = 0; + double m_fileModificationTime; + RPRMtlxLoader::RenderableElements m_renderableElements; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPR_VOPS_MATERIAL_H diff --git a/pxr/imaging/plugin/rprHoudini/activateHoudiniPlugin.cpp.in b/pxr/imaging/plugin/rprHoudini/activateHoudiniPlugin.cpp.in new file mode 100644 index 000000000..d43595eca --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/activateHoudiniPlugin.cpp.in @@ -0,0 +1,268 @@ +#include +#include +#include +#include +#include + +#include +namespace fs = ghc::filesystem; + +#if defined(__linux__) +#include +#include +#endif + +#if defined(_WIN32) || defined(_WIN64) +#include +#elif defined(__APPLE__) +#include +#endif + +using CharType = fs::path::value_type; +using StringType = fs::path::string_type; + +#ifndef ARCH_PATH_MAX + #ifdef PATH_MAX + #define ARCH_PATH_MAX PATH_MAX + #else + #ifdef MAXPATHLEN + #define ARCH_PATH_MAX MAXPATHLEN + #else + #ifdef _MAX_PATH + #define ARCH_PATH_MAX _MAX_PATH + #else + #define ARCH_PATH_MAX 1024 + #endif + #endif + #endif +#endif + +// Getting the executable path requires a dynamically allocated buffer +// on all platforms. This helper function handles the allocation. +StringType _DynamicSizedRead( + size_t initialSize, + const std::function& callback) { + // Make a buffer for the data. + // We use an explicit deleter to work around libc++ bug. + // See https://llvm.org/bugs/show_bug.cgi?id=18350. + std::unique_ptr > buffer; + buffer.reset(new CharType[initialSize]); + + // Repeatedly invoke the callback with our buffer until it's big enough. + size_t size = initialSize; + while (!callback(buffer.get(), &size)) { + if (size == std::numeric_limits::max()) { + // callback is never going to succeed. + return StringType(); + } + buffer.reset(new CharType[size]); + } + + // Make a string. + return StringType(buffer.get()); +} + +fs::path ArchGetExecutablePath() { +#if defined(__linux__) + + // On Linux the executable path is retrieved from the /proc/self/exe + // symlink. + return + _DynamicSizedRead(ARCH_PATH_MAX, + [](char* buffer, size_t* size) { + const ssize_t n = readlink("/proc/self/exe", buffer, *size); + if (n == -1) { + fprintf(stderr, "Unable to read /proc/self/exe to obtain executable path"); + *size = std::numeric_limits::max(); + return false; + } + else if (static_cast(n) >= *size) { + // Find out how much space we need. + struct stat sb; + if (lstat("/proc/self/exe", &sb) == 0) { + *size = sb.st_size + 1; + } + else { + // Try iterating on the size. + *size *= 2; + } + return false; + } + else { + buffer[n] = '\0'; + return true; + } + }); + +#elif defined(__APPLE__) + + // On Darwin _NSGetExecutablePath() returns the executable path. + return + _DynamicSizedRead(ARCH_PATH_MAX, + [](char* buffer, size_t* size) { + uint32_t bufsize = *size; + if (_NSGetExecutablePath(buffer, &bufsize) == -1) { + // We're told the correct size. + *size = bufsize; + return false; + } + else { + return true; + } + }); + +#elif defined(_WIN32) || defined(_WIN64) + + // On Windows GetModuleFileName() returns the executable path. + return + _DynamicSizedRead(ARCH_PATH_MAX, + [](wchar_t* buffer, size_t* size) { + DWORD nSize = *size; + const DWORD n = GetModuleFileNameW(NULL, buffer, nSize); + if (n == 0) { + fprintf(stderr, "Unable to read GetModuleFileName() to obtain executable path"); + *size = std::numeric_limits::max(); + return false; + } + else if (n >= nSize) { + // We have to iterate to find a suitable size. + *size *= 2; + return false; + } + else { + return true; + } + }); +#endif +} + +fs::path GetHoudiniUserPrefDirFromParentDir(fs::path const& parentDir, const char* hver) { + auto prefDir = parentDir / (std::string("houdini") + hver); + if (fs::is_directory(prefDir)) { + return prefDir.string(); + } + + return {}; +} + +fs::path GetHoudiniUserPrefDir(const char* hver) { + if (auto prefDirCstr = std::getenv("HOUDINI_USER_PREF_DIR")) { + std::string prefDir(prefDirCstr); + + std::string hverPattern("__HVER__"); + auto hverIdx = prefDir.find(hverPattern); + if (hverIdx != std::string::npos) { + prefDir.replace(hverIdx, hverPattern.size(), hver); + } + + return prefDir; + } + + const char* home = std::getenv("HOME"); + if (home) { + auto prefDir = GetHoudiniUserPrefDirFromParentDir(home, hver); + if (!prefDir.empty()) return prefDir; + } + +#if defined(_WIN32) || defined(_WIN64) + auto userprofile = _wgetenv(L"USERPROFILE"); + if (!userprofile) { + std::cout << "USERPROFILE environment variable is not set" << std::endl; + return {}; + } + + auto documentsDir = fs::path(userprofile) / "documents"; + auto prefDir = GetHoudiniUserPrefDirFromParentDir(documentsDir, hver); + if (!prefDir.empty()) return prefDir; +#elif defined(__APPLE__) + if (home) { + auto prefDir = fs::path(home) / "Library" / "Preferences" / "houdini" / hver; + if (fs::is_directory(prefDir)) { + return prefDir.string(); + } + } +#endif + + return {}; +} + +int ActivateHoudiniPlugin() { + 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; + return EXIT_FAILURE; + } + std::cout << "HOUDINI_USER_PREF_DIR: " << houdiniUserPrefDir << std::endl; + + auto executablePath = ArchGetExecutablePath(); + if (executablePath.empty()) { + std::cout << "Can not determine executable path" << std::endl; + return EXIT_FAILURE; + } + + auto rprPath = executablePath.parent_path().string(); + std::replace(rprPath.begin(), rprPath.end(), '\\', '/'); + std::cout << "RPR path: " << rprPath << std::endl; + + auto packagesDir = houdiniUserPrefDir / "packages"; + fs::create_directories(packagesDir); + + auto packageJsonFilepath = packagesDir / "RPR_for_Houdini.json"; + std::ofstream packageJson(packageJsonFilepath); + if (!packageJson.is_open()) { + std::cout << "Failed to open output file: " << packageJsonFilepath << std::endl; + return EXIT_FAILURE; + } + + std::map env = { + {"RPR", rprPath}, + {"HOUDINI_PATH", "$RPR/houdini"}, + {"PYTHONPATH", "$RPR/lib/python"}, +#if defined(_WIN32) || defined(_WIN64) + {"PATH", "$RPR/lib"} +#endif + }; + + packageJson << '{'; + packageJson << '\"' << "env" << '\"'; + packageJson << ':'; + packageJson << '['; + for (auto it = env.begin(); it != env.end(); ++it) { + packageJson << '{'; + packageJson << '\"' << it->first << '\"'; + packageJson << ':'; +#if defined(_WIN32) || defined(_WIN64) + packageJson << '\"'; + auto path = it->second.string(); + std::replace(path.begin(), path.end(), '\\', '/'); + packageJson << path; + packageJson << '\"'; +#else + packageJson << it->second; +#endif + packageJson << '}'; + if (std::next(it) != env.end()) { + packageJson << ','; + } + } + packageJson << ']'; + packageJson << '}'; + + if (packageJson.fail()) { + std::cout << "Failed to write output file: " << packageJsonFilepath << '\n' << std::endl; + return EXIT_FAILURE; + } + + std::cout << "Successfully activated RPR 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/hda/rpr_exportRpr1.hda b/pxr/imaging/plugin/rprHoudini/hda/rpr_exportRpr1.hda new file mode 100644 index 000000000..7fbba1679 Binary files /dev/null and b/pxr/imaging/plugin/rprHoudini/hda/rpr_exportRpr1.hda differ diff --git a/pxr/imaging/plugin/rprHoudini/hda/rpr_standard_rendervars.hda b/pxr/imaging/plugin/rprHoudini/hda/rpr_standard_rendervars.hda new file mode 100644 index 000000000..f95e261fc Binary files /dev/null and b/pxr/imaging/plugin/rprHoudini/hda/rpr_standard_rendervars.hda differ diff --git a/pxr/imaging/plugin/rprHoudini/plugin.cpp b/pxr/imaging/plugin/rprHoudini/plugin.cpp new file mode 100644 index 000000000..382ec71fb --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/plugin.cpp @@ -0,0 +1,37 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "VOP_RPRMaterial.h" +#include "LOP_RPRExportHelper.h" +#include "pxr/imaging/rprUsd/materialRegistry.h" + +#include +#include + +void newVopOperator(OP_OperatorTable* io_table) { + PXR_NAMESPACE_USING_DIRECTIVE + for (auto& nodeDesc : RprUsdMaterialRegistry::GetInstance().GetRegisteredNodes()) { + if (!nodeDesc.info) continue; + + try { + io_table->addOperator(VOP_RPRMaterialOperator::Create(nodeDesc.info)); + } catch (std::exception& e) { + fprintf(stderr, "Failed to add %s VOP", nodeDesc.info->GetName()); + } + } +} + +void newLopOperator(OP_OperatorTable *table) { + PXR_NAMESPACE_USING_DIRECTIVE + LOP_RPRExportHelper::Register(table); +} diff --git a/pxr/imaging/plugin/rprHoudini/scripts/python/houRpr/__init__.py b/pxr/imaging/plugin/rprHoudini/scripts/python/houRpr/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pxr/imaging/plugin/rprHoudini/scripts/python/houRpr/cache.py b/pxr/imaging/plugin/rprHoudini/scripts/python/houRpr/cache.py new file mode 100644 index 000000000..23fa3a132 --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/scripts/python/houRpr/cache.py @@ -0,0 +1,35 @@ +import os +import glob + +import hou +from rpr import RprUsd + +def _set_cache_dir(type, start_directory, setter): + directory = hou.ui.selectFile( + title='RPR {} Cache Directory'.format(type), + start_directory=start_directory.replace('\\', '/'), + file_type=hou.fileType.Directory, + chooser_mode=hou.fileChooserMode.Write) + if directory: + setter(hou.expandString(directory)) + +def set_texture_cache_dir(): + _set_cache_dir('Texture', RprUsd.Config.GetTextureCacheDir(), RprUsd.Config.SetTextureCacheDir) + +def set_kernel_cache_dir(): + _set_cache_dir('Kernel', RprUsd.Config.GetKernelCacheDir(), RprUsd.Config.SetKernelCacheDir) + + +def _clear_cache(cache_dir): + num_files_removed = 0 + for pattern in ('*.bin.check', '*.bin', '*.cache'): + for cache_file in glob.iglob(os.path.join(cache_dir, pattern)): + os.remove(cache_file) + num_files_removed += 1 + print('RPR: removed {} cache files'.format(num_files_removed)) + +def clear_texture_cache(): + _clear_cache(RprUsd.Config.GetTextureCacheDir()) + +def clear_kernel_cache(): + _clear_cache(RprUsd.Config.GetKernelCacheDir()) diff --git a/pxr/imaging/plugin/rprHoudini/ui/MainMenuCommon.xml b/pxr/imaging/plugin/rprHoudini/ui/MainMenuCommon.xml new file mode 100644 index 000000000..021caccd6 --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/ui/MainMenuCommon.xml @@ -0,0 +1,43 @@ + + + + + + + help_menu + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pxr/imaging/plugin/rprHoudini/ui/RPR.svg b/pxr/imaging/plugin/rprHoudini/ui/RPR.svg new file mode 100644 index 000000000..1c6d9f1ce --- /dev/null +++ b/pxr/imaging/plugin/rprHoudini/ui/RPR.svg @@ -0,0 +1,16 @@ + + + + + + + diff --git a/pxr/imaging/rprUsd/CMakeLists.txt b/pxr/imaging/rprUsd/CMakeLists.txt new file mode 100644 index 000000000..a29e21570 --- /dev/null +++ b/pxr/imaging/rprUsd/CMakeLists.txt @@ -0,0 +1,80 @@ +set(PXR_PREFIX pxr/imaging) +set(PXR_PACKAGE rprUsd) + +pxr_library(rprUsd + DISABLE_PRECOMPILED_HEADERS + + LIBRARIES + gf + tf + vt + hd + glf + sdf + arch + work + cpprpr + json + + PUBLIC_CLASSES + util + config + contextHelpers + coreImage + debugCodes + imageCache + material + materialMappings + materialRegistry + + PUBLIC_HEADERS + api.h + contextMetadata.h + error.h + helpers.h + materialHelpers.h + boostIncludePath.h + materialNodes/rpr/materialXNode.h + + PYTHON_CPPFILES + moduleDeps.cpp + + PYMODULE_CPPFILES + module.cpp + wrapConfig.cpp + + PYMODULE_FILES + __init__.py +) + +if(HoudiniUSD_FOUND) + target_compile_definitions(rprUsd PUBLIC BUILD_AS_HOUDINI_PLUGIN) +endif() + +target_sources(rprUsd PRIVATE + materialNodes/materialNode.h + materialNodes/usdNode.h + materialNodes/usdNode.cpp + materialNodes/mtlxNode.h + materialNodes/mtlxNode.cpp + materialNodes/rpr/baseNode.h + materialNodes/rpr/baseNode.cpp + materialNodes/rpr/nodeInfo.h + materialNodes/rpr/catcherNode.cpp + materialNodes/rpr/displaceNode.cpp + materialNodes/rpr/materialXNode.cpp + materialNodes/rpr/arithmeticNode.h + materialNodes/rpr/arithmeticNode.cpp + materialNodes/rpr/combineShadersNode.cpp + materialNodes/houdiniPrincipledShaderNode.h + materialNodes/houdiniPrincipledShaderNode.cpp) + +add_subdirectory(materialNodes/mtlxFiles) + +GroupSources(rprUsd) + +if(RPR_ENABLE_VULKAN_INTEROP_SUPPORT) + target_link_libraries(rprUsd ${Vulkan_LIBRARIES}) + target_include_directories(rprUsd PRIVATE ${Vulkan_INCLUDE_DIRS}) + target_compile_definitions(rprUsd PUBLIC HDRPR_ENABLE_VULKAN_INTEROP_SUPPORT) +endif() diff --git a/pxr/imaging/rprUsd/__init__.py b/pxr/imaging/rprUsd/__init__.py new file mode 100644 index 000000000..3d7b29f54 --- /dev/null +++ b/pxr/imaging/rprUsd/__init__.py @@ -0,0 +1,11 @@ +from . import _rprUsd +from pxr import Tf +Tf.PrepareModule(_rprUsd, locals()) +del Tf + +try: + from . import __DOC + __DOC.Execute(locals()) + del __DOC +except Exception: + pass diff --git a/pxr/imaging/rprUsd/api.h b/pxr/imaging/rprUsd/api.h new file mode 100644 index 000000000..bce8830c4 --- /dev/null +++ b/pxr/imaging/rprUsd/api.h @@ -0,0 +1,37 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef PXR_IMAGING_RPR_USD_API_H +#define PXR_IMAGING_RPR_USD_API_H + +#include "pxr/base/arch/export.h" + +#if defined(PXR_STATIC) +# define RPRUSD_API +# define RPRUSD_API_TEMPLATE_CLASS(...) +# define RPRUSD_API_TEMPLATE_STRUCT(...) +# define RPRUSD_LOCAL +#else +# if defined(RPRUSD_EXPORTS) +# define RPRUSD_API ARCH_EXPORT +# define RPRUSD_API_TEMPLATE_CLASS(...) ARCH_EXPORT_TEMPLATE(class, __VA_ARGS__) +# define RPRUSD_API_TEMPLATE_STRUCT(...) ARCH_EXPORT_TEMPLATE(struct, __VA_ARGS__) +# else +# define RPRUSD_API ARCH_IMPORT +# define RPRUSD_API_TEMPLATE_CLASS(...) ARCH_IMPORT_TEMPLATE(class, __VA_ARGS__) +# define RPRUSD_API_TEMPLATE_STRUCT(...) ARCH_IMPORT_TEMPLATE(struct, __VA_ARGS__) +# endif +# define RPRUSD_LOCAL ARCH_HIDDEN +#endif + +#endif // PXR_IMAGING_RPR_USD_API_H diff --git a/pxr/imaging/plugin/hdRpr/boostIncludePath.h b/pxr/imaging/rprUsd/boostIncludePath.h similarity index 100% rename from pxr/imaging/plugin/hdRpr/boostIncludePath.h rename to pxr/imaging/rprUsd/boostIncludePath.h diff --git a/pxr/imaging/rprUsd/config.cpp b/pxr/imaging/rprUsd/config.cpp new file mode 100644 index 000000000..4f6e6cab9 --- /dev/null +++ b/pxr/imaging/rprUsd/config.cpp @@ -0,0 +1,209 @@ +#include "config.h" +#include "pxr/base/arch/env.h" +#include "pxr/base/arch/fileSystem.h" +#include "pxr/base/tf/tf.h" +#include "pxr/base/tf/instantiateSingleton.h" + +#include +using json = nlohmann::json; + +#include + +#ifdef WIN32 +#include +#pragma comment(lib,"Shell32.lib") +#elif defined(__linux__) +#include +#include +#endif // __APPLE__ + +PXR_NAMESPACE_OPEN_SCOPE + +namespace { + +bool ArchCreateDirectory(const char* path) { +#ifdef WIN32 + return CreateDirectory(path, NULL) == TRUE; +#else + return mkdir(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) == 0; +#endif +} + +std::string GetAppDataPath() { +#ifdef WIN32 + char appDataPath[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, appDataPath))) { + return appDataPath; + } +#elif defined(__linux__) + auto homeEnv = ArchGetEnv("XDG_DATA_HOME"); + if (!homeEnv.empty() && homeEnv[0] == '/') { + return homeEnv; + } + + int uid = getuid(); + homeEnv = ArchGetEnv("HOME"); + if (uid != 0 && !homeEnv.empty()) { + return homeEnv + "/.config"; + } + +#elif defined(__APPLE__) + auto homeEnv = ArchGetEnv("HOME"); + if (!homeEnv.empty() && homeEnv[0] == '/') { + return homeEnv + "/Library/Application Support"; + } +#else +#error "Unknown platform" +#endif + + return "."; +} + +std::string GetDefaultCacheDir(const char* cacheType) { + auto cacheDir = ArchGetEnv("RPR"); + if (cacheDir.empty()) { + // Fallback to AppData + cacheDir = GetAppDataPath() + (ARCH_PATH_SEP "hdRpr"); + ArchCreateDirectory(cacheDir.c_str()); + } + + cacheDir += (ARCH_PATH_SEP "cache"); + ArchCreateDirectory(cacheDir.c_str()); + + cacheDir = cacheDir + ARCH_PATH_SEP + cacheType; + ArchCreateDirectory(cacheDir.c_str()); + + return cacheDir; +} + +template +bool InitJsonProperty(const char* propertyName, T const& defaultValue, json* json) { + bool setDefaultValue = false; + + auto it = json->find(propertyName); + if (it == json->end()) { + setDefaultValue = true; + } else { + try { + it->get(); + } catch (json::exception& e) { + TF_UNUSED(e); + setDefaultValue = true; + } + } + + if (setDefaultValue) { + (*json)[propertyName] = defaultValue; + } + return setDefaultValue; +} + +template +bool GetJsonProperty(const char* propertyName, json const& json, T* property) { + auto it = json.find(propertyName); + if (it != json.end()) { + try { + *property = it->get(); + return true; + } catch (json::exception& e) { + TF_UNUSED(e); + } + } + + return false; +} + +const char* kShowRestartRequiredMessage = "ShowRestartRequiredMessage"; +const char* kTextureCacheDir = "TextureCacheDir"; +const char* kKernelCacheDir = "KernelCacheDir"; + +} // namespace anonymous + +TF_INSTANTIATE_SINGLETON(RprUsdConfig); + +std::unique_lock RprUsdConfig::GetInstance(RprUsdConfig** instance) { + static std::mutex instanceMutex; + std::unique_lock lock(instanceMutex); + + *instance = &TfSingleton::GetInstance(); + return lock; +} + +RprUsdConfig::~RprUsdConfig() = default; + +struct RprUsdConfig::Impl { + json cfg; +}; + +RprUsdConfig::RprUsdConfig() + : m_impl(new Impl) { + auto configDir = ArchGetEnv("RPRUSD_CONFIG_PATH"); + if (configDir.empty()) { + configDir = GetAppDataPath() + (ARCH_PATH_SEP "hdRpr"); + } + if (!configDir.empty()) { + ArchCreateDirectory(configDir.c_str()); + } + m_filepath = configDir + (ARCH_PATH_SEP "cfg.json"); + + std::ifstream cfgFile(m_filepath); + if (cfgFile.is_open()) { + cfgFile >> m_impl->cfg; + } + + bool configDirty = false; + configDirty |= InitJsonProperty(kShowRestartRequiredMessage, true, &m_impl->cfg); + if (configDirty) { + Save(); + } +} + +void RprUsdConfig::Save() { + std::ofstream cfgFile(m_filepath); + if (!cfgFile.is_open()) { + TF_RUNTIME_ERROR("Failed to save RprUsd config: cannot open file \"%s\"", m_filepath.c_str()); + return; + } + + cfgFile << m_impl->cfg; +} + +void RprUsdConfig::SetRestartWarning(bool newValue) { + if (m_impl->cfg[kShowRestartRequiredMessage] != newValue) { + m_impl->cfg[kShowRestartRequiredMessage] = newValue; + Save(); + } +} +bool RprUsdConfig::IsRestartWarningEnabled() const { + return m_impl->cfg[kShowRestartRequiredMessage]; +} + +void RprUsdConfig::SetTextureCacheDir(std::string const& newValue) { + if (m_impl->cfg[kTextureCacheDir] != newValue) { + m_impl->cfg[kTextureCacheDir] = newValue; + Save(); + } +} +std::string RprUsdConfig::GetTextureCacheDir() const { + std::string ret; + if (!GetJsonProperty(kTextureCacheDir, m_impl->cfg, &ret)) { + ret = GetDefaultCacheDir("texture"); + } + return ret; +} + +void RprUsdConfig::SetKernelCacheDir(std::string const& newValue) { + if (m_impl->cfg[kKernelCacheDir] != newValue) { + m_impl->cfg[kKernelCacheDir] = newValue; + Save(); + } +} +std::string RprUsdConfig::GetKernelCacheDir() const { + std::string ret; + if (!GetJsonProperty(kKernelCacheDir, m_impl->cfg, &ret)) { + ret = GetDefaultCacheDir("kernel"); + } + return ret; +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/config.h b/pxr/imaging/rprUsd/config.h new file mode 100644 index 000000000..e803e47b1 --- /dev/null +++ b/pxr/imaging/rprUsd/config.h @@ -0,0 +1,68 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef PXR_IMAGING_RPR_USD_CONFIG_H +#define PXR_IMAGING_RPR_USD_CONFIG_H + +#include "pxr/imaging/rprUsd/api.h" +#include "pxr/base/tf/singleton.h" + +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +class RprUsdConfig { +public: + RPRUSD_API + static std::unique_lock GetInstance(RprUsdConfig** instance); + + RPRUSD_API + ~RprUsdConfig(); + + RPRUSD_API + std::string const& GetFilePath() const { return m_filepath; } + + RPRUSD_API + bool IsRestartWarningEnabled() const; + RPRUSD_API + void SetRestartWarning(bool); + + RPRUSD_API + std::string GetTextureCacheDir() const; + RPRUSD_API + void SetTextureCacheDir(std::string const&); + + RPRUSD_API + std::string GetKernelCacheDir() const; + RPRUSD_API + void SetKernelCacheDir(std::string const&); + +private: + RprUsdConfig(); + friend class TfSingleton; + + void Save(); + +private: + std::string m_filepath; + + struct Impl; + std::unique_ptr m_impl; +}; + +RPRUSD_API_TEMPLATE_CLASS(TfSingleton); + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_IMAGING_RPR_USD_CONFIG_H diff --git a/pxr/imaging/plugin/hdRpr/rpr/contextHelpers.cpp b/pxr/imaging/rprUsd/contextHelpers.cpp similarity index 70% rename from pxr/imaging/plugin/hdRpr/rpr/contextHelpers.cpp rename to pxr/imaging/rprUsd/contextHelpers.cpp index 340ab3ef8..cb524f959 100644 --- a/pxr/imaging/plugin/hdRpr/rpr/contextHelpers.cpp +++ b/pxr/imaging/rprUsd/contextHelpers.cpp @@ -11,9 +11,11 @@ See the License for the specific language governing permissions and limitations under the License. ************************************************************************/ -#include "contextHelpers.h" -#include "error.h" -#include "debugCodes.h" +#include "pxr/imaging/rprUsd/contextHelpers.h" +#include "pxr/imaging/rprUsd/contextMetadata.h" +#include "pxr/imaging/rprUsd/debugCodes.h" +#include "pxr/imaging/rprUsd/config.h" +#include "pxr/imaging/rprUsd/error.h" #include "pxr/imaging/glf/glew.h" #include "pxr/base/arch/env.h" @@ -22,6 +24,12 @@ limitations under the License. #include +#ifdef HDRPR_ENABLE_VULKAN_INTEROP_SUPPORT +#include +#include +#include +#endif // HDRPR_ENABLE_VULKAN_INTEROP_SUPPORT + #ifdef __APPLE__ #include #include @@ -33,16 +41,12 @@ limitations under the License. #include #define PRINT_CONTEXT_CREATION_DEBUG_INFO(format, ...) \ - if (!PXR_NS::TfDebug::IsEnabled(PXR_NS::HD_RPR_DEBUG_CONTEXT_CREATION)) /* empty */; else PXR_NS::TfDebug::Helper().Msg(format, ##__VA_ARGS__) + if (!TfDebug::IsEnabled(RPR_USD_DEBUG_CORE_UNSUPPORTED_ERROR)) /* empty */; else TfDebug::Helper().Msg(format, ##__VA_ARGS__) PXR_NAMESPACE_OPEN_SCOPE -TF_DEFINE_ENV_SETTING(HDRPR_ENABLE_TRACING, false, "Enable tracing of RPR core"); -TF_DEFINE_ENV_SETTING(HDRPR_TRACING_DIR, "", "Where to store RPR core tracing files. Must be a path to valid directory"); - -PXR_NAMESPACE_CLOSE_SCOPE - -namespace rpr { +TF_DEFINE_ENV_SETTING(RPRUSD_ENABLE_TRACING, false, "Enable tracing of RPR core"); +TF_DEFINE_ENV_SETTING(RPRUSD_TRACING_DIR, "", "Where to store RPR core tracing files. Must be a path to valid directory"); namespace { @@ -100,10 +104,10 @@ std::string GetRprSdkPath() { } void SetupRprTracing() { - if (PXR_NS::TfGetEnvSetting(PXR_NS::HDRPR_ENABLE_TRACING)) { + if (TfGetEnvSetting(RPRUSD_ENABLE_TRACING)) { RPR_ERROR_CHECK(rprContextSetParameterByKey1u(nullptr, RPR_CONTEXT_TRACING_ENABLED, 1), "Failed to set context tracing parameter"); - auto tracingDir = PXR_NS::TfGetEnvSetting(PXR_NS::HDRPR_TRACING_DIR); + auto tracingDir = TfGetEnvSetting(RPRUSD_TRACING_DIR); if (!tracingDir.empty()) { printf("RPR tracing directory: %s\n", tracingDir.c_str()); } @@ -111,16 +115,18 @@ void SetupRprTracing() { } } -const std::map kPluginLibNames = { +const std::map kPluginLibNames = { #ifdef WIN32 {kPluginTahoe, "Tahoe64.dll"}, - {kPluginNorthStar, "Northstar64.dll"}, + {kPluginNorthstar, "Northstar64.dll"}, {kPluginHybrid, "Hybrid.dll"}, #elif defined __linux__ + {kPluginNorthstar, "libNorthstar64.so"}, {kPluginTahoe, "libTahoe64.so"}, {kPluginHybrid, "Hybrid.so"}, #elif defined __APPLE__ {kPluginTahoe, "libTahoe64.dylib"}, + {kPluginNorthstar, "libNorthstar64.dylib"}, #endif }; @@ -148,6 +154,7 @@ rpr::CreationFlags getAllCompatibleGpuFlags(rpr_int pluginID, const char* cacheP } if (infoStatus != RPR_SUCCESS) { PRINT_CONTEXT_CREATION_DEBUG_INFO("Failed to query device name: %d\n", infoStatus); + return false; } rprObjectDelete(temporaryContext); @@ -187,14 +194,14 @@ rpr::CreationFlags getAllCompatibleGpuFlags(rpr_int pluginID, const char* cacheP return creationFlags; } -rpr::CreationFlags getRprCreationFlags(RenderDeviceType renderDevice, rpr_int pluginID, const char* cachePath) { +rpr::CreationFlags getRprCreationFlags(RprUsdRenderDeviceType renderDevice, rpr_int pluginID, const char* cachePath) { rpr::CreationFlags flags = 0x0; - if (kRenderDeviceCPU == renderDevice) { - PRINT_CONTEXT_CREATION_DEBUG_INFO("hdRpr CPU context\n"); + if (RprUsdRenderDeviceType::CPU == renderDevice) { + PRINT_CONTEXT_CREATION_DEBUG_INFO("RPR CPU context\n"); flags = RPR_CREATION_FLAGS_ENABLE_CPU; - } else if (kRenderDeviceGPU == renderDevice) { - PRINT_CONTEXT_CREATION_DEBUG_INFO("hdRpr GPU context\n"); + } else if (RprUsdRenderDeviceType::GPU == renderDevice) { + PRINT_CONTEXT_CREATION_DEBUG_INFO("RPR GPU context\n"); flags = getAllCompatibleGpuFlags(pluginID, cachePath); } else { return 0x0; @@ -209,9 +216,13 @@ rpr::CreationFlags getRprCreationFlags(RenderDeviceType renderDevice, rpr_int pl } // namespace anonymous -Context* CreateContext(char const* cachePath, ContextMetadata* metadata) { +rpr::Context* RprUsdCreateContext(RprUsdContextMetadata* metadata) { SetupRprTracing(); + RprUsdConfig* config; + auto configLock = RprUsdConfig::GetInstance(&config); + auto cachePath = config->GetKernelCacheDir(); + auto pluginLibNameIter = kPluginLibNames.find(metadata->pluginType); if (pluginLibNameIter == kPluginLibNames.end()) { PRINT_CONTEXT_CREATION_DEBUG_INFO("Plugin is not supported: %d", metadata->pluginType); @@ -236,12 +247,12 @@ Context* CreateContext(char const* cachePath, ContextMetadata* metadata) { // 3) MultiGPU can be enabled only through vulkan interop flags = RPR_CREATION_FLAGS_ENABLE_GPU0; } else { - flags = getRprCreationFlags(metadata->renderDeviceType, pluginID, cachePath); + flags = getRprCreationFlags(metadata->renderDeviceType, pluginID, cachePath.c_str()); if (!flags) { - bool isGpuIncompatible = metadata->renderDeviceType == kRenderDeviceGPU; + bool isGpuIncompatible = metadata->renderDeviceType == RprUsdRenderDeviceType::GPU; PRINT_CONTEXT_CREATION_DEBUG_INFO("%s is not compatible", isGpuIncompatible ? "GPU" : "CPU"); - metadata->renderDeviceType = isGpuIncompatible ? kRenderDeviceCPU : kRenderDeviceGPU; - flags = getRprCreationFlags(metadata->renderDeviceType, pluginID, cachePath); + metadata->renderDeviceType = isGpuIncompatible ? RprUsdRenderDeviceType::CPU : RprUsdRenderDeviceType::GPU; + flags = getRprCreationFlags(metadata->renderDeviceType, pluginID, cachePath.c_str()); if (!flags) { PRINT_CONTEXT_CREATION_DEBUG_INFO("Could not find compatible device"); return nullptr; @@ -252,10 +263,10 @@ Context* CreateContext(char const* cachePath, ContextMetadata* metadata) { } if (metadata->isGlInteropEnabled) { - if (metadata->renderDeviceType == kRenderDeviceCPU || metadata->pluginType == kPluginHybrid) { + if (metadata->renderDeviceType == RprUsdRenderDeviceType::CPU || metadata->pluginType == kPluginHybrid) { PRINT_CONTEXT_CREATION_DEBUG_INFO("GL interop could not be used with CPU rendering or Hybrid plugin"); metadata->isGlInteropEnabled = false; - } else if (!PXR_NS::GlfGlewInit()) { + } else if (!GlfGlewInit()) { PRINT_CONTEXT_CREATION_DEBUG_INFO("Failed to init GLEW. Disabling GL interop"); metadata->isGlInteropEnabled = false; } else { @@ -267,8 +278,35 @@ Context* CreateContext(char const* cachePath, ContextMetadata* metadata) { flags |= RPR_CREATION_FLAGS_ENABLE_GL_INTEROP; } - Status status; - auto context = Context::Create(RPR_API_VERSION, &pluginID, 1, flags, nullptr, cachePath, &status); + std::vector contextProperties; + auto appendContextProperty = [&contextProperties](uint64_t propertyKey, void* propertyValue) { + contextProperties.push_back((rpr_context_properties)propertyKey); + contextProperties.push_back((rpr_context_properties)propertyValue); + }; + +#ifdef HDRPR_ENABLE_VULKAN_INTEROP_SUPPORT + if (metadata->pluginType == RprUsdPluginType::kPluginHybrid && metadata->interopInfo) { + // Create interop context for hybrid + // TODO: should not it be configurable? + constexpr std::uint32_t MB = 1024u * 1024u; + static std::uint32_t acc_size = 1024 * MB; + static std::uint32_t vbuf_size = 1024 * MB; + static std::uint32_t ibuf_size = 512 * MB; + static std::uint32_t sbuf_size = 512 * MB; + + appendContextProperty(RPR_CONTEXT_CREATEPROP_VK_INTEROP_INFO, metadata->interopInfo); + appendContextProperty(RPR_CONTEXT_CREATEPROP_HYBRID_ACC_MEMORY_SIZE, &acc_size); + appendContextProperty(RPR_CONTEXT_CREATEPROP_HYBRID_VERTEX_MEMORY_SIZE, &vbuf_size); + appendContextProperty(RPR_CONTEXT_CREATEPROP_HYBRID_INDEX_MEMORY_SIZE, &ibuf_size); + appendContextProperty(RPR_CONTEXT_CREATEPROP_HYBRID_STAGING_MEMORY_SIZE, &sbuf_size); + } +#endif // HDRPR_ENABLE_VULKAN_INTEROP_SUPPORT + + contextProperties.push_back(nullptr); + + rpr::Status status; + rpr::Context* context = rpr::Context::Create(RPR_API_VERSION, &pluginID, 1, flags, contextProperties.data(), cachePath.c_str(), &status); + if (context) { if (RPR_ERROR_CHECK(context->SetActivePlugin(pluginID), "Failed to set active plugin")) { delete context; @@ -278,7 +316,13 @@ Context* CreateContext(char const* cachePath, ContextMetadata* metadata) { RPR_ERROR_CHECK(status, "Failed to create RPR context"); } + if (TfGetEnvSetting(RPRUSD_ENABLE_TRACING)) { + RPR_ERROR_CHECK(context->SetParameter(RPR_CONTEXT_TRACING_ENABLED, 1), "Failed to set context tracing parameter"); + } + + RPR_ERROR_CHECK(context->SetParameter(RPR_CONTEXT_TEXTURE_CACHE_PATH, config->GetTextureCacheDir().c_str()), "Failed to set texture cache path"); + return context; } -} // namespace rpr +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/rpr/helpers.h b/pxr/imaging/rprUsd/contextHelpers.h similarity index 65% rename from pxr/imaging/plugin/hdRpr/rpr/helpers.h rename to pxr/imaging/rprUsd/contextHelpers.h index 513b1bdf3..6f7358f7b 100644 --- a/pxr/imaging/plugin/hdRpr/rpr/helpers.h +++ b/pxr/imaging/rprUsd/contextHelpers.h @@ -11,21 +11,20 @@ See the License for the specific language governing permissions and limitations under the License. ************************************************************************/ -#ifndef HDRPR_CORE_HELPERS_H -#define HDRPR_CORE_HELPERS_H +#ifndef PXR_IMAGING_RPR_USD_CONTEXT_HELPERS_H +#define PXR_IMAGING_RPR_USD_CONTEXT_HELPERS_H -#include "error.h" +#include "pxr/imaging/rprUsd/api.h" -namespace rpr { +namespace rpr { class Context; } -template -T GetInfo(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"); - return value; -} +PXR_NAMESPACE_OPEN_SCOPE -} // namespace rpr +struct RprUsdContextMetadata; -#endif // HDRPR_CORE_HELPERS_H +RPRUSD_API +rpr::Context* RprUsdCreateContext(RprUsdContextMetadata* metadata); + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_IMAGING_RPR_USD_CONTEXT_HELPERS_H diff --git a/pxr/imaging/plugin/hdRpr/rpr/contextMetadata.h b/pxr/imaging/rprUsd/contextMetadata.h similarity index 61% rename from pxr/imaging/plugin/hdRpr/rpr/contextMetadata.h rename to pxr/imaging/rprUsd/contextMetadata.h index 9852d5bcb..742692dc5 100644 --- a/pxr/imaging/plugin/hdRpr/rpr/contextMetadata.h +++ b/pxr/imaging/rprUsd/contextMetadata.h @@ -11,32 +11,34 @@ See the License for the specific language governing permissions and limitations under the License. ************************************************************************/ -#ifndef HDRPR_CORE_CONTEXT_METADATA_H -#define HDRPR_CORE_CONTEXT_METADATA_H +#ifndef PXR_IMAGING_RPR_USD_CONTEXT_METADATA_H +#define PXR_IMAGING_RPR_USD_CONTEXT_METADATA_H -namespace rpr { +#include "pxr/pxr.h" -enum PluginType { +PXR_NAMESPACE_OPEN_SCOPE + +enum RprUsdPluginType { kPluginInvalid = -1, kPluginTahoe, - kPluginNorthStar, + kPluginNorthstar, kPluginHybrid, kPluginsCount }; -enum RenderDeviceType { - kRenderDeviceInvalid = -1, - kRenderDeviceCPU, - kRenderDeviceGPU, - kRenderDevicesCount +enum class RprUsdRenderDeviceType { + Invalid, + CPU, + GPU, }; -struct ContextMetadata { - PluginType pluginType = kPluginInvalid; - RenderDeviceType renderDeviceType = kRenderDeviceInvalid; +struct RprUsdContextMetadata { + RprUsdPluginType pluginType = kPluginInvalid; + RprUsdRenderDeviceType renderDeviceType = RprUsdRenderDeviceType::Invalid; bool isGlInteropEnabled = false; + void* interopInfo = nullptr; }; -} // namespace rpr +PXR_NAMESPACE_CLOSE_SCOPE -#endif // HDRPR_CORE_CONTEXT_METADATA_H +#endif // PXR_IMAGING_RPR_USD_CONTEXT_METADATA_H diff --git a/pxr/imaging/rprUsd/coreImage.cpp b/pxr/imaging/rprUsd/coreImage.cpp new file mode 100644 index 000000000..89824c0c4 --- /dev/null +++ b/pxr/imaging/rprUsd/coreImage.cpp @@ -0,0 +1,240 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "pxr/imaging/glf/glew.h" + +#include "pxr/imaging/rprUsd/coreImage.h" +#include "pxr/imaging/rprUsd/helpers.h" + +PXR_NAMESPACE_OPEN_SCOPE + +namespace { + +rpr::ImageDesc GetRprImageDesc(rpr::ImageFormat format, uint32_t width, uint32_t height, uint32_t depth = 1) { + int bytesPerComponent = 1; + if (format.type == RPR_COMPONENT_TYPE_FLOAT16) { + bytesPerComponent = 2; + } else if (format.type == RPR_COMPONENT_TYPE_FLOAT32) { + bytesPerComponent = 4; + } + + rpr::ImageDesc desc = {}; + desc.image_width = width; + desc.image_height = height; + desc.image_depth = depth; + desc.image_row_pitch = width * format.num_components * bytesPerComponent; + desc.image_slice_pitch = desc.image_row_pitch * height; + + return desc; +} + +rpr::Image* CreateRprImage(rpr::Context* context, GlfUVTextureData* textureData) { + rpr::ImageFormat format = {}; + +#if PXR_VERSION >= 2011 + auto hioFormat = textureData->GetHioFormat(); + GLenum glType = GlfGetGLType(hioFormat); + GLenum glFormat = GlfGetGLFormat(hioFormat); +#else + GLenum glType = textureData->GLType(); + GLenum glFormat = textureData->GLFormat(); +#endif + + switch (glType) { + case GL_UNSIGNED_BYTE: + format.type = RPR_COMPONENT_TYPE_UINT8; + break; + case GL_HALF_FLOAT: + format.type = RPR_COMPONENT_TYPE_FLOAT16; + break; + case GL_FLOAT: + format.type = RPR_COMPONENT_TYPE_FLOAT32; + break; + default: + TF_RUNTIME_ERROR("Unsupported pixel data GLtype: %#x", glType); + return nullptr; + } + + switch (glFormat) { + case GL_RED: + format.num_components = 1; + break; + case GL_RGB: + format.num_components = 3; + break; + case GL_RGBA: + format.num_components = 4; + break; + default: + TF_RUNTIME_ERROR("Unsupported pixel data GLformat: %#x", glFormat); + return nullptr; + } + rpr::ImageDesc desc = GetRprImageDesc(format, textureData->ResizedWidth(), textureData->ResizedHeight()); + + rpr::Status status; + auto rprImage = context->CreateImage(format, desc, textureData->GetRawBuffer(), &status); + if (!rprImage) { + RPR_ERROR_CHECK(status, "Failed to create image from data", context); + return nullptr; + } + + return rprImage; +} + +} // namespace anonymous + +RprUsdCoreImage* RprUsdCoreImage::Create(rpr::Context* context, std::string const& path) { + auto textureData = GlfUVTextureData::New(path, INT_MAX, 0, 0, 0, 0); + if (!textureData || !textureData->Read(0, false)) { + return nullptr; + } + + return Create(context, {{0, textureData.operator->()}}); +} + +RprUsdCoreImage* RprUsdCoreImage::Create(rpr::Context* context, uint32_t width, uint32_t height, rpr::ImageFormat format, void const* data, rpr::Status* status) { + auto rootImage = context->CreateImage(format, GetRprImageDesc(format, width, height), data, status); + if (!rootImage) { + return nullptr; + } + + return new RprUsdCoreImage(rootImage); +} + +RprUsdCoreImage* RprUsdCoreImage::Create( + rpr::Context* context, + std::vector const& tiles) { + + if (tiles.empty()) { + return nullptr; + } + + if (tiles.size() == 1 && tiles[0].id == 0) { + // Single non-UDIM tile + auto rprImage = CreateRprImage(context, tiles[0].textureData); + if (!rprImage) { + return nullptr; + } + + return new RprUsdCoreImage(rprImage); + } else { + // Process UDIM + RprUsdCoreImage* coreImage = nullptr; + + for (auto tile : tiles) { + if (tile.id < 1001 || tile.id > 1100) { + TF_RUNTIME_ERROR("Invalid UDIM tile id - %u", tile.id); + continue; + } + + auto rprImage = CreateRprImage(context, tile.textureData); + if (!rprImage) { + continue; + } + + if (!coreImage) { + coreImage = new RprUsdCoreImage; + + rpr::ImageFormat rootImageFormat = {}; + rootImageFormat.num_components = 0; + rootImageFormat.type = RPR_COMPONENT_TYPE_UINT8; + rpr::ImageDesc rootImageDesc = {}; + + rpr::Status status; + coreImage->m_rootImage = context->CreateImage(rootImageFormat, rootImageDesc, nullptr, &status); + if (!coreImage->m_rootImage) { + delete coreImage; + delete rprImage; + RPR_ERROR_CHECK(status, "Failed to create UDIM root image", context); + return nullptr; + } + } + + RPR_ERROR_CHECK(coreImage->m_rootImage->SetUDIM(tile.id, rprImage), "Failed to set UDIM"); + coreImage->m_subImages.push_back(rprImage); + } + + return coreImage; + } +} + +RprUsdCoreImage::~RprUsdCoreImage() { + delete m_rootImage; + for (auto image : m_subImages) { + delete image; + } + + m_rootImage = nullptr; + m_subImages.clear(); +} + +rpr::Image* RprUsdCoreImage::GetBaseImage() { + return m_subImages.empty() ? m_rootImage : m_subImages[0]; +} + +template +rpr::Status RprUsdCoreImage::ForEachImage(F f) { + if (m_subImages.empty()) { + return f(m_rootImage); + } else { + for (auto image : m_subImages) { + auto status = f(image); + if (status != RPR_SUCCESS) { + return status; + } + } + return RPR_SUCCESS; + } +} + +rpr::ImageFormat RprUsdCoreImage::GetFormat() { + return RprUsdGetInfo(GetBaseImage(), RPR_IMAGE_FORMAT); +} + +rpr::ImageDesc RprUsdCoreImage::GetDesc() { + return RprUsdGetInfo(GetBaseImage(), RPR_IMAGE_DESC); +} + +rpr::Status RprUsdCoreImage::GetInfo(rpr::ImageInfo imageInfo, size_t size, void* data, size_t* size_ret) { + return GetBaseImage()->GetInfo(imageInfo, size, data, size_ret); +} + +rpr::Status RprUsdCoreImage::SetWrap(rpr::ImageWrapType type) { + return ForEachImage([type](rpr::Image* image) { return image->SetWrap(type); }); +} + +rpr::Status RprUsdCoreImage::SetGamma(float gamma) { + return ForEachImage([gamma](rpr::Image* image) { return image->SetGamma(gamma); }); +} + +rpr::Status RprUsdCoreImage::SetColorSpace(const char* colorSpace) { + return ForEachImage([colorSpace](rpr::Image* image) { + // TODO: add C++ wrapper + auto rprImageHandle = rpr::GetRprObject(image); + return rprImageSetOcioColorspace(rprImageHandle, colorSpace); + }); +} + +rpr::Status RprUsdCoreImage::SetMipmapEnabled(bool enabled) { + return ForEachImage([enabled](rpr::Image* image) { return image->SetMipmapEnabled(enabled); }); +} + +rpr::Status RprUsdCoreImage::SetFilter(rpr::ImageFilterType type) { + return ForEachImage([type](rpr::Image* image) { return image->SetFilter(type); }); +} + +rpr::Status RprUsdCoreImage::SetName(const char* name) { + return ForEachImage([name](rpr::Image* image) { return image->SetName(name); }); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/coreImage.h b/pxr/imaging/rprUsd/coreImage.h new file mode 100644 index 000000000..7711f2a84 --- /dev/null +++ b/pxr/imaging/rprUsd/coreImage.h @@ -0,0 +1,90 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef PXR_IMAGING_RPR_USD_CORE_IMAGE_H +#define PXR_IMAGING_RPR_USD_CORE_IMAGE_H + +#include "pxr/imaging/rprUsd/api.h" +#include "pxr/imaging/glf/uvTextureData.h" + +#include + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +class RprUsdCoreImage { +public: + RPRUSD_API + static RprUsdCoreImage* Create(rpr::Context* context, std::string const& path); + + struct UDIMTile { + uint32_t id; + GlfUVTextureData* textureData; + + UDIMTile(uint32_t id, GlfUVTextureData* textureData) : id(id), textureData(textureData) {} + }; + RPRUSD_API + static RprUsdCoreImage* Create(rpr::Context* context, std::vector const& textureData); + + RPRUSD_API + static RprUsdCoreImage* Create(rpr::Context* context, uint32_t width, uint32_t height, rpr::ImageFormat format, void const* data, rpr::Status* status = nullptr); + + RPRUSD_API + ~RprUsdCoreImage(); + + RPRUSD_API + rpr::Image* GetRootImage() { return m_rootImage; } + + RPRUSD_API + rpr::ImageFormat GetFormat(); + + RPRUSD_API + rpr::ImageDesc GetDesc(); + + RPRUSD_API + rpr::Status GetInfo(rpr::ImageInfo imageInfo, size_t size, void* data, size_t* size_ret); + + RPRUSD_API + rpr::Status SetWrap(rpr::ImageWrapType type); + + RPRUSD_API + rpr::Status SetGamma(float gamma); + + RPRUSD_API + rpr::Status SetColorSpace(const char* colorSpace); + + RPRUSD_API + rpr::Status SetMipmapEnabled(bool enabled); + + RPRUSD_API + rpr::Status SetFilter(rpr::ImageFilterType type); + + RPRUSD_API + rpr::Status SetName(const char* name); + +private: + RprUsdCoreImage(rpr::Image* rootImage = nullptr) : m_rootImage(rootImage) {}; + rpr::Image* GetBaseImage(); + + template + rpr::Status ForEachImage(F f); + +private: + rpr::Image* m_rootImage = nullptr; + std::vector m_subImages; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_IMAGING_RPR_USD_CORE_IMAGE_H diff --git a/pxr/imaging/rprUsd/debugCodes.cpp b/pxr/imaging/rprUsd/debugCodes.cpp new file mode 100644 index 000000000..b32860920 --- /dev/null +++ b/pxr/imaging/rprUsd/debugCodes.cpp @@ -0,0 +1,32 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "pxr/imaging/rprUsd/debugCodes.h" + +#include "pxr/base/tf/debug.h" +#include "pxr/base/tf/registryManager.h" + +PXR_NAMESPACE_OPEN_SCOPE + +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"); +} + +bool RprUsdIsLeakCheckEnabled() { + static bool forceLeakCheck = false; + return forceLeakCheck || TfDebug::IsEnabled(RPR_USD_DEBUG_LEAKS); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/rpr/imageHelpers.h b/pxr/imaging/rprUsd/debugCodes.h similarity index 60% rename from pxr/imaging/plugin/hdRpr/rpr/imageHelpers.h rename to pxr/imaging/rprUsd/debugCodes.h index 8b3cbdbfa..339fe0024 100644 --- a/pxr/imaging/plugin/hdRpr/rpr/imageHelpers.h +++ b/pxr/imaging/rprUsd/debugCodes.h @@ -11,19 +11,24 @@ See the License for the specific language governing permissions and limitations under the License. ************************************************************************/ -#ifndef HDRPR_CORE_IMAGE_HELPERS_H -#define HDRPR_CORE_IMAGE_HELPERS_H +#ifndef PXR_IMAGING_RPR_USD_DEBUG_CODES_H +#define PXR_IMAGING_RPR_USD_DEBUG_CODES_H -#include +#include "pxr/pxr.h" +#include "pxr/base/tf/debug.h" +#include "pxr/imaging/rprUsd/api.h" -namespace rpr { +PXR_NAMESPACE_OPEN_SCOPE -Image* CreateImage(Context* context, char const* path, bool forceLinearSpace = false); -Image* CreateImage(Context* context, uint32_t width, uint32_t height, ImageFormat format, void const* data, rpr::Status* status = nullptr); +TF_DEBUG_CODES( + RPR_USD_DEBUG_CORE_UNSUPPORTED_ERROR, + RPR_USD_DEBUG_DUMP_MATERIALS, + RPR_USD_DEBUG_LEAKS +); -ImageFormat GetImageFormat(Image* image); -ImageDesc GetImageDesc(Image* image); +RPRUSD_API +bool RprUsdIsLeakCheckEnabled(); -} // namespace rpr +PXR_NAMESPACE_CLOSE_SCOPE -#endif // HDRPR_CORE_IMAGE_HELPERS_H +#endif // PXR_IMAGING_RPR_USD_DEBUG_CODES_H diff --git a/pxr/imaging/plugin/hdRpr/rpr/error.h b/pxr/imaging/rprUsd/error.h similarity index 57% rename from pxr/imaging/plugin/hdRpr/rpr/error.h rename to pxr/imaging/rprUsd/error.h index 055da3ed5..e13dac002 100644 --- a/pxr/imaging/plugin/hdRpr/rpr/error.h +++ b/pxr/imaging/rprUsd/error.h @@ -11,10 +11,10 @@ See the License for the specific language governing permissions and limitations under the License. ************************************************************************/ -#ifndef HDRPR_CORE_ERROR_H -#define HDRPR_CORE_ERROR_H +#ifndef PXR_IMAGING_RPR_USD_ERROR_H +#define PXR_IMAGING_RPR_USD_ERROR_H -#include "debugCodes.h" +#include "pxr/imaging/rprUsd/debugCodes.h" #include "pxr/base/arch/functionLite.h" #include "pxr/base/tf/stringUtils.h" @@ -29,22 +29,22 @@ limitations under the License. auto st = status; \ if (st != RPR_SUCCESS) { \ assert(false); \ - throw rpr::Error(st, msg, __ARCH_FILE__, __ARCH_FUNCTION__, __LINE__, ##__VA_ARGS__); \ + throw RprUsdError(st, msg, __ARCH_FILE__, __ARCH_FUNCTION__, __LINE__, ##__VA_ARGS__); \ } \ } while(0); #define RPR_ERROR_CHECK(status, msg, ...) \ - rpr::IsErrorCheck(status, msg, __ARCH_FILE__, __ARCH_FUNCTION__, __LINE__, ##__VA_ARGS__) + RprUsdFailed(status, msg, __ARCH_FILE__, __ARCH_FUNCTION__, __LINE__, ##__VA_ARGS__) #define RPR_GET_ERROR_MESSAGE(status, msg, ...) \ - rpr::ConstructErrorMessage(status, msg, __ARCH_FILE__, __ARCH_FUNCTION__, __LINE__, ##__VA_ARGS__) + RprUsdConstructErrorMessage(status, msg, __ARCH_FILE__, __ARCH_FUNCTION__, __LINE__, ##__VA_ARGS__) #define RPR_THROW_ERROR_MSG(fmt, ...) \ - rpr::ThrowErrorMsg(__ARCH_FILE__, __ARCH_FUNCTION__, __LINE__, fmt, ##__VA_ARGS__); + RprUsdThrowErrorMsg(__ARCH_FILE__, __ARCH_FUNCTION__, __LINE__, fmt, ##__VA_ARGS__); -namespace rpr { +PXR_NAMESPACE_OPEN_SCOPE -inline std::string ConstructErrorMessage(rpr_status errorStatus, std::string const& messageOnFail, char const* file, char const* function, size_t line, rpr::Context* context = nullptr) { +inline std::string RprUsdConstructErrorMessage(rpr::Status errorStatus, std::string const& messageOnFail, char const* file, char const* function, size_t line, rpr::Context* context = nullptr) { auto rprErrorString = [errorStatus, context]() -> std::string { if (context) { size_t lastErrorMessageSize = 0; @@ -71,52 +71,52 @@ inline std::string ConstructErrorMessage(rpr_status errorStatus, std::string con return "error code - " + std::to_string(errorStatus); }; - auto suffix = PXR_NS::TfStringPrintf(" in %s at line %zu of %s", function, line, file); + auto suffix = TfStringPrintf(" in %s at line %zu of %s", function, line, file); #ifdef RPR_GIT_SHORT_HASH - suffix += PXR_NS::TfStringPrintf("(%s)", RPR_GIT_SHORT_HASH); + suffix += TfStringPrintf("(%s)", RPR_GIT_SHORT_HASH); #endif // RPR_GIT_SHORT_HASH if (errorStatus == RPR_SUCCESS) { - return PXR_NS::TfStringPrintf("[RPR ERROR] %s%s", messageOnFail.c_str(), suffix.c_str()); + return TfStringPrintf("[RPR ERROR] %s%s", messageOnFail.c_str(), suffix.c_str()); } else { auto errorStr = rprErrorString(); - return PXR_NS::TfStringPrintf("[RPR ERROR] %s -- %s%s", messageOnFail.c_str(), errorStr.c_str(), suffix.c_str()); + return TfStringPrintf("[RPR ERROR] %s -- %s%s", messageOnFail.c_str(), errorStr.c_str(), suffix.c_str()); } } -inline bool IsErrorCheck(rpr_status status, const std::string& messageOnFail, char const* file, char const* function, size_t line, rpr::Context* context = nullptr) { +inline bool RprUsdFailed(rpr::Status status, const char* messageOnFail, char const* file, char const* function, size_t line, rpr::Context* context = nullptr) { if (RPR_SUCCESS == status) { return false; } - if ((status == RPR_ERROR_UNSUPPORTED || status == RPR_ERROR_UNIMPLEMENTED) && !PXR_NS::TfDebug::IsEnabled(PXR_NS::HD_RPR_DEBUG_CORE_UNSUPPORTED_ERROR)) { + if ((status == RPR_ERROR_UNSUPPORTED || status == RPR_ERROR_UNIMPLEMENTED) && !TfDebug::IsEnabled(RPR_USD_DEBUG_CORE_UNSUPPORTED_ERROR)) { return true; } - auto errorMessage = ConstructErrorMessage(status, messageOnFail.c_str(), file, function, line, context); + auto errorMessage = RprUsdConstructErrorMessage(status, messageOnFail, file, function, line, context); fprintf(stderr, "%s\n", errorMessage.c_str()); return true; } -class Error : public std::runtime_error { +class RprUsdError : public std::runtime_error { public: - Error(rpr_status errorStatus, const char* messageOnFail, char const* file, char const* function, size_t line, rpr::Context* context = nullptr) - : std::runtime_error(ConstructErrorMessage(errorStatus, messageOnFail, file, function, line, context)) { + RprUsdError(rpr::Status errorStatus, const char* messageOnFail, char const* file, char const* function, size_t line, rpr::Context* context = nullptr) + : std::runtime_error(RprUsdConstructErrorMessage(errorStatus, messageOnFail, file, function, line, context)) { } - Error(std::string const& errorMesssage) + RprUsdError(std::string const& errorMesssage) : std::runtime_error(errorMesssage) { } }; -inline void ThrowErrorMsg(char const* file, char const* function, size_t line, const char* fmt, ...) { +inline void RprUsdThrowErrorMsg(char const* file, char const* function, size_t line, const char* fmt, ...) { va_list ap; va_start(ap, fmt); - auto messageOnFail = PXR_NS::TfVStringPrintf(fmt, ap); + auto messageOnFail = TfVStringPrintf(fmt, ap); va_end(ap); - throw Error(ConstructErrorMessage(RPR_SUCCESS, messageOnFail, file, function, line)); + throw RprUsdError(RprUsdConstructErrorMessage(RPR_SUCCESS, messageOnFail, file, function, line)); } -} // namespace rpr +PXR_NAMESPACE_CLOSE_SCOPE -#endif // HDRPR_CORE_ERROR_H +#endif // PXR_IMAGING_RPR_USD_ERROR_H diff --git a/pxr/imaging/rprUsd/helpers.h b/pxr/imaging/rprUsd/helpers.h new file mode 100644 index 000000000..416faf891 --- /dev/null +++ b/pxr/imaging/rprUsd/helpers.h @@ -0,0 +1,51 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef PXR_IMAGING_RPR_USD_HELPERS_H +#define PXR_IMAGING_RPR_USD_HELPERS_H + +#include "pxr/imaging/rprUsd/error.h" + +#include + +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"); + return value; +} + +template +std::string RprUsdGetStringInfo(U* object, R info) { + size_t size = 0; + RPR_ERROR_CHECK_THROW(object->GetInfo(info, 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"); + + // discard null-terminator + --size; + + return std::string(buffer.get(), size); +} + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_IMAGING_RPR_USD_HELPERS_H diff --git a/pxr/imaging/rprUsd/imageCache.cpp b/pxr/imaging/rprUsd/imageCache.cpp new file mode 100644 index 000000000..c6bbe58ad --- /dev/null +++ b/pxr/imaging/rprUsd/imageCache.cpp @@ -0,0 +1,175 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "pxr/imaging/glf/glew.h" +#include "pxr/imaging/rprUsd/imageCache.h" +#include "pxr/imaging/rprUsd/coreImage.h" +#include "pxr/imaging/rprUsd/helpers.h" +#include "pxr/imaging/rprUsd/util.h" +#include "pxr/base/arch/fileSystem.h" +#include "pxr/base/tf/diagnostic.h" + +PXR_NAMESPACE_OPEN_SCOPE + +template +size_t GetHash(T const& value) { + return std::hash{}(value); +} + +double GetModificationTime(std::string const& path) { + double modificationTime = 0.0; + ArchGetModificationTime(path.c_str(), &modificationTime); + return modificationTime; +} + +RprUsdImageCache::RprUsdImageCache(rpr::Context* context) + : m_context(context) { + +} + +std::shared_ptr +RprUsdImageCache::GetImage( + std::string const& path, + std::string const& colorspace, + rpr::ImageWrapType wrapType, + std::vector const& tiles) { + if (!wrapType) { + wrapType = RPR_IMAGE_WRAP_TYPE_REPEAT; + } + + CacheKey key = {}; + key.path = path; + key.colorspace = colorspace; + key.wrapType = wrapType; + key.hash = GetHash(path) ^ GetHash(colorspace) ^ GetHash(wrapType); + + auto it = m_cache.find(key); + if (it != m_cache.end()) { + CacheValue& cacheValue = it->second; + if (auto image = cacheValue.Lock(key)) { + return image; + } else { + m_cache.erase(it); + it = m_cache.end(); + } + } + + auto coreImage = RprUsdCoreImage::Create(m_context, tiles); + if (!coreImage) { + return nullptr; + } + + if (RprUsdIsLeakCheckEnabled()) { + coreImage->SetName(path.c_str()); + } + + float gamma = 1.0f; + if (key.colorspace == "srgb") { + gamma = 2.2f; + } else if (key.colorspace.empty()) { + // Figure out gamma from the internal format. + // Assume that all tiles have the same colorspace + // + auto data = tiles[0].textureData; +#if PXR_VERSION >= 2011 + GLenum internalFormat = GlfGetGLInternalFormat(data->GetHioFormat()); +#else + GLenum internalFormat = data->GLInternalFormat(); +#endif + if (internalFormat == GL_SRGB || + internalFormat == GL_SRGB8 || + internalFormat == GL_SRGB_ALPHA || + internalFormat == GL_SRGB8_ALPHA8) { + // XXX(RPR): sRGB formula is different from straight pow decoding, but it's the best we can do without OCIO + gamma = 2.2f; + } else { + gamma = 1.0f; + } + } + + RPR_ERROR_CHECK(coreImage->SetGamma(gamma), "Failed to set image gamma"); + RPR_ERROR_CHECK(coreImage->SetWrap(key.wrapType), "Failed to set image wrap type"); + + CacheValue cacheValue; + if (tiles.size() != 1 || tiles[0].id != 0) { + // UDIM tiles + std::string formatString; + if (!RprUsdGetUDIMFormatString(path, &formatString)) { + return nullptr; + } + + cacheValue.tileModificationTimes.reserve(tiles.size()); + for (auto& tile : tiles) { + auto tilePath = TfStringPrintf(formatString.c_str(), tile.id); + cacheValue.tileModificationTimes.emplace_back(tile.id, GetModificationTime(tilePath)); + } + } else { + cacheValue.tileModificationTimes.emplace_back(0, GetModificationTime(path)); + } + + std::shared_ptr cachedImage(coreImage, + [this, key](RprUsdCoreImage* coreImage) { + delete coreImage; + m_cache.erase(key); + } + ); + cacheValue.handle = cachedImage; + + it = m_cache.emplace(std::move(key), std::move(cacheValue)).first; + + return cachedImage; +} + +std::shared_ptr RprUsdImageCache::CacheValue::Lock(CacheKey const& key) const { + auto image = handle.lock(); + if (!image) { + return nullptr; + } + + std::string udimFormatString; + + // Check if image files were not changed + // + for (auto& entry : tileModificationTimes) { + double modificationTime = entry.second; + if (modificationTime == 0.0) { + // If the path points to a non-filesystem image (e.g. usdz embedded image) + // we rely on the user of the Hydra to correctly reload all materials that use this image + // + continue; + } + + double currentModificationTime; + + uint32_t tileId = entry.first; + if (tileId == 0) { + currentModificationTime = GetModificationTime(key.path); + } else { + if (udimFormatString.empty()) { + RprUsdGetUDIMFormatString(key.path, &udimFormatString); + } + + auto tilePath = TfStringPrintf(udimFormatString.c_str(), tileId); + currentModificationTime = GetModificationTime(tilePath); + } + + if (modificationTime != currentModificationTime) { + return nullptr; + } + } + + return image; + +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/imageCache.h b/pxr/imaging/rprUsd/imageCache.h new file mode 100644 index 000000000..7b18f1e93 --- /dev/null +++ b/pxr/imaging/rprUsd/imageCache.h @@ -0,0 +1,66 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_IMAGE_CACHE_H +#define RPRUSD_IMAGE_CACHE_H + +#include "pxr/imaging/rprUsd/coreImage.h" + +#include +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +class RprUsdImageCache { +public: + RPRUSD_API + RprUsdImageCache(rpr::Context* context); + + RPRUSD_API + std::shared_ptr GetImage( + std::string const& path, + std::string const& colorspace, + rpr::ImageWrapType wrapType, + std::vector const& data = {}); + +private: + rpr::Context* m_context; + + struct CacheKey { + std::string path; + std::string colorspace; + rpr::ImageWrapType wrapType; + + bool operator==(CacheKey const& rhs) const { + return wrapType == rhs.wrapType && colorspace == rhs.colorspace && path == rhs.path; + } + + size_t hash; + struct Hash { size_t operator()(CacheKey const& key) const { return key.hash; }; }; + }; + + struct CacheValue { + std::vector> tileModificationTimes; + std::weak_ptr handle; + + // Convenience wrapper over std::weak_ptr::lock that includes checking if image files are outdated + std::shared_ptr Lock(CacheKey const& key) const; + }; + + std::unordered_map m_cache; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_IMAGE_CACHE_H diff --git a/pxr/imaging/rprUsd/material.cpp b/pxr/imaging/rprUsd/material.cpp new file mode 100644 index 000000000..1f2eafa12 --- /dev/null +++ b/pxr/imaging/rprUsd/material.cpp @@ -0,0 +1,83 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "pxr/imaging/rprUsd/material.h" +#include "pxr/imaging/rprUsd/error.h" + +#include "pxr/base/gf/vec2f.h" + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +bool RprUsdMaterial::AttachTo(rpr::Shape* mesh, bool displacementEnabled) const { + bool fail = RPR_ERROR_CHECK(mesh->SetMaterial(m_surfaceNode), "Failed to set shape material"); + + fail |= RPR_ERROR_CHECK(mesh->SetVolumeMaterial(m_volumeNode), "Failed to set shape volume material"); + + if (displacementEnabled && m_displacementNode) { + size_t dummy; + int subdFactor; + if (RPR_ERROR_CHECK(mesh->GetInfo(RPR_SHAPE_SUBDIVISION_FACTOR, sizeof(subdFactor), &subdFactor, &dummy), "Failed to query mesh subdivision factor")) { + subdFactor = 0; + } + + if (subdFactor == 0) { + TF_WARN("Displacement material requires subdivision to be enabled. The subdivision will be enabled with refine level of 1"); + if (!RPR_ERROR_CHECK(mesh->SetSubdivisionFactor(1), "Failed to set mesh subdividion")) { + subdFactor = 1; + } + } + if (subdFactor > 0) { + fail |= RPR_ERROR_CHECK(mesh->SetDisplacementMaterial(m_displacementNode), "Failed to set shape displacement material"); + + GfVec2f displacementScale(0.0f, 1.0f); + if (m_displacementScale.IsHolding()) { + displacementScale = m_displacementScale.UncheckedGet(); + } + + fail |= RPR_ERROR_CHECK(mesh->SetDisplacementScale(displacementScale[0], displacementScale[1]), "Failed to set shape displacement scale"); + } + } else { + fail |= RPR_ERROR_CHECK(mesh->SetDisplacementMaterial(nullptr), "Failed to unset shape displacement material"); + } + + fail |= RPR_ERROR_CHECK(mesh->SetShadowCatcher(m_isShadowCatcher), "Failed to set shape shadow catcher"); + fail |= RPR_ERROR_CHECK(mesh->SetReflectionCatcher(m_isReflectionCatcher), "Failed to set shape reflection catcher"); + + return !fail; +} + +bool RprUsdMaterial::AttachTo(rpr::Curve* curve) const { + return !RPR_ERROR_CHECK(curve->SetMaterial(m_surfaceNode), "Failed to set curve material"); +} + +void RprUsdMaterial::DetachFrom(rpr::Shape* mesh) { + RPR_ERROR_CHECK(mesh->SetMaterial(nullptr), "Failed to unset shape material"); + RPR_ERROR_CHECK(mesh->SetVolumeMaterial(nullptr), "Failed to unset shape volume material"); + RPR_ERROR_CHECK(mesh->SetDisplacementMaterial(nullptr), "Failed to unset shape displacement material"); + RPR_ERROR_CHECK(mesh->SetShadowCatcher(false), "Failed to unset shape shadow catcher"); + RPR_ERROR_CHECK(mesh->SetReflectionCatcher(false), "Failed to unset shape reflection catcher"); +} + +void RprUsdMaterial::DetachFrom(rpr::Curve* curve) { + RPR_ERROR_CHECK(curve->SetMaterial(nullptr), "Failed to unset curve material"); +} + +void RprUsdMaterial::SetName(const char *name) { + if (m_surfaceNode) m_surfaceNode->SetName(name); + if (m_displacementNode) m_displacementNode->SetName(name); + if (m_volumeNode) m_volumeNode->SetName(name); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/material.h b/pxr/imaging/rprUsd/material.h new file mode 100644 index 000000000..05f1a432b --- /dev/null +++ b/pxr/imaging/rprUsd/material.h @@ -0,0 +1,60 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_MATERIAL_H +#define RPRUSD_MATERIAL_H + +#include "pxr/imaging/rprUsd/api.h" +#include "pxr/base/vt/value.h" +#include "pxr/base/tf/token.h" + +namespace rpr { class Shape; class Curve; class MaterialNode; } + +PXR_NAMESPACE_OPEN_SCOPE + +class RprUsdMaterial { +public: + RPRUSD_API + virtual ~RprUsdMaterial() = default; + + RPRUSD_API + TfToken const& GetUvPrimvarName() const { return m_uvPrimvarName; } + + RPRUSD_API + bool AttachTo(rpr::Shape* mesh, bool displacementEnabled) const; + + RPRUSD_API + bool AttachTo(rpr::Curve* curve) const; + + RPRUSD_API + static void DetachFrom(rpr::Shape* mesh); + + RPRUSD_API + static void DetachFrom(rpr::Curve* curve); + + RPRUSD_API + void SetName(const char* name); + +protected: + rpr::MaterialNode* m_surfaceNode = nullptr; + rpr::MaterialNode* m_displacementNode = nullptr; + rpr::MaterialNode* m_volumeNode = nullptr; + bool m_isShadowCatcher = false; + bool m_isReflectionCatcher = false; + TfToken m_uvPrimvarName; + VtValue m_displacementScale; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_MATERIAL_H diff --git a/pxr/imaging/rprUsd/materialHelpers.h b/pxr/imaging/rprUsd/materialHelpers.h new file mode 100644 index 000000000..25e01211d --- /dev/null +++ b/pxr/imaging/rprUsd/materialHelpers.h @@ -0,0 +1,87 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_MATERIAL_HELPERS_H +#define RPRUSD_MATERIAL_HELPERS_H + +#include "pxr/base/vt/value.h" +#include "pxr/base/gf/vec2f.h" +#include "pxr/base/gf/vec3f.h" +#include "pxr/base/gf/vec4f.h" +#include "pxr/imaging/rprUsd/error.h" + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +using RprMaterialNodePtr = std::shared_ptr; + +inline rpr::Status SetRprInput(rpr::MaterialNode* node, rpr::MaterialNodeInput input, VtValue const& value) { + rpr::Status status; + if (value.IsHolding()) { + status = node->SetInput(input, value.UncheckedGet()); + } else if (value.IsHolding()) { + status = node->SetInput(input, rpr_uint(value.UncheckedGet())); + } else if (value.IsHolding()) { + rpr_uint v = value.UncheckedGet() ? 1 : 0; + status = node->SetInput(input, v); + } else if (value.IsHolding()) { + auto v = value.UncheckedGet(); + status = node->SetInput(input, v, v, v, v); + } else if (value.IsHolding()) { + auto& v = value.UncheckedGet(); + status = node->SetInput(input, v[0], v[1], v[2], 1.0f); + } else if (value.IsHolding()) { + auto& v = value.UncheckedGet(); + status = node->SetInput(input, v[0], v[1], 1.0f, 1.0f); + } else if (value.IsHolding()) { + auto& v = value.UncheckedGet(); + status = node->SetInput(input, v[0], v[1], v[2], v[3]); + } else if (value.IsHolding()) { + status = node->SetInput(input, value.UncheckedGet().get()); + } else { + TF_RUNTIME_ERROR("Failed to set material input %d: unsupported VtValue type - %s", input, value.GetTypeName().c_str()); + return RPR_ERROR_INVALID_PARAMETER_TYPE; + } + + if (status != RPR_SUCCESS) { + auto errMsg = TfStringPrintf("Failed to set material input %d(%s)", input, value.GetTypeName().c_str()); + RPR_ERROR_CHECK(status, errMsg.c_str()); + } + + return status; +} + +inline GfVec4f GetRprFloat(VtValue const& value) { + if (value.IsHolding()) { + return GfVec4f(value.Get()); + } else if (value.IsHolding()) { + GfVec3f v = value.Get(); + return GfVec4f(v[0], v[1], v[2], 1.0f); + } if (value.IsHolding()) { + return GfVec4f(value.Get()); + } else { + return value.Get(); + } +} + +inline bool GfIsEqual(GfVec4f const& v1, GfVec4f const& v2, float tolerance = 1e-5f) { + return std::abs(v1[0] - v2[0]) <= tolerance && + std::abs(v1[1] - v2[1]) <= tolerance && + std::abs(v1[2] - v2[2]) <= tolerance && + std::abs(v1[3] - v2[3]) <= tolerance; +} + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_MATERIAL_HELPERS_H diff --git a/pxr/imaging/rprUsd/materialMappings.cpp b/pxr/imaging/rprUsd/materialMappings.cpp new file mode 100644 index 000000000..801b5ba1a --- /dev/null +++ b/pxr/imaging/rprUsd/materialMappings.cpp @@ -0,0 +1,224 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "pxr/imaging/rprUsd/materialMappings.h" + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEFINE_PUBLIC_TOKENS(RprUsdMaterialNodeInputTokens, RPRUSD_MATERIAL_NODE_INPUT_TOKENS); +TF_DEFINE_PUBLIC_TOKENS(RprUsdMaterialNodeTypeTokens, RPRUSD_MATERIAL_NODE_TYPE_TOKENS); +TF_DEFINE_PUBLIC_TOKENS(RprUsdMaterialInputModeTokens, RPRUSD_MATERIAL_INPUT_MODE_TOKENS); + +bool ToRpr(TfToken const& id, rpr::MaterialNodeType* out, bool pedantic) { + static const std::map s_mapping = { + {RprUsdMaterialNodeTypeTokens->diffuse, RPR_MATERIAL_NODE_DIFFUSE}, + {RprUsdMaterialNodeTypeTokens->microfacet, RPR_MATERIAL_NODE_MICROFACET}, + {RprUsdMaterialNodeTypeTokens->reflection, RPR_MATERIAL_NODE_REFLECTION}, + {RprUsdMaterialNodeTypeTokens->refraction, RPR_MATERIAL_NODE_REFRACTION}, + {RprUsdMaterialNodeTypeTokens->microfacet_refraction, RPR_MATERIAL_NODE_MICROFACET_REFRACTION}, + {RprUsdMaterialNodeTypeTokens->transparent, RPR_MATERIAL_NODE_TRANSPARENT}, + {RprUsdMaterialNodeTypeTokens->emissive, RPR_MATERIAL_NODE_EMISSIVE}, + {RprUsdMaterialNodeTypeTokens->ward, RPR_MATERIAL_NODE_WARD}, + {RprUsdMaterialNodeTypeTokens->add, RPR_MATERIAL_NODE_ADD}, + {RprUsdMaterialNodeTypeTokens->blend, RPR_MATERIAL_NODE_BLEND}, + {RprUsdMaterialNodeTypeTokens->arithmetic, RPR_MATERIAL_NODE_ARITHMETIC}, + {RprUsdMaterialNodeTypeTokens->fresnel, RPR_MATERIAL_NODE_FRESNEL}, + {RprUsdMaterialNodeTypeTokens->normal_map, RPR_MATERIAL_NODE_NORMAL_MAP}, + {RprUsdMaterialNodeTypeTokens->image_texture, RPR_MATERIAL_NODE_IMAGE_TEXTURE}, + {RprUsdMaterialNodeTypeTokens->noise2d_texture, RPR_MATERIAL_NODE_NOISE2D_TEXTURE}, + {RprUsdMaterialNodeTypeTokens->dot_texture, RPR_MATERIAL_NODE_DOT_TEXTURE}, + {RprUsdMaterialNodeTypeTokens->gradient_texture, RPR_MATERIAL_NODE_GRADIENT_TEXTURE}, + {RprUsdMaterialNodeTypeTokens->checker_texture, RPR_MATERIAL_NODE_CHECKER_TEXTURE}, + {RprUsdMaterialNodeTypeTokens->constant_texture, RPR_MATERIAL_NODE_CONSTANT_TEXTURE}, + {RprUsdMaterialNodeTypeTokens->input_lookup, RPR_MATERIAL_NODE_INPUT_LOOKUP}, + {RprUsdMaterialNodeTypeTokens->blend_value, RPR_MATERIAL_NODE_BLEND_VALUE}, + {RprUsdMaterialNodeTypeTokens->passthrough, RPR_MATERIAL_NODE_PASSTHROUGH}, + {RprUsdMaterialNodeTypeTokens->orennayar, RPR_MATERIAL_NODE_ORENNAYAR}, + {RprUsdMaterialNodeTypeTokens->fresnel_schlick, RPR_MATERIAL_NODE_FRESNEL_SCHLICK}, + {RprUsdMaterialNodeTypeTokens->diffuse_refraction, RPR_MATERIAL_NODE_DIFFUSE_REFRACTION}, + {RprUsdMaterialNodeTypeTokens->bump_map, RPR_MATERIAL_NODE_BUMP_MAP}, + {RprUsdMaterialNodeTypeTokens->volume, RPR_MATERIAL_NODE_VOLUME}, + {RprUsdMaterialNodeTypeTokens->microfacet_anisotropic_reflection, RPR_MATERIAL_NODE_MICROFACET_ANISOTROPIC_REFLECTION}, + {RprUsdMaterialNodeTypeTokens->microfacet_anisotropic_refraction, RPR_MATERIAL_NODE_MICROFACET_ANISOTROPIC_REFRACTION}, + {RprUsdMaterialNodeTypeTokens->twosided, RPR_MATERIAL_NODE_TWOSIDED}, + {RprUsdMaterialNodeTypeTokens->uv_procedural, RPR_MATERIAL_NODE_UV_PROCEDURAL}, + {RprUsdMaterialNodeTypeTokens->microfacet_beckmann, RPR_MATERIAL_NODE_MICROFACET_BECKMANN}, + {RprUsdMaterialNodeTypeTokens->phong, RPR_MATERIAL_NODE_PHONG}, + {RprUsdMaterialNodeTypeTokens->buffer_sampler, RPR_MATERIAL_NODE_BUFFER_SAMPLER}, + {RprUsdMaterialNodeTypeTokens->uv_triplanar, RPR_MATERIAL_NODE_UV_TRIPLANAR}, + {RprUsdMaterialNodeTypeTokens->ao_map, RPR_MATERIAL_NODE_AO_MAP}, + {RprUsdMaterialNodeTypeTokens->user_texture_0, RPR_MATERIAL_NODE_USER_TEXTURE_0}, + {RprUsdMaterialNodeTypeTokens->user_texture_1, RPR_MATERIAL_NODE_USER_TEXTURE_1}, + {RprUsdMaterialNodeTypeTokens->user_texture_2, RPR_MATERIAL_NODE_USER_TEXTURE_2}, + {RprUsdMaterialNodeTypeTokens->user_texture_3, RPR_MATERIAL_NODE_USER_TEXTURE_3}, + {RprUsdMaterialNodeTypeTokens->uberv2, RPR_MATERIAL_NODE_UBERV2}, + {RprUsdMaterialNodeTypeTokens->transform, RPR_MATERIAL_NODE_TRANSFORM}, + {RprUsdMaterialNodeTypeTokens->rgb_to_hsv, RPR_MATERIAL_NODE_RGB_TO_HSV}, + {RprUsdMaterialNodeTypeTokens->hsv_to_rgb, RPR_MATERIAL_NODE_HSV_TO_RGB}, + }; + + auto it = s_mapping.find(id); + if (it == s_mapping.end()) { + if (pedantic) { + TF_CODING_ERROR("Invalid rpr::MaterialNodeType id: %s", id.GetText()); + } + return false; + } + + *out = it->second; + return true; +} + +bool ToRpr(TfToken const& id, rpr::MaterialNodeInput* out, bool pedantic) { + static const std::map s_mapping = { + {RprUsdMaterialNodeInputTokens->color, RPR_MATERIAL_INPUT_COLOR}, + {RprUsdMaterialNodeInputTokens->color0, RPR_MATERIAL_INPUT_COLOR0}, + {RprUsdMaterialNodeInputTokens->color1, RPR_MATERIAL_INPUT_COLOR1}, + {RprUsdMaterialNodeInputTokens->normal, RPR_MATERIAL_INPUT_NORMAL}, + {RprUsdMaterialNodeInputTokens->uv, RPR_MATERIAL_INPUT_UV}, + {RprUsdMaterialNodeInputTokens->data, RPR_MATERIAL_INPUT_DATA}, + {RprUsdMaterialNodeInputTokens->roughness, RPR_MATERIAL_INPUT_ROUGHNESS}, + {RprUsdMaterialNodeInputTokens->ior, RPR_MATERIAL_INPUT_IOR}, + {RprUsdMaterialNodeInputTokens->roughness_x, RPR_MATERIAL_INPUT_ROUGHNESS_X}, + {RprUsdMaterialNodeInputTokens->roughness_y, RPR_MATERIAL_INPUT_ROUGHNESS_Y}, + {RprUsdMaterialNodeInputTokens->rotation, RPR_MATERIAL_INPUT_ROTATION}, + {RprUsdMaterialNodeInputTokens->weight, RPR_MATERIAL_INPUT_WEIGHT}, + {RprUsdMaterialNodeInputTokens->op, RPR_MATERIAL_INPUT_OP}, + {RprUsdMaterialNodeInputTokens->invec, RPR_MATERIAL_INPUT_INVEC}, + {RprUsdMaterialNodeInputTokens->uv_scale, RPR_MATERIAL_INPUT_UV_SCALE}, + {RprUsdMaterialNodeInputTokens->value, RPR_MATERIAL_INPUT_VALUE}, + {RprUsdMaterialNodeInputTokens->reflectance, RPR_MATERIAL_INPUT_REFLECTANCE}, + {RprUsdMaterialNodeInputTokens->scale, RPR_MATERIAL_INPUT_SCALE}, + {RprUsdMaterialNodeInputTokens->scattering, RPR_MATERIAL_INPUT_SCATTERING}, + {RprUsdMaterialNodeInputTokens->absorption, RPR_MATERIAL_INPUT_ABSORBTION}, + {RprUsdMaterialNodeInputTokens->emission, RPR_MATERIAL_INPUT_EMISSION}, + {RprUsdMaterialNodeInputTokens->g, RPR_MATERIAL_INPUT_G}, + {RprUsdMaterialNodeInputTokens->multiscatter, RPR_MATERIAL_INPUT_MULTISCATTER}, + {RprUsdMaterialNodeInputTokens->color2, RPR_MATERIAL_INPUT_COLOR2}, + {RprUsdMaterialNodeInputTokens->color3, RPR_MATERIAL_INPUT_COLOR3}, + {RprUsdMaterialNodeInputTokens->anisotropic, RPR_MATERIAL_INPUT_ANISOTROPIC}, + {RprUsdMaterialNodeInputTokens->frontface, RPR_MATERIAL_INPUT_FRONTFACE}, + {RprUsdMaterialNodeInputTokens->backface, RPR_MATERIAL_INPUT_BACKFACE}, + {RprUsdMaterialNodeInputTokens->origin, RPR_MATERIAL_INPUT_ORIGIN}, + {RprUsdMaterialNodeInputTokens->zaxis, RPR_MATERIAL_INPUT_ZAXIS}, + {RprUsdMaterialNodeInputTokens->xaxis, RPR_MATERIAL_INPUT_XAXIS}, + {RprUsdMaterialNodeInputTokens->threshold, RPR_MATERIAL_INPUT_THRESHOLD}, + {RprUsdMaterialNodeInputTokens->offset, RPR_MATERIAL_INPUT_OFFSET}, + {RprUsdMaterialNodeInputTokens->uv_type, RPR_MATERIAL_INPUT_UV_TYPE}, + {RprUsdMaterialNodeInputTokens->radius, RPR_MATERIAL_INPUT_RADIUS}, + {RprUsdMaterialNodeInputTokens->side, RPR_MATERIAL_INPUT_SIDE}, + {RprUsdMaterialNodeInputTokens->caustics, RPR_MATERIAL_INPUT_CAUSTICS}, + {RprUsdMaterialNodeInputTokens->transmission_color, RPR_MATERIAL_INPUT_TRANSMISSION_COLOR}, + {RprUsdMaterialNodeInputTokens->thickness, RPR_MATERIAL_INPUT_THICKNESS}, + {RprUsdMaterialNodeInputTokens->input0, RPR_MATERIAL_INPUT_0}, + {RprUsdMaterialNodeInputTokens->input1, RPR_MATERIAL_INPUT_1}, + {RprUsdMaterialNodeInputTokens->input2, RPR_MATERIAL_INPUT_2}, + {RprUsdMaterialNodeInputTokens->input3, RPR_MATERIAL_INPUT_3}, + {RprUsdMaterialNodeInputTokens->input4, RPR_MATERIAL_INPUT_4}, + {RprUsdMaterialNodeInputTokens->schlick_approximation, RPR_MATERIAL_INPUT_SCHLICK_APPROXIMATION}, + {RprUsdMaterialNodeInputTokens->applysurface, RPR_MATERIAL_INPUT_APPLYSURFACE}, + {RprUsdMaterialNodeInputTokens->uber_diffuse_color, RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR}, + {RprUsdMaterialNodeInputTokens->uber_diffuse_weight, RPR_MATERIAL_INPUT_UBER_DIFFUSE_WEIGHT}, + {RprUsdMaterialNodeInputTokens->uber_diffuse_roughness, RPR_MATERIAL_INPUT_UBER_DIFFUSE_ROUGHNESS}, + {RprUsdMaterialNodeInputTokens->uber_diffuse_normal, RPR_MATERIAL_INPUT_UBER_DIFFUSE_NORMAL}, + {RprUsdMaterialNodeInputTokens->uber_reflection_color, RPR_MATERIAL_INPUT_UBER_REFLECTION_COLOR}, + {RprUsdMaterialNodeInputTokens->uber_reflection_weight, RPR_MATERIAL_INPUT_UBER_REFLECTION_WEIGHT}, + {RprUsdMaterialNodeInputTokens->uber_reflection_roughness, RPR_MATERIAL_INPUT_UBER_REFLECTION_ROUGHNESS}, + {RprUsdMaterialNodeInputTokens->uber_reflection_anisotropy, RPR_MATERIAL_INPUT_UBER_REFLECTION_ANISOTROPY}, + {RprUsdMaterialNodeInputTokens->uber_reflection_anisotropy_rotation, RPR_MATERIAL_INPUT_UBER_REFLECTION_ANISOTROPY_ROTATION}, + {RprUsdMaterialNodeInputTokens->uber_reflection_mode, RPR_MATERIAL_INPUT_UBER_REFLECTION_MODE}, + {RprUsdMaterialNodeInputTokens->uber_reflection_ior, RPR_MATERIAL_INPUT_UBER_REFLECTION_IOR}, + {RprUsdMaterialNodeInputTokens->uber_reflection_metalness, RPR_MATERIAL_INPUT_UBER_REFLECTION_METALNESS}, + {RprUsdMaterialNodeInputTokens->uber_reflection_normal, RPR_MATERIAL_INPUT_UBER_REFLECTION_NORMAL}, + {RprUsdMaterialNodeInputTokens->uber_refraction_color, RPR_MATERIAL_INPUT_UBER_REFRACTION_COLOR}, + {RprUsdMaterialNodeInputTokens->uber_refraction_weight, RPR_MATERIAL_INPUT_UBER_REFRACTION_WEIGHT}, + {RprUsdMaterialNodeInputTokens->uber_refraction_roughness, RPR_MATERIAL_INPUT_UBER_REFRACTION_ROUGHNESS}, + {RprUsdMaterialNodeInputTokens->uber_refraction_ior, RPR_MATERIAL_INPUT_UBER_REFRACTION_IOR}, + {RprUsdMaterialNodeInputTokens->uber_refraction_normal, RPR_MATERIAL_INPUT_UBER_REFRACTION_NORMAL}, + {RprUsdMaterialNodeInputTokens->uber_refraction_thin_surface, RPR_MATERIAL_INPUT_UBER_REFRACTION_THIN_SURFACE}, + {RprUsdMaterialNodeInputTokens->uber_refraction_absorption_color, RPR_MATERIAL_INPUT_UBER_REFRACTION_ABSORPTION_COLOR}, + {RprUsdMaterialNodeInputTokens->uber_refraction_absorption_distance, RPR_MATERIAL_INPUT_UBER_REFRACTION_ABSORPTION_DISTANCE}, + {RprUsdMaterialNodeInputTokens->uber_refraction_caustics, RPR_MATERIAL_INPUT_UBER_REFRACTION_CAUSTICS}, + {RprUsdMaterialNodeInputTokens->uber_coating_color, RPR_MATERIAL_INPUT_UBER_COATING_COLOR}, + {RprUsdMaterialNodeInputTokens->uber_coating_weight, RPR_MATERIAL_INPUT_UBER_COATING_WEIGHT}, + {RprUsdMaterialNodeInputTokens->uber_coating_roughness, RPR_MATERIAL_INPUT_UBER_COATING_ROUGHNESS}, + {RprUsdMaterialNodeInputTokens->uber_coating_mode, RPR_MATERIAL_INPUT_UBER_COATING_MODE}, + {RprUsdMaterialNodeInputTokens->uber_coating_ior, RPR_MATERIAL_INPUT_UBER_COATING_IOR}, + {RprUsdMaterialNodeInputTokens->uber_coating_metalness, RPR_MATERIAL_INPUT_UBER_COATING_METALNESS}, + {RprUsdMaterialNodeInputTokens->uber_coating_normal, RPR_MATERIAL_INPUT_UBER_COATING_NORMAL}, + {RprUsdMaterialNodeInputTokens->uber_coating_transmission_color, RPR_MATERIAL_INPUT_UBER_COATING_TRANSMISSION_COLOR}, + {RprUsdMaterialNodeInputTokens->uber_coating_thickness, RPR_MATERIAL_INPUT_UBER_COATING_THICKNESS}, + {RprUsdMaterialNodeInputTokens->uber_sheen, RPR_MATERIAL_INPUT_UBER_SHEEN}, + {RprUsdMaterialNodeInputTokens->uber_sheen_tint, RPR_MATERIAL_INPUT_UBER_SHEEN_TINT}, + {RprUsdMaterialNodeInputTokens->uber_sheen_weight, RPR_MATERIAL_INPUT_UBER_SHEEN_WEIGHT}, + {RprUsdMaterialNodeInputTokens->uber_emission_color, RPR_MATERIAL_INPUT_UBER_EMISSION_COLOR}, + {RprUsdMaterialNodeInputTokens->uber_emission_weight, RPR_MATERIAL_INPUT_UBER_EMISSION_WEIGHT}, + {RprUsdMaterialNodeInputTokens->uber_emission_mode, RPR_MATERIAL_INPUT_UBER_EMISSION_MODE}, + {RprUsdMaterialNodeInputTokens->uber_transparency, RPR_MATERIAL_INPUT_UBER_TRANSPARENCY}, + {RprUsdMaterialNodeInputTokens->uber_sss_scatter_color, RPR_MATERIAL_INPUT_UBER_SSS_SCATTER_COLOR}, + {RprUsdMaterialNodeInputTokens->uber_sss_scatter_distance, RPR_MATERIAL_INPUT_UBER_SSS_SCATTER_DISTANCE}, + {RprUsdMaterialNodeInputTokens->uber_sss_scatter_direction, RPR_MATERIAL_INPUT_UBER_SSS_SCATTER_DIRECTION}, + {RprUsdMaterialNodeInputTokens->uber_sss_weight, RPR_MATERIAL_INPUT_UBER_SSS_WEIGHT}, + {RprUsdMaterialNodeInputTokens->uber_sss_multiscatter, RPR_MATERIAL_INPUT_UBER_SSS_MULTISCATTER}, + {RprUsdMaterialNodeInputTokens->uber_backscatter_weight, RPR_MATERIAL_INPUT_UBER_BACKSCATTER_WEIGHT}, + {RprUsdMaterialNodeInputTokens->uber_backscatter_color, RPR_MATERIAL_INPUT_UBER_BACKSCATTER_COLOR}, + {RprUsdMaterialNodeInputTokens->uber_fresnel_schlick_approximation, RPR_MATERIAL_INPUT_UBER_FRESNEL_SCHLICK_APPROXIMATION}, + }; + + auto it = s_mapping.find(id); + if (it == s_mapping.end()) { + if (pedantic) { + TF_CODING_ERROR("Invalid rpr::MaterialNodeInput id: %s", id.GetText()); + } + return false; + } + + *out = it->second; + return true; +} + +bool ToRpr(TfToken const& id, uint32_t* out, bool pedantic) { + static const std::map s_mapping = { + {RprUsdMaterialInputModeTokens->pbr, RPR_UBER_MATERIAL_IOR_MODE_PBR}, + {RprUsdMaterialInputModeTokens->metalness, RPR_UBER_MATERIAL_IOR_MODE_METALNESS}, + {RprUsdMaterialInputModeTokens->singlesided, RPR_UBER_MATERIAL_EMISSION_MODE_SINGLESIDED}, + {RprUsdMaterialInputModeTokens->doublesided, RPR_UBER_MATERIAL_EMISSION_MODE_DOUBLESIDED}, + {RprUsdMaterialInputModeTokens->uv, RPR_MATERIAL_NODE_LOOKUP_UV}, + {RprUsdMaterialInputModeTokens->normal, RPR_MATERIAL_NODE_LOOKUP_N}, + {RprUsdMaterialInputModeTokens->position, RPR_MATERIAL_NODE_LOOKUP_P}, + {RprUsdMaterialInputModeTokens->invec, RPR_MATERIAL_NODE_LOOKUP_INVEC}, + {RprUsdMaterialInputModeTokens->outvec, RPR_MATERIAL_NODE_LOOKUP_OUTVEC}, + {RprUsdMaterialInputModeTokens->localPosition, RPR_MATERIAL_NODE_LOOKUP_P_LOCAL}, + {RprUsdMaterialInputModeTokens->shapeRandomColor, RPR_MATERIAL_NODE_LOOKUP_SHAPE_RANDOM_COLOR}, + {RprUsdMaterialInputModeTokens->objectId, RPR_MATERIAL_NODE_LOOKUP_OBJECT_ID}, + {RprUsdMaterialInputModeTokens->planar, RPR_MATERIAL_NODE_UVTYPE_PLANAR}, + {RprUsdMaterialInputModeTokens->cylindical, RPR_MATERIAL_NODE_UVTYPE_CYLINDICAL}, + {RprUsdMaterialInputModeTokens->spherical, RPR_MATERIAL_NODE_UVTYPE_SPHERICAL}, + {RprUsdMaterialInputModeTokens->project, RPR_MATERIAL_NODE_UVTYPE_PROJECT}, + }; + + auto it = s_mapping.find(id); + if (it == s_mapping.end()) { + if (pedantic) { + TF_CODING_ERROR("Invalid rpr mode id: %s", id.GetText()); + } + return false; + } + + *out = it->second; + return true; +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialMappings.h b/pxr/imaging/rprUsd/materialMappings.h new file mode 100644 index 000000000..0bf03a1f8 --- /dev/null +++ b/pxr/imaging/rprUsd/materialMappings.h @@ -0,0 +1,196 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_MATERIAL_MAPPINGS_H +#define RPRUSD_MATERIAL_MAPPINGS_H + +#include "pxr/base/tf/staticTokens.h" + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +/// \p pedantic controls whether function should trigger TF errors +bool ToRpr(TfToken const& id, rpr::MaterialNodeType* out, bool pedantic = true); +bool ToRpr(TfToken const& id, rpr::MaterialNodeInput* out, bool pedantic = true); +bool ToRpr(TfToken const& id, uint32_t* out, bool pedantic = true); + +#define RPRUSD_MATERIAL_NODE_INPUT_TOKENS \ + (color) \ + (color0) \ + (color1) \ + (normal) \ + (uv) \ + (data) \ + (roughness) \ + (ior) \ + (roughness_x) \ + (roughness_y) \ + (rotation) \ + (weight) \ + (op) \ + (invec) \ + (uv_scale) \ + (value) \ + (reflectance) \ + (scale) \ + (scattering) \ + (absorption) \ + (emission) \ + (g) \ + (multiscatter) \ + (color2) \ + (color3) \ + (anisotropic) \ + (frontface) \ + (backface) \ + (origin) \ + (zaxis) \ + (xaxis) \ + (threshold) \ + (offset) \ + (uv_type) \ + (radius) \ + (side) \ + (caustics) \ + (transmission_color) \ + (thickness) \ + ((input0, "0")) \ + ((input1, "1")) \ + ((input2, "2")) \ + ((input3, "3")) \ + ((input4, "4")) \ + (schlick_approximation) \ + (applysurface) \ + (uber_diffuse_color) \ + (uber_diffuse_weight) \ + (uber_diffuse_roughness) \ + (uber_diffuse_normal) \ + (uber_reflection_color) \ + (uber_reflection_weight) \ + (uber_reflection_roughness) \ + (uber_reflection_anisotropy) \ + (uber_reflection_anisotropy_rotation) \ + (uber_reflection_mode) \ + (uber_reflection_ior) \ + (uber_reflection_metalness) \ + (uber_reflection_normal) \ + (uber_refraction_color) \ + (uber_refraction_weight) \ + (uber_refraction_roughness) \ + (uber_refraction_ior) \ + (uber_refraction_normal) \ + (uber_refraction_thin_surface) \ + (uber_refraction_absorption_color) \ + (uber_refraction_absorption_distance) \ + (uber_refraction_caustics) \ + (uber_coating_color) \ + (uber_coating_weight) \ + (uber_coating_roughness) \ + (uber_coating_mode) \ + (uber_coating_ior) \ + (uber_coating_metalness) \ + (uber_coating_normal) \ + (uber_coating_transmission_color) \ + (uber_coating_thickness) \ + (uber_sheen) \ + (uber_sheen_tint) \ + (uber_sheen_weight) \ + (uber_emission_color) \ + (uber_emission_weight) \ + (uber_emission_mode) \ + (uber_transparency) \ + (uber_sss_scatter_color) \ + (uber_sss_scatter_distance) \ + (uber_sss_scatter_direction) \ + (uber_sss_weight) \ + (uber_sss_multiscatter) \ + (uber_backscatter_weight) \ + (uber_backscatter_color) \ + (uber_fresnel_schlick_approximation) + +#define RPRUSD_MATERIAL_NODE_TYPE_TOKENS \ + (diffuse) \ + (microfacet) \ + (reflection) \ + (refraction) \ + (microfacet_refraction) \ + (transparent) \ + (emissive) \ + (ward) \ + (add) \ + (blend) \ + (arithmetic) \ + (fresnel) \ + (normal_map) \ + (image_texture) \ + (noise2d_texture) \ + (dot_texture) \ + (gradient_texture) \ + (checker_texture) \ + (constant_texture) \ + (input_lookup) \ + (blend_value) \ + (passthrough) \ + (orennayar) \ + (fresnel_schlick) \ + (diffuse_refraction) \ + (bump_map) \ + (volume) \ + (microfacet_anisotropic_reflection) \ + (microfacet_anisotropic_refraction) \ + (twosided) \ + (uv_procedural) \ + (microfacet_beckmann) \ + (phong) \ + (buffer_sampler) \ + (uv_triplanar) \ + (ao_map) \ + (user_texture_0) \ + (user_texture_1) \ + (user_texture_2) \ + (user_texture_3) \ + (uberv2) \ + (transform) \ + (rgb_to_hsv) \ + (hsv_to_rgb) + +#define RPRUSD_MATERIAL_INPUT_MODE_TOKENS \ + /* IOR mode */ \ + ((pbr, "PBR")) \ + ((metalness, "Metalness")) \ + /* Emission mode */ \ + ((singlesided, "Singlesided")) \ + ((doublesided, "Doublesided")) \ + /* Lookup value */ \ + ((uv, "UV")) \ + ((normal, "Normal")) \ + ((position, "Position")) \ + ((invec, "Incoming ray direction")) \ + ((outvec, "Outgoing ray direction")) \ + ((localPosition, "Local Position")) \ + ((shapeRandomColor, "Shape Random Color")) \ + ((objectId, "Object Id")) \ + /* UV Type */ \ + ((planar, "Flat Plane")) \ + ((cylindical, "Cylinder (in xy direction)")) \ + ((spherical, "Spherical")) \ + ((project, "Projected from a camera position")) \ + +TF_DECLARE_PUBLIC_TOKENS(RprUsdMaterialNodeInputTokens, RPRUSD_MATERIAL_NODE_INPUT_TOKENS); +TF_DECLARE_PUBLIC_TOKENS(RprUsdMaterialNodeTypeTokens, RPRUSD_MATERIAL_NODE_TYPE_TOKENS); +TF_DECLARE_PUBLIC_TOKENS(RprUsdMaterialInputModeTokens, RPRUSD_MATERIAL_INPUT_MODE_TOKENS); + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_MATERIAL_MAPPINGS_H diff --git a/pxr/imaging/rprUsd/materialNodes/houdiniPrincipledShaderNode.cpp b/pxr/imaging/rprUsd/materialNodes/houdiniPrincipledShaderNode.cpp new file mode 100644 index 000000000..4d9ef9b8a --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/houdiniPrincipledShaderNode.cpp @@ -0,0 +1,561 @@ +/************************************************************************ +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 "houdiniPrincipledShaderNode.h" +#include "rpr/arithmeticNode.h" +#include "usdNode.h" + +#include "pxr/imaging/rprUsd/materialRegistry.h" +#include "pxr/imaging/rprUsd/materialMappings.h" +#include "pxr/imaging/rprUsd/materialHelpers.h" +#include "pxr/base/tf/staticTokens.h" + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEFINE_PRIVATE_TOKENS(HoudiniPrincipledShaderTokens, + (basecolor) + (albedomult) + (ior) + ((roughness, "rough")) + ((anisotropy, "aniso")) + ((anisotropyDirection, "anisodir")) + (metallic) + ((reflectivity, "reflect")) + ((reflectTint, "reflecttint")) + (coat) + ((coatRoughness, "coatrough")) + (transparency) + ((transmissionColor, "transcolor")) + ((transmissionDistance, "transdist")) + ((subsurface, "sss")) + ((subsurfaceDistance, "sssdist")) + ((subsurfaceModel, "sssmodel")) + ((subsurfaceColor, "ssscolor")) + ((subsurfacePhase, "sssphase")) + (sheen) + ((sheenTint, "sheentint")) + ((emissionColor, "emitcolor")) + ((emissionIntensity, "emitint")) + ((opacity, "opac")) + ((opacityColor, "opaccolor")) + (baseNormal) + ((baseNormalScale, "baseNormal_scale")) + (coatNormal) + ((coatNormalScale, "coatNormal_scale")) + ((baseNormalEnable, "baseBumpAndNormal_enable")) + ((baseNormalType, "baseBumpAndNormal_type")) + (separateCoatNormals) + ((doubleSided, "frontface")) + ((displacementEnable, "dispTex_enable")) + ((displacementTexture, "dispTex_texture")) + ((displacementOffset, "dispTex_offset")) + ((displacementScale, "dispTex_scale")) + ((displacementColorSpace, "dispTex_colorSpace")) + ((displacementChannel, "dispTex_channel")) + ((displacementWrap, "dispTex_wrap")) + ((displacementType, "dispTex_type")) + ((infoSourceAsset, "info:sourceAsset")) + ((infoImplementationSource, "info:implementationSource")) + (sourceAsset) + (karma) +); + +namespace { + +std::map g_houdiniPrincipledShaderParameterDefaultValues = { + {HoudiniPrincipledShaderTokens->basecolor, VtValue(0.2f)}, + {HoudiniPrincipledShaderTokens->ior, VtValue(1.5f)}, + {HoudiniPrincipledShaderTokens->roughness, VtValue(0.3f)}, + {HoudiniPrincipledShaderTokens->anisotropy, VtValue(0.0f)}, + {HoudiniPrincipledShaderTokens->anisotropyDirection, VtValue(0.0f)}, + {HoudiniPrincipledShaderTokens->metallic, VtValue(0.0f)}, + {HoudiniPrincipledShaderTokens->reflectivity, VtValue(1.0f)}, + {HoudiniPrincipledShaderTokens->reflectTint, VtValue(0.0f)}, + {HoudiniPrincipledShaderTokens->coat, VtValue(0.0f)}, + {HoudiniPrincipledShaderTokens->coatRoughness, VtValue(0.0f)}, + {HoudiniPrincipledShaderTokens->transparency, VtValue(0.0f)}, + {HoudiniPrincipledShaderTokens->transmissionColor, VtValue(1.0f)}, + {HoudiniPrincipledShaderTokens->transmissionDistance, VtValue(0.1f)}, + {HoudiniPrincipledShaderTokens->subsurface, VtValue(0.0f)}, + {HoudiniPrincipledShaderTokens->subsurfaceDistance, VtValue(0.1f)}, + {HoudiniPrincipledShaderTokens->subsurfaceColor, VtValue(1.0f)}, + {HoudiniPrincipledShaderTokens->sheen, VtValue(0.0f)}, + {HoudiniPrincipledShaderTokens->sheenTint, VtValue(0.0f)}, + {HoudiniPrincipledShaderTokens->emissionColor, VtValue(0.0f)}, + {HoudiniPrincipledShaderTokens->opacityColor, VtValue(1.0f)}, +}; + +template +T GetParameter(TfToken const& name, std::map const& parameters, T defaultValue = T()) { + auto parameterIt = parameters.find(name); + if (parameterIt != parameters.end() && + parameterIt->second.IsHolding()) { + return parameterIt->second.UncheckedGet(); + } + + return defaultValue; +} + +TfToken ToUsdUVTextureWrapMode(std::string const& mode) { + if (mode == "streak") { + return RprUsd_UsdUVTextureTokens->clamp; + } else if (mode == "decal") { + return RprUsd_UsdUVTextureTokens->black; + } else { + return RprUsd_UsdUVTextureTokens->repeat; + } +} + +TfToken ToUsdUVTextureOutputId(int channel) { + if (channel == 1) { + return RprUsd_UsdUVTextureTokens->r; + } else if (channel == 2) { + return RprUsd_UsdUVTextureTokens->g; + } else if (channel == 3) { + return RprUsd_UsdUVTextureTokens->b; + } else { + return TfToken(); + } +} + +} // namespace anonymous + +RprUsd_HoudiniPrincipledNode::RprUsd_HoudiniPrincipledNode( + RprUsd_MaterialBuilderContext* ctx, + std::map const& params, + std::map const* dispParamsPtr) + : RprUsd_BaseRuntimeNode(RPR_MATERIAL_NODE_UBERV2, ctx) { + + // XXX: unused parameters: + // reflectTint + // reflectivity + + bool useBaseColorTextureAlpha = false; + + auto getTextureValue = [&](TfToken const& baseParameter, bool forceLinearSpace = false) -> VtValue { + // Each parameter (e.g. basecolor) may have set of properties in the form: + // paramName_propertyName (e.g. basecolor_texture) + // but parameter itself may be missing in input params + + SdfAssetPath fileAsset; + std::string wrapMode; + float scale = 1.0f; + TfToken textureOutputId; + + bool useTexture = false; + for (auto it = params.lower_bound(baseParameter); it != params.end(); ++it) { + // check that this property corresponds to our base parameter + if (it->first.GetString().compare(0, baseParameter.size(), baseParameter.GetText())) { + break; + } + + // skip paramName + auto propertyName = it->first.GetText() + baseParameter.size(); + + if (*propertyName != '_') { + continue; + } + ++propertyName; + + if (std::strncmp(propertyName, "texture", sizeof("texture") - 1) == 0) { + auto texturePropertyName = propertyName + (sizeof("texture") - 1); + if (*texturePropertyName == '\0') { + if (it->second.IsHolding()) { + auto& assetPath = it->second.UncheckedGet(); + + std::string const* filepath; + if (assetPath.GetResolvedPath().empty()) { + filepath = &assetPath.GetAssetPath(); + } else { + filepath = &assetPath.GetResolvedPath(); + } + + if (filepath->empty()) { + return VtValue(); + } + + fileAsset = assetPath; + } + } else if (std::strcmp(texturePropertyName, "Intensity") == 0) { + if (it->second.IsHolding()) { + scale = it->second.UncheckedGet(); + } + } else if (std::strcmp(texturePropertyName, "ColorSpace") == 0) { + if (it->second.IsHolding()) { + forceLinearSpace = it->second.UncheckedGet() == "linear"; + } + } else if (std::strcmp(texturePropertyName, "Wrap") == 0) { + if (it->second.IsHolding()) { + wrapMode = it->second.UncheckedGet(); + } + } + } else if (std::strncmp(propertyName, "useTexture", sizeof("useTexture") - 1) == 0) { + auto useTexturePropertyName = propertyName + sizeof("useTexture") - 1; + if (*useTexturePropertyName == '\0') { + if (it->second.IsHolding()) { + useTexture = static_cast(it->second.UncheckedGet()); + if (!useTexture) { + return VtValue(); + } + } + } else if (std::strcmp(useTexturePropertyName, "Alpha") == 0) { + if (it->second.IsHolding() && + it->second.UncheckedGet() == 1) { + useBaseColorTextureAlpha = true; + } + } + } else if (std::strcmp(propertyName, "monoChannel") == 0) { + if (it->second.IsHolding()) { + textureOutputId = ToUsdUVTextureOutputId(it->second.UncheckedGet()); + } + } + } + + if (baseParameter == HoudiniPrincipledShaderTokens->baseNormal) { + // baseNormal's texture enabled by baseBumpAndNormal_enable parameter unlike all other textures by *_useTexture + useTexture = true; + } + + if (!useTexture) { + return VtValue(); + } + + RprUsd_MaterialNode** uvTexture = nullptr; + + if (baseParameter == HoudiniPrincipledShaderTokens->basecolor || + baseParameter == HoudiniPrincipledShaderTokens->transmissionColor || + baseParameter == HoudiniPrincipledShaderTokens->subsurfaceColor || + baseParameter == HoudiniPrincipledShaderTokens->baseNormal || + baseParameter == HoudiniPrincipledShaderTokens->coatNormal) { + if (textureOutputId.IsEmpty()) { + textureOutputId = RprUsd_UsdUVTextureTokens->rgba; + } + if (baseParameter == HoudiniPrincipledShaderTokens->basecolor) { + uvTexture = &m_baseColorNode; + } + } + + return GetTextureOutput(fileAsset, wrapMode, scale, 0.0f, forceLinearSpace, textureOutputId, uvTexture); + }; + + struct ParameterValue { + VtValue value; + bool isDefault; + + bool IsAuthored() { + return !isDefault && !value.IsEmpty(); + } + }; + auto getParameterValue = [&](TfToken const& paramName) -> ParameterValue { + ParameterValue param; + param.isDefault = false; + + param.value = getTextureValue(paramName); + + // Check for plain parameter + if (param.value.IsEmpty()) { + auto parameterIt = params.find(paramName); + if (parameterIt != params.end()) { + param.value = parameterIt->second; + } + } + + // Check for default parameter + if (param.value.IsEmpty()) { + auto parameterIt = g_houdiniPrincipledShaderParameterDefaultValues.find(paramName); + if (parameterIt != g_houdiniPrincipledShaderParameterDefaultValues.end()) { + param.value = parameterIt->second; + param.isDefault = true; + } + } + + return param; + }; + + auto setInputs = [this](VtValue const& value, std::vector const& rprInputs) { + for (auto rprInput : rprInputs) { + SetRprInput(m_rprNode.get(), rprInput, value); + } + }; + + auto populateParameter = [&](TfToken const& paramName, std::vector const& rprInputs) { + auto param = getParameterValue(paramName); + if (!param.value.IsEmpty()) { + setInputs(param.value, rprInputs); + } + }; + + auto basecolorParam = getParameterValue(HoudiniPrincipledShaderTokens->basecolor); + if (!basecolorParam.value.IsEmpty()) { + auto albedoMultiplyNode = AddAuxiliaryNode(RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_MUL, m_ctx)); + albedoMultiplyNode->SetInput(0, VtValue(GetParameter(HoudiniPrincipledShaderTokens->albedomult, params, 1.0f))); + albedoMultiplyNode->SetInput(1, basecolorParam.value); + setInputs(albedoMultiplyNode->GetOutput(), {RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR, RPR_MATERIAL_INPUT_UBER_REFLECTION_COLOR, RPR_MATERIAL_INPUT_UBER_COATING_COLOR, RPR_MATERIAL_INPUT_UBER_COATING_TRANSMISSION_COLOR, RPR_MATERIAL_INPUT_UBER_SHEEN}); + } + + populateParameter(HoudiniPrincipledShaderTokens->ior, {RPR_MATERIAL_INPUT_UBER_REFRACTION_IOR, RPR_MATERIAL_INPUT_UBER_COATING_IOR}); + populateParameter(HoudiniPrincipledShaderTokens->roughness, {RPR_MATERIAL_INPUT_UBER_DIFFUSE_ROUGHNESS, RPR_MATERIAL_INPUT_UBER_REFLECTION_ROUGHNESS, RPR_MATERIAL_INPUT_UBER_REFRACTION_ROUGHNESS}); + + populateParameter(HoudiniPrincipledShaderTokens->anisotropy, {RPR_MATERIAL_INPUT_UBER_REFLECTION_ANISOTROPY}); + populateParameter(HoudiniPrincipledShaderTokens->anisotropyDirection, {RPR_MATERIAL_INPUT_UBER_REFLECTION_ANISOTROPY_ROTATION}); + + populateParameter(HoudiniPrincipledShaderTokens->coatRoughness, {RPR_MATERIAL_INPUT_UBER_COATING_ROUGHNESS}); + auto coatParam = getParameterValue(HoudiniPrincipledShaderTokens->coat); + if (!coatParam.value.IsEmpty()) { + auto coatingWeightNode = AddAuxiliaryNode(RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_GREATER, m_ctx)); + coatingWeightNode->SetInput(0, coatParam.value); + coatingWeightNode->SetInput(1, VtValue(0.0f)); + + SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_COATING_WEIGHT, coatingWeightNode->GetOutput()); + SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_COATING_THICKNESS, coatParam.value); + } + + auto subsurfaceParam = getParameterValue(HoudiniPrincipledShaderTokens->subsurface); + if (!subsurfaceParam.value.IsEmpty()) { + SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_SSS_WEIGHT, subsurfaceParam.value); + SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_BACKSCATTER_WEIGHT, subsurfaceParam.value); + } + populateParameter(HoudiniPrincipledShaderTokens->subsurfaceDistance, {RPR_MATERIAL_INPUT_UBER_SSS_SCATTER_DISTANCE}); + populateParameter(HoudiniPrincipledShaderTokens->subsurfaceColor, {RPR_MATERIAL_INPUT_UBER_SSS_SCATTER_COLOR, RPR_MATERIAL_INPUT_UBER_BACKSCATTER_COLOR}); + + auto subsurfaceModel = GetParameter(HoudiniPrincipledShaderTokens->subsurfaceModel, params, std::string("full")); + if (subsurfaceModel == "full") { + RPR_ERROR_CHECK(m_rprNode->SetInput(RPR_MATERIAL_INPUT_UBER_SSS_MULTISCATTER, 1u), "Failed to set sss multiscatter input"); + RPR_ERROR_CHECK(m_rprNode->SetInput(RPR_MATERIAL_INPUT_UBER_SSS_SCATTER_DIRECTION, 0, 0, 0, 0), "Failed to set sss multiscatter input"); + } else { + RPR_ERROR_CHECK(m_rprNode->SetInput(RPR_MATERIAL_INPUT_UBER_SSS_MULTISCATTER, 0u), "Failed to set sss multiscatter input"); + populateParameter(HoudiniPrincipledShaderTokens->subsurfacePhase, {RPR_MATERIAL_INPUT_UBER_SSS_SCATTER_DIRECTION}); + } + + populateParameter(HoudiniPrincipledShaderTokens->sheen, {RPR_MATERIAL_INPUT_UBER_SHEEN_WEIGHT}); + populateParameter(HoudiniPrincipledShaderTokens->sheenTint, {RPR_MATERIAL_INPUT_UBER_SHEEN_TINT}); + + auto emissionColorParam = getParameterValue(HoudiniPrincipledShaderTokens->emissionColor); + if (!emissionColorParam.value.IsEmpty()) { + auto emissiveWeightNode = AddAuxiliaryNode(RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_GREATER, m_ctx)); + emissiveWeightNode->SetInput(0, emissionColorParam.value); + emissiveWeightNode->SetInput(1, VtValue(0.0f)); + SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_EMISSION_WEIGHT, emissiveWeightNode->GetOutput()); + + auto emissiveIntensityNode = AddAuxiliaryNode(RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_MUL, m_ctx)); + emissiveIntensityNode->SetInput(0, emissionColorParam.value); + emissiveIntensityNode->SetInput(1, VtValue(GetParameter(HoudiniPrincipledShaderTokens->emissionIntensity, params, 1.0f))); + SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_EMISSION_COLOR, emissiveIntensityNode->GetOutput()); + } + + m_rprNode->SetInput(RPR_MATERIAL_INPUT_UBER_REFLECTION_WEIGHT, 1, 1, 1, 1); + + if (GetParameter(HoudiniPrincipledShaderTokens->baseNormalEnable, params, 0)) { + std::vector rprInputs = {RPR_MATERIAL_INPUT_UBER_DIFFUSE_NORMAL, RPR_MATERIAL_INPUT_UBER_REFLECTION_NORMAL, RPR_MATERIAL_INPUT_UBER_REFRACTION_NORMAL}; + + if (GetParameter(HoudiniPrincipledShaderTokens->separateCoatNormals, params, 0)) { + auto coatNormal = getTextureValue(HoudiniPrincipledShaderTokens->coatNormal, true); + if (!coatNormal.IsEmpty()) { + auto normalMapNode = AddAuxiliaryNode(std::make_unique(RPR_MATERIAL_NODE_NORMAL_MAP, m_ctx)); + normalMapNode->SetInput(RprUsdMaterialNodeInputTokens->color, coatNormal); + normalMapNode->SetInput(RprUsdMaterialNodeInputTokens->scale, VtValue(GetParameter(HoudiniPrincipledShaderTokens->coatNormalScale, params, 1.0f))); + SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_COATING_NORMAL, normalMapNode->GetOutput(TfToken())); + } + } else { + rprInputs.push_back(RPR_MATERIAL_INPUT_UBER_COATING_NORMAL); + } + + auto baseNormal = getTextureValue(HoudiniPrincipledShaderTokens->baseNormal, true); + if (!baseNormal.IsEmpty()) { + auto normalMapNode = AddAuxiliaryNode(std::make_unique(RPR_MATERIAL_NODE_NORMAL_MAP, m_ctx)); + normalMapNode->SetInput(RprUsdMaterialNodeInputTokens->color, baseNormal); + normalMapNode->SetInput(RprUsdMaterialNodeInputTokens->scale, VtValue(GetParameter(HoudiniPrincipledShaderTokens->baseNormalScale, params, 1.0f))); + auto normalMap = normalMapNode->GetOutput(TfToken()); + for (auto rprInput : rprInputs) { + SetRprInput(m_rprNode.get(), rprInput, normalMap); + } + } + } + + bool hasTransparency = false; + + auto transparencyParam = getParameterValue(HoudiniPrincipledShaderTokens->transparency); + if (!transparencyParam.value.IsEmpty()) { + if (transparencyParam.IsAuthored()) { + hasTransparency = true; + } + + RPR_ERROR_CHECK(m_rprNode->SetInput(RPR_MATERIAL_INPUT_UBER_REFRACTION_CAUSTICS, 1), "Failed to set caustics input"); + SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_REFRACTION_WEIGHT, transparencyParam.value); + + auto diffuseWeightNode = AddAuxiliaryNode(RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_SUB, m_ctx)); + diffuseWeightNode->SetInput(0, VtValue(1.0f)); + diffuseWeightNode->SetInput(1, transparencyParam.value); + SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_DIFFUSE_WEIGHT, diffuseWeightNode->GetOutput()); + } + populateParameter(HoudiniPrincipledShaderTokens->transmissionColor, {RPR_MATERIAL_INPUT_UBER_REFRACTION_COLOR, RPR_MATERIAL_INPUT_UBER_REFRACTION_ABSORPTION_COLOR}); + populateParameter(HoudiniPrincipledShaderTokens->transmissionDistance, {RPR_MATERIAL_INPUT_UBER_REFRACTION_ABSORPTION_DISTANCE}); + + if (useBaseColorTextureAlpha) { + VtValue opacity; + if (m_baseColorNode) { + opacity = m_baseColorNode->GetOutput(RprUsd_UsdUVTextureTokens->a); + hasTransparency = true; + } else { + auto opacityParam = getParameterValue(HoudiniPrincipledShaderTokens->opacityColor); + if (opacityParam.IsAuthored()) { + hasTransparency = true; + } + opacity = opacityParam.value; + } + + if (!opacity.IsEmpty()) { + auto transparencyNode = AddAuxiliaryNode(RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_MUL, m_ctx)); + transparencyNode->SetInput(0, opacity); + transparencyNode->SetInput(1, VtValue(GetParameter(HoudiniPrincipledShaderTokens->opacity, params, 1.0f))); + auto transparency = transparencyNode->GetOutput(); + + transparencyNode = AddAuxiliaryNode(RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_SUB, m_ctx)); + transparencyNode->SetInput(0, VtValue(1.0f)); + transparencyNode->SetInput(1, transparency); + transparency = transparencyNode->GetOutput(); + + if (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_TRANSPARENCY, transparency) == RPR_SUCCESS) { + hasTransparency = true; + } + } + } + + auto iorMode = RPR_UBER_MATERIAL_IOR_MODE_PBR; + if (!hasTransparency) { + iorMode = RPR_UBER_MATERIAL_IOR_MODE_METALNESS; + populateParameter(HoudiniPrincipledShaderTokens->metallic, {RPR_MATERIAL_INPUT_UBER_REFLECTION_METALNESS, RPR_MATERIAL_INPUT_UBER_COATING_METALNESS}); + } + + RPR_ERROR_CHECK(m_rprNode->SetInput(RPR_MATERIAL_INPUT_UBER_REFLECTION_MODE, iorMode), "Failed to set ior mode input"); + RPR_ERROR_CHECK(m_rprNode->SetInput(RPR_MATERIAL_INPUT_UBER_COATING_MODE, iorMode), "Failed to set ior mode input"); + + if (dispParamsPtr) { + auto& dispParams = *dispParamsPtr; + if (GetParameter(HoudiniPrincipledShaderTokens->displacementEnable, dispParams, 0)) { + auto dispType = GetParameter(HoudiniPrincipledShaderTokens->displacementType, dispParams); + if (dispType == "vectordisp") { + TF_RUNTIME_ERROR("Vector displacement unsupported"); + } else { + auto dispTexturePath = GetParameter(HoudiniPrincipledShaderTokens->displacementTexture, dispParams); + if (!dispTexturePath.GetResolvedPath().empty()) { + m_displacementOutput = GetTextureOutput( + dispTexturePath, + GetParameter(HoudiniPrincipledShaderTokens->displacementWrap, dispParams), + GetParameter(HoudiniPrincipledShaderTokens->displacementScale, dispParams, 0.05f), + GetParameter(HoudiniPrincipledShaderTokens->displacementOffset, dispParams, -0.5f), + GetParameter(HoudiniPrincipledShaderTokens->displacementColorSpace, dispParams, std::string("linear")) == "linear", + ToUsdUVTextureOutputId(GetParameter(HoudiniPrincipledShaderTokens->displacementChannel, dispParams, 0))); + } + } + } + } +} + +VtValue RprUsd_HoudiniPrincipledNode::GetOutput(TfToken const& outputId) { + if (outputId == HdMaterialTerminalTokens->displacement) { + return m_displacementOutput; + } else { + return RprUsd_BaseRuntimeNode::GetOutput(outputId); + } +} + +VtValue RprUsd_HoudiniPrincipledNode::GetTextureOutput( + SdfAssetPath const& path, + std::string const& wrapMode, + float scale, float bias, + bool forceLinearSpace, + TfToken const& outputId, + RprUsd_MaterialNode** out_uvTextureNode) { + if (path.GetResolvedPath().empty() && + path.GetAssetPath().empty()) { + return VtValue(); + } + + std::map uvTextureParams = { + {RprUsd_UsdUVTextureTokens->file, VtValue(path)}, + {RprUsd_UsdUVTextureTokens->wrapS, VtValue(ToUsdUVTextureWrapMode(wrapMode))}, + }; + + if (forceLinearSpace) { + uvTextureParams.emplace(RprUsd_UsdUVTextureTokens->sourceColorSpace, RprUsd_UsdUVTextureTokens->srgblinear); + } + + if (scale != 1.0f) { + uvTextureParams.emplace(RprUsd_UsdUVTextureTokens->scale, VtValue(GfVec4f(scale))); + } + + if (bias != 0.0f) { + uvTextureParams.emplace(RprUsd_UsdUVTextureTokens->bias, VtValue(GfVec4f(bias))); + } + + RprUsd_MaterialNode* uvTexture; + try { + uvTexture = AddAuxiliaryNode(std::make_unique(m_ctx, uvTextureParams)); + } catch (RprUsd_NodeError& e) { + TF_RUNTIME_ERROR("Failed to create texture node: %s", e.what()); + return VtValue(); + } + + if (out_uvTextureNode) { + *out_uvTextureNode = uvTexture; + } + + if (outputId.IsEmpty()) { + // Output luminance + auto output = uvTexture->GetOutput(RprUsd_UsdUVTextureTokens->rgba); + + auto luminanceNode = AddAuxiliaryNode(RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_DOT3, m_ctx)); + luminanceNode->SetInput(0, output); + luminanceNode->SetInput(1, VtValue(GfVec4f(0.2126, 0.7152, 0.0722, 0.0))); + return luminanceNode->GetOutput(); + } else { + return uvTexture->GetOutput(outputId); + } +} + +bool IsHoudiniPrincipledShaderHydraNode(HdSceneDelegate* delegate, SdfPath const& nodePath, bool* isSurface) { + // Houdini's principled shader is hidden under `karma` namespace. + // If HdRprRenderDelegate uses some other network selector that is not + // a `karma` one then Hydra will never feed us a definition of the principled shader. + if (RprUsdMaterialRegistry::GetInstance().GetMaterialNetworkSelector() != HoudiniPrincipledShaderTokens->karma) { + return false; + } + + auto implementationSource = delegate->Get(nodePath, HoudiniPrincipledShaderTokens->infoImplementationSource); + if (implementationSource.IsHolding() && + implementationSource.UncheckedGet() == HoudiniPrincipledShaderTokens->sourceAsset) { + auto nodeAsset = delegate->Get(nodePath, HoudiniPrincipledShaderTokens->infoSourceAsset); + if (nodeAsset.IsHolding()) { + auto& asset = nodeAsset.UncheckedGet(); + if (!asset.GetAssetPath().empty()) { + std::string principledShaderDef = "opdef:/Vop/principledshader::2.0?"; + if (asset.GetAssetPath().compare(0, principledShaderDef.size(), principledShaderDef.c_str()) == 0) { + auto vexCodeType = asset.GetAssetPath().substr(principledShaderDef.size()); + if (vexCodeType == "SurfaceVexCode") { + *isSurface = true; + } else if (vexCodeType == "DisplacementVexCode") { + *isSurface = false; + } else { + return false; + } + return true; + } + } + } + } + + return false; +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialNodes/houdiniPrincipledShaderNode.h b/pxr/imaging/rprUsd/materialNodes/houdiniPrincipledShaderNode.h new file mode 100644 index 000000000..6542dd0ff --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/houdiniPrincipledShaderNode.h @@ -0,0 +1,81 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_MATERIAL_NODES_HOUDINI_PRINCIPLED_SHADER_NODE_H +#define RPRUSD_MATERIAL_NODES_HOUDINI_PRINCIPLED_SHADER_NODE_H + +#include "rpr/baseNode.h" + +#include "pxr/usd/sdf/assetPath.h" + +PXR_NAMESPACE_OPEN_SCOPE + +/// \class RprUsd_HoudiniPrincipledNode +/// +/// The node that implements Houdini's principled node +/// (https://www.sidefx.com/docs/houdini/nodes/vop/principledshader.html) +/// +/// Before expecting this node to work you need to make sure that +/// `RPRUSD_MATERIAL_NETWORK_SELECTOR` environment variable is set to `karma`. +/// If it's not set to `karma`, Hydra will ignore houdini's principled node and +/// its data will not be present in `HdMaterialNetwork` passed to hdRpr. +/// +/// Support of the principled shader is limited to the node without any connected nodes. +/// This is due to the fact that the principled shader node input parameters listed as +/// HdMaterialNode::parameters only if there are no connected nodes. If principled shader +/// node has any node connected to it, Houdini will automatically convert its +/// implementation to VEX code that is not supported. +/// +class RprUsd_HoudiniPrincipledNode : public RprUsd_BaseRuntimeNode { +public: + RprUsd_HoudiniPrincipledNode( + RprUsd_MaterialBuilderContext* ctx, + std::map const& surfaceParameters, + std::map const* displacementParameters); + ~RprUsd_HoudiniPrincipledNode() override = default; + + VtValue GetOutput(TfToken const& outputId) override; + + bool SetInput( + TfToken const& inputId, + VtValue const& value) override { + return false; + } + +private: + template + T* AddAuxiliaryNode(std::unique_ptr node) { + T* ret = node.get(); + m_auxiliaryNodes.push_back(std::move(node)); + return ret; + } + + VtValue GetTextureOutput( + SdfAssetPath const& path, + std::string const& wrapMode, + float scale, float bias, + bool forceLinearSpace, + TfToken const& outputId, + RprUsd_MaterialNode** uvTextureNode = nullptr); + +private: + std::vector> m_auxiliaryNodes; + RprUsd_MaterialNode* m_baseColorNode = nullptr; + VtValue m_displacementOutput; +}; + +bool IsHoudiniPrincipledShaderHydraNode(HdSceneDelegate* delegate, SdfPath const& nodePath, bool* isSurface); + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_MATERIAL_NODES_HOUDINI_PRINCIPLED_SHADER_NODE_H diff --git a/pxr/imaging/rprUsd/materialNodes/materialNode.h b/pxr/imaging/rprUsd/materialNodes/materialNode.h new file mode 100644 index 000000000..71170915d --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/materialNode.h @@ -0,0 +1,70 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_MATERIAL_NODES_MATERIAL_NODE_H +#define RPRUSD_MATERIAL_NODES_MATERIAL_NODE_H + +#include "pxr/imaging/hd/material.h" + +namespace rpr { class Context; } + +class RPRMtlxLoader; + +PXR_NAMESPACE_OPEN_SCOPE + +class RprUsdImageCache; + +struct RprUsd_MaterialBuilderContext { + rpr::Context* rprContext; + RprUsdImageCache* imageCache; + + TfToken uvPrimvarName; + bool isShadowCatcher; + bool isReflectionCatcher; + + VtValue displacementScale; + + RPRMtlxLoader* mtlxLoader; +}; + +class RprUsd_MaterialNode { +public: + virtual ~RprUsd_MaterialNode() = default; + + virtual VtValue GetOutput( + TfToken const& outputId) = 0; + + virtual bool SetInput( + TfToken const& inputId, + VtValue const& value) = 0; +}; + +class RprUsd_NodeError : public std::exception { +public: + RprUsd_NodeError(std::string errorMessage) : m_msg(std::move(errorMessage)) {} + ~RprUsd_NodeError() override = default; + + const char* what() const noexcept override { return m_msg.c_str(); } + +private: + std::string m_msg; +}; + +/// Thrown when node is empty. +/// There is no point to keep a node in memory when all it does is propagates inputs to outputs. +class RprUsd_NodeEmpty : public std::exception { +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_MATERIAL_NODES_MATERIAL_NODE_H diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/CMakeLists.txt b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/CMakeLists.txt new file mode 100644 index 000000000..6b4a5c418 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/CMakeLists.txt @@ -0,0 +1,51 @@ +set(MTLX_FILES + Patterns/rpr_noise2d_texture.mtlx + Patterns/rpr_checker_texture.mtlx + Patterns/rpr_gradient_texture.mtlx + Patterns/rpr_dot_texture.mtlx + + Utilities/rpr_bump_map.mtlx + Utilities/rpr_normal_map.mtlx + Utilities/rpr_twosided.mtlx + Utilities/rpr_blend_value.mtlx + Utilities/rpr_constant_texture.mtlx + + Utilities/rpr_rgb_to_hsv.mtlx + Utilities/rpr_hsv_to_rgb.mtlx + + Utilities/rpr_input_lookup.mtlx + + Utilities/rpr_uv_triplanar.mtlx + Utilities/rpr_uv_procedural.mtlx + + Utilities/rpr_fresnel.mtlx + Utilities/rpr_fresnel_schlick.mtlx + + Shaders/rpr_add.mtlx + Shaders/rpr_blend.mtlx + + Shaders/rpr_uber.mtlx + Shaders/rpr_diffuse.mtlx + Shaders/rpr_diffuse_refraction.mtlx + Shaders/rpr_emissive.mtlx + Shaders/rpr_microfacet.mtlx + Shaders/rpr_microfacet_anisotropic_reflection.mtlx + Shaders/rpr_microfacet_anisotropic_refraction.mtlx + Shaders/rpr_microfacet_beckmann.mtlx + Shaders/rpr_microfacet_refraction.mtlx + Shaders/rpr_orennayar.mtlx + Shaders/rpr_passthrough.mtlx + Shaders/rpr_phong.mtlx + Shaders/rpr_reflection.mtlx + Shaders/rpr_refraction.mtlx + Shaders/rpr_transparent.mtlx + Shaders/rpr_volume.mtlx + Shaders/rpr_ward.mtlx +) + +foreach(mtlx ${MTLX_FILES}) + get_filename_component(dir ${mtlx} DIRECTORY) + install( + FILES ${mtlx} + DESTINATION materials/${dir}) +endforeach() diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Patterns/rpr_checker_texture.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Patterns/rpr_checker_texture.mtlx new file mode 100644 index 000000000..7efffddb9 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Patterns/rpr_checker_texture.mtlx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Patterns/rpr_dot_texture.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Patterns/rpr_dot_texture.mtlx new file mode 100644 index 000000000..71236ca6e --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Patterns/rpr_dot_texture.mtlx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Patterns/rpr_gradient_texture.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Patterns/rpr_gradient_texture.mtlx new file mode 100644 index 000000000..849aa56bb --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Patterns/rpr_gradient_texture.mtlx @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Patterns/rpr_noise2d_texture.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Patterns/rpr_noise2d_texture.mtlx new file mode 100644 index 000000000..f697c892a --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Patterns/rpr_noise2d_texture.mtlx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_add.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_add.mtlx new file mode 100644 index 000000000..f490c483c --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_add.mtlx @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_blend.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_blend.mtlx new file mode 100644 index 000000000..ea9bf069b --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_blend.mtlx @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_diffuse.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_diffuse.mtlx new file mode 100644 index 000000000..ba47887eb --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_diffuse.mtlx @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_diffuse_refraction.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_diffuse_refraction.mtlx new file mode 100644 index 000000000..bed07f6c2 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_diffuse_refraction.mtlx @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_emissive.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_emissive.mtlx new file mode 100644 index 000000000..37c0ebb57 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_emissive.mtlx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet.mtlx new file mode 100644 index 000000000..ebd1a78e9 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet.mtlx @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet_anisotropic_reflection.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet_anisotropic_reflection.mtlx new file mode 100644 index 000000000..fff2dd503 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet_anisotropic_reflection.mtlx @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet_anisotropic_refraction.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet_anisotropic_refraction.mtlx new file mode 100644 index 000000000..48da8544e --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet_anisotropic_refraction.mtlx @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet_beckmann.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet_beckmann.mtlx new file mode 100644 index 000000000..bc97daa07 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet_beckmann.mtlx @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet_refraction.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet_refraction.mtlx new file mode 100644 index 000000000..4bdd0c31d --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_microfacet_refraction.mtlx @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_orennayar.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_orennayar.mtlx new file mode 100644 index 000000000..3d5e46c12 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_orennayar.mtlx @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_passthrough.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_passthrough.mtlx new file mode 100644 index 000000000..2c9c65fa4 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_passthrough.mtlx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_phong.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_phong.mtlx new file mode 100644 index 000000000..0b8632e43 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_phong.mtlx @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_reflection.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_reflection.mtlx new file mode 100644 index 000000000..15beb2c76 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_reflection.mtlx @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_refraction.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_refraction.mtlx new file mode 100644 index 000000000..dee6efb86 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_refraction.mtlx @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_transparent.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_transparent.mtlx new file mode 100644 index 000000000..cb857f505 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_transparent.mtlx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_uber.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_uber.mtlx new file mode 100644 index 000000000..166f3ffba --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_uber.mtlx @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_volume.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_volume.mtlx new file mode 100644 index 000000000..9e9788121 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_volume.mtlx @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_ward.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_ward.mtlx new file mode 100644 index 000000000..7e41d6b5f --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Shaders/rpr_ward.mtlx @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_blend_value.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_blend_value.mtlx new file mode 100644 index 000000000..915d31b7d --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_blend_value.mtlx @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_bump_map.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_bump_map.mtlx new file mode 100644 index 000000000..604321e6a --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_bump_map.mtlx @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_constant_texture.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_constant_texture.mtlx new file mode 100644 index 000000000..7c9881179 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_constant_texture.mtlx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_fresnel.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_fresnel.mtlx new file mode 100644 index 000000000..3d137e361 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_fresnel.mtlx @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_fresnel_schlick.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_fresnel_schlick.mtlx new file mode 100644 index 000000000..21ef03dff --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_fresnel_schlick.mtlx @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_hsv_to_rgb.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_hsv_to_rgb.mtlx new file mode 100644 index 000000000..9b4b5b8f2 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_hsv_to_rgb.mtlx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_input_lookup.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_input_lookup.mtlx new file mode 100644 index 000000000..9f5a635df --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_input_lookup.mtlx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_normal_map.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_normal_map.mtlx new file mode 100644 index 000000000..ab067b61e --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_normal_map.mtlx @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_rgb_to_hsv.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_rgb_to_hsv.mtlx new file mode 100644 index 000000000..e180575f0 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_rgb_to_hsv.mtlx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_twosided.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_twosided.mtlx new file mode 100644 index 000000000..5f24ad7d8 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_twosided.mtlx @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_uv_procedural.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_uv_procedural.mtlx new file mode 100644 index 000000000..82ec86a94 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_uv_procedural.mtlx @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_uv_triplanar.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_uv_triplanar.mtlx new file mode 100644 index 000000000..f7de5fb41 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/Utilities/rpr_uv_triplanar.mtlx @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxFiles/rpr_defs.mtlx b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/rpr_defs.mtlx new file mode 100644 index 000000000..d75b48686 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxFiles/rpr_defs.mtlx @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxNode.cpp b/pxr/imaging/rprUsd/materialNodes/mtlxNode.cpp new file mode 100644 index 000000000..44d92d262 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxNode.cpp @@ -0,0 +1,336 @@ +/************************************************************************ +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 "mtlxNode.h" +#include "rpr/baseNode.h" + +#include "pxr/imaging/rprUsd/material.h" +#include "pxr/imaging/rprUsd/materialMappings.h" +#include "pxr/base/gf/vec2f.h" + +PXR_NAMESPACE_OPEN_SCOPE + +namespace { + +void ParseMtlxBoolValue(std::string const& valueString, VtValue* value) { + if (valueString == "true") { + *value = true; + } else if (valueString == "false") { + *value = false; + } else { + TF_RUNTIME_ERROR("Invalid Mtlx boolean value: %s", valueString.c_str()); + } +} + +void ParseMtlxFloatValue(std::string const& valueString, VtValue* value) { + try { + *value = std::stof(valueString); + } catch (std::logic_error& e) { + TF_RUNTIME_ERROR("Invalid Mtlx float value: %s - %s", valueString.c_str(), e.what()); + } +} + +void ParseMtlxIntValue(std::string const& valueString, VtValue* value) { + try { + *value = std::stoi(valueString); + } catch (std::logic_error& e) { + TF_RUNTIME_ERROR("Invalid Mtlx int value: %s - %s", valueString.c_str(), e.what()); + } +} + +template +void ParseMtlxVecValue(std::string const& valueString, VtValue* value) { + auto tokens = TfStringTokenize(valueString, ", \t"); + if (tokens.size() != VecType::dimension) { + TF_RUNTIME_ERROR("Invalid Mtlx value: %s - expected %zu components, got %zu", + valueString.c_str(), VecType::dimension, tokens.size()); + return; + } + + try { + VecType vec; + for (size_t i = 0; i < tokens.size(); ++i) { + vec.data()[i] = std::stof(tokens[i]); + } + *value = vec; + } catch (std::logic_error& e) { + TF_RUNTIME_ERROR("Invalid Mtlx value: %s - %s", valueString.c_str(), e.what()); + } +} + +VtValue ParseMtlxValue(RprUsd_MtlxNodeElement const& input) { + VtValue ret; + if (auto valueString = input.GetValueString()) { + if (input.GetType() == RprUsdMaterialNodeElement::kBoolean) { + ParseMtlxBoolValue(valueString, &ret); + } else if (input.GetType() == RprUsdMaterialNodeElement::kInteger) { + ParseMtlxIntValue(valueString, &ret); + } else if (input.GetType() == RprUsdMaterialNodeElement::kFloat || + input.GetType() == RprUsdMaterialNodeElement::kAngle) { + ParseMtlxFloatValue(valueString, &ret); + } else if (input.GetType() == RprUsdMaterialNodeElement::kVector3 || + input.GetType() == RprUsdMaterialNodeElement::kColor3) { + ParseMtlxVecValue(valueString, &ret); + } else if (input.GetType() == RprUsdMaterialNodeElement::kVector2) { + ParseMtlxVecValue(valueString, &ret); + } + } + return ret; +} + +struct TokenParameterMapping { + rpr::MaterialNodeInput rprInput; + std::vector values; +}; + +bool GetTokenParameterMapping( + TfToken const& inputId, + const char* defaultValue, + TfTokenVector const& tokenValues, + int* out_defaultIndex, + TokenParameterMapping* out_mappings) { + *out_defaultIndex = -1; + + bool isValid = true; + + if (ToRpr(inputId, &out_mappings->rprInput)) { + out_mappings->values.reserve(tokenValues.size()); + for (size_t i = 0; i < tokenValues.size(); ++i) { + uint32_t value; + if (!ToRpr(tokenValues[i], &value)) { + isValid = false; + break; + } + + out_mappings->values.push_back(value); + + if (tokenValues[i] == defaultValue) { + *out_defaultIndex = i; + } + } + } else { + isValid = false; + } + + if (*out_defaultIndex == -1) { + TF_RUNTIME_ERROR("Invalid .mtlx definition: no default value"); + isValid = false; + } + + return isValid; +} + +VtValue RemapTokenInput(VtValue const& input, TokenParameterMapping const& mapping) { + if (!input.IsHolding()) { + return VtValue(); + } + + int idx = input.UncheckedGet(); + if (idx < 0 || size_t(idx) >= mapping.values.size()) { + return VtValue(); + } + + return VtValue(idx); +} + +} // namespace anonymous + +//------------------------------------------------------------------------------ +// RprUsd_MtlxNodeInfo +//------------------------------------------------------------------------------ + +namespace { + +RprUsdMaterialNodeInput::Type RprUsd_GetMaterialNodeElementType(MaterialX::TypedElementPtr const& element) { + static std::map s_mapping = { + {"boolean", RprUsdMaterialNodeElement::kBoolean}, + {"color3", RprUsdMaterialNodeElement::kColor3}, + {"float", RprUsdMaterialNodeElement::kFloat}, + {"angle", RprUsdMaterialNodeElement::kAngle}, + {"integer", RprUsdMaterialNodeElement::kInteger}, + {"volumeshader", RprUsdMaterialNodeElement::kVolumeShader}, + {"surfaceshader", RprUsdMaterialNodeElement::kSurfaceShader}, + {"displacementshader", RprUsdMaterialNodeElement::kDisplacementShader}, + {"vector3", RprUsdMaterialNodeElement::kVector3}, + {"vector2", RprUsdMaterialNodeElement::kVector2}, + {"string", RprUsdMaterialNodeElement::kString}, + }; + + auto it = s_mapping.find(element->getType()); + if (it == s_mapping.end()) return RprUsdMaterialNodeElement::kInvalid; + + if (it->second == RprUsdMaterialNodeElement::kString) { + // If enum attribute is specified, we have kToken type + auto& enumAttr = element->getAttribute(MaterialX::ValueElement::ENUM_ATTRIBUTE); + if (!enumAttr.empty()) return RprUsdMaterialNodeElement::kToken; + } + + return it->second; +} + +RprUsdMaterialNodeInput::Type RprUsd_GetMaterialNodeElementType(MaterialX::InputPtr const& input) { + if (input->getDefaultGeomPropString() == "Nworld") { + return RprUsdMaterialNodeElement::kNormal; + } else { + return RprUsd_GetMaterialNodeElementType(MaterialX::TypedElementPtr(input)); + } +} + +} // namespace anonymous + +RprUsd_MtlxNodeInfo::RprUsd_MtlxNodeInfo( + MaterialX::DocumentPtr const& mtlxDoc, + MaterialX::NodeDefPtr const& mtlxNodeDef, + std::string const& uiFolder) + : m_uiFolder(uiFolder) + , m_mtlxDoc(mtlxDoc) + , m_mtlxNodeDef(mtlxNodeDef) { + + auto mtlxInputs = m_mtlxNodeDef->getInputs(); + m_mtlxInputs.reserve(mtlxInputs.size()); + for (auto& input : mtlxInputs) { + auto inputType = RprUsd_GetMaterialNodeElementType(input); + if (inputType != RprUsdMaterialNodeElement::kInvalid) { + m_mtlxInputs.emplace_back(std::move(input), inputType); + } + } + + auto mtlxOutputs = m_mtlxNodeDef->getOutputs(); + m_mtlxOutputs.reserve(mtlxOutputs.size()); + for (auto& output : mtlxOutputs) { + auto outputType = RprUsd_GetMaterialNodeElementType(output); + if (outputType != RprUsdMaterialNodeElement::kInvalid) { + m_mtlxOutputs.emplace_back(std::move(output), outputType); + } + } +} + +RprUsdMaterialNodeFactoryFnc RprUsd_MtlxNodeInfo::GetFactory() const { + static const std::string kRprPrefix("rpr_"); + + // Check if node definition matches to the one of rpr native nodes + auto& nodeDefName = m_mtlxNodeDef->getNodeString(); + if (TfStringStartsWith(nodeDefName, kRprPrefix)) { + TfToken rprNodeId(nodeDefName.substr(kRprPrefix.size())); + + rpr::MaterialNodeType rprNodeType; + if (ToRpr(rprNodeId, &rprNodeType, false)) { + std::vector> rprNodeDefaultParameters; + + // Token parameter has strict list of possible values. + // Houdini converts such paramaters to int type. + // We build lookup table for such parameters to workaround this behavior + std::map tokenParamMappings; + for (auto& input : m_mtlxInputs) { + if (input.GetType() == RprUsdMaterialNodeElement::kToken) { + TfToken inputId(input.GetName()); + + int defaultIndex; + TokenParameterMapping mapping; + if (GetTokenParameterMapping(inputId, input.GetValueString(), input.GetTokenValues(), + &defaultIndex, &mapping)) { + + tokenParamMappings[inputId] = mapping; + rprNodeDefaultParameters.emplace_back(inputId, VtValue(defaultIndex)); + } + } else { + auto value = ParseMtlxValue(input); + if (!value.IsEmpty()) { + rprNodeDefaultParameters.emplace_back(TfToken(input.GetName()), std::move(value)); + } + } + } + + return [rprNodeType, rprNodeDefaultParameters, tokenParamMappings]( + RprUsd_MaterialBuilderContext* context, + std::map const& parameters) -> RprUsd_MaterialNode* { + + class RprUsd_MtlxNode : public RprUsd_BaseRuntimeNode { + public: + RprUsd_MtlxNode( + rpr::MaterialNodeType type, + RprUsd_MaterialBuilderContext* ctx, + std::map const& tokenParamMappings) + : RprUsd_BaseRuntimeNode(type, ctx) + , m_tokenParamMappings(tokenParamMappings) { + + } + + bool SetInput( + TfToken const& inputId, + VtValue const& value) override { + auto tokenParamIt = m_tokenParamMappings.find(inputId); + if (tokenParamIt != m_tokenParamMappings.end()) { + auto& mapping = tokenParamIt->second; + + auto remappedValue = RemapTokenInput(value, mapping); + if (remappedValue.IsEmpty()) { + TF_RUNTIME_ERROR("Failed to remap token parameter %s - %s", inputId.GetText(), value.GetTypeName().c_str()); + return false; + } + + return RprUsd_BaseRuntimeNode::SetInput(mapping.rprInput, remappedValue); + } + + return RprUsd_BaseRuntimeNode::SetInput(inputId, value); + } + + private: + std::map const& m_tokenParamMappings; + }; + + auto rprNode = new RprUsd_MtlxNode(rprNodeType, context, tokenParamMappings); + + bool validInput = true; + for (auto& entry : rprNodeDefaultParameters) { + auto it = parameters.find(entry.first); + if (it != parameters.end()) { + validInput = rprNode->SetInput(entry.first, it->second); + } else { + validInput = rprNode->SetInput(entry.first, entry.second); + } + + if (!validInput) { + break; + } + } + + if (!validInput) { + delete rprNode; + return nullptr; + } + + return rprNode; + }; + } + } + + TF_WARN("Nodes with custom implementation are not supported (yet)"); + return nullptr; +} + +RprUsd_MtlxNodeElement::RprUsd_MtlxNodeElement( + MaterialX::ValueElementPtr element, + RprUsdMaterialNodeElement::Type type) + : RprUsdMaterialNodeInput(type) + , m_mtlx(std::move(element)) { + if (m_type == kToken) { + auto& enumValues = m_mtlx->getAttribute(MaterialX::ValueElement::ENUM_ATTRIBUTE); + auto values = TfStringTokenize(enumValues, ","); + for (auto& value : values) { + m_tokenValues.emplace_back(value, TfToken::Immortal); + } + } +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialNodes/mtlxNode.h b/pxr/imaging/rprUsd/materialNodes/mtlxNode.h new file mode 100644 index 000000000..8fb70e0cb --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/mtlxNode.h @@ -0,0 +1,86 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_MATERIAL_NODES_MTLX_NODE_H +#define RPRUSD_MATERIAL_NODES_MTLX_NODE_H + +#include "pxr/imaging/rprUsd/materialRegistry.h" + +#include + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +class RprUsd_MtlxNodeElement : public RprUsdMaterialNodeInput { +public: + RprUsd_MtlxNodeElement( + MaterialX::ValueElementPtr element, + RprUsdMaterialNodeElement::Type type); + ~RprUsd_MtlxNodeElement() override = default; + + const char* GetName() const override { return GetCStr(m_mtlx->getName()); } + const char* GetUIName() const override { return GetCStr(m_mtlx->getAttribute(MaterialX::ValueElement::UI_NAME_ATTRIBUTE)); } + const char* GetUIMin() const override { return GetCStr(m_mtlx->getAttribute(MaterialX::ValueElement::UI_MIN_ATTRIBUTE)); } + const char* GetUISoftMin() const override { return GetCStr(m_mtlx->getAttribute(MaterialX::ValueElement::UI_SOFT_MIN_ATTRIBUTE)); } + const char* GetUIMax() const override { return GetCStr(m_mtlx->getAttribute(MaterialX::ValueElement::UI_MAX_ATTRIBUTE)); } + const char* GetUISoftMax() const override { return GetCStr(m_mtlx->getAttribute(MaterialX::ValueElement::UI_SOFT_MAX_ATTRIBUTE)); } + const char* GetUIFolder() const override { return GetCStr(m_mtlx->getAttribute(MaterialX::ValueElement::UI_FOLDER_ATTRIBUTE)); } + const char* GetDocString() const override { return GetCStr(m_mtlx->getAttribute(MaterialX::ValueElement::DOC_ATTRIBUTE)); } + const char* GetValueString() const override { return GetCStr(m_mtlx->getValueString()); } + std::vector const& GetTokenValues() const override { return m_tokenValues; } + +private: + MaterialX::ValueElementPtr m_mtlx; + std::vector m_tokenValues; +}; + +/// \class RprUsd_MtlxNodeInfo +/// +/// RprUsd_MtlxNodeInfo describes the node that is defined by .mtlx file. +/// Right now, we support only those .mtlx definitions that corresponds to +/// the native RPR material node (RPR_MATERIAL_NODE_*). +/// In the future, obviously, we would like to be able to process custom +/// nodes that are implemented as MaterialX implementation graphs. +/// +class RprUsd_MtlxNodeInfo : public RprUsdMaterialNodeInfo { +public: + RprUsd_MtlxNodeInfo( + MaterialX::DocumentPtr const& mtlxDoc, + MaterialX::NodeDefPtr const& mtlxNodeDef, + std::string const& uiFolder); + ~RprUsd_MtlxNodeInfo() override = default; + + const char* GetName() const override { return GetCStr(m_mtlxNodeDef->getNodeString()); } + const char* GetUIName() const override { return GetCStr(m_mtlxNodeDef->getAttribute(MaterialX::ValueElement::UI_NAME_ATTRIBUTE)); } + const char* GetUIFolder() const override { return GetCStr(m_uiFolder); } + + size_t GetNumInputs() const override { return m_mtlxInputs.size(); } + RprUsdMaterialNodeInput const* GetInput(size_t idx) const override { return &m_mtlxInputs[idx]; } + + size_t GetNumOutputs() const override { return m_mtlxOutputs.size(); } + RprUsdMaterialNodeElement const* GetOutput(size_t idx) const override { return &m_mtlxOutputs[idx]; } + + RprUsdMaterialNodeFactoryFnc GetFactory() const; + +private: + std::string m_uiFolder; + MaterialX::DocumentPtr m_mtlxDoc; + MaterialX::NodeDefPtr m_mtlxNodeDef; + std::vector m_mtlxInputs; + std::vector m_mtlxOutputs; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_MATERIAL_NODES_MTLX_NODE_H diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/arithmeticNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/arithmeticNode.cpp new file mode 100644 index 000000000..ca9d21568 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/rpr/arithmeticNode.cpp @@ -0,0 +1,486 @@ +#include "arithmeticNode.h" +#include "nodeInfo.h" + +#include "pxr/imaging/rprUsd/materialHelpers.h" +#include "pxr/imaging/rprUsd/materialMappings.h" +#include "pxr/base/tf/instantiateSingleton.h" +#include "pxr/base/arch/attributes.h" +#include "pxr/base/gf/matrix3f.h" + +PXR_NAMESPACE_OPEN_SCOPE + +template +RprUsd_MaterialNode* RprUsd_CreateRprNode( + RprUsd_MaterialBuilderContext* ctx, + std::map const& parameters) { + return new T(ctx, parameters); +} + +template +RprUsd_RprNodeInfo* GetNodeInfo() { + auto ret = new RprUsd_RprNodeInfo; + auto& nodeInfo = *ret; + + // Take `RPR_MATERIAL_NODE_OP_OPERATION`-like name and convert it into `operation` + std::string name(Node::kOpName + sizeof("RPR_MATERIAL_NODE_OP")); + for (size_t i = 0; i < name.size(); ++i) { + name[i] = std::tolower(name[i], std::locale()); + } + + nodeInfo.name = "rpr_arithmetic_" + name; + nodeInfo.uiName = std::string("RPR ") + Node::kUiName; + nodeInfo.uiFolder = "Arithmetics"; + + for (int i = 0; i < Node::kArity; ++i) { + RprUsd_RprNodeInput input(RprUsd_RprNodeInput::kColor3); + input.name = TfStringPrintf("color%d", i); + input.uiName = TfStringPrintf("Color %d", i); + input.valueString = "0,0,0"; + input.uiSoftMin = "0"; + input.uiSoftMax = "1"; + nodeInfo.inputs.push_back(std::move(input)); + } + + RprUsd_RprNodeOutput output(RprUsdMaterialNodeElement::kColor3); + output.name = "out"; + output.uiName = "out"; + nodeInfo.outputs.push_back(std::move(output)); + + return ret; +} + +/// \class RprUsd_RprArithmeticNodeRegistry +/// +/// Each arithmetic node is registered in RprUsd_RprArithmeticNodeRegistry. +/// In such a way we can easily create an arithmetic node for particular operation +/// from the code (e.g. RprUsd_UsdPreviewSurface, RprUsd_HoudiniPrincipledNode). +class RprUsd_RprArithmeticNodeRegistry { +public: + static RprUsd_RprArithmeticNodeRegistry& GetInstance() { + return TfSingleton::GetInstance(); + } + + template + void Register(rpr::MaterialNodeArithmeticOperation op) { + if (m_lookupOp.count(op)) { + TF_CODING_ERROR("Attempt to define the same arithmetic node twice: %u", op); + return; + } + + m_lookupOp[op] = m_descs.size(); + m_descs.emplace_back(); + auto& desc = m_descs.back(); + desc.factory = &RprUsd_CreateRprNode; + desc.info = GetNodeInfo(); + + // Register this node in the general node registry + RprUsdMaterialRegistry::GetInstance().Register( + TfToken(desc.info->name), + desc.factory, + desc.info); + } + + RprUsd_RprArithmeticNode* Create( + rpr::MaterialNodeArithmeticOperation op, + RprUsd_MaterialBuilderContext* ctx, + std::map const& parameters) { + auto it = m_lookupOp.find(op); + if (it != m_lookupOp.end()) { + return static_cast(m_descs[it->second].factory(ctx, parameters)); + } + + TF_CODING_ERROR("Unknown arithmetic node or not implemented: %u", op); + return nullptr; + } + +private: + friend class TfSingleton; + +private: + using FactoryFnc = std::function const&)>; + struct NodeDesc { + RprUsd_RprNodeInfo* info; + FactoryFnc factory; + }; + + std::vector m_descs; + std::unordered_map m_lookupOp; +}; + +TF_INSTANTIATE_SINGLETON(RprUsd_RprArithmeticNodeRegistry); + +namespace { + +/// There are roughly 42 arithmetic operations, each of them defines a C++ class. +/// The following define allows us to minimize code required to define these classes. +/// +/// Depending on inputs, arithmetic node output can be calculated at time of +/// material graph construction or it will be calculated in RPR engine (rpr::MaterialNode). +/// The output can be calculated at the time of material graph construction when all inputs +/// are of trivial type (float, vectors, etc). If one of the inputs is a rpr::MaterialNode +/// then arithmetic node's output will be a rpr::MaterialNode. +/// This behavior allows to implement one uniform code-path for some complex logic over material +/// node inputs (for example, see Houdini's principled node implementation of emissive color). +#define DEFINE_ARITHMETIC_NODE(op, arity, eval, uiName, doc) \ +class RprUsd_ ## op ## Node : public RprUsd_RprArithmeticNode { \ +public: \ + RprUsd_ ## op ## Node( \ + RprUsd_MaterialBuilderContext* ctx, \ + std::map const& parameters) : RprUsd_RprArithmeticNode(ctx) { \ + for (auto& entry : parameters) SetInput(entry.first, entry.second); \ + } \ + static constexpr int kArity = arity; \ + static constexpr rpr::MaterialNodeArithmeticOperation kOp = op; \ + static constexpr const char* kOpName = #op; \ + static constexpr const char* kUiName = uiName; \ + /*static constexpr const char* kDoc = doc;*/ \ +protected: \ + int GetNumArguments() const final { return kArity; } \ + rpr::MaterialNodeArithmeticOperation GetOp() const final { return kOp; } \ + VtValue EvalOperation() const final { eval } \ +}; \ +ARCH_CONSTRUCTOR(RprUsd_InitArithmeticNode ## op, 255, void) { \ + RprUsd_RprArithmeticNodeRegistry::GetInstance().Register(op); \ +} + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_SUB, 2, { + return VtValue(GetRprFloat(m_args[0]) - GetRprFloat(m_args[1])); +}, "Subtraction", "Subtraction."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_ADD, 2, { + return VtValue(GetRprFloat(m_args[0]) + GetRprFloat(m_args[1])); +}, "Addition", "Addition."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_MUL, 2, { + return VtValue(GfCompMult(GetRprFloat(m_args[0]), GetRprFloat(m_args[1]))); +}, "Multiplication", "Multiplication."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_DIV, 2, { + auto lhs = GetRprFloat(m_args[0]); + auto rhs = GetRprFloat(m_args[1]); + decltype(lhs) out; + for (size_t i = 0; i < out.dimension; ++i) { + out[i] = lhs[i] / rhs[i]; + } + return VtValue(out); +}, "Division", "Division."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_NORMALIZE3, 1, { + auto in = GetRprFloat(m_args[0]); + return VtValue(GfVec3f(in[0], in[1], in[2]).GetNormalized()); +}, "Normalize", "Normalize output of color0"); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_LENGTH3, 1, { + auto in = GetRprFloat(m_args[0]); + return VtValue(GfVec3f(in[0], in[1], in[2]).GetLength()); +}, "Length", "Length of color0"); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_DOT3, 2, { + auto in0 = GetRprFloat(m_args[0]); + auto in1 = GetRprFloat(m_args[1]); + return VtValue(GfDot(GfVec3f(in0.data()), GfVec3f(in1.data()))); +}, "Dot", "Dot product of two rgb vectors."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_DOT4, 2, { + auto in0 = GetRprFloat(m_args[0]); + auto in1 = GetRprFloat(m_args[1]); + return VtValue(GfDot(in0, in1)); +}, "Dot4", "Dot product of two rgba vectors."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_CROSS3, 2, { + auto in0 = GetRprFloat(m_args[0]); + auto in1 = GetRprFloat(m_args[1]); + return VtValue(GfCross(GfVec3f(in0.data()), GfVec3f(in1.data()))); +}, "Cross", "Cross product."); + +#define PER_COMPONENT_UNARY_IMPL(mathFunc) \ + auto in = GetRprFloat(m_args[0]); \ + for (size_t i = 0; i < in.dimension; ++i) in[i] = mathFunc(in[i]); \ + return VtValue(in); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_SIN, 1, { + PER_COMPONENT_UNARY_IMPL(std::sin); +}, "Sin", "Trigometric sine (in radians)."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_COS, 1, { + PER_COMPONENT_UNARY_IMPL(std::cos); +}, "Cos", "Trigometric cosine (in radians)."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_TAN, 1, { + PER_COMPONENT_UNARY_IMPL(std::tan); +}, "Tan", "Trigometric tangent (in radians)."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_LOG, 1, { + PER_COMPONENT_UNARY_IMPL(std::log); +}, "Log", "Log() function."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_ATAN, 1, { + PER_COMPONENT_UNARY_IMPL(std::atan); +}, "Atan", "Trigometric arctangent (in radians)."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_ASIN, 1, { + PER_COMPONENT_UNARY_IMPL(std::asin); +}, "Asin", "Trigometric arcsine (in radians)."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_ACOS, 1, { + PER_COMPONENT_UNARY_IMPL(std::acos); +}, "Acos", "Trigometric arccosine (in radians)."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_ABS, 1, { + PER_COMPONENT_UNARY_IMPL(std::abs); +}, "Abs", "Absolute value."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_FLOOR, 1, { + PER_COMPONENT_UNARY_IMPL(std::floor); +}, "Floor", "Mathematical floor value of color0."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_AVERAGE_XYZ, 1, { + auto in = GetRprFloat(m_args[0]); + return VtValue(GfVec4f((in[0] + in[1] + in[2]) / 3.0f)); +}, "Average XYZ", "Average of color0 RGB values."); + +#define PER_COMPONENT_BINARY_IMPL(mathFunc) \ + auto in0 = GetRprFloat(m_args[0]); \ + auto in1 = GetRprFloat(m_args[1]); \ + decltype(in0) out; \ + for (size_t i = 0; i < in0.dimension; ++i) out[i] = mathFunc(in0[i], in1[i]); \ + return VtValue(out); + +static float GetAverage(float v0, float v1) { return 0.5f * (v0 + v1); } + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_AVERAGE, 2, { + PER_COMPONENT_BINARY_IMPL(GetAverage); +}, "Average", "Average of color0 and color1."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_MIN, 2, { + PER_COMPONENT_BINARY_IMPL(std::min); +}, "Min", "Minimum of two inputs."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_MAX, 2, { + PER_COMPONENT_BINARY_IMPL(std::max); +}, "Max", "Maximum of two inputs."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_MOD, 2, { + PER_COMPONENT_BINARY_IMPL(std::fmod); +}, "Mod", "Modulus of two values."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_POW, 2, { + PER_COMPONENT_BINARY_IMPL(std::pow); +}, "Pow", "Power (color0 ^ color1)."); + +#define LOGICAL_OPERATION_IMPL(OP) \ + auto lhs = GetRprFloat(m_args[0]); \ + auto rhs = GetRprFloat(m_args[1]); \ + decltype(lhs) out; \ + for (size_t i = 0; i < lhs.dimension; ++i) { \ + out[i] = (lhs[i] OP rhs[i]) ? 1.0f : 0.0f; \ + } \ + return VtValue(out); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_LOWER_OR_EQUAL, 2, { + LOGICAL_OPERATION_IMPL(<=); +}, "Lower or Equal", "Return 1 if color0 <= color1 else 0."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_LOWER, 2, { + LOGICAL_OPERATION_IMPL(<); +}, "Lower", "Return 1 if color0 < color1 else 0."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_GREATER_OR_EQUAL, 2, { + LOGICAL_OPERATION_IMPL(>=); +}, "Greater or Equal", "Return 1 if color0 >= color1 else 0."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_GREATER, 2, { + LOGICAL_OPERATION_IMPL(>); +}, "Greater", "Return 1 if color0 > color1 else 0."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_EQUAL, 2, { + LOGICAL_OPERATION_IMPL(==); +}, "Equal", "Return 1 if color0 == color1 else 0."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_NOT_EQUAL, 2, { + LOGICAL_OPERATION_IMPL(!=); +}, "Not Equal", "Return 1 if color0 != color1 else 0."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_AND, 2, { + LOGICAL_OPERATION_IMPL(&&); +}, "And", "Return 1 if color0 and color1 are not 0."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_OR, 2, { + LOGICAL_OPERATION_IMPL(||); +}, "Or", "Return 1 if color0 or color1 are not 0"); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_TERNARY, 3, { + auto in0 = GetRprFloat(m_args[0]); + auto in1 = GetRprFloat(m_args[1]); + auto in2 = GetRprFloat(m_args[2]); + decltype(in0) out; + for (size_t i = 0; i < in0.dimension; ++i) { + out[i] = in0[i] ? in1[i] : in2[i]; + } + return VtValue(out); +}, "Ternary", "Return color1 if color0 is 0 else color2."); + +#define SELECT_OPERATION_IMPL(component_index) \ + auto value = GetRprFloat(m_args[0]); \ + return VtValue(GfVec4f(value[component_index])); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_SELECT_X, 1, { + SELECT_OPERATION_IMPL(0); +}, "Select X", "Select the X component."); +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_SELECT_Y, 1, { + SELECT_OPERATION_IMPL(1); +}, "Select Y", "Select the Y component."); +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_SELECT_Z, 1, { + SELECT_OPERATION_IMPL(2); +}, "Select Z", "Select the Z component."); +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_SELECT_W, 1, { + SELECT_OPERATION_IMPL(3); +}, "Select W", "Select the W component."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_SHUFFLE_YZWX, 1, { + auto in = GetRprFloat(m_args[0]); + return VtValue(GfVec4f(in[1], in[2], in[3], in[0])); +}, "Shuffle YZWX", "Shuffle channels of color0."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_SHUFFLE_ZWXY, 1, { + auto in = GetRprFloat(m_args[0]); + return VtValue(GfVec4f(in[2], in[3], in[0], in[1])); +}, "Shuffle ZWXY", "Shuffle channels of color0."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_SHUFFLE_WXYZ, 1, { + auto in = GetRprFloat(m_args[0]); + return VtValue(GfVec4f(in[3], in[0], in[1], in[2])); +}, "Shuffle WXYZ", "Shuffle channels of color0."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_MAT_MUL, 4, { + GfMatrix3f mat; + for (size_t i = 0; i < mat.numRows; ++i) { + auto input = GetRprFloat(m_args[i]); + for (size_t j = 0; j < mat.numColumns; ++j) { + mat[i][j] = input[j]; + } + } + + auto input = GetRprFloat(m_args[3]); + + GfVec3f vec(input.data()); + return VtValue(mat * vec); +}, "Matrix multiply", "color0,1,2 make a 3x3 matrix, multiply by color3."); + +DEFINE_ARITHMETIC_NODE(RPR_MATERIAL_NODE_OP_COMBINE, 4, { + auto in0 = GetRprFloat(m_args[0]); + auto in1 = GetRprFloat(m_args[1]); + auto in2 = GetRprFloat(m_args[2]); + decltype(in0) out(in0[0], in1[1], in2[2], 1.0f); + + if (!m_args[3].IsEmpty()) { + auto in3 = GetRprFloat(m_args[3]); + out[3] = in3[3]; + } + return VtValue(out); +}, "Combine", "Combine to (color0.r, color1.g, color2.b, 1) with three inputs. Combine to (color0.r, color1.g, color2.b, color3.a) with four inputs."); + +} // namespace anonymous + +std::unique_ptr RprUsd_RprArithmeticNode::Create( + rpr::MaterialNodeArithmeticOperation operation, + RprUsd_MaterialBuilderContext* ctx, + std::map const& parameters) { + auto node = RprUsd_RprArithmeticNodeRegistry::GetInstance().Create(operation, ctx, parameters); + return std::unique_ptr(node); +} + +bool RprUsd_RprArithmeticNode::SetInput( + TfToken const& inputId, + VtValue const& value) { + int argIndex; + if (inputId == RprUsdMaterialNodeInputTokens->color0) { + argIndex = 0; + } else if (inputId == RprUsdMaterialNodeInputTokens->color1) { + argIndex = 1; + } else if (inputId == RprUsdMaterialNodeInputTokens->color2) { + argIndex = 2; + } else if (inputId == RprUsdMaterialNodeInputTokens->color3) { + argIndex = 3; + } else { + TF_CODING_ERROR("Unexpected input for arithmetic node: %s", inputId.GetText()); + return false; + } + + return SetInput(argIndex, value); +} + +bool RprUsd_RprArithmeticNode::SetInput( + int index, + VtValue const& value) { + if (index < 0 || index > 3) { + TF_CODING_ERROR("Invalid index: %d", index); + return false; + } + + // Output is invalidated when input changes + m_output = VtValue(); + + m_args[index] = value; + return true; +} + +VtValue RprUsd_RprArithmeticNode::GetOutput() { + if (m_output.IsEmpty()) { + // If all inputs are of trivial type (uint or float, GfVec3f, etc) we can + // evaluate the output value on the CPU + bool isInputsTrivial = true; + + int numArgs = GetNumArguments(); + for (int i = 0; i < numArgs; ++i) { + if (m_args[i].IsHolding()) { + isInputsTrivial = false; + break; + } + } + + if (isInputsTrivial) { + m_output = EvalOperation(); + } else { + // Otherwise, we setup rpr::MaterialNode that calculates the value in runtime + RprMaterialNodePtr rprNode; + + rpr::Status status; + rprNode.reset(m_ctx->rprContext->CreateMaterialNode(RPR_MATERIAL_NODE_ARITHMETIC, &status)); + + if (rprNode) { + if (RPR_ERROR_CHECK(rprNode->SetInput(RPR_MATERIAL_INPUT_OP, GetOp()), "Failed to set arithmetic node operation")) { + return m_output; + } + + static const rpr::MaterialNodeInput s_arithmeticNodeInputs[] = { + RPR_MATERIAL_INPUT_COLOR0, + RPR_MATERIAL_INPUT_COLOR1, + RPR_MATERIAL_INPUT_COLOR2, + RPR_MATERIAL_INPUT_COLOR3, + }; + for (int i = 0; i < numArgs; ++i) { + if (m_args[i].IsEmpty()) { + if (RPR_ERROR_CHECK(rprNode->SetInput(s_arithmeticNodeInputs[i], 0.0f, 0.0f, 0.0f, 0.0f), "Failed to set arithmetic node input")) { + return m_output; + } + } else { + if (SetRprInput(rprNode.get(), s_arithmeticNodeInputs[i], m_args[i]) != RPR_SUCCESS) { + return m_output; + } + } + } + + m_output = VtValue(rprNode); + } else { + TF_RUNTIME_ERROR("%s", RPR_GET_ERROR_MESSAGE(status, "Failed to create arithmetic material node", m_ctx->rprContext).c_str()); + } + } + } + + return m_output; +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/arithmeticNode.h b/pxr/imaging/rprUsd/materialNodes/rpr/arithmeticNode.h new file mode 100644 index 000000000..a517c56ed --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/rpr/arithmeticNode.h @@ -0,0 +1,63 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_MATERIAL_NODES_RPR_ARITHMETIC_NODE_H +#define RPRUSD_MATERIAL_NODES_RPR_ARITHMETIC_NODE_H + +#include "../materialNode.h" + +#include "pxr/imaging/rprUsd/materialRegistry.h" + +PXR_NAMESPACE_OPEN_SCOPE + +/// \class RprUsd_RprArithmeticNode +/// +/// Wrapper over RPR_MATERIAL_NODE_ARITHMETIC +class RprUsd_RprArithmeticNode : public RprUsd_MaterialNode { +public: + static std::unique_ptr Create( + rpr::MaterialNodeArithmeticOperation operation, + RprUsd_MaterialBuilderContext* ctx, + std::map const& parameters = {}); + + ~RprUsd_RprArithmeticNode() override = default; + + bool SetInput( + TfToken const& inputId, + VtValue const& value) override; + + VtValue GetOutput(TfToken const& outputId) override { return GetOutput(); } + + /// Arithmetic node has only one output + VtValue GetOutput(); + + /// Arithmetic node has up to four arguments (\p index in range [0; 3]) + bool SetInput(int index, VtValue const& value); + +protected: + RprUsd_RprArithmeticNode(RprUsd_MaterialBuilderContext* ctx) : m_ctx(ctx) {} + + // Provided by concrete operation node + virtual int GetNumArguments() const = 0; + virtual VtValue EvalOperation() const = 0; + virtual rpr::MaterialNodeArithmeticOperation GetOp() const = 0; + +protected: + RprUsd_MaterialBuilderContext* m_ctx; + VtValue m_args[4]; + VtValue m_output; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_MATERIAL_NODES_RPR_ARITHMETIC_NODE_H diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/baseNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/baseNode.cpp new file mode 100644 index 000000000..4f039cb57 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/rpr/baseNode.cpp @@ -0,0 +1,64 @@ +/************************************************************************ +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 "pxr/imaging/rprUsd/materialMappings.h" +#include "pxr/imaging/rprUsd/materialHelpers.h" +#include "pxr/imaging/rprUsd/error.h" + +PXR_NAMESPACE_OPEN_SCOPE + +RprUsd_BaseRuntimeNode::RprUsd_BaseRuntimeNode( + rpr::MaterialNodeType type, + RprUsd_MaterialBuilderContext* ctx) + : m_type(type) + , m_ctx(ctx) { + + rpr::Status status; + m_rprNode.reset(ctx->rprContext->CreateMaterialNode(type, &status)); + + if (!m_rprNode) { + throw RprUsd_NodeError(RPR_GET_ERROR_MESSAGE(status, "Failed to create material node", ctx->rprContext)); + } +} + +bool RprUsd_BaseRuntimeNode::SetInput( + TfToken const& inputId, + VtValue const& value) { + rpr::MaterialNodeInput rprInput; + if (ToRpr(inputId, &rprInput)) { + return SetInput(rprInput, value); + } + return false; +} + +bool RprUsd_BaseRuntimeNode::SetInput( + rpr::MaterialNodeInput input, + VtValue const& value) { + rpr::Status status = SetRprInput(m_rprNode.get(), input, value); + if (status == RPR_SUCCESS) { + return true; + } + + // XXX: Currently Hybrid does not support all UBER material parameters. + // Do not invalidate whole node because of it. + return m_type == RPR_MATERIAL_NODE_UBERV2 && + (status == RPR_ERROR_UNSUPPORTED || status == RPR_ERROR_UNIMPLEMENTED); +} + +VtValue RprUsd_BaseRuntimeNode::GetOutput(TfToken const& outputId) { + return VtValue(m_rprNode); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/baseNode.h b/pxr/imaging/rprUsd/materialNodes/rpr/baseNode.h new file mode 100644 index 000000000..c0cc9221d --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/rpr/baseNode.h @@ -0,0 +1,55 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_MATERIAL_NODES_RPR_BASE_NODE_H +#define RPRUSD_MATERIAL_NODES_RPR_BASE_NODE_H + +#include "../materialNode.h" + +#include "pxr/imaging/rprUsd/materialRegistry.h" + +#include + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +/// \class RprUsd_BaseRuntimeNode +/// +/// The node output of which is always rpr::MaterialNode*. +class RprUsd_BaseRuntimeNode : public RprUsd_MaterialNode { +public: + RprUsd_BaseRuntimeNode( + rpr::MaterialNodeType type, + RprUsd_MaterialBuilderContext* ctx); + + ~RprUsd_BaseRuntimeNode() override = default; + + bool SetInput( + TfToken const& inputId, + VtValue const& value) override; + bool SetInput( + rpr::MaterialNodeInput input, + VtValue const& value); + + VtValue GetOutput(TfToken const& outputId) override; + +protected: + rpr::MaterialNodeType m_type; + RprUsd_MaterialBuilderContext* m_ctx; + std::shared_ptr m_rprNode; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_MATERIAL_NODES_RPR_BASE_NODE_H diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/catcherNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/catcherNode.cpp new file mode 100644 index 000000000..5ba58719f --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/rpr/catcherNode.cpp @@ -0,0 +1,113 @@ +/************************************************************************ +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 "../materialNode.h" +#include "nodeInfo.h" + +#include "pxr/imaging/rprUsd/materialRegistry.h" +#include "pxr/base/arch/attributes.h" +#include "pxr/base/gf/vec2f.h" + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEFINE_PRIVATE_TOKENS(RprUsdRprCatcherNodeTokens, + (in) + (enable) +); + +/// \class RprUsd_RprCatcherNode +/// +/// The node that allows the user to enable either shadow catcher or reflection +/// catcher (depending on how this node is constructed). +/// +/// This node has two inputs: +/// 1) boolean input that enables or disables catcher mode. +/// 2) surface shader input that is simply transmitted to the surface output. +/// This automatically means that this node can not be created without +/// existing material that outputs the real surface shader. +class RprUsd_RprCatcherNode : public RprUsd_MaterialNode { +public: + RprUsd_RprCatcherNode(bool* catcherToggle) + : m_catcherToggle(catcherToggle) { + // true by default + *m_catcherToggle = true; + } + ~RprUsd_RprCatcherNode() override = default; + + VtValue GetOutput(TfToken const& outputId) override { return m_output; } + + bool SetInput( + TfToken const& inputId, + VtValue const& value) override { + if (inputId == RprUsdRprCatcherNodeTokens->in) { + m_output = value; + return true; + } else if (inputId == RprUsdRprCatcherNodeTokens->enable) { + if (value.IsHolding()) { + *m_catcherToggle = value.UncheckedGet() != 0; + return true; + } + } + + return false; + } + + static RprUsd_RprNodeInfo* GetInfo(std::string catcherType) { + auto ret = new RprUsd_RprNodeInfo; + auto& nodeInfo = *ret; + + nodeInfo.uiName = "RPR " + catcherType + " Catcher"; + nodeInfo.uiFolder = "Shaders"; + catcherType[0] = std::tolower(catcherType[0], std::locale()); + nodeInfo.name = "rpr_" + catcherType + "_catcher"; + + RprUsd_RprNodeInput in(RprUsdMaterialNodeElement::kSurfaceShader); + in.name = "in"; + nodeInfo.inputs.push_back(in); + + RprUsd_RprNodeInput enable(RprUsdMaterialNodeElement::kBoolean); + enable.name = "enable"; + enable.uiName = "Enable"; + enable.valueString = "true"; + nodeInfo.inputs.push_back(enable); + + RprUsd_RprNodeOutput surface(RprUsdMaterialNodeElement::kSurfaceShader); + surface.name = "surface"; + nodeInfo.outputs.push_back(surface); + + return ret; + } + +private: + bool* m_catcherToggle; + VtValue m_output; +}; + +#define REGISTER_CATCHER_NODE(CATCHER_TYPE) \ +ARCH_CONSTRUCTOR(RprUsd_Init ## CATCHER_TYPE ## CatcherNode, 255, void) { \ + auto nodeInfo = RprUsd_RprCatcherNode::GetInfo(#CATCHER_TYPE); \ + RprUsdMaterialRegistry::GetInstance().Register( \ + TfToken(nodeInfo->name, TfToken::Immortal), \ + [](RprUsd_MaterialBuilderContext* context, \ + std::map const& parameters) { \ + auto node = new RprUsd_RprCatcherNode(&context->is ## CATCHER_TYPE ## Catcher); \ + for (auto& entry : parameters) node->SetInput(entry.first, entry.second); \ + return node; \ + }, \ + nodeInfo); \ +} + +REGISTER_CATCHER_NODE(Shadow); +REGISTER_CATCHER_NODE(Reflection); + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/combineShadersNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/combineShadersNode.cpp new file mode 100644 index 000000000..7335443a0 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/rpr/combineShadersNode.cpp @@ -0,0 +1,121 @@ +/************************************************************************ +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 "../materialNode.h" +#include "nodeInfo.h" + +#include "pxr/imaging/rprUsd/materialRegistry.h" +#include "pxr/base/arch/attributes.h" + +PXR_NAMESPACE_OPEN_SCOPE + +/// \class RprUsd_RprCombineShadersNode +/// +/// Convenience node to allow combination of nodes of different type. +/// +/// A bit more about why we need this node. +/// In USD you can bind only one material to the mesh. +/// But what if you want apply both displacement and surface shaders on the mesh? +/// You have two options: +/// a) add `displacement` component to the all surface shaders +/// b) take outputs of `surface` node and `displacement` and combine them into one node +/// This node implements the second option +/// +class RprUsd_RprCombineShadersNode : public RprUsd_MaterialNode { +public: + RprUsd_RprCombineShadersNode() = default; + ~RprUsd_RprCombineShadersNode() override = default; + + VtValue GetOutput(TfToken const& outputId) override { + auto it = m_outputs.find(outputId); + if (it != m_outputs.end()) { + return it->second; + } + return VtValue(); + } + + bool SetInput( + TfToken const& inputId, + VtValue const& value) override { + if (inputId == HdMaterialTerminalTokens->volume || + inputId == HdMaterialTerminalTokens->surface || + inputId == HdMaterialTerminalTokens->displacement) { + if (value.IsHolding>()) { + m_outputs[inputId] = value; + return true; + } else { + TF_RUNTIME_ERROR("Invalid input for Combine Shaders node: must be of shader type, type=%s", value.GetTypeName().c_str()); + } + } else { + TF_RUNTIME_ERROR("Invalid input for Combine Shaders node: must be `surface` or `displacement` or `volume`, inputId=%s", inputId.GetText()); + } + + return false; + } + + static RprUsd_RprNodeInfo* GetInfo() { + auto ret = new RprUsd_RprNodeInfo; + auto& nodeInfo = *ret; + + nodeInfo.name = "rpr_combine"; + nodeInfo.uiName = "RPR Combine Shaders"; + nodeInfo.uiFolder = "Shaders"; + + RprUsd_RprNodeInput surfaceInput(RprUsdMaterialNodeElement::kSurfaceShader); + surfaceInput.name = "surface"; + surfaceInput.uiName = "Surface Shader"; + nodeInfo.inputs.push_back(surfaceInput); + + RprUsd_RprNodeInput displacementInput(RprUsdMaterialNodeElement::kDisplacementShader); + displacementInput.name = "displacement"; + displacementInput.uiName = "Displacement Shader"; + nodeInfo.inputs.push_back(displacementInput); + + RprUsd_RprNodeInput volumeInput(RprUsdMaterialNodeElement::kVolumeShader); + volumeInput.name = "volume"; + volumeInput.uiName = "Volume Shader"; + nodeInfo.inputs.push_back(volumeInput); + + RprUsd_RprNodeOutput outputSurface(RprUsdMaterialNodeElement::kSurfaceShader); + outputSurface.name = "surface"; + nodeInfo.outputs.push_back(outputSurface); + + RprUsd_RprNodeOutput outputDisplacement(RprUsdMaterialNodeElement::kDisplacementShader); + outputDisplacement.name = "displacement"; + nodeInfo.outputs.push_back(outputDisplacement); + + RprUsd_RprNodeOutput outputVolume(RprUsdMaterialNodeElement::kVolumeShader); + outputVolume.name = "volume"; + nodeInfo.outputs.push_back(outputVolume); + + return ret; + } + +private: + std::map m_outputs; +}; + +ARCH_CONSTRUCTOR(RprUsd_InitCombineShadersNode, 255, void) { + auto nodeInfo = RprUsd_RprCombineShadersNode::GetInfo(); + RprUsdMaterialRegistry::GetInstance().Register( + TfToken(nodeInfo->name, TfToken::Immortal), + [](RprUsd_MaterialBuilderContext* context, + std::map const& parameters) { + auto node = new RprUsd_RprCombineShadersNode(); + for (auto& entry : parameters) node->SetInput(entry.first, entry.second); + return node; + }, + nodeInfo); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/displaceNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/displaceNode.cpp new file mode 100644 index 000000000..79c2867f1 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/rpr/displaceNode.cpp @@ -0,0 +1,148 @@ +/************************************************************************ +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/vec2f.h" + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEFINE_PRIVATE_TOKENS(RprUsdRprDisplaceNodeTokens, + (minscale) + (maxscale) + (in) +); + +/// \class RprUsd_RprDisplaceNode +/// +/// The node that maps RPR displacement functionality one-to-one: +/// - `in` expects rpr::MaterialNode as input and corresponds to `rprShapeSetDisplacementMaterial` +/// - `minscale` and `maxscale` inputs are expected to be of float type and correspond to `rprShapeSetDisplacementScale` +/// +class RprUsd_RprDisplaceNode : public RprUsd_MaterialNode { +public: + RprUsd_RprDisplaceNode( + RprUsd_MaterialBuilderContext* ctx) + : m_ctx(ctx) + , m_displacementScale(0, 1) { + + } + ~RprUsd_RprDisplaceNode() override = default; + + VtValue GetOutput(TfToken const& outputId) override { + if (m_output.IsEmpty()) { + return VtValue(); + } + + m_ctx->displacementScale = VtValue(m_displacementScale); + return m_output; + } + + bool SetInput( + TfToken const& inputId, + VtValue const& value) override { + if (inputId == RprUsdRprDisplaceNodeTokens->minscale) { + if (value.IsHolding()) { + m_displacementScale[0] = value.UncheckedGet(); + } else { + TF_RUNTIME_ERROR("Input `minscale` has invalid type: %s, expected - float", value.GetTypeName().c_str()); + m_displacementScale[0] = 0.0f; + return false; + } + } else if (inputId == RprUsdRprDisplaceNodeTokens->maxscale) { + if (value.IsHolding()) { + m_displacementScale[1] = value.UncheckedGet(); + } else { + TF_RUNTIME_ERROR("Input `maxscale` has invalid type: %s, expected - float", value.GetTypeName().c_str()); + m_displacementScale[1] = 1.0f; + return false; + } + } else if (inputId == RprUsdRprDisplaceNodeTokens->in) { + if (value.IsHolding>()) { + m_output = value; + } else { + auto vec = GetRprFloat(value); + if (!GfIsEqual(vec, GfVec4f(0.0f))) { + if (!m_scalarDisplaceNode) { + m_scalarDisplaceNode.reset(new RprUsd_BaseRuntimeNode(RPR_MATERIAL_NODE_CONSTANT_TEXTURE, m_ctx)); + } + + m_scalarDisplaceNode->SetInput(RPR_MATERIAL_INPUT_VALUE, value); + m_output = VtValue(m_scalarDisplaceNode); + } else { + m_scalarDisplaceNode = nullptr; + m_output = VtValue(); + } + } + } + + return true; + } + + static RprUsd_RprNodeInfo* GetInfo() { + auto ret = new RprUsd_RprNodeInfo; + auto& nodeInfo = *ret; + + nodeInfo.name = "rpr_displace"; + nodeInfo.uiName = "RPR Displace"; + nodeInfo.uiFolder = "Shaders"; + + RprUsd_RprNodeInput input(RprUsdMaterialNodeElement::kFloat); + input.uiSoftMin = "0"; + input.uiSoftMax = "1"; + + input.name = "in"; + input.uiName = "Displacement"; + input.valueString = "0"; + nodeInfo.inputs.push_back(input); + + input.name = "minscale"; + input.uiName = "Minimum Scale"; + nodeInfo.inputs.push_back(input); + + input.name = "maxscale"; + input.uiName = "Maximum Scale"; + input.valueString = "1"; + nodeInfo.inputs.push_back(input); + + RprUsd_RprNodeOutput output(RprUsdMaterialNodeElement::kDisplacementShader); + output.name = "displacement"; + nodeInfo.outputs.push_back(output); + + return ret; + } + +private: + RprUsd_MaterialBuilderContext* m_ctx; + GfVec2f m_displacementScale; + std::shared_ptr m_scalarDisplaceNode; + VtValue m_output; +}; + +ARCH_CONSTRUCTOR(RprUsd_InitDisplaceNode, 255, void) { + auto nodeInfo = RprUsd_RprDisplaceNode::GetInfo(); + RprUsdMaterialRegistry::GetInstance().Register( + TfToken(nodeInfo->name, TfToken::Immortal), + [](RprUsd_MaterialBuilderContext* context, + std::map const& parameters) { + auto node = new RprUsd_RprDisplaceNode(context); + for (auto& entry : parameters) node->SetInput(entry.first, entry.second); + return node; + }, + nodeInfo); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.cpp b/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.cpp new file mode 100644 index 000000000..6a69f2913 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.cpp @@ -0,0 +1,381 @@ +/************************************************************************ +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 "materialXNode.h" +#include "baseNode.h" +#include "nodeInfo.h" + +#include "pxr/usd/sdf/assetPath.h" +#include "pxr/base/arch/attributes.h" +#include "pxr/imaging/rprUsd/error.h" +#include "pxr/imaging/rprUsd/coreImage.h" + +#include + +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEFINE_PUBLIC_TOKENS(RprUsdRprMaterialXNodeTokens, RPRUSD_RPR_MATERIALX_NODE_TOKENS); + +static rpr_material_node ReleaseOutputNodeOwnership(RPRMtlxLoader::Result* mtlx, RPRMtlxLoader::OutputType outputType) { + auto idx = mtlx->rootNodeIndices[outputType]; + auto ret = mtlx->nodes[idx]; + mtlx->nodes[idx] = nullptr; + return ret; +} + +class RprUsd_RprMaterialXNode : public RprUsd_MaterialNode { +public: + RprUsd_RprMaterialXNode(RprUsd_MaterialBuilderContext* ctx) + : m_ctx(ctx) { + + } + + ~RprUsd_RprMaterialXNode() override = default; + + VtValue GetOutput(TfToken const& outputId) override { + if (m_isDirty) { + m_isDirty = false; + + UpdateNodeOutput(); + } + + if (outputId == HdMaterialTerminalTokens->surface) { + if (m_surfaceNode) { + return VtValue(m_surfaceNode); + } + } else if (outputId == HdMaterialTerminalTokens->displacement) { + if (m_displacementNode) { + return VtValue(m_displacementNode); + } + } + + return VtValue(); + } + + bool SetInput( + TfToken const& inputId, + VtValue const& value) override { + if (inputId == RprUsdRprMaterialXNodeTokens->file) { + if (value.IsHolding()) { + auto& assetPath = value.UncheckedGet(); + + m_mtlxFilepath = assetPath.GetResolvedPath(); + ResetNodeOutput(); + + return true; + } else { + TF_RUNTIME_ERROR("[%s] file input should be of SdfAssetPath type: %s", + RprUsdRprMaterialXNodeTokens->rpr_materialx_node.GetText(), value.GetTypeName().c_str()); + return false; + } + } else if (inputId == RprUsdRprMaterialXNodeTokens->surfaceElement) { + return SetRenderElement(RPRMtlxLoader::kOutputSurface, value); + } else if (inputId == RprUsdRprMaterialXNodeTokens->displacementElement) { + return SetRenderElement(RPRMtlxLoader::kOutputDisplacement, value); + } else if (inputId == RprUsdRprMaterialXNodeTokens->stPrimvarName) { + if (value.IsHolding()) { + m_ctx->uvPrimvarName = TfToken(value.UncheckedGet()); + return true; + } else { + TF_RUNTIME_ERROR("[%s] file input should be of string type: %s", + RprUsdRprMaterialXNodeTokens->rpr_materialx_node.GetText(), value.GetTypeName().c_str()); + return false; + } + } + + TF_RUNTIME_ERROR("[%s] Unknown input %s", + RprUsdRprMaterialXNodeTokens->rpr_materialx_node.GetText(), inputId.GetText()); + return false; + } + + bool SetRenderElement(RPRMtlxLoader::OutputType outputType, VtValue const& value) { + if (!value.IsHolding()) { + TF_RUNTIME_ERROR("[rpr_materialx_node] Invalid type of render element: %s", + value.GetTypeName().c_str()); + return false; + } + + auto& namePath = value.UncheckedGet(); + if (m_selectedRenderElements[outputType] != namePath) { + m_selectedRenderElements[outputType] = namePath; + + ResetNodeOutput(); + } + + return true; + } + + void ResetNodeOutput() { + m_isDirty = true; + m_surfaceNode.reset(); + m_displacementNode.reset(); + } + + bool UpdateNodeOutput() { + auto basePath = TfGetPathName(m_mtlxFilepath); + + if (m_ctx->mtlxLoader) { + RPRMtlxLoader::Result mtlx; + try { + auto mtlxDoc = MaterialX::createDocument(); + MaterialX::readFromXmlFile(mtlxDoc, m_mtlxFilepath); + mtlxDoc->importLibrary(m_ctx->mtlxLoader->GetStdlib()); + + rpr_material_system matSys; + if (RPR_ERROR_CHECK(m_ctx->rprContext->GetInfo(RPR_CONTEXT_LIST_CREATED_MATERIALSYSTEM, sizeof(matSys), &matSys, nullptr), "Failed to get rpr material system")) { + return false; + } + + bool hasAnySelectedElement = std::any_of(std::begin(m_selectedRenderElements), std::end(m_selectedRenderElements), + [](std::string const& elem) { + return !elem.empty(); + } + ); + std::string* selectedElements = hasAnySelectedElement ? m_selectedRenderElements : nullptr; + + MaterialX::FileSearchPath searchPath(basePath); + mtlx = m_ctx->mtlxLoader->Load(mtlxDoc.get(), selectedElements, searchPath, matSys); + } catch (MaterialX::ExceptionParseError& e) { + fprintf(stderr, "Failed to parse %s: %s\n", m_mtlxFilepath.c_str(), e.what()); + } catch (MaterialX::ExceptionFileMissing& e) { + fprintf(stderr, "Failed to parse %s: no such file - %s\n", m_mtlxFilepath.c_str(), e.what()); + } + + if (!mtlx.nodes) { + return false; + } + + // Check if mtlx has more than one output + // + int numOutputs = 0; + for (auto index : mtlx.rootNodeIndices) { + if (index != RPRMtlxLoader::Result::kInvalidRootNodeIndex) { + numOutputs++; + if (numOutputs > 1) { + break; + } + } + } + + using RetainedImages = std::vector>; + RetainedImages* retainedImagesPtr; + auto mtlxPtr = &mtlx; + + if (numOutputs > 1) { + // Share mtlx and retained images between all output nodes + // + struct SharedData { + RPRMtlxLoader::Result mtlx; + RetainedImages retainedImages; + + ~SharedData() { + RPRMtlxLoader::Release(&mtlx); + } + }; + auto sharedData = std::make_shared(); + sharedData->mtlx = mtlx; + retainedImagesPtr = &sharedData->retainedImages; + mtlxPtr = &sharedData->mtlx; + + class OutputWrapNode : public rpr::MaterialNode { + public: + OutputWrapNode(rpr::Context& ctx, std::shared_ptr sharedData, RPRMtlxLoader::OutputType output) + : rpr::MaterialNode(ctx, ReleaseOutputNodeOwnership(&sharedData->mtlx, output)) + , _sharedData(std::move(sharedData)) {} + ~OutputWrapNode() override = default; + + private: + std::shared_ptr _sharedData; + }; + + auto createOutputWrapNode = [&sharedData, this](RPRMtlxLoader::OutputType outputType) -> std::unique_ptr { + if (sharedData->mtlx.rootNodeIndices[outputType] == RPRMtlxLoader::Result::kInvalidRootNodeIndex) { + return nullptr; + } + return std::make_unique(*m_ctx->rprContext, sharedData, outputType); + }; + m_surfaceNode = createOutputWrapNode(RPRMtlxLoader::kOutputSurface); + m_displacementNode = createOutputWrapNode(RPRMtlxLoader::kOutputDisplacement); + + } else { + // Find the only existing output + RPRMtlxLoader::OutputType outputType; + for (int i = 0; i < RPRMtlxLoader::kOutputsTotal; ++i) { + if (mtlx.rootNodeIndices[i] != RPRMtlxLoader::Result::kInvalidRootNodeIndex) { + outputType = RPRMtlxLoader::OutputType(i); + break; + } + } + + struct OutputWrapNode : public rpr::MaterialNode { + OutputWrapNode(rpr::Context& ctx, RPRMtlxLoader::Result mtlx, RPRMtlxLoader::OutputType output) + : rpr::MaterialNode(ctx, ReleaseOutputNodeOwnership(&mtlx, output)) + , mtlx(mtlx) {} + ~OutputWrapNode() override { + RPRMtlxLoader::Release(&mtlx); + } + + RPRMtlxLoader::Result mtlx; + std::vector> retainedImages; + }; + auto wrapNode = std::make_unique(*m_ctx->rprContext, mtlx, outputType); + retainedImagesPtr = &wrapNode->retainedImages; + mtlxPtr = &wrapNode->mtlx; + + if (outputType == RPRMtlxLoader::kOutputSurface) { + m_surfaceNode = std::move(wrapNode); + } else if (outputType == RPRMtlxLoader::kOutputDisplacement) { + m_displacementNode = std::move(wrapNode); + } + } + + // Commit all textures + // + if (mtlxPtr->imageNodes && (m_surfaceNode || m_displacementNode)) { + RprUsdMaterialRegistry::TextureCommit textureCommit = {}; + for (size_t i = 0; i < mtlxPtr->numImageNodes; ++i) { + auto& mtlxImageNode = mtlxPtr->imageNodes[i]; + + textureCommit.filepath = std::move(mtlxImageNode.file); + + std::string& addressmode = !mtlxImageNode.uaddressmode.empty() ? mtlxImageNode.uaddressmode : mtlxImageNode.vaddressmode; + if (!addressmode.empty()) { + if (mtlxImageNode.uaddressmode != mtlxImageNode.vaddressmode) { + TF_WARN("RPR does not support different address modes on an image. Using %s for %s image", + addressmode.c_str(), textureCommit.filepath.c_str()); + } + + textureCommit.wrapType = RPR_IMAGE_WRAP_TYPE_REPEAT; + if (addressmode == "constant") { + TF_WARN("The constant uv address mode is not supported. Falling back to periodic."); + } else if (addressmode == "clamp") { + textureCommit.wrapType = RPR_IMAGE_WRAP_TYPE_CLAMP_TO_EDGE; + } else if (addressmode == "mirror") { + textureCommit.wrapType = RPR_IMAGE_WRAP_TYPE_MIRRORED_REPEAT; + } + } + + rpr_material_node rprImageNode = mtlxImageNode.rprNode; + textureCommit.setTextureCallback = [retainedImagesPtr, rprImageNode](std::shared_ptr const& image) { + if (!image) return; + + auto imageData = rpr::GetRprObject(image->GetRootImage()); + if (!RPR_ERROR_CHECK(rprMaterialNodeSetInputImageDataByKey(rprImageNode, RPR_MATERIAL_INPUT_DATA, imageData), "Failed to set material node image data input")) { + retainedImagesPtr->push_back(image); + } + }; + + RprUsdMaterialRegistry::GetInstance().CommitTexture(std::move(textureCommit)); + } + + delete[] mtlxPtr->imageNodes; + mtlxPtr->imageNodes = nullptr; + mtlxPtr->numImageNodes = 0; + } + + return m_surfaceNode || m_displacementNode; + } + + std::ifstream mtlxFile(m_mtlxFilepath); + if (!mtlxFile.good()) { + TF_RUNTIME_ERROR("Failed to open \"%s\" file", m_mtlxFilepath.c_str()); + return false; + } + + mtlxFile.seekg(0, std::ios::end); + auto fileSize = mtlxFile.tellg(); + if (fileSize == 0) { + TF_RUNTIME_ERROR("Empty file: \"%s\"", m_mtlxFilepath.c_str()); + return false; + } + + auto xmlData = std::make_unique(fileSize); + mtlxFile.seekg(0); + mtlxFile.read(&xmlData[0], fileSize); + + rpr::Status status; + m_surfaceNode.reset(m_ctx->rprContext->CreateMaterialXNode(xmlData.get(), basePath.c_str(), 0, nullptr, nullptr, &status)); + + if (!m_surfaceNode) { + RPR_ERROR_CHECK(status, "Failed to create materialX node", m_ctx->rprContext); + } + return m_surfaceNode != nullptr; + } + + static RprUsd_RprNodeInfo* GetInfo() { + auto ret = new RprUsd_RprNodeInfo; + auto& nodeInfo = *ret; + + nodeInfo.name = RprUsdRprMaterialXNodeTokens->rpr_materialx_node.GetText(); + nodeInfo.uiName = "RPR MaterialX"; + nodeInfo.uiFolder = "Shaders"; + + RprUsd_RprNodeInput fileInput(RprUsdMaterialNodeElement::kFilepath); + fileInput.name = RprUsdRprMaterialXNodeTokens->file.GetText(); + fileInput.uiName = "File"; + nodeInfo.inputs.push_back(fileInput); + + RprUsd_RprNodeInput stPrimvarNameInput(RprUsdMaterialNodeElement::kString); + stPrimvarNameInput.name = RprUsdRprMaterialXNodeTokens->stPrimvarName.GetText(); + stPrimvarNameInput.uiName = "UV Primvar Name"; + stPrimvarNameInput.valueString = "st"; + nodeInfo.inputs.push_back(stPrimvarNameInput); + + RprUsd_RprNodeInput surfaceElementInput(RprUsdMaterialNodeElement::kString); + surfaceElementInput.name = RprUsdRprMaterialXNodeTokens->surfaceElement.GetText(); + surfaceElementInput.uiName = "Surface Element"; + nodeInfo.inputs.push_back(surfaceElementInput); + + RprUsd_RprNodeInput displacementElementInput(RprUsdMaterialNodeElement::kString); + displacementElementInput.name = RprUsdRprMaterialXNodeTokens->displacementElement.GetText(); + displacementElementInput.uiName = "Displacement Element"; + nodeInfo.inputs.push_back(displacementElementInput); + + RprUsd_RprNodeOutput surfaceOutput(RprUsdMaterialNodeElement::kSurfaceShader); + surfaceOutput.name = "surface"; + nodeInfo.outputs.push_back(surfaceOutput); + + RprUsd_RprNodeOutput displacementOutput(RprUsdMaterialNodeElement::kDisplacementShader); + displacementOutput.name = "displacement"; + nodeInfo.outputs.push_back(displacementOutput); + + return ret; + } + +private: + RprUsd_MaterialBuilderContext* m_ctx; + std::string m_mtlxFilepath; + std::string m_selectedRenderElements[RPRMtlxLoader::kOutputsTotal]; + + bool m_isDirty = true; + std::shared_ptr m_surfaceNode; + std::shared_ptr m_displacementNode; +}; + +ARCH_CONSTRUCTOR(RprUsd_InitMaterialXNode, 255, void) { + auto nodeInfo = RprUsd_RprMaterialXNode::GetInfo(); + RprUsdMaterialRegistry::GetInstance().Register( + RprUsdRprMaterialXNodeTokens->rpr_materialx_node, + [](RprUsd_MaterialBuilderContext* context, + std::map const& parameters) { + auto node = new RprUsd_RprMaterialXNode(context); + for (auto& entry : parameters) node->SetInput(entry.first, entry.second); + return node; + }, + nodeInfo); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.h b/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.h new file mode 100644 index 000000000..90e1f61d5 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/rpr/materialXNode.h @@ -0,0 +1,33 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_MATERIAL_NODES_RPR_MATERIALX_NODE_H +#define RPRUSD_MATERIAL_NODES_RPR_MATERIALX_NODE_H + +#include "pxr/base/tf/staticTokens.h" +#include "pxr/imaging/rprUsd/api.h" + +PXR_NAMESPACE_OPEN_SCOPE + +#define RPRUSD_RPR_MATERIALX_NODE_TOKENS \ + (rpr_materialx_node) \ + (file) \ + (surfaceElement) \ + (displacementElement) \ + (stPrimvarName) + +TF_DECLARE_PUBLIC_TOKENS(RprUsdRprMaterialXNodeTokens, RPRUSD_API, RPRUSD_RPR_MATERIALX_NODE_TOKENS); + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_MATERIAL_NODES_RPR_MATERIALX_NODE_H diff --git a/pxr/imaging/rprUsd/materialNodes/rpr/nodeInfo.h b/pxr/imaging/rprUsd/materialNodes/rpr/nodeInfo.h new file mode 100644 index 000000000..5364a904a --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/rpr/nodeInfo.h @@ -0,0 +1,77 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_MATERIAL_NODES_RPR_NODE_INFO_H +#define RPRUSD_MATERIAL_NODES_RPR_NODE_INFO_H + +#include "pxr/imaging/rprUsd/materialRegistry.h" + +PXR_NAMESPACE_OPEN_SCOPE + +struct RprUsd_RprNodeInput : public RprUsdMaterialNodeInput { + RprUsd_RprNodeInput(RprUsdMaterialNodeInput::Type type) : RprUsdMaterialNodeInput(type) {} + const char* GetName() const override { return GetCStr(name); } + const char* GetUIName() const override { return GetCStr(uiName); } + const char* GetUIMin() const override { return GetCStr(uiMin); } + const char* GetUISoftMin() const override { return GetCStr(uiSoftMin); } + const char* GetUIMax() const override { return GetCStr(uiMax); } + const char* GetUISoftMax() const override { return GetCStr(uiSoftMax); } + 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::string name; + std::string uiName; + std::string uiMin; + std::string uiSoftMin; + std::string uiMax; + std::string uiSoftMax; + std::string uiFolder; + std::string docString; + std::string valueString; + std::vector m_tokenValues; +}; + +struct RprUsd_RprNodeOutput : public RprUsdMaterialNodeElement { + RprUsd_RprNodeOutput(RprUsdMaterialNodeElement::Type type) : RprUsdMaterialNodeElement(type) {} + const char* GetName() const override { return !name.empty() ? name.c_str() : nullptr; }; + const char* GetUIName() const override { return !uiName.empty() ? uiName.c_str() : nullptr; }; + const char* GetDocString() const override { return !docString.empty() ? docString.c_str() : nullptr; }; + + std::string name; + std::string uiName; + std::string docString; +}; + +struct RprUsd_RprNodeInfo : public RprUsdMaterialNodeInfo { + const char* GetName() const override { return GetCStr(name); } + const char* GetUIName() const override { return GetCStr(uiName); } + const char* GetUIFolder() const override { return GetCStr(uiFolder); } + + size_t GetNumInputs() const override { return inputs.size(); } + RprUsdMaterialNodeInput const* GetInput(size_t idx) const override { return &inputs[idx]; } + + size_t GetNumOutputs() const override { return outputs.size(); } + RprUsdMaterialNodeElement const* GetOutput(size_t idx) const override { return &outputs[idx]; } + + std::string name; + std::string uiName; + std::string uiFolder; + std::vector inputs; + std::vector outputs; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_MATERIAL_NODES_RPR_NODE_INFO_H diff --git a/pxr/imaging/rprUsd/materialNodes/usdNode.cpp b/pxr/imaging/rprUsd/materialNodes/usdNode.cpp new file mode 100644 index 000000000..ff84b8322 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/usdNode.cpp @@ -0,0 +1,541 @@ +/************************************************************************ +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 "usdNode.h" +#include "rpr/arithmeticNode.h" + +#include "pxr/imaging/rprUsd/materialHelpers.h" +#include "pxr/imaging/rprUsd/materialMappings.h" +#include "pxr/imaging/rprUsd/imageCache.h" +#include "pxr/imaging/rprUsd/coreImage.h" +#include "pxr/imaging/rprUsd/error.h" +#include "pxr/base/arch/attributes.h" +#include "pxr/base/tf/staticTokens.h" +#include "pxr/usd/sdf/assetPath.h" +#include "pxr/base/gf/matrix3f.h" +#include "pxr/base/gf/vec2f.h" + +PXR_NAMESPACE_OPEN_SCOPE + +//------------------------------------------------------------------------------ +// RprUsd_UsdPreviewSurface +//------------------------------------------------------------------------------ + +TF_DEFINE_PRIVATE_TOKENS(UsdPreviewSurfaceTokens, + (diffuseColor) + (emissiveColor) + (useSpecularWorkflow) + (specularColor) + (metallic) + (roughness) + (clearcoat) + (clearcoatRoughness) + (opacity) + (opacityThreshold) + (ior) + (displacement) + (normal) +); + +RprUsd_UsdPreviewSurface::RprUsd_UsdPreviewSurface( + RprUsd_MaterialBuilderContext* ctx, + std::map const& hydraParameters) + : RprUsd_BaseRuntimeNode(RPR_MATERIAL_NODE_UBERV2, ctx) { + + m_albedo = VtValue(GfVec4f(1.0f)); + m_reflection = VtValue(GfVec4f(1.0f)); + + auto setInput = [&hydraParameters, this](TfToken const& id, VtValue defaultValue) { + auto it = hydraParameters.find(id); + if (it == hydraParameters.end()) { + SetInput(id, defaultValue); + } else { + SetInput(id, it->second); + } + }; + setInput(UsdPreviewSurfaceTokens->diffuseColor, VtValue(GfVec3f(0.18f))); + setInput(UsdPreviewSurfaceTokens->emissiveColor, VtValue(GfVec3f(0.0f))); + setInput(UsdPreviewSurfaceTokens->useSpecularWorkflow, VtValue(0)); + setInput(UsdPreviewSurfaceTokens->specularColor, VtValue(GfVec3f(0.0f))); + setInput(UsdPreviewSurfaceTokens->metallic, VtValue(0.0f)); + setInput(UsdPreviewSurfaceTokens->roughness, VtValue(0.5f)); + setInput(UsdPreviewSurfaceTokens->clearcoat, VtValue(0.0f)); + setInput(UsdPreviewSurfaceTokens->clearcoatRoughness, VtValue(0.01f)); + setInput(UsdPreviewSurfaceTokens->opacity, VtValue(1.0f)); + setInput(UsdPreviewSurfaceTokens->opacityThreshold, VtValue(0.0f)); + setInput(UsdPreviewSurfaceTokens->ior, VtValue(1.5f)); + setInput(UsdPreviewSurfaceTokens->displacement, VtValue(0.0f)); + + m_rprNode->SetInput(RPR_MATERIAL_INPUT_UBER_REFLECTION_WEIGHT, 1.0f, 1.0f, 1.0f, 1.0f); +} + +bool RprUsd_UsdPreviewSurface::SetInput( + TfToken const& inputId, + VtValue const& value) { + if (UsdPreviewSurfaceTokens->diffuseColor == inputId) { + m_albedo = value; + return (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_DIFFUSE_COLOR, value) == RPR_SUCCESS) && + (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_REFRACTION_COLOR, value) == RPR_SUCCESS); + } else if (UsdPreviewSurfaceTokens->emissiveColor == inputId) { + if (!m_emissiveWeightNode) { + m_emissiveWeightNode = RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_GREATER, m_ctx); + } + m_emissiveWeightNode->SetInput(0, value); + m_emissiveWeightNode->SetInput(1, VtValue(GfVec4f(0.0f))); + auto emissionWeight = m_emissiveWeightNode->GetOutput(); + + return (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_EMISSION_WEIGHT, emissionWeight) == RPR_SUCCESS) && + (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_EMISSION_COLOR, value) == RPR_SUCCESS); + } else if (UsdPreviewSurfaceTokens->useSpecularWorkflow == inputId) { + m_useSpecular = value.Get(); + } else if (UsdPreviewSurfaceTokens->specularColor == inputId) { + m_reflection = value; + } else if (UsdPreviewSurfaceTokens->metallic == inputId) { + return SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_REFLECTION_METALNESS, value) == RPR_SUCCESS; + } else if (UsdPreviewSurfaceTokens->roughness == inputId) { + return (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_DIFFUSE_ROUGHNESS, value) == RPR_SUCCESS) && + (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_REFLECTION_ROUGHNESS, value) == RPR_SUCCESS) && + (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_REFRACTION_ROUGHNESS, value) == RPR_SUCCESS); + } else if (UsdPreviewSurfaceTokens->clearcoat == inputId) { + return SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_COATING_WEIGHT, value) == RPR_SUCCESS; + } else if (UsdPreviewSurfaceTokens->clearcoatRoughness == inputId) { + return SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_COATING_ROUGHNESS, value) == RPR_SUCCESS; + } else if (UsdPreviewSurfaceTokens->opacity == inputId) { + if (!m_refractionWeightNode) { + m_refractionWeightNode = RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_SUB, m_ctx); + } + m_refractionWeightNode->SetInput(0, VtValue(GfVec4f(1.0f))); + m_refractionWeightNode->SetInput(1, value); + auto refractionWeight = m_refractionWeightNode->GetOutput(); + + return (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_DIFFUSE_WEIGHT, value) == RPR_SUCCESS) && + (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_REFRACTION_WEIGHT, refractionWeight) == RPR_SUCCESS); + } else if (UsdPreviewSurfaceTokens->opacityThreshold == inputId) { + + } else if (UsdPreviewSurfaceTokens->ior == inputId) { + return SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_REFRACTION_IOR, value) == RPR_SUCCESS; + } else if (UsdPreviewSurfaceTokens->displacement == inputId) { + if (value.IsHolding()) { + m_displacementOutput = value; + } else { + auto vec = GetRprFloat(value); + if (!GfIsEqual(vec, GfVec4f(0.0f))) { + if (!m_displaceNode) { + m_displaceNode.reset(new RprUsd_BaseRuntimeNode(RPR_MATERIAL_NODE_CONSTANT_TEXTURE, m_ctx)); + } + + m_displaceNode->SetInput(RPR_MATERIAL_INPUT_VALUE, value); + m_displacementOutput = VtValue(m_displaceNode); + } else { + m_displaceNode = nullptr; + m_displacementOutput = VtValue(); + } + } + } else if (UsdPreviewSurfaceTokens->normal == inputId) { + if (value.IsHolding()) { + if (!m_normalMapNode) { + m_normalMapNode.reset(new RprUsd_BaseRuntimeNode(RPR_MATERIAL_NODE_NORMAL_MAP, m_ctx)); + } + m_normalMapNode->SetInput(RPR_MATERIAL_INPUT_COLOR, value); + + auto normalMapOutput = m_normalMapNode->GetOutput(TfToken()); + return (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_DIFFUSE_NORMAL, normalMapOutput) == RPR_SUCCESS) && + (SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_REFLECTION_NORMAL, normalMapOutput) == RPR_SUCCESS); + } else { + TF_RUNTIME_ERROR("`normal` input should be of material node type - %s", value.GetTypeName().c_str()); + return false; + } + } else { + TF_CODING_ERROR("Unknown UsdPreviewSurface parameter %s: %s", inputId.GetText(), value.GetTypeName().c_str()); + return false; + } + + return true; +} + +VtValue RprUsd_UsdPreviewSurface::GetOutput(TfToken const& outputId) { + if (HdMaterialTerminalTokens->surface == outputId) { + if (m_useSpecular) { + RPR_ERROR_CHECK(m_rprNode->SetInput(RPR_MATERIAL_INPUT_UBER_REFLECTION_MODE, RPR_UBER_MATERIAL_IOR_MODE_PBR), "Failed to set material input"); + SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_REFLECTION_COLOR, m_reflection); + } else { + RPR_ERROR_CHECK(m_rprNode->SetInput(RPR_MATERIAL_INPUT_UBER_REFLECTION_MODE, RPR_UBER_MATERIAL_IOR_MODE_METALNESS), "Failed to set material input"); + SetRprInput(m_rprNode.get(), RPR_MATERIAL_INPUT_UBER_REFLECTION_COLOR, m_albedo); + } + + return RprUsd_BaseRuntimeNode::GetOutput(outputId); + } else if (HdMaterialTerminalTokens->displacement == outputId) { + return m_displacementOutput; + } + + return VtValue(); +} + +//------------------------------------------------------------------------------ +// RprUsd_UsdUVTexture +//------------------------------------------------------------------------------ + +TF_DEFINE_PUBLIC_TOKENS(RprUsd_UsdUVTextureTokens, RPRUSD_USD_UV_TEXTURE_TOKENS); + +namespace { + +rpr::ImageWrapType GetWrapType(VtValue const& value) { + if (value.IsHolding()) { + auto& id = value.UncheckedGet(); + if (id == RprUsd_UsdUVTextureTokens->black) { + return RPR_IMAGE_WRAP_TYPE_CLAMP_ZERO; + } else if (id == RprUsd_UsdUVTextureTokens->clamp) { + return RPR_IMAGE_WRAP_TYPE_CLAMP_TO_EDGE; + } else if (id == RprUsd_UsdUVTextureTokens->mirror) { + return RPR_IMAGE_WRAP_TYPE_MIRRORED_REPEAT; + } else if (id == RprUsd_UsdUVTextureTokens->repeat) { + return RPR_IMAGE_WRAP_TYPE_REPEAT; + } else { + TF_CODING_ERROR("Unknown image wrap type: %s", id.GetText()); + } + } + + return {}; +} + +} // namespace anonymous + +RprUsd_UsdUVTexture::RprUsd_UsdUVTexture( + RprUsd_MaterialBuilderContext* ctx, + std::map const& hydraParameters) + : m_ctx(ctx) { + + auto fileIt = hydraParameters.find(RprUsd_UsdUVTextureTokens->file); + if (fileIt == hydraParameters.end()) { + throw RprUsd_NodeError("UsdUVTexture requires file parameter"); + } + + RprUsdMaterialRegistry::TextureCommit textureCommit = {}; + + if (fileIt->second.IsHolding()) { + auto& assetPath = fileIt->second.UncheckedGet(); + if (assetPath.GetResolvedPath().empty()) { + textureCommit.filepath = assetPath.GetAssetPath(); + } else { + textureCommit.filepath = assetPath.GetResolvedPath(); + } + } + + if (textureCommit.filepath.empty()) { + throw RprUsd_NodeError("UsdUVTexture: empty file path"); + } + + auto colorSpaceIt = hydraParameters.find(RprUsd_UsdUVTextureTokens->sourceColorSpace); + if (colorSpaceIt != hydraParameters.end()) { + if (colorSpaceIt->second.IsHolding()) { + auto& colorSpace = colorSpaceIt->second.UncheckedGet(); + if (colorSpace == RprUsd_UsdUVTextureTokens->sRGB) { + textureCommit.colorspace = "srgb"; + } else if (colorSpace == RprUsd_UsdUVTextureTokens->raw) { + textureCommit.colorspace = "raw"; + } else if (colorSpace == RprUsd_UsdUVTextureTokens->colorSpaceAuto) { + textureCommit.colorspace = ""; + } else { + textureCommit.colorspace = colorSpace.GetString(); + } + } + } + + rpr::ImageWrapType wrapS = {}; + auto wrapSIt = hydraParameters.find(RprUsd_UsdUVTextureTokens->wrapS); + if (wrapSIt != hydraParameters.end()) wrapS = GetWrapType(wrapSIt->second); + + rpr::ImageWrapType wrapT = {}; + auto wrapTIt = hydraParameters.find(RprUsd_UsdUVTextureTokens->wrapT); + if (wrapTIt != hydraParameters.end()) wrapT = GetWrapType(wrapTIt->second); + + if (wrapS || wrapT) { + if (wrapS != wrapT) { + TF_RUNTIME_ERROR("RPR renderer does not support different wrapS and wrapT modes"); + } + + textureCommit.wrapType = wrapS ? wrapS : wrapT; + } + + rpr::Status status; + m_imageNode.reset(ctx->rprContext->CreateMaterialNode(RPR_MATERIAL_NODE_IMAGE_TEXTURE, &status)); + if (!m_imageNode) { + throw RprUsd_NodeError(RPR_GET_ERROR_MESSAGE(status, "Failed to create image texture material node")); + } + m_outputs[RprUsd_UsdUVTextureTokens->rgba] = VtValue(m_imageNode); + + textureCommit.setTextureCallback = [this](std::shared_ptr const& image) { + if (!image) return; + + if (!RPR_ERROR_CHECK(m_imageNode->SetInput(RPR_MATERIAL_INPUT_DATA, image->GetRootImage()), "Failed to set material node image data input")) { + m_image = image; + } + }; + + // Texture loading is postponed to allow multi-threading loading. + // + RprUsdMaterialRegistry::GetInstance().CommitTexture(std::move(textureCommit)); + + auto scaleIt = hydraParameters.find(RprUsd_UsdUVTextureTokens->scale); + if (scaleIt != hydraParameters.end() && + scaleIt->second.IsHolding()) { + auto& scale = scaleIt->second.UncheckedGet(); + if (!GfIsEqual(scale, GfVec4f(1.0f))) { + m_scaleNode = RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_MUL, m_ctx); + if (m_scaleNode) { + if (!m_scaleNode->SetInput(0, m_outputs[RprUsd_UsdUVTextureTokens->rgba]) || + !m_scaleNode->SetInput(1, scaleIt->second) || + m_scaleNode->GetOutput().IsEmpty()) { + m_scaleNode = nullptr; + } else { + m_outputs[RprUsd_UsdUVTextureTokens->rgba] = m_scaleNode->GetOutput(); + } + } + } + } + + auto biasIt = hydraParameters.find(RprUsd_UsdUVTextureTokens->bias); + if (biasIt != hydraParameters.end() && + biasIt->second.IsHolding()) { + auto& bias = biasIt->second.UncheckedGet(); + if (!GfIsEqual(bias, GfVec4f(0.0f))) { + m_biasNode = RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_ADD, m_ctx); + if (m_biasNode) { + if (!m_biasNode->SetInput(0, m_outputs[RprUsd_UsdUVTextureTokens->rgba]) || + !m_biasNode->SetInput(1, biasIt->second) || + m_biasNode->GetOutput().IsEmpty()) { + m_biasNode = nullptr; + } else { + m_outputs[RprUsd_UsdUVTextureTokens->rgba] = m_biasNode->GetOutput(); + } + } + } + } +} + +VtValue RprUsd_UsdUVTexture::GetOutput(TfToken const& outputId) { + if (outputId == RprUsd_UsdUVTextureTokens->rgb) { + return GetOutput(RprUsd_UsdUVTextureTokens->rgba); + } + + auto outputIt = m_outputs.find(outputId); + if (outputIt != m_outputs.end()) { + return outputIt->second; + } + + rpr::MaterialNodeArithmeticOperation channel; + if (outputId == RprUsd_UsdUVTextureTokens->r) { + channel = RPR_MATERIAL_NODE_OP_SELECT_X; + } else if (outputId == RprUsd_UsdUVTextureTokens->g) { + channel = RPR_MATERIAL_NODE_OP_SELECT_Y; + } else if (outputId == RprUsd_UsdUVTextureTokens->b) { + channel = RPR_MATERIAL_NODE_OP_SELECT_Z; + } else if (outputId == RprUsd_UsdUVTextureTokens->a) { + channel = RPR_MATERIAL_NODE_OP_SELECT_W; + } else { + TF_CODING_ERROR("Invalid outputId requested: %s", outputId.GetText()); + return VtValue(); + } + + auto selectChannelNode = RprUsd_RprArithmeticNode::Create(channel, m_ctx); + if (selectChannelNode) { + if (selectChannelNode->SetInput(0, m_outputs[RprUsd_UsdUVTextureTokens->rgba]) && + !selectChannelNode->GetOutput().IsEmpty()) { + auto output = selectChannelNode->GetOutput(); + m_outputs[outputId] = output; + return output; + } else { + TF_RUNTIME_ERROR("Failed to set select node inputs"); + } + } else { + TF_RUNTIME_ERROR("Failed to create select node"); + } + + return VtValue(); +} + +bool RprUsd_UsdUVTexture::SetInput( + TfToken const& inputId, + VtValue const& value) { + if (inputId == RprUsd_UsdUVTextureTokens->st) { + return SetRprInput(m_imageNode.get(), RPR_MATERIAL_INPUT_UV, value) == RPR_SUCCESS; + } else { + TF_CODING_ERROR("UsdUVTexture accepts only `st` input"); + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ +// RprUsd_UsdPrimvarReader +//------------------------------------------------------------------------------ + +TF_DEFINE_PRIVATE_TOKENS(UsdPrimvarReaderTokens, + (varname) +); + +RprUsd_UsdPrimvarReader::RprUsd_UsdPrimvarReader( + RprUsd_MaterialBuilderContext* ctx, + std::map const& hydraParameters) + : RprUsd_BaseRuntimeNode(RPR_MATERIAL_NODE_INPUT_LOOKUP, ctx) { + // Primvar reader node is a node that allows the user to read arbitrary primvar. + // There is no such functionality in RPR, at least the one exposed in the same expressive manner. + // + // TODO: I think that we can use buffer sampler node to fully implement primvar reader but this is only an idea + // + // For now, we allow the creation of float2 primvar reader only + // and we use it only for one purpose - control over mesh UVs. + // + // We are using this node to get the name of the primvar that should be interpreted as UVs. + // Also, from the node logic standpoint, this node is fully correct - + // it outputs actual UVs so that the user can manipulate it in any way. + + auto varnameNameIt = hydraParameters.find(UsdPrimvarReaderTokens->varname); + if (varnameNameIt != hydraParameters.end() && + varnameNameIt->second.IsHolding()) { + auto& varname = varnameNameIt->second.UncheckedGet(); + if (!varname.IsEmpty()) { + ctx->uvPrimvarName = varname; + } + } + + auto status = m_rprNode->SetInput(RPR_MATERIAL_INPUT_VALUE, RPR_MATERIAL_NODE_LOOKUP_UV); + if (status != RPR_SUCCESS) { + throw RprUsd_NodeError(RPR_GET_ERROR_MESSAGE(status, "Failed to set lookup node input", ctx->rprContext)); + } +} + +bool RprUsd_UsdPrimvarReader::SetInput( + TfToken const& inputId, + VtValue const& value) { + TF_RUNTIME_ERROR("No inputs supported. Got %s", inputId.GetText()); + return false; +} + +//------------------------------------------------------------------------------ +// RprUsd_UsdTransform2d +//------------------------------------------------------------------------------ + +TF_DEFINE_PRIVATE_TOKENS(UsdTransform2dTokens, + (rotation) + (scale) + (translation) +); + +RprUsd_UsdTransform2d::RprUsd_UsdTransform2d( + RprUsd_MaterialBuilderContext* ctx, + std::map const& hydraParameters) + : m_ctx(ctx) { + + float rotationDegrees = 0.0f; + GfVec2f scale(1.0f); + GfVec2f translation(0.0f); + + auto rotationIt = hydraParameters.find(UsdTransform2dTokens->rotation); + if (rotationIt != hydraParameters.end() && + rotationIt->second.IsHolding()) { + rotationDegrees = rotationIt->second.UncheckedGet(); + } + + auto scaleIt = hydraParameters.find(UsdTransform2dTokens->scale); + if (scaleIt != hydraParameters.end() && + scaleIt->second.IsHolding()) { + scale = scaleIt->second.UncheckedGet(); + } + + auto translationIt = hydraParameters.find(UsdTransform2dTokens->translation); + if (translationIt != hydraParameters.end() && + translationIt->second.IsHolding()) { + translation = translationIt->second.UncheckedGet(); + } + + if (rotationDegrees == 0.0f && + scale == GfVec2f(1.0f) && + translation == GfVec2f(0.0f)) { + throw RprUsd_NodeEmpty(); + } + + float rotation = GfDegreesToRadians(rotationDegrees); + float rotCos = std::cos(rotation); + float rotSin = std::sin(rotation); + + // XXX (Houdini): Proposal of UsdPreviewSurface states that rotation is + // "Counter-clockwise rotation in degrees around the origin", + // by default, the origin is the zero point on the UV coordinate system + // but Houdini's Karma uses origin = (0.5, 0.5). We do the same right now + GfVec2f origin(0.5f); + + GfMatrix3f uvTransform( + 1.0, 0.0, -origin[0], + 0.0, 1.0, -origin[1], + 0.0, 0.0, 1.0); + uvTransform = GfMatrix3f( + scale[0], 0.0, 0.0, + 0.0, scale[1], 0.0, + 0.0, 0.0, 1.0) * uvTransform; + uvTransform = GfMatrix3f( + rotCos, -rotSin, 0.0, + rotSin, rotCos, 0.0, + 0.0f, 0.0f, 1.0f) * uvTransform; + uvTransform[0][2] += translation[0] + origin[0]; + uvTransform[1][2] += translation[1] + origin[1]; + + m_setZToOneNode = RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_ADD, m_ctx); + m_transformNode = RprUsd_RprArithmeticNode::Create(RPR_MATERIAL_NODE_OP_MAT_MUL, m_ctx); + + if (!m_setZToOneNode || !m_transformNode) { + throw RprUsd_NodeError("Failed to create required arithmetic nodes"); + } + + auto& m = uvTransform; + if (!m_setZToOneNode->SetInput(0, VtValue(GfVec4f(0.0f, 0.0f, 1.0f, 0.0f))) || + !m_transformNode->SetInput(0, VtValue(GfVec4f(m[0][0], m[0][1], m[0][2], 0.0f))) || + !m_transformNode->SetInput(1, VtValue(GfVec4f(m[1][0], m[1][1], m[1][2], 0.0f))) || + !m_transformNode->SetInput(2, VtValue(GfVec4f(m[2][0], m[2][1], m[2][2], 0.0f)))) { + throw RprUsd_NodeError("Failed to set arithmetic node inputs"); + } +} + +VtValue RprUsd_UsdTransform2d::GetOutput(TfToken const& outputId) { + return m_transformNode->GetOutput(); +} + +bool RprUsd_UsdTransform2d::SetInput( + TfToken const& inputId, + VtValue const& value) { + return m_setZToOneNode->SetInput(1, value) && m_transformNode->SetInput(3, m_setZToOneNode->GetOutput()); +} + +template +RprUsd_MaterialNode* RprUsd_CreateUsdNode( + RprUsd_MaterialBuilderContext* ctx, + std::map const& params) { + return new T(ctx, params); +} + +template +void RprUsd_RegisterUsdNode(const char* id) { + RprUsdMaterialRegistry::GetInstance().Register( + TfToken(id, TfToken::Immortal), + &RprUsd_CreateUsdNode); +} + +ARCH_CONSTRUCTOR(RprUsd_RegisterUsdNodes, 255, void) { + RprUsd_RegisterUsdNode("UsdPreviewSurface"); + RprUsd_RegisterUsdNode("UsdPrimvarReader_float2"); + RprUsd_RegisterUsdNode("UsdTransform2d"); + RprUsd_RegisterUsdNode("UsdUVTexture"); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialNodes/usdNode.h b/pxr/imaging/rprUsd/materialNodes/usdNode.h new file mode 100644 index 000000000..cd10b5503 --- /dev/null +++ b/pxr/imaging/rprUsd/materialNodes/usdNode.h @@ -0,0 +1,133 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_MATERIAL_NODES_USD_NODE_H +#define RPRUSD_MATERIAL_NODES_USD_NODE_H + +#include "rpr/baseNode.h" + +PXR_NAMESPACE_OPEN_SCOPE + +class RprUsdCoreImage; +class RprUsd_RprArithmeticNode; + +class RprUsd_UsdPreviewSurface : public RprUsd_BaseRuntimeNode { +public: + RprUsd_UsdPreviewSurface( + RprUsd_MaterialBuilderContext* ctx, + std::map const& hydraParameters); + ~RprUsd_UsdPreviewSurface() override = default; + + VtValue GetOutput(TfToken const& outputId) override; + + bool SetInput( + TfToken const& inputId, + VtValue const& value) override; + +private: + bool m_useSpecular; + VtValue m_albedo; + VtValue m_reflection; + + std::unique_ptr m_emissiveWeightNode; + std::unique_ptr m_refractionWeightNode; + std::unique_ptr m_normalMapNode; + + std::shared_ptr m_displaceNode; + VtValue m_displacementOutput; +}; + +#define RPRUSD_USD_UV_TEXTURE_TOKENS \ + (file) \ + (scale) \ + (bias) \ + (wrapS) \ + (wrapT) \ + (black) \ + (clamp) \ + (mirror) \ + (repeat) \ + (sourceColorSpace) \ + (sRGB) \ + (srgblinear) \ + (raw) \ + ((colorSpaceAuto, "auto")) \ + (st) \ + (rgba) \ + (rgb) \ + (r) \ + (g) \ + (b) \ + (a) + +TF_DECLARE_PUBLIC_TOKENS(RprUsd_UsdUVTextureTokens, RPRUSD_USD_UV_TEXTURE_TOKENS); + +class RprUsd_UsdUVTexture : public RprUsd_MaterialNode { +public: + RprUsd_UsdUVTexture( + RprUsd_MaterialBuilderContext* ctx, + std::map const& hydraParameters); + ~RprUsd_UsdUVTexture() override = default; + + VtValue GetOutput(TfToken const& outputId) override; + + bool SetInput( + TfToken const& inputId, + VtValue const& value) override; + +private: + RprUsd_MaterialBuilderContext* m_ctx; + + std::shared_ptr m_image; + std::shared_ptr m_imageNode; + std::shared_ptr m_scaleNode; + std::shared_ptr m_biasNode; + + std::map m_outputs; +}; + +class RprUsd_UsdPrimvarReader : public RprUsd_BaseRuntimeNode { +public: + RprUsd_UsdPrimvarReader( + RprUsd_MaterialBuilderContext* ctx, + std::map const& hydraParameters); + ~RprUsd_UsdPrimvarReader() override = default; + + bool SetInput( + TfToken const& inputId, + VtValue const& value) override; +}; + +class RprUsd_UsdTransform2d : public RprUsd_MaterialNode { +public: + RprUsd_UsdTransform2d( + RprUsd_MaterialBuilderContext* ctx, + std::map const& hydraParameters); + ~RprUsd_UsdTransform2d() override = default; + + VtValue GetOutput(TfToken const& outputId) override; + + bool SetInput( + TfToken const& inputId, + VtValue const& value) override; + +private: + RprUsd_MaterialBuilderContext* m_ctx; + + std::unique_ptr m_setZToOneNode; + std::shared_ptr m_transformNode; +}; + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_MATERIAL_NODES_USD_NODE_H diff --git a/pxr/imaging/rprUsd/materialRegistry.cpp b/pxr/imaging/rprUsd/materialRegistry.cpp new file mode 100644 index 000000000..33ae6f5b6 --- /dev/null +++ b/pxr/imaging/rprUsd/materialRegistry.cpp @@ -0,0 +1,531 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "pxr/imaging/glf/glew.h" +#include "pxr/imaging/glf/uvTextureData.h" +#include "pxr/imaging/glf/image.h" +#include "pxr/imaging/rprUsd/materialRegistry.h" +#include "pxr/imaging/rprUsd/imageCache.h" +#include "pxr/imaging/rprUsd/debugCodes.h" +#include "pxr/imaging/rprUsd/material.h" +#include "pxr/imaging/rprUsd/util.h" +#include "pxr/base/tf/instantiateSingleton.h" +#include "pxr/base/tf/staticTokens.h" +#include "pxr/base/tf/envSetting.h" +#include "pxr/base/tf/pathUtils.h" +#include "pxr/base/tf/getenv.h" +#include "pxr/base/work/loops.h" + +#include "materialNodes/usdNode.h" +#include "materialNodes/houdiniPrincipledShaderNode.h" + +#include "materialNodes/mtlxNode.h" +#include +#include + +PXR_NAMESPACE_OPEN_SCOPE + +TF_INSTANTIATE_SINGLETON(RprUsdMaterialRegistry); + +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"); + +RprUsdMaterialRegistry::RprUsdMaterialRegistry() + : m_materialNetworkSelector(TfGetEnvSetting(RPRUSD_MATERIAL_NETWORK_SELECTOR)) { + +} + +RprUsdMaterialRegistry::~RprUsdMaterialRegistry() = default; + +std::vector const& +RprUsdMaterialRegistry::GetRegisteredNodes() { + if (m_mtlxDefsDirty) { + m_mtlxDefsDirty = false; + + auto RPR = TfGetenv("RPR"); + if (RPR.empty()) { + TF_WARN("RPR environment variable is not set"); + return m_registeredNodes; + } + + if (TfGetEnvSetting(RPRUSD_USE_RPRMTLXLOADER)) { + MaterialX::FilePathVec libraryNames = {"libraries"}; + MaterialX::FileSearchPath searchPath = RPR; + m_mtlxLoader = std::make_unique(); + m_mtlxLoader->SetupStdlib(libraryNames, searchPath); + m_mtlxLoader->SetLogging(TfGetEnvSetting(RPRUSD_RPRMTLXLOADER_ENABLE_LOGGING)); + } + + auto rprMaterialsPath = TfAbsPath(TfNormPath(RPR + "/materials")); + + auto materialFiles = TfGlob(TfNormPath(rprMaterialsPath + "/*/*.mtlx"), ARCH_GLOB_DEFAULT | ARCH_GLOB_NOSORT); + if (materialFiles.empty()) { + TF_WARN("No materials found"); + } + + for (auto& file : materialFiles) { + // 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); + uiFolder = TfNormPath(TfGetPathName(uiFolder)); + if (uiFolder == ".") { + uiFolder = std::string(); + } + + try { + auto mtlxDoc = MaterialX::createDocument(); + MaterialX::readFromXmlFile(mtlxDoc, file); + + auto nodeDefs = mtlxDoc->getNodeDefs(); + if (nodeDefs.size() == 0) { + TF_WARN("\"%s\" file has no node definitions", file.c_str()); + } else { + for (auto& nodeDef : nodeDefs) { + auto shaderInfo = std::make_unique(mtlxDoc, nodeDef, uiFolder); + if (auto factory = shaderInfo->GetFactory()) { + Register(TfToken(shaderInfo->GetName()), factory, shaderInfo.get()); + m_mtlxInfos.push_back(std::move(shaderInfo)); + } + } + } + } catch (MaterialX::Exception& e) { + TF_RUNTIME_ERROR("Error on parsing of \"%s\": materialX error - %s", file.c_str(), e.what()); + } + } + } + + return m_registeredNodes; +} + +void RprUsdMaterialRegistry::CommitResources( + RprUsdImageCache* imageCache) { + if (m_textureCommits.empty()) { + return; + } + + using CommitUniqueTextureIndices = std::vector; + auto uniqueTextureIndicesPerCommit = std::make_unique(m_textureCommits.size()); + + struct UniqueTextureInfo { + std::string path; + uint32_t udimTileId; + + GlfUVTextureDataRefPtr data; + + UniqueTextureInfo(std::string const& path, uint32_t udimTileId) + : path(path), udimTileId(udimTileId), data(nullptr) {} + }; + std::vector uniqueTextures; + std::map uniqueTexturesMapping; + auto getUniqueTextureIndex = [&uniqueTexturesMapping, &uniqueTextures](std::string const& path, uint32_t udimTileId = 0) { + auto status = uniqueTexturesMapping.emplace(path, uniqueTexturesMapping.size()); + if (status.second) { + uniqueTextures.emplace_back(path, udimTileId); + } + return status.first->second; + }; + + // Iterate over all texture commits and collect unique textures including UDIM tiles + // + std::string formatString; + for (size_t i = 0; i < m_textureCommits.size(); ++i) { + auto& commit = m_textureCommits[i]; + if (auto rprImage = imageCache->GetImage(commit.filepath, commit.colorspace, commit.wrapType)) { + commit.setTextureCallback(rprImage); + continue; + } + + auto& commitTexIndices = uniqueTextureIndicesPerCommit[i]; + + if (RprUsdGetUDIMFormatString(commit.filepath, &formatString)) { + constexpr uint32_t kStartTile = 1001; + constexpr uint32_t kEndTile = 1100; + + for (uint32_t tileId = kStartTile; tileId <= kEndTile; ++tileId) { + auto tilePath = TfStringPrintf(formatString.c_str(), tileId); + if (ArchFileAccess(tilePath.c_str(), F_OK) == 0) { + commitTexIndices.push_back(getUniqueTextureIndex(tilePath, tileId)); + } + } + } else { + commitTexIndices.push_back(getUniqueTextureIndex(commit.filepath)); + } + } + + // Read all textures from disk from multi threads + // + 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)) { + uniqueTextures[i].data = textureData; + } else { + TF_RUNTIME_ERROR("Failed to load %s texture", uniqueTextures[i].path.c_str()); + } + } + } + ); + + // Create rpr::Image for each previously read unique texture + // XXX(RPR): so as RPR API is single-threaded we cannot parallelize this + // + for (size_t i = 0; i < m_textureCommits.size(); ++i) { + auto& commitTexIndices = uniqueTextureIndicesPerCommit[i]; + if (commitTexIndices.empty()) continue; + + std::vector tiles; + tiles.reserve(commitTexIndices.size()); + for (auto uniqueTextureIdx : commitTexIndices) { + auto& texture = uniqueTextures[uniqueTextureIdx]; + if (!texture.data) continue; + + tiles.emplace_back(texture.udimTileId, texture.data.operator->()); + } + + auto& commit = m_textureCommits[i]; + auto coreImage = imageCache->GetImage(commit.filepath, commit.colorspace, commit.wrapType, tiles); + commit.setTextureCallback(coreImage); + } + + m_textureCommits.clear(); +} + +namespace { + +void DumpMaterialNetwork(HdMaterialNetworkMap const& networkMap) { + SdfPath const* primitivePath = nullptr; + if (!networkMap.terminals.empty()) { + primitivePath = &networkMap.terminals[0]; + } else if (!networkMap.map.empty()) { + auto& network = networkMap.map.begin()->second; + if (!network.nodes.empty()) { + primitivePath = &network.nodes[0].path; + } + } + + bool closeFile = false; + FILE* file = stdout; + if (primitivePath) { + auto materialPath = primitivePath->GetParentPath(); + std::string filepath = materialPath.GetString(); + for (size_t i = 0; i < filepath.size(); ++i) { + if (std::strchr("/\\", filepath[i])) { + filepath[i] = '_'; + } + } + file = fopen(filepath.c_str(), "w"); + if (!file) { + file = stdout; + } else { + closeFile = true; + } + } + + fprintf(file, "terminals: [\n"); + for (auto& terminal : networkMap.terminals) { + fprintf(file, " \"%s\",\n", terminal.GetText()); + } + fprintf(file, "]\n"); + + fprintf(file, "map: {\n"); + for (auto& entry : networkMap.map) { + fprintf(file, " \"%s\": {\n", entry.first.GetText()); + + auto& network = entry.second; + fprintf(file, " relationships: [\n"); + for (auto& rel : network.relationships) { + SdfPath inputId; + TfToken inputName; + SdfPath outputId; + TfToken outputName; + fprintf(file, " {\n"); + fprintf(file, " inputId=%s\n", rel.inputId.GetText()); + fprintf(file, " inputName=%s\n", rel.inputName.GetText()); + fprintf(file, " outputId=%s\n", rel.outputId.GetText()); + fprintf(file, " outputName=%s\n", rel.outputName.GetText()); + fprintf(file, " },\n"); + } + fprintf(file, " ],\n"); + + fprintf(file, " primvars: [\n"); + for (auto& primvar : network.primvars) { + fprintf(file, " %s,\n", primvar.GetText()); + } + fprintf(file, " ]\n"); + + fprintf(file, " nodes: [\n"); + for (auto& node : network.nodes) { + fprintf(file, " {\n"); + fprintf(file, " path=%s\n", node.path.GetText()); + fprintf(file, " identifier=%s\n", node.identifier.GetText()); + fprintf(file, " parameters: {\n"); + for (auto& param : node.parameters) { + fprintf(file, " {%s: %s", param.first.GetText(), param.second.GetTypeName().c_str()); + if (param.second.IsHolding()) { + fprintf(file, "(\"%s\")", param.second.UncheckedGet().GetText()); + } else if (param.second.IsHolding()) { + fprintf(file, "(\"%s\")", param.second.UncheckedGet().GetResolvedPath().c_str()); + } else if (param.second.IsHolding()) { + auto& v = param.second.UncheckedGet(); + fprintf(file, "(%g, %g, %g, %g)", v[0], v[1], v[2], v[3]); + } + fprintf(file, "},\n"); + } + fprintf(file, " }\n"); + fprintf(file, " },\n"); + } + fprintf(file, " ]\n"); + + fprintf(file, " }\n"); + } + fprintf(file, "}\n"); + + if (closeFile) { + fclose(file); + } +} + +// Structures are taken from hdSt/materialNetwork.cpp + +struct RprUsd_MaterialNetwork { + struct Connection { + SdfPath upstreamNode; + TfToken upstreamOutputName; + }; + + struct Node { + TfToken nodeTypeId; + std::map parameters; + std::map inputConnections; + }; + + std::map nodes; + std::map terminals; +}; + +void ConvertLegacyHdMaterialNetwork( + HdMaterialNetworkMap const& hdNetworkMap, + RprUsd_MaterialNetwork *result) { + + for (auto& entry : hdNetworkMap.map) { + auto& terminalName = entry.first; + auto& hdNetwork = entry.second; + + // Transfer over individual nodes + for (auto& node : hdNetwork.nodes) { + // Check if this node is a terminal + auto termIt = std::find(hdNetworkMap.terminals.begin(), hdNetworkMap.terminals.end(), node.path); + if (termIt != hdNetworkMap.terminals.end()) { + result->terminals.emplace( + terminalName, + RprUsd_MaterialNetwork::Connection{node.path, terminalName}); + } + + if (result->nodes.count(node.path)) { + continue; + } + + auto& newNode = result->nodes[node.path]; + newNode.nodeTypeId = node.identifier; + newNode.parameters = node.parameters; + } + + // Transfer relationships to inputConnections on receiving/downstream nodes. + for (HdMaterialRelationship const& rel : hdNetwork.relationships) { + // outputId (in hdMaterial terms) is the input of the receiving node + auto const& iter = result->nodes.find(rel.outputId); + // skip connection if the destination node doesn't exist + if (iter == result->nodes.end()) { + continue; + } + auto &connection = iter->second.inputConnections[rel.outputName]; + connection.upstreamNode = rel.inputId; + connection.upstreamOutputName = rel.inputName; + } + + // Currently unused + // Transfer primvars: + //result->primvars.insert(hdNetwork.primvars.begin(), hdNetwork.primvars.end()); + } +} + +} // namespace anonymous + +RprUsdMaterial* RprUsdMaterialRegistry::CreateMaterial( + HdSceneDelegate* sceneDelegate, + HdMaterialNetworkMap const& legacyNetworkMap, + rpr::Context* rprContext, + RprUsdImageCache* imageCache) const { + + if (TfDebug::IsEnabled(RPR_USD_DEBUG_DUMP_MATERIALS)) { + DumpMaterialNetwork(legacyNetworkMap); + } + + // HdMaterialNetworkMap is deprecated, + // convert HdMaterialNetwork over to the new description + // so we do not have to redo all the code when new description comes in Hd + RprUsd_MaterialNetwork network; + ConvertLegacyHdMaterialNetwork(legacyNetworkMap, &network); + + RprUsd_MaterialBuilderContext context = {}; + context.rprContext = rprContext; + context.imageCache = imageCache; + context.mtlxLoader = m_mtlxLoader.get(); + + // The simple wrapper to retain material nodes that are used to build terminal outputs + struct RprUsdGraphBasedMaterial : public RprUsdMaterial { + std::map> materialNodes; + + bool Finalize(RprUsd_MaterialBuilderContext& context, + VtValue const& surfaceOutput, + VtValue const& displacementOutput, + VtValue const& volumeOutput) { + + auto getTerminalRprNode = [](VtValue const& terminalOutput) -> rpr::MaterialNode* { + if (!terminalOutput.IsEmpty()) { + if (terminalOutput.IsHolding>()) { + return terminalOutput.UncheckedGet>().get(); + } else { + TF_RUNTIME_ERROR("Terminal node should output material node"); + } + } + + return nullptr; + }; + + m_volumeNode = getTerminalRprNode(volumeOutput); + m_surfaceNode = getTerminalRprNode(surfaceOutput); + m_displacementNode = getTerminalRprNode(displacementOutput); + + m_isShadowCatcher = context.isShadowCatcher; + m_isReflectionCatcher = context.isReflectionCatcher; + m_uvPrimvarName = std::move(context.uvPrimvarName); + m_displacementScale = std::move(context.displacementScale); + + return m_volumeNode || m_surfaceNode || m_displacementNode; + } + }; + + auto out = std::make_unique(); + + // Houdini's principled shader node does not have a valid nodeTypeId + // So we find both surface and displacement nodes and then create one material node + bool isSurfaceNode; + SdfPath const* houdiniPrincipledShaderNodePath = nullptr; + std::map const* houdiniPrincipledShaderSurfaceParams = nullptr; + std::map const* houdiniPrincipledShaderDispParams = nullptr; + + // Create RprUsd_MaterialNode for each Hydra node + auto& materialNodes = out->materialNodes; + for (auto& entry : network.nodes) { + auto& nodePath = entry.first; + auto& node = entry.second; + + try { + // Check if we have registered node that match nodeTypeId + auto nodeLookupIt = m_registeredNodesLookup.find(node.nodeTypeId); + if (nodeLookupIt != m_registeredNodesLookup.end()) { + if (auto materialNode = m_registeredNodes[nodeLookupIt->second].factory(&context, node.parameters)) { + materialNodes[nodePath].reset(materialNode); + } + } else if (IsHoudiniPrincipledShaderHydraNode(sceneDelegate, nodePath, &isSurfaceNode)) { + if (isSurfaceNode) { + houdiniPrincipledShaderNodePath = &nodePath; + houdiniPrincipledShaderSurfaceParams = &node.parameters; + } else { + houdiniPrincipledShaderDispParams = &node.parameters; + } + } else { + TF_WARN("Unknown node type: id=%s", node.nodeTypeId.GetText()); + } + } catch (RprUsd_NodeError& e) { + TF_RUNTIME_ERROR("Failed to create %s(%s): %s", nodePath.GetText(), node.nodeTypeId.GetText(), e.what()); + } catch (RprUsd_NodeEmpty&) { + TF_WARN("Empty node: %s", nodePath.GetText()); + } + } + + if (houdiniPrincipledShaderNodePath) { + auto materialNode = new RprUsd_HoudiniPrincipledNode(&context, *houdiniPrincipledShaderSurfaceParams, houdiniPrincipledShaderDispParams); + materialNodes[*houdiniPrincipledShaderNodePath].reset(materialNode); + } + + std::set visited; + std::function getNodeOutput = + [&materialNodes, &network, &getNodeOutput, &visited] + (RprUsd_MaterialNetwork::Connection const& nodeConnection) -> VtValue { + auto& nodePath = nodeConnection.upstreamNode; + + auto nodeIt = network.nodes.find(nodePath); + if (nodeIt == network.nodes.end()) { + TF_CODING_ERROR("Invalid connection: %s", nodePath.GetText()); + return VtValue(); + } + auto& node = nodeIt->second; + + auto materialNodeIt = materialNodes.find(nodePath); + if (materialNodeIt != materialNodes.end()) { + auto materialNode = materialNodeIt->second.get(); + + // Set node inputs only once + if (visited.count(nodePath) == 0) { + visited.insert(nodePath); + + for (auto& inputConnection : node.inputConnections) { + auto& connection = inputConnection.second; + + auto nodeOutput = getNodeOutput(connection); + if (!nodeOutput.IsEmpty()) { + auto& inputId = inputConnection.first; + materialNode->SetInput(inputId, nodeOutput); + } + } + } + + return materialNode->GetOutput(nodeConnection.upstreamOutputName); + } else { + // Rpr node can be missing in two cases: + // a) we failed to create the node + // b) this node has no effect on the input + // In such a case, we simply interpret the output of the + // first connection as the output of the current node + if (node.inputConnections.empty()) { + return VtValue(); + } else { + return getNodeOutput(node.inputConnections.begin()->second); + } + } + }; + + auto getTerminalOutput = [&network, &getNodeOutput](TfToken const& terminalName) { + auto terminalIt = network.terminals.find(terminalName); + if (terminalIt == network.terminals.end()) { + return VtValue(); + } + + return getNodeOutput(terminalIt->second); + }; + + auto volumeOutput = getTerminalOutput(HdMaterialTerminalTokens->volume); + auto surfaceOutput = getTerminalOutput(HdMaterialTerminalTokens->surface); + auto displacementOutput = getTerminalOutput(HdMaterialTerminalTokens->displacement); + + return out->Finalize(context, surfaceOutput, displacementOutput, volumeOutput) ? out.release() : nullptr; +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/materialRegistry.h b/pxr/imaging/rprUsd/materialRegistry.h new file mode 100644 index 000000000..737efb608 --- /dev/null +++ b/pxr/imaging/rprUsd/materialRegistry.h @@ -0,0 +1,221 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#ifndef RPRUSD_MATERIAL_REGISTRY_H +#define RPRUSD_MATERIAL_REGISTRY_H + +#include "pxr/imaging/rprUsd/api.h" +#include "pxr/imaging/hd/material.h" +#include "pxr/base/arch/demangle.h" +#include "pxr/base/tf/singleton.h" + +#include + +class RPRMtlxLoader; + +PXR_NAMESPACE_OPEN_SCOPE + +class RprUsdImageCache; +class RprUsdCoreImage; +class RprUsdMaterial; +class RprUsdMaterialNodeInfo; + +class RprUsd_MaterialNode; +class RprUsd_MtlxNodeInfo; +struct RprUsd_MaterialBuilderContext; + +using RprUsdMaterialNodeFactoryFnc = std::function< + RprUsd_MaterialNode*( + RprUsd_MaterialBuilderContext* context, + std::map const& parameters)>; + +struct RprUsdMaterialNodeDesc { + RprUsdMaterialNodeFactoryFnc factory; + RprUsdMaterialNodeInfo const* info; +}; + +/// \class RprUsdMaterialRegistry +/// +/// Interface for the material resolution system. An RPR materials library is +/// responsible for resolving material node definitions and may create materials +/// from HdMaterialNetworkMap that are ready for use in hdRpr. +/// +class RprUsdMaterialRegistry { +public: + ~RprUsdMaterialRegistry(); + + RPRUSD_API + static RprUsdMaterialRegistry& GetInstance() { + return TfSingleton::GetInstance(); + } + + RPRUSD_API + RprUsdMaterial* CreateMaterial( + HdSceneDelegate* sceneDelegate, + HdMaterialNetworkMap const& networkMap, + rpr::Context* rprContext, + RprUsdImageCache* imageCache) const; + + RPRUSD_API + TfToken const& GetMaterialNetworkSelector(); + + RPRUSD_API + std::vector const& GetRegisteredNodes(); + + RPRUSD_API + RPRMtlxLoader* GetMtlxLoader() const { return m_mtlxLoader.get(); } + + /// Register implementation of the Node with \p id + RPRUSD_API + void Register( + TfToken const& id, + RprUsdMaterialNodeFactoryFnc factory, + RprUsdMaterialNodeInfo const* info = nullptr); + + struct TextureCommit { + std::string filepath; + std::string colorspace; + rpr::ImageWrapType wrapType; + + std::function const&)> setTextureCallback; + }; + + RPRUSD_API + void CommitTexture(TextureCommit commit); + + RPRUSD_API + void CommitResources(RprUsdImageCache* imageCache); + +private: + friend class TfSingleton; + RprUsdMaterialRegistry(); + +private: + /// Material network selector for the current session, controlled via env variable + TfToken m_materialNetworkSelector; + + std::unique_ptr m_mtlxLoader; + + std::vector m_registeredNodes; + std::map m_registeredNodesLookup; + + std::vector> m_mtlxInfos; + bool m_mtlxDefsDirty = true; + + std::vector m_textureCommits; +}; + +class RprUsdMaterialNodeInput; +class RprUsdMaterialNodeElement; + +/// \class RprUsdMaterialNodeInfo +/// +/// Describes resolved node, its name, inputs, outputs, etc +/// +class RprUsdMaterialNodeInfo { +public: + virtual ~RprUsdMaterialNodeInfo() = default; + + virtual const char* GetName() const = 0; + + virtual size_t GetNumInputs() const = 0; + virtual RprUsdMaterialNodeInput const* GetInput(size_t idx) const = 0; + + virtual size_t GetNumOutputs() const = 0; + virtual RprUsdMaterialNodeElement const* GetOutput(size_t idx) const = 0; + + virtual const char* GetUIName() const = 0; + virtual const char* GetUIFolder() const { return nullptr; }; +}; + +class RprUsdMaterialNodeElement { +public: + virtual ~RprUsdMaterialNodeElement() = default; + + virtual const char* GetName() const = 0; + virtual const char* GetUIName() const = 0; + virtual const char* GetDocString() const = 0; + + enum Type { + kInvalid, + kFloat, + kAngle, + kVector2, + kVector3, + kColor3, + kNormal, + kBoolean, + kInteger, + kToken, + kString, + kFilepath, + kVolumeShader, + kSurfaceShader, + kDisplacementShader, + }; + Type GetType() const { return m_type; } + +protected: + RprUsdMaterialNodeElement(Type type) : m_type(type) {} + +protected: + Type m_type; +}; + +class RprUsdMaterialNodeInput : public RprUsdMaterialNodeElement { +public: + ~RprUsdMaterialNodeInput() override = default; + + virtual const char* GetUIMin() const = 0; + virtual const char* GetUISoftMin() const = 0; + virtual const char* GetUIMax() const = 0; + virtual const char* GetUISoftMax() const = 0; + virtual const char* GetUIFolder() const = 0; + virtual const char* GetValueString() const = 0; + virtual std::vector const& GetTokenValues() const = 0; + +protected: + RprUsdMaterialNodeInput(Type type) : RprUsdMaterialNodeElement(type) {} +}; + +inline TfToken const& RprUsdMaterialRegistry::GetMaterialNetworkSelector() { + return m_materialNetworkSelector; +} + +inline void RprUsdMaterialRegistry::Register( + TfToken const& id, + RprUsdMaterialNodeFactoryFnc factory, + RprUsdMaterialNodeInfo const* info) { + RprUsdMaterialNodeDesc desc = {}; + desc.factory = std::move(factory); + desc.info = info; + + auto status = m_registeredNodesLookup.emplace(id, m_registeredNodes.size()); + if (!status.second) { + TF_CODING_ERROR("Failed to register %s: already registered", id.GetText()); + } else { + m_registeredNodes.push_back(std::move(desc)); + } +} + +inline void RprUsdMaterialRegistry::CommitTexture(TextureCommit commit) { + m_textureCommits.push_back(std::move(commit)); +} + +inline const char* GetCStr(std::string const& str) { + return !str.empty() ? str.c_str() : nullptr; +} + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // RPRUSD_MATERIAL_REGISTRY_H diff --git a/pxr/imaging/rprUsd/module.cpp b/pxr/imaging/rprUsd/module.cpp new file mode 100644 index 000000000..9e9ff327d --- /dev/null +++ b/pxr/imaging/rprUsd/module.cpp @@ -0,0 +1,22 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "pxr/pxr.h" +#include "pxr/base/tf/pyModule.h" + +PXR_NAMESPACE_USING_DIRECTIVE + +TF_WRAP_MODULE +{ + TF_WRAP(Config); +} diff --git a/pxr/imaging/rprUsd/moduleDeps.cpp b/pxr/imaging/rprUsd/moduleDeps.cpp new file mode 100644 index 000000000..dd5ba8629 --- /dev/null +++ b/pxr/imaging/rprUsd/moduleDeps.cpp @@ -0,0 +1,32 @@ +/************************************************************************ +Copyright 2020 Advanced Micro Devices, Inc +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +************************************************************************/ + +#include "pxr/pxr.h" +#include "pxr/base/tf/registryManager.h" +#include "pxr/base/tf/scriptModuleLoader.h" +#include "pxr/base/tf/token.h" + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +TF_REGISTRY_FUNCTION(TfScriptModuleLoader) { + // List of direct dependencies for this library. + const std::vector reqs = { + TfToken("tf"), + }; + TfScriptModuleLoader::GetInstance(). + RegisterLibrary(TfToken("rprUsd"), TfToken("rpr.RprUsd"), reqs); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/rprUsd/util.cpp b/pxr/imaging/rprUsd/util.cpp new file mode 100644 index 000000000..ec5d55c94 --- /dev/null +++ b/pxr/imaging/rprUsd/util.cpp @@ -0,0 +1,41 @@ +/************************************************************************ +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 "util.h" + +#include "pxr/base/tf/staticTokens.h" + +#include + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEFINE_PRIVATE_TOKENS( + RprUsdUDIMTags, + ((arnoldTag, "")) + ((sidefxTag, "%(UDIM)d")) +); + +bool RprUsdGetUDIMFormatString(std::string const& filepath, std::string* out_formatString) { + for (auto& udimTag : RprUsdUDIMTags->allTokens) { + auto idx = filepath.rfind(udimTag.GetString()); + if (idx != std::string::npos) { + *out_formatString = filepath; + out_formatString->replace(idx, udimTag.size(), "%i"); + return true; + } + } + + return false; +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdRpr/rpr/contextHelpers.h b/pxr/imaging/rprUsd/util.h similarity index 73% rename from pxr/imaging/plugin/hdRpr/rpr/contextHelpers.h rename to pxr/imaging/rprUsd/util.h index d9daee1a4..9b5e6a0ae 100644 --- a/pxr/imaging/plugin/hdRpr/rpr/contextHelpers.h +++ b/pxr/imaging/rprUsd/util.h @@ -11,17 +11,17 @@ See the License for the specific language governing permissions and limitations under the License. ************************************************************************/ -#ifndef HDRPR_CORE_CONTEXT_HELPERS_H -#define HDRPR_CORE_CONTEXT_HELPERS_H +#ifndef RPRUSD_UTIL_H +#define RPRUSD_UTIL_H -#include "contextMetadata.h" +#include "pxr/pxr.h" -namespace rpr { +#include -class Context; +PXR_NAMESPACE_OPEN_SCOPE -Context* CreateContext(char const* cachePath, ContextMetadata* metadata); +bool RprUsdGetUDIMFormatString(std::string const& filepath, std::string* out_formatString); -} // namespace rpr +PXR_NAMESPACE_CLOSE_SCOPE -#endif // HDRPR_CORE_CONTEXT_HELPERS_H +#endif // RPRUSD_UTIL_H diff --git a/pxr/imaging/rprUsd/wrapConfig.cpp b/pxr/imaging/rprUsd/wrapConfig.cpp new file mode 100644 index 000000000..1baad4d1c --- /dev/null +++ b/pxr/imaging/rprUsd/wrapConfig.cpp @@ -0,0 +1,52 @@ +/************************************************************************ +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 "config.h" +#include "boostIncludePath.h" + +#include BOOST_INCLUDE_PATH(python.hpp) +#include BOOST_INCLUDE_PATH(python/class.hpp) +#include BOOST_INCLUDE_PATH(python/def.hpp) +#include BOOST_INCLUDE_PATH(python/scope.hpp) + +using namespace BOOST_NS::python; + +PXR_NAMESPACE_USING_DIRECTIVE + +void wrapConfig() { + +#define CONFIG_SETTER(setter, type) \ + .def(#setter, +[](type value) { \ + RprUsdConfig* config; \ + auto configLock = RprUsdConfig::GetInstance(&config); \ + config->setter(value); \ + }) \ + .staticmethod(#setter) + +#define CONFIG_GETTER(getter) \ + .def(#getter, +[]() { \ + RprUsdConfig* config; \ + auto configLock = RprUsdConfig::GetInstance(&config); \ + return config->getter(); \ + }) \ + .staticmethod(#getter) + + scope s = class_("Config", no_init) + CONFIG_SETTER(SetRestartWarning, bool) + CONFIG_SETTER(SetTextureCacheDir, std::string) + CONFIG_SETTER(SetKernelCacheDir, std::string) + CONFIG_GETTER(IsRestartWarningEnabled) + CONFIG_GETTER(GetTextureCacheDir) + CONFIG_GETTER(GetKernelCacheDir) + ; +}