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.
 
 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)
 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)
 
 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 923 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 2240 of file JsMaterialXNodeEditor.js.

2240 {
2241
2242 var nodeInputs = [];
2243 var isOutput = (node.getCategory() == 'output');
2244 var isInput = (node.getCategory() == 'input');
2245 if (isOutput || isInput) {
2246 nodeInputs = [node];
2247 }
2248 else {
2249 nodeInputs = node.getInputs();
2250 }
2251 for (var input of nodeInputs) {
2252
2253 var _name = ''
2254
2255 if (!isOutput && !isInput) {
2256 _name = input.getName();
2257 explicitInputs.push(_name);
2258 }
2259
2260 var nodeName = input.getNodeName();
2261 var nodeGraphName = input.getNodeGraphString();
2262 var inputInterfaceName = input.getInterfaceName();
2263 var outputName = input.getOutputString();
2264
2265 if (nodeName.length ||
2266 nodeGraphName.length ||
2267 inputInterfaceName.length ||
2268 outputName.length) {
2269
2270 //console.log('Test connection on input:', input.getNamePath(), 'nodeName:[ ', nodeName,
2271 // '] nodeGraphName:[', nodeGraphName,
2272 // '] inputInterfaceName:[', inputInterfaceName,
2273 // ']outputName:[', outputName, ']');
2274
2275 var target_node = lg_node;
2276 var target_slot = null;
2277 if (!isOutput && !isInput)
2278 target_slot = target_node.findInputSlot(_name);
2279 else
2280 target_slot = 0;
2281 var source_node = null;
2282 var source_slot = 0;
2283 var source_name = nodeName;
2284 if (nodeGraphName.length) {
2285 source_name = nodeGraphName;
2286 }
2287 if (inputInterfaceName.length) {
2288 source_name = inputInterfaceName;
2289 }
2290
2291 var graphToCheck = graph;
2292 if (isInput && graph._subgraph_node) {
2293 target_node = graph._subgraph_node;
2294 target_slot = target_node.findInputSlot(lg_node.title);
2295 // Go up to parent graph
2296 graphToCheck = parentGraph;
2297 //console.log(' go up to parent graph:', graphToCheck,
2298 // 'from:', graph, 'subgraph:', graph._subgraph_node,
2299 //'target_node:', target_node.title, 'target_slot:', target_slot);
2300 }
2301 source_node = graphToCheck.findNodeByTitle(source_name);
2302 if (source_node) {
2303 if (outputName) {
2304 var outputSlot = source_node.findOutputSlot(outputName);
2305 if (outputSlot >= 0) {
2306 source_slot = outputSlot;
2307 }
2308 else {
2309 editor.debugOutput('Failed to find output slot:' + outputName, 1);
2310 }
2311 var linkInfo = source_node.connect(source_slot, target_node, target_slot);
2312 if (!linkInfo) {
2313 editor.debugOutput('Failed to connect:' + source_node.title + '.' + outputName, '->', target_node.title + '.' + _name), 1, false;
2314 }
2315 }
2316 //console.log('CONNECT START: source[', source_node.title, '.', source_slot,
2317 // '] --> target[:', target_node.title, ".", target_slot);
2318 var linkInfo = null;
2319 if (source_slot == null || target_slot == null || target_node == null) {
2320 console.warning('Cannot connect!')
2321 }
2322 else {
2323 linkInfo = source_node.connect(source_slot, target_node, target_slot);
2324 }
2325 if (!linkInfo) {
2326 editor.debugOutput('Failed to connect:' + source_node.title + '.' + outputName, '->', target_node.title + '.' + _name, 1);
2327 }
2328 //console.log('CONNECT END: source[', source_node.title, '.', source_slot,
2329 // '] --> target[:', target_node.title, ".", target_slot);
2330 }
2331 else {
2332 console.log('Failed to find node ', source_name, 'in graph:', graphToCheck);
2333 this.editor.debugOutput('Failed to find source node: ' + source_node + "." +
2334 source_name, '->', lg_node.title + "." + _name, 2);
2335 }
2336 }
2337 else {
2338 var _value = input.getResolvedValueString(); // input.getValueString();
2339 if (_value.length > 0) {
2340 if (this.isArray(input.getType())) {
2341 // split by commas or spaces
2342 let valueArray = _value.split(/[\s,]+/);
2343 _value = valueArray;
2344 }
2345
2346 //console.log('-- Value Input:',
2347 //lg_node.title + "." + _name, 'value:', _value);
2348 lg_node.setProperty(_name, _value);
2349 }
2350 }
2351
2352 var property_info = lg_node.getPropertyInfo(_name);
2353 this.loadInputMetaData(node, input, property_info);
2354 }
2355 }
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 2439 of file JsMaterialXNodeEditor.js.

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

