This commit is contained in:
2026-01-08 03:06:30 +00:00
parent 757f3946c0
commit 8dd30db30d
4162 changed files with 0 additions and 1147373 deletions

View File

@@ -1,3 +0,0 @@
# Python Code Examples
This folder contains example Python scripts that generate, process, and validate material content using the MaterialX API.

View File

@@ -1,72 +0,0 @@
#!/usr/bin/env python
'''
Generate a baked version of each material in the input document, using the TextureBaker class in the MaterialXRenderGlsl library.
'''
import sys, os, argparse
from sys import platform
import MaterialX as mx
from MaterialX import PyMaterialXRender as mx_render
from MaterialX import PyMaterialXRenderGlsl as mx_render_glsl
if platform == "darwin":
from MaterialX import PyMaterialXRenderMsl as mx_render_msl
def main():
parser = argparse.ArgumentParser(description="Generate a baked version of each material in the input document.")
parser.add_argument("--width", dest="width", type=int, default=1024, help="Specify the width of baked textures.")
parser.add_argument("--height", dest="height", type=int, default=1024, help="Specify the height of baked textures.")
parser.add_argument("--hdr", dest="hdr", action="store_true", help="Save images to hdr format.")
parser.add_argument("--average", dest="average", action="store_true", help="Average baked images to generate constant values.")
parser.add_argument("--path", dest="paths", action='append', nargs='+', help="An additional absolute search path location (e.g. '/projects/MaterialX')")
parser.add_argument("--library", dest="libraries", action='append', nargs='+', help="An additional relative path to a custom data library folder (e.g. 'libraries/custom')")
parser.add_argument('--writeDocumentPerMaterial', dest='writeDocumentPerMaterial', type=mx.stringToBoolean, default=True, help='Specify whether to write baked materials to seprate MaterialX documents. Default is True')
if platform == "darwin":
parser.add_argument("--glsl", dest="useGlslBackend", default=False, type=bool, help="Set to True to use GLSL backend (default = Metal).")
parser.add_argument(dest="inputFilename", help="Filename of the input document.")
parser.add_argument(dest="outputFilename", help="Filename of the output document.")
opts = parser.parse_args()
doc = mx.createDocument()
try:
mx.readFromXmlFile(doc, opts.inputFilename)
except mx.ExceptionFileMissing as err:
print(err)
sys.exit(0)
stdlib = mx.createDocument()
searchPath = mx.getDefaultDataSearchPath()
searchPath.append(os.path.dirname(opts.inputFilename))
libraryFolders = []
if opts.paths:
for pathList in opts.paths:
for path in pathList:
searchPath.append(path)
if opts.libraries:
for libraryList in opts.libraries:
for library in libraryList:
libraryFolders.append(library)
libraryFolders.extend(mx.getDefaultDataLibraryFolders())
mx.loadLibraries(libraryFolders, searchPath, stdlib)
doc.importLibrary(stdlib)
valid, msg = doc.validate()
if not valid:
print("Validation warnings for input document:")
print(msg)
baseType = mx_render.BaseType.FLOAT if opts.hdr else mx_render.BaseType.UINT8
if platform == "darwin" and not opts.useGlslBackend:
baker = mx_render_msl.TextureBaker.create(opts.width, opts.height, baseType)
else:
baker = mx_render_glsl.TextureBaker.create(opts.width, opts.height, baseType)
if opts.average:
baker.setAverageImages(True)
baker.writeDocumentPerMaterial(opts.writeDocumentPerMaterial)
baker.bakeAllMaterials(doc, searchPath, opts.outputFilename)
if __name__ == '__main__':
main()

View File

