diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index fecdbf7a52..d5ac9d3f79 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -47,18 +47,12 @@ jobs: cmake_config: -DMATERIALX_BUILD_SHARED_LIBS=ON static_analysis: ON - - name: Linux_Clang_13_Python39 - os: ubuntu-22.04 - compiler: clang - compiler_version: "13" - python: 3.9 - test_render: ON - - name: Linux_Clang_14_Python311 os: ubuntu-22.04 compiler: clang compiler_version: "14" python: 3.11 + test_render: ON clang_format: ON - name: Linux_Clang_14_DynamicAnalysis @@ -89,6 +83,13 @@ jobs: python: 3.11 test_shaders: ON + - name: iOS_Xcode_15 + os: macos-13 + compiler: xcode + compiler_version: "15.0" + python: None + cmake_config: -DMATERIALX_BUILD_IOS=ON -DCMAKE_OSX_SYSROOT=`xcrun --sdk iphoneos --show-sdk-path` -DCMAKE_OSX_ARCHITECTURES=arm64 + - name: Windows_VS2019_Win32_Python37 os: windows-2019 architecture: x86 @@ -159,11 +160,6 @@ jobs: python-version: ${{ matrix.python }} architecture: ${{ matrix.architecture }} - - name: Install OpenImageIO - if: matrix.install_oiio == 'ON' && runner.os == 'Windows' - run: | - vcpkg/vcpkg install openimageio --triplet=x64-windows - - name: Install Emscripten if: matrix.build_javascript == 'ON' run: | @@ -325,3 +321,91 @@ jobs: name: MaterialX_JavaScript path: javascript/build/installed/JavaScript/MaterialX if-no-files-found: ignore + + sdist: + name: Python SDist + runs-on: ubuntu-latest + if: github.repository == 'AcademySoftwareFoundation/MaterialX' + outputs: + sdist_filename: ${{ steps.generate.outputs.filename }} + + steps: + - name: Sync Repository + uses: actions/checkout@v3 + + - name: Install Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Build SDist + id: generate + run: | + python -m pip install build + python -m build -s . --outdir dist + echo "filename=$(ls dist)" >> "$GITHUB_OUTPUT" + + - name: Upload SDist + uses: actions/upload-artifact@v3 + with: + name: MaterialX_Python_SDist + path: dist/*.tar.gz + + wheels: + name: Python Wheels + runs-on: ${{ matrix.os }} + needs: ['sdist'] + if: github.repository == 'AcademySoftwareFoundation/MaterialX' + strategy: + fail-fast: false + matrix: + python-minor: ['7', '8', '9', '10', '11'] + os: ['ubuntu-latest', 'macos-latest', 'windows-latest'] + + steps: + - name: Sync Repository + uses: actions/checkout@v3 + + - name: Install Python 3.${{ matrix.python-minor }} + uses: actions/setup-python@v4 + with: + python-version: 3.${{ matrix.python-minor }} + + - name: Download Sdist + uses: actions/download-artifact@v3 + with: + name: MaterialX_Python_SDist + path: sdist + + - name: Build Wheel + uses: pypa/cibuildwheel@v2.13.1 + with: + package-dir: ${{ github.workspace }}/sdist/${{ needs.sdist.outputs.sdist_filename }} + env: + CIBW_BUILD: 'cp3${{ matrix.python-minor }}-*' + CIBW_SKIP: '*musllinux*' + CIBW_ARCHS: 'auto64' + # https://github.com/pypa/manylinux + # manylinux2014 is CentOS 7 based. Which means GCC 10 and glibc 2.17. + CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014 + CIBW_BEFORE_ALL_LINUX: yum install -y libXt-devel + CIBW_BEFORE_ALL_MACOS: sudo xcode-select -switch /Applications/Xcode_13.4.app + CIBW_BUILD_VERBOSITY: 1 + CIBW_ENVIRONMENT: CMAKE_BUILD_PARALLEL_LEVEL=2 + # CIBW_BUILD_FRONTEND: build # https://github.com/pypa/build + MACOSX_DEPLOYMENT_TARGET: '10.15' + + - name: Install Wheel + run: python -m pip install MaterialX --find-links wheelhouse --no-index + + - name: Python Tests + run: | + python MaterialXTest/main.py + python MaterialXTest/genshader.py + working-directory: python + + - name: Upload Wheel + uses: actions/upload-artifact@v3 + with: + name: MaterialX_Python_Wheels + path: wheelhouse/*.whl diff --git a/.gitignore b/.gitignore index 378eac25d3..9d0b71a3c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ build +dist diff --git a/CHANGELOG.md b/CHANGELOG.md index a27b8d124c..5704c54b84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,40 @@ # Change Log -## [1.38.8] - Development +## [1.38.8] - 2023-09-08 + +### Added +- Added a broad set of new pattern nodes to MaterialX, including [Circle, Hexagon, Cloverleaf, Line, Grid, Crosshatch](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1411), [Checkerboard](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1328), [Random Color, Random Float](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1330), [Triangle Wave](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1334), [Integer Floor, Integer Ceiling](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1362), and [Distance](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1333). +- Added support for [MaterialX builds on iOS](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1435). +- Added support for [drag-and-drop import](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1482) of MaterialX files in the [Web Viewer](https://academysoftwarefoundation.github.io/MaterialX/). +- Added generation of [MaterialX Python wheels](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1317) in GitHub Actions, enabling the distribution of MaterialX Python packages through PyPI. +- Added support for the [lin_displayp3 and srgb_displayp3](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1368) colorspaces in shader generation. +- Added support for the [blackbody PBR node](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1367) in shader generation. +- Added support for [displacement](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1396) in MDL generation. +- Added [blend](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1350) and [up-axis](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1489) controls to the triplanar projection node. +- Added version details to [shared libraries](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1447) on Windows. +- Added a [MacOS 13](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1375) build to GitHub Actions. + +### Changed +- Raised the minimum C++ version for MaterialX builds to [C++14](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1340). +- Upgraded the [PyBind11 library](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1343) to version 2.10.4, raising the minimum Python version to 3.6, and enabling support for Python versions 3.11 and beyond. +- Improved the performance and convergence of [GGX importance sampling](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1390) in GLSL generation, leveraging insights from the HPG 2023 paper by Jonathan Dupuy and Anis Benyoub. +- Improved [property panel display](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1346) in the MaterialX Graph Editor. +- Improved [node spacing](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1476) in the MaterialX Graph Editor. +- Improved the robustness of [MaterialX unit tests](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1370) with respect to the current working directory. +- Simplified the handling of [default colors](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1452) in GLSL generation, removing dynamic branches on texture size. +- Simplified the definitions of the [default color transforms](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1352), implementing them as language-independent MaterialX graphs. +- Simplified the interface of [ShaderGenerator::emitFunctionCall](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1344), marking its original interface as deprecated. +- Marked legacy interfaces for [findRenderableElements and findRenderableMaterialNodes](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1340) as deprecated, making their usage visible to clients as compiler warnings. +- Moved the MaterialX specification to [public Markdown files in GitHub](https://github.com/AcademySoftwareFoundation/MaterialX/tree/main/documents/Specification), enabling direct contributions from the community. + +### Fixed +- Fixed brightness artifacts in the [triplanar projection node](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1350). +- Aligned default values for [conductor_bsdf](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1379) with the MaterialX specification. +- Fixed [volume mixing](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1395) in MDL generation. +- Fixed a bug to improve [shader generation determinism](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1376). +- Fixed a bug to improve the [consistency of auto layout](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1389) in the MaterialX Graph Editor. +- Fixed a bug to enable [multi-output connection edits](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1506) in the MaterialX Graph Editor. +- Fixed a bug in [dot node optimizations](https://github.com/AcademySoftwareFoundation/MaterialX/pull/1522) for shader generation. ## [1.38.7] - 2023-04-21 diff --git a/CMakeLists.txt b/CMakeLists.txt index a1731b0df0..504563d28b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ set(MATERIALX_BUILD_VERSION 0) set(MATERIALX_LIBRARY_VERSION ${MATERIALX_MAJOR_VERSION}.${MATERIALX_MINOR_VERSION}.${MATERIALX_BUILD_VERSION}) # Cmake setup -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.5) set(CMAKE_CXX_STANDARD 14) set(CMAKE_POSITION_INDEPENDENT_CODE TRUE) set(CMAKE_MACOSX_RPATH ON) @@ -31,7 +31,7 @@ if (MATERIALX_BUILD_JS) endif() endif() -project(MaterialX) +project(MaterialX VERSION ${MATERIALX_LIBRARY_VERSION}) option(MATERIALX_BUILD_PYTHON "Build the MaterialX Python package from C++ bindings. Requires Python 3.6 or greater." OFF) option(MATERIALX_BUILD_VIEWER "Build the MaterialX Viewer." OFF) @@ -49,11 +49,25 @@ option(MATERIALX_BUILD_TESTS "Build unit tests." ON) option(MATERIALX_BUILD_SHARED_LIBS "Build MaterialX libraries as shared rather than static." OFF) option(MATERIALX_PYTHON_LTO "Enable link-time optimizations for MaterialX Python." ON) option(MATERIALX_INSTALL_PYTHON "Install the MaterialX Python package as a third-party library when the install target is built." ON) +option(MATERIALX_INSTALL_RESOURCES "Install the resources folder when building render modules." ON) option(MATERIALX_TEST_RENDER "Run rendering tests for MaterialX Render module. GPU required for graphics validation." ON) option(MATERIALX_WARNINGS_AS_ERRORS "Interpret all compiler warnings as errors." OFF) option(MATERIALX_DYNAMIC_ANALYSIS "Build MaterialX libraries with dynamic analysis on supporting platforms." OFF) option(MATERIALX_OSL_LEGACY_CLOSURES "Build OSL shader generation supporting the legacy OSL closures." ON) +option(MATERIALX_BUILD_IOS "Build MaterialX for iOS." OFF) +if (MATERIALX_BUILD_IOS) + set(CMAKE_SYSTEM_NAME iOS) + add_definitions(-DTARGET_OS_IOS=1) + set(MATERIALX_BUILD_PYTHON OFF) + set(MATERIALX_BUILD_VIEWER OFF) + set(MATERIALX_BUILD_GRAPH_EDITOR OFF) + set(MATERIALX_BUILD_GEN_GLSL OFF) + set(MATERIALX_BUILD_GEN_OSL OFF) + set(MATERIALX_BUILD_GEN_MDL OFF) + set(MATERIALX_BUILD_TESTS OFF) +endif() + set(MATERIALX_PYTHON_VERSION "" CACHE STRING "Python version to be used in building the MaterialX Python package (e.g. '3.9').") set(MATERIALX_PYTHON_EXECUTABLE "" CACHE FILEPATH @@ -79,6 +93,12 @@ set(MATERIALX_OSL_BINARY_OSLC "" CACHE FILEPATH "Full path to the OSL compiler b set(MATERIALX_OSL_BINARY_TESTRENDER "" CACHE FILEPATH "Full path to the OSL test render binary.") set(MATERIALX_OSL_INCLUDE_PATH "" CACHE PATH "Full path to OSL shader includes (e.g. 'stdosl.h').") +set(MATERIALX_PYTHON_FOLDER_NAME "python/MaterialX" CACHE INTERNAL "Folder name to user for installing the Python library.") + +if(SKBUILD) + set(MATERIALX_PYTHON_FOLDER_NAME "MaterialX") +endif() + # Helpers for MDL validation if (MATERIALX_BUILD_GEN_MDL) set(MATERIALX_MDLC_EXECUTABLE "" CACHE FILEPATH "Full path to the mdlc binary.") @@ -115,6 +135,7 @@ mark_as_advanced(MATERIALX_NAMESPACE_SUFFIX) mark_as_advanced(MATERIALX_LIBNAME_SUFFIX) mark_as_advanced(MATERIALX_PYTHON_LTO) mark_as_advanced(MATERIALX_INSTALL_PYTHON) +mark_as_advanced(MATERIALX_INSTALL_RESOURCES) mark_as_advanced(MATERIALX_TEST_RENDER) mark_as_advanced(MATERIALX_WARNINGS_AS_ERRORS) mark_as_advanced(MATERIALX_DYNAMIC_ANALYSIS) @@ -132,6 +153,7 @@ mark_as_advanced(MATERIALX_INSTALL_LIB_PATH) mark_as_advanced(MATERIALX_INSTALL_STDLIB_PATH) mark_as_advanced(MATERIALX_BUILD_JS) mark_as_advanced(MATERIALX_EMSDK_PATH) +mark_as_advanced(MATERIALX_BUILD_IOS) if (MATERIALX_BUILD_GEN_MDL) mark_as_advanced(MATERIALX_MDLC_EXECUTABLE) mark_as_advanced(MATERIALX_MDL_RENDER_EXECUTABLE) @@ -183,6 +205,12 @@ set(MATERIALX_SAME_DIR_RPATH "${RPATH_RELATIVE_SYMBOL};${CMAKE_INSTALL_PREFIX}/$ set(MATERIALX_UP_ONE_RPATH "${RPATH_RELATIVE_SYMBOL}/../${MATERIALX_INSTALL_LIB_PATH};${MATERIALX_SAME_DIR_RPATH}") # For linking to libraries where source is two directories deep, ie: "MATX/python/MaterialX/../../lib" set(MATERIALX_UP_TWO_RPATH "${RPATH_RELATIVE_SYMBOL}/../../${MATERIALX_INSTALL_LIB_PATH};${MATERIALX_SAME_DIR_RPATH}") +if(SKBUILD) + # When building the Python wheels, we don't want to set any RPATH because + # we want to wheel to be self-contained. We don't want any interference from + # external paths. + set(MATERIALX_UP_TWO_RPATH "${RPATH_RELATIVE_SYMBOL}") +endif() # Adjust compiler settings if(MSVC) @@ -276,7 +304,9 @@ if(MATERIALX_BUILD_RENDER) if(MATERIALX_BUILD_GRAPH_EDITOR) add_subdirectory(source/MaterialXGraphEditor) endif() - add_subdirectory(resources) + if(MATERIALX_INSTALL_RESOURCES AND NOT SKBUILD) + add_subdirectory(resources) + endif() endif() # Add test subdirectory @@ -309,26 +339,28 @@ if(${CMAKE_VERSION} VERSION_GREATER "3.6.2") endif() # Install root-level documents -install(FILES LICENSE CHANGELOG.md README.md THIRD-PARTY.md - DESTINATION .) - -set(MATERIALX_GEN_CONFIG_PATH "${MATERIALX_INSTALL_LIB_PATH}/cmake/${CMAKE_PROJECT_NAME}") - -include(CMakePackageConfigHelpers) -configure_package_config_file(cmake/modules/MaterialXConfig.cmake.in - ${CMAKE_BINARY_DIR}/cmake/${CMAKE_PROJECT_NAME}Config.cmake - INSTALL_DESTINATION "${MATERIALX_GEN_CONFIG_PATH}" - PATH_VARS CMAKE_INSTALL_PREFIX CMAKE_PROJECT_NAME) -write_basic_package_version_file(${CMAKE_BINARY_DIR}/cmake/${CMAKE_PROJECT_NAME}ConfigVersion.cmake - VERSION ${MATERIALX_LIBRARY_VERSION} - COMPATIBILITY AnyNewerVersion) - -# Install the auto-generated CMake configuration files: - -install(EXPORT MaterialX - DESTINATION "${MATERIALX_GEN_CONFIG_PATH}" - FILE ${CMAKE_PROJECT_NAME}Targets.cmake) - -install(FILES "${CMAKE_BINARY_DIR}/cmake/${CMAKE_PROJECT_NAME}ConfigVersion.cmake" - "${CMAKE_BINARY_DIR}/cmake/${CMAKE_PROJECT_NAME}Config.cmake" - DESTINATION "${MATERIALX_GEN_CONFIG_PATH}") +if(NOT SKBUILD) + install(FILES LICENSE CHANGELOG.md README.md THIRD-PARTY.md + DESTINATION .) + + set(MATERIALX_GEN_CONFIG_PATH "${MATERIALX_INSTALL_LIB_PATH}/cmake/${CMAKE_PROJECT_NAME}") + + include(CMakePackageConfigHelpers) + configure_package_config_file(cmake/modules/MaterialXConfig.cmake.in + ${CMAKE_BINARY_DIR}/cmake/${CMAKE_PROJECT_NAME}Config.cmake + INSTALL_DESTINATION "${MATERIALX_GEN_CONFIG_PATH}" + PATH_VARS CMAKE_INSTALL_PREFIX CMAKE_PROJECT_NAME) + write_basic_package_version_file(${CMAKE_BINARY_DIR}/cmake/${CMAKE_PROJECT_NAME}ConfigVersion.cmake + VERSION ${MATERIALX_LIBRARY_VERSION} + COMPATIBILITY AnyNewerVersion) + + # Install the auto-generated CMake configuration files: + + install(EXPORT MaterialX + DESTINATION "${MATERIALX_GEN_CONFIG_PATH}" + FILE ${CMAKE_PROJECT_NAME}Targets.cmake) + + install(FILES "${CMAKE_BINARY_DIR}/cmake/${CMAKE_PROJECT_NAME}ConfigVersion.cmake" + "${CMAKE_BINARY_DIR}/cmake/${CMAKE_PROJECT_NAME}Config.cmake" + DESTINATION "${MATERIALX_GEN_CONFIG_PATH}") +endif() diff --git a/cmake/modules/MaterialXConfig.cmake.in b/cmake/modules/MaterialXConfig.cmake.in index b4c8b7fad6..e1598087bf 100644 --- a/cmake/modules/MaterialXConfig.cmake.in +++ b/cmake/modules/MaterialXConfig.cmake.in @@ -22,6 +22,8 @@ set_and_check(MATERIALX_STDLIB_DIR "@PACKAGE_CMAKE_INSTALL_PREFIX@/libraries") if(@MATERIALX_BUILD_PYTHON@ AND @MATERIALX_INSTALL_PYTHON@) set_and_check(MATERIALX_PYTHON_DIR "@PACKAGE_CMAKE_INSTALL_PREFIX@/python") endif() -set_and_check(MATERIALX_RESOURCES_DIR "@PACKAGE_CMAKE_INSTALL_PREFIX@/resources") +if(@MATERIALX_BUILD_RENDER@ AND @MATERIALX_INSTALL_RESOURCES@) + set_and_check(MATERIALX_RESOURCES_DIR "@PACKAGE_CMAKE_INSTALL_PREFIX@/resources") +endif() check_required_components(@CMAKE_PROJECT_NAME@) diff --git a/cmake/modules/MaterialXVersion.rc.in b/cmake/modules/MaterialXVersion.rc.in new file mode 100644 index 0000000000..989fbb8e55 --- /dev/null +++ b/cmake/modules/MaterialXVersion.rc.in @@ -0,0 +1,34 @@ + +#define MATERIALX_FILEVERSION @MATERIALX_MAJOR_VERSION@,@MATERIALX_MINOR_VERSION@,@MATERIALX_BUILD_VERSION@,0 +#define MATERIALX_FILEVERSION_STR "@MATERIALX_MAJOR_VERSION@.@MATERIALX_MINOR_VERSION@.@MATERIALX_BUILD_VERSION@.0\0" +#define MATERIALX_FILENAME_STR "@MATERIALX_MODULE_NAME@.dll\0" + +1 VERSIONINFO + FILEVERSION MATERIALX_FILEVERSION + PRODUCTVERSION MATERIALX_FILEVERSION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" + BEGIN + VALUE "FileVersion", MATERIALX_FILEVERSION_STR + VALUE "LegalCopyright", "Apache License 2.0\0" + VALUE "OriginalFilename", MATERIALX_FILENAME_STR + VALUE "ProductName", "MaterialX\0" + VALUE "ProductVersion", MATERIALX_FILEVERSION_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END diff --git a/documents/Specification/MaterialX.Specification.md b/documents/Specification/MaterialX.Specification.md index 3a90a269fb..7e876bdb57 100644 --- a/documents/Specification/MaterialX.Specification.md +++ b/documents/Specification/MaterialX.Specification.md @@ -656,6 +656,7 @@ Standard Texture nodes: * **`image`**: samples data from a single image, or from a layer within a multi-layer image. When used in the context of rendering a geometry, the image is mapped onto the geometry based on geometry UV coordinates, with the lower-left corner of an image mapping to the (0,0) UV coordinate (or to the fractional (0,0) UV coordinate for tiled images). +The type of the <image> node determines the number of channels output, which may be less than the number of channels in the image file, outputting the first N channels from the image file. So a `float` <image> would return the Red channel of an RGB image, and a `color3` <image> would return the RGB channels of an RGBA image. * `file` (uniform filename): the URI of an image file. The filename can include one or more substitutions to change the file name (including frame number) that is accessed, as described in the [Filename Substitutions](#filename-substitutions) section above. * `layer` (uniform string): the name of the layer to extract from a multi-layer input file. If no value for `layer` is provided and the input file has multiple layers, then the "default" layer will be used, or "rgba" if there is no "default" layer. Note: the number of channels defined by the `type` of the `` must match the number of channels in the named layer. * `default` (float or colorN or vectorN): a default value to use if the `file` reference can not be resolved (e.g. if a <_geometry token_>, [_interface token_] or {_hostattr_} is included in the filename but no substitution value or default is defined, or if the resolved `file` URI cannot be read), or if the specified `layer` does not exist in the file. The `default` value must be the same type as the `` element itself. If `default` is not defined, the default color value will be 0.0 in all channels. @@ -786,8 +787,8 @@ Standard Procedural nodes: * **`checkerboard`**: a 2D checkerboard pattern. * `color1` (color3): The first color used in the checkerboard pattern. * `color2` (color3): The second color used in the checkerboard pattern. - * `freq` (vector2): The frequency of checkers, with higher values producing smaller squares. Default is (8, 8). - * `offset` (vector2): Shift the pattern in 2d space. Default is (0, 0). + * `uvtiling` (vector2): The tiling of the checkerboard pattern along each axis, with higher values producing smaller squares. Default is (8, 8). + * `uvoffset` (vector2): The offset of the checkerboard pattern along each axis. Default is (0, 0). * `texcoord` (vector2): The input 2d space. Default is the first texture coordinates. @@ -2369,7 +2370,7 @@ An input with a shader-semantic type may be given a value of "" to indicate no s The Standard MaterialX Library defines the following nodes and node variants operating on "shader"-semantic types. Standard library shaders do not respond to external illumination; please refer to the [**MaterialX Physically Based Shading Nodes**](./MaterialX.PBRSpec.md#materialx-pbs-library) document for definitions of additional nodes and shader constructors which do respond to illumination. - + * **`surface_unlit`**: an unlit surface shader node, representing a surface that can emit and transmit light, but does not receive illumination from light sources or other surfaces. Output type surfaceshader. * `emission` (float): the surface emission amount; default is 1.0 diff --git a/javascript/MaterialXView/package-lock.json b/javascript/MaterialXView/package-lock.json index 84d65f8589..7bdf38ceba 100644 --- a/javascript/MaterialXView/package-lock.json +++ b/javascript/MaterialXView/package-lock.json @@ -10,8 +10,8 @@ "license": "ISC", "dependencies": { "dat.gui": "^0.7.9", - "three": "^0.135.0", - "webpack": "^5.88.1" + "three": "^0.136.0", + "webpack": "^5.88.2" }, "devDependencies": { "copy-webpack-plugin": "^8.1.1", @@ -166,9 +166,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.40.2", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.40.2.tgz", - "integrity": "sha512-PRVjQ4Eh9z9pmmtaq8nTjZjQwKFk7YIHIud3lRoKRBgUQjgjRmoGxxGEPXQkF+lH7QkHJRNr5F4aBgYCW0lqpQ==", + "version": "8.44.1", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.1.tgz", + "integrity": "sha512-XpNDc4Z5Tb4x+SW1MriMVeIsMoONHCkWFMkR/aPJbzEsxqHy+4Glu/BqTdPrApfDeMaXbtNh6bseNgl5KaWrSg==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -245,9 +245,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.3.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.3.3.tgz", - "integrity": "sha512-wheIYdr4NYML61AjC8MKj/2jrR/kDQri/CIpVoZwldwhnIrD/j9jIU5bJ8yBKuB2VhpFV7Ab6G2XkBjv9r9Zzw==" + "version": "20.4.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.5.tgz", + "integrity": "sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg==" }, "node_modules/@types/qs": { "version": "6.9.7", @@ -506,9 +506,9 @@ } }, "node_modules/acorn": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.9.0.tgz", - "integrity": "sha512-jaVNAFBHNLXspO543WnNNPZFRtavh3skAkITqD0/2aeMkKZTN+254PyhwxFYrk3vQ1xfY+2wbesJMs/JC8/PwQ==", + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", "bin": { "acorn": "bin/acorn" }, @@ -798,9 +798,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001512", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001512.tgz", - "integrity": "sha512-2S9nK0G/mE+jasCUsMPlARhRCts1ebcp2Ji8Y8PWi4NDE1iRdLCnEPHkEfeBrGC45L4isBx5ur3IQ6yTE2mRZw==", + "version": "1.0.30001517", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz", + "integrity": "sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA==", "funding": [ { "type": "opencollective", @@ -1223,9 +1223,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.449", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.449.tgz", - "integrity": "sha512-TxLRpRUj/107ATefeP8VIUWNOv90xJxZZbCW/eIbSZQiuiFANCx2b7u+GbVc9X4gU+xnbvypNMYVM/WArE1DNQ==" + "version": "1.4.470", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.470.tgz", + "integrity": "sha512-zZM48Lmy2FKWgqyvsX9XK+J6FfP7aCDUFLmgooLJzA7v1agCs/sxSoBpTIwDLhmbhpx9yJIxj2INig/ncjJRqg==" }, "node_modules/encodeurl": { "version": "1.0.2", @@ -1427,9 +1427,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.0.tgz", - "integrity": "sha512-ChDuvbOypPuNjO8yIDf36x7BlZX1smcUMTTcyoIjycexOxd6DFsKsg21qVBzEmr3G7fUKIRy2/psii+CIUt7FA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -2397,9 +2397,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.12.tgz", - "integrity": "sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==" + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -3405,9 +3405,9 @@ } }, "node_modules/terser": { - "version": "5.18.2", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.18.2.tgz", - "integrity": "sha512-Ah19JS86ypbJzTzvUCX7KOsEIhDaRONungA4aYBjEP3JZRf4ocuDzTg4QWZnPn9DEMiMYGJPiSOy7aykoCc70w==", + "version": "5.19.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.19.2.tgz", + "integrity": "sha512-qC5+dmecKJA4cpYxRa5aVkKehYsQKc+AHeKl0Oe62aYjBL8ZA33tTljktDHJSaxxMnbI5ZYw+o/S2DxxLu8OfA==", "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.8.2", @@ -3468,9 +3468,9 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "node_modules/three": { - "version": "0.135.0", - "resolved": "https://registry.npmjs.org/three/-/three-0.135.0.tgz", - "integrity": "sha512-kuEpuuxRzLv0MDsXai9huCxOSQPZ4vje6y0gn80SRmQvgz6/+rI0NAvCRAw56zYaWKMGMfqKWsxF9Qa2Z9xymQ==" + "version": "0.136.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.136.0.tgz", + "integrity": "sha512-+fEMX7nYLz2ZesVP/dyifli5Jf8gR3XPAnFJveQ80aMhibFduzrADnjMbARXh8+W9qLK7rshJCjAIL/6cDxC+A==" }, "node_modules/thunky": { "version": "1.1.0", @@ -3500,9 +3500,9 @@ } }, "node_modules/tslib": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.0.tgz", - "integrity": "sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.1.tgz", + "integrity": "sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==", "dev": true }, "node_modules/type-is": { @@ -3625,9 +3625,9 @@ } }, "node_modules/webpack": { - "version": "5.88.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.1.tgz", - "integrity": "sha512-FROX3TxQnC/ox4N+3xQoWZzvGXSuscxR32rbzjpXgEzWudJFEJBpdlkkob2ylrv5yzzufD1zph1OoFsLtm6stQ==", + "version": "5.88.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz", + "integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==", "dependencies": { "@types/eslint-scope": "^3.7.3", "@types/estree": "^1.0.0", diff --git a/javascript/MaterialXView/package.json b/javascript/MaterialXView/package.json index 2113d145cf..adce3d2e82 100644 --- a/javascript/MaterialXView/package.json +++ b/javascript/MaterialXView/package.json @@ -11,8 +11,8 @@ "license": "ISC", "dependencies": { "dat.gui": "^0.7.9", - "three": "^0.135.0", - "webpack": "^5.88.1" + "three": "^0.136.0", + "webpack": "^5.88.2" }, "devDependencies": { "copy-webpack-plugin": "^8.1.1", diff --git a/javascript/MaterialXView/source/dropHandling.js b/javascript/MaterialXView/source/dropHandling.js new file mode 100644 index 0000000000..adcbef2ca1 --- /dev/null +++ b/javascript/MaterialXView/source/dropHandling.js @@ -0,0 +1,281 @@ +import * as THREE from 'three'; +import * as fflate from 'three/examples/jsm/libs/fflate.module.js'; + +const debugFileHandling = false; +let loadingCallback; + +export function setLoadingCallback(cb) { + loadingCallback = cb; +} + +export function dropHandler(ev) { + if (debugFileHandling) console.log('File(s) dropped', ev.dataTransfer.items, ev.dataTransfer.files); + + // Prevent default behavior (Prevent file from being opened) + ev.preventDefault(); + + if (ev.dataTransfer.items) + { + const allEntries = []; + + let haveGetAsEntry = false; + if (ev.dataTransfer.items.length > 0) + haveGetAsEntry = + ("getAsEntry" in ev.dataTransfer.items[0]) || + ("webkitGetAsEntry" in ev.dataTransfer.items[0]); + + // Useful for debugging file handling on platforms that don't support newer file system APIs + // haveGetAsEntry = false; + + if (haveGetAsEntry) { + for (var i = 0; i < ev.dataTransfer.items.length; i++) + { + let item = ev.dataTransfer.items[i]; + let entry = ("getAsEntry" in item) ? item.getAsEntry() : item.webkitGetAsEntry(); + allEntries.push(entry); + } + handleFilesystemEntries(allEntries); + return; + } + + for (var i = 0; i < ev.dataTransfer.items.length; i++) + { + let item = ev.dataTransfer.items[i]; + + // API when there's no "getAsEntry" support + console.log(item.kind, item); + if (item.kind === 'file') + { + var file = item.getAsFile(); + testAndLoadFile(file); + } + // could also be a directory + else if (item.kind === 'directory') + { + var dirReader = item.createReader(); + dirReader.readEntries(function(entries) { + for (var i = 0; i < entries.length; i++) { + console.log(entries[i].name); + var entry = entries[i]; + if (entry.isFile) { + entry.file(function(file) { + testAndLoadFile(file); + }); + } + } + }); + } + } + } else { + for (var i = 0; i < ev.dataTransfer.files.length; i++) { + let file = ev.dataTransfer.files[i]; + testAndLoadFile(file); + } + } +} + +export function dragOverHandler(ev) { + ev.preventDefault(); +} + +async function getBufferFromFile(fileEntry) { + + if (fileEntry instanceof ArrayBuffer) return fileEntry; + if (fileEntry instanceof String) return fileEntry; + + const name = fileEntry.fullPath || fileEntry.name; + const ext = name.split('.').pop(); + const readAsText = ext === 'mtlx'; + + if (debugFileHandling) console.log("reading ", fileEntry, "as text?", readAsText); + + if (debugFileHandling) console.log("getBufferFromFile", fileEntry); + const buffer = await new Promise((resolve, reject) => { + function readFile(file) { + var reader = new FileReader(); + reader.onloadend = function(e) { + if (debugFileHandling) console.log("loaded", "should be text?", readAsText, this.result); + resolve(this.result); + }; + + if (readAsText) + reader.readAsText(file); + else + reader.readAsArrayBuffer(file); + } + + if ("file" in fileEntry) { + fileEntry.file(function(file) { + readFile(file); + }, (e) => { + console.error("Error reading file ", e); + }); + } + else { + readFile(fileEntry); + } + }); + return buffer; +} + +async function handleFilesystemEntries(entries) { + const allFiles = []; + const fileIgnoreList = [ + '.gitignore', + 'README.md', + '.DS_Store', + ] + const dirIgnoreList = [ + '.git', + 'node_modules', + ] + + for (let entry of entries) { + if (debugFileHandling) console.log("file entry", entry) + if (entry.isFile) { + if (debugFileHandling) console.log("single file", entry); + if (fileIgnoreList.includes(entry.name)) { + continue; + } + allFiles.push(entry); + } + else if (entry.isDirectory) { + if (dirIgnoreList.includes(entry.name)) { + continue; + } + const files = await readDirectory(entry); + if (debugFileHandling) console.log("all files", files); + for (const file of files) { + if (fileIgnoreList.includes(file.name)) { + continue; + } + allFiles.push(file); + } + } + } + + const imageLoader = new THREE.ImageLoader(); + + // unpack zip files first + for (const fileEntry of allFiles) { + // special case: zip archives + if (fileEntry.fullPath.toLowerCase().endsWith('.zip')) { + await new Promise(async (resolve, reject) => { + const arrayBuffer = await getBufferFromFile(fileEntry); + + // use fflate to unpack them and add the files to the cache + fflate.unzip(new Uint8Array(arrayBuffer), (error, unzipped) => { + // push these files into allFiles + for (const [filePath, buffer] of Object.entries(unzipped)) { + + // mock FileEntry for easier usage downstream + const blob = new Blob([buffer]); + const newFileEntry = { + fullPath: "/" + filePath, + name: filePath.split('/').pop(), + file: (callback) => { + callback(blob); + }, + isFile: true, + }; + allFiles.push(newFileEntry); + } + + resolve(); + }); + }); + } + } + + // sort so mtlx files come first + allFiles.sort((a, b) => { + if (a.name.endsWith('.mtlx') && !b.name.endsWith('.mtlx')) { + return -1; + } + if (!a.name.endsWith('.mtlx') && b.name.endsWith('.mtlx')) { + return 1; + } + return 0; + }); + + if (debugFileHandling) console.log("all files", allFiles); + + // put all files in three' Cache + for (const fileEntry of allFiles) { + + const allowedFileTypes = [ + 'png', 'jpg', 'jpeg' + ]; + + const ext = fileEntry.fullPath.split('.').pop(); + if (!allowedFileTypes.includes(ext)) { + // console.log("skipping file", fileEntry.fullPath); + continue; + } + + const buffer = await getBufferFromFile(fileEntry); + const img = await imageLoader.loadAsync(URL.createObjectURL(new Blob([buffer]))); + if (debugFileHandling) console.log("caching file", fileEntry.fullPath, img); + THREE.Cache.add(fileEntry.fullPath, img); + } + + // TODO we could also allow dropping of multiple MaterialX files (or folders with them inside) + // and seed the dropdown from that. + // At that point, actually reading files and textures into memory should be deferred until they are actually used. + const rootFile = allFiles[0]; + THREE.Cache.add(rootFile.fullPath, await getBufferFromFile(rootFile)); + + if (debugFileHandling) console.log("CACHE", THREE.Cache.files); + + loadingCallback(rootFile); +} + +async function readDirectory(directory) { + let entries = []; + let getEntries = async (directory) => { + let dirReader = directory.createReader(); + await new Promise((resolve, reject) => { + dirReader.readEntries( + async (results) => { + if (results.length) { + // entries = entries.concat(results); + for (let entry of results) { + if (entry.isDirectory) { + await getEntries(entry); + } + else { + entries.push(entry); + } + } + } + resolve(); + }, + (error) => { + /* handle error — error is a FileError object */ + }, + )} + )}; + + await getEntries(directory); + return entries; +} + +async function testAndLoadFile(file) { + let ext = file.name.split('.').pop(); + if (debugFileHandling) console.log(file.name + ", " + file.size + ", " + ext); + + const arrayBuffer = await getBufferFromFile(file); + console.log(arrayBuffer) + + // mock a fileEntry and pass through the same loading logic + const newFileEntry = { + fullPath: "/" + file.name, + name: file.name.split('/').pop(), + isFile: true, + file: (callback) => { + callback(file); + } + }; + + handleFilesystemEntries([newFileEntry]); +} \ No newline at end of file diff --git a/javascript/MaterialXView/source/helper.js b/javascript/MaterialXView/source/helper.js index e8ffb9dcae..111dfd9b5d 100644 --- a/javascript/MaterialXView/source/helper.js +++ b/javascript/MaterialXView/source/helper.js @@ -20,8 +20,6 @@ const IMAGE_PATH_SEPARATOR = "/"; export function prepareEnvTexture(texture, capabilities) { const rgbaTexture = RGBToRGBA_Float(texture); - // RGBELoader sets flipY to true by default - rgbaTexture.flipY = false; rgbaTexture.wrapS = THREE.RepeatWrapping; rgbaTexture.anisotropy = capabilities.getMaxAnisotropy(); rgbaTexture.minFilter = THREE.LinearMipmapLinearFilter; diff --git a/javascript/MaterialXView/source/index.js b/javascript/MaterialXView/source/index.js index 424f63ec43..56e2e1f5a2 100644 --- a/javascript/MaterialXView/source/index.js +++ b/javascript/MaterialXView/source/index.js @@ -12,6 +12,7 @@ import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'; import { GammaCorrectionShader } from 'three/examples/jsm/shaders/GammaCorrectionShader.js'; import { Viewer } from './viewer.js' +import { dropHandler, dragOverHandler, setLoadingCallback } from './dropHandling.js'; let renderer, composer, orbitControls; @@ -115,6 +116,20 @@ function init() console.error(Number.isInteger(err) ? this.getMx().getExceptionMessage(err) : err); }) + // allow dropping files and directories + document.addEventListener('drop', dropHandler, false); + document.addEventListener('dragover', dragOverHandler, false); + + setLoadingCallback(file => { + materialFilename = file.fullPath || file.name; + viewer.getEditor().clearFolders(); + viewer.getMaterial().loadMaterials(viewer, materialFilename); + viewer.getEditor().updateProperties(0.9); + viewer.getScene().setUpdateTransforms(); + }); + + // enable three.js Cache so that dropped files can reference each other + THREE.Cache.enabled = true; } function onWindowResize() diff --git a/javascript/MaterialXView/source/viewer.js b/javascript/MaterialXView/source/viewer.js index 0bc537faf0..d1adfd9ef4 100644 --- a/javascript/MaterialXView/source/viewer.js +++ b/javascript/MaterialXView/source/viewer.js @@ -589,6 +589,7 @@ export class Material // Set search path. Assumes images are relative to current file // location. + if (!materialFilename) materialFilename = "/"; const paths = materialFilename.split('/'); paths.pop(); const searchPath = paths.join('/'); diff --git a/libraries/CMakeLists.txt b/libraries/CMakeLists.txt index 0d47cad5ed..d60a54cf3b 100644 --- a/libraries/CMakeLists.txt +++ b/libraries/CMakeLists.txt @@ -1,20 +1,28 @@ -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ - DESTINATION "${MATERIALX_INSTALL_STDLIB_PATH}" MESSAGE_NEVER - PATTERN "CMakeLists.txt" EXCLUDE - PATTERN "pbrlib_genosl_impl.*" EXCLUDE) - if (MATERIALX_OSL_LEGACY_CLOSURES) set(PBRLIB_SUFFIX "legacy") else() set(PBRLIB_SUFFIX "mtlx") endif() -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/pbrlib/genosl/pbrlib_genosl_impl.${PBRLIB_SUFFIX}" - DESTINATION "${MATERIALX_INSTALL_STDLIB_PATH}/pbrlib/genosl/" RENAME pbrlib_genosl_impl.mtlx) +if(NOT SKBUILD) + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ + DESTINATION "${MATERIALX_INSTALL_STDLIB_PATH}" + PATTERN "CMakeLists.txt" EXCLUDE + PATTERN "pbrlib_genosl_impl.*" EXCLUDE) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/pbrlib/genosl/pbrlib_genosl_impl.${PBRLIB_SUFFIX}" + DESTINATION "${MATERIALX_INSTALL_STDLIB_PATH}/pbrlib/genosl/" RENAME pbrlib_genosl_impl.mtlx) +endif() -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ - DESTINATION "python/MaterialX/${MATERIALX_INSTALL_STDLIB_PATH}" - PATTERN "CMakeLists.txt" EXCLUDE - PATTERN "pbrlib_genosl_impl.*" EXCLUDE) -install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/pbrlib/genosl/pbrlib_genosl_impl.${PBRLIB_SUFFIX}" - DESTINATION "python/MaterialX/${MATERIALX_INSTALL_STDLIB_PATH}/pbrlib/genosl/" RENAME pbrlib_genosl_impl.mtlx) +set(MATERIALX_PYTHON_LIBRARIES_PATH "${MATERIALX_PYTHON_FOLDER_NAME}/${MATERIALX_INSTALL_STDLIB_PATH}") +if(SKBUILD) + set(MATERIALX_PYTHON_LIBRARIES_PATH "${SKBUILD_PLATLIB_DIR}/MaterialX/libraries") +endif() + +if(MATERIALX_BUILD_PYTHON) + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ + DESTINATION "${MATERIALX_PYTHON_LIBRARIES_PATH}" + PATTERN "CMakeLists.txt" EXCLUDE + PATTERN "pbrlib_genosl_impl.*" EXCLUDE) + install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/pbrlib/genosl/pbrlib_genosl_impl.${PBRLIB_SUFFIX}" + DESTINATION "${MATERIALX_PYTHON_LIBRARIES_PATH}/pbrlib/genosl/" RENAME pbrlib_genosl_impl.mtlx) +endif() diff --git a/libraries/bxdf/gltf_pbr.mtlx b/libraries/bxdf/gltf_pbr.mtlx index cb393fb82a..6654e63454 100644 --- a/libraries/bxdf/gltf_pbr.mtlx +++ b/libraries/bxdf/gltf_pbr.mtlx @@ -359,7 +359,7 @@ - + @@ -375,13 +375,10 @@ - - - - + @@ -422,7 +419,7 @@ - + @@ -434,9 +431,6 @@ - - - @@ -458,7 +452,7 @@ - + @@ -480,7 +474,7 @@ - + @@ -492,9 +486,6 @@ - - - @@ -516,7 +507,7 @@ - + @@ -538,7 +529,7 @@ - + @@ -550,9 +541,6 @@ - - - @@ -574,7 +562,7 @@ - + @@ -595,7 +583,7 @@ - + @@ -607,9 +595,6 @@ - - - @@ -631,7 +616,7 @@ - + @@ -648,7 +633,7 @@ - + @@ -660,9 +645,6 @@ - - - @@ -687,7 +669,7 @@ - + @@ -704,7 +686,7 @@ - + @@ -725,7 +707,7 @@ - + diff --git a/libraries/pbrlib/genosl/mx_blackbody.osl b/libraries/pbrlib/genosl/mx_blackbody.osl index 8b4de5ba9f..95951330f2 100644 --- a/libraries/pbrlib/genosl/mx_blackbody.osl +++ b/libraries/pbrlib/genosl/mx_blackbody.osl @@ -1,10 +1,10 @@ -void mx_blackbody(float temperature, output color color_value) +void mx_blackbody(float temp, output color color_value) { float xc, yc; float t, t2, t3, xc2, xc3; // if value outside valid range of approximation clamp to accepted temperature range - temperature = clamp(temperature, 1667.0, 25000.0); + float temperature = clamp(temp, 1667.0, 25000.0); t = 1000.0 / temperature; t2 = t * t; @@ -39,9 +39,10 @@ void mx_blackbody(float temperature, output color color_value) vector XYZ = vector(xc / yc, 1.0, (1 - xc - yc) / yc); /// XYZ to Rec.709 RGB colorspace conversion - matrix XYZ_to_RGB = matrix( 3.2406, -0.9689, 0.0557, - -1.5372, 1.8758, -0.2040, - -0.4986, 0.0415, 1.0570); + matrix XYZ_to_RGB = matrix( 3.2406, -0.9689, 0.0557, 0.0, + -1.5372, 1.8758, -0.2040, 0.0, + -0.4986, 0.0415, 1.0570, 0.0, + 0.0, 0.0, 0.0, 1.0); color_value = transform(XYZ_to_RGB, XYZ); color_value = max(color_value, vector(0.0)); diff --git a/libraries/stdlib/genglsl/mx_image_color3.glsl b/libraries/stdlib/genglsl/mx_image_color3.glsl index 802318f475..8c4c039554 100644 --- a/libraries/stdlib/genglsl/mx_image_color3.glsl +++ b/libraries/stdlib/genglsl/mx_image_color3.glsl @@ -2,13 +2,6 @@ void mx_image_color3(sampler2D tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result) { - if (textureSize(tex_sampler, 0).x > 1) - { - vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); - result = texture(tex_sampler, uv).rgb; - } - else - { - result = defaultval; - } + vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); + result = texture(tex_sampler, uv).rgb; } diff --git a/libraries/stdlib/genglsl/mx_image_color4.glsl b/libraries/stdlib/genglsl/mx_image_color4.glsl index e74ad9445f..7541b9da04 100644 --- a/libraries/stdlib/genglsl/mx_image_color4.glsl +++ b/libraries/stdlib/genglsl/mx_image_color4.glsl @@ -2,13 +2,6 @@ void mx_image_color4(sampler2D tex_sampler, int layer, vec4 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec4 result) { - if (textureSize(tex_sampler, 0).x > 1) - { - vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); - result = texture(tex_sampler, uv); - } - else - { - result = defaultval; - } + vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); + result = texture(tex_sampler, uv); } diff --git a/libraries/stdlib/genglsl/mx_image_float.glsl b/libraries/stdlib/genglsl/mx_image_float.glsl index 9b831035d2..0a402a101c 100644 --- a/libraries/stdlib/genglsl/mx_image_float.glsl +++ b/libraries/stdlib/genglsl/mx_image_float.glsl @@ -2,13 +2,6 @@ void mx_image_float(sampler2D tex_sampler, int layer, float defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out float result) { - if (textureSize(tex_sampler, 0).x > 1) - { - vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); - result = texture(tex_sampler, uv).r; - } - else - { - result = defaultval; - } + vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); + result = texture(tex_sampler, uv).r; } diff --git a/libraries/stdlib/genglsl/mx_image_vector2.glsl b/libraries/stdlib/genglsl/mx_image_vector2.glsl index 124cb09287..42a7235297 100644 --- a/libraries/stdlib/genglsl/mx_image_vector2.glsl +++ b/libraries/stdlib/genglsl/mx_image_vector2.glsl @@ -2,13 +2,6 @@ void mx_image_vector2(sampler2D tex_sampler, int layer, vec2 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec2 result) { - if (textureSize(tex_sampler, 0).x > 1) - { - vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); - result = texture(tex_sampler, uv).rg; - } - else - { - result = defaultval; - } + vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); + result = texture(tex_sampler, uv).rg; } diff --git a/libraries/stdlib/genglsl/mx_image_vector3.glsl b/libraries/stdlib/genglsl/mx_image_vector3.glsl index 840e60fe6f..d49eab735e 100644 --- a/libraries/stdlib/genglsl/mx_image_vector3.glsl +++ b/libraries/stdlib/genglsl/mx_image_vector3.glsl @@ -2,13 +2,6 @@ void mx_image_vector3(sampler2D tex_sampler, int layer, vec3 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec3 result) { - if (textureSize(tex_sampler, 0).x > 1) - { - vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); - result = texture(tex_sampler, uv).rgb; - } - else - { - result = defaultval; - } + vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); + result = texture(tex_sampler, uv).rgb; } diff --git a/libraries/stdlib/genglsl/mx_image_vector4.glsl b/libraries/stdlib/genglsl/mx_image_vector4.glsl index f4b61d83f0..c8bdc66fc4 100644 --- a/libraries/stdlib/genglsl/mx_image_vector4.glsl +++ b/libraries/stdlib/genglsl/mx_image_vector4.glsl @@ -2,13 +2,6 @@ void mx_image_vector4(sampler2D tex_sampler, int layer, vec4 defaultval, vec2 texcoord, int uaddressmode, int vaddressmode, int filtertype, int framerange, int frameoffset, int frameendaction, vec2 uv_scale, vec2 uv_offset, out vec4 result) { - if (textureSize(tex_sampler, 0).x > 1) - { - vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); - result = texture(tex_sampler, uv); - } - else - { - result = defaultval; - } + vec2 uv = mx_transform_uv(texcoord, uv_scale, uv_offset); + result = texture(tex_sampler, uv); } diff --git a/libraries/stdlib/stdlib_defs.mtlx b/libraries/stdlib/stdlib_defs.mtlx index bc1240e574..bb5e8edd1f 100644 --- a/libraries/stdlib/stdlib_defs.mtlx +++ b/libraries/stdlib/stdlib_defs.mtlx @@ -307,6 +307,7 @@ + @@ -324,6 +325,7 @@ + @@ -341,6 +343,7 @@ + @@ -358,6 +361,7 @@ + @@ -375,6 +379,7 @@ + @@ -392,6 +397,7 @@ + @@ -1083,12 +1089,131 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/stdlib/stdlib_ng.mtlx b/libraries/stdlib/stdlib_ng.mtlx index f40001e14d..e031e72412 100644 --- a/libraries/stdlib/stdlib_ng.mtlx +++ b/libraries/stdlib/stdlib_ng.mtlx @@ -226,11 +226,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -242,7 +276,7 @@ - + @@ -254,7 +288,7 @@ - + @@ -346,11 +380,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -362,7 +430,7 @@ - + @@ -374,7 +442,7 @@ - + @@ -466,11 +534,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -482,7 +584,7 @@ - + @@ -494,7 +596,7 @@ - + @@ -586,11 +688,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -602,7 +738,7 @@ - + @@ -614,7 +750,7 @@ - + @@ -706,11 +842,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -722,7 +892,7 @@ - + @@ -734,7 +904,7 @@ - + @@ -826,11 +996,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + @@ -842,7 +1046,7 @@ - + @@ -854,7 +1058,7 @@ - + @@ -1246,13 +1450,9 @@ - - - - - - + + @@ -1380,49 +1580,788 @@ A 2D checkerboard pattern. --> - - - - - + + + + + + + + + + + + + + + + + - + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + + + + + + - - - - - - - - - + + + + + + + + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..e674339900 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,78 @@ +[build-system] +# Use a fixed version because we use an experimental feature +# (a custom plugin) and for now that functionality has +# no compatibility promises. +requires = ["scikit-build-core==0.4.4"] +build-backend = "scikit_build_core.build" + +[project] +name = "MaterialX" +dynamic = ["version"] + +authors = [ + { name="Contributors to the MaterialX project", email="materialx-discussion@lists.aswf.io" }, +] +readme = "README.md" +requires-python = ">=3.6" + +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: Apache Software License", + "Operating System :: OS Independent", +] + +[project.urls] +"Homepage" = "https://materialx.org" +"Source" = "https://github.com/AcademySoftwareFoundation/MaterialX" +"Bug Tracker" = "https://github.com/AcademySoftwareFoundation/MaterialX/issues" + +[project.scripts] +baketextures = "MaterialX._scripts.baketextures:main" +generateshader = "MaterialX._scripts.generateshader:main" +genmdl = "MaterialX._scripts.genmdl:main" +mxdoc = "MaterialX._scripts.mxdoc:main" +mxupdate = "MaterialX._scripts.mxupdate:main" +mxvalidate = "MaterialX._scripts.mxvalidate:main" +translateshader = "MaterialX._scripts.translateshader:main" +writenodegraphs = "MaterialX._scripts.writenodegraphs:main" + +[tool.scikit-build] +cmake.minimum-version = "3.18" +cmake.verbose = false +cmake.build-type = "Release" + +# Enable experimental features if any are available +# In this case we need custom local plugin to get +# the project version from cmake. +experimental = true +metadata.version.provider = "mtx_skbuild_plugin" +metadata.version.provider-path = "./python" + +# Uncoment when developing locally to enable inplace builds. +# build-dir = "build/" + +logging.level = "DEBUG" + +# Since the python package doesn't live in a standard directory +# in the source (i.e ./src or ./), we need to manually specify +# where the package is. +wheel.packages = ["python/MaterialX"] + +sdist.exclude = [ + "/build", + "/dist", + "/resources", + "/javascript", + "/documents", + "/.github", + "MANIFEST.in" +] + +[tool.scikit-build.cmake.define] +MATERIALX_BUILD_SHARED_LIBS = 'OFF' # Be explicit +MATERIALX_BUILD_PYTHON = 'ON' +MATERIALX_TEST_RENDER = 'OFF' +MATERIALX_WARNINGS_AS_ERRORS = 'ON' +MATERIALX_BUILD_TESTS = 'OFF' +# TODO: How could we harmonize this variable with SKBUILD? +MATERIALX_INSTALL_PYTHON = 'OFF' diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index e036c5abd5..da35a38b72 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -1,18 +1,25 @@ -set(SETUP_PY_IN "${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in") -set(SETUP_PY "${CMAKE_INSTALL_PREFIX}/python/setup.py") - -configure_file(${SETUP_PY_IN} ${SETUP_PY}) +if(NOT SKBUILD) + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/MaterialX" DESTINATION "python" MESSAGE_NEVER) + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/Scripts" DESTINATION "python" MESSAGE_NEVER) +endif() -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/MaterialX" DESTINATION "python" MESSAGE_NEVER) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/Scripts" DESTINATION "python" MESSAGE_NEVER) +if(SKBUILD) + install( + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/Scripts/" + DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}/_scripts" + PATTERN "README.md" EXCLUDE + ) +endif() if(MATERIALX_PYTHON_OCIO_DIR) if(NOT EXISTS "${MATERIALX_PYTHON_OCIO_DIR}/config.ocio") message(WARNING "No file named config.ocio was found in the given OCIO directory.") endif() - install(DIRECTORY "${MATERIALX_PYTHON_OCIO_DIR}/" DESTINATION "python/MaterialX/config/" MESSAGE_NEVER) + install(DIRECTORY "${MATERIALX_PYTHON_OCIO_DIR}/" DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}/config/" MESSAGE_NEVER) endif() -if(MATERIALX_INSTALL_PYTHON AND PYTHON_EXECUTABLE) - install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} install clean --all)" MESSAGE_NEVER) +if(MATERIALX_INSTALL_PYTHON AND PYTHON_EXECUTABLE AND NOT SKBUILD) + set(SETUP_PY "${CMAKE_INSTALL_PREFIX}/python/setup.py") + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in" "${SETUP_PY}") + install(CODE "execute_process(COMMAND ${PYTHON_EXECUTABLE} ${SETUP_PY} install clean --all)") endif() diff --git a/python/MaterialX/_scripts/README.md b/python/MaterialX/_scripts/README.md new file mode 100644 index 0000000000..f3586c5133 --- /dev/null +++ b/python/MaterialX/_scripts/README.md @@ -0,0 +1,2 @@ +This directory is empty buit it's used when packaging the Python library. +the files in ../../Scripts will be copied inside. diff --git a/python/MaterialX/_scripts/__init__.py b/python/MaterialX/_scripts/__init__.py new file mode 100644 index 0000000000..1217aefc79 --- /dev/null +++ b/python/MaterialX/_scripts/__init__.py @@ -0,0 +1 @@ +# Only required for entry-points. diff --git a/python/mtx_skbuild_plugin.py b/python/mtx_skbuild_plugin.py new file mode 100644 index 0000000000..56de6a9f53 --- /dev/null +++ b/python/mtx_skbuild_plugin.py @@ -0,0 +1,90 @@ +""" +This is a custom scikit-build-core plugin that will +fetch the MaterialX version from the CMake project. +""" +import os +import tempfile +import subprocess +from pathlib import Path +from typing import FrozenSet, Dict, Optional, Union, List + +from scikit_build_core.file_api.query import stateless_query +from scikit_build_core.file_api.reply import load_reply_dir + + +def dynamic_metadata( + fields: FrozenSet[str], + settings: Optional[Dict[str, object]] = None, +) -> Dict[str, Union[str, Dict[str, Optional[str]]]]: + print("mtx_skbuild_plugin: Computing MaterialX version from CMake...") + + if fields != {"version"}: + msg = "Only the 'version' field is supported" + raise ValueError(msg) + + if settings: + msg = "No inline configuration is supported" + raise ValueError(msg) + + current_dir = os.path.dirname(__file__) + + with tempfile.TemporaryDirectory() as tmpdir: + # We will use CMake's file API to get the version + # instead of parsing the CMakeLists files. + + # First generate the query folder so that CMake can generate replies. + reply_dir = stateless_query(Path(tmpdir)) + + # Run cmake (configure). CMake will generate a reply automatically. + try: + subprocess.run( + [ + "cmake", + "-S", + os.path.dirname(current_dir), + "-B", + tmpdir, + "-DMATERIALX_BUILD_SHARED_LIBS=OFF", + "-DMATERIALX_BUILD_PYTHON=OFF", + "-DMATERIALX_TEST_RENDER=OFF", + "-DMATERIALX_BUILD_TESTS=OFF", + "-DMATERIALX_INSTALL_PYTHON=OFF", + "-DMATERIALX_BUILD_RENDER=OFF", + ], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=True, + text=True, + ) + except subprocess.CalledProcessError as exc: + print(exc.stdout) + raise RuntimeError( + "Failed to configure project to get the version" + ) from exc + + # Get the generated replies. + index = load_reply_dir(reply_dir) + + # Get the version from the CMAKE_PROJECT_VERSION variable. + entries = [ + entry + for entry in index.reply.cache_v2.entries + if entry.name == "CMAKE_PROJECT_VERSION" + ] + + if not entries: + raise ValueError("Could not find MaterialX version from CMake project") + + if len(entries) > 1: + raise ValueError("More than one entry for CMAKE_PROJECT_VERSION found...") + + version = entries[0].value + print("mtx_skbuild_plugin: Computed version: {0}".format(version)) + + return {"version": version} + + +def get_requires_for_dynamic_metadata( + _settings: Optional[Dict[str, object]] = None, +) -> List[str]: + return ["cmake"] diff --git a/python/setup.py.in b/python/setup.py.in index dfe67330c2..072bafa968 100644 --- a/python/setup.py.in +++ b/python/setup.py.in @@ -14,4 +14,5 @@ setup(name='MaterialX', url='www.materialx.org', version='${MATERIALX_MAJOR_VERSION}.${MATERIALX_MINOR_VERSION}.${MATERIALX_BUILD_VERSION}', packages=['MaterialX'], - package_data={'MaterialX' : getRecursivePackageData('MaterialX')}) + package_data={'MaterialX' : getRecursivePackageData('MaterialX')}, + zip_safe = False) diff --git a/resources/CMakeLists.txt b/resources/CMakeLists.txt index 5c1a433cf9..10212c95c2 100644 --- a/resources/CMakeLists.txt +++ b/resources/CMakeLists.txt @@ -1,2 +1,4 @@ -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ - DESTINATION "resources" MESSAGE_NEVER) +if(NOT SKBUILD) + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ + DESTINATION "resources" MESSAGE_NEVER) +endif() diff --git a/resources/Materials/TestSuite/stdlib/nodegraph_inputs/top_level_input.mtlx b/resources/Materials/TestSuite/stdlib/nodegraph_inputs/top_level_input.mtlx new file mode 100644 index 0000000000..d92eae7f19 --- /dev/null +++ b/resources/Materials/TestSuite/stdlib/nodegraph_inputs/top_level_input.mtlx @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/resources/Materials/TestSuite/stdlib/organization/organization.mtlx b/resources/Materials/TestSuite/stdlib/organization/organization.mtlx index 21154aecdd..0f4c024848 100644 --- a/resources/Materials/TestSuite/stdlib/organization/organization.mtlx +++ b/resources/Materials/TestSuite/stdlib/organization/organization.mtlx @@ -90,4 +90,14 @@ + + + + + + + + + + diff --git a/resources/Materials/TestSuite/stdlib/procedural/linepattern.mtlx b/resources/Materials/TestSuite/stdlib/procedural/linepattern.mtlx new file mode 100644 index 0000000000..032abf129d --- /dev/null +++ b/resources/Materials/TestSuite/stdlib/procedural/linepattern.mtlx @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/Materials/TestSuite/stdlib/procedural/tiledshape.mtlx b/resources/Materials/TestSuite/stdlib/procedural/tiledshape.mtlx new file mode 100644 index 0000000000..69fd225f2c --- /dev/null +++ b/resources/Materials/TestSuite/stdlib/procedural/tiledshape.mtlx @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/resources/Materials/TestSuite/stdlib/texture/image_default.mtlx b/resources/Materials/TestSuite/stdlib/texture/image_default.mtlx new file mode 100644 index 0000000000..89b99c3c39 --- /dev/null +++ b/resources/Materials/TestSuite/stdlib/texture/image_default.mtlx @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/MaterialXCore/CMakeLists.txt b/source/MaterialXCore/CMakeLists.txt index 37edf7929a..33e4fafdf4 100644 --- a/source/MaterialXCore/CMakeLists.txt +++ b/source/MaterialXCore/CMakeLists.txt @@ -1,25 +1,33 @@ +set(MATERIALX_MODULE_NAME MaterialXCore) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Generated.h.in ${CMAKE_CURRENT_BINARY_DIR}/Generated.h) file(GLOB materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h" "${CMAKE_CURRENT_BINARY_DIR}/*.h") -add_library(MaterialXCore ${materialx_source} ${materialx_headers}) +add_library(${MATERIALX_MODULE_NAME} ${materialx_source} ${materialx_headers}) add_definitions(-DMATERIALX_CORE_EXPORTS) +# Create version resource +if(MATERIALX_BUILD_SHARED_LIBS AND MSVC) + configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/MaterialXVersion.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + target_sources(${MATERIALX_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + set_target_properties( - MaterialXCore PROPERTIES - OUTPUT_NAME MaterialXCore${MATERIALX_LIBNAME_SUFFIX} + ${MATERIALX_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX} COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" VERSION "${MATERIALX_LIBRARY_VERSION}" SOVERSION "${MATERIALX_MAJOR_VERSION}") target_link_libraries( - MaterialXCore + ${MATERIALX_MODULE_NAME} ${CMAKE_DL_LIBS}) -target_include_directories(MaterialXCore +target_include_directories(${MATERIALX_MODULE_NAME} PUBLIC $ $ @@ -27,14 +35,16 @@ target_include_directories(MaterialXCore PRIVATE ${EXTERNAL_INCLUDE_DIRS}) -install(TARGETS MaterialXCore - EXPORT MaterialX - ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - RUNTIME DESTINATION bin) +if(NOT SKBUILD) + install(TARGETS ${MATERIALX_MODULE_NAME} + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + RUNTIME DESTINATION bin) -install(FILES ${materialx_headers} - DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXCore/) + install(FILES ${materialx_headers} + DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/${MATERIALX_MODULE_NAME}/) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXCore.pdb" - DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${MATERIALX_MODULE_NAME}.pdb" + DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) +endif() diff --git a/source/MaterialXCore/Definition.cpp b/source/MaterialXCore/Definition.cpp index 5ec7044325..cf9587ecda 100644 --- a/source/MaterialXCore/Definition.cpp +++ b/source/MaterialXCore/Definition.cpp @@ -72,17 +72,29 @@ InterfaceElementPtr NodeDef::getImplementation(const string& target) const const TargetDefPtr targetDef = getDocument()->getTargetDef(target); const StringVec candidateTargets = targetDef ? targetDef->getMatchingTargets() : StringVec(); - // Search the candidate targets in order + // First, search for a target-specific match. for (const string& candidateTarget : candidateTargets) { for (InterfaceElementPtr interface : interfaces) { - if (targetStringsMatch(interface->getTarget(), candidateTarget)) + const std::string& interfaceTarget = interface->getTarget(); + if (!interfaceTarget.empty() && targetStringsMatch(interfaceTarget, candidateTarget)) { return interface; } } } + + // Then search for a generic match. + for (InterfaceElementPtr interface : interfaces) + { + // Look for interfaces without targets + const std::string& interfaceTarget = interface->getTarget(); + if (interfaceTarget.empty()) + { + return interface; + } + } return InterfaceElementPtr(); } diff --git a/source/MaterialXCore/Document.cpp b/source/MaterialXCore/Document.cpp index 46d0552250..79297fa5a9 100644 --- a/source/MaterialXCore/Document.cpp +++ b/source/MaterialXCore/Document.cpp @@ -124,6 +124,7 @@ class Document::Cache for (ElementPtr elem : doc.lock()->traverseTree()) { const string& nodeName = elem->getAttribute(PortElement::NODE_NAME_ATTRIBUTE); + const string& nodeGraphName = elem->getAttribute(PortElement::NODE_GRAPH_ATTRIBUTE); const string& nodeString = elem->getAttribute(NodeDef::NODE_ATTRIBUTE); const string& nodeDefString = elem->getAttribute(InterfaceElement::NODE_DEF_ATTRIBUTE); @@ -135,6 +136,17 @@ class Document::Cache portElementMap.emplace(portElem->getQualifiedName(nodeName), portElem); } } + else + { + if (!nodeGraphName.empty()) + { + PortElementPtr portElem = elem->asA(); + if (portElem) + { + portElementMap.emplace(portElem->getQualifiedName(nodeGraphName), portElem); + } + } + } if (!nodeString.empty()) { NodeDefPtr nodeDef = elem->asA(); diff --git a/source/MaterialXCore/Element.h b/source/MaterialXCore/Element.h index 8e43d9aa33..0e00bf596a 100644 --- a/source/MaterialXCore/Element.h +++ b/source/MaterialXCore/Element.h @@ -300,7 +300,7 @@ class MX_CORE_API Element : public std::enable_shared_from_this /// Return the element, if any, that this one directly inherits from. ElementPtr getInheritsFrom() const { - return resolveNameReference(getInheritString()); + return hasInheritString() ? resolveNameReference(getInheritString()) : nullptr; } /// Return true if this element has the given element as an inherited base, @@ -739,13 +739,13 @@ class MX_CORE_API Element : public std::enable_shared_from_this void copyContentFrom(const ConstElementPtr& source); /// Clear all attributes and descendants from this element. - void clearContent(); + virtual void clearContent(); /// Using the input name as a starting point, modify it to create a valid, /// unique name for a child element. string createValidChildName(string name) const { - name = createValidName(name); + name = name.empty() ? "_" : createValidName(name); while (_childMap.count(name)) { name = incrementName(name); diff --git a/source/MaterialXCore/Interface.cpp b/source/MaterialXCore/Interface.cpp index 3a93a384c2..65570ab6b6 100644 --- a/source/MaterialXCore/Interface.cpp +++ b/source/MaterialXCore/Interface.cpp @@ -305,7 +305,7 @@ InputPtr Input::getInterfaceInput() const { if (hasInterfaceName()) { - ConstNodeGraphPtr graph = getAncestorOfType(); + ConstGraphElementPtr graph = getAncestorOfType(); if (graph) { return graph->getInput(getInterfaceName()); @@ -611,6 +611,13 @@ ConstInterfaceElementPtr InterfaceElement::getDeclaration(const string&) const return InterfaceElementPtr(); } +void InterfaceElement::clearContent() +{ + _inputCount = 0; + _outputCount = 0; + TypedElement::clearContent(); +} + bool InterfaceElement::hasExactInputMatch(ConstInterfaceElementPtr declaration, string* message) const { for (InputPtr input : getActiveInputs()) diff --git a/source/MaterialXCore/Interface.h b/source/MaterialXCore/Interface.h index 94f6eddbaa..0773d5f836 100644 --- a/source/MaterialXCore/Interface.h +++ b/source/MaterialXCore/Interface.h @@ -639,6 +639,9 @@ class MX_CORE_API InterfaceElement : public TypedElement /// no declaration was found. virtual ConstInterfaceElementPtr getDeclaration(const string& target = EMPTY_STRING) const; + /// Clear all attributes and descendants from this element. + void clearContent() override; + /// Return true if this instance has an exact input match with the given /// declaration, where each input of this the instance corresponds to a /// declaration input of the same name and type. diff --git a/source/MaterialXCore/Node.cpp b/source/MaterialXCore/Node.cpp index 45524b116d..9596d905bc 100644 --- a/source/MaterialXCore/Node.cpp +++ b/source/MaterialXCore/Node.cpp @@ -439,9 +439,7 @@ vector GraphElement::topologicalSort() const } } - size_t visitCount = 0; vector result; - while (!childQueue.empty()) { // Pop the queue and add to topological order. @@ -467,14 +465,6 @@ vector GraphElement::topologicalSort() const } } } - - visitCount++; - } - - // Check if there was a cycle. - if (visitCount != children.size()) - { - throw ExceptionFoundCycle("Encountered a cycle in graph: " + getName()); } return result; @@ -738,6 +728,25 @@ InterfaceElementPtr NodeGraph::getImplementation() const return nodedef ? nodedef->getImplementation() : InterfaceElementPtr(); } +vector NodeGraph::getDownstreamPorts() const +{ + vector downstreamPorts; + for (PortElementPtr port : getDocument()->getMatchingPorts(getQualifiedName(getName()))) + { + ElementPtr node = port->getParent(); + ElementPtr graph = node ? node->getParent() : nullptr; + if (graph && graph->isA() && graph == getParent()) + { + downstreamPorts.push_back(port); + } + } + std::sort(downstreamPorts.begin(), downstreamPorts.end(), [](const ConstElementPtr& a, const ConstElementPtr& b) + { + return a->getName() > b->getName(); + }); + return downstreamPorts; +} + bool NodeGraph::validate(string* message) const { bool res = true; diff --git a/source/MaterialXCore/Node.h b/source/MaterialXCore/Node.h index a316b0eb6c..55846cf2e3 100644 --- a/source/MaterialXCore/Node.h +++ b/source/MaterialXCore/Node.h @@ -308,7 +308,6 @@ class MX_CORE_API GraphElement : public InterfaceElement /// Return a vector of all children (nodes and outputs) sorted in /// topological order. - /// @throws ExceptionFoundCycle if a cycle is encountered. vector topologicalSort() const; /// If not yet present, add a geometry node to this graph matching the given property @@ -358,6 +357,14 @@ class MX_CORE_API NodeGraph : public GraphElement /// none was found. InterfaceElementPtr getImplementation() const; + /// @} + /// @name Traversal + /// @{ + + /// Return a vector of all downstream ports that connect to this graph, ordered by + /// the names of the port elements. + vector getDownstreamPorts() const; + /// @} /// @name Utility /// @{ diff --git a/source/MaterialXCore/Types.h b/source/MaterialXCore/Types.h index 68f477ee04..5c64d0950a 100644 --- a/source/MaterialXCore/Types.h +++ b/source/MaterialXCore/Types.h @@ -592,9 +592,9 @@ class MX_CORE_API Matrix33 : public MatrixN float m20, float m21, float m22) : MatrixN(Uninit{}) { - _arr = { m00, m01, m02, - m10, m11, m12, - m20, m21, m22 }; + _arr = { RowArray{ m00, m01, m02 }, + RowArray{ m10, m11, m12 }, + RowArray{ m20, m21, m22 } }; } /// @name Matrix Operations @@ -663,10 +663,10 @@ class MX_CORE_API Matrix44 : public MatrixN float m30, float m31, float m32, float m33) : MatrixN(Uninit{}) { - _arr = { m00, m01, m02, m03, - m10, m11, m12, m13, - m20, m21, m22, m23, - m30, m31, m32, m33 }; + _arr = { RowArray{ m00, m01, m02, m03 }, + RowArray{ m10, m11, m12, m13 }, + RowArray{ m20, m21, m22, m23 }, + RowArray{ m30, m31, m32, m33 } }; } /// @name Matrix Operations diff --git a/source/MaterialXFormat/CMakeLists.txt b/source/MaterialXFormat/CMakeLists.txt index 940ef9662c..420149bae4 100644 --- a/source/MaterialXFormat/CMakeLists.txt +++ b/source/MaterialXFormat/CMakeLists.txt @@ -1,16 +1,24 @@ +set(MATERIALX_MODULE_NAME MaterialXFormat) + file(GLOB_RECURSE materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB_RECURSE materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h*") assign_source_group("Source Files" ${materialx_source}) assign_source_group("Header Files" ${materialx_headers}) -add_library(MaterialXFormat ${materialx_source} ${materialx_headers}) +add_library(${MATERIALX_MODULE_NAME} ${materialx_source} ${materialx_headers}) add_definitions(-DMATERIALX_FORMAT_EXPORTS) +# Create version resource +if(MATERIALX_BUILD_SHARED_LIBS AND MSVC) + configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/MaterialXVersion.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + target_sources(${MATERIALX_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + set_target_properties( - MaterialXFormat PROPERTIES - OUTPUT_NAME MaterialXFormat${MATERIALX_LIBNAME_SUFFIX} + ${MATERIALX_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX} COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_SAME_DIR_RPATH}" @@ -18,26 +26,28 @@ set_target_properties( SOVERSION "${MATERIALX_MAJOR_VERSION}") target_link_libraries( - MaterialXFormat + ${MATERIALX_MODULE_NAME} MaterialXCore ${CMAKE_DL_LIBS}) -target_include_directories(MaterialXFormat +target_include_directories(${MATERIALX_MODULE_NAME} PUBLIC $ $ PRIVATE ${EXTERNAL_INCLUDE_DIRS}) -install(TARGETS MaterialXFormat - EXPORT MaterialX - ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - RUNTIME DESTINATION bin) +if(NOT SKBUILD) + install(TARGETS ${MATERIALX_MODULE_NAME} + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + RUNTIME DESTINATION bin) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXFormat/ MESSAGE_NEVER - FILES_MATCHING PATTERN "*.h*") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/${MATERIALX_MODULE_NAME}/ MESSAGE_NEVER + FILES_MATCHING PATTERN "*.h*") -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXFormat.pdb" - DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${MATERIALX_MODULE_NAME}.pdb" + DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) +endif() diff --git a/source/MaterialXGenGlsl/CMakeLists.txt b/source/MaterialXGenGlsl/CMakeLists.txt index 2ddcb5475c..da497f9f88 100644 --- a/source/MaterialXGenGlsl/CMakeLists.txt +++ b/source/MaterialXGenGlsl/CMakeLists.txt @@ -1,16 +1,24 @@ +set(MATERIALX_MODULE_NAME MaterialXGenGlsl) + file(GLOB_RECURSE materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB_RECURSE materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h*") assign_source_group("Source Files" ${materialx_source}) assign_source_group("Header Files" ${materialx_headers}) -add_library(MaterialXGenGlsl ${materialx_source} ${materialx_headers}) +add_library(${MATERIALX_MODULE_NAME} ${materialx_source} ${materialx_headers}) add_definitions(-DMATERIALX_GENGLSL_EXPORTS) +# Create version resource +if(MATERIALX_BUILD_SHARED_LIBS AND MSVC) + configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/MaterialXVersion.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + target_sources(${MATERIALX_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + set_target_properties( - MaterialXGenGlsl PROPERTIES - OUTPUT_NAME MaterialXGenGlsl${MATERIALX_LIBNAME_SUFFIX} + ${MATERIALX_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX} COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_SAME_DIR_RPATH}" @@ -18,27 +26,29 @@ set_target_properties( SOVERSION "${MATERIALX_MAJOR_VERSION}") target_link_libraries( - MaterialXGenGlsl + ${MATERIALX_MODULE_NAME} MaterialXGenShader MaterialXCore ${CMAKE_DL_LIBS}) -target_include_directories(MaterialXGenGlsl +target_include_directories(${MATERIALX_MODULE_NAME} PUBLIC $ $ PRIVATE ${EXTERNAL_INCLUDE_DIRS}) -install(TARGETS MaterialXGenGlsl - EXPORT MaterialX - ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - RUNTIME DESTINATION bin) +if(NOT SKBUILD) + install(TARGETS ${MATERIALX_MODULE_NAME} + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + RUNTIME DESTINATION bin) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXGenGlsl/ MESSAGE_NEVER - FILES_MATCHING PATTERN "*.h*") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/${MATERIALX_MODULE_NAME}/ MESSAGE_NEVER + FILES_MATCHING PATTERN "*.h*") -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXGenGlsl.pdb" - DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${MATERIALX_MODULE_NAME}.pdb" + DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) +endif() diff --git a/source/MaterialXGenMdl/CMakeLists.txt b/source/MaterialXGenMdl/CMakeLists.txt index b73e674b7f..d9991843ce 100644 --- a/source/MaterialXGenMdl/CMakeLists.txt +++ b/source/MaterialXGenMdl/CMakeLists.txt @@ -1,16 +1,24 @@ +set(MATERIALX_MODULE_NAME MaterialXGenMdl) + file(GLOB_RECURSE materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB_RECURSE materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h*") assign_source_group("Source Files" ${materialx_source}) assign_source_group("Header Files" ${materialx_headers}) -add_library(MaterialXGenMdl ${materialx_source} ${materialx_headers}) +add_library(${MATERIALX_MODULE_NAME} ${materialx_source} ${materialx_headers}) add_definitions(-DMATERIALX_GENMDL_EXPORTS) +# Create version resource +if(MATERIALX_BUILD_SHARED_LIBS AND MSVC) + configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/MaterialXVersion.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + target_sources(${MATERIALX_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + set_target_properties( - MaterialXGenMdl PROPERTIES - OUTPUT_NAME MaterialXGenMdl${MATERIALX_LIBNAME_SUFFIX} + ${MATERIALX_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX} COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_SAME_DIR_RPATH}" @@ -18,30 +26,32 @@ set_target_properties( SOVERSION "${MATERIALX_MAJOR_VERSION}") target_link_libraries( - MaterialXGenMdl + ${MATERIALX_MODULE_NAME} MaterialXGenShader MaterialXCore ${CMAKE_DL_LIBS}) -target_include_directories(MaterialXGenMdl +target_include_directories(${MATERIALX_MODULE_NAME} PUBLIC $ $ PRIVATE ${EXTERNAL_INCLUDE_DIRS}) -install(TARGETS MaterialXGenMdl - EXPORT MaterialX - ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - RUNTIME DESTINATION bin) +if(NOT SKBUILD) + install(TARGETS ${MATERIALX_MODULE_NAME} + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + RUNTIME DESTINATION bin) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXGenMdl/ MESSAGE_NEVER - FILES_MATCHING PATTERN "*.h*") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/${MATERIALX_MODULE_NAME}/ MESSAGE_NEVER + FILES_MATCHING PATTERN "*.h*") -install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/mdl - DESTINATION "${MATERIALX_INSTALL_MDL_MODULE_PATH}") + install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/mdl + DESTINATION "${MATERIALX_INSTALL_MDL_MODULE_PATH}") -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXGenMdl.pdb" - DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${MATERIALX_MODULE_NAME}.pdb" + DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) +endif() diff --git a/source/MaterialXGenMsl/CMakeLists.txt b/source/MaterialXGenMsl/CMakeLists.txt index 31d2599531..8d6d009f50 100644 --- a/source/MaterialXGenMsl/CMakeLists.txt +++ b/source/MaterialXGenMsl/CMakeLists.txt @@ -1,16 +1,24 @@ +set(MATERIALX_MODULE_NAME MaterialXGenMsl) + file(GLOB_RECURSE materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB_RECURSE materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h*") assign_source_group("Source Files" ${materialx_source}) assign_source_group("Header Files" ${materialx_headers}) -add_library(MaterialXGenMsl ${materialx_source} ${materialx_headers}) +add_library(${MATERIALX_MODULE_NAME} ${materialx_source} ${materialx_headers}) add_definitions(-DMATERIALX_GENMSL_EXPORTS) +# Create version resource +if(MATERIALX_BUILD_SHARED_LIBS AND MSVC) + configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/MaterialXVersion.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + target_sources(${MATERIALX_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + set_target_properties( - MaterialXGenMsl PROPERTIES - OUTPUT_NAME MaterialXGenMsl${MATERIALX_LIBNAME_SUFFIX} + ${MATERIALX_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX} COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_SAME_DIR_RPATH}" @@ -18,27 +26,29 @@ set_target_properties( SOVERSION "${MATERIALX_MAJOR_VERSION}") target_link_libraries( - MaterialXGenMsl + ${MATERIALX_MODULE_NAME} MaterialXGenShader MaterialXCore ${CMAKE_DL_LIBS}) -target_include_directories(MaterialXGenMsl +target_include_directories(${MATERIALX_MODULE_NAME} PUBLIC $ $ PRIVATE ${EXTERNAL_INCLUDE_DIRS}) -install(TARGETS MaterialXGenMsl - EXPORT MaterialX - ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - RUNTIME DESTINATION bin) +if(NOT SKBUILD) + install(TARGETS ${MATERIALX_MODULE_NAME} + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + RUNTIME DESTINATION bin) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXGenMsl/ MESSAGE_NEVER - FILES_MATCHING PATTERN "*.h*") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/${MATERIALX_MODULE_NAME}/ MESSAGE_NEVER + FILES_MATCHING PATTERN "*.h*") -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXGenMsl.pdb" - DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${MATERIALX_MODULE_NAME}.pdb" + DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) +endif() diff --git a/source/MaterialXGenMsl/MslShaderGenerator.cpp b/source/MaterialXGenMsl/MslShaderGenerator.cpp index 61a1c8f572..dcb0a3ed69 100644 --- a/source/MaterialXGenMsl/MslShaderGenerator.cpp +++ b/source/MaterialXGenMsl/MslShaderGenerator.cpp @@ -43,6 +43,8 @@ #include "MslResourceBindingContext.h" +#include + MATERIALX_NAMESPACE_BEGIN const string MslShaderGenerator::TARGET = "genmsl"; diff --git a/source/MaterialXGenOsl/CMakeLists.txt b/source/MaterialXGenOsl/CMakeLists.txt index 03e30de0c1..3cf7b93438 100644 --- a/source/MaterialXGenOsl/CMakeLists.txt +++ b/source/MaterialXGenOsl/CMakeLists.txt @@ -1,16 +1,24 @@ +set(MATERIALX_MODULE_NAME MaterialXGenOsl) + file(GLOB_RECURSE materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB_RECURSE materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h*") assign_source_group("Source Files" ${materialx_source}) assign_source_group("Header Files" ${materialx_headers}) -add_library(MaterialXGenOsl ${materialx_source} ${materialx_headers}) +add_library(${MATERIALX_MODULE_NAME} ${materialx_source} ${materialx_headers}) add_definitions(-DMATERIALX_GENOSL_EXPORTS) +# Create version resource +if(MATERIALX_BUILD_SHARED_LIBS AND MSVC) + configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/MaterialXVersion.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + target_sources(${MATERIALX_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + set_target_properties( - MaterialXGenOsl PROPERTIES - OUTPUT_NAME MaterialXGenOsl${MATERIALX_LIBNAME_SUFFIX} + ${MATERIALX_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX} COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_SAME_DIR_RPATH}" @@ -18,28 +26,29 @@ set_target_properties( SOVERSION "${MATERIALX_MAJOR_VERSION}") target_link_libraries( - MaterialXGenOsl + ${MATERIALX_MODULE_NAME} MaterialXGenShader MaterialXCore ${CMAKE_DL_LIBS}) -target_include_directories(MaterialXGenOsl +target_include_directories(${MATERIALX_MODULE_NAME} PUBLIC $ $ PRIVATE ${EXTERNAL_INCLUDE_DIRS}) -install(TARGETS MaterialXGenOsl - EXPORT MaterialX - ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - RUNTIME DESTINATION bin) - -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXGenOsl/ MESSAGE_NEVER - FILES_MATCHING PATTERN "*.h*") +if(NOT SKBUILD) + install(TARGETS ${MATERIALX_MODULE_NAME} + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + RUNTIME DESTINATION bin) -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXGenOsl.pdb" - DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/${MATERIALX_MODULE_NAME}/ MESSAGE_NEVER + FILES_MATCHING PATTERN "*.h*") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${MATERIALX_MODULE_NAME}.pdb" + DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) +endif() diff --git a/source/MaterialXGenShader/CMakeLists.txt b/source/MaterialXGenShader/CMakeLists.txt index 231dda6e5c..1b9533e0a5 100644 --- a/source/MaterialXGenShader/CMakeLists.txt +++ b/source/MaterialXGenShader/CMakeLists.txt @@ -1,16 +1,24 @@ +set(MATERIALX_MODULE_NAME MaterialXGenShader) + file(GLOB_RECURSE materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB_RECURSE materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h*") assign_source_group("Source Files" ${materialx_source}) assign_source_group("Header Files" ${materialx_headers}) -add_library(MaterialXGenShader ${materialx_source} ${materialx_headers}) +add_library(${MATERIALX_MODULE_NAME} ${materialx_source} ${materialx_headers}) add_definitions(-DMATERIALX_GENSHADER_EXPORTS) +# Create version resource +if(MATERIALX_BUILD_SHARED_LIBS AND MSVC) + configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/MaterialXVersion.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + target_sources(${MATERIALX_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + set_target_properties( - MaterialXGenShader PROPERTIES - OUTPUT_NAME MaterialXGenShader${MATERIALX_LIBNAME_SUFFIX} + ${MATERIALX_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX} COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_SAME_DIR_RPATH}" @@ -18,30 +26,32 @@ set_target_properties( SOVERSION "${MATERIALX_MAJOR_VERSION}") target_link_libraries( - MaterialXGenShader + ${MATERIALX_MODULE_NAME} MaterialXCore MaterialXFormat ${CMAKE_DL_LIBS}) -target_include_directories(MaterialXGenShader +target_include_directories(${MATERIALX_MODULE_NAME} PUBLIC $ $ PRIVATE ${EXTERNAL_INCLUDE_DIRS}) -install(TARGETS MaterialXGenShader - EXPORT MaterialX - ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - RUNTIME DESTINATION bin) +if(NOT SKBUILD) + install(TARGETS ${MATERIALX_MODULE_NAME} + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + RUNTIME DESTINATION bin) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXGenShader/ MESSAGE_NEVER - FILES_MATCHING PATTERN "*.h*") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/${MATERIALX_MODULE_NAME}/ MESSAGE_NEVER + FILES_MATCHING PATTERN "*.h*") -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXGenShader.pdb" - DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${MATERIALX_MODULE_NAME}.pdb" + DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../resources" - DESTINATION . MESSAGE_NEVER) + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../resources" + DESTINATION . MESSAGE_NEVER) +endif() diff --git a/source/MaterialXGenShader/ShaderGraph.cpp b/source/MaterialXGenShader/ShaderGraph.cpp index e6ad6eca29..8482659b34 100644 --- a/source/MaterialXGenShader/ShaderGraph.cpp +++ b/source/MaterialXGenShader/ShaderGraph.cpp @@ -1027,48 +1027,16 @@ void ShaderGraph::optimize(GenContext& context) } else if (node->hasClassification(ShaderNode::Classification::DOT)) { - // Dot nodes without modifiers can be elided by moving their connection downstream. + // Filename dot nodes must be elided so they do not create extra samplers. ShaderInput* in = node->getInput("in"); - if (in->getChannels().empty()) + if (in->getChannels().empty() && in->getType() == Type::FILENAME) { bypass(context, node, 0); ++numEdits; } } - else if (node->hasClassification(ShaderNode::Classification::IFELSE)) - { - // Check if we have a constant conditional expression - ShaderInput* intest = node->getInput("intest"); - if (!intest->getConnection()) - { - // Find which branch should be taken - ShaderInput* cutoff = node->getInput("cutoff"); - ValuePtr value = intest->getValue(); - const float intestValue = value ? value->asA() : 0.0f; - const int branch = (intestValue <= cutoff->getValue()->asA() ? 2 : 3); - - // Bypass the conditional using the taken branch - bypass(context, node, branch); - - ++numEdits; - } - } - else if (node->hasClassification(ShaderNode::Classification::SWITCH)) - { - // Check if we have a constant conditional expression - const ShaderInput* which = node->getInput("which"); - if (!which->getConnection()) - { - // Find which branch should be taken - ValuePtr value = which->getValue(); - const int branch = int(value == nullptr ? 0 : (which->getType() == Type::FLOAT ? value->asA() : value->asA())); - - // Bypass the conditional using the taken branch - bypass(context, node, branch); - - ++numEdits; - } - } + // Adding more nodes here requires them to have an input that is tagged + // "uniform" in the NodeDef or to handle very specific cases, like FILENAME. } if (numEdits > 0) @@ -1225,12 +1193,6 @@ void ShaderGraph::topologicalSort() } } } - - // Check if there was a cycle. - if (count != _nodeMap.size()) - { - throw ExceptionFoundCycle("Encountered a cycle in graph: " + getName()); - } } void ShaderGraph::setVariableNames(GenContext& context) diff --git a/source/MaterialXGenShader/ShaderGraph.h b/source/MaterialXGenShader/ShaderGraph.h index 4e30c4ae66..4117599c27 100644 --- a/source/MaterialXGenShader/ShaderGraph.h +++ b/source/MaterialXGenShader/ShaderGraph.h @@ -103,7 +103,6 @@ class MX_GENSHADER_API ShaderGraph : public ShaderNode void addDefaultGeomNode(ShaderInput* input, const GeomPropDef& geomprop, GenContext& context); /// Sort the nodes in topological order. - /// @throws ExceptionFoundCycle if a cycle is encountered. void topologicalSort(); /// Return an iterator for traversal upstream from the given output diff --git a/source/MaterialXGenShader/ShaderNode.cpp b/source/MaterialXGenShader/ShaderNode.cpp index c923e4be57..04551cb6d2 100644 --- a/source/MaterialXGenShader/ShaderNode.cpp +++ b/source/MaterialXGenShader/ShaderNode.cpp @@ -135,8 +135,6 @@ const ShaderNodePtr ShaderNode::NONE = createEmptyNode(); const string ShaderNode::CONSTANT = "constant"; const string ShaderNode::DOT = "dot"; const string ShaderNode::IMAGE = "image"; -const string ShaderNode::COMPARE = "compare"; -const string ShaderNode::SWITCH = "switch"; const string ShaderNode::SURFACESHADER = "surfaceshader"; const string ShaderNode::SCATTER_MODE = "scatter_mode"; const string ShaderNode::BSDF_R = "R"; @@ -292,14 +290,6 @@ ShaderNodePtr ShaderNode::create(const ShaderGraph* parent, const string& name, { newNode->_classification = Classification::TEXTURE | Classification::DOT; } - else if (nodeDef.getNodeString() == COMPARE) - { - newNode->_classification = Classification::TEXTURE | Classification::CONDITIONAL | Classification::IFELSE; - } - else if (nodeDef.getNodeString() == SWITCH) - { - newNode->_classification = Classification::TEXTURE | Classification::CONDITIONAL | Classification::SWITCH; - } // Third, check for file texture classification by group name else if (groupName == TEXTURE2D_GROUPNAME || groupName == TEXTURE3D_GROUPNAME) { @@ -381,7 +371,17 @@ void ShaderNode::initialize(const Node& node, const NodeDef& nodeDef, GenContext ShaderInput* input = getInput(nodeValue->getName()); if (input) { - input->setPath(nodeValue->getNamePath()); + string path = nodeValue->getNamePath(); + InputPtr nodeInput = nodeValue->asA(); + if (nodeInput) + { + InputPtr interfaceInput = nodeInput->getInterfaceInput(); + if (interfaceInput) + { + path = interfaceInput->getNamePath(); + } + } + input->setPath(path); } } diff --git a/source/MaterialXGenShader/ShaderNode.h b/source/MaterialXGenShader/ShaderNode.h index 30e3ab6d59..536e0adc1e 100644 --- a/source/MaterialXGenShader/ShaderNode.h +++ b/source/MaterialXGenShader/ShaderNode.h @@ -350,14 +350,11 @@ class MX_GENSHADER_API ShaderNode static const uint32_t VOLUME = 1 << 15; /// A volume shader node static const uint32_t LIGHT = 1 << 16; /// A light shader node static const uint32_t UNLIT = 1 << 17; /// An unlit surface shader node - // Specific conditional types - static const uint32_t IFELSE = 1 << 18; /// An if-else statement - static const uint32_t SWITCH = 1 << 19; /// A switch statement // Types based on nodegroup - static const uint32_t SAMPLE2D = 1 << 20; /// Can be sampled in 2D (uv space) - static const uint32_t SAMPLE3D = 1 << 21; /// Can be sampled in 3D (position) - static const uint32_t GEOMETRIC = 1 << 22; /// Geometric input - static const uint32_t DOT = 1 << 23; /// A dot node + static const uint32_t SAMPLE2D = 1 << 18; /// Can be sampled in 2D (uv space) + static const uint32_t SAMPLE3D = 1 << 19; /// Can be sampled in 3D (position) + static const uint32_t GEOMETRIC = 1 << 20; /// Geometric input + static const uint32_t DOT = 1 << 21; /// A dot node }; static const ShaderNodePtr NONE; @@ -365,8 +362,6 @@ class MX_GENSHADER_API ShaderNode static const string CONSTANT; static const string DOT; static const string IMAGE; - static const string COMPARE; - static const string SWITCH; static const string SURFACESHADER; static const string SCATTER_MODE; static const string BSDF_R; diff --git a/source/MaterialXGraphEditor/Graph.cpp b/source/MaterialXGraphEditor/Graph.cpp index 9e3fbccc40..2b28317ee2 100644 --- a/source/MaterialXGraphEditor/Graph.cpp +++ b/source/MaterialXGraphEditor/Graph.cpp @@ -17,13 +17,13 @@ namespace { -// the default node size is based off the of the size of the dot_color3 node using ed::getNodeSize() on that node +// Based on the dimensions of the dot_color3 node, computed by calling ed::getNodeSize const ImVec2 DEFAULT_NODE_SIZE = ImVec2(138, 116); const int DEFAULT_ALPHA = 255; const int FILTER_ALPHA = 50; -// Function based off ImRect_Expanded function from ImGui Node Editor blueprints-example.cpp +// Based on ImRect_Expanded function in ImGui Node Editor blueprints-example.cpp ImRect expandImRect(const ImRect& rect, float x, float y) { ImRect result = rect; @@ -34,10 +34,40 @@ ImRect expandImRect(const ImRect& rect, float x, float y) return result; } -// Get more user friendly node definition identifier. -// Will try and remove "ND_" prefix if it exists. Otherwise just returns -// the nodedef identifier. -std::string getNodeDefId(const std::string& val) +// Based on the splitter function in the ImGui Node Editor blueprints-example.cpp +static bool splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size = -1.0f) +{ + using namespace ImGui; + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + ImGuiID id = window->GetID("##Splitter"); + ImRect bb; + bb.Min = window->DC.CursorPos + (split_vertically ? ImVec2(*size1, 0.0f) : ImVec2(0.0f, *size1)); + bb.Max = bb.Min + CalcItemSize(split_vertically ? ImVec2(thickness, splitter_long_axis_size) : ImVec2(splitter_long_axis_size, thickness), 0.0f, 0.0f); + return SplitterBehavior(bb, id, split_vertically ? ImGuiAxis_X : ImGuiAxis_Y, size1, size2, min_size1, min_size2, 0.0f); +} + +// Based on showLabel from ImGui Node Editor blueprints-example.cpp +auto showLabel = [](const char* label, ImColor color) +{ + ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetTextLineHeight()); + auto size = ImGui::CalcTextSize(label); + + auto padding = ImGui::GetStyle().FramePadding; + auto spacing = ImGui::GetStyle().ItemSpacing; + + ImGui::SetCursorPos(ImGui::GetCursorPos() + ImVec2(spacing.x, -spacing.y)); + + auto rectMin = ImGui::GetCursorScreenPos() - padding; + auto rectMax = ImGui::GetCursorScreenPos() + size + padding; + + auto drawList = ImGui::GetWindowDrawList(); + drawList->AddRectFilled(rectMin, rectMax, color, size.y * 0.15f); + ImGui::TextUnformatted(label); +}; + +// Create a more user-friendly node definition name +std::string getUserNodeDefName(const std::string& val) { const std::string ND_PREFIX = "ND_"; std::string result = val; @@ -72,7 +102,8 @@ Graph::Graph(const std::string& materialFilename, _isCut(false), _autoLayout(false), _frameCount(INT_MIN), - _pinFilterType(mx::EMPTY_STRING) + _fontScale(1.0f), + _saveNodePositions(true) { loadStandardLibraries(); setPinColor(); @@ -84,7 +115,6 @@ Graph::Graph(const std::string& materialFilename, _geomFilter.push_back(".gltf"); _graphDoc = loadDocument(materialFilename); - _graphDoc->importLibrary(_stdLib); _initial = true; createNodeUIList(_stdLib); @@ -177,12 +207,16 @@ mx::DocumentPtr Graph::loadDocument(mx::FilePath filename) if (!filename.isEmpty()) { mx::readFromXmlFile(doc, filename, _searchPath, &readOptions); + doc->importLibrary(_stdLib); std::string message; if (!doc->validate(&message)) { std::cerr << "*** Validation warnings for " << filename.asString() << " ***" << std::endl; std::cerr << message << std::endl; } + + // Cache the currently loaded file + _materialFilename = filename; } } catch (mx::Exception& e) @@ -195,7 +229,6 @@ mx::DocumentPtr Graph::loadDocument(mx::FilePath filename) return doc; } -// populate nodes to add with input output group and nodegraph nodes which are not found in the stdlib void Graph::addExtraNodes() { if (!_graphDoc) @@ -203,10 +236,10 @@ void Graph::addExtraNodes() return; } - // clear any old nodes, if we previously used tab with another graph doc + // Clear any old nodes, if we previously used tab with another graph doc _extraNodes.clear(); - // get all types from the doc + // Get all types from the doc std::vector types; std::vector typeDefs = _graphDoc->getTypeDefs(); types.reserve(typeDefs.size()); @@ -215,7 +248,7 @@ void Graph::addExtraNodes() types.push_back(typeDef->getName()); } - // add input and output nodes for all types + // Add input and output nodes for all types for (const std::string& type : types) { std::string nodeName = "ND_input_" + type; @@ -224,16 +257,15 @@ void Graph::addExtraNodes() _extraNodes["Output Nodes"].push_back({ nodeName, type, "output" }); } - // add group node + // Add group node std::vector groupNode{ "ND_group", "", "group" }; _extraNodes["Group Nodes"].push_back(groupNode); - // add nodegraph node + // Add nodegraph node std::vector nodeGraph{ "ND_nodegraph", "", "nodegraph" }; _extraNodes["Node Graph"].push_back(nodeGraph); } -// return output pin needed to link the inputs and outputs ed::PinId Graph::getOutputPin(UiNodePtr node, UiNodePtr upNode, UiPinPtr input) { if (upNode->getNodeGraph() != nullptr) @@ -286,11 +318,11 @@ ed::PinId Graph::getOutputPin(UiNodePtr node, UiNodePtr upNode, UiPinPtr input) } } -// connect links via connected nodes in UiNodePtr void Graph::linkGraph() { _currLinks.clear(); - // start with bottom of graph + + // Start with bottom of graph for (UiNodePtr node : _graphNodes) { std::vector inputs = node->inputPins; @@ -298,20 +330,21 @@ void Graph::linkGraph() { for (size_t i = 0; i < inputs.size(); i++) { - // get upstream node for all inputs + // Get upstream node for all inputs std::string inputName = inputs[i]->_name; UiNodePtr inputNode = node->getConnectedNode(inputName); if (inputNode != nullptr) { Link link; - // getting the input connections for the current uiNode + + // Get the input connections for the current UiNode ax::NodeEditor::PinId id = inputs[i]->_pinId; inputs[i]->setConnected(true); int end = int(id.Get()); link._endAttr = end; - // get id number of output of node + // Get id number of output of node ed::PinId outputId = getOutputPin(node, inputNode, inputs[i]); int start = int(outputId.Get()); @@ -352,21 +385,16 @@ void Graph::linkGraph() } } -// connect all the links via the graph editor library void Graph::connectLinks() { - for (Link const& link : _currLinks) { - ed::Link(link.id, link._startAttr, link._endAttr); } } -// find link position in currLinks vector from link id int Graph::findLinkPosition(int id) { - int count = 0; for (size_t i = 0; i < _currLinks.size(); i++) { @@ -378,7 +406,7 @@ int Graph::findLinkPosition(int id) } return -1; } -// check if a node has already been assigned a position + bool Graph::checkPosition(UiNodePtr node) { if (node->getMxElement() != nullptr) @@ -390,7 +418,8 @@ bool Graph::checkPosition(UiNodePtr node) } return false; } -// calculate the total vertical space the node level takes up + +// Calculate the total vertical space the node level takes up float Graph::totalHeight(int level) { float total = 0.f; @@ -400,7 +429,8 @@ float Graph::totalHeight(int level) } return total; } -// set the y position of node based of the starting position and the nodes above it + +// Set the y-position of node based on the starting position and the nodes above it void Graph::setYSpacing(int level, float startingPos) { // set the y spacing for each node @@ -413,7 +443,7 @@ void Graph::setYSpacing(int level, float startingPos) } } -// calculate the average y position for a specific node level +// Calculate the average y-position for a specific node level float Graph::findAvgY(const std::vector& nodes) { // find the mid point of node level grou[ @@ -432,8 +462,8 @@ float Graph::findAvgY(const std::vector& nodes) void Graph::findYSpacing(float startY) { - // assume level 0 is set - // for each level find the average y position of the previous level to use as a spacing guide + // Assume level 0 is set + // For each level find the average y position of the previous level to use as a spacing guide int i = 0; for (std::pair> levelChunk : _levelMap) { @@ -441,7 +471,6 @@ void Graph::findYSpacing(float startY) { if (_levelMap[i][0]->_level > 0) { - int prevLevel = _levelMap[i].front()->_level - 1; float avgY = findAvgY(_levelMap[prevLevel]); float height = totalHeight(_levelMap[i].front()->_level); @@ -458,14 +487,13 @@ void Graph::findYSpacing(float startY) } } -// layout the x position by assigning the node levels based off its distance from the first node ImVec2 Graph::layoutPosition(UiNodePtr layoutNode, ImVec2 startingPos, bool initialLayout, int level) { if (checkPosition(layoutNode) && !_autoLayout) { for (UiNodePtr node : _graphNodes) { - // since nodegrpah nodes do not have any materialX info they are placed based off their conneced node + // Since nodegraph nodes do not have MaterialX info they are placed based on their connected node if (node->getNodeGraph() != nullptr) { std::vector outputCon = node->getOutputConnections(); @@ -478,7 +506,7 @@ ImVec2 Graph::layoutPosition(UiNodePtr layoutNode, ImVec2 startingPos, bool init } else { - // don't set position of group nodes + // Don't set position of group nodes if (node->getMessage().empty()) { if (node->getMxElement()->hasAttribute("xpos")) @@ -506,7 +534,7 @@ ImVec2 Graph::layoutPosition(UiNodePtr layoutNode, ImVec2 startingPos, bool init { if (layoutNode->_level < level) { - // remove the old instance of the node from the map + // Remove the old instance of the node from the map int levelNum = 0; int removeNum = -1; for (UiNodePtr levelNode : _levelMap[layoutNode->_level]) @@ -533,7 +561,7 @@ ImVec2 Graph::layoutPosition(UiNodePtr layoutNode, ImVec2 startingPos, bool init auto it = _levelMap.find(layoutNode->_level); if (it != _levelMap.end()) { - // key already exists add to it + // Key already exists so add to it bool nodeFound = false; for (UiNodePtr node : it->second) { @@ -550,30 +578,31 @@ ImVec2 Graph::layoutPosition(UiNodePtr layoutNode, ImVec2 startingPos, bool init } else { - // insert new vector into key + // Insert new vector into key std::vector newValue = { layoutNode }; _levelMap.insert({ layoutNode->_level, newValue }); } std::vector pins = layoutNode->inputPins; if (initialLayout) { - // check number of inputs that are connected to node + // Check number of inputs that are connected to node if (layoutNode->getInputConnect() > 0) { - // not top of node graph stop recursion + // Not top of node graph so stop recursion if (pins.size() != 0 && layoutNode->getInput() == nullptr) { for (size_t i = 0; i < pins.size(); i++) { - // get upstream node for all inputs + // Get upstream node for all inputs newPos = startingPos; UiNodePtr nextNode = layoutNode->getConnectedNode(pins[i]->_name); if (nextNode) { - startingPos.x = (1200.f - ((layoutNode->_level) * 350)) * _fontScale; + startingPos.x = (1200.f - ((layoutNode->_level) * 250)) * _fontScale; ed::SetNodePosition(layoutNode->getId(), startingPos); layoutNode->setPos(ImVec2(startingPos)); - // call layout position on upstream node with newPos as -140 to the left of current node + + // Call layout position on upstream node with newPos to the left of current node layoutPosition(nextNode, ImVec2(newPos.x, startingPos.y), initialLayout, layoutNode->_level + 1); } } @@ -581,9 +610,10 @@ ImVec2 Graph::layoutPosition(UiNodePtr layoutNode, ImVec2 startingPos, bool init } else { - startingPos.x = (1200.f - ((layoutNode->_level) * 350)) * _fontScale; + startingPos.x = (1200.f - ((layoutNode->_level) * 250)) * _fontScale; layoutNode->setPos(ImVec2(startingPos)); - // set current node position + + // Set current node position ed::SetNodePosition(layoutNode->getId(), ImVec2(startingPos)); } } @@ -591,10 +621,9 @@ ImVec2 Graph::layoutPosition(UiNodePtr layoutNode, ImVec2 startingPos, bool init } } -// extra layout pass for inputs and nodes that do not attach to an output node void Graph::layoutInputs() { - // layout inputs after other nodes so that they can be all in a line on far left side of node graph + // Layout inputs after other nodes so that they can be all in a line on far left side of node graph if (_levelMap.begin() != _levelMap.end()) { int levelCount = -1; @@ -613,7 +642,6 @@ void Graph::layoutInputs() startingPos.y += ed::GetNodeSize(uiNode->getId()).y; startingPos.y += 23; } - // accoutning for extra nodes like in gltf else if (uiNode->getOutputConnections().size() == 0 && (uiNode->getNode() != nullptr)) { if (uiNode->getNode()->getCategory() != mx::SURFACE_MATERIAL_NODE_STRING) @@ -625,7 +653,6 @@ void Graph::layoutInputs() } } -// reutrn pin color based on the type of the value of that pin void Graph::setPinColor() { _pinColor.insert(std::make_pair("integer", ImColor(255, 255, 28, 255))); @@ -644,8 +671,8 @@ void Graph::setPinColor() _pinColor.insert(std::make_pair("BSDF", ImColor(10, 181, 150, 255))); _pinColor.insert(std::make_pair("EDF", ImColor(255, 50, 100, 255))); _pinColor.insert(std::make_pair("VDF", ImColor(0, 100, 151, 255))); - _pinColor.insert(std::make_pair("surfaceshader", ImColor(150, 255, 255, 255))); - _pinColor.insert(std::make_pair("material", ImColor(255, 255, 255, 255))); + _pinColor.insert(std::make_pair(mx::SURFACE_SHADER_TYPE_STRING, ImColor(150, 255, 255, 255))); + _pinColor.insert(std::make_pair(mx::MATERIAL_TYPE_STRING, ImColor(255, 255, 255, 255))); _pinColor.insert(std::make_pair(mx::DISPLACEMENT_SHADER_TYPE_STRING, ImColor(155, 50, 100, 255))); _pinColor.insert(std::make_pair(mx::VOLUME_SHADER_TYPE_STRING, ImColor(155, 250, 100, 255))); _pinColor.insert(std::make_pair(mx::LIGHT_SHADER_TYPE_STRING, ImColor(100, 150, 100, 255))); @@ -662,28 +689,9 @@ void Graph::setPinColor() _pinColor.insert(std::make_pair("stringarray", ImColor(120, 180, 100))); } -// based off of showLabel from ImGui Node Editor blueprints-example.cpp -auto showLabel = [](const char* label, ImColor color) -{ - ImGui::SetCursorPosY(ImGui::GetCursorPosY() - ImGui::GetTextLineHeight()); - auto size = ImGui::CalcTextSize(label); - - auto padding = ImGui::GetStyle().FramePadding; - auto spacing = ImGui::GetStyle().ItemSpacing; - - ImGui::SetCursorPos(ImGui::GetCursorPos() + ImVec2(spacing.x, -spacing.y)); - - auto rectMin = ImGui::GetCursorScreenPos() - padding; - auto rectMax = ImGui::GetCursorScreenPos() + size + padding; - - auto drawList = ImGui::GetWindowDrawList(); - drawList->AddRectFilled(rectMin, rectMax, color, size.y * 0.15f); - ImGui::TextUnformatted(label); -}; - void Graph::selectMaterial(UiNodePtr uiNode) { - // find renderable element that correspond with material uiNode + // Find renderable element that corresponds with material UiNode std::vector elems = mx::findRenderableElements(_graphDoc); mx::TypedElementPtr typedElem = nullptr; for (mx::TypedElementPtr elem : elems) @@ -698,13 +706,17 @@ void Graph::selectMaterial(UiNodePtr uiNode) _renderer->setMaterial(typedElem); } -// set the node to display in render veiw based off the selected node or nodegraph void Graph::setRenderMaterial(UiNodePtr node) { - // set render node right away is node is a material - if (node->getNode() && node->getNode()->getType() == "material") + // For now only surface shaders and materials are considered renderable. + // This can be adjusted as desired to include being able to use outputs, + // and / a sub-graph in the nodegraph. + const mx::StringSet RENDERABLE_TYPES = { mx::MATERIAL_TYPE_STRING, mx::SURFACE_SHADER_TYPE_STRING }; + + // Set render node right away is node is renderable + if (node->getNode() && RENDERABLE_TYPES.count(node->getNode()->getType())) { - // only set new render node if different material has been selected + // Only set new render node if different material has been selected if (_currRenderNode != node) { _currRenderNode = node; @@ -712,55 +724,124 @@ void Graph::setRenderMaterial(UiNodePtr node) _renderer->setMaterialCompilation(true); } } + + // Traverse downstream looking for the first renderable element. else { - // continue downstream using output connections until a material node is found - std::vector outNodes = node->getOutputConnections(); - if (outNodes.size() > 0) + mx::NodePtr mtlxNode = node->getNode(); + mx::NodeGraphPtr mtlxNodeGraph = node->getNodeGraph(); + mx::OutputPtr mtlxOutput = node->getOutput(); + if (mtlxOutput) { - if (outNodes[0]->getNode()) + mx::ElementPtr parent = mtlxOutput->getParent(); + if (parent->isA()) + mtlxNodeGraph = parent->asA(); + else if (parent->isA()) + mtlxNode = parent->asA(); + } + mx::StringSet testPaths; + if (mtlxNode) + { + mx::ElementPtr parent = mtlxNode->getParent(); + if (parent->isA()) { - if (outNodes[0]->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING) + // There is no logic to support traversing from inside a functional graph + // to it's instance and hence downstream so skip this from consideration. + // The closest approach would be to "flatten" all definitions to compound graphs. + mx::NodeGraphPtr parentGraph = parent->asA(); + if (parentGraph->getNodeDef()) { - std::vector shaderOut = outNodes[0]->getOutputConnections(); - if (shaderOut.size() > 0) + return; + } + } + testPaths.insert(mtlxNode->getNamePath()); + } + else if (mtlxNodeGraph) + { + testPaths.insert(mtlxNodeGraph->getNamePath()); + } + + mx::NodePtr foundNode = nullptr; + while (!testPaths.empty() && !foundNode) + { + mx::StringSet nextPaths; + for (const std::string& testPath : testPaths) + { + mx::ElementPtr testElem = _graphDoc->getDescendant(testPath); + mx::NodePtr testNode = testElem->asA(); + std::vector downstreamPorts; + if (testNode) + { + downstreamPorts = testNode->getDownstreamPorts(); + } + else + { + mx::NodeGraphPtr testGraph = testElem->asA(); + if (testGraph) { - if (shaderOut[0]) + downstreamPorts = testGraph->getDownstreamPorts(); + } + } + + // Test all downstream ports. If the port's node is renderable + // then stop searching. + for (mx::PortElementPtr downstreamPort : downstreamPorts) + { + mx::ElementPtr parent = downstreamPort->getParent(); + if (parent) + { + mx::NodePtr downstreamNode = parent->asA(); + if (downstreamNode) { - if (shaderOut[0]->getNode()->getType() == "material") + mx::NodeDefPtr nodeDef = downstreamNode->getNodeDef(); + if (nodeDef) { - if (_currRenderNode != shaderOut[0]) + if (RENDERABLE_TYPES.count(nodeDef->getType())) { - _currRenderNode = shaderOut[0]; - _frameCount = ImGui::GetFrameCount(); - _renderer->setMaterialCompilation(true); + foundNode = downstreamNode; + break; } } } + if (!foundNode) + { + nextPaths.insert(parent->getNamePath()); + } } - else - { - _currRenderNode = nullptr; - } } - else if (outNodes[0]->getNode()->getType() == mx::MATERIAL_TYPE_STRING) + if (foundNode) + { + break; + } + } + + // Set up next set of nodes to search downstream + testPaths = nextPaths; + } + + // Update rendering. If found use that node, otherwise + // use the current fallback of using the first renderable node. + if (foundNode) + { + for (auto uiNode : _graphNodes) + { + if (uiNode->getNode() == foundNode) { - if (_currRenderNode != outNodes[0]) + if (_currRenderNode != uiNode) { - _currRenderNode = outNodes[0]; + _currRenderNode = uiNode; _frameCount = ImGui::GetFrameCount(); _renderer->setMaterialCompilation(true); } + break; } } - else - { - _currRenderNode = nullptr; - } } else { _currRenderNode = nullptr; + _frameCount = ImGui::GetFrameCount(); + _renderer->setMaterialCompilation(true); } } } @@ -768,38 +849,15 @@ void Graph::setRenderMaterial(UiNodePtr node) void Graph::updateMaterials(mx::InputPtr input, mx::ValuePtr value) { std::string renderablePath; - mx::TypedElementPtr renderableElem; - std::vector elems = mx::findRenderableElements(_graphDoc); - - size_t num = 0; - int num2 = 0; - for (mx::TypedElementPtr elem : elems) + if (_currRenderNode) { - renderableElem = elem; - mx::NodePtr node = elem->asA(); - if (node) + if (_currRenderNode->getNode()) { - if (_currRenderNode) - { - if (node->getName() == _currRenderNode->getName()) - { - renderablePath = renderableElem->getNamePath(); - break; - } - } - else - { - renderablePath = renderableElem->getNamePath(); - } + renderablePath = _currRenderNode->getNode()->getNamePath(); } - else + else if (_currRenderNode->getOutput()) { - renderablePath = renderableElem->getNamePath(); - if (num2 == 2) - { - break; - } - num2++; + renderablePath = _currRenderNode->getOutput()->getNamePath(); } } @@ -821,42 +879,37 @@ void Graph::updateMaterials(mx::InputPtr input, mx::ValuePtr value) else { std::string name = input->getNamePath(); - // need to use exact interface name in order for input - mx::InputPtr interfaceInput = findInput(input, input->getName()); - if (interfaceInput) - { - name = interfaceInput->getNamePath(); - } + // Note that if there is a topogical change due to // this value change or a transparency change, then // this is not currently caught here. - _renderer->getMaterials()[num]->modifyUniform(name, value); + _renderer->getMaterials()[0]->modifyUniform(name, value); } } } -// set the value of the selected node constants in the node property editor + void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIProperties& uiProperties) { - std::string inName = !uiProperties.uiName.empty() ? uiProperties.uiName : input->getName(); ImGui::PushItemWidth(-1); mx::ValuePtr minVal = uiProperties.uiMin; mx::ValuePtr maxVal = uiProperties.uiMax; - // if input is a float set the float slider Ui to the value + // If input is a float set the float slider UI to the value if (input->getType() == "float") { mx::ValuePtr val = input->getValue(); if (val && val->isA()) { - // updates the value to the default for new nodes + // Update the value to the default for new nodes float prev = val->asA(), temp = val->asA(); float min = minVal ? minVal->asA() : 0.f; float max = maxVal ? maxVal->asA() : 100.f; float speed = (max - min) / 1000.0f; ImGui::DragFloat("##hidelabel", &temp, speed, min, max); - // set input value and update materials if different from previous value + + // Set input value and update materials if different from previous value if (prev != temp) { addNodeInput(_currUiNode, input); @@ -875,7 +928,8 @@ void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIPropert int max = maxVal ? maxVal->asA() : 100; float speed = (max - min) / 100.0f; ImGui::DragInt("##hidelabel", &temp, speed, min, max); - // set input value and update materials if different from previous value + + // Set input value and update materials if different from previous value if (prev != temp) { addNodeInput(_currUiNode, input); @@ -899,7 +953,7 @@ void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIPropert ImGui::SameLine(); ImGui::ColorEdit3("##color", &temp[0], ImGuiColorEditFlags_NoInputs); - // set input value and update materials if different from previous value + // Set input value and update materials if different from previous value if (prev != temp) { addNodeInput(_currUiNode, input); @@ -921,9 +975,11 @@ void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIPropert ImGui::DragFloat4("##hidelabel", &temp[0], speed, min, max); ImGui::PopItemWidth(); ImGui::SameLine(); - // color edit for the color picker to the right of the color floats + + // Color edit for the color picker to the right of the color floats ImGui::ColorEdit4("##color", &temp[0], ImGuiColorEditFlags_NoInputs); - // set input value and update materials if different from previous value + + // Set input value and update materials if different from previous value if (temp != prev) { addNodeInput(_currUiNode, input); @@ -942,7 +998,8 @@ void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIPropert float max = maxVal ? maxVal->asA()[0] : 100.f; float speed = (max - min) / 1000.0f; ImGui::DragFloat2("##hidelabel", &temp[0], speed, min, max); - // set input value and update materials if different from previous value + + // Set input value and update materials if different from previous value if (prev != temp) { addNodeInput(_currUiNode, input); @@ -961,7 +1018,8 @@ void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIPropert float max = maxVal ? maxVal->asA()[0] : 100.f; float speed = (max - min) / 1000.0f; ImGui::DragFloat3("##hidelabel", &temp[0], speed, min, max); - // set input value and update materials if different from previous value + + // Set input value and update materials if different from previous value if (prev != temp) { addNodeInput(_currUiNode, input); @@ -980,7 +1038,8 @@ void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIPropert float max = maxVal ? maxVal->asA()[0] : 100.f; float speed = (max - min) / 1000.0f; ImGui::DragFloat4("##hidelabel", &temp[0], speed, min, max); - // set input value and update materials if different from previous value + + // Set input value and update materials if different from previous value if (prev != temp) { addNodeInput(_currUiNode, input); @@ -996,7 +1055,8 @@ void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIPropert { std::string prev = val->asA(), temp = val->asA(); ImGui::InputText("##constant", &temp); - // set input value and update materials if different from previous value + + // Set input value and update materials if different from previous value if (prev != temp) { addNodeInput(_currUiNode, input); @@ -1014,10 +1074,12 @@ void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIPropert std::string temp = val->asA(), prev = val->asA(); ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.15f, .15f, .15f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.2f, .4f, .6f, 1.0f)); - // browser button to select new file + + // Browser button to select new file ImGui::PushItemWidth(-100); if (ImGui::Button("Browse")) { + _fileDialogImageInputName = input->getName(); _fileDialogImage.setTitle("Node Input Dialog"); _fileDialogImage.open(); _fileDialogImage.setTypeFilters(_imageFilter); @@ -1028,19 +1090,21 @@ void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIPropert ImGui::PopStyleColor(); ImGui::PopStyleColor(); - // create and load document from selected file - if (_fileDialogImage.hasSelected()) + // Create and load document from selected file + if (_fileDialogImage.hasSelected() && _fileDialogImageInputName == input->getName()) { - // set the new filename to the complete file path + // Set the new filename to the complete file path mx::FilePath fileName = _fileDialogImage.getSelected(); temp = fileName; - // need to set the file prefix for the input to "" so that it can find the new file + + // Need to clear the file prefix so that it can find the new file input->setAttribute(input->FILE_PREFIX_ATTRIBUTE, ""); _fileDialogImage.clearSelected(); _fileDialogImage.setTypeFilters(std::vector()); + _fileDialogImageInputName = ""; } - // set input value and update materials if different from previous value + // Set input value and update materials if different from previous value if (prev != temp) { addNodeInput(_currUiNode, input); @@ -1057,7 +1121,8 @@ void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIPropert { bool prev = val->asA(), temp = val->asA(); ImGui::Checkbox("", &temp); - // set input value and update materials if different from previous value + + // Set input value and update materials if different from previous value if (prev != temp) { addNodeInput(_currUiNode, input); @@ -1069,13 +1134,14 @@ void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIPropert ImGui::PopItemWidth(); } -// build the initial graph of a loaded mtlx document including shader, material and nodegraph node + void Graph::setUiNodeInfo(UiNodePtr node, const std::string& type, const std::string& category) { node->setType(type); node->setCategory(category); ++_graphTotalSize; - // create pins + + // Create pins if (node->getNodeGraph()) { std::vector outputs = node->getNodeGraph()->getOutputs(); @@ -1155,14 +1221,13 @@ void Graph::setUiNodeInfo(UiNodePtr node, const std::string& type, const std::st _graphNodes.push_back(std::move(node)); } -// Generate node UI from nodedefs. void Graph::createNodeUIList(mx::DocumentPtr doc) { _nodesToAdd.clear(); const std::string EXTRA_GROUP_NAME = "extra"; for (mx::NodeDefPtr nodeDef : doc->getNodeDefs()) { - // nodeDef is the key for the map + // NodeDef is the key for the map std::string group = nodeDef->getNodeGroup(); if (group.empty()) { @@ -1178,7 +1243,6 @@ void Graph::createNodeUIList(mx::DocumentPtr doc) addExtraNodes(); } -// build the UiNode node graph based off of loading a document void Graph::buildUiBaseGraph(mx::DocumentPtr doc) { std::vector nodeGraphs = doc->getNodeGraphs(); @@ -1194,7 +1258,8 @@ void Graph::buildUiBaseGraph(mx::DocumentPtr doc) _newLinks.clear(); _currPins.clear(); _graphTotalSize = 1; - // creating uiNodes for nodes that belong to the document so they are not in a nodegraph + + // Create UiNodes for nodes that belong to the document so they are not in a nodegraph for (mx::NodePtr node : docNodes) { if (!includeElement(node)) @@ -1204,7 +1269,8 @@ void Graph::buildUiBaseGraph(mx::DocumentPtr doc) currNode->setNode(node); setUiNodeInfo(currNode, node->getType(), node->getCategory()); } - // creating uiNodes for the nodegraph + + // Create UiNodes for the nodegraph for (mx::NodeGraphPtr nodeGraph : nodeGraphs) { if (!includeElement(nodeGraph)) @@ -1230,7 +1296,8 @@ void Graph::buildUiBaseGraph(mx::DocumentPtr doc) currNode->setOutput(output); setUiNodeInfo(currNode, output->getType(), output->getCategory()); } - // creating edges for nodegraphs + + // Create edges for nodegraphs for (mx::NodeGraphPtr graph : nodeGraphs) { for (mx::InputPtr input : graph->getActiveInputs()) @@ -1256,7 +1323,8 @@ void Graph::buildUiBaseGraph(mx::DocumentPtr doc) } } } - // creating edges for surface and material nodes + + // Create edges for surface and material nodes for (mx::NodePtr node : docNodes) { mx::NodeDefPtr nD = node->getNodeDef(node->getName()); @@ -1291,7 +1359,6 @@ void Graph::buildUiBaseGraph(mx::DocumentPtr doc) } if (upNum != -1) { - UiEdge newEdge = UiEdge(_graphNodes[upNum], _graphNodes[downNum], input); if (!edgeExists(newEdge)) { @@ -1304,11 +1371,10 @@ void Graph::buildUiBaseGraph(mx::DocumentPtr doc) } } } -// build the UiNode node graph based off of diving into a node graph node + void Graph::buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs) { - - // clear all values so that ids can start with 0 or 1 + // Clear all values so that ids can start with 0 or 1 _graphNodes.clear(); _currLinks.clear(); _currEdge.clear(); @@ -1319,12 +1385,10 @@ void Graph::buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs) { mx::NodeGraphPtr nodeGraph = nodeGraphs; std::vector children = nodeGraph->topologicalSort(); - // Write out all nodes. - mx::NodeDefPtr nodeDef = nodeGraph->getNodeDef(); mx::NodeDefPtr currNodeDef; - // create input nodes + // Create input nodes if (nodeDef) { std::vector inputs = nodeDef->getActiveInputs(); @@ -1337,7 +1401,7 @@ void Graph::buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs) } } - // search node graph children to create uiNodes + // Search node graph children to create uiNodes for (mx::ElementPtr elem : children) { mx::NodePtr node = elem->asA(); @@ -1410,10 +1474,9 @@ void Graph::buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs) } int upNode = findNode(upName, upstreamType); int downNode = findNode(downName, downstreamType); - if (downNode > 0 && upNode > 0 && - _graphNodes[downNode]->getOutput() != nullptr) + if (downNode > 0 && upNode > 0 && _graphNodes[downNode]->getOutput()) { - // creating edges for the output nodes + // Create edges for the output nodes UiEdge newEdge = UiEdge(_graphNodes[upNode], _graphNodes[downNode], nullptr); if (!edgeExists(newEdge)) { @@ -1448,7 +1511,7 @@ void Graph::buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs) std::vector ins = upstreamNode->getActiveInputs(); for (mx::InputPtr input : ins) { - // connecting input nodes + // Connect input nodes if (input->hasInterfaceName()) { std::string interfaceName = input->getInterfaceName(); @@ -1474,7 +1537,7 @@ void Graph::buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs) } } - // second pass to catch all of the connections that arent part of an output + // Second pass to catch all of the connections that arent part of an output for (mx::ElementPtr elem : children) { mx::NodePtr node = elem->asA(); @@ -1543,7 +1606,6 @@ void Graph::buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs) } } -// return node position in _graphNodes based off node name and type to account for input/output UiNodes with same names as mx Nodes int Graph::findNode(const std::string& name, const std::string& type) { int count = 0; @@ -1573,13 +1635,12 @@ int Graph::findNode(const std::string& name, const std::string& type) return -1; } -// set position of pasted nodes based off of original node position void Graph::positionPasteBin(ImVec2 pos) { ImVec2 totalPos = ImVec2(0, 0); ImVec2 avgPos = ImVec2(0, 0); - // get average position of original nodes + // Get average position of original nodes for (auto pasteNode : _copiedNodes) { ImVec2 origPos = ed::GetNodePosition(pasteNode.first->getId()); @@ -1589,7 +1650,7 @@ void Graph::positionPasteBin(ImVec2 pos) avgPos.x = totalPos.x / (int) _copiedNodes.size(); avgPos.y = totalPos.y / (int) _copiedNodes.size(); - // get offset from clciked position + // Get offset from clicked position ImVec2 offset = ImVec2(0, 0); offset.x = pos.x - avgPos.x; offset.y = pos.y - avgPos.y; @@ -1605,11 +1666,12 @@ void Graph::positionPasteBin(ImVec2 pos) ed::SetNodePosition(pasteNode.second->getId(), newPos); } } + void Graph::createEdge(UiNodePtr upNode, UiNodePtr downNode, mx::InputPtr connectingInput) { if (downNode->getOutput()) { - // creating edges for the output nodes + // Create edges for the output nodes UiEdge newEdge = UiEdge(upNode, downNode, nullptr); if (!edgeExists(newEdge)) { @@ -1674,6 +1736,7 @@ void Graph::copyUiNode(UiNodePtr node) _copiedNodes[node] = copyNode; _graphNodes.push_back(copyNode); } + void Graph::copyNodeGraph(UiNodePtr origGraph, UiNodePtr copyGraph) { copyGraph->getNodeGraph()->copyContentFrom(origGraph->getNodeGraph()); @@ -1684,6 +1747,7 @@ void Graph::copyNodeGraph(UiNodePtr origGraph, UiNodePtr copyGraph) input->setName(newName); } } + void Graph::copyInputs() { for (std::map::iterator iter = _copiedNodes.begin(); iter != _copiedNodes.end(); ++iter) @@ -1695,19 +1759,18 @@ void Graph::copyInputs() { if (origNode->getConnectedNode(pin->_name) && !_ctrlClick) { - // if original node is connected check if connect node is in copied nodes + // If original node is connected check if connect node is in copied nodes if (_copiedNodes.find(origNode->getConnectedNode(pin->_name)) != _copiedNodes.end()) { - // set copy node connected to the value at this key - // create an edge + // Set copy node connected to the value at this key createEdge(_copiedNodes[origNode->getConnectedNode(pin->_name)], copyNode, copyNode->inputPins[count]->_input); UiNodePtr upNode = _copiedNodes[origNode->getConnectedNode(pin->_name)]; if (copyNode->getNode() || copyNode->getNodeGraph()) { - mx::InputPtr connectingInput = nullptr; copyNode->inputPins[count]->_input->copyContentFrom(pin->_input); - // update value to be empty + + // Update value to be empty if (copyNode->getNode() && copyNode->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING) { if (upNode->getOutput()) @@ -1721,7 +1784,6 @@ void Graph::copyInputs() } else { - // node graph if (upNode->getNodeGraph()) { ed::PinId outputId = getOutputPin(copyNode, upNode, copyNode->inputPins[count]); @@ -1761,7 +1823,7 @@ void Graph::copyInputs() copyNode->getOutput()->setConnectedNode(upNode->getNode()); } - // update input node num and output connections + // Update input node num and output connections copyNode->setInputNodeNum(1); upNode->setOutputConnection(copyNode); } @@ -1781,12 +1843,13 @@ void Graph::copyInputs() } } } -// add node to graphNodes based off of node def information + void Graph::addNode(const std::string& category, const std::string& name, const std::string& type) { mx::NodePtr node = nullptr; std::vector matchingNodeDefs; - // create document or node graph is there is not already one + + // Create document or node graph is there is not already one if (category == "output") { std::string outName = ""; @@ -1803,7 +1866,8 @@ void Graph::addNode(const std::string& category, const std::string& name, const { std::string inName = ""; mx::InputPtr newIn = nullptr; - // add input as child of correct parent and create valid name + + // Add input as child of correct parent and create valid name inName = _currGraphElem->createValidChildName(name); newIn = _currGraphElem->addInput(inName, type); auto inputNode = std::make_shared(inName, int(++_graphTotalSize)); @@ -1815,20 +1879,23 @@ void Graph::addNode(const std::string& category, const std::string& name, const else if (category == "group") { auto groupNode = std::make_shared(name, int(++_graphTotalSize)); - // set message of group uinode in order to identify it as such + + // Set message of group UiNode in order to identify it as such groupNode->setMessage("Comment"); setUiNodeInfo(groupNode, type, "group"); - // create ui portions of group node + + // Create ui portions of group node buildGroupNode(_graphNodes.back()); return; } else if (category == "nodegraph") { - // create new mx::NodeGraph and set as current node graph + // Create new mx::NodeGraph and set as current node graph _graphDoc->addNodeGraph(); std::string nodeGraphName = _graphDoc->getNodeGraphs().back()->getName(); auto nodeGraphNode = std::make_shared(nodeGraphName, int(++_graphTotalSize)); - // set mx::Nodegraph as node graph for uiNode + + // Set mx::Nodegraph as node graph for uiNode nodeGraphNode->setNodeGraph(_graphDoc->getNodeGraphs().back()); setUiNodeInfo(nodeGraphNode, type, "nodegraph"); @@ -1839,9 +1906,8 @@ void Graph::addNode(const std::string& category, const std::string& name, const matchingNodeDefs = _graphDoc->getMatchingNodeDefs(category); for (mx::NodeDefPtr nodedef : matchingNodeDefs) { - std::string nodedefName = nodedef->getName(); - std::string sub = getNodeDefId(nodedefName); - if (sub == name) + std::string userNodeDefName = getUserNodeDefName(nodedef->getName()); + if (userNodeDefName == name) { node = _currGraphElem->addNodeInstance(nodedef, _currGraphElem->createValidChildName(name)); } @@ -1854,17 +1920,16 @@ void Graph::addNode(const std::string& category, const std::string& name, const int countDef = 0; for (size_t i = 0; i < matchingNodeDefs.size(); i++) { - // use substring of name in order to remove ND_ - std::string nodedefName = matchingNodeDefs[i]->getName(); - std::string sub = getNodeDefId(nodedefName); - if (sub == name) + std::string userNodeDefName = getUserNodeDefName(matchingNodeDefs[i]->getName()); + if (userNodeDefName == name) { num = countDef; } countDef++; } std::vector defInputs = matchingNodeDefs[num]->getActiveInputs(); - // adding inputs to ui node as pins so that we can later add them to the node if necessary + + // Add inputs to UiNode as pins so that we can later add them to the node if necessary auto newNode = std::make_shared(node->getName(), int(++_graphTotalSize)); newNode->setCategory(category); newNode->setType(type); @@ -1892,7 +1957,7 @@ void Graph::addNode(const std::string& category, const std::string& name, const updateMaterials(); } } -// return node pos + int Graph::getNodeId(ed::PinId pinId) { for (UiPinPtr pin : _currPins) @@ -1905,7 +1970,6 @@ int Graph::getNodeId(ed::PinId pinId) return -1; } -// return pin based off of UiPin id UiPinPtr Graph::getPin(ed::PinId pinId) { for (UiPinPtr pin : _currPins) @@ -1919,8 +1983,7 @@ UiPinPtr Graph::getPin(ed::PinId pinId) return nullPin; } -// This function is based off of the pin icon function in the ImGui Node Editor blueprints-example.cpp -void Graph::DrawPinIcon(std::string type, bool connected, int alpha) +void Graph::drawPinIcon(std::string type, bool connected, int alpha) { ax::Drawing::IconType iconType = ax::Drawing::IconType::Flow; ImColor color = ImColor(0, 0, 0, 255); @@ -1934,7 +1997,6 @@ void Graph::DrawPinIcon(std::string type, bool connected, int alpha) ax::Widgets::Icon(ImVec2(24, 24), iconType, connected, color, ImColor(32, 32, 32, alpha)); } -// This function is based off of the comment node in the ImGui Node Editor blueprints-example.cpp void Graph::buildGroupNode(UiNodePtr node) { const float commentAlpha = 0.75f; @@ -1991,12 +2053,14 @@ void Graph::buildGroupNode(UiNodePtr node) } ed::EndGroupHint(); } + bool Graph::readOnly() { - // if the sources are not the same then the current graph cannot be modified + // If the sources are not the same then the current graph cannot be modified return _currGraphElem->getActiveSourceUri() != _graphDoc->getActiveSourceUri(); } -mx::InputPtr Graph::findInput(mx::InputPtr nodeInput, std::string name) + +mx::InputPtr Graph::findInput(mx::InputPtr nodeInput, const std::string& name) { if (_isNodeGraph) { @@ -2008,7 +2072,6 @@ mx::InputPtr Graph::findInput(mx::InputPtr nodeInput, std::string name) { if (input->getInterfaceInput()) { - if (input->getInterfaceInput() == nodeInput) { return input; @@ -2040,18 +2103,6 @@ mx::InputPtr Graph::findInput(mx::InputPtr nodeInput, std::string name) } return nullptr; } -// This function is based off the splitter function in the ImGui Node Editor blueprints-example.cpp -static bool Splitter(bool split_vertically, float thickness, float* size1, float* size2, float min_size1, float min_size2, float splitter_long_axis_size = -1.0f) -{ - using namespace ImGui; - ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - ImGuiID id = window->GetID("##Splitter"); - ImRect bb; - bb.Min = window->DC.CursorPos + (split_vertically ? ImVec2(*size1, 0.0f) : ImVec2(0.0f, *size1)); - bb.Max = bb.Min + CalcItemSize(split_vertically ? ImVec2(thickness, splitter_long_axis_size) : ImVec2(splitter_long_axis_size, thickness), 0.0f, 0.0f); - return SplitterBehavior(bb, id, split_vertically ? ImGuiAxis_X : ImGuiAxis_Y, size1, size2, min_size1, min_size2, 0.0f); -} void Graph::drawOutputPins(UiNodePtr node, const std::string& longestInputLabel) { @@ -2075,11 +2126,11 @@ void Graph::drawOutputPins(UiNodePtr node, const std::string& longestInputLabel) bool connected = pin->getConnected(); if (!_pinFilterType.empty()) { - DrawPinIcon(pin->_type, connected, _pinFilterType == pin->_type ? DEFAULT_ALPHA : FILTER_ALPHA); + drawPinIcon(pin->_type, connected, _pinFilterType == pin->_type ? DEFAULT_ALPHA : FILTER_ALPHA); } else { - DrawPinIcon(pin->_type, connected, DEFAULT_ALPHA); + drawPinIcon(pin->_type, connected, DEFAULT_ALPHA); } ed::EndPin(); @@ -2096,16 +2147,16 @@ void Graph::drawInputPin(UiPinPtr pin) { if (_pinFilterType == pin->_type) { - DrawPinIcon(pin->_type, connected, DEFAULT_ALPHA); + drawPinIcon(pin->_type, connected, DEFAULT_ALPHA); } else { - DrawPinIcon(pin->_type, connected, FILTER_ALPHA); + drawPinIcon(pin->_type, connected, FILTER_ALPHA); } } else { - DrawPinIcon(pin->_type, connected, DEFAULT_ALPHA); + drawPinIcon(pin->_type, connected, DEFAULT_ALPHA); } ImGui::PopID(); ed::EndPin(); @@ -2126,7 +2177,7 @@ std::vector Graph::createNodes(bool nodegraph) } else { - // color for output pin + // Color for output pin std::string outputType; if (node->getNode() != nullptr) { @@ -2180,8 +2231,8 @@ std::vector Graph::createNodes(bool nodegraph) } } drawOutputPins(node, longestInputLabel); - // set color of output pin + // Set color of output pin if (node->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING) { if (node->getOutputConnections().size() > 0) @@ -2241,16 +2292,16 @@ std::vector Graph::createNodes(bool nodegraph) { if (_pinFilterType == pin->_type) { - DrawPinIcon(pin->_type, true, DEFAULT_ALPHA); + drawPinIcon(pin->_type, true, DEFAULT_ALPHA); } else { - DrawPinIcon(pin->_type, true, FILTER_ALPHA); + drawPinIcon(pin->_type, true, FILTER_ALPHA); } } else { - DrawPinIcon(pin->_type, true, DEFAULT_ALPHA); + drawPinIcon(pin->_type, true, DEFAULT_ALPHA); } ImGui::SameLine(); @@ -2311,16 +2362,16 @@ std::vector Graph::createNodes(bool nodegraph) { if (_pinFilterType == pin->_type) { - DrawPinIcon(pin->_type, true, DEFAULT_ALPHA); + drawPinIcon(pin->_type, true, DEFAULT_ALPHA); } else { - DrawPinIcon(pin->_type, true, FILTER_ALPHA); + drawPinIcon(pin->_type, true, FILTER_ALPHA); } } else { - DrawPinIcon(pin->_type, true, DEFAULT_ALPHA); + drawPinIcon(pin->_type, true, DEFAULT_ALPHA); } ImGui::SameLine(); ImGui::TextUnformatted("input"); @@ -2377,7 +2428,6 @@ std::vector Graph::createNodes(bool nodegraph) return outputNum; } -// add mx::InputPtr to node based off of input pin void Graph::addNodeInput(UiNodePtr node, mx::InputPtr& input) { if (node->getNode()) @@ -2433,165 +2483,211 @@ void Graph::setDefaults(mx::InputPtr input) } } -// add link to nodegraph and set up connections between UiNodes and MaterialX Nodes to update shader -void Graph::AddLink(ed::PinId inputPinId, ed::PinId outputPinId) +void Graph::addLink(ed::PinId inputPinId, ed::PinId outputPinId) { int end_attr = int(outputPinId.Get()); int start_attr = int(inputPinId.Get()); UiPinPtr inputPin = getPin(outputPinId); UiPinPtr outputPin = getPin(inputPinId); - if (inputPinId && outputPinId && (outputPin->_type == inputPin->_type)) + + if (!inputPin || !outputPin) { - if (inputPin->_connected == false) + ed::RejectNewItem(); + return; + } + + // Perform type check + bool typesMatch = (outputPin->_type == inputPin->_type); + if (!typesMatch) + { + ed::RejectNewItem(); + showLabel("Invalid connection due to mismatched types", ImColor(50, 50, 50, 255)); + return; + } + + if (inputPin->_connected == false) + { + int upNode = getNodeId(inputPinId); + int downNode = getNodeId(outputPinId); + UiNodePtr uiDownNode = _graphNodes[downNode]; + UiNodePtr uiUpNode = _graphNodes[upNode]; + if (!uiDownNode || !uiUpNode) { - int upNode = getNodeId(inputPinId); - int downNode = getNodeId(outputPinId); + ed::RejectNewItem(); + return; + } - // make sure there is an implementation for node - const mx::ShaderGenerator& shadergen = _renderer->getGenContext().getShaderGenerator(); + // make sure there is an implementation for node + const mx::ShaderGenerator& shadergen = _renderer->getGenContext().getShaderGenerator(); + + // Prevent direct connecting from input to output + if (uiDownNode->getInput() && uiUpNode->getOutput()) + { + ed::RejectNewItem(); + showLabel("Direct connections between inputs and outputs is invalid", ImColor(50, 50, 50, 255)); + return; + } - // Find the implementation for this nodedef if not an input or output uinode - if (_graphNodes[downNode]->getInput() && _isNodeGraph) + // Find the implementation for this nodedef if not an input or output uinode + if (uiDownNode->getInput() && _isNodeGraph) + { + ed::RejectNewItem(); + showLabel("Cannot connect to inputs inside of graph", ImColor(50, 50, 50, 255)); + return; + } + else if (uiUpNode->getNode()) + { + mx::ShaderNodeImplPtr impl = shadergen.getImplementation(*_graphNodes[upNode]->getNode()->getNodeDef(), _renderer->getGenContext()); + if (!impl) { ed::RejectNewItem(); - showLabel("Cannot connect to inputs inside of graph", ImColor(50, 50, 50, 255)); + showLabel("Invalid Connection: Node does not have an implementation", ImColor(50, 50, 50, 255)); return; } - else if (_graphNodes[upNode]->getNode()) - { - mx::ShaderNodeImplPtr impl = shadergen.getImplementation(*_graphNodes[upNode]->getNode()->getNodeDef(), _renderer->getGenContext()); - if (!impl) - { - ed::RejectNewItem(); - showLabel("Invalid Connection: Node does not have an implementation", ImColor(50, 50, 50, 255)); - return; - } - } + } - if (ed::AcceptNewItem()) - { - // Since we accepted new link, lets add one to our list of links. - Link link; - link._startAttr = start_attr; - link._endAttr = end_attr; - _currLinks.push_back(link); - _frameCount = ImGui::GetFrameCount(); - _renderer->setMaterialCompilation(true); + if (ed::AcceptNewItem()) + { + // Since we accepted new link, lets add one to our list of links. + Link link; + link._startAttr = start_attr; + link._endAttr = end_attr; + _currLinks.push_back(link); + _frameCount = ImGui::GetFrameCount(); + _renderer->setMaterialCompilation(true); - if (_graphNodes[downNode]->getNode() || _graphNodes[downNode]->getNodeGraph()) + if (uiDownNode->getNode() || uiDownNode->getNodeGraph()) + { + mx::InputPtr connectingInput = nullptr; + for (UiPinPtr pin : uiDownNode->inputPins) { - mx::InputPtr connectingInput = nullptr; - for (UiPinPtr pin : _graphNodes[downNode]->inputPins) + if (pin->_pinId == outputPinId) { - if (pin->_pinId == outputPinId) + addNodeInput(uiDownNode, pin->_input); + // update value to be empty + if (uiDownNode->getNode() && uiDownNode->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING) { - addNodeInput(_graphNodes[downNode], pin->_input); - // update value to be empty - if (_graphNodes[downNode]->getNode() && _graphNodes[downNode]->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING) + if (uiUpNode->getOutput() != nullptr) { - if (_graphNodes[upNode]->getOutput() != nullptr) - { - pin->_input->setConnectedOutput(_graphNodes[upNode]->getOutput()); - } - else if (_graphNodes[upNode]->getInput() != nullptr) - { - pin->_input->setInterfaceName(_graphNodes[upNode]->getName()); - } - else + pin->_input->setConnectedOutput(uiUpNode->getOutput()); + } + else if (uiUpNode->getInput() != nullptr) + { + pin->_input->setInterfaceName(uiUpNode->getName()); + } + else + { + // node graph + if (uiUpNode->getNodeGraph() != nullptr) { - // node graph - if (_graphNodes[upNode]->getNodeGraph() != nullptr) + for (UiPinPtr outPin : uiUpNode->outputPins) { - for (UiPinPtr outPin : _graphNodes[upNode]->outputPins) + // set pin connection to correct output + if (outPin->_pinId == inputPinId) { - // set pin connection to correct output - if (outPin->_pinId == inputPinId) - { - mx::OutputPtr outputs = _graphNodes[upNode]->getNodeGraph()->getOutput(outPin->_name); - pin->_input->setConnectedOutput(outputs); - } + mx::OutputPtr outputs = uiUpNode->getNodeGraph()->getOutput(outPin->_name); + pin->_input->setConnectedOutput(outputs); } } - else - { - pin->_input->setConnectedNode(_graphNodes[upNode]->getNode()); - } } + else + { + pin->_input->setConnectedNode(uiUpNode->getNode()); + } + } + } + else + { + if (uiUpNode->getInput()) + { + pin->_input->setInterfaceName(uiUpNode->getName()); } else { - if (_graphNodes[upNode]->getInput()) + if (uiUpNode->getNode()) { - - pin->_input->setInterfaceName(_graphNodes[upNode]->getName()); - } - else - { - if (_graphNodes[upNode]->getNode()) + mx::NodePtr upstreamNode = _graphNodes[upNode]->getNode(); + mx::NodeDefPtr upstreamNodeDef = upstreamNode->getNodeDef(); + bool isMultiOutput = upstreamNodeDef ? upstreamNodeDef->getOutputs().size() > 1 : false; + + // This is purely to avoid adding a reference to an update node only 1 output, + // as currently validation consides adding this an error. Otherwise + // it will add an "output" attribute all the time. + if (!isMultiOutput) { - pin->_input->setConnectedNode(_graphNodes[upNode]->getNode()); + pin->_input->setConnectedNode(uiUpNode->getNode()); } - else if (_graphNodes[upNode]->getNodeGraph()) + else { for (UiPinPtr outPin : _graphNodes[upNode]->outputPins) { // set pin connection to correct output if (outPin->_pinId == inputPinId) { - mx::OutputPtr outputs = _graphNodes[upNode]->getNodeGraph()->getOutput(outPin->_name); + mx::OutputPtr outputs = uiUpNode->getNode()->getOutput(outPin->_name); + if (!outputs) + { + outputs = uiUpNode->getNode()->addOutput(outPin->_name, pin->_input->getType()); + } pin->_input->setConnectedOutput(outputs); } } } } + else if (uiUpNode->getNodeGraph()) + { + for (UiPinPtr outPin : uiUpNode->outputPins) + { + // set pin connection to correct output + if (outPin->_pinId == inputPinId) + { + mx::OutputPtr outputs = uiUpNode->getNodeGraph()->getOutput(outPin->_name); + pin->_input->setConnectedOutput(outputs); + } + } + } } - - pin->setConnected(true); - pin->_input->removeAttribute(mx::ValueElement::VALUE_ATTRIBUTE); - connectingInput = pin->_input; - break; } + + pin->setConnected(true); + pin->_input->removeAttribute(mx::ValueElement::VALUE_ATTRIBUTE); + connectingInput = pin->_input; + break; } - // create new edge and set edge information - createEdge(_graphNodes[upNode], _graphNodes[downNode], connectingInput); } - else if (_graphNodes[downNode]->getOutput() != nullptr) - { - mx::InputPtr connectingInput = nullptr; - _graphNodes[downNode]->getOutput()->setConnectedNode(_graphNodes[upNode]->getNode()); + // create new edge and set edge information + createEdge(_graphNodes[upNode], _graphNodes[downNode], connectingInput); + } + else if (_graphNodes[downNode]->getOutput() != nullptr) + { + mx::InputPtr connectingInput = nullptr; + _graphNodes[downNode]->getOutput()->setConnectedNode(_graphNodes[upNode]->getNode()); - // create new edge and set edge information - createEdge(_graphNodes[upNode], _graphNodes[downNode], connectingInput); - } - else + // create new edge and set edge information + createEdge(_graphNodes[upNode], _graphNodes[downNode], connectingInput); + } + else + { + // create new edge and set edge info + UiEdge newEdge = UiEdge(_graphNodes[upNode], _graphNodes[downNode], nullptr); + if (!edgeExists(newEdge)) { - // create new edge and set edge info - UiEdge newEdge = UiEdge(_graphNodes[upNode], _graphNodes[downNode], nullptr); - if (!edgeExists(newEdge)) - { - _graphNodes[downNode]->edges.push_back(newEdge); - _currEdge.push_back(newEdge); + _graphNodes[downNode]->edges.push_back(newEdge); + _currEdge.push_back(newEdge); - // update input node num and output connections - _graphNodes[downNode]->setInputNodeNum(1); - _graphNodes[upNode]->setOutputConnection(_graphNodes[downNode]); - } + // update input node num and output connections + _graphNodes[downNode]->setInputNodeNum(1); + _graphNodes[upNode]->setOutputConnection(_graphNodes[downNode]); } } } - else - { - ed::RejectNewItem(); - } } else { ed::RejectNewItem(); - showLabel("Invalid Connection due to Mismatch Types", ImColor(50, 50, 50, 255)); } } -// remove node edge based of off connecting input void Graph::removeEdge(int downNode, int upNode, UiPinPtr pin) { int num = _graphNodes[downNode]->getEdgeIndex(_graphNodes[upNode]->getId(), pin); @@ -2607,9 +2703,7 @@ void Graph::removeEdge(int downNode, int upNode, UiPinPtr pin) } } - // downNode set node num -1 _graphNodes[downNode]->setInputNodeNum(-1); - // upNode remove outputconnection _graphNodes[upNode]->removeOutputConnection(_graphNodes[downNode]->getName()); } @@ -2617,8 +2711,8 @@ void Graph::deleteLinkInfo(int startAttr, int endAttr) { int upNode = getNodeId(startAttr); int downNode = getNodeId(endAttr); - // change input so that is default val - // change informtion of actual mx::Node + + // Change input to default value if (_graphNodes[downNode]->getNode()) { mx::NodeDefPtr nodeDef = _graphNodes[downNode]->getNode()->getNodeDef(_graphNodes[downNode]->getNode()->getName()); @@ -2639,14 +2733,18 @@ void Graph::deleteLinkInfo(int startAttr, int endAttr) } if (_graphNodes[upNode]->getInput()) { - // remove interface value in order to set the default of the input + // Remove interface value in order to set the default of the input pin->_input->removeAttribute(mx::ValueElement::INTERFACE_NAME_ATTRIBUTE); setDefaults(pin->_input); setDefaults(_graphNodes[upNode]->getInput()); } + // Remove any output reference + pin->_input->removeAttribute(mx::PortElement::OUTPUT_ATTRIBUTE); + pin->setConnected(false); - // if a value exists update the input with it + + // If a value exists update the input with it if (val) { pin->_input->setValueString(val->getValueString()); @@ -2656,7 +2754,7 @@ void Graph::deleteLinkInfo(int startAttr, int endAttr) } else if (_graphNodes[downNode]->getNodeGraph()) { - // set default values for nodegraph node pins ie nodegraph inputs + // Set default values for nodegraph node pins ie nodegraph inputs mx::NodeDefPtr nodeDef = _graphNodes[downNode]->getNodeGraph()->getNodeDef(); for (UiPinPtr pin : _graphNodes[downNode]->inputPins) { @@ -2687,7 +2785,7 @@ void Graph::deleteLinkInfo(int startAttr, int endAttr) } } } -// delete link from currLink vector and remove any connections in UiNode or MaterialX Nodes to update shader + void Graph::deleteLink(ed::LinkId deletedLinkId) { // If you agree that link can be deleted, accept deletion. @@ -2696,10 +2794,11 @@ void Graph::deleteLink(ed::LinkId deletedLinkId) _renderer->setMaterialCompilation(true); _frameCount = ImGui::GetFrameCount(); int link_id = int(deletedLinkId.Get()); + // Then remove link from your data. int pos = findLinkPosition(link_id); - // link start -1 equals node num + // Link start -1 equals node num Link currLink = _currLinks[pos]; deleteLinkInfo(currLink._startAttr, currLink._endAttr); _currLinks.erase(_currLinks.begin() + pos); @@ -2708,7 +2807,7 @@ void Graph::deleteLink(ed::LinkId deletedLinkId) void Graph::deleteNode(UiNodePtr node) { - // delete link + // Delete link for (UiPinPtr inputPin : node->inputPins) { UiNodePtr upNode = node->getConnectedNode(inputPin->_name); @@ -2716,7 +2815,8 @@ void Graph::deleteNode(UiNodePtr node) { upNode->removeOutputConnection(node->getName()); int num = node->getEdgeIndex(upNode->getId(), inputPin); - // erase edge between node and up node + + // Erase edge between node and up node if (num != -1) { if (node->edges.size() == 1) @@ -2733,7 +2833,7 @@ void Graph::deleteNode(UiNodePtr node) if (node->outputPins.size() > 0) { - // update downNode info + // Update downNode info for (UiPinPtr pin : node->outputPins.front()->getConnections()) { mx::ValuePtr val; @@ -2741,7 +2841,7 @@ void Graph::deleteNode(UiNodePtr node) { mx::NodeDefPtr nodeDef = pin->_pinNode->getNode()->getNodeDef(pin->_pinNode->getNode()->getName()); val = nodeDef->getActiveInput(pin->_input->getName())->getValue(); - if (pin->_pinNode->getNode()->getType() == "surfaceshader") + if (pin->_pinNode->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING) { pin->_input->setConnectedOutput(nullptr); } @@ -2782,19 +2882,19 @@ void Graph::deleteNode(UiNodePtr node) } pin->_pinNode->setInputNodeNum(-1); - // not really necessary since it will be deleted + + // Not really necessary since it will be deleted node->removeOutputConnection(pin->_pinNode->getName()); } } - // remove from NodeGraph - // all link information is handled in delete link which is called before this + // Remove from NodeGraph + // All link information is handled in delete link which is called before this int nodeNum = findNode(node->getId()); _currGraphElem->removeChild(node->getName()); _graphNodes.erase(_graphNodes.begin() + nodeNum); } -// create pins for outputs/inputs added while inside the node graph void Graph::addNodeGraphPins() { for (UiNodePtr node : _graphNodes) @@ -2893,18 +2993,34 @@ void Graph::clearGraph() _renderer->updateMaterials(nullptr); } -void Graph::loadGraphFromFile() +void Graph::loadGraphFromFile(bool prompt) { - // deselect node before loading new file - if (_currUiNode != nullptr) + // Deselect node before loading new file + if (_currUiNode) { ed::DeselectNode(_currUiNode->getId()); _currUiNode = nullptr; } - _fileDialog.setTitle("Open File"); - _fileDialog.setTypeFilters(_mtlxFilter); - _fileDialog.open(); + if (prompt || _materialFilename.isEmpty()) + { + _fileDialog.setTitle("Open File"); + _fileDialog.setTypeFilters(_mtlxFilter); + _fileDialog.open(); + } + else + { + _graphDoc = loadDocument(_materialFilename); + + // Rebuild the UI + _initial = true; + buildUiBaseGraph(_graphDoc); + _currGraphElem = _graphDoc; + _prevUiNode = nullptr; + + _renderer->setDocument(_graphDoc); + _renderer->updateMaterials(nullptr); + } } void Graph::saveGraphToFile() @@ -2930,15 +3046,18 @@ void Graph::graphButtons() { if (ImGui::BeginMenu("File")) { - // buttons for loading and saving a .mtlx - // new Material button + // Buttons for loading and saving a .mtlx if (ImGui::MenuItem("New", "Ctrl-N")) { clearGraph(); } else if (ImGui::MenuItem("Open", "Ctrl-O")) { - loadGraphFromFile(); + loadGraphFromFile(true); + } + else if (ImGui::MenuItem("Reload", "Ctrl-R")) + { + loadGraphFromFile(false); } else if (ImGui::MenuItem("Save", "Ctrl-S")) { @@ -2965,6 +3084,12 @@ void Graph::graphButtons() ImGui::EndMenu(); } + if (ImGui::BeginMenu("Options")) + { + ImGui::Checkbox("Save Node Positions", &_saveNodePositions); + ImGui::EndMenu(); + } + if (ImGui::Button("Help")) { ImGui::OpenPopup("Help"); @@ -2984,23 +3109,28 @@ void Graph::graphButtons() { if (ImGui::IsKeyReleased(ImGuiKey_O)) { - loadGraphFromFile(); + loadGraphFromFile(true); } else if (ImGui::IsKeyReleased(ImGuiKey_N)) { clearGraph(); } + else if (ImGui::IsKeyReleased(ImGuiKey_R)) + { + loadGraphFromFile(false); + } else if (ImGui::IsKeyReleased(ImGuiKey_S)) { saveGraphToFile(); } } - // split window into panes for NodeEditor + // Split window into panes for NodeEditor static float leftPaneWidth = 375.0f; static float rightPaneWidth = 750.0f; - Splitter(true, 4.0f, &leftPaneWidth, &rightPaneWidth, 20.0f, 20.0f); - // create back button and graph hiearchy name display + splitter(true, 4.0f, &leftPaneWidth, &rightPaneWidth, 20.0f, 20.0f); + + // Create back button and graph hierarchy name display ImGui::Indent(leftPaneWidth + 15.f); if (ImGui::Button("<")) { @@ -3024,20 +3154,21 @@ void Graph::graphButtons() ImGui::Unindent(leftPaneWidth + 15.f); ImGui::PopStyleColor(); ImGui::NewLine(); - // creating two windows using splitter + + // Create two windows using splitter float paneWidth = (leftPaneWidth - 2.0f); ImGui::BeginChild("Selection", ImVec2(paneWidth, 0)); ImVec2 windowPos = ImGui::GetWindowPos(); - // renderView window + + // RenderView window ImVec2 wsize = ImVec2((float) _renderer->getViewWidth(), (float) _renderer->getViewHeight()); float aspectRatio = _renderer->getPixelRatio(); ImVec2 screenSize = ImVec2(paneWidth, paneWidth / aspectRatio); _renderer->setViewWidth((int) screenSize[0]); _renderer->setViewHeight((int) screenSize[1]); - if (_renderer != nullptr) + if (_renderer) { - glEnable(GL_FRAMEBUFFER_SRGB); _renderer->getViewCamera()->setViewportSize(mx::Vector2(screenSize[0], screenSize[1])); GLuint64 my_image_texture = _renderer->_textureID; @@ -3047,19 +3178,20 @@ void Graph::graphButtons() } ImGui::Separator(); - // property editor for current nodes + // Property editor for current nodes propertyEditor(); ImGui::EndChild(); ImGui::SameLine(0.0f, 12.0f); handleRenderViewInputs(windowPos, screenSize[0], screenSize[1]); } + void Graph::propertyEditor() { ImGui::Text("Node Property Editor"); if (_currUiNode) { - // set and edit name + // Set and edit name ImGui::Text("Name: "); ImGui::SameLine(); std::string original = _currUiNode->getName(); @@ -3095,9 +3227,7 @@ void Graph::propertyEditor() { if (temp != original) { - std::string name = _currUiNode->getInput()->getParent()->createValidChildName(temp); - std::vector downstreamNodes = _currUiNode->getOutputConnections(); for (UiNodePtr nodes : downstreamNodes) { @@ -3157,7 +3287,8 @@ void Graph::propertyEditor() ImGui::Text("Category:"); ImGui::SameLine(); - // change button color to match background + + // Change button color to match background ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.096f, .096f, .096f, 1.0f)); ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.1f, .1f, .1f, 1.0f)); if (_currUiNode->getNode()) @@ -3202,7 +3333,8 @@ void Graph::propertyEditor() mx::getUIProperties(input->_input, mx::EMPTY_STRING, uiProperties); std::string inputLabel = !uiProperties.uiName.empty() ? uiProperties.uiName : input->_input->getName(); mx::OutputPtr out = input->_input->getConnectedOutput(); - // setting comment help box + + // Set comment help box ImGui::PushID(int(input->_pinId.Get())); ImGui::Text("%s", inputLabel.c_str()); mx::InputPtr tempInt = _currUiNode->getNode()->getNodeDef()->getActiveInput(input->_input->getName()); @@ -3218,7 +3350,7 @@ void Graph::propertyEditor() } docString += "\t \n"; - // setting constant sliders for input values + // Set constant sliders for input values ImGui::TableNextColumn(); if (!input->getConnected()) { @@ -3265,13 +3397,13 @@ void Graph::propertyEditor() mx::getUIProperties(mxinput, mx::EMPTY_STRING, uiProperties); std::string inputLabel = !uiProperties.uiName.empty() ? uiProperties.uiName : mxinput->getName(); - // setting comment help box + // Set comment help box ImGui::PushID(int(inputs[i]->_pinId.Get())); ImGui::Text("%s", inputLabel.c_str()); ImGui::TableNextColumn(); - // setting constant sliders for input values + // Set constant sliders for input values if (!inputs[i]->getConnected()) { setConstant(_currUiNode, inputs[i]->_input, uiProperties); @@ -3324,7 +3456,7 @@ void Graph::propertyEditor() mx::getUIProperties(mxinput, mx::EMPTY_STRING, uiProperties); std::string inputLabel = !uiProperties.uiName.empty() ? uiProperties.uiName : mxinput->getName(); - // setting comment help box + // Set comment help box ImGui::PushID(int(input->_pinId.Get())); ImGui::Text("%s", inputLabel.c_str()); @@ -3368,7 +3500,6 @@ void Graph::propertyEditor() } } -// Helper to display basic user controls. void Graph::showHelp() const { ImGui::Text("MATERIALX GRAPH EDITOR HELP"); @@ -3431,11 +3562,13 @@ void Graph::addNodePopup(bool cursor) } ImGui::InputText("##input", input, sizeof(input)); std::string subs(input); - // input string length - // filter extra nodes - includes inputs, outputs, groups, and node graphs + + // Input string length + // Filter extra nodes - includes inputs, outputs, groups, and node graphs + const std::string NODEGRAPH_ENTRY = "Node Graph"; for (std::unordered_map>>::iterator it = _extraNodes.begin(); it != _extraNodes.end(); ++it) { - // filter out list of nodes + // Filter out list of nodes if (subs.size() > 0) { ImGui::SetNextWindowSizeConstraints(ImVec2(250.0f, 300.0f), ImVec2(-1.0f, 500.0f)); @@ -3443,14 +3576,21 @@ void Graph::addNodePopup(bool cursor) { std::string str(it->second[i][0]); std::string nodeName = it->second[i][0]; - // allow spaces to be used to search for node names + + // Disallow creating nested nodegraphs + if (_isNodeGraph && it->first == NODEGRAPH_ENTRY) + { + continue; + } + + // Allow spaces to be used to search for node names std::replace(subs.begin(), subs.end(), ' ', '_'); if (str.find(subs) != std::string::npos) { - if (ImGui::MenuItem(getNodeDefId(nodeName).c_str()) || (ImGui::IsItemFocused() && ImGui::IsKeyPressedMap(ImGuiKey_Enter))) + if (ImGui::MenuItem(getUserNodeDefName(nodeName).c_str()) || (ImGui::IsItemFocused() && ImGui::IsKeyPressedMap(ImGuiKey_Enter))) { - addNode(it->second[i][2], getNodeDefId(nodeName), it->second[i][1]); + addNode(it->second[i][2], getUserNodeDefName(nodeName), it->second[i][1]); _addNewNode = true; memset(input, '\0', sizeof(input)); } @@ -3466,9 +3606,9 @@ void Graph::addNodePopup(bool cursor) for (size_t j = 0; j < it->second.size(); j++) { std::string name = it->second[j][0]; - if (ImGui::MenuItem(getNodeDefId(name).c_str()) || (ImGui::IsItemFocused() && ImGui::IsKeyPressedMap(ImGuiKey_Enter))) + if (ImGui::MenuItem(getUserNodeDefName(name).c_str()) || (ImGui::IsItemFocused() && ImGui::IsKeyPressedMap(ImGuiKey_Enter))) { - addNode(it->second[j][2], getNodeDefId(name), it->second[j][1]); + addNode(it->second[j][2], getUserNodeDefName(name), it->second[j][1]); _addNewNode = true; } } @@ -3476,10 +3616,11 @@ void Graph::addNodePopup(bool cursor) } } } - // filter nodedefs and add to menu if matches filter + + // Filter nodedefs and add to menu if matches filter for (std::unordered_map>::iterator it = _nodesToAdd.begin(); it != _nodesToAdd.end(); ++it) { - // filter out list of nodes + // Filter out list of nodes if (subs.size() > 0) { ImGui::SetNextWindowSizeConstraints(ImVec2(250.0f, 300.0f), ImVec2(-1.0f, 500.0f)); @@ -3489,7 +3630,7 @@ void Graph::addNodePopup(bool cursor) std::string nodeName = it->second[i]->getName(); if (str.find(subs) != std::string::npos) { - std::string val = getNodeDefId(nodeName); + std::string val = getUserNodeDefName(nodeName); if (ImGui::MenuItem(val.c_str()) || (ImGui::IsItemFocused() && ImGui::IsKeyPressedMap(ImGuiKey_Enter))) { addNode(it->second[i]->getNodeString(), val, it->second[i]->getType()); @@ -3508,7 +3649,7 @@ void Graph::addNodePopup(bool cursor) for (size_t i = 0; i < it->second.size(); i++) { std::string name = it->second[i]->getName(); - std::string val = getNodeDefId(name); + std::string val = getUserNodeDefName(name); if (ImGui::MenuItem(val.c_str()) || (ImGui::IsItemFocused() && ImGui::IsKeyPressedMap(ImGuiKey_Enter))) { addNode(it->second[i]->getNodeString(), val, it->second[i]->getType()); @@ -3524,6 +3665,7 @@ void Graph::addNodePopup(bool cursor) open_AddPopup = false; } } + void Graph::searchNodePopup(bool cursor) { const bool open_search = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && ImGui::IsKeyDown(ImGuiKey_F) && ImGui::IsKeyDown(ImGuiKey_LeftCtrl); @@ -3547,7 +3689,6 @@ void Graph::searchNodePopup(bool cursor) if (std::string(input).size() > 0) { - for (UiNodePtr node : _graphNodes) { if (node->getName().find(std::string(input)) != std::string::npos) @@ -3581,7 +3722,6 @@ void Graph::readOnlyPopup() } } -// compiling shaders message void Graph::shaderPopup() { if (_renderer->getMaterialCompilation()) @@ -3601,7 +3741,6 @@ void Graph::shaderPopup() } } -// allow for camera manipulation of render view window void Graph::handleRenderViewInputs(ImVec2 minValue, float width, float height) { ImVec2 mousePos = ImGui::GetMousePos(); @@ -3645,14 +3784,15 @@ void Graph::handleRenderViewInputs(ImVec2 minValue, float width, float height) { _renderer->setKeyEvent(ImGuiKey_KeypadSubtract); } - // scrolling not possible if open or save file dialog is open + + // Scrolling not possible if open or save file dialog is open if (scrollAmt != 0 && !_fileDialogSave.isOpened() && !_fileDialog.isOpened() && !_fileDialogGeom.isOpened()) { _renderer->setScrollEvent(scrollAmt); } } } -// sets up graph editor + void Graph::drawGraph(ImVec2 mousePos) { if (_searchNodeId > 0) @@ -3663,7 +3803,8 @@ void Graph::drawGraph(ImVec2 mousePos) } bool TextCursor = false; - // center imgui window and setting size + + // Center imgui window and set size ImGuiIO& io2 = ImGui::GetIO(); ImGui::SetNextWindowSize(io2.DisplaySize); ImGui::SetNextWindowPos(ImVec2(io2.DisplaySize.x * 0.5f, io2.DisplaySize.y * 0.5f), ImGuiCond_Always, ImVec2(0.5f, 0.5f)); @@ -3676,7 +3817,8 @@ void Graph::drawGraph(ImVec2 mousePos) ed::Begin("My Editor"); { ed::Suspend(); - // set up pop ups for adding a node when tab is pressed + + // Set up popups for adding a node when tab is pressed ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f)); ImGui::SetNextWindowSizeConstraints(ImVec2(250.0f, 300.0f), ImVec2(-1.0f, 500.0f)); addNodePopup(TextCursor); @@ -3686,7 +3828,7 @@ void Graph::drawGraph(ImVec2 mousePos) ed::Resume(); - // Gathering selected nodes / links - from ImGui Node Editor blueprints-example.cpp + // Gather selected nodes / links - from ImGui Node Editor blueprints-example.cpp std::vector selectedNodes; std::vector selectedLinks; selectedNodes.resize(ed::GetSelectedObjectCount()); @@ -3702,29 +3844,23 @@ void Graph::drawGraph(ImVec2 mousePos) _ctrlClick = true; } - // setting current node based off of selected node + // Set current node based off of selected node if (selectedNodes.size() > 0) { int graphPos = findNode(int(selectedNodes[0].Get())); if (graphPos > -1) { - // only selected not if its not the same as previously selected + // Only selected if its not the same as previously selected if (!_prevUiNode || (_prevUiNode->getName() != _graphNodes[graphPos]->getName())) { _currUiNode = _graphNodes[graphPos]; - // update render material if needed + + // Update render material if needed if (_currUiNode->getNode()) - { - if (_currUiNode->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING || _currUiNode->getNode()->getType() == mx::MATERIAL_TYPE_STRING) - { - setRenderMaterial(_currUiNode); - } - } - else if (_currUiNode->getNodeGraph()) { setRenderMaterial(_currUiNode); } - else if (_currUiNode->getOutput()) + else if (_currUiNode->getNodeGraph() || _currUiNode->getOutput()) { setRenderMaterial(_currUiNode); } @@ -3733,7 +3869,7 @@ void Graph::drawGraph(ImVec2 mousePos) } } - // check if keyboard shortcuts for copy/cut/paste have been used + // Check if keyboard shortcuts for copy/cut/paste have been used if (ed::BeginShortcut()) { if (ed::AcceptCopy()) @@ -3753,7 +3889,8 @@ void Graph::drawGraph(ImVec2 mousePos) if (!readOnly()) { _copiedNodes.clear(); - // same as copy but remove from graphNodes + + // Same as copy but remove from graphNodes for (ed::NodeId selected : selectedNodes) { int pos = findNode((int) selected.Get()); @@ -3786,40 +3923,44 @@ void Graph::drawGraph(ImVec2 mousePos) } } - // set y position of first node + // Set y-position of first node std::vector outputNum = createNodes(_isNodeGraph); - // address copy information if applicable and relink graph if a new node has been added + // Address copy information if applicable and relink graph if a new node has been added if (_addNewNode) { copyInputs(); linkGraph(); ImVec2 canvasPos = ed::ScreenToCanvas(mousePos); - // place the copied nodes or the individual new nodes - if ((int) _copiedNodes.size() > 0) + + // Place the copied nodes or the individual new nodes + if (!_copiedNodes.empty()) { positionPasteBin(canvasPos); } - else + else if (!_graphNodes.empty()) { ed::SetNodePosition(_graphNodes.back()->getId(), canvasPos); } _copiedNodes.clear(); _addNewNode = false; } - // layout and link graph during the initial call of drawGraph() + + // Layout and link graph during the initial call of drawGraph if (_initial || _autoLayout) { _currLinks.clear(); float y = 0.f; _levelMap = std::unordered_map>(); - // start layout with output or material nodes since layout algorithm works right to left + + // Start layout with output or material nodes since layout algorithm works right to left for (int outN : outputNum) { layoutPosition(_graphNodes[outN], ImVec2(1200.f, y), true, 0); y += 350; } - // if there are no output or material nodes but the nodes have position layout each individual node + + // If there are no output or material nodes but the nodes have position layout each individual node if (_graphNodes.size() > 0) { @@ -3834,7 +3975,8 @@ void Graph::drawGraph(ImVec2 mousePos) linkGraph(); findYSpacing(0.f); layoutInputs(); - // automatically frame node graph upon loading + + // Automatically frame node graph upon loading ed::NavigateToContent(); } if (_delete) @@ -3844,13 +3986,15 @@ void Graph::drawGraph(ImVec2 mousePos) _delete = false; } connectLinks(); - // set to false after intial layout so that nodes can be moved + + // Set to false after intial layout so that nodes can be moved _initial = false; _autoLayout = false; - // delete selected nodes and their links if delete key is pressed or if the shortcut for cut is used + + // Delete selected nodes and their links if delete key is pressed + // or if the shortcut for cut is used if (ImGui::IsKeyReleased(ImGuiKey_Delete) || _isCut) { - if (selectedNodes.size() > 0) { _frameCount = ImGui::GetFrameCount(); @@ -3880,24 +4024,25 @@ void Graph::drawGraph(ImVec2 mousePos) _isCut = false; } - // start the session with content centered + // Start the session with content centered if (ImGui::GetFrameCount() == 2) { ed::NavigateToContent(0.0f); } - // hotkey to frame selected node(s) + // Hotkey to frame selected node(s) if (ImGui::IsKeyReleased(ImGuiKey_F) && !_fileDialogSave.isOpened()) { ed::NavigateToSelection(); } - // go back up from inside a subgraph + // Go back up from inside a subgraph if (ImGui::IsKeyReleased(ImGuiKey_U) && (!ImGui::IsPopupOpen("add node")) && (!ImGui::IsPopupOpen("search")) && !_fileDialogSave.isOpened()) { upNodeGraph(); } - // adding new link + + // Add new link if (ed::BeginCreate()) { ed::PinId inputPinId, outputPinId, filterPinId; @@ -3905,7 +4050,7 @@ void Graph::drawGraph(ImVec2 mousePos) { if (!readOnly()) { - AddLink(inputPinId, outputPinId); + addLink(inputPinId, outputPinId); } else { @@ -3925,7 +4070,8 @@ void Graph::drawGraph(ImVec2 mousePos) _pinFilterType = mx::EMPTY_STRING; } ed::EndCreate(); - // deleting link + + // Delete link if (ed::BeginDelete()) { ed::LinkId deletedLinkId; @@ -3944,7 +4090,7 @@ void Graph::drawGraph(ImVec2 mousePos) ed::EndDelete(); } - // diving into a node that has a subgraph + // Dive into a node that has a subgraph ed::NodeId clickedNode = ed::GetDoubleClickedNode(); if (clickedNode.Get() > 0) { @@ -3952,9 +4098,9 @@ void Graph::drawGraph(ImVec2 mousePos) { if (_currUiNode->getNode() != nullptr) { - mx::InterfaceElementPtr impl = _currUiNode->getNode()->getImplementation(); - // only dive if current node is a node graph + + // Only dive if current node is a node graph if (impl && impl->isA()) { savePosition(); @@ -4022,22 +4168,21 @@ void Graph::drawGraph(ImVec2 mousePos) ed::Suspend(); _fileDialogSave.display(); - // saving file + + // Save file if (_fileDialogSave.hasSelected()) { - std::string message; if (!_graphDoc->validate(&message)) { std::cerr << "*** Validation warnings for " << _materialFilename.getBaseName() << " ***" << std::endl; std::cerr << message; } - std::string fileName = _fileDialogSave.getSelected(); - mx::FilePath name = _fileDialogSave.getSelected(); + _materialFilename = _fileDialogSave.getSelected(); ed::Resume(); savePosition(); - writeText(fileName, name); + saveDocument(_materialFilename); _fileDialogSave.clearSelected(); } else @@ -4049,7 +4194,8 @@ void Graph::drawGraph(ImVec2 mousePos) ImGui::End(); _fileDialog.display(); - // create and load document from selected file + + // Create and load document from selected file if (_fileDialog.hasSelected()) { mx::FilePath fileName = _fileDialog.getSelected(); @@ -4057,7 +4203,6 @@ void Graph::drawGraph(ImVec2 mousePos) std::string graphName = fileName.getBaseName(); _currGraphName.push_back(graphName.substr(0, graphName.length() - 5)); _graphDoc = loadDocument(fileName); - _graphDoc->importLibrary(_stdLib); _initial = true; buildUiBaseGraph(_graphDoc); @@ -4081,7 +4226,6 @@ void Graph::drawGraph(ImVec2 mousePos) _fileDialogImage.display(); } -// return node location in graphNodes vector based off of node id int Graph::findNode(int nodeId) { int count = 0; @@ -4096,7 +4240,6 @@ int Graph::findNode(int nodeId) return -1; } -// find a link based on an attribute id std::vector Graph::findLinkId(int id) { std::vector ids; @@ -4109,7 +4252,7 @@ std::vector Graph::findLinkId(int id) } return ids; } -// check if current edge is already in edge vector + bool Graph::edgeExists(UiEdge newEdge) { if (_currEdge.size() > 0) @@ -4145,7 +4288,6 @@ bool Graph::edgeExists(UiEdge newEdge) return false; } -// check if a link exists in currLink vector bool Graph::linkExists(Link newLink) { for (const auto& link : _currLinks) @@ -4154,7 +4296,6 @@ bool Graph::linkExists(Link newLink) { if (link._endAttr == newLink._endAttr) { - // link exists return true; } } @@ -4162,7 +4303,6 @@ bool Graph::linkExists(Link newLink) { if (link._endAttr == newLink._startAttr) { - // link exists return true; } } @@ -4170,12 +4310,11 @@ bool Graph::linkExists(Link newLink) return false; } -// set materialX attribute positions for nodes which changed pos void Graph::savePosition() { for (UiNodePtr node : _graphNodes) { - if (node->getMxElement() != nullptr) + if (node->getMxElement()) { ImVec2 pos = ed::GetNodePosition(node->getId()); pos.x /= DEFAULT_NODE_SIZE.x; @@ -4189,14 +4328,27 @@ void Graph::savePosition() } } } -void Graph::writeText(std::string fileName, mx::FilePath filePath) +void Graph::saveDocument(mx::FilePath filePath) { if (filePath.getExtension() != mx::MTLX_EXTENSION) { filePath.addExtension(mx::MTLX_EXTENSION); } + mx::DocumentPtr writeDoc = _graphDoc; + + // If requested, create a modified version of the document for saving. + if (!_saveNodePositions) + { + writeDoc = _graphDoc->copy(); + for (mx::ElementPtr elem : writeDoc->traverseTree()) + { + elem->removeAttribute("xpos"); + elem->removeAttribute("ypos"); + } + } + mx::XmlWriteOptions writeOptions; writeOptions.elementPredicate = getElementPredicate(); - mx::writeToXmlFile(_graphDoc, filePath, &writeOptions); + mx::writeToXmlFile(writeDoc, filePath, &writeOptions); } diff --git a/source/MaterialXGraphEditor/Graph.h b/source/MaterialXGraphEditor/Graph.h index 68140cdaae..8c76f0f68d 100644 --- a/source/MaterialXGraphEditor/Graph.h +++ b/source/MaterialXGraphEditor/Graph.h @@ -60,105 +60,160 @@ class Graph private: mx::ElementPredicate getElementPredicate() const; void loadStandardLibraries(); + + // Generate node UI from nodedefs void createNodeUIList(mx::DocumentPtr doc); + + // Build UiNode nodegraph upon loading a document void buildUiBaseGraph(mx::DocumentPtr doc); + + // Build UiNode node graph upon diving into a nodegraph node void buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs); + + // Based on the comment node in the ImGui Node Editor blueprints-example.cpp. void buildGroupNode(UiNodePtr node); - // handling link information + // Connect links via connected nodes in UiNodePtr void linkGraph(); + + // Connect all links via the graph editor library void connectLinks(); + + // Find link position in current links vector from link id int findLinkPosition(int id); + + // Find link from attribute id std::vector findLinkId(int attrId); + + // Check if link exists in the current link vector bool linkExists(Link newLink); - void AddLink(ed::PinId inputPinId, ed::PinId outputPinId); + + // Add link to nodegraph and set up connections between UiNodes and + // MaterialX Nodes to update shader + void addLink(ed::PinId inputPinId, ed::PinId outputPinId); + + // Delete link from current link vector and remove any connections in + // UiNode or MaterialX Nodes to update shader void deleteLink(ed::LinkId deletedLinkId); + void deleteLinkInfo(int startAtrr, int endAttr); - // functions for the layout of the nodes + // Layout the x-position by assigning the node levels based on its distance from the first node ImVec2 layoutPosition(UiNodePtr node, ImVec2 pos, bool initialLayout, int level); + + // Extra layout pass for inputs and nodes that do not attach to an output node void layoutInputs(); + void findYSpacing(float startPos); float totalHeight(int level); void setYSpacing(int level, float startingPos); float findAvgY(const std::vector& nodes); - // pin information + // Return pin color based on the type of the value of that pin void setPinColor(); - void DrawPinIcon(std::string type, bool connected, int alpha); + + // Based on the pin icon function in the ImGui Node Editor blueprints-example.cpp + void drawPinIcon(std::string type, bool connected, int alpha); + UiPinPtr getPin(ed::PinId id); void drawInputPin(UiPinPtr pin); + + // Return output pin needed to link the inputs and outputs ed::PinId getOutputPin(UiNodePtr node, UiNodePtr inputNode, UiPinPtr input); + void drawOutputPins(UiNodePtr node, const std::string& longestInputLabel); + + // Create pins for outputs/inputs added while inside the node graph void addNodeGraphPins(); - // UiNode functions std::vector createNodes(bool nodegraph); int getNodeId(ed::PinId pinId); + + // Find node location in graph nodes vector from node id int findNode(int nodeId); + + // Return node position in _graphNodes from node name and type to account for + // input/output UiNodes with same names as MaterialX nodes int findNode(const std::string& name, const std::string& type); + + // Add node to graphNodes based on nodedef information void addNode(const std::string& category, const std::string& name, const std::string& type); + void deleteNode(UiNodePtr node); + + // Build the initial graph of a loaded document including shader, material and nodegraph node void setUiNodeInfo(UiNodePtr node, const std::string& type, const std::string& category); - // UiEdge functions + // Check if edge exists in edge vector bool edgeExists(UiEdge edge); + void createEdge(UiNodePtr upNode, UiNodePtr downNode, mx::InputPtr connectingInput); + + // Remove node edge based on connecting input void removeEdge(int downNode, int upNode, UiPinPtr pin); - void writeText(std::string filename, mx::FilePath filePath); + void saveDocument(mx::FilePath filePath); + + // Set position attributes for nodes which changed position void savePosition(); + + // Check if node has already been assigned a position bool checkPosition(UiNodePtr node); + // Add input pointer to node based on input pin void addNodeInput(UiNodePtr node, mx::InputPtr& input); - mx::InputPtr findInput(mx::InputPtr input, std::string name); - // travel up from inside a node graph + mx::InputPtr findInput(mx::InputPtr input, const std::string& name); void upNodeGraph(); - // property editor information + // Set the value of the selected node constants in the node property editor void setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIProperties& uiProperties); + void propertyEditor(); void setDefaults(mx::InputPtr input); - // set up Ui information for add node popup + // Setup UI information for add node popup void addExtraNodes(); - // copy and paste functions void copyInputs(); + + // Set position of pasted nodes based on original node position void positionPasteBin(ImVec2 pos); + void copyNodeGraph(UiNodePtr origGraph, UiNodePtr copyGraph); void copyUiNode(UiNodePtr node); - // renderview window and buttons void graphButtons(); - // popup information void addNodePopup(bool cursor); void searchNodePopup(bool cursor); bool readOnly(); void readOnlyPopup(); + + // Compiling shaders message void shaderPopup(); - // modifying materials void updateMaterials(mx::InputPtr input = nullptr, mx::ValuePtr value = nullptr); void selectMaterial(UiNodePtr node); + + // Allow for camera manipulation of render view window void handleRenderViewInputs(ImVec2 minValue, float width, float height); + + // Set the node to display in render view based on selected node or nodegraph void setRenderMaterial(UiNodePtr node); - // File I/O void clearGraph(); - void loadGraphFromFile(); + void loadGraphFromFile(bool prompt); void saveGraphToFile(); void loadGeometry(); + void showHelp() const; + + private: mx::StringVec _geomFilter; mx::StringVec _mtlxFilter; mx::StringVec _imageFilter; - // Help - void showHelp() const; - RenderViewPtr _renderer; // document and intializing information @@ -214,7 +269,7 @@ class Graph FileDialog _fileDialogSave; FileDialog _fileDialogImage; FileDialog _fileDialogGeom; - + std::string _fileDialogImageInputName; bool _isNodeGraph; @@ -236,7 +291,10 @@ class Graph std::string _pinFilterType; // DPI scaling for fonts - float _fontScale = 1.0f; + float _fontScale; + + // Options + bool _saveNodePositions; }; #endif diff --git a/source/MaterialXGraphEditor/RenderView.cpp b/source/MaterialXGraphEditor/RenderView.cpp index faf48f543a..176014ba65 100644 --- a/source/MaterialXGraphEditor/RenderView.cpp +++ b/source/MaterialXGraphEditor/RenderView.cpp @@ -159,7 +159,6 @@ RenderView::RenderView(mx::DocumentPtr doc, _genContext(mx::GlslShaderGenerator::create()), _unitRegistry(mx::UnitConverterRegistry::create()), _splitByUdims(true), - _mergeMaterials(false), _materialCompilation(false), _renderTransparency(true), _renderDoubleSided(true), @@ -410,18 +409,15 @@ void RenderView::updateMaterials(mx::TypedElementPtr typedElem) // Clear user data on the generator. _genContext.clearUserData(); - // Clear materials if merging is not requested. - if (!_mergeMaterials) + // Clear materials. + for (mx::MeshPartitionPtr geom : _geometryList) { - for (mx::MeshPartitionPtr geom : _geometryList) + if (_materialAssignments.count(geom)) { - if (_materialAssignments.count(geom)) - { - assignMaterial(geom, nullptr); - } + assignMaterial(geom, nullptr); } - _materials.clear(); } + _materials.clear(); std::vector newMaterials; try @@ -434,10 +430,19 @@ void RenderView::updateMaterials(mx::TypedElementPtr typedElem) // Apply direct lights. applyDirectLights(_document); - //// Check for any udim set. + // Check for any udim set. mx::ValuePtr udimSetValue = _document->getGeomPropValue(mx::UDIM_SET_PROPERTY); - //// Create new materials. + // Skip material nodes without upstream shaders. + mx::NodePtr node = typedElem ? typedElem->asA() : nullptr; + if (node && + node->getCategory() == mx::SURFACE_MATERIAL_NODE_STRING && + mx::getShaderNodes(node).empty()) + { + typedElem = nullptr; + } + + // Create new materials. if (!typedElem) { std::vector elems = mx::findRenderableElements(_document); @@ -451,8 +456,7 @@ void RenderView::updateMaterials(mx::TypedElementPtr typedElem) mx::NodePtr materialNode = nullptr; if (typedElem) { - mx::NodePtr node = typedElem->asA(); - materialNode = node && node->getType() == mx::MATERIAL_TYPE_STRING ? node : nullptr; + materialNode = node; if (udimSetValue && udimSetValue->isA()) { for (const std::string& udim : udimSetValue->asA()) @@ -551,14 +555,11 @@ void RenderView::updateMaterials(mx::TypedElementPtr typedElem) // Apply fallback assignments. mx::GlslMaterialPtr fallbackMaterial = newMaterials[0]; - if (!_mergeMaterials || fallbackMaterial->getUdim().empty()) + for (mx::MeshPartitionPtr geom : _geometryList) { - for (mx::MeshPartitionPtr geom : _geometryList) + if (!_materialAssignments[geom]) { - if (!_materialAssignments[geom]) - { - assignMaterial(geom, fallbackMaterial); - } + assignMaterial(geom, fallbackMaterial); } } @@ -720,7 +721,7 @@ void RenderView::loadEnvironmentLight() envIrradianceMap = _imageHandler->acquireImage(envIrradiancePath); // If not found, then generate an irradiance map via spherical harmonics. - if (envIrradianceMap == _imageHandler->getInvalidImage()) + if (envIrradianceMap == _imageHandler->getZeroImage()) { mx::Sh3ColorCoeffs shIrradiance = mx::projectEnvironment(envRadianceMap, true); envIrradianceMap = mx::renderEnvironment(shIrradiance, IRRADIANCE_MAP_WIDTH, IRRADIANCE_MAP_HEIGHT); diff --git a/source/MaterialXGraphEditor/RenderView.h b/source/MaterialXGraphEditor/RenderView.h index e931be9592..354e898ce1 100644 --- a/source/MaterialXGraphEditor/RenderView.h +++ b/source/MaterialXGraphEditor/RenderView.h @@ -145,11 +145,6 @@ class RenderView return _materialAssignments; } - bool getMergeMaterials() - { - return _mergeMaterials; - } - std::vector getMaterials() { return _materials; @@ -323,7 +318,6 @@ class RenderView bool _splitByUdims; // Material options - bool _mergeMaterials; bool _materialCompilation; // Unit options diff --git a/source/MaterialXRender/CMakeLists.txt b/source/MaterialXRender/CMakeLists.txt index d0a50232cd..52d07e688e 100644 --- a/source/MaterialXRender/CMakeLists.txt +++ b/source/MaterialXRender/CMakeLists.txt @@ -1,3 +1,5 @@ +set(MATERIALX_MODULE_NAME MaterialXRender) + include_directories( ${EXTERNAL_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/../) @@ -18,12 +20,18 @@ if(UNIX) add_compile_options(-Wno-unused-function) endif() -add_library(MaterialXRender ${materialx_source} ${materialx_headers} ${materialx_inlined}) +add_library(${MATERIALX_MODULE_NAME} ${materialx_source} ${materialx_headers} ${materialx_inlined}) add_definitions(-DMATERIALX_RENDER_EXPORTS) +# Create version resource +if(MATERIALX_BUILD_SHARED_LIBS AND MSVC) + configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/MaterialXVersion.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + target_sources(${MATERIALX_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + target_link_libraries( - MaterialXRender + ${MATERIALX_MODULE_NAME} MaterialXGenShader ${CMAKE_DL_LIBS}) @@ -33,7 +41,7 @@ if(MATERIALX_BUILD_OIIO) find_package(OpenImageIO REQUIRED) if(OPENIMAGEIO_FOUND) include_directories(${OPENIMAGEIO_INCLUDE_DIR}) - target_link_libraries(MaterialXRender ${OPENIMAGEIO_LIBRARIES}) + target_link_libraries(${MATERIALX_MODULE_NAME} ${OPENIMAGEIO_LIBRARIES}) # Also needed by MaterialXTest: set(OPENIMAGEIO_FOUND "${OPENIMAGEIO_FOUND}" PARENT_SCOPE) set(OPENIMAGEIO_INCLUDE_DIR "${OPENIMAGEIO_INCLUDE_DIR}" PARENT_SCOPE) @@ -44,25 +52,27 @@ if(MATERIALX_BUILD_OIIO) endif() set_target_properties( - MaterialXRender PROPERTIES - OUTPUT_NAME MaterialXRender${MATERIALX_LIBNAME_SUFFIX} + ${MATERIALX_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX} COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_SAME_DIR_RPATH}" VERSION "${MATERIALX_LIBRARY_VERSION}" SOVERSION "${MATERIALX_MAJOR_VERSION}") -install(TARGETS MaterialXRender - EXPORT MaterialX - ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - RUNTIME DESTINATION bin) +if(NOT SKBUILD) + install(TARGETS ${MATERIALX_MODULE_NAME} + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + RUNTIME DESTINATION bin) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXRender/ MESSAGE_NEVER - FILES_MATCHING - PATTERN "*.h*" - PATTERN "*.inl") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/${MATERIALX_MODULE_NAME}/ MESSAGE_NEVER + FILES_MATCHING + PATTERN "*.h*" + PATTERN "*.inl") -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXRender.pdb" - DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${MATERIALX_MODULE_NAME}.pdb" + DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) +endif() diff --git a/source/MaterialXRender/External/OpenImageIO/FindOpenImageIO.cmake b/source/MaterialXRender/External/OpenImageIO/FindOpenImageIO.cmake index 4fab4f9814..6c1b1682d6 100644 --- a/source/MaterialXRender/External/OpenImageIO/FindOpenImageIO.cmake +++ b/source/MaterialXRender/External/OpenImageIO/FindOpenImageIO.cmake @@ -45,6 +45,13 @@ find_library ( OPENIMAGEIO_LIBRARY HINTS ${OPENIMAGEIO_ROOT_DIR}/lib PATH_SUFFIXES lib64 lib PATHS "${OPENIMAGEIO_ROOT_DIR}/lib" ) + +find_library ( OPENIMAGEIO_UTIL_LIBRARY + NAMES OpenImageIO_Util${OIIO_LIBNAME_SUFFIX} + HINTS ${OPENIMAGEIO_ROOT_DIR}/lib + PATH_SUFFIXES lib64 lib + PATHS "${OPENIMAGEIO_ROOT_DIR}/lib" ) + find_path ( OPENIMAGEIO_INCLUDE_DIR NAMES OpenImageIO/imageio.h HINTS ${OPENIMAGEIO_ROOT_DIR}/include @@ -66,7 +73,7 @@ if (EXISTS "${OIIO_VERSION_HEADER}") set (OPENIMAGEIO_VERSION "${OPENIMAGEIO_VERSION_MAJOR}.${OPENIMAGEIO_VERSION_MINOR}.${OPENIMAGEIO_VERSION_PATCH}") endif () -set ( OPENIMAGEIO_LIBRARIES ${OPENIMAGEIO_LIBRARY}) +set ( OPENIMAGEIO_LIBRARIES ${OPENIMAGEIO_LIBRARY} ${OPENIMAGEIO_UTIL_LIBRARY}) get_filename_component (OPENIMAGEIO_LIBRARY_DIRS "${OPENIMAGEIO_LIBRARY}" DIRECTORY CACHE) if (NOT OpenImageIO_FIND_QUIETLY) diff --git a/source/MaterialXRender/ImageHandler.cpp b/source/MaterialXRender/ImageHandler.cpp index bc43b617ff..d54a18fedd 100644 --- a/source/MaterialXRender/ImageHandler.cpp +++ b/source/MaterialXRender/ImageHandler.cpp @@ -56,9 +56,6 @@ ImageHandler::ImageHandler(ImageLoaderPtr imageLoader) { addLoader(imageLoader); _zeroImage = createUniformImage(2, 2, 4, Image::BaseType::UINT8, Color4(0.0f)); - - // Generated shaders interpret 1x1 textures as invalid images. - _invalidImage = createUniformImage(1, 1, 4, Image::BaseType::UINT8, Color4(0.0f)); } void ImageHandler::addLoader(ImageLoaderPtr loader) @@ -118,7 +115,7 @@ bool ImageHandler::saveImage(const FilePath& filePath, return false; } -ImagePtr ImageHandler::acquireImage(const FilePath& filePath) +ImagePtr ImageHandler::acquireImage(const FilePath& filePath, const Color4& defaultColor) { // Resolve the input filepath. FilePath resolvedFilePath = filePath; @@ -142,9 +139,12 @@ ImagePtr ImageHandler::acquireImage(const FilePath& filePath) return image; } - // No valid image was found, so cache the sentinel invalid image. - cacheImage(resolvedFilePath, _invalidImage); - return _invalidImage; + // No valid image was found, so generate a uniform texture with the given default color. + // TODO: This step assumes that the missing image and its default color are in the same + // color space, which is not always the case. + ImagePtr defaultImage = createUniformImage(1, 1, 4, Image::BaseType::UINT8, defaultColor); + cacheImage(resolvedFilePath, defaultImage); + return defaultImage; } bool ImageHandler::bindImage(ImagePtr, const ImageSamplingProperties&) @@ -189,7 +189,7 @@ ImageVec ImageHandler::getReferencedImages(ConstDocumentPtr doc) if (file) { ImagePtr image = acquireImage(file->getResolvedValueString()); - if (image && image != _invalidImage) + if (image) { imageVec.push_back(image); } @@ -214,14 +214,6 @@ ImagePtr ImageHandler::loadImage(const FilePath& filePath) } if (image) { - // Generated shaders interpret 1x1 textures as invalid images, so valid 1x1 - // images must be resized. - if (image->getWidth() == 1 && image->getHeight() == 1) - { - image = createUniformImage(2, 2, image->getChannelCount(), - image->getBaseType(), image->getTexelColor(0, 0)); - } - return image; } } @@ -288,23 +280,23 @@ void ImageSamplingProperties::setProperties(const string& fileNameUniform, root = root.substr(0, pos); } - const string uaddressmodeStr = root + UADDRESS_MODE_SUFFIX; - const ShaderPort* port = uniformBlock.find(uaddressmodeStr); + const ShaderPort* port = uniformBlock.find(root + UADDRESS_MODE_SUFFIX); ValuePtr intValue = port ? port->getValue() : nullptr; uaddressMode = ImageSamplingProperties::AddressMode(intValue && intValue->isA() ? intValue->asA() : INVALID_MAPPED_INT_VALUE); - const string vaddressmodeStr = root + VADDRESS_MODE_SUFFIX; - port = uniformBlock.find(vaddressmodeStr); + port = uniformBlock.find(root + VADDRESS_MODE_SUFFIX); intValue = port ? port->getValue() : nullptr; vaddressMode = ImageSamplingProperties::AddressMode(intValue && intValue->isA() ? intValue->asA() : INVALID_MAPPED_INT_VALUE); - const string filtertypeStr = root + FILTER_TYPE_SUFFIX; - port = uniformBlock.find(filtertypeStr); + port = uniformBlock.find(root + FILTER_TYPE_SUFFIX); intValue = port ? port->getValue() : nullptr; filterType = ImageSamplingProperties::FilterType(intValue && intValue->isA() ? intValue->asA() : INVALID_MAPPED_INT_VALUE); - const string defaultColorStr = root + DEFAULT_COLOR_SUFFIX; - port = uniformBlock.find(defaultColorStr); + port = uniformBlock.find(root + DEFAULT_COLOR_SUFFIX); + if (!port) + { + port = uniformBlock.find(root + DEFAULT_COLOR_SUFFIX + "_cm_in"); + } ValuePtr colorValue = port ? port->getValue() : nullptr; if (colorValue) { diff --git a/source/MaterialXRender/ImageHandler.h b/source/MaterialXRender/ImageHandler.h index ee1fe127c4..c744eb2f6a 100644 --- a/source/MaterialXRender/ImageHandler.h +++ b/source/MaterialXRender/ImageHandler.h @@ -188,7 +188,7 @@ class MX_RENDER_API ImageHandler /// found in the cache, then each image loader will be applied in turn. /// @param filePath File path of the image. /// @return On success, a shared pointer to the acquired image. - ImagePtr acquireImage(const FilePath& filePath); + ImagePtr acquireImage(const FilePath& filePath, const Color4& defaultColor = Color4(0.0f)); /// Bind an image for rendering. /// @param image The image to bind. @@ -247,13 +247,6 @@ class MX_RENDER_API ImageHandler return _zeroImage; } - /// Return the sentinel invalid image, representing images that cannot be loaded - /// and should be replaced with their declared default value. - ImagePtr getInvalidImage() const - { - return _invalidImage; - } - /// Acquire all images referenced by the given document, and return the /// images in a vector. ImageVec getReferencedImages(ConstDocumentPtr doc); @@ -278,7 +271,6 @@ class MX_RENDER_API ImageHandler FileSearchPath _searchPath; StringResolverPtr _resolver; ImagePtr _zeroImage; - ImagePtr _invalidImage; }; MATERIALX_NAMESPACE_END diff --git a/source/MaterialXRender/ShaderRenderer.cpp b/source/MaterialXRender/ShaderRenderer.cpp index a5e81d1f68..3bba62795e 100644 --- a/source/MaterialXRender/ShaderRenderer.cpp +++ b/source/MaterialXRender/ShaderRenderer.cpp @@ -25,17 +25,26 @@ const float DEFAULT_FAR_PLANE = 100.0f; // ShaderRenderer methods // -ShaderRenderer::ShaderRenderer(unsigned int width, unsigned int height, Image::BaseType baseType) : +ShaderRenderer::ShaderRenderer(unsigned int width, unsigned int height, Image::BaseType baseType, MatrixConvention matrixConvention) : _width(width), _height(height), - _baseType(baseType) + _baseType(baseType), + _matrixConvention(matrixConvention) { // Initialize a default camera. float fH = std::tan(DEFAULT_FIELD_OF_VIEW / 360.0f * PI) * DEFAULT_NEAR_PLANE; float fW = fH * 1.0f; _camera = Camera::create(); _camera->setViewMatrix(Camera::createViewMatrix(DEFAULT_EYE_POSITION, DEFAULT_TARGET_POSITION, DEFAULT_UP_VECTOR)); - _camera->setProjectionMatrix(Camera::createPerspectiveMatrix(-fW, fW, -fH, fH, DEFAULT_NEAR_PLANE, DEFAULT_FAR_PLANE)); + + if (_matrixConvention == ShaderRenderer::MatrixConvention::Metal) + { + _camera->setProjectionMatrix(Camera::createPerspectiveMatrixZP(-fW, fW, -fH, fH, DEFAULT_NEAR_PLANE, DEFAULT_FAR_PLANE)); + } + else // MatrixConvention::OpenGL (default) + { + _camera->setProjectionMatrix(Camera::createPerspectiveMatrix(-fW, fW, -fH, fH, DEFAULT_NEAR_PLANE, DEFAULT_FAR_PLANE)); + } } void ShaderRenderer::createProgram(ShaderPtr) @@ -50,4 +59,9 @@ void ShaderRenderer::setSize(unsigned int, unsigned int) { } +void ShaderRenderer::updateUniform(const string&, ConstValuePtr) +{ + throw ExceptionRenderError("Update uniform is not yet supported"); +} + MATERIALX_NAMESPACE_END diff --git a/source/MaterialXRender/ShaderRenderer.h b/source/MaterialXRender/ShaderRenderer.h index 2c6f5fbf87..c1cfbecfa0 100644 --- a/source/MaterialXRender/ShaderRenderer.h +++ b/source/MaterialXRender/ShaderRenderer.h @@ -30,6 +30,12 @@ using ShaderRendererPtr = std::shared_ptr; class MX_RENDER_API ShaderRenderer { public: + /// Viewing API matrix conventions designation (default to OpenGL). + enum class MatrixConvention + { + OpenGL = 0, + Metal = 1 + }; /// A map with name and source code for each shader stage. using StageMap = StringMap; @@ -104,6 +110,9 @@ class MX_RENDER_API ShaderRenderer /// Validate inputs for the program. virtual void validateInputs() { } + /// Update the program with value of the uniform. + virtual void updateUniform(const string& name, ConstValuePtr value); + /// Set the size of the rendered image. virtual void setSize(unsigned int width, unsigned int height); @@ -123,13 +132,16 @@ class MX_RENDER_API ShaderRenderer /// @} protected: - ShaderRenderer(unsigned int width, unsigned int height, Image::BaseType baseType); + ShaderRenderer(unsigned int width, unsigned int height, Image::BaseType baseType, + MatrixConvention matrixConvention = MatrixConvention::OpenGL); protected: unsigned int _width; unsigned int _height; Image::BaseType _baseType; + MatrixConvention _matrixConvention; + CameraPtr _camera; ImageHandlerPtr _imageHandler; GeometryHandlerPtr _geometryHandler; diff --git a/source/MaterialXRenderGlsl/CMakeLists.txt b/source/MaterialXRenderGlsl/CMakeLists.txt index ddab96c318..cb2f3c27c2 100644 --- a/source/MaterialXRenderGlsl/CMakeLists.txt +++ b/source/MaterialXRenderGlsl/CMakeLists.txt @@ -1,3 +1,5 @@ +set(MATERIALX_MODULE_NAME MaterialXRenderGlsl) + file(GLOB_RECURSE materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.c*") file(GLOB_RECURSE materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h*") @@ -34,12 +36,18 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wno-deprecated-declarations) endif() -add_library(MaterialXRenderGlsl ${materialx_source} ${materialx_headers}) +add_library(${MATERIALX_MODULE_NAME} ${materialx_source} ${materialx_headers}) add_definitions(-DMATERIALX_RENDERGLSL_EXPORTS) +# Create version resource +if(MATERIALX_BUILD_SHARED_LIBS AND MSVC) + configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/MaterialXVersion.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + target_sources(${MATERIALX_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + if(MATERIALX_BUILD_SHARED_LIBS) - target_compile_definitions(MaterialXRenderGlsl PUBLIC GLAD_GLAPI_EXPORT PRIVATE GLAD_GLAPI_EXPORT_BUILD) + target_compile_definitions(${MATERIALX_MODULE_NAME} PUBLIC GLAD_GLAPI_EXPORT PRIVATE GLAD_GLAPI_EXPORT_BUILD) endif() set(COMMON_LIBRARIES @@ -50,19 +58,19 @@ set(COMMON_LIBRARIES if(WIN32) if(MSVC) target_link_libraries( - MaterialXRenderGlsl + ${MATERIALX_MODULE_NAME} ${COMMON_LIBRARIES} Opengl32) elseif(MINGW) target_link_libraries( - MaterialXRenderGlsl + ${MATERIALX_MODULE_NAME} ${COMMON_LIBRARIES} Opengl32 gdi32) endif() elseif(APPLE) target_link_libraries( - MaterialXRenderGlsl + ${MATERIALX_MODULE_NAME} ${COMMON_LIBRARIES} ${OPENGL_LIBRARIES} "-framework Foundation" @@ -70,7 +78,7 @@ elseif(APPLE) "-framework Metal") elseif(UNIX) target_link_libraries( - MaterialXRenderGlsl + ${MATERIALX_MODULE_NAME} ${COMMON_LIBRARIES} ${OPENGL_LIBRARIES} ${X11_LIBRARIES} @@ -78,30 +86,32 @@ elseif(UNIX) endif() set_target_properties( - MaterialXRenderGlsl PROPERTIES - OUTPUT_NAME MaterialXRenderGlsl${MATERIALX_LIBNAME_SUFFIX} + ${MATERIALX_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX} COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_SAME_DIR_RPATH}" VERSION "${MATERIALX_LIBRARY_VERSION}" SOVERSION "${MATERIALX_MAJOR_VERSION}") -target_include_directories(MaterialXRenderGlsl +target_include_directories(${MATERIALX_MODULE_NAME} PUBLIC $ $ PRIVATE ${EXTERNAL_INCLUDE_DIRS}) -install(TARGETS MaterialXRenderGlsl - EXPORT MaterialX - ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - RUNTIME DESTINATION bin) +if(NOT SKBUILD) + install(TARGETS ${MATERIALX_MODULE_NAME} + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + RUNTIME DESTINATION bin) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXRenderGlsl/ MESSAGE_NEVER - FILES_MATCHING PATTERN "*.h*") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/${MATERIALX_MODULE_NAME}/ MESSAGE_NEVER + FILES_MATCHING PATTERN "*.h*") -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXRenderGlsl.pdb" - DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${MATERIALX_MODULE_NAME}.pdb" + DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) +endif() diff --git a/source/MaterialXRenderGlsl/GlslMaterial.cpp b/source/MaterialXRenderGlsl/GlslMaterial.cpp index ef7a0ed0c2..ce4582d44f 100644 --- a/source/MaterialXRenderGlsl/GlslMaterial.cpp +++ b/source/MaterialXRenderGlsl/GlslMaterial.cpp @@ -209,7 +209,7 @@ ImagePtr GlslMaterial::bindImage(const FilePath& filePath, const std::string& un imageHandler->setFilenameResolver(resolver); // Acquire the given image. - ImagePtr image = imageHandler->acquireImage(filePath); + ImagePtr image = imageHandler->acquireImage(filePath, samplingProperties.defaultColor); if (!image) { return nullptr; diff --git a/source/MaterialXRenderGlsl/GlslProgram.cpp b/source/MaterialXRenderGlsl/GlslProgram.cpp index a60997716a..900cac0ef1 100644 --- a/source/MaterialXRenderGlsl/GlslProgram.cpp +++ b/source/MaterialXRenderGlsl/GlslProgram.cpp @@ -491,7 +491,7 @@ ImagePtr GlslProgram::bindTexture(unsigned int uniformType, int uniformLocation, uniformType >= GL_SAMPLER_1D && uniformType <= GL_SAMPLER_CUBE) { // Acquire the image. - ImagePtr image = imageHandler->acquireImage(filePath); + ImagePtr image = imageHandler->acquireImage(filePath, samplingProperties.defaultColor); if (imageHandler->bindImage(image, samplingProperties)) { GLTextureHandlerPtr textureHandler = std::static_pointer_cast(imageHandler); diff --git a/source/MaterialXRenderGlsl/GlslRenderer.cpp b/source/MaterialXRenderGlsl/GlslRenderer.cpp index e81ded996e..374305f32f 100644 --- a/source/MaterialXRenderGlsl/GlslRenderer.cpp +++ b/source/MaterialXRenderGlsl/GlslRenderer.cpp @@ -25,7 +25,7 @@ GlslRendererPtr GlslRenderer::create(unsigned int width, unsigned int height, Im } GlslRenderer::GlslRenderer(unsigned int width, unsigned int height, Image::BaseType baseType) : - ShaderRenderer(width, height, baseType), + ShaderRenderer(width, height, baseType, MatrixConvention::OpenGL), _initialized(false), _screenColor(DEFAULT_SCREEN_COLOR_LIN_REC709) { @@ -119,6 +119,16 @@ void GlslRenderer::validateInputs() _program->getAttributesList(); } +void GlslRenderer::updateUniform(const string& name, ConstValuePtr value) +{ + if (!_program->bind()) + { + return; + } + + _program->bindUniform(name, value); +} + void GlslRenderer::setSize(unsigned int width, unsigned int height) { if (_context->makeCurrent()) diff --git a/source/MaterialXRenderGlsl/GlslRenderer.h b/source/MaterialXRenderGlsl/GlslRenderer.h index 8538fe2208..9ea121aec9 100644 --- a/source/MaterialXRenderGlsl/GlslRenderer.h +++ b/source/MaterialXRenderGlsl/GlslRenderer.h @@ -79,6 +79,9 @@ class MX_RENDERGLSL_API GlslRenderer : public ShaderRenderer /// Validate inputs for the program void validateInputs() override; + /// Update the program with value of the uniform. + void updateUniform(const string& name, ConstValuePtr value) override; + /// Set the size of the rendered image void setSize(unsigned int width, unsigned int height) override; diff --git a/source/MaterialXRenderHw/CMakeLists.txt b/source/MaterialXRenderHw/CMakeLists.txt index 85ef13c55c..dfd262a3ae 100644 --- a/source/MaterialXRenderHw/CMakeLists.txt +++ b/source/MaterialXRenderHw/CMakeLists.txt @@ -1,8 +1,12 @@ +set(MATERIALX_MODULE_NAME MaterialXRenderHw) + file(GLOB materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h*") if(APPLE) +if (NOT MATERIALX_BUILD_IOS) find_library(COCOA_FRAMEWORK Cocoa) +endif() file(GLOB materialx_source_oc "${CMAKE_CURRENT_SOURCE_DIR}/*.m") message("Objective C files: " ${materialx_source_oc}) set_source_files_properties(${materialx_source_oc} PROPERTIES @@ -20,26 +24,37 @@ endif() assign_source_group("Source Files" ${materialx_source}) assign_source_group("Header Files" ${materialx_headers}) -add_library(MaterialXRenderHw ${materialx_source} ${materialx_headers}) +add_library(${MATERIALX_MODULE_NAME} ${materialx_source} ${materialx_headers}) add_definitions(-DMATERIALX_RENDERHW_EXPORTS) +if(APPLE AND NOT MATERIALX_BUILD_IOS) +set(CMAKE_DL_LIBS +${CMAKE_DL_LIBS} +"-framework Cocoa") +endif() + +# Create version resource +if(MATERIALX_BUILD_SHARED_LIBS AND MSVC) + configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/MaterialXVersion.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + target_sources(${MATERIALX_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + if(MSVC) target_link_libraries( - MaterialXRenderHw + ${MATERIALX_MODULE_NAME} MaterialXRender ${CMAKE_DL_LIBS}) elseif(APPLE) target_link_libraries( - MaterialXRenderHw + ${MATERIALX_MODULE_NAME} MaterialXRender ${CMAKE_DL_LIBS} "-framework Foundation" - "-framework Cocoa" "-framework Metal") elseif(UNIX) target_link_libraries( - MaterialXRenderHw + ${MATERIALX_MODULE_NAME} MaterialXRender ${CMAKE_DL_LIBS} ${X11_LIBRARIES} @@ -47,30 +62,32 @@ elseif(UNIX) endif() set_target_properties( - MaterialXRenderHw PROPERTIES - OUTPUT_NAME MaterialXRenderHw${MATERIALX_LIBNAME_SUFFIX} + ${MATERIALX_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX} COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_SAME_DIR_RPATH}" VERSION "${MATERIALX_LIBRARY_VERSION}" SOVERSION "${MATERIALX_MAJOR_VERSION}") -target_include_directories(MaterialXRenderHw +target_include_directories(${MATERIALX_MODULE_NAME} PUBLIC $ $ PRIVATE ${EXTERNAL_INCLUDE_DIRS}) -install(TARGETS MaterialXRenderHw - EXPORT MaterialX - ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - RUNTIME DESTINATION bin) +if(NOT SKBUILD) + install(TARGETS ${MATERIALX_MODULE_NAME} + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + RUNTIME DESTINATION bin) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXRenderHw/ MESSAGE_NEVER - FILES_MATCHING PATTERN "*.h*") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/${MATERIALX_MODULE_NAME}/ MESSAGE_NEVER + FILES_MATCHING PATTERN "*.h*") -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXRenderHw.pdb" - DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${MATERIALX_MODULE_NAME}.pdb" + DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) +endif() diff --git a/source/MaterialXRenderHw/SimpleWindowIOS.cpp b/source/MaterialXRenderHw/SimpleWindowIOS.cpp new file mode 100644 index 0000000000..1d4189b2ab --- /dev/null +++ b/source/MaterialXRenderHw/SimpleWindowIOS.cpp @@ -0,0 +1,40 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#if defined(__APPLE__) + +#ifdef TARGET_OS_IOS + +#include + +MATERIALX_NAMESPACE_BEGIN + +SimpleWindow::SimpleWindow() : + _width(0), + _height(0) +{ + // Give a unique identifier to this window. + static unsigned int windowCount = 1; + _id = windowCount; + windowCount++; +} + +bool SimpleWindow::initialize(const char* title, + unsigned int width, unsigned int height, + void* /*applicationShell*/) +{ + _windowWrapper = WindowWrapper::create(nullptr); + return true; +} + +SimpleWindow::~SimpleWindow() +{ +} + +MATERIALX_NAMESPACE_END + +#endif + +#endif diff --git a/source/MaterialXRenderHw/SimpleWindowMac.cpp b/source/MaterialXRenderHw/SimpleWindowMac.cpp index 1cf070ac74..a04d1f49ae 100644 --- a/source/MaterialXRenderHw/SimpleWindowMac.cpp +++ b/source/MaterialXRenderHw/SimpleWindowMac.cpp @@ -5,6 +5,8 @@ #if defined(__APPLE__) +#ifndef TARGET_OS_IOS + #include #include @@ -42,3 +44,5 @@ SimpleWindow::~SimpleWindow() MATERIALX_NAMESPACE_END #endif + +#endif diff --git a/source/MaterialXRenderHw/WindowCocoaWrappers.m b/source/MaterialXRenderHw/WindowCocoaWrappers.m index c73de77810..a012966a8f 100644 --- a/source/MaterialXRenderHw/WindowCocoaWrappers.m +++ b/source/MaterialXRenderHw/WindowCocoaWrappers.m @@ -5,6 +5,8 @@ #if defined (__APPLE__) +#ifndef TARGET_OS_IOS + #import #import #import @@ -72,3 +74,5 @@ void NSUtilDisposeWindow(void* pWindow) } #endif + +#endif diff --git a/source/MaterialXRenderHw/WindowWrapper.cpp b/source/MaterialXRenderHw/WindowWrapper.cpp index 315960fb2b..654498d5cd 100644 --- a/source/MaterialXRenderHw/WindowWrapper.cpp +++ b/source/MaterialXRenderHw/WindowWrapper.cpp @@ -84,8 +84,12 @@ WindowWrapper::WindowWrapper(ExternalWindowHandle externalHandle, DisplayHandle display) { _externalHandle = externalHandle; +#ifndef TARGET_OS_IOS // Cache a pointer to the window. _internalHandle = NSUtilGetView(externalHandle); +#else + _internalHandle = nullptr; +#endif } WindowWrapper::~WindowWrapper() diff --git a/source/MaterialXRenderMsl/CMakeLists.txt b/source/MaterialXRenderMsl/CMakeLists.txt index 5ad50ea2cc..4d5e286463 100644 --- a/source/MaterialXRenderMsl/CMakeLists.txt +++ b/source/MaterialXRenderMsl/CMakeLists.txt @@ -1,3 +1,5 @@ +set(MATERIALX_MODULE_NAME MaterialXRenderMsl) + file(GLOB_RECURSE materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.m*") file(GLOB_RECURSE materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h*") @@ -9,8 +11,10 @@ if(POLICY CMP0072) endif() if(APPLE) +if(NOT MATERIALX_BUILD_IOS) find_library(COCOA_FRAMEWORK Cocoa) find_package(OpenGL REQUIRED) +endif() file(GLOB_RECURSE materialx_source_oc "${CMAKE_CURRENT_SOURCE_DIR}/*.m") message("Objective C files: " ${materialx_source_oc}) set_source_files_properties(${materialx_source_oc} PROPERTIES @@ -34,7 +38,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options(-Wno-deprecated-declarations) endif() -add_library(MaterialXRenderMsl ${materialx_source} ${materialx_headers}) +add_library(${MATERIALX_MODULE_NAME} ${materialx_source} ${materialx_headers}) add_definitions(-DMATERIALX_RENDERMSL_EXPORTS) @@ -42,23 +46,28 @@ set(COMMON_LIBRARIES MaterialXRenderHw MaterialXGenMsl ${CMAKE_DL_LIBS}) + +if(APPLE AND NOT MATERIALX_BUILD_IOS) +set(COMMON_LIBRARIES + ${COMMON_LIBRARIES} + "-framework Cocoa") +endif() if(MSVC) target_link_libraries( - MaterialXRenderMsl + ${MATERIALX_MODULE_NAME} ${COMMON_LIBRARIES} Opengl32) elseif(APPLE) target_link_libraries( - MaterialXRenderMsl + ${MATERIALX_MODULE_NAME} ${COMMON_LIBRARIES} ${OPENGL_LIBRARIES} "-framework Foundation" - "-framework Cocoa" "-framework Metal") elseif(UNIX) target_link_libraries( - MaterialXRenderMsl + ${MATERIALX_MODULE_NAME} ${COMMON_LIBRARIES} ${OPENGL_LIBRARIES} ${X11_LIBRARIES} @@ -66,29 +75,31 @@ elseif(UNIX) endif() set_target_properties( - MaterialXRenderMsl PROPERTIES - OUTPUT_NAME MaterialXRenderMsl${MATERIALX_LIBNAME_SUFFIX} + ${MATERIALX_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX} COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" VERSION "${MATERIALX_LIBRARY_VERSION}" SOVERSION "${MATERIALX_MAJOR_VERSION}") -target_include_directories(MaterialXRenderMsl +target_include_directories(${MATERIALX_MODULE_NAME} PUBLIC $ $ PRIVATE ${EXTERNAL_INCLUDE_DIRS}) -install(TARGETS MaterialXRenderMsl - EXPORT MaterialX - ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - RUNTIME DESTINATION bin) +if(NOT SKBUILD) + install(TARGETS ${MATERIALX_MODULE_NAME} + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + RUNTIME DESTINATION bin) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXRenderMsl/ MESSAGE_NEVER - FILES_MATCHING PATTERN "*.h*") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/${MATERIALX_MODULE_NAME}/ MESSAGE_NEVER + FILES_MATCHING PATTERN "*.h*") -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXRenderMsl.pdb" - DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${MATERIALX_MODULE_NAME}.pdb" + DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) +endif() diff --git a/source/MaterialXRenderMsl/MslMaterial.mm b/source/MaterialXRenderMsl/MslMaterial.mm index 7e9cd38adc..66fc0462f2 100644 --- a/source/MaterialXRenderMsl/MslMaterial.mm +++ b/source/MaterialXRenderMsl/MslMaterial.mm @@ -199,7 +199,7 @@ imageHandler->setFilenameResolver(resolver); // Acquire the given image. - return imageHandler->acquireImage(filePath); + return imageHandler->acquireImage(filePath, samplingProperties.defaultColor); } void MslMaterial::bindLighting(LightHandlerPtr lightHandler, diff --git a/source/MaterialXRenderMsl/MslPipelineStateObject.mm b/source/MaterialXRenderMsl/MslPipelineStateObject.mm index 84a615fd61..4f8378ae46 100644 --- a/source/MaterialXRenderMsl/MslPipelineStateObject.mm +++ b/source/MaterialXRenderMsl/MslPipelineStateObject.mm @@ -542,7 +542,7 @@ int GetStrideOfMetalType(MTLDataType type) { // Acquire the image. string error; - ImagePtr image = imageHandler->acquireImage(filePath); + ImagePtr image = imageHandler->acquireImage(filePath, samplingProperties.defaultColor); imageHandler->bindImage(image, samplingProperties); return bindTexture(renderCmdEncoder, uniformLocation, image, imageHandler); } diff --git a/source/MaterialXRenderMsl/MslRenderer.h b/source/MaterialXRenderMsl/MslRenderer.h index bf968078fe..ca87d9dd3e 100644 --- a/source/MaterialXRenderMsl/MslRenderer.h +++ b/source/MaterialXRenderMsl/MslRenderer.h @@ -82,6 +82,9 @@ class MX_RENDERMSL_API MslRenderer : public ShaderRenderer /// Validate inputs for the program void validateInputs() override; + /// Update the program with value of the uniform. + void updateUniform(const string& name, ConstValuePtr value) override; + /// Set the size of the rendered image void setSize(unsigned int width, unsigned int height) override; @@ -126,9 +129,6 @@ class MX_RENDERMSL_API MslRenderer : public ShaderRenderer protected: MslRenderer(unsigned int width, unsigned int height, Image::BaseType baseType); - - virtual void updateViewInformation(); - virtual void updateWorldInformation(); void triggerProgrammaticCapture(); void stopProgrammaticCapture(); @@ -146,11 +146,6 @@ class MX_RENDERMSL_API MslRenderer : public ShaderRenderer bool _initialized; - const Vector3 _eye; - const Vector3 _center; - const Vector3 _up; - float _objectScale; - SimpleWindowPtr _window; Color3 _screenColor; }; diff --git a/source/MaterialXRenderMsl/MslRenderer.mm b/source/MaterialXRenderMsl/MslRenderer.mm index f6de188b2f..f5e794c2b3 100644 --- a/source/MaterialXRenderMsl/MslRenderer.mm +++ b/source/MaterialXRenderMsl/MslRenderer.mm @@ -14,13 +14,6 @@ MATERIALX_NAMESPACE_BEGIN -const float PI = std::acos(-1.0f); - -// View information -const float FOV_PERSP = 45.0f; // degrees -const float NEAR_PLANE_PERSP = 0.05f; -const float FAR_PLANE_PERSP = 100.0f; - // // MslRenderer methods // @@ -36,20 +29,14 @@ } MslRenderer::MslRenderer(unsigned int width, unsigned int height, Image::BaseType baseType) : - ShaderRenderer(width, height, baseType), + ShaderRenderer(width, height, baseType, MatrixConvention::Metal), _initialized(false), - _eye(0.0f, 0.0f, 3.0f), - _center(0.0f, 0.0f, 0.0f), - _up(0.0f, 1.0f, 0.0f), - _objectScale(1.0f), _screenColor(DEFAULT_SCREEN_COLOR_LIN_REC709) { _program = MslProgram::create(); _geometryHandler = GeometryHandler::create(); _geometryHandler->addLoader(TinyObjLoader::create()); - - _camera = Camera::create(); } void MslRenderer::initialize(RenderContextHandle) @@ -137,6 +124,11 @@ _program->getAttributesList(); } +void MslRenderer::updateUniform(const string& name, ConstValuePtr value) +{ + _program->bindUniform(name, value); +} + void MslRenderer::createFrameBuffer(bool encodeSrgb) { _framebuffer = MetalFramebuffer::create(_device, @@ -160,20 +152,6 @@ } -void MslRenderer::updateViewInformation() -{ - float fH = std::tan(FOV_PERSP / 360.0f * PI) * NEAR_PLANE_PERSP; - float fW = fH * 1.0f; - - _camera->setViewMatrix(Camera::createViewMatrix(_eye, _center, _up)); - _camera->setProjectionMatrix(Camera::createPerspectiveMatrixZP(-fW, fW, -fH, fH, NEAR_PLANE_PERSP, FAR_PLANE_PERSP)); -} - -void MslRenderer::updateWorldInformation() -{ - _camera->setWorldMatrix(Matrix44::createScale(Vector3(_objectScale))); -} - void MslRenderer::triggerProgrammaticCapture() { MTLCaptureManager* captureManager = [MTLCaptureManager sharedCaptureManager]; @@ -217,9 +195,6 @@ [renderCmdEncoder setCullMode:MTLCullModeBack]; - - updateViewInformation(); - updateWorldInformation(); try { diff --git a/source/MaterialXRenderOsl/CMakeLists.txt b/source/MaterialXRenderOsl/CMakeLists.txt index a4ffa81235..ac1de93d2e 100644 --- a/source/MaterialXRenderOsl/CMakeLists.txt +++ b/source/MaterialXRenderOsl/CMakeLists.txt @@ -1,16 +1,24 @@ +set(MATERIALX_MODULE_NAME MaterialXRenderOsl) + file(GLOB_RECURSE materialx_source "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp") file(GLOB_RECURSE materialx_headers "${CMAKE_CURRENT_SOURCE_DIR}/*.h*") assign_source_group("Source Files" ${materialx_source}) assign_source_group("Header Files" ${materialx_headers}) -add_library(MaterialXRenderOsl ${materialx_source} ${materialx_headers}) +add_library(${MATERIALX_MODULE_NAME} ${materialx_source} ${materialx_headers}) add_definitions(-DMATERIALX_RENDEROSL_EXPORTS) +# Create version resource +if(MATERIALX_BUILD_SHARED_LIBS AND MSVC) + configure_file(${CMAKE_SOURCE_DIR}/cmake/modules/MaterialXVersion.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + target_sources(${MATERIALX_MODULE_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + set_target_properties( - MaterialXRenderOsl PROPERTIES - OUTPUT_NAME MaterialXRenderOsl${MATERIALX_LIBNAME_SUFFIX} + ${MATERIALX_MODULE_NAME} PROPERTIES + OUTPUT_NAME ${MATERIALX_MODULE_NAME}${MATERIALX_LIBNAME_SUFFIX} COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_SAME_DIR_RPATH}" @@ -18,26 +26,28 @@ set_target_properties( SOVERSION "${MATERIALX_MAJOR_VERSION}") target_link_libraries( - MaterialXRenderOsl + ${MATERIALX_MODULE_NAME} MaterialXRender ${CMAKE_DL_LIBS}) -target_include_directories(MaterialXRenderOsl +target_include_directories(${MATERIALX_MODULE_NAME} PUBLIC $ $ PRIVATE ${EXTERNAL_INCLUDE_DIRS}) -install(TARGETS MaterialXRenderOsl - EXPORT MaterialX - ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} - RUNTIME DESTINATION bin) +if(NOT SKBUILD) + install(TARGETS ${MATERIALX_MODULE_NAME} + EXPORT MaterialX + ARCHIVE DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + LIBRARY DESTINATION ${MATERIALX_INSTALL_LIB_PATH} + RUNTIME DESTINATION bin) -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" - DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/MaterialXRenderOsl/ MESSAGE_NEVER - FILES_MATCHING PATTERN "*.h*") + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION ${MATERIALX_INSTALL_INCLUDE_PATH}/${MATERIALX_MODULE_NAME}/ MESSAGE_NEVER + FILES_MATCHING PATTERN "*.h*") -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/MaterialXRenderOsl.pdb" - DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/${MATERIALX_MODULE_NAME}.pdb" + DESTINATION "${MATERIALX_INSTALL_LIB_PATH}/" OPTIONAL) +endif() diff --git a/source/MaterialXTest/MaterialXCore/Node.cpp b/source/MaterialXTest/MaterialXCore/Node.cpp index 2477d9f486..655ec5b7fb 100644 --- a/source/MaterialXTest/MaterialXCore/Node.cpp +++ b/source/MaterialXTest/MaterialXCore/Node.cpp @@ -136,6 +136,24 @@ TEST_CASE("Node", "[node]") REQUIRE(doc->getOutputs().empty()); } +TEST_CASE("Node inputCount repro", "[node]") +{ + // Create a document. + mx::DocumentPtr doc = mx::createDocument(); + mx::NodePtr constant = doc->addNode("constant"); + constant->setInputValue("value", 0.5f); + + // Check that input count is correct after clearContent + constant->clearContent(); + CHECK(constant->getInputCount() == 0); + + // Check that validate succeeds after clear and rebuild + constant->setType("float"); + mx::OutputPtr output = doc->addOutput(mx::EMPTY_STRING, "float"); + output->setConnectedNode(constant); + CHECK(doc->validate()); +} + TEST_CASE("Flatten", "[nodegraph]") { // Read an example containing graph-based custom nodes. diff --git a/source/MaterialXTest/MaterialXRenderMsl/RenderMsl.mm b/source/MaterialXTest/MaterialXRenderMsl/RenderMsl.mm index f33e793690..248d458542 100644 --- a/source/MaterialXTest/MaterialXRenderMsl/RenderMsl.mm +++ b/source/MaterialXTest/MaterialXRenderMsl/RenderMsl.mm @@ -78,7 +78,7 @@ void runBake(mx::DocumentPtr doc, const mx::FileSearchPath& imageSearchPath, con void MslShaderRenderTester::loadAdditionalLibraries(mx::DocumentPtr document, GenShaderUtil::TestSuiteOptions& options) { - mx::FilePath lightDir = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/TestSuite/lights"); + mx::FilePath lightDir = mx::getDefaultDataSearchPath().find("resources/Materials/TestSuite/lights"); for (const auto& lightFile : options.lightFiles) { loadLibrary(lightDir / mx::FilePath(lightFile), document); @@ -135,6 +135,7 @@ void runBake(mx::DocumentPtr doc, const mx::FileSearchPath& imageSearchPath, con mx::StbImageLoaderPtr stbLoader = mx::StbImageLoader::create(); mx::ImageHandlerPtr imageHandler = _renderer->createImageHandler(stbLoader); + imageHandler->setSearchPath(mx::getDefaultDataSearchPath()); #if defined(MATERIALX_BUILD_OIIO) mx::OiioImageLoaderPtr oiioLoader = mx::OiioImageLoader::create(); imageHandler->addLoader(oiioLoader); @@ -179,6 +180,7 @@ void runBake(mx::DocumentPtr doc, const mx::FileSearchPath& imageSearchPath, con std::cout << "Validating MSL rendering for: " << doc->getSourceUri() << std::endl; mx::ScopedTimer totalMSLTime(&profileTimes.languageTimes.totalTime); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); const mx::ShaderGenerator& shadergen = context.getShaderGenerator(); @@ -277,7 +279,7 @@ void runBake(mx::DocumentPtr doc, const mx::FileSearchPath& imageSearchPath, con { if (!testOptions.renderGeometry.isAbsolute()) { - geomPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Geometry") / testOptions.renderGeometry; + geomPath = searchPath.find("resources/Geometry") / testOptions.renderGeometry; } else { @@ -286,7 +288,7 @@ void runBake(mx::DocumentPtr doc, const mx::FileSearchPath& imageSearchPath, con } else { - geomPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Geometry/sphere.obj"); + geomPath = searchPath.find("resources/Geometry/sphere.obj"); } if (!geomHandler->hasGeometry(geomPath)) @@ -373,7 +375,7 @@ void runBake(mx::DocumentPtr doc, const mx::FileSearchPath& imageSearchPath, con { { mx::ScopedTimer renderTimer(&profileTimes.languageTimes.renderTime); - _renderer->getImageHandler()->setSearchPath(imageSearchPath); + _renderer->getImageHandler()->setSearchPath(mx::getDefaultDataSearchPath()); _renderer->setSize(static_cast(testOptions.renderSize[0]), static_cast(testOptions.renderSize[1])); _renderer->render(); } @@ -456,18 +458,10 @@ void runBake(mx::DocumentPtr doc, const mx::FileSearchPath& imageSearchPath, con TEST_CASE("Render: MSL TestSuite", "[rendermsl]") { - MslShaderRenderTester renderTester(mx::MslShaderGenerator::create()); - - const mx::FilePath testRootPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/TestSuite"); - const mx::FilePath testRootPath2 = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/Examples/StandardSurface"); - const mx::FilePath testRootPath3 = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/Examples/UsdPreviewSurface"); - mx::FilePathVec testRootPaths; - testRootPaths.push_back(testRootPath); - testRootPaths.push_back(testRootPath2); - testRootPaths.push_back(testRootPath3); - - mx::FilePath optionsFilePath = testRootPath / mx::FilePath("_options.mtlx"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); + MslShaderRenderTester renderTester(mx::MslShaderGenerator::create()); renderTester.validate(optionsFilePath); } diff --git a/source/MaterialXView/Viewer.cpp b/source/MaterialXView/Viewer.cpp index ecbb4352fc..6346658d07 100644 --- a/source/MaterialXView/Viewer.cpp +++ b/source/MaterialXView/Viewer.cpp @@ -474,7 +474,7 @@ void Viewer::loadEnvironmentLight() } // Look for an irradiance map using an expected filename convention. - mx::ImagePtr envIrradianceMap = _imageHandler->getInvalidImage(); + mx::ImagePtr envIrradianceMap = _imageHandler->getZeroImage(); if (!_normalizeEnvironment && !_splitDirectLight) { mx::FilePath envIrradiancePath = _envRadianceFilename.getParentPath() / IRRADIANCE_MAP_FOLDER / _envRadianceFilename.getBaseName(); @@ -482,7 +482,7 @@ void Viewer::loadEnvironmentLight() } // If not found, then generate an irradiance map via spherical harmonics. - if (envIrradianceMap == _imageHandler->getInvalidImage()) + if (envIrradianceMap == _imageHandler->getZeroImage()) { if (_generateReferenceIrradiance) { diff --git a/source/PyMaterialX/PyMaterialXCore/CMakeLists.txt b/source/PyMaterialX/PyMaterialXCore/CMakeLists.txt index dfc2aefac0..9f3d2291c0 100644 --- a/source/PyMaterialX/PyMaterialXCore/CMakeLists.txt +++ b/source/PyMaterialX/PyMaterialXCore/CMakeLists.txt @@ -14,8 +14,6 @@ set_target_properties( COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_UP_TWO_RPATH}" - VERSION "${MATERIALX_LIBRARY_VERSION}" - SOVERSION "${MATERIALX_MAJOR_VERSION}" DEBUG_POSTFIX "${MATERIALX_PYTHON_DEBUG_POSTFIX}") target_link_libraries( @@ -24,4 +22,4 @@ target_link_libraries( PRIVATE ${CMAKE_DL_LIBS}) install(TARGETS PyMaterialXCore - DESTINATION "python/MaterialX") + DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}") diff --git a/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp b/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp index 2a4645d3b6..2e9b264381 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyInterface.cpp @@ -93,6 +93,7 @@ void bindPyInterface(py::module& mod) .def("getDefaultVersion", &mx::InterfaceElement::getDefaultVersion) .def("getDeclaration", &mx::InterfaceElement::getDeclaration, py::arg("target") = mx::EMPTY_STRING) + .def("clearContent", &mx::InterfaceElement::clearContent) .def("hasExactInputMatch", &mx::InterfaceElement::hasExactInputMatch, py::arg("declaration"), py::arg("message") = nullptr) BIND_INTERFACE_TYPE_INSTANCE(integer, int) diff --git a/source/PyMaterialX/PyMaterialXCore/PyNode.cpp b/source/PyMaterialX/PyMaterialXCore/PyNode.cpp index 704cda7589..55448dee80 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyNode.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyNode.cpp @@ -59,6 +59,7 @@ void bindPyNode(py::module& mod) .def("addInterfaceName", &mx::NodeGraph::addInterfaceName) .def("removeInterfaceName", &mx::NodeGraph::removeInterfaceName) .def("modifyInterfaceName", &mx::NodeGraph::modifyInterfaceName) + .def("getDownstreamPorts", &mx::NodeGraph::getDownstreamPorts) .def_readonly_static("CATEGORY", &mx::NodeGraph::CATEGORY); py::class_(mod, "Backdrop") diff --git a/source/PyMaterialX/PyMaterialXFormat/CMakeLists.txt b/source/PyMaterialX/PyMaterialXFormat/CMakeLists.txt index 9566e792fd..095b3eb087 100644 --- a/source/PyMaterialX/PyMaterialXFormat/CMakeLists.txt +++ b/source/PyMaterialX/PyMaterialXFormat/CMakeLists.txt @@ -14,8 +14,6 @@ set_target_properties( COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_UP_TWO_RPATH}" - VERSION "${MATERIALX_LIBRARY_VERSION}" - SOVERSION "${MATERIALX_MAJOR_VERSION}" DEBUG_POSTFIX "${MATERIALX_PYTHON_DEBUG_POSTFIX}") target_link_libraries( @@ -25,4 +23,4 @@ target_link_libraries( PRIVATE ${CMAKE_DL_LIBS}) install(TARGETS PyMaterialXFormat - DESTINATION "python/MaterialX") + DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}") diff --git a/source/PyMaterialX/PyMaterialXGenGlsl/CMakeLists.txt b/source/PyMaterialX/PyMaterialXGenGlsl/CMakeLists.txt index b65ef857a4..24266712fd 100644 --- a/source/PyMaterialX/PyMaterialXGenGlsl/CMakeLists.txt +++ b/source/PyMaterialX/PyMaterialXGenGlsl/CMakeLists.txt @@ -14,8 +14,6 @@ set_target_properties( COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_UP_TWO_RPATH}" - VERSION "${MATERIALX_LIBRARY_VERSION}" - SOVERSION "${MATERIALX_MAJOR_VERSION}" DEBUG_POSTFIX "${MATERIALX_PYTHON_DEBUG_POSTFIX}") target_link_libraries( @@ -25,4 +23,4 @@ target_link_libraries( PRIVATE ${CMAKE_DL_LIBS}) install(TARGETS PyMaterialXGenGlsl - DESTINATION "python/MaterialX") + DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}") diff --git a/source/PyMaterialX/PyMaterialXGenMdl/CMakeLists.txt b/source/PyMaterialX/PyMaterialXGenMdl/CMakeLists.txt index a1bc225a4f..328a2439ed 100644 --- a/source/PyMaterialX/PyMaterialXGenMdl/CMakeLists.txt +++ b/source/PyMaterialX/PyMaterialXGenMdl/CMakeLists.txt @@ -14,8 +14,6 @@ set_target_properties( COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_UP_TWO_RPATH}" - VERSION "${MATERIALX_LIBRARY_VERSION}" - SOVERSION "${MATERIALX_MAJOR_VERSION}" DEBUG_POSTFIX "${MATERIALX_PYTHON_DEBUG_POSTFIX}") target_link_libraries( @@ -24,4 +22,4 @@ target_link_libraries( PRIVATE ${CMAKE_DL_LIBS}) install(TARGETS PyMaterialXGenMdl - DESTINATION "python/MaterialX") + DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}") diff --git a/source/PyMaterialX/PyMaterialXGenMsl/CMakeLists.txt b/source/PyMaterialX/PyMaterialXGenMsl/CMakeLists.txt index 88e21687bc..33dec7319a 100644 --- a/source/PyMaterialX/PyMaterialXGenMsl/CMakeLists.txt +++ b/source/PyMaterialX/PyMaterialXGenMsl/CMakeLists.txt @@ -14,8 +14,6 @@ set_target_properties( COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_UP_TWO_RPATH}" - VERSION "${MATERIALX_LIBRARY_VERSION}" - SOVERSION "${MATERIALX_MAJOR_VERSION}" DEBUG_POSTFIX "${MATERIALX_PYTHON_DEBUG_POSTFIX}") target_link_libraries( @@ -25,4 +23,4 @@ target_link_libraries( PRIVATE ${CMAKE_DL_LIBS}) install(TARGETS PyMaterialXGenMsl - DESTINATION "python/MaterialX") + DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}") diff --git a/source/PyMaterialX/PyMaterialXGenOsl/CMakeLists.txt b/source/PyMaterialX/PyMaterialXGenOsl/CMakeLists.txt index 5d30769b4d..65b0dabe67 100644 --- a/source/PyMaterialX/PyMaterialXGenOsl/CMakeLists.txt +++ b/source/PyMaterialX/PyMaterialXGenOsl/CMakeLists.txt @@ -14,8 +14,6 @@ set_target_properties( COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_UP_TWO_RPATH}" - VERSION "${MATERIALX_LIBRARY_VERSION}" - SOVERSION "${MATERIALX_MAJOR_VERSION}" DEBUG_POSTFIX "${MATERIALX_PYTHON_DEBUG_POSTFIX}") target_link_libraries( @@ -25,4 +23,4 @@ target_link_libraries( PRIVATE ${CMAKE_DL_LIBS}) install(TARGETS PyMaterialXGenOsl - DESTINATION "python/MaterialX") + DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}") diff --git a/source/PyMaterialX/PyMaterialXGenShader/CMakeLists.txt b/source/PyMaterialX/PyMaterialXGenShader/CMakeLists.txt index 13c01b3351..5bee142c24 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/CMakeLists.txt +++ b/source/PyMaterialX/PyMaterialXGenShader/CMakeLists.txt @@ -14,8 +14,6 @@ set_target_properties( COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_UP_TWO_RPATH}" - VERSION "${MATERIALX_LIBRARY_VERSION}" - SOVERSION "${MATERIALX_MAJOR_VERSION}" DEBUG_POSTFIX "${MATERIALX_PYTHON_DEBUG_POSTFIX}") target_link_libraries( @@ -26,4 +24,4 @@ target_link_libraries( PRIVATE ${CMAKE_DL_LIBS}) install(TARGETS PyMaterialXGenShader - DESTINATION "python/MaterialX") + DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}") diff --git a/source/PyMaterialX/PyMaterialXRender/CMakeLists.txt b/source/PyMaterialX/PyMaterialXRender/CMakeLists.txt index a050cb6aa3..2617a0b392 100644 --- a/source/PyMaterialX/PyMaterialXRender/CMakeLists.txt +++ b/source/PyMaterialX/PyMaterialXRender/CMakeLists.txt @@ -17,8 +17,6 @@ set_target_properties( COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_UP_TWO_RPATH}" - VERSION "${MATERIALX_LIBRARY_VERSION}" - SOVERSION "${MATERIALX_MAJOR_VERSION}" DEBUG_POSTFIX "${MATERIALX_PYTHON_DEBUG_POSTFIX}") target_link_libraries( @@ -28,4 +26,4 @@ target_link_libraries( PRIVATE ${CMAKE_DL_LIBS}) install(TARGETS PyMaterialXRender - DESTINATION "python/MaterialX") + DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}") diff --git a/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp b/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp index 7ece49d60b..d261bc7306 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp @@ -41,7 +41,8 @@ void bindPyImageHandler(py::module& mod) .def("addLoader", &mx::ImageHandler::addLoader) .def("saveImage", &mx::ImageHandler::saveImage, py::arg("filePath"), py::arg("image"), py::arg("verticalFlip") = false) - .def("acquireImage", &mx::ImageHandler::acquireImage) + .def("acquireImage", &mx::ImageHandler::acquireImage, + py::arg("filePath"), py::arg("defaultColor") = mx::Color4(0.0f)) .def("bindImage", &mx::ImageHandler::bindImage) .def("unbindImage", &mx::ImageHandler::unbindImage) .def("unbindImages", &mx::ImageHandler::unbindImages) @@ -54,6 +55,5 @@ void bindPyImageHandler(py::module& mod) py::arg("image") = nullptr) .def("clearImageCache", &mx::ImageHandler::clearImageCache) .def("getZeroImage", &mx::ImageHandler::getZeroImage) - .def("getInvalidImage", &mx::ImageHandler::getInvalidImage) .def("getReferencedImages", &mx::ImageHandler::getReferencedImages); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp b/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp index c67238dea5..94c0d49cee 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp @@ -25,6 +25,7 @@ void bindPyShaderRenderer(py::module& mod) .def("createProgram", static_cast(&mx::ShaderRenderer::createProgram)) .def("createProgram", static_cast(&mx::ShaderRenderer::createProgram)) .def("validateInputs", &mx::ShaderRenderer::validateInputs) + .def("updateUniform", &mx::ShaderRenderer::updateUniform) .def("setSize", &mx::ShaderRenderer::setSize) .def("render", &mx::ShaderRenderer::render); diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/CMakeLists.txt b/source/PyMaterialX/PyMaterialXRenderGlsl/CMakeLists.txt index a29cdc5c1e..a0b04ddbda 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/CMakeLists.txt +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/CMakeLists.txt @@ -14,8 +14,6 @@ set_target_properties( COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_UP_TWO_RPATH}" - VERSION "${MATERIALX_LIBRARY_VERSION}" - SOVERSION "${MATERIALX_MAJOR_VERSION}" DEBUG_POSTFIX "${MATERIALX_PYTHON_DEBUG_POSTFIX}") target_link_libraries( @@ -27,4 +25,4 @@ target_link_libraries( PRIVATE ${CMAKE_DL_LIBS}) install(TARGETS PyMaterialXRenderGlsl - DESTINATION "python/MaterialX") + DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}") diff --git a/source/PyMaterialX/PyMaterialXRenderMsl/CMakeLists.txt b/source/PyMaterialX/PyMaterialXRenderMsl/CMakeLists.txt index ec562fe8cf..04c6d95e3c 100644 --- a/source/PyMaterialX/PyMaterialXRenderMsl/CMakeLists.txt +++ b/source/PyMaterialX/PyMaterialXRenderMsl/CMakeLists.txt @@ -14,8 +14,6 @@ set_target_properties( COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_UP_TWO_RPATH}" - VERSION "${MATERIALX_LIBRARY_VERSION}" - SOVERSION "${MATERIALX_MAJOR_VERSION}" DEBUG_POSTFIX "${MATERIALX_PYTHON_DEBUG_POSTFIX}") target_link_libraries( @@ -27,4 +25,4 @@ target_link_libraries( PRIVATE ${CMAKE_DL_LIBS}) install(TARGETS PyMaterialXRenderMsl - DESTINATION "python/MaterialX") + DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}") diff --git a/source/PyMaterialX/PyMaterialXRenderOsl/CMakeLists.txt b/source/PyMaterialX/PyMaterialXRenderOsl/CMakeLists.txt index 4777b13e7b..c0b83948a9 100644 --- a/source/PyMaterialX/PyMaterialXRenderOsl/CMakeLists.txt +++ b/source/PyMaterialX/PyMaterialXRenderOsl/CMakeLists.txt @@ -14,8 +14,6 @@ set_target_properties( COMPILE_FLAGS "${EXTERNAL_COMPILE_FLAGS}" LINK_FLAGS "${EXTERNAL_LINK_FLAGS}" INSTALL_RPATH "${MATERIALX_UP_TWO_RPATH}" - VERSION "${MATERIALX_LIBRARY_VERSION}" - SOVERSION "${MATERIALX_MAJOR_VERSION}" DEBUG_POSTFIX "${MATERIALX_PYTHON_DEBUG_POSTFIX}") target_link_libraries( @@ -25,4 +23,4 @@ target_link_libraries( PRIVATE ${CMAKE_DL_LIBS}) install(TARGETS PyMaterialXRenderOsl - DESTINATION "python/MaterialX") + DESTINATION "${MATERIALX_PYTHON_FOLDER_NAME}")