1169 {
1170 // Create a struct with the metadata names as key and value
1171 var metaData = {};
1172 metaData['colorspace'] = colorSpace;
1173 metaData['unit'] = unit;
1174 metaData['unittype'] = unitType;
1175 metaData['uiname'] = uiname;
1176 if (_type == 'vector2' || _type == 'vector3' || _type == 'vector4' || _type == 'matrix33' || _type == 'matrix44') {
1177 if (uimin) {
1178 uimin = uimin.split(',').map(Number);
1179 }
1180 if (uimax) {
1181 uimax = uimax.split(',').map(Number);
1182 }
1183 }
1184 metaData['uimin'] = uimin;
1185 metaData['uimax'] = uimax;
1186 metaData['uifolder'] = uifolder;
1187
1188 // Return struct in an array
1189 return metaData;
1190 }

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

931 {
932 super(id, extension);
933 }

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

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

3097 {
3098 if (name.length == 0) {
3099 if (msg) {
3100 msg = 'Setting empty name as "blank"';
3101 }
3102 name = "blank";
3103 }
3104
3105 // Get list of all names in graph.
3106 var graph = graphcanvas.graph;
3107 var nodes = graph._nodes;
3108 var nodenames = [];
3109 for (var node of nodes) {
3110 nodenames.push(node.title);
3111 }
3112 //console.log('Current graph nodes:', nodenames);
3113
3114 name = ne_mx.createValidName(name);
3115
3116 if (!nodenames.includes(name)) {
3117 return name;
3118 }
3119
3120 // Get starting number and root name
3121 var rootName = name;
3122 var i = 1;
3123 var number = name.match(/\d+$/);
3124 if (number) {
3125 i = (parseInt(number) + 1)
3126 rootName = name.slice(0, -number[0].length);
3127 }
3128
3129 var valid_name = rootName + i.toString();
3130 while (nodenames.includes(valid_name)) {
3131 i++;
3132 valid_name = rootName + i.toString();
3133 }
3134 return valid_name;
3135 }

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

1085 {
1086
1087 let graphWriteOptions = { writeCustomLibs : false, saveNodePositions: false, writeOutputs : true };
1088 let mdoc = this.saveGraphToDocument(graph, graphWriteOptions);
1089 if (!mdoc) {
1090 console.log('Failed to save graph to document');
1091 return;
1092 }
1093 return this.findRenderableItemsInDoc(mdoc);
1094 }
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 1102 of file JsMaterialXNodeEditor.js.

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

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

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

2222 {
2223 var ARRAY_TYPES = ['color3', 'color4', 'vector2', 'vector3', 'vector4', 'matrix33', 'matrix44'];
2224 if (ARRAY_TYPES.includes(_type)) {
2225 return true;
2226 }
2227 return false;
2228 }

◆ loadDefinitionsFromFile()

MxMaterialXHandler::loadDefinitionsFromFile ( )

Load MaterialX document containing node definitions from a file.

Returns
{void}

Definition at line 2757 of file JsMaterialXNodeEditor.js.