@@ -1,268 +0,0 @@
#!/usr/bin/env python
'''
Construct a MaterialX file from the textures in the given folder, using the standard data libraries
to build a mapping from texture filenames to shader inputs.
By default the standard_surface shading model is assumed, with the --shadingModel option used to
select any other shading model in the data libraries.
'''
import os
import re
import argparse
from difflib import SequenceMatcher
import MaterialX as mx
UDIM_TOKEN = '.<UDIM>.'
UDIM_REGEX = r'\.\d+\.'
TEXTURE_EXTENSIONS = [ "exr", "png", "jpg", "jpeg", "tif", "hdr" ]
INPUT_ALIASES = { "roughness": "specular_roughness" }
class UdimFilePath(mx.FilePath):
def __init__(self, pathString):
super().__init__(pathString)
self._isUdim = False
self._udimFiles = []
self._udimRegex = re.compile(UDIM_REGEX)
textureDir = self.getParentPath()
textureName = self.getBaseName()
textureExtension = self.getExtension()
if not self._udimRegex.search(textureName):
self._udimFiles = [self]
return
self._isUdim = True
fullNamePattern = self._udimRegex.sub(self._udimRegex.pattern.replace('\\', '\\\\'),
textureName)
udimFiles = filter(
lambda f: re.search(fullNamePattern, f.asString()),
textureDir.getFilesInDirectory(textureExtension)
)
self._udimFiles = [textureDir / f for f in udimFiles]
def __str__(self):
return self.asPattern()
def asPattern(self):
if not self._isUdim:
return self.asString()
textureDir = self.getParentPath()
textureName = self.getBaseName()
pattern = textureDir / mx.FilePath(
self._udimRegex.sub(UDIM_TOKEN, textureName))
return pattern.asString()
def isUdim(self):
return self._isUdim
def getUdimFiles(self):
return self._udimFiles
def getUdimNumbers(self):
def _extractUdimNumber(_file):
pattern = self._udimRegex.search(_file.getBaseName())
if pattern:
return re.search(r"\d+", pattern.group()).group()
return list(map(_extractUdimNumber, self._udimFiles))
def getNameWithoutExtension(self):
if self._isUdim:
name = self._udimRegex.split(self.getBaseName())[0]
else:
name = self.getBaseName().rsplit('.', 1)[0]
return re.sub(r'[^\w\s]+', '_', name)
def listTextures(textureDir, texturePrefix=None):
'''
Return a list of texture filenames matching known extensions.
'''
texturePrefix = texturePrefix or ""
allTextures = []
for ext in TEXTURE_EXTENSIONS:
textures = [textureDir / f for f in textureDir.getFilesInDirectory(ext)
if f.asString().lower().startswith(texturePrefix.lower())]
while textures:
textureFile = UdimFilePath(textures[0].asString())
allTextures.append(textureFile)
for udimFile in textureFile.getUdimFiles():
textures.remove(udimFile)
return allTextures
def findBestMatch(textureName, shadingModel):
'''
Given a texture name and shading model, return the shader input that is the closest match.
'''
parts = textureName.rsplit("_")
baseTexName = parts[-1]
if baseTexName.lower() == 'color':
baseTexName = ''.join(parts[-2:])
if baseTexName in INPUT_ALIASES:
baseTexName = INPUT_ALIASES.get(baseTexName.lower())
shaderInputs = shadingModel.getActiveInputs()
ratios = []
for shaderInput in shaderInputs:
inputName = shaderInput.getName()
inputName = re.sub(r'[^a-zA-Z0-9\s]', '', inputName).lower()
baseTexName = re.sub(r'[^a-zA-Z0-9\s]', '', baseTexName).lower()
sequenceScore = SequenceMatcher(None, inputName, baseTexName).ratio()
ratios.append(sequenceScore * 100)
highscore = max(ratios)
if highscore < 50:
return None
idx = ratios.index(highscore)
return shaderInputs[idx]
def buildDocument(textureFiles, mtlxFile, shadingModel, colorspace, useTiledImage):
'''
Build a MaterialX document from the given textures and shading model.
'''
# Find the default library nodedef, if any, for the requested shading model.
stdlib = mx.createDocument()
mx.loadLibraries(mx.getDefaultDataLibraryFolders(), mx.getDefaultDataSearchPath(), stdlib)
matchingNodeDefs = stdlib.getMatchingNodeDefs(shadingModel)
if not matchingNodeDefs:
print('Shading model', shadingModel, 'not found in the MaterialX data libraries')
return None
shadingModelNodeDef = matchingNodeDefs[0]
for nodeDef in matchingNodeDefs:
if nodeDef.getAttribute('isdefaultversion') == 'true':
shadingModelNodeDef = nodeDef
# Create content document.
doc = mx.createDocument()
materialName = mx.createValidName(mtlxFile.getBaseName().rsplit('.', 1)[0])
nodeGraph = doc.addNodeGraph('NG_' + materialName)
shaderNode = doc.addNode(shadingModel, 'SR_' + materialName, 'surfaceshader')
doc.addMaterialNode('M_' + materialName, shaderNode)
# Iterate over texture files.
imageNodeCategory = 'tiledimage' if useTiledImage else 'image'
udimNumbers = set()
for textureFile in textureFiles:
textureName = textureFile.getNameWithoutExtension()
shaderInput = findBestMatch(textureName, shadingModelNodeDef)
if not shaderInput:
print('Skipping', textureFile.getBaseName(), 'which does not match any', shadingModel, 'input')
continue
inputName = shaderInput.getName()
inputType = shaderInput.getType()
# Skip inputs that have already been created, e.g. in multi-UDIM materials.
if shaderNode.getInput(inputName) or nodeGraph.getChild(textureName):
continue
mtlInput = shaderNode.addInput(inputName)
textureName = nodeGraph.createValidChildName(textureName)
imageNode = nodeGraph.addNode(imageNodeCategory, textureName, inputType)
# Set color space.
if shaderInput.isColorType():
imageNode.setColorSpace(colorspace)
# Set file path.
filePathString = os.path.relpath(textureFile.asPattern(), mtlxFile.getParentPath().asString())
imageNode.setInputValue('file', filePathString, 'filename')
# Apply special cases for normal maps.
inputNode = imageNode
connNode = imageNode
inBetweenNodes = []
if inputName.endswith('normal') and shadingModel == 'standard_surface':
inBetweenNodes = ["normalmap"]
for inNodeName in inBetweenNodes:
connNode = nodeGraph.addNode(inNodeName, textureName + '_' + inNodeName, inputType)
connNode.setConnectedNode('in', inputNode)
inputNode = connNode
# Create output.
outputNode = nodeGraph.addOutput(textureName + '_output', inputType)
outputNode.setConnectedNode(connNode)
mtlInput.setConnectedOutput(outputNode)
mtlInput.setType(inputType)
if textureFile.isUdim():
udimNumbers.update(set(textureFile.getUdimNumbers()))
# Create udim set
if udimNumbers:
geomInfoName = doc.createValidChildName('GI_' + materialName)
geomInfo = doc.addGeomInfo(geomInfoName)
geomInfo.setGeomPropValue('udimset', list(udimNumbers), "stringarray")
# Return the new document
return doc
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--outputFilename', dest='outputFilename', type=str, help='Filename of the output MaterialX document.')
parser.add_argument('--shadingModel', dest='shadingModel', type=str, default="standard_surface", help='The shading model used in analyzing input textures.')
parser.add_argument('--colorSpace', dest='colorSpace', type=str, help='The colorspace in which input textures should be interpreted, defaulting to srgb_texture.')
parser.add_argument('--texturePrefix', dest='texturePrefix', type=str, help='Filter input textures by the given prefix.')
parser.add_argument('--tiledImage', dest='tiledImage', action="store_true", help='Request tiledimage nodes instead of image nodes.')
parser.add_argument(dest='inputDirectory', nargs='?', help='Input folder that will be scanned for textures, defaulting to the current working directory.')
options = parser.parse_args()
texturePath = mx.FilePath.getCurrentPath()
if options.inputDirectory:
texturePath = mx.FilePath(options.inputDirectory)
if not texturePath.isDirectory():
print('Input folder not found:', texturePath)
return
mtlxFile = texturePath / mx.FilePath('material.mtlx')
if options.outputFilename:
mtlxFile = mx.FilePath(options.outputFilename)
textureFiles = listTextures(texturePath, texturePrefix=options.texturePrefix)
if not textureFiles:
print('No matching textures found in input folder.')
return
# Get shading model and color space.
shadingModel = 'standard_surface'
colorspace = 'srgb_texture'
if options.shadingModel:
shadingModel = options.shadingModel
if options.colorSpace:
colorspace = options.colorSpace
print('Analyzing textures in the', texturePath.asString(), 'folder for the', shadingModel, 'shading model.')
# Create the MaterialX document.
doc = buildDocument(textureFiles, mtlxFile, shadingModel, colorspace, options.tiledImage)
if not doc:
return
if options.outputFilename:
# Write the document to disk.
if not mtlxFile.getParentPath().exists():
mtlxFile.getParentPath().createDirectory()
mx.writeToXmlFile(doc, mtlxFile.asString())
print('Wrote MaterialX document to disk:', mtlxFile.asString())
else:
# Print the document to the standard output.
print('Generated MaterialX document:')
print(mx.writeToXmlString(doc))
if __name__ == '__main__':
main()

View File

