MaterialXLab API  0.0.1
APIs For MaterialXLab Libraries
Loading...
Searching...
No Matches
MxMaterialXHandler Class Reference

This class extends the MxGraphHandler class to provide MaterialX-specific functionality for handling MaterialX graphs within the editor. More...

Inheritance diagram for MxMaterialXHandler:
MxGraphHandler

Public Member Functions

 constructor (id, extension)
 Constructor for the MxMaterialXHandler class.
 
 loadMaterialX ()
 Load in the MaterialX library.
 
 loadLibraryDocument (editor, materialFilename)
 Load the MaterialX document from library into the editor.
 
 initialize (editor, materialFilename)
 Initialize the MaterialX handler for the given editor.
 
 findRenderableItems (graph)
 Find all MaterialX renderable items in a graph.
 
 findRenderableItemsInDoc (mdoc)
 Find all renderable items in the MaterialX document.
 
 buildMetaData (colorSpace, unit, unitType, uiname, uimin, uimax, uifolder, _type)
 Builds and returns metadata for a node based on the provided parameters.
 
 createLiteGraphDefinitions (doc, debug, addInputOutputs, definitionsList, libraryPrefix='mtlx', editor, icon='')
 Creates LiteGraph node definitions based on the MaterialX document.
 
 validateDocument (doc)
 Validates the provided MaterialX document.
 
 saveGraphToDocument (graph, graphWriteOptions)
 Saves the graph to a MaterialX document.
 
 saveGraphToString (extension, graph, graphWriteOptions)
 Saves the graph to a string in the specified format.
 
 saveGraphToFile (extension, graph, graphWriteOptions)
 Saves the graph to a file with the specified extension.
 
 writeGraphToDocument (mltxgraph, graph, graphWriteOptions)
 Writes the graph to the specified MaterialX document.
 
 isArray (_type)
 Determines if the specified type is an array type.
 
 isURI (s)
 
 buildConnections (editor, node, lg_node, explicitInputs, graph, parentGraph)
 Builds the connections between MaterialX nodes.
 
 loadInputMetaData (node, input, property_info)
 Set the meta-data for the specified input based on LiteGraph node property info.
 
 buildGraphFromDoc (doc, editor, auto_arrange)
 Builds the LiteGraph graph from the specified MaterialX document.
 
 loadDefinitionsFromFile ()
 Load MaterialX document containing node definitions from a file.
 
 loadFromString (extension, fileContents, fileName, auto_arrange, rerender=false)
 Load graph editor from a string.
 
 loadFromFile (extension, file, fileName, editor, auto_arrange)
 Load graph editor from a file.
 
 loadFromZip (extension, file, fileName, editor, auto_arrange, rerender=false)
 
 loadMaterialXLibraries (stdlib)
 Load MaterialX definition libraries.
 
 createValidName (name, msg=null)
 Create a valid MaterialX name within the context of the current graph.
 
- Public Member Functions inherited from MxGraphHandler
 constructor (id, extension)
 
 addConverter (converter)
 Add a converter to the handler.
 
 setMonitor (monitor)
 Set the monitor for the handler.
 
 canExport (extension)
 Return if the handler can export to the given extension / format.
 
 getExporter (extension='')
 Find the first exporter that can export to the given extension / format.
 
 canImport (extension)
 Return if the handler can import the given extension / format.
 
 getImporter (extension='')
 Find the first importer that can import the given extension / format.
 
 setColorSpaces (colorSpaces)
 Set the color spaces used by the handler.
 
 getColorSpaces ()
 Get the color spaces used by the handler.
 
 setUnits (units)
 Set the units used by the handler.
 
 getUnits ()
 Get the units used by the handler.
 
 setSourceColorSpace (colorSpace)
 Set the source color space for the handler.
 
 setTargetDistanceUnit (unit)
 Set the target distance unit for the handler.
 
 getSourceColorSpace ()
 Get the source color space for the handler.
 
 getTargetDistanceUnit ()
 Get the target distance unit for the handler.
 
 getExtension ()
 Get the extension /format for the handler.
 
 initialize (editor)
 Initialize the handler for the given editor.
 
 createValidName (name)
 Create a valid name for the given name.
 
 getDefaultValue (value, _type)
 Get default value as a string for the given value and type.
 

Detailed Description

This class extends the MxGraphHandler class to provide MaterialX-specific functionality for handling MaterialX graphs within the editor.

Definition at line 932 of file JsMaterialXNodeEditor.js.

Member Function Documentation

◆ buildConnections()

MxMaterialXHandler::buildConnections ( editor,
node,
lg_node,
explicitInputs,
graph,
parentGraph )

Builds the connections between MaterialX nodes.

Parameters
editor- The graph editor.
node- The MaterialXnode to build connections for.
lg_node- The LiteGraph node
explicitInputs- The explicit inputs to the node.
graph- The LiteGraph parent.
parentGraph- The parent MaterialX graph.

Definition at line 2259 of file JsMaterialXNodeEditor.js.

2259 {
2260
2261 var nodeInputs = [];
2262 var isOutput = (node.getCategory() == 'output');
2263 var isInput = (node.getCategory() == 'input');
2264 if (isOutput || isInput) {
2265 nodeInputs = [node];
2266 }
2267 else {
2268 nodeInputs = node.getInputs();
2269 }
2270 for (var input of nodeInputs) {
2271
2272 var _name = ''
2273
2274 if (!isOutput && !isInput) {
2275 _name = input.getName();
2276 explicitInputs.push(_name);
2277 }
2278
2279 var nodeName = input.getNodeName();
2280 var nodeGraphName = input.getNodeGraphString();
2281 var inputInterfaceName = input.getInterfaceName();
2282 var outputName = input.getOutputString();
2283
2284 if (nodeName.length ||
2285 nodeGraphName.length ||
2286 inputInterfaceName.length ||
2287 outputName.length) {
2288
2289 //console.log('Test connection on input:', input.getNamePath(), 'nodeName:[ ', nodeName,
2290 // '] nodeGraphName:[', nodeGraphName,
2291 // '] inputInterfaceName:[', inputInterfaceName,
2292 // ']outputName:[', outputName, ']');
2293
2294 var target_node = lg_node;
2295 var target_slot = null;
2296 if (!isOutput && !isInput)
2297 target_slot = target_node.findInputSlot(_name);
2298 else
2299 target_slot = 0;
2300 var source_node = null;
2301 var source_slot = 0;
2302 var source_name = nodeName;
2303 if (nodeGraphName.length) {
2304 source_name = nodeGraphName;
2305 }
2306 if (inputInterfaceName.length) {
2307 source_name = inputInterfaceName;
2308 }
2309
2310 var graphToCheck = graph;
2311 if (isInput && graph._subgraph_node) {
2312 target_node = graph._subgraph_node;
2313 target_slot = target_node.findInputSlot(lg_node.title);
2314 // Go up to parent graph
2315 graphToCheck = parentGraph;
2316 //console.log(' go up to parent graph:', graphToCheck,
2317 // 'from:', graph, 'subgraph:', graph._subgraph_node,
2318 //'target_node:', target_node.title, 'target_slot:', target_slot);
2319 }
2320 source_node = graphToCheck.findNodeByTitle(source_name);
2321 if (source_node) {
2322 if (outputName) {
2323 var outputSlot = source_node.findOutputSlot(outputName);
2324 if (outputSlot >= 0) {
2325 source_slot = outputSlot;
2326 }
2327 else {
2328 editor.debugOutput('Failed to find output slot:' + outputName, 1);
2329 }
2330 var linkInfo = source_node.connect(source_slot, target_node, target_slot);
2331 if (!linkInfo) {
2332 editor.debugOutput('Failed to connect:' + source_node.title + '.' + outputName, '->', target_node.title + '.' + _name), 1, false;
2333 }
2334 }
2335 //console.log('CONNECT START: source[', source_node.title, '.', source_slot,
2336 // '] --> target[:', target_node.title, ".", target_slot);
2337 var linkInfo = null;
2338 if (source_slot == null || target_slot == null || target_node == null) {
2339 console.warning('Cannot connect!')
2340 }
2341 else {
2342 linkInfo = source_node.connect(source_slot, target_node, target_slot);
2343 }
2344 if (!linkInfo) {
2345 editor.debugOutput('Failed to connect:' + source_node.title + '.' + outputName, '->', target_node.title + '.' + _name, 1);
2346 }
2347 //console.log('CONNECT END: source[', source_node.title, '.', source_slot,
2348 // '] --> target[:', target_node.title, ".", target_slot);
2349 }
2350 else {
2351 console.log('Failed to find node ', source_name, 'in graph:', graphToCheck);
2352 this.editor.debugOutput('Failed to find source node: ' + source_node + "." +
2353 source_name, '->', lg_node.title + "." + _name, 2);
2354 }
2355 }
2356 else {
2357 const inputType = input.getAttribute(ne_mx.TypedElement.TYPE_ATTRIBUTE);
2358 let valueString = input.getValueString();
2359 if (valueString.length > 0) {
2360 let _value = '';
2361
2362 if (inputType === ne_mx.FILENAME_TYPE_STRING) {
2363 // If the string is a http or blob url then call input.getValuesString()
2364 // else call input.getResolvedValueString()
2365 if (this.isURI(valueString)) {
2366 this.editor.debugOutput('Filename is a url:' + valueString + '. Cannot use resolved path value.');
2367 _value = input.getValueString();
2368 }
2369 else {
2370 _value = input.getResolvedValueString();
2371 }
2372 }
2373 else
2374 {
2375 _value = input.getResolvedValueString(); // input.getValueString();
2376
2377 if (this.isArray(input.getType())) {
2378 let valueArray = "[" + _value + "]"
2379 valueArray = JSON.parse(valueArray);
2380 //valueArray = _value.split(/[\s,]+/); - This does not parse all configs properly.
2381 //console.log('>>> Read array value:', _value, 'as:', valueArray);
2382 _value = valueArray;
2383 }
2384 }
2385
2386 //console.log('-- Value Input:',
2387 //lg_node.title + "." + _name, 'value:', _value);
2388 lg_node.setProperty(_name, _value);
2389 }
2390 }
2391
2392 var property_info = lg_node.getPropertyInfo(_name);
2393 this.loadInputMetaData(node, input, property_info);
2394 }
2395 }
isArray(_type)
Determines if the specified type is an array type.
loadInputMetaData(node, input, property_info)
Set the meta-data for the specified input based on LiteGraph node property info.

◆ buildGraphFromDoc()

MxMaterialXHandler::buildGraphFromDoc ( doc,
editor,
auto_arrange )

Builds the LiteGraph graph from the specified MaterialX document.

Parameters
doc- The MaterialX document to build the graph from.
editor- The graph editor.
auto_arrange- True to auto-arrange the graph, false otherwise.
Returns
{void}

Definition at line 2479 of file JsMaterialXNodeEditor.js.

