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' || extension ==
'zip')
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 let doc_string =
ne_mx.writeToXmlString(outputDoc);
1819 if (writeCustomLibs) {
1820 console.log(
'Write custom libraries:',
customlibs.length);
1822 outputDoc.copyContentFrom(customlib[1]);
1824 console.log(
'Write document custom definitions:',
customDocLibs.length);
1826 outputDoc.copyContentFrom(customDocLib[1]);
1832 outputDoc.removeAttribute(
'fileprefix');
1850 this.editor.debugOutput(
"MaterialX is not initialized", 2);
1851 return [
'',
'MaterialX is not initialized'];
1856 this.editor.debugOutput(
"Failed to save graph to document", 2);
1857 return [
'',
'Failed to save graph to document'];
1860 if (extension ==
'mtlx')
1862 const writeOptions =
new ne_mx.XmlWriteOptions();
1863 writeOptions.writeXIncludeEnable =
false;
1866 data =
ne_mx.writeToXmlString(outputDoc, writeOptions);
1868 this.editor.debugOutput(
"Failed to write graph:" + e, 2);
1878 this.editor.debugOutput(
'Failed to find ' + extension +
' exporter', 2);
1881 let exportDoc =
ne_mx.createDocument();
1882 exportDoc.copyContentFrom(outputDoc);
1883 exportDoc.importLibrary(
stdlib);
1885 let result = exporter.export(
ne_mx, exportDoc);
1889 return [
'',
'Failed to export graph to ' + extension];
1907 var blob =
new Blob([data[0]], { type:
"text/plain" });
1908 var url = URL.createObjectURL(blob);
1909 var a = document.createElement(
"a");
1911 a.download =
"output_graph.mtlx";
1928 console.log(
'***** START Scan Graph:',
graph.title);
1929 for (var node of
graph._nodes) {
1930 if (node.type ==
'graph/subgraph') {
1931 var subgraph = node.subgraph;
1933 var subgraphNode = mltxgraph.addChildOfCategory(
'nodegraph', node.title);
1935 console.log(
'---->>> Scan NodeGraph:', node.title);
1941 console.log(
'---->>> Scan Node:', node.title);
1943 var nodeDefName = node.nodedef_name;
1952 var nodedefName = node.nodedef_name;
1954 var nodeElement =
null;
1967 nodeElement = mltxgraph.addChildOfCategory(node.nodedef_node, node.nodedef_type);
1968 nodeElement.setType(node.nodedef_type);
1970 if (graphWriteOptions.saveNodePositions) {
1972 nodeElement.setAttribute(
'xpos', JSON.stringify(node.pos[0]));
1973 nodeElement.setAttribute(
'ypos', JSON.stringify(node.pos[1]));
1976 console.log(
'** Create node:', nodeElement.getNamePath(), nodeElement.getType());
1977 nodeElement.setName(node.title);
1983 console.log(
'-> Write Node:',
graph.title +
'/' + node.title,
' --> ', nodeElement.getNamePath());
1986 console.log(
'Skip writing :', node.title);
1991 var properties = node.properties;
1993 var node_inputs = node.inputs;
1994 var isInputNode =
false;
1995 var isOutputNode =
false;
1996 if (nodeElement.getCategory() ==
'input') {
1998 node_inputs = [node];
2000 else if (nodeElement.getCategory() ==
'output') {
2001 isOutputNode =
true;
2002 node_inputs = [node];
2006 if (!isInputNode && !isOutputNode)
2008 if (node.nodedef_type ==
"multioutput")
2010 console.log(
'Write outputs for:', node,
'. type: ', node.nodedef_type);
2011 for (var output of node.outputs) {
2012 var outputName = output.name;
2013 var outputType = output.type;
2014 var outputElement = nodeElement.addOutput(outputName, outputType);
2016 console.log(
'> Read: node.nodedef_type: ', node.nodedef_type);
2017 console.log(
'> Write: output:', outputElement.getNamePath(), outputElement.getType());
2026 var inputs = node_inputs;
2027 for (var i in inputs) {
2028 let input = inputs[i];
2030 console.log(
'---- Write port:', input);
2032 let inputName = input.name;
2033 let inputType = input.type;
2034 if (nodeElement.getCategory() ==
'input' ||
2035 nodeElement.getCategory() ==
'output') {
2037 inputType = node.nodedef_type;
2041 var inputElement =
null;
2042 var nodeToCheck = node;
2043 var inputNode =
null;
2044 var inputLink =
null;
2045 if (isInputNode && node.graph._subgraph_node) {
2046 nodeToCheck = node.graph._subgraph_node;
2047 for (var i = 0; i < nodeToCheck.inputs.length; i++) {
2048 var nci = nodeToCheck.inputs[i];
2049 if (nci.name == node.title) {
2050 inputNode = nodeToCheck.getInputNode(i);
2051 inputLink = nodeToCheck.getInputLink(i);
2058 inputNode = node.getInputNode(i);
2059 inputLink = node.getInputLink(i);
2061 var inputLinkOutput =
'';
2062 var numInputOutputs = 0;
2064 numInputOutputs = inputNode.outputs.length;
2065 inputLinkOutput = inputNode.outputs[inputLink.origin_slot];
2069 if (nodeElement.getCategory() !=
'input' &&
2070 nodeElement.getCategory() !=
'output') {
2071 inputElement = nodeElement.getInput(inputName);
2073 inputElement = nodeElement.addInput(inputName, inputType);
2076 inputElement = nodeElement;
2080 console.log(
'Write connection');
2081 console.log(
' - TO:', inputElement.getName() +
"." + inputName);
2082 console.log(
' - FROM link:', inputNode.id +
"." + inputLinkOutput.name);
2084 if (inputNode.type ==
'graph/subgraph') {
2085 inputElement.setNodeGraphString(inputNode.title);
2087 if (numInputOutputs > 1 && inputLinkOutput) {
2088 inputElement.setOutputString(inputLinkOutput.name);
2096 if (inputNode.nodedef_node ==
'input') {
2099 inputElement.setInterfaceName(inputNode.title);
2103 inputElement.setNodeName(inputNode.title);
2106 if (numInputOutputs > 1 && inputNode.nodedef_node !=
'output') {
2108 if (inputLinkOutput) {
2109 inputElement.setOutputString(inputLinkOutput.name);
2118 var inputValue = node.properties[inputName];
2119 if (inputValue ==
null) {
2120 console.log(
'Cannot find property value for input:', inputName);
2123 var origValue = inputValue;
2125 if ([
'float',
'integer'].includes(inputType)) {
2126 inputValue = inputValue.toString();
2128 else if ([
'vector2',
'vector3',
'vector4',
'matrix33',
'matrix44',
'color3',
'color4'].includes(inputType)) {
2129 inputValue = inputValue.toString();
2130 inputValue = inputValue.split(
',').map(Number).join(
', ');
2132 else if (inputType ===
'boolean') {
2133 if (inputValue ===
'true')
2134 inputValue =
'true';
2136 inputValue =
'false';
2139 inputValue = inputValue.toString();
2143 if (nodeElement.getCategory() !=
'input' &&
2144 nodeElement.getCategory() !=
'output') {
2145 inputElement = nodeElement.getInput(inputName);
2147 inputElement = nodeElement.addInput(inputName, inputType);
2150 console.log(
'Error> Trying add input more than once:', inputName,
' to node: ', nodeElement.getNamePath());
2154 inputElement = nodeElement;
2158 inputElement.setValueString(inputValue, inputType);
2161 console.warn(
"Set value error: ", e);
2168 var propInfo =
null;
2169 var skip_attributes = [];
2170 if (isInputNode || isOutputNode) {
2171 if (input.properties_info) {
2173 skip_attributes = [
'interfacename',
'nodegraph',
'nodename',
'name',
'type',
'value',
'default_value'];
2174 propInfo = input.properties_info[0];
2178 if (node.properties_info) {
2180 skip_attributes = [
'interfacename',
'nodegraph',
'nodename',
'name',
'type',
'value',
'default_value',
'uimin',
'uimax',
'uiname',
'uifolder'];
2181 propInfo = node.properties_info[i];
2188 skip_attributes = skip_attributes.concat([
'defaultgeomprop',
'enum',
'enumvalues']);
2190 for (var propAttribute in propInfo) {
2191 if (skip_attributes.includes(propAttribute))
2195 var propAttributeValue = propInfo[propAttribute];
2196 if (propAttributeValue && propAttributeValue.length > 0) {
2197 inputElement.setAttribute(propAttribute, propAttributeValue);
2205 console.log(
'---- END Write inputs:', node.inputs);
2209 console.log(
'---> End write node', node.title);
2213 console.log(
'***** END Scan Graph:',
graph.title);
2223 var ARRAY_TYPES = [
'color3',
'color4',
'vector2',
'vector3',
'vector4',
'matrix33',
'matrix44'];
2224 if (ARRAY_TYPES.includes(_type)) {
2242 var nodeInputs = [];
2243 var isOutput = (node.getCategory() ==
'output');
2244 var isInput = (node.getCategory() ==
'input');
2245 if (isOutput || isInput) {
2246 nodeInputs = [node];
2249 nodeInputs = node.getInputs();
2251 for (var input of nodeInputs) {
2255 if (!isOutput && !isInput) {
2256 _name = input.getName();
2257 explicitInputs.push(_name);
2260 var nodeName = input.getNodeName();
2261 var nodeGraphName = input.getNodeGraphString();
2262 var inputInterfaceName = input.getInterfaceName();
2263 var outputName = input.getOutputString();
2265 if (nodeName.length ||
2266 nodeGraphName.length ||
2267 inputInterfaceName.length ||
2268 outputName.length) {
2275 var target_node = lg_node;
2276 var target_slot =
null;
2277 if (!isOutput && !isInput)
2278 target_slot = target_node.findInputSlot(_name);
2281 var source_node =
null;
2282 var source_slot = 0;
2283 var source_name = nodeName;
2284 if (nodeGraphName.length) {
2285 source_name = nodeGraphName;
2287 if (inputInterfaceName.length) {
2288 source_name = inputInterfaceName;
2291 var graphToCheck =
graph;
2292 if (isInput &&
graph._subgraph_node) {
2293 target_node =
graph._subgraph_node;
2294 target_slot = target_node.findInputSlot(lg_node.title);
2296 graphToCheck = parentGraph;
2301 source_node = graphToCheck.findNodeByTitle(source_name);
2304 var outputSlot = source_node.findOutputSlot(outputName);
2305 if (outputSlot >= 0) {
2306 source_slot = outputSlot;
2309 editor.debugOutput(
'Failed to find output slot:' + outputName, 1);
2311 var linkInfo = source_node.connect(source_slot, target_node, target_slot);
2313 editor.debugOutput(
'Failed to connect:' + source_node.title +
'.' + outputName,
'->', target_node.title +
'.' + _name), 1,
false;
2318 var linkInfo =
null;
2319 if (source_slot ==
null || target_slot ==
null || target_node ==
null) {
2320 console.warning(
'Cannot connect!')
2323 linkInfo = source_node.connect(source_slot, target_node, target_slot);
2326 editor.debugOutput(
'Failed to connect:' + source_node.title +
'.' + outputName,
'->', target_node.title +
'.' + _name, 1);
2332 console.log(
'Failed to find node ', source_name,
'in graph:', graphToCheck);
2333 this.editor.debugOutput(
'Failed to find source node: ' + source_node +
"." +
2334 source_name,
'->', lg_node.title +
"." + _name, 2);
2338 var _value = input.getResolvedValueString();
2339 if (_value.length > 0) {
2340 if (this.
isArray(input.getType())) {
2342 let valueArray = _value.split(/[\s,]+/);
2343 _value = valueArray;
2348 lg_node.setProperty(_name, _value);
2352 var property_info = lg_node.getPropertyInfo(_name);
2366 if (input && property_info) {
2369 var colorspace = input.getColorSpace();
2370 if (colorspace.length > 0)
2371 property_info[
'colorspace'] = colorspace;
2373 var unit = input.getUnit();
2374 if (unit.length > 0)
2375 property_info[
'unit'] = unit;
2377 var uiname = input.getAttribute(
'uiname');
2378 if (uiname.length > 0)
2379 property_info[
'uiname'] = uiname;
2381 var uimin = input.getAttribute(
'uimin');
2382 if (uimin.length > 0)
2383 property_info[
'uimin'] = uimin;
2385 var uimax = input.getAttribute(
'uimax');
2386 if (uimax.length > 0)
2387 property_info[
'uimax'] = uimax;
2388 var uisoftmin = input.getAttribute(
'uisoftmin');
2389 if (uisoftmin.length > 0)
2390 property_info[
'uimin'] = uisoftmin;
2392 var uisoftmax = input.getAttribute(
'uisoftmax');
2393 if (uisoftmax.length > 0)
2394 property_info[
'uimax'] = uisoftmax;
2396 var uifolder = input.getAttribute(
'uifolder');
2397 if (uifolder.length > 0)
2398 property_info[
'uifolder'] = uifolder;
2400 var basicMetaData = [
'colorspace',
'unit',
'uiname',
'uimin',
'uimax',
'uifolder',
'name',
'type',
'output',
'nodename',
'nodegraph'];
2401 for (var attrName of input.getAttributeNames()) {
2402 if (!basicMetaData.includes(attrName)) {
2403 property_info[attrName] = input.getAttribute(attrName);
2407 if (node && input.getType() ==
'filename')
2409 let nodeType = node.getType();
2410 let colorTypes = [
'color3',
'color4'];
2412 if (colorTypes.includes(nodeType))
2414 if (!property_info[
'colorspace']) {
2415 console.log(
'Auto create "none" colorspace for input:', input.getName());
2416 let
doc = node.getDocument();
2417 let defaultColorSpace =
'none';
2422 property_info[
'colorspace'] = defaultColorSpace;
2441 let loadNodePositions =
false;
2445 editor.debugOutput(
"MaterialX is not initialized", 2);
2449 editor.clearGraph();
2452 editor.monitor.monitorGraph(
graph,
false);
2456 var mtlxNodeDefs = [];
2458 for (var interfaceInput of
doc.getInputs()) {
2459 var _type = interfaceInput.getType();
2460 var
id =
'mtlx/input/input_' + _type;
2462 var lg_node = LiteGraph.createNode(
id);
2464 lg_node.title = interfaceInput.getName();
2466 console.log(
'Add top level input:', lg_node.title,
'to graph',
graph);
2468 var _value = interfaceInput.getValueString();
2469 if (_value && _value.length > 0) {
2470 if (this.
isArray(interfaceInput.getType())) {
2471 _value =
"[" + _value +
"]"
2472 _value = JSON.parse(_value);
2474 lg_node.setProperty(
'in', _value);
2477 if (loadNodePositions) {
2478 var xpos = interfaceInput.getAttribute(
'xpos');
2479 var ypos = interfaceInput.getAttribute(
'ypos');
2480 if (xpos.length > 0 && ypos.length > 0) {
2481 lg_node.pos[0] = xpos;
2482 lg_node.pos[1] = ypos;
2488 lg_node.setSize(lg_node.computeSize());
2496 for (var interfaceOutput of
doc.getOutputs()) {
2497 var _type = interfaceOutput.getType()
2498 var
id =
'mtlx/output/output_' + _type;
2500 var lg_node = LiteGraph.createNode(
id);
2502 lg_node.title = interfaceOutput.getName();
2505 console.log(
'Add graph output:', lg_node.title);
2509 lg_node.setSize(lg_node.computeSize());
2511 if (loadNodePositions) {
2512 var xpos = interfaceOutput.getAttribute(
'xpos');
2513 var ypos = interfaceOutput.getAttribute(
'ypos');
2514 if (xpos.length > 0 && ypos.length > 0)
2515 lg_node.pos = [xpos, ypos];
2518 mtlxNodes.push([interfaceOutput, lg_node,
graph]);
2522 for (var node of
doc.getNodes()) {
2523 var nodeDef = node.getNodeDef();
2525 editor.debugOutput(
'Skip node w/o nodedef:' + node.getName(), 1)
2530 var
id =
'mtlx/' + nodeDef.getNodeGroup() +
'/' + nodeDef.getName();
2531 id =
id.replace(
'ND_',
'');
2533 console.log(
'Load node:', node.getName(),
' -> ',
id);
2535 var lg_node = LiteGraph.createNode(
id);
2538 lg_node.title = node.getName();
2543 lg_node.setSize(lg_node.computeSize());
2545 if (loadNodePositions) {
2546 var xpos = node.getAttribute(
'xpos');
2547 var ypos = node.getAttribute(
'ypos');
2548 if (xpos.length > 0 && ypos.length > 0)
2549 lg_node.pos = [xpos, ypos];
2552 mtlxNodes.push([node, lg_node,
graph]);
2553 mtlxNodeDefs.push(nodeDef);
2556 editor.debugOutput(
'Failed to create node:' + node.getName(), 2);
2560 for (var nodegraph of
doc.getNodeGraphs()) {
2561 if (nodegraph.hasSourceUri()) {
2564 var nodedefAttrib = nodegraph.getAttribute(
'nodedef');
2565 if (nodedefAttrib && nodedefAttrib.length > 0) {
2566 console.log(
'Skip loading in functional graph:', nodegraph.getName(),
'nodedef:', nodedefAttrib);
2570 console.log(
'Create nodegraph:', nodegraph.getName());
2572 var title = nodegraph.getName();
2573 var subgraphNode = LiteGraph.createNode(
"graph/subgraph", title);
2577 subgraphNode.bgImageUrl =
"./Icons/nodegraph.png";
2579 var mtlxSubGraphNodes = [];
2580 for (var interfaceInput of nodegraph.getInputs()) {
2581 var _type = interfaceInput.getType();
2582 var
id =
'mtlx/input/input_' + _type;
2584 var lg_node = LiteGraph.createNode(
id);
2586 lg_node.title = interfaceInput.getName();
2588 subgraphNode.subgraph.add(lg_node);
2591 console.log(
'-------- Add subgraph input:', lg_node.title, lg_node);
2593 subgraphNode.addInput(interfaceInput.getName(), _type);
2594 subgraphNode.subgraph.addInput(interfaceInput.getName(), _type);
2596 var _value = interfaceInput.getValueString();
2597 if (_value && _value.length > 0) {
2598 if (this.
isArray(interfaceInput.getType())) {
2599 _value =
"[" + _value +
"]"
2600 _value = JSON.parse(_value);
2602 lg_node.setProperty(
'in', _value);
2606 lg_node.setSize(lg_node.computeSize());
2608 if (loadNodePositions) {
2609 var xpos = nodegraph.getAttribute(
'xpos');
2610 var ypos = nodegraph.getAttribute(
'ypos');
2611 if (xpos.length > 0 && ypos.length > 0)
2612 lg_node.pos = [xpos, ypos];
2615 mtlxSubGraphNodes.push([interfaceInput, lg_node,
graph]);
2619 for (var interfaceOutput of nodegraph.getOutputs()) {
2620 var _type = interfaceOutput.getType()
2621 var
id =
'mtlx/output/output_' + _type;
2623 var lg_node = LiteGraph.createNode(
id);
2625 lg_node.title = interfaceOutput.getName();
2626 subgraphNode.subgraph.add(lg_node);
2628 console.log(
'Add subgraph output:', lg_node.title);
2630 subgraphNode.addOutput(interfaceOutput.getName(), _type);
2631 subgraphNode.subgraph.addOutput(interfaceOutput.getName(), _type);
2634 lg_node.setSize(lg_node.computeSize());
2636 if (loadNodePositions) {
2637 var xpos = interfaceOutput.getAttribute(
'xpos');
2638 var ypos = interfaceOutput.getAttribute(
'ypos');
2639 if (xpos.length > 0 && ypos.length > 0)
2640 lg_node.pos = [xpos, ypos];
2643 mtlxSubGraphNodes.push([interfaceOutput, lg_node,
graph]);
2648 for (var node of nodegraph.getNodes()) {
2649 var nodeDef = node.getNodeDef();
2651 editor.debugOutput(
'Skip node w/o nodedef:' + node.getName(), 1)
2656 var
id =
'mtlx/' + nodeDef.getNodeGroup() +
'/' + nodeDef.getName();
2657 id =
id.replace(
'ND_',
'');
2659 var lg_node = LiteGraph.createNode(
id);
2660 lg_node.title = node.getName();
2661 subgraphNode.subgraph.add(lg_node);
2663 console.log(
'Add subgraph node:', lg_node.title);
2666 lg_node.setSize(lg_node.computeSize());
2668 if (loadNodePositions) {
2669 var xpos = node.getAttribute(
'xpos');
2670 var ypos = node.getAttribute(
'ypos');
2671 if (xpos.length > 0 && ypos.length > 0)
2672 lg_node.pos = [xpos, ypos];
2675 mtlxSubGraphNodes.push([node, lg_node,
graph]);
2678 for (var item of mtlxSubGraphNodes) {
2680 var lg_node = item[1];
2681 var parentGraph = item[2];
2682 var explicitInputs = [];
2685 lg_node.setSize(lg_node.computeSize());
2688 this.
buildConnections(editor, node, lg_node, explicitInputs, subgraphNode.subgraph, parentGraph);
2692 console.log(
'Add subgraph:', subgraphNode.title);
2694 if (auto_arrange > 0) {
2695 subgraphNode.subgraph.arrange(auto_arrange);
2698 graph.add(subgraphNode);
2705 for (var item of mtlxNodes) {
2707 var lg_node = item[1];
2710 var explicitInputs = [];
2714 if (lg_node.nodedef_node ==
'input' || lg_node.nodedef_node ==
'output') {
2742 editor.monitor.monitorGraph(
graph,
true);
2744 if (auto_arrange > 0) {
2745 graph.arrange(auto_arrange);
2748 graph.setDirtyCanvas(
true,
true);
2761 var input = document.createElement(
"input");
2762 input.style = this.fontSizeStyle;
2763 input.type =
"file";
2764 input.accept =
".mtlx";
2765 input.onchange =
function (e) {
2766 var file = e.target.files[0];
2767 console.log(
'Loading definitions from file: ' + file.name);
2772 const reader =
new FileReader();
2773 reader.readAsText(file,
'UTF-8');
2775 reader.onload =
function (e) {
2777 let fileContents = e.target.result;
2782 const readOptions =
new ne_mx.XmlReadOptions();
2783 readOptions.readXIncludes =
false;
2784 var customLib =
ne_mx.createDocument();
2786 await
ne_mx.readFromXmlString(customLib, fileContents,
'', readOptions);
2790 console.log(
'Create custom library definitions',
ne_mx.prettyPrint(customLib));
2792 var scanForIcon =
false;
2795 var iconName = file.name.replace(/\.[^/.]+$/,
".webp");
2797 var iconExists = await that.editor.uriExists(iconName);
2802 var definitionsList = [];
2803 var result = that.createLiteGraphDefinitions(customLib,
false,
false, definitionsList,
'mtlx', that.editor, iconName);
2806 var definitionsListString = definitionsList.join(
', ');
2807 that.editor.debugOutput(
"Registered custom node types: [" + definitionsListString +
"]", 0,
false);
2808 that.editor.displayNodeTypes();
2811 console.log(
'Error evaluating source:', e);
2819 that.editor.debugOutput(
'Error reading definitions:' + error, 2,
false);
2826 that.editor.debugOutput(
"MaterialX is not initialized", 2);
2845 console.log(
'MaterialX is not initialized');
2850 if (extension !=
'mtlx')
2854 let result = converter.import(
ne_mx, fileContents,
stdlib);
2856 fileContents = result[0];
2859 console.log(
'Failed to convert from:', extension,
'to mtlx. Errors:', result[1]);
2865 console.log(
'Failed to find converter from:', extension,
'to mtlx.');
2872 const readOptions =
new ne_mx.XmlReadOptions();
2873 readOptions.readXIncludes =
false;
2879 console.log(
'Import custom library:', item[0]);
2880 doc.importLibrary(item[1]);
2882 var loadDoc =
ne_mx.createDocument();
2883 await
ne_mx.readFromXmlString(loadDoc, fileContents,
'', readOptions);
2887 var customLib =
ne_mx.createDocument();
2888 customLib.copyContentFrom(loadDoc);
2889 var keepChildren = [];
2890 var existingDefs = []
2891 var saveCustomLib =
false;
2892 doc.getNodeDefs().forEach(def => { existingDefs.push(def.getName()); });
2893 for (var nodedef of loadDoc.getNodeDefs()) {
2894 var nodedefName = nodedef.getName();
2895 if (!existingDefs.includes(nodedefName)) {
2896 keepChildren.push(nodedef.getName());
2897 saveCustomLib =
true;
2900 for (var ng of loadDoc.getNodeGraphs()) {
2901 if (ng.getAttribute(
'nodedef') && ng.getAttribute(
'nodedef').length > 0) {
2902 saveCustomLib =
true;
2903 keepChildren.push(ng.getName());
2907 if (saveCustomLib) {
2909 for (var child of customLib.getChildren()) {
2910 if (!keepChildren.includes(child.getName())) {
2912 customLib.removeChild(child.getName());
2916 var additionDefs = [];
2917 console.log(
'Create custom library definitions from addtionDefs:',
2918 ne_mx.prettyPrint(customLib));
2923 console.log(
'Loaded local definitions: ', additionDefs);
2925 console.log(
'Error evaluating source:', e);
2929 doc.copyContentFrom(loadDoc);
2936 if (saveCustomLib) {
2941 var documentColorSpace =
doc.getColorSpace();
2947 loadDoc.removeAttribute(
'fileprefix');
2948 fileContents =
ne_mx.writeToXmlString(loadDoc);
2956 if (documentDisplayUpdater) {
2957 documentDisplayUpdater(fileContents);
2960 console.log(
'No docDisplayUpdater!!!');
2965 if (renderableItemUpdater) {
2967 if (!renderableItems || renderableItems.length == 0) {
2968 MxShadingGraphEditor.theEditor.debugOutput(
'No renderable items found in graph: ' + fileName, 1,
false);
2970 renderableItemUpdater(renderableItems);
2974 MxShadingGraphEditor.theEditor.debugOutput(
'Error reading document: ' + fileName +
'. Error: ' + error, 2,
false);
2998 const reader =
new FileReader();
2999 reader.readAsText(file,
'UTF-8');
3000 reader.accept =
'.mtlx';
3003 console.log(
'loadFromFile:', file, fileName);
3005 reader.onload =
function (e) {
3007 let fileContents = e.target.result;
3008 console.log(
"read file: ", file.name,
" with extension: ", extension,
" and length: ", fileContents.length);
3010 that.loadFromString(
'mtlx', fileContents, fileName, auto_arrange);
3013 MxShadingGraphEditor.theEditor.debugOutput(
'Error reading document: ' + fileName +
'. Error: ' + error, 2,
false);
3017 editor.debugOutput(
"MaterialX is not initialized", 2,
false);
3025 console.log(
"Loading content from zip:", file.name);
3027 const reader =
new FileReader();
3030 reader.onload = async
function (e) {
3032 const zipData =
new Uint8Array(e.target.result);
3034 let documents = result[0];
3035 let docText = documents[0].content;
3036 let docFile = documents[0].name;
3037 console.log(
'Documents:', docText);
3039 let textures = result[1];
3040 for (let i = 0; i < textures.length; i++) {
3041 const url = textures[i].url;
3042 const textureName = textures[i].name;
3046 docText = docText.replace(
new RegExp(textureName,
'i'), url);
3047 console.log(
'Replace reference:' + textureName +
' with blob: ' + url);
3050 that.loadFromString(
'mtlx', docText, docFile, auto_arrange);
3054 console.error(
'Error loading ZIP file:', error);
3058 reader.readAsArrayBuffer(file);
3061 console.error(
"MaterialX is not initialized");
3080 var generator =
new ne_mx.EsslShaderGenerator();
3081 var genContext =
new ne_mx.GenContext(generator);
3084 console.log(
'Loaded standard libraries:',
stdlib.getNodeDefs().length);
3098 if (name.length == 0) {
3100 msg =
'Setting empty name as "blank"';
3107 var nodes =
graph._nodes;
3109 for (var node of nodes) {
3110 nodenames.push(node.title);
3114 name =
ne_mx.createValidName(name);
3116 if (!nodenames.includes(name)) {
3121 var rootName = name;
3123 var number = name.match(/\d+$/);
3125 i = (parseInt(number) + 1)
3126 rootName = name.slice(0, -number[0].length);
3129 var valid_name = rootName + i.toString();
3130 while (nodenames.includes(valid_name)) {
3132 valid_name = rootName + i.toString();
3154 this.fontSizeStyle =
'font-size: 11px;';
3157 let gltfConverter =
new glTFMaterialX();
3158 this.handler.addConverter(gltfConverter);
3160 console.log(
'Create new editor with exporter for:', gltfConverter.exportType());
3179 setDirty(w =
null, h =
null) {
3181 graph.setDirtyCanvas(
true,
true);
3195 debugOutput(text, severity, clear =
null) {
3198 consoleLog(text, severity, clear);
3201 console.log(
'> ', text,
' severity:', severity);
3211 setSourceColorSpace(colorSpace) {
3213 this.handler.setSourceColorSpace(colorSpace);
3223 setTargetDistanceUnit(unit) {
3225 this.handler.setTargetDistanceUnit(unit);
3234 getSourceColorSpace() {
3236 return this.handler.getSourceColorSpace();
3238 return 'lin_rec709';
3246 getTargetDistanceUnit() {
3248 return this.handler.getTargetDistanceUnit();
3260 if (title.length == 0) {
3265 let nodesFound = [];
3269 const pattern =
new RegExp(title);
3270 for (var i = 0, l = theGraph._nodes.length; i < l; ++i) {
3271 if (pattern.test(theGraph._nodes[i].title)) {
3272 console.log(
'-- add found node:', theGraph._nodes[i].title);
3273 nodesFound.push(theGraph._nodes[i]);
3276 if (theGraph._nodes[i].title == title)
3278 nodesFound.length = 0;
3279 nodesFound.push(theGraph._nodes[i]);
3285 if (nodesFound.length > 0)
3293 this.debugOutput(
'Node not found: ' + title, 0,
false);
3304 arrangeGraph(spacing = 80) {
3316 for (var s in selected) {
3317 var node = selected[s];
3318 if (node.type ==
'graph/subgraph') {
3361 this.handler.sourceColorSpace = this.handler.DEFAULT_COLOR_SPACE;
3362 this.handler.targetDistanceUnits = this.handler.DEFAULT_DISTANCE_UNITS;
3364 this.updatePropertyPanel(
null);
3378 saveSerialization() {
3379 var data = JSON.stringify(
graph.serialize(),
null, 2);
3380 var blob =
new Blob([data], { type:
"text/plain" });
3381 var url = URL.createObjectURL(blob);
3382 var a = document.createElement(
"a");
3384 a.download =
"serialized_graph.json";
3391 loadSerialization() {
3394 var input = document.createElement(
"input");
3395 input.style = this.fontSizeStyle;
3396 input.type =
"file";
3397 input.accept =
".json";
3398 input.onchange =
function (e) {
3399 var file = e.target.files[0];
3400 var reader =
new FileReader();
3401 reader.onload =
function (event) {
3402 var data = JSON.parse(event.target.result);
3403 graph.configure(data);
3405 reader.readAsText(file);
3417 saveGraphToFile(extension, graphWriteOptions) {
3418 if (this.handler.canExport(extension)) {
3419 this.handler.saveGraphToFile(extension,
graph, graphWriteOptions);
3423 this.debugOutput(
'Unsupported extension for saving graph:' + extension, 2,
false);
3433 saveGraphToString(extension, graphWriteOptions) {
3434 if (this.handler.canExport(extension)) {
3435 return this.handler.saveGraphToString(extension,
graph, graphWriteOptions);
3439 this.debugOutput(
'Unsupported extension for saving graph: ' + extension, 2,
false);
3450 loadDefinitionsFromFile(extension) {
3451 if (extension ==
'mtlx') {
3452 this.handler.loadDefinitionsFromFile();
3456 this.debugOutput(
'Unsupported extension for loading definitions: ' + extension, 2,
false);
3467 loadGraphFromFile(extension, auto_arrange) {
3469 if (!this.handler.canImport(extension)) {
3470 this.debugOutput(
'Unsupported extension for loading graph: ' + extension, 2,
false);
3475 if (extension ==
'mtlx')
3477 var input = document.createElement(
"input");
3478 input.style = this.fontSizeStyle;
3479 input.type =
"file";
3480 input.accept =
"." + this.handler.getExtension();
3481 input.onchange =
function (e) {
3482 var file = e.target.files[0];
3483 console.log(
'Loading file: ' + file.name);
3488 else if (extension ==
'zip')
3490 var input = document.createElement(
"input");
3491 input.style = this.fontSizeStyle;
3492 input.type =
"file";
3493 input.accept =
".zip";
3494 input.onchange =
function (e) {
3495 var file = e.target.files[0];
3497 console.log(
'Loading zip file: ' + file.name);
3510 findRenderableItems() {
3511 return this.handler.findRenderableItems(
graph);
3523 loadGraphFromString(extension, content, fileName, auto_arrange) {
3524 if (!this.handler.canImport(extension)) {
3525 this.debugOutput(
'Unsupported extension for loading graph: ' + extension, 2,
false);
3529 if (content.length > 0)
3530 this.handler.loadFromString(extension, content, fileName, auto_arrange);
3543 console.log(
'rgbToHex empty !', rgb);
3546 return '#' + rgb.map(x => {
3547 var hex = Math.round(x * 255).toString(16);
3548 return hex.length === 1 ?
'0' + hex : hex;
3560 createButtonWithImageAndText(imageSrc, text,
id) {
3562 var img = document.createElement(
"img");
3563 img.id =
id +
"_img";
3565 img.classList.add(
"img-fluid");
3568 var span = document.createElement(
"span");
3569 span.id =
id +
"_text";
3570 span.textContent =
" " + text;
3573 var button = document.createElement(
"button");
3575 button.classList.add(
"btn",
"btn-sm",
"btn-outline-secondary",
"form-control",
"form-control-sm");
3576 button.style = this.fontSizeStyle;
3577 button.appendChild(img);
3578 button.appendChild(span);
3590 openImageDialog(theNode, updateProp, wantURI) {
3593 var fileInput = document.createElement(
'input');
3594 fileInput.type =
'file';
3595 fileInput.accept =
'image/*';
3596 fileInput.style.display =
'none';
3597 document.body.appendChild(fileInput);
3602 fileInput.addEventListener(
'change',
function () {
3603 var fileURI = fileInput.value.split(
'\\').pop();
3604 var file = fileInput.files[0];
3606 fileURI = URL.createObjectURL(file);
3608 var updateElementId =
'__pp:' + updateProp;
3609 var textInput = document.getElementById(updateElementId);
3611 textInput.value = fileURI;
3612 theNode.setProperty(updateProp, fileURI);
3614 var propertypanel_preview = document.getElementById(
'propertypanel_preview');
3615 if (propertypanel_preview) {
3616 propertypanel_preview.src = URL.createObjectURL(file);
3617 propertypanel_preview.style.display =
"block";
3620 var previewImage =
false;
3622 if (propertypanel_preview) {
3623 var reader =
new FileReader();
3624 reader.onload =
function (event) {
3625 propertypanel_preview.src =
event.target.result;
3629 reader.readAsDataURL(file);
3630 propertypanel_preview.style.display =
"block";
3634 document.body.removeChild(fileInput);
3648 return Promise.resolve(
true);
3650 return Promise.resolve(
false);
3654 console.log(
'Error checking URI:', error);
3655 return Promise.resolve(
false);
3666 createColorSpaceInput(colorSpaces, activeItem) {
3667 var select = document.createElement(
"select");
3668 select.className =
"form-control form-control-sm";
3669 select.style = this.fontSizeStyle;
3670 select.id =
"propertypanel_colorspace";
3671 for (var i = 0; i < colorSpaces.length; i++) {
3672 var option = document.createElement(
"option");
3673 option.value = colorSpaces[i];
3674 option.text = colorSpaces[i];
3678 var option = document.createElement(
"option");
3679 option.value =
"none";
3680 option.text =
"none";
3683 select.value = activeItem;
3695 createUnitsInput(units, unittype, activeItem) {
3696 var select = document.createElement(
"select");
3697 select.className =
"form-control form-control-sm";
3698 select.style = this.fontSizeStyle;
3699 select.id =
"propertypanel_units";
3700 for (var i = 0; i < units.length; i++) {
3701 var option = document.createElement(
"option");
3702 var unit_pair = units[i];
3703 if (unit_pair[1] == unittype) {
3704 option.value = unit_pair[0];
3705 option.text = unit_pair[0];
3709 select.value = activeItem;
3719 updateImagePreview(curImage) {
3720 var propertypanel_preview = document.getElementById(
'propertypanel_preview');
3721 if (curImage && propertypanel_preview) {
3722 this.uriExists(curImage)
3725 propertypanel_preview.src = curImage;
3726 propertypanel_preview.style.display =
"block";
3729 propertypanel_preview.src =
"./Icons/no_image.png";
3730 propertypanel_preview.style.display =
"block";
3747 updatePropertyPanel(node) {
3750 if (!propertypanelcontent) {
3751 console.error(
'No property panel content widget found!');
3755 while (propertypanelcontent.firstChild) {
3756 propertypanelcontent.removeChild(propertypanelcontent.firstChild);
3761 if (node && node.nodedef_icon) {
3762 panelIcon.src = node.nodedef_icon;
3764 else if (this.ui.icon_map) {
3765 if (!node || node.type ==
'graph/subgraph') {
3766 panelIcon.src = this.ui.icon_map[
'_default_graph_'];
3768 panelIcon.src = this.ui.icon_map[
'_default_'];
3772 propertypanelcontent.innerHTML =
"";
3774 let colorSpaces = this.handler.getColorSpaces();
3775 let targetUnits = this.handler.getUnits();
3781 else if (!node && !
graphcanvas.graph._is_subgraph) {
3782 var docInfo = [[
'Colorspace', this.getSourceColorSpace()],
3783 [
'Distance', this.getTargetDistanceUnit()]];
3785 for (let item of docInfo) {
3787 let elem = document.createElement(
"div");
3788 elem.className =
"row px-1 py-0";
3789 let label = document.createElement(
"div");
3790 label.className =
"col py-0 col-form-label-sm text-left";
3791 label.style = this.fontSizeStyle;
3792 label.innerHTML =
"<b>" + item[0] +
"</b>";
3793 elem.appendChild(label);
3795 if (item[0] ==
'Colorspace' && colorSpaces.length > 0) {
3797 var inputCol = document.createElement(
"div");
3798 inputCol.className =
"col text-left";
3799 var select = this.createColorSpaceInput(colorSpaces, item[1]);
3800 select.onchange =
function (e) {
3803 inputCol.appendChild(select);
3804 elem.appendChild(inputCol);
3806 else if (item[0] ==
'Distance' && targetUnits.length > 0) {
3808 var inputCol = document.createElement(
"div");
3809 inputCol.className =
"col text-left";
3810 var select = this.createUnitsInput(targetUnits,
'distance', item[1]);
3811 select.onchange =
function (e) {
3814 inputCol.appendChild(select);
3815 elem.appendChild(inputCol);
3828 propertypanelcontent.appendChild(elem);
3833 var _category = node.nodedef_node;
3834 var _type = node.nodedef_type;
3836 var isNodeGraph = node.type ==
'graph/subgraph';
3839 _category =
'nodegraph';
3841 if (node.outputs.length > 1) {
3844 else if (node.outputs.length > 0) {
3845 _type = node.outputs[0].type;
3853 if (_category ==
'surfacematerial') {
3859 var elem = document.createElement(
"div");
3860 elem.className =
"row px-1 py-1";
3863 var label = document.createElement(
"div");
3864 label.className =
"col-4 px-1 py-0 col-form-label-sm text-end";
3865 label.style = this.fontSizeStyle;
3866 label.innerHTML =
"<b>" + _category;
3867 if (_type.length > 0) {
3868 label.innerHTML +=
'<br>' + _type;
3870 label.innerHTML +=
"</b>";
3871 elem.appendChild(label);
3874 var inputCol = document.createElement(
"div");
3875 inputCol.className =
"col py-0";
3876 var nameInput = document.createElement(
"input");
3877 nameInput.style = this.fontSizeStyle;
3878 nameInput.type =
"text";
3879 nameInput.value = node.title;
3880 nameInput.className =
"form-control form-control-sm";
3882 nameInput.onchange =
function (e) {
3883 var oldTitle = node.title;
3885 if (newTitle != oldTitle)
3887 that.monitor.onNodeRenamed(node, newTitle);
3888 node.title = newTitle;
3890 e.target.value = node.title;
3892 if (node.graph._is_subgraph) {
3893 if (node.nodedef_node ==
'input') {
3895 node.graph.renameInput(oldTitle, node.title);
3897 else if (node.nodedef_node ==
'output') {
3899 node.graph.renameOutput(oldTitle, node.title);
3904 node.setSize(node.computeSize());
3905 node.setDirtyCanvas(
true,
true);
3907 inputCol.appendChild(nameInput);
3910 if (node.nodedef_node !=
'input' && node.nodedef_node !=
'output'
3911 && node.type !=
'graph/subgraph') {
3912 var imagePreview = document.createElement(
"img");
3913 imagePreview.src =
"./Icons/no_image.png";
3914 var previewSet =
false;
3916 imagePreview.style.display =
"none";
3917 imagePreview.src =
"./Icons/no_image.png";
3930 imagePreview.id =
"propertypanel_preview";
3931 imagePreview.className =
"img-fluid form-control form-control-sm";
3932 inputCol.appendChild(imagePreview);
3935 elem.appendChild(label);
3936 elem.appendChild(inputCol);
3941 var filterCol = document.createElement(
"div");
3942 filterCol.className =
"col-2 py-0";
3943 filterCol.width = 16;
3944 var filterIcon = document.createElement(
"button");
3947 if (node.showDefaultValueInputs ==
null)
3949 node.showDefaultValueInputs =
true;
3951 var img = document.createElement(
"img");
3952 if (node.showDefaultValueInputs)
3954 img.src =
"./Icons/funnel_white.svg";
3955 filterIcon.className =
"btn btn-sm btn-outline-secondary";
3959 img.src =
"./Icons/funnel-fill_white.svg";
3960 filterIcon.className =
"btn btn-sm btn-outline-warning";
3962 filterIcon.appendChild(img);
3963 filterIcon.onclick =
function (e) {
3964 node.showDefaultValueInputs = !node.showDefaultValueInputs;
3967 filterCol.appendChild(filterIcon);
3968 elem.appendChild(filterCol);
3971 propertypanelcontent.appendChild(elem);
3973 var hr = document.createElement(
"hr");
3974 hr.classList.add(
"my-1");
3975 propertypanelcontent.appendChild(hr);
3977 var current_details =
null;
3978 var first_details =
true;
3979 var nodeInputs = node.inputs
3981 let targetNodes = [];
3982 for (var i in nodeInputs) {
3983 let nodeInput = nodeInputs[i];
3985 let inputName = nodeInput.name;
3986 let nodeInputLink = nodeInput.link;
3987 let uiName = inputName;
3989 uiName = uiName.replace(/_/g,
' ');
3992 let colorspace =
'';
3994 let defaultgeomprop =
'';
3998 let property_info = node.getPropertyInfo(inputName);
3999 let ng_property_info =
null;
4003 let sg = node.subgraph;
4006 let sg_nodes = sg._nodes;
4007 for (var sg_node of sg_nodes)
4009 if (sg_node.title == inputName)
4012 ng_property_info = sg_node.getPropertyInfo(
"in");
4013 if (ng_property_info)
4024 var skipInterorConnectedInput =
false;
4025 if (node.graph._is_subgraph) {
4028 var sg_node = node.graph._subgraph_node;
4031 var slot = sg_node.findInputSlot(node.title);
4033 if (sg_node.inputs) {
4035 var slotInput = sg_node.inputs[slot];
4037 if (slotInput !=
null && slotInput.link !=
null) {
4038 skipInterorConnectedInput =
true;
4048 if (skipInterorConnectedInput) {
4049 console.log(
'Skip interior connected input: ', nodeInput);
4054 if (ng_property_info) {
4056 property_info = ng_property_info;
4058 if (property_info) {
4060 if (property_info.defaultgeomprop)
4062 defaultgeomprop = property_info.defaultgeomprop;
4064 if (property_info.colorspace) {
4065 colorspace = property_info.colorspace;
4067 if (property_info.unit) {
4068 units = property_info.unit;
4070 if (property_info.uiname) {
4071 uiName = property_info.uiname;
4073 if (property_info.uimin) {
4074 uimin = property_info.uimin;
4076 if (property_info.uimax) {
4077 uimax = property_info.uimax;
4079 if (property_info.uifolder && property_info.uifolder.length > 0) {
4081 if (current_details ==
null || current_details.id != property_info.uifolder) {
4083 current_details = document.createElement(
"details");
4084 current_details.id = property_info.uifolder;
4085 current_details.open = first_details;
4086 current_details.classList.add(
'w-100',
'p-1',
'border',
'border-secondary',
'rounded',
'my-1');
4087 first_details =
false;
4088 var summary = document.createElement(
'summary')
4089 summary.style = this.fontSizeStyle;
4090 summary.innerHTML =
"<b>" + property_info.uifolder +
"</b>"
4092 current_details.appendChild(summary);
4100 current_details =
null;
4105 current_details =
null;
4111 if (nodeInputLink) {
4112 let upstreamLink =
null;
4114 let nodegraph = node.graph;
4115 let link = nodegraph.links[nodeInputLink];
4117 let linkId = link && link.origin_id;
4118 let linkNode = linkId && nodegraph.getNodeById(linkId);
4123 let linkSlot = link.origin_slot;
4125 let linkOutput = linkNode.outputs[linkSlot];
4127 upstreamLink = linkNode.title +
'.' + linkOutput.name;
4130 let
id =
"__pp:" + inputName;
4131 let buttonText = upstreamLink;
4133 if (buttonText.length > 15) {
4134 buttonText = buttonText.substring(0, 15) +
"...";
4136 let input = this.createButtonWithImageAndText(
"./Icons/arrow_up_white.svg", buttonText,
id);
4138 input.onclick =
function (e) {
4140 var inputName = e.target.id;
4141 inputName = inputName.replace(
'__pp:',
'');
4142 inputName = inputName.replace(
'_text',
'');
4143 inputName = inputName.replace(
'_img',
'');
4144 console.log(
'Clicked traversal button:', inputName);
4146 console.log(
'Jump to node:', linkNode.title);
4150 node.setDirtyCanvas(
true,
true);
4154 elem = document.createElement(
"div");
4155 elem.className =
"row px-1 py-0";
4157 input.id =
"__pp:" + inputName;
4159 var label = document.createElement(
"div");
4161 label.className =
"col-4 px-1 py-0 col-form-label-sm text-end";
4162 label.style = this.fontSizeStyle;
4163 label.innerHTML = uiName;
4164 label.for = input.id;
4165 elem.appendChild(label);
4168 if (useFormControl) {
4169 input.classList.add(
"form-control");
4171 input.classList.add(
"form-control-sm");
4174 input.disabled =
true;
4176 var propvalue = document.createElement(
"div");
4177 propvalue.className =
"col p-1";
4178 propvalue.appendChild(input);
4180 elem.appendChild(propvalue);
4186 targetNodes[i] = node;
4187 let targetNode = targetNodes[i];
4188 let propertyKey = inputName;
4190 var
property = targetNode.properties[inputName];
4191 if (property ==
null) {
4193 var subgraph = targetNode.subgraph;
4196 var subNode = subgraph.findNodeByTitle(inputName);
4198 targetNodes[i] = subNode;
4200 property = targetNodes[i].properties[
'in'];
4205 if (property ==
null) {
4206 console.log(
'Update: Cannot find property value for input:', inputName);
4212 if (defaultgeomprop)
4219 if (!node.showDefaultValueInputs && !isNodeGraph)
4221 let isDefault = node.isDefaultValue(inputName);
4229 elem = document.createElement(
"div");
4230 elem.className =
"row px-1 py-0";
4233 var input_btn =
null;
4234 let input_slider =
null;
4235 var colorspace_unit_btn =
null;
4236 var useFormControl =
true;
4239 if (colorspace.length > 0) {
4243 colorspace_unit_btn = this.createColorSpaceInput(colorSpaces, colorspace);
4244 let theNode = targetNodes[i];
4245 colorspace_unit_btn.onchange =
function (e) {
4247 theNode.setPropertyInfo(inputName,
'colorspace', e.target.value);
4250 else if (units.length > 0 && property_info.unittype) {
4252 colorspace_unit_btn = this.createUnitsInput(targetUnits, property_info.unittype, units);
4253 let theNode = targetNodes[i];
4254 colorspace_unit_btn.onchange =
function (e) {
4255 theNode.setPropertyInfo(inputName,
'unit', e.target.value);
4259 let proptype = nodeInput.type;
4260 if (proptype ==
'float' || proptype ==
'integer') {
4261 var isFloat = proptype ==
'float';
4263 input = document.createElement(
"input");
4264 input.id = propertyKey +
'_box';
4265 input.style = this.fontSizeStyle;
4266 input.type =
'number';
4267 input.classList.add(
"form-control",
"form-control-sm",
"ps-0");
4268 input.setAttribute(
'propertyKey', propertyKey);
4270 input_slider = document.createElement(
"input");
4271 input_slider.id = propertyKey +
'_slider';
4273 input_slider.type =
'range';
4274 input_slider.classList.add(
'form-range',
'custom-slider',
'pe-0');
4275 input_slider.setAttribute(
'propertyKey', propertyKey);
4281 input.min = Math.min(property, 0);
4289 input.max = Math.max(property*3, 10.0);
4292 input.max = Math.max(property*3, 100);
4297 input_slider.min = input.min;
4298 input_slider.max = input.max;
4300 input.step = (input.max - input.min) / 100.0;
4301 input_slider.step = input.step;
4304 input_slider.step = 1;
4308 input.value = input_slider.value = property;
4318 let theSlider = input_slider;
4319 let theNode = targetNodes[i];
4320 input_slider.onchange =
function (e) {
4321 var pi = e.target.getAttribute(
'propertyKey');
4322 var val = parseFloat(e.target.value);
4323 theNode.setProperty(pi, val);
4326 input_slider.oninput =
function(e) {
4327 var pi = e.target.getAttribute(
'propertyKey');
4328 var val = parseFloat(e.target.value);
4329 theNode.setProperty(pi, val);
4330 theBox.value = e.target.value;
4333 input.onchange =
function (e) {
4334 var pi = e.target.getAttribute(
'propertyKey');
4335 var val = parseFloat(e.target.value);
4336 theNode.setProperty(pi, val);
4339 input.oninput =
function(e) {
4340 var pi = e.target.getAttribute(
'propertyKey');
4341 var val = parseFloat(e.target.value);
4342 theNode.setProperty(pi, val);
4343 theSlider.value = e.target.value;
4346 else if (proptype ==
'string' || proptype ==
'filename') {
4347 input = document.createElement(
"input");
4348 input.style = this.fontSizeStyle;
4349 input.type =
"text";
4350 if (proptype ==
'filename') {
4351 var curImage = property;
4352 this.updateImagePreview(curImage);
4354 input_btn = document.createElement(
"button");
4355 input_btn.classList.add(
"btn",
"btn-sm",
"btn-outline-secondary");
4356 input_btn.innerHTML =
"+";
4357 input_btn.setAttribute(
'propertyKey', propertyKey);
4358 var fileId =
"__pp:" + inputName;
4359 let theNode = targetNodes[i];
4360 input_btn.onclick =
function (e) {
4361 var pi = e.target.getAttribute(
'propertyKey');
4370 if (property_info && property_info.enum) {
4375 input = document.createElement(
"select");
4376 input.style = this.fontSizeStyle;
4377 input.classList.add(
"form-control",
"form-control-sm");
4379 input.setAttribute(
'propertyKey', propertyKey);
4380 let theNode = targetNodes[i];
4381 let enums = property_info.enum;
4382 for (let j = 0; j < enums.length; j++) {
4383 let option = document.createElement(
"option");
4384 option.value = enums[j];
4385 option.text = enums[j];
4388 input.value = property;
4389 input.setAttribute(
'propertyKey', propertyKey);
4390 input.onchange =
function (e) {
4391 var pi = e.target.getAttribute(
'propertyKey');
4392 theNode.setProperty(pi, e.target.value);
4398 if (property_info && !property_info.enm) {
4399 input.value = property;
4400 input.setAttribute(
'propertyKey', propertyKey);
4401 let theNode = targetNodes[i];
4402 let isFilename = proptype ==
'filename';
4404 input.onchange =
function (e) {
4405 var pi = e.target.getAttribute(
'propertyKey');
4407 theNode.setProperty(pi, e.target.value);
4410 that.updateImagePreview(e.target.value);
4418 else if (proptype ==
'boolean') {
4420 input = document.createElement(
"input");
4421 input.style = this.fontSizeStyle;
4422 input.type =
"checkbox";
4423 input.classList =
"form-check-input";
4424 useFormControl =
false;
4425 input.checked = property;
4426 input.setAttribute(
'propertyKey', propertyKey);
4427 let theNode = targetNodes[i];
4428 input.onchange =
function (e) {
4429 var pi = e.target.getAttribute(
'propertyKey');
4431 theNode.setProperty(pi, e.target.checked);
4436 else if (proptype ==
'vector2' || proptype ==
'vector3' || proptype ==
'vector4')
4439 var vector_size = [
'vector2',
'vector3',
'vector4'].indexOf(proptype) + 2;
4440 input = document.createElement(
"div");
4441 useFormControl =
false;
4443 input.className =
"row py-1 ps-4 pe-0";
4445 for (let v=0; v<vector_size; v++)
4448 let subinput = document.createElement(
"input");
4449 subinput.style = this.fontSizeStyle;
4450 subinput.type =
'number';
4451 subinput.classList.add(
"form-control");
4452 subinput.classList.add(
"form-control-sm");
4453 subinput.setAttribute(
'propertyKey', propertyKey);
4455 let subinput_slider = document.createElement(
"input");
4456 subinput_slider.id = propertyKey +
'_slider';
4457 subinput_slider.type =
'range';
4458 subinput_slider.classList.add(
'form-range',
'custom-slider',
'pe-0');
4459 subinput_slider.setAttribute(
'propertyKey', propertyKey);
4462 subinput.min = uimin[v];
4465 subinput.min = Math.min(property[v]*3, 0);
4468 subinput.max = uimax[v];
4471 subinput.max = Math.max(property[v]*3, 10.0);
4474 subinput_slider.min = subinput.min;
4475 subinput_slider.max = subinput.max;
4476 subinput.step = (subinput.max - subinput.min) / 100.0;
4477 subinput_slider.step = subinput.step;
4479 subinput.value = subinput_slider.value =
property[v];
4481 let theNode = targetNodes[i];
4482 let vector_index = v;
4483 let theBox = subinput;
4484 let theSlider = subinput_slider;
4485 theBox.onchange =
function (e) {
4486 let pi = e.target.getAttribute(
'propertyKey');
4487 let value = parseFloat(e.target.value);
4488 let newValue = theNode.properties[pi].map(item => item);
4489 newValue[vector_index] = value;
4490 theNode.setProperty(pi, newValue);
4494 theBox.oninput =
function(e) {
4495 let pi = e.target.getAttribute(
'propertyKey');
4496 let value = parseFloat(e.target.value);
4497 let newValue = theNode.properties[pi].map(item => item);
4498 newValue[vector_index] = value;
4499 theNode.setProperty(pi, newValue);
4500 theSlider.value = e.target.value;
4503 theSlider.onchange =
function (e) {
4504 let pi = e.target.getAttribute(
'propertyKey');
4505 let value = parseFloat(e.target.value);
4506 let newValue = theNode.properties[pi].map(item => item);
4507 newValue[vector_index] = value;
4508 theNode.setProperty(pi, newValue);
4511 theSlider.oninput =
function(e) {
4512 let pi = e.target.getAttribute(
'propertyKey');
4513 let value = parseFloat(e.target.value);
4514 let newValue = theNode.properties[pi].map(item => item);
4515 newValue[vector_index] = value;
4516 theNode.setProperty(pi, newValue);
4517 theBox.value = e.target.value;
4521 let propvalue_slider = document.createElement(
"div");
4522 propvalue_slider.className =
"col p-0";
4523 propvalue_slider.appendChild(subinput_slider);
4525 let propvalue_box = document.createElement(
"div");
4526 propvalue_box.className =
"col p-0";
4527 propvalue_box.appendChild(subinput);
4529 let input_row = document.createElement(
"div");
4530 input_row.className =
"row p-0";
4531 input_row.appendChild(propvalue_slider);
4532 input_row.appendChild(propvalue_box);
4534 input.appendChild(input_row);
4537 else if (proptype ==
'color3' || proptype ==
'color4') {
4538 input = document.createElement(
"input");
4539 input.type =
"color";
4541 if (property.length == 4) {
4542 input.value = this.rgbToHex([ property[0], property[1], property[2] ]);
4545 input.value = this.rgbToHex(property);
4547 input.setAttribute(
'propertyKey', propertyKey);
4548 let theNode = targetNodes[i];
4549 input.onchange =
function (e) {
4551 var hex = e.target.value;
4554 rgb[0] = parseInt(hex.substring(1, 3), 16) / 255.0;
4555 rgb[0] = parseFloat(rgb[0].toFixed(fprecision));
4556 rgb[1] = parseInt(hex.substring(3, 5), 16) / 255.0;
4557 rgb[1] = parseFloat(rgb[1].toFixed(fprecision));
4558 rgb[2] = parseInt(hex.substring(5, 7), 16) / 255.0;
4559 rgb[2] = parseFloat(rgb[2].toFixed(fprecision));
4560 if (proptype ==
'color4')
4563 var pi = e.target.getAttribute(
'propertyKey');
4564 theNode.setProperty(pi, rgb);
4566 let func =
function (e) {
4568 var hex = e.target.value;
4569 let rgb = [0, 0, 0];
4571 rgb[0] = parseInt(hex.substring(1, 3), 16) / 255.0;
4572 rgb[0] = parseFloat(rgb[0].toFixed(fprecision));
4573 rgb[1] = parseInt(hex.substring(3, 5), 16) / 255.0;
4574 rgb[1] = parseFloat(rgb[1].toFixed(fprecision));
4575 rgb[2] = parseInt(hex.substring(5, 7), 16) / 255.0;
4576 rgb[2] = parseFloat(rgb[2].toFixed(fprecision));
4577 if (proptype ==
'color4')
4580 var pi = e.target.getAttribute(
'propertyKey');
4581 theNode.setProperty(pi, rgb);
4583 input.onchange = func;
4584 input.oninput = func;
4587 input = document.createElement(
"input");
4588 input.style = this.fontSizeStyle;
4589 input.type =
"text";
4590 input.value = property;
4591 let propertyKey = inputName;
4592 let theNode = targetNodes[i];
4593 input.onchange =
function (e) {
4594 theNode.setProperty(propertyKey, e.target.value);
4600 input.id =
"__pp:" + inputName;
4603 var label = document.createElement(
"div");
4604 label.className =
"col-4 p-0 col-form-label-sm text-end";
4605 label.style = this.fontSizeStyle;
4606 label.innerHTML = uiName;
4607 label.for = input.id;
4608 elem.appendChild(label);
4611 if (useFormControl) {
4612 input.classList.add(
"form-control");
4614 input.classList.add(
"form-control-sm");
4617 input.disabled =
true;
4619 var propvalue = document.createElement(
"div");
4620 propvalue.className =
"col py-0";
4623 propvalue.classList.add(
'ps-1');
4625 propvalue.appendChild(input);
4628 var propbutton = document.createElement(
"div");
4629 propbutton.className =
"col-2 py-0";
4631 propbutton.appendChild(input_btn);
4632 elem.appendChild(propbutton);
4634 if (colorspace_unit_btn) {
4636 var propbutton = document.createElement(
"div");
4637 propbutton.className =
"col col-form-label-sm";
4638 var details = document.createElement(
"details");
4639 var summary = document.createElement(
'summary')
4640 summary.style = this.fontSizeStyle;
4641 if (colorspace.length > 0)
4642 summary.innerHTML =
"Colorspace";
4643 else if (targetUnits.length > 0)
4644 summary.innerHTML =
"Units";
4645 details.appendChild(summary);
4646 details.appendChild(colorspace_unit_btn);
4647 propbutton.appendChild(details);
4648 propvalue.appendChild(propbutton);
4653 var propvalue_slider = document.createElement(
"div");
4654 propvalue_slider.className =
"col py-0 pe-0";
4655 propvalue_slider.appendChild(input_slider);
4656 elem.appendChild(propvalue_slider);
4659 elem.appendChild(propvalue);
4664 if (current_details) {
4666 current_details.appendChild(elem);
4668 if (current_details.parentElement ==
null) {
4669 propertypanelcontent.appendChild(current_details);
4673 propertypanelcontent.appendChild(elem);
4689 initializeLiteGraph(canvas, readOnly =
false) {
4691 graph =
new LiteGraph.LGraph();
4706 graphcanvas.default_connection_color_byTypeOff = {
4745 let parentGraph =
'';
4748 parentGraph =
graphcanvas.graph._subgraph_node.title;
4758 let parentGraph =
'';
4761 parentGraph =
graphcanvas.graph._subgraph_node.title;
4769 LGraphNode.prototype.setPropertyInfo =
function(property, propertyInfo, value)
4773 if (this.properties_info) {
4774 for (var i = 0; i < this.properties_info.length; ++i) {
4775 if (this.properties_info[i].name == property) {
4776 info = this.properties_info[i];
4782 if (info && info[propertyInfo])
4784 if (this.onPropertyInfoChanged)
4786 this.onPropertyInfoChanged(property, propertyInfo, value, info[propertyInfo]);
4788 info[propertyInfo] = value;
4792 console.warning(
'Failed to set property: ', property,
'. info: ', propertyInfo,
'. Value: ', value,
'. Infos: ', this.properties_info);
4797 LGraphNode.prototype.isDefaultValue =
function(property)
4802 if (this.properties[property] ==
null)
4804 console.warn(
'> Property value does not exist:', property);
4808 if (this.getInputLink(property))
4813 if (this.properties_info !=
null)
4815 for (let i = 0; i < this.properties_info.length; ++i) {
4816 if (this.properties_info[i].name == property) {
4817 info = this.properties_info[i];
4823 if (info !=
null && info.default_value !=
null)
4825 let property_string = this.properties[property];
4826 let default_value_string = info.default_value;
4827 let isDefault =
false;
4828 if (Array.isArray(default_value_string)) {
4829 default_value_string = default_value_string.map(String);
4830 property_string = property_string.map(String);
4831 isDefault = (JSON.stringify(default_value_string) == JSON.stringify(property_string));
4835 isDefault = (default_value_string == property_string);
4841 console.warn(
'> Default value does not exist for:', property);
4850 this.monitor.monitorGraph(
graph,
true);
4858 console.log(
'> Read only mode: ', readOnly);
4874 graph.ctrl_shift_v_paste_connect_unselected_outputs =
true;
4880 canvas.addEventListener(
"keydown",
function (e) {
4881 if (e.key ===
"f") {
4887 canvas.addEventListener(
"keydown",
function (e) {
4888 if (e.key ===
"l") {
4895 var context = canvas.getContext(
'2d');
4897 function drawstart(event) {
4900 console.log(
'>>>>>>>>>>> draw start');
4904 function drawmove(event) {
4908 console.log(
'>>>>>>>>>>> draw move');
4911 function drawend(event) {resources/Lights/rural_crossroads_1k
4914 console.log(
'>>>>>>>>>>> draw move');
4918 function touchstart(event) {
4919 drawstart(event.touches[0]);
4922 function touchmove(event) {
4923 drawmove(event.touches[0]);
4927 function touchend(event) {
4928 drawend(event.changedTouches[0]);
4946 var haveSelected =
false;
4947 for (var s in selected) {
4948 haveSelected =
true;
4951 console.log(
'Center nodes:', selected,
'. Have selected:', haveSelected);
4959 LiteGraph.searchbox_extras = [];
4960 var nodeTypes = LiteGraph.registered_node_types;
4961 for (var typeName in nodeTypes) {
4962 if (typeName !==
"graph/subgraph") {
4963 console.log(
'Removing node type:', LiteGraph.getNodeType(typeName));
4964 LiteGraph.unregisterNodeType(typeName);
4972 collapseNode(node, collapse) {
4973 if (node.constructor.collapsable ===
false) {
4976 if (node.flags.collapsed != collapse) {
4977 node.flags.collapsed = collapse;
4986 collapseExpandNodes(collapse) {
4991 var modified =
false;
4992 if (selected_nodes) {
4993 for (var i in selected_nodes) {
4994 var node = selected_nodes[i];
4996 if (this.collapseNode(node, collapse))
5001 var nodes = curGraph._nodes;
5002 for (var i in nodes) {
5003 var node = nodes[i];
5004 if (this.collapseNode(node, collapse))
5011 graph.setDirtyCanvas(
true,
true);
5025 pasteFromClipboard() {
5032 extractNodeGraph() {
5034 if (selected.length == 0) {
5035 console.log(
'No nodes selected.');
5039 var subgraphsSelected = []
5040 for (var i in selected) {
5041 var node = selected[i];
5042 if (node.type ==
'graph/subgraph') {
5043 subgraphsSelected.push(node);
5046 if (subgraphsSelected.length == 0) {
5047 console.log(
'No subgraphs selected.');
5052 var subGraph = subgraphsSelected[0];
5053 var subGraphNodes = subGraph.subgraph._nodes;
5054 for (var i in subGraphNodes) {
5055 var node = subGraphNodes[i];
5076 this.debugOutput(
'Cannot create nest subgraphs.', 1);
5082 if (selected.length == 0) {
5083 console.log(
'No nodes selected.');
5091 var node = LiteGraph.createNode(
'graph/subgraph');
5099 node.subgraph.arrange(80);
5107 displayNodeTypes() {
5109 var nodeTypesListUpdater = this.ui.nodeTypesListUpdater;
5110 if (!nodeTypesListUpdater) {
5115 var nodeTypes = LiteGraph.registered_node_types;
5116 nodeTypesListUpdater(nodeTypes);
5128 initialize(canvas, ui, monitor, materialFilename, readOnly =
false) {
5132 console.log(
'Set custom monitor:', monitor.getName());
5134 this.monitor = monitor;
5135 this.initializeLiteGraph(canvas, readOnly);
5137 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.
loadFromZip(extension, file, fileName, editor, auto_arrange)
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.
Utility class for unzipping ZIP which contain MaterialX files and dependent images .
static async unzipMaterialXData(zipData)
Unzips the given ZIP data and returns the MaterialX documents and textures.