@@ -1,208 +0,0 @@
#!/usr/bin/env python
'''
Utility to generate the shader for materials found in a MaterialX document. One file will be generated
for each material / shader found. The currently supported target languages are GLSL, OSL, MDL and ESSL.
'''
import sys, os, argparse, subprocess
import MaterialX as mx
import MaterialX.PyMaterialXGenGlsl as mx_gen_glsl
import MaterialX.PyMaterialXGenMdl as mx_gen_mdl
import MaterialX.PyMaterialXGenMsl as mx_gen_msl
import MaterialX.PyMaterialXGenOsl as mx_gen_osl
import MaterialX.PyMaterialXGenShader as mx_gen_shader
def validateCode(sourceCodeFile, codevalidator, codevalidatorArgs):
if codevalidator:
cmd = codevalidator.split()
cmd.append(sourceCodeFile)
if codevalidatorArgs:
cmd.append(codevalidatorArgs)
cmd_flatten ='----- Run Validator: '
for c in cmd:
cmd_flatten += c + ' '
print(cmd_flatten)
try:
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
return output.decode(encoding='utf-8')
except subprocess.CalledProcessError as out:
return (out.output.decode(encoding='utf-8'))
return ""
def getMaterialXFiles(rootPath):
filelist = []
if os.path.isdir(rootPath):
for subdir, dirs, files in os.walk(rootPath):
for file in files:
if file.endswith('mtlx'):
filelist.append(os.path.join(subdir, file))
else:
filelist.append( rootPath )
return filelist
def main():
parser = argparse.ArgumentParser(description='Generate shader code for each material / shader in a document.')
parser.add_argument('--path', dest='paths', action='append', nargs='+', help='An additional absolute search path location (e.g. "/projects/MaterialX")')
parser.add_argument('--library', dest='libraries', action='append', nargs='+', help='An additional relative path to a custom data library folder (e.g. "libraries/custom")')
parser.add_argument('--target', dest='target', default='glsl', help='Target shader generator to use (e.g. "glsl, osl, mdl, essl, vulkan"). Default is glsl.')
parser.add_argument('--outputPath', dest='outputPath', help='File path to output shaders to. If not specified, is the location of the input document is used.')
parser.add_argument('--validator', dest='validator', nargs='?', const=' ', type=str, help='Name of executable to perform source code validation.')
parser.add_argument('--validatorArgs', dest='validatorArgs', nargs='?', const=' ', type=str, help='Optional arguments for code validator.')
parser.add_argument('--vulkanGlsl', dest='vulkanCompliantGlsl', default=False, type=bool, help='Set to True to generate Vulkan-compliant GLSL when using the genglsl target.')
parser.add_argument('--shaderInterfaceType', dest='shaderInterfaceType', default=0, type=int, help='Set the type of shader interface to be generated')
parser.add_argument(dest='inputFilename', help='Path to input document or folder containing input documents.')
opts = parser.parse_args()
filelist = getMaterialXFiles(opts.inputFilename)
for inputFilename in filelist:
doc = mx.createDocument()
try:
mx.readFromXmlFile(doc, inputFilename)
except mx.ExceptionFileMissing as err:
print('Generation failed: "', err, '"')
sys.exit(-1)
print('---------- Generate code for file: ', inputFilename, '--------------------')
stdlib = mx.createDocument()
searchPath = mx.getDefaultDataSearchPath()
searchPath.append(os.path.dirname(inputFilename))
libraryFolders = []
if opts.paths:
for pathList in opts.paths:
for path in pathList:
searchPath.append(path)
if opts.libraries:
for libraryList in opts.libraries:
for library in libraryList:
libraryFolders.append(library)
libraryFolders.extend(mx.getDefaultDataLibraryFolders())
try:
mx.loadLibraries(libraryFolders, searchPath, stdlib)
doc.importLibrary(stdlib)
except Exception as err:
print('Generation failed: "', err, '"')
sys.exit(-1)
valid, msg = doc.validate()
if not valid:
print('Validation warnings for input document:')
print(msg)
gentarget = 'glsl'
if opts.target:
gentarget = opts.target
if gentarget == 'osl':
shadergen = mx_gen_osl.OslShaderGenerator.create()
elif gentarget == 'mdl':
shadergen = mx_gen_mdl.MdlShaderGenerator.create()
elif gentarget == 'essl':
shadergen = mx_gen_glsl.EsslShaderGenerator.create()
elif gentarget == 'vulkan':
shadergen = mx_gen_glsl.VkShaderGenerator.create()
elif gentarget == 'msl':
shadergen = mx_gen_msl.MslShaderGenerator.create()
else:
shadergen = mx_gen_glsl.GlslShaderGenerator.create()
context = mx_gen_shader.GenContext(shadergen)
context.registerSourceCodeSearchPath(searchPath)
# If we're generating Vulkan-compliant GLSL then set the binding context
if opts.vulkanCompliantGlsl:
bindingContext = mx_gen_glsl.GlslResourceBindingContext.create(0,0)
context.pushUserData('udbinding', bindingContext)
genoptions = context.getOptions()
if opts.shaderInterfaceType == 0 or opts.shaderInterfaceType == 1:
genoptions.shaderInterfaceType = mx_gen_shader.ShaderInterfaceType(opts.shaderInterfaceType)
else:
genoptions.shaderInterfaceType = mx_gen_shader.ShaderInterfaceType.SHADER_INTERFACE_COMPLETE
print('- Set up CMS ...')
cms = mx_gen_shader.DefaultColorManagementSystem.create(shadergen.getTarget())
cms.loadLibrary(doc)
shadergen.setColorManagementSystem(cms)
print('- Set up Units ...')
unitsystem = mx_gen_shader.UnitSystem.create(shadergen.getTarget())
registry = mx.UnitConverterRegistry.create()
distanceTypeDef = doc.getUnitTypeDef('distance')
registry.addUnitConverter(distanceTypeDef, mx.LinearUnitConverter.create(distanceTypeDef))
angleTypeDef = doc.getUnitTypeDef('angle')
registry.addUnitConverter(angleTypeDef, mx.LinearUnitConverter.create(angleTypeDef))
unitsystem.loadLibrary(stdlib)
unitsystem.setUnitConverterRegistry(registry)
shadergen.setUnitSystem(unitsystem)
genoptions.targetDistanceUnit = 'meter'
# Look for renderable nodes
nodes = mx_gen_shader.findRenderableElements(doc)
if not nodes:
nodes = doc.getMaterialNodes()
if not nodes:
nodes = doc.getNodesOfType(mx.SURFACE_SHADER_TYPE_STRING)
pathPrefix = ''
if opts.outputPath and os.path.exists(opts.outputPath):
pathPrefix = opts.outputPath + os.path.sep
else:
pathPrefix = os.path.dirname(os.path.abspath(inputFilename))
print('- Shader output path: ' + pathPrefix)
failedShaders = ""
for node in nodes:
nodeName = node.getName()
print('-- Generate code for node: ' + nodeName)
nodeName = mx.createValidName(nodeName)
shader = shadergen.generate(nodeName, node, context)
if shader:
# Use extension of .vert and .frag as it's type is
# recognized by glslangValidator
if gentarget in ['glsl', 'essl', 'vulkan', 'msl']:
pixelSource = shader.getSourceCode(mx_gen_shader.PIXEL_STAGE)
filename = pathPrefix + "/" + shader.getName() + "." + gentarget + ".frag"
print('--- Wrote pixel shader to: ' + filename)
file = open(filename, 'w+')
file.write(pixelSource)
file.close()
errors = validateCode(filename, opts.validator, opts.validatorArgs)
vertexSource = shader.getSourceCode(mx_gen_shader.VERTEX_STAGE)
filename = pathPrefix + "/" + shader.getName() + "." + gentarget + ".vert"
print('--- Wrote vertex shader to: ' + filename)
file = open(filename, 'w+')
file.write(vertexSource)
file.close()
errors += validateCode(filename, opts.validator, opts.validatorArgs)
else:
pixelSource = shader.getSourceCode(mx_gen_shader.PIXEL_STAGE)
filename = pathPrefix + "/" + shader.getName() + "." + gentarget
print('--- Wrote pixel shader to: ' + filename)
file = open(filename, 'w+')
file.write(pixelSource)
file.close()
errors = validateCode(filename, opts.validator, opts.validatorArgs)
if errors != "":
print("--- Validation failed for node: ", nodeName)
print("----------------------------")
print('--- Error log: ', errors)
print("----------------------------")
failedShaders += (nodeName + ' ')
else:
print("--- Validation passed for node:", nodeName)
else:
print("--- Validation failed for node:", nodeName)
failedShaders += (nodeName + ' ')
if failedShaders != "":
sys.exit(-1)
if __name__ == '__main__':
main()