2479 {
2480 let debug = false;
2481 let loadNodePositions = false; // TODO: Some issues with UI update when setting xpos, ypos. To address.
2482
2483 //console.log('Build graph from doc. auto_arrange: ', auto_arrange);
2484 if (!ne_mx) {
2485 editor.debugOutput("MaterialX is not initialized", 2);
2486 return;
2487 }
2488
2489 editor.clearGraph();
2490
2491 // Don't try and update the graph while building it
2492 editor.monitor.monitorGraph(graph, false);
2493
2494 // Index here is index into litegraph nodes
2495 var mtlxNodes = [];
2496 var mtlxNodeDefs = [];
2497
2498 for (var interfaceInput of doc.getInputs()) {
2499 var _type = interfaceInput.getType();
2500 var id = 'mtlx/input/input_' + _type;
2501
2502 var lg_node = LiteGraph.createNode(id);
2503 if (lg_node) {
2504 lg_node.title = interfaceInput.getName();
2505 if (debug)
2506 console.log('Add top level input:', lg_node.title, 'to graph', graph);
2507
2508 var _value = interfaceInput.getValueString();
2509 if (_value && _value.length > 0) {
2510 if (this.isArray(interfaceInput.getType())) {
2511 _value = "[" + _value + "]"
2512 _value = JSON.parse(_value);
2513 }
2514 lg_node.setProperty('in', _value);
2515 }
2516
2517 if (loadNodePositions) {
2518 var xpos = interfaceInput.getAttribute('xpos');
2519 var ypos = interfaceInput.getAttribute('ypos');
2520 if (xpos.length > 0 && ypos.length > 0) {
2521 lg_node.pos[0] = xpos;
2522 lg_node.pos[1] = ypos;
2523 //lg_node.flags.collapsed = false;
2524 }
2525 }
2526
2527 // Make sure size is updated
2528 lg_node.setSize(lg_node.computeSize());
2529
2530 graph.add(lg_node);
2531
2532 //mtlxNodes.push([interfaceInput, lg_node, graph]);
2533 }
2534 }
2535
2536 for (var interfaceOutput of doc.getOutputs()) {
2537 var _type = interfaceOutput.getType()
2538 var id = 'mtlx/output/output_' + _type;
2539
2540 var lg_node = LiteGraph.createNode(id);
2541 if (lg_node) {
2542 lg_node.title = interfaceOutput.getName();
2543 graph.add(lg_node);
2544 if (debug) {
2545 console.log('Add graph output:', lg_node.title);
2546 }
2547
2548 // Make sure size is updated
2549 lg_node.setSize(lg_node.computeSize());
2550
2551 if (loadNodePositions) {
2552 var xpos = interfaceOutput.getAttribute('xpos');
2553 var ypos = interfaceOutput.getAttribute('ypos');
2554 if (xpos.length > 0 && ypos.length > 0)
2555 lg_node.pos = [xpos, ypos];
2556 }
2557
2558 mtlxNodes.push([interfaceOutput, lg_node, graph]);
2559 }
2560 }
2561
2562 for (var node of doc.getNodes()) {
2563 var nodeDef = node.getNodeDef();
2564 if (!nodeDef) {
2565 editor.debugOutput('Skip node w/o nodedef:' + node.getName(), 1)
2566 continue;
2567 }
2568
2569 // mtlx/pbr/gltf_pbr_surfaceshader
2570 var id = 'mtlx/' + nodeDef.getNodeGroup() + '/' + nodeDef.getName();
2571 id = id.replace('ND_', '');
2572 if (debug)
2573 console.log('Load node:', node.getName(), ' -> ', id);
2574
2575 var lg_node = LiteGraph.createNode(id);
2576 if (lg_node) {
2577 //console.log('LiteGraph node:', lg_node);
2578 lg_node.title = node.getName();
2579
2580 graph.add(lg_node);
2581
2582 // Make sure size is updated
2583 lg_node.setSize(lg_node.computeSize());
2584
2585 if (loadNodePositions) {
2586 var xpos = node.getAttribute('xpos');
2587 var ypos = node.getAttribute('ypos');
2588 if (xpos.length > 0 && ypos.length > 0)
2589 lg_node.pos = [xpos, ypos];
2590 }
2591
2592 mtlxNodes.push([node, lg_node, graph]);
2593 mtlxNodeDefs.push(nodeDef);
2594 }
2595 else {
2596 editor.debugOutput('Failed to create node:' + node.getName(), 2);
2597 }
2598 }
2599
2600 for (var nodegraph of doc.getNodeGraphs()) {
2601 if (nodegraph.hasSourceUri()) {
2602 continue;
2603 }
2604 var nodedefAttrib = nodegraph.getAttribute('nodedef');
2605 if (nodedefAttrib && nodedefAttrib.length > 0) {
2606 console.log('Skip loading in functional graph:', nodegraph.getName(), 'nodedef:', nodedefAttrib);
2607 continue;
2608 }
2609 if (debug)
2610 console.log('Create nodegraph:', nodegraph.getName());
2611
2612 var title = nodegraph.getName();
2613 var subgraphNode = LiteGraph.createNode("graph/subgraph", title);
2614 //var subgraph = new LiteGraph.LGraph();
2615 //subgraphNode._subgraph_node = subgraph;
2616 //subgraphNode.bgcolor = "#112";
2617 subgraphNode.bgImageUrl = "./Icons/nodegraph.png";
2618
2619 var mtlxSubGraphNodes = [];
2620 for (var interfaceInput of nodegraph.getInputs()) {
2621 var _type = interfaceInput.getType();
2622 var id = 'mtlx/input/input_' + _type;
2623
2624 var lg_node = LiteGraph.createNode(id);
2625 if (lg_node) {
2626 lg_node.title = interfaceInput.getName();
2627 this.loadInputMetaData(null, interfaceInput, lg_node.properties_info[0]);
2628 subgraphNode.subgraph.add(lg_node);
2629
2630 if (debug)
2631 console.log('-------- Add subgraph input:', lg_node.title, lg_node);
2632
2633 subgraphNode.addInput(interfaceInput.getName(), _type);
2634 subgraphNode.subgraph.addInput(interfaceInput.getName(), _type);
2635
2636 var _value = interfaceInput.getValueString();
2637 if (_value && _value.length > 0) {
2638 if (this.isArray(interfaceInput.getType())) {
2639 _value = "[" + _value + "]"
2640 _value = JSON.parse(_value);
2641 }
2642 lg_node.setProperty('in', _value);
2643 }
2644
2645 // Make sure size is updated
2646 lg_node.setSize(lg_node.computeSize());
2647
2648 if (loadNodePositions) {
2649 var xpos = nodegraph.getAttribute('xpos');
2650 var ypos = nodegraph.getAttribute('ypos');
2651 if (xpos.length > 0 && ypos.length > 0)
2652 lg_node.pos = [xpos, ypos];
2653 }
2654
2655 mtlxSubGraphNodes.push([interfaceInput, lg_node, graph]);
2656 }
2657 }
2658
2659 for (var interfaceOutput of nodegraph.getOutputs()) {
2660 var _type = interfaceOutput.getType()
2661 var id = 'mtlx/output/output_' + _type;
2662
2663 var lg_node = LiteGraph.createNode(id);
2664 if (lg_node) {
2665 lg_node.title = interfaceOutput.getName();
2666 subgraphNode.subgraph.add(lg_node);
2667 if (debug)
2668 console.log('Add subgraph output:', lg_node.title);
2669
2670 subgraphNode.addOutput(interfaceOutput.getName(), _type);
2671 subgraphNode.subgraph.addOutput(interfaceOutput.getName(), _type);
2672
2673 // Make sure size is updated
2674 lg_node.setSize(lg_node.computeSize());
2675
2676 if (loadNodePositions) {
2677 var xpos = interfaceOutput.getAttribute('xpos');
2678 var ypos = interfaceOutput.getAttribute('ypos');
2679 if (xpos.length > 0 && ypos.length > 0)
2680 lg_node.pos = [xpos, ypos];
2681 }
2682
2683 mtlxSubGraphNodes.push([interfaceOutput, lg_node, graph]);
2684 }
2685 }
2686
2687
2688 for (var node of nodegraph.getNodes()) {
2689 var nodeDef = node.getNodeDef();
2690 if (!nodeDef) {
2691 editor.debugOutput('Skip node w/o nodedef:' + node.getName(), 1)
2692 continue;
2693 }
2694
2695 // mtlx/pbr/gltf_pbr_surfaceshader
2696 var id = 'mtlx/' + nodeDef.getNodeGroup() + '/' + nodeDef.getName();
2697 id = id.replace('ND_', '');
2698
2699 var lg_node = LiteGraph.createNode(id);
2700 lg_node.title = node.getName();
2701 subgraphNode.subgraph.add(lg_node);
2702 if (debug)
2703 console.log('Add subgraph node:', lg_node.title);
2704
2705 // Make sure size is updated
2706 lg_node.setSize(lg_node.computeSize());
2707
2708 if (loadNodePositions) {
2709 var xpos = node.getAttribute('xpos');
2710 var ypos = node.getAttribute('ypos');
2711 if (xpos.length > 0 && ypos.length > 0)
2712 lg_node.pos = [xpos, ypos];
2713 }
2714
2715 mtlxSubGraphNodes.push([node, lg_node, graph]);
2716 }
2717
2718 for (var item of mtlxSubGraphNodes) {
2719 var node = item[0];
2720 var lg_node = item[1];
2721 var parentGraph = item[2];
2722 var explicitInputs = [];
2723
2724 // Make sure size is updated
2725 lg_node.setSize(lg_node.computeSize());
2726
2727 //console.log('Build connections for subgraog node:', lg_node.title);
2728 this.buildConnections(editor, node, lg_node, explicitInputs, subgraphNode.subgraph, parentGraph);
2729 }
2730
2731 if (debug)
2732 console.log('Add subgraph:', subgraphNode.title);
2733
2734 if (auto_arrange > 0) {
2735 subgraphNode.subgraph.arrange(auto_arrange);
2736 }
2737
2738 graph.add(subgraphNode);
2739
2740 }
2741
2742 // Build top level connections last after top level nodes
2743 // and nodegraph have been added.
2744 var itemCount = 0;
2745 for (var item of mtlxNodes) {
2746 var node = item[0];
2747 var lg_node = item[1];
2748
2749 // Keep track of explicit inputs
2750 var explicitInputs = [];
2751 //console.log('Build connections for:', lg_node.title);
2752 this.buildConnections(editor, node, lg_node, explicitInputs, graph, null);
2753
2754 if (lg_node.nodedef_node == 'input' || lg_node.nodedef_node == 'output') {
2755 continue;
2756 }
2757
2758 /*
2759 var removeInputs = [];
2760 var nodeDef = mtlxNodeDefs[itemCount];
2761 if (nodeDef) {
2762 for (var nodeDefInput of nodeDef.getActiveInputs()) {
2763 var _name = nodeDefInput.getName();
2764 if (!explicitInputs.includes(_name)) {
2765 removeInputs.push(_name);
2766 }
2767 }
2768 for (var _name of removeInputs) {
2769 var slot = lg_node.findInputSlot(_name);
2770 lg_node.inputs[slot].hidden = true;
2771 console.log('Hide input:', _name, '. ', slot, ' on: ', lg_node);
2772 //lg_node.removeInput(slot);
2773 }
2774
2775 // Make sure size is updated
2776 lg_node.setSize(lg_node.computeSize());
2777 }
2778 */
2779 itemCount++;
2780 }
2781
2782 editor.monitor.monitorGraph(graph, true);
2783
2784 if (auto_arrange > 0) {
2785 graph.arrange(auto_arrange);
2786 }
2787
2788 graph.setDirtyCanvas(true, true);
2789 graphcanvas.setDirty(true, true);
2790 }
var graphcanvas
buildConnections(editor, node, lg_node, explicitInputs, graph, parentGraph)
Builds the connections between MaterialX nodes.

◆ buildMetaData()

MxMaterialXHandler::buildMetaData ( colorSpace,
unit,
unitType,
uiname,
uimin,
uimax,
uifolder,
_type )

Builds and returns metadata for a node based on the provided parameters.

This metadata can be used to enhance the node's representation and interaction within the UI, specifying details like color space, unit, and UI properties.

Parameters
colorSpace- The color space of the node (e.g., 'sRGB', 'linear').
unit- The unit of measurement for the node's value (e.g., 'meters', 'seconds').
unitType- The type of unit measurement (e.g., 'distance', 'time').
uiname- The name to display in the UI for this node.
uimin- The minimum value allowed for this node in the UI.
uimax- The maximum value allowed for this node in the UI.
uifolder- The folder/group in the UI where this node should be placed.
_type- The data type of the node (e.g., 'float', 'vector3').
Returns
An object containing the constructed metadata based on the input parameters.

Definition at line 1178 of file JsMaterialXNodeEditor.js.

1178 {
1179 // Create a struct with the metadata names as key and value
1180 var metaData = {};
1181 metaData['colorspace'] = colorSpace;
1182 metaData['unit'] = unit;
1183 metaData['unittype'] = unitType;
1184 metaData['uiname'] = uiname;
1185 if (_type == 'vector2' || _type == 'vector3' || _type == 'vector4' || _type == 'matrix33' || _type == 'matrix44') {
1186 if (uimin) {
1187 uimin = uimin.split(',').map(Number);
1188 }
1189 if (uimax) {
1190 uimax = uimax.split(',').map(Number);
1191 }
1192 }
1193 metaData['uimin'] = uimin;
1194 metaData['uimax'] = uimax;
1195 metaData['uifolder'] = uifolder;
1196
1197 // Return struct in an array
1198 return metaData;
1199 }