2757 {
2758 var that = this;
2759
2760 // Load mtlx document from disk
2761 var input = document.createElement("input");
2762 input.style = this.fontSizeStyle;
2763 input.type = "file";
2764 input.accept = ".mtlx";
2765 input.onchange = function (e) {
2766 var file = e.target.files[0];
2767 console.log('Loading definitions from file: ' + file.name);
2768
2769 if (ne_mx) {
2770 // Load the content from the specified file (replace this with actual loading logic)
2771
2772 const reader = new FileReader();
2773 reader.readAsText(file, 'UTF-8');
2774
2775 reader.onload = function (e) {
2776 // Display the contents of the file in the output div
2777 let fileContents = e.target.result;
2778 //console.log(fileContents);
2779
2780 (async () => {
2781 try {
2782 const readOptions = new ne_mx.XmlReadOptions();
2783 readOptions.readXIncludes = false;
2784 var customLib = ne_mx.createDocument();
2785
2786 await ne_mx.readFromXmlString(customLib, fileContents, '', readOptions);
2787
2788 // Create JS from custom library
2789 try {
2790 console.log('Create custom library definitions', ne_mx.prettyPrint(customLib));
2791 var iconName = '';
2792 var scanForIcon = false;
2793 if (scanForIcon) {
2794 // Icon name is filename with webp as extension
2795 var iconName = file.name.replace(/\.[^/.]+$/, ".webp");
2796 // Check if iconName file exists
2797 var iconExists = await that.editor.uriExists(iconName);
2798 if (!iconExists) {
2799 iconName = '';
2800 }
2801 }
2802 var definitionsList = [];
2803 var result = that.createLiteGraphDefinitions(customLib, false, false, definitionsList, 'mtlx', that.editor, iconName);
2804 if (result) {
2805 eval(result);
2806 var definitionsListString = definitionsList.join(', ');
2807 that.editor.debugOutput("Registered custom node types: [" + definitionsListString + "]", 0, false);
2808 that.editor.displayNodeTypes();
2809 }
2810 } catch (e) {
2811 console.log('Error evaluating source:', e);
2812 }
2813
2814
2815 // Keep track of libraries loaded by filename.
2816 customlibs.push([file.name, customLib]);
2817
2818 } catch (error) {
2819 that.editor.debugOutput('Error reading definitions:' + error, 2, false);
2820 }
2821 })();
2822
2823 };
2824
2825 } else {
2826 that.editor.debugOutput("MaterialX is not initialized", 2);
2827 }
2828
2829 //customlibs
2830 };
2831 input.click();
2832 }
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 2989 of file JsMaterialXNodeEditor.js.

2989 {
2990 var debug = false;
2991
2992 if (ne_mx) {
2993 if (!this.loadMaterialXLibraries(stdlib))
2994 return;
2995
2996 // Load the content from the specified file (replace this with actual loading logic)
2997
2998 const reader = new FileReader();
2999 reader.readAsText(file, 'UTF-8');
3000 reader.accept = '.mtlx';
3001
3002 var that = this;
3003 console.log('loadFromFile:', file, fileName);
3004 try {
3005 reader.onload = function (e) {
3006 // Display the contents of the file in the output div
3007 let fileContents = e.target.result;
3008 console.log("read file: ", file.name, " with extension: ", extension, " and length: ", fileContents.length);
3009
3010 that.loadFromString('mtlx', fileContents, fileName, auto_arrange);
3011 };
3012 } catch (error) {
3013 MxShadingGraphEditor.theEditor.debugOutput('Error reading document: ' + fileName + '. Error: ' + error, 2, false);
3014 }
3015
3016 } else {
3017 editor.debugOutput("MaterialX is not initialized", 2, false);
3018 }
3019 }
loadMaterialXLibraries(stdlib)
Load MaterialX definition libraries.

◆ loadFromString()

MxMaterialXHandler::loadFromString ( extension,
fileContents,
fileName,
auto_arrange )

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.
Returns
{void}

Definition at line 2843 of file JsMaterialXNodeEditor.js.