File diff suppressed because it is too large Load Diff

View File

@@ -1,113 +0,0 @@
#!/usr/bin/env python
'''
Print markdown documentation for each nodedef in the given document.
'''
import argparse
import sys
import MaterialX as mx
HEADERS = ('Name', 'Type', 'Default Value',
'UI name', 'UI min', 'UI max', 'UI Soft Min', 'UI Soft Max', 'UI step', 'UI group', 'UI Advanced', 'Doc', 'Uniform')
ATTR_NAMES = ('uiname', 'uimin', 'uimax', 'uisoftmin', 'uisoftmax', 'uistep', 'uifolder', 'uiadvanced', 'doc', 'uniform' )
def main():
parser = argparse.ArgumentParser(description="Print documentation for each nodedef in the given document.")
parser.add_argument(dest="inputFilename", help="Filename of the input MaterialX document.")
parser.add_argument('--docType', dest='documentType', default='md', help='Document type. Default is "md" (Markdown). Specify "html" for HTML output')
parser.add_argument('--showInherited', default=False, action='store_true', help='Show inherited inputs. Default is False')
opts = parser.parse_args()
doc = mx.createDocument()
try:
mx.readFromXmlFile(doc, opts.inputFilename)
except mx.ExceptionFileMissing as err:
print(err)
sys.exit(0)
for nd in doc.getNodeDefs():
# HTML output
if opts.documentType == "html":
print('<head><style>')
print('table, th, td {')
print(' border-bottom: 1px solid; border-collapse: collapse; padding: 10px;')
print('}')
print('</style></head>')
print('<ul>')
print('<li> <em>Nodedef</em>: %s' % nd.getName())
print('<li> <em>Type</em>: %s' % nd.getType())
if len(nd.getNodeGroup()) > 0:
print('<li> <em>Node Group</em>: %s' % nd.getNodeGroup())
if len(nd.getVersionString()) > 0:
print('<li> <em>Version</em>: %s. Is default: %s' % (nd.getVersionString(), nd.getDefaultVersion()))
if len(nd.getInheritString()) > 0:
print('<li> <em>Inherits From</em>: %s' % nd.getInheritString())
print('<li> <em>Doc</em>: %s\n' % nd.getAttribute('doc'))
print('</ul>')
print('<table><tr>')
for h in HEADERS:
print('<th>' + h + '</th>')
print('</tr>')
inputList = nd.getActiveInputs() if opts.showInherited else nd.getInputs()
tokenList = nd.getActiveTokens() if opts.showInherited else nd.getTokens()
outputList = nd.getActiveOutputs() if opts.showInherited else nd.getOutputs()
totalList = inputList + tokenList + outputList;
for port in totalList:
print('<tr>')
infos = []
if port in outputList:
infos.append('<em>'+ port.getName() + '</em>')
elif port in tokenList:
infos.append(port.getName())
else:
infos.append('<b>'+ port.getName() + '</b>')
infos.append(port.getType())
val = port.getValue()
if port.getType() == "float":
val = round(val, 6)
infos.append(str(val))
for attrname in ATTR_NAMES:
infos.append(port.getAttribute(attrname))
for info in infos:
print('<td>' + info + '</td>')
print('</tr>')
print('</table>')
# Markdown output
else:
print('- *Nodedef*: %s' % nd.getName())
print('- *Type*: %s' % nd.getType())
if len(nd.getNodeGroup()) > 0:
print('- *Node Group*: %s' % nd.getNodeGroup())
if len(nd.getVersionString()) > 0:
print('- *Version*: %s. Is default: %s' % (nd.getVersionString(), nd.getDefaultVersion()))
if len(nd.getInheritString()) > 0:
print('- *Inherits From*: %s' % nd.getInheritString())
print('- *Doc*: %s\n' % nd.getAttribute('doc'))
print('| ' + ' | '.join(HEADERS) + ' |')
print('|' + ' ---- |' * len(HEADERS) + '')
inputList = nd.getActiveInputs() if opts.showInherited else nd.getInputs()
tokenList = nd.getActiveTokens() if opts.showInherited else nd.getTokens()
outputList = nd.getActiveOutputs() if opts.showInherited else nd.getOutputs()
totalList = inputList + tokenList + outputList;
for port in totalList:
infos = []
if port in outputList:
infos.append('*'+ port.getName() + '*')
elif port in tokenList:
infos.append(port.getName())
else:
infos.append('**'+ port.getName() + '**')
infos.append(port.getType())
val = port.getValue()
if port.getType() == "float":
val = round(val, 6)
infos.append(str(val))
for attrname in ATTR_NAMES:
infos.append(port.getAttribute(attrname))
print('| ' + " | ".join(infos) + ' |')
if __name__ == '__main__':
main()