◆ constructor()

MxMaterialXHandler::constructor ( id,
extension )

Constructor for the MxMaterialXHandler class.

Parameters
id- The identifier for the handler.
extension- The associated extension for the handler.

Definition at line 940 of file JsMaterialXNodeEditor.js.

940 {
941 super(id, extension);
942 }

◆ createLiteGraphDefinitions()

MxMaterialXHandler::createLiteGraphDefinitions ( doc,
debug,
addInputOutputs,
definitionsList,
libraryPrefix = 'mtlx',
editor,
icon = '' )

Creates LiteGraph node definitions based on the MaterialX document.

This function parses the MaterialX document to extract node definitions and generates Javascript code for corresponding LiteGraph nodes that can be used within the editor.

Parameters
doc- The MaterialX document to parse.
debug- Flag to enable debug logging.
addInputOutputs- Flag to determine whether to add inputs and outputs to the LiteGraph nodes.
definitionsList- List to store the generated LiteGraph node definitions.
libraryPrefix- Prefix to use for the LiteGraph nodes, default is 'mtlx'.
editor- The instance of the editor where the nodes will be used.
icon- Icon to use for the LiteGraph nodes, default is an empty string.
Returns
{void}

Definition at line 1215 of file JsMaterialXNodeEditor.js.

1217 {
1218 var definition_code = ""
1219
1220 console.log('Creating LiteGraph definitions from MaterialX document:', doc);
1221
1222 // Get the node definitions from the MaterialX document
1223 var nodeDefs = doc.getNodeDefs();
1224
1225 if (debug)
1226 definition_code += "console.log('Loading MaterialX Definitions...');\n";
1227
1228 definition_code += "// MaterialX LiteGraph Functional Definitions\n"
1229 definition_code += "//\n";
1230 definition_code += "// MaterialX Version: " + ne_mx.getVersionString() + "\n";
1231 const date = new Date();
1232 definition_code += "// Generated on: " + date.toString() + "\n";
1233 definition_code += "//\n";
1234
1235 var TMAP = {}
1236 TMAP['float'] = 'float';
1237 TMAP['color3'] = 'color3';
1238 TMAP['color4'] = 'color4';
1239 TMAP['vector2'] = 'vector2';
1240 TMAP['vector3'] = 'vector3';
1241 TMAP['vector4'] = 'vector4';
1242 TMAP['matrix33'] = 'matrix33';
1243 TMAP['matrix44'] = 'matrix44';
1244 TMAP['integer'] = 'integer';
1245 TMAP['string'] = 'string';
1246 TMAP['boolean'] = 'boolean';
1247 TMAP['filename'] = 'filename';
1248 TMAP['BSDF'] = 'BSDF';
1249 TMAP['EDF'] = 'EDF';
1250 TMAP['VDF'] = 'VDF';
1251 TMAP['surfaceshader'] = 'surfaceshader';
1252 TMAP['volumeshader'] = 'volumeshader';
1253 TMAP['displacementshader'] = 'displacementshader';
1254 TMAP['lightshader'] = 'lightshader';
1255 TMAP['material'] = 'material';
1256 TMAP['vector2array'] = 'vector2array';
1257
1258 var CMAP = {}
1259 CMAP['integer'] = "#A32";
1260 CMAP['float'] = "#161";
1261 CMAP['vector2'] = "#265";
1262 CMAP['vector3'] = "#465";
1263 CMAP['vector4'] = "#275";
1264 CMAP['color3'] = "#37A";
1265 CMAP['color4'] = "#69A";
1266 CMAP['matrix33'] = "#333";
1267 CMAP['matrix44'] = "#444";
1268 CMAP['string'] = "#395";
1269 CMAP['filename'] = "#888";
1270 CMAP['boolean'] = "#060";
1271
1272 var inputTypes = ['float', 'color3', 'color4', 'vector2', 'vector3', 'vector4', 'matrix33', 'matrix44', 'integer', 'string', 'boolean', 'filename', 'BSDF', 'EDF', 'VDF', 'surfaceshader', 'volumeshader', 'displacementshader', 'lightshader', 'material', 'vector2array'];
1273 var outputTypes = ['float', 'color3', 'color4', 'vector2', 'vector3', 'vector4', 'matrix33', 'matrix44', 'integer', 'string', 'boolean', 'filename', 'BSDF', 'EDF', 'VDF', 'surfaceshader', 'volumeshader', 'displacementshader', 'lightshader', 'material', 'vector2array'];
1274
1275 // TODO: Support tokens
1276 var supporTokens = false;
1277 if (supporTokens) {
1278 inputTypes.push('token');
1279 TMAP['token'] = 'string';
1280 }
1281
1282 const INPUT_ND = 'ND_input_';
1283 const OUTPUT_ND = 'ND_output_';
1284 const INPUT_NODE_STRING = 'input';
1285 const OUTPUT_NODE_STRING = 'output';
1286 const LIBRARY_ICON = '';
1287
1288 // Register inputs (which have no nodedef)
1289 if (addInputOutputs) {
1290 for (var _type of inputTypes) {
1291 var id = libraryPrefix + '/input/input_' + _type;
1292 var functionName = ne_mx.createValidName(id);
1293 var titleName = 'input_' + _type;
1294 definition_code += "\n/**\n";
1295 definition_code += " * @function "+ functionName + "\n";
1296 definition_code += " * @description Library: " + libraryPrefix + ". Category: input. Type: " + _type + "\n";
1297 definition_code += " * LiteGraph id: " + id + "\n";
1298 definition_code += " */\n";
1299 definition_code += "function " + functionName + "() {\n";
1300 {
1301 definition_code += " this.nodedef_icon = '" + LIBRARY_ICON + "';\n";
1302 definition_code += " this.nodedef_name = '" + INPUT_ND + _type + "';\n";
1303 definition_code += " this.nodedef_node = '" + INPUT_NODE_STRING + "';\n";
1304 definition_code += " this.nodedef_type = '" + _type + "';\n";
1305 definition_code += " this.nodedef_group = '" + INPUT_NODE_STRING + "';\n";
1306 if (_type == 'token')
1307 _type = 'string';
1308 definition_code += " this.addInput('in', '" + TMAP[_type] + "');\n";
1309 var value = this.getDefaultValue('', _type);
1310 var metaData = this.buildMetaData('', '', '', '', null, null, '', null);
1311 metaData = JSON.stringify(metaData);
1312
1313 // TODO: It's not possible to add a default colorspace since you
1314 // don't know what node type it will feed into. i.e. the specification
1315 // is underdefined at this time (1.39)
1316 if (_type == 'filename')
1317 {
1318 ; // Nothing for now.
1319 }
1320
1321 definition_code += " this.addProperty('in', " + value + ", '" + _type + "'," + metaData + ");\n";
1322 definition_code += " this.addOutput('out', '" + TMAP[_type] + "');\n";
1323
1324 definition_code += " this.title = '" + titleName + "';\n"
1325 var desc = '"MaterialX:' + id + '"';
1326 definition_code += " this.desc = " + desc + ";\n";
1327
1328 var onNodeCreated = "function() {\n";
1329 onNodeCreated += " //console.log('Node created:', this);\n";
1330 onNodeCreated += " }";
1331 definition_code += " this.onNodeCreated = " + onNodeCreated + "\n";
1332 var onRemoved = "function() {\n";
1333 onRemoved += " //console.log('Node removed:', this);\n";
1334 onRemoved += " }";
1335 definition_code += " this.onRemoved = " + onRemoved + "\n";
1336
1337 // Property changed callback
1338 let monitor = editor.monitor;
1339 var onPropertyChanged = "function(name, value, prev_value) {\n";
1340 if (monitor)
1341 {
1342 onPropertyChanged += " MxShadingGraphEditor.theEditor.monitor.onPropertyChanged(this.title, name, value, prev_value, this);\n";
1343 }
1344 else
1345 {
1346 onPropertyChanged += " console.log('+ Internal property changed:', this.title, name, value, prev_value, this);\n";
1347 }
1348 onPropertyChanged += " }";
1349 definition_code += " this.onPropertyChanged = " + onPropertyChanged + "\n";
1350
1351 // Property info / attribute changed callback
1352 var onPropertyInfoChanged = "function(name, info, value, prev_value) {\n";
1353 if (monitor)
1354 {
1355 onPropertyInfoChanged += " MxShadingGraphEditor.theEditor.monitor.onPropertyInfoChanged(this.title, name, info, value, prev_value, this);\n";
1356 }
1357 else
1358 {
1359 onPropertyInfoChanged += " console.log('+ Internal property info changed:', this.title, name, info, value, prev_value, this);\n";
1360 }
1361 onPropertyInfoChanged += " }"
1362 definition_code += " this.onPropertyInfoChanged = " + onPropertyInfoChanged + "\n";
1363
1364 // Output connection callback
1365 var onConnectOutput = "function(slot, input_type, input, target_node, target_slot) {\n";
1366 if (monitor)
1367 {
1368 onConnectOutput += " MxShadingGraphEditor.theEditor.monitor.onConnectOutput(slot, input_type, input, target_node, target_slot, this);\n";
1369 }
1370 else
1371 {
1372 onConnectOutput += " console.log('+ Output connection changed:', this.title, slot, input_type, input, target_node, target_slot);\n";
1373 }
1374 onConnectOutput += " }"
1375 definition_code += " this.onConnectOutput = " + onConnectOutput + "\n";
1376
1377 // Input connection callback
1378 var onConnectInput = "function(target_slot, output_type, output, source, slot) {\n";
1379 if (monitor)
1380 {
1381 onConnectInput += " MxShadingGraphEditor.theEditor.monitor.onConnectInput(target_slot, output_type, output, source, slot, this);\n";
1382 }
1383 else
1384 {
1385 onConnectInput += " console.log('+ Input connection changed:', this.title, target_slot, output_type, output, source, slot);\n";
1386 }
1387 onConnectInput += " }"
1388 definition_code += " this.onConnectInput = " + onConnectInput + "\n";
1389
1390 definition_code += " this.color = '#004C94';\n";
1391 definition_code += " this.bgcolor = '#000';\n";
1392 if (_type in CMAP) {
1393 definition_code += " this.boxcolor = '" + CMAP[_type] + "';\n";
1394 }
1395 definition_code += " this.shape = LiteGraph.ROUND_SHAPE;\n";
1396
1397 definition_code += " this.onExecute = function() {\n";
1398 definition_code += " console.log('Executing node: ', this);\n";
1399 definition_code += " }\n";
1400 }
1401 definition_code += "}\n"
1402 definition_code += "LiteGraph.registerNodeType('" + id + "', " + functionName + ");\n";
1403 }
1404
1405 // Register outputs (which have no nodedef)
1406 for (var _type of outputTypes) {
1407 var id = libraryPrefix + '/output/output_' + _type;
1408 var functionName = ne_mx.createValidName(id);
1409 var titleName = 'output_' + _type;
1410
1411 definition_code += "\n/**\n";
1412 definition_code += " * @function "+ functionName + "\n";
1413 definition_code += " * @description Library: " + libraryPrefix + ". Category: output. Type: " + _type + "\n";
1414 definition_code += " * LiteGraph id: " + id + "\n";
1415 definition_code += " */\n";
1416
1417 definition_code += "function " + functionName + "() {\n";
1418 {
1419 definition_code += " this.title = '" + titleName + "';\n"
1420 var desc = '"MaterialX Node :' + id + '"';
1421 definition_code += " this.desc = " + desc + ";\n";
1422
1423 definition_code += " this.nodedef_icon = '" + LIBRARY_ICON + "';\n";
1424 definition_code += " this.nodedef_name = '" + OUTPUT_ND + + _type + "';\n";
1425 definition_code += " this.nodedef_node = '" + OUTPUT_NODE_STRING + "';\n";
1426 definition_code += " this.nodedef_type = '" + _type + "';\n";
1427 definition_code += " this.nodedef_group = '" + OUTPUT_NODE_STRING + "';\n";
1428 definition_code += " this.addInput('in', '" + TMAP[_type] + "');\n";
1429 var value = this.getDefaultValue('', _type);
1430 definition_code += " this.addProperty('in', " + value + ", '" + _type + "');\n";
1431 definition_code += " this.addOutput('out', '" + TMAP[_type] + "');\n";
1432
1433 var onNodeCreated = "function() {\n";
1434 onNodeCreated += " //console.log('Node created:', this);\n";
1435 onNodeCreated += " }";
1436 definition_code += " this.onNodeCreated = " + onNodeCreated + "\n";
1437 var onRemoved = "function() {\n";
1438 onRemoved += " //console.log('Node removed:', this);\n";
1439 onRemoved += " }";
1440 definition_code += " this.onRemoved = " + onRemoved + "\n";
1441
1442 let monitor = editor.monitor;
1443 var onPropertyChanged = "function(name, value, prev_value) {\n";
1444 if (monitor)
1445 {
1446 onPropertyChanged += " MxShadingGraphEditor.theEditor.monitor.onPropertyChanged(this.title, name, value, prev_value, this);\n";
1447 }
1448 else
1449 {
1450 onPropertyChanged += " console.log('+ Internal property changed:', this.title, name, value, prev_value, this);\n";
1451 }
1452 onPropertyChanged += " }";
1453 definition_code += " this.onPropertyChanged = " + onPropertyChanged + "\n";
1454
1455 var onPropertyInfoChanged = "function(name, info, value, prev_value) {\n";
1456 if (monitor)
1457 {
1458 onPropertyInfoChanged += " MxShadingGraphEditor.theEditor.monitor.onPropertyInfoChanged(this.title, name, info, value, prev_value, this);\n";
1459 }
1460 else
1461 {
1462 onPropertyInfoChanged += " console.log('+ Internal property info changed:', this.title, name, info, value, prev_value, this);\n";
1463 }
1464 onPropertyInfoChanged += " }"
1465 definition_code += " this.onPropertyInfoChanged = " + onPropertyInfoChanged + "\n";
1466
1467
1468 // Output connection callback
1469 var onConnectOutput = "function(slot, input_type, input, target_node, target_slot) {\n";
1470 if (monitor)
1471 {
1472 onConnectOutput += " MxShadingGraphEditor.theEditor.monitor.onConnectOutput(slot, input_type, input, target_node, target_slot, this);\n";
1473 }
1474 else
1475 {
1476 onConnectOutput += " console.log('+ Output connection changed:', this.title, slot, input_type, input, target_node, target_slot);\n";
1477 }
1478 onConnectOutput += " }"
1479 definition_code += " this.onConnectOutput = " + onConnectOutput + "\n";
1480
1481 // Input connection callback
1482 var onConnectInput = "function(target_slot, output_type, output, source, slot) {\n";
1483 if (monitor)
1484 {
1485 onConnectInput += " MxShadingGraphEditor.theEditor.monitor.onConnectInput(target_slot, output_type, output, source, slot, this);\n";
1486 }
1487 else
1488 {
1489 onConnectInput += " console.log('+ Input connection changed:', this.title, target_slot, output_type, output, source, slot);\n";
1490 }
1491 onConnectInput += " }"
1492 definition_code += " this.onConnectInput = " + onConnectInput + "\n";
1493
1494 definition_code += " this.color = '#013514';\n";
1495 definition_code += " this.bgcolor = '#000';\n";
1496 if (_type in CMAP) {
1497 definition_code += " this.boxcolor = '" + CMAP[_type] + "';\n";
1498 }
1499 definition_code += " this.shape = LiteGraph.ROUND_SHAPE;\n";
1500
1501 definition_code += " this.onExecute = function() {\n";
1502 definition_code += " console.log('Executing node:', this);\n";
1503 definition_code += " }\n";
1504 }
1505 definition_code += "}\n"
1506 definition_code += "LiteGraph.registerNodeType('" + id + "', " + functionName + ");\n";
1507 definitionsList.push(id);
1508 }
1509 }
1510
1511 // Iterate over all node definitions
1512 for (var nodeDef of nodeDefs) {
1513
1514 var nodeDefName = nodeDef.getName();
1515 var id = libraryPrefix + '/' + nodeDef.getNodeGroup() + '/' + nodeDefName;
1516 id = id.replace('ND_', '');
1517 var functionName = ne_mx.createValidName(id);
1518 var nodeType = nodeDef.getType();
1519 var titleName = nodeDef.getNodeString() + "_" + nodeType;
1520 var swatchLocation = 'https://kwokcb.github.io/MaterialX_Learn/resources/mtlx/nodedef_materials/';
1521 var outputs = nodeDef.getActiveOutputs();
1522 var outputName = outputs[0].getName(); // TODO: Handle swatch for multiple outputs
1523 var swatchId = swatchLocation + 'material_' + nodeDefName + '_' + outputName + '_genglsl.png';
1524 swatchId = swatchId.replace('ND_', '');
1525 if (debug)
1526 console.log('\n--- Registering node type:', id, '----');
1527
1528
1529 definition_code += "\n/**\n";
1530 definition_code += " * @function "+ functionName + "\n";
1531 definition_code += " * @description Library: " + libraryPrefix + ". Category: " + nodeString + ". Type: " + nodeType + "\n";
1532 definition_code += " * LiteGraph id: " + id + "\n";
1533 definition_code += " */\n";
1534
1535 definition_code += "function " + functionName + "() {\n";
1536 {
1537 var nodeGroup = nodeDef.getNodeGroup();
1538 var nodeString = nodeDef.getNodeString();
1539 var theIcon = icon;
1540 if (theIcon.length == 0) {
1541 for (var key in editor.ui.icon_map) {
1542 if (nodeString.toLowerCase().startsWith(key.toLowerCase())) {
1543 if (key in editor.ui.icon_map)
1544 theIcon = editor.ui.icon_map[key];
1545 //console.log('set icon:', theIcon, 'for:', key, nodeString);
1546 break;
1547 }
1548 else if (nodeGroup.toLowerCase().startsWith(key.toLowerCase())) {
1549 if (key in editor.ui.icon_map)
1550 theIcon = editor.ui.icon_map[key];
1551 //console.log('set icon:', theIcon, 'for:', key, nodeGroup);
1552 break;
1553 }
1554 }
1555 }
1556
1557 definition_code += " this.nodedef_icon = '" + theIcon + "';\n";
1558 definition_code += " this.nodedef_name = '" + nodeDefName + "';\n";
1559 definition_code += " this.nodedef_type = '" + nodeType + "';\n";
1560 definition_code += " this.nodedef_node = '" + nodeString + "';\n";
1561 definition_code += " this.nodedef_href = 'https://kwokcb.github.io/MaterialX_Learn/documents/definitions/" + nodeString + ".html';\n";
1562 definition_code += " this.nodedef_swatch = '" + swatchId + "';\n";
1563 definition_code += " this.nodedef_group = '" + nodeGroup + "';\n";
1564
1565 for (var input of nodeDef.getActiveInputs()) {
1566 var _name = input.getName();
1567 var _type = input.getType();
1568 if (_type in TMAP)
1569 _type = TMAP[_type];
1570 else
1571 console.log('Unmappable type:', _type)
1572 definition_code += " this.addInput('" + _name + "','" + _type + "');\n";
1573
1574 let value = input.getValueString();
1575 value = this.getDefaultValue(value, _type);
1576 let uiname = input.getAttribute('uiname');
1577
1578 let uimin = input.getAttribute('uimin');
1579 if (uimin.length == 0) {
1580 uimin = null;
1581 }
1582 let uimax = input.getAttribute('uimax');
1583 if (uimax.length == 0) {
1584 uimax = null;
1585 }
1586 let uisoftmin = input.getAttribute('uisoftmin');
1587 if (uisoftmin.length > 0) {
1588 uimin = uisoftmin;
1589 }
1590 let uisoftmax = input.getAttribute('uisoftmax');
1591 if (uisoftmax.length > 0) {
1592 uimax = uisoftmax;
1593 }
1594 var uifolder = input.getAttribute('uifolder');
1595 var metaData = this.buildMetaData('', '', '', uiname, uimin, uimax, uifolder, _type);
1596
1597 // Add colorspace on nodedefs
1598 let colorspace = input.getAttribute('colorspace');
1599 let nodeDefType = nodeDef.getType();
1600 if (_type == 'filename' && (nodeDefType == 'color3' || nodeDefType == 'color4'))
1601 {
1602 if (colorspace.length == 0)
1603 {
1604 colorspace = 'none';
1605 }
1606 }
1607 if (colorspace.length > 0)
1608 metaData['colorspace'] = colorspace;
1609
1610 // TODO: Add units, unitype on nodedefs. There is no
1611 // default unittype or units.
1612 let unitAttributes = ['unit', 'unittype'];
1613 for (let unitAttribute of unitAttributes)
1614 {
1615 let value = input.getAttribute(unitAttribute);
1616 if (value.length > 0)
1617 {
1618 metaData[unitAttribute] = value;
1619 }
1620 }
1621
1622 // Add in defaultgeomprop to denote geometric inputs.
1623 let defaultgeomprop = input.getAttribute('defaultgeomprop')
1624 metaData['defaultgeomprop'] = defaultgeomprop;
1625
1626 // Add enumerations
1627 let enums = input.getAttribute('enum');
1628 if (enums.length > 0)
1629 {
1630 metaData['enum'] = enums.split(',');
1631 metaData['enum'].map(function(x) { return x.trim(); });
1632 }
1633 let enumvalues = input.getAttribute('enumvalues');
1634 if (enumvalues.length > 0)
1635 {
1636 metaData['enumvalues'] = enumvalues.split(',');
1637 metaData['enumvalues'].map(function(x) { return x.trim(); });
1638 }
1639
1640 metaData = JSON.stringify(metaData);
1641 definition_code += " this.addProperty('" + _name + "', " + value + ", '" + _type + "'," + metaData + ");\n";
1642 }
1643 for (var output of nodeDef.getActiveOutputs()) {
1644 var _name = output.getName();
1645 var _type = output.getType();
1646 if (_type in TMAP)
1647 _type = TMAP[_type];
1648 else
1649 console.log('Unmappable type:', _type)
1650 //if(_type && _type.constructor === String)
1651 // _type = '"'+_type+'"';
1652 definition_code += " this.addOutput('" + _name + "','" + _type + "');\n";
1653 }
1654
1655 definition_code += " this.title = '" + titleName + "';\n"
1656 var desc = '"MaterialX:' + id + '"';
1657 definition_code += " this.desc = " + desc + ";\n";
1658
1659 //definition_code += " /**\n";
1660 //definition_code += " * @function " + "onNodeCreated" + "\n";
1661 //definition_code += " */";
1662
1663 var onNodeCreated = "function() {\n";
1664 onNodeCreated += " //console.log('Node created:', this);\n";
1665 onNodeCreated += "}";
1666 definition_code += " this.onNodeCreated = " + onNodeCreated + "\n";
1667 var onRemoved = "function() {\n";
1668 onRemoved += " //console.log('Node removed:', this);\n";
1669 onRemoved += " }";
1670 definition_code += " this.onRemoved = " + onRemoved + "\n";
1671
1672 let monitor = editor.monitor;
1673 var onPropertyChanged = "function(name, value, prev_value) {\n";
1674 if (monitor)
1675 {
1676 onPropertyChanged += " MxShadingGraphEditor.theEditor.monitor.onPropertyChanged(this.title, name, value, prev_value, this);\n";
1677 }
1678 else
1679 {
1680 onPropertyChanged += " console.log('+ Internal property changed:', this.title, name, value, prev_value, this);\n";
1681 }
1682 onPropertyChanged += " }";
1683 definition_code += " this.onPropertyChanged = " + onPropertyChanged + "\n";
1684
1685 var onPropertyInfoChanged = "function(name, info, value, prev_value) {\n";
1686 if (monitor)
1687 {
1688 onPropertyInfoChanged += " MxShadingGraphEditor.theEditor.monitor.onPropertyInfoChanged(this.title, name, info, value, prev_value, this);\n";
1689 }
1690 else
1691 {
1692 onPropertyInfoChanged += " console.log('+ Internal property info changed:', this.title, name, info, value, prev_value, this);\n";
1693 }
1694 onPropertyInfoChanged += " }"
1695 definition_code += " this.onPropertyInfoChanged = " + onPropertyInfoChanged + "\n";
1696
1697 // Output connection callback
1698 var onConnectOutput = "function(slot, input_type, input, target_node, target_slot) {\n";
1699 if (monitor)
1700 {
1701 onConnectOutput += " MxShadingGraphEditor.theEditor.monitor.onConnectOutput(slot, input_type, input, target_node, target_slot, this);\n";
1702 }
1703 else
1704 {
1705 onConnectOutput += " console.log('+ Output connection changed:', this.title, slot, input_type, input, target_node, target_slot);\n";
1706 }
1707 onConnectOutput += " }"
1708 definition_code += " this.onConnectOutput = " + onConnectOutput + "\n";
1709
1710 // Input connection callback
1711 var onConnectInput = "function(target_slot, output_type, output, source, slot) {\n";
1712 if (monitor)
1713 {
1714 onConnectInput += " MxShadingGraphEditor.theEditor.monitor.onConnectInput(target_slot, output_type, output, source, slot, this);\n";
1715 }
1716 else
1717 {
1718 onConnectInput += " console.log('+ Input connection changed:', this.title, target_slot, output_type, output, source, slot);\n";
1719 }
1720 onConnectInput += " }"
1721 definition_code += " this.onConnectInput = " + onConnectInput + "\n";
1722
1723 // Set the background color to slate grey
1724 definition_code += " this.bgcolor = '#111';\n";
1725 //console.log('Node group:', nodeGroup, nodeDefName);
1726 if (nodeGroup == 'conditional') {
1727 //console.log('Cond Node group:', nodeGroup)
1728 definition_code += " this.color = '#532200';\n";
1729 definition_code += " this.title_text_color = '#000';\n";
1730 definition_code += " this.shape = LiteGraph.CARD_SHAPE;\n";
1731 }
1732
1733 else if (nodeString != 'convert' &&
1734 (nodeGroup == 'shader' || nodeType == 'surfaceshader' || nodeType == 'volumshader' || nodeType == 'displacementshader')) {
1735 definition_code += " this.color = '#232';\n";
1736 definition_code += " this.shape = LiteGraph.ROUND_SHAPE;\n";
1737 }
1738 else if (nodeGroup == 'material') {
1739 definition_code += " this.color = '#151';\n";
1740 definition_code += " this.shape = LiteGraph.BOX_SHAPE;\n";
1741 }
1742 else {
1743 definition_code += " this.color = '#222';\n";
1744 definition_code += " this.shape = LiteGraph.ROUND_SHAPE;\n";
1745 }
1746 if (nodeType in CMAP) {
1747 definition_code += " this.boxcolor = '" + CMAP[nodeType] + "';\n";
1748 }
1749 }
1750 definition_code += "}\n"
1751
1752 // Register the node type
1753 definition_code += functionName + ".nodedef_name = '" + nodeDefName + "';\n";
1754 definition_code += functionName + ".nodedef_node = '" + nodeString + "';\n";
1755 definition_code += functionName + ".nodedef_href = 'https://kwokcb.github.io/MaterialX_Learn/documents/definitions/" + nodeString + ".html';\n";
1756
1757 definition_code += "LiteGraph.registerNodeType(" + "'" + id + "'," + functionName + ");\n";
1758 definitionsList.push(id);
1759 if (debug)
1760 definition_code += "console.log('Registered node type:', '" + id + "');\n";
1761 }
1762
1763 //definition_code += "}\n";
1764 return definition_code;
1765 }
getDefaultValue(value, _type)
Get default value as a string for the given value and type.
buildMetaData(colorSpace, unit, unitType, uiname, uimin, uimax, uifolder, _type)
Builds and returns metadata for a node based on the provided parameters.

