Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Definition and NodeGraph Publishing Logic Updates #1303

Closed
Closed
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
dd2155f
Update nodedef creation logic to match specification, add in interfac…
kwokcb Mar 22, 2023
d1d1e16
Merge branch 'main' into nodedef_API_spec_updates
kwokcb Mar 22, 2023
4ea33b8
Fix remove removeInterfaceName() to copy back any value from the inte…
kwokcb Mar 23, 2023
dd03d17
Update test file.
kwokcb Mar 23, 2023
8823164
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb Mar 29, 2023
11bdd6b
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb Apr 5, 2023
1a4da27
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb Apr 13, 2023
b245be5
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb Apr 16, 2023
301f39c
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb Apr 17, 2023
0ffc0c9
Use definition creation in Node Editor.
kwokcb Apr 19, 2023
cda1e6a
Merge remote-tracking branch 'aswf/main' into nodedef_API_spec_updates
kwokcb Apr 20, 2023
96735ea
User tables for formatting property panel.
kwokcb Apr 20, 2023
f226abe
Cleanup table conversion.
kwokcb Apr 20, 2023
04b589a
Clleanup + add publish button to property editor.
kwokcb Apr 21, 2023
2d7dca6
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb Apr 28, 2023
665c643
Merge branch 'main' of https://github.com/AcademySoftwareFoundation/M…
kwokcb May 5, 2023
c83b102
Rework to have user dialog for input and create default "user_library…
kwokcb May 5, 2023
e7fa28a
Doc-string, and enum fo nodegroup.
kwokcb May 6, 2023
a3f3d16
Consolidate creation into addNoDefFromGraph. Fix up namespaces.
kwokcb May 6, 2023
7a28a7a
Fix warning.
kwokcb May 6, 2023
e26bdc7
Add createdefinition.py script. Works for pre 1.38.8 by applying the …
kwokcb May 8, 2023
ed4c56e
Hook up remaining attributes, comments, and custom location browsing.
kwokcb May 9, 2023
8694cd6
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb May 13, 2023
29eccb7
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb May 17, 2023
ffcf89f
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb May 22, 2023
02ac316
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb May 24, 2023
8a12cd0
Remove node positions
jstone-lucasfilm May 26, 2023
1c7ef0a
Remove node positions
jstone-lucasfilm May 26, 2023
9200855
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb May 30, 2023
6de05cd
Merge branch 'main' into nodedef_API_spec_updates
kwokcb Jun 15, 2023
db43c3c
Create new options class: NodeDefCreateOptions
kwokcb Jun 15, 2023
4ee2a18
Fix build.
kwokcb Jun 15, 2023
ba9c3ee
Use new options class for graph editor and createdefinitions.py script.
kwokcb Jun 16, 2023
3e810aa
Revert accidental check-in.
kwokcb Jun 16, 2023
98cd6e6
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb Jun 23, 2023
4873512
Merge branch 'AcademySoftwareFoundation:main' into nodedef_API_spec_u…
kwokcb Jul 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
205 changes: 205 additions & 0 deletions python/Scripts/createdefinition.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
#!/usr/bin/env python
'''
Create definitions based on the nodegraphs in the specified document. Definitions are
saved to new documents with either the definition and functional graph in a single file or
in separated into a file for definitions and a file for functional graphs.

It is assumed that all nodegraphs have the same category, nodegroup, version and namespace
as only one set of these input options can be provided. It is also assumed that each nodegraph
has a different output signature. If the same signature if out then no definition will be created
for that nodegraph.

e.g. python createdefinition.py
resources/Materials/Examples/StandardSurface/standard_surface_marble_solid.mtlx
--category mymarble
--version "2.0.9"
--nodegroup 'texture2d'
--separateDocuments 1
--comment "This is a new custom marble node."
'''

import sys, argparse, os
import MaterialX as mx