2843 {
2844 if (!ne_mx) {
2845 console.log('MaterialX is not initialized');
2846 return;
2847 }
2848
2849 // Check if we need to pre-convert from extension type to mtlx
2850 if (extension != 'mtlx')
2851 {
2852 let converter = this.getImporter(extension);
2853 if (converter) {
2854 let result = converter.import(ne_mx, fileContents, stdlib);
2855 if (result) {
2856 fileContents = result[0];
2857 }
2858 else {
2859 console.log('Failed to convert from:', extension, 'to mtlx. Errors:', result[1]);
2860 return;
2861 }
2862 }
2863 else
2864 {
2865 console.log('Failed to find converter from:', extension, 'to mtlx.');
2866 return;
2867 }
2868 }
2869
2870 (async () => {
2871 try {
2872 const readOptions = new ne_mx.XmlReadOptions();
2873 readOptions.readXIncludes = false;
2874
2875 doc.clearContent();
2876
2877 doc.importLibrary(stdlib);
2878 for (var item of customlibs) {
2879 console.log('Import custom library:', item[0]);
2880 doc.importLibrary(item[1]);
2881 }
2882 var loadDoc = ne_mx.createDocument();
2883 await ne_mx.readFromXmlString(loadDoc, fileContents, '', readOptions);
2884
2885 // Check if nodedef is not in existingDefs
2886 //
2887 var customLib = ne_mx.createDocument();
2888 customLib.copyContentFrom(loadDoc);
2889 var keepChildren = [];
2890 var existingDefs = []
2891 var saveCustomLib = false;
2892 doc.getNodeDefs().forEach(def => { existingDefs.push(def.getName()); });
2893 for (var nodedef of loadDoc.getNodeDefs()) {
2894 var nodedefName = nodedef.getName();
2895 if (!existingDefs.includes(nodedefName)) {
2896 keepChildren.push(nodedef.getName());
2897 saveCustomLib = true;
2898 }
2899 }
2900 for (var ng of loadDoc.getNodeGraphs()) {
2901 if (ng.getAttribute('nodedef') && ng.getAttribute('nodedef').length > 0) {
2902 saveCustomLib = true;
2903 keepChildren.push(ng.getName());
2904 }
2905 }
2906
2907 if (saveCustomLib) {
2908
2909 for (var child of customLib.getChildren()) {
2910 if (!keepChildren.includes(child.getName())) {
2911 //console.log('Remove child:', child.getName());
2912 customLib.removeChild(child.getName());
2913 }
2914 }
2915
2916 var additionDefs = [];
2917 console.log('Create custom library definitions from addtionDefs:',
2918 ne_mx.prettyPrint(customLib));
2919
2920 var result = this.createLiteGraphDefinitions(customLib, true, false, additionDefs, 'mtlx', MxShadingGraphEditor.theEditor);
2921 try {
2922 eval(result);
2923 console.log('Loaded local definitions: ', additionDefs);
2924 } catch (e) {
2925 console.log('Error evaluating source:', e);
2926 }
2927 }
2928
2929 doc.copyContentFrom(loadDoc);
2930
2931 this.validateDocument(doc);
2932
2933 this.buildGraphFromDoc(doc, MxShadingGraphEditor.theEditor, auto_arrange);
2934
2935 // Must do this after build as build will clear customDocLibs array
2936 if (saveCustomLib) {
2937 customDocLibs.push([fileName, customLib]);
2938 }
2939
2940 // Get the document's colorspace and set it as the active colorspace
2941 var documentColorSpace = doc.getColorSpace();
2942 this.setSourceColorSpace(documentColorSpace);
2943 documentColorSpace = this.getSourceColorSpace();
2944 MxShadingGraphEditor.theEditor.updatePropertyPanel(null);
2945
2946 // Cleanup document, and get up-to-date contents after any possible upgrade.
2947 loadDoc.removeAttribute('fileprefix');
2948 fileContents = ne_mx.writeToXmlString(loadDoc);
2949
2950 this.validateDocument(loadDoc);
2951
2952 MxShadingGraphEditor.theEditor.debugOutput('Loaded document: "' + fileName + '"', 0, false);
2953
2954 // Update mtlx text area
2955 let documentDisplayUpdater = MxShadingGraphEditor.theEditor.ui.documentDisplayUpdater;
2956 if (documentDisplayUpdater) {
2957 documentDisplayUpdater(fileContents);
2958 }
2959 else {
2960 console.log('No docDisplayUpdater!!!');
2961 }
2962
2963 // Update render items in UI
2964 let renderableItemUpdater = MxShadingGraphEditor.theEditor.ui.renderableItemUpdater;
2965 if (renderableItemUpdater) {
2966 let renderableItems = this.findRenderableItemsInDoc(doc);
2967 if (!renderableItems || renderableItems.length == 0) {
2968 MxShadingGraphEditor.theEditor.debugOutput('No renderable items found in graph: ' + fileName, 1, false);
2969 }
2970 renderableItemUpdater(renderableItems);
2971 }
2972
2973 } catch (error) {
2974 MxShadingGraphEditor.theEditor.debugOutput('Error reading document: ' + fileName + '. Error: ' + error, 2, false);
2975 }
2976 })();
2977 }
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 )