◆ createValidName()

MxMaterialXHandler::createValidName ( name,
msg = null )

Create a valid MaterialX name within the context of the current graph.

Parameters
name- The name to validate.
msg- The message to display if the name is invalid.
Returns
The valid name.

Definition at line 3149 of file JsMaterialXNodeEditor.js.

3149 {
3150 if (name.length == 0) {
3151 if (msg) {
3152 msg = 'Setting empty name as "blank"';
3153 }
3154 name = "blank";
3155 }
3156
3157 // Get list of all names in graph.
3158 var graph = graphcanvas.graph;
3159 var nodes = graph._nodes;
3160 var nodenames = [];
3161 for (var node of nodes) {
3162 nodenames.push(node.title);
3163 }
3164 //console.log('Current graph nodes:', nodenames);
3165
3166 name = ne_mx.createValidName(name);
3167
3168 if (!nodenames.includes(name)) {
3169 return name;
3170 }
3171
3172 // Get starting number and root name
3173 var rootName = name;
3174 var i = 1;
3175 var number = name.match(/\d+$/);
3176 if (number) {
3177 i = (parseInt(number) + 1)
3178 rootName = name.slice(0, -number[0].length);
3179 }
3180
3181 var valid_name = rootName + i.toString();
3182 while (nodenames.includes(valid_name)) {
3183 i++;
3184 valid_name = rootName + i.toString();
3185 }
3186 return valid_name;
3187 }