def main():
parser = argparse.ArgumentParser(description="Create definition and functional graph from compound graphs.")
parser.add_argument("--category", dest="category", default='', help="Category of the definition. If not specified the nodegraph name will be used")
parser.add_argument("--nodegroup", dest="nodegroup", default='', help="Node group of the definition. Defaults to an empty string")
parser.add_argument("--version", dest="version", default='', help="Version string of the definition. Defaults to empty string")
parser.add_argument("--defaultversion", dest="defaultversion", type=mx.stringToBoolean, default=False, help="Flag to indicate this is the default version. Defaults to false.")
parser.add_argument("--namespace", dest="namespace", default='', help="Namespace of the definition. Defaults to an empty string.")
parser.add_argument("--comment", dest="comment", default='', help="XML comment to embed before the definition or nodegraph")
parser.add_argument("--documentation", dest="documentation", default='', help="Definition documentation")
parser.add_argument('--separateDocuments', dest='separateDocuments', type=mx.stringToBoolean, default=False, help="Writ the definition and functional graph to separate files.")
parser.add_argument(dest="inputFilename", help="Filename of the input document.")
parser.add_argument('--outputPath', dest='outputPath', default="./", help='File path to output documents to. If not specified the files are output to the current directory.')

opts = parser.parse_args()

version_major, version_minor, version_patch = mx.getVersionIntegers()
use_1_38_8 = False
print(version_major, version_minor, version_patch)
if version_major >=1 and version_minor >= 38 and version_patch >= 8:
use_1_38_8 = True

doc = mx.createDocument()
try:
mx.readFromXmlFile(doc, opts.inputFilename)
except mx.ExceptionFileMissing as err:
print(err)
sys.exit(-1)

valid, msg = doc.validate()
if not valid:
print("Validation warnings for input document:")
print(msg)
sys.exit(-1)

nodeGraphs = doc.getNodeGraphs()
parameter_signatures = set()
for nodeGraph in nodeGraphs:
# Skip nodegraphs which are already functional graphs
if nodeGraph.getNodeDef():
print('Skip functional nodegraph %s' % nodeGraph.getNamePath())
continue

version = mx.createValidName(opts.version)
category = mx.createValidName(opts.category)
nodegroup = mx.createValidName(opts.nodegroup)
namespace = mx.createValidName(opts.namespace)
documentation = opts.documentation
defaultversion = opts.defaultversion

# User nodegraph name if no category provided as a a category name
# must be provided
if not category:
category = nodeGraph.getName()

# Build identifier for new nodedef and functional graph. Includes:
# - version
# - output types
# - namespace
#
identifier = category
if version:
identifier = identifier + '_' + version

outputs = nodeGraph.getOutputs()
parameter_signature = ''
for output in outputs:
outputType = output.getType()
parameter_signature = parameter_signature + '_' + outputType
if parameter_signature in parameter_signatures:
print('Duplicate parameter signature found on on nodegraph %s. Skipping definition createion.' % nodeGraph.getNamePath())
continue
else:
parameter_signatures.add(parameter_signature)
identifier = identifier + parameter_signature

# Prefix with "ND_" or "NG_" for definition and functional graph names
nodedefName = 'ND_' + identifier
nodegraphName = 'NG_' + identifier

# Create the definition
definition = None
if use_1_38_8:
definition = doc.addNodeDefFromGraph(nodeGraph, nodedefName,
category, opts.version, defaultversion, nodegroup, nodegraphName,
documentation, namespace)
else:
definition = doc.addNodeDefFromGraph(nodeGraph, nodedefName,
category, opts.version, defaultversion, nodegroup, nodegraphName)
funcgraph = doc.getNodeGraph(nodegraphName)

if not definition or not funcgraph:
print('Failed to create definition for nodegraph %s' % nodeGraph.getNamePath())
continue

if not use_1_38_8:
print('Patch 1.38.8 definition creation...')
definition.setDocString(documentation)
definition.setNamespace(namespace)

funcgraph.setNamespace(namespace)
for graphChild in funcgraph.getChildren():
graphChild.removeAttribute('xpos')
graphChild.removeAttribute('ypos')

