-
Notifications
You must be signed in to change notification settings - Fork 22
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
Handle locale for MaterialX inputs #1178
Changes from 12 commits
ed705c2
653ce00
95a0994
602597d
c9cb95c
99dede2
3218625
1038e47
120a99d
273ccfc
0d19f4c
790b8f7
2d3439e
a829e74
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?xml version="1.0"?> | ||
<materialx version="1.38" cms="ocio" colorspace="lin_rec709"> | ||
<nodedef name="ND_simple_srf_surface" node="simple_srf"> | ||
<input name="diffColor" type="color3" value="0.18,0.18, 0.18"/> | ||
<input name="specColor" type="color3" value="0.05, 0.05,0.05" /> | ||
<input name="normal" type="vector3" value="1,0.5,1" /> | ||
<input name="specRoughness" type="float" value="0.25" /> | ||
<input name="intensity" type="integer" value="-1" /> | ||
<output name="out" type="surfaceshader" /> | ||
</nodedef> | ||
<implementation name="IM_simple_srf" nodedef="ND_simple_srf_surface" /> | ||
</materialx> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
<?xml version="1.0"?> | ||
<materialx version="1.38" cms="ocio" colorspace="lin_rec709"> | ||
<nodedef name="ND_simple_srf_surface" node="simple_srf"> | ||
<input name="diffColor" type="color3" value="0.18,0.18, 0.18" uiname="びまん性"/> | ||
<input name="specColor" type="color3" value="0.05, 0.05,0.05" uiname="spéculaire"/> | ||
<input name="normal" type="vector3" value="1,0.5,1" /> | ||
<input name="specRoughness" type="float" value="0.25" uiname="表面粗さ"/> | ||
<input name="intensity" type="integer" value="-1" /> | ||
<output name="out" type="surfaceshader" /> | ||
</nodedef> | ||
<implementation name="IM_simple_srf" nodedef="ND_simple_srf_surface" /> | ||
</materialx> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/Scripts/" DESTINATION "${CMAKE_INSTALL_PREFIX}/python" MESSAGE_NEVER) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,263 @@ | ||
#!/usr/bin/env python | ||
""" | ||
Utility to generate json and hpp from MaterialX nodedef | ||
|
||
Given a node def e.g. ND_standard_surface_surfaceshader will | ||
generate a standard_surface.json and standard_surface.hpp | ||
The hpp/json can be used for simple reflection instead | ||
of parsing mtlx libraries | ||
""" | ||
|
||
import sys | ||
import os | ||
import argparse | ||
import json | ||
import hashlib | ||
import MaterialX as mx | ||
|
||
INPUTFILEHASH = 0 | ||
mx_stdTypes = { | ||
'color3': ['MaterialX::Color3', mx.Color3(1, 1, 1)], | ||
'color4': ['MaterialX::Color4', mx.Color4(1, 1, 1, 1)], | ||
'vector4': ['MaterialX::Vector4', mx.Vector4(1, 1, 1, 1)], | ||
'vector3': ['MaterialX::Vector3', mx.Vector3(1, 1, 1)], | ||
'vector2': ['MaterialX::Vector2', mx.Vector2(1, 1)], | ||
'matrix33': ['MaterialX::Matrix33', None], | ||
'matrix44': ['MaterialX::Matrix44', None], | ||
'integerarray': ['std::vector<int>', None], | ||
'floatarray': ['std::vector<float>', None], | ||
'color3array': ['std::vector<MaterialX::Color3>', None], | ||
'color4array': ['std::vector<MaterialX::Color4>', None], | ||
'vector2array': ['std::vector<MaterialX::Vector2>', None], | ||
'vector3array': ['std::vector<MaterialX::Vector3>', None], | ||
'vector4array': ['std::vector<MaterialX::Vector4>', None], | ||
'stringarray': ['std::vector<std::string>', None], | ||
'boolean': ['bool', False], | ||
'integer': ['int', 0], | ||
'file': ['std::string', ""], | ||
'filename': ['std::string', ""], | ||
'string': ['std::string', ""], | ||
'float': ['float', 0], | ||
|
||
#TODO: create custom structs (fixme) | ||
'lightshader': ['lightshader', None], | ||
'volumeshader': ['volumeshader', None], | ||
'displacementshader': ['displacementshader', None], | ||
'surfaceshader': ['surfaceshader', None], | ||
'BSDF': ['BSDF', None], | ||
'EDF': ['EDF', None], | ||
'VDF': ['VDF', None], | ||
} | ||
|
||
|
||
def _getType(mxType): | ||
return mx_stdTypes[mxType][0] | ||
|
||
|
||
def _getDefault(mxType): | ||
return mx_stdTypes[mxType][1] | ||
|
||
# Compute gitHash | ||
|
||
|
||
def _computeGitHash(mtlxfile): | ||
with open(mtlxfile, 'r') as afile: | ||
buf = afile.read().encode() | ||
hasher = hashlib.sha1() | ||
hasher.update(b"blob %u\0" % len(buf)) | ||
hasher.update(buf) | ||
return hasher.hexdigest() | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser( | ||
description="MaterialX nodedef to json/hpp convert.") | ||
parser.add_argument(dest="inputFilename", | ||
help="Filename of the input document.") | ||
parser.add_argument("--node", dest="nodedef", type=str, | ||
default="ND_standard_surface_surfaceshader", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe leave the default to be empty string instead ? |
||
help="Node to export") | ||
parser.add_argument("--stdlib", dest="stdlib", action="store_true", | ||
help="Import standard MaterialX libraries into the document.") | ||
opts = parser.parse_args() | ||
|
||
doc = mx.createDocument() | ||
try: | ||
mx.readFromXmlFile(doc, opts.inputFilename) | ||
# Git hash for tracking source document | ||
global INPUTFILEHASH | ||
INPUTFILEHASH = _computeGitHash(opts.inputFilename) | ||
|
||
except mx.ExceptionFileMissing as err: | ||
print(err) | ||
sys.exit(0) | ||
|
||
if opts.stdlib: | ||
stdlib = mx.createDocument() | ||
filePath = os.path.dirname(os.path.abspath(__file__)) | ||
searchPath = mx.FileSearchPath(os.path.join(filePath, '..', '..')) | ||
searchPath.append(os.path.dirname(opts.inputFilename)) | ||
libraryFolders = ["libraries"] | ||
mx.loadLibraries(libraryFolders, searchPath, stdlib) | ||
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) | ||
|
||
nodedefs = doc.getNodeDefs() | ||
nodedef = findNodeDef(nodedefs, opts.nodedef) | ||
|
||
print("Document Version: {}.{:02d}".format(*doc.getVersionIntegers())) | ||
if nodedef is None: | ||
print("Nodedef %s not found" % (opts.nodedef)) | ||
else: | ||
exportNodeDef(nodedef) | ||
print("%d NodeDef%s found.\nNode '%s' exported to %s(.json/.hpp)" | ||
% (len(nodedefs), pl(nodedefs), opts.nodedef, nodedef.getNodeString())) | ||
|
||
|
||
def findNodeDef(elemlist, nodedefname): | ||
if len(elemlist) == 0: | ||
return None | ||
for elem in elemlist: | ||
if elem.isA(mx.NodeDef) and elem.getName() == nodedefname: | ||
#print (elem) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove commented out code. |
||
return elem | ||
return None | ||
|
||
|
||
def exportNodeDef(elem): | ||
if elem.isA(mx.NodeDef): | ||
jsonfilename = elem.getNodeString()+'.json' | ||
hppfilename = elem.getNodeString()+'.hpp' | ||
export_json(elem, jsonfilename) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we catch exceptions here in case the files cannot be written. |
||
export_hpp(elem, hppfilename) | ||
|
||
|
||
def export_json(elem, filename): | ||
nodefInterface = {} | ||
nodefInterface["Nodedef"] = elem.getName() | ||
nodefInterface["SHA1"] = INPUTFILEHASH | ||
nodefInterface["MaterialX"] = mx.getVersionString() | ||
nodefInterface["name"] = elem.getNodeString() | ||
nodefInterface["members"] = asJsonArray(elem) | ||
with open(filename, 'w', encoding='utf-8') as f: | ||
json.dump(nodefInterface, f, indent=4) | ||
|
||
|
||
def asJsonArray(nodedef): | ||
memberlist = [] | ||
for inp in nodedef.getActiveInputs(): | ||
memberlist.append((_getType(inp.getType()), | ||
inp.getName(), | ||
str(inp.getValue()))) | ||
return memberlist | ||
|
||
|
||
def export_hpp(elem, filename): | ||
# write to file | ||
preamble = "/*\nGenerated using MaterialX nodedef \ | ||
\n{nodename}\nSHA1:{filehash}\nVersion:{version}\n*/\n"\ | ||
.format(nodename=elem, filehash=INPUTFILEHASH, version=mx.getVersionString()) | ||
variable_defs = "" | ||
for inp in elem.getActiveInputs(): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned in chat, would you want to export outputs as well ? |
||
#create decl | ||
decl = getVarDeclaration(inp) | ||
|
||
#emit variable decl | ||
if decl is None: | ||
variable_def = ' {typename} {inputname};\n' \ | ||
.format(typename=_getType(inp.getType()), | ||
inputname=inp.getName()) | ||
else: | ||
variable_def = ' {typename} {inputname} = {declaration};\n' \ | ||
.format(typename=_getType(inp.getType()), | ||
inputname=inp.getName(), | ||
declaration=decl) | ||
variable_defs += variable_def | ||
nodename_definition = ' std::string _nodename_ = "{nodename}";\n'.format( | ||
nodename=elem.getNodeString()) | ||
struct_definition = """struct {structname} {{\n{variabledefs}{nodeiddef}}};""" \ | ||
.format(structname=elem.getName(), | ||
variabledefs=variable_defs, | ||
nodeiddef=nodename_definition) | ||
|
||
with open(filename, 'w', encoding='utf-8') as f: | ||
f.write(preamble) | ||
f.write(struct_definition) | ||
f.close() | ||
|
||
|
||
def getVarDeclaration(inputVar): | ||
|
||
inputValue = inputVar.getValue() | ||
typeName = _getType(inputVar.getType()) | ||
if isinstance(inputValue, (mx.Color3, mx.Vector3)): | ||
val = '{typename}({v0}f, {v1}f, {v2}f)'.format(typename=typeName, | ||
v0=round( | ||
inputValue[0], 5), | ||
v1=round( | ||
inputValue[1], 5), | ||
v2=round(inputValue[2], 5)) | ||
return val | ||
if isinstance(inputValue, (mx.Color4, mx.Vector4)): | ||
val = '{typename}({v0}f, {v1}f, {v2}f, {v3}f)'.format(typename=typeName, | ||
v0=round( | ||
inputValue[0], 5), | ||
v1=round( | ||
inputValue[1], 5), | ||
v2=round( | ||
inputValue[2], 5), | ||
v3=round(inputValue[3], 5)) | ||
return val | ||
if isinstance(inputValue, float): | ||
val = '{0}f'.format(round(inputValue, 5)) | ||
return val | ||
if isinstance(inputValue, bool): | ||
val = '{0}'.format('true' if inputValue is True else 'false') | ||
return val | ||
if isinstance(inputValue, int): | ||
val = '{0}'.format(inputValue) | ||
return val | ||
|
||
# use input type if value is not defined and set default | ||
defaultValue = _getDefault(inputVar.getType()) | ||
if inputValue is None: | ||
if inputVar.getType() in ['vector2']: | ||
val = '{typename}({v0}f, {v1}f)'.format(typename=typeName, | ||
v0=defaultValue[0], | ||
v1=defaultValue[1]) | ||
return val | ||
if inputVar.getType() in ['vector3', 'color3']: | ||
val = '{typename}({v0}f, {v1}f, {v2}f)'.format(typename=typeName, | ||
v0=defaultValue[0], | ||
v1=defaultValue[1], | ||
v2=defaultValue[2]) | ||
return val | ||
if inputVar.getType() in ['vector4', 'color4']: | ||
val = '{typename}({v0}f, {v1}f, {v2}f, {v3}f)'.format(typename=typeName, | ||
v0=defaultValue[0], | ||
v1=defaultValue[1], | ||
v2=defaultValue[2], | ||
v3=defaultValue[3]) | ||
return val | ||
else: | ||
print("unhandled: " + typeName) | ||
return None | ||
|
||
|
||
def pl(elem): | ||
if len(elem) == 1: | ||
return "" | ||
else: | ||
return "s" | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick. "converter" instead of "convert" ?