◆ findRenderableItems()

MxMaterialXHandler::findRenderableItems ( graph)

Find all MaterialX renderable items in a graph.

Parameters
graph- The graph to scan.
Returns
An array of MaterialX renderable items found in the graph.

Definition at line 1094 of file JsMaterialXNodeEditor.js.

1094 {
1095
1096 let graphWriteOptions = { writeCustomLibs : false, saveNodePositions: false, writeOutputs : true };
1097 let mdoc = this.saveGraphToDocument(graph, graphWriteOptions);
1098 if (!mdoc) {
1099 console.log('Failed to save graph to document');
1100 return;
1101 }
1102 return this.findRenderableItemsInDoc(mdoc);
1103 }
saveGraphToDocument(graph, graphWriteOptions)
Saves the graph to a MaterialX document.
findRenderableItemsInDoc(mdoc)
Find all renderable items in the MaterialX document.

◆ findRenderableItemsInDoc()

MxMaterialXHandler::findRenderableItemsInDoc ( mdoc)

Find all renderable items in the MaterialX document.

Parameters
mdoc- The MaterialX document to scan.
Returns
An array of renderable items found in the document.

Definition at line 1111 of file JsMaterialXNodeEditor.js.

1111 {
1112
1113 const materialNodes = mdoc.getMaterialNodes();
1114 let shaderList = [];
1115 let renderableItems = [];
1116
1117 for (let i = 0; i < materialNodes.length; ++i) {
1118 let materialNode = materialNodes[i];
1119 if (materialNode) {
1120 //console.log('Scan material: ', materialNode.getNamePath());
1121 let shaderNodes = ne_mx.getShaderNodes(materialNode)
1122 if (shaderNodes.length > 0) {
1123 let shaderNodePath = shaderNodes[0].getNamePath()
1124 if (!shaderList.includes(shaderNodePath)) {
1125 //console.log('-- add shader: ', shaderNodePath);
1126 shaderList.push(shaderNodePath);
1127 renderableItems.push(shaderNodePath);
1128 }
1129 }
1130 }
1131 }
1132 const nodeGraphs = mdoc.getNodeGraphs();
1133 for (let i = 0; i < nodeGraphs.length; ++i) {
1134 let nodeGraph = nodeGraphs[i];
1135 if (nodeGraph) {
1136 if (nodeGraph.hasAttribute('nodedef') || nodeGraph.hasSourceUri()) {
1137 continue;
1138 }
1139 // Skip any nodegraph that is connected to something downstream
1140 if (nodeGraph.getDownstreamPorts().length > 0) {
1141 continue
1142 }
1143 let outputs = nodeGraph.getOutputs();
1144 for (let j = 0; j < outputs.length; ++j) {
1145 let output = outputs[j];
1146 {
1147 renderableItems.push(output.getNamePath());
1148 }
1149 }
1150 }
1151 }
1152 const outputs = mdoc.getOutputs();
1153 for (let i = 0; i < outputs.length; ++i) {
1154 let output = outputs[i];
1155 if (output) {
1156 renderableItems.push(output.getNamePath());
1157 }
1158 }
1159
1160 return renderableItems;
1161 }

◆ initialize()

MxMaterialXHandler::initialize ( editor,
materialFilename )

Initialize the MaterialX handler for the given editor.

Initializes MaterialX and sets up the MaterialX document and standard libraries. If a default MaterialX document is provided, it will be loaded into the editor. The standard libraries are scanned for colorspaces and units, which are cached for later usage.

Parameters
editor- The editor instance
materialFilename- The filename of the default MaterialX document to graph

Definition at line 1000 of file JsMaterialXNodeEditor.js.

1000 {
1001 super.initialize(editor);
1002
1003 if (!ne_mx) {
1004
1005 this.loadMaterialX().then(() => {
1006
1007 // Additional logic after MaterialX is loaded
1008 editor.debugOutput("Loaded MaterialX version:" + ne_mx.getVersionString(), 0, true);
1009 doc = ne_mx.createDocument();
1010
1011 var generator = new ne_mx.EsslShaderGenerator.create();
1012 var genContext = new ne_mx.GenContext(generator);
1013 stdlib = ne_mx.loadStandardLibraries(genContext);
1014 editor.debugOutput('Loaded standard libraries definitions:' + stdlib.getNodeDefs().length, 0, false);
1015
1016 // Find units, unittype pairs available. Keep in unique list.
1017 let units = new Map();
1018 for (let ud of stdlib.getUnitDefs()) {
1019 let unittype = ud.getAttribute('unittype')
1020 for (let unit of ud.getChildren()) {
1021 units.set(unit.getName(), unittype);
1022 }
1023 }
1024 // Sort units
1025 this.setUnits(Array.from(units).sort());
1026 console.log('> Setup real-world units: ', this.getUnits());
1027
1028 // Find the colorspaces available. Keep in unique list. Hack as there
1029 // is no way to find a list of colorspaces.
1030 let colorSpaces = new Set();
1031 let docNodeDefs = stdlib.getNodeDefs();
1032 for (let i = 0; i < docNodeDefs.length; i++) {
1033 let cmnode = docNodeDefs[i];
1034 if (cmnode.getNodeGroup() === 'colortransform') {
1035 let name = cmnode.getName();
1036 name = name.replace('ND_', '');
1037 let namesplit = name.split('_to_');
1038 let type = 'color3';
1039 if (namesplit[1].includes('color4')) {
1040 namesplit[1] = namesplit[1].replace('_color4', '');
1041 type = 'color4';
1042 } else {
1043 namesplit[1] = namesplit[1].replace('_color3', '');
1044 }
1045 colorSpaces.add(namesplit[0]);
1046 colorSpaces.add(namesplit[1]);
1047 }
1048 }
1049 // Sort the colorspaces
1050 this.setColorSpaces(Array.from(colorSpaces).sort())
1051 console.log('Set up colorspaces: ', this.getColorSpaces());
1052
1053 var definitionsList = [];
1054 var result = this.createLiteGraphDefinitions(stdlib, false, true, definitionsList, 'mtlx', MxShadingGraphEditor.theEditor);
1055 var definitionsDisplayUpdater = editor.ui.definitionsDisplayUpdater;
1056 if (definitionsDisplayUpdater) {
1057 definitionsDisplayUpdater(result);
1058 }
1059
1060 editor.clearNodeTypes();
1061 try {
1062 eval(result);
1063 } catch (e) {
1064 editor.debugOutput('Error evaluating source: ' + e, 2, false);
1065 }
1066
1067 var nodeTypes = LiteGraph.registered_node_types;
1068 var i = 0;
1069 for (var typeName in nodeTypes) {
1070 i++;
1071 }
1072 editor.debugOutput("Registered node types:" + definitionsList.length, 0, false);
1073
1074 editor.displayNodeTypes();
1075
1076 if (materialFilename.length > 0) {
1077 this.loadLibraryDocument(editor, materialFilename);
1078 }
1079
1080 editor.updatePropertyPanel(null);
1081
1082 }).catch((error) => {
1083 editor.debugOutput("Error on initialization:" + error, 2);
1084 });
1085 }
1086 }
getColorSpaces()
Get the color spaces used by the handler.
getUnits()
Get the units used by the handler.
setColorSpaces(colorSpaces)
Set the color spaces used by the handler.
setUnits(units)
Set the units used by the handler.
createLiteGraphDefinitions(doc, debug, addInputOutputs, definitionsList, libraryPrefix='mtlx', editor, icon='')
Creates LiteGraph node definitions based on the MaterialX document.
loadMaterialX()
Load in the MaterialX library.
This class is a wrapper around the LiteGraph library to provide a MaterialX node editor.

