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.
 
 getAllFilesRecursive (dir)
 Recursively collects all file paths in a folder.
 
async loadFolderToDocument (folderPath)
 
 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.
 

Public Attributes

return userlib
 

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 2268 of file JsMaterialXNodeEditor.js.

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

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

1187 {
1188 // Create a struct with the metadata names as key and value
1189 var metaData = {};
1190 metaData['colorspace'] = colorSpace;
1191 metaData['unit'] = unit;
1192 metaData['unittype'] = unitType;
1193 metaData['uiname'] = uiname;
1194 if (_type == 'vector2' || _type == 'vector3' || _type == 'vector4' || _type == 'matrix33' || _type == 'matrix44') {
1195 if (uimin) {
1196 uimin = uimin.split(',').map(Number);
1197 }
1198 if (uimax) {
1199 uimax = uimax.split(',').map(Number);
1200 }
1201 }
1202 metaData['uimin'] = uimin;
1203 metaData['uimax'] = uimax;
1204 metaData['uifolder'] = uifolder;
1205
1206 // Return struct in an array
1207 return metaData;
1208 }

◆ 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 1224 of file JsMaterialXNodeEditor.js.

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

3245 {
3246 if (name.length == 0) {
3247 if (msg) {
3248 msg = 'Setting empty name as "blank"';
3249 }
3250 name = "blank";
3251 }
3252
3253 // Get list of all names in graph.
3254 var graph = graphcanvas.graph;
3255 var nodes = graph._nodes;
3256 var nodenames = [];
3257 for (var node of nodes) {
3258 nodenames.push(node.title);
3259 }
3260 //console.log('Current graph nodes:', nodenames);
3261
3262 name = ne_mx.createValidName(name);
3263
3264 if (!nodenames.includes(name)) {
3265 return name;
3266 }
3267
3268 // Get starting number and root name
3269 var rootName = name;
3270 var i = 1;
3271 var number = name.match(/\d+$/);
3272 if (number) {
3273 i = (parseInt(number) + 1)
3274 rootName = name.slice(0, -number[0].length);
3275 }
3276
3277 var valid_name = rootName + i.toString();
3278 while (nodenames.includes(valid_name)) {
3279 i++;
3280 valid_name = rootName + i.toString();
3281 }
3282 return valid_name;
3283 }

◆ 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 1103 of file JsMaterialXNodeEditor.js.

1103 {
1104
1105 let graphWriteOptions = { writeCustomLibs : false, saveNodePositions: false, writeOutputs : true };
1106 let mdoc = this.saveGraphToDocument(graph, graphWriteOptions);
1107 if (!mdoc) {
1108 console.log('Failed to save graph to document');
1109 return;
1110 }
1111 return this.findRenderableItemsInDoc(mdoc);
1112 }
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 1120 of file JsMaterialXNodeEditor.js.

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

◆ getAllFilesRecursive()

MxMaterialXHandler::getAllFilesRecursive ( dir)

Recursively collects all file paths in a folder.

Parameters
{string}dir - Folder path
Returns
{string[]} Array of file paths

Definition at line 2806 of file JsMaterialXNodeEditor.js.

2806 {
2807 // Prefer build-time manifest injection when available to avoid
2808 // relying on directory index HTML. HtmlWebpackPlugin injects
2809 // `adsklibFiles` into the page as `window.ADSKLIB_FILES`.
2810 const webPath = String(dir).replace(/^\/+/, '');
2811 return (async () => {
2812 try {
2813 // If a global manifest was injected, and it contains entries
2814 // for this folder, use it.
2815 if (window && Array.isArray(window.ADSKLIB_FILES) && window.ADSKLIB_FILES.length > 0) {
2816 const prefix = webPath.replace(/\/+$|^\/+/, '');
2817 const matched = window.ADSKLIB_FILES.filter(p => p.startsWith(prefix) || p.startsWith('/' + prefix));
2818 if (matched.length > 0) {
2819 // Ensure absolute paths
2820 return matched.map(p => p.startsWith('/') ? p : '/' + p);
2821 }
2822 }
2823
2824 // Fallback: fetch directory index and parse anchor links.
2825 const res = await fetch(`/${webPath}/`);
2826 if (!res.ok) {
2827 console.warn('getAllFilesRecursive: directory fetch failed', `/${webPath}/`, res.status);
2828 return [];
2829 }
2830 const html = await res.text();
2831 const parser = new DOMParser();
2832 const doc = parser.parseFromString(html, 'text/html');
2833 const anchors = Array.from(doc.querySelectorAll('a')).map(a => a.getAttribute('href'));
2834 const files = anchors
2835 .filter(h => h && h.toLowerCase().endsWith('.mtlx'))
2836 .map(h => (h.startsWith('/') ? h : `/${webPath}/${h}`));
2837 // Normalize and deduplicate
2838 const uniq = Array.from(new Set(files));
2839 return uniq;
2840 } catch (e) {
2841 console.warn('getAllFilesRecursive: error parsing directory', dir, e);
2842 return [];
2843 }
2844 })();
2845 }

