MaterialXMaterials 0.0.1
Utilities for retrieving materials from remote servers
Loading...
Searching...
No Matches
JsPhysicallyBasedMaterialLoader Class Reference

Javascript class for querying materials from the Physically Based database and creating MaterialX materials. More...

Public Member Functions

 constructor (mtlx_module=null, mtlx_stdlib=null)
 Constructor for the PhysicallyBasedMaterialLoader.
 
 getJSON ()
 Get the Physically Based Materials as JSON.
 
 getJSONMaterialNames ()
 Get list of the Physically Based Material names.
 
 getMaterialXDocument ()
 Get the MaterialX document.
 
 validateDocument ()
 Validate the MaterialX document.
 
 getInputRemapping (shadingModel)
 Get the remapping keys for a given shading model.
 
 initializeInputRemapping ()
 Initialize the input remapping for different shading models.
 
 loadMaterialX ()
 Load the MaterialX module.
 
async getPhysicallyBasedMaterials ()
 Get the Physically Based Materials from the API.
 
 loadStandardLibraries ()
 Load the MaterialX standard libraries.
 
 skipLibraryElement (element)
 Predicate to skip library elements.
 
 getMaterialXString ()
 Get the MaterialX document as a string.
 
 addComment (doc, commentString)
 Add a comment to the MaterialX document.
 
 convertToMaterialX (shaderCategory, addAllInputs=false, materialNames=[], remapKeys={}, shaderPreFix='')
 Convert the Physically Based Materials to MaterialX.
 

Public Attributes

 url = ''
 URL to fetch the Physically Based Materials.
 
 headers = {}
 Headers for the fetch operation.
 
 materials = null
 List of Physically Based Materials.
 
 materialNames = []
 List of Physically Based Material names.
 
 mxMaterialNames = []
 List of MaterialX Material names.
 
 mx = null
 MaterialX module.
 
 doc = null
 Working MaterialX document.
 
 stdlib = null
 MaterialX standard libraries.
 
 remapMap = {}
 Remap keys for input values for different shading models.
 

Detailed Description

Javascript class for querying materials from the Physically Based database and creating MaterialX materials.

Definition at line 8 of file JsMaterialXPhysicallyBased.js.

Member Function Documentation

◆ addComment()

JsPhysicallyBasedMaterialLoader::addComment ( doc,
commentString )

Add a comment to the MaterialX document.

Parameters
doc- MaterialX document
commentString- Comment string to add

Definition at line 298 of file JsMaterialXPhysicallyBased.js.

299 {
300 let comment = doc.addChildOfCategory('comment')
301 comment.setDocString(commentString)
302 }

◆ constructor()

JsPhysicallyBasedMaterialLoader::constructor ( mtlx_module = null,
mtlx_stdlib = null )

Constructor for the PhysicallyBasedMaterialLoader.

Returns
{void}

Definition at line 60 of file JsMaterialXPhysicallyBased.js.

61 {
62 this.url = 'https://api.physicallybased.info/materials';
63 this.headers = { 'Accept': 'application/json' };
64
65 this.materials = null;
66 this.materialNames = [];
67
68 this.mxMaterialNames = [];
69 this.mx = null;
70 if (mtlx_module) {
71 this.mx = mtlx_module;
72 }
73 this.stdlib = null;
74 if (mtlx_stdlib) {
75 this.stdlib = mtlx_stdlib;
76 }
77 this.doc = null;
78
80 }
initializeInputRemapping()
Initialize the input remapping for different shading models.
materials
List of Physically Based Materials.
materialNames
List of Physically Based Material names.
mxMaterialNames
List of MaterialX Material names.
headers
Headers for the fetch operation.
url
URL to fetch the Physically Based Materials.

◆ convertToMaterialX()

JsPhysicallyBasedMaterialLoader::convertToMaterialX ( shaderCategory,
addAllInputs = false,
materialNames = [],
remapKeys = {},
shaderPreFix = '' )

Convert the Physically Based Materials to MaterialX.

Parameters
shaderCategory- MaterialX shader category
addAllInputs- Add all inputs from node definitions
materialNames- List of material names to convert. If empty all materials are converted
remapKeys- Remap keys to MaterialX shader inputs. If not specified the default remap keys are used if any.
shaderPreFix- Prefix for the shader name. Default is empty
Returns
True if the conversion is successful. False otherwise

Definition at line 314 of file JsMaterialXPhysicallyBased.js.