View File

@@ -1,65 +0,0 @@
#!/usr/bin/env python
'''
Reformat a folder of MaterialX documents in place, optionally upgrading
the documents to the latest version of the standard.
'''
import argparse
import os
import MaterialX as mx
def main():
parser = argparse.ArgumentParser(description="Reformat a folder of MaterialX documents in place.")
parser.add_argument("--yes", dest="yes", action="store_true", help="Proceed without asking for confirmation from the user.")
parser.add_argument('--upgrade', dest='upgrade', action="store_true", help='Upgrade documents to the latest version of the standard.')
parser.add_argument(dest="inputFolder", help="An input folder to scan for MaterialX documents.")
opts = parser.parse_args()
validDocs = dict()
for root, dirs, files in os.walk(opts.inputFolder):
for file in files:
if file.endswith('.mtlx'):
filename = os.path.join(root, file)
doc = mx.createDocument()
try:
readOptions = mx.XmlReadOptions()
readOptions.readComments = True
readOptions.readNewlines = True
readOptions.upgradeVersion = opts.upgrade
try:
mx.readFromXmlFile(doc, filename, mx.FileSearchPath(), readOptions)
except Exception as err:
print('Skipping "' + file + '" due to exception: ' + str(err))
continue
validDocs[filename] = doc
except mx.Exception:
pass
if not validDocs:
print('No MaterialX documents were found in "%s"' % (opts.inputFolder))
return
print('Found %s MaterialX files in "%s"' % (len(validDocs), opts.inputFolder))
mxVersion = mx.getVersionIntegers()
if not opts.yes:
if opts.upgrade:
question = 'Would you like to upgrade all %i documents to MaterialX v%i.%i in place (y/n)?' % (len(validDocs), mxVersion[0], mxVersion[1])
else:
question = 'Would you like to reformat all %i documents in place (y/n)?' % len(validDocs)
answer = input(question)
if answer != 'y' and answer != 'Y':
return
for (filename, doc) in validDocs.items():
mx.writeToXmlFile(doc, filename)
if opts.upgrade:
print('Upgraded %i documents to MaterialX v%i.%i' % (len(validDocs), mxVersion[0], mxVersion[1]))
else:
print('Reformatted %i documents ' % len(validDocs))
if __name__ == '__main__':
main()

View File