filterAttributes = { 'nodegraph', 'nodename', 'channels', 'interfacename', 'xpos', 'ypos' }

# Transfer input interface from the graph to the nodedef
for input in funcgraph.getInputs():
nodeDefInput = definition.addInput(input.getName(), input.getType())
if nodeDefInput:
nodeDefInput.copyContentFrom(input)
for filterAttribute in filterAttributes:
nodeDefInput.removeAttribute(filterAttribute);
nodeDefInput.setSourceUri('')
input.setInterfaceName(nodeDefInput.getName())

# Remove interface from the nodegraph
for input in funcgraph.getInputs():
funcgraph.removeInput(input.getName())

# Copy the output interface from the graph to the nodedef
for output in nodeGraph.getOutputs():
nodeDefOutput = definition.getOutput(output.getName())
if nodeDefOutput:
definition.removeOutput(output.getName())
definition.addOutput(output.getName(), output.getType())
if nodeDefOutput:
nodeDefOutput.copyContentFrom(output)
for filterAttribute in filterAttributes:
nodeDefOutput.removeAttribute(filterAttribute)
nodeDefOutput.setSourceUri('')

# Generate an XML comment string from the given comment.
commentString = ''
if opts.comment:
commentString = '\n\tNode: <' + category + '> '
commentString = commentString + '\n\t' + opts.comment + '\n '

# Separate out the new definition and functional graph and write
# to either 1 or 2 files depending on if the "separateDocuments" flag
# is set.
#
defDoc = mx.createDocument()
if commentString:
comment = defDoc.addChildOfCategory('comment')
comment.setDocString(commentString);
# Note: that addNodeDef() will automatically add an output if a type is specified
# so leave this empty. The outputs will be copied when the content is copied over.
newDef = defDoc.addNodeDef(definition.getName(), '', definition.getCategory())
newDef.copyContentFrom(definition)

graphDoc = defDoc
if opts.separateDocuments:
graphDoc = mx.createDocument()
if commentString:
comment = graphDoc.addChildOfCategory('comment')
comment.setDocString(commentString);
newGraph = graphDoc.addNodeGraph(funcgraph.getName())
newGraph.copyContentFrom(funcgraph);

# Write to file
outputFilePath = mx.FilePath(opts.outputPath)
pathExists = os.path.exists(outputFilePath.asString())
if not pathExists:
print('Created folder: ', outputFilePath.asString())
os.makedirs(outputFilePath.asString())

fileLocation = identifier + '.mtlx'
if opts.separateDocuments:
fileLocation = 'ND_' + fileLocation
fileLocation = outputFilePath / mx.FilePath(fileLocation)
print('Write definition to file:', fileLocation.asString())
mx.writeToXmlFile(defDoc, fileLocation);

if opts.separateDocuments:
fileLocation = 'NG_' + identifier + '.mtlx'
fileLocation = outputFilePath / mx.FilePath(fileLocation)
print('Write functional graph to file:', fileLocation.asString())
mx.writeToXmlFile(graphDoc, fileLocation);