◆ isArray()

MxMaterialXHandler::isArray ( _type)

Determines if the specified type is an array type.

Parameters
_type- The type to check.
Returns
True if the type is an array type, false otherwise.

Definition at line 2231 of file JsMaterialXNodeEditor.js.

2231 {
2232 var ARRAY_TYPES = ['color3', 'color4', 'vector2', 'vector3', 'vector4', 'matrix33', 'matrix44'];
2233 if (ARRAY_TYPES.includes(_type)) {
2234 return true;
2235 }
2236 return false;
2237 }

◆ isURI()

MxMaterialXHandler::isURI ( s)

Definition at line 2243 of file JsMaterialXNodeEditor.js.

2243 {
2244 // Check if the string starts with common URI schemes
2245 const uriSchemes = ['http://', 'https://', 'ftp://', 'blob:', 'file://', 'data:'];
2246 return uriSchemes.some(scheme => s.startsWith(scheme));
2247 }

◆ loadDefinitionsFromFile()

MxMaterialXHandler::loadDefinitionsFromFile ( )

Load MaterialX document containing node definitions from a file.

Returns
{void}

Definition at line 2797 of file JsMaterialXNodeEditor.js.

2797 {
2798 var that = this;
2799
2800 // Load mtlx document from disk
2801 var input = document.createElement("input");
2802 input.style = this.fontSizeStyle;
2803 input.type = "file";
2804 input.accept = ".mtlx";
2805 input.onchange = function (e) {
2806 var file = e.target.files[0];
2807 console.log('Loading definitions from file: ' + file.name);
2808
2809 if (ne_mx) {
2810 // Load the content from the specified file (replace this with actual loading logic)
2811
2812 const reader = new FileReader();
2813 reader.readAsText(file, 'UTF-8');
2814
2815 reader.onload = function (e) {
2816 // Display the contents of the file in the output div
2817 let fileContents = e.target.result;
2818 //console.log(fileContents);
2819
2820 (async () => {
2821 try {
2822 const readOptions = new ne_mx.XmlReadOptions();
2823 readOptions.readXIncludes = false;
2824 var customLib = ne_mx.createDocument();
2825
2826 await ne_mx.readFromXmlString(customLib, fileContents, '', readOptions);
2827
2828 // Create JS from custom library
2829 try {
2830 console.log('Create custom library definitions', ne_mx.prettyPrint(customLib));
2831 var iconName = '';
2832 var scanForIcon = false;
2833 if (scanForIcon) {
2834 // Icon name is filename with webp as extension
2835 var iconName = file.name.replace(/\.[^/.]+$/, ".webp");
2836 // Check if iconName file exists
2837 var iconExists = await that.editor.uriExists(iconName);
2838 if (!iconExists) {
2839 iconName = '';
2840 }
2841 }
2842 var definitionsList = [];
2843 var result = that.createLiteGraphDefinitions(customLib, false, false, definitionsList, 'mtlx', that.editor, iconName);
2844 if (result) {
2845 eval(result);
2846 var definitionsListString = definitionsList.join(', ');
2847 that.editor.debugOutput("Registered custom node types: [" + definitionsListString + "]", 0, false);
2848 that.editor.displayNodeTypes();
2849 }
2850 } catch (e) {
2851 console.log('Error evaluating source:', e);
2852 }
2853
2854
2855 // Keep track of libraries loaded by filename.
2856 customlibs.push([file.name, customLib]);
2857
2858 } catch (error) {
2859 that.editor.debugOutput('Error reading definitions:' + error, 2, false);
2860 }
2861 })();
2862
2863 };
2864
2865 } else {
2866 that.editor.debugOutput("MaterialX is not initialized", 2);
2867 }
2868
2869 //customlibs
2870 };
2871 input.click();
2872 }
var customlibs

◆ loadFromFile()

MxMaterialXHandler::loadFromFile ( extension,
file,
fileName,
editor,
auto_arrange )

Load graph editor from a file.

Parameters
extension- The extension indicating the format of the file.
file- The file to load the graph from.
fileName- The identifier for the source file, or a arbitrary name.
editor- The graph editor.
auto_arrange- True to auto-arrange the graph, false otherwise.
Returns
{void}

Definition at line 3041 of file JsMaterialXNodeEditor.js.

3041 {
3042 var debug = false;
3043
3044 if (ne_mx) {
3045 if (!this.loadMaterialXLibraries(stdlib))
3046 return;
3047
3048 // Load the content from the specified file (replace this with actual loading logic)
3049
3050 const reader = new FileReader();
3051 reader.readAsText(file, 'UTF-8');
3052 reader.accept = '.mtlx';
3053
3054 var that = this;
3055 console.log('loadFromFile:', file, fileName);
3056 try {
3057 reader.onload = function (e) {
3058 // Display the contents of the file in the output div
3059 let fileContents = e.target.result;
3060 console.log("read file: ", file.name, " with extension: ", extension, " and length: ", fileContents.length);
3061
3062 that.loadFromString('mtlx', fileContents, fileName, auto_arrange, true);
3063 };
3064 } catch (error) {
3065 MxShadingGraphEditor.theEditor.debugOutput('Error reading document: ' + fileName + '. Error: ' + error, 2, false);
3066 }
3067
3068 } else {
3069 editor.debugOutput("MaterialX is not initialized", 2, false);
3070 }
3071 }
loadMaterialXLibraries(stdlib)
Load MaterialX definition libraries.

◆ loadFromString()

MxMaterialXHandler::loadFromString ( extension,
fileContents,
fileName,
auto_arrange,
rerender = false )

Load graph editor from a string.

Parameters
extension- The extension indicating the format of the string.
fileContents- The document string.
fileName- The identifier for the source file, or a arbitrary name.
auto_arrange- True to auto-arrange the graph, false otherwise.
rerender- True to re-render the scene after loading, false otherwise.
Returns
{void}

Definition at line 2884 of file JsMaterialXNodeEditor.js.

2884 {
2885 if (!ne_mx) {
2886 console.log('MaterialX is not initialized');
2887 return;
2888 }
2889
2890 // Check if we need to pre-convert from extension type to mtlx
2891 if (extension != 'mtlx')
2892 {
2893 let converter = this.getImporter(extension);
2894 if (converter) {
2895 let result = converter.import(ne_mx, fileContents, stdlib);
2896 if (result) {
2897 fileContents = result[0];
2898 }
2899 else {
2900 console.log('Failed to convert from:', extension, 'to mtlx. Errors:', result[1]);
2901 return;
2902 }
2903 }
2904 else
2905 {
2906 console.log('Failed to find converter from:', extension, 'to mtlx.');
2907 return;
2908 }
2909 }
2910
2911 (async () => {
2912 try {
2913 const readOptions = new ne_mx.XmlReadOptions();
2914 readOptions.readXIncludes = false;
2915
2916 doc.clearContent();
2917
2918 doc.importLibrary(stdlib);
2919 for (var item of customlibs) {
2920 console.log('Import custom library:', item[0]);
2921 doc.importLibrary(item[1]);
2922 }
2923 var loadDoc = ne_mx.createDocument();
2924 await ne_mx.readFromXmlString(loadDoc, fileContents, '', readOptions);
2925
2926 // Check if nodedef is not in existingDefs
2927 //
2928 var customLib = ne_mx.createDocument();
2929 customLib.copyContentFrom(loadDoc);
2930 var keepChildren = [];
2931 var existingDefs = []
2932 var saveCustomLib = false;
2933 doc.getNodeDefs().forEach(def => { existingDefs.push(def.getName()); });
2934 for (var nodedef of loadDoc.getNodeDefs()) {
2935 var nodedefName = nodedef.getName();
2936 if (!existingDefs.includes(nodedefName)) {
2937 keepChildren.push(nodedef.getName());
2938 saveCustomLib = true;
2939 }
2940 }
2941 for (var ng of loadDoc.getNodeGraphs()) {
2942 if (ng.getAttribute('nodedef') && ng.getAttribute('nodedef').length > 0) {
2943 saveCustomLib = true;
2944 keepChildren.push(ng.getName());
2945 }
2946 }
2947
2948 if (saveCustomLib) {
2949
2950 for (var child of customLib.getChildren()) {
2951 if (!keepChildren.includes(child.getName())) {
2952 //console.log('Remove child:', child.getName());
2953 customLib.removeChild(child.getName());
2954 }
2955 }
2956
2957 var additionDefs = [];
2958 console.log('Create custom library definitions from addtionDefs:',
2959 ne_mx.prettyPrint(customLib));
2960
2961 var result = this.createLiteGraphDefinitions(customLib, true, false, additionDefs, 'mtlx', MxShadingGraphEditor.theEditor);
2962 try {
2963 eval(result);
2964 console.log('Loaded local definitions: ', additionDefs);
2965 } catch (e) {
2966 console.log('Error evaluating source:', e);
2967 }
2968 }
2969
2970 doc.copyContentFrom(loadDoc);
2971
2972 this.validateDocument(doc);
2973
2974 this.buildGraphFromDoc(doc, MxShadingGraphEditor.theEditor, auto_arrange);
2975
2976 // Must do this after build as build will clear customDocLibs array
2977 if (saveCustomLib) {
2978 customDocLibs.push([fileName, customLib]);
2979 }
2980
2981 // Get the document's colorspace and set it as the active colorspace
2982 var documentColorSpace = doc.getColorSpace();
2983 this.setSourceColorSpace(documentColorSpace);
2984 documentColorSpace = this.getSourceColorSpace();
2985 MxShadingGraphEditor.theEditor.updatePropertyPanel(null);
2986
2987 // Cleanup document, and get up-to-date contents after any possible upgrade.
2988 loadDoc.removeAttribute('fileprefix');
2989 fileContents = ne_mx.writeToXmlString(loadDoc);
2990
2991 this.validateDocument(loadDoc);
2992
2993 MxShadingGraphEditor.theEditor.debugOutput('Loaded document: "' + fileName + '"', 0, false);
2994
2995 // Update mtlx text area
2996 let documentDisplayUpdater = MxShadingGraphEditor.theEditor.ui.documentDisplayUpdater;
2997 if (documentDisplayUpdater) {
2998 documentDisplayUpdater(fileContents);
2999 }
3000 else {
3001 console.log('No docDisplayUpdater!!!');
3002 }
3003
3004 // Update render items in UI
3005 let renderableItemUpdater = MxShadingGraphEditor.theEditor.ui.renderableItemUpdater;
3006 if (renderableItemUpdater) {
3007 let renderableItems = this.findRenderableItemsInDoc(doc);
3008 if (!renderableItems || renderableItems.length == 0) {
3009 MxShadingGraphEditor.theEditor.debugOutput('No renderable items found in graph: ' + fileName, 1, false);
3010 }
3011 renderableItemUpdater(renderableItems);
3012 }
3013
3014 //
3015
3016 if (rerender)
3017 {
3018 console.log('> Update rendering from document');
3019 let theRenderer = this.editor.monitor.renderer;
3020 //console.log(this.editor.monitor, this.editor.monitor.renderer)
3021 if (theRenderer)
3022 theRenderer.updateMaterialFromText(fileContents);
3023 }
3024
3025 } catch (error) {
3026 MxShadingGraphEditor.theEditor.debugOutput('Error reading document: ' + fileName + '. Error: ' + error, 2, false);
3027 }
3028 })();
3029 }
var customDocLibs
setSourceColorSpace(colorSpace)
Set the source color space for the handler.
getImporter(extension='')
Find the first importer that can import the given extension / format.
getSourceColorSpace()
Get the source color space for the handler.
validateDocument(doc)
Validates the provided MaterialX document.
buildGraphFromDoc(doc, editor, auto_arrange)
Builds the LiteGraph graph from the specified MaterialX document.

◆ loadFromZip()

MxMaterialXHandler::loadFromZip ( extension,
file,
fileName,
editor,
auto_arrange,
rerender = false )

Definition at line 3073 of file JsMaterialXNodeEditor.js.