@@ -1,359 +0,0 @@
#!/usr/bin/env python
'''
Verify that the given file is a valid MaterialX document.
'''
import argparse
import sys
import MaterialX as mx
def main():
parser = argparse.ArgumentParser(description="Verify that the given file is a valid MaterialX document.")
parser.add_argument("--resolve", dest="resolve", action="store_true", help="Resolve inheritance and string substitutions.")
parser.add_argument("--verbose", dest="verbose", action="store_true", help="Print summary of elements found in the document.")
parser.add_argument("--stdlib", dest="stdlib", action="store_true", help="Import standard MaterialX libraries into the document.")
parser.add_argument(dest="inputFilename", help="Filename of the input document.")
opts = parser.parse_args()
doc = mx.createDocument()
try:
mx.readFromXmlFile(doc, opts.inputFilename)
except mx.ExceptionFileMissing as err:
print(err)
sys.exit(0)
if opts.stdlib:
stdlib = mx.createDocument()
try:
mx.loadLibraries(mx.getDefaultDataLibraryFolders(), mx.getDefaultDataSearchPath(), stdlib)
except Exception as err:
print(err)
sys.exit(0)
doc.importLibrary(stdlib)
(valid, message) = doc.validate()
if (valid):
print("%s is a valid MaterialX document in v%s" % (opts.inputFilename, mx.getVersionString()))
else:
print("%s is not a valid MaterialX document in v%s" % (opts.inputFilename, mx.getVersionString()))
print(message)
if opts.verbose:
nodegraphs = doc.getNodeGraphs()
materials = doc.getMaterialNodes()
looks = doc.getLooks()
lookgroups = doc.getLookGroups()
collections = doc.getCollections()
nodedefs = doc.getNodeDefs()
implementations = doc.getImplementations()
geominfos = doc.getGeomInfos()
geompropdefs = doc.getGeomPropDefs()
typedefs = doc.getTypeDefs()
propsets = doc.getPropertySets()
variantsets = doc.getVariantSets()
backdrops = doc.getBackdrops()
print("----------------------------------")
print("Document Version: {}.{:02d}".format(*doc.getVersionIntegers()))
print("%4d Custom Type%s%s" % (len(typedefs), pl(typedefs), listContents(typedefs, opts.resolve)))
print("%4d Custom GeomProp%s%s" % (len(geompropdefs), pl(geompropdefs), listContents(geompropdefs, opts.resolve)))
print("%4d NodeDef%s%s" % (len(nodedefs), pl(nodedefs), listContents(nodedefs, opts.resolve)))
print("%4d Implementation%s%s" % (len(implementations), pl(implementations), listContents(implementations, opts.resolve)))
print("%4d Nodegraph%s%s" % (len(nodegraphs), pl(nodegraphs), listContents(nodegraphs, opts.resolve)))
print("%4d VariantSet%s%s" % (len(variantsets), pl(variantsets), listContents(variantsets, opts.resolve)))
print("%4d Material%s%s" % (len(materials), pl(materials), listContents(materials, opts.resolve)))
print("%4d Collection%s%s" % (len(collections), pl(collections), listContents(collections, opts.resolve)))
print("%4d GeomInfo%s%s" % (len(geominfos), pl(geominfos), listContents(geominfos, opts.resolve)))
print("%4d PropertySet%s%s" % (len(propsets), pl(propsets), listContents(propsets, opts.resolve)))
print("%4d Look%s%s" % (len(looks), pl(looks), listContents(looks, opts.resolve)))
print("%4d LookGroup%s%s" % (len(lookgroups), pl(lookgroups), listContents(lookgroups, opts.resolve)))
print("%4d Top-level backdrop%s%s" % (len(backdrops), pl(backdrops), listContents(backdrops, opts.resolve)))
print("----------------------------------")
def listContents(elemlist, resolve):
if len(elemlist) == 0:
return ''
names = []
for elem in elemlist:
if elem.isA(mx.NodeDef):
outtype = elem.getType()
outs = ""
if outtype == "multioutput":
for ot in elem.getOutputs():
outs = outs + \
'\n\t %s output "%s"' % (ot.getType(), ot.getName())
names.append('%s %s "%s"%s' %
(outtype, elem.getNodeString(), elem.getName(), outs))
names.append(listNodedefInterface(elem))
elif elem.isA(mx.Implementation):
impl = elem.getName()
targs = []
if elem.hasTarget():
targs.append("target %s" % elem.getTarget())
if targs:
impl = "%s (%s)" % (impl, ", ".join(targs))
if elem.hasFunction():
if elem.hasFile():
impl = "%s [%s:%s()]" % (
impl, elem.getFile(), elem.getFunction())
else:
impl = "%s [function %s()]" % (impl, elem.getFunction())
elif elem.hasFile():
impl = "%s [%s]" % (impl, elem.getFile())
names.append(impl)
elif elem.isA(mx.Backdrop):
names.append('%s: contains "%s"' %
(elem.getName(), elem.getContainsString()))
elif elem.isA(mx.NodeGraph):
nchildnodes = len(elem.getChildren()) - elem.getOutputCount()
backdrops = elem.getBackdrops()
nbackdrops = len(backdrops)
outs = ""
if nbackdrops > 0:
for bd in backdrops:
outs = outs + '\n\t backdrop "%s"' % (bd.getName())
outs = outs + ' contains "%s"' % bd.getContainsString()
if elem.getOutputCount() > 0:
for ot in elem.getOutputs():
outs = outs + '\n\t %s output "%s"' % (ot.getType(), ot.getName())
outs = outs + traverseInputs(ot, "", 0)
nd = elem.getNodeDef()
if nd:
names.append('%s (implementation for nodedef "%s"): %d nodes%s' % (
elem.getName(), nd.getName(), nchildnodes, outs))
else:
names.append("%s: %d nodes, %d backdrop%s%s" % (
elem.getName(), nchildnodes, nbackdrops, pl(backdrops), outs))
elif elem.isA(mx.Node, mx.SURFACE_MATERIAL_NODE_STRING):
shaders = mx.getShaderNodes(elem)
names.append("%s: %d connected shader node%s" % (elem.getName(), len(shaders), pl(shaders)))
for shader in shaders:
names.append('Shader node "%s" (%s), with bindings:%s' % (shader.getName(), shader.getCategory(), listShaderBindings(shader)))
elif elem.isA(mx.GeomInfo):
props = elem.getGeomProps()
if props:
propnames = " (Geomprops: " + ", ".join(map(
lambda x: "%s=%s" % (x.getName(), getConvertedValue(x)), props)) + ")"
else:
propnames = ""
tokens = elem.getTokens()
if tokens:
tokennames = " (Tokens: " + ", ".join(map(
lambda x: "%s=%s" % (x.getName(), x.getValueString()), tokens)) + ")"
else:
tokennames = ""
names.append("%s%s%s" % (elem.getName(), propnames, tokennames))
elif elem.isA(mx.VariantSet):
vars = elem.getVariants()
if vars:
varnames = " (variants " + ", ".join(map(
lambda x: '"' + x.getName()+'"', vars)) + ")"
else:
varnames = ""
names.append("%s%s" % (elem.getName(), varnames))
elif elem.isA(mx.PropertySet):
props = elem.getProperties()
if props:
propnames = " (" + ", ".join(map(
lambda x: "%s %s%s" % (x.getType(), x.getName(), getTarget(x)), props)) + ")"
else:
propnames = ""
names.append("%s%s" % (elem.getName(), propnames))
elif elem.isA(mx.LookGroup):
lks = elem.getLooks()
if lks:
names.append("%s (looks: %s)" % (elem.getName(), lks))
else:
names.append("%s (no looks)" % (elem.getName()))
elif elem.isA(mx.Look):
mas = ""
if resolve:
mtlassns = elem.getActiveMaterialAssigns()
else:
mtlassns = elem.getMaterialAssigns()
for mtlassn in mtlassns:
mas = mas + "\n\t MaterialAssign %s to%s" % (
mtlassn.getMaterial(), getGeoms(mtlassn, resolve))
pas = ""
if resolve:
propassns = elem.getActivePropertyAssigns()
else:
propassns = elem.getPropertyAssigns()
for propassn in propassns:
propertyname = propassn.getAttribute("property")
pas = pas + "\n\t PropertyAssign %s %s to%s" % (
propassn.getType(), propertyname, getGeoms(propassn, resolve))
psas = ""
if resolve:
propsetassns = elem.getActivePropertySetAssigns()
else:
propsetassns = elem.getPropertySetAssigns()
for propsetassn in propsetassns:
propertysetname = propsetassn.getAttribute("propertyset")
psas = psas + "\n\t PropertySetAssign %s to%s" % (
propertysetname, getGeoms(propsetassn, resolve))
varas = ""
if resolve:
variantassns = elem.getActiveVariantAssigns()
else:
variantassns = elem.getVariantAssigns()
for varassn in variantassns:
varas = varas + "\n\t VariantAssign %s from variantset %s" % (
varassn.getVariantString(), varassn.getVariantSetString())
visas = ""
if resolve:
visassns = elem.getActiveVisibilities()
else:
visassns = elem.getVisibilities()
for vis in visassns:
visstr = 'on' if vis.getVisible() else 'off'
visas = visas + "\n\t Set %s visibility%s %s to%s" % (
vis.getVisibilityType(), getViewerGeoms(vis), visstr, getGeoms(vis, resolve))
names.append("%s%s%s%s%s%s" %
(elem.getName(), mas, pas, psas, varas, visas))
else:
names.append(elem.getName())
return ":\n\t" + "\n\t".join(names)
def listShaderBindings(shader):
s = ''
for inp in shader.getInputs():
bname = inp.getName()
btype = inp.getType()
if inp.hasOutputString():
outname = inp.getOutputString()
if inp.hasNodeGraphString():
ngname = inp.getNodeGraphString()
s = s + '\n\t %s "%s" -> nodegraph "%s" output "%s"' % (btype, bname, ngname, outname)
else:
s = s + '\n\t %s "%s" -> output "%s"' % (btype, bname, outname)
else:
bval = getConvertedValue(inp)
s = s + '\n\t %s "%s" = %s' % (btype, bname, bval)
return s
def listNodedefInterface(nodedef):
s = ''
for inp in nodedef.getActiveInputs():
iname = inp.getName()
itype = inp.getType()
if s:
s = s + '\n\t'
s = s + ' %s input "%s"' % (itype, iname)
for tok in nodedef.getActiveTokens():
tname = tok.getName()
ttype = tok.getType()
if s:
s = s + '\n\t'
s = s + ' %s token "%s"' % (ttype, tname)
return s
def traverseInputs(node, port, depth):
s = ''
if node.isA(mx.Output):
parent = node.getConnectedNode()
s = s + traverseInputs(parent, "", depth+1)
else:
s = s + '%s%s -> %s %s "%s"' % (spc(depth), port,
node.getType(), node.getCategory(), node.getName())
ins = node.getActiveInputs()
for i in ins:
if i.hasInterfaceName():
intname = i.getInterfaceName()
s = s + \
'%s%s ^- %s interface "%s"' % (spc(depth+1),
i.getName(), i.getType(), intname)
elif i.hasValueString():
val = getConvertedValue(i)
s = s + \
'%s%s = %s value %s' % (
spc(depth+1), i.getName(), i.getType(), val)
else:
parent = i.getConnectedNode()
if parent:
s = s + traverseInputs(parent, i.getName(), depth+1)
toks = node.getActiveTokens()
for i in toks:
if i.hasInterfaceName():
intname = i.getInterfaceName()
s = s + \
'%s[T]%s ^- %s interface "%s"' % (
spc(depth+1), i.getName(), i.getType(), intname)
elif i.hasValueString():
val = i.getValueString()
s = s + \
'%s[T]%s = %s value "%s"' % (
spc(depth+1), i.getName(), i.getType(), val)
else:
s = s + \
'%s[T]%s error: no valueString' % (
spc(depth+1), i.getName())
return s
def pl(elem):
if len(elem) == 1:
return ""
else:
return "s"
def spc(depth):
return "\n\t " + ": "*depth
# Return a value string for the element, converting units if appropriate
def getConvertedValue(elem):
if elem.getType() in ["float", "vector2", "vector3", "vector4"]:
if elem.hasUnit():
u = elem.getUnit()
print ("[Unit for %s is %s]" % (elem.getName(), u))
if elem.hasUnitType():
utype = elem.getUnitType()
print ("[Unittype for %s is %s]" % (elem.getName(), utype))
# NOTDONE...
return elem.getValueString()
def getGeoms(elem, resolve):
s = ""
if elem.hasGeom():
if resolve:
s = s + ' geom "%s"' % elem.getActiveGeom()
else:
s = s + ' geom "%s"' % elem.getGeom()
if elem.hasCollectionString():
s = s + ' collection "%s"' % elem.getCollectionString()
return s
def getViewerGeoms(elem):
s = ""
if elem.hasViewerGeom():
s = s + ' viewergeom "%s"' % elem.getViewerGeom()
if elem.hasViewerCollection():
s = s + ' viewercollection "%s"' % elem.getViewerCollection()
if s:
s = " of" + s
return s
def getTarget(elem):
if elem.hasTarget():
return ' [target "%s"]' % elem.getTarget()
else:
return ""
if __name__ == '__main__':
main()

