31 this.monitoring =
true;
44 console.log(text + path);
57 if (parentGraph.length > 0)
58 path = path + parentGraph +
'/'
78 this.
debugMessage(
'Monitor> Document attribute "' + attribute +
'" changed from: ' + prevValue +
' to: ' + value,
'');
112 if (!this.monitoring)
119 let targetOutput = node.outputs[slot].name;
121 let sourceInput = input.name;
123 this.
debugMessage(
'Monitor> Output connection change: ' +
124 targetPath +
"[" + slot +
"]." + targetOutput +
' to: ' +
125 sourcePath +
"[" + target_slot +
"]." + sourceInput +
" (" + input_type +
")",
"");
142 if (!this.monitoring)
150 let sourceOutput = output.name;
152 let targetInput = node.inputs[target_slot].name;
154 this.
debugMessage(
'Monitor> Input connection change: ' +
155 sourcePath +
"[" + slot +
"]." + sourceOutput +
' to: ' +
156 targetPath +
"[" + target_slot +
"]." + targetInput +
" (" + output_type +
")",
"");
169 if (!this.monitoring)
199 if (!this.monitoring)
220 path =
graphcanvas.graph._subgraph_node.title +
'/';
226 is_subgraph = node.graph._is_subgraph;
228 path = node.graph._subgraph_node.title +
'/';
244 if (!this.monitoring)
250 let path = parentPath + node.title;
251 let newpath = parentPath + newName;
253 this.
debugMessage(
'Monitor> Node renamed: ', path +
' to: ' + newpath);
266 if (!this.monitoring)
285 if (!this.monitoring)
307 if (!this.monitoring)
314 console.log(
'Monitor> Property changed:', path,
'. Property: ' + propertyName +
315 '. Value: ' + newValue +
'. Previous Value: ' + previousValue,
'. Category:', node.nodedef_node);
332 if (!this.monitoring)
339 console.log(
'Monitor> Property Info changed:', path,
'. Property: ' + propertyName +
340 '. Property Info: ' + propertyInfoName +
341 '. Value: ' + newValue +
'. Previous Value: ' + previousValue,
'. Category:', node.nodedef_node);
363 this.renderer = theRenderer
374 this.monitoring = monitor;
384 return this.monitoring;
494 this.monitoring = monitor;
499 theGraph.onConnectionChange =
null;
500 theGraph.onNodeAdded =
null;
501 theGraph.onNodeRemoved =
null;
505 console.log(
'> Monitor graph: ',
graph.title?
graph.title :
'ROOT')
509 theGraph.onConnectionChange =
function (node) {
510 let parentGraph =
'';
511 var is_subgraph = node.graph._is_subgraph;
513 parentGraph =
graphcanvas.graph._subgraph_node.title;
514 that.onConnectionChange(node, parentGraph);
517 for (var s in selected) {
524 theGraph.onNodeAdded =
function (node) {
525 let parentGraph =
'';
527 if (node.type ==
'graph/subgraph') {
531 var node_subgraph = node.subgraph;
532 var node_graph = node.graph;
535 for (var i in node_subgraph._nodes) {
536 let theNode = node_subgraph._nodes[i];
537 if (!node_graph.findNodeByTitle(theNode.title)) {
538 if (theNode.nodedef_node ==
'input') {
539 node.addInput(theNode.title, theNode.nodedef_type);
542 else if (theNode.nodedef_node ==
'output') {
544 node.addOutput(theNode.title, theNode.nodedef_type);
552 node.setSize(node.computeSize());
556 var is_subgraph = node.graph._is_subgraph;
558 parentGraph =
graphcanvas.graph._subgraph_node.title;
560 if (node.nodedef_node ==
'input') {
562 node.graph.addInput(node.title, node.nodedef_type);
564 else if (node.nodedef_node ==
'output') {
566 node.graph.addOutput(node.title, node.nodedef_type);
570 if (node.type ==
'graph/subgraph') {
571 that.monitorGraph(node.subgraph, monitor);
574 that.onNodeAdded(node, parentGraph);
578 theGraph.onNodeRemoved =
function (node) {
580 let parentGraph =
'';
584 parentGraph =
graphcanvas.graph._subgraph_node.title;
585 if (node.nodedef_node ==
'input') {
589 else if (node.nodedef_node ==
'output') {
595 that.onNodeRemoved(node, parentGraph);
598 for (var i in theGraph._nodes) {
599 var node = theGraph._nodes[i];
600 if (node.type ==
'graph/subgraph') {
601 console.log(
'> Monitor subgraph:', node.title);
602 that.monitorGraph(node.subgraph, monitor);
623 this.extension = extension;
628 this.DEFAULT_COLOR_SPACE =
'lin_rec709';
629 this.DEFAULT_DISTANCE_UNIT =
'meter';
630 this.sourceColorSpace = this.DEFAULT_COLOR_SPACE;
631 this.targetDistanceUnit = this.DEFAULT_DISTANCE_UNIT;
632 this.colorSpaces = [];
637 this.converters = [];
651 this.converters.unshift(converter);
663 this.monitor = monitor;
675 if (extension ==
'mtlx')
693 for (let converter of this.converters) {
694 if (converter.exportType() == extension) {
709 if (extension ==
'mtlx')
727 for (let converter of this.converters) {
728 if (converter.importType() == extension) {
742 this.colorSpaces = colorSpaces;
751 return this.colorSpaces;
780 let newSpace = this.DEFAULT_COLOR_SPACE;
781 if (colorSpace && colorSpace.length > 0)
782 newSpace = colorSpace;
786 this.monitor.onDocumentChange(
'colorspace', colorSpace, this.sourceColorSpace);
788 this.sourceColorSpace = newSpace;
798 let newUnit = this.DEFAULT_DISTANCE_UNIT;
799 if (unit && unit.length > 0)
804 this.monitor.onDocumentChange(
'distanceunit', newUnit, this.targetDistanceUnit);
806 this.targetDistanceUnit = newUnit;
817 return this.sourceColorSpace;
827 return this.targetDistanceUnit;
836 return this.extension;
846 this.editor = editor;
868 if (_type ===
'string' || _type ===
'filename') {
869 value =
"'" + value +
"'";
871 else if (this.isArray(_type)) {
872 if (value.length == 0) {
873 if (_type ===
'color3')
874 value =
"[0.0, 0.0, 0.0]";
875 else if (_type ===
'color4')
876 value =
"[0.0, 0.0, 0.0, 0.0]";
877 else if (_type ===
'vector2')
878 value =
"[0.0, 0.0]";
879 else if (_type ===
'vector3')
880 value =
"[0.0, 0.0, 0.0]";
881 else if (_type ===
'vector4')
882 value =
"[0.0, 0.0, 0.0, 0.0]";
883 else if (_type ===
'matrix33')
884 value =
"[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]";
885 else if (_type ===
'matrix44')
886 value =
"[1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]";
889 value =
"[" + value +
"]";
892 else if (_type ===
'integer') {
893 if (value.length == 0) {
897 else if (_type ===
'float') {
898 if (value.length == 0) {
902 else if (_type ===
'boolean') {
909 if (value.length == 0) {
932 super(
id, extension);
942 return new Promise((resolve, reject) => {
943 MaterialX().then((ne_mtlx) => {
947 }).
catch((error) => {
961 function loadInitialText(filePath, handler) {
964 .then(response => response.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);
971 reader.readAsText(blob);
974 console.error(
'Error loading file %s:' % filePath, error);
978 loadInitialText(materialFilename,
this);
992 super.initialize(editor);
999 editor.debugOutput(
"Loaded MaterialX version:" +
ne_mx.getVersionString(), 0,
true);
1002 var generator =
new ne_mx.EsslShaderGenerator();
1003 var genContext =
new ne_mx.GenContext(generator);
1005 editor.debugOutput(
'Loaded standard libraries definitions:' +
stdlib.getNodeDefs().length, 0,
false);
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);
1016 this.
setUnits(Array.from(units).sort());
1017 console.log(
'> Setup real-world units: ', this.
getUnits());
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',
'');
1034 namesplit[1] = namesplit[1].replace(
'_color3',
'');
1036 colorSpaces.add(namesplit[0]);
1037 colorSpaces.add(namesplit[1]);
1044 var definitionsList = [];
1046 var definitionsDisplayUpdater = editor.ui.definitionsDisplayUpdater;
1047 if (definitionsDisplayUpdater) {
1048 definitionsDisplayUpdater(result);
1051 editor.clearNodeTypes();
1055 editor.debugOutput(
'Error evaluating source: ' + e, 2,
false);
1058 var nodeTypes = LiteGraph.registered_node_types;
1060 for (var typeName in nodeTypes) {
1063 editor.debugOutput(
"Registered node types:" + definitionsList.length, 0,
false);
1065 editor.displayNodeTypes();
1067 if (materialFilename.length > 0) {
1071 editor.updatePropertyPanel(
null);
1073 }).
catch((error) => {
1074 editor.debugOutput(
"Error on initialization:" + error, 2);
1087 let graphWriteOptions = { writeCustomLibs :
false, saveNodePositions:
false, writeOutputs :
true };
1090 console.log(
'Failed to save graph to document');
1104 const materialNodes = mdoc.getMaterialNodes();
1105 let shaderList = [];
1106 let renderableItems = [];
1108 for (let i = 0; i < materialNodes.length; ++i) {
1109 let materialNode = materialNodes[i];
1112 let shaderNodes =
ne_mx.getShaderNodes(materialNode)
1113 if (shaderNodes.length > 0) {
1114 let shaderNodePath = shaderNodes[0].getNamePath()
1115 if (!shaderList.includes(shaderNodePath)) {
1117 shaderList.push(shaderNodePath);
1118 renderableItems.push(shaderNodePath);
1123 const nodeGraphs = mdoc.getNodeGraphs();
1124 for (let i = 0; i < nodeGraphs.length; ++i) {
1125 let nodeGraph = nodeGraphs[i];
1127 if (nodeGraph.hasAttribute(
'nodedef') || nodeGraph.hasSourceUri()) {
1131 if (nodeGraph.getDownstreamPorts().length > 0) {
1134 let outputs = nodeGraph.getOutputs();
1135 for (let j = 0; j < outputs.length; ++j) {
1136 let output = outputs[j];
1138 renderableItems.push(output.getNamePath());
1143 const outputs = mdoc.getOutputs();
1144 for (let i = 0; i < outputs.length; ++i) {
1145 let output = outputs[i];
1147 renderableItems.push(output.getNamePath());
1151 return renderableItems;
1169 buildMetaData(colorSpace, unit, unitType, uiname, uimin, uimax, uifolder, _type) {
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') {
1178 uimin = uimin.split(
',').map(Number);
1181 uimax = uimax.split(
',').map(Number);
1184 metaData[
'uimin'] = uimin;
1185 metaData[
'uimax'] = uimax;
1186 metaData[
'uifolder'] = uifolder;
1209 var definition_code =
""
1211 console.log(
'Creating LiteGraph definitions from MaterialX document:',
doc);
1214 var nodeDefs =
doc.getNodeDefs();
1217 definition_code +=
"console.log('Loading MaterialX Definitions...');\n";
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";
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';
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";
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'];
1267 var supporTokens =
false;
1269 inputTypes.push(
'token');
1270 TMAP[
'token'] =
'string';
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 =
'';
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";
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')
1299 definition_code +=
" this.addInput('in', '" + TMAP[_type] +
"');\n";
1301 var metaData = this.
buildMetaData(
'',
'',
'',
'',
null,
null,
'',
null);
1302 metaData = JSON.stringify(metaData);
1307 if (_type ==
'filename')
1312 definition_code +=
" this.addProperty('in', " + value +
", '" + _type +
"'," + metaData +
");\n";
1313 definition_code +=
" this.addOutput('out', '" + TMAP[_type] +
"');\n";
1315 definition_code +=
" this.title = '" + titleName +
"';\n"
1316 var desc =
'"MaterialX:' +
id +
'"';
1317 definition_code +=
" this.desc = " + desc +
";\n";
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";
1326 definition_code +=
" this.onRemoved = " + onRemoved +
"\n";
1329 let monitor = editor.monitor;
1330 var onPropertyChanged =
"function(name, value, prev_value) {\n";
1333 onPropertyChanged +=
" MxShadingGraphEditor.theEditor.monitor.onPropertyChanged(this.title, name, value, prev_value, this);\n";
1337 onPropertyChanged +=
" console.log('+ Internal property changed:', this.title, name, value, prev_value, this);\n";
1339 onPropertyChanged +=
" }";
1340 definition_code +=
" this.onPropertyChanged = " + onPropertyChanged +
"\n";
1343 var onPropertyInfoChanged =
"function(name, info, value, prev_value) {\n";
1346 onPropertyInfoChanged +=
" MxShadingGraphEditor.theEditor.monitor.onPropertyInfoChanged(this.title, name, info, value, prev_value, this);\n";
1350 onPropertyInfoChanged +=
" console.log('+ Internal property info changed:', this.title, name, info, value, prev_value, this);\n";
1352 onPropertyInfoChanged +=
" }"
1353 definition_code +=
" this.onPropertyInfoChanged = " + onPropertyInfoChanged +
"\n";
1356 var onConnectOutput =
"function(slot, input_type, input, target_node, target_slot) {\n";
1359 onConnectOutput +=
" MxShadingGraphEditor.theEditor.monitor.onConnectOutput(slot, input_type, input, target_node, target_slot, this);\n";
1363 onConnectOutput +=
" console.log('+ Output connection changed:', this.title, slot, input_type, input, target_node, target_slot);\n";
1365 onConnectOutput +=
" }"
1366 definition_code +=
" this.onConnectOutput = " + onConnectOutput +
"\n";
1369 var onConnectInput =
"function(target_slot, output_type, output, source, slot) {\n";
1372 onConnectInput +=
" MxShadingGraphEditor.theEditor.monitor.onConnectInput(target_slot, output_type, output, source, slot, this);\n";
1376 onConnectInput +=
" console.log('+ Input connection changed:', this.title, target_slot, output_type, output, source, slot);\n";
1378 onConnectInput +=
" }"
1379 definition_code +=
" this.onConnectInput = " + onConnectInput +
"\n";
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";
1386 definition_code +=
" this.shape = LiteGraph.ROUND_SHAPE;\n";
1388 definition_code +=
" this.onExecute = function() {\n";
1389 definition_code +=
" console.log('Executing node: ', this);\n";
1390 definition_code +=
" }\n";
1392 definition_code +=
"}\n"
1393 definition_code +=
"LiteGraph.registerNodeType('" +
id +
"', " + functionName +
");\n";
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;
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";
1408 definition_code +=
"function " + functionName +
"() {\n";
1410 definition_code +=
" this.title = '" + titleName +
"';\n"
1411 var desc =
'"MaterialX Node :' +
id +
'"';
1412 definition_code +=
" this.desc = " + desc +
";\n";
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";
1421 definition_code +=
" this.addProperty('in', " + value +
", '" + _type +
"');\n";
1422 definition_code +=
" this.addOutput('out', '" + TMAP[_type] +
"');\n";
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";
1431 definition_code +=
" this.onRemoved = " + onRemoved +
"\n";
1433 let monitor = editor.monitor;
1434 var onPropertyChanged =
"function(name, value, prev_value) {\n";
1437 onPropertyChanged +=
" MxShadingGraphEditor.theEditor.monitor.onPropertyChanged(this.title, name, value, prev_value, this);\n";
1441 onPropertyChanged +=
" console.log('+ Internal property changed:', this.title, name, value, prev_value, this);\n";
1443 onPropertyChanged +=
" }";
1444 definition_code +=
" this.onPropertyChanged = " + onPropertyChanged +
"\n";
1446 var onPropertyInfoChanged =
"function(name, info, value, prev_value) {\n";
1449 onPropertyInfoChanged +=
" MxShadingGraphEditor.theEditor.monitor.onPropertyInfoChanged(this.title, name, info, value, prev_value, this);\n";
1453 onPropertyInfoChanged +=
" console.log('+ Internal property info changed:', this.title, name, info, value, prev_value, this);\n";
1455 onPropertyInfoChanged +=
" }"
1456 definition_code +=
" this.onPropertyInfoChanged = " + onPropertyInfoChanged +
"\n";
1460 var onConnectOutput =
"function(slot, input_type, input, target_node, target_slot) {\n";
1463 onConnectOutput +=
" MxShadingGraphEditor.theEditor.monitor.onConnectOutput(slot, input_type, input, target_node, target_slot, this);\n";
1467 onConnectOutput +=
" console.log('+ Output connection changed:', this.title, slot, input_type, input, target_node, target_slot);\n";
1469 onConnectOutput +=
" }"
1470 definition_code +=
" this.onConnectOutput = " + onConnectOutput +
"\n";
1473 var onConnectInput =
"function(target_slot, output_type, output, source, slot) {\n";
1476 onConnectInput +=
" MxShadingGraphEditor.theEditor.monitor.onConnectInput(target_slot, output_type, output, source, slot, this);\n";
1480 onConnectInput +=
" console.log('+ Input connection changed:', this.title, target_slot, output_type, output, source, slot);\n";
1482 onConnectInput +=
" }"
1483 definition_code +=
" this.onConnectInput = " + onConnectInput +
"\n";
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";
1490 definition_code +=
" this.shape = LiteGraph.ROUND_SHAPE;\n";
1492 definition_code +=
" this.onExecute = function() {\n";
1493 definition_code +=
" console.log('Executing node:', this);\n";
1494 definition_code +=
" }\n";
1496 definition_code +=
"}\n"
1497 definition_code +=
"LiteGraph.registerNodeType('" +
id +
"', " + functionName +
");\n";
1498 definitionsList.push(
id);
1503 for (var nodeDef of nodeDefs) {
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();
1514 var swatchId = swatchLocation +
'material_' + nodeDefName +
'_' + outputName +
'_genglsl.png';
1515 swatchId = swatchId.replace(
'ND_',
'');
1517 console.log(
'\n--- Registering node type:',
id,
'----');
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";
1526 definition_code +=
"function " + functionName +
"() {\n";
1528 var nodeGroup = nodeDef.getNodeGroup();
1529 var nodeString = nodeDef.getNodeString();
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];
1539 else if (nodeGroup.toLowerCase().startsWith(key.toLowerCase())) {
1540 if (key in editor.ui.icon_map)
1541 theIcon = editor.ui.icon_map[key];
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";
1556 for (var input of nodeDef.getActiveInputs()) {
1557 var _name = input.getName();
1558 var _type = input.getType();
1560 _type = TMAP[_type];
1562 console.log(
'Unmappable type:', _type)
1563 definition_code +=
" this.addInput('" + _name +
"','" + _type +
"');\n";
1565 let value = input.getValueString();
1567 let uiname = input.getAttribute(
'uiname');
1569 let uimin = input.getAttribute(
'uimin');
1570 if (uimin.length == 0) {
1573 let uimax = input.getAttribute(
'uimax');
1574 if (uimax.length == 0) {
1577 let uisoftmin = input.getAttribute(
'uisoftmin');
1578 if (uisoftmin.length > 0) {
1581 let uisoftmax = input.getAttribute(
'uisoftmax');
1582 if (uisoftmax.length > 0) {
1585 var uifolder = input.getAttribute(
'uifolder');
1586 var metaData = this.
buildMetaData(
'',
'',
'', uiname, uimin, uimax, uifolder, _type);
1589 let colorspace = input.getAttribute(
'colorspace');
1590 let nodeDefType = nodeDef.getType();
1591 if (_type ==
'filename' && (nodeDefType ==
'color3' || nodeDefType ==
'color4'))
1593 if (colorspace.length == 0)
1595 colorspace =
'none';
1598 if (colorspace.length > 0)
1599 metaData[
'colorspace'] = colorspace;
1603 let unitAttributes = [
'unit',
'unittype'];
1604 for (let unitAttribute of unitAttributes)
1606 let value = input.getAttribute(unitAttribute);
1607 if (value.length > 0)
1609 metaData[unitAttribute] = value;
1614 let defaultgeomprop = input.getAttribute(
'defaultgeomprop')
1615 metaData[
'defaultgeomprop'] = defaultgeomprop;
1618 let enums = input.getAttribute(
'enum');
1619 if (enums.length > 0)
1621 metaData[
'enum'] = enums.split(
',');
1622 metaData[
'enum'].map(
function(x) {
return x.trim(); });
1624 let enumvalues = input.getAttribute(
'enumvalues');
1625 if (enumvalues.length > 0)
1627 metaData[
'enumvalues'] = enumvalues.split(
',');
1628 metaData[
'enumvalues'].map(
function(x) {
return x.trim(); });
1631 metaData = JSON.stringify(metaData);
1632 definition_code +=
" this.addProperty('" + _name +
"', " + value +
", '" + _type +
"'," + metaData +
");\n";
1634 for (var output of nodeDef.getActiveOutputs()) {
1635 var _name = output.getName();
1636 var _type = output.getType();
1638 _type = TMAP[_type];
1640 console.log(
'Unmappable type:', _type)
1643 definition_code +=
" this.addOutput('" + _name +
"','" + _type +
"');\n";
1646 definition_code +=
" this.title = '" + titleName +
"';\n"
1647 var desc =
'"MaterialX:' +
id +
'"';
1648 definition_code +=
" this.desc = " + desc +
";\n";
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";
1661 definition_code +=
" this.onRemoved = " + onRemoved +
"\n";
1663 let monitor = editor.monitor;
1664 var onPropertyChanged =
"function(name, value, prev_value) {\n";
1667 onPropertyChanged +=
" MxShadingGraphEditor.theEditor.monitor.onPropertyChanged(this.title, name, value, prev_value, this);\n";
1671 onPropertyChanged +=
" console.log('+ Internal property changed:', this.title, name, value, prev_value, this);\n";
1673 onPropertyChanged +=
" }";
1674 definition_code +=
" this.onPropertyChanged = " + onPropertyChanged +
"\n";
1676 var onPropertyInfoChanged =
"function(name, info, value, prev_value) {\n";
1679 onPropertyInfoChanged +=
" MxShadingGraphEditor.theEditor.monitor.onPropertyInfoChanged(this.title, name, info, value, prev_value, this);\n";
1683 onPropertyInfoChanged +=
" console.log('+ Internal property info changed:', this.title, name, info, value, prev_value, this);\n";
1685 onPropertyInfoChanged +=
" }"
1686 definition_code +=
" this.onPropertyInfoChanged = " + onPropertyInfoChanged +
"\n";
1689 var onConnectOutput =
"function(slot, input_type, input, target_node, target_slot) {\n";
1692 onConnectOutput +=
" MxShadingGraphEditor.theEditor.monitor.onConnectOutput(slot, input_type, input, target_node, target_slot, this);\n";
1696 onConnectOutput +=
" console.log('+ Output connection changed:', this.title, slot, input_type, input, target_node, target_slot);\n";
1698 onConnectOutput +=
" }"
1699 definition_code +=
" this.onConnectOutput = " + onConnectOutput +
"\n";
1702 var onConnectInput =
"function(target_slot, output_type, output, source, slot) {\n";
1705 onConnectInput +=
" MxShadingGraphEditor.theEditor.monitor.onConnectInput(target_slot, output_type, output, source, slot, this);\n";
1709 onConnectInput +=
" console.log('+ Input connection changed:', this.title, target_slot, output_type, output, source, slot);\n";
1711 onConnectInput +=
" }"
1712 definition_code +=
" this.onConnectInput = " + onConnectInput +
"\n";
1715 definition_code +=
" this.bgcolor = '#111';\n";
1717 if (nodeGroup ==
'conditional') {
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";
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";
1729 else if (nodeGroup ==
'material') {
1730 definition_code +=
" this.color = '#151';\n";
1731 definition_code +=
" this.shape = LiteGraph.BOX_SHAPE;\n";
1734 definition_code +=
" this.color = '#222';\n";
1735 definition_code +=
" this.shape = LiteGraph.ROUND_SHAPE;\n";
1737 if (nodeType in CMAP) {
1738 definition_code +=
" this.boxcolor = '" + CMAP[nodeType] +
"';\n";
1741 definition_code +=
"}\n"
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";
1748 definition_code +=
"LiteGraph.registerNodeType(" +
"'" +
id +
"'," + functionName +
");\n";
1749 definitionsList.push(
id);
1751 definition_code +=
"console.log('Registered node type:', '" +
id +
"');\n";
1755 return definition_code;
1772 let validationDocument =
ne_mx.createDocument();
1773 validationDocument.copyContentFrom(
doc);
1774 validationDocument.importLibrary(
stdlib);
1777 var valid = validationDocument.validate(errors);
1779 this.editor.debugOutput(
'Failed to validate document:\n' + errors.message, 2);
1793 this.editor.debugOutput(
"MaterialX is not initialized", 2);
1797 let writeCustomLibs = graphWriteOptions.writeCustomLibs;
1798 if (writeCustomLibs == undefined)
1800 console.error(
'Graph output option: writeCustomLibs is undefined.')
1801 writeCustomLibs =
true;
1804 var outputDoc =
ne_mx.createDocument();
1807 var generator =
new ne_mx.EsslShaderGenerator();
1808 var genContext =
new ne_mx.GenContext(generator);
1815 if (writeCustomLibs) {
1816 console.log(
'Write custom libraries:',
customlibs.length);
1818 outputDoc.importLibrary(customlib[1]);
1820 console.log(
'Write document custom definitions:',
customDocLibs.length);
1822 outputDoc.importLibrary(customDocLib[1]);
1828 outputDoc.removeAttribute(
'fileprefix');
1846 this.editor.debugOutput(
"MaterialX is not initialized", 2);
1847 return [
'',
'MaterialX is not initialized'];
1852 this.editor.debugOutput(
"Failed to save graph to document", 2);
1853 return [
'',
'Failed to save graph to document'];
1856 if (extension ==
'mtlx')
1858 const writeOptions =
new ne_mx.XmlWriteOptions();
1859 writeOptions.writeXIncludeEnable =
false;
1862 data =
ne_mx.writeToXmlString(outputDoc, writeOptions);
1864 this.editor.debugOutput(
"Failed to write graph:" + e, 2);
1874 this.editor.debugOutput(
'Failed to find ' + extension +
' exporter', 2);
1877 let exportDoc =
ne_mx.createDocument();
1878 exportDoc.copyContentFrom(outputDoc);
1879 exportDoc.importLibrary(
stdlib);
1881 let result = exporter.export(
ne_mx, exportDoc);
1885 return [
'',
'Failed to export graph to ' + extension];
1903 var blob =
new Blob([data[0]], { type:
"text/plain" });
1904 var url = URL.createObjectURL(blob);
1905 var a = document.createElement(
"a");
1907 a.download =
"output_graph.mtlx";
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;
1929 var subgraphNode = mltxgraph.addChildOfCategory(
'nodegraph', node.title);
1931 console.log(
'---->>> Scan NodeGraph:', node.title);
1937 console.log(
'---->>> Scan Node:', node.title);
1939 var nodeDefName = node.nodedef_name;
1948 var nodedefName = node.nodedef_name;
1950 var nodeElement =
null;
1963 nodeElement = mltxgraph.addChildOfCategory(node.nodedef_node, node.nodedef_type);
1964 nodeElement.setType(node.nodedef_type);
1966 if (graphWriteOptions.saveNodePositions) {
1968 nodeElement.setAttribute(
'xpos', JSON.stringify(node.pos[0]));
1969 nodeElement.setAttribute(
'ypos', JSON.stringify(node.pos[1]));
1972 console.log(
'** Create node:', nodeElement.getNamePath(), nodeElement.getType());
1973 nodeElement.setName(node.title);
1979 console.log(
'-> Write Node:',
graph.title +
'/' + node.title,
' --> ', nodeElement.getNamePath());
1982 console.log(
'Skip writing :', node.title);
1987 var properties = node.properties;
1989 var node_inputs = node.inputs;
1990 var isInputNode =
false;
1991 var isOutputNode =
false;
1992 if (nodeElement.getCategory() ==
'input') {
1994 node_inputs = [node];
1996 else if (nodeElement.getCategory() ==
'output') {
1997 isOutputNode =
true;
1998 node_inputs = [node];
2002 if (!isInputNode && !isOutputNode)
2004 if (node.nodedef_type ==
"multioutput")
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);
2012 console.log(
'> Read: node.nodedef_type: ', node.nodedef_type);
2013 console.log(
'> Write: output:', outputElement.getNamePath(), outputElement.getType());
2022 var inputs = node_inputs;
2023 for (var i in inputs) {
2024 let input = inputs[i];
2026 console.log(
'---- Write port:', input);
2028 let inputName = input.name;
2029 let inputType = input.type;
2030 if (nodeElement.getCategory() ==
'input' ||
2031 nodeElement.getCategory() ==
'output') {
2033 inputType = node.nodedef_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);
2054 inputNode = node.getInputNode(i);
2055 inputLink = node.getInputLink(i);
2057 var inputLinkOutput =
'';
2058 var numInputOutputs = 0;
2060 numInputOutputs = inputNode.outputs.length;
2061 inputLinkOutput = inputNode.outputs[inputLink.origin_slot];
2065 if (nodeElement.getCategory() !=
'input' &&
2066 nodeElement.getCategory() !=
'output') {
2067 inputElement = nodeElement.getInput(inputName);
2069 inputElement = nodeElement.addInput(inputName, inputType);
2072 inputElement = nodeElement;
2076 console.log(
'Write connection');
2077 console.log(
' - TO:', inputElement.getName() +
"." + inputName);
2078 console.log(
' - FROM link:', inputNode.id +
"." + inputLinkOutput.name);
2080 if (inputNode.type ==
'graph/subgraph') {
2081 inputElement.setNodeGraphString(inputNode.title);
2083 if (numInputOutputs > 1 && inputLinkOutput) {
2084 inputElement.setOutputString(inputLinkOutput.name);
2092 if (inputNode.nodedef_node ==
'input') {
2093 inputElement.setInterfaceName(inputNode.title);
2096 inputElement.setNodeName(inputNode.title);
2099 if (numInputOutputs > 1 && inputNode.nodedef_node !=
'output') {
2101 if (inputLinkOutput) {
2102 inputElement.setOutputString(inputLinkOutput.name);
2111 var inputValue = node.properties[inputName];
2112 if (inputValue ==
null) {
2113 console.log(
'Cannot find property value for input:', inputName);
2118 var origValue = inputValue;
2120 if (inputType in [
'float',
'integer',
'vector2',
'vector3',
'vector4',
2121 'matrix33',
'matrix44',
'color3',
'color4']) {
2122 inputValue =
'"' + parseFloat(inputValue) +
'"';
2124 else if (inputType ===
'boolean') {
2125 if (inputValue ===
'true')
2126 inputValue =
'true';
2128 inputValue =
'false';
2131 inputValue = inputValue.toString();
2135 if (nodeElement.getCategory() !=
'input' &&
2136 nodeElement.getCategory() !=
'output') {
2137 inputElement = nodeElement.getInput(inputName);
2139 inputElement = nodeElement.addInput(inputName, inputType);
2142 console.log(
'Error> Trying add input more than once:', inputName,
' to node: ', nodeElement.getNamePath());
2146 inputElement = nodeElement;
2150 inputElement.setValueString(inputValue, inputType);
2153 console.warn(
"Set value error: ", e);
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];
2168 if (node.properties_info) {
2169 skip_attributes = [
'name',
'type',
'value',
'default_value',
'uimin',
'uimax',
'uiname',
'uifolder'];
2170 propInfo = node.properties_info[i];
2177 skip_attributes = skip_attributes.concat([
'defaultgeomprop',
'enum',
'enumvalues']);
2179 for (var propAttribute in propInfo) {
2180 if (skip_attributes.includes(propAttribute))
2184 var propAttributeValue = propInfo[propAttribute];
2185 if (propAttributeValue && propAttributeValue.length > 0) {
2186 inputElement.setAttribute(propAttribute, propAttributeValue);
2194 console.log(
'---- END Write inputs:', node.inputs);
2198 console.log(
'---> End write node', node.title);
2202 console.log(
'***** END Scan Graph:',
graph.title);
2212 var ARRAY_TYPES = [
'color3',
'color4',
'vector2',
'vector3',
'vector4',
'matrix33',
'matrix44'];
2213 if (ARRAY_TYPES.includes(_type)) {
2231 var nodeInputs = [];
2232 var isOutput = (node.getCategory() ==
'output');
2233 var isInput = (node.getCategory() ==
'input');
2234 if (isOutput || isInput) {
2235 nodeInputs = [node];
2238 nodeInputs = node.getInputs();
2240 for (var input of nodeInputs) {
2244 if (!isOutput && !isInput) {
2245 _name = input.getName();
2246 explicitInputs.push(_name);
2249 var nodeName = input.getNodeName();
2250 var nodeGraphName = input.getNodeGraphString();
2251 var inputInterfaceName = input.getInterfaceName();
2252 var outputName = input.getOutputString();
2254 if (nodeName.length ||
2255 nodeGraphName.length ||
2256 inputInterfaceName.length ||
2257 outputName.length) {
2264 var target_node = lg_node;
2265 var target_slot =
null;
2266 if (!isOutput && !isInput)
2267 target_slot = target_node.findInputSlot(_name);
2270 var source_node =
null;
2271 var source_slot = 0;
2272 var source_name = nodeName;
2273 if (nodeGraphName.length) {
2274 source_name = nodeGraphName;
2276 if (inputInterfaceName.length) {
2277 source_name = inputInterfaceName;
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);
2285 graphToCheck = parentGraph;
2290 source_node = graphToCheck.findNodeByTitle(source_name);
2293 var outputSlot = source_node.findOutputSlot(outputName);
2294 if (outputSlot >= 0) {
2295 source_slot = outputSlot;
2298 editor.debugOutput(
'Failed to find output slot:' + outputName, 1);
2300 var linkInfo = source_node.connect(source_slot, target_node, target_slot);
2302 editor.debugOutput(
'Failed to connect:' + source_node.title +
'.' + outputName,
'->', target_node.title +
'.' + _name), 1,
false;
2307 var linkInfo =
null;
2308 if (source_slot ==
null || target_slot ==
null || target_node ==
null) {
2309 console.warning(
'Cannot connect!')
2312 linkInfo = source_node.connect(source_slot, target_node, target_slot);
2315 editor.debugOutput(
'Failed to connect:' + source_node.title +
'.' + outputName,
'->', target_node.title +
'.' + _name, 1);
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);
2327 var _value = input.getResolvedValueString();
2328 if (_value.length > 0) {
2329 if (this.
isArray(input.getType())) {
2331 let valueArray = _value.split(/[\s,]+/);
2332 _value = valueArray;
2337 lg_node.setProperty(_name, _value);
2341 var property_info = lg_node.getPropertyInfo(_name);
2355 if (input && property_info) {
2358 var colorspace = input.getColorSpace();
2359 if (colorspace.length > 0)
2360 property_info[
'colorspace'] = colorspace;
2362 var unit = input.getUnit();
2363 if (unit.length > 0)
2364 property_info[
'unit'] = unit;
2366 var uiname = input.getAttribute(
'uiname');
2367 if (uiname.length > 0)
2368 property_info[
'uiname'] = uiname;
2370 var uimin = input.getAttribute(
'uimin');
2371 if (uimin.length > 0)
2372 property_info[
'uimin'] = uimin;
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;
2381 var uisoftmax = input.getAttribute(
'uisoftmax');
2382 if (uisoftmax.length > 0)
2383 property_info[
'uimax'] = uisoftmax;
2385 var uifolder = input.getAttribute(
'uifolder');
2386 if (uifolder.length > 0)
2387 property_info[
'uifolder'] = uifolder;
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);
2396 if (node && input.getType() ==
'filename')
2398 let nodeType = node.getType();
2399 let colorTypes = [
'color3',
'color4'];
2401 if (colorTypes.includes(nodeType))
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';
2411 property_info[
'colorspace'] = defaultColorSpace;
2430 let loadNodePositions =
false;
2434 editor.debugOutput(
"MaterialX is not initialized", 2);
2438 editor.clearGraph();
2441 editor.monitor.monitorGraph(
graph,
false);
2445 var mtlxNodeDefs = [];
2447 for (var interfaceInput of
doc.getInputs()) {
2448 var _type = interfaceInput.getType();
2449 var
id =
'mtlx/input/input_' + _type;
2451 var lg_node = LiteGraph.createNode(
id);
2453 lg_node.title = interfaceInput.getName();
2455 console.log(
'Add top level input:', lg_node.title,
'to graph',
graph);
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);
2463 lg_node.setProperty(
'in', _value);
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;
2477 lg_node.setSize(lg_node.computeSize());
2485 for (var interfaceOutput of
doc.getOutputs()) {
2486 var _type = interfaceOutput.getType()
2487 var
id =
'mtlx/output/output_' + _type;
2489 var lg_node = LiteGraph.createNode(
id);
2491 lg_node.title = interfaceOutput.getName();
2494 console.log(
'Add graph output:', lg_node.title);
2498 lg_node.setSize(lg_node.computeSize());
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];
2507 mtlxNodes.push([interfaceOutput, lg_node,
graph]);
2511 for (var node of
doc.getNodes()) {
2512 var nodeDef = node.getNodeDef();
2514 editor.debugOutput(
'Skip node w/o nodedef:' + node.getName(), 1)
2519 var
id =
'mtlx/' + nodeDef.getNodeGroup() +
'/' + nodeDef.getName();
2520 id =
id.replace(
'ND_',
'');
2522 console.log(
'Load node:', node.getName(),
' -> ',
id);
2524 var lg_node = LiteGraph.createNode(
id);
2527 lg_node.title = node.getName();
2532 lg_node.setSize(lg_node.computeSize());
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];
2541 mtlxNodes.push([node, lg_node,
graph]);
2542 mtlxNodeDefs.push(nodeDef);
2545 editor.debugOutput(
'Failed to create node:' + node.getName(), 2);
2549 for (var nodegraph of
doc.getNodeGraphs()) {
2550 if (nodegraph.hasSourceUri()) {
2553 var nodedefAttrib = nodegraph.getAttribute(
'nodedef');
2554 if (nodedefAttrib && nodedefAttrib.length > 0) {
2555 console.log(
'Skip loading in functional graph:', nodegraph.getName(),
'nodedef:', nodedefAttrib);
2559 console.log(
'Create nodegraph:', nodegraph.getName());
2561 var title = nodegraph.getName();
2562 var subgraphNode = LiteGraph.createNode(
"graph/subgraph", title);
2566 subgraphNode.bgImageUrl =
"./Icons/nodegraph.png";
2568 var mtlxSubGraphNodes = [];
2569 for (var interfaceInput of nodegraph.getInputs()) {
2570 var _type = interfaceInput.getType();
2571 var
id =
'mtlx/input/input_' + _type;
2573 var lg_node = LiteGraph.createNode(
id);
2575 lg_node.title = interfaceInput.getName();
2577 subgraphNode.subgraph.add(lg_node);
2580 console.log(
'-------- Add subgraph input:', lg_node.title, lg_node);
2582 subgraphNode.addInput(interfaceInput.getName(), _type);
2583 subgraphNode.subgraph.addInput(interfaceInput.getName(), _type);
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);
2591 lg_node.setProperty(
'in', _value);
2595 lg_node.setSize(lg_node.computeSize());
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];
2604 mtlxSubGraphNodes.push([interfaceInput, lg_node,
graph]);
2608 for (var interfaceOutput of nodegraph.getOutputs()) {
2609 var _type = interfaceOutput.getType()
2610 var
id =
'mtlx/output/output_' + _type;
2612 var lg_node = LiteGraph.createNode(
id);
2614 lg_node.title = interfaceOutput.getName();
2615 subgraphNode.subgraph.add(lg_node);
2617 console.log(
'Add subgraph output:', lg_node.title);
2619 subgraphNode.addOutput(interfaceOutput.getName(), _type);
2620 subgraphNode.subgraph.addOutput(interfaceOutput.getName(), _type);
2623 lg_node.setSize(lg_node.computeSize());
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];
2632 mtlxSubGraphNodes.push([interfaceOutput, lg_node,
graph]);
2637 for (var node of nodegraph.getNodes()) {
2638 var nodeDef = node.getNodeDef();
2640 editor.debugOutput(
'Skip node w/o nodedef:' + node.getName(), 1)
2645 var
id =
'mtlx/' + nodeDef.getNodeGroup() +
'/' + nodeDef.getName();
2646 id =
id.replace(
'ND_',
'');
2648 var lg_node = LiteGraph.createNode(
id);
2649 lg_node.title = node.getName();
2650 subgraphNode.subgraph.add(lg_node);
2652 console.log(
'Add subgraph node:', lg_node.title);
2655 lg_node.setSize(lg_node.computeSize());
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];
2664 mtlxSubGraphNodes.push([node, lg_node,
graph]);
2667 for (var item of mtlxSubGraphNodes) {
2669 var lg_node = item[1];
2670 var parentGraph = item[2];
2671 var explicitInputs = [];
2674 lg_node.setSize(lg_node.computeSize());
2677 this.
buildConnections(editor, node, lg_node, explicitInputs, subgraphNode.subgraph, parentGraph);
2681 console.log(
'Add subgraph:', subgraphNode.title);
2683 if (auto_arrange > 0) {
2684 subgraphNode.subgraph.arrange(auto_arrange);
2687 graph.add(subgraphNode);
2694 for (var item of mtlxNodes) {
2696 var lg_node = item[1];
2699 var explicitInputs = [];
2703 if (lg_node.nodedef_node ==
'input' || lg_node.nodedef_node ==
'output') {
2731 editor.monitor.monitorGraph(
graph,
true);
2733 if (auto_arrange > 0) {
2734 graph.arrange(auto_arrange);
2737 graph.setDirtyCanvas(
true,
true);
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);
2761 const reader =
new FileReader();
2762 reader.readAsText(file,
'UTF-8');
2764 reader.onload =
function (e) {
2766 let fileContents = e.target.result;
2771 const readOptions =
new ne_mx.XmlReadOptions();
2772 readOptions.readXIncludes =
false;
2773 var customLib =
ne_mx.createDocument();
2775 await
ne_mx.readFromXmlString(customLib, fileContents,
'', readOptions);
2779 console.log(
'Create custom library definitions')
2781 var scanForIcon =
false;
2784 var iconName = file.name.replace(/\.[^/.]+$/,
".webp");
2786 var iconExists = await that.editor.uriExists(iconName);
2791 var definitionsList = [];
2792 var result = that.createLiteGraphDefinitions(customLib,
false,
false, definitionsList,
'mtlx', that.editor, iconName);
2795 var definitionsListString = definitionsList.join(
', ');
2796 that.editor.debugOutput(
"Registered custom node types: [" + definitionsListString +
"]", 0,
false);
2797 that.editor.displayNodeTypes();
2800 console.log(
'Error evaluating source:', e);
2808 that.editor.debugOutput(
'Error reading definitions:' + error, 2,
false);
2815 that.editor.debugOutput(
"MaterialX is not initialized", 2);
2834 console.log(
'MaterialX is not initialized');
2839 if (extension !=
'mtlx')
2843 let result = converter.import(
ne_mx, fileContents,
stdlib);
2845 fileContents = result[0];
2848 console.log(
'Failed to convert from:', extension,
'to mtlx. Errors:', result[1]);
2854 console.log(
'Failed to find converter from:', extension,
'to mtlx.');
2861 const readOptions =
new ne_mx.XmlReadOptions();
2862 readOptions.readXIncludes =
false;
2868 console.log(
'Import custom library:', item[0]);
2869 doc.importLibrary(item[1]);
2871 var loadDoc =
ne_mx.createDocument();
2872 await
ne_mx.readFromXmlString(loadDoc, fileContents,
'', readOptions);
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;
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());
2896 if (saveCustomLib) {
2898 for (var child of customLib.getChildren()) {
2899 if (!keepChildren.includes(child.getName())) {
2901 customLib.removeChild(child.getName());
2905 var additionDefs = [];
2909 console.log(
'Loaded local definitions: ', additionDefs);
2911 console.log(
'Error evaluating source:', e);
2915 doc.copyContentFrom(loadDoc);
2922 if (saveCustomLib) {
2927 var documentColorSpace =
doc.getColorSpace();
2933 loadDoc.removeAttribute(
'fileprefix');
2934 fileContents =
ne_mx.writeToXmlString(loadDoc);
2942 if (documentDisplayUpdater) {
2943 documentDisplayUpdater(fileContents);
2946 console.log(
'No docDisplayUpdater!!!');
2951 if (renderableItemUpdater) {
2953 if (!renderableItems || renderableItems.length == 0) {
2954 MxShadingGraphEditor.theEditor.debugOutput(
'No renderable items found in graph: ' + fileName, 1,
false);
2956 renderableItemUpdater(renderableItems);
2960 MxShadingGraphEditor.theEditor.debugOutput(
'Error reading document: ' + fileName +
'. Error: ' + error, 2,
false);
2984 const reader =
new FileReader();
2985 reader.readAsText(file,
'UTF-8');
2986 reader.accept =
'.mtlx';
2989 console.log(
'loadFromFile:', file, fileName);
2991 reader.onload =
function (e) {
2993 let fileContents = e.target.result;
2994 console.log(
"read file: ", file.name,
" with extension: ", extension,
" and length: ", fileContents.length);
2996 that.loadFromString(extension, fileContents, fileName, auto_arrange);
2999 MxShadingGraphEditor.theEditor.debugOutput(
'Error reading document: ' + fileName +
'. Error: ' + error, 2,
false);
3003 editor.debugOutput(
"MaterialX is not initialized", 2,
false);
3022 var generator =
new ne_mx.EsslShaderGenerator();
3023 var genContext =
new ne_mx.GenContext(generator);
3026 console.log(
'Loaded standard libraries:',
stdlib.getNodeDefs().length);
3040 if (name.length == 0) {
3042 msg =
'Setting empty name as "blank"';
3049 var nodes =
graph._nodes;
3051 for (var node of nodes) {
3052 nodenames.push(node.title);
3056 name =
ne_mx.createValidName(name);
3058 if (!nodenames.includes(name)) {
3063 var rootName = name;
3065 var number = name.match(/\d+$/);
3067 i = (parseInt(number) + 1)
3068 rootName = name.slice(0, -number[0].length);
3071 var valid_name = rootName + i.toString();
3072 while (nodenames.includes(valid_name)) {
3074 valid_name = rootName + i.toString();
3096 this.fontSizeStyle =
'font-size: 11px;';
3099 let gltfConverter =
new glTFMaterialX();
3100 this.handler.addConverter(gltfConverter);
3102 console.log(
'Create new editor with exporter for:', gltfConverter.exportType());
3121 setDirty(w =
null, h =
null) {
3123 graph.setDirtyCanvas(
true,
true);
3137 debugOutput(text, severity, clear =
null) {
3140 consoleLog(text, severity, clear);
3143 console.log(
'> ', text,
' severity:', severity);
3153 setSourceColorSpace(colorSpace) {
3155 this.handler.setSourceColorSpace(colorSpace);
3165 setTargetDistanceUnit(unit) {
3167 this.handler.setTargetDistanceUnit(unit);
3176 getSourceColorSpace() {
3178 return this.handler.getSourceColorSpace();
3180 return 'lin_rec709';
3188 getTargetDistanceUnit() {
3190 return this.handler.getTargetDistanceUnit();
3201 arrangeGraph(spacing = 80) {
3213 for (var s in selected) {
3214 var node = selected[s];
3215 if (node.type ==
'graph/subgraph') {
3248 this.handler.sourceColorSpace = this.handler.DEFAULT_COLOR_SPACE;
3249 this.handler.targetDistanceUnits = this.handler.DEFAULT_DISTANCE_UNITS;
3251 this.updatePropertyPanel(
null);
3265 saveSerialization() {
3266 var data = JSON.stringify(
graph.serialize(),
null, 2);
3267 var blob =
new Blob([data], { type:
"text/plain" });
3268 var url = URL.createObjectURL(blob);
3269 var a = document.createElement(
"a");
3271 a.download =
"serialized_graph.json";
3278 loadSerialization() {
3281 var input = document.createElement(
"input");
3282 input.style = this.fontSizeStyle;
3283 input.type =
"file";
3284 input.accept =
".json";
3285 input.onchange =
function (e) {
3286 var file = e.target.files[0];
3287 var reader =
new FileReader();
3288 reader.onload =
function (event) {
3289 var data = JSON.parse(event.target.result);
3290 graph.configure(data);
3292 reader.readAsText(file);
3304 saveGraphToFile(extension, graphWriteOptions) {
3305 if (this.handler.canExport(extension)) {
3306 this.handler.saveGraphToFile(extension,
graph, graphWriteOptions);
3310 this.debugOutput(
'Unsupported extension for saving graph:' + extension, 2,
false);
3320 saveGraphToString(extension, graphWriteOptions) {
3321 if (this.handler.canExport(extension)) {
3322 return this.handler.saveGraphToString(extension,
graph, graphWriteOptions);
3326 this.debugOutput(
'Unsupported extension for saving graph: ' + extension, 2,
false);
3337 loadDefinitionsFromFile(extension) {
3338 if (extension ==
'mtlx') {
3339 this.handler.loadDefinitionsFromFile();
3343 this.debugOutput(
'Unsupported extension for loading definitions: ' + extension, 2,
false);
3354 loadGraphFromFile(extension, auto_arrange) {
3356 if (!this.handler.canImport(extension)) {
3357 this.debugOutput(
'Unsupported extension for loading graph: ' + extension, 2,
false);
3362 var input = document.createElement(
"input");
3363 input.style = this.fontSizeStyle;
3364 input.type =
"file";
3365 input.accept =
"." + this.handler.getExtension();
3366 input.onchange =
function (e) {
3367 var file = e.target.files[0];
3368 console.log(
'Loading file: ' + file.name);
3379 findRenderableItems() {
3380 return this.handler.findRenderableItems(
graph);
3392 loadGraphFromString(extension, content, fileName, auto_arrange) {
3393 if (!this.handler.canImport(extension)) {
3394 this.debugOutput(
'Unsupported extension for loading graph: ' + extension, 2,
false);
3398 if (content.length > 0)
3399 this.handler.loadFromString(extension, content, fileName, auto_arrange);
3412 console.log(
'rgbToHex empty !', rgb);
3415 return '#' + rgb.map(x => {
3416 var hex = Math.round(x * 255).toString(16);
3417 return hex.length === 1 ?
'0' + hex : hex;
3429 createButtonWithImageAndText(imageSrc, text,
id) {
3431 var img = document.createElement(
"img");
3432 img.id =
id +
"_img";
3434 img.classList.add(
"img-fluid");
3437 var span = document.createElement(
"span");
3438 span.id =
id +
"_text";
3439 span.textContent =
" " + text;
3442 var button = document.createElement(
"button");
3444 button.classList.add(
"btn",
"btn-sm",
"btn-outline-secondary",
"form-control",
"form-control-sm");
3445 button.style = this.fontSizeStyle;
3446 button.appendChild(img);
3447 button.appendChild(span);
3459 openImageDialog(theNode, updateProp, wantURI) {
3462 var fileInput = document.createElement(
'input');
3463 fileInput.type =
'file';
3464 fileInput.accept =
'image/*';
3465 fileInput.style.display =
'none';
3466 document.body.appendChild(fileInput);
3471 fileInput.addEventListener(
'change',
function () {
3472 var fileURI = fileInput.value.split(
'\\').pop();
3473 var file = fileInput.files[0];
3475 fileURI = URL.createObjectURL(file);
3477 var updateElementId =
'__pp:' + updateProp;
3478 var textInput = document.getElementById(updateElementId);
3480 textInput.value = fileURI;
3481 theNode.setProperty(updateProp, fileURI);
3483 var propertypanel_preview = document.getElementById(
'propertypanel_preview');
3484 if (propertypanel_preview) {
3485 propertypanel_preview.src = URL.createObjectURL(file);
3486 propertypanel_preview.style.display =
"block";
3489 var previewImage =
false;
3491 if (propertypanel_preview) {
3492 var reader =
new FileReader();
3493 reader.onload =
function (event) {
3494 propertypanel_preview.src =
event.target.result;
3498 reader.readAsDataURL(file);
3499 propertypanel_preview.style.display =
"block";
3503 document.body.removeChild(fileInput);
3517 return Promise.resolve(
true);
3519 return Promise.resolve(
false);
3523 console.log(
'Error checking URI:', error);
3524 return Promise.resolve(
false);
3535 createColorSpaceInput(colorSpaces, activeItem) {
3536 var select = document.createElement(
"select");
3537 select.className =
"form-control form-control-sm";
3538 select.style = this.fontSizeStyle;
3539 select.id =
"propertypanel_colorspace";
3540 for (var i = 0; i < colorSpaces.length; i++) {
3541 var option = document.createElement(
"option");
3542 option.value = colorSpaces[i];
3543 option.text = colorSpaces[i];
3547 var option = document.createElement(
"option");
3548 option.value =
"none";
3549 option.text =
"none";
3552 select.value = activeItem;
3564 createUnitsInput(units, unittype, activeItem) {
3565 var select = document.createElement(
"select");
3566 select.className =
"form-control form-control-sm";
3567 select.style = this.fontSizeStyle;
3568 select.id =
"propertypanel_units";
3569 for (var i = 0; i < units.length; i++) {
3570 var option = document.createElement(
"option");
3571 var unit_pair = units[i];
3572 if (unit_pair[1] == unittype) {
3573 option.value = unit_pair[0];
3574 option.text = unit_pair[0];
3578 select.value = activeItem;
3588 updateImagePreview(curImage) {
3589 var propertypanel_preview = document.getElementById(
'propertypanel_preview');
3590 if (curImage && propertypanel_preview) {
3591 this.uriExists(curImage)
3594 propertypanel_preview.src = curImage;
3595 propertypanel_preview.style.display =
"block";
3598 propertypanel_preview.src =
"./Icons/no_image.png";
3599 propertypanel_preview.style.display =
"block";
3616 updatePropertyPanel(node) {
3619 if (!propertypanelcontent) {
3620 console.error(
'No property panel content widget found!');
3624 while (propertypanelcontent.firstChild) {
3625 propertypanelcontent.removeChild(propertypanelcontent.firstChild);
3630 if (node && node.nodedef_icon) {
3631 panelIcon.src = node.nodedef_icon;
3633 else if (this.ui.icon_map) {
3635 panelIcon.src = this.ui.icon_map[
'_default_graph_'];
3637 panelIcon.src = this.ui.icon_map[
'_default_'];
3640 propertypanelcontent.innerHTML =
"";
3642 let colorSpaces = this.handler.getColorSpaces();
3643 let targetUnits = this.handler.getUnits();
3649 else if (!node && !
graphcanvas.graph._is_subgraph) {
3650 var docInfo = [[
'Colorspace', this.getSourceColorSpace()],
3651 [
'Distance', this.getTargetDistanceUnit()]];
3653 for (let item of docInfo) {
3655 let elem = document.createElement(
"div");
3656 elem.className =
"row px-1 py-0";
3657 let label = document.createElement(
"div");
3658 label.className =
"col py-0 col-form-label-sm text-left";
3659 label.style = this.fontSizeStyle;
3660 label.innerHTML =
"<b>" + item[0] +
"</b>";
3661 elem.appendChild(label);
3663 if (item[0] ==
'Colorspace' && colorSpaces.length > 0) {
3665 var inputCol = document.createElement(
"div");
3666 inputCol.className =
"col text-left";
3667 var select = this.createColorSpaceInput(colorSpaces, item[1]);
3668 select.onchange =
function (e) {
3671 inputCol.appendChild(select);
3672 elem.appendChild(inputCol);
3674 else if (item[0] ==
'Distance' && targetUnits.length > 0) {
3676 var inputCol = document.createElement(
"div");
3677 inputCol.className =
"col text-left";
3678 var select = this.createUnitsInput(targetUnits,
'distance', item[1]);
3679 select.onchange =
function (e) {
3682 inputCol.appendChild(select);
3683 elem.appendChild(inputCol);
3696 propertypanelcontent.appendChild(elem);
3701 var _category = node.nodedef_node;
3702 var _type = node.nodedef_type;
3704 var isNodeGraph = node.type ==
'graph/subgraph';
3706 _category =
'nodegraph';
3708 if (node.outputs.length > 1) {
3711 else if (node.outputs.length > 0) {
3712 _type = node.outputs[0].type;
3720 if (_category ==
'surfacematerial') {
3726 var elem = document.createElement(
"div");
3727 elem.className =
"row px-1 py-1";
3730 var label = document.createElement(
"div");
3731 label.className =
"col-4 px-1 py-0 col-form-label-sm text-end";
3732 label.style = this.fontSizeStyle;
3733 label.innerHTML =
"<b>" + _category;
3734 if (_type.length > 0) {
3735 label.innerHTML +=
'<br>' + _type;
3737 label.innerHTML +=
"</b>";
3738 elem.appendChild(label);
3741 var inputCol = document.createElement(
"div");
3742 inputCol.className =
"col py-0";
3743 var nameInput = document.createElement(
"input");
3744 nameInput.style = this.fontSizeStyle;
3745 nameInput.type =
"text";
3746 nameInput.value = node.title;
3747 nameInput.className =
"form-control form-control-sm";
3749 nameInput.onchange =
function (e) {
3750 var oldTitle = node.title;
3752 if (newTitle != oldTitle)
3754 that.monitor.onNodeRenamed(node, newTitle);
3755 node.title = newTitle;
3757 e.target.value = node.title;
3759 if (node.graph._is_subgraph) {
3760 if (node.nodedef_node ==
'input') {
3762 node.graph.renameInput(oldTitle, node.title);
3764 else if (node.nodedef_node ==
'output') {
3766 node.graph.renameOutput(oldTitle, node.title);
3771 node.setSize(node.computeSize());
3772 node.setDirtyCanvas(
true,
true);
3774 inputCol.appendChild(nameInput);
3777 if (node.nodedef_node !=
'input' && node.nodedef_node !=
'output'
3778 && node.type !=
'graph/subgraph') {
3779 var imagePreview = document.createElement(
"img");
3780 imagePreview.src =
"./Icons/no_image.png";
3781 var previewSet =
false;
3783 imagePreview.style.display =
"none";
3784 imagePreview.src =
"./Icons/no_image.png";
3797 imagePreview.id =
"propertypanel_preview";
3798 imagePreview.className =
"img-fluid form-control form-control-sm";
3799 inputCol.appendChild(imagePreview);
3802 elem.appendChild(label);
3803 elem.appendChild(inputCol);
3808 var filterCol = document.createElement(
"div");
3809 filterCol.className =
"col-2 py-0";
3810 filterCol.width = 16;
3811 var filterIcon = document.createElement(
"button");
3814 if (node.showDefaultValueInputs ==
null)
3816 node.showDefaultValueInputs =
true;
3818 var img = document.createElement(
"img");
3819 if (node.showDefaultValueInputs)
3821 img.src =
"./Icons/funnel_white.svg";
3822 filterIcon.className =
"btn btn-sm btn-outline-secondary";
3826 img.src =
"./Icons/funnel-fill_white.svg";
3827 filterIcon.className =
"btn btn-sm btn-outline-warning";
3829 filterIcon.appendChild(img);
3830 filterIcon.onclick =
function (e) {
3831 node.showDefaultValueInputs = !node.showDefaultValueInputs;
3834 filterCol.appendChild(filterIcon);
3835 elem.appendChild(filterCol);
3838 propertypanelcontent.appendChild(elem);
3840 var hr = document.createElement(
"hr");
3841 hr.classList.add(
"my-1");
3842 propertypanelcontent.appendChild(hr);
3844 var current_details =
null;
3845 var first_details =
true;
3846 var nodeInputs = node.inputs
3848 let targetNodes = [];
3849 for (var i in nodeInputs) {
3850 let nodeInput = nodeInputs[i];
3852 let inputName = nodeInput.name;
3853 let nodeInputLink = nodeInput.link;
3854 let uiName = inputName;
3856 uiName = uiName.replace(/_/g,
' ');
3859 let colorspace =
'';
3861 let defaultgeomprop =
'';
3865 let property_info = node.getPropertyInfo(inputName);
3868 var skipInterorConnectedInput =
false;
3869 if (node.graph._is_subgraph) {
3872 var sg_node = node.graph._subgraph_node;
3875 var slot = sg_node.findInputSlot(node.title);
3877 if (sg_node.inputs) {
3879 var slotInput = sg_node.inputs[slot];
3881 if (slotInput !=
null && slotInput.link !=
null) {
3882 skipInterorConnectedInput =
true;
3892 if (skipInterorConnectedInput) {
3893 console.log(
'Skip interior connected input: ', nodeInput);
3898 if (property_info) {
3899 if (property_info.defaultgeomprop)
3901 defaultgeomprop = property_info.defaultgeomprop;
3903 if (property_info.colorspace) {
3904 colorspace = property_info.colorspace;
3906 if (property_info.unit) {
3907 units = property_info.unit;
3909 if (property_info.uiname) {
3910 uiName = property_info.uiname;
3912 if (property_info.uimin) {
3913 uimin = property_info.uimin;
3915 if (property_info.uimax) {
3916 uimax = property_info.uimax;
3918 if (property_info.uifolder && property_info.uifolder.length > 0) {
3920 if (current_details ==
null || current_details.id != property_info.uifolder) {
3922 current_details = document.createElement(
"details");
3923 current_details.id = property_info.uifolder;
3924 current_details.open = first_details;
3925 current_details.classList.add(
'w-100',
'p-1',
'border',
'border-secondary',
'rounded',
'my-1');
3926 first_details =
false;
3927 var summary = document.createElement(
'summary')
3928 summary.style = this.fontSizeStyle;
3929 summary.innerHTML =
"<b>" + property_info.uifolder +
"</b>"
3931 current_details.appendChild(summary);
3939 current_details =
null;
3944 current_details =
null;
3950 if (nodeInputLink) {
3951 let upstreamLink =
null;
3953 let nodegraph = node.graph;
3954 let link = nodegraph.links[nodeInputLink];
3956 let linkId = link && link.origin_id;
3957 let linkNode = linkId && nodegraph.getNodeById(linkId);
3962 let linkSlot = link.origin_slot;
3964 let linkOutput = linkNode.outputs[linkSlot];
3966 upstreamLink = linkNode.title +
'.' + linkOutput.name;
3969 let
id =
"__pp:" + inputName;
3970 let buttonText = upstreamLink;
3972 if (buttonText.length > 15) {
3973 buttonText = buttonText.substring(0, 15) +
"...";
3975 let input = this.createButtonWithImageAndText(
"./Icons/arrow_up_white.svg", buttonText,
id);
3977 input.onclick =
function (e) {
3979 var inputName = e.target.id;
3980 inputName = inputName.replace(
'__pp:',
'');
3981 inputName = inputName.replace(
'_text',
'');
3982 inputName = inputName.replace(
'_img',
'');
3983 console.log(
'Clicked traversal button:', inputName);
3985 console.log(
'Jump to node:', linkNode.title);
3988 node.setDirtyCanvas(
true,
true);
3992 elem = document.createElement(
"div");
3993 elem.className =
"row px-1 py-0";
3995 input.id =
"__pp:" + inputName;
3997 var label = document.createElement(
"div");
3999 label.className =
"col-4 px-1 py-0 col-form-label-sm text-end";
4000 label.style = this.fontSizeStyle;
4001 label.innerHTML = uiName;
4002 label.for = input.id;
4003 elem.appendChild(label);
4006 if (useFormControl) {
4007 input.classList.add(
"form-control");
4009 input.classList.add(
"form-control-sm");
4012 input.disabled =
true;
4014 var propvalue = document.createElement(
"div");
4015 propvalue.className =
"col p-1";
4016 propvalue.appendChild(input);
4018 elem.appendChild(propvalue);
4024 targetNodes[i] = node;
4025 let targetNode = targetNodes[i];
4026 let propertyKey = inputName;
4028 var
property = targetNode.properties[inputName];
4029 if (property ==
null) {
4031 var subgraph = targetNode.subgraph;
4034 var subNode = subgraph.findNodeByTitle(inputName);
4036 targetNodes[i] = subNode;
4038 property = targetNodes[i].properties[
'in'];
4043 if (property ==
null) {
4044 console.log(
'Update: Cannot find property value for input:', inputName);
4050 if (defaultgeomprop)
4057 if (!node.showDefaultValueInputs && !isNodeGraph)
4059 let isDefault = node.isDefaultValue(inputName);
4067 elem = document.createElement(
"div");
4068 elem.className =
"row px-1 py-0";
4071 var input_btn =
null;
4072 let input_slider =
null;
4073 var colorspace_unit_btn =
null;
4074 var useFormControl =
true;
4077 if (colorspace.length > 0) {
4081 colorspace_unit_btn = this.createColorSpaceInput(colorSpaces, colorspace);
4082 let theNode = targetNodes[i];
4083 colorspace_unit_btn.onchange =
function (e) {
4085 theNode.setPropertyInfo(inputName,
'colorspace', e.target.value);
4088 else if (units.length > 0 && property_info.unittype) {
4090 colorspace_unit_btn = this.createUnitsInput(targetUnits, property_info.unittype, units);
4091 let theNode = targetNodes[i];
4092 colorspace_unit_btn.onchange =
function (e) {
4093 theNode.setPropertyInfo(inputName,
'unit', e.target.value);
4097 var proptype = nodeInput.type;
4098 if (proptype ==
'float' || proptype ==
'integer') {
4099 var isFloat = proptype ==
'float';
4101 input = document.createElement(
"input");
4102 input.id = propertyKey +
'_box';
4103 input.style = this.fontSizeStyle;
4104 input.type =
'number';
4105 input.classList.add(
"form-control",
"form-control-sm",
"ps-0");
4106 input.setAttribute(
'propertyKey', propertyKey);
4108 input_slider = document.createElement(
"input");
4109 input_slider.id = propertyKey +
'_slider';
4111 input_slider.type =
'range';
4112 input_slider.classList.add(
'form-range',
'custom-slider',
'pe-0');
4113 input_slider.setAttribute(
'propertyKey', propertyKey);
4119 input.min = Math.min(property, 0);
4127 input.max = Math.max(property*3, 10.0);
4130 input.max = Math.max(property*3, 100);
4135 input_slider.min = input.min;
4136 input_slider.max = input.max;
4138 input.step = (input.max - input.min) / 100.0;
4139 input_slider.step = input.step;
4142 input_slider.step = 1;
4146 input.value = input_slider.value = property;
4156 let theSlider = input_slider;
4157 let theNode = targetNodes[i];
4158 input_slider.onchange =
function (e) {
4159 var pi = e.target.getAttribute(
'propertyKey');
4160 var val = parseFloat(e.target.value);
4161 theNode.setProperty(pi, val);
4164 input_slider.oninput =
function(e) {
4165 var pi = e.target.getAttribute(
'propertyKey');
4166 var val = parseFloat(e.target.value);
4167 theNode.setProperty(pi, val);
4168 theBox.value = e.target.value;
4171 input.onchange =
function (e) {
4172 var pi = e.target.getAttribute(
'propertyKey');
4173 var val = parseFloat(e.target.value);
4174 theNode.setProperty(pi, val);
4177 input.oninput =
function(e) {
4178 var pi = e.target.getAttribute(
'propertyKey');
4179 var val = parseFloat(e.target.value);
4180 theNode.setProperty(pi, val);
4181 theSlider.value = e.target.value;
4184 else if (proptype ==
'string' || proptype ==
'filename') {
4185 input = document.createElement(
"input");
4186 input.style = this.fontSizeStyle;
4187 input.type =
"text";
4188 if (proptype ==
'filename') {
4189 var curImage = property;
4190 this.updateImagePreview(curImage);
4192 input_btn = document.createElement(
"button");
4193 input_btn.classList.add(
"btn",
"btn-sm",
"btn-outline-secondary");
4194 input_btn.innerHTML =
"+";
4195 input_btn.setAttribute(
'propertyKey', propertyKey);
4196 var fileId =
"__pp:" + inputName;
4197 let theNode = targetNodes[i];
4198 input_btn.onclick =
function (e) {
4199 var pi = e.target.getAttribute(
'propertyKey');
4207 console.log(
'------------------- handle enum property info:', property_info,
'. property:', property);
4208 if (property_info && property_info.enum) {
4210 console.log(
'----------------- found enum property info:', property_info.enum);
4213 input = document.createElement(
"select");
4214 input.style = this.fontSizeStyle;
4215 input.classList.add(
"form-control",
"form-control-sm");
4217 input.setAttribute(
'propertyKey', propertyKey);
4218 let theNode = targetNodes[i];
4219 let enums = property_info.enum;
4220 for (let j = 0; j < enums.length; j++) {
4221 let option = document.createElement(
"option");
4222 option.value = enums[j];
4223 option.text = enums[j];
4226 input.value = property;
4227 input.setAttribute(
'propertyKey', propertyKey);
4228 input.onchange =
function (e) {
4229 var pi = e.target.getAttribute(
'propertyKey');
4230 theNode.setProperty(pi, e.target.value);
4236 if (property_info && !property_info.enm) {
4237 input.value = property;
4238 input.setAttribute(
'propertyKey', propertyKey);
4239 let theNode = targetNodes[i];
4240 let isFilename = proptype ==
'filename';
4242 input.onchange =
function (e) {
4243 var pi = e.target.getAttribute(
'propertyKey');
4245 theNode.setProperty(pi, e.target.value);
4248 that.updateImagePreview(e.target.value);
4256 else if (proptype ==
'boolean') {
4258 input = document.createElement(
"input");
4259 input.style = this.fontSizeStyle;
4260 input.type =
"checkbox";
4261 input.classList =
"form-check-input";
4262 useFormControl =
false;
4263 input.checked = property;
4264 input.setAttribute(
'propertyKey', propertyKey);
4265 let theNode = targetNodes[i];
4266 input.onchange =
function (e) {
4267 var pi = e.target.getAttribute(
'propertyKey');
4269 theNode.setProperty(pi, e.target.checked);
4274 else if (proptype ==
'vector2' || proptype ==
'vector3' || proptype ==
'vector4')
4277 var vector_size = [
'vector2',
'vector3',
'vector4'].indexOf(proptype) + 2;
4278 input = document.createElement(
"div");
4279 useFormControl =
false;
4281 input.className =
"row py-1 ps-4 pe-0";
4283 for (let v=0; v<vector_size; v++)
4286 let subinput = document.createElement(
"input");
4287 subinput.style = this.fontSizeStyle;
4288 subinput.type =
'number';
4289 subinput.classList.add(
"form-control");
4290 subinput.classList.add(
"form-control-sm");
4291 subinput.setAttribute(
'propertyKey', propertyKey);
4293 let subinput_slider = document.createElement(
"input");
4294 subinput_slider.id = propertyKey +
'_slider';
4295 subinput_slider.type =
'range';
4296 subinput_slider.classList.add(
'form-range',
'custom-slider',
'pe-0');
4297 subinput_slider.setAttribute(
'propertyKey', propertyKey);
4300 subinput.min = uimin[v];
4303 subinput.min = Math.min(property[v]*3, 0);
4306 subinput.max = uimax[v];
4309 subinput.max = Math.max(property[v]*3, 10.0);
4312 subinput_slider.min = subinput.min;
4313 subinput_slider.max = subinput.max;
4314 subinput.step = (subinput.max - subinput.min) / 100.0;
4315 subinput_slider.step = subinput.step;
4317 subinput.value = subinput_slider.value =
property[v];
4319 let theNode = targetNodes[i];
4320 let vector_index = v;
4321 let theBox = subinput;
4322 let theSlider = subinput_slider;
4323 theBox.onchange =
function (e) {
4324 let pi = e.target.getAttribute(
'propertyKey');
4325 let value = parseFloat(e.target.value);
4326 let newValue = theNode.properties[pi].map(item => item);
4327 newValue[vector_index] = value;
4328 theNode.setProperty(pi, newValue);
4332 theBox.oninput =
function(e) {
4333 let pi = e.target.getAttribute(
'propertyKey');
4334 let value = parseFloat(e.target.value);
4335 let newValue = theNode.properties[pi].map(item => item);
4336 newValue[vector_index] = value;
4337 theNode.setProperty(pi, newValue);
4338 theSlider.value = e.target.value;
4341 theSlider.onchange =
function (e) {
4342 let pi = e.target.getAttribute(
'propertyKey');
4343 let value = parseFloat(e.target.value);
4344 let newValue = theNode.properties[pi].map(item => item);
4345 newValue[vector_index] = value;
4346 theNode.setProperty(pi, newValue);
4349 theSlider.oninput =
function(e) {
4350 let pi = e.target.getAttribute(
'propertyKey');
4351 let value = parseFloat(e.target.value);
4352 let newValue = theNode.properties[pi].map(item => item);
4353 newValue[vector_index] = value;
4354 theNode.setProperty(pi, newValue);
4355 theBox.value = e.target.value;
4359 let propvalue_slider = document.createElement(
"div");
4360 propvalue_slider.className =
"col p-0";
4361 propvalue_slider.appendChild(subinput_slider);
4363 let propvalue_box = document.createElement(
"div");
4364 propvalue_box.className =
"col p-0";
4365 propvalue_box.appendChild(subinput);
4367 let input_row = document.createElement(
"div");
4368 input_row.className =
"row p-0";
4369 input_row.appendChild(propvalue_slider);
4370 input_row.appendChild(propvalue_box);
4372 input.appendChild(input_row);
4375 else if (proptype ==
'color3' || proptype ==
'color4') {
4376 input = document.createElement(
"input");
4377 input.type =
"color";
4379 input.value = this.rgbToHex(property);
4380 input.setAttribute(
'propertyKey', propertyKey);
4381 let theNode = targetNodes[i];
4382 input.onchange =
function (e) {
4384 var hex = e.target.value;
4386 rgb[0] = parseInt(hex.substring(1, 3), 16) / 255.0;
4387 rgb[0] = parseFloat(rgb[0].toFixed(fprecision));
4388 rgb[1] = parseInt(hex.substring(3, 5), 16) / 255.0;
4389 rgb[1] = parseFloat(rgb[1].toFixed(fprecision));
4390 rgb[2] = parseInt(hex.substring(5, 7), 16) / 255.0;
4391 rgb[2] = parseFloat(rgb[2].toFixed(fprecision));
4393 var pi = e.target.getAttribute(
'propertyKey');
4394 theNode.setProperty(pi, rgb);
4396 let func =
function (e) {
4398 var hex = e.target.value;
4399 var rgb = [0, 0, 0];
4401 rgb[0] = parseInt(hex.substring(1, 3), 16) / 255.0;
4402 rgb[0] = parseFloat(rgb[0].toFixed(fprecision));
4403 rgb[1] = parseInt(hex.substring(3, 5), 16) / 255.0;
4404 rgb[1] = parseFloat(rgb[1].toFixed(fprecision));
4405 rgb[2] = parseInt(hex.substring(5, 7), 16) / 255.0;
4406 rgb[2] = parseFloat(rgb[2].toFixed(fprecision));
4408 var pi = e.target.getAttribute(
'propertyKey');
4409 theNode.setProperty(pi, rgb);
4411 input.onchange = func;
4412 input.oninput = func;
4415 input = document.createElement(
"input");
4416 input.style = this.fontSizeStyle;
4417 input.type =
"text";
4418 input.value = property;
4419 let propertyKey = inputName;
4420 let theNode = targetNodes[i];
4421 input.onchange =
function (e) {
4422 theNode.setProperty(propertyKey, e.target.value);
4428 input.id =
"__pp:" + inputName;
4431 var label = document.createElement(
"div");
4432 label.className =
"col-4 p-0 col-form-label-sm text-end";
4433 label.style = this.fontSizeStyle;
4434 label.innerHTML = uiName;
4435 label.for = input.id;
4436 elem.appendChild(label);
4439 if (useFormControl) {
4440 input.classList.add(
"form-control");
4442 input.classList.add(
"form-control-sm");
4445 input.disabled =
true;
4447 var propvalue = document.createElement(
"div");
4448 propvalue.className =
"col py-0";
4451 propvalue.classList.add(
'ps-1');
4453 propvalue.appendChild(input);
4456 var propbutton = document.createElement(
"div");
4457 propbutton.className =
"col-2 py-0";
4459 propbutton.appendChild(input_btn);
4460 elem.appendChild(propbutton);
4462 if (colorspace_unit_btn) {
4464 var propbutton = document.createElement(
"div");
4465 propbutton.className =
"col col-form-label-sm";
4466 var details = document.createElement(
"details");
4467 var summary = document.createElement(
'summary')
4468 summary.style = this.fontSizeStyle;
4469 if (colorspace.length > 0)
4470 summary.innerHTML =
"Colorspace";
4471 else if (targetUnits.length > 0)
4472 summary.innerHTML =
"Units";
4473 details.appendChild(summary);
4474 details.appendChild(colorspace_unit_btn);
4475 propbutton.appendChild(details);
4476 propvalue.appendChild(propbutton);
4481 var propvalue_slider = document.createElement(
"div");
4482 propvalue_slider.className =
"col py-0 pe-0";
4483 propvalue_slider.appendChild(input_slider);
4484 elem.appendChild(propvalue_slider);
4487 elem.appendChild(propvalue);
4492 if (current_details) {
4494 current_details.appendChild(elem);
4496 if (current_details.parentElement ==
null) {
4497 propertypanelcontent.appendChild(current_details);
4501 propertypanelcontent.appendChild(elem);
4517 initializeLiteGraph(canvas, readOnly =
false) {
4519 graph =
new LiteGraph.LGraph();
4534 graphcanvas.default_connection_color_byTypeOff = {
4573 let parentGraph =
'';
4576 parentGraph =
graphcanvas.graph._subgraph_node.title;
4586 let parentGraph =
'';
4589 parentGraph =
graphcanvas.graph._subgraph_node.title;
4597 LGraphNode.prototype.setPropertyInfo =
function(property, propertyInfo, value)
4601 if (this.properties_info) {
4602 for (var i = 0; i < this.properties_info.length; ++i) {
4603 if (this.properties_info[i].name == property) {
4604 info = this.properties_info[i];
4610 if (info && info[propertyInfo])
4612 if (this.onPropertyInfoChanged)
4614 this.onPropertyInfoChanged(property, propertyInfo, value, info[propertyInfo]);
4616 info[propertyInfo] = value;
4620 console.warning(
'Failed to set property: ', property,
'. info: ', propertyInfo,
'. Value: ', value,
'. Infos: ', this.properties_info);
4625 LGraphNode.prototype.isDefaultValue =
function(property)
4630 if (this.properties[property] ==
null)
4632 console.warn(
'> Property value does not exist:', property);
4636 if (this.getInputLink(property))
4641 if (this.properties_info !=
null)
4643 for (let i = 0; i < this.properties_info.length; ++i) {
4644 if (this.properties_info[i].name == property) {
4645 info = this.properties_info[i];
4651 if (info !=
null && info.default_value !=
null)
4653 let property_string = this.properties[property];
4654 let default_value_string = info.default_value;
4655 let isDefault =
false;
4656 if (Array.isArray(default_value_string)) {
4657 default_value_string = default_value_string.map(String);
4658 property_string = property_string.map(String);
4659 isDefault = (JSON.stringify(default_value_string) == JSON.stringify(property_string));
4663 isDefault = (default_value_string == property_string);
4669 console.warn(
'> Default value does not exist for:', property);
4678 this.monitor.monitorGraph(
graph,
true);
4686 console.log(
'> Read only mode: ', readOnly);
4702 graph.ctrl_shift_v_paste_connect_unselected_outputs =
true;
4708 canvas.addEventListener(
"keydown",
function (e) {
4709 if (e.key ===
"f") {
4715 canvas.addEventListener(
"keydown",
function (e) {
4716 if (e.key ===
"l") {
4723 var context = canvas.getContext(
'2d');
4725 function drawstart(event) {
4728 console.log(
'>>>>>>>>>>> draw start');
4732 function drawmove(event) {
4736 console.log(
'>>>>>>>>>>> draw move');
4739 function drawend(event) {
4742 console.log(
'>>>>>>>>>>> draw move');
4746 function touchstart(event) {
4747 drawstart(event.touches[0]);
4750 function touchmove(event) {
4751 drawmove(event.touches[0]);
4755 function touchend(event) {
4756 drawend(event.changedTouches[0]);
4774 var haveSelected =
false;
4775 for (var s in selected) {
4776 haveSelected =
true;
4779 console.log(
'Center nodes:', selected,
'. Have selected:', haveSelected);
4787 LiteGraph.searchbox_extras = [];
4788 var nodeTypes = LiteGraph.registered_node_types;
4789 for (var typeName in nodeTypes) {
4790 if (typeName !==
"graph/subgraph") {
4791 console.log(
'Removing node type:', LiteGraph.getNodeType(typeName));
4792 LiteGraph.unregisterNodeType(typeName);
4800 collapseNode(node, collapse) {
4801 if (node.constructor.collapsable ===
false) {
4804 if (node.flags.collapsed != collapse) {
4805 node.flags.collapsed = collapse;
4814 collapseExpandNodes(collapse) {
4819 var modified =
false;
4820 if (selected_nodes) {
4821 for (var i in selected_nodes) {
4822 var node = selected_nodes[i];
4824 if (this.collapseNode(node, collapse))
4829 var nodes = curGraph._nodes;
4830 for (var i in nodes) {
4831 var node = nodes[i];
4832 if (this.collapseNode(node, collapse))
4839 graph.setDirtyCanvas(
true,
true);
4853 pasteFromClipboard() {
4860 extractNodeGraph() {
4862 if (selected.length == 0) {
4863 console.log(
'No nodes selected.');
4867 var subgraphsSelected = []
4868 for (var i in selected) {
4869 var node = selected[i];
4870 if (node.type ==
'graph/subgraph') {
4871 subgraphsSelected.push(node);
4874 if (subgraphsSelected.length == 0) {
4875 console.log(
'No subgraphs selected.');
4880 var subGraph = subgraphsSelected[0];
4881 var subGraphNodes = subGraph.subgraph._nodes;
4882 for (var i in subGraphNodes) {
4883 var node = subGraphNodes[i];
4904 this.debugOutput(
'Cannot create nest subgraphs.', 1);
4910 if (selected.length == 0) {
4911 console.log(
'No nodes selected.');
4919 var node = LiteGraph.createNode(
'graph/subgraph');
4927 node.subgraph.arrange(80);
4935 displayNodeTypes() {
4937 var nodeTypesListUpdater = this.ui.nodeTypesListUpdater;
4938 if (!nodeTypesListUpdater) {
4943 var nodeTypes = LiteGraph.registered_node_types;
4944 nodeTypesListUpdater(nodeTypes);
4956 initialize(canvas, ui, monitor, materialFilename, readOnly =
false) {
4960 console.log(
'Set custom monitor:', monitor.getName());
4962 this.monitor = monitor;
4963 this.initializeLiteGraph(canvas, readOnly);
4965 this.handler.setMonitor(this.monitor);
Base class for graph handlers.
getColorSpaces()
Get the color spaces used by the handler.
setMonitor(monitor)
Set the monitor for the handler.
getDefaultValue(value, _type)
Get default value as a string for the given value and type.
setSourceColorSpace(colorSpace)
Set the source color space for the handler.
canImport(extension)
Return if the handler can import the given extension / format.
createValidName(name)
Create a valid name for the given name.
getImporter(extension='')
Find the first importer that can import the given extension / format.
getUnits()
Get the units used by the handler.
setTargetDistanceUnit(unit)
Set the target distance unit for the handler.
initialize(editor)
Initialize the handler for the given editor.
getSourceColorSpace()
Get the source color space for the handler.
getTargetDistanceUnit()
Get the target distance unit for the handler.
getExporter(extension='')
Find the first exporter that can export to the given extension / format.
getExtension()
Get the extension /format for the handler.
canExport(extension)
Return if the handler can export to the given extension / format.
constructor(id, extension)
setColorSpaces(colorSpaces)
Set the color spaces used by the handler.
setUnits(units)
Set the units used by the handler.
addConverter(converter)
Add a converter to the handler.
This class provides a monitoring interface for the graph editor.
debugMessage(text, path)
Output a debug message to the console.
onConnectOutput(slot, input_type, input, target_node, target_slot, node)
Callback for connection to output.
onNodeDeselected(node, parentGraph)
Callback for when a node is deselected in the graph.
getPath(node, parentGraph)
Get a '/' separated path.
setOnNodeRemoved(callback)
Set node removed callback.
setMonitoring(monitor)
Set the monitoring state of the monitor.
onNodeRemoved(node, parentGraph)
Callback for when a node is removed from the graph.
onNodeSelected(node, parentGraph)
Callback for when a node is selected in the graph.
setOnNodeDeselected(callback)
Set node deselected callback.
onConnectInput(target_slot, output_type, output, source, slot, node)
Callback for connection to output.
getMonitoring()
Get the monitoring state of the monitor.
onPropertyInfoChanged(nodeName, propertyName, propertyInfoName, newValue, previousValue, node)
Callback for when a property info changes on a node in the graph.
onNodeAdded(node, parentGraph)
Callback for when a node is added to the graph.
monitorGraph(theGraph, monitor)
Core monitoring of graph changes.
setOnNodeAdded(callback)
Set node added callback.
setRenderer(theRenderer)
Set the renderer for the monitor.
setOnNodeSelected(callback)
Set node selected callback.
setOnPropertyChanged(callback)
Set property changed callback.
onNodeRenamed(node, newName)
Callback for when a node is renamed in the graph.
setOnConnectionChange(callback)
Set connection change callback.
onPropertyChanged(nodeName, propertyName, newValue, previousValue, node)
Callback for when a property changes on a node in the graph.
getName()
Get the name of the monitor.
onDocumentChange(attribute, value, prevValue)
Callback for when a scene / document level change is made.
setOnNodeRenamed(callback)
Set node renamed callback.
getParentPath(node)
Get the parent path of a node.
onConnectionChange(node, parentGraph)
Callback for when a connection changes in the graph.
This class extends the MxGraphHandler class to provide MaterialX-specific functionality for handling ...
saveGraphToDocument(graph, graphWriteOptions)
Saves the graph to a MaterialX document.
validateDocument(doc)
Validates the provided MaterialX document.
constructor(id, extension)
Constructor for the MxMaterialXHandler class.
buildConnections(editor, node, lg_node, explicitInputs, graph, parentGraph)
Builds the connections between MaterialX nodes.
loadLibraryDocument(editor, materialFilename)
Load the MaterialX document from library into the editor.
loadFromFile(extension, file, fileName, editor, auto_arrange)
Load graph editor from a file.
findRenderableItemsInDoc(mdoc)
Find all renderable items in the MaterialX document.
loadDefinitionsFromFile()
Load MaterialX document containing node definitions from a file.
isArray(_type)
Determines if the specified type is an array type.
initialize(editor, materialFilename)
Initialize the MaterialX handler for the given editor.
createLiteGraphDefinitions(doc, debug, addInputOutputs, definitionsList, libraryPrefix='mtlx', editor, icon='')
Creates LiteGraph node definitions based on the MaterialX document.
buildMetaData(colorSpace, unit, unitType, uiname, uimin, uimax, uifolder, _type)
Builds and returns metadata for a node based on the provided parameters.
loadMaterialXLibraries(stdlib)
Load MaterialX definition libraries.
writeGraphToDocument(mltxgraph, graph, graphWriteOptions)
Writes the graph to the specified MaterialX document.
buildGraphFromDoc(doc, editor, auto_arrange)
Builds the LiteGraph graph from the specified MaterialX document.
saveGraphToFile(extension, graph, graphWriteOptions)
Saves the graph to a file with the specified extension.
loadMaterialX()
Load in the MaterialX library.
saveGraphToString(extension, graph, graphWriteOptions)
Saves the graph to a string in the specified format.
loadFromString(extension, fileContents, fileName, auto_arrange)
Load graph editor from a string.
createValidName(name, msg=null)
Create a valid MaterialX name within the context of the current graph.
findRenderableItems(graph)
Find all MaterialX renderable items in a graph.
loadInputMetaData(node, input, property_info)
Set the meta-data for the specified input based on LiteGraph node property info.
This class is a wrapper around the LiteGraph library to provide a MaterialX node editor.