Definition at line 3021 of file JsMaterialXNodeEditor.js.

3022 {
3023 if (ne_mx) {
3024 // Load the content from the specified file (replace this with actual loading logic)
3025 console.log("Loading content from zip:", file.name);
3026
3027 const reader = new FileReader();
3028
3029 var that = this;
3030 reader.onload = async function (e) {
3031 try {
3032 const zipData = new Uint8Array(e.target.result); // Convert to Uint8Array
3033 const result = await MxZipUtils.unzipMaterialXData(zipData);
3034 let documents = result[0];
3035 let docText = documents[0].content;
3036 let docFile = documents[0].name;
3037 console.log('Documents:', docText);
3038
3039 let textures = result[1];
3040 for (let i = 0; i < textures.length; i++) {
3041 const url = textures[i].url;
3042 const textureName = textures[i].name;
3043 // Replace fileName with url in docText.
3044 // Do not care about case sensitivity since some examples
3045 // from GPUOpen have the incorrect case.
3046 docText = docText.replace(new RegExp(textureName, 'i'), url);
3047 console.log('Replace reference:' + textureName + ' with blob: ' + url);
3048
3049 }
3050 that.loadFromString('mtlx', docText, docFile, auto_arrange);
3051
3052 } catch (error) {
3053 //editor.debugOutput("MaterialX is not initialized", 2, false);
3054 console.error('Error loading ZIP file:', error);
3055 }
3056 };
3057
3058 reader.readAsArrayBuffer(file);
3059
3060 } else {
3061 console.error("MaterialX is not initialized");
3062 }
3063 }
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 2365 of file JsMaterialXNodeEditor.js.