314 {}, shaderPreFix = '')
315 {
316 if (!this.mx) {
317 console.error('MaterialX module is not loaded');
318 return false;
319 }
320
321 if (!this.materials) {
322 console.warn('No Physically Based Materials to convert');
323 return false;
324 }
325
326 if (remapKeys.length == 0) {
327 remapKeys = this.getInputRemapping(shaderCategory);
328 }
329
330 // Create a dummy doc with the surface shader with all inputs
331 // as reference
332 let refDoc = this.mx.createDocument();
333 refDoc.importLibrary(this.stdlib);
334 const refNode = refDoc.addNode(shaderCategory, 'refShader', this.mx.SURFACE_SHADER_TYPE_STRING);
335 //refNode.addInputsFromNodeDef() -- This is missing from the JS API.
336 this.doc = this.mx.createDocument();
337
338 // Add header comments
339 this.addComment(this.doc, 'Physically Based Materials from https://api.physicallybased.info ');
340 this.addComment(this.doc, ' Processed via API and converted to MaterialX ');
341 this.addComment(this.doc, ' Target Shading Model: ' + shaderCategory);
342 this.addComment(this.doc, ' Utility Author: Bernard Kwok. kwokcb@gmail.com ');
343
344 // Add properties to the material
345 for (let i = 0; i < this.materials.length; i++) {
346 const mat = this.materials[i];
347 let matName = mat['name'];
348
349 // Filter by material name(s)
350 if (materialNames.length > 0 && !materialNames.includes(matName)) {
351 // Skip material
352 console.log('Skipping material:', matName);
353 continue;
354 }
355
356
357 if (shaderPreFix.length > 0) {
358 matName = shaderPreFix + '_' + matName;
359 }
360
361 const shaderName = this.doc.createValidChildName('SPB_' + matName);
362 this.addComment(this.doc, ' Generated shader: ' + shaderName + ' ');
363 const shaderNode = this.doc.addNode(shaderCategory, shaderName, this.mx.SURFACE_SHADER_TYPE_STRING);
364 let docString = mat['description'];
365 const refString = mat['reference'];
366 if (refString.length > 0) {
367 if (docString.length > 0) {
368 docString += '. ';
369 }
370 docString += 'Reference: ' + refString[0];
371 }
372 if (docString.length > 0) {
373 shaderNode.setDocString(docString);
374 }
375
376 // Create a new material
377 const materialName = this.doc.createValidChildName('MPB_' + matName);
378 this.addComment(this.doc, ' Generated material: ' + materialName + ' ');
379 const materialNode = this.doc.addNode(this.mx.SURFACE_MATERIAL_NODE_STRING, materialName, this.mx.MATERIAL_TYPE_STRING);
380 const shaderInput = materialNode.addInput(this.mx.SURFACE_SHADER_TYPE_STRING, this.mx.SURFACE_SHADER_TYPE_STRING);
381 shaderInput.setAttribute(MTLX_NODE_NAME_ATTRIBUTE, shaderNode.getName());
382
383 // Warning this is a bit bespoke for remapping keys
384 // to Autodesk Standard Surface shader inputs
385 const skipKeys = ['name', "density", "category", "description", "sources", "tags", "reference"];
386
387 let metallness = null;
388 let roughness = null;
389 let transmission_color = null;
390 let transmission = null;
391 Object.entries(mat).forEach(([key, value]) => {
392
393 if (!skipKeys.includes(key)) {
394
395 if (key == 'metalness') {
396 metallness = value;
397 //console.log('Metalness:', metallness);
398 }
399 if (key == 'roughness') {
400 roughness = value;
401 //console.log('Roughness:', roughness);
402 }
403 if (key == 'transmission') {
404 transmission = value;
405 //console.log('Transmission:', transmission);
406 }
407 if (key == 'color') {
408 transmission_color = value;
409 //console.log('Color:', color);
410 }
411
412 if (remapKeys[key]) {
413 key = remapKeys[key];
414 }
415
416 let refInput = refNode.getInput(key);
417 if (!refInput)
418 refInput = refNode.addInputFromNodeDef(key);
419 if (refInput) {
420 const input = shaderNode.addInput(key);
421 input.copyContentFrom(refInput);
422 if (input) {
423 // Convert number vector to string
424 if (Array.isArray(value)) {
425 value = value.join(',');
426 }
427 // Convert number to string
428 else if (typeof value === 'number') {
429 value = value.toString();
430 }
431 // Note: This API has side-effects as the
432 // type is set to "string" when the value is set. Thus
433 // we must explicitly set the type here.
434 input.setValueString(value, refInput.getType());
435 }
436 }
437 else {
438 //console.log('>>> Cannot create input:', key)
439 }
440 }
441 });
442
443 if (transmission !== null && metallness !== null && roughness !== null && transmission_color !== null)
444 {
445 if (metallness == 0 && roughness == 0)
446 {
447 if (remapKeys['transmission_color']) {
448 let inputName = remapKeys['transmission_color'];
449 let input = shaderNode.addInput(inputName);
450 if (input) {
451 let value = transmission_color.join(',');
452 console.log(`Add "${inputName}": "${value}"`);
453 input.setValueString(value, 'color3');
454 }
455 }
456 }
457 };
458 }
459 return true;
460 }
getInputRemapping(shadingModel)
Get the remapping keys for a given shading model.
addComment(doc, commentString)
Add a comment to the MaterialX document.