◆ 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 1009 of file JsMaterialXNodeEditor.js.

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

2240 {
2241 var ARRAY_TYPES = ['color3', 'color4', 'vector2', 'vector3', 'vector4', 'matrix33', 'matrix44'];
2242 if (ARRAY_TYPES.includes(_type)) {
2243 return true;
2244 }
2245 return false;
2246 }

◆ isURI()

MxMaterialXHandler::isURI ( s)

Definition at line 2252 of file JsMaterialXNodeEditor.js.

2252 {
2253 // Check if the string starts with common URI schemes
2254 const uriSchemes = ['http://', 'https://', 'ftp://', 'blob:', 'file://', 'data:'];
2255 return uriSchemes.some(scheme => s.startsWith(scheme));
2256 }

◆ loadDefinitionsFromFile()

MxMaterialXHandler::loadDefinitionsFromFile ( )

Load MaterialX document containing node definitions from a file.

Returns
{void}

Definition at line 2890 of file JsMaterialXNodeEditor.js.

2890 {
2891 var that = this;
2892
2893 // Load mtlx document from disk
2894 var input = document.createElement("input");
2895 input.style = this.fontSizeStyle;
2896 input.type = "file";
2897 input.accept = ".mtlx";
2898 input.onchange = function (e) {
2899 var file = e.target.files[0];
2900 console.log('Loading definitions from file: ' + file.name);
2901
2902 if (ne_mx) {
2903 // Load the content from the specified file (replace this with actual loading logic)
2904
2905 const reader = new FileReader();
2906 reader.readAsText(file, 'UTF-8');
2907
2908 reader.onload = function (e) {
2909 // Display the contents of the file in the output div
2910 let fileContents = e.target.result;
2911 //console.log(fileContents);
2912
2913 (async () => {
2914 try {
2915 const readOptions = new ne_mx.XmlReadOptions();
2916 readOptions.readXIncludes = false;
2917 var customLib = ne_mx.createDocument();
2918
2919 await ne_mx.readFromXmlString(customLib, fileContents, '', readOptions);
2920
2921 // Create JS from custom library
2922 try {
2923 console.log('Create custom library definitions', ne_mx.prettyPrint(customLib));
2924 var iconName = '';
2925 var scanForIcon = false;
2926 if (scanForIcon) {
2927 // Icon name is filename with webp as extension
2928 var iconName = file.name.replace(/\.[^/.]+$/, ".webp");
2929 // Check if iconName file exists
2930 var iconExists = await that.editor.uriExists(iconName);
2931 if (!iconExists) {
2932 iconName = '';
2933 }
2934 }
2935 var definitionsList = [];
2936 var result = that.createLiteGraphDefinitions(customLib, false, false, definitionsList, 'mtlx', that.editor, iconName);
2937 if (result) {
2938 eval(result);
2939 var definitionsListString = definitionsList.join(', ');
2940 that.editor.debugOutput("Registered custom node types: [" + definitionsListString + "]", 0, false);
2941 that.editor.displayNodeTypes();
2942 }
2943 } catch (e) {
2944 console.log('Error evaluating source:', e);
2945 }
2946
2947
2948 // Keep track of libraries loaded by filename.
2949 customlibs.push([file.name, customLib]);
2950
2951 } catch (error) {
2952 that.editor.debugOutput('Error reading definitions:' + error, 2, false);
2953 }
2954 })();
2955
2956 };
2957
2958 } else {
2959 that.editor.debugOutput("MaterialX is not initialized", 2);
2960 }
2961
2962 //customlibs
2963 };
2964 input.click();
2965 }
var customlibs

◆ loadFolderToDocument()

async MxMaterialXHandler::loadFolderToDocument ( folderPath)

