mirror of
https://github.com/johndoe6345789/SDL3CPlusPlus.git
synced 2026-04-26 06:34:57 +00:00
stuff
This commit is contained in:
0
MaterialX/python/MaterialXTest/__init__.py
Normal file
0
MaterialX/python/MaterialXTest/__init__.py
Normal file
126
MaterialX/python/MaterialXTest/genshader.py
Normal file
126
MaterialX/python/MaterialXTest/genshader.py
Normal file
@@ -0,0 +1,126 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
Unit tests for shader generation in MaterialX Python.
|
||||
'''
|
||||
|
||||
import os, unittest
|
||||
|
||||
import MaterialX as mx
|
||||
import MaterialX.PyMaterialXGenShader as mx_gen_shader
|
||||
import MaterialX.PyMaterialXGenOsl as mx_gen_osl
|
||||
|
||||
class TestGenShader(unittest.TestCase):
|
||||
def test_ShaderInterface(self):
|
||||
doc = mx.createDocument()
|
||||
searchPath = mx.getDefaultDataSearchPath()
|
||||
mx.loadLibraries(mx.getDefaultDataLibraryFolders(), searchPath, doc)
|
||||
|
||||
exampleName = u"shader_interface"
|
||||
|
||||
# Create a nodedef taking three color3 and producing another color3
|
||||
nodeDef = doc.addNodeDef("ND_foo", "color3", "foo")
|
||||
fooInputA = nodeDef.addInput("a", "color3")
|
||||
fooInputB = nodeDef.addInput("b", "color3")
|
||||
fooOutput = nodeDef.getOutput("out")
|
||||
fooInputA.setValue(mx.Color3(1.0, 1.0, 0.0))
|
||||
fooInputB.setValue(mx.Color3(0.8, 0.1, 0.1))
|
||||
|
||||
# Create an implementation graph for the nodedef performing
|
||||
# a multiplication of the three colors.
|
||||
nodeGraph = doc.addNodeGraph("IMP_foo")
|
||||
nodeGraph.setAttribute("nodedef", nodeDef.getName())
|
||||
|
||||
output = nodeGraph.addOutput(fooOutput.getName(), "color3")
|
||||
mult1 = nodeGraph.addNode("multiply", "mult1", "color3")
|
||||
in1 = mult1.addInput("in1", "color3")
|
||||
in1.setInterfaceName(fooInputA.getName())
|
||||
in2 = mult1.addInput("in2", "color3")
|
||||
in2.setInterfaceName(fooInputB.getName())
|
||||
output.setConnectedNode(mult1)
|
||||
|
||||
doc.addNode("foo", "foo1", "color3")
|
||||
output = doc.addOutput("foo_test", "color3");
|
||||
output.setNodeName("foo1");
|
||||
output.setAttribute("output", "o");
|
||||
|
||||
# Test for target
|
||||
targetDefs = doc.getTargetDefs()
|
||||
self.assertTrue(len(targetDefs))
|
||||
shadergen = mx_gen_osl.OslShaderGenerator.create()
|
||||
target = shadergen.getTarget()
|
||||
foundTarget = next((
|
||||
t for t in targetDefs
|
||||
if t.getName() == target), None)
|
||||
self.assertTrue(foundTarget)
|
||||
context = mx_gen_shader.GenContext(shadergen)
|
||||
context.registerSourceCodeSearchPath(searchPath)
|
||||
shadergen.registerTypeDefs(doc);
|
||||
|
||||
# Test generator with complete mode
|
||||
context.getOptions().shaderInterfaceType = mx_gen_shader.ShaderInterfaceType.SHADER_INTERFACE_COMPLETE;
|
||||
shader = shadergen.generate(exampleName, output, context);
|
||||
self.assertTrue(shader)
|
||||
self.assertTrue(len(shader.getSourceCode(mx_gen_shader.PIXEL_STAGE)) > 0)
|
||||
|
||||
ps = shader.getStage(mx_gen_shader.PIXEL_STAGE);
|
||||
uniforms = ps.getUniformBlock(mx_gen_osl.OSL_UNIFORMS)
|
||||
self.assertTrue(uniforms.size() == 2)
|
||||
|
||||
outputs = ps.getOutputBlock(mx_gen_osl.OSL_OUTPUTS)
|
||||
self.assertTrue(outputs.size() == 1)
|
||||
self.assertTrue(outputs[0].getName() == output.getName())
|
||||
|
||||
file = open(shader.getName() + "_complete.osl", "w+")
|
||||
file.write(shader.getSourceCode(mx_gen_shader.PIXEL_STAGE))
|
||||
file.close()
|
||||
os.remove(shader.getName() + "_complete.osl");
|
||||
|
||||
# Test generator with reduced mode
|
||||
context.getOptions().shaderInterfaceType = mx_gen_shader.ShaderInterfaceType.SHADER_INTERFACE_REDUCED;
|
||||
shader = shadergen.generate(exampleName, output, context);
|
||||
self.assertTrue(shader)
|
||||
self.assertTrue(len(shader.getSourceCode(mx_gen_shader.PIXEL_STAGE)) > 0)
|
||||
|
||||
ps = shader.getStage(mx_gen_shader.PIXEL_STAGE);
|
||||
uniforms = ps.getUniformBlock(mx_gen_osl.OSL_UNIFORMS)
|
||||
self.assertTrue(uniforms.size() == 0)
|
||||
|
||||
outputs = ps.getOutputBlock(mx_gen_osl.OSL_OUTPUTS)
|
||||
self.assertTrue(outputs.size() == 1)
|
||||
self.assertTrue(outputs[0].getName() == output.getName())
|
||||
|
||||
file = open(shader.getName() + "_reduced.osl", "w+")
|
||||
file.write(shader.getSourceCode(mx_gen_shader.PIXEL_STAGE))
|
||||
file.close()
|
||||
os.remove(shader.getName() + "_reduced.osl");
|
||||
|
||||
# Define a custom attribute
|
||||
customAttribute = doc.addAttributeDef("AD_attribute_node_name");
|
||||
self.assertIsNotNone(customAttribute)
|
||||
customAttribute.setType("string");
|
||||
customAttribute.setAttrName("node_name");
|
||||
customAttribute.setExportable(True);
|
||||
|
||||
# Define a nodedef referencing the custom attribute.
|
||||
stdSurfNodeDef = doc.getNodeDef("ND_standard_surface_surfaceshader");
|
||||
self.assertIsNotNone(stdSurfNodeDef)
|
||||
stdSurfNodeDef.setAttribute("node_name", "Standard_Surface_Number_1");
|
||||
self.assertTrue(stdSurfNodeDef.getAttribute("node_name") == "Standard_Surface_Number_1")
|
||||
stdSurf1 = doc.addNodeInstance(stdSurfNodeDef, "standardSurface1");
|
||||
self.assertIsNotNone(stdSurf1)
|
||||
|
||||
# Register shader metadata
|
||||
shadergen.registerShaderMetadata(doc, context);
|
||||
|
||||
# Generate and test that attribute is in the code
|
||||
context.getOptions().shaderInterfaceType = mx_gen_shader.ShaderInterfaceType.SHADER_INTERFACE_COMPLETE;
|
||||
shader = shadergen.generate(stdSurf1.getName(), stdSurf1, context);
|
||||
self.assertIsNotNone(shader)
|
||||
code = shader.getSourceCode(mx_gen_shader.PIXEL_STAGE)
|
||||
self.assertTrue('Standard_Surface_Number_1' in code)
|
||||
self.assertTrue('node_name' in code)
|
||||
|
||||
print()
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
517
MaterialX/python/MaterialXTest/main.py
Normal file
517
MaterialX/python/MaterialXTest/main.py
Normal file
@@ -0,0 +1,517 @@
|
||||
#!/usr/bin/env python
|
||||
'''
|
||||
Unit tests for MaterialX Python.
|
||||
'''
|
||||
|
||||
import math, os, unittest
|
||||
|
||||
import MaterialX as mx
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
_testValues = (1,
|
||||
True,
|
||||
1.0,
|
||||
mx.Color3(0.1, 0.2, 0.3),
|
||||
mx.Color4(0.1, 0.2, 0.3, 0.4),
|
||||
mx.Vector2(1.0, 2.0),
|
||||
mx.Vector3(1.0, 2.0, 3.0),
|
||||
mx.Vector4(1.0, 2.0, 3.0, 4.0),
|
||||
mx.Matrix33(0.0),
|
||||
mx.Matrix44(1.0),
|
||||
'value',
|
||||
[1, 2, 3],
|
||||
[False, True, False],
|
||||
[1.0, 2.0, 3.0],
|
||||
['one', 'two', 'three'])
|
||||
|
||||
_fileDir = os.path.dirname(os.path.abspath(__file__))
|
||||
_libraryDir = os.path.join(_fileDir, '../../libraries/stdlib/')
|
||||
_exampleDir = os.path.join(_fileDir, '../../resources/Materials/Examples/')
|
||||
_searchPath = _libraryDir + mx.PATH_LIST_SEPARATOR + _exampleDir
|
||||
|
||||
_libraryFilenames = ('stdlib_defs.mtlx',
|
||||
'stdlib_ng.mtlx')
|
||||
_exampleFilenames = ('StandardSurface/standard_surface_brass_tiled.mtlx',
|
||||
'StandardSurface/standard_surface_brick_procedural.mtlx',
|
||||
'StandardSurface/standard_surface_carpaint.mtlx',
|
||||
'StandardSurface/standard_surface_marble_solid.mtlx',
|
||||
'StandardSurface/standard_surface_look_brass_tiled.mtlx',
|
||||
'UsdPreviewSurface/usd_preview_surface_gold.mtlx',
|
||||
'UsdPreviewSurface/usd_preview_surface_plastic.mtlx')
|
||||
|
||||
_epsilon = 1e-4
|
||||
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
class TestMaterialX(unittest.TestCase):
|
||||
def test_Globals(self):
|
||||
self.assertTrue(mx.__version__ == mx.getVersionString())
|
||||
|
||||
def test_DataTypes(self):
|
||||
for value in _testValues:
|
||||
valueString = mx.getValueString(value)
|
||||
typeString = mx.getTypeString(value)
|
||||
newValue = mx.createValueFromStrings(valueString, typeString)
|
||||
self.assertTrue(newValue == value)
|
||||
self.assertTrue(mx.getTypeString(newValue) == typeString)
|
||||
|
||||
def test_Vectors(self):
|
||||
v1 = mx.Vector3(1, 2, 3)
|
||||
v2 = mx.Vector3(2, 4, 6)
|
||||
|
||||
# Indexing operators
|
||||
self.assertTrue(v1[2] == 3)
|
||||
v1[2] = 4
|
||||
self.assertTrue(v1[2] == 4)
|
||||
v1[2] = 3
|
||||
|
||||
# Component-wise operators
|
||||
self.assertTrue(v2 + v1 == mx.Vector3(3, 6, 9))
|
||||
self.assertTrue(v2 - v1 == mx.Vector3(1, 2, 3))
|
||||
self.assertTrue(v2 * v1 == mx.Vector3(2, 8, 18))
|
||||
self.assertTrue(v2 / v1 == mx.Vector3(2, 2, 2))
|
||||
v2 += v1
|
||||
self.assertTrue(v2 == mx.Vector3(3, 6, 9))
|
||||
v2 -= v1
|
||||
self.assertTrue(v2 == mx.Vector3(2, 4, 6))
|
||||
v2 *= v1
|
||||
self.assertTrue(v2 == mx.Vector3(2, 8, 18))
|
||||
v2 /= v1
|
||||
self.assertTrue(v2 == mx.Vector3(2, 4, 6))
|
||||
self.assertTrue(v1 * 2 == v2)
|
||||
self.assertTrue(v2 / 2 == v1)
|
||||
|
||||
# Unary operation
|
||||
self.assertTrue(-v1 == mx.Vector3(-1, -2, -3))
|
||||
v1 *= -1
|
||||
self.assertTrue(+v1 == mx.Vector3(-1, -2, -3))
|
||||
v1 *= -1
|
||||
|
||||
# Geometric methods
|
||||
v3 = mx.Vector4(4)
|
||||
self.assertTrue(v3.getMagnitude() == 8)
|
||||
self.assertTrue(v3.getNormalized().getMagnitude() == 1)
|
||||
self.assertTrue(v1.dot(v2) == 28)
|
||||
self.assertTrue(v1.cross(v2) == mx.Vector3())
|
||||
|
||||
# Vector copy
|
||||
v4 = v2.copy()
|
||||
self.assertTrue(v4 == v2)
|
||||
v4[0] += 1;
|
||||
self.assertTrue(v4 != v2)
|
||||
|
||||
def test_Matrices(self):
|
||||
# Translation and scale
|
||||
trans = mx.Matrix44.createTranslation(mx.Vector3(1, 2, 3))
|
||||
scale = mx.Matrix44.createScale(mx.Vector3(2))
|
||||
self.assertTrue(trans == mx.Matrix44(1, 0, 0, 0,
|
||||
0, 1, 0, 0,
|
||||
0, 0, 1, 0,
|
||||
1, 2, 3, 1))
|
||||
self.assertTrue(scale == mx.Matrix44(2, 0, 0, 0,
|
||||
0, 2, 0, 0,
|
||||
0, 0, 2, 0,
|
||||
0, 0, 0, 1))
|
||||
|
||||
# Indexing operators
|
||||
self.assertTrue(trans[3, 2] == 3)
|
||||
trans[3, 2] = 4
|
||||
self.assertTrue(trans[3, 2] == 4)
|
||||
trans[3, 2] = 3
|
||||
|
||||
# Matrix methods
|
||||
self.assertTrue(trans.getTranspose() == mx.Matrix44(1, 0, 0, 1,
|
||||
0, 1, 0, 2,
|
||||
0, 0, 1, 3,
|
||||
0, 0, 0, 1))
|
||||
self.assertTrue(scale.getTranspose() == scale)
|
||||
self.assertTrue(trans.getDeterminant() == 1)
|
||||
self.assertTrue(scale.getDeterminant() == 8)
|
||||
self.assertTrue(trans.getInverse() ==
|
||||
mx.Matrix44.createTranslation(mx.Vector3(-1, -2, -3)))
|
||||
|
||||
# Matrix copy
|
||||
trans2 = trans.copy()
|
||||
self.assertTrue(trans2 == trans)
|
||||
trans2[0, 0] += 1;
|
||||
self.assertTrue(trans2 != trans)
|
||||
|
||||
# Matrix product
|
||||
prod1 = trans * scale
|
||||
prod2 = scale * trans
|
||||
prod3 = trans * 2
|
||||
prod4 = trans.copy()
|
||||
prod4 *= scale
|
||||
self.assertTrue(prod1 == mx.Matrix44(2, 0, 0, 0,
|
||||
0, 2, 0, 0,
|
||||
0, 0, 2, 0,
|
||||
2, 4, 6, 1))
|
||||
self.assertTrue(prod2 == mx.Matrix44(2, 0, 0, 0,
|
||||
0, 2, 0, 0,
|
||||
0, 0, 2, 0,
|
||||
1, 2, 3, 1))
|
||||
self.assertTrue(prod3 == mx.Matrix44(2, 0, 0, 0,
|
||||
0, 2, 0, 0,
|
||||
0, 0, 2, 0,
|
||||
2, 4, 6, 2))
|
||||
self.assertTrue(prod4 == prod1)
|
||||
|
||||
# Matrix division
|
||||
quot1 = prod1 / scale
|
||||
quot2 = prod2 / trans
|
||||
quot3 = prod3 / 2
|
||||
quot4 = quot1.copy()
|
||||
quot4 /= trans
|
||||
self.assertTrue(quot1 == trans)
|
||||
self.assertTrue(quot2 == scale)
|
||||
self.assertTrue(quot3 == trans)
|
||||
self.assertTrue(quot4 == mx.Matrix44.IDENTITY)
|
||||
|
||||
# Unary operation
|
||||
self.assertTrue(-trans == mx.Matrix44(-1, 0, 0, 0,
|
||||
0, -1, 0, 0,
|
||||
0, 0, -1, 0,
|
||||
-1, -2, -3, -1))
|
||||
trans *= -1
|
||||
self.assertTrue(+trans == mx.Matrix44(-1, 0, 0, 0,
|
||||
0, -1, 0, 0,
|
||||
0, 0, -1, 0,
|
||||
-1, -2, -3, -1))
|
||||
trans *= -1
|
||||
|
||||
# 2D rotation
|
||||
rot1 = mx.Matrix33.createRotation(math.pi / 2)
|
||||
rot2 = mx.Matrix33.createRotation(math.pi)
|
||||
self.assertTrue((rot1 * rot1).isEquivalent(rot2, _epsilon))
|
||||
self.assertTrue(rot2.isEquivalent(
|
||||
mx.Matrix33.createScale(mx.Vector2(-1)), _epsilon))
|
||||
self.assertTrue((rot2 * rot2).isEquivalent(mx.Matrix33.IDENTITY, _epsilon))
|
||||
|
||||
# 3D rotation
|
||||
rotX = mx.Matrix44.createRotationX(math.pi)
|
||||
rotY = mx.Matrix44.createRotationY(math.pi)
|
||||
rotZ = mx.Matrix44.createRotationZ(math.pi)
|
||||
self.assertTrue((rotX * rotY).isEquivalent(
|
||||
mx.Matrix44.createScale(mx.Vector3(-1, -1, 1)), _epsilon))
|
||||
self.assertTrue((rotX * rotZ).isEquivalent(
|
||||
mx.Matrix44.createScale(mx.Vector3(-1, 1, -1)), _epsilon))
|
||||
self.assertTrue((rotY * rotZ).isEquivalent(
|
||||
mx.Matrix44.createScale(mx.Vector3(1, -1, -1)), _epsilon))
|
||||
|
||||
|
||||
def test_BuildDocument(self):
|
||||
# Create a document.
|
||||
doc = mx.createDocument()
|
||||
|
||||
# Create a node graph with constant and image sources.
|
||||
nodeGraph = doc.addNodeGraph()
|
||||
self.assertTrue(nodeGraph)
|
||||
self.assertRaises(LookupError, doc.addNodeGraph, nodeGraph.getName())
|
||||
constant = nodeGraph.addNode('constant')
|
||||
image = nodeGraph.addNode('image')
|
||||
|
||||
# Connect sources to outputs.
|
||||
output1 = nodeGraph.addOutput()
|
||||
output2 = nodeGraph.addOutput()
|
||||
output1.setConnectedNode(constant)
|
||||
output2.setConnectedNode(image)
|
||||
self.assertTrue(output1.getConnectedNode() == constant)
|
||||
self.assertTrue(output2.getConnectedNode() == image)
|
||||
self.assertTrue(output1.getUpstreamElement() == constant)
|
||||
self.assertTrue(output2.getUpstreamElement() == image)
|
||||
|
||||
# Set constant node color.
|
||||
color = mx.Color3(0.1, 0.2, 0.3)
|
||||
constant.setInputValue('value', color)
|
||||
self.assertTrue(constant.getInputValue('value') == color)
|
||||
|
||||
# Set image node file.
|
||||
file = 'image1.tif'
|
||||
image.setInputValue('file', file, 'filename')
|
||||
self.assertTrue(image.getInputValue('file') == file)
|
||||
|
||||
# Create a custom nodedef.
|
||||
nodeDef = doc.addNodeDef('nodeDef1', 'float', 'turbulence3d')
|
||||
nodeDef.setInputValue('octaves', 3)
|
||||
nodeDef.setInputValue('lacunarity', 2.0)
|
||||
nodeDef.setInputValue('gain', 0.5)
|
||||
|
||||
# Reference the custom nodedef.
|
||||
custom = nodeGraph.addNode('turbulence3d', 'turbulence1', 'float')
|
||||
self.assertTrue(custom.getInputValue('octaves') == 3)
|
||||
custom.setInputValue('octaves', 5)
|
||||
self.assertTrue(custom.getInputValue('octaves') == 5)
|
||||
|
||||
# Test scoped attributes.
|
||||
nodeGraph.setFilePrefix('folder/')
|
||||
nodeGraph.setColorSpace('lin_rec709')
|
||||
self.assertTrue(image.getInput('file').getResolvedValueString() == 'folder/image1.tif')
|
||||
self.assertTrue(constant.getActiveColorSpace() == 'lin_rec709')
|
||||
|
||||
# Create a simple shader interface.
|
||||
simpleSrf = doc.addNodeDef('', 'surfaceshader', 'simpleSrf')
|
||||
simpleSrf.setInputValue('diffColor', mx.Color3(1.0))
|
||||
simpleSrf.setInputValue('specColor', mx.Color3(0.0))
|
||||
roughness = simpleSrf.setInputValue('roughness', 0.25)
|
||||
self.assertTrue(roughness.getIsUniform() == False)
|
||||
roughness.setIsUniform(True);
|
||||
self.assertTrue(roughness.getIsUniform() == True)
|
||||
|
||||
# Instantiate shader and material nodes.
|
||||
shaderNode = doc.addNodeInstance(simpleSrf)
|
||||
materialNode = doc.addMaterialNode('', shaderNode)
|
||||
|
||||
# Bind the diffuse color input to the constant color output.
|
||||
shaderNode.setConnectedOutput('diffColor', output1)
|
||||
self.assertTrue(shaderNode.getUpstreamElement() == constant)
|
||||
|
||||
# Bind the roughness input to a value.
|
||||
instanceRoughness = shaderNode.setInputValue('roughness', 0.5)
|
||||
self.assertTrue(instanceRoughness.getValue() == 0.5)
|
||||
self.assertTrue(instanceRoughness.getDefaultValue() == 0.25)
|
||||
|
||||
# Create a look for the material.
|
||||
look = doc.addLook()
|
||||
self.assertTrue(len(doc.getLooks()) == 1)
|
||||
|
||||
# Bind the material to a geometry string.
|
||||
matAssign1 = look.addMaterialAssign("matAssign1", materialNode.getName())
|
||||
matAssign1.setGeom("/robot1")
|
||||
self.assertTrue(matAssign1.getReferencedMaterial() == materialNode)
|
||||
self.assertTrue(len(mx.getGeometryBindings(materialNode, "/robot1")) == 1)
|
||||
self.assertTrue(len(mx.getGeometryBindings(materialNode, "/robot2")) == 0)
|
||||
|
||||
# Bind the material to a collection.
|
||||
matAssign2 = look.addMaterialAssign("matAssign2", materialNode.getName())
|
||||
collection = doc.addCollection()
|
||||
collection.setIncludeGeom("/robot2")
|
||||
collection.setExcludeGeom("/robot2/left_arm")
|
||||
matAssign2.setCollection(collection)
|
||||
self.assertTrue(len(mx.getGeometryBindings(materialNode, "/robot2")) == 1)
|
||||
self.assertTrue(len(mx.getGeometryBindings(materialNode, "/robot2/right_arm")) == 1)
|
||||
self.assertTrue(len(mx.getGeometryBindings(materialNode, "/robot2/left_arm")) == 0)
|
||||
|
||||
# Create a property assignment.
|
||||
propertyAssign = look.addPropertyAssign()
|
||||
propertyAssign.setProperty("twosided")
|
||||
propertyAssign.setGeom("/robot1")
|
||||
propertyAssign.setValue(True)
|
||||
self.assertTrue(propertyAssign.getProperty() == "twosided")
|
||||
self.assertTrue(propertyAssign.getGeom() == "/robot1")
|
||||
self.assertTrue(propertyAssign.getValue() == True)
|
||||
|
||||
# Create a property set assignment.
|
||||
propertySet = doc.addPropertySet()
|
||||
propertySet.setPropertyValue('matte', False)
|
||||
self.assertTrue(propertySet.getPropertyValue('matte') == False)
|
||||
propertySetAssign = look.addPropertySetAssign()
|
||||
propertySetAssign.setPropertySet(propertySet)
|
||||
propertySetAssign.setGeom('/robot1')
|
||||
self.assertTrue(propertySetAssign.getPropertySet() == propertySet)
|
||||
self.assertTrue(propertySetAssign.getGeom() == '/robot1')
|
||||
|
||||
# Create a variant set.
|
||||
variantSet = doc.addVariantSet()
|
||||
variantSet.addVariant("original")
|
||||
variantSet.addVariant("damaged")
|
||||
self.assertTrue(len(variantSet.getVariants()) == 2)
|
||||
|
||||
# Validate the document.
|
||||
valid, message = doc.validate()
|
||||
self.assertTrue(valid, 'Document returned validation warnings: ' + message)
|
||||
|
||||
# Disconnect outputs from sources.
|
||||
output1.setConnectedNode(None)
|
||||
output2.setConnectedNode(None)
|
||||
self.assertTrue(output1.getConnectedNode() == None)
|
||||
self.assertTrue(output2.getConnectedNode() == None)
|
||||
|
||||
def test_TraverseGraph(self):
|
||||
# Create a document.
|
||||
doc = mx.createDocument()
|
||||
|
||||
# Create a node graph with the following structure:
|
||||
#
|
||||
# [image1] [constant] [image2]
|
||||
# \ / |
|
||||
# [multiply] [contrast] [noise3d]
|
||||
# \____________ | ____________/
|
||||
# [mix]
|
||||
# |
|
||||
# [output]
|
||||
#
|
||||
nodeGraph = doc.addNodeGraph()
|
||||
image1 = nodeGraph.addNode('image')
|
||||
image2 = nodeGraph.addNode('image')
|
||||
constant = nodeGraph.addNode('constant')
|
||||
multiply = nodeGraph.addNode('multiply')
|
||||
contrast = nodeGraph.addNode('contrast')
|
||||
noise3d = nodeGraph.addNode('noise3d')
|
||||
mix = nodeGraph.addNode('mix')
|
||||
output = nodeGraph.addOutput()
|
||||
multiply.setConnectedNode('in1', image1)
|
||||
multiply.setConnectedNode('in2', constant)
|
||||
contrast.setConnectedNode('in', image2)
|
||||
mix.setConnectedNode('fg', multiply)
|
||||
mix.setConnectedNode('bg', contrast)
|
||||
mix.setConnectedNode('mask', noise3d)
|
||||
output.setConnectedNode(mix)
|
||||
|
||||
# Validate the document.
|
||||
valid, message = doc.validate()
|
||||
self.assertTrue(valid, 'Document returned validation warnings: ' + message)
|
||||
|
||||
# Traverse the document tree (implicit iterator).
|
||||
nodeCount = 0
|
||||
for elem in doc.traverseTree():
|
||||
if elem.isA(mx.Node):
|
||||
nodeCount += 1
|
||||
self.assertTrue(nodeCount == 7)
|
||||
|
||||
# Traverse the document tree (explicit iterator).
|
||||
nodeCount = 0
|
||||
maxElementDepth = 0
|
||||
treeIter = doc.traverseTree()
|
||||
for elem in treeIter:
|
||||
if elem.isA(mx.Node):
|
||||
nodeCount += 1
|
||||
maxElementDepth = max(maxElementDepth, treeIter.getElementDepth())
|
||||
self.assertTrue(nodeCount == 7)
|
||||
self.assertTrue(maxElementDepth == 3)
|
||||
|
||||
# Traverse the document tree (prune subtree).
|
||||
nodeCount = 0
|
||||
treeIter = doc.traverseTree()
|
||||
for elem in treeIter:
|
||||
if elem.isA(mx.Node):
|
||||
nodeCount += 1
|
||||
if elem.isA(mx.NodeGraph):
|
||||
treeIter.setPruneSubtree(True)
|
||||
self.assertTrue(nodeCount == 0)
|
||||
|
||||
# Traverse upstream from the graph output (implicit iterator).
|
||||
nodeCount = 0
|
||||
for edge in output.traverseGraph():
|
||||
upstreamElem = edge.getUpstreamElement()
|
||||
connectingElem = edge.getConnectingElement()
|
||||
downstreamElem = edge.getDownstreamElement()
|
||||
if upstreamElem.isA(mx.Node):
|
||||
nodeCount += 1
|
||||
if downstreamElem.isA(mx.Node):
|
||||
self.assertTrue(connectingElem.isA(mx.Input))
|
||||
self.assertTrue(nodeCount == 7)
|
||||
|
||||
# Traverse upstream from the graph output (explicit iterator).
|
||||
nodeCount = 0
|
||||
maxElementDepth = 0
|
||||
maxNodeDepth = 0
|
||||
graphIter = output.traverseGraph()
|
||||
for edge in graphIter:
|
||||
upstreamElem = edge.getUpstreamElement()
|
||||
connectingElem = edge.getConnectingElement()
|
||||
downstreamElem = edge.getDownstreamElement()
|
||||
if upstreamElem.isA(mx.Node):
|
||||
nodeCount += 1
|
||||
maxElementDepth = max(maxElementDepth, graphIter.getElementDepth())
|
||||
maxNodeDepth = max(maxNodeDepth, graphIter.getNodeDepth())
|
||||
self.assertTrue(nodeCount == 7)
|
||||
self.assertTrue(maxElementDepth == 3)
|
||||
self.assertTrue(maxNodeDepth == 3)
|
||||
|
||||
# Traverse upstream from the graph output (prune subgraph).
|
||||
nodeCount = 0
|
||||
graphIter = output.traverseGraph()
|
||||
for edge in graphIter:
|
||||
upstreamElem = edge.getUpstreamElement()
|
||||
connectingElem = edge.getConnectingElement()
|
||||
downstreamElem = edge.getDownstreamElement()
|
||||
if upstreamElem.isA(mx.Node):
|
||||
nodeCount += 1
|
||||
if upstreamElem.getCategory() == 'multiply':
|
||||
graphIter.setPruneSubgraph(True)
|
||||
self.assertTrue(nodeCount == 5)
|
||||
|
||||
# Create and detect a cycle.
|
||||
multiply.setConnectedNode('in2', mix)
|
||||
self.assertTrue(output.hasUpstreamCycle())
|
||||
self.assertFalse(doc.validate()[0])
|
||||
multiply.setConnectedNode('in2', constant)
|
||||
self.assertFalse(output.hasUpstreamCycle())
|
||||
self.assertTrue(doc.validate()[0])
|
||||
|
||||
# Create and detect a loop.
|
||||
contrast.setConnectedNode('in', contrast)
|
||||
self.assertTrue(output.hasUpstreamCycle())
|
||||
self.assertFalse(doc.validate()[0])
|
||||
contrast.setConnectedNode('in', image2)
|
||||
self.assertFalse(output.hasUpstreamCycle())
|
||||
self.assertTrue(doc.validate()[0])
|
||||
|
||||
def test_Xmlio(self):
|
||||
# Read the standard library.
|
||||
libs = []
|
||||
for filename in _libraryFilenames:
|
||||
lib = mx.createDocument()
|
||||
mx.readFromXmlFile(lib, filename, _searchPath)
|
||||
libs.append(lib)
|
||||
|
||||
# Declare write predicate for write filter test
|
||||
def skipLibraryElement(elem):
|
||||
return not elem.hasSourceUri()
|
||||
|
||||
# Read and validate each example document.
|
||||
for filename in _exampleFilenames:
|
||||
doc = mx.createDocument()
|
||||
mx.readFromXmlFile(doc, filename, _searchPath)
|
||||
valid, message = doc.validate()
|
||||
self.assertTrue(valid, filename + ' returned validation warnings: ' + message)
|
||||
|
||||
# Copy the document.
|
||||
copiedDoc = doc.copy()
|
||||
self.assertTrue(copiedDoc == doc)
|
||||
copiedDoc.addLook()
|
||||
self.assertTrue(copiedDoc != doc)
|
||||
|
||||
# Traverse the document tree.
|
||||
valueElementCount = 0
|
||||
for elem in doc.traverseTree():
|
||||
if elem.isA(mx.ValueElement):
|
||||
valueElementCount += 1
|
||||
self.assertTrue(valueElementCount > 0)
|
||||
|
||||
# Serialize to XML.
|
||||
writeOptions = mx.XmlWriteOptions()
|
||||
writeOptions.writeXIncludeEnable = False
|
||||
xmlString = mx.writeToXmlString(doc, writeOptions)
|
||||
|
||||
# Verify that the serialized document is identical.
|
||||
writtenDoc = mx.createDocument()
|
||||
mx.readFromXmlString(writtenDoc, xmlString)
|
||||
self.assertTrue(writtenDoc == doc)
|
||||
|
||||
# Combine document with the standard library.
|
||||
doc2 = doc.copy()
|
||||
for lib in libs:
|
||||
doc2.importLibrary(lib)
|
||||
self.assertTrue(doc2.validate()[0])
|
||||
|
||||
# Write without definitions
|
||||
writeOptions.writeXIncludeEnable = False
|
||||
writeOptions.elementPredicate = skipLibraryElement
|
||||
result = mx.writeToXmlString(doc2, writeOptions)
|
||||
doc3 = mx.createDocument()
|
||||
mx.readFromXmlString(doc3, result)
|
||||
self.assertTrue(len(doc3.getNodeDefs()) == 0)
|
||||
|
||||
# Read the same document twice, and verify that duplicate elements
|
||||
# are skipped.
|
||||
doc = mx.createDocument()
|
||||
filename = 'StandardSurface/standard_surface_carpaint.mtlx'
|
||||
mx.readFromXmlFile(doc, filename, _searchPath)
|
||||
mx.readFromXmlFile(doc, filename, _searchPath)
|
||||
self.assertTrue(doc.validate()[0])
|
||||
|
||||
#--------------------------------------------------------------------------------
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
2
MaterialX/python/MaterialXTest/tests_to_html.bat
Normal file
2
MaterialX/python/MaterialXTest/tests_to_html.bat
Normal file
@@ -0,0 +1,2 @@
|
||||
@echo off
|
||||
python tests_to_html.py -i1 ../../build %* -d
|
||||
230
MaterialX/python/MaterialXTest/tests_to_html.py
Normal file
230
MaterialX/python/MaterialXTest/tests_to_html.py
Normal file
@@ -0,0 +1,230 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
import sys
|
||||
import os
|
||||
import datetime
|
||||
import argparse
|
||||
|
||||
try:
|
||||
# Install pillow via pip to enable image differencing and statistics.
|
||||
from PIL import Image, ImageChops, ImageStat
|
||||
DIFF_ENABLED = True
|
||||
except Exception:
|
||||
DIFF_ENABLED = False
|
||||
|
||||
def computeDiff(image1Path, image2Path, imageDiffPath):
|
||||
try:
|
||||
if os.path.exists(imageDiffPath):
|
||||
os.remove(imageDiffPath)
|
||||
|
||||
if not os.path.exists(image1Path):
|
||||
print ("Image diff input missing: " + image1Path)
|
||||
return
|
||||
|
||||
if not os.path.exists(image2Path):
|
||||
print ("Image diff input missing: " + image2Path)
|
||||
return
|
||||
|
||||
image1 = Image.open(image1Path).convert('RGB')
|
||||
image2 = Image.open(image2Path).convert('RGB')
|
||||
diff = ImageChops.difference(image1, image2)
|
||||
diff.save(imageDiffPath)
|
||||
diffStat = ImageStat.Stat(diff)
|
||||
return sum(diffStat.rms) / (3.0 * 255.0)
|
||||
except Exception:
|
||||
if os.path.exists(imageDiffPath):
|
||||
os.remove(imageDiffPath)
|
||||
print ("Failed to create image diff between: " + image1Path + ", " + image2Path)
|
||||
|
||||
def main(args=None):
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-i1', '--inputdir1', dest='inputdir1', action='store', help='Input directory', default=".")
|
||||
parser.add_argument('-i2', '--inputdir2', dest='inputdir2', action='store', help='Second input directory', default="")
|
||||
parser.add_argument('-i3', '--inputdir3', dest='inputdir3', action='store', help='Third input directory', default="")
|
||||
parser.add_argument('-o', '--outputfile', dest='outputfile', action='store', help='Output file name', default="tests.html")
|
||||
parser.add_argument('-d', '--diff', dest='CREATE_DIFF', action='store_true', help='Perform image diff', default=False)
|
||||
parser.add_argument('-t', '--timestamp', dest='ENABLE_TIMESTAMPS', action='store_true', help='Write image timestamps', default=False)
|
||||
parser.add_argument('-w', '--imagewidth', type=int, dest='imagewidth', action='store', help='Set image display width', default=256)
|
||||
parser.add_argument('-ht', '--imageheight', type=int, dest='imageheight', action='store', help='Set image display height', default=256)
|
||||
parser.add_argument('-cp', '--cellpadding', type=int, dest='cellpadding', action='store', help='Set table cell padding', default=0)
|
||||
parser.add_argument('-tb', '--tableborder', type=int, dest='tableborder', action='store', help='Table border width. 0 means no border', default=3)
|
||||
parser.add_argument('-l1', '--lang1', dest='lang1', action='store', help='First target language for comparison. Default is glsl', default="glsl")
|
||||
parser.add_argument('-l2', '--lang2', dest='lang2', action='store', help='Second target language for comparison. Default is osl', default="osl")
|
||||
parser.add_argument('-l3', '--lang3', dest='lang3', action='store', help='Third target language for comparison. Default is empty', default="")
|
||||
parser.add_argument('-e', '--error', dest='error', action='store', help='Filter out results with RMS less than this. Negative means all results are kept.', default=-1, type=float)
|
||||
|
||||
args = parser.parse_args(args)
|
||||
|
||||
fh = open(args.outputfile,"w+")
|
||||
fh.write("<html>\n")
|
||||
fh.write("<style>\n")
|
||||
fh.write("td {")
|
||||
fh.write(" padding: " + str(args.cellpadding) + ";")
|
||||
fh.write(" border: " + str(args.tableborder) + "px solid black;")
|
||||
fh.write("}")
|
||||
fh.write("table, tbody, th, .td_image {")
|
||||
fh.write(" border-collapse: collapse;")
|
||||
fh.write(" padding: 0;")
|
||||
fh.write(" margin: 0;")
|
||||
fh.write("}")
|
||||
fh.write("</style>")
|
||||
fh.write("<body>\n")
|
||||
|
||||
if args.inputdir1 == ".":
|
||||
args.inputdir1 = os.getcwd()
|
||||
|
||||
if args.inputdir2 == ".":
|
||||
args.inputdir2 = os.getcwd()
|
||||
elif args.inputdir2 == "":
|
||||
args.inputdir2 = args.inputdir1
|
||||
|
||||
if args.inputdir3 == ".":
|
||||
args.inputdir3 = os.getcwd()
|
||||
elif args.inputdir3 == "":
|
||||
args.inputdir3 = args.inputdir1
|
||||
|
||||
useThirdLang = args.lang3
|
||||
|
||||
if useThirdLang:
|
||||
fh.write("<h3>" + args.lang1 + " (in: " + args.inputdir1 + ") vs "+ args.lang2 + " (in: " + args.inputdir2 + ") vs "+ args.lang3 + " (in: " + args.inputdir3 + ")</h3>\n")
|
||||
else:
|
||||
fh.write("<h3>" + args.lang1 + " (in: " + args.inputdir1 + ") vs "+ args.lang2 + " (in: " + args.inputdir2 + ")</h3>\n")
|
||||
|
||||
if not DIFF_ENABLED and args.CREATE_DIFF:
|
||||
print("--diff argument ignored. Diff utility not installed.")
|
||||
|
||||
# Remove potential trailing path separators
|
||||
if args.inputdir1[-1:] == '/' or args.inputdir1[-1:] == '\\':
|
||||
args.inputdir1 = args.inputdir1[:-1]
|
||||
if args.inputdir2[-1:] == '/' or args.inputdir2[-1:] == '\\':
|
||||
args.inputdir2 = args.inputdir2[:-1]
|
||||
if args.inputdir3[-1:] == '/' or args.inputdir3[-1:] == '\\':
|
||||
args.inputdir3 = args.inputdir3[:-1]
|
||||
|
||||
# Get all source files
|
||||
langFiles1 = []
|
||||
langPaths1 = []
|
||||
for subdir, _, files in os.walk(args.inputdir1):
|
||||
for curFile in files:
|
||||
if curFile.endswith(args.lang1 + ".png"):
|
||||
langFiles1.append(curFile)
|
||||
langPaths1.append(subdir)
|
||||
|
||||
# Get all destination files, matching source files
|
||||
langFiles2 = []
|
||||
langPaths2 = []
|
||||
langFiles3 = []
|
||||
langPaths3 = []
|
||||
preFixLen: int = len(args.inputdir1) + 1 # including the path separator
|
||||
postFix: str = args.lang1 + ".png"
|
||||
for file1, path1 in zip(langFiles1, langPaths1):
|
||||
# Allow for just one language to be shown if source and dest are the same.
|
||||
# Otherwise add in equivalent name with dest language replacement if
|
||||
# pointing to the same directory
|
||||
if args.inputdir1 != args.inputdir2 or args.lang1 != args.lang2:
|
||||
file2 = file1[:-len(postFix)] + args.lang2 + ".png"
|
||||
path2 = os.path.join(args.inputdir2, path1[len(args.inputdir1)+1:])
|
||||
else:
|
||||
file2 = ""
|
||||
path2 = None
|
||||
langFiles2.append(file2)
|
||||
langPaths2.append(path2)
|
||||
|
||||
if useThirdLang:
|
||||
file3 = file1[:-len(postFix)] + args.lang3 + ".png"
|
||||
path3 = os.path.join(args.inputdir2, path1[len(args.inputdir1)+1:])
|
||||
else:
|
||||
file3 = ""
|
||||
path3 = None
|
||||
langFiles3.append(file3)
|
||||
langPaths3.append(path3)
|
||||
|
||||
if langFiles1:
|
||||
curPath = ""
|
||||
for file1, file2, file3, path1, path2, path3 in zip(langFiles1, langFiles2, langFiles3, langPaths1, langPaths2, langPaths3):
|
||||
|
||||
fullPath1 = os.path.join(path1, file1) if file1 else None
|
||||
fullPath2 = os.path.join(path2, file2) if file2 else None
|
||||
fullPath3 = os.path.join(path3, file3) if file3 else None
|
||||
diffPath1 = diffPath2 = diffPath3 = None
|
||||
diffRms1 = diffRms2 = diffRms3 = None
|
||||
|
||||
if file1 and file2 and DIFF_ENABLED and args.CREATE_DIFF:
|
||||
diffPath1 = fullPath1[0:-8] + "_" + args.lang1 + "-1_vs_" + args.lang2 + "-2_diff.png"
|
||||
diffRms1 = computeDiff(fullPath1, fullPath2, diffPath1)
|
||||
|
||||
if useThirdLang and file1 and file3 and DIFF_ENABLED and args.CREATE_DIFF:
|
||||
diffPath2 = fullPath1[0:-8] + "_" + args.lang1 + "-1_vs_" + args.lang3 + "-3_diff.png"
|
||||
diffRms2 = computeDiff(fullPath1, fullPath3, diffPath2)
|
||||
diffPath3 = fullPath1[0:-8] + "_" + args.lang2 + "-2_vs_" + args.lang3 + "-3_diff.png"
|
||||
diffRms3 = computeDiff(fullPath2, fullPath3, diffPath3)
|
||||
|
||||
if args.error >= 0:
|
||||
ok1 = (not diffPath1) or (not diffRms1) or (diffRms1 and diffRms1 <= args.error)
|
||||
ok2 = (not diffPath2) or (not diffRms2) or (diffRms2 and diffRms2 <= args.error)
|
||||
ok3 = (not diffPath3) or (not diffRms3) or (diffRms3 and diffRms3 <= args.error)
|
||||
if ok1 and ok2 and ok3:
|
||||
continue
|
||||
|
||||
if curPath != path1:
|
||||
if curPath != "":
|
||||
fh.write("</table>\n")
|
||||
fh.write("<p>" + os.path.normpath(path1) + ":</p>\n")
|
||||
fh.write("<table>\n")
|
||||
curPath = path1
|
||||
|
||||
def prependFileUri(filepath: str) -> str:
|
||||
if os.path.isabs(filepath):
|
||||
return 'file:///' + filepath
|
||||
else:
|
||||
return filepath
|
||||
|
||||
fh.write("<tr>\n")
|
||||
if fullPath1:
|
||||
fh.write("<td class='td_image'><img src='" + prependFileUri(fullPath1) + "' height='" + str(args.imageheight) + "' width='" + str(args.imagewidth) + "' loading='lazy' style='background-color:black;'/></td>\n")
|
||||
if fullPath2:
|
||||
fh.write("<td class='td_image'><img src='" + prependFileUri(fullPath2) + "' height='" + str(args.imageheight) + "' width='" + str(args.imagewidth) + "' loading='lazy' style='background-color:black;'/></td>\n")
|
||||
if fullPath3:
|
||||
fh.write("<td class='td_image'><img src='" + prependFileUri(fullPath3) + "' height='" + str(args.imageheight) + "' width='" + str(args.imagewidth) + "' loading='lazy' style='background-color:black;'/></td>\n")
|
||||
if diffPath1:
|
||||
fh.write("<td class='td_image'><img src='" + prependFileUri(diffPath1) + "' height='" + str(args.imageheight) + "' width='" + str(args.imagewidth) + "' loading='lazy' style='background-color:black;'/></td>\n")
|
||||
if diffPath2:
|
||||
fh.write("<td class='td_image'><img src='" + prependFileUri(diffPath2) + "' height='" + str(args.imageheight) + "' width='" + str(args.imagewidth) + "' loading='lazy' style='background-color:black;'/></td>\n")
|
||||
if diffPath3:
|
||||
fh.write("<td class='td_image'><img src='" + prependFileUri(diffPath3) + "' height='" + str(args.imageheight) + "' width='" + str(args.imagewidth) + "' loading='lazy' style='background-color:black;'/></td>\n")
|
||||
fh.write("</tr>\n")
|
||||
|
||||
fh.write("<tr>\n")
|
||||
if fullPath1:
|
||||
fh.write("<td align='center'>" + file1)
|
||||
if args.ENABLE_TIMESTAMPS and os.path.isfile(fullPath1):
|
||||
fh.write("<br>(" + str(datetime.datetime.fromtimestamp(os.path.getmtime(fullPath1))) + ")")
|
||||
fh.write("</td>\n")
|
||||
if fullPath2:
|
||||
fh.write("<td align='center'>" + file2)
|
||||
if args.ENABLE_TIMESTAMPS and os.path.isfile(fullPath2):
|
||||
fh.write("<br>(" + str(datetime.datetime.fromtimestamp(os.path.getmtime(fullPath2))) + ")")
|
||||
fh.write("</td>\n")
|
||||
if fullPath3:
|
||||
fh.write("<td align='center'>" + file3)
|
||||
if args.ENABLE_TIMESTAMPS and os.path.isfile(fullPath3):
|
||||
fh.write("<br>(" + str(datetime.datetime.fromtimestamp(os.path.getmtime(fullPath3))) + ")")
|
||||
fh.write("</td>\n")
|
||||
if diffPath1:
|
||||
rms = " (RMS " + "%.5f" % diffRms1 + ")" if diffRms1 else ""
|
||||
fh.write("<td align='center'>" + args.lang1.upper() + " vs. " + args.lang2.upper() + rms + "</td>\n")
|
||||
if diffPath2:
|
||||
rms = " (RMS " + "%.5f" % diffRms2 + ")" if diffRms2 else ""
|
||||
fh.write("<td align='center'>" + args.lang1.upper() + " vs. " + args.lang3.upper() + rms + "</td>\n")
|
||||
if diffPath3:
|
||||
rms = " (RMS " + "%.5f" % diffRms3 + ")" if diffRms3 else ""
|
||||
fh.write("<td align='center'>" + args.lang2.upper() + " vs. " + args.lang3.upper() + rms + "</td>\n")
|
||||
fh.write("</tr>\n")
|
||||
|
||||
fh.write("</table>\n")
|
||||
fh.write("</body>\n")
|
||||
fh.write("</html>\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv[1:])
|
||||
Reference in New Issue
Block a user