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

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

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

3039 {
3040 if (name.length == 0) {
3041 if (msg) {
3042 msg = 'Setting empty name as "blank"';
3043 }
3044 name = "blank";
3045 }
3046
3047 // Get list of all names in graph.
3048 var graph = graphcanvas.graph;
3049 var nodes = graph._nodes;
3050 var nodenames = [];
3051 for (var node of nodes) {
3052 nodenames.push(node.title);
3053 }
3054 //console.log('Current graph nodes:', nodenames);
3055
3056 name = ne_mx.createValidName(name);
3057
3058 if (!nodenames.includes(name)) {
3059 return name;
3060 }
3061
3062 // Get starting number and root name
3063 var rootName = name;
3064 var i = 1;
3065 var number = name.match(/\d+$/);
3066 if (number) {
3067 i = (parseInt(number) + 1)
3068 rootName = name.slice(0, -number[0].length);
3069 }
3070
3071 var valid_name = rootName + i.toString();
3072 while (nodenames.includes(valid_name)) {
3073 i++;
3074 valid_name = rootName + i.toString();
3075 }
3076 return valid_name;
3077 }

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

2211 {
2212 var ARRAY_TYPES = ['color3', 'color4', 'vector2', 'vector3', 'vector4', 'matrix33', 'matrix44'];
2213 if (ARRAY_TYPES.includes(_type)) {
2214 return true;
2215 }
2216 return false;
2217 }

◆ loadDefinitionsFromFile()

MxMaterialXHandler::loadDefinitionsFromFile ( )

Load MaterialX document containing node definitions from a file.

Returns
{void}

Definition at line 2746 of file JsMaterialXNodeEditor.js.

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

2975 {
2976 var debug = false;
2977
2978 if (ne_mx) {
2979 if (!this.loadMaterialXLibraries(stdlib))
2980 return;
2981
2982 // Load the content from the specified file (replace this with actual loading logic)
2983
2984 const reader = new FileReader();
2985 reader.readAsText(file, 'UTF-8');
2986 reader.accept = '.mtlx';
2987
2988 var that = this;
2989 console.log('loadFromFile:', file, fileName);
2990 try {
2991 reader.onload = function (e) {
2992 // Display the contents of the file in the output div
2993 let fileContents = e.target.result;
2994 console.log("read file: ", file.name, " with extension: ", extension, " and length: ", fileContents.length);
2995
2996 that.loadFromString(extension, fileContents, fileName, auto_arrange);
2997 };
2998 } catch (error) {
2999 MxShadingGraphEditor.theEditor.debugOutput('Error reading document: ' + fileName + '. Error: ' + error, 2, false);
3000 }
3001
3002 } else {
3003 editor.debugOutput("MaterialX is not initialized", 2, false);
3004 }
3005 }
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 2832 of file JsMaterialXNodeEditor.js.

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

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

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

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

3013 {
3014 if (stdlib)
3015 return stdlib;
3016
3017 if (!ne_mx) {
3018 MxShadingGraphEditor.theEditor.debugOutput("MaterialX is not initialized", 2);
3019 return null;
3020 }
3021
3022 var generator = new ne_mx.EsslShaderGenerator();
3023 var genContext = new ne_mx.GenContext(generator);
3024 {
3025 stdlib = ne_mx.loadStandardLibraries(genContext);
3026 console.log('Loaded standard libraries:', stdlib.getNodeDefs().length);
3027 }
3028
3029 return stdlib;
3030 }

◆ 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 if (writeCustomLibs) {
1816 console.log('Write custom libraries:', customlibs.length);
1817 for (var customlib of customlibs) {
1818 outputDoc.importLibrary(customlib[1]);
1819 }
1820 console.log('Write document custom definitions:', customDocLibs.length);
1821 for (var customDocLib of customDocLibs) {
1822 outputDoc.importLibrary(customDocLib[1]);
1823 }
1824 }
1825
1826 // TODO: Add in other globals
1827 outputDoc.setColorSpace(this.getSourceColorSpace());
1828 outputDoc.removeAttribute('fileprefix');
1829
1830 this.validateDocument(outputDoc);
1831
1832 return outputDoc;
1833 }
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 1896 of file JsMaterialXNodeEditor.js.

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

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

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

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