2365 {
2366 if (input && property_info) {
2367
2368 // Load in basic meta-data
2369 var colorspace = input.getColorSpace();
2370 if (colorspace.length > 0)
2371 property_info['colorspace'] = colorspace;
2372
2373 var unit = input.getUnit();
2374 if (unit.length > 0)
2375 property_info['unit'] = unit;
2376
2377 var uiname = input.getAttribute('uiname');
2378 if (uiname.length > 0)
2379 property_info['uiname'] = uiname;
2380
2381 var uimin = input.getAttribute('uimin');
2382 if (uimin.length > 0)
2383 property_info['uimin'] = uimin;
2384
2385 var uimax = input.getAttribute('uimax');
2386 if (uimax.length > 0)
2387 property_info['uimax'] = uimax;
2388 var uisoftmin = input.getAttribute('uisoftmin');
2389 if (uisoftmin.length > 0)
2390 property_info['uimin'] = uisoftmin;
2391
2392 var uisoftmax = input.getAttribute('uisoftmax');
2393 if (uisoftmax.length > 0)
2394 property_info['uimax'] = uisoftmax;
2395
2396 var uifolder = input.getAttribute('uifolder');
2397 if (uifolder.length > 0)
2398 property_info['uifolder'] = uifolder;
2399
2400 var basicMetaData = ['colorspace', 'unit', 'uiname', 'uimin', 'uimax', 'uifolder', 'name', 'type', 'output', 'nodename', 'nodegraph'];
2401 for (var attrName of input.getAttributeNames()) {
2402 if (!basicMetaData.includes(attrName)) {
2403 property_info[attrName] = input.getAttribute(attrName);
2404 }
2405 }
2406
2407 if (node && input.getType() == 'filename')
2408 {
2409 let nodeType = node.getType();
2410 let colorTypes = ['color3', 'color4'];
2411 //console.log('Load input metadata for:', input.getName(), input.getType(), property_info, nodeType);
2412 if (colorTypes.includes(nodeType))
2413 {
2414 if (!property_info['colorspace']) {
2415 console.log('Auto create "none" colorspace for input:', input.getName());
2416 let doc = node.getDocument();
2417 let defaultColorSpace = 'none';
2418 // For now don't use the document color space as 'none' is more flexible.
2419 //let docColorSpace = doc.getAttribute('colorspace');
2420 //if (docColorSpace.length > 0)
2421 // defaultColorSpace = docColorSpace;
2422 property_info['colorspace'] = defaultColorSpace;
2423 }
2424 }
2425 }
2426
2427 //console.log('load input metadata for:', input.getNamePath(), property_info);
2428 }
2429 }

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

959 {
960
961 function loadInitialText(filePath, handler) {
962 try {
963 fetch(filePath)
964 .then(response => response.blob())
965 .then(blob => {
966 const reader = new FileReader();
967 reader.onload = function (e) {
968 console.log('Loaded document:', filePath);
969 editor.loadGraphFromString('mtlx', e.target.result, filePath, 80);
970 }
971 reader.readAsText(blob);
972 })
973 } catch (error) {
974 console.error('Error loading file %s:' % filePath, error);
975 }
976 }
977
978 loadInitialText(materialFilename, this);
979 }

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

941 {
942 return new Promise((resolve, reject) => {
943 MaterialX().then((ne_mtlx) => {
944 // Save the MaterialX instance to the global variable
945 ne_mx = ne_mtlx;
946 resolve();
947 }).catch((error) => {
948 reject(error);
949 });
950 });
951 }

◆ loadMaterialXLibraries()

MxMaterialXHandler::loadMaterialXLibraries ( stdlib)

Load MaterialX definition libraries.

Returns
The MaterialX document containing the standard libraries.

Definition at line 3070 of file JsMaterialXNodeEditor.js.

3071 {
3072 if (stdlib)
3073 return stdlib;
3074
3075 if (!ne_mx) {
3076 MxShadingGraphEditor.theEditor.debugOutput("MaterialX is not initialized", 2);
3077 return null;
3078 }
3079
3080 var generator = new ne_mx.EsslShaderGenerator();
3081 var genContext = new ne_mx.GenContext(generator);
3082 {
3083 stdlib = ne_mx.loadStandardLibraries(genContext);
3084 console.log('Loaded standard libraries:', stdlib.getNodeDefs().length);
3085 }
3086
3087 return stdlib;
3088 }

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