Definition at line 2847 of file JsMaterialXNodeEditor.js.

2847 {
2848 // Browser-only: use getAllFilesRecursive to obtain file URLs and fetch
2849 // them. Assumes files are served under '/<folderPath>/'.
2850 let files = [];
2851 try {
2852 files = await this.getAllFilesRecursive(folderPath);
2853 console.log('===================== FOund files, count:', files.length);
2854 } catch (e) {
2855 console.error('Error listing files for', folderPath, e);
2856 return null;
2857 }
2858
2859 const userlib = ne_mx.createDocument();
2860 for (const fileUrl of files) {
2861 try {
2862 // Ensure URL is absolute-path style
2863 let fetchUrl = fileUrl;
2864 if (!fetchUrl.startsWith('/') && !fetchUrl.match(/^https?:\/\//)) {
2865 const base = String(folderPath).replace(/^\/+/, '');
2866 fetchUrl = `/${base}/${fetchUrl}`;
2867 }
2868 console.log('----------- FETCH: ', fetchUrl);
2869 const res = await fetch(fetchUrl);
2870 if (!res.ok) {
2871 console.error('Failed to fetch', fetchUrl, res.statusText);
2872 continue;
2873 }
2874 const mtlxString = await res.text();
2875 const readOptions = new ne_mx.XmlReadOptions();
2876 await ne_mx.readFromXmlString(userlib, mtlxString, '', readOptions);
2877
2878 } catch (err) {
2879 console.error('Error loading file', fileUrl, err);
2880 }
2881 }
getAllFilesRecursive(dir)
Recursively collects all file paths in a folder.

◆ 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 3134 of file JsMaterialXNodeEditor.js.

3134 {
3135 var debug = false;
3136
3137 if (ne_mx) {
3138 if (!this.loadMaterialXLibraries(stdlib))
3139 return;
3140
3141 // Load the content from the specified file (replace this with actual loading logic)
3142
3143 const reader = new FileReader();
3144 reader.readAsText(file, 'UTF-8');
3145 reader.accept = '.mtlx';
3146
3147 var that = this;
3148 console.log('loadFromFile:', file, fileName);
3149 try {
3150 reader.onload = function (e) {
3151 // Display the contents of the file in the output div
3152 let fileContents = e.target.result;
3153 console.log("read file: ", file.name, " with extension: ", extension, " and length: ", fileContents.length);
3154
3155 that.loadFromString('mtlx', fileContents, fileName, auto_arrange, true);
3156 };
3157 } catch (error) {
3158 MxShadingGraphEditor.theEditor.debugOutput('Error reading document: ' + fileName + '. Error: ' + error, 2, false);
3159 }
3160
3161 } else {
3162 editor.debugOutput("MaterialX is not initialized", 2, false);
3163 }
3164 }
loadMaterialXLibraries(stdlib)
Load MaterialX definition libraries.
debugOutput(text, severity, clear=null)
Output debug information to the console or the UI console logger.

◆ 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 2977 of file JsMaterialXNodeEditor.js.

2977 {
2978 if (!ne_mx) {
2979 console.log('MaterialX is not initialized');
2980 return;
2981 }
2982
2983 // Check if we need to pre-convert from extension type to mtlx
2984 if (extension != 'mtlx')
2985 {
2986 let converter = this.getImporter(extension);
2987 if (converter) {
2988 let result = converter.import(ne_mx, fileContents, stdlib);
2989 if (result) {
2990 fileContents = result[0];
2991 }
2992 else {
2993 console.log('Failed to convert from:', extension, 'to mtlx. Errors:', result[1]);
2994 return;
2995 }
2996 }
2997 else
2998 {
2999 console.log('Failed to find converter from:', extension, 'to mtlx.');
3000 return;
3001 }
3002 }
3003
3004 (async () => {
3005 try {
3006 const readOptions = new ne_mx.XmlReadOptions();
3007 readOptions.readXIncludes = false;
3008
3009 doc.clearContent();
3010
3011 doc.importLibrary(stdlib);
3012 for (var item of customlibs) {
3013 console.log('Import custom library:', item[0]);
3014 doc.importLibrary(item[1]);
3015 }
3016 var loadDoc = ne_mx.createDocument();
3017 await ne_mx.readFromXmlString(loadDoc, fileContents, '', readOptions);
3018
3019 // Check if nodedef is not in existingDefs
3020 //
3021 var customLib = ne_mx.createDocument();
3022 customLib.copyContentFrom(loadDoc);
3023 var keepChildren = [];
3024 var existingDefs = []
3025 var saveCustomLib = false;
3026 doc.getNodeDefs().forEach(def => { existingDefs.push(def.getName()); });
3027 for (var nodedef of loadDoc.getNodeDefs()) {
3028 var nodedefName = nodedef.getName();
3029 if (!existingDefs.includes(nodedefName)) {
3030 keepChildren.push(nodedef.getName());
3031 saveCustomLib = true;
3032 }
3033 }
3034 for (var ng of loadDoc.getNodeGraphs()) {
3035 if (ng.getAttribute('nodedef') && ng.getAttribute('nodedef').length > 0) {
3036 saveCustomLib = true;
3037 keepChildren.push(ng.getName());
3038 }
3039 }
3040
3041 if (saveCustomLib) {
3042
3043 for (var child of customLib.getChildren()) {
3044 if (!keepChildren.includes(child.getName())) {
3045 //console.log('Remove child:', child.getName());
3046 customLib.removeChild(child.getName());
3047 }
3048 }
3049
3050 var additionDefs = [];
3051 console.log('Create custom library definitions from addtionDefs:',
3052 ne_mx.prettyPrint(customLib));
3053
3054 var result = this.createLiteGraphDefinitions(customLib, true, false, additionDefs, 'mtlx', MxShadingGraphEditor.theEditor);
3055 try {
3056 eval(result);
3057 console.log('Loaded local definitions: ', additionDefs);
3058 } catch (e) {
3059 console.log('Error evaluating source:', e);
3060 }
3061 }
3062
3063 doc.copyContentFrom(loadDoc);
3064
3065 this.validateDocument(doc);
3066
3067 this.buildGraphFromDoc(doc, MxShadingGraphEditor.theEditor, auto_arrange);
3068
3069 // Must do this after build as build will clear customDocLibs array
3070 if (saveCustomLib) {
3071 customDocLibs.push([fileName, customLib]);
3072 }
3073
3074 // Get the document's colorspace and set it as the active colorspace
3075 var documentColorSpace = doc.getColorSpace();
3076 this.setSourceColorSpace(documentColorSpace);
3077 documentColorSpace = this.getSourceColorSpace();
3079
3080 // Cleanup document, and get up-to-date contents after any possible upgrade.
3081 loadDoc.removeAttribute('fileprefix');
3082 fileContents = ne_mx.writeToXmlString(loadDoc);
3083
3084 this.validateDocument(loadDoc);
3085
3086 MxShadingGraphEditor.theEditor.debugOutput('Loaded document: "' + fileName + '"', 0, false);
3087
3088 // Update mtlx text area
3089 let documentDisplayUpdater = MxShadingGraphEditor.theEditor.ui.documentDisplayUpdater;
3090 if (documentDisplayUpdater) {
3091 documentDisplayUpdater(fileContents);
3092 }
3093 else {
3094 console.log('No docDisplayUpdater!!!');
3095 }
3096
3097 // Update render items in UI
3098 let renderableItemUpdater = MxShadingGraphEditor.theEditor.ui.renderableItemUpdater;
3099 if (renderableItemUpdater) {
3100 let renderableItems = this.findRenderableItemsInDoc(doc);
3101 if (!renderableItems || renderableItems.length == 0) {
3102 MxShadingGraphEditor.theEditor.debugOutput('No renderable items found in graph: ' + fileName, 1, false);
3103 }
3104 renderableItemUpdater(renderableItems);
3105 }
3106
3107 //
3108
3109 if (rerender)
3110 {
3111 console.log('> Update rendering from document');
3112 let theRenderer = this.editor.monitor.renderer;
3113 //console.log(this.editor.monitor, this.editor.monitor.renderer)
3114 if (theRenderer)
3115 theRenderer.updateMaterialFromText(fileContents);
3116 }
3117
3118 } catch (error) {
3119 MxShadingGraphEditor.theEditor.debugOutput('Error reading document: ' + fileName + '. Error: ' + error, 2, false);
3120 }
3121 })();
3122 }
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.
updatePropertyPanel(node)
This method is called when a node is selected in the graph.

◆ loadFromZip()

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

Definition at line 3166 of file JsMaterialXNodeEditor.js.

3167 {
3168 if (ne_mx) {
3169 // Load the content from the specified file (replace this with actual loading logic)
3170 console.log("Loading content from zip:", file.name);
3171
3172 const reader = new FileReader();
3173
3174 var that = this;
3175 reader.onload = async function (e) {
3176 try {
3177 const zipData = new Uint8Array(e.target.result); // Convert to Uint8Array
3178 const result = await MxZipUtils.unzipMaterialXData(zipData);
3179 let documents = result[0];
3180 let docText = documents[0].content;
3181 let docFile = documents[0].name;
3182 console.log('Documents:', docText);
3183
3184 let textures = result[1];
3185 for (let i = 0; i < textures.length; i++) {
3186 const url = textures[i].url;
3187 const textureName = textures[i].name;
3188 // Replace fileName with url in docText.
3189 // Do not care about case sensitivity since some examples
3190 // from GPUOpen have the incorrect case.
3191 docText = docText.replace(new RegExp(textureName, 'i'), url);
3192 console.log('Replace reference:' + textureName + ' with blob: ' + url);
3193
3194 }
3195 that.loadFromString('mtlx', docText, docFile, auto_arrange, rerender);
3196
3197 } catch (error) {
3198 //editor.debugOutput("MaterialX is not initialized", 2, false);
3199 console.error('Error loading ZIP file:', error);
3200 }
3201 };
3202
3203 reader.readAsArrayBuffer(file);
3204
3205 } else {
3206 console.error("MaterialX is not initialized");
3207 }
3208 }
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 2414 of file JsMaterialXNodeEditor.js.

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

◆ 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 977 of file JsMaterialXNodeEditor.js.

977 {
978
979 function loadInitialText(filePath, handler) {
980 try {
981 fetch(filePath)
982 .then(response => response.blob())
983 .then(blob => {
984 const reader = new FileReader();
985 reader.onload = function (e) {
986 console.log('Loaded document:', filePath);
987 editor.loadGraphFromString('mtlx', e.target.result, filePath, 80, true);
988 }
989 reader.readAsText(blob);
990 })
991 } catch (error) {
992 console.error('Error loading file %s:' % filePath, error);
993 }
994 }
995
996 loadInitialText(materialFilename, this);
997 }

◆ 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 console.log('>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Loaded MaterialX version: ' + ne_mx.getVersionString());
956 // Only attempt to auto-load the adsklib if a build-time manifest
957 // is available (window.ADSKLIB_FILES). This avoids fetching the
958 // directory index (which often returns 404 on static servers).
959 if (typeof window !== 'undefined' && Array.isArray(window.ADSKLIB_FILES) && window.ADSKLIB_FILES.length > 0) {
960 let adsklib = this.loadFolderToDocument('Libraries/adsklib');
961 } else {
962 console.log('Skipping auto-load of Libraries/adsklib (no manifest available)');
963 }
964 resolve();
965 }).catch((error) => {
966 reject(error);
967 });
968 });
969 }
async loadFolderToDocument(folderPath)