View File

@@ -1,101 +0,0 @@
#!/usr/bin/env python
'''
Generate a baked translated version of each material in the input document, using the ShaderTranslator class in the MaterialXShaderGen library
and the TextureBaker class in the MaterialXRenderGlsl library.
'''
import sys, os, argparse
from sys import platform
import MaterialX as mx
from MaterialX import PyMaterialXGenShader as mx_gen_shader
from MaterialX import PyMaterialXRender as mx_render
from MaterialX import PyMaterialXRenderGlsl as mx_render_glsl
if platform == "darwin":
from MaterialX import PyMaterialXRenderMsl as mx_render_msl
def main():
parser = argparse.ArgumentParser(description="Generate a translated baked version of each material in the input document.")
parser.add_argument("--width", dest="width", type=int, default=0, help="Specify an optional width for baked textures (defaults to the maximum image height in the source document).")
parser.add_argument("--height", dest="height", type=int, default=0, help="Specify an optional height for baked textures (defaults to the maximum image width in the source document).")
parser.add_argument("--hdr", dest="hdr", action="store_true", help="Bake images with high dynamic range (e.g. in HDR or EXR format).")
parser.add_argument("--path", dest="paths", action='append', nargs='+', help="An additional absolute search path location (e.g. '/projects/MaterialX')")
parser.add_argument("--library", dest="libraries", action='append', nargs='+', help="An additional relative path to a custom data library folder (e.g. 'libraries/custom')")
parser.add_argument('--writeDocumentPerMaterial', dest='writeDocumentPerMaterial', type=mx.stringToBoolean, default=True, help='Specify whether to write baked materials to seprate MaterialX documents. Default is True')
if platform == "darwin":
parser.add_argument("--glsl", dest="useGlslBackend", default=False, type=bool, help="Set to True to use GLSL backend (default = Metal).")
parser.add_argument(dest="inputFilename", help="Filename of the input document.")
parser.add_argument(dest="outputFilename", help="Filename of the output document.")
parser.add_argument(dest="destShader", help="Destination shader for translation")
opts = parser.parse_args()
doc = mx.createDocument()
try:
mx.readFromXmlFile(doc, opts.inputFilename)
except mx.ExceptionFileMissing as err:
print(err)
sys.exit(0)
stdlib = mx.createDocument()
searchPath = mx.getDefaultDataSearchPath()
searchPath.append(os.path.dirname(opts.inputFilename))
libraryFolders = []
if opts.paths:
for pathList in opts.paths:
for path in pathList:
searchPath.append(path)
if opts.libraries:
for libraryList in opts.libraries:
for library in libraryList:
libraryFolders.append(library)
libraryFolders.extend(mx.getDefaultDataLibraryFolders())
mx.loadLibraries(libraryFolders, searchPath, stdlib)
doc.importLibrary(stdlib)
valid, msg = doc.validate()
if not valid:
print("Validation warnings for input document:")
print(msg)
# Check the document for a UDIM set.
udimSetValue = doc.getGeomPropValue(mx.UDIM_SET_PROPERTY)
udimSet = udimSetValue.getData() if udimSetValue else []
# Compute baking resolution from the source document.
imageHandler = mx_render.ImageHandler.create(mx_render.StbImageLoader.create())
imageHandler.setSearchPath(searchPath)
if udimSet:
resolver = doc.createStringResolver()
resolver.setUdimString(udimSet[0])
imageHandler.setFilenameResolver(resolver)
imageVec = imageHandler.getReferencedImages(doc)
bakeWidth, bakeHeight = mx_render.getMaxDimensions(imageVec)
# Apply baking resolution settings.
if opts.width > 0:
bakeWidth = opts.width
if opts.height > 0:
bakeHeight = opts.height
bakeWidth = max(bakeWidth, 4)
bakeHeight = max(bakeHeight, 4)
# Translate materials between shading models
translator = mx_gen_shader.ShaderTranslator.create()
try:
translator.translateAllMaterials(doc, opts.destShader)
except mx.Exception as err:
print(err)
sys.exit(0)
# Bake translated materials to flat textures.
baseType = mx_render.BaseType.FLOAT if opts.hdr else mx_render.BaseType.UINT8
if platform == "darwin" and not opts.useGlslBackend:
baker = mx_render_msl.TextureBaker.create(bakeWidth, bakeHeight, baseType)
else:
baker = mx_render_glsl.TextureBaker.create(bakeWidth, bakeHeight, baseType)
baker.writeDocumentPerMaterial(opts.writeDocumentPerMaterial)
baker.bakeAllMaterials(doc, searchPath, opts.outputFilename)
if __name__ == '__main__':
main()