if __name__ == '__main__':
main()
Original file line number Diff line number Diff line change
@@ -1,76 +1,29 @@
<?xml version="1.0"?>
jstone-lucasfilm marked this conversation as resolved.
Show resolved Hide resolved
<materialx version="1.38">
<nodegraph name="test_colorcorrect">
<range name="AlphaClampAndGamma" type="float">
<input name="in" type="float" nodename="AlphaOffset" />
<input name="gamma" type="float" value="3" />
<input name="doclamp" type="boolean" value="true" />
</range>
<multiply name="AlphaGain" type="float">
<input name="in1" type="float" nodename="extractAlphaForGain" />
<input name="in1" type="float" nodename="inputAlpha" />
<input name="in2" type="float" value="0.8" />
</multiply>
<add name="AlphaOffset" type="float">
<input name="in1" type="float" nodename="AlphaGain" />
<input name="in2" type="float" value="1" />
</add>
<range name="ColorClampAndGamma" type="color3">
<input name="in" type="color3" nodename="ColorOffset" />
<input name="gamma" type="color3" value="2, 1, 1" />
</range>
<multiply name="ColorGain" type="color3">
<input name="in1" type="color3" nodename="HSV_adjust" />
<input name="in1" type="color3" nodename="inputColor" />
<input name="in2" type="color3" value="0.9, 0.9, 0.9" />
</multiply>
<add name="ColorOffset" type="color3">
<input name="in1" type="color3" nodename="ColorGain" />
<input name="in2" type="color3" value="0.379147, 0.0341412, 0.0341412" />
</add>
<combine2 name="CombineColorAlpha" type="color4">
<input name="in1" type="color3" nodename="ColorClampAndGamma" />
<input name="in2" type="float" nodename="AlphaClampAndGamma" />
</combine2>
<hsvadjust name="HSV_adjust" type="color3">
<input name="in" type="color3" nodename="extractColorForHsv" />
</hsvadjust>
<premult name="premultiplyColor" type="color4">
<input name="in" type="color4" nodename="CombineColorAlpha" />
</premult>
<ifequal name="if_premultiply_condition" type="color4">
<input name="value2" type="boolean" value="true" />
<input name="in1" type="color4" nodename="premultiplyColor" />
<input name="in2" type="color4" nodename="CombineColorAlpha" />
</ifequal>
<unpremult name="unpremultiply" type="color4">
<input name="in" type="color4" nodename="combineInput" />
</unpremult>
<ifequal name="if_unpremultiply_condition" type="color4">
<input name="value2" type="boolean" value="true" />
<input name="in1" type="color4" nodename="unpremultiply" />
<input name="in2" type="color4" nodename="combineInput" />
</ifequal>
<swizzle name="outputColor" type="color3">
<input name="in" type="color4" nodename="if_premultiply_condition" />
<input name="channels" type="string" value="rgb" />
</swizzle>
<swizzle name="outputAlpha" type="float">
<input name="in" type="color4" nodename="if_premultiply_condition" />
<input name="channels" type="string" value="a" />
</swizzle>
<swizzle name="extractColorForHsv" type="color3">
<input name="in" type="color4" nodename="if_unpremultiply_condition" />
<input name="channels" type="string" value="rgb" />
</swizzle>
<swizzle name="extractAlphaForGain" type="float">
<input name="in" type="color4" nodename="if_unpremultiply_condition" />
</swizzle>
<constant name="inputColor" type="color3">
<input name="value" type="color3" value="0.5, 0.5, 0.5" />
</constant>
<constant name="inputAlpha" type="float">
<input name="value" type="float" value="1" />
</constant>
<combine2 name="combineInput" type="color4">
<input name="in1" type="color3" nodename="inputColor" />
<input name="in2" type="float" nodename="inputAlpha" />
</combine2>
<output name="out" type="color3" nodename="outputColor" />
<output name="out1" type="float" nodename="outputAlpha" />
<output name="out" type="color3" nodename="ColorOffset" />
<output name="out1" type="float" nodename="AlphaOffset" />
</nodegraph>
</materialx>
65 changes: 61 additions & 4 deletions source/MaterialXCore/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ void Document::initialize()
}