3074 {
3075 if (ne_mx) {
3076 // Load the content from the specified file (replace this with actual loading logic)
3077 console.log("Loading content from zip:", file.name);
3078
3079 const reader = new FileReader();
3080
3081 var that = this;
3082 reader.onload = async function (e) {
3083 try {
3084 const zipData = new Uint8Array(e.target.result); // Convert to Uint8Array
3085 const result = await MxZipUtils.unzipMaterialXData(zipData);
3086 let documents = result[0];
3087 let docText = documents[0].content;
3088 let docFile = documents[0].name;
3089 console.log('Documents:', docText);
3090
3091 let textures = result[1];
3092 for (let i = 0; i < textures.length; i++) {
3093 const url = textures[i].url;
3094 const textureName = textures[i].name;
3095 // Replace fileName with url in docText.
3096 // Do not care about case sensitivity since some examples
3097 // from GPUOpen have the incorrect case.
3098 docText = docText.replace(new RegExp(textureName, 'i'), url);
3099 console.log('Replace reference:' + textureName + ' with blob: ' + url);
3100
3101 }
3102 that.loadFromString('mtlx', docText, docFile, auto_arrange, rerender);
3103
3104 } catch (error) {
3105 //editor.debugOutput("MaterialX is not initialized", 2, false);
3106 console.error('Error loading ZIP file:', error);
3107 }
3108 };
3109
3110 reader.readAsArrayBuffer(file);
3111
3112 } else {
3113 console.error("MaterialX is not initialized");
3114 }
3115 }
Utility class for unzipping ZIP which contain MaterialX files and dependent images .
static async unzipMaterialXData(zipData)
Unzips the given ZIP data and returns the MaterialX documents and textures.

◆ loadInputMetaData()

MxMaterialXHandler::loadInputMetaData ( node,
input,
property_info )

Set the meta-data for the specified input based on LiteGraph node property info.

Parameters
node- The MaterialX node parent of the input. If parent is a nodegraph then null is passed in.
input- The input to load the meta-data for.
property_info- The property info object to load the meta-data into.
Returns
{void}

Definition at line 2405 of file JsMaterialXNodeEditor.js.

2405 {
2406 if (input && property_info) {
2407
2408 // Load in basic meta-data
2409 var colorspace = input.getColorSpace();
2410 if (colorspace.length > 0)
2411 property_info['colorspace'] = colorspace;
2412
2413 var unit = input.getUnit();
2414 if (unit.length > 0)
2415 property_info['unit'] = unit;
2416
2417 var uiname = input.getAttribute('uiname');
2418 if (uiname.length > 0)
2419 property_info['uiname'] = uiname;
2420
2421 var uimin = input.getAttribute('uimin');
2422 if (uimin.length > 0)
2423 property_info['uimin'] = uimin;
2424
2425 var uimax = input.getAttribute('uimax');
2426 if (uimax.length > 0)
2427 property_info['uimax'] = uimax;
2428 var uisoftmin = input.getAttribute('uisoftmin');
2429 if (uisoftmin.length > 0)
2430 property_info['uimin'] = uisoftmin;
2431
2432 var uisoftmax = input.getAttribute('uisoftmax');
2433 if (uisoftmax.length > 0)
2434 property_info['uimax'] = uisoftmax;
2435
2436 var uifolder = input.getAttribute('uifolder');
2437 if (uifolder.length > 0)
2438 property_info['uifolder'] = uifolder;
2439
2440 var basicMetaData = ['colorspace', 'unit', 'uiname', 'uimin', 'uimax', 'uifolder', 'name', 'type', 'output', 'nodename', 'nodegraph'];
2441 for (var attrName of input.getAttributeNames()) {
2442 if (!basicMetaData.includes(attrName)) {
2443 property_info[attrName] = input.getAttribute(attrName);
2444 }
2445 }
2446
2447 if (node && input.getType() == 'filename')
2448 {
2449 let nodeType = node.getType();
2450 let colorTypes = ['color3', 'color4'];
2451 //console.log('Load input metadata for:', input.getName(), input.getType(), property_info, nodeType);
2452 if (colorTypes.includes(nodeType))
2453 {
2454 if (!property_info['colorspace']) {
2455 console.log('Auto create "none" colorspace for input:', input.getName());
2456 let doc = node.getDocument();
2457 let defaultColorSpace = 'none';
2458 // For now don't use the document color space as 'none' is more flexible.
2459 //let docColorSpace = doc.getAttribute('colorspace');
2460 //if (docColorSpace.length > 0)
2461 // defaultColorSpace = docColorSpace;
2462 property_info['colorspace'] = defaultColorSpace;
2463 }
2464 }
2465 }
2466
2467 //console.log('load input metadata for:', input.getNamePath(), property_info);
2468 }
2469 }

◆ loadLibraryDocument()

MxMaterialXHandler::loadLibraryDocument ( editor,
materialFilename )

Load the MaterialX document from library into the editor.

Parameters
editor- The editor instance.
materialFilename- The filename of the library MaterialX document to graph.

Definition at line 968 of file JsMaterialXNodeEditor.js.

968 {
969
970 function loadInitialText(filePath, handler) {
971 try {
972 fetch(filePath)
973 .then(response => response.blob())
974 .then(blob => {
975 const reader = new FileReader();
976 reader.onload = function (e) {
977 console.log('Loaded document:', filePath);
978 editor.loadGraphFromString('mtlx', e.target.result, filePath, 80, true);
979 }
980 reader.readAsText(blob);
981 })
982 } catch (error) {
983 console.error('Error loading file %s:' % filePath, error);
984 }
985 }
986
987 loadInitialText(materialFilename, this);
988 }

◆ loadMaterialX()

MxMaterialXHandler::loadMaterialX ( )

Load in the MaterialX library.

Sets the global ne_mx variable to the MaterialX instance.

Returns
{Promise} A promise that resolves when the MaterialX library is loaded.

Definition at line 950 of file JsMaterialXNodeEditor.js.

950 {
951 return new Promise((resolve, reject) => {
952 MaterialX().then((ne_mtlx) => {
953 // Save the MaterialX instance to the global variable
954 ne_mx = ne_mtlx;
955 resolve();
956 }).catch((error) => {
957 reject(error);
958 });
959 });
960 }

◆ loadMaterialXLibraries()

MxMaterialXHandler::loadMaterialXLibraries ( stdlib)

Load MaterialX definition libraries.

Returns
The MaterialX document containing the standard libraries.

Definition at line 3122 of file JsMaterialXNodeEditor.js.

3123 {
3124 if (stdlib)
3125 return stdlib;
3126
3127 if (!ne_mx) {
3128 MxShadingGraphEditor.theEditor.debugOutput("MaterialX is not initialized", 2);
3129 return null;
3130 }
3131
3132 var generator = new ne_mx.EsslShaderGenerator.create();
3133 var genContext = new ne_mx.GenContext(generator);
3134 {
3135 stdlib = ne_mx.loadStandardLibraries(genContext);
3136 console.log('Loaded standard libraries:', stdlib.getNodeDefs().length);
3137 }
3138
3139 return stdlib;
3140 }

◆ saveGraphToDocument()

MxMaterialXHandler::saveGraphToDocument ( graph,
graphWriteOptions )

Saves the graph to a MaterialX document.

Parameters
graph- The graph to save.
graphWriteOptions- Options to use when writing the graph.
Returns
The MaterialX document containing the graph.

Definition at line 1799 of file JsMaterialXNodeEditor.js.

1799 {
1800
1801 if (!ne_mx) {
1802 this.editor.debugOutput("MaterialX is not initialized", 2);
1803 return;
1804 }
1805
1806 let writeCustomLibs = graphWriteOptions.writeCustomLibs;
1807 if (writeCustomLibs == undefined)
1808 {
1809 console.error('Graph output option: writeCustomLibs is undefined.')
1810 writeCustomLibs = true;
1811 }
1812
1813 var outputDoc = ne_mx.createDocument();
1814
1815 if (!stdlib) {
1816 var generator = new ne_mx.EsslShaderGenerator.create();
1817 var genContext = new ne_mx.GenContext(generator);
1818 stdlib = ne_mx.loadStandardLibraries(genContext);
1819 }
1820
1821 // Handle top level
1822 this.writeGraphToDocument(outputDoc, graph, graphWriteOptions);
1823
1824 let doc_string = ne_mx.writeToXmlString(outputDoc);
1825 //console.log(doc_string);
1826 //console.log('-----------------------------------------------')
1827
1828 if (writeCustomLibs) {
1829 console.log('Write custom libraries:', customlibs.length);
1830 for (var customlib of customlibs) {
1831 outputDoc.copyContentFrom(customlib[1]);
1832 }
1833 console.log('Write document custom definitions:', customDocLibs.length);
1834 for (var customDocLib of customDocLibs) {
1835 outputDoc.copyContentFrom(customDocLib[1]);
1836 }
1837 }
1838
1839 // TODO: Add in other globals
1840 outputDoc.setColorSpace(this.getSourceColorSpace());
1841 outputDoc.removeAttribute('fileprefix');
1842
1843 this.validateDocument(outputDoc);
1844
1845 return outputDoc;
1846 }
writeGraphToDocument(mltxgraph, graph, graphWriteOptions)
Writes the graph to the specified MaterialX document.

◆ saveGraphToFile()

MxMaterialXHandler::saveGraphToFile ( extension,
graph,
graphWriteOptions )

Saves the graph to a file with the specified extension.

Parameters
extension- The file extension to save the graph as.
graph- The graph to save.
graphWriteOptions- Options to use when writing the graph.
Returns
{void}

Definition at line 1909 of file JsMaterialXNodeEditor.js.

1910 {
1911 var data = this.saveGraphToString(extension, graph, graphWriteOptions);
1912 if (!data[0]) {
1913 return;
1914 }
1915
1916 var blob = new Blob([data[0]], { type: "text/plain" });
1917 var url = URL.createObjectURL(blob);
1918 var a = document.createElement("a");
1919 a.href = url;
1920 a.download = "output_graph.mtlx";
1921 a.click();
1922 }
saveGraphToString(extension, graph, graphWriteOptions)
Saves the graph to a string in the specified format.

◆ saveGraphToString()

MxMaterialXHandler::saveGraphToString ( extension,
graph,
graphWriteOptions )

Saves the graph to a string in the specified format.

Parameters
extension- The file extension to save the graph as.
graph- The graph to save.
graphWriteOptions- Options to use when writing the graph.
Returns
An array containing the data string and any error messages.

Definition at line 1856 of file JsMaterialXNodeEditor.js.

1856 {
1857
1858 if (!ne_mx) {
1859 this.editor.debugOutput("MaterialX is not initialized", 2);
1860 return ['', 'MaterialX is not initialized'];
1861 }
1862
1863 var outputDoc = this.saveGraphToDocument(graph, graphWriteOptions);
1864 if (!outputDoc) {
1865 this.editor.debugOutput("Failed to save graph to document", 2);
1866 return ['', 'Failed to save graph to document'];
1867 }
1868
1869 if (extension == 'mtlx')
1870 {
1871 const writeOptions = new ne_mx.XmlWriteOptions();
1872 writeOptions.writeXIncludeEnable = false;
1873 var data = '';
1874 try {
1875 data = ne_mx.writeToXmlString(outputDoc, writeOptions);
1876 } catch (e) {
1877 this.editor.debugOutput("Failed to write graph:" + e, 2);
1878 }
1879 return [data, ''];
1880 }
1881
1882 // Look for a registered exporter
1883 else
1884 {
1885 let exporter = this.getExporter(extension);
1886 if (!exporter) {
1887 this.editor.debugOutput('Failed to find ' + extension + ' exporter', 2);
1888 }
1889 else {
1890 let exportDoc = ne_mx.createDocument();
1891 exportDoc.copyContentFrom(outputDoc);
1892 exportDoc.importLibrary(stdlib);
1893
1894 let result = exporter.export(ne_mx, exportDoc);
1895 return result;
1896 }
1897 }
1898 return ['', 'Failed to export graph to ' + extension];
1899 }
getExporter(extension='')
Find the first exporter that can export to the given extension / format.

◆ validateDocument()

MxMaterialXHandler::validateDocument ( doc)

Validates the provided MaterialX document.

Parameters
doc- The MaterialX document to validate.
Returns
True if the document is valid, false otherwise.