◆ loadMaterialXLibraries()

MxMaterialXHandler::loadMaterialXLibraries ( stdlib)

Load MaterialX definition libraries.

Returns
The MaterialX document containing the standard libraries.

Definition at line 3215 of file JsMaterialXNodeEditor.js.

3216 {
3217 console.log('------------------------------- LOAD MTLX LIBRARIES -------------------------------');
3218 if (stdlib)
3219 return stdlib;
3220
3221 if (!ne_mx) {
3222 MxShadingGraphEditor.theEditor.debugOutput("MaterialX is not initialized", 2);
3223 return null;
3224 }
3225
3226 var generator = new ne_mx.EsslShaderGenerator.create();
3227 var genContext = new ne_mx.GenContext(generator);
3228 {
3229 stdlib = ne_mx.loadStandardLibraries(genContext);
3230 console.log('Loaded standard libraries:', stdlib.getNodeDefs().length);
3231 }
3232
3233 let adsklib = this.loadFolderToDocument('./Libraries/adsklib');
3234
3235 return stdlib;
3236 }

◆ 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 1808 of file JsMaterialXNodeEditor.js.

1808 {
1809
1810 if (!ne_mx) {
1811 this.editor.debugOutput("MaterialX is not initialized", 2);
1812 return;
1813 }
1814
1815 let writeCustomLibs = graphWriteOptions.writeCustomLibs;
1816 if (writeCustomLibs == undefined)
1817 {
1818 console.error('Graph output option: writeCustomLibs is undefined.')
1819 writeCustomLibs = true;
1820 }
1821
1822 var outputDoc = ne_mx.createDocument();
1823
1824 if (!stdlib) {
1825 var generator = new ne_mx.EsslShaderGenerator.create();
1826 var genContext = new ne_mx.GenContext(generator);
1827 stdlib = ne_mx.loadStandardLibraries(genContext);
1828 }
1829
1830 // Handle top level
1831 this.writeGraphToDocument(outputDoc, graph, graphWriteOptions);
1832
1833 let doc_string = ne_mx.writeToXmlString(outputDoc);
1834 //console.log(doc_string);
1835 //console.log('-----------------------------------------------')
1836
1837 if (writeCustomLibs) {
1838 console.log('Write custom libraries:', customlibs.length);
1839 for (var customlib of customlibs) {
1840 outputDoc.copyContentFrom(customlib[1]);
1841 }
1842 console.log('Write document custom definitions:', customDocLibs.length);
1843 for (var customDocLib of customDocLibs) {
1844 outputDoc.copyContentFrom(customDocLib[1]);
1845 }
1846 }
1847
1848 // TODO: Add in other globals
1849 outputDoc.setColorSpace(this.getSourceColorSpace());
1850 outputDoc.removeAttribute('fileprefix');
1851
1852 this.validateDocument(outputDoc);
1853
1854 return outputDoc;
1855 }
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 1918 of file JsMaterialXNodeEditor.js.