View File

@@ -1,138 +0,0 @@
#!/usr/bin/env python
'''
Generate the "NodeGraphs.mtlx" example file programmatically.
'''
import MaterialX as mx
def main():
doc = mx.createDocument()
#
# Nodegraph example 1
#
ng1 = doc.addNodeGraph("NG_example1")
img1 = ng1.addNode("image", "img1", "color3")
# Because filenames look like string types, it is necessary to explicitly declare
# this parameter value as type "filename".
img1.setInputValue("file", "layer1.tif", "filename")
img2 = ng1.addNode("image", "img2", "color3")
img2.setInputValue("file", "layer2.tif", "filename")
img3 = ng1.addNode("image", "img3", "float")
img3.setInputValue("file", "mask1.tif", "filename")
n0 = ng1.addNode("mix", "n0", "color3")
# To connect an input to another node, you must first add the input with the expected
# type, and then setConnectedNode() that input to the desired Node object.
infg = n0.addInput("fg", "color3")
infg.setConnectedNode(img1)
inbg = n0.addInput("bg", "color3")
inbg.setConnectedNode(img2)
inmx = n0.addInput("mix", "float")
inmx.setConnectedNode(img3)
n1 = ng1.addNode("multiply", "n1", "color3")
inp1 = n1.addInput("in1", "color3")
inp1.setConnectedNode(n0)
inp2 = n1.setInputValue("in2", 0.22)
nout = ng1.addOutput("diffuse", "color3")
nout.setConnectedNode(n1)
#
# Nodegraph example 3
#
ng3 = doc.addNodeGraph("NG_example3")
img1 = ng3.addNode("image", "img1", "color3")
img1.setInputValue("file", "<diff_albedo>", "filename")
img2 = ng3.addNode("image", "img2", "color3")
img2.setInputValue("file", "<dirt_albedo>", "filename")
img3 = ng3.addNode("image", "img3", "float")
img3.setInputValue("file", "<areamask>", "filename")
img4 = ng3.addNode("image", "img4", "float")
img4.setInputValue("file", "<noisemask>", "filename")
n5 = ng3.addNode("constant", "n5", "color3")
# For colorN, vectorN or matrix types, use the appropriate mx Type constructor.
n5.setInputValue("value", mx.Color3(0.8,1.0,1.3))
n6 = ng3.addNode("multiply", "n6", "color3")
inp1 = n6.addInput("in1", "color3")
inp1.setConnectedNode(n5)
inp2 = n6.addInput("in2", "color3")
inp2.setConnectedNode(img1)
n7 = ng3.addNode("contrast", "n7", "color3")
inp = n7.addInput("in", "color3")
inp.setConnectedNode(img2)
n7.setInputValue("amount", 0.2)
n7.setInputValue("pivot", 0.5)
n8 = ng3.addNode("mix", "n8", "color3")
infg = n8.addInput("fg", "color3")
infg.setConnectedNode(n7)
inbg = n8.addInput("bg", "color3")
inbg.setConnectedNode(n6)
inmx = n8.addInput("mix", "float")
inmx.setConnectedNode(img3)
t1 = ng3.addNode("texcoord", "t1", "vector2")
m1 = ng3.addNode("multiply", "m1", "vector2")
inp1 = m1.addInput("in1", "vector2")
inp1.setConnectedNode(t1)
m1.setInputValue("in2", 0.003)
# If limited floating-point precision results in output value strings like "0.00299999",
# you could instead write this as a ValueString (must add the input to the node first):
# inp2 = m1.addInput("in2", "float")
# inp2.setValueString("0.003")
n9 = ng3.addNode("noise2d", "n9", "color3")
intx = n9.addInput("texcoord", "vector2")
intx.setConnectedNode(m1)
n9.setInputValue("amplitude", mx.Vector3(0.05,0.04,0.06))
n10 = ng3.addNode("inside", "n10", "color3")
inmask = n10.addInput("mask", "float")
inmask.setConnectedNode(img4)
inp = n10.addInput("in", "color3")
inp.setConnectedNode(n9)
n11 = ng3.addNode("add", "n11", "color3")
inp1 = n11.addInput("in1", "color3")
inp1.setConnectedNode(n10)
inp2 = n11.addInput("in2", "color3")
inp2.setConnectedNode(n8)
nout1 = ng3.addOutput("albedo", "color3")
nout1.setConnectedNode(n11)
nout2 = ng3.addOutput("areamask", "float")
nout2.setConnectedNode(img3)
# It is not necessary to validate a document before writing but it's nice
# to know for sure. And you can validate any element (and its children)
# independently, not just the whole document.
rc = ng1.validate()
if (len(rc) >= 1 and rc[0]):
print("Nodegraph %s is valid." % ng1.getName())
else:
print("Nodegraph %s is NOT valid: %s" % (ng1.getName(), str(rc[1])))
rc = ng3.validate()
if (len(rc) >= 1 and rc[0]):
print("Nodegraph %s is valid." % ng3.getName())
else:
print("Nodegraph %s is NOT valid: %s" % (ng3.getName(), str(rc[1])))
outfile = "myNodeGraphs.mtlx"
mx.writeToXmlFile(doc, outfile)
print("Wrote nodegraphs to %s" % outfile)
if __name__ == '__main__':
main()