diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 8e68529175..fecdbf7a52 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -59,8 +59,14 @@ jobs: compiler: clang compiler_version: "14" python: 3.11 - cmake_config: -DMATERIALX_DYNAMIC_ANALYSIS=ON clang_format: ON + + - name: Linux_Clang_14_DynamicAnalysis + os: ubuntu-22.04 + compiler: clang + compiler_version: "14" + python: None + cmake_config: -DMATERIALX_DYNAMIC_ANALYSIS=ON dynamic_analysis: ON - name: MacOS_Xcode_11_Python37 @@ -77,9 +83,9 @@ jobs: python: 3.9 - name: MacOS_Xcode_14_Python311 - os: macos-12 + os: macos-13 compiler: xcode - compiler_version: "14.2" + compiler_version: "14.3" python: 3.11 test_shaders: ON @@ -147,6 +153,7 @@ jobs: Add-Content $env:GITHUB_PATH "$PWD/vcpkg/installed/x64-windows/tools" - name: Install Python ${{ matrix.python }} + if: matrix.python != 'None' uses: actions/setup-python@v4 with: python-version: ${{ matrix.python }} @@ -192,11 +199,11 @@ jobs: working-directory: build - name: Python Tests - if: matrix.dynamic_analysis != 'ON' + if: matrix.python != 'None' run: | python MaterialXTest/main.py python MaterialXTest/genshader.py - python Scripts/mxformat.py ../resources/Materials/TestSuite/stdlib/upgrade --yes --upgradeVersion True + python Scripts/mxformat.py ../resources/Materials/TestSuite/stdlib/upgrade --yes --upgrade python Scripts/mxvalidate.py ../resources/Materials/Examples/StandardSurface/standard_surface_marble_solid.mtlx --stdlib --verbose python Scripts/mxdoc.py --docType md ../libraries/pbrlib/pbrlib_defs.mtlx python Scripts/mxdoc.py --docType html ../libraries/bxdf/standard_surface.mtlx @@ -249,7 +256,7 @@ jobs: working-directory: build/render - name: Upload Installed Package - if: matrix.dynamic_analysis != 'ON' + if: matrix.python != 'None' uses: actions/upload-artifact@v3 with: name: MaterialX_${{ matrix.name }} diff --git a/documents/Specification/inprog_v1.39/MaterialX.GeomExts.md b/documents/Specification/MaterialX.GeomExts.md similarity index 100% rename from documents/Specification/inprog_v1.39/MaterialX.GeomExts.md rename to documents/Specification/MaterialX.GeomExts.md diff --git a/documents/Specification/inprog_v1.39/MaterialX.PBRSpec.md b/documents/Specification/MaterialX.PBRSpec.md similarity index 99% rename from documents/Specification/inprog_v1.39/MaterialX.PBRSpec.md rename to documents/Specification/MaterialX.PBRSpec.md index 58138c8308..9a9fc7527a 100644 --- a/documents/Specification/inprog_v1.39/MaterialX.PBRSpec.md +++ b/documents/Specification/MaterialX.PBRSpec.md @@ -37,7 +37,7 @@ This document describes a number of shader-semantic nodes implementing widely-us  [BSDF Nodes](#bsdf-nodes)  [EDF Nodes](#edf-nodes)  [VDF Nodes](#vdf-nodes) - [Shader Nodes](#shader-nodes) + [PBR Shader Nodes](#pbr-shader-nodes)  [Utility Nodes](#utility-nodes) **[Shading Model Examples](#shading-model-examples)** @@ -303,7 +303,7 @@ The PBS nodes also make use of the following standard MaterialX types: * `anisotropy` (float): Anisotropy factor, controlling the scattering direction, range [-1.0, 1.0]. Negative values give backwards scattering, positive values give forward scattering, and a value of 0.0 (the default) gives uniform scattering. -## Shader Nodes +## PBR Shader Nodes @@ -334,6 +334,9 @@ The PBS nodes also make use of the following standard MaterialX types: * `intensity` (color3): Intensity multiplier for the light's emittance. Defaults to (1.0, 1.0, 1.0). * `exposure` (float): Exposure control for the light's emittance. Defaults to 0.0. +Note that the standard library includes definitions for [**`displacement`**](./MaterialX.Specification.md#node-displacement) and [**`surface_unlit`**](./MaterialX.Specification.md#node-surfaceunlit) shader nodes. + + ## Utility Nodes diff --git a/documents/Specification/inprog_v1.39/MaterialX.Specification.md b/documents/Specification/MaterialX.Specification.md similarity index 88% rename from documents/Specification/inprog_v1.39/MaterialX.Specification.md rename to documents/Specification/MaterialX.Specification.md index c0dc5c9b48..3a90a269fb 100644 --- a/documents/Specification/inprog_v1.39/MaterialX.Specification.md +++ b/documents/Specification/MaterialX.Specification.md @@ -64,7 +64,6 @@ This document describes the core MaterialX specification. Companion documents [   [Conditional Nodes](#conditional-nodes)   [Channel Nodes](#channel-nodes)   [Convolution Nodes](#convolution-nodes) -  [Shader Nodes](#shader-nodes)  [Standard Node Inputs](#standard-node-inputs)  [Standard UI Attributes](#standard-ui-attributes) @@ -90,7 +89,7 @@ This document describes the core MaterialX specification. Companion documents [    [Example Custom Node Defined by a Nodegraph](#example-custom-node-defined-by-a-nodegraph)   [Custom Node Use](#custom-node-use)  [Shader Nodes](#shader-nodes) -  [Standard Shader-Semantic Operator Nodes](#standard-shader-semantic-operator-nodes) +  [Standard Library Shader Nodes](#standard-library-shader-nodes)   [AOV Output Elements](#aov-output-elements)    [AOVOutput Example](#aovoutput-example)  [Material Nodes](#material-nodes) @@ -339,6 +338,8 @@ By default, MaterialX supports the following color spaces as defined in ACES 1.2 * `lin_srgb` * `adobergb` * `lin_adobergb` +* `srgb_displayp3` +* `lin_displayp3` The working color space of a MaterialX document is defined by the `colorspace` attribute of its root <materialx> element, and it is strongly recommended that all <materialx> elements define a specific `colorspace` if they wish to use a color-managed workflow rather than relying on a default colorspace setting from an external configuration file. @@ -549,7 +550,7 @@ where _nodecategory_ is the general "category" of the node (e.g. "image", "add" Node elements may optionally specify a `version` string attribute in "_major_[._minor_]" format, requesting that a specific version of that node's definition be used instead of the default version. Normally, the types of a node's inputs and outputs are sufficient to disambiguate which signature of the applicable version of a node is intended, but if necessary, a node instantiation may also declare a specific nodedef name to precisely define exactly which node signature is desired. Please refer to the [Custom Node Declaration NodeDef Elements](#custom-node-declaration-nodedef-elements) section below for further details. -MaterialX defines a number of Standard Nodes which all implementations should support as described to the degree their architecture and capabilities allow. One can define new nodes by declaring their parameter interfaces and providing portable or target-specific implementations. Please see the [Custom Nodes](#custom-nodes) section for notes and implementation details. +MaterialX defines a number of Standard Nodes which all implementations should support as described to the degree their architecture and capabilities allow. These standard nodes are grouped into [Standard Source Nodes](#standard-source-nodes) and [Standard Operator Nodes](#standard-operator-nodes); these groups are further divided into additional subcategories of nodes. In the descriptions below, a node with an "(NG)" annotation indicates a node that is implemented using a nodegraph in the MaterialX distribution, while unannotated nodes are implemented natively in the various renderer shading languages. One can define new nodes by declaring their parameter interfaces and providing portable nodegraph or target-specific shading language implementations. Please see the [Custom Nodes](#custom-nodes) section for notes and implementation details. @@ -618,13 +619,14 @@ Attributes for Output elements: * `nodename` (string, optional): the name of a node at the same scope within the document, whose result value will be output. This attribute is required for <output> elements within a node graph, but is not allowed in <output> elements within a <nodedef>. * `output` (string, optional): if the node specified by `nodename` has multiple outputs, the name of the specific output to connect this <output> to. * `uniform` (boolean, optional): If set to "true", then the output of this node is treated as a uniform value, and this output may be connected to a uniform input of the same (or compatible) type. It is up to the application creating the nodegraph to ensure that the value actually is uniform. Default is "false". + +MaterialX also supports the following additional attributes for Output elements in applications which process node graphs in 2D space and save or cache outputs as images for efficiency, such as texture baking or image caching. These attributes do **not** affect values from this <output> connected to other nodes, e.g. they would remain in the working colorspace and retain full resolution and bitdepth precision. + * `colorspace` (string, optional): the name of the color space for the output image. Applications that support color space management are expected to perform the required transformations of output colors into this space. * `width` (integer, optional): the expected width in pixels of the output image. * `height` (integer, optional): the expected height in pixels of the output image. * `bitdepth` (integer, optional): the expected per-channel bit depth of the output image, which may be used to capture expected color quantization effects. Common values for `bitdepth` are 8, 16, 32, and 64. It is up to the application to determine what the internal representation of any declared bit depth is (e.g. scaling factor, signed or unsigned, etc.). -The `colorspace`, `width`, `height` and `bitdepth` attributes are intended to be used in applications which process node graphs in 2D space and save or cache outputs as images for efficiency. - ## Standard Source Nodes @@ -662,10 +664,37 @@ Standard Texture nodes: * `vaddressmode` (uniform string): determines how V coordinates outside the 0-1 range are processed before sampling the image; see below. Default is "periodic". * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. + + +* **`tiledimage`** (NG): samples data from a single image, with provisions for tiling and offsetting the image across uv space. + * `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. + * `default` (float or colorN or vectorN): a default value to use if the `file` reference can not be resolved (e.g. if a <geomtoken>, [interfacetoken] 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. + * `texcoord` (vector2): the name of a vector2-type node specifying the 2D texture coordinate at which the image data is read. Default is to use the current u,v coordinate. + * `uvtiling` (vector2): the tiling rate for the given image along the U and V axes. Mathematically equivalent to multiplying the incoming texture coordinates by the given vector value. Default value is (1.0, 1.0). + * `uvoffset` (vector2): the offset for the given image along the U and V axes. Mathematically equivalent to subtracting the given vector value from the incoming texture coordinates. Default value is (0.0, 0.0). + * `realworldimagesize` (vector2): the real-world size represented by the `file` image, with unittype "distance". A `unit` attribute may be provided to indicate the units that `realworldimagesize` is expressed in. + * `realworldtilesize` (vector2): the real-world size of a single square 0-1 UV tile, with unittype "distance". A `unit` attribute may be provided to indicate the units that `realworldtilesize` is expressed in. + * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. + + + +* **`triplanarprojection`** (NG): samples data from three images (or layers within multi-layer images), and projects a tiled representation of the images along each of the three respective coordinate axes, computing a weighted blend of the three samples using the geometric normal. + * `filex` (uniform filename): the URI of an image file to be projected in the direction from the +X axis back toward the origin. + * `filey` (uniform filename): the URI of an image file to be projected in the direction from the +Y axis back toward the origin with the +X axis to the right. + * `filez` (uniform filename): the URI of an image file to be projected in the direction from the +Z axis back toward the origin. + * `layerx` (uniform string): the name of the layer to extract from a multi-layer input file for the x-axis projection. If no value for `layerx` 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. + * `layery` (uniform string): the name of the layer to extract from a multi-layer input file for the y-axis projection. + * `layerz` (uniform string): the name of the layer to extract from a multi-layer input file for the z-axis projection. + * `default` (float or colorN or vectorN): a default value to use if any `fileX` reference can not be resolved (e.g. if a <geomtoken>, [interfacetoken] or {hostattr} is included in the filename but no substitution value or default is defined, or if the resolved file URI cannot be read) 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. + * `position` (vector3): a spatially-varying input specifying the 3D position at which the projection is evaluated. Default is to use the current 3D object-space coordinate. + * `normal` (vector3): a spatially-varying input specifying the 3D normal vector used for blending. Default is to use the current object-space surface normal. + * `blend` (float): a 0-1 weighting factor for blending the three axis samples using the geometric normal, with higher values giving softer blending. Default is 1.0. + * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. + -The following values are supported by `uaddressmode` and `vaddressmode` inputs: +The following values are supported by `uaddressmode` and `vaddressmode` inputs of [image](#node-image) nodes: * “constant”: Texture coordinates outside the 0-1 range return the value of the node's `default` input. * “clamp”: Texture coordinates are clamped to the 0-1 range before sampling the image. @@ -685,8 +714,6 @@ Texture nodes using `file*` inputs also support the following inputs to handle b Arbitrary frame number expressions and speed changes are not supported. -Additional texture nodes, including **``** and **``**, may be found in the [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md#supplemental-texture-nodes) document. - ### Procedural Nodes @@ -729,6 +756,15 @@ Standard Procedural nodes: * `valueb` (float or colorN or vectorN): the value at the bottom (V=0) edge * `texcoord` (vector2): the name of a vector2-type node specifying the 2D texture coordinate at which the ramp interpolation is evaluated. Default is to use the first set of texture coordinates. + + +* **`ramp4`** (NG): a 4-corner bilinear value ramp. + * `valuetl` (float or colorN or vectorN): the value at the top-left (U0V1) corner + * `valuetr` (float or colorN or vectorN): the value at the top-right (U1V1) corner + * `valuebl` (float or colorN or vectorN): the value at the bottom-left (U0V0) corner + * `valuebr` (float or colorN or vectorN): the value at the bottom-right (U1V0) corner + * `texcoord` (vector2, optional): the name of a vector2-type node specifying the 2D texture coordinate at which the ramp interpolation is evaluated. Default is to use the first set of texture coordinates. + * **`splitlr`**: a left-right split matte, split at a specified U value. @@ -745,6 +781,15 @@ Standard Procedural nodes: * `center` (float): a value representing the V-coordinate of the split; all pixels above "center" will be `valuet`, all pixels below "center" will be `valueb`. Default is 0.5. * `texcoord` (vector2): the name of a vector2-type node specifying the 2D texture coordinate at which the split position is evaluated. Default is to use the first set of texture coordinates. + + +* **`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). + * `texcoord` (vector2): The input 2d space. Default is the first texture coordinates. + * **`noise2d`**: 2D Perlin noise in 1, 2, 3 or 4 channels. @@ -815,10 +860,38 @@ Standard Procedural nodes: * `jitter` (float): amount to jitter the cell center position, with smaller values creating a more regular pattern. Default is 1.0. * `position` (vector3): the 3D position at which the noise is evaluated. Default is to use the current 3D object-space coordinate. + + +* **`unifiednoise2d`** (NG): a single node supporting 2D Perlin, Cell, Worley or Fractal noise in a unified interface. + * `type` (integer): The type of noise function to use. One of 0 (Perlin), 1 (Cell), 2 (Worley), or 3 (Fractal); default is Perlin. + * `texcoord` (vector2): the input 2d space. Default is the first texture coordinates. + * `freq` (vector2): Adjusts the noise frequency, with higher values producing smaller noise shapes. Default is (1,1). + * `offset` (vector2): Shift the noise in 2d space. Default is (0,0). + * `jitter` (float): Adjust uniformity of Worley noise; for other noise types jitters the results. + * `outmin` (float): The lowest values fit to the noise. Default is 0.0. + * `outmax` (float): The highest values fit to the noise. Default is 1.0. + * `clampoutput` (boolean): Clamp the output to the min and max output values. + * `octaves` (integer): The number of octaves of Fractal noise to be generated. Default is 3. + * `lacunarity` (float): The exponential scale between successive octaves of Fractal noise. Default is 2.0. + * `diminish` (float): The rate at which noise amplitude is diminished for each octave of Fractal noise. Default is 0.5. + + + +* **`unifiednoise3d`** (NG): a single node supporting 3D Perlin, Cell, Worley or Fractal noise in a unified interface. + * `type` (integer): The type of noise function to use. One of 0 (Perlin), 1 (Cell), 2 (Worley), or 3 (Fractal); default is Perlin. + * `position` (vector3): the input 3d space. Default is position in object-space. + * `freq` (vector3): Adjusts the noise frequency, with higher values producing smaller noise shapes. Default is (1,1,1). + * `offset` (vector3): Shift the noise in 3d space. Default is (0,0,0). + * `jitter` (float): Adjust uniformity of Worley noise; for other noise types jitters the results. + * `outmin` (float): The lowest values fit to the noise. Default is 0.0. + * `outmax` (float): The highest values fit to the noise. Default is 1.0. + * `clampoutput` (boolean): Clamp the output to the min and max output values. + * `octaves` (integer): The number of octaves of Fractal noise to be generated. Default is 3. + * `lacunarity` (float): The exponential scale between successive octaves of Fractal noise. Default is 2.0. + * `diminish` (float): The rate at which noise amplitude is diminished for each octave of Fractal noise. Default is 0.5. -To scale or offset the noise pattern generated by `noise3d`, `fractal3d` or `cellnoise3d`, use a <position> or other [Geometric Node](#geometric-nodes) (see below) connected to vector3 <multiply> and/or <add> nodes, in turn connected to the noise node's `position` input. To scale or offset `rampX`, `splitX`, `noise2d` or `cellnoise2d` input coordinates, use a <texcoord> or similar Geometric node processed by vector2 <multiply>, <rotate> and/or <add> nodes, and connect to the node's `texcoord` input. -Additional procedural nodes, including **``**, may be found in the [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md#supplemental-procedural-nodes) document. +To scale or offset the noise pattern generated by `noise3d`, `fractal3d` or `cellnoise3d`, use a <position> or other [Geometric Node](#geometric-nodes) (see below) connected to vector3 <multiply> and/or <add> nodes, in turn connected to the noise node's `position` input. To scale or offset `rampX`, `splitX`, `noise2d` or `cellnoise2d` input coordinates, use a <texcoord> or similar Geometric node processed by vector2 <multiply>, <rotate> and/or <add> nodes, and connect to the node's `texcoord` input. @@ -887,6 +960,8 @@ Standard Geometric nodes: * `geomprop` (uniform string): the geometric property to be referenced. * `default` (same type as the geomprop's value): a value to return if the specified `geomprop` is not defined on the current geometry. +Additionally, the `geomcolor`, `geompropvalue` and `geompropvalueuniform` nodes for color3/color4-type properties can take a `colorspace` attribute to declare what colorspace the color property value is in; the default is "none" for no colorspace declaration (and hence no colorspace conversion). + @@ -969,7 +1044,7 @@ Operator nodes process one or more required input streams to form an output. Li The inputs of compositing operators are called "fg" and "bg" (plus "alpha" for float and color3 variants, and "mix" for all variants of the `mix` operator), while the inputs of other operators are called "in" if there is exactly one input, or "in1", "in2" etc. if there are more than one input. If an implementation does not support a particular operator, it should pass through the "bg", "in" or "in1" input unchanged. -This section defines the Operator Nodes that all MaterialX implementations are expected to support. Standard Operator Nodes are grouped into the following classifications: [Math Nodes](#math-nodes), [Adjustment Nodes](#adjustment-nodes), [Compositing Nodes](#compositing-nodes), [Conditional Nodes](#conditional-nodes), [Channel Nodes](#channel-nodes), [Convolution Nodes](#convolution-nodes) and [Shader Nodes](#shader-nodes). +This section defines the Operator Nodes that all MaterialX implementations are expected to support. Standard Operator Nodes are grouped into the following classifications: [Math Nodes](#math-nodes), [Adjustment Nodes](#adjustment-nodes), [Compositing Nodes](#compositing-nodes), [Conditional Nodes](#conditional-nodes), [Channel Nodes](#channel-nodes) and [Convolution Nodes](#convolution-nodes). @@ -980,7 +1055,7 @@ Math nodes have one or two spatially-varying inputs, and are used to perform a m -* **`add`**: add a value to the incoming float/color/vector/matrix. See also the [Shader Nodes](#shader-nodes) section below for additional `add` variants supporting shader-semantic types. +* **`add`**: add a value to the incoming float/color/vector/matrix. * `in1` (float or colorN or vectorN or matrixNN): the value or nodename for the primary input * `in2` (same type as `in1` or float): the value or nodename to add; for matrix types, the default is the zero matrix. @@ -992,7 +1067,7 @@ Math nodes have one or two spatially-varying inputs, and are used to perform a m -* **`multiply`**: multiply an incoming float/color/vector/matrix by a value. Multiplication of two vectors is interpreted as a component-wise vector multiplication, while multiplication of two matrices is interpreted as a standard matrix product. To multiply a vector and a matrix, use one of the `transform*` nodes. See also the [Shader Nodes](#shader-nodes) section below for additional `multiply` variants supporting shader-semantic types. +* **`multiply`**: multiply an incoming float/color/vector/matrix by a value. Multiplication of two vectors is interpreted as a component-wise vector multiplication, while multiplication of two matrices is interpreted as a standard matrix product. To multiply a vector and a matrix, use one of the `transform*` nodes. * `in1` (float or colorN or vectorN or matrixNN): the value or nodename for the primary input * `in2` (same type as `in1` or float): the value or nodename to multiply by; default is 1.0 in all channels for float/color/vector types, or the identity matrix for matrix types. @@ -1020,24 +1095,30 @@ Math nodes have one or two spatially-varying inputs, and are used to perform a m -* **`floor`**: the per-channel nearest integer value less than or equal to the incoming float/color/vector; the output remains in floating point per-channel, i.e. the same type as the input. +* **`floor`**: the per-channel nearest integer value less than or equal to the incoming float/color/vector. The output remains in floating point per-channel, i.e. the same type as the input, except that the floor(float) also has a variant outputting an integer type. * `in` (float or colorN or vectorN): the input value or nodename -* **`ceil`**: the per-channel nearest integer value greater than or equal to the incoming float/color/vector; the output remains in floating point per-channel, i.e. the same type as the input. +* **`ceil`**: the per-channel nearest integer value greater than or equal to the incoming float/color/vector. The output remains in floating point per-channel, i.e. the same type as the input, except that the ceil(float) also has a variant outputting an integer type. * `in` (float or colorN or vectorN): the input value or nodename -* **`round`**: round each channel of the incoming float/color/vector values to the nearest integer value, e.g "floor(in+0.5)". +* **`round`**: round each channel of the incoming float/color/vector values to the nearest integer value, e.g "floor(in+0.5)"; the round(float) also has a variant outputting an integer type. * `in` (float or colorN or vectorN): the input value or nodename * **`power`**: raise incoming float/color values to the specified exponent, commonly used for "gamma" adjustment. * `in1` (float or colorN or vectorN): the value or nodename for the primary input - * `in2` (same type as `in` or float): exponent value or nodename; output = pow(in1, in2); default is 1.0 in all channels + * `in2` (same type as `in1` or float): exponent value or nodename; output = pow(in1, in2); default is 1.0 in all channels + + + +* **`safepower`** (NG): raise incoming float/color values to the specified exponent. Unlike the standard [<power>](#node-power) node, negative `in1` values for <safepower> will result in negative output values, e.g. `out = sign(in1)*pow(abs(in1),in2)`. + * `in1` (float or colorN or vectorN): the value or nodename for the primary input + * `in2` (same type as `in1` or float): exponent value or nodename; default is 1.0 in all channels @@ -1131,6 +1212,13 @@ Math nodes have one or two spatially-varying inputs, and are used to perform a m * **`magnitude`**: output the float magnitude (vector length) of the incoming vectorN stream; cannot be used on float or colorN streams. Note: the fourth channel in vector4 streams is not treated any differently, e.g. not as a homogeneous "w" value. * `in` (vectorN): the input value or nodename + + + +* **`distance`**: Measures the distance between two points in 2D, 3D, or 4D. + * `in1` (vectorN): the first input value or nodename + * `in2` (same type as `in1`): the second input value or nodename + * **`dotproduct`**: output the (float) dot product of two incoming vectorN streams; cannot be used on float or colorN streams. @@ -1222,19 +1310,45 @@ Math nodes have one or two spatially-varying inputs, and are used to perform a m * `amount` (float): the amount to rotate, specified in degrees; default is 0. * `axis` (vector3): For vector3 inputs only, the unit axis vector about which to rotate; default is (0,1,0). + + +* **`place2d`** (NG): transform incoming UV texture coordinates for 2D texture placement. + * `texcoord` (vector2): the input UV coordinate to transform; defaults to the current surface index=0 uv coordinate. + * `pivot` (vector2): the pivot coordinate for scale and rotate: this is subtracted from u,v before applying scale/rotate, then added back after. Default is (0,0). + * `scale` (vector2): divide the u,v coord (after subtracting `pivot`) by this, so a scale (2,2) makes the texture image appear twice as big. Negative values can be used to flip or flop the texture space. Default is (1,1). + * `rotate` (float): rotate u,v coord (after subtracting pivot) by this amount in degrees, so a positive value rotates UV coords counter-clockwise, and the image clockwise. Default is 0. + * `offset` (vector2): subtract this amount from the scaled/rotated/“pivot added back” UV coordinate; since U0,V0 is typically the lower left corner, a positive offset moves the texture image up and right. Default is (0,0). + * `operationorder` (integer enum): the order in which to perform the transform operations. "0" or "SRT" performs -pivot scale rotate translate +pivot as per the original implementation matching the behavior of certain DCC packages, and "1" or "TRS" performs -pivot translate rotate scale +pivot which does not introduce texture shear. Default is 0 "SRT" for backward compatibility. + + + +* **`triplanarblend`** (NG): samples data from three inputs, and projects a tiled representation of the images along each of the three respective coordinate axes, computing a weighted blend of the three samples using the geometric normal. + * `inx` (float or colorN): the image to be projected in the direction from the +X axis back toward the origin. Default is 0 in all channels. + * `iny` (float or colorN): the image to be projected in the direction from the +Y axis back toward the origin with the +X axis to the right. Default is 0 in all channels. + * `inz` (float or colorN): the image to be projected in the direction from the +Z axis back toward the origin. Default is 0 in all channels. + * `position` (vector3): a spatially-varying input specifying the 3D position at which the projection is evaluated. Default is to use the current 3D object-space coordinate. + * `normal` (vector3): a spatially-varying input specifying the 3D normal vector used for blending. Default is to use the current object-space surface normal. + * `blend` (float): a 0-1 weighting factor for blending the three axis samples using the geometric normal, with higher values giving softer blending. Default is 1.0. + * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. + * **`dot`**: a no-op, passes its input through to its output unchanged. Users can use dot nodes to shape edge connection paths or provide documentation checkpoints in node graph layout UI's. Dot nodes may also pass uniform values from <constant>, <tokenvalue> or other nodes with uniform="true" outputs to uniform <input>s and <token>s. * `in` (any type): the nodename to be connected to the Dot node's "in" input. Unlike inputs on other node types, the <dot> node's input is specifically disallowed to provide a `channels` attribute: input data can only be passed through unmodified. -Additional math nodes, including **``**, **``** and **``** may be found in the [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md#supplemental-math-nodes) document. - ### Adjustment Nodes Adjustment nodes have one input named "in", and apply a specified function to values in the incoming stream. + + +* **`contrast`** (NG): increase or decrease contrast of incoming float/color values using a linear slope multiplier. + * `in` (float or colorN or vectorN): the input value or nodename + * `amount` (same type as `in` or float): slope multiplier for contrast adjustment, 0.0 to infinity range. Values greater than 1.0 increase contrast, values between 0.0 and 1.0 reduce contrast. Default is 1.0 in all channels. + * `pivot` (same type as `in` or float): center pivot value of contrast adjustment; this is the value that will not change as contrast is adjusted. Default is 0.5 in all channels. + * **`remap`**: linearly remap incoming values from one range of float/color/vector values to another. @@ -1244,6 +1358,17 @@ Adjustment nodes have one input named "in", and apply a specified function to va * `outlow` (same type as `in` or float): low value for output range; default is 0.0 in all channels * `outhigh` (same type as `in` or float): high value for output range; default is 1.0 in all channels + + +* **`range`** (NG): remap incoming values from one range of float/color/vector values to another, optionally applying a gamma correction "in the middle". Input values below `inlow` or above `inhigh` are extrapolated unless `doclamp` is true, in which case the output values will be clamped to the `outlow`..`outhigh` range. + * `in` (float or colorN or vectorN): the input value or nodename + * `inlow` (same type as `in` or float): low value for input range. Default is 0.0 in all channels. + * `inhigh` (same type as `in` or float): high value for input range. Default is 1.0 in all channels. + * `gamma` (same type as `in` or float): inverse exponent applied to input value after first transforming from `inlow`..`inhigh` to 0..1; `gamma` values greater than 1.0 make midtones brighter. Default is 1.0 in all channels. + * `outlow` (same type as `in` or float): low value for output range. Default is 0.0 in all channels. + * `outhigh` (same type as `in` or float): high value for output range. Default is 1.0 in all channels. + * `doclamp` (boolean): If true, the output is clamped to the range `outlow`..`outhigh`. Default is false. + * **`smoothstep`**: output a smooth (hermite-interpolated) remapping of input values from low-high to output 0-1. @@ -1269,6 +1394,22 @@ Adjustment nodes have one input named "in", and apply a specified function to va * `in` (float): the input interpolant value or nodename * `knotvalues` (uniform floatarray or colorNarray or vectorNarray): the array of at least 2 values to interpolate between. + + +* **`curveadjust`** (NG): output a smooth remapping of input values using the centripetal Catmull-Rom cubic spline curve defined by specified knot values, using an inverse spline lookup on input knot values and a forward spline through output knot values. All channels of the input will be remapped using the same curve. + * `in` (float or colorN or vectorN): the input value or nodename + * `numknots` (uniform integer): the number of values in the knots and knotvalues arrays + * `knots` (uniform floatarray): the list of input values defining the curve for the remapping. At least 2 and at most 16 values must be provided. + * `knotvalues` (uniform floatarray): the list of output values defining the curve for the remapping. Must be the same length as knots. + + + +* **`curvelookup`** (NG): output a float, colorN or vectorN value smoothly interpolated between a number of knotvalue values, using the position of in within knots as the knotvalues interpolant. + * `in` (float): the input interpolant value or nodename + * `numknots` (uniform integer): the number of values in the knots and knotvalues arrays + * `knots` (uniform floatarray): the list of knot values to interpolate in within. At least 2 and at most 16 values must be provided. + * `knotvalues` (uniform floatarray or colorNarray or vectorNarray): the values at each knot position to interpolate between. Must be the same length as knots. + * **`luminance`**: (color3 or color4 only) output a grayscale value containing the luminance of the incoming RGB color in all color channels, computed using the dot product of the incoming color with the luma coefficients of the working colorspace; the alpha channel is left unchanged if present. @@ -1285,7 +1426,31 @@ Adjustment nodes have one input named "in", and apply a specified function to va * **`hsvtorgb`**: (color3 or color4 only) convert an incoming color from HSV to RGB space; the alpha channel is left unchanged if present. This conversion is not affected by the current color space. * `in` (color3/color4): the input value or nodename -Additional adjustment nodes, including **``**, **``**, **``**, **``**, **``**, **``** and **``** may be found in the [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md#supplemental-adjustment-nodes) document. + + +* **`hsvadjust`** (NG): adjust the hue, saturation and value of an RGB color by converting the input color to HSV, adding amount.x to the hue, multiplying the saturation by amount.y, multiplying the value by amount.z, then converting back to RGB. A positive "amount.x" rotates hue in the "red to green to blue" direction, with amount of 1.0 being the equivalent to a 360 degree (e.g. no-op) rotation. Negative or greater-than-1.0 hue adjustment values are allowed, wrapping at the 0-1 boundaries. For color4 inputs, the alpha value is unchanged. + * `in` (color3 or color4): the input value or nodename + * `amount` (vector3): the HSV adjustment; a value of (0, 1, 1) is "no change" and is the default. + + + +* **`saturate`** (NG): (color3 or color4 only) adjust the saturation of a color; the alpha channel will be unchanged if present. Note that this operation is **not** equivalent to the "amount.y" saturation adjustment of `hsvadjust`, as that operator does not take the working or any other colorspace into account. + * `in` (float or colorN or vectorN): the input value or nodename + * `amount` (float): a multiplier for saturation; the saturate operator performs a linear interpolation between the luminance of the incoming color value (copied to all three color channels) and the incoming color value itself. Note that setting amount to 0 will result in an R=G=B gray value equal to the value that the `luminance` node (below) returns. Default is 1.0. + * `lumacoeffs` (uniform color3): the luma coefficients of the current working color space; if no specific color space can be determined, the ACEScg (ap1) luma coefficients [0.272287, 0.6740818, 0.0536895] will be used. Applications which support color management systems may choose to retrieve this value from the CMS to pass to the <saturate> node's implementation directly, rather than exposing it to the user. + + + +* **`colorcorrect`** (NG): Combines various adjustment nodes into one artist-friendly color correction node. For color4 inputs, the alpha value is unchanged. + * `in` (color3 or color4): the input color to be adjusted. + * `hue` (float): Rotates the color hue, with values wrapping at 0-1 boundaries; default is 0. + * `saturation` (float): Multiplies the input color saturation level; default is 1. + * `gamma` (float): Applies a gamma correction to the color; default is 1. + * `lift` (float): Raise the dark color values, leaving the white values unchanged; default is 0. + * `gain` (float): Multiplier increases lighter color values, leaving black values unchanged; default is 1. + * `contrast` (float): Linearly increase or decrease the color contrast; default is 1. + * `contrastpivot` (float): Pivot value around which contrast applies. This value will not change as contrast is adjusted; default is 0.5. + * `exposure` (float): Multplier which increases or decreases color brightness by 2^value; default is 0. @@ -1313,6 +1478,14 @@ Premult nodes operate on 4-channel (color4) inputs/outputs, have one input named Blend nodes take two 1-4 channel inputs and apply the same operator to all channels (the math for alpha is the same as for R or RGB). In the Blend Operator table, "F" and "B" refer to any individual channel of the `fg` and `bg` inputs respectively. Blend nodes support an optional float input `mix`, which can be used to mix the original `bg` value (`mix`=0) with the result of the blend operation (`mix`=1, the default). + + + + + + + + | Blend Operator | Each Channel Output | Supported Types | | --- | --- | --- | @@ -1329,6 +1502,13 @@ Blend nodes take two 1-4 channel inputs and apply the same operator to all chann Merge nodes take two 4-channel (color4) inputs and use the built-in alpha channel(s) to control the compositing of the `fg` and `bg` inputs. In the Merge Operator table, "F" and "B" refer to the non-alpha channels of the `fg` and `bg` inputs respectively, and "f" and "b" refer to the alpha channels of the `fg` and `bg` inputs. Merge nodes are not defined for 1-channel or 3-channel inputs, and cannot be used on vectorN streams. Merge nodes support an optional float input `mix`, which can be used to mix the original `bg` value (`mix`=0) with the result of the blend operation (`mix`=1, the default). + + + + + + + | Merge Operator | RGB output | Alpha Output | | --- | --- | --- | @@ -1344,6 +1524,9 @@ Merge nodes take two 4-channel (color4) inputs and use the built-in alpha channe Masking nodes take one 1-4 channel input `in` plus a separate float `mask` input and apply the same operator to all channels (if present, the math for alpha is the same as for R or RGB). The default value for the `mask` input is 1.0 for the `inside` operator, and 0.0 for the `outside` operator In the Masking Operator table, "F" refers to any individual channel of the `in` input. + + + | Masking Operator | Each Channel Output | | --- | --- | @@ -1358,13 +1541,15 @@ Note: for all types, `inside` is equivalent to the `multiply` node: both operato The Mix node takes two 1-4 channel inputs `fg` and `bg` plus a separate 1-channel (float) or N-channel (same type and number of channels as `fg` and `bg`) `mix` input and mixes the `fg` and `bg` according to the mix value, either uniformly for a "float" `mix` type, or per-channel for non-float `mix` types. The equation for "mix" is as follows, with "F" and "B" referring to any channel of the `fg` and `bg` inputs respectively (which can be float, colorN or vectorN but must match), and "m" referring to the float `mix` input value (which has a default value of 0): + + | Mix Operator | Each Channel Output | | --- | --- | | **`mix`** | Fm+B(1-m) | -See also the [Shader Nodes](#shader-nodes) section below for additional `mix` operator variants supporting shader-semantic types. +See also the [Standard Library Shader Nodes](#standard-library-shader-nodes) section below for additional `mix` operator variants supporting shader-semantic types. @@ -1400,7 +1585,7 @@ Conditional nodes are used to compare values of two streams, or to select a valu * **`switch`**: output the value of one of up to ten input streams, according to the value of a selector input `which`. Switch nodes can be of output type float, colorN or vectorN, and have five inputs, in1 through in10 (not all of which must be connected), which must match the output type. - * `in1`, `in2`, `in3`, `in4`, `in5`, `in6`, `in7`, `in8`, `in9`, `in10` (float or colorN or vectorN): the values or nodenames to select from based on the value of the `which` input. The types of the various `inN` inputs must match the type of the `switch` node itself. The default value of all `inN` inputs is 0.0 in all channels. + * `in1`, `in2`, `in3`, `in4`, `in5`, `in6`, `in7`, `in8`, `in9`, `in10` (float or colorN or vectorN): the values or nodenames to select from based on the value of the `which` input. The types of the various `in`N inputs must match the type of the `switch` node itself. The default value of all `in`N inputs is 0.0 in all channels. * `which` (integer or float): a selector to choose which input to take values from; the output comes from input "floor(`which`)+1", clamped to the 1-10 range. So `which`<1 will pass on the value from in1, 1<=`which`<2 will pass the value from in2, 2<=`which`<3 will pass the value from in3, and so on up to 9<=`which` will pass the value from in10. The default value of `which` is 0. @@ -1437,12 +1622,43 @@ Channel nodes are used to perform channel manipulations and data type conversion -* **`combine2`**, **`combine3`**, **`combine4`**: combine the channels from two, three or four streams into the same total number of channels of a single output stream of a specified compatible type; please see the table below for a list of all supported combinations of input and output types. For color output types, no colorspace conversion will take place; the channels are simply copied as-is. +* **`combine2`**, **`combine3`**, **`combine4`**: combine the channels from two, three or four streams into the same total number of channels of a single output stream of a specified compatible type; please see the table below for a list of all supported combinations of input and output types. For colorN output types, no colorspace conversion will take place; the channels are simply copied as-is. * `in1` (float/color3/vector2/vector3): the input value or nodename which will be sent to the N channels of the output; default is 0.0 in all channels * `in2` (float/vector2): the input value or nodename which will be sent to the next N channels of the output; default is 0.0 in all channels * `in3` (float): for **`combine3`** or **`combine4`**, the input value or nodename which will be sent to the next channel of the output after `in2`; default is 0.0 * `in4` (float): for **`combine4`**, the input value or nodename which will be sent to the last channel of the output; default is 0.0 + + +* **`separate2`** (NG): output each of the channels of a vector2 as a separate float output. + * `in` (vector2): the input value or nodename + * `outx` (**output**, float): the value of x channel. + * `outy` (**output**, float): the value of y channel. + + + +* **`separate3`** (NG): output each of the channels of a color3 or vector3 as a separate float output. + * `in` (color3 or vector3): the input value or nodename + * `outr`/`outx` (**output**, float): the value of the red (for color3 streams) or x (for vector3 streams) channel. + * `outg`/`outy` (**output**, float): the value of the green (for color3 streams) or y (for vector3 streams) channel. + * `outb`/`outz` (**output**, float): the value of the blue (for color3 streams) or z (for vector3 streams) channel. + + + +* **`separate4`** (NG): output each of the channels of a color4 or vector4 as a separate float output. + * `in` (color4 or vector4): the input value or nodename + * `outr`/`outx` (**output**, float): the value of the red (for color4 streams) or x (for vector4 streams) channel. + * `outg`/`outy` (**output**, float): the value of the green (for color4 streams) or y (for vector4 streams) channel. + * `outb`/`outz` (**output**, float): the value of the blue (for color4 streams) or z (for vector4 streams) channel. + * `outa`/`outw` (**output**, float): the value of the alpha (for color4 streams) or w (for vector4 streams) channel. + + + +* **`separatecolor4`** (NG): output the RGB and alpha channels of a color4 as separate outputs. + * `in` (color4): the input value or nodename + * `outcolor` (output, color3): the RGB channel values. + * `outa` (output, float): the value of the alpha channel. + The following input/output data type conversions are supported by **`convert`**: @@ -1470,8 +1686,6 @@ Table of allowable input/output types for **`combine2`**, **`combine3`**, **`com | `combine2` | `vector4` | `vector2` "xy" | `vector2` "zw" | n/a | n/a | "xyzw" | -Additional channel nodes, including **``** and **``**, may be found in the [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md#supplemental-channel-nodes) document. - ### Convolution Nodes @@ -1495,25 +1709,6 @@ Convolution nodes have one input named "in", and apply a defined convolution fun -### Shader Nodes - -Shader nodes construct a shader (a node with a shader semantic output type) from the specified inputs, which may then be connected to a material. 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`**: Constructs a surface shader for an unlit surface with a plain unshaded color value. Useful for visualizing texture data or rendering non-PBR materials. Output type "surfaceshader". - * `color` (color3): Color value to display. Default is (0, 0, 0). - * `opacity` (float or color3): Cutout (float) or transmission (color3) opacity for the surface. Default is 1.0, representing a fully-opaque surface. - - - -* **`displacement`**: Constructs a displacement shader describing geometric modification to surfaces. Output type "displacementshader". - * `displacement` (float or vector3): Scalar (along the surface normal direction) or vector displacement (in (dPdu, dPdv, N) tangent/normal space) for each position. Default is 0. - * `scale` (float): Scale factor for the displacement vector. Default is 1.0. - - - ## Standard Node Inputs All standard nodes which define a `defaultinput` or `default` value support the following input: @@ -2170,14 +2365,11 @@ An input with a shader-semantic type may be given a value of "" to indicate no s -### Standard Shader-Semantic Operator Nodes +### Standard Library Shader Nodes -The Standard MaterialX Library defines the following nodes and node variants operating on "shader"-semantic types. +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. -* **`mix`**: linear blend between two surface/displacement/volumeshader closures. - * `bg` (surface/displacement/volumeshader): the name of the background shader-semantic node - * `fg` (surface/displacement/volumeshader): the name of the foreground shader-semantic node - * `mix` (float): the blending factor used to mix the two input closures + * **`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 @@ -2186,6 +2378,20 @@ The Standard MaterialX Library defines the following nodes and node variants ope * `transmission_color` (color3): surface transmission color; default is (1, 1, 1) * `opacity` (float): surface cutout opacity; default is 1.0 + + +* **`displacement`**: Constructs a displacement shader describing geometric modification to surfaces. Output type "displacementshader". + * `displacement` (float or vector3): Scalar (along the surface normal direction) or vector displacement (in (dPdu, dPdv, N) tangent/normal space) for each position. Default is 0. + * `scale` (float): Scale factor for the displacement vector. Default is 1.0. + + + +* **`mix`**: linear blend between two surface/displacement/volumeshader closures. + * `bg` (surface/displacement/volumeshader): the name of the background shader-semantic node + * `fg` (surface/displacement/volumeshader): the name of the foreground shader-semantic node + * `mix` (float): the blending factor used to mix the two input closures + + ### AOV Output Elements diff --git a/documents/Specification/MaterialX.Supplement.md b/documents/Specification/MaterialX.Supplement.md new file mode 100644 index 0000000000..1a3d322dcc --- /dev/null +++ b/documents/Specification/MaterialX.Supplement.md @@ -0,0 +1,261 @@ + + + +# MaterialX: Supplemental Notes + +**Version 1.39** +Doug Smythe - Industrial Light & Magic +Jonathan Stone - Lucasfilm Advanced Development Group +March 25, 2023 + + + +# Introduction + +This document details additional information about MaterialX and how it may be incorporated into studio pipelines. The document describes recommended naming convention for node definition elements and a directory structure to define packages of node definitions and implementations from various sources. + +Previous versions of the MaterialX Supplemental Notes document included descriptions of additional node types: these node descriptions have now been folded back into the [Main Specification Document](./MaterialX.Specification.md#nodes) alongside all the other standard node descriptions. + + +## Table of Contents + +**[Introduction](#introduction)** + +**[Recommended Element Naming Conventions](#recommended-element-naming-conventions)** + +**[Material and Node Library File Structure](#material-and-node-library-file-structure)** + [Examples](#examples) + +**[Definitions, Assets, and Libraries](#definitions-assets-and-libraries)** + [Organization Using Node Graphs](#organization-using-node-graphs) + [Publishing Definitions](#publishing-definitions) + [Dependencies and Organization](#dependencies-and-organization) + [Deployment, Transmission, and Translation](#deployment-transmission-and-translation) + + + +# Recommended Element Naming Conventions + +While MaterialX elements can be given any valid name as described in the MaterialX Names section of the main specification, adhering to the following recommended naming conventions will make it easier to predict the name of a nodedef for use in implementation and nodegraph elements as well as help reduce the possibility of elements from different sources having the same name. + +**Nodedef**: "ND\__nodename_\__outputtype_[\__target_][\__version_]", or for nodes with multiple input types for a given output type (e.g. <convert>), "ND\__nodename_\__inputtype_\__outputtype_[\__target_][\__version_]". + +**Implementation**: "IM\__nodename_[\__inputtype_]\__outputtype_[\__target_][\__version_]". + +**Nodegraph**, as an implementation for a node: "NG\__nodename_[\__inputtype_]\__outputtype_[\__target_][\__version_]". + + + +# Material and Node Library File Structure + +As studios and vendors develop libraries of shared definitions and implementations of MaterialX materials and nodes for various targets, it becomes beneficial to have a consistent, logical organizational structure for the files on disk that make up these libraries. In this section, we propose a structure for files defining libraries of material nodes, <nodedef>s, nodegraph implementations and actual target-specific native source code, as well as a mechanism for applications and MaterialX content to find and reference files within these libraries. + +Legend for various components within folder hierarchies: + +| Term | Description | +| --- | --- | +| _libname_ | The name of the library; the MaterialX Standard nodes are the "stdlib" library. Libraries may choose to declare themselves to be in the libname namespace, although this is not required. | +| _target_ | The target for an implementation, e.g. "glsl", "oslpattern", "osl" or "mdl". | +| _sourcefiles_ | Source files (including includes and makefiles) for the target, in whatever format and structure the applicable build system requires. | + + +Here is the suggested structure and naming for the various files making up a MaterialX material or node definition library setup. Italicized terms should be replaced with appropriate values, while boldface terms should appear verbatim. The optional "\_\*" component of the filename can be any useful descriptor of the file's contents, e.g. "\_ng" for nodegraphs or "\_mtls" for materials. + + + _libname_/_libname_**\_defs.mtlx** (1) + _libname_/_libname_\_\***.mtlx** (2) + _libname_/_target_/_libname_\_target[\_\*]**\_impl.mtlx** (3) + _libname_/_target_/_sourcefiles_ (4) + + + +1. Nodedefs and other definitions in library _libname_. +2. Additional elements (e.g. nodegraph implementations for nodes, materials, etc.) in library _libname_. +3. Implementation elements for _libname_ specific to target _target_. +4. Source code files for _libname_ implementations specific to target _target_. + +Note that nodedef files and nodegraph-implementation files go at the top _libname_ level, while <implementation> element files go under the corresponding _libname_/_target_ level, next to their source code files. This is so that studios may easily install only the implementations that are relevant to them, and applications can easily locate the implementations of nodes for specific desired targets. Libraries are free to add additional arbitrarily-named folders for related content, such as an "images" subfolder for material library textures. + +The _libname_\_defs.mtlx file typically contains nodedefs for the library, but may also contain other node types such as implementation nodegraphs, materials, looks, and any other element types. The use of additional _libname_\_\*.mtlx files is optional, but those files should be Xincluded by the _libname_\_defs.mtlx file. + +A file referenced by a MaterialX document or tool (e.g. XInclude files, filenames in <image> or other MaterialX nodes, or command-line arguments in MaterialX tools) can be specified using either a relative or a fully-qualified absolute filesystem path. A relative path is interpreted to be relative to either the location of the referencing MaterialX document itself, or relative to a location found within the current MaterialX search path: this path may be specified via an application setting (e.g. the `--path` option in MaterialXView) or globally using the MATERIALX_SEARCH_PATH environment variable. These search paths are used for both XIncluded definitions and filename input values (e.g. images for nodes or source code for <implementation>s), and applications may choose to define different search paths for different contexts if desired, e.g. for document processing vs. rendering. + +The standard libraries `stdlib` and `pbrlib` are typically included _automatically_ by MaterialX applications, rather than through explicit XInclude directives within .mtlx files. Non-standard libraries are included into MaterialX documents by XIncluding the top-level _libname_/_libname_\_defs.mtlx file, which is expected to in turn XInclude any additional .mtlx files needed by the library. + + +### Examples + +In the examples below, MXROOT is a placeholder for one of the root paths defined in the current MaterialX search path. + +A library of studio-custom material shading networks and example library materials: + +``` + MXROOT/mtllib/mtllib_defs.mtlx (material nodedefs and nodegraphs) + MXROOT/mtllib/mtllib_mtls.mtlx (library of materials using mtllib_defs) + MXROOT/mtllib/images/*.tif (texture files used by mtllib_mtls nodes) +``` + +Documents may include the above library using + +``` + +``` + +and that file would XInclude `mtllib_mtls.mtlx`. <Image> nodes within `mtllib_mtls.mtlx` would use `file` input values such as "images/bronze_color.tif", e.g. relative to the path of the `mtllib_mtls.mtlx` file itself. + +Standard node definitions and reference OSL implementation: + +``` + MXROOT/stdlib/stdlib_defs.mtlx (standard library node definitions) + MXROOT/stdlib/stdlib_ng.mtlx (supplemental library node nodegraphs) + MXROOT/stdlib/osl/stdlib_osl_impl.mtlx (stdlib OSL implementation elem file) + MXROOT/stdlib/osl/*.{h,osl} (etc) (stdlib OSL source files) +``` + +Layout for "genglsl" and "genosl" implementations of "stdlib" for MaterialX's shadergen component, referencing the above standard `stdlib_defs.mtlx` file: + +``` + # Generated-GLSL implementations + MXROOT/stdlib/genglsl/stdlib_genglsl_impl.mtlx (stdlib genGLSL implementation file) + MXROOT/stdlib/genglsl/stdlib_genglsl_cm_impl.mtlx (stdlib genGLSL color-mgmt impl. file) + MXROOT/stdlib/genglsl/*.{inline,glsl} (stdlib common genGLSL code) + + # Generated-OSL implementations + MXROOT/stdlib/genosl/stdlib_genosl_impl.mtlx (stdlib genOSL implementation file) + MXROOT/stdlib/genosl/stdlib_genosl_cm_impl.mtlx (stdlib genOSL color-mgmt impl. file) + MXROOT/stdlib/genosl/*.{inline,osl} (stdlib common genOSL code) +``` + +Layout for the shadergen PBR shader library ("pbrlib") with implementations for "genglsl" and "genosl" (generated GLSL and OSL, respectively) targets: + +``` + MXROOT/pbrlib/pbrlib_defs.mtlx (PBR library definitions) + MXROOT/pbrlib/pbrlib_ng.mtlx (PBR library nodegraphs) + MXROOT/pbrlib/genglsl/pbrlib_genglsl_impl.mtlx (pbr impl file referencing genGLSL source) + MXROOT/pbrlib/genglsl/*.{inline,glsl} (pbr common genGLSL code) + MXROOT/pbrlib/genosl/pbrlib_genosl_impl.mtlx (pbr impl file referencing genOSL source) + MXROOT/pbrlib/genosl/*.{inline,osl} (pbr common genOSL code) +``` + + + +# Definitions, Assets, and Libraries + +In this section we propose a set of guidelines for managing unique definitions or assets and organization into libraries, wherein: + +* Definitions: Correspond directly to <nodedefs> which may or be either source code implementations or based on existing node definitions. +* Assets: is a term which corresponds to a definition plus any additional metadata on a definition and /or related resources such as input images. These can be organized in logical groupings based on a desired semantic. +* Libraries: are a collection of assets. + + +### Organization Using Node Graphs + +While it is possible to just have a set of connected nodes in a document, it is not possible to have any formal unique interface. This can invariably lead to nodes which have duplicate names, the inability to control what interfaces are exposed and inability to maintain variations over time. + +Thus the base requirement for a definition is to encapsulate the nodes into a <nodegraph>. This provides for: + +1. Hiding Complexity: Where all nodes are scoped with the graph. For user interaction point, it makes possible the ability to “drill down” into a graph as needed but otherwise a black box representation can be provided. +2. Identifier / Path Uniqueness : The nodegraph name decreases the chances of name clashes. For example two top level nodes both called “foo” would have unique paths “bar1/foo” and “bar2/foo” when placed into two nodegraphs “bar1” and “bar2”. +3. Interface / node signature control where specific inputs may be exposed via “interfacename” connections and outputs as desired. This differs from “hiding” inputs or outputs which do not change the signature. The former enforces what is exposed to the user while the latter are just interface hints. + +For individual inputs it is recommended to add the following additional attributes as required: + +1. Real World Units : If an input value depends on scene / geometry size then a unit attribute should always be added. For example if the graph represents floor tile, then to place it properly the size of the tile. A preset set of “distance” units is provided as part of the standard library. +2. Colorspace: If an input value is represented in a given color space then to support proper transformation into rendering color space this attribute should be set. A preset set of colorspace names conforming to the AcesCg 1.2 configuration is provided as part of the standard library. + +Though not strictly required it is very useful to have appropriate default values as without these the defaults will be zero values. Thus for example a “scale” attribute for a texture node should not be left to default to zero. + + +### Publishing Definitions + +From a <nodegraph> a definition can be (new <nodedef>) created or “published”. Publishing allows for the following important capabilities: + +1. Reuse: The ability to reuse an unique node definition as opposed to duplicating graph implementations. +2. Variation: The ability to create or apply variations to an instance independent from the implementation. +3. Interoperability: Support definitions with common properties that are mutually understood by all consumers be exchanged. + +In order to support these capabilities It is recommended that the following attributes always be specified: + +1. A unique name identifier: This can follow the ND\_ and NG\_ convention described. It is recommended that the signature of the interface also be encoded to provide uniqueness, especially if the definition may be polymorphic. +2. A namespace identifier (\*): When checking uniqueness namespace is always used so it is not required to be part of the name identifier to provide uniqueness. + * It should not be used as it will result in the namespace being prepended multiple times. E.g. a “foo” node with a namespace “myspace” has a unique identifier of “myspace:node”. If the node is named “myspace:node”, then the resulting identifier is “myspace:myspace:node”. + * Note that import of a Document will prepend namespaces as required without namespace duplication. +3. A version identifier: While this can be a general string, it is recommended that this be a template with a specific format to allow for known increment numbering. E.g. The format may be “v#.#” to support minor and major versioning. This requires that only one out of all versions be tagged as the default version. Care should be taken to ensure this as the first one found will be used as the default. +4. A nodegroup identifier: This can be one mechanism used for definition organization, or for user interface presentation. It is also used for shader generation to some extent as it provides hints as to the type of node. For example <image> nodes are in the “texture2d” node group. +5. A documentation string. Though not strictly required this provides some ability to tell what a node does, and can be used as a user interface helper. Currently there is no formatting associated with this but it is possible to embed a format. + +Note that utilities which codify publishing logic are provided as part of the core distribution. + +To support variation it is proposed that both <token>s and <variant>s be used. + + + +1. Tokens: These allow for the sample “template” be used for filenames with having to create new definitions. This can reduce the number of required definitions but also reduce the number of presets required. For example tokens can be used to control the format, resolution of the desired filename identifier. +2. Variants and Variant Sets: There are no hard-and-fast “rules” for when to create a definition vs use a definition with variants but one possible recommendation is to use variants when there are no interface signature differences (_Discuss_?). Some advantages include the fact that variants may be packaged and deployed independent of definitions and/or new definitions do not need to be deployed per variation. Note that for now only value overrides are possible. + + +### Dependencies and Organization + +The more definitions are added including those based on other definitions, the harder it can be to discover what definitions are required given documents with some set of node instances. + +To support separability of dependents, the following logical high level semantic breakdown is proposed: + + + +1. Static “Core” library definitions. These include stdlib, pbrlib and bxdf. The recommendation is to always load these in and assume that they exist. For separability, it is recommended that these all be stored in a single runtime Document. +2. Static custom library definitions. These are based on core libraries. The recommendation is to not directly reference any core libraries using the Xinclude mechanism. This can result in duplicate possibly conflicting definitions being loaded. The “upgrade” mechanism will ensure that all core and custom libraries are upgraded to the appropriate target version. For separability, it is recommended that these all be stored in a single runtime Document. +3. Dynamically created definitions. If this capability is allowed then it can be useful to have a fixed set of locations that these definitions can update. This could be local to the user or to update an existing custom library. + +Additional groupings can be added to provide semantic organization (such as by “nodegroup”) though the recommendation is that they live within a common library root or package. + +For an asset with dependent resources there are many options. Two of which are: + + + +1. Co-locate resources with the definition. This allows for easier “packaging” of a single asset such as for transmission purposes but this can require additional discovery logic to find resources associated with a definition and may result in duplication of resources. +2. Located in a parallel folder structure to definitions. The onus is to maintain this parallel structure but the search logic complexity is the same for resources as it is for definitions. + +If a definition is a source code implementation, then additional path search logic is required for discoverability during generation. + +The following search paths are available: + + + +* MATERIALX_SEARCH_PATH: This environment variable is used as part of both definition and resource search (e.g. relative filename path support). +* Source code paths: This can be registered at time of code generation as part of the generation “context”. It is recommended to follow the source path locations which would be relative to any custom definitions, using the “language” identifier of the code generator to discover the appropriate source code files. + +An example usage of pathing can be found in the sample Viewer. The logic is as follows: + + + +* The module/binary path is set as the default “root” definition path. Additional definition roots are included via MATERIALX_SEARCH_PATH. +* The set of roots are considered to be the parent of “resources” and “libraries” folders, for resource and definitions respectively. +* The search path root for resources would be the “`/resources`” by default. This allows for handling of resources which are part of an assets definition. For example a brick shader located at “`/myroot/shaders/brick.mtlx`” may have the brick textures referenced at location “`/myroot/textures/brick.exr`”. Setting a single search path to “`/myroot`” handles the “parallel” folder organization mentioned, with the relative reference being “`textures/brick.exr`” +* For any shader at a given path a path to that shader can be added when resolving dependent resources. This can be used to handle the co-located folder organization. For example the shader may reside in “`/myroot/shader/brick.mtlx`”, and the texture in “`/myroot/shader/textures/brick.exr`”. Setting a root to “`myroot/shader`” and a relative reference to “`textures/brick.exr`” will allow for proper discovery. + +For runtime, it is recommended that instead of reading in documents that they be “imported”. This allows for mechanisms such as namespace handling as well as tagging of source location (“sourceURI”) to occur per document. At some point all dependent documents need to be merged into a single one as there is no concept of referenced in-memory documents. Tagging is a useful mechanism to allow filtering out / exclusion of definitions from the main document content. For example, the main document can be “cleared” out while retaining the library definitions. + +As there may be definitions dependent on other definitions, it is never recommended to unload core libraries, and care be taken when unloading custom or dynamic libraries. It may be useful to re-load all definitions if there is a desire to unload any one library. + +Note that code generation is context based. If the context is not cleared, then dependent source code implementations will be retained. It is recommended to clear implementations if definitions are unloaded. + + +### Deployment, Transmission, and Translation + +Given a set of definitions it is useful to consider how it will be deployed. + +Some deployments for which file access may be restricted or accessing many files is a performance issue, pre-package up definitions, source and associated resources may be required. For example, this is currently used for the sample Web viewer deployment. + +Some deployments may not want to handle non-core definitions or may not be able to handle (complex) node graphs. Additionally the definition may be transmitted as a shader. Thus, when creating a new definition it is recommended to determine the level of support for: + + + +1. Flattening: Can the definition be converted to a series of nodes which have source code implementations. +2. Baking: Can the definition be converted to an image. +3. Translation: Can the implementation be converted mapped / converted to another implementation which can be consumed. +4. Shader Reflection: Can the desired metadata be passed to the shader for introspection. + +Additional metadata which is not a formal part of the specification may lead to the inability to be understood by consumers. + diff --git a/documents/Specification/inprog_v1.39/README_v1.39.md b/documents/Specification/README.md similarity index 82% rename from documents/Specification/inprog_v1.39/README_v1.39.md rename to documents/Specification/README.md index f63017ba9d..a04dfb5caa 100644 --- a/documents/Specification/inprog_v1.39/README_v1.39.md +++ b/documents/Specification/README.md @@ -9,7 +9,7 @@ The documents in this folder comprise the complete MaterialX Specification, vers * [**MaterialX Specification**](./MaterialX.Specification.md) - the main Specification, describing definitions, core functionality and the standard node library * [**MaterialX Physically Based Shading Nodes**](./MaterialX.PBRSpec.md) - describes BSDF and other shading function nodes useful in constructing complex layered rendering shaders using node graphs * [**MaterialX Geometry Extensions**](./MaterialX.GeomExts.md) - additional MaterialX elements to define geometry-related information such as collections, properties and material assignments -* [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md) - describes a number of additional node types built from the standard nodes as well as recommended naming and structuring conventions for libraries of custom node definitions +* [**MaterialX Supplemental Notes**](./MaterialX.Supplement.md) - describes recommended naming and structuring conventions for libraries of custom node definitions

@@ -33,7 +33,7 @@ Previously, MaterialX used custom types with a structure of output variables to **Array Types Now Uniform and Static Length** -Many shading languages do not support dynamic array types with a variable length, so MaterialX now only supports arrays with a fixed maximum length, and all array-type node inputs must be uniform; nodes are no longer permitted to output an array type. Array-type inputs may be accompanied by a uniform integer input declaring the number of array elements actually used in the array. Because of this change, the unimplemented <arrayappend> node has been removed. +Many shading languages do not support dynamic array types with a variable length, so MaterialX now only supports arrays with a fixed maximum length, and all array-type node inputs must be uniform; nodes are no longer permitted to output an array type. Array-type inputs may be accompanied by a uniform integer input declaring the number of array elements actually used in the array (the <curveadjust> node has been updated in this way). Because of this change, the unimplemented <arrayappend> node has been removed. **Connectable Uniform Inputs and New Tokenvalue Node** @@ -45,7 +45,7 @@ Similarly, <token>s in materials and other node instances may now be connecte **Standardized Color Space Names** -The [standard colorspace names](./MaterialX.Specification.md#color-spaces-and-color-management-systems) in MaterialX have now been defined explicitly in the Specification, and are aligned to their definitions in the ACES 1.2 OCIO config file. With this change, there is no need for a definition of "cms" or "cmsconfig" in MaterialX documents, so those two attributes have been deprecated. +The [standard colorspace names](./MaterialX.Specification.md#color-spaces-and-color-management-systems) in MaterialX have now been defined explicitly in the Specification, and are aligned to their definitions in the ACES 1.2 OCIO config file. With this change, there is no need for a definition of "cms" or "cmsconfig" in MaterialX documents, so those two attributes have been deprecated. Additionally, two new colorspaces, "srgb_displayp3" and "lin_displayp3" have been added as standard colorspaces. **Disambiguated Nodedef and Nodegraph References** @@ -62,7 +62,7 @@ The standard <swizzle> node using a string of channel names and allowing arbi **New Unlit Surface Shader and Standard Materials** -A new standard <surface> constructor node for unlit surfaces has been added to the standard library. +A new <surface_unlit> node for unlit surfaces has been added to the standard library. Additionally, the standard <surfacematerial> material now supports both single- or double-sided surfaces with the addition of a separate `backsurface` input. @@ -72,23 +72,18 @@ Additionally, the standard <surfacematerial> material now supports both singl Typedefs may now inherit from other types, including built-in types, and may provide hints about their values such as floating-point precision. These new "inherit" and "hint" attributes are themselves merely metadata hints about the types; applications and code generators are still expected to provide their own precise definitions for all custom types. -**New Nodes** +**New and Updated Standard Library Nodes** -The following new standard operator nodes have been added: +In 1.39, we are removing the distinction between "standard nodes" and "supplemental nodes", and descriptions of both can now be found in the main Specification document. Nodes that are implemented in the standard distribution using nodegraphs are annotated with "(NG)" in the Spec to differentiate them from nodes implemented in each rendering target's native shading language. -* [Procedural nodes](./MaterialX.Specification.md#procedural-nodes): **tokenvalue**, **fractal2d**, **cellnoise1d** +Additionally, the following new operator nodes have been added to the standard library: + +* [Procedural nodes](./MaterialX.Specification.md#procedural-nodes): **tokenvalue**, **checkerboard**, **fractal2d**, **cellnoise1d**, **unifiednoise2d**, **unifiednoise3d** * [Geometric nodes](./MaterialX.Specification.md#geometric-nodes): **bump**, **geompropvalueuniform** -* [Math nodes](./MaterialX.Specification.md#math-nodes): boolean **and**, **or**, **not**; **transformcolor**, **creatematrix** -* [Adjustment nodes](./MaterialX.Specification.md#adjustment-nodes): **curveinversecubic**, **curveuniformlinear** and **curveuniformcubic** +* [Math nodes](./MaterialX.Specification.md#math-nodes): boolean **and**, **or**, **not**; **distance**, **transformcolor**, **creatematrix** and **triplanarblend**, as well as integer-output variants of **floor** and **ceil** +* [Adjustment nodes](./MaterialX.Specification.md#adjustment-nodes): **curveinversecubic**, **curveuniformlinear**, **curveuniformcubic** and **colorcorrect** * [Conditional nodes](./MaterialX.Specification.md#conditional-nodes): boolean-output variants of **ifgreater**, **ifgreatereq** and **ifequal**; new **ifelse** node -* [Channel nodes](./MaterialX.Specification.md#channel-nodes): **extractrowvector** - -Additionally, the following new supplemental nodes have been added: - -* [Procedural nodes](./MaterialX.Supplement.md#supplemental-procedural-nodes): **unifiednoise2d**, **unifiednoise3d** -* [Math nodes](./MaterialX.Supplement.md#supplemental-math-nodes): **triplanarblend** -* [Adjustment nodes](./MaterialX.Supplement.md#supplemental-adjustment-nodes): **colorcorrect** -* [Channel nodes](./MaterialX.Supplement.md#supplemental-channel-nodes): **separatecolor4** +* [Channel nodes](./MaterialX.Specification.md#channel-nodes): **extractrowvector** and **separatecolor4** **New Physically Based Shading Nodes** @@ -103,10 +98,13 @@ The following new standard physically based shading nodes have been added: * The <member> element for <typedef>s and the "member" attribute for inputs have been removed from the Specification, as they had never been implemented and it was not clear how they could be implemented generally. * The "valuerange" and "valuecurve" attributes describing expressions and function curves have been removed, in favor of using the new <curveinversecubic> / <curveuniformcubic> / etc. nodes. +* The <geomcolor>, <geompropvalue> and <geompropvalueuniform> nodes for color3/4-type values can now take a "colorspace" attribute to declare the colorspace of the property value. * The <cellnoise2d> and <cellnoise3d> nodes now support vectorN output types in addition to float output. +* The <noise2d/3d>, <fractal2d/3d>, <cellnoise2d/3d> and <worleynoise2d/3d> nodes now support a "period" input. * The <worleynoise2d> and <worleynoise3d> nodes now support a number of different distance metrics. -* The <time> node no longer has a "frames per second" input: the application is now always expected to generate the "current time in seconds" using an appropriate method. "Fps" was removed because varible-rate real-time applications have no static "fps", and it's generally not good to bake a situation-dependent value like fps into a shading network. +* The <time> node no longer has a "frames per second" input: the application is now always expected to generate the "current time in seconds" using an appropriate method. The "fps" input was removed because variable-rate real-time applications have no static "fps", and it's generally not good to bake a situation-dependent value like fps into a shading network. * A standard "tangent" space is now defined in addition to "model", "object" and "world" spaces, and the <heighttonormal> node now accepts a uniform "space" input to define the space of the output normal vector. +* The <switch> node now supports 10 inputs instead of just 5. * The <surface> and <displacement> nodes are now part of the main Specification rather than being Physically Based Shading nodes. * <Token> elements are now explicitly allowed to be children of compound nodegraphs, and token values may now have defined enum/enumvalues. * Inputs in <nodedef>s may now supply "hints" to code generators as to their intended interpretation, e.g. "transparency" or "opacity". diff --git a/documents/Specification/inprog_v1.39/MaterialX.Supplement.md b/documents/Specification/inprog_v1.39/MaterialX.Supplement.md deleted file mode 100644 index 703fa1275c..0000000000 --- a/documents/Specification/inprog_v1.39/MaterialX.Supplement.md +++ /dev/null @@ -1,493 +0,0 @@ - - - -# MaterialX: Supplemental Notes - -**Version 1.39** -Doug Smythe - Industrial Light & Magic -Jonathan Stone - Lucasfilm Advanced Development Group -March 25, 2023 - - - -# Introduction - -This document details additional information about MaterialX and how it may be incorporated into studio pipelines. The document describes a number of additional Supplemental Nodes providing enhanced functionality over the basic Standard Nodes, as well as a recommended naming convention for node definition elements and a directory structure to define packages of node definitions and implementations from various sources. - - - -## Table of Contents - -**[Introduction](#introduction)** - -**[Supplemental Nodes](#supplemental-nodes)** - [Supplemental Texture Nodes](#supplemental-texture-nodes) - [Supplemental Procedural Nodes](#supplemental-procedural-nodes) - [Supplemental Math Nodes](#supplemental-math-nodes) - [Supplemental Adjustment Nodes](#supplemental-adjustment-nodes) - [Supplemental Channel Nodes](#supplemental-channel-nodes) - -**[Recommended Element Naming Conventions](#recommended-element-naming-conventions)** - -**[Material and Node Library File Structure](#material-and-node-library-file-structure)** - [Examples](#examples) - -**[Definitions, Assets, and Libraries](#definitions-assets-and-libraries)** - [Organization Using Node Graphs](#organization-using-node-graphs) - [Publishing Definitions](#publishing-definitions) - [Dependencies and Organization](#dependencies-and-organization) - [Deployment, Transmission, and Translation](#deployment-transmission-and-translation) - - - -# Supplemental Nodes - -The MaterialX Specification defines a number of Standard Nodes, which all implementations of MaterialX are expected to support, to the degree their host applications allow. These nodes are the basic "building blocks" upon which more complex node functionality can be built. - -This section describes a number of supplemental nodes for MaterialX. These nodes are considered part of MaterialX, but are typically implemented using graphs of standard MaterialX nodes rather than being implemented for specific targets. Certain applications may choose to implement these supplemental nodes using native coding languages for efficiency. It is also expected that various applications will choose to extend these supplemental nodes with additional parameters and additional functionality. - - -### Supplemental Texture Nodes - - - -* **`tiledimage`**: samples data from a single image, with provisions for tiling and offsetting the image across uv space. - * `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 [Filename Substitutions in the main Specification document](./MaterialX.Specification.md#filename-substitutions). - * `default` (float or colorN or vectorN): a default value to use if the `file` reference can not be resolved (e.g. if a <geomtoken>, [interfacetoken] 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. - * `texcoord` (vector2): the name of a vector2-type node specifying the 2D texture coordinate at which the image data is read. Default is to use the current u,v coordinate. - * `uvtiling` (vector2): the tiling rate for the given image along the U and V axes. Mathematically equivalent to multiplying the incoming texture coordinates by the given vector value. Default value is (1.0, 1.0). - * `uvoffset` (vector2): the offset for the given image along the U and V axes. Mathematically equivalent to subtracting the given vector value from the incoming texture coordinates. Default value is (0.0, 0.0). - * `realworldimagesize` (vector2): the real-world size represented by the `file` image, with unittype "distance". A `unit` attribute may be provided to indicate the units that `realworldimagesize` is expressed in. - * `realworldtilesize` (vector2): the real-world size of a single square 0-1 UV tile, with unittype "distance". A `unit` attribute may be provided to indicate the units that `realworldtilesize` is expressed in. - * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. - - ``` - - - - - - - ``` - - - -* **`triplanarprojection`**: samples data from three images (or layers within multi-layer images), and projects a tiled representation of the images along each of the three respective coordinate axes, computing a weighted blend of the three samples using the geometric normal. - * `filex` (uniform filename): the URI of an image file to be projected in the direction from the +X axis back toward the origin. - * `filey` (uniform filename): the URI of an image file to be projected in the direction from the +Y axis back toward the origin with the +X axis to the right. - * `filez` (uniform filename): the URI of an image file to be projected in the direction from the +Z axis back toward the origin. - * `layerx` (uniform string): the name of the layer to extract from a multi-layer input file for the x-axis projection. If no value for `layerx` 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. - * `layery` (uniform string): the name of the layer to extract from a multi-layer input file for the y-axis projection. - * `layerz` (uniform string): the name of the layer to extract from a multi-layer input file for the z-axis projection. - * `default` (float or colorN or vectorN): a default value to use if any `fileX` reference can not be resolved (e.g. if a <geomtoken>, [interfacetoken] or {hostattr} is included in the filename but no substitution value or default is defined, or if the resolved file URI cannot be read) 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. - * `position` (vector3): a spatially-varying input specifying the 3D position at which the projection is evaluated. Default is to use the current 3D object-space coordinate. - * `normal` (vector3): a spatially-varying input specifying the 3D normal vector used for blending. Default is to use the current object-space surface normal. - * `filtertype` (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. - - ``` - - - - - - - ``` - - -### Supplemental Procedural Nodes - - - -* **`ramp4`**: a 4-corner bilinear value ramp. - * `valuetl` (float or colorN or vectorN): the value at the top-left (U0V1) corner - * `valuetr` (float or colorN or vectorN): the value at the top-right (U1V1) corner - * `valuebl` (float or colorN or vectorN): the value at the bottom-left (U0V0) corner - * `valuebr` (float or colorN or vectorN): the value at the bottom-right (U1V0) corner - * `texcoord` (vector2, optional): the name of a vector2-type node specifying the 2D texture coordinate at which the ramp interpolation is evaluated. Default is to use the first set of texture coordinates. - - - -* **`unifiednoise2d`**: a single node supporting 2D Perlin, Cell, Worley or Fractal noise in a unified interface. - * `type` (integer): The type of noise function to use. One of 0 (Perlin), 1 (Cell), 2 (Worley), or 3 (Fractal); default is Perlin. - * `texcoord` (vector2): the input 2d space. Default is the first texture coordinates. - * `freq` (vector2): Adjusts the noise frequency, with higher values producing smaller noise shapes. Default is (1,1). - * `offset` (vector2): Shift the noise in 2d space. Default is (0,0). - * `jitter` (float): Adjust uniformity of Worley noise; for other noise types jitters the results. - * `outmin` (float): The lowest values fit to the noise. Default is 0.0. - * `outmax` (float): The highest values fit to the noise. Default is 1.0. - * `clampoutput` (boolean): Clamp the output to the min and max output values. - * `octaves` (integer): The number of octaves of Fractal noise to be generated. Default is 3. - * `lacunarity` (float): The exponential scale between successive octaves of Fractal noise. Default is 2.0. - * `diminish` (float): The rate at which noise amplitude is diminished for each octave of Fractal noise. Default is 0.5. - - - -* **`unifiednoise3d`**: a single node supporting 3D Perlin, Cell, Worley or Fractal noise in a unified interface. - * `type` (integer): The type of noise function to use. One of 0 (Perlin), 1 (Cell), 2 (Worley), or 3 (Fractal); default is Perlin. - * `position` (vector3): the input 3d space. Default is position in object-space. - * `freq` (vector3): Adjusts the noise frequency, with higher values producing smaller noise shapes. Default is (1,1,1). - * `offset` (vector3): Shift the noise in 3d space. Default is (0,0,0). - * `jitter` (float): Adjust uniformity of Worley noise; for other noise types jitters the results. - * `outmin` (float): The lowest values fit to the noise. Default is 0.0. - * `outmax` (float): The highest values fit to the noise. Default is 1.0. - * `clampoutput` (boolean): Clamp the output to the min and max output values. - * `octaves` (integer): The number of octaves of Fractal noise to be generated. Default is 3. - * `lacunarity` (float): The exponential scale between successive octaves of Fractal noise. Default is 2.0. - * `diminish` (float): The rate at which noise amplitude is diminished for each octave of Fractal noise. Default is 0.5. - - - -### Supplemental Math Nodes - - - -* **`place2d`**: transform incoming UV texture coordinates for 2D texture placement. - * `texcoord` (vector2): the input UV coordinate to transform; defaults to the current surface index=0 uv coordinate. - * `pivot` (vector2): the pivot coordinate for scale and rotate: this is subtracted from u,v before applying scale/rotate, then added back after. Default is (0,0). - * `scale` (vector2): divide the u,v coord (after subtracting `pivot`) by this, so a scale (2,2) makes the texture image appear twice as big. Negative values can be used to flip or flop the texture space. Default is (1,1). - * `rotate` (float): rotate u,v coord (after subtracting pivot) by this amount in degrees, so a positive value rotates UV coords counter-clockwise, and the image clockwise. Default is 0. - * `offset` (vector2): subtract this amount from the scaled/rotated/“pivot added back” UV coordinate; since U0,V0 is typically the lower left corner, a positive offset moves the texture image up and right. Default is (0,0). - * `operationorder` (integer enum): the order in which to perform the transform operations. "0" or "SRT" performs -pivot scale rotate translate +pivot as per the original implementation matching the behavior of certain DCC packages, and "1" or "TRS" performs -pivot translate rotate scale +pivot which does not introduce texture shear. Default is 0 "SRT" for backward compatibility. - - - -* **`safepower`**: raise incoming float/color values to the specified exponent. Unlike the standard <power> node, negative `in1` values for <safepower> will result in negative output values, e.g. `out = sign(in1)*pow(abs(in1),in2)`. - * `in1` (float or colorN or vectorN): the value or nodename for the primary input - * `in2` (same type as `in` or float): exponent value or nodename; default is 1.0 in all channels - - - -* **`triplanarblend`**: samples data from three inputs, and projects a tiled representation of the images along each of the three respective coordinate axes, computing a weighted blend of the three samples using the geometric normal. - * inx (float or colorN): the image to be projected in the direction from the +X axis back toward the origin. Default is 0 in all channels. - * iny (float or colorN): the image to be projected in the direction from the +Y axis back toward the origin with the +X axis to the right. Default is 0 in all channels. - * inz (float or colorN): the image to be projected in the direction from the +Z axis back toward the origin. Default is 0 in all channels. - * position (vector3): a spatially-varying input specifying the 3D position at which the projection is evaluated. Default is to use the current 3D object-space coordinate. - * normal (vector3): a spatially-varying input specifying the 3D normal vector used for blending. Default is to use the current object-space surface normal. - * filtertype (uniform string): the type of texture filtering to use; standard values include "closest" (nearest-neighbor single-sample), "linear", and "cubic". If not specified, an application may use its own default texture filtering method. - - - -### Supplemental Adjustment Nodes - - - -* **`contrast`**: increase or decrease contrast of incoming float/color values using a linear slope multiplier. - * `in` (float or colorN or vectorN): the input value or nodename - * `amount` (same type as `in` or float): slope multiplier for contrast adjustment, 0.0 to infinity range. Values greater than 1.0 increase contrast, values between 0.0 and 1.0 reduce contrast. Default is 1.0 in all channels. - * `pivot` (same type as `in` or float): center pivot value of contrast adjustment; this is the value that will not change as contrast is adjusted. Default is 0.5 in all channels. - - - -* **`range`**: remap incoming values from one range of float/color/vector values to another, optionally applying a gamma correction "in the middle". Input values below `inlow` or above `inhigh` are extrapolated unless `doclamp` is true, in which case the output values will be clamped to the `outlow`..`outhigh` range. - * `in` (float or colorN or vectorN): the input value or nodename - * `inlow` (same type as `in` or float): low value for input range. Default is 0.0 in all channels. - * `inhigh` (same type as `in` or float): high value for input range. Default is 1.0 in all channels. - * `gamma` (same type as `in` or float): inverse exponent applied to input value after first transforming from `inlow`..`inhigh` to 0..1; `gamma` values greater than 1.0 make midtones brighter. Default is 1.0 in all channels. - * `outlow` (same type as `in` or float): low value for output range. Default is 0.0 in all channels. - * `outhigh` (same type as `in` or float): high value for output range. Default is 1.0 in all channels. - * `doclamp` (boolean): If true, the output is clamped to the range `outlow`..`outhigh`. Default is false. - - - -* **`hsvadjust`**: adjust the hue, saturation and value of an RGB color by converting the input color to HSV, adding amount.x to the hue, multiplying the saturation by amount.y, multiplying the value by amount.z, then converting back to RGB. A positive "amount.x" rotates hue in the "red to green to blue" direction, with amount of 1.0 being the equivalent to a 360 degree (e.g. no-op) rotation. Negative or greater-than-1.0 hue adjustment values are allowed, wrapping at the 0-1 boundaries. For color4 inputs, the alpha value is unchanged. - * `in` (color3 or color4): the input value or nodename - * `amount` (vector3): the HSV adjustment; a value of (0, 1, 1) is "no change" and is the default. - - - -* **`saturate`**: (color3 or color4 only) adjust the saturation of a color; the alpha channel will be unchanged if present. Note that this operation is **not** equivalent to the "amount.y" saturation adjustment of `hsvadjust`, as that operator does not take the working or any other colorspace into account. - * `in` (float or colorN or vectorN): the input value or nodename - * `amount` (float): a multiplier for saturation; the saturate operator performs a linear interpolation between the luminance of the incoming color value (copied to all three color channels) and the incoming color value itself. Note that setting amount to 0 will result in an R=G=B gray value equal to the value that the `luminance` node (below) returns. Default is 1.0. - * `lumacoeffs` (uniform color3): the luma coefficients of the current working color space; if no specific color space can be determined, the ACEScg (ap1) luma coefficients [0.272287, 0.6740818, 0.0536895] will be used. Applications which support color management systems may choose to retrieve this value from the CMS to pass to the <saturate> node's implementation directly, rather than exposing it to the user. - - - -* **`colorcorrect`**: Combines various adjustment nodes into one artist-friendly color correction node. For color4 inputs, the alpha value is unchanged. - * `in` (color3 or color4): the input color to be adjusted. - * `hue` (float): Rotates the color hue, with values wrapping at 0-1 boundaries; default is 0. - * `saturation` (float): Multiplies the input color saturation level; default is 1. - * `gamma` (float): Applies a gamma correction to the color; default is 1. - * `lift` (float): Raise the dark color values, leaving the white values unchanged; default is 0. - * `gain` (float): Multiplier increases lighter color values, leaving black values unchanged; default is 1. - * `contrast` (float): Linearly increase or decrease the color contrast; default is 1. - * `contrastpivot` (float): Pivot value around which contrast applies. This value will not change as contrast is adjusted; default is 0.5. - * `exposure` (float): Multplier which increases or decreases color brightness by 2^value; default is 0. - - - -* **`curveadjust`**: output a smooth remapping of input values using the centripetal Catmull-Rom cubic spline curve defined by specified knot values, using an inverse spline lookup on input knot values and a forward spline through output knot values. All channels of the input will be remapped using the same curve. - * `in` (float or colorN or vectorN): the input value or nodename - * `numknots` (uniform integer): the number of values in the knots and knotvalues arrays - * `knots` (uniform floatarray): the list of input values defining the curve for the remapping. At least 2 and at most 16 values must be provided. - * `knotvalues` (uniform floatarray): the list of output values defining the curve for the remapping. Must be the same length as knots. - - - -* **`curvelookup`**: output a float, colorN or vectorN value smoothly interpolated between a number of knotvalue values, using the position of in within knots as the knotvalues interpolant. - * `in` (float): the input interpolant value or nodename - * `numknots` (uniform integer): the number of values in the knots and knotvalues arrays - * `knots` (uniform floatarray): the list of knot values to interpolate in within. At least 2 and at most 16 values must be provided. - * `knotvalues` (uniform floatarray or colorNarray or vectorNarray): the values at each knot position to interpolate between. Must be the same length as knots. - - - -### Supplemental Channel Nodes - - - -* **`separate2`**: output each of the channels of a vector2 as a separate float output. - * `in` (vector2): the input value or nodename - * `outx` (**output**, float): the value of x channel. - * `outy` (**output**, float): the value of y channel. - - - -* **`separate3`**: output each of the channels of a color3 or vector3 as a separate float output. - * `in` (color3 or vector3): the input value or nodename - * `outr`/`outx` (**output**, float): the value of the red (for color3 streams) or x (for vector3 streams) channel. - * `outg`/`outy` (**output**, float): the value of the green (for color3 streams) or y (for vector3 streams) channel. - * `outb`/`outz` (**output**, float): the value of the blue (for color3 streams) or z (for vector3 streams) channel. - - - -* **`separate4`**: output each of the channels of a color4 or vector4 as a separate float output. - * `in` (color4 or vector4): the input value or nodename - * `outr`/`outx` (**output**, float): the value of the red (for color4 streams) or x (for vector4 streams) channel. - * `outg`/`outy` (**output**, float): the value of the green (for color4 streams) or y (for vector4 streams) channel. - * `outb`/`outz` (**output**, float): the value of the blue (for color4 streams) or z (for vector4 streams) channel. - * `outa`/`outw` (**output**, float): the value of the alpha (for color4 streams) or w (for vector4 streams) channel. - - - -* **`separatecolor4`**: output the RGB and alpha channels of a color4 as separate outputs. - * `in` (color4): the input value or nodename - * `outcolor` (output, color3): the RGB channel values. - * `outa` (output, float): the value of the alpha channel. - - - -# Recommended Element Naming Conventions - -While MaterialX elements can be given any valid name as described in the MaterialX Names section of the main specification, adhering to the following recommended naming conventions will make it easier to predict the name of a nodedef for use in implementation and nodegraph elements as well as help reduce the possibility of elements from different sources having the same name. - -**Nodedef**: "ND\__nodename_\__outputtype_[\__target_][\__version_]", or for nodes with multiple input types for a given output type (e.g. <convert>), "ND\__nodename_\__inputtype_\__outputtype_[\__target_][\__version_]". - -**Implementation**: "IM\__nodename_[\__inputtype_]\__outputtype_[\__target_][\__version_]". - -**Nodegraph**, as an implementation for a node: "NG\__nodename_[\__inputtype_]\__outputtype_[\__target_][\__version_]". - - - -# Material and Node Library File Structure - -As studios and vendors develop libraries of shared definitions and implementations of MaterialX materials and nodes for various targets, it becomes beneficial to have a consistent, logical organizational structure for the files on disk that make up these libraries. In this section, we propose a structure for files defining libraries of material nodes, <nodedef>s, nodegraph implementations and actual target-specific native source code, as well as a mechanism for applications and MaterialX content to find and reference files within these libraries. - -Legend for various components within folder hierarchies: - -| Term | Description | -| --- | --- | -| _libname_ | The name of the library; the MaterialX Standard nodes are the "stdlib" library. Libraries may choose to declare themselves to be in the libname namespace, although this is not required. | -| _target_ | The target for an implementation, e.g. "glsl", "oslpattern", "osl" or "mdl". | -| _sourcefiles_ | Source files (including includes and makefiles) for the target, in whatever format and structure the applicable build system requires. | - - -Here is the suggested structure and naming for the various files making up a MaterialX material or node definition library setup. Italicized terms should be replaced with appropriate values, while boldface terms should appear verbatim. The optional "\_\*" component of the filename can be any useful descriptor of the file's contents, e.g. "\_ng" for nodegraphs or "\_mtls" for materials. - - - _libname_/_libname_**\_defs.mtlx** (1) - _libname_/_libname_\_\***.mtlx** (2) - _libname_/_target_/_libname_\_target[\_\*]**\_impl.mtlx** (3) - _libname_/_target_/_sourcefiles_ (4) - - - -1. Nodedefs and other definitions in library _libname_. -2. Additional elements (e.g. nodegraph implementations for nodes, materials, etc.) in library _libname_. -3. Implementation elements for _libname_ specific to target _target_. -4. Source code files for _libname_ implementations specific to target _target_. - -Note that nodedef files and nodegraph-implementation files go at the top _libname_ level, while <implementation> element files go under the corresponding _libname_/_target_ level, next to their source code files. This is so that studios may easily install only the implementations that are relevant to them, and applications can easily locate the implementations of nodes for specific desired targets. Libraries are free to add additional arbitrarily-named folders for related content, such as an "images" subfolder for material library textures. - -The _libname_\_defs.mtlx file typically contains nodedefs for the library, but may also contain other node types such as implementation nodegraphs, materials, looks, and any other element types. The use of additional _libname_\_\*.mtlx files is optional, but those files should be Xincluded by the _libname_\_defs.mtlx file. - -A file referenced by a MaterialX document or tool (e.g. XInclude files, filenames in <image> or other MaterialX nodes, or command-line arguments in MaterialX tools) can be specified using either a relative or a fully-qualified absolute filesystem path. A relative path is interpreted to be relative to either the location of the referencing MaterialX document itself, or relative to a location found within the current MaterialX search path: this path may be specified via an application setting (e.g. the `--path` option in MaterialXView) or globally using the MATERIALX_SEARCH_PATH environment variable. These search paths are used for both XIncluded definitions and filename input values (e.g. images for nodes or source code for <implementation>s), and applications may choose to define different search paths for different contexts if desired, e.g. for document processing vs. rendering. - -The standard libraries `stdlib` and `pbrlib` are typically included _automatically_ by MaterialX applications, rather than through explicit XInclude directives within .mtlx files. Non-standard libraries are included into MaterialX documents by XIncluding the top-level _libname_/_libname_\_defs.mtlx file, which is expected to in turn XInclude any additional .mtlx files needed by the library. - - -### Examples - -In the examples below, MXROOT is a placeholder for one of the root paths defined in the current MaterialX search path. - -A library of studio-custom material shading networks and example library materials: - -``` - MXROOT/mtllib/mtllib_defs.mtlx (material nodedefs and nodegraphs) - MXROOT/mtllib/mtllib_mtls.mtlx (library of materials using mtllib_defs) - MXROOT/mtllib/images/*.tif (texture files used by mtllib_mtls nodes) -``` - -Documents may include the above library using - -``` - -``` - -and that file would XInclude `mtllib_mtls.mtlx`. <Image> nodes within `mtllib_mtls.mtlx` would use `file` input values such as "images/bronze_color.tif", e.g. relative to the path of the `mtllib_mtls.mtlx` file itself. - -Standard node definitions and reference OSL implementation: - -``` - MXROOT/stdlib/stdlib_defs.mtlx (standard library node definitions) - MXROOT/stdlib/stdlib_ng.mtlx (supplemental library node nodegraphs) - MXROOT/stdlib/osl/stdlib_osl_impl.mtlx (stdlib OSL implementation elem file) - MXROOT/stdlib/osl/*.{h,osl} (etc) (stdlib OSL source files) -``` - -Layout for "genglsl" and "genosl" implementations of "stdlib" for MaterialX's shadergen component, referencing the above standard `stdlib_defs.mtlx` file: - -``` - # Generated-GLSL implementations - MXROOT/stdlib/genglsl/stdlib_genglsl_impl.mtlx (stdlib genGLSL implementation file) - MXROOT/stdlib/genglsl/stdlib_genglsl_cm_impl.mtlx (stdlib genGLSL color-mgmt impl. file) - MXROOT/stdlib/genglsl/*.{inline,glsl} (stdlib common genGLSL code) - - # Generated-OSL implementations - MXROOT/stdlib/genosl/stdlib_genosl_impl.mtlx (stdlib genOSL implementation file) - MXROOT/stdlib/genosl/stdlib_genosl_cm_impl.mtlx (stdlib genOSL color-mgmt impl. file) - MXROOT/stdlib/genosl/*.{inline,osl} (stdlib common genOSL code) -``` - -Layout for the shadergen PBR shader library ("pbrlib") with implementations for "genglsl" and "genosl" (generated GLSL and OSL, respectively) targets: - -``` - MXROOT/pbrlib/pbrlib_defs.mtlx (PBR library definitions) - MXROOT/pbrlib/pbrlib_ng.mtlx (PBR library nodegraphs) - MXROOT/pbrlib/genglsl/pbrlib_genglsl_impl.mtlx (pbr impl file referencing genGLSL source) - MXROOT/pbrlib/genglsl/*.{inline,glsl} (pbr common genGLSL code) - MXROOT/pbrlib/genosl/pbrlib_genosl_impl.mtlx (pbr impl file referencing genOSL source) - MXROOT/pbrlib/genosl/*.{inline,osl} (pbr common genOSL code) -``` - - - -# Definitions, Assets, and Libraries - -In this section we propose a set of guidelines for managing unique definitions or assets and organization into libraries, wherein: - -* Definitions: Correspond directly to <nodedefs> which may or be either source code implementations or based on existing node definitions. -* Assets: is a term which corresponds to a definition plus any additional metadata on a definition and /or related resources such as input images. These can be organized in logical groupings based on a desired semantic. -* Libraries: are a collection of assets. - - -### Organization Using Node Graphs - -While it is possible to just have a set of connected nodes in a document, it is not possible to have any formal unique interface. This can invariably lead to nodes which have duplicate names, the inability to control what interfaces are exposed and inability to maintain variations over time. - -Thus the base requirement for a definition is to encapsulate the nodes into a <nodegraph>. This provides for: - -1. Hiding Complexity: Where all nodes are scoped with the graph. For user interaction point, it makes possible the ability to “drill down” into a graph as needed but otherwise a black box representation can be provided. -2. Identifier / Path Uniqueness : The nodegraph name decreases the chances of name clashes. For example two top level nodes both called “foo” would have unique paths “bar1/foo” and “bar2/foo” when placed into two nodegraphs “bar1” and “bar2”. -3. Interface / node signature control where specific inputs may be exposed via “interfacename” connections and outputs as desired. This differs from “hiding” inputs or outputs which do not change the signature. The former enforces what is exposed to the user while the latter are just interface hints. - -For individual inputs it is recommended to add the following additional attributes as required: - -1. Real World Units : If an input value depends on scene / geometry size then a unit attribute should always be added. For example if the graph represents floor tile, then to place it properly the size of the tile. A preset set of “distance” units is provided as part of the standard library. -2. Colorspace: If an input value is represented in a given color space then to support proper transformation into rendering color space this attribute should be set. A preset set of colorspace names conforming to the AcesCg 1.2 configuration is provided as part of the standard library. - -Though not strictly required it is very useful to have appropriate default values as without these the defaults will be zero values. Thus for example a “scale” attribute for a texture node should not be left to default to zero. - - -### Publishing Definitions - -From a <nodegraph> a definition can be (new <nodedef>) created or “published”. Publishing allows for the following important capabilities: - -1. Reuse: The ability to reuse an unique node definition as opposed to duplicating graph implementations. -2. Variation: The ability to create or apply variations to an instance independent from the implementation. -3. Interoperability: Support definitions with common properties that are mutually understood by all consumers be exchanged. - -In order to support these capabilities It is recommended that the following attributes always be specified: - -1. A unique name identifier: This can follow the ND\_ and NG\_ convention described. It is recommended that the signature of the interface also be encoded to provide uniqueness, especially if the definition may be polymorphic. -2. A namespace identifier (\*): When checking uniqueness namespace is always used so it is not required to be part of the name identifier to provide uniqueness. - * It should not be used as it will result in the namespace being prepended multiple times. E.g. a “foo” node with a namespace “myspace” has a unique identifier of “myspace:node”. If the node is named “myspace:node”, then the resulting identifier is “myspace:myspace:node”. - * Note that import of a Document will prepend namespaces as required without namespace duplication. -3. A version identifier: While this can be a general string, it is recommended that this be a template with a specific format to allow for known increment numbering. E.g. The format may be “v#.#” to support minor and major versioning. This requires that only one out of all versions be tagged as the default version. Care should be taken to ensure this as the first one found will be used as the default. -4. A nodegroup identifier: This can be one mechanism used for definition organization, or for user interface presentation. It is also used for shader generation to some extent as it provides hints as to the type of node. For example <image> nodes are in the “texture2d” node group. -5. A documentation string. Though not strictly required this provides some ability to tell what a node does, and can be used as a user interface helper. Currently there is no formatting associated with this but it is possible to embed a format. - -Note that utilities which codify publishing logic are provided as part of the core distribution. - -To support variation it is proposed that both <token>s and <variant>s be used. - - - -1. Tokens: These allow for the sample “template” be used for filenames with having to create new definitions. This can reduce the number of required definitions but also reduce the number of presets required. For example tokens can be used to control the format, resolution of the desired filename identifier. -2. Variants and Variant Sets: There are no hard-and-fast “rules” for when to create a definition vs use a definition with variants but one possible recommendation is to use variants when there are no interface signature differences (_Discuss_?). Some advantages include the fact that variants may be packaged and deployed independent of definitions and/or new definitions do not need to be deployed per variation. Note that for now only value overrides are possible. - - -### Dependencies and Organization - -The more definitions are added including those based on other definitions, the harder it can be to discover what definitions are required given documents with some set of node instances. - -To support separability of dependents, the following logical high level semantic breakdown is proposed: - - - -1. Static “Core” library definitions. These include stdlib, pbrlib and bxdf. The recommendation is to always load these in and assume that they exist. For separability, it is recommended that these all be stored in a single runtime Document. -2. Static custom library definitions. These are based on core libraries. The recommendation is to not directly reference any core libraries using the Xinclude mechanism. This can result in duplicate possibly conflicting definitions being loaded. The “upgrade” mechanism will ensure that all core and custom libraries are upgraded to the appropriate target version. For separability, it is recommended that these all be stored in a single runtime Document. -3. Dynamically created definitions. If this capability is allowed then it can be useful to have a fixed set of locations that these definitions can update. This could be local to the user or to update an existing custom library. - -Additional groupings can be added to provide semantic organization (such as by “nodegroup”) though the recommendation is that they live within a common library root or package. - -For an asset with dependent resources there are many options. Two of which are: - - - -1. Co-locate resources with the definition. This allows for easier “packaging” of a single asset such as for transmission purposes but this can require additional discovery logic to find resources associated with a definition and may result in duplication of resources. -2. Located in a parallel folder structure to definitions. The onus is to maintain this parallel structure but the search logic complexity is the same for resources as it is for definitions. - -If a definition is a source code implementation, then additional path search logic is required for discoverability during generation. - -The following search paths are available: - - - -* MATERIALX_SEARCH_PATH: This environment variable is used as part of both definition and resource search (e.g. relative filename path support). -* Source code paths: This can be registered at time of code generation as part of the generation “context”. It is recommended to follow the source path locations which would be relative to any custom definitions, using the “language” identifier of the code generator to discover the appropriate source code files. - -An example usage of pathing can be found in the sample Viewer. The logic is as follows: - - - -* The module/binary path is set as the default “root” definition path. Additional definition roots are included via MATERIALX_SEARCH_PATH. -* The set of roots are considered to be the parent of “resources” and “libraries” folders, for resource and definitions respectively. -* The search path root for resources would be the “`/resources`” by default. This allows for handling of resources which are part of an assets definition. For example a brick shader located at “`/myroot/shaders/brick.mtlx`” may have the brick textures referenced at location “`/myroot/textures/brick.exr`”. Setting a single search path to “`/myroot`” handles the “parallel” folder organization mentioned, with the relative reference being “`textures/brick.exr`” -* For any shader at a given path a path to that shader can be added when resolving dependent resources. This can be used to handle the co-located folder organization. For example the shader may reside in “`/myroot/shader/brick.mtlx`”, and the texture in “`/myroot/shader/textures/brick.exr`”. Setting a root to “`myroot/shader`” and a relative reference to “`textures/brick.exr`” will allow for proper discovery. - -For runtime, it is recommended that instead of reading in documents that they be “imported”. This allows for mechanisms such as namespace handling as well as tagging of source location (“sourceURI”) to occur per document. At some point all dependent documents need to be merged into a single one as there is no concept of referenced in-memory documents. Tagging is a useful mechanism to allow filtering out / exclusion of definitions from the main document content. For example, the main document can be “cleared” out while retaining the library definitions. - -As there may be definitions dependent on other definitions, it is never recommended to unload core libraries, and care be taken when unloading custom or dynamic libraries. It may be useful to re-load all definitions if there is a desire to unload any one library. - -Note that code generation is context based. If the context is not cleared, then dependent source code implementations will be retained. It is recommended to clear implementations if definitions are unloaded. - - -### Deployment, Transmission, and Translation - -Given a set of definitions it is useful to consider how it will be deployed. - -Some deployments for which file access may be restricted or accessing many files is a performance issue, pre-package up definitions, source and associated resources may be required. For example, this is currently used for the sample Web viewer deployment. - -Some deployments may not want to handle non-core definitions or may not be able to handle (complex) node graphs. Additionally the definition may be transmitted as a shader. Thus, when creating a new definition it is recommended to determine the level of support for: - - - -1. Flattening: Can the definition be converted to a series of nodes which have source code implementations. -2. Baking: Can the definition be converted to an image. -3. Translation: Can the implementation be converted mapped / converted to another implementation which can be consumed. -4. Shader Reflection: Can the desired metadata be passed to the shader for introspection. - -Additional metadata which is not a formal part of the specification may lead to the inability to be understood by consumers. - diff --git a/documents/Specification/inprog_v1.39/media/MaterialX_1.39_Overview_v5.png b/documents/Specification/media/MaterialX_1.39_Overview_v5.png similarity index 100% rename from documents/Specification/inprog_v1.39/media/MaterialX_1.39_Overview_v5.png rename to documents/Specification/media/MaterialX_1.39_Overview_v5.png diff --git a/documents/Specification/inprog_v1.39/media/PBSdiagram.png b/documents/Specification/media/PBSdiagram.png similarity index 100% rename from documents/Specification/inprog_v1.39/media/PBSdiagram.png rename to documents/Specification/media/PBSdiagram.png diff --git a/documents/Specification/inprog_v1.39/media/nodegraph1.png b/documents/Specification/media/nodegraph1.png similarity index 100% rename from documents/Specification/inprog_v1.39/media/nodegraph1.png rename to documents/Specification/media/nodegraph1.png diff --git a/documents/Specification/inprog_v1.39/media/nodegraph2.png b/documents/Specification/media/nodegraph2.png similarity index 100% rename from documents/Specification/inprog_v1.39/media/nodegraph2.png rename to documents/Specification/media/nodegraph2.png diff --git a/documents/Specification/MaterialX.v1.38.PBRSpec.pdf b/documents/Specification/v1.38/MaterialX.v1.38.PBRSpec.pdf similarity index 100% rename from documents/Specification/MaterialX.v1.38.PBRSpec.pdf rename to documents/Specification/v1.38/MaterialX.v1.38.PBRSpec.pdf diff --git a/documents/Specification/MaterialX.v1.38.Spec.pdf b/documents/Specification/v1.38/MaterialX.v1.38.Spec.pdf similarity index 100% rename from documents/Specification/MaterialX.v1.38.Spec.pdf rename to documents/Specification/v1.38/MaterialX.v1.38.Spec.pdf diff --git a/documents/Specification/MaterialX.v1.38.Supplement.pdf b/documents/Specification/v1.38/MaterialX.v1.38.Supplement.pdf similarity index 100% rename from documents/Specification/MaterialX.v1.38.Supplement.pdf rename to documents/Specification/v1.38/MaterialX.v1.38.Supplement.pdf diff --git a/libraries/README.md b/libraries/README.md index bf932e9b47..15916f76e2 100644 --- a/libraries/README.md +++ b/libraries/README.md @@ -42,8 +42,18 @@ This folder contains the standard data libraries for MaterialX, providing declar - [lama](bxdf/lama) : Graph definitions of the [MaterialX Lama](https://rmanwiki.pixar.com/display/REN24/MaterialX+Lama) node set. ## Color Management Library -- MaterialX shader generation natively supports a small set of common spaces for input colors, with all color transforms implemented as language-independent MaterialX graphs. The canonical definitions of these color transforms may be found in the OpenColorIO configuration for [ACES 1.2](https://github.com/colour-science/OpenColorIO-Configs/tree/feature/aces-1.2-config/aces_1.2). - - lin_rec709, g18_rec709, g22_rec709, rec709_display, acescg (lin_ap1), g22_ap1, srgb_texture, lin_adobergb, adobergb +- MaterialX shader generation natively supports a small set of common spaces for input colors, with all color transforms implemented as language-independent MaterialX graphs.The canonical definitions of these color transforms may be found in the OpenColorIO configuration for [ACES 1.2](https://github.com/colour-science/OpenColorIO-Configs/tree/feature/aces-1.2-config/aces_1.2). + - lin_rec709 + - g18_rec709 + - g22_rec709 + - rec709_display + - acescg (lin_ap1) + - g22_ap1 + - srgb_texture + - lin_adobergb + - adobergb + - srgb_displayp3 + - lin_displayp3 - [cmlib](cmlib) - [cmlib_defs.mtlx](cmlib/cmlib_defs.mtlx) : Nodedef declarations. - [cmlib_ng.mtlx](cmlib/cmlib_ng.mtlx) : Nodegraph definitions. diff --git a/libraries/cmlib/cmlib_defs.mtlx b/libraries/cmlib/cmlib_defs.mtlx index 4f413e9baa..bee30267ef 100644 --- a/libraries/cmlib/cmlib_defs.mtlx +++ b/libraries/cmlib/cmlib_defs.mtlx @@ -8,82 +8,102 @@ --> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/cmlib/cmlib_ng.mtlx b/libraries/cmlib/cmlib_ng.mtlx index 8216504c02..8045e80a4d 100644 --- a/libraries/cmlib/cmlib_ng.mtlx +++ b/libraries/cmlib/cmlib_ng.mtlx @@ -294,4 +294,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/libraries/lights/genmsl/lights_genmsl_impl.mtlx b/libraries/lights/genmsl/lights_genmsl_impl.mtlx index 1b1782638c..90b4917a8a 100644 --- a/libraries/lights/genmsl/lights_genmsl_impl.mtlx +++ b/libraries/lights/genmsl/lights_genmsl_impl.mtlx @@ -1,5 +1,5 @@ - + diff --git a/libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl b/libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl index d66f3a8cfb..575991e28d 100644 --- a/libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl +++ b/libraries/pbrlib/genglsl/lib/mx_environment_fis.glsl @@ -23,6 +23,7 @@ vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distributio // Compute derived properties. float NdotV = clamp(V.z, M_FLOAT_EPS, 1.0); float avgAlpha = mx_average_alpha(alpha); + float G1V = mx_ggx_smith_G1(NdotV, avgAlpha); // Integrate outgoing radiance using filtered importance sampling. // http://cgg.mff.cuni.cz/~jaroslav/papers/2008-egsr-fis/2008-egsr-fis-final-embedded.pdf @@ -33,18 +34,16 @@ vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distributio vec2 Xi = mx_spherical_fibonacci(i, envRadianceSamples); // Compute the half vector and incoming light direction. - vec3 H = mx_ggx_importance_sample_NDF(Xi, alpha); + vec3 H = mx_ggx_importance_sample_VNDF(Xi, V, alpha); vec3 L = fd.refraction ? mx_refraction_solid_sphere(-V, H, fd.ior.x) : -reflect(V, H); // Compute dot products for this sample. - float NdotH = clamp(H.z, M_FLOAT_EPS, 1.0); float NdotL = clamp(L.z, M_FLOAT_EPS, 1.0); float VdotH = clamp(dot(V, H), M_FLOAT_EPS, 1.0); - float LdotH = VdotH; // Sample the environment light from the given direction. vec3 Lw = tangentToWorld * L; - float pdf = mx_ggx_PDF(H, LdotH, alpha); + float pdf = mx_ggx_NDF(H, alpha) * G1V / (4.0 * NdotV); float lod = mx_latlong_compute_lod(Lw, pdf, float($envRadianceMips - 1), envRadianceSamples); vec3 sampleColor = mx_latlong_map_lookup(Lw, $envMatrix, lod, $envRadiance); @@ -61,13 +60,15 @@ vec3 mx_environment_radiance(vec3 N, vec3 V, vec3 X, vec2 alpha, int distributio // From https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf // incidentLight = sampleColor * NdotL // microfacetSpecular = D * F * G / (4 * NdotL * NdotV) - // pdf = D * NdotH / (4 * VdotH) + // pdf = D * G1V / (4 * NdotV); // radiance = incidentLight * microfacetSpecular / pdf - radiance += sampleColor * FG * VdotH / (NdotV * NdotH); + radiance += sampleColor * FG; } - // Normalize and return the final radiance. - radiance /= float(envRadianceSamples); + // Apply the global component of the geometric term and normalize. + radiance /= G1V * float(envRadianceSamples); + + // Return the final radiance. return radiance; } diff --git a/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl b/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl index cd165c0615..63aba17869 100644 --- a/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl +++ b/libraries/pbrlib/genglsl/lib/mx_microfacet_specular.glsl @@ -61,48 +61,22 @@ float mx_ggx_NDF(vec3 H, vec2 alpha) return 1.0 / (M_PI * alpha.x * alpha.y * mx_square(denom)); } -// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf -// Appendix B.1 Equation 3 -float mx_ggx_PDF(vec3 H, float LdotH, vec2 alpha) -{ - float NdotH = H.z; - return mx_ggx_NDF(H, alpha) * NdotH / (4.0 * LdotH); -} - -// https://media.disneyanimation.com/uploads/production/publication_asset/48/asset/s2012_pbs_disney_brdf_notes_v3.pdf -// Appendix B.2 Equation 15 -vec3 mx_ggx_importance_sample_NDF(vec2 Xi, vec2 alpha) -{ - float phi = 2.0 * M_PI * Xi.x; - float tanTheta = sqrt(Xi.y / (1.0 - Xi.y)); - vec3 H = vec3(tanTheta * alpha.x * cos(phi), - tanTheta * alpha.y * sin(phi), - 1.0); - return normalize(H); -} - -// http://jcgt.org/published/0007/04/01/paper.pdf -// Appendix A Listing 1 +// https://ggx-research.github.io/publication/2023/06/09/publication-ggx.html vec3 mx_ggx_importance_sample_VNDF(vec2 Xi, vec3 V, vec2 alpha) { // Transform the view direction to the hemisphere configuration. V = normalize(vec3(V.xy * alpha, V.z)); - // Construct an orthonormal basis from the view direction. - float len = length(V.xy); - vec3 T1 = (len > 0.0) ? vec3(-V.y, V.x, 0.0) / len : vec3(1.0, 0.0, 0.0); - vec3 T2 = cross(V, T1); - - // Parameterization of the projected area. - float r = sqrt(Xi.y); + // Sample a spherical cap in (-V.z, 1]. float phi = 2.0 * M_PI * Xi.x; - float t1 = r * cos(phi); - float t2 = r * sin(phi); - float s = 0.5 * (1.0 + V.z); - t2 = (1.0 - s) * sqrt(1.0 - mx_square(t1)) + s * t2; - - // Reprojection onto hemisphere. - vec3 H = t1 * T1 + t2 * T2 + sqrt(max(0.0, 1.0 - mx_square(t1) - mx_square(t2))) * V; + float z = (1.0 - Xi.y) * (1.0 + V.z) - V.z; + float sinTheta = sqrt(clamp(1.0 - z * z, 0.0, 1.0)); + float x = sinTheta * cos(phi); + float y = sinTheta * sin(phi); + vec3 c = vec3(x, y, z); + + // Compute the microfacet normal. + vec3 H = c + V; // Transform the microfacet normal back to the ellipsoid configuration. H = normalize(vec3(H.xy * alpha, max(H.z, 0.0))); diff --git a/libraries/pbrlib/pbrlib_defs.mtlx b/libraries/pbrlib/pbrlib_defs.mtlx index 0a931cdc7d..e63625a45d 100644 --- a/libraries/pbrlib/pbrlib_defs.mtlx +++ b/libraries/pbrlib/pbrlib_defs.mtlx @@ -76,8 +76,8 @@ --> - - + + diff --git a/libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx b/libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx index eb1e60167f..ab0d776936 100644 --- a/libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx +++ b/libraries/stdlib/genmdl/stdlib_genmdl_impl.mtlx @@ -614,8 +614,8 @@ - - + + diff --git a/libraries/stdlib/stdlib_defs.mtlx b/libraries/stdlib/stdlib_defs.mtlx index a21f771127..3ffe235b2b 100644 --- a/libraries/stdlib/stdlib_defs.mtlx +++ b/libraries/stdlib/stdlib_defs.mtlx @@ -295,7 +295,7 @@ Node: Supplemental Node Samples data from three images, or layers within multi-layer images, and projects a tiled representation of the images along each of the three respective coordinate axes, computing - a weighted blend of the three samples using the geometric normal. + an adjustable weighted blend of the three samples using the geometric normal. --> @@ -307,6 +307,7 @@ + @@ -323,6 +324,7 @@ + @@ -339,6 +341,7 @@ + @@ -355,6 +358,7 @@ + @@ -371,6 +375,7 @@ + @@ -387,6 +392,7 @@ + @@ -1024,6 +1030,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + --> diff --git a/source/MaterialXGenShader/ShaderGraph.cpp b/source/MaterialXGenShader/ShaderGraph.cpp index 4fd92c11aa..e6ad6eca29 100644 --- a/source/MaterialXGenShader/ShaderGraph.cpp +++ b/source/MaterialXGenShader/ShaderGraph.cpp @@ -1073,7 +1073,8 @@ void ShaderGraph::optimize(GenContext& context) if (numEdits > 0) { - std::set usedNodes; + std::set usedNodesSet; + std::vector usedNodesVec; // Traverse the graph to find nodes still in use for (ShaderGraphOutputSocket* outputSocket : getOutputSockets()) @@ -1084,7 +1085,12 @@ void ShaderGraph::optimize(GenContext& context) { for (ShaderGraphEdge edge : ShaderGraph::traverseUpstream(upstreamPort)) { - usedNodes.insert(edge.upstream->getNode()); + ShaderNode* node = edge.upstream->getNode(); + if (usedNodesSet.count(node) == 0) + { + usedNodesSet.insert(node); + usedNodesVec.push_back(node); + } } } } @@ -1092,7 +1098,7 @@ void ShaderGraph::optimize(GenContext& context) // Remove any unused nodes for (ShaderNode* node : _nodeOrder) { - if (usedNodes.count(node) == 0) + if (usedNodesSet.count(node) == 0) { // Break all connections disconnect(node); @@ -1102,8 +1108,7 @@ void ShaderGraph::optimize(GenContext& context) } } - _nodeOrder.resize(usedNodes.size()); - _nodeOrder.assign(usedNodes.begin(), usedNodes.end()); + _nodeOrder = usedNodesVec; } } diff --git a/source/MaterialXGraphEditor/Graph.cpp b/source/MaterialXGraphEditor/Graph.cpp index 9710640130..9e3fbccc40 100644 --- a/source/MaterialXGraphEditor/Graph.cpp +++ b/source/MaterialXGraphEditor/Graph.cpp @@ -481,12 +481,18 @@ ImVec2 Graph::layoutPosition(UiNodePtr layoutNode, ImVec2 startingPos, bool init // don't set position of group nodes if (node->getMessage().empty()) { - float x = std::stof(node->getMxElement()->getAttribute("xpos")); - float y = std::stof(node->getMxElement()->getAttribute("ypos")); - x *= DEFAULT_NODE_SIZE.x; - y *= DEFAULT_NODE_SIZE.y; - ed::SetNodePosition(node->getId(), ImVec2(x, y)); - node->setPos(ImVec2(x, y)); + if (node->getMxElement()->hasAttribute("xpos")) + { + float x = std::stof(node->getMxElement()->getAttribute("xpos")); + if (node->getMxElement()->hasAttribute("ypos")) + { + float y = std::stof(node->getMxElement()->getAttribute("ypos")); + x *= DEFAULT_NODE_SIZE.x; + y *= DEFAULT_NODE_SIZE.y; + ed::SetNodePosition(node->getId(), ImVec2(x, y)); + node->setPos(ImVec2(x, y)); + } + } } } } @@ -831,7 +837,7 @@ void Graph::updateMaterials(mx::InputPtr input, mx::ValuePtr 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(); + std::string inName = !uiProperties.uiName.empty() ? uiProperties.uiName : input->getName(); ImGui::PushItemWidth(-1); mx::ValuePtr minVal = uiProperties.uiMin; @@ -2487,6 +2493,10 @@ void Graph::AddLink(ed::PinId inputPinId, ed::PinId outputPinId) { pin->_input->setConnectedOutput(_graphNodes[upNode]->getOutput()); } + else if (_graphNodes[upNode]->getInput() != nullptr) + { + pin->_input->setInterfaceName(_graphNodes[upNode]->getName()); + } else { // node graph @@ -2936,7 +2946,7 @@ void Graph::graphButtons() } ImGui::EndMenu(); } - + if (ImGui::BeginMenu("Graph")) { if (ImGui::MenuItem("Auto Layout")) @@ -2945,7 +2955,7 @@ void Graph::graphButtons() } ImGui::EndMenu(); } - + if (ImGui::BeginMenu("Viewer")) { if (ImGui::MenuItem("Load Geometry")) @@ -2953,7 +2963,7 @@ void Graph::graphButtons() loadGeometry(); } ImGui::EndMenu(); - } + } if (ImGui::Button("Help")) { @@ -3141,7 +3151,7 @@ void Graph::propertyEditor() } const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing() * 1.3f; - const int SCROLL_LINE_COUNT= 20; + const int SCROLL_LINE_COUNT = 20; ImGuiTableFlags tableFlags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings | ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_NoBordersInBody; @@ -3241,7 +3251,7 @@ void Graph::propertyEditor() if (count) { bool haveTable = ImGui::BeginTable("inputs_input_table", 2, tableFlags, - ImVec2(0.0f, TEXT_BASE_HEIGHT * std::min(SCROLL_LINE_COUNT, count))); + ImVec2(0.0f, TEXT_BASE_HEIGHT * std::min(SCROLL_LINE_COUNT, count))); if (haveTable) { ImGui::SetWindowFontScale(_fontScale); @@ -3298,7 +3308,7 @@ void Graph::propertyEditor() if (count) { bool haveTable = ImGui::BeginTable("inputs_nodegraph_table", 2, tableFlags, - ImVec2(0.0f, TEXT_BASE_HEIGHT * std::min(SCROLL_LINE_COUNT, count))); + ImVec2(0.0f, TEXT_BASE_HEIGHT * std::min(SCROLL_LINE_COUNT, count))); if (haveTable) { ImGui::SetWindowFontScale(_fontScale); @@ -3361,7 +3371,7 @@ void Graph::propertyEditor() // Helper to display basic user controls. void Graph::showHelp() const { - ImGui::Text("MATERIALX GRAPH EDITOR HELP"); + ImGui::Text("MATERIALX GRAPH EDITOR HELP"); if (ImGui::CollapsingHeader("Graph")) { if (ImGui::TreeNode("Navigation")) @@ -3405,6 +3415,7 @@ void Graph::showHelp() const void Graph::addNodePopup(bool cursor) { bool open_AddPopup = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows) && ImGui::IsKeyReleased(ImGuiKey_Tab); + static char input[32]{ "" }; if (open_AddPopup) { cursor = true; @@ -3414,7 +3425,6 @@ void Graph::addNodePopup(bool cursor) { ImGui::Text("Add Node"); ImGui::Separator(); - static char input[32]{ "" }; if (cursor) { ImGui::SetKeyboardFocusHere(); @@ -3433,6 +3443,9 @@ 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 + 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))) @@ -3812,12 +3825,9 @@ void Graph::drawGraph(ImVec2 mousePos) if (outputNum.size() == 0 && _graphNodes[0]->getMxElement()) { - if (_graphNodes[0]->getMxElement()->hasAttribute("xpos")) + for (UiNodePtr node : _graphNodes) { - for (UiNodePtr node : _graphNodes) - { - layoutPosition(node, ImVec2(0, 0), true, 0); - } + layoutPosition(node, ImVec2(0, 0), true, 0); } } } diff --git a/source/MaterialXRenderOsl/OslRenderer.cpp b/source/MaterialXRenderOsl/OslRenderer.cpp index bf258b6762..14dd81a090 100644 --- a/source/MaterialXRenderOsl/OslRenderer.cpp +++ b/source/MaterialXRenderOsl/OslRenderer.cpp @@ -143,6 +143,12 @@ void OslRenderer::renderOSL(const FilePath& dirPath, const string& shaderName, c " does not include proper tokens for rendering"); } + // Set the working directory for rendering. + FileSearchPath searchPath = getDefaultDataSearchPath(); + FilePath rootPath = searchPath.isEmpty() ? FilePath() : searchPath[0]; + FilePath origWorkingPath = FilePath::getCurrentPath(); + rootPath.setCurrentPath(); + // Write scene file const string sceneFileName("scene_template.xml"); std::ofstream shaderFileStream; @@ -178,6 +184,9 @@ void OslRenderer::renderOSL(const FilePath& dirPath, const string& shaderName, c } } + // Restore the working directory after rendering. + origWorkingPath.setCurrentPath(); + // Report errors on a non-zero return value. if (returnValue) { diff --git a/source/MaterialXTest/Main.cpp b/source/MaterialXTest/Main.cpp index 414e5f88b1..4e53d943f4 100644 --- a/source/MaterialXTest/Main.cpp +++ b/source/MaterialXTest/Main.cpp @@ -13,10 +13,8 @@ namespace mx = MaterialX; int main(int argc, char* const argv[]) { Catch::Session session; - -#ifndef _DEBUG session.configData().showDurations = Catch::ShowDurations::Always; -#endif + #ifdef CATCH_PLATFORM_WINDOWS BOOL inDebugger = IsDebuggerPresent(); if (inDebugger) @@ -29,18 +27,6 @@ int main(int argc, char* const argv[]) } #endif - // If the current path has no valid resources folder, as can occur when launching the - // test suite from an IDE, then align the current path with the module path. - mx::FilePath resourcesPath = mx::FilePath::getCurrentPath() / "resources"; - if (!resourcesPath.exists()) - { - resourcesPath = mx::FilePath::getModulePath().getParentPath() / "resources"; - if (resourcesPath.exists()) - { - resourcesPath.getParentPath().setCurrentPath(); - } - } - int returnCode = session.applyCommandLine(argc, argv); if (returnCode != 0) { diff --git a/source/MaterialXTest/MaterialXCore/Document.cpp b/source/MaterialXTest/MaterialXCore/Document.cpp index 5ae6f77c97..29092071cf 100644 --- a/source/MaterialXTest/MaterialXCore/Document.cpp +++ b/source/MaterialXTest/MaterialXCore/Document.cpp @@ -111,22 +111,24 @@ TEST_CASE("Document", "[document]") TEST_CASE("Version", "[document]") { - mx::DocumentPtr doc = mx::createDocument(); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_ng.mtlx"), doc); - mx::FileSearchPath searchPath("resources/Materials/TestSuite/stdlib/upgrade/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::DocumentPtr stdlib = mx::createDocument(); + mx::loadLibraries({ "libraries" }, searchPath, stdlib); + searchPath.append(searchPath.find("resources/Materials/TestSuite/stdlib/upgrade")); // 1.36 to 1.37 { + mx::DocumentPtr doc = mx::createDocument(); mx::readFromXmlFile(doc, "1_36_to_1_37.mtlx", searchPath); + doc->importLibrary(stdlib); REQUIRE(doc->validate()); mx::XmlWriteOptions writeOptions; writeOptions.writeXIncludeEnable = true; - mx::writeToXmlFile(doc, "1_36_to_1_37_updated.mtlx", &writeOptions); + std::string xmlString = mx::writeToXmlString(doc, &writeOptions); mx::DocumentPtr doc2 = mx::createDocument(); - mx::readFromXmlFile(doc2, "1_36_to_1_37_updated.mtlx"); + mx::readFromXmlString(doc2, xmlString); REQUIRE(doc2->validate()); // Check conversion to desired types occurred @@ -160,18 +162,17 @@ TEST_CASE("Version", "[document]") // 1.37 to 1.38 { - doc = mx::createDocument(); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_ng.mtlx"), doc); + mx::DocumentPtr doc = mx::createDocument(); mx::readFromXmlFile(doc, "1_37_to_1_38.mtlx", searchPath); + doc->importLibrary(stdlib); REQUIRE(doc->validate()); mx::XmlWriteOptions writeOptions; writeOptions.writeXIncludeEnable = false; - mx::writeToXmlFile(doc, "1_37_to_1_38_updated.mtlx", &writeOptions); + std::string xmlString = mx::writeToXmlString(doc, &writeOptions); mx::DocumentPtr doc2 = mx::createDocument(); - mx::readFromXmlFile(doc2, "1_37_to_1_38_updated.mtlx"); + mx::readFromXmlString(doc2, xmlString); REQUIRE(doc2->validate()); // atan2 test diff --git a/source/MaterialXTest/MaterialXCore/Material.cpp b/source/MaterialXTest/MaterialXCore/Material.cpp index e5be7c5f9e..21e9d7db0d 100644 --- a/source/MaterialXTest/MaterialXCore/Material.cpp +++ b/source/MaterialXTest/MaterialXCore/Material.cpp @@ -61,12 +61,9 @@ TEST_CASE("Material", "[material]") TEST_CASE("Material Discovery", "[material]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - - const mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath searchPath(currentPath / mx::FilePath("resources/Materials/TestSuite")); - mx::FilePath filename = "stdlib/materials/material_node_discovery.mtlx"; - mx::readFromXmlFile(doc, filename, searchPath); + mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/materials/material_node_discovery.mtlx", searchPath); // 1. Find all materials referenced by material assignments // which are found in connected nodegraphs diff --git a/source/MaterialXTest/MaterialXCore/Node.cpp b/source/MaterialXTest/MaterialXCore/Node.cpp index 9487ab286d..2477d9f486 100644 --- a/source/MaterialXTest/MaterialXCore/Node.cpp +++ b/source/MaterialXTest/MaterialXCore/Node.cpp @@ -139,8 +139,9 @@ TEST_CASE("Node", "[node]") TEST_CASE("Flatten", "[nodegraph]") { // Read an example containing graph-based custom nodes. + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/shader/surface.mtlx"); + mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/shader/surface.mtlx", searchPath); REQUIRE(doc->validate()); // Count root-level, nested, and custom nodes. @@ -174,8 +175,8 @@ TEST_CASE("Flatten", "[nodegraph]") TEST_CASE("Inheritance", "[nodedef]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - mx::FileSearchPath searchPath(mx::FilePath::getCurrentPath()); mx::loadLibraries({ "libraries" }, searchPath, doc); REQUIRE(doc->validate()); auto nodedef = doc->getNodeDef("ND_standard_surface_surfaceshader"); @@ -559,13 +560,12 @@ TEST_CASE("Organization", "[nodegraph]") TEST_CASE("Tokens", "[nodegraph]") { - mx::DocumentPtr doc = mx::createDocument(); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_ng.mtlx"), doc); - mx::FileSearchPath searchPath("resources/Materials/TestSuite/stdlib/texture/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::DocumentPtr stdlib = mx::createDocument(); + mx::loadLibraries({ "libraries" }, searchPath, stdlib); - mx::readFromXmlFile(doc, "tokenGraph.mtlx", searchPath); - doc->validate(); + mx::DocumentPtr doc = mx::createDocument(); + mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/texture/tokenGraph.mtlx", searchPath); mx::StringVec graphNames = { "Tokenized_Image_2k_png", "Tokenized_Image_4k_jpg" }; mx::StringVec resolutionStrings = { "2k", "4k" }; @@ -597,13 +597,13 @@ TEST_CASE("Tokens", "[nodegraph]") TEST_CASE("Node Definition Creation", "[nodedef]") { - mx::DocumentPtr doc = mx::createDocument(); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_ng.mtlx"), doc); - mx::FileSearchPath searchPath("resources/Materials/TestSuite/stdlib/definition/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::DocumentPtr stdlib = mx::createDocument(); + mx::loadLibraries({ "libraries" }, searchPath, stdlib); - mx::readFromXmlFile(doc, "definition_from_nodegraph.mtlx", searchPath); - REQUIRE(doc->validate()); + mx::DocumentPtr doc = mx::createDocument(); + mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/definition/definition_from_nodegraph.mtlx", searchPath); + doc->importLibrary(stdlib); mx::NodeGraphPtr graph = doc->getNodeGraph("test_colorcorrect"); REQUIRE(graph); @@ -721,7 +721,5 @@ TEST_CASE("Node Definition Creation", "[nodedef]") } REQUIRE(findDefault); } - REQUIRE(doc->validate()); - mx::writeToXmlFile(doc, "definition_from_nodegraph_out.mtlx"); } diff --git a/source/MaterialXTest/MaterialXCore/Traversal.cpp b/source/MaterialXTest/MaterialXCore/Traversal.cpp index 3ffc5a3485..f65228c286 100644 --- a/source/MaterialXTest/MaterialXCore/Traversal.cpp +++ b/source/MaterialXTest/MaterialXCore/Traversal.cpp @@ -176,14 +176,9 @@ TEST_CASE("IntraGraph Traversal", "[traversal]") TEST_CASE("InterGraph Traversal", "[traversal]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath searchPath(currentPath); - mx::loadLibraries({ "libraries" }, searchPath, doc); - - mx::FilePath testFile = currentPath / mx::FilePath("resources/Materials/TestSuite/stdlib/nodegraph_inputs/nodegraph_nodegraph.mtlx"); - mx::readFromXmlFile(doc, testFile, searchPath); - REQUIRE(doc->validate()); + mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/nodegraph_inputs/nodegraph_nodegraph.mtlx", searchPath); for (mx::NodeGraphPtr graph : doc->getNodeGraphs()) { diff --git a/source/MaterialXTest/MaterialXCore/Unit.cpp b/source/MaterialXTest/MaterialXCore/Unit.cpp index 80ce2be0c4..01cefe98c6 100644 --- a/source/MaterialXTest/MaterialXCore/Unit.cpp +++ b/source/MaterialXTest/MaterialXCore/Unit.cpp @@ -17,8 +17,10 @@ const float EPSILON = 1e-4f; TEST_CASE("UnitAttribute", "[unit]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); + mx::loadLibraries({ "libraries" }, searchPath, doc); + std::vector unitTypeDefs = doc->getUnitTypeDefs(); REQUIRE(!unitTypeDefs.empty()); @@ -60,8 +62,9 @@ TEST_CASE("UnitAttribute", "[unit]") TEST_CASE("UnitEvaluation", "[unit]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); + mx::loadLibraries({ "libraries" }, searchPath, doc); // // Test distance converter @@ -117,11 +120,11 @@ TEST_CASE("UnitEvaluation", "[unit]") TEST_CASE("UnitDocument", "[unit]") { - mx::FilePath libraryPath("libraries/stdlib"); - mx::FilePath examplesPath("resources/Materials/TestSuite/stdlib/units"); - std::string searchPath = libraryPath.asString() + - mx::PATH_LIST_SEPARATOR + - examplesPath.asString(); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::DocumentPtr stdlib = mx::createDocument(); + mx::loadLibraries({ "libraries" }, searchPath, stdlib); + mx::FilePath examplesPath = searchPath.find("resources/Materials/TestSuite/stdlib/units"); + searchPath.append(examplesPath); static const std::string DISTANCE_DEFAULT("meter"); @@ -130,7 +133,7 @@ TEST_CASE("UnitDocument", "[unit]") { mx::DocumentPtr doc = mx::createDocument(); mx::readFromXmlFile(doc, filename, searchPath); - mx::loadLibrary(mx::FilePath::getCurrentPath() / mx::FilePath("libraries/stdlib/stdlib_defs.mtlx"), doc); + doc->importLibrary(stdlib); mx::UnitTypeDefPtr distanceTypeDef = doc->getUnitTypeDef("distance"); REQUIRE(distanceTypeDef); @@ -149,11 +152,14 @@ TEST_CASE("UnitDocument", "[unit]") mx::NodePtr pNode = elem->asA(); if (pNode) { - if (pNode->getInputCount()) { - for (mx::InputPtr input : pNode->getInputs()) { + if (pNode->getInputCount()) + { + for (mx::InputPtr input : pNode->getInputs()) + { const std::string type = input->getType(); const mx::ValuePtr value = input->getValue(); - if (input->hasUnit() && value) { + if (input->hasUnit() && value) + { if (type == "float") { float originalval = value->asA(); diff --git a/source/MaterialXTest/MaterialXFormat/File.cpp b/source/MaterialXTest/MaterialXFormat/File.cpp index 26515f0aec..83e0e01f34 100644 --- a/source/MaterialXTest/MaterialXFormat/File.cpp +++ b/source/MaterialXTest/MaterialXFormat/File.cpp @@ -32,32 +32,27 @@ TEST_CASE("Syntactic operations", "[file]") TEST_CASE("File system operations", "[file]") { - mx::StringVec filenames = + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePathVec examplePaths = { "libraries/stdlib/stdlib_defs.mtlx", "resources/Materials/Examples/StandardSurface/standard_surface_brass_tiled.mtlx", "resources/Materials/Examples/StandardSurface/standard_surface_marble_solid.mtlx", }; - - for (const std::string& filename : filenames) + for (const mx::FilePath& path : examplePaths) { - mx::FilePath path(filename); - REQUIRE(path.exists()); - REQUIRE(mx::FileSearchPath().find(path).exists()); + REQUIRE(searchPath.find(path).exists()); } - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FilePath modulePath = mx::FilePath::getModulePath(); - bool expectedPaths = currentPath == modulePath || - currentPath == modulePath.getParentPath(); - REQUIRE(expectedPaths); + REQUIRE(mx::FilePath::getCurrentPath().exists()); + REQUIRE(mx::FilePath::getModulePath().exists()); } TEST_CASE("File search path operations", "[file]") { - mx::FileSearchPath searchPath = "libraries/stdlib" + - mx::PATH_LIST_SEPARATOR + - "resources/Materials/Examples/StandardSurface"; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + searchPath.append(searchPath.find("libraries/stdlib")); + searchPath.append(searchPath.find("resources/Materials/Examples/StandardSurface")); mx::FilePathVec filenames = { @@ -102,10 +97,8 @@ TEST_CASE("Flatten filenames", "[file]") image2->setInputValue("file", "brass_color.jpg", mx::FILENAME_TYPE_STRING); // 2. Test resolving to absolute paths - mx::FilePath rootPath(mx::FilePath::getCurrentPath()); - - mx::FileSearchPath searchPath; - searchPath.append(rootPath); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath rootPath = searchPath.isEmpty() ? mx::FilePath() : searchPath[0]; mx::flattenFilenames(doc1, searchPath); REQUIRE(nodeGraph->getFilePrefix() == mx::EMPTY_STRING); diff --git a/source/MaterialXTest/MaterialXFormat/XmlIo.cpp b/source/MaterialXTest/MaterialXFormat/XmlIo.cpp index 1aea085bb2..1336a080c4 100644 --- a/source/MaterialXTest/MaterialXFormat/XmlIo.cpp +++ b/source/MaterialXTest/MaterialXFormat/XmlIo.cpp @@ -14,11 +14,11 @@ namespace mx = MaterialX; TEST_CASE("Load content", "[xmlio]") { - mx::FilePath libraryPath("libraries/stdlib"); - mx::FilePath examplesPath("resources/Materials/Examples/StandardSurface"); - mx::FileSearchPath searchPath = libraryPath.asString() + - mx::PATH_LIST_SEPARATOR + - examplesPath.asString(); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath libraryPath = searchPath.find("libraries/stdlib"); + mx::FilePath examplesPath = searchPath.find("resources/Materials/Examples/StandardSurface"); + searchPath.append(libraryPath); + searchPath.append(examplesPath); // Read the standard library. std::vector libs; @@ -242,7 +242,8 @@ TEST_CASE("Load content", "[xmlio]") TEST_CASE("Comments and newlines", "[xmlio]") { - mx::FilePath testPath("resources/Materials/Examples/StandardSurface/standard_surface_chess_set.mtlx"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath testPath = searchPath.find("resources/Materials/Examples/StandardSurface/standard_surface_chess_set.mtlx"); // Read the example file into an XML string buffer. std::string origXml = mx::readFile(testPath); diff --git a/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.cpp b/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.cpp index a357651cb7..54b6fbf420 100644 --- a/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.cpp +++ b/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.cpp @@ -91,10 +91,7 @@ TEST_CASE("GenShader: GLSL Implementation Check", "[genglsl]") TEST_CASE("GenShader: GLSL Unique Names", "[genglsl]") { mx::GenContext context(mx::GlslShaderGenerator::create()); - - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - context.registerSourceCodeSearchPath(currentPath); - + context.registerSourceCodeSearchPath(mx::getDefaultDataSearchPath()); GenShaderUtil::testUniqueNames(context, mx::Stage::PIXEL); } @@ -102,8 +99,7 @@ TEST_CASE("GenShader: Bind Light Shaders", "[genglsl]") { mx::DocumentPtr doc = mx::createDocument(); - mx::FileSearchPath searchPath; - searchPath.append(mx::FilePath::getCurrentPath()); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); loadLibraries({ "libraries" }, searchPath, doc); mx::NodeDefPtr pointLightShader = doc->getNodeDef("ND_point_light"); @@ -146,20 +142,17 @@ const std::string GlslTypeToString(GlslType e) throw() static void generateGlslCode(GlslType type = GlslType::Glsl400) { - mx::FilePathVec testRootPaths; - testRootPaths.push_back("resources/Materials/TestSuite"); - testRootPaths.push_back("resources/Materials/Examples/StandardSurface"); - const mx::FilePath libSearchPath = mx::FilePath::getCurrentPath(); - const mx::FileSearchPath srcSearchPath(libSearchPath.asString()); - bool writeShadersToDisk = false; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); - const mx::GenOptions genOptions; - mx::FilePath optionsFilePath("resources/Materials/TestSuite/_options.mtlx"); + mx::FilePathVec testRootPaths; + testRootPaths.push_back(searchPath.find("resources/Materials/TestSuite")); + testRootPaths.push_back(searchPath.find("resources/Materials/Examples/StandardSurface")); const mx::FilePath logPath("genglsl_" + GlslTypeToString(type) + "_generate_test.txt"); + bool writeShadersToDisk = false; GlslShaderGeneratorTester tester((type == GlslType::GlslVulkan) ? mx::VkShaderGenerator::create() : mx::GlslShaderGenerator::create(), - testRootPaths, libSearchPath, srcSearchPath, logPath, writeShadersToDisk); + testRootPaths, searchPath, logPath, writeShadersToDisk); // Add resource binding context for glsl 4.20 if (type == GlslType::Glsl420) @@ -170,6 +163,8 @@ static void generateGlslCode(GlslType type = GlslType::Glsl400) tester.addUserData(mx::HW::USER_DATA_BINDING_CONTEXT, glslresourceBinding); } + const mx::GenOptions genOptions; + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); tester.validate(genOptions, optionsFilePath); } diff --git a/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.h b/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.h index 5868d7b5b4..97ad62f0e4 100644 --- a/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.h +++ b/source/MaterialXTest/MaterialXGenGlsl/GenGlsl.h @@ -19,9 +19,8 @@ class GlslShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester using ParentClass = GenShaderUtil::ShaderGeneratorTester; GlslShaderGeneratorTester(mx::ShaderGeneratorPtr shaderGenerator, const mx::FilePathVec& testRootPaths, - const mx::FilePath& libSearchPath, const mx::FileSearchPath& srcSearchPath, - const mx::FilePath& logFilePath, bool writeShadersToDisk) : - GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, libSearchPath, srcSearchPath, logFilePath, writeShadersToDisk) + const mx::FileSearchPath& searchPath, const mx::FilePath& logFilePath, bool writeShadersToDisk) : + GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, searchPath, logFilePath, writeShadersToDisk) {} void setTestStages() override @@ -42,7 +41,7 @@ class GlslShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester { ParentClass::setupDependentLibraries(); - mx::FilePath lightDir = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/TestSuite/lights"); + mx::FilePath lightDir = mx::getDefaultDataSearchPath().find("resources/Materials/TestSuite/lights"); loadLibrary(lightDir / mx::FilePath("light_compound_test.mtlx"), _dependLib); loadLibrary(lightDir / mx::FilePath("light_rig_test_1.mtlx"), _dependLib); } diff --git a/source/MaterialXTest/MaterialXGenMdl/GenMdl.cpp b/source/MaterialXTest/MaterialXGenMdl/GenMdl.cpp index c58d754b92..fbe19a5957 100644 --- a/source/MaterialXTest/MaterialXGenMdl/GenMdl.cpp +++ b/source/MaterialXTest/MaterialXGenMdl/GenMdl.cpp @@ -112,9 +112,9 @@ class MdlStringResolver : public mx::StringResolver std::ofstream* logFile, std::initializer_list additionalSearchpaths) { - // Note: These paths are based on - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FilePath coreModulePath = currentPath / std::string(MATERIALX_INSTALL_MDL_MODULE_PATH) / "mdl"; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath rootPath = searchPath.isEmpty() ? mx::FilePath() : searchPath[0]; + mx::FilePath coreModulePath = rootPath / std::string(MATERIALX_INSTALL_MDL_MODULE_PATH) / "mdl"; mx::FilePath coreModulePath2 = coreModulePath / mx::FilePath("materialx"); // use the source search paths as base @@ -130,7 +130,7 @@ class MdlStringResolver : public mx::StringResolver } // add additional search paths for the tests - paths.append(currentPath); + paths.append(rootPath); paths.append(coreModulePath); paths.append(coreModulePath2); for (const auto& addSp : additionalSearchpaths) @@ -209,7 +209,7 @@ void MdlShaderGeneratorTester::preprocessDocument(mx::DocumentPtr doc) if (!_mdlCustomResolver) _mdlCustomResolver = MdlStringResolver::create(); - _mdlCustomResolver->initialize(doc, &_logFile, { _libSearchPath.asString() }); + _mdlCustomResolver->initialize(doc, &_logFile, { _searchPath.asString() }); mx::flattenFilenames(doc, _mdlCustomResolver->getMdlSearchPaths(), _mdlCustomResolver); } @@ -282,18 +282,19 @@ void MdlShaderGeneratorTester::compileSource(const std::vector& so renderCommand += " --mdl_path \"" + moduleToTestPath.asString() + "\""; renderCommand += " --mdl_path \"" + moduleToTestPath.getParentPath().asString() + "\""; - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath rootPath = searchPath.isEmpty() ? mx::FilePath() : searchPath[0]; // set environment - std::string iblFile = (currentPath / "resources/lights/san_giuseppe_bridge.hdr").asString(); + std::string iblFile = (rootPath / "resources/lights/san_giuseppe_bridge.hdr").asString(); renderCommand += " --hdr \"" + iblFile + "\" --hdr_rotate 90"; // set scene renderCommand += " --uv_scale 0.5 1.0 --uv_offset 0.0 0.0 --uv_repeat"; renderCommand += " --camera 0 0 3 0 0 0 --fov 45"; // set the material - // compute the MDL module name as fully qualified name wrt to the "currentPath/resources" as MDL search path + // compute the MDL module name as fully qualified name wrt to the "rootPath/resources" as MDL search path std::string mdlModuleName = "::resources::"; - for (size_t s = currentPath.size() + 1; s < moduleToTestPath.size(); ++s) + for (size_t s = rootPath.size() + 1; s < moduleToTestPath.size(); ++s) { mdlModuleName += moduleToTestPath[s] + "::"; } @@ -348,24 +349,22 @@ void MdlShaderGeneratorTester::compileSource(const std::vector& so TEST_CASE("GenShader: MDL Shader Generation", "[genmdl]") { - mx::FilePathVec testRootPaths; - testRootPaths.push_back("resources/Materials/TestSuite"); - testRootPaths.push_back("resources/Materials/Examples"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); - const mx::FilePath libSearchPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath srcSearchPath(libSearchPath.asString()); - srcSearchPath.append(libSearchPath / mx::FilePath("libraries/stdlib/genmdl")); + mx::FilePathVec testRootPaths; + testRootPaths.push_back(searchPath.find("resources/Materials/TestSuite")); + testRootPaths.push_back(searchPath.find("resources/Materials/Examples/StandardSurface")); const mx::FilePath logPath("genmdl_mdl_generate_test.txt"); // Write shaders and try to compile only if mdlc exe specified. std::string mdlcExec(MATERIALX_MDLC_EXECUTABLE); bool writeShadersToDisk = !mdlcExec.empty(); - MdlShaderGeneratorTester tester(mx::MdlShaderGenerator::create(), testRootPaths, libSearchPath, srcSearchPath, logPath, writeShadersToDisk); + MdlShaderGeneratorTester tester(mx::MdlShaderGenerator::create(), testRootPaths, searchPath, logPath, writeShadersToDisk); tester.addSkipLibraryFiles(); mx::GenOptions genOptions; genOptions.targetColorSpaceOverride = "lin_rec709"; - mx::FilePath optionsFilePath("resources/Materials/TestSuite/_options.mtlx"); + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); tester.validate(genOptions, optionsFilePath); } diff --git a/source/MaterialXTest/MaterialXGenMdl/GenMdl.h b/source/MaterialXTest/MaterialXGenMdl/GenMdl.h index 04425262d5..179688a05a 100644 --- a/source/MaterialXTest/MaterialXGenMdl/GenMdl.h +++ b/source/MaterialXTest/MaterialXGenMdl/GenMdl.h @@ -21,9 +21,8 @@ class MdlShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester using ParentClass = GenShaderUtil::ShaderGeneratorTester; MdlShaderGeneratorTester(mx::ShaderGeneratorPtr shaderGenerator, const std::vector& testRootPaths, - const mx::FilePath& libSearchPath, const mx::FileSearchPath& srcSearchPath, - const mx::FilePath& logFilePath, bool writeShadersToDisk) : - GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, libSearchPath, srcSearchPath, logFilePath, writeShadersToDisk) + const mx::FileSearchPath& searchPath, const mx::FilePath& logFilePath, bool writeShadersToDisk) : + GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, searchPath, logFilePath, writeShadersToDisk) {} void setTestStages() override diff --git a/source/MaterialXTest/MaterialXGenMsl/GenMsl.cpp b/source/MaterialXTest/MaterialXGenMsl/GenMsl.cpp index 8393acfca7..e3bcc7cfa1 100644 --- a/source/MaterialXTest/MaterialXGenMsl/GenMsl.cpp +++ b/source/MaterialXTest/MaterialXGenMsl/GenMsl.cpp @@ -90,20 +90,15 @@ TEST_CASE("GenShader: MSL Implementation Check", "[genmsl]") TEST_CASE("GenShader: MSL Unique Names", "[genmsl]") { mx::GenContext context(mx::MslShaderGenerator::create()); - - mx::FilePath searchPath = mx::FilePath::getCurrentPath() / mx::FilePath("libraries"); - context.registerSourceCodeSearchPath(searchPath); - + context.registerSourceCodeSearchPath(mx::getDefaultDataSearchPath()); GenShaderUtil::testUniqueNames(context, mx::Stage::PIXEL); } TEST_CASE("GenShader: MSL Bind Light Shaders", "[genmsl]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - - mx::FileSearchPath searchPath; - searchPath.append(mx::FilePath::getCurrentPath() / mx::FilePath("libraries")); - loadLibraries({ "targets", "stdlib", "pbrlib", "lights" }, searchPath, doc); + mx::loadLibraries({ "libraries" }, searchPath, doc); mx::NodeDefPtr pointLightShader = doc->getNodeDef("ND_point_light"); mx::NodeDefPtr spotLightShader = doc->getNodeDef("ND_spot_light"); @@ -111,7 +106,7 @@ TEST_CASE("GenShader: MSL Bind Light Shaders", "[genmsl]") REQUIRE(spotLightShader != nullptr); mx::GenContext context(mx::MslShaderGenerator::create()); - context.registerSourceCodeSearchPath(mx::FilePath::getCurrentPath() / mx::FilePath("libraries")); + context.registerSourceCodeSearchPath(searchPath); mx::HwShaderGenerator::bindLightShader(*pointLightShader, 42, context); REQUIRE_THROWS(mx::HwShaderGenerator::bindLightShader(*spotLightShader, 42, context)); @@ -124,27 +119,22 @@ TEST_CASE("GenShader: MSL Bind Light Shaders", "[genmsl]") static void generateMslCode() { - 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); - const mx::FilePath libSearchPath = mx::FilePath::getCurrentPath() / mx::FilePath("libraries"); - const mx::FileSearchPath srcSearchPath(libSearchPath.asString()); - bool writeShadersToDisk = false; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); - const mx::GenOptions genOptions; - mx::FilePath optionsFilePath = testRootPath / mx::FilePath("_options.mtlx"); + mx::FilePathVec testRootPaths; + testRootPaths.push_back(searchPath.find("resources/Materials/TestSuite")); + testRootPaths.push_back(searchPath.find("resources/Materials/Examples/StandardSurface")); const mx::FilePath logPath("genmsl_msl23_layout_generate_test.txt"); - MslShaderGeneratorTester tester(mx::MslShaderGenerator::create(), testRootPaths, libSearchPath, srcSearchPath, logPath, writeShadersToDisk); + bool writeShadersToDisk = false; + MslShaderGeneratorTester tester(mx::MslShaderGenerator::create(), testRootPaths, searchPath, logPath, writeShadersToDisk); // Set binding context to handle resource binding layouts tester.addUserData(mx::HW::USER_DATA_BINDING_CONTEXT, mx::MslResourceBindingContext::create()); + const mx::GenOptions genOptions; + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); tester.validate(genOptions, optionsFilePath); } diff --git a/source/MaterialXTest/MaterialXGenMsl/GenMsl.h b/source/MaterialXTest/MaterialXGenMsl/GenMsl.h index 114ab854d3..78cb7a8f4e 100644 --- a/source/MaterialXTest/MaterialXGenMsl/GenMsl.h +++ b/source/MaterialXTest/MaterialXGenMsl/GenMsl.h @@ -23,9 +23,8 @@ class MslShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester using ParentClass = GenShaderUtil::ShaderGeneratorTester; MslShaderGeneratorTester(mx::ShaderGeneratorPtr shaderGenerator, const mx::FilePathVec& testRootPaths, - const mx::FilePath& libSearchPath, const mx::FileSearchPath& srcSearchPath, - const mx::FilePath& logFilePath, bool writeShadersToDisk) : - GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, libSearchPath, srcSearchPath, logFilePath, writeShadersToDisk) + const mx::FileSearchPath& searchPath, const mx::FilePath& logFilePath, bool writeShadersToDisk) : + GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, searchPath, logFilePath, writeShadersToDisk) {} void setTestStages() override @@ -46,7 +45,7 @@ class MslShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester { ParentClass::setupDependentLibraries(); - mx::FilePath lightDir = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/TestSuite/lights"); + mx::FilePath lightDir = mx::getDefaultDataSearchPath().find("resources/Materials/TestSuite/lights"); loadLibrary(lightDir / mx::FilePath("light_compound_test.mtlx"), _dependLib); loadLibrary(lightDir / mx::FilePath("light_rig_test_1.mtlx"), _dependLib); } diff --git a/source/MaterialXTest/MaterialXGenOsl/GenOsl.cpp b/source/MaterialXTest/MaterialXGenOsl/GenOsl.cpp index b6e71732b6..9d3fa78db4 100644 --- a/source/MaterialXTest/MaterialXGenOsl/GenOsl.cpp +++ b/source/MaterialXTest/MaterialXGenOsl/GenOsl.cpp @@ -95,18 +95,13 @@ TEST_CASE("GenShader: OSL Implementation Check", "[genosl]") TEST_CASE("GenShader: OSL Unique Names", "[genosl]") { mx::GenContext context(mx::OslShaderGenerator::create()); - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - context.registerSourceCodeSearchPath(currentPath); - context.registerSourceCodeSearchPath(currentPath / mx::FilePath("libraries/stdlib/genosl/include")); - + context.registerSourceCodeSearchPath(mx::getDefaultDataSearchPath()); GenShaderUtil::testUniqueNames(context, mx::Stage::PIXEL); } TEST_CASE("GenShader: OSL Metadata", "[genosl]") { - mx::FileSearchPath searchPath; - searchPath.append(mx::FilePath::getCurrentPath()); - + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); mx::loadLibraries({ "libraries" }, searchPath, doc); @@ -176,33 +171,27 @@ TEST_CASE("GenShader: OSL Metadata", "[genosl]") // Custom generators can override this method to customize which metadata gets registered. generator->registerShaderMetadata(doc, context); - // Generate the shader and write to file for inspection. + // Generate the shader. mx::ShaderPtr shader = generator->generate(stdSurf1->getName(), stdSurf1, context); REQUIRE(shader != nullptr); - const std::string filepath = mx::FilePath::getCurrentPath() / mx::FilePath("standardSurfaceWithMetadata.osl"); - std::ofstream file; - file.open(filepath); - file << shader->getSourceCode(); } static void generateOslCode() { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePathVec testRootPaths; - testRootPaths.push_back("resources/Materials/TestSuite"); - testRootPaths.push_back("resources/Materials/Examples"); + testRootPaths.push_back(searchPath.find("resources/Materials/TestSuite")); + testRootPaths.push_back(searchPath.find("resources/Materials/Examples/StandardSurface")); - const mx::FilePath libSearchPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath srcSearchPath(libSearchPath.asString()); - srcSearchPath.append(libSearchPath / mx::FilePath("libraries/stdlib/genosl/include")); - srcSearchPath.append(mx::FilePath::getCurrentPath()); const mx::FilePath logPath("genosl_vanilla_generate_test.txt"); bool writeShadersToDisk = false; - OslShaderGeneratorTester tester(mx::OslShaderGenerator::create(), testRootPaths, libSearchPath, srcSearchPath, logPath, writeShadersToDisk); + OslShaderGeneratorTester tester(mx::OslShaderGenerator::create(), testRootPaths, searchPath, logPath, writeShadersToDisk); tester.addSkipLibraryFiles(); const mx::GenOptions genOptions; - mx::FilePath optionsFilePath("resources/Materials/TestSuite/_options.mtlx"); + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); tester.validate(genOptions, optionsFilePath); } diff --git a/source/MaterialXTest/MaterialXGenOsl/GenOsl.h b/source/MaterialXTest/MaterialXGenOsl/GenOsl.h index b214574493..a4ccdc2003 100644 --- a/source/MaterialXTest/MaterialXGenOsl/GenOsl.h +++ b/source/MaterialXTest/MaterialXGenOsl/GenOsl.h @@ -19,9 +19,8 @@ class OslShaderGeneratorTester : public GenShaderUtil::ShaderGeneratorTester using ParentClass = GenShaderUtil::ShaderGeneratorTester; OslShaderGeneratorTester(mx::ShaderGeneratorPtr shaderGenerator, const std::vector& testRootPaths, - const mx::FilePath& libSearchPath, const mx::FileSearchPath& srcSearchPath, - const mx::FilePath& logFilePath, bool writeShadersToDisk) : - GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, libSearchPath, srcSearchPath, logFilePath, writeShadersToDisk) + const mx::FileSearchPath& searchPath, const mx::FilePath& logFilePath, bool writeShadersToDisk) : + GenShaderUtil::ShaderGeneratorTester(shaderGenerator, testRootPaths, searchPath, logFilePath, writeShadersToDisk) {} void setTestStages() override diff --git a/source/MaterialXTest/MaterialXGenShader/GenShader.cpp b/source/MaterialXTest/MaterialXGenShader/GenShader.cpp index 293398cbc1..9b25a1d6b7 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShader.cpp +++ b/source/MaterialXTest/MaterialXGenShader/GenShader.cpp @@ -58,11 +58,9 @@ TEST_CASE("GenShader: Utilities", "[genshader]") TEST_CASE("GenShader: Valid Libraries", "[genshader]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr doc = mx::createDocument(); - - mx::FileSearchPath searchPath; - searchPath.append(mx::FilePath::getCurrentPath()); - loadLibraries({ "libraries/targets", "libraries/stdlib", "libraries/pbrlib" }, searchPath, doc); + loadLibraries({ "libraries" }, searchPath, doc); std::string validationErrors; bool valid = doc->validate(&validationErrors); @@ -109,11 +107,10 @@ TEST_CASE("GenShader: TypeDesc Check", "[genshader]") TEST_CASE("GenShader: Shader Translation", "[translate]") { - const mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath searchPath(currentPath); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::ShaderTranslatorPtr shaderTranslator = mx::ShaderTranslator::create(); - mx::FilePath testPath = currentPath / mx::FilePath("resources/Materials/Examples/StandardSurface"); + mx::FilePath testPath = searchPath.find("resources/Materials/Examples/StandardSurface"); for (mx::FilePath& mtlxFile : testPath.getFilesInDirectory(mx::MTLX_EXTENSION)) { mx::DocumentPtr doc = mx::createDocument(); @@ -147,12 +144,11 @@ TEST_CASE("GenShader: Shader Translation", "[translate]") TEST_CASE("GenShader: Transparency Regression Check", "[genshader]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr libraries = mx::createDocument(); - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath searchPath(currentPath); mx::loadLibraries({ "libraries" }, searchPath, libraries); - const mx::FilePath resourcePath(currentPath / "resources"); + const mx::FilePath resourcePath = searchPath.find("resources"); mx::StringVec failedTests; mx::FilePathVec testFiles = { "Materials/Examples/StandardSurface/standard_surface_default.mtlx", @@ -200,8 +196,9 @@ TEST_CASE("GenShader: Transparency Regression Check", "[genshader]") void testDeterministicGeneration(mx::DocumentPtr libraries, mx::GenContext& context) { - const mx::FilePath testFile = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/Examples/StandardSurface/standard_surface_marble_solid.mtlx"); - const mx::string testElement = "SR_marble1"; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath testFile = searchPath.find("resources/Materials/Examples/StandardSurface/standard_surface_marble_solid.mtlx"); + mx::string testElement = "SR_marble1"; const size_t numRuns = 10; mx::vector testDocs(numRuns); @@ -234,9 +231,9 @@ void testDeterministicGeneration(mx::DocumentPtr libraries, mx::GenContext& cont TEST_CASE("GenShader: Deterministic Generation", "[genshader]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr libraries = mx::createDocument(); - mx::FileSearchPath searchPath(mx::FilePath::getCurrentPath()); - mx::loadLibraries({ "libraries/targets", "libraries/stdlib", "libraries/pbrlib", "libraries/bxdf" }, searchPath, libraries); + mx::loadLibraries({ "libraries" }, searchPath, libraries); #ifdef MATERIALX_BUILD_GEN_GLSL { @@ -270,8 +267,9 @@ TEST_CASE("GenShader: Deterministic Generation", "[genshader]") void checkPixelDependencies(mx::DocumentPtr libraries, mx::GenContext& context) { - const mx::FilePath testFile = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Materials/Examples/GltfPbr/gltf_pbr_boombox.mtlx"); - const mx::string testElement = "Material_boombox"; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath testFile = searchPath.find("resources/Materials/Examples/GltfPbr/gltf_pbr_boombox.mtlx"); + mx::string testElement = "Material_boombox"; mx::DocumentPtr testDoc = mx::createDocument(); mx::readFromXmlFile(testDoc, testFile); @@ -290,9 +288,9 @@ void checkPixelDependencies(mx::DocumentPtr libraries, mx::GenContext& context) TEST_CASE("GenShader: Track Dependencies", "[genshader]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr libraries = mx::createDocument(); - mx::FileSearchPath searchPath(mx::FilePath::getCurrentPath()); - mx::loadLibraries({ "libraries/targets", "libraries/stdlib", "libraries/pbrlib", "libraries/bxdf" }, searchPath, libraries); + mx::loadLibraries({ "libraries" }, searchPath, libraries); #ifdef MATERIALX_BUILD_GEN_GLSL { @@ -382,9 +380,9 @@ TEST_CASE("GenShader: Track Application Variables", "[genshader]") const mx::string testElement = "surfacematerial"; + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr libraries = mx::createDocument(); - mx::FileSearchPath searchPath(mx::FilePath::getCurrentPath()); - mx::loadLibraries({ "libraries/targets", "libraries/stdlib", "libraries/pbrlib", "libraries/bxdf" }, searchPath, libraries); + mx::loadLibraries({ "libraries" }, searchPath, libraries); mx::DocumentPtr testDoc = mx::createDocument(); mx::readFromXmlString(testDoc, testDocumentString); diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp index 2db73f3bb6..75b69ff93b 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp @@ -70,11 +70,11 @@ void checkImplementations(mx::GenContext& context, const mx::StringSet& generatorSkipNodeDefs, unsigned int expectedSkipCount) { - mx::DocumentPtr doc = mx::createDocument(); const mx::ShaderGenerator& shadergen = context.getShaderGenerator(); - mx::FileSearchPath searchPath(mx::FilePath::getCurrentPath()); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::DocumentPtr doc = mx::createDocument(); loadLibraries({ "libraries/targets", "libraries/stdlib", "libraries/pbrlib" }, searchPath, doc); const std::string& target = shadergen.getTarget(); @@ -280,7 +280,7 @@ void testUniqueNames(mx::GenContext& context, const std::string& stage) { mx::DocumentPtr doc = mx::createDocument(); - mx::FileSearchPath searchPath(mx::FilePath::getCurrentPath()); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); loadLibraries({ "libraries/targets", "libraries/stdlib" }, searchPath, doc); const std::string exampleName = "unique_names"; @@ -478,7 +478,7 @@ void ShaderGeneratorTester::setupDependentLibraries() _dependLib = mx::createDocument(); // Load the standard libraries. - loadLibraries({ "libraries" }, _libSearchPath, _dependLib, _skipLibraryFiles); + loadLibraries({ "libraries" }, _searchPath, _dependLib, _skipLibraryFiles); } void ShaderGeneratorTester::addSkipFiles() @@ -603,10 +603,9 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons // Load in all documents to test mx::StringVec errorLog; - mx::FileSearchPath searchPath(_libSearchPath); for (const auto& testRoot : _testRootPaths) { - mx::loadDocuments(testRoot, searchPath, _skipFiles, overrideFiles, _documents, _documentPaths, + mx::loadDocuments(testRoot, _searchPath, _skipFiles, overrideFiles, _documents, _documentPaths, nullptr, &errorLog); } CHECK(errorLog.empty()); @@ -627,7 +626,7 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons // Create our context mx::GenContext context(_shaderGenerator); context.getOptions() = generateOptions; - context.registerSourceCodeSearchPath(_srcSearchPath); + context.registerSourceCodeSearchPath(_searchPath); // Register shader metadata defined in the libraries. _shaderGenerator->registerShaderMetadata(_dependLib, context); @@ -783,7 +782,8 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons } else { - path = mx::FilePath::getCurrentPath(); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + path = searchPath.isEmpty() ? mx::FilePath() : searchPath[0]; } std::vector sourceCodePaths; diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h index 7bd2cd2459..7be195e9de 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.h @@ -156,13 +156,11 @@ class ShaderGeneratorTester { public: ShaderGeneratorTester(mx::ShaderGeneratorPtr shaderGenerator, const mx::FilePathVec& testRootPaths, - const mx::FilePath& libSearchPath, const mx::FileSearchPath& srcSearchPath, - const mx::FilePath& logFilePath, bool writeShadersToDisk) : + const mx::FileSearchPath& searchPath, const mx::FilePath& logFilePath, bool writeShadersToDisk) : _shaderGenerator(shaderGenerator), _targetString(shaderGenerator ? shaderGenerator->getTarget() : "NULL"), _testRootPaths(testRootPaths), - _libSearchPath(libSearchPath), - _srcSearchPath(srcSearchPath), + _searchPath(searchPath), _logFilePath(logFilePath), _writeShadersToDisk(writeShadersToDisk) { @@ -251,8 +249,7 @@ class ShaderGeneratorTester mx::DocumentPtr _dependLib; const mx::FilePathVec _testRootPaths; - const mx::FileSearchPath _libSearchPath; - const mx::FileSearchPath _srcSearchPath; + const mx::FileSearchPath _searchPath; const mx::FilePath _logFilePath; bool _writeShadersToDisk; diff --git a/source/MaterialXTest/MaterialXRender/Render.cpp b/source/MaterialXTest/MaterialXRender/Render.cpp index 994b82c323..49b8ecb4ba 100644 --- a/source/MaterialXTest/MaterialXRender/Render.cpp +++ b/source/MaterialXTest/MaterialXRender/Render.cpp @@ -11,6 +11,8 @@ #include #include +#include + #ifdef MATERIALX_BUILD_OIIO #include #endif @@ -82,7 +84,8 @@ struct GeomHandlerTestOptions void testGeomHandler(GeomHandlerTestOptions& options) { - mx::FilePath imagePath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Geometry/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath imagePath = searchPath.find("resources/Geometry/"); mx::FilePathVec files; unsigned int loadFailed = 0; @@ -156,7 +159,8 @@ struct ImageHandlerTestOptions void testImageHandler(ImageHandlerTestOptions& options) { - mx::FilePath imagePath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Images/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath imagePath = searchPath.find("resources/Images/"); mx::FilePathVec files; unsigned int loadFailed = 0; diff --git a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp index 2f16cef541..af392a9a05 100644 --- a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp +++ b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp @@ -132,11 +132,14 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) testfileOverride.insert(filterFile); } + // Data search path + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::ScopedTimer ioTimer(&profileTimes.ioTime); mx::FilePathVec dirs; for (const auto& root : options.renderTestPaths) { - mx::FilePathVec testRootDirs = root.getSubDirectories(); + mx::FilePathVec testRootDirs = searchPath.find(root).getSubDirectories(); dirs.insert(std::end(dirs), std::begin(testRootDirs), std::end(testRootDirs)); } ioTimer.endTimer(); @@ -144,11 +147,6 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) // Add files to skip addSkipFiles(); - // Library search path - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath searchPath; - searchPath.append(currentPath); - // Load in the library dependencies once // This will be imported in each test document below ioTimer.startTimer(); @@ -177,8 +175,8 @@ bool ShaderRenderTester::validate(const mx::FilePath optionsFilePath) _shaderGenerator->getUnitSystem()->setUnitConverterRegistry(registry); mx::GenContext context(_shaderGenerator); - context.registerSourceCodeSearchPath(currentPath); - context.registerSourceCodeSearchPath(currentPath / mx::FilePath("libraries/stdlib/genosl/include")); + context.registerSourceCodeSearchPath(searchPath); + context.registerSourceCodeSearchPath(searchPath.find("libraries/stdlib/genosl/include")); // Set target unit space context.getOptions().targetDistanceUnit = "meter"; diff --git a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp index 2e8a8b4cf2..719914a62e 100644 --- a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp +++ b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp @@ -73,7 +73,7 @@ class GlslShaderRenderTester : public RenderUtil::ShaderRenderTester void GlslShaderRenderTester::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); @@ -128,6 +128,7 @@ void GlslShaderRenderTester::createRenderer(std::ostream& log) // Set image handler on renderer 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); @@ -174,6 +175,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, mx::ScopedTimer totalGLSLTime(&profileTimes.languageTimes.totalTime); const mx::ShaderGenerator& shadergen = context.getShaderGenerator(); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); // Perform validation if requested if (testOptions.validateElementToRender) @@ -271,7 +273,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, { if (!testOptions.renderGeometry.isAbsolute()) { - geomPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Geometry") / testOptions.renderGeometry; + geomPath = searchPath.find("resources/Geometry") / testOptions.renderGeometry; } else { @@ -280,7 +282,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, } else { - geomPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Geometry/sphere.obj"); + geomPath = searchPath.find("resources/Geometry/sphere.obj"); } if (!geomHandler->hasGeometry(geomPath)) @@ -450,9 +452,9 @@ void GlslShaderRenderTester::runBake(mx::DocumentPtr doc, const mx::FileSearchPa TEST_CASE("Render: GLSL TestSuite", "[renderglsl]") { - GlslShaderRenderTester renderTester(mx::GlslShaderGenerator::create()); - - mx::FilePath optionsFilePath("resources/Materials/TestSuite/_options.mtlx"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); + GlslShaderRenderTester renderTester(mx::GlslShaderGenerator::create()); renderTester.validate(optionsFilePath); } diff --git a/source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp b/source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp index d828faf373..a7f7293755 100644 --- a/source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp +++ b/source/MaterialXTest/MaterialXRenderOsl/GenReference.cpp @@ -22,9 +22,8 @@ namespace mx = MaterialX; TEST_CASE("GenReference: OSL Reference", "[genreference]") { + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr stdlib = mx::createDocument(); - mx::FilePath currentPath = mx::FilePath::getCurrentPath(); - mx::FileSearchPath searchPath(currentPath); loadLibraries({ "libraries/targets", "libraries/stdlib" }, searchPath, stdlib); // Create renderer if requested. @@ -38,7 +37,7 @@ TEST_CASE("GenReference: OSL Reference", "[genreference]") oslIncludePaths.append(mx::FilePath(MATERIALX_OSL_INCLUDE_PATH)); // Add in library include path for compile testing as the generated // shader's includes are not added with absolute paths. - oslIncludePaths.append(currentPath / mx::FilePath("libraries/stdlib/genosl/include")); + oslIncludePaths.append(searchPath.find("libraries/stdlib/genosl/include")); oslRenderer->setOslIncludePath(oslIncludePaths); } @@ -46,11 +45,11 @@ TEST_CASE("GenReference: OSL Reference", "[genreference]") mx::ShaderGeneratorPtr generator = mx::OslShaderGenerator::create(); mx::GenContext context(generator); context.getOptions().addUpstreamDependencies = false; - context.registerSourceCodeSearchPath(currentPath); + context.registerSourceCodeSearchPath(searchPath); context.getOptions().fileTextureVerticalFlip = true; // Create output directory. - mx::FilePath outputPath = mx::FilePath::getCurrentPath() / mx::FilePath("reference/osl"); + mx::FilePath outputPath = searchPath.find("reference/osl"); outputPath.getParentPath().createDirectory(); outputPath.createDirectory(); @@ -82,7 +81,7 @@ TEST_CASE("GenReference: OSL Reference", "[genreference]") continue; } - mx::InterfaceElementPtr interface = nodedef->getImplementation(); + mx::InterfaceElementPtr interface = nodedef->getImplementation(generator->getTarget()); if (!interface) { logFile << "Skip generating reference for unimplemented node '" << nodeName << "'" << std::endl; diff --git a/source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp b/source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp index 41b2a30d85..c23f74cba3 100644 --- a/source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp +++ b/source/MaterialXTest/MaterialXRenderOsl/RenderOsl.cpp @@ -15,6 +15,8 @@ #include +#include + namespace mx = MaterialX; namespace @@ -132,6 +134,7 @@ void OslShaderRenderTester::createRenderer(std::ostream& log) mx::StbImageLoaderPtr stbLoader = mx::StbImageLoader::create(); mx::ImageHandlerPtr imageHandler = mx::ImageHandler::create(stbLoader); + imageHandler->setSearchPath(mx::getDefaultDataSearchPath()); #if defined(MATERIALX_BUILD_OIIO) mx::OiioImageLoaderPtr oiioLoader = mx::OiioImageLoader::create(); imageHandler->addLoader(oiioLoader); @@ -143,7 +146,8 @@ void OslShaderRenderTester::createRenderer(std::ostream& log) // Pre-compile some required shaders for testrender if (!oslcExecutable.empty() && !testRenderExecutable.empty()) { - mx::FilePath shaderPath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Utilities/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath shaderPath = searchPath.find("resources/Utilities/"); _renderer->setOslOutputFilePath(shaderPath); const std::string OSL_EXTENSION("osl"); @@ -309,7 +313,8 @@ bool OslShaderRenderTester::runRenderer(const std::string& shaderName, _renderer->setOslShaderOutput(outputName, outputType); // Set scene template file. For now we only have the constant color scene file - mx::FilePath sceneTemplatePath = mx::FilePath::getCurrentPath() / mx::FilePath("resources/Utilities/"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath sceneTemplatePath = searchPath.find("resources/Utilities/"); sceneTemplatePath = sceneTemplatePath / sceneTemplateFile; _renderer->setOslTestRenderSceneTemplateFile(sceneTemplatePath.asString()); @@ -366,9 +371,9 @@ TEST_CASE("Render: OSL TestSuite", "[renderosl]") return; } - OslShaderRenderTester renderTester(mx::OslShaderGenerator::create()); - - mx::FilePath optionsFilePath("resources/Materials/TestSuite/_options.mtlx"); + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::FilePath optionsFilePath = searchPath.find("resources/Materials/TestSuite/_options.mtlx"); + OslShaderRenderTester renderTester(mx::OslShaderGenerator::create()); renderTester.validate(optionsFilePath); } diff --git a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp index d79b45850e..d4ccdfb13c 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp @@ -43,6 +43,9 @@ void bindPyDefinition(py::module& mod) .def("getFunction", &mx::Implementation::getFunction) .def("setNodeDef", &mx::Implementation::setNodeDef) .def("getNodeDef", &mx::Implementation::getNodeDef) + .def("setNodeGraph", &mx::Implementation::setNodeGraph) + .def("hasNodeGraph", &mx::Implementation::hasNodeGraph) + .def("getNodeGraph", &mx::Implementation::getNodeGraph) .def_readonly_static("CATEGORY", &mx::Implementation::CATEGORY) .def_readonly_static("FILE_ATTRIBUTE", &mx::Implementation::FILE_ATTRIBUTE) .def_readonly_static("FUNCTION_ATTRIBUTE", &mx::Implementation::FUNCTION_ATTRIBUTE); diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyGenOptions.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyGenOptions.cpp index 633315d832..75b5fb11bb 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyGenOptions.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyGenOptions.cpp @@ -27,12 +27,18 @@ void bindPyGenOptions(py::module& mod) .def_readwrite("shaderInterfaceType", &mx::GenOptions::shaderInterfaceType) .def_readwrite("fileTextureVerticalFlip", &mx::GenOptions::fileTextureVerticalFlip) .def_readwrite("targetColorSpaceOverride", &mx::GenOptions::targetColorSpaceOverride) + .def_readwrite("addUpstreamDependencies", &mx::GenOptions::addUpstreamDependencies) + .def_readwrite("libraryPrefix", &mx::GenOptions::libraryPrefix) .def_readwrite("targetDistanceUnit", &mx::GenOptions::targetDistanceUnit) .def_readwrite("hwTransparency", &mx::GenOptions::hwTransparency) .def_readwrite("hwSpecularEnvironmentMethod", &mx::GenOptions::hwSpecularEnvironmentMethod) + .def_readwrite("hwWriteDepthMoments", &mx::GenOptions::hwWriteDepthMoments) + .def_readwrite("hwShadowMap", &mx::GenOptions::hwShadowMap) .def_readwrite("hwMaxActiveLightSources", &mx::GenOptions::hwMaxActiveLightSources) .def_readwrite("hwNormalizeUdimTexCoords", &mx::GenOptions::hwNormalizeUdimTexCoords) + .def_readwrite("hwAmbientOcclusion", &mx::GenOptions::hwAmbientOcclusion) .def_readwrite("hwWriteAlbedoTable", &mx::GenOptions::hwWriteAlbedoTable) + .def_readwrite("hwImplicitBitangents", &mx::GenOptions::hwImplicitBitangents) .def_readwrite("emitColorTransforms", &mx::GenOptions::emitColorTransforms) .def(py::init<>()); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp b/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp new file mode 100644 index 0000000000..158e6b4849 --- /dev/null +++ b/source/PyMaterialX/PyMaterialXRender/PyCgltfLoader.cpp @@ -0,0 +1,18 @@ +// +// Copyright Contributors to the MaterialX Project +// SPDX-License-Identifier: Apache-2.0 +// + +#include +#include + +namespace py = pybind11; +namespace mx = MaterialX; + +void bindPyCgltfLoader(py::module& mod) +{ + py::class_(mod, "CgltfLoader") + .def_static("create", &mx::CgltfLoader::create) + .def(py::init<>()) + .def("load", &mx::CgltfLoader::load); +} diff --git a/source/PyMaterialX/PyMaterialXRender/PyLightHandler.cpp b/source/PyMaterialX/PyMaterialXRender/PyLightHandler.cpp index 39b0d9e1a3..50725a7768 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyLightHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyLightHandler.cpp @@ -31,6 +31,8 @@ void bindPyLightHandler(py::module& mod) .def("getAlbedoTable", &mx::LightHandler::getAlbedoTable) .def("setEnvSampleCount", &mx::LightHandler::setEnvSampleCount) .def("getEnvSampleCount", &mx::LightHandler::getEnvSampleCount) + .def("setRefractionTwoSided", &mx::LightHandler::setRefractionTwoSided) + .def("getRefractionTwoSided", &mx::LightHandler::getRefractionTwoSided) .def("addLightSource", &mx::LightHandler::addLightSource) .def("setLightSources", &mx::LightHandler::setLightSources) .def("getLightSources", &mx::LightHandler::getLightSources) diff --git a/source/PyMaterialX/PyMaterialXRender/PyModule.cpp b/source/PyMaterialX/PyMaterialXRender/PyModule.cpp index 9c6a191108..1c60142346 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyModule.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyModule.cpp @@ -19,6 +19,7 @@ void bindPyOiioImageLoader(py::module& mod); void bindPyTinyObjLoader(py::module& mod); void bindPyCamera(py::module& mod); void bindPyShaderRenderer(py::module& mod); +void bindPyCgltfLoader(py::module& mod); PYBIND11_MODULE(PyMaterialXRender, mod) { @@ -36,4 +37,5 @@ PYBIND11_MODULE(PyMaterialXRender, mod) bindPyTinyObjLoader(mod); bindPyCamera(mod); bindPyShaderRenderer(mod); + bindPyCgltfLoader(mod); }