1919 {
1920 var data = this.saveGraphToString(extension, graph, graphWriteOptions);
1921 if (!data[0]) {
1922 return;
1923 }
1924
1925 var blob = new Blob([data[0]], { type: "text/plain" });
1926 var url = URL.createObjectURL(blob);
1927 var a = document.createElement("a");
1928 a.href = url;
1929 a.download = "output_graph.mtlx";
1930 a.click();
1931 }
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 1865 of file JsMaterialXNodeEditor.js.

1865 {
1866
1867 if (!ne_mx) {
1868 this.editor.debugOutput("MaterialX is not initialized", 2);
1869 return ['', 'MaterialX is not initialized'];
1870 }
1871
1872 var outputDoc = this.saveGraphToDocument(graph, graphWriteOptions);
1873 if (!outputDoc) {
1874 this.editor.debugOutput("Failed to save graph to document", 2);
1875 return ['', 'Failed to save graph to document'];
1876 }
1877
1878 if (extension == 'mtlx')
1879 {
1880 const writeOptions = new ne_mx.XmlWriteOptions();
1881 writeOptions.writeXIncludeEnable = false;
1882 var data = '';
1883 try {
1884 data = ne_mx.writeToXmlString(outputDoc, writeOptions);
1885 } catch (e) {
1886 this.editor.debugOutput("Failed to write graph:" + e, 2);
1887 }
1888 return [data, ''];
1889 }
1890
1891 // Look for a registered exporter
1892 else
1893 {
1894 let exporter = this.getExporter(extension);
1895 if (!exporter) {
1896 this.editor.debugOutput('Failed to find ' + extension + ' exporter', 2);
1897 }
1898 else {
1899 let exportDoc = ne_mx.createDocument();
1900 exportDoc.copyContentFrom(outputDoc);
1901 exportDoc.importLibrary(stdlib);
1902
1903 let result = exporter.export(ne_mx, exportDoc);
1904 return result;
1905 }
1906 }
1907 return ['', 'Failed to export graph to ' + extension];
1908 }
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 1782 of file JsMaterialXNodeEditor.js.

1783 {
1784 if (!doc || !stdlib)
1785 {
1786 return true;
1787 }
1788
1789 // Need to create a dummy "validation" doc
1790 let validationDocument = ne_mx.createDocument();
1791 validationDocument.copyContentFrom(doc);
1792 validationDocument.importLibrary(stdlib);
1793
1794 var errors = {};
1795 var valid = validationDocument.validate(errors);
1796 if (!valid) {
1797 this.editor.debugOutput('Failed to validate document:\n' + errors.message, 2);
1798 }
1799 }

◆ 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 1941 of file JsMaterialXNodeEditor.js.

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

Member Data Documentation

◆ userlib

return MxMaterialXHandler::userlib

Definition at line 2882 of file JsMaterialXNodeEditor.js.


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