NodeDefPtr Document::addNodeDefFromGraph(const NodeGraphPtr nodeGraph, const string& nodeDefName, const string& node,
const string& version, bool isDefaultVersion, const string& group, const string& newGraphName)
const string& version, bool isDefaultVersion, const string& group, const string& newGraphName,
const string& docString, const string& nameSpace)
{
if (getNodeDef(nodeDefName))
{
Expand All @@ -187,11 +188,21 @@ NodeDefPtr Document::addNodeDefFromGraph(const NodeGraphPtr nodeGraph, const str
}
graph = addNodeGraph(newGraphName);
graph->copyContentFrom(nodeGraph);

for (auto graphChild : graph->getChildren())
{
graphChild->removeAttribute("xpos");
graphChild->removeAttribute("ypos");
}
}
graph->setNodeDefString(nodeDefName);

NodeDefPtr nodeDef = addChild<NodeDef>(nodeDefName);
NodeDefPtr nodeDef = addChild<NodeDef>(nodeDefName);
nodeDef->setNodeString(node);
if (!docString.empty())
{
nodeDef->setDocString(docString);
}
if (!group.empty())
{
nodeDef->setNodeGroup(group);
Expand All @@ -208,9 +219,55 @@ NodeDefPtr Document::addNodeDefFromGraph(const NodeGraphPtr nodeGraph, const str
}
}

for (auto output : graph->getOutputs())
if (!nameSpace.empty())
{
nodeDef->addOutput(output->getName(), output->getType());
graph->setNamespace(nameSpace);
nodeDef->setNamespace(nameSpace);
}

// Expose any existing interface.
// Any connection attributes on the existing interface should be removed from the definition.
// as well as any source URI

// Attributes which should not be copied over
jstone-lucasfilm marked this conversation as resolved.
Show resolved Hide resolved
StringSet filterAttributes = { PortElement::NODE_GRAPH_ATTRIBUTE, PortElement::NODE_NAME_ATTRIBUTE,
PortElement::CHANNELS_ATTRIBUTE, PortElement::INTERFACE_NAME_ATTRIBUTE,
"xpos", "ypos" };

// Transfer input interface from the graph to the nodedef
for (InputPtr input : graph->getInputs())
{
InputPtr nodeDefInput = nodeDef->addInput(input->getName(), input->getType());
if (nodeDefInput)
{
nodeDefInput->copyContentFrom(input);
for (const string& filterAttribute : filterAttributes )
{
nodeDefInput->removeAttribute(filterAttribute);
}
nodeDefInput->setSourceUri(EMPTY_STRING);
input->setInterfaceName(nodeDefInput->getName());
}
}
// Remove interface from the nodegraph
for (InputPtr input : graph->getInputs())
{
graph->removeInput(input->getName());
}

// Copy the output interface from the graph to the nodedef
for (OutputPtr output : graph->getOutputs())
{
OutputPtr nodeDefOutput = nodeDef->addOutput(output->getName(), output->getType());
if (nodeDefOutput)
{
nodeDefOutput->copyContentFrom(output);
for (const string& filterAttribute : filterAttributes)
{
nodeDefOutput->removeAttribute(filterAttribute);
}
nodeDefOutput->setSourceUri(EMPTY_STRING);
}
}

return nodeDef;
Expand Down
7 changes: 5 additions & 2 deletions source/MaterialXCore/Document.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,12 +339,15 @@ class MX_CORE_API Document : public GraphElement
/// @param node Node type for the new declaration
/// @param version Version for the new declaration
/// @param isDefaultVersion If a version is specified is thie definition the default version
/// @param nodeGroup Optional node group for the new declaration. The Default value is an emptry string.
/// @param newGraphName Make a copy of this NodeGraph with the given name if a non-empty name is provided. Otherwise
/// modify the existing NodeGraph. Default value is an empty string.
/// @param nodeGroup Optional node group for the new declaration. The Default value is an emptry string.
/// @param docString Optional documentation string. Default is an empty string.
/// @param namesSpace Optional namespace for the Nodedef and NodeGraph. Default is an empty string.
/// @return New declaration if successful.
NodeDefPtr addNodeDefFromGraph(const NodeGraphPtr nodeGraph, const string& nodeDefName, const string& node, const string& version,
jstone-lucasfilm marked this conversation as resolved.
Show resolved Hide resolved
bool isDefaultVersion, const string& nodeGroup, const string& newGraphName);
bool isDefaultVersion, const string& nodeGroup, const string& newGraphName,
const string& docString = EMPTY_STRING, const string& nameSpace = EMPTY_STRING);

/// Return the NodeDef, if any, with the given name.
NodeDefPtr getNodeDef(const string& name) const
Expand Down
Loading