Definition at line 1773 of file JsMaterialXNodeEditor.js.

1774 {
1775 if (!doc || !stdlib)
1776 {
1777 return true;
1778 }
1779
1780 // Need to create a dummy "validation" doc
1781 let validationDocument = ne_mx.createDocument();
1782 validationDocument.copyContentFrom(doc);
1783 validationDocument.importLibrary(stdlib);
1784
1785 var errors = {};
1786 var valid = validationDocument.validate(errors);
1787 if (!valid) {
1788 this.editor.debugOutput('Failed to validate document:\n' + errors.message, 2);
1789 }
1790 }

◆ writeGraphToDocument()

MxMaterialXHandler::writeGraphToDocument ( mltxgraph,
graph,
graphWriteOptions )

Writes the graph to the specified MaterialX document.

Parameters
mltxgraph- The MaterialX document to write the graph to.
graph- The graph to write.
graphWriteOptions- Options to use when writing the graph.
Returns
{void}

Definition at line 1932 of file JsMaterialXNodeEditor.js.

1932 {
1933
1934 var debug = false;
1935
1936 if (debug)
1937 console.log('***** START Scan Graph:', graph.title);
1938 for (var node of graph._nodes) {
1939 if (node.type == 'graph/subgraph') {
1940 var subgraph = node.subgraph;
1941 //var subgraphNode = mltxgraph.addNodeGraph(node.title);
1942 var subgraphNode = mltxgraph.addChildOfCategory('nodegraph', node.title);
1943 if (debug)
1944 console.log('---->>> Scan NodeGraph:', node.title);
1945 this.writeGraphToDocument(subgraphNode, subgraph, graphWriteOptions);
1946 continue;
1947 }
1948
1949 if (debug)
1950 console.log('---->>> Scan Node:', node.title);
1951
1952 var nodeDefName = node.nodedef_name;
1953 /* if (!nodeDefName)
1954 {
1955 this.editor.debugOutput('Failed to find nodeDef for:' + node.title, 1);
1956 continue;
1957 } */
1958
1959 //var nodeTypes = LiteGraph.registered_node_types;
1960 //var nodeType = nodeTypes[node.type];
1961 var nodedefName = node.nodedef_name;
1962 var nodedef = null;
1963 var nodeElement = null;
1964 //if (nodeType) {
1965 // nodedefName = nodeType.nodedef_name;
1966 // nodedef = stdlib.getNodeDef(nodedefName);
1967 //}
1968
1969 //if (nodedef) {
1970 // nodeElement = mltxgraph.addNodeInstance(nodedef, name)
1971 // nodeElement.setName(node.title);
1972 //}
1973 //else
1974 {
1975 if (nodedefName) {
1976 nodeElement = mltxgraph.addChildOfCategory(node.nodedef_node, node.nodedef_type);
1977 nodeElement.setType(node.nodedef_type);
1978
1979 if (graphWriteOptions.saveNodePositions) {
1980 // TODO: Get properly remapping for xpos, ypos.
1981 nodeElement.setAttribute('xpos', JSON.stringify(node.pos[0]));
1982 nodeElement.setAttribute('ypos', JSON.stringify(node.pos[1]));
1983 }
1984 if (debug)
1985 console.log('** Create node:', nodeElement.getNamePath(), nodeElement.getType());
1986 nodeElement.setName(node.title);
1987 }
1988 }
1989
1990 if (nodeElement) {
1991 if (debug)
1992 console.log('-> Write Node:', graph.title + '/' + node.title, ' --> ', nodeElement.getNamePath());
1993 }
1994 else {
1995 console.log('Skip writing :', node.title);
1996 //this.editor.debugOutput('No nodedef for:' + node.title + 'Nodetype: ' + node.type, 0);
1997 continue;
1998 }
1999
2000 var properties = node.properties;
2001
2002 var node_inputs = node.inputs;
2003 var isInputNode = false;
2004 var isOutputNode = false;
2005 if (nodeElement.getCategory() == 'input') {
2006 isInputNode = true;
2007 node_inputs = [node];
2008 }
2009 else if (nodeElement.getCategory() == 'output') {
2010 isOutputNode = true;
2011 node_inputs = [node];
2012 }
2013
2014 // Add all outputs if the type is multioutput, or user option set
2015 if (!isInputNode && !isOutputNode)
2016 {
2017 if (node.nodedef_type == "multioutput")
2018 {
2019 console.log('Write outputs for:', node, '. type: ', node.nodedef_type);
2020 for (var output of node.outputs) {
2021 var outputName = output.name;
2022 var outputType = output.type;
2023 var outputElement = nodeElement.addOutput(outputName, outputType);
2024 if (debug) {
2025 console.log('> Read: node.nodedef_type: ', node.nodedef_type);
2026 console.log('> Write: output:', outputElement.getNamePath(), outputElement.getType());
2027 }
2028 }
2029 }
2030 }
2031
2032 // Add inputs
2033 if (node_inputs) {
2034
2035 var inputs = node_inputs;
2036 for (var i in inputs) {
2037 let input = inputs[i];
2038 if (debug)
2039 console.log('---- Write port:', input);
2040
2041 let inputName = input.name;
2042 let inputType = input.type;
2043 if (nodeElement.getCategory() == 'input' ||
2044 nodeElement.getCategory() == 'output') {
2045 inputName = 'in';
2046 inputType = node.nodedef_type;
2047 }
2048
2049 //var inputType = input.type;
2050 var inputElement = null;
2051 var nodeToCheck = node;
2052 var inputNode = null;
2053 var inputLink = null;
2054 if (isInputNode && node.graph._subgraph_node) {
2055 nodeToCheck = node.graph._subgraph_node;
2056 for (var i = 0; i < nodeToCheck.inputs.length; i++) {
2057 var nci = nodeToCheck.inputs[i];
2058 if (nci.name == node.title) {
2059 inputNode = nodeToCheck.getInputNode(i);
2060 inputLink = nodeToCheck.getInputLink(i);
2061 //console.log('--- Found parent input:', nci.name, 'inputNode:', inputNode, 'inputLink:', inputLink);
2062 break;
2063 }
2064 }
2065 }
2066 else {
2067 inputNode = node.getInputNode(i);
2068 inputLink = node.getInputLink(i);
2069 }
2070 var inputLinkOutput = '';
2071 var numInputOutputs = 0;
2072 if (inputLink) {
2073 numInputOutputs = inputNode.outputs.length;
2074 inputLinkOutput = inputNode.outputs[inputLink.origin_slot];
2075 }
2076 if (inputNode) {
2077 //console.log('inputNode', inputNode, 'inputLink:', inputLink, '. --- upsteream Output:', inputLinkOutput);
2078 if (nodeElement.getCategory() != 'input' &&
2079 nodeElement.getCategory() != 'output') {
2080 inputElement = nodeElement.getInput(inputName);
2081 //console.log('Call add input on elem', nodeElement, inputName);
2082 inputElement = nodeElement.addInput(inputName, inputType);
2083 }
2084 else {
2085 inputElement = nodeElement;
2086 }
2087
2088 if (debug) {
2089 console.log('Write connection');
2090 console.log(' - TO:', inputElement.getName() + "." + inputName);
2091 console.log(' - FROM link:', inputNode.id + "." + inputLinkOutput.name);
2092 }
2093 if (inputNode.type == 'graph/subgraph') {
2094 inputElement.setNodeGraphString(inputNode.title);
2095 // Set output string if there was an output link.
2096 if (numInputOutputs > 1 && inputLinkOutput) {
2097 inputElement.setOutputString(inputLinkOutput.name);
2098 }
2099 }
2100 else {
2101 //var upstream_nodeType = nodeTypes[inputNode.type];
2102 //if (upstream_nodeType)
2103 //console.log('Write connection: ', inputNode.title)
2104 {
2105 if (inputNode.nodedef_node == 'input') {
2106 //console.log('Set interface name:', inputNode.title, ' on ', inputElement.getNamePath());
2107 //console.log(inputNode)
2108 inputElement.setInterfaceName(inputNode.title);
2109 //console.log('---------- Get interface name:', inputElement.getInterfaceName());
2110 }
2111 else {
2112 inputElement.setNodeName(inputNode.title);
2113 // Need to check that upstream has > 1 output.
2114 // TODO: Log issue that this is annoying to disallow an explicit output in validation.
2115 if (numInputOutputs > 1 && inputNode.nodedef_node != 'output') {
2116 // Set output string if there was an output link.
2117 if (inputLinkOutput) {
2118 inputElement.setOutputString(inputLinkOutput.name);
2119 }
2120 }
2121 }
2122 }
2123 }
2124 }
2125 else {
2126
2127 var inputValue = node.properties[inputName];
2128 if (inputValue == null) {
2129 console.log('Cannot find property value for input:', inputName);
2130 }
2131 else {
2132 var origValue = inputValue;
2133 //var inputType = propInfo.type;
2134 if (['float', 'integer'].includes(inputType)) {
2135 inputValue = inputValue.toString();
2136 }
2137 else if (['vector2', 'vector3', 'vector4', 'matrix33', 'matrix44', 'color3', 'color4'].includes(inputType)) {
2138 inputValue = inputValue.toString();
2139 inputValue = inputValue.split(',').map(Number).join(', ');
2140 }
2141 else if (inputType === 'boolean') {
2142 if (inputValue === 'true')
2143 inputValue = 'true';
2144 else
2145 inputValue = 'false';
2146 }
2147 else {
2148 inputValue = inputValue.toString();
2149 }
2150 //console.log('Write input:', inputElement, node, inputName, origValue, inputValue, inputType);
2151
2152 if (nodeElement.getCategory() != 'input' &&
2153 nodeElement.getCategory() != 'output') {
2154 inputElement = nodeElement.getInput(inputName);
2155 if (!inputElement)
2156 inputElement = nodeElement.addInput(inputName, inputType);
2157 else {
2158 // TODO: LiteGraph seems that copy+paste adds same input > once.
2159 console.log('Error> Trying add input more than once:', inputName, ' to node: ', nodeElement.getNamePath());
2160 }
2161 }
2162 else {
2163 inputElement = nodeElement;
2164 }
2165
2166 try {
2167 inputElement.setValueString(inputValue, inputType);
2168 }
2169 catch (e) {
2170 console.warn("Set value error: ", e);
2171 }
2172 }
2173 }
2174
2175 if (inputElement) {
2176
2177 var propInfo = null;
2178 var skip_attributes = [];
2179 if (isInputNode || isOutputNode) {
2180 if (input.properties_info) {
2181 // Make sure not to clobber connections like interfacename
2182 skip_attributes = ['interfacename', 'nodegraph', 'nodename', 'name', 'type', 'value', 'default_value'];
2183 propInfo = input.properties_info[0];
2184 }
2185 }
2186 else {
2187 if (node.properties_info) {
2188 // Make sure not to clobber connections like interfacename
2189 skip_attributes = ['interfacename', 'nodegraph', 'nodename', 'name', 'type', 'value', 'default_value', 'uimin', 'uimax', 'uiname', 'uifolder'];
2190 propInfo = node.properties_info[i];
2191 }
2192 }
2193 if (propInfo) {
2194 //console.log('Scan propinfo:', propInfo, 'for input:', inputElement.getNamePath(), 'prop_info:', propInfo);
2195
2196 // Write node_properties metadata to input
2197 skip_attributes = skip_attributes.concat(['defaultgeomprop', 'enum', 'enumvalues']);
2198 //console.log('SKIP ATTRIBUTES:', skip_attributes);
2199 for (var propAttribute in propInfo) {
2200 if (skip_attributes.includes(propAttribute))
2201 continue;
2202
2203 //console.log('-- scan attrib:', propAttribute, 'value:', propInfo[propAttribute]);
2204 var propAttributeValue = propInfo[propAttribute];
2205 if (propAttributeValue && propAttributeValue.length > 0) {
2206 inputElement.setAttribute(propAttribute, propAttributeValue);
2207 }
2208 }
2209 }
2210 }
2211 }
2212
2213 if (debug)
2214 console.log('---- END Write inputs:', node.inputs);
2215 }
2216
2217 if (debug)
2218 console.log('---> End write node', node.title);
2219 }
2220
2221 if (debug)
2222 console.log('***** END Scan Graph:', graph.title);
2223 }

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