1790 {
1791
1792 if (!ne_mx) {
1793 this.editor.debugOutput("MaterialX is not initialized", 2);
1794 return;
1795 }
1796
1797 let writeCustomLibs = graphWriteOptions.writeCustomLibs;
1798 if (writeCustomLibs == undefined)
1799 {
1800 console.error('Graph output option: writeCustomLibs is undefined.')
1801 writeCustomLibs = true;
1802 }
1803
1804 var outputDoc = ne_mx.createDocument();
1805
1806 if (!stdlib) {
1807 var generator = new ne_mx.EsslShaderGenerator();
1808 var genContext = new ne_mx.GenContext(generator);
1809 stdlib = ne_mx.loadStandardLibraries(genContext);
1810 }
1811
1812 // Handle top level
1813 this.writeGraphToDocument(outputDoc, graph, graphWriteOptions);
1814
1815 let doc_string = ne_mx.writeToXmlString(outputDoc);
1816 //console.log(doc_string);
1817 //console.log('-----------------------------------------------')
1818
1819 if (writeCustomLibs) {
1820 console.log('Write custom libraries:', customlibs.length);
1821 for (var customlib of customlibs) {
1822 outputDoc.copyContentFrom(customlib[1]);
1823 }
1824 console.log('Write document custom definitions:', customDocLibs.length);
1825 for (var customDocLib of customDocLibs) {
1826 outputDoc.copyContentFrom(customDocLib[1]);
1827 }
1828 }
1829
1830 // TODO: Add in other globals
1831 outputDoc.setColorSpace(this.getSourceColorSpace());
1832 outputDoc.removeAttribute('fileprefix');
1833
1834 this.validateDocument(outputDoc);
1835
1836 return outputDoc;
1837 }
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 1900 of file JsMaterialXNodeEditor.js.

1901 {
1902 var data = this.saveGraphToString(extension, graph, graphWriteOptions);
1903 if (!data[0]) {
1904 return;
1905 }
1906
1907 var blob = new Blob([data[0]], { type: "text/plain" });
1908 var url = URL.createObjectURL(blob);
1909 var a = document.createElement("a");
1910 a.href = url;
1911 a.download = "output_graph.mtlx";
1912 a.click();
1913 }
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 1847 of file JsMaterialXNodeEditor.js.

1847 {
1848
1849 if (!ne_mx) {
1850 this.editor.debugOutput("MaterialX is not initialized", 2);
1851 return ['', 'MaterialX is not initialized'];
1852 }
1853
1854 var outputDoc = this.saveGraphToDocument(graph, graphWriteOptions);
1855 if (!outputDoc) {
1856 this.editor.debugOutput("Failed to save graph to document", 2);
1857 return ['', 'Failed to save graph to document'];
1858 }
1859
1860 if (extension == 'mtlx')
1861 {
1862 const writeOptions = new ne_mx.XmlWriteOptions();
1863 writeOptions.writeXIncludeEnable = false;
1864 var data = '';
1865 try {
1866 data = ne_mx.writeToXmlString(outputDoc, writeOptions);
1867 } catch (e) {
1868 this.editor.debugOutput("Failed to write graph:" + e, 2);
1869 }
1870 return [data, ''];
1871 }
1872
1873 // Look for a registered exporter
1874 else
1875 {
1876 let exporter = this.getExporter(extension);
1877 if (!exporter) {
1878 this.editor.debugOutput('Failed to find ' + extension + ' exporter', 2);
1879 }
1880 else {
1881 let exportDoc = ne_mx.createDocument();
1882 exportDoc.copyContentFrom(outputDoc);
1883 exportDoc.importLibrary(stdlib);
1884
1885 let result = exporter.export(ne_mx, exportDoc);
1886 return result;
1887 }
1888 }
1889 return ['', 'Failed to export graph to ' + extension];
1890 }
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 1764 of file JsMaterialXNodeEditor.js.

1765 {
1766 if (!doc || !stdlib)
1767 {
1768 return true;
1769 }
1770
1771 // Need to create a dummy "validation" doc
1772 let validationDocument = ne_mx.createDocument();
1773 validationDocument.copyContentFrom(doc);
1774 validationDocument.importLibrary(stdlib);
1775
1776 var errors = {};
1777 var valid = validationDocument.validate(errors);
1778 if (!valid) {
1779 this.editor.debugOutput('Failed to validate document:\n' + errors.message, 2);
1780 }
1781 }

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

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

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