This notebook will examine the usage definitions found in custom libraries.
Aspects covered include:
The basic data flow examined is shown below:
Note that regardless of all libraries are in MaterialX form so can be accessed outside of the application integraion.
# MaterialX and MaterialX utilities
import MaterialX as mx
import mtlxutils.mxfile as mxf
from mtlxutils.mxnodegraph import MtlxNodeGraph as mxg
# For markdown display
from IPython.display import display_markdown
# Version check
from mtlxutils.mxbase import *
haveVersion1387 = haveVersion(1, 38, 7)
if not haveVersion1387:
print("** Warning: Recommended minimum version is 1.38.7 for tutorials. Have version: ", mx.__version__)
stdlib = mx.createDocument()
searchPath = mx.getDefaultDataSearchPath()
libraryFolders = mx.getDefaultDataLibraryFolders()
try:
libFiles = mx.loadLibraries(libraryFolders, searchPath, stdlib)
print('Loaded %s standard library definitions' % len(stdlib.getNodeDefs()))
except mx.Exception as err:
print('Failed to load standard library definitions: "', err, '"')
doc = mx.createDocument()
doc.importLibrary(stdlib)
# Write predicate
def skipLibraryElement(elem):
return not elem.hasSourceUri()
Loaded 730 standard library definitions
mayausd\USD/libraries
. This come with all shader implementation for core shading languages: MDL, OSL, MSL, GLSL and variants: Vulkan, ESSL.mayausd/MayaUSD/libraries
For the most part these libraries can be referenced outside of Maya by providing the appropriate library name and search path to the importLibraries()
interface. For example a Maya "Blinn" shader code could be created as it's definition exists.
In the following code we add a search path (to the installed Maya definitions) and a library name (adsk
) and load
in the definitions the same wat the "standard" definitions loaded in. We create a new document called mayalib
for loading.
mayalib = mx.createDocument()
# Maya libraries were copied here for demonstration purposes. The actual install location can be used locally.
searchPath = "C:/Users/home/Desktop/Houd_Maya_Libs"
libraryFolders = [ 'maya' ]
try:
libFiles = mx.loadLibraries(libraryFolders, searchPath, mayalib)
print('Loaded %s Maya definitions' % len(mayalib.getNodeDefs()))
except mx.Exception as err:
print('Failed to load standard library definitions: "', err, '"')
# Import custom definitions into same document
doc.importLibrary(mayalib)
Loaded 29 Maya definitions
The definitions can be queried as used to create new instances as desired. In the example one of each node will be created.
The utility function addNode()
(found in mtlxutils
) is used for instance creation.
def createLibraryDictionary(lib):
"""
Utility to examine a library document
"""
libDict = dict()
# Create a category indxed dictionary of the available definitions
for nodedef in lib.getNodeDefs():
category = nodedef.getNodeString()
if category in libDict:
libDict[category].append(nodedef.getName())
else:
libDict[category] = [nodedef.getName()]
return libDict
def createInstanceFromLibrary(libDict):
count = 0
for category in libDict:
print('- Category[%d]:' % count, category, '')
for defname in libDict[category]:
nodedef = doc.getDescendant(defname)
#print(' - Definition:', defname, 'type:', nodedef.getType())
newNode = mxg.addNode(doc, defname, '')
print(' - Instance:', newNode)
doc.removeChild(newNode.getName())
count = count + 1
mayalibDict = createLibraryDictionary(mayalib)
if mayalibDict:
createInstanceFromLibrary(mayalibDict)
else:
print('No definitions found.')
- Category[0]: LdkColorCorrect - Instance: <LdkColorCorrect name="node1" type="color4" nodedef="LdkND_ColorCorrect_color4"> - Category[1]: LdkFloatCorrect - Instance: <LdkFloatCorrect name="node1" type="float" nodedef="LdkND_FloatCorrect_float"> - Category[2]: MayaLambert - Instance: <MayaLambert name="node1" type="surfaceshader" nodedef="MayaND_lambert_surfaceshader"> - Category[3]: MayaPhong - Instance: <MayaPhong name="node1" type="surfaceshader" nodedef="MayaND_phong_surfaceshader"> - Category[4]: MayaBlinn - Instance: <MayaBlinn name="node1" type="surfaceshader" nodedef="MayaND_blinn_surfaceshader"> - Category[5]: place2dTexture - Instance: <place2dTexture name="node1" type="vector2" nodedef="MayaND_place2dTexture_vector2"> - Category[6]: MayaClamp - Instance: <MayaClamp name="node1" type="vector3" nodedef="MayaND_clamp_vector3"> - Category[7]: fileTexture - Instance: <fileTexture name="node1" type="float" nodedef="MayaND_fileTexture_float"> - Instance: <fileTexture name="node1" type="color3" nodedef="MayaND_fileTexture_color3"> - Instance: <fileTexture name="node1" type="color4" nodedef="MayaND_fileTexture_color4"> - Instance: <fileTexture name="node1" type="vector2" nodedef="MayaND_fileTexture_vector2"> - Instance: <fileTexture name="node1" type="vector3" nodedef="MayaND_fileTexture_vector3"> - Instance: <fileTexture name="node1" type="vector4" nodedef="MayaND_fileTexture_vector4"> - Category[8]: rotateUV - Instance: <rotateUV name="node1" type="vector2" nodedef="MayaND_rotateUV_vector2"> - Category[9]: noiseUV - Instance: <noiseUV name="node1" type="vector2" nodedef="MayaND_noiseUV_vector2"> - Category[10]: linearUV - Instance: <linearUV name="node1" type="float" nodedef="MayaND_linearUV_float"> - Category[11]: mirrorUV - Instance: <mirrorUV name="node1" type="float" nodedef="MayaND_mirrorUV_float"> - Category[12]: texcoordtangents - Instance: <texcoordtangents name="node1" type="vector3" nodedef="MayaND_texcoordtangents_vector3"> - Category[13]: arbitrarytangents - Instance: <arbitrarytangents name="node1" type="vector3" nodedef="MayaND_arbitrarytangents_vector3"> - Category[14]: sRGBtoLinrec709 - Instance: <sRGBtoLinrec709 name="node1" type="color3" nodedef="MayaND_sRGBtoLinrec709_color3"> - Instance: <sRGBtoLinrec709 name="node1" type="color4" nodedef="MayaND_sRGBtoLinrec709_color4"> - Category[15]: sRGBtoACEScg - Instance: <sRGBtoACEScg name="node1" type="color3" nodedef="MayaND_sRGBtoACEScg_color3"> - Instance: <sRGBtoACEScg name="node1" type="color4" nodedef="MayaND_sRGBtoACEScg_color4"> - Category[16]: sRGBtoACES2065 - Instance: <sRGBtoACES2065 name="node1" type="color3" nodedef="MayaND_sRGBtoACES2065_color3"> - Instance: <sRGBtoACES2065 name="node1" type="color4" nodedef="MayaND_sRGBtoACES2065_color4"> - Category[17]: sRGBtoLinDCIP3D65 - Instance: <sRGBtoLinDCIP3D65 name="node1" type="color3" nodedef="MayaND_sRGBtoLinDCIP3D65_color3"> - Instance: <sRGBtoLinDCIP3D65 name="node1" type="color4" nodedef="MayaND_sRGBtoLinDCIP3D65_color4"> - Category[18]: sRGBtoLinrec2020 - Instance: <sRGBtoLinrec2020 name="node1" type="color3" nodedef="MayaND_sRGBtoLinrec2020_color3"> - Instance: <sRGBtoLinrec2020 name="node1" type="color4" nodedef="MayaND_sRGBtoLinrec2020_color4">
The following example will create a graph mixing both core as well as custom nodes. Once the definitions are loaded in they can be added and manipulated in the same way as any of the core nodes.
comment = doc.addChildOfCategory('comment')
comment.setDocString(' Add in a custom Maya Blinn node ')
mayaBlinn = mxg.addNode(doc, 'MayaND_blinn_surfaceshader', 'blinn')
if not mayaBlinn:
print('Failed to create a instance of the definition: MayaND_blinn_surfaceshader')
else:
mayaBlinn.addInputsFromNodeDef()
comment = doc.addChildOfCategory('comment')
comment.setDocString(' Add in a custom Maya File Texture node ')
filetexture = mxg.addNode(doc, 'MayaND_fileTexture_color3', 'filetexture')
if not filetexture:
print('Failed to create a instance of the definition: MayaND_fileTexture_color3')
else:
filetexture.addInputsFromNodeDef()
writeOptions = mx.XmlWriteOptions()
writeOptions.writeXIncludeEnable = False
writeOptions.elementPredicate = mxf.MtlxFile.skipLibraryElement
if mayaBlinn and filetexture:
comment = doc.addChildOfCategory('comment')
comment.setDocString(' Add in a custom standard image node ')
imageNode = mxg.addNode(doc, 'ND_tiledimage_color3', 'image')
# Connect image -> filetexture -> MayaBlinn
mxg.connectNodeToNode(filetexture, 'inColor', imageNode, '')
mxg.connectNodeToNode(mayaBlinn, 'color', filetexture, '')
documentContents = mx.writeToXmlString(doc, writeOptions)
text = '<details open><summary>Initial Maya MaterialX Document</summary>\n\n' + '```xml\n' + documentContents + '```\n' + '</details>\n'
display_markdown(text , raw=True)
<?xml version="1.0"?>
<materialx version="1.38">
<!-- Add in a custom Maya Blinn node -->
<MayaBlinn name="blinn" type="surfaceshader" nodedef="MayaND_blinn_surfaceshader">
<input name="diffuse" type="float" value="0.8" />
<input name="color" type="color3" nodename="filetexture" />
<input name="transparency" type="color3" value="0.0, 0.0, 0.0" />
<input name="incandescence" type="color3" value="0.0, 0.0, 0.0" />
<input name="normalCamera" type="vector3" />
<input name="specularColor" type="color3" value="0.5, 0.5, 0.5" />
<input name="reflectivity" type="float" value="0.5" />
<input name="reflectedColor" type="color3" value="0.0, 0.0, 0.0" />
<input name="eccentricity" type="float" value="0.3" />
<input name="specularRollOff" type="float" value="0.7" />
</MayaBlinn>
<!-- Add in a custom Maya File Texture node -->
<fileTexture name="filetexture" type="color3" nodedef="MayaND_fileTexture_color3">
<input name="inColor" type="color3" nodename="image" />
<input name="colorSpace" type="string" />
<input name="invert" type="boolean" value="false" />
<input name="colorGain" type="color3" value="1.0, 1.0, 1.0" />
<input name="colorOffset" type="color3" value="0.0, 0.0, 0.0" />
<input name="exposure" type="float" value="0" />
<input name="uvCoord" type="vector2" value="0.0, 0.0" />
<input name="defaultColor" type="color3" value="0.5, 0.5, 0.5" />
</fileTexture>
<!-- Add in a custom standard image node -->
<tiledimage name="image" type="color3" nodedef="ND_tiledimage_color3" />
</materialx>
It is always worthwhile to run validation (validate()
) when creating and using definition documents. Note that the function exists at different levels from Document down to individual nodes so validation can be selectively performed.
For instance that validation issues are discovered when creating instances of filetexture
and MayaBlinn
.
These are minor warnings dealing with lack of initialization of input values.
normalCamera
is due to an issue with validate()
validation recognizing that the definition has a defaultgeomprop
.colorSpace
is due to an missing default value in Maya node definition.valid, error = doc.validate()
if not valid:
print('Initial document is invalid:\n', '\n '.join(mx.splitString(error,'\n')))
else:
print('Document is valid')
print('Patch document...')
# Remove the attribute to patch
if mayaBlinn:
mayaBlinn.removeChild('normalCamera')
if filetexture:
filetexture.getInput('colorSpace').setValueString('')
valid, error = doc.validate()
if not valid:
print('Document is invalid: ', error)
else:
print('Document is valid after patch.')
documentContents = mx.writeToXmlString(doc, writeOptions)
text = '<details open><summary>Patched Maya MaterialX Document</summary>\n\n' + '```xml\n' + documentContents + '```\n' + '</details>\n'
display_markdown(text , raw=True)
Initial document is invalid: Node input binds no value or connection: <input name="colorSpace" type="string"> Patch document... Document is valid after patch.
<?xml version="1.0"?>
<materialx version="1.38">
<!-- Add in a custom Maya Blinn node -->
<MayaBlinn name="blinn" type="surfaceshader" nodedef="MayaND_blinn_surfaceshader">
<input name="diffuse" type="float" value="0.8" />
<input name="color" type="color3" nodename="filetexture" />
<input name="transparency" type="color3" value="0.0, 0.0, 0.0" />
<input name="incandescence" type="color3" value="0.0, 0.0, 0.0" />
<input name="specularColor" type="color3" value="0.5, 0.5, 0.5" />
<input name="reflectivity" type="float" value="0.5" />
<input name="reflectedColor" type="color3" value="0.0, 0.0, 0.0" />
<input name="eccentricity" type="float" value="0.3" />
<input name="specularRollOff" type="float" value="0.7" />
</MayaBlinn>
<!-- Add in a custom Maya File Texture node -->
<fileTexture name="filetexture" type="color3" nodedef="MayaND_fileTexture_color3">
<input name="inColor" type="color3" nodename="image" />
<input name="colorSpace" type="string" value="" />
<input name="invert" type="boolean" value="false" />
<input name="colorGain" type="color3" value="1.0, 1.0, 1.0" />
<input name="colorOffset" type="color3" value="0.0, 0.0, 0.0" />
<input name="exposure" type="float" value="0" />
<input name="uvCoord" type="vector2" value="0.0, 0.0" />
<input name="defaultColor" type="color3" value="0.5, 0.5, 0.5" />
</fileTexture>
<!-- Add in a custom standard image node -->
<tiledimage name="image" type="color3" nodedef="ND_tiledimage_color3" />
</materialx>
By including the search path and library names to the MaterialX node editor which comes with the core distribution, graphs can be created and edited with this tool.
The following is a similar nodegraph example but created in the editor.
<?xml version="1.0"?>
<materialx version="1.38" colorspace="lin_rec709">
<surfacematerial name="MayaBliinChecker" type="material" xpos="14.442029" ypos="-2.456897">
<input name="surfaceshader" type="surfaceshader" nodename="MayaBlinn_1" />
</surfacematerial>
<MayaBlinn name="MayaBlinn_1" type="surfaceshader" xpos="12.239130" ypos="-3.112069">
<input name="color" type="color3" nodename="fileTexture_color3" />
</MayaBlinn>
<fileTexture name="fileTexture_color3" type="color3" xpos="9.014493" ypos="-3.198276">
<input name="inColor" type="color3" nodename="image_color3" />
<input name="invert" type="boolean" value="true" />
<input name="uvCoord" type="vector2" value="0, 0" />
<input name="exposure" type="float" value="0" />
</fileTexture>
<image name="image_color3" type="color3" xpos="6.898551" ypos="-3.163793">
<input name="file" type="filename" value="" fileprefix="checker.png" />
</image>
</materialx>
Note that LookdevX is the Maya 2024 native editor for Usd and MaterialX graphs stored as UsdShade. It is possible to convert between Usd and MaterialX representations though the process requires additional conversion steps in either direction.
For example this Usd file from Maya was translated to MaterialX using the logic found in the Usd notebook:
#usda 1.0
def Sphere "Sphere1" (
prepend apiSchemas = ["MaterialBindingAPI"]
)
{
uniform bool doubleSided = 0
rel material:binding = </mtl/MyMaterial>
}
def Scope "mtl"
{
def Material "MyMaterial" (
prepend apiSchemas = ["NodeGraphNodeAPI"]
)
{
token outputs:mtlx:surface.connect = </mtl/MyMaterial/MyShader.outputs:out>
uniform float2 ui:nodegraph:node:pos = (-1.7199334, -0.5843111)
def Shader "MyShader" (
prepend apiSchemas = ["NodeGraphNodeAPI"]
)
{
uniform token info:id = "ND_standard_surface_surfaceshader"
color3f inputs:base_color.connect = </mtl/MyMaterial/MyGraph.outputs:out>
token outputs:out
uniform float2 ui:nodegraph:node:pos = (-0.5611111, -0.85555553)
}
def NodeGraph "MyGraph" (
prepend apiSchemas = ["NodeGraphNodeAPI"]
)
{
color3f outputs:out.connect = </mtl/MyMaterial/MyGraph/MyImage.outputs:out>
uniform float2 ui:nodegraph:node:pos = (-2.211111, -0.23888889)
def Shader "MyImage" (
prepend apiSchemas = ["NodeGraphNodeAPI"]
)
{
uniform token info:id = "ND_image_color3"
color3f outputs:out
uniform float2 ui:nodegraph:node:pos = (-0.7916667, -0.8055556)
}
}
}
def Material "MyMayaMaterial" (
prepend apiSchemas = ["NodeGraphNodeAPI"]
)
{
token outputs:mtlx:surface.connect = </mtl/MyMayaMaterial/MayaBlinn1.outputs:outColor>
uniform float2 ui:nodegraph:node:pos = (-0.36928108, 0.3856211)
def Shader "MayaBlinn1" (
prepend apiSchemas = ["NodeGraphNodeAPI"]
)
{
uniform token info:id = "MayaND_blinn_surfaceshader"
color3f inputs:color.connect = </mtl/MyMayaMaterial/MyBlinnGraph.outputs:out>
token outputs:outColor
uniform float2 ui:nodegraph:node:pos = (-0.5888889, -0.7222222)
}
def NodeGraph "MyBlinnGraph" (
prepend apiSchemas = ["NodeGraphNodeAPI"]
)
{
color3f outputs:out.connect = </mtl/MyMayaMaterial/MyBlinnGraph/MyImage2.outputs:out>
uniform float2 ui:nodegraph:node:pos = (-2.0555556, 0.17222223)
def Shader "MyImage2" (
prepend apiSchemas = ["NodeGraphNodeAPI"]
)
{
uniform token info:id = "ND_image_color3"
color3f outputs:out
uniform float2 ui:nodegraph:node:pos = (-0.55833334, -0.6111111)
}
}
}
}
Resulting MaterialX file:
<?xml version="1.0"?>
<materialx version="1.38">
<surfacematerial name="MyMaterial" type="material">
<input name="surfaceshader" type="surfaceshader" nodename="MyShader" />
</surfacematerial>
<standard_surface name="MyShader" type="surfaceshader" nodedef="ND_standard_surface_surfaceshader">
<input name="base_color" type="color3" nodegraph="MyGraph" />
</standard_surface>
<nodegraph name="MyGraph">
<output name="out" type="color3" nodename="MyImage" />
<image name="MyImage" type="color3" nodedef="ND_image_color3" />
</nodegraph>
<surfacematerial name="MyMayaMaterial" type="material">
<input name="surfaceshader" type="surfaceshader" nodename="MayaBlinn1" />
</surfacematerial>
<MayaBlinn name="MayaBlinn1" type="surfaceshader" nodedef="MayaND_blinn_surfaceshader">
<input name="color" type="color3" nodegraph="MyBlinnGraph" />
</MayaBlinn>
<nodegraph name="MyBlinnGraph">
<output name="out" type="color3" nodename="MyImage2" />
<image name="MyImage2" type="color3" nodedef="ND_image_color3" />
</nodegraph>
</materialx>
Source code implementations in the Maya library can also be examined by using getImplementations()
for each shader API target returned from getTargetDefs()
.
The general recommendation is to implement definitions using node graphs as any source code implementation may or may not be portable depending on the level of support for the languages required for a integration.
| Note that code generation logic currently relies on source code implementations all color space transforms (at time of writing) -- and these are what show up as supplemental implementations inside the Maya library.
The hope is that this will migrate to a more portable and OCIO conformant representation in the future. In lieu of this, it is recommended to propose to the ASWF MaterialX TSC to add any new color management transforms to the core distribution to have a common reusable implementation.
# Print out custom source code implementations for each target.
print('Custom implementations:')
targets = stdlib.getTargetDefs()
for target in targets:
printTarget = True
for impl in mayalib.getImplementations():
if impl.getTarget() == target.getName():
if printTarget:
print('Target: ', target)
printTarget = False
print('-', impl.getName(), ' nodedef:', impl.getNodeDefString())
print(' File:', impl.getAttribute('file'), 'Function:', impl.getAttribute('function'))
#print(impl)
Custom implementations: Target: <targetdef name="genglsl"> - MayaIM_texcoordtangents_vector3_genglsl nodedef: MayaND_texcoordtangents_vector3 File: libraries/adsk/maya/genglsl/mx_texcoordtangents_vector3.glsl Function: mx_texcoordtangents_vector3 - MayaIM_arbitrarytangents_vector3_genglsl nodedef: MayaND_arbitrarytangents_vector3 File: libraries/adsk/maya/genglsl/mx_arbitrarytangents_vector3.glsl Function: mx_arbitrarytangents_vector3 - MayaIM_sRGBtoLinrec709_color3_genglsl nodedef: MayaND_sRGBtoLinrec709_color3 File: libraries/stdlib/genglsl/mx_srgb_texture_to_lin_rec709_color3.glsl Function: mx_srgb_texture_to_lin_rec709_color3 - MayaIM_sRGBtoLinrec709_color4_genglsl nodedef: MayaND_sRGBtoLinrec709_color4 File: libraries/stdlib/genglsl/mx_srgb_texture_to_lin_rec709_color4.glsl Function: mx_srgb_texture_to_lin_rec709_color4 - MayaIM_sRGBtoACEScg_color3_genglsl nodedef: MayaND_sRGBtoACEScg_color3 File: libraries/adsk/maya/genglsl/mx_srgb_texture_to_acescg_color3.glsl Function: mx_srgb_texture_to_acescg_color3 - MayaIM_sRGBtoACEScg_color4_genglsl nodedef: MayaND_sRGBtoACEScg_color4 File: libraries/adsk/maya/genglsl/mx_srgb_texture_to_acescg_color4.glsl Function: mx_srgb_texture_to_acescg_color4 - MayaIM_sRGBtoACES2065_color3_genglsl nodedef: MayaND_sRGBtoACES2065_color3 File: libraries/adsk/maya/genglsl/mx_srgb_texture_to_aces_2065_1_color3.glsl Function: mx_srgb_texture_to_aces_2065_1_color3 - MayaIM_sRGBtoACES2065_color4_genglsl nodedef: MayaND_sRGBtoACES2065_color4 File: libraries/adsk/maya/genglsl/mx_srgb_texture_to_aces_2065_1_color4.glsl Function: mx_srgb_texture_to_aces_2065_1_color4 - MayaIM_sRGBtoLinDCIP3D65_color3_genglsl nodedef: MayaND_sRGBtoLinDCIP3D65_color3 File: libraries/adsk/maya/genglsl/mx_srgb_texture_to_lin_dci_p3_d65_color3.glsl Function: mx_srgb_texture_to_lin_dci_p3_d65_color3 - MayaIM_sRGBtoLinDCIP3D65_color4_genglsl nodedef: MayaND_sRGBtoLinDCIP3D65_color4 File: libraries/adsk/maya/genglsl/mx_srgb_texture_to_lin_dci_p3_d65_color4.glsl Function: mx_srgb_texture_to_lin_dci_p3_d65_color4 - MayaIM_sRGBtoLinrec2020_color3_genglsl nodedef: MayaND_sRGBtoLinrec2020_color3 File: libraries/adsk/maya/genglsl/mx_srgb_texture_to_linrec2020_color3.glsl Function: mx_srgb_texture_to_linrec2020_color3 - MayaIM_sRGBtoLinrec2020_color4_genglsl nodedef: MayaND_sRGBtoLinrec2020_color4 File: libraries/adsk/maya/genglsl/mx_srgb_texture_to_linrec2020_color4.glsl Function: mx_srgb_texture_to_linrec2020_color4
The distribution of Houdini examiend (19.5.605):
libraries
folder organization is slightly different by placing some additional definitions in a houdini
folder under the same parent folder as the "core" libraries. i.e. the libraries
folder.libraries
, butThe same process can be used to load in the custom definitions by pointing to the appropriate folder and setting the search path. Below is an example pointing to a local folder containing the content.
Note that it is possible to load in a version which is at or newer than the one shipping with Houdini as long as only the custom definition folders are loaded in after the standard libraries.
This shown in the example below where the additional houdini
library (which is at 1.38.4) is loaded in independently after the libraries which are part
of the distribution used for this notebook (which is at 1.38.8). This works as the an upgrade process is performed on load of the houdini
library.
There is no "downgrade" process so reading into a version older than 1.38.4 may not work or have compatibility issues.
houdinilib = mx.createDocument()
# This sample uses the 19.5.605 Houdini libraries for demonstration purposes. The actual install location can be used locally.
searchPath = "C:/Users/home/Desktop/Houd_Maya_Libs"
libraryFolders = [ 'houdini' ]
try:
libFiles = mx.loadLibraries(libraryFolders, searchPath, houdinilib)
print('Loaded %s Houdini definitions' % len(houdinilib.getNodeDefs()))
except mx.Exception as err:
print('Failed to load Houdini library definitions: "', err, '"')
# Import custom definitions into same document
doc.importLibrary(houdinilib)
Loaded 10 Houdini definitions
houdiniDict = createLibraryDictionary(houdinilib)
createInstanceFromLibrary(houdiniDict)
- Category[0]: hcatmullrom - Instance: <hcatmullrom name="node1" type="float" nodedef="ND_hcatmullrom_float"> - Instance: <hcatmullrom name="node1" type="color3" nodedef="ND_hcatmullrom_color3"> - Category[1]: hinvlinear - Instance: <hinvlinear name="node1" type="float" nodedef="ND_hinvlinear_float"> - Category[2]: hmtlxbias - Instance: <hmtlxbias name="node1" type="float" nodedef="ND_hmtlxbias_float"> - Category[3]: hmtlxfacingratio - Instance: <hmtlxfacingratio name="node1" type="float" nodedef="ND_hmtlxfacingratio_float"> - Category[4]: hmtlxgain - Instance: <hmtlxgain name="node1" type="float" nodedef="ND_hmtlxgain_float"> - Category[5]: huniformcubic - Instance: <huniformcubic name="node1" type="float" nodedef="ND_huniformcubic_float"> - Instance: <huniformcubic name="node1" type="color3" nodedef="ND_huniformcubic_color3"> - Category[6]: huniformramp - Instance: <huniformramp name="node1" type="color3" nodedef="ND_huniformramp_color3"> - Instance: <huniformramp name="node1" type="float" nodedef="ND_huniformramp_float">
nodegroup
is set to be houdini
which makes it harder to classify for code generation, but Houdini doesnot directly use a MaterialX code generator to produce VEX.
Houdini allows for creation of a VEX subnet
which contains MaterialX native definitions as well as the any custom Houdini definitions.
The approach of encapsulating an subnet "asset" as a MaterialX compound nodegraph allows for any inputs and outputs to naturally be mappable
to MaterialX and allows for correspondence to a Usd Material (which is a graph). This is similar to the Maya approach for encapsulation,
though it is premature to comment on mapping to MaterialX as this export workflow is not supported yet.
Note that both must contend with how to handle MaterialX surfacematerial
or volumematerial
Material nodes which is the "natural" semantic mapping for
a Usd Material (node graph). For now both do no handle these MaterialX nodes.
Export to a MaterialX document is directly exposed (unlike in Maya currently). For this, the vop2mtlx
Python script can be used directly or via the Houdini user interface.
In either case, an asset (hda
) must be created from the subnet before export. The additional houdini
library definitions are recogized by the converter.
Note that nodes not in shader graphs connected to outputs are not currently exported by default from the UI. This and addition options are available as script arguments.
the version of MaterialX used. For instance nested subnets (nodegraphs) export will result in an error.
An interesting point to keep in mind is that vop2mtlx
does not embed any houdini
definitions
within the MaterialX document.
to not add any "include" references (XML xincludes) to definitions. This avoids the downside of either definitions being duplicated and statically embedded in these documents or having fixed library dependency locations. As a historical note, the Adobe Substance Designer MaterialX plug-in
when available also kept custom definitions in a separate Adobe library and did not add any "include" dependencies.
Below is a comparison of an example created in Houdini, and the exported result in the MaterialX Graph Editor
With the following MaterialX document exported. Note that:
standardsurface
for brevity, as Houdini exports all inputs even if theyhave default values
<?xml version="1.0"?>
<materialx version="1.38">
<!-- Generated in Houdini from /mat/HoudiniGraph -->
<nodegraph name="NG_HoudiniGraph_surfaceshader_surfaceshader">
<surface_unlit name="surface_unlit1" type="surfaceshader">
<input name="emission" type="float" nodename="hmtlxhcatmullrom1" value="1" />
<input name="emission_color" type="color3" value="1, 1, 1" />
<input name="transmission" type="float" value="0" />
<input name="transmission_color" type="color3" value="1, 1, 1" />
<input name="opacity" type="float" value="1" />
</surface_unlit>
<output name="mtlx_houd_out" type="surfaceshader" nodename="surface_unlit1" />
<standard_surface name="standard_surface1" type="surfaceshader">
<input name="base" type="float" nodename="ramplr1" value="1" />
<input name="base_color" type="color3" nodename="image1" value="0.8, 0.8, 0.8" />
</standard_surface>
<output name="stdsurf_out" type="surfaceshader" nodename="standard_surface1" />
<hcatmullrom name="hmtlxhcatmullrom1" type="float">
<input name="t" type="float" value="0.8" />
<input name="tension" type="float" value="0.5" />
<input name="key0" type="float" value="0.94" />
<input name="key1" type="float" value="1.44" />
<input name="key2" type="float" value="0.7" />
<input name="key3" type="float" value="1.44" />
</hcatmullrom>
<ramplr name="ramplr1" type="float">
<input name="valuel" type="float" value="0" />
<input name="valuer" type="float" value="1" />
<input name="texcoord" type="vector2" value="0, 0" />
</ramplr>
<image name="image1" type="color3">
<input name="file" type="filename" value="checker.png" />
<input name="layer" type="string" value="" />
<input name="default" type="color3" value="0, 0, 0" />
<input name="texcoord" type="vector2" value="0, 0" />
<input name="uaddressmode" type="string" value="periodic" />
<input name="vaddressmode" type="string" value="periodic" />
<input name="filtertype" type="string" value="linear" />
<input name="framerange" type="string" value="" />
<input name="frameoffset" type="integer" value="0" />
<input name="frameendaction" type="string" value="constant" />
</image>
</nodegraph>
</materialx>