◆ getInputRemapping()

JsPhysicallyBasedMaterialLoader::getInputRemapping ( shadingModel)

Get the remapping keys for a given shading model.

Parameters
shadingModel- Shading model to get the remapping keys
Returns
Remapping keys for the shading model. Empty object if not found

Definition at line 130 of file JsMaterialXPhysicallyBased.js.

131 {
132 if (shadingModel in this.remapMap) {
133 return this.remapMap[shadingModel];
134 }
135 return {};
136 }
remapMap
Remap keys for input values for different shading models.

◆ getJSON()

JsPhysicallyBasedMaterialLoader::getJSON ( )

Get the Physically Based Materials as JSON.

Returns
{object[]} - List of Physically Based Materials

Definition at line 86 of file JsMaterialXPhysicallyBased.js.

87 {
88 return this.materials
89 }

◆ getJSONMaterialNames()

JsPhysicallyBasedMaterialLoader::getJSONMaterialNames ( )

Get list of the Physically Based Material names.

Definition at line 94 of file JsMaterialXPhysicallyBased.js.

95 {
96 return this.materialNames
97 }

◆ getMaterialXDocument()

JsPhysicallyBasedMaterialLoader::getMaterialXDocument ( )

Get the MaterialX document.

Definition at line 102 of file JsMaterialXPhysicallyBased.js.

103 {
104 return this.doc;
105 }

◆ getMaterialXString()

JsPhysicallyBasedMaterialLoader::getMaterialXString ( )

Get the MaterialX document as a string.

Returns
{string} - MaterialX document as a string. Empty string if no document

Definition at line 275 of file JsMaterialXPhysicallyBased.js.

276 {
277 if (!this.doc) {
278 console.error('No MaterialX document to convert');
279 return '';
280 }
281
282 // Create write options
283 const writeOptions = new this.mx.XmlWriteOptions();
284 writeOptions.writeXIncludeEnable = false;
285 //writeOptions.writeXIncludes = false;
286 writeOptions.elementPredicate = this.skipLibraryElement;
287
288 // Convert the MaterialX document to a string
289 const mtlx = this.mx.writeToXmlString(this.doc, writeOptions);
290 return mtlx;
291 }
skipLibraryElement(element)
Predicate to skip library elements.

◆ getPhysicallyBasedMaterials()

async JsPhysicallyBasedMaterialLoader::getPhysicallyBasedMaterials ( )

Get the Physically Based Materials from the API.

Returns
{object[]} - List of Physically Based Materials in JSON format

Definition at line 208 of file JsMaterialXPhysicallyBased.js.

209 {
210 try {
211 this.materials = null
212 this.materialNames = [];
213
214 const response = await fetch(this.url, {
215 method: 'GET',
216 headers: this.headers
217 });
218
219 if (!response.ok) {
220 throw new Error('Network response was not ok ' + response.statusText);
221 }
222
223 this.materials = await response.json();
224 for (let i = 0; i < this.materials.length; i++) {
225 this.materialNames.push(this.materials[i]['name']);
226 }
227 return this.materials;
228 } catch (error) {
229 console.error('There has been a problem with your fetch operation:', error);
230 }
231
232 return null;
233 }

◆ initializeInputRemapping()

JsPhysicallyBasedMaterialLoader::initializeInputRemapping ( )

Initialize the input remapping for different shading models.

Returns
{void}

Definition at line 142 of file JsMaterialXPhysicallyBased.js.

143 {
144 // Remap keys for Autodesk Standard Surface shader. How to verify this?
145 const standard_surface_remapKeys = {
146 'color': 'base_color',
147 'specularColor': 'specular_color',
148 'roughness': 'specular_roughness',
149 //'metalness': 'metalness',
150 'ior': 'specular_IOR',
151 //'transmission': 'transmission',
152 'transmission_color': 'transmission_color',
153 'thinFilmIor': 'thin_film_IOR',
154 'thinFilmThickness': 'thin_film_thickness',
155 'transmissionDispersion': 'transmission_dispersion',
156 }
157 // Remap keys for OpenPBR shading model.
158 const openpbr_remapKeys = {
159 'color': 'base_color',
160 'specularColor': 'specular_color',
161 'roughness': 'specular_roughness', // 'base_diffuse_roughness',
162 'metalness': 'base_metalness',
163 'ior': 'specular_ior',
164 'transmission': 'transmission_weight',
165 'transmission_color': 'transmission_color',
166 'subsurfaceRadius': 'subsurface_radius',
167 'thinFilmIor': 'thin_film_ior',
168 'thinFilmThickness': 'thin_film_thickness',
169 'transmissionDispersion': 'transmission_dispersion_scale',
170 }
171 // Remap keys for Khronos glTF shading model.
172 const gltf_remapKeys = {
173 'color': 'base_color',
174 'specularColor': 'specular_color',
175 'roughness': 'roughness',
176 'metalness': 'metallic',
177 'transmission_color': 'attenuation_color',
178 //'ior': 'ior',
179 //'transmission': 'transmission',
180 }
181
182 this.remapMap = {}
183 this.remapMap['standard_surface'] = standard_surface_remapKeys;
184 this.remapMap['gltf_pbr'] = gltf_remapKeys;
185 this.remapMap['open_pbr_surface'] = openpbr_remapKeys;
186 }

◆ loadMaterialX()

JsPhysicallyBasedMaterialLoader::loadMaterialX ( )

Load the MaterialX module.

Returns
{Promise} - Promise to load the MaterialX module

Definition at line 192 of file JsMaterialXPhysicallyBased.js.

193 {
194 return new Promise((resolve, reject) => {
195 MaterialX().then((mtlx) => {
196 this.mx = mtlx;
197 resolve();
198 }).catch((error) => {
199 reject(error);
200 });
201 });
202 }

◆ loadStandardLibraries()

JsPhysicallyBasedMaterialLoader::loadStandardLibraries ( )

Load the MaterialX standard libraries.

Returns
{void}

Definition at line 239 of file JsMaterialXPhysicallyBased.js.

240 {
241 if (!this.mx) {
242 // Call the asynchronous function and then perform additional logic
243 this.loadMaterialX().then(() => {
244
245 this.esslgenerator = new this.mx.EsslShaderGenerator();
246 this.esslgenContext = new this.mx.GenContext(this.esslgenerator);
247 this.stdlib = this.mx.loadStandardLibraries(this.esslgenContext);
248 let children = this.stdlib.getChildren();
249 for (let i = 0; i < children.length; i++) {
250 let child = children[i];
251 child.setSourceUri('STDLIB_ELEMENT');
252 }
253
254 console.log("MaterialX is loaded");
255 }).catch((error) => {
256 console.error("Error loading MaterialX:", error);
257 });
258 }
259 }

◆ skipLibraryElement()

JsPhysicallyBasedMaterialLoader::skipLibraryElement ( element)

Predicate to skip library elements.

Parameters
element- MaterialX element
Returns
True if the element is a library element. False otherwise

Definition at line 266 of file JsMaterialXPhysicallyBased.js.

267 {
268 return !elem.hasSourceUri()
269 }

◆ validateDocument()

JsPhysicallyBasedMaterialLoader::validateDocument ( )

Validate the MaterialX document.

Returns
{[boolean, errors]} - True if the document is valid. False otherwise

Definition at line 111 of file JsMaterialXPhysicallyBased.js.

112 {
113 if (this.doc) {
114 let errors = {}
115 let errorString = ''
116 var valid = this.doc.validate(errors);
117 if (!valid) {
118 errorString = errors.message;
119 }
120 return [valid, errorString]
121 }
122 return [false, 'No MaterialX document'];
123 }

Member Data Documentation

◆ doc

JsPhysicallyBasedMaterialLoader::doc = null

Working MaterialX document.

Definition at line 43 of file JsMaterialXPhysicallyBased.js.

◆ headers

JsPhysicallyBasedMaterialLoader::headers = {}

Headers for the fetch operation.

Definition at line 18 of file JsMaterialXPhysicallyBased.js.

18{};

◆ materialNames

JsPhysicallyBasedMaterialLoader::materialNames = []

List of Physically Based Material names.

Definition at line 28 of file JsMaterialXPhysicallyBased.js.

◆ materials

JsPhysicallyBasedMaterialLoader::materials = null

List of Physically Based Materials.

Definition at line 23 of file JsMaterialXPhysicallyBased.js.

◆ mx

JsPhysicallyBasedMaterialLoader::mx = null

MaterialX module.

Definition at line 38 of file JsMaterialXPhysicallyBased.js.

◆ mxMaterialNames

JsPhysicallyBasedMaterialLoader::mxMaterialNames = []

List of MaterialX Material names.

Definition at line 33 of file JsMaterialXPhysicallyBased.js.

◆ remapMap

JsPhysicallyBasedMaterialLoader::remapMap = {}

Remap keys for input values for different shading models.

Definition at line 53 of file JsMaterialXPhysicallyBased.js.

53{};

◆ stdlib

JsPhysicallyBasedMaterialLoader::stdlib = null

MaterialX standard libraries.

Definition at line 48 of file JsMaterialXPhysicallyBased.js.

◆ url

JsPhysicallyBasedMaterialLoader::url = ''

URL to fetch the Physically Based Materials.

Definition at line 13 of file JsMaterialXPhysicallyBased.js.


The documentation for this class was generated from the following file: