Command to parse PhysicallyBased materials and create MaterialX materials.
26def physicallBasedMaterialXCmd():
27 '''
28 Command to parse PhysicallyBased materials and create MaterialX materials
29 '''
30 logger = logging.getLogger('PB_CMD')
31 logging.basicConfig(level=logging.INFO)
32
33
34 parser = argparse.ArgumentParser(description='Convert Physically Based Materials to MaterialX')
35 parser.add_argument('-m', '--shadingModel', type=str, default='', help='Shading models to use for conversion. '
36 ' If not specified then all will be used. '
37 ' Options: standard_surface, gltf_pbr, open_pbr_surface')
38 parser.add_argument('-o', '--outputDir', type=str, default='',
39 help='Output directory for MaterialX files. Default location is PhysicallyBasedMaterialX')
40 parser.add_argument('-j', '--writeJSON', type=bool, default=True,
41 help='Write materials JSON file. Default is True')
42 parser.add_argument('-s', '--separateFiles', type=bool, default=False,
43 help='Convert individual MaterialX files per material. Default is false')
44 parser.add_argument('-l', '--loadFromFile', type=str, default='', help='Load materials a specified file')
45 parser.add_argument('-wr', '--writeRemapping', type=bool, default=False, help='Write remapping from PhysicallyBased to MaterialX. Default is False')
46 parser.add_argument('-rr', '--readRemapping', type=str, default='', help='Read remapping from PhysicallyBased to MaterialX. Default is empty')
47 parser.add_argument('-nd', '--createNodeDef', type=bool, default=False, help='Create NodeDef for Physically Based Material inputs. Default is False')
48 opts = parser.parse_args()
49
50 outputDir = 'PhysicallyBasedMaterialX'
51 if opts.outputDir:
52 if not os.path.exists(opts.outputDir):
53 logger.info(f'Error: Output directory does not exist: {opts.outputDir}')
54
55 try:
56 os.makedirs(opts.outputDir)
57 logger.info(f'> Created output directory: {opts.outputDir}')
58 outputDir = opts.outputDir
59 except Exception as e:
60 logger.info(f'> Error: Could not create output directory: {opts.outputDir}')
61 sys.exit(1)
62 else:
63 outputDir = opts.outputDir
64
65 shadingModels = []
66 if opts.shadingModel:
67 shadingModels = opts.shadingModel.split(',')
68 shadingModePrefixMap = { 'standard_surface': 'SS', 'gltf_pbr': 'GLTF', 'open_pbr_surface': 'OPBR' }
69 shadingModelPrefixes = []
70 if len(shadingModels) == 0:
71 shadingModels = ['standard_surface', 'gltf_pbr', 'open_pbr_surface']
72 shadingModelPrefixes = ['SS', 'GLTF', 'OPBR']
73 else:
74 for shadingModel in shadingModels:
75 shadingModelPrefixes.append(shadingModePrefixMap[shadingModel])
76
77 writeJSON = opts.writeJSON
78 separateFiles = opts.separateFiles
79
80 material_file = opts.loadFromFile
81 if material_file:
82 if not os.path.exists(opts.loadFromFile):
83 logger.info(f'> Error: File does not exist: {material_file}')
84 sys.exit(1)
85 logger.info(f'> Load materials from file: {material_file}')
86
87
88
89 loader = pbmx.PhysicallyBasedMaterialLoader(mx, None, material_file)
90
91 readRemapping = opts.readRemapping
92 if readRemapping:
93 if not os.path.exists(readRemapping):
94 logger.info(f'> Error: Remapping file does not exist: {readRemapping}')
95 logger.info(f'> Read remapping file: {readRemapping}')
96 loader.readRemappingFile(readRemapping)
97 else:
98 writeRemapping = opts.writeRemapping
99 if writeRemapping:
100 outputFile = os.path.join(outputDir, 'PhysicallyBasedToMtlxMappings.json')
101 logger.info(f'> Write remapping file: {outputFile}')
102 loader.writeRemappingFile(outputFile)
103
104 jsonMat = loader.getJSON()
105 if jsonMat:
106
107
108 os.makedirs(outputDir, exist_ok=True)
109
110 create_nodedef = opts.createNodeDef
111 if create_nodedef:
112
113
114
115 logger.info('> Write definition for PhysicallyBased materials')
116 definitions_doc = loader.get_physlib()
117 if definitions_doc:
118 nodedef_file_name = os.path.join(outputDir, 'physbased_pbr.mtlx')
119 mx.writeToXmlFile(definitions_doc, nodedef_file_name)
120 logger.info(f'> Write definition file: {nodedef_file_name}')
121
122
123
124 doc_mat = loader.get_physlib_materials()
125 if doc_mat:
126 status, error = doc_mat.validate()
127 if not status:
128 logger.error('> Error validating definition materials document:')
129 logger.error(error)
130 else:
131 logger.info('> Definition materials document passed validation.')
132
133 nodedef_mat_file_name = os.path.join(outputDir, 'physbased_pbr_materials.mtlx')
134 mx.writeToXmlFile(doc_mat, mx.FilePath(nodedef_mat_file_name))
135 logger.info(f'> Write materials file: {nodedef_mat_file_name}')
136
137
138
139 translators_doc = loader.get_translators()
140 translators = translators_doc.getNodeDefs()
141 print(f'Found translator definitions: {len(translators)}')
142
143 if translators_doc and translators:
144 output_file_name = 'physbased_pbr_translators.mtlx'
145 output_path = os.path.join(outputDir, output_file_name)
146 logger.info('> Write translator file:' + output_path)
147 mx.writeToXmlFile(translators_doc, mx.FilePath(output_path))
148
149
150 stdlib = loader.get_definitions()
151
152
153 if not separateFiles:
154 translated_doc = loader.get_physlib_materials()
155
156
157
158
159
160 for shadingModel, prefix in zip(shadingModels, shadingModelPrefixes):
161
162 for node in translated_doc.getNodes():
163 if node.getCategory() == loader.get_physlib_category():
164 trans_result = loader.translate_node(translated_doc, loader.get_physlib_category(), shadingModel, node)
165
166 logger.info(f'> Generate MaterialX using nodedefs for shading model: {shadingModel}')
167 fileName = os.path.join(outputDir, f'PhysicallyBasedMaterialX_translated_{prefix}.mtlx')
168 loader.writeMaterialXToFile(fileName, translated_doc)
169 logger.info(f'> Write: {fileName}')
170
171
172 else:
173 matDir = os.path.join(outputDir, 'Sep')
174 os.makedirs(matDir, exist_ok=True)
175 for shadingModel, prefix in zip(shadingModels, shadingModelPrefixes):
176 converted = []
177 for mat in loader.getJSONMaterialNames():
178 materialFilter = [mat]
179
180
181 matdoc = loader.create_definition_materials(None, materialFilter)
182 if matdoc is not None:
183
184
185 mat_name = mx.createValidName(mat)
186 node = matdoc.getNode(mat_name)
187 if node:
188 trans_result = loader.translate_node(matdoc, loader.get_physlib_category(), shadingModel, node)
189 if not trans_result:
190 logger.warning(f'Failed to translate node: {mat_name} for shading model: {shadingModel}')
191 else:
192 converted.append(trans_result['targetNode'].getName())
193 valid, errors = loader.validateMaterialXDocument(matdoc)
194 if valid:
195
196 fileName = os.path.join(matDir, f'PB_{prefix}_{mat}.mtlx')
197 loader.writeMaterialXToFile(fileName, matdoc)
198
199 logger.info(f'> Converted {len(converted)} materials for shading model: {shadingModel}')
200
201 if writeJSON:
202 logger.info(f'> Write PB material file: {outputDir}/PhysicallyBasedMaterial.json')
203 loader.writeJSONToFile(os.path.join(outputDir, 'PhysicallyBasedMaterial.json'))
204
205 if create_nodedef:
206 return
207
208
209 if not separateFiles:
210 for shadingModel, prefix in zip(shadingModels, shadingModelPrefixes):
211 logger.info(f'> Generate MaterialX for shading model: {shadingModel}')
212 matdoc = loader.convertToMaterialX([], shadingModel, {}, prefix)
213 valid, errors = loader.validateMaterialXDocument(matdoc)
214 if valid:
215 fileName = os.path.join(outputDir, f'PhysicallyBasedMaterialX_{prefix}.mtlx')
216 loader.writeMaterialXToFile(fileName)
217 logger.info(f'> Write: {fileName}')
218
219 else:
220 for shadingModel, prefix in zip(shadingModels, shadingModelPrefixes):
221 logger.info(f'> Generate MaterialX for shading model: {shadingModel}')
222 for mat in loader.getJSONMaterialNames():
223 materialFilter = [mat]
224 matdoc = loader.convertToMaterialX(materialFilter, shadingModel, {}, prefix)
225 if matdoc is not None:
226 valid, errors = loader.validateMaterialXDocument(matdoc)
227 if valid:
228 fileName = os.path.join(outputDir, f'PB_{prefix}_{mat}.mtlx')
229 loader.writeMaterialXToFile(fileName)
230 logger.info(f'> Write: {fileName}')
231
232 else:
233 logger.info('Could not retrieve PhysicallyBased Materials')
234