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
372 return this.renderer;
383 this.monitoring = monitor;
393 return this.monitoring;
503 this.monitoring = monitor;
508 theGraph.onConnectionChange =
null;
509 theGraph.onNodeAdded =
null;
510 theGraph.onNodeRemoved =
null;
514 console.log(
'> Monitor graph: ',
graph.title?
graph.title :
'ROOT')
518 theGraph.onConnectionChange =
function (node) {
519 let parentGraph =
'';
520 var is_subgraph = node.graph._is_subgraph;
522 parentGraph =
graphcanvas.graph._subgraph_node.title;
523 that.onConnectionChange(node, parentGraph);
526 for (var s in selected) {
533 theGraph.onNodeAdded =
function (node) {
534 let parentGraph =
'';
536 if (node.type ==
'graph/subgraph') {
540 var node_subgraph = node.subgraph;
541 var node_graph = node.graph;
544 for (var i in node_subgraph._nodes) {
545 let theNode = node_subgraph._nodes[i];
546 if (!node_graph.findNodeByTitle(theNode.title)) {
547 if (theNode.nodedef_node ==
'input') {
548 node.addInput(theNode.title, theNode.nodedef_type);
551 else if (theNode.nodedef_node ==
'output') {
553 node.addOutput(theNode.title, theNode.nodedef_type);
561 node.setSize(node.computeSize());
565 var is_subgraph = node.graph._is_subgraph;
567 parentGraph =
graphcanvas.graph._subgraph_node.title;
569 if (node.nodedef_node ==
'input') {
571 node.graph.addInput(node.title, node.nodedef_type);
573 else if (node.nodedef_node ==
'output') {
575 node.graph.addOutput(node.title, node.nodedef_type);
579 if (node.type ==
'graph/subgraph') {
580 that.monitorGraph(node.subgraph, monitor);
583 that.onNodeAdded(node, parentGraph);
587 theGraph.onNodeRemoved =
function (node) {
589 let parentGraph =
'';
593 parentGraph =
graphcanvas.graph._subgraph_node.title;
594 if (node.nodedef_node ==
'input') {
598 else if (node.nodedef_node ==
'output') {
604 that.onNodeRemoved(node, parentGraph);
607 for (var i in theGraph._nodes) {
608 var node = theGraph._nodes[i];
609 if (node.type ==
'graph/subgraph') {
610 console.log(
'> Monitor subgraph:', node.title);
611 that.monitorGraph(node.subgraph, monitor);
632 this.extension = extension;
637 this.DEFAULT_COLOR_SPACE =
'lin_rec709';
638 this.DEFAULT_DISTANCE_UNIT =
'meter';
639 this.sourceColorSpace = this.DEFAULT_COLOR_SPACE;
640 this.targetDistanceUnit = this.DEFAULT_DISTANCE_UNIT;
641 this.colorSpaces = [];
646 this.converters = [];
660 this.converters.unshift(converter);
672 this.monitor = monitor;
684 if (extension ==
'mtlx')
702 for (let converter of this.converters) {
703 if (converter.exportType() == extension) {
718 if (extension ==
'mtlx' || extension ==
'zip')
736 for (let converter of this.converters) {
737 if (converter.importType() == extension) {
751 this.colorSpaces = colorSpaces;
760 return this.colorSpaces;
789 let newSpace = this.DEFAULT_COLOR_SPACE;
790 if (colorSpace && colorSpace.length > 0)
791 newSpace = colorSpace;
795 this.monitor.onDocumentChange(
'colorspace', colorSpace, this.sourceColorSpace);
797 this.sourceColorSpace = newSpace;
807 let newUnit = this.DEFAULT_DISTANCE_UNIT;
808 if (unit && unit.length > 0)
813 this.monitor.onDocumentChange(
'distanceunit', newUnit, this.targetDistanceUnit);
815 this.targetDistanceUnit = newUnit;
826 return this.sourceColorSpace;
836 return this.targetDistanceUnit;
845 return this.extension;
855 this.editor = editor;
877 if (_type ===
'string' || _type ===
'filename') {
878 value =
"'" + value +
"'";
880 else if (this.isArray(_type)) {
881 if (value.length == 0) {
882 if (_type ===
'color3')
883 value =
"[0.0, 0.0, 0.0]";
884 else if (_type ===
'color4')
885 value =
"[0.0, 0.0, 0.0, 0.0]";
886 else if (_type ===
'vector2')
887 value =
"[0.0, 0.0]";
888 else if (_type ===
'vector3')
889 value =
"[0.0, 0.0, 0.0]";
890 else if (_type ===
'vector4')
891 value =
"[0.0, 0.0, 0.0, 0.0]";
892 else if (_type ===
'matrix33')
893 value =
"[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]";
894 else if (_type ===
'matrix44')
895 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]";
898 value =
"[" + value +
"]";
901 else if (_type ===
'integer') {
902 if (value.length == 0) {
906 else if (_type ===
'float') {
907 if (value.length == 0) {
911 else if (_type ===
'boolean') {
918 if (value.length == 0) {
941 super(
id, extension);
951 return new Promise((resolve, reject) => {
952 MaterialX().then((ne_mtlx) => {
956 }).
catch((error) => {
970 function loadInitialText(filePath, handler) {
973 .then(response => response.blob())
975 const reader =
new FileReader();
976 reader.onload =
function (e) {
977 console.log(
'Loaded document:', filePath);
978 editor.loadGraphFromString(
'mtlx', e.target.result, filePath, 80,
true);
980 reader.readAsText(blob);
983 console.error(
'Error loading file %s:' % filePath, error);
987 loadInitialText(materialFilename,
this);
1001 super.initialize(editor);
1008 editor.debugOutput(
"Loaded MaterialX version:" +
ne_mx.getVersionString(), 0,
true);
1011 var generator =
new ne_mx.EsslShaderGenerator.create();
1012 var genContext =
new ne_mx.GenContext(generator);
1014 editor.debugOutput(
'Loaded standard libraries definitions:' +
stdlib.getNodeDefs().length, 0,
false);
1017 let units =
new Map();
1018 for (let ud of
stdlib.getUnitDefs()) {
1019 let unittype = ud.getAttribute(
'unittype')
1020 for (let unit of ud.getChildren()) {
1021 units.set(unit.getName(), unittype);
1025 this.
setUnits(Array.from(units).sort());
1026 console.log(
'> Setup real-world units: ', this.
getUnits());
1030 let colorSpaces =
new Set();
1031 let docNodeDefs =
stdlib.getNodeDefs();
1032 for (let i = 0; i < docNodeDefs.length; i++) {
1033 let cmnode = docNodeDefs[i];
1034 if (cmnode.getNodeGroup() ===
'colortransform') {
1035 let name = cmnode.getName();
1036 name = name.replace(
'ND_',
'');
1037 let namesplit = name.split(
'_to_');
1038 let type =
'color3';
1039 if (namesplit[1].includes(
'color4')) {
1040 namesplit[1] = namesplit[1].replace(
'_color4',
'');
1043 namesplit[1] = namesplit[1].replace(
'_color3',
'');
1045 colorSpaces.add(namesplit[0]);
1046 colorSpaces.add(namesplit[1]);
1053 var definitionsList = [];
1055 var definitionsDisplayUpdater = editor.ui.definitionsDisplayUpdater;
1056 if (definitionsDisplayUpdater) {
1057 definitionsDisplayUpdater(result);
1060 editor.clearNodeTypes();
1064 editor.debugOutput(
'Error evaluating source: ' + e, 2,
false);
1067 var nodeTypes = LiteGraph.registered_node_types;
1069 for (var typeName in nodeTypes) {
1072 editor.debugOutput(
"Registered node types:" + definitionsList.length, 0,
false);
1074 editor.displayNodeTypes();
1076 if (materialFilename.length > 0) {
1080 editor.updatePropertyPanel(
null);
1082 }).
catch((error) => {
1083 editor.debugOutput(
"Error on initialization:" + error, 2);
1096 let graphWriteOptions = { writeCustomLibs :
false, saveNodePositions:
false, writeOutputs :
true };
1099 console.log(
'Failed to save graph to document');
1113 const materialNodes = mdoc.getMaterialNodes();
1114 let shaderList = [];
1115 let renderableItems = [];
1117 for (let i = 0; i < materialNodes.length; ++i) {
1118 let materialNode = materialNodes[i];
1121 let shaderNodes =
ne_mx.getShaderNodes(materialNode)
1122 if (shaderNodes.length > 0) {
1123 let shaderNodePath = shaderNodes[0].getNamePath()
1124 if (!shaderList.includes(shaderNodePath)) {
1126 shaderList.push(shaderNodePath);
1127 renderableItems.push(shaderNodePath);
1132 const nodeGraphs = mdoc.getNodeGraphs();
1133 for (let i = 0; i < nodeGraphs.length; ++i) {
1134 let nodeGraph = nodeGraphs[i];
1136 if (nodeGraph.hasAttribute(
'nodedef') || nodeGraph.hasSourceUri()) {
1140 if (nodeGraph.getDownstreamPorts().length > 0) {
1143 let outputs = nodeGraph.getOutputs();
1144 for (let j = 0; j < outputs.length; ++j) {
1145 let output = outputs[j];
1147 renderableItems.push(output.getNamePath());
1152 const outputs = mdoc.getOutputs();
1153 for (let i = 0; i < outputs.length; ++i) {
1154 let output = outputs[i];
1156 renderableItems.push(output.getNamePath());
1160 return renderableItems;
1178 buildMetaData(colorSpace, unit, unitType, uiname, uimin, uimax, uifolder, _type) {
1181 metaData[
'colorspace'] = colorSpace;
1182 metaData[
'unit'] = unit;
1183 metaData[
'unittype'] = unitType;
1184 metaData[
'uiname'] = uiname;
1185 if (_type ==
'vector2' || _type ==
'vector3' || _type ==
'vector4' || _type ==
'matrix33' || _type ==
'matrix44') {
1187 uimin = uimin.split(
',').map(Number);
1190 uimax = uimax.split(
',').map(Number);
1193 metaData[
'uimin'] = uimin;
1194 metaData[
'uimax'] = uimax;
1195 metaData[
'uifolder'] = uifolder;
1218 var definition_code =
""
1220 console.log(
'Creating LiteGraph definitions from MaterialX document:',
doc);
1223 var nodeDefs =
doc.getNodeDefs();
1226 definition_code +=
"console.log('Loading MaterialX Definitions...');\n";
1228 definition_code +=
"// MaterialX LiteGraph Functional Definitions\n"
1229 definition_code +=
"//\n";
1230 definition_code +=
"// MaterialX Version: " +
ne_mx.getVersionString() +
"\n";
1231 const date =
new Date();
1232 definition_code +=
"// Generated on: " + date.toString() +
"\n";
1233 definition_code +=
"//\n";
1236 TMAP[
'float'] =
'float';
1237 TMAP[
'color3'] =
'color3';
1238 TMAP[
'color4'] =
'color4';
1239 TMAP[
'vector2'] =
'vector2';
1240 TMAP[
'vector3'] =
'vector3';
1241 TMAP[
'vector4'] =
'vector4';
1242 TMAP[
'matrix33'] =
'matrix33';
1243 TMAP[
'matrix44'] =
'matrix44';
1244 TMAP[
'integer'] =
'integer';
1245 TMAP[
'string'] =
'string';
1246 TMAP[
'boolean'] =
'boolean';
1247 TMAP[
'filename'] =
'filename';
1248 TMAP[
'BSDF'] =
'BSDF';
1249 TMAP[
'EDF'] =
'EDF';
1250 TMAP[
'VDF'] =
'VDF';
1251 TMAP[
'surfaceshader'] =
'surfaceshader';
1252 TMAP[
'volumeshader'] =
'volumeshader';
1253 TMAP[
'displacementshader'] =
'displacementshader';
1254 TMAP[
'lightshader'] =
'lightshader';
1255 TMAP[
'material'] =
'material';
1256 TMAP[
'vector2array'] =
'vector2array';
1259 CMAP[
'integer'] =
"#A32";
1260 CMAP[
'float'] =
"#161";
1261 CMAP[
'vector2'] =
"#265";
1262 CMAP[
'vector3'] =
"#465";
1263 CMAP[
'vector4'] =
"#275";
1264 CMAP[
'color3'] =
"#37A";
1265 CMAP[
'color4'] =
"#69A";
1266 CMAP[
'matrix33'] =
"#333";
1267 CMAP[
'matrix44'] =
"#444";
1268 CMAP[
'string'] =
"#395";
1269 CMAP[
'filename'] =
"#888";
1270 CMAP[
'boolean'] =
"#060";
1272 var inputTypes = [
'float',
'color3',
'color4',
'vector2',
'vector3',
'vector4',
'matrix33',
'matrix44',
'integer',
'string',
'boolean',
'filename',
'BSDF',
'EDF',
'VDF',
'surfaceshader',
'volumeshader',
'displacementshader',
'lightshader',
'material',
'vector2array'];
1273 var outputTypes = [
'float',
'color3',
'color4',
'vector2',
'vector3',
'vector4',
'matrix33',
'matrix44',
'integer',
'string',
'boolean',
'filename',
'BSDF',
'EDF',
'VDF',
'surfaceshader',
'volumeshader',
'displacementshader',
'lightshader',
'material',
'vector2array'];
1276 var supporTokens =
false;
1278 inputTypes.push(
'token');
1279 TMAP[
'token'] =
'string';
1282 const INPUT_ND =
'ND_input_';
1283 const OUTPUT_ND =
'ND_output_';
1284 const INPUT_NODE_STRING =
'input';
1285 const OUTPUT_NODE_STRING =
'output';
1286 const LIBRARY_ICON =
'';
1289 if (addInputOutputs) {
1290 for (var _type of inputTypes) {
1291 var
id = libraryPrefix +
'/input/input_' + _type;
1292 var functionName =
ne_mx.createValidName(
id);
1293 var titleName =
'input_' + _type;
1294 definition_code +=
"\n/**\n";
1295 definition_code +=
" * @function "+ functionName +
"\n";
1296 definition_code +=
" * @description Library: " + libraryPrefix +
". Category: input. Type: " + _type +
"\n";
1297 definition_code +=
" * LiteGraph id: " +
id +
"\n";
1298 definition_code +=
" */\n";
1299 definition_code +=
"function " + functionName +
"() {\n";
1301 definition_code +=
" this.nodedef_icon = '" + LIBRARY_ICON +
"';\n";
1302 definition_code +=
" this.nodedef_name = '" + INPUT_ND + _type +
"';\n";
1303 definition_code +=
" this.nodedef_node = '" + INPUT_NODE_STRING +
"';\n";
1304 definition_code +=
" this.nodedef_type = '" + _type +
"';\n";
1305 definition_code +=
" this.nodedef_group = '" + INPUT_NODE_STRING +
"';\n";
1306 if (_type ==
'token')
1308 definition_code +=
" this.addInput('in', '" + TMAP[_type] +
"');\n";
1310 var metaData = this.
buildMetaData(
'',
'',
'',
'',
null,
null,
'',
null);
1311 metaData = JSON.stringify(metaData);
1316 if (_type ==
'filename')
1321 definition_code +=
" this.addProperty('in', " + value +
", '" + _type +
"'," + metaData +
");\n";
1322 definition_code +=
" this.addOutput('out', '" + TMAP[_type] +
"');\n";
1324 definition_code +=
" this.title = '" + titleName +
"';\n"
1325 var desc =
'"MaterialX:' +
id +
'"';
1326 definition_code +=
" this.desc = " + desc +
";\n";
1328 var onNodeCreated =
"function() {\n";
1329 onNodeCreated +=
" //console.log('Node created:', this);\n";
1330 onNodeCreated +=
" }";
1331 definition_code +=
" this.onNodeCreated = " + onNodeCreated +
"\n";
1332 var onRemoved =
"function() {\n";
1333 onRemoved +=
" //console.log('Node removed:', this);\n";
1335 definition_code +=
" this.onRemoved = " + onRemoved +
"\n";
1338 let monitor = editor.monitor;
1339 var onPropertyChanged =
"function(name, value, prev_value) {\n";
1342 onPropertyChanged +=
" MxShadingGraphEditor.theEditor.monitor.onPropertyChanged(this.title, name, value, prev_value, this);\n";
1346 onPropertyChanged +=
" console.log('+ Internal property changed:', this.title, name, value, prev_value, this);\n";
1348 onPropertyChanged +=
" }";
1349 definition_code +=
" this.onPropertyChanged = " + onPropertyChanged +
"\n";
1352 var onPropertyInfoChanged =
"function(name, info, value, prev_value) {\n";
1355 onPropertyInfoChanged +=
" MxShadingGraphEditor.theEditor.monitor.onPropertyInfoChanged(this.title, name, info, value, prev_value, this);\n";
1359 onPropertyInfoChanged +=
" console.log('+ Internal property info changed:', this.title, name, info, value, prev_value, this);\n";
1361 onPropertyInfoChanged +=
" }"
1362 definition_code +=
" this.onPropertyInfoChanged = " + onPropertyInfoChanged +
"\n";
1365 var onConnectOutput =
"function(slot, input_type, input, target_node, target_slot) {\n";
1368 onConnectOutput +=
" MxShadingGraphEditor.theEditor.monitor.onConnectOutput(slot, input_type, input, target_node, target_slot, this);\n";
1372 onConnectOutput +=
" console.log('+ Output connection changed:', this.title, slot, input_type, input, target_node, target_slot);\n";
1374 onConnectOutput +=
" }"
1375 definition_code +=
" this.onConnectOutput = " + onConnectOutput +
"\n";
1378 var onConnectInput =
"function(target_slot, output_type, output, source, slot) {\n";
1381 onConnectInput +=
" MxShadingGraphEditor.theEditor.monitor.onConnectInput(target_slot, output_type, output, source, slot, this);\n";
1385 onConnectInput +=
" console.log('+ Input connection changed:', this.title, target_slot, output_type, output, source, slot);\n";
1387 onConnectInput +=
" }"
1388 definition_code +=
" this.onConnectInput = " + onConnectInput +
"\n";
1390 definition_code +=
" this.color = '#004C94';\n";
1391 definition_code +=
" this.bgcolor = '#000';\n";
1392 if (_type in CMAP) {
1393 definition_code +=
" this.boxcolor = '" + CMAP[_type] +
"';\n";
1395 definition_code +=
" this.shape = LiteGraph.ROUND_SHAPE;\n";
1397 definition_code +=
" this.onExecute = function() {\n";
1398 definition_code +=
" console.log('Executing node: ', this);\n";
1399 definition_code +=
" }\n";
1401 definition_code +=
"}\n"
1402 definition_code +=
"LiteGraph.registerNodeType('" +
id +
"', " + functionName +
");\n";
1406 for (var _type of outputTypes) {
1407 var
id = libraryPrefix +
'/output/output_' + _type;
1408 var functionName =
ne_mx.createValidName(
id);
1409 var titleName =
'output_' + _type;
1411 definition_code +=
"\n/**\n";
1412 definition_code +=
" * @function "+ functionName +
"\n";
1413 definition_code +=
" * @description Library: " + libraryPrefix +
". Category: output. Type: " + _type +
"\n";
1414 definition_code +=
" * LiteGraph id: " +
id +
"\n";
1415 definition_code +=
" */\n";
1417 definition_code +=
"function " + functionName +
"() {\n";
1419 definition_code +=
" this.title = '" + titleName +
"';\n"
1420 var desc =
'"MaterialX Node :' +
id +
'"';
1421 definition_code +=
" this.desc = " + desc +
";\n";
1423 definition_code +=
" this.nodedef_icon = '" + LIBRARY_ICON +
"';\n";
1424 definition_code +=
" this.nodedef_name = '" + OUTPUT_ND + + _type +
"';\n";
1425 definition_code +=
" this.nodedef_node = '" + OUTPUT_NODE_STRING +
"';\n";
1426 definition_code +=
" this.nodedef_type = '" + _type +
"';\n";
1427 definition_code +=
" this.nodedef_group = '" + OUTPUT_NODE_STRING +
"';\n";
1428 definition_code +=
" this.addInput('in', '" + TMAP[_type] +
"');\n";
1430 definition_code +=
" this.addProperty('in', " + value +
", '" + _type +
"');\n";
1431 definition_code +=
" this.addOutput('out', '" + TMAP[_type] +
"');\n";
1433 var onNodeCreated =
"function() {\n";
1434 onNodeCreated +=
" //console.log('Node created:', this);\n";
1435 onNodeCreated +=
" }";
1436 definition_code +=
" this.onNodeCreated = " + onNodeCreated +
"\n";
1437 var onRemoved =
"function() {\n";
1438 onRemoved +=
" //console.log('Node removed:', this);\n";
1440 definition_code +=
" this.onRemoved = " + onRemoved +
"\n";
1442 let monitor = editor.monitor;
1443 var onPropertyChanged =
"function(name, value, prev_value) {\n";
1446 onPropertyChanged +=
" MxShadingGraphEditor.theEditor.monitor.onPropertyChanged(this.title, name, value, prev_value, this);\n";
1450 onPropertyChanged +=
" console.log('+ Internal property changed:', this.title, name, value, prev_value, this);\n";
1452 onPropertyChanged +=
" }";
1453 definition_code +=
" this.onPropertyChanged = " + onPropertyChanged +
"\n";
1455 var onPropertyInfoChanged =
"function(name, info, value, prev_value) {\n";
1458 onPropertyInfoChanged +=
" MxShadingGraphEditor.theEditor.monitor.onPropertyInfoChanged(this.title, name, info, value, prev_value, this);\n";
1462 onPropertyInfoChanged +=
" console.log('+ Internal property info changed:', this.title, name, info, value, prev_value, this);\n";
1464 onPropertyInfoChanged +=
" }"
1465 definition_code +=
" this.onPropertyInfoChanged = " + onPropertyInfoChanged +
"\n";
1469 var onConnectOutput =
"function(slot, input_type, input, target_node, target_slot) {\n";
1472 onConnectOutput +=
" MxShadingGraphEditor.theEditor.monitor.onConnectOutput(slot, input_type, input, target_node, target_slot, this);\n";
1476 onConnectOutput +=
" console.log('+ Output connection changed:', this.title, slot, input_type, input, target_node, target_slot);\n";
1478 onConnectOutput +=
" }"
1479 definition_code +=
" this.onConnectOutput = " + onConnectOutput +
"\n";
1482 var onConnectInput =
"function(target_slot, output_type, output, source, slot) {\n";
1485 onConnectInput +=
" MxShadingGraphEditor.theEditor.monitor.onConnectInput(target_slot, output_type, output, source, slot, this);\n";
1489 onConnectInput +=
" console.log('+ Input connection changed:', this.title, target_slot, output_type, output, source, slot);\n";
1491 onConnectInput +=
" }"
1492 definition_code +=
" this.onConnectInput = " + onConnectInput +
"\n";
1494 definition_code +=
" this.color = '#013514';\n";
1495 definition_code +=
" this.bgcolor = '#000';\n";
1496 if (_type in CMAP) {
1497 definition_code +=
" this.boxcolor = '" + CMAP[_type] +
"';\n";
1499 definition_code +=
" this.shape = LiteGraph.ROUND_SHAPE;\n";
1501 definition_code +=
" this.onExecute = function() {\n";
1502 definition_code +=
" console.log('Executing node:', this);\n";
1503 definition_code +=
" }\n";
1505 definition_code +=
"}\n"
1506 definition_code +=
"LiteGraph.registerNodeType('" +
id +
"', " + functionName +
");\n";
1507 definitionsList.push(
id);
1512 for (var nodeDef of nodeDefs) {
1514 var nodeDefName = nodeDef.getName();
1515 var
id = libraryPrefix +
'/' + nodeDef.getNodeGroup() +
'/' + nodeDefName;
1516 id =
id.replace(
'ND_',
'');
1517 var functionName =
ne_mx.createValidName(
id);
1518 var nodeType = nodeDef.getType();
1519 var titleName = nodeDef.getNodeString() +
"_" + nodeType;
1520 var swatchLocation =
'https://kwokcb.github.io/MaterialX_Learn/resources/mtlx/nodedef_materials/';
1521 var outputs = nodeDef.getActiveOutputs();
1522 var outputName = outputs[0].getName();
1523 var swatchId = swatchLocation +
'material_' + nodeDefName +
'_' + outputName +
'_genglsl.png';
1524 swatchId = swatchId.replace(
'ND_',
'');
1526 console.log(
'\n--- Registering node type:',
id,
'----');
1529 definition_code +=
"\n/**\n";
1530 definition_code +=
" * @function "+ functionName +
"\n";
1531 definition_code +=
" * @description Library: " + libraryPrefix +
". Category: " + nodeString +
". Type: " + nodeType +
"\n";
1532 definition_code +=
" * LiteGraph id: " +
id +
"\n";
1533 definition_code +=
" */\n";
1535 definition_code +=
"function " + functionName +
"() {\n";
1537 var nodeGroup = nodeDef.getNodeGroup();
1538 var nodeString = nodeDef.getNodeString();
1540 if (theIcon.length == 0) {
1541 for (var key in editor.ui.icon_map) {
1542 if (nodeString.toLowerCase().startsWith(key.toLowerCase())) {
1543 if (key in editor.ui.icon_map)
1544 theIcon = editor.ui.icon_map[key];
1548 else if (nodeGroup.toLowerCase().startsWith(key.toLowerCase())) {
1549 if (key in editor.ui.icon_map)
1550 theIcon = editor.ui.icon_map[key];
1557 definition_code +=
" this.nodedef_icon = '" + theIcon +
"';\n";
1558 definition_code +=
" this.nodedef_name = '" + nodeDefName +
"';\n";
1559 definition_code +=
" this.nodedef_type = '" + nodeType +
"';\n";
1560 definition_code +=
" this.nodedef_node = '" + nodeString +
"';\n";
1561 definition_code +=
" this.nodedef_href = 'https://kwokcb.github.io/MaterialX_Learn/documents/definitions/" + nodeString +
".html';\n";
1562 definition_code +=
" this.nodedef_swatch = '" + swatchId +
"';\n";
1563 definition_code +=
" this.nodedef_group = '" + nodeGroup +
"';\n";
1565 for (var input of nodeDef.getActiveInputs()) {
1566 var _name = input.getName();
1567 var _type = input.getType();
1569 _type = TMAP[_type];
1571 console.log(
'Unmappable type:', _type)
1572 definition_code +=
" this.addInput('" + _name +
"','" + _type +
"');\n";
1574 let value = input.getValueString();
1576 let uiname = input.getAttribute(
'uiname');
1578 let uimin = input.getAttribute(
'uimin');
1579 if (uimin.length == 0) {
1582 let uimax = input.getAttribute(
'uimax');
1583 if (uimax.length == 0) {
1586 let uisoftmin = input.getAttribute(
'uisoftmin');
1587 if (uisoftmin.length > 0) {
1590 let uisoftmax = input.getAttribute(
'uisoftmax');
1591 if (uisoftmax.length > 0) {
1594 var uifolder = input.getAttribute(
'uifolder');
1595 var metaData = this.
buildMetaData(
'',
'',
'', uiname, uimin, uimax, uifolder, _type);
1598 let colorspace = input.getAttribute(
'colorspace');
1599 let nodeDefType = nodeDef.getType();
1600 if (_type ==
'filename' && (nodeDefType ==
'color3' || nodeDefType ==
'color4'))
1602 if (colorspace.length == 0)
1604 colorspace =
'none';
1607 if (colorspace.length > 0)
1608 metaData[
'colorspace'] = colorspace;
1612 let unitAttributes = [
'unit',
'unittype'];
1613 for (let unitAttribute of unitAttributes)
1615 let value = input.getAttribute(unitAttribute);
1616 if (value.length > 0)
1618 metaData[unitAttribute] = value;
1623 let defaultgeomprop = input.getAttribute(
'defaultgeomprop')
1624 metaData[
'defaultgeomprop'] = defaultgeomprop;
1627 let enums = input.getAttribute(
'enum');
1628 if (enums.length > 0)
1630 metaData[
'enum'] = enums.split(
',');
1631 metaData[
'enum'].map(
function(x) {
return x.trim(); });
1633 let enumvalues = input.getAttribute(
'enumvalues');
1634 if (enumvalues.length > 0)
1636 metaData[
'enumvalues'] = enumvalues.split(
',');
1637 metaData[
'enumvalues'].map(
function(x) {
return x.trim(); });
1640 metaData = JSON.stringify(metaData);
1641 definition_code +=
" this.addProperty('" + _name +
"', " + value +
", '" + _type +
"'," + metaData +
");\n";
1643 for (var output of nodeDef.getActiveOutputs()) {
1644 var _name = output.getName();
1645 var _type = output.getType();
1647 _type = TMAP[_type];
1649 console.log(
'Unmappable type:', _type)
1652 definition_code +=
" this.addOutput('" + _name +
"','" + _type +
"');\n";
1655 definition_code +=
" this.title = '" + titleName +
"';\n"
1656 var desc =
'"MaterialX:' +
id +
'"';
1657 definition_code +=
" this.desc = " + desc +
";\n";
1663 var onNodeCreated =
"function() {\n";
1664 onNodeCreated +=
" //console.log('Node created:', this);\n";
1665 onNodeCreated +=
"}";
1666 definition_code +=
" this.onNodeCreated = " + onNodeCreated +
"\n";
1667 var onRemoved =
"function() {\n";
1668 onRemoved +=
" //console.log('Node removed:', this);\n";
1670 definition_code +=
" this.onRemoved = " + onRemoved +
"\n";
1672 let monitor = editor.monitor;
1673 var onPropertyChanged =
"function(name, value, prev_value) {\n";
1676 onPropertyChanged +=
" MxShadingGraphEditor.theEditor.monitor.onPropertyChanged(this.title, name, value, prev_value, this);\n";
1680 onPropertyChanged +=
" console.log('+ Internal property changed:', this.title, name, value, prev_value, this);\n";
1682 onPropertyChanged +=
" }";
1683 definition_code +=
" this.onPropertyChanged = " + onPropertyChanged +
"\n";
1685 var onPropertyInfoChanged =
"function(name, info, value, prev_value) {\n";
1688 onPropertyInfoChanged +=
" MxShadingGraphEditor.theEditor.monitor.onPropertyInfoChanged(this.title, name, info, value, prev_value, this);\n";
1692 onPropertyInfoChanged +=
" console.log('+ Internal property info changed:', this.title, name, info, value, prev_value, this);\n";
1694 onPropertyInfoChanged +=
" }"
1695 definition_code +=
" this.onPropertyInfoChanged = " + onPropertyInfoChanged +
"\n";
1698 var onConnectOutput =
"function(slot, input_type, input, target_node, target_slot) {\n";
1701 onConnectOutput +=
" MxShadingGraphEditor.theEditor.monitor.onConnectOutput(slot, input_type, input, target_node, target_slot, this);\n";
1705 onConnectOutput +=
" console.log('+ Output connection changed:', this.title, slot, input_type, input, target_node, target_slot);\n";
1707 onConnectOutput +=
" }"
1708 definition_code +=
" this.onConnectOutput = " + onConnectOutput +
"\n";
1711 var onConnectInput =
"function(target_slot, output_type, output, source, slot) {\n";
1714 onConnectInput +=
" MxShadingGraphEditor.theEditor.monitor.onConnectInput(target_slot, output_type, output, source, slot, this);\n";
1718 onConnectInput +=
" console.log('+ Input connection changed:', this.title, target_slot, output_type, output, source, slot);\n";
1720 onConnectInput +=
" }"
1721 definition_code +=
" this.onConnectInput = " + onConnectInput +
"\n";
1724 definition_code +=
" this.bgcolor = '#111';\n";
1726 if (nodeGroup ==
'conditional') {
1728 definition_code +=
" this.color = '#532200';\n";
1729 definition_code +=
" this.title_text_color = '#000';\n";
1730 definition_code +=
" this.shape = LiteGraph.CARD_SHAPE;\n";
1733 else if (nodeString !=
'convert' &&
1734 (nodeGroup ==
'shader' || nodeType ==
'surfaceshader' || nodeType ==
'volumshader' || nodeType ==
'displacementshader')) {
1735 definition_code +=
" this.color = '#232';\n";
1736 definition_code +=
" this.shape = LiteGraph.ROUND_SHAPE;\n";
1738 else if (nodeGroup ==
'material') {
1739 definition_code +=
" this.color = '#151';\n";
1740 definition_code +=
" this.shape = LiteGraph.BOX_SHAPE;\n";
1743 definition_code +=
" this.color = '#222';\n";
1744 definition_code +=
" this.shape = LiteGraph.ROUND_SHAPE;\n";
1746 if (nodeType in CMAP) {
1747 definition_code +=
" this.boxcolor = '" + CMAP[nodeType] +
"';\n";
1750 definition_code +=
"}\n"
1753 definition_code += functionName +
".nodedef_name = '" + nodeDefName +
"';\n";
1754 definition_code += functionName +
".nodedef_node = '" + nodeString +
"';\n";
1755 definition_code += functionName +
".nodedef_href = 'https://kwokcb.github.io/MaterialX_Learn/documents/definitions/" + nodeString +
".html';\n";
1757 definition_code +=
"LiteGraph.registerNodeType(" +
"'" +
id +
"'," + functionName +
");\n";
1758 definitionsList.push(
id);
1760 definition_code +=
"console.log('Registered node type:', '" +
id +
"');\n";
1764 return definition_code;
1781 let validationDocument =
ne_mx.createDocument();
1782 validationDocument.copyContentFrom(
doc);
1783 validationDocument.importLibrary(
stdlib);
1786 var valid = validationDocument.validate(errors);
1788 this.editor.debugOutput(
'Failed to validate document:\n' + errors.message, 2);
1802 this.editor.debugOutput(
"MaterialX is not initialized", 2);
1806 let writeCustomLibs = graphWriteOptions.writeCustomLibs;
1807 if (writeCustomLibs == undefined)
1809 console.error(
'Graph output option: writeCustomLibs is undefined.')
1810 writeCustomLibs =
true;
1813 var outputDoc =
ne_mx.createDocument();
1816 var generator =
new ne_mx.EsslShaderGenerator.create();
1817 var genContext =
new ne_mx.GenContext(generator);
1824 let doc_string =
ne_mx.writeToXmlString(outputDoc);
1828 if (writeCustomLibs) {
1829 console.log(
'Write custom libraries:',
customlibs.length);
1831 outputDoc.copyContentFrom(customlib[1]);
1833 console.log(
'Write document custom definitions:',
customDocLibs.length);
1835 outputDoc.copyContentFrom(customDocLib[1]);
1841 outputDoc.removeAttribute(
'fileprefix');
1859 this.editor.debugOutput(
"MaterialX is not initialized", 2);
1860 return [
'',
'MaterialX is not initialized'];
1865 this.editor.debugOutput(
"Failed to save graph to document", 2);
1866 return [
'',
'Failed to save graph to document'];
1869 if (extension ==
'mtlx')
1871 const writeOptions =
new ne_mx.XmlWriteOptions();
1872 writeOptions.writeXIncludeEnable =
false;
1875 data =
ne_mx.writeToXmlString(outputDoc, writeOptions);
1877 this.editor.debugOutput(
"Failed to write graph:" + e, 2);
1887 this.editor.debugOutput(
'Failed to find ' + extension +
' exporter', 2);
1890 let exportDoc =
ne_mx.createDocument();
1891 exportDoc.copyContentFrom(outputDoc);
1892 exportDoc.importLibrary(
stdlib);
1894 let result = exporter.export(
ne_mx, exportDoc);
1898 return [
'',
'Failed to export graph to ' + extension];
1916 var blob =
new Blob([data[0]], { type:
"text/plain" });
1917 var url = URL.createObjectURL(blob);
1918 var a = document.createElement(
"a");
1920 a.download =
"output_graph.mtlx";
1937 console.log(
'***** START Scan Graph:',
graph.title);
1938 for (var node of
graph._nodes) {
1939 if (node.type ==
'graph/subgraph') {
1940 var subgraph = node.subgraph;
1942 var subgraphNode = mltxgraph.addChildOfCategory(
'nodegraph', node.title);
1944 console.log(
'---->>> Scan NodeGraph:', node.title);
1950 console.log(
'---->>> Scan Node:', node.title);
1952 var nodeDefName = node.nodedef_name;
1961 var nodedefName = node.nodedef_name;
1963 var nodeElement =
null;
1976 nodeElement = mltxgraph.addChildOfCategory(node.nodedef_node, node.nodedef_type);
1977 nodeElement.setType(node.nodedef_type);
1979 if (graphWriteOptions.saveNodePositions) {
1981 nodeElement.setAttribute(
'xpos', JSON.stringify(node.pos[0]));
1982 nodeElement.setAttribute(
'ypos', JSON.stringify(node.pos[1]));
1985 console.log(
'** Create node:', nodeElement.getNamePath(), nodeElement.getType());
1986 nodeElement.setName(node.title);
1992 console.log(
'-> Write Node:',
graph.title +
'/' + node.title,
' --> ', nodeElement.getNamePath());
1995 console.log(
'Skip writing :', node.title);
2000 var properties = node.properties;
2002 var node_inputs = node.inputs;
2003 var isInputNode =
false;
2004 var isOutputNode =
false;
2005 if (nodeElement.getCategory() ==
'input') {
2007 node_inputs = [node];
2009 else if (nodeElement.getCategory() ==
'output') {
2010 isOutputNode =
true;
2011 node_inputs = [node];
2015 if (!isInputNode && !isOutputNode)
2017 if (node.nodedef_type ==
"multioutput")
2019 console.log(
'Write outputs for:', node,
'. type: ', node.nodedef_type);
2020 for (var output of node.outputs) {
2021 var outputName = output.name;
2022 var outputType = output.type;
2023 var outputElement = nodeElement.addOutput(outputName, outputType);
2025 console.log(
'> Read: node.nodedef_type: ', node.nodedef_type);
2026 console.log(
'> Write: output:', outputElement.getNamePath(), outputElement.getType());
2035 var inputs = node_inputs;
2036 for (var i in inputs) {
2037 let input = inputs[i];
2039 console.log(
'---- Write port:', input);
2041 let inputName = input.name;
2042 let inputType = input.type;
2043 if (nodeElement.getCategory() ==
'input' ||
2044 nodeElement.getCategory() ==
'output') {
2046 inputType = node.nodedef_type;
2050 var inputElement =
null;
2051 var nodeToCheck = node;
2052 var inputNode =
null;
2053 var inputLink =
null;
2054 if (isInputNode && node.graph._subgraph_node) {
2055 nodeToCheck = node.graph._subgraph_node;
2056 for (var i = 0; i < nodeToCheck.inputs.length; i++) {
2057 var nci = nodeToCheck.inputs[i];
2058 if (nci.name == node.title) {
2059 inputNode = nodeToCheck.getInputNode(i);
2060 inputLink = nodeToCheck.getInputLink(i);
2067 inputNode = node.getInputNode(i);
2068 inputLink = node.getInputLink(i);
2070 var inputLinkOutput =
'';
2071 var numInputOutputs = 0;
2073 numInputOutputs = inputNode.outputs.length;
2074 inputLinkOutput = inputNode.outputs[inputLink.origin_slot];
2078 if (nodeElement.getCategory() !=
'input' &&
2079 nodeElement.getCategory() !=
'output') {
2080 inputElement = nodeElement.getInput(inputName);
2082 inputElement = nodeElement.addInput(inputName, inputType);
2085 inputElement = nodeElement;
2089 console.log(
'Write connection');
2090 console.log(
' - TO:', inputElement.getName() +
"." + inputName);
2091 console.log(
' - FROM link:', inputNode.id +
"." + inputLinkOutput.name);
2093 if (inputNode.type ==
'graph/subgraph') {
2094 inputElement.setNodeGraphString(inputNode.title);
2096 if (numInputOutputs > 1 && inputLinkOutput) {
2097 inputElement.setOutputString(inputLinkOutput.name);
2105 if (inputNode.nodedef_node ==
'input') {
2108 inputElement.setInterfaceName(inputNode.title);
2112 inputElement.setNodeName(inputNode.title);
2115 if (numInputOutputs > 1 && inputNode.nodedef_node !=
'output') {
2117 if (inputLinkOutput) {
2118 inputElement.setOutputString(inputLinkOutput.name);
2127 var inputValue = node.properties[inputName];
2128 if (inputValue ==
null) {
2129 console.log(
'Cannot find property value for input:', inputName);
2132 var origValue = inputValue;
2134 if ([
'float',
'integer'].includes(inputType)) {
2135 inputValue = inputValue.toString();
2137 else if ([
'vector2',
'vector3',
'vector4',
'matrix33',
'matrix44',
'color3',
'color4'].includes(inputType)) {
2138 inputValue = inputValue.toString();
2139 inputValue = inputValue.split(
',').map(Number).join(
', ');
2141 else if (inputType ===
'boolean') {
2142 if (inputValue ===
'true')
2143 inputValue =
'true';
2145 inputValue =
'false';
2148 inputValue = inputValue.toString();
2152 if (nodeElement.getCategory() !=
'input' &&
2153 nodeElement.getCategory() !=
'output') {
2154 inputElement = nodeElement.getInput(inputName);
2156 inputElement = nodeElement.addInput(inputName, inputType);
2159 console.log(
'Error> Trying add input more than once:', inputName,
' to node: ', nodeElement.getNamePath());
2163 inputElement = nodeElement;
2167 inputElement.setValueString(inputValue, inputType);
2170 console.warn(
"Set value error: ", e);
2177 var propInfo =
null;
2178 var skip_attributes = [];
2179 if (isInputNode || isOutputNode) {
2180 if (input.properties_info) {
2182 skip_attributes = [
'interfacename',
'nodegraph',
'nodename',
'name',
'type',
'value',
'default_value'];
2183 propInfo = input.properties_info[0];
2187 if (node.properties_info) {
2189 skip_attributes = [
'interfacename',
'nodegraph',
'nodename',
'name',
'type',
'value',
'default_value',
'uimin',
'uimax',
'uiname',
'uifolder'];
2190 propInfo = node.properties_info[i];
2197 skip_attributes = skip_attributes.concat([
'defaultgeomprop',
'enum',
'enumvalues']);
2199 for (var propAttribute in propInfo) {
2200 if (skip_attributes.includes(propAttribute))
2204 var propAttributeValue = propInfo[propAttribute];
2205 if (propAttributeValue && propAttributeValue.length > 0) {
2206 inputElement.setAttribute(propAttribute, propAttributeValue);
2214 console.log(
'---- END Write inputs:', node.inputs);
2218 console.log(
'---> End write node', node.title);
2222 console.log(
'***** END Scan Graph:',
graph.title);
2232 var ARRAY_TYPES = [
'color3',
'color4',
'vector2',
'vector3',
'vector4',
'matrix33',
'matrix44'];
2233 if (ARRAY_TYPES.includes(_type)) {
2245 const uriSchemes = [
'http://',
'https://',
'ftp://',
'blob:',
'file://',
'data:'];
2246 return uriSchemes.some(scheme => s.startsWith(scheme));
2261 var nodeInputs = [];
2262 var isOutput = (node.getCategory() ==
'output');
2263 var isInput = (node.getCategory() ==
'input');
2264 if (isOutput || isInput) {
2265 nodeInputs = [node];
2268 nodeInputs = node.getInputs();
2270 for (var input of nodeInputs) {
2274 if (!isOutput && !isInput) {
2275 _name = input.getName();
2276 explicitInputs.push(_name);
2279 var nodeName = input.getNodeName();
2280 var nodeGraphName = input.getNodeGraphString();
2281 var inputInterfaceName = input.getInterfaceName();
2282 var outputName = input.getOutputString();
2284 if (nodeName.length ||
2285 nodeGraphName.length ||
2286 inputInterfaceName.length ||
2287 outputName.length) {
2294 var target_node = lg_node;
2295 var target_slot =
null;
2296 if (!isOutput && !isInput)
2297 target_slot = target_node.findInputSlot(_name);
2300 var source_node =
null;
2301 var source_slot = 0;
2302 var source_name = nodeName;
2303 if (nodeGraphName.length) {
2304 source_name = nodeGraphName;
2306 if (inputInterfaceName.length) {
2307 source_name = inputInterfaceName;
2310 var graphToCheck =
graph;
2311 if (isInput &&
graph._subgraph_node) {
2312 target_node =
graph._subgraph_node;
2313 target_slot = target_node.findInputSlot(lg_node.title);
2315 graphToCheck = parentGraph;
2320 source_node = graphToCheck.findNodeByTitle(source_name);
2323 var outputSlot = source_node.findOutputSlot(outputName);
2324 if (outputSlot >= 0) {
2325 source_slot = outputSlot;
2328 editor.debugOutput(
'Failed to find output slot:' + outputName, 1);
2330 var linkInfo = source_node.connect(source_slot, target_node, target_slot);
2332 editor.debugOutput(
'Failed to connect:' + source_node.title +
'.' + outputName,
'->', target_node.title +
'.' + _name), 1,
false;
2337 var linkInfo =
null;
2338 if (source_slot ==
null || target_slot ==
null || target_node ==
null) {
2339 console.warning(
'Cannot connect!')
2342 linkInfo = source_node.connect(source_slot, target_node, target_slot);
2345 editor.debugOutput(
'Failed to connect:' + source_node.title +
'.' + outputName,
'->', target_node.title +
'.' + _name, 1);
2351 console.log(
'Failed to find node ', source_name,
'in graph:', graphToCheck);
2352 this.editor.debugOutput(
'Failed to find source node: ' + source_node +
"." +
2353 source_name,
'->', lg_node.title +
"." + _name, 2);
2357 const inputType = input.getAttribute(
ne_mx.TypedElement.TYPE_ATTRIBUTE);
2358 let valueString = input.getValueString();
2359 if (valueString.length > 0) {
2362 if (inputType ===
ne_mx.FILENAME_TYPE_STRING) {
2365 if (this.
isURI(valueString)) {
2366 this.editor.debugOutput(
'Filename is a url:' + valueString +
'. Cannot use resolved path value.');
2367 _value = input.getValueString();
2370 _value = input.getResolvedValueString();
2375 _value = input.getResolvedValueString();
2377 if (this.
isArray(input.getType())) {
2378 let valueArray =
"[" + _value +
"]"
2379 valueArray = JSON.parse(valueArray);
2382 _value = valueArray;
2388 lg_node.setProperty(_name, _value);
2392 var property_info = lg_node.getPropertyInfo(_name);
2406 if (input && property_info) {
2409 var colorspace = input.getColorSpace();
2410 if (colorspace.length > 0)
2411 property_info[
'colorspace'] = colorspace;
2413 var unit = input.getUnit();
2414 if (unit.length > 0)
2415 property_info[
'unit'] = unit;
2417 var uiname = input.getAttribute(
'uiname');
2418 if (uiname.length > 0)
2419 property_info[
'uiname'] = uiname;
2421 var uimin = input.getAttribute(
'uimin');
2422 if (uimin.length > 0)
2423 property_info[
'uimin'] = uimin;
2425 var uimax = input.getAttribute(
'uimax');
2426 if (uimax.length > 0)
2427 property_info[
'uimax'] = uimax;
2428 var uisoftmin = input.getAttribute(
'uisoftmin');
2429 if (uisoftmin.length > 0)
2430 property_info[
'uimin'] = uisoftmin;
2432 var uisoftmax = input.getAttribute(
'uisoftmax');
2433 if (uisoftmax.length > 0)
2434 property_info[
'uimax'] = uisoftmax;
2436 var uifolder = input.getAttribute(
'uifolder');
2437 if (uifolder.length > 0)
2438 property_info[
'uifolder'] = uifolder;
2440 var basicMetaData = [
'colorspace',
'unit',
'uiname',
'uimin',
'uimax',
'uifolder',
'name',
'type',
'output',
'nodename',
'nodegraph'];
2441 for (var attrName of input.getAttributeNames()) {
2442 if (!basicMetaData.includes(attrName)) {
2443 property_info[attrName] = input.getAttribute(attrName);
2447 if (node && input.getType() ==
'filename')
2449 let nodeType = node.getType();
2450 let colorTypes = [
'color3',
'color4'];
2452 if (colorTypes.includes(nodeType))
2454 if (!property_info[
'colorspace']) {
2455 console.log(
'Auto create "none" colorspace for input:', input.getName());
2456 let
doc = node.getDocument();
2457 let defaultColorSpace =
'none';
2462 property_info[
'colorspace'] = defaultColorSpace;
2481 let loadNodePositions =
false;
2485 editor.debugOutput(
"MaterialX is not initialized", 2);
2489 editor.clearGraph();
2492 editor.monitor.monitorGraph(
graph,
false);
2496 var mtlxNodeDefs = [];
2498 for (var interfaceInput of
doc.getInputs()) {
2499 var _type = interfaceInput.getType();
2500 var
id =
'mtlx/input/input_' + _type;
2502 var lg_node = LiteGraph.createNode(
id);
2504 lg_node.title = interfaceInput.getName();
2506 console.log(
'Add top level input:', lg_node.title,
'to graph',
graph);
2508 var _value = interfaceInput.getValueString();
2509 if (_value && _value.length > 0) {
2510 if (this.
isArray(interfaceInput.getType())) {
2511 _value =
"[" + _value +
"]"
2512 _value = JSON.parse(_value);
2514 lg_node.setProperty(
'in', _value);
2517 if (loadNodePositions) {
2518 var xpos = interfaceInput.getAttribute(
'xpos');
2519 var ypos = interfaceInput.getAttribute(
'ypos');
2520 if (xpos.length > 0 && ypos.length > 0) {
2521 lg_node.pos[0] = xpos;
2522 lg_node.pos[1] = ypos;
2528 lg_node.setSize(lg_node.computeSize());
2536 for (var interfaceOutput of
doc.getOutputs()) {
2537 var _type = interfaceOutput.getType()
2538 var
id =
'mtlx/output/output_' + _type;
2540 var lg_node = LiteGraph.createNode(
id);
2542 lg_node.title = interfaceOutput.getName();
2545 console.log(
'Add graph output:', lg_node.title);
2549 lg_node.setSize(lg_node.computeSize());
2551 if (loadNodePositions) {
2552 var xpos = interfaceOutput.getAttribute(
'xpos');
2553 var ypos = interfaceOutput.getAttribute(
'ypos');
2554 if (xpos.length > 0 && ypos.length > 0)
2555 lg_node.pos = [xpos, ypos];
2558 mtlxNodes.push([interfaceOutput, lg_node,
graph]);
2562 for (var node of
doc.getNodes()) {
2563 var nodeDef = node.getNodeDef();
2565 editor.debugOutput(
'Skip node w/o nodedef:' + node.getName(), 1)
2570 var
id =
'mtlx/' + nodeDef.getNodeGroup() +
'/' + nodeDef.getName();
2571 id =
id.replace(
'ND_',
'');
2573 console.log(
'Load node:', node.getName(),
' -> ',
id);
2575 var lg_node = LiteGraph.createNode(
id);
2578 lg_node.title = node.getName();
2583 lg_node.setSize(lg_node.computeSize());
2585 if (loadNodePositions) {
2586 var xpos = node.getAttribute(
'xpos');
2587 var ypos = node.getAttribute(
'ypos');
2588 if (xpos.length > 0 && ypos.length > 0)
2589 lg_node.pos = [xpos, ypos];
2592 mtlxNodes.push([node, lg_node,
graph]);
2593 mtlxNodeDefs.push(nodeDef);
2596 editor.debugOutput(
'Failed to create node:' + node.getName(), 2);
2600 for (var nodegraph of
doc.getNodeGraphs()) {
2601 if (nodegraph.hasSourceUri()) {
2604 var nodedefAttrib = nodegraph.getAttribute(
'nodedef');
2605 if (nodedefAttrib && nodedefAttrib.length > 0) {
2606 console.log(
'Skip loading in functional graph:', nodegraph.getName(),
'nodedef:', nodedefAttrib);
2610 console.log(
'Create nodegraph:', nodegraph.getName());
2612 var title = nodegraph.getName();
2613 var subgraphNode = LiteGraph.createNode(
"graph/subgraph", title);
2617 subgraphNode.bgImageUrl =
"./Icons/nodegraph.png";
2619 var mtlxSubGraphNodes = [];
2620 for (var interfaceInput of nodegraph.getInputs()) {
2621 var _type = interfaceInput.getType();
2622 var
id =
'mtlx/input/input_' + _type;
2624 var lg_node = LiteGraph.createNode(
id);
2626 lg_node.title = interfaceInput.getName();
2628 subgraphNode.subgraph.add(lg_node);
2631 console.log(
'-------- Add subgraph input:', lg_node.title, lg_node);
2633 subgraphNode.addInput(interfaceInput.getName(), _type);
2634 subgraphNode.subgraph.addInput(interfaceInput.getName(), _type);
2636 var _value = interfaceInput.getValueString();
2637 if (_value && _value.length > 0) {
2638 if (this.
isArray(interfaceInput.getType())) {
2639 _value =
"[" + _value +
"]"
2640 _value = JSON.parse(_value);
2642 lg_node.setProperty(
'in', _value);
2646 lg_node.setSize(lg_node.computeSize());
2648 if (loadNodePositions) {
2649 var xpos = nodegraph.getAttribute(
'xpos');
2650 var ypos = nodegraph.getAttribute(
'ypos');
2651 if (xpos.length > 0 && ypos.length > 0)
2652 lg_node.pos = [xpos, ypos];
2655 mtlxSubGraphNodes.push([interfaceInput, lg_node,
graph]);
2659 for (var interfaceOutput of nodegraph.getOutputs()) {
2660 var _type = interfaceOutput.getType()
2661 var
id =
'mtlx/output/output_' + _type;
2663 var lg_node = LiteGraph.createNode(
id);
2665 lg_node.title = interfaceOutput.getName();
2666 subgraphNode.subgraph.add(lg_node);
2668 console.log(
'Add subgraph output:', lg_node.title);
2670 subgraphNode.addOutput(interfaceOutput.getName(), _type);
2671 subgraphNode.subgraph.addOutput(interfaceOutput.getName(), _type);
2674 lg_node.setSize(lg_node.computeSize());
2676 if (loadNodePositions) {
2677 var xpos = interfaceOutput.getAttribute(
'xpos');
2678 var ypos = interfaceOutput.getAttribute(
'ypos');
2679 if (xpos.length > 0 && ypos.length > 0)
2680 lg_node.pos = [xpos, ypos];
2683 mtlxSubGraphNodes.push([interfaceOutput, lg_node,
graph]);
2688 for (var node of nodegraph.getNodes()) {
2689 var nodeDef = node.getNodeDef();
2691 editor.debugOutput(
'Skip node w/o nodedef:' + node.getName(), 1)
2696 var
id =
'mtlx/' + nodeDef.getNodeGroup() +
'/' + nodeDef.getName();
2697 id =
id.replace(
'ND_',
'');
2699 var lg_node = LiteGraph.createNode(
id);
2700 lg_node.title = node.getName();
2701 subgraphNode.subgraph.add(lg_node);
2703 console.log(
'Add subgraph node:', lg_node.title);
2706 lg_node.setSize(lg_node.computeSize());
2708 if (loadNodePositions) {
2709 var xpos = node.getAttribute(
'xpos');
2710 var ypos = node.getAttribute(
'ypos');
2711 if (xpos.length > 0 && ypos.length > 0)
2712 lg_node.pos = [xpos, ypos];
2715 mtlxSubGraphNodes.push([node, lg_node,
graph]);
2718 for (var item of mtlxSubGraphNodes) {
2720 var lg_node = item[1];
2721 var parentGraph = item[2];
2722 var explicitInputs = [];
2725 lg_node.setSize(lg_node.computeSize());
2728 this.
buildConnections(editor, node, lg_node, explicitInputs, subgraphNode.subgraph, parentGraph);
2732 console.log(
'Add subgraph:', subgraphNode.title);
2734 if (auto_arrange > 0) {
2735 subgraphNode.subgraph.arrange(auto_arrange);
2738 graph.add(subgraphNode);
2745 for (var item of mtlxNodes) {
2747 var lg_node = item[1];
2750 var explicitInputs = [];
2754 if (lg_node.nodedef_node ==
'input' || lg_node.nodedef_node ==
'output') {
2782 editor.monitor.monitorGraph(
graph,
true);
2784 if (auto_arrange > 0) {
2785 graph.arrange(auto_arrange);
2788 graph.setDirtyCanvas(
true,
true);
2801 var input = document.createElement(
"input");
2802 input.style = this.fontSizeStyle;
2803 input.type =
"file";
2804 input.accept =
".mtlx";
2805 input.onchange =
function (e) {
2806 var file = e.target.files[0];
2807 console.log(
'Loading definitions from file: ' + file.name);
2812 const reader =
new FileReader();
2813 reader.readAsText(file,
'UTF-8');
2815 reader.onload =
function (e) {
2817 let fileContents = e.target.result;
2822 const readOptions =
new ne_mx.XmlReadOptions();
2823 readOptions.readXIncludes =
false;
2824 var customLib =
ne_mx.createDocument();
2826 await
ne_mx.readFromXmlString(customLib, fileContents,
'', readOptions);
2830 console.log(
'Create custom library definitions',
ne_mx.prettyPrint(customLib));
2832 var scanForIcon =
false;
2835 var iconName = file.name.replace(/\.[^/.]+$/,
".webp");
2837 var iconExists = await that.editor.uriExists(iconName);
2842 var definitionsList = [];
2843 var result = that.createLiteGraphDefinitions(customLib,
false,
false, definitionsList,
'mtlx', that.editor, iconName);
2846 var definitionsListString = definitionsList.join(
', ');
2847 that.editor.debugOutput(
"Registered custom node types: [" + definitionsListString +
"]", 0,
false);
2848 that.editor.displayNodeTypes();
2851 console.log(
'Error evaluating source:', e);
2859 that.editor.debugOutput(
'Error reading definitions:' + error, 2,
false);
2866 that.editor.debugOutput(
"MaterialX is not initialized", 2);
2886 console.log(
'MaterialX is not initialized');
2891 if (extension !=
'mtlx')
2895 let result = converter.import(
ne_mx, fileContents,
stdlib);
2897 fileContents = result[0];
2900 console.log(
'Failed to convert from:', extension,
'to mtlx. Errors:', result[1]);
2906 console.log(
'Failed to find converter from:', extension,
'to mtlx.');
2913 const readOptions =
new ne_mx.XmlReadOptions();
2914 readOptions.readXIncludes =
false;
2920 console.log(
'Import custom library:', item[0]);
2921 doc.importLibrary(item[1]);
2923 var loadDoc =
ne_mx.createDocument();
2924 await
ne_mx.readFromXmlString(loadDoc, fileContents,
'', readOptions);
2928 var customLib =
ne_mx.createDocument();
2929 customLib.copyContentFrom(loadDoc);
2930 var keepChildren = [];
2931 var existingDefs = []
2932 var saveCustomLib =
false;
2933 doc.getNodeDefs().forEach(def => { existingDefs.push(def.getName()); });
2934 for (var nodedef of loadDoc.getNodeDefs()) {
2935 var nodedefName = nodedef.getName();
2936 if (!existingDefs.includes(nodedefName)) {
2937 keepChildren.push(nodedef.getName());
2938 saveCustomLib =
true;
2941 for (var ng of loadDoc.getNodeGraphs()) {
2942 if (ng.getAttribute(
'nodedef') && ng.getAttribute(
'nodedef').length > 0) {
2943 saveCustomLib =
true;
2944 keepChildren.push(ng.getName());
2948 if (saveCustomLib) {
2950 for (var child of customLib.getChildren()) {
2951 if (!keepChildren.includes(child.getName())) {
2953 customLib.removeChild(child.getName());
2957 var additionDefs = [];
2958 console.log(
'Create custom library definitions from addtionDefs:',
2959 ne_mx.prettyPrint(customLib));
2964 console.log(
'Loaded local definitions: ', additionDefs);
2966 console.log(
'Error evaluating source:', e);
2970 doc.copyContentFrom(loadDoc);
2977 if (saveCustomLib) {
2982 var documentColorSpace =
doc.getColorSpace();
2988 loadDoc.removeAttribute(
'fileprefix');
2989 fileContents =
ne_mx.writeToXmlString(loadDoc);
2997 if (documentDisplayUpdater) {
2998 documentDisplayUpdater(fileContents);
3001 console.log(
'No docDisplayUpdater!!!');
3006 if (renderableItemUpdater) {
3008 if (!renderableItems || renderableItems.length == 0) {
3009 MxShadingGraphEditor.theEditor.debugOutput(
'No renderable items found in graph: ' + fileName, 1,
false);
3011 renderableItemUpdater(renderableItems);
3018 console.log(
'> Update rendering from document');
3019 let theRenderer = this.editor.monitor.renderer;
3022 theRenderer.updateMaterialFromText(fileContents);
3026 MxShadingGraphEditor.theEditor.debugOutput(
'Error reading document: ' + fileName +
'. Error: ' + error, 2,
false);
3050 const reader =
new FileReader();
3051 reader.readAsText(file,
'UTF-8');
3052 reader.accept =
'.mtlx';
3055 console.log(
'loadFromFile:', file, fileName);
3057 reader.onload =
function (e) {
3059 let fileContents = e.target.result;
3060 console.log(
"read file: ", file.name,
" with extension: ", extension,
" and length: ", fileContents.length);
3062 that.loadFromString(
'mtlx', fileContents, fileName, auto_arrange,
true);
3065 MxShadingGraphEditor.theEditor.debugOutput(
'Error reading document: ' + fileName +
'. Error: ' + error, 2,
false);
3069 editor.debugOutput(
"MaterialX is not initialized", 2,
false);
3073 loadFromZip(extension, file, fileName, editor, auto_arrange, rerender=
false)
3077 console.log(
"Loading content from zip:", file.name);
3079 const reader =
new FileReader();
3082 reader.onload = async
function (e) {
3084 const zipData =
new Uint8Array(e.target.result);
3086 let documents = result[0];
3087 let docText = documents[0].content;
3088 let docFile = documents[0].name;
3089 console.log(
'Documents:', docText);
3091 let textures = result[1];
3092 for (let i = 0; i < textures.length; i++) {
3093 const url = textures[i].url;
3094 const textureName = textures[i].name;
3098 docText = docText.replace(
new RegExp(textureName,
'i'), url);
3099 console.log(
'Replace reference:' + textureName +
' with blob: ' + url);
3102 that.loadFromString(
'mtlx', docText, docFile, auto_arrange, rerender);
3106 console.error(
'Error loading ZIP file:', error);
3110 reader.readAsArrayBuffer(file);
3113 console.error(
"MaterialX is not initialized");
3132 var generator =
new ne_mx.EsslShaderGenerator.create();
3133 var genContext =
new ne_mx.GenContext(generator);
3136 console.log(
'Loaded standard libraries:',
stdlib.getNodeDefs().length);
3150 if (name.length == 0) {
3152 msg =
'Setting empty name as "blank"';
3159 var nodes =
graph._nodes;
3161 for (var node of nodes) {
3162 nodenames.push(node.title);
3166 name =
ne_mx.createValidName(name);
3168 if (!nodenames.includes(name)) {
3173 var rootName = name;
3175 var number = name.match(/\d+$/);
3177 i = (parseInt(number) + 1)
3178 rootName = name.slice(0, -number[0].length);
3181 var valid_name = rootName + i.toString();
3182 while (nodenames.includes(valid_name)) {
3184 valid_name = rootName + i.toString();
3206 this.fontSizeStyle =
'font-size: 11px;';
3209 let gltfConverter =
new glTFMaterialX();
3210 this.handler.addConverter(gltfConverter);
3212 console.log(
'Create new editor with exporter for:', gltfConverter.exportType());
3231 setDirty(w =
null, h =
null) {
3233 graph.setDirtyCanvas(
true,
true);
3247 debugOutput(text, severity, clear =
null) {
3250 consoleLog(text, severity, clear);
3253 console.log(
'> ', text,
' severity:', severity);
3263 setSourceColorSpace(colorSpace) {
3265 this.handler.setSourceColorSpace(colorSpace);
3275 setTargetDistanceUnit(unit) {
3277 this.handler.setTargetDistanceUnit(unit);
3286 getSourceColorSpace() {
3288 return this.handler.getSourceColorSpace();
3290 return 'lin_rec709';
3298 getTargetDistanceUnit() {
3300 return this.handler.getTargetDistanceUnit();
3312 if (title.length == 0) {
3317 let nodesFound = [];
3321 const pattern =
new RegExp(title);
3322 for (var i = 0, l = theGraph._nodes.length; i < l; ++i) {
3323 if (pattern.test(theGraph._nodes[i].title)) {
3324 console.log(
'-- add found node:', theGraph._nodes[i].title);
3325 nodesFound.push(theGraph._nodes[i]);
3328 if (theGraph._nodes[i].title == title)
3330 nodesFound.length = 0;
3331 nodesFound.push(theGraph._nodes[i]);
3337 if (nodesFound.length > 0)
3345 this.debugOutput(
'Node not found: ' + title, 0,
false);
3356 arrangeGraph(spacing = 80) {
3368 for (var s in selected) {
3369 var node = selected[s];
3370 if (node.type ==
'graph/subgraph') {
3413 this.handler.sourceColorSpace = this.handler.DEFAULT_COLOR_SPACE;
3414 this.handler.targetDistanceUnits = this.handler.DEFAULT_DISTANCE_UNITS;
3416 this.updatePropertyPanel(
null);
3430 saveSerialization() {
3431 var data = JSON.stringify(
graph.serialize(),
null, 2);
3432 var blob =
new Blob([data], { type:
"text/plain" });
3433 var url = URL.createObjectURL(blob);
3434 var a = document.createElement(
"a");
3436 a.download =
"serialized_graph.json";
3443 loadSerialization() {
3446 var input = document.createElement(
"input");
3447 input.style = this.fontSizeStyle;
3448 input.type =
"file";
3449 input.accept =
".json";
3450 input.onchange =
function (e) {
3451 var file = e.target.files[0];
3452 var reader =
new FileReader();
3453 reader.onload =
function (event) {
3454 var data = JSON.parse(event.target.result);
3455 graph.configure(data);
3457 reader.readAsText(file);
3469 saveGraphToFile(extension, graphWriteOptions) {
3470 if (this.handler.canExport(extension)) {
3471 this.handler.saveGraphToFile(extension,
graph, graphWriteOptions);
3475 this.debugOutput(
'Unsupported extension for saving graph:' + extension, 2,
false);
3485 saveGraphToString(extension, graphWriteOptions) {
3486 if (this.handler.canExport(extension)) {
3487 return this.handler.saveGraphToString(extension,
graph, graphWriteOptions);
3491 this.debugOutput(
'Unsupported extension for saving graph: ' + extension, 2,
false);
3502 loadDefinitionsFromFile(extension) {
3503 if (extension ==
'mtlx') {
3504 this.handler.loadDefinitionsFromFile();
3508 this.debugOutput(
'Unsupported extension for loading definitions: ' + extension, 2,
false);
3520 loadGraphFromFile(extension, auto_arrange, rerender=
false) {
3522 if (!this.handler.canImport(extension)) {
3523 this.debugOutput(
'Unsupported extension for loading graph: ' + extension, 2,
false);
3528 if (extension ==
'mtlx')
3530 var input = document.createElement(
"input");
3531 input.style = this.fontSizeStyle;
3532 input.type =
"file";
3533 input.accept =
"." + this.handler.getExtension();
3534 input.onchange =
function (e) {
3535 var file = e.target.files[0];
3536 console.log(
'Loading file: ' + file.name);
3541 else if (extension ==
'zip')
3543 var input = document.createElement(
"input");
3544 input.style = this.fontSizeStyle;
3545 input.type =
"file";
3546 input.accept =
".zip";
3547 input.onchange =
function (e) {
3548 var file = e.target.files[0];
3550 console.log(
'Loading zip file: ' + file.name);
3563 findRenderableItems() {
3564 return this.handler.findRenderableItems(
graph);
3577 loadGraphFromString(extension, content, fileName, auto_arrange, rerender=
false) {
3578 if (!this.handler.canImport(extension)) {
3579 this.debugOutput(
'Unsupported extension for loading graph: ' + extension, 2,
false);
3583 if (content.length > 0)
3584 this.handler.loadFromString(extension, content, fileName, auto_arrange, rerender);
3597 console.log(
'rgbToHex empty !', rgb);
3600 return '#' + rgb.map(x => {
3601 var hex = Math.round(x * 255).toString(16);
3602 return hex.length === 1 ?
'0' + hex : hex;
3614 createButtonWithImageAndText(imageSrc, text,
id) {
3616 var img = document.createElement(
"img");
3617 img.id =
id +
"_img";
3619 img.classList.add(
"img-fluid");
3622 var span = document.createElement(
"span");
3623 span.id =
id +
"_text";
3624 span.textContent =
" " + text;
3627 var button = document.createElement(
"button");
3629 button.classList.add(
"btn",
"btn-sm",
"btn-outline-secondary",
"form-control",
"form-control-sm");
3630 button.style = this.fontSizeStyle;
3631 button.appendChild(img);
3632 button.appendChild(span);
3644 openImageDialog(theNode, updateProp, wantURI) {
3647 var fileInput = document.createElement(
'input');
3648 fileInput.type =
'file';
3649 fileInput.accept =
'image/*';
3650 fileInput.style.display =
'none';
3651 document.body.appendChild(fileInput);
3656 fileInput.addEventListener(
'change',
function () {
3657 var fileURI = fileInput.value.split(
'\\').pop();
3658 var file = fileInput.files[0];
3660 fileURI = URL.createObjectURL(file);
3662 var updateElementId =
'__pp:' + updateProp;
3663 var textInput = document.getElementById(updateElementId);
3665 textInput.value = fileURI;
3666 theNode.setProperty(updateProp, fileURI);
3668 var propertypanel_preview = document.getElementById(
'propertypanel_preview');
3669 if (propertypanel_preview) {
3670 propertypanel_preview.src = URL.createObjectURL(file);
3671 propertypanel_preview.style.display =
"block";
3674 var previewImage =
false;
3676 if (propertypanel_preview) {
3677 var reader =
new FileReader();
3678 reader.onload =
function (event) {
3679 propertypanel_preview.src =
event.target.result;
3683 reader.readAsDataURL(file);
3684 propertypanel_preview.style.display =
"block";
3688 document.body.removeChild(fileInput);
3702 return Promise.resolve(
true);
3704 return Promise.resolve(
false);
3708 console.log(
'Error checking URI:', error);
3709 return Promise.resolve(
false);
3720 createColorSpaceInput(colorSpaces, activeItem) {
3721 var select = document.createElement(
"select");
3722 select.className =
"form-control form-control-sm";
3723 select.style = this.fontSizeStyle;
3724 select.id =
"propertypanel_colorspace";
3725 for (var i = 0; i < colorSpaces.length; i++) {
3726 var option = document.createElement(
"option");
3727 option.value = colorSpaces[i];
3728 option.text = colorSpaces[i];
3732 var option = document.createElement(
"option");
3733 option.value =
"none";
3734 option.text =
"none";
3737 select.value = activeItem;
3749 createUnitsInput(units, unittype, activeItem) {
3750 var select = document.createElement(
"select");
3751 select.className =
"form-control form-control-sm";
3752 select.style = this.fontSizeStyle;
3753 select.id =
"propertypanel_units";
3754 for (var i = 0; i < units.length; i++) {
3755 var option = document.createElement(
"option");
3756 var unit_pair = units[i];
3757 if (unit_pair[1] == unittype) {
3758 option.value = unit_pair[0];
3759 option.text = unit_pair[0];
3763 select.value = activeItem;
3773 updateImagePreview(curImage) {
3774 var propertypanel_preview = document.getElementById(
'propertypanel_preview');
3775 if (curImage && propertypanel_preview) {
3776 this.uriExists(curImage)
3779 propertypanel_preview.src = curImage;
3780 propertypanel_preview.style.display =
"block";
3783 propertypanel_preview.src =
"./Icons/no_image.png";
3784 propertypanel_preview.style.display =
"block";
3801 updatePropertyPanel(node) {
3802 const TRUNC_TEXT = 20;
3806 if (!propertypanelcontent) {
3807 console.error(
'No property panel content widget found!');
3811 while (propertypanelcontent.firstChild) {
3812 propertypanelcontent.removeChild(propertypanelcontent.firstChild);
3817 if (node && node.nodedef_icon) {
3818 panelIcon.src = node.nodedef_icon;
3820 else if (this.ui.icon_map) {
3821 if (!node || node.type ==
'graph/subgraph') {
3822 panelIcon.src = this.ui.icon_map[
'_default_graph_'];
3824 panelIcon.src = this.ui.icon_map[
'_default_'];
3828 propertypanelcontent.innerHTML =
"";
3830 let colorSpaces = this.handler.getColorSpaces();
3831 let targetUnits = this.handler.getUnits();
3833 let inUnselectedNodeGraph =
false
3836 inUnselectedNodeGraph =
true
3842 else if (!node && !
graphcanvas.graph._is_subgraph) {
3843 var docInfo = [[
'Colorspace', this.getSourceColorSpace()],
3844 [
'Distance', this.getTargetDistanceUnit()]];
3846 for (let item of docInfo) {
3848 let elem = document.createElement(
"div");
3849 elem.className =
"row px-1 py-0";
3850 let label = document.createElement(
"div");
3851 label.className =
"col py-0 col-form-label-sm text-left";
3852 label.style = this.fontSizeStyle;
3853 label.innerHTML =
"<b>" + item[0] +
"</b>";
3854 elem.appendChild(label);
3856 if (item[0] ==
'Colorspace' && colorSpaces.length > 0) {
3858 var inputCol = document.createElement(
"div");
3859 inputCol.className =
"col text-left";
3860 var select = this.createColorSpaceInput(colorSpaces, item[1]);
3861 select.onchange =
function (e) {
3864 inputCol.appendChild(select);
3865 elem.appendChild(inputCol);
3867 else if (item[0] ==
'Distance' && targetUnits.length > 0) {
3869 var inputCol = document.createElement(
"div");
3870 inputCol.className =
"col text-left";
3871 var select = this.createUnitsInput(targetUnits,
'distance', item[1]);
3872 select.onchange =
function (e) {
3875 inputCol.appendChild(select);
3876 elem.appendChild(inputCol);
3889 propertypanelcontent.appendChild(elem);
3894 var _category = node.nodedef_node;
3895 var _type = node.nodedef_type;
3897 var isNodeGraph = node.type ==
'graph/subgraph';
3900 _category =
'nodegraph';
3902 if (node.outputs.length > 1) {
3905 else if (node.outputs.length > 0) {
3906 _type = node.outputs[0].type;
3914 if (_category ==
'surfacematerial') {
3920 var elem = document.createElement(
"div");
3921 elem.className =
"row px-1 py-1";
3924 var label = document.createElement(
"div");
3925 label.className =
"col-4 px-1 py-0 col-form-label-sm text-end";
3926 label.style = this.fontSizeStyle;
3927 label.innerHTML =
"<b>" + _category;
3928 if (_type.length > 0) {
3929 label.innerHTML +=
'<br>' + _type;
3931 label.innerHTML +=
"</b>";
3932 elem.appendChild(label);
3935 var inputCol = document.createElement(
"div");
3936 inputCol.className =
"col py-0";
3937 var nameInput = document.createElement(
"input");
3938 nameInput.style = this.fontSizeStyle;
3939 nameInput.type =
"text";
3940 nameInput.value = node.title;
3941 nameInput.className =
"form-control form-control-sm";
3943 nameInput.onchange =
function (e) {
3944 var oldTitle = node.title;
3946 if (newTitle != oldTitle)
3948 that.monitor.onNodeRenamed(node, newTitle);
3949 node.title = newTitle;
3951 e.target.value = node.title;
3953 if (node.graph._is_subgraph) {
3954 if (node.nodedef_node ==
'input') {
3956 node.graph.renameInput(oldTitle, node.title);
3958 else if (node.nodedef_node ==
'output') {
3960 node.graph.renameOutput(oldTitle, node.title);
3965 node.setSize(node.computeSize());
3966 node.setDirtyCanvas(
true,
true);
3968 inputCol.appendChild(nameInput);
3971 if (node.nodedef_node !=
'input' && node.nodedef_node !=
'output'
3972 && node.type !=
'graph/subgraph') {
3973 var imagePreview = document.createElement(
"img");
3974 imagePreview.src =
"./Icons/no_image.png";
3975 var previewSet =
false;
3977 imagePreview.style.display =
"none";
3978 imagePreview.src =
"./Icons/no_image.png";
3991 imagePreview.id =
"propertypanel_preview";
3992 imagePreview.className =
"img-fluid form-control form-control-sm";
3993 inputCol.appendChild(imagePreview);
3996 elem.appendChild(label);
3997 elem.appendChild(inputCol);
4002 var filterCol = document.createElement(
"div");
4003 filterCol.className =
"col-2 py-0";
4004 filterCol.width = 16;
4005 var filterIcon = document.createElement(
"button");
4008 if (node.showDefaultValueInputs ==
null)
4010 node.showDefaultValueInputs =
true;
4012 var img = document.createElement(
"img");
4013 if (node.showDefaultValueInputs)
4015 img.src =
"./Icons/funnel_white.svg";
4016 filterIcon.className =
"btn btn-sm btn-outline-secondary";
4020 img.src =
"./Icons/funnel-fill_white.svg";
4021 filterIcon.className =
"btn btn-sm btn-outline-warning";
4023 filterIcon.appendChild(img);
4024 filterIcon.onclick =
function (e) {
4025 node.showDefaultValueInputs = !node.showDefaultValueInputs;
4028 filterCol.appendChild(filterIcon);
4029 elem.appendChild(filterCol);
4032 propertypanelcontent.appendChild(elem);
4034 let hr = document.createElement(
"hr");
4035 hr.classList.add(
"my-1");
4036 propertypanelcontent.appendChild(hr);
4038 let current_details =
null;
4039 let first_details =
true;
4040 let nodeInputs = node.inputs;
4043 let targetNodes = [];
4044 for (var i in nodeInputs) {
4045 let nodeInput = nodeInputs[i];
4047 let inputName = nodeInput.name;
4048 let nodeInputLink = nodeInput.link;
4049 let uiName = inputName;
4051 uiName = uiName.replace(/_/g,
' ');
4054 let colorspace =
'';
4056 let defaultgeomprop =
'';
4060 let property_info = node.getPropertyInfo(inputName);
4061 let ng_property_info =
null;
4065 let sg = node.subgraph;
4068 let sg_nodes = sg._nodes;
4069 for (var sg_node of sg_nodes)
4071 if (sg_node.title == inputName)
4074 ng_property_info = sg_node.getPropertyInfo(
"in");
4075 if (ng_property_info)
4086 var skipInterorConnectedInput =
false;
4087 if (node.graph._is_subgraph) {
4090 var sg_node = node.graph._subgraph_node;
4093 var slot = sg_node.findInputSlot(node.title);
4095 if (sg_node.inputs) {
4097 var slotInput = sg_node.inputs[slot];
4099 if (slotInput !=
null && slotInput.link !=
null) {
4100 skipInterorConnectedInput =
true;
4110 if (skipInterorConnectedInput) {
4111 console.log(
'Skip interior connected input: ', nodeInput);
4116 if (ng_property_info) {
4118 property_info = ng_property_info;
4120 if (property_info) {
4122 if (property_info.defaultgeomprop)
4124 defaultgeomprop = property_info.defaultgeomprop;
4126 if (property_info.colorspace) {
4127 colorspace = property_info.colorspace;
4129 if (property_info.unit) {
4130 units = property_info.unit;
4132 if (property_info.uiname) {
4133 uiName = property_info.uiname;
4135 if (property_info.uimin) {
4136 uimin = property_info.uimin;
4138 if (property_info.uimax) {
4139 uimax = property_info.uimax;
4141 if (property_info.uifolder && property_info.uifolder.length > 0) {
4143 if (current_details ==
null || current_details.id != property_info.uifolder) {
4145 current_details = document.createElement(
"details");
4146 current_details.id = property_info.uifolder;
4147 current_details.open = first_details;
4148 current_details.classList.add(
'w-100',
'p-1',
'border',
'border-secondary',
'rounded',
'my-1');
4149 first_details =
false;
4150 var summary = document.createElement(
'summary')
4151 summary.style = this.fontSizeStyle;
4152 summary.innerHTML =
"<b>" + property_info.uifolder +
"</b>"
4154 current_details.appendChild(summary);
4162 current_details =
null;
4167 current_details =
null;
4173 if (nodeInputLink) {
4174 let upstreamLink =
null;
4176 let nodegraph = node.graph;
4177 let link = nodegraph.links[nodeInputLink];
4179 let linkId = link && link.origin_id;
4180 let linkNode = linkId && nodegraph.getNodeById(linkId);
4185 let linkSlot = link.origin_slot;
4187 let linkOutput = linkNode.outputs[linkSlot];
4189 upstreamLink = linkNode.title +
'.' + linkOutput.name;
4192 let
id =
"__pp:" + inputName;
4193 let buttonText = upstreamLink;
4195 if (buttonText.length > TRUNC_TEXT) {
4196 buttonText = buttonText.substring(0, TRUNC_TEXT) +
"...";
4198 let input = this.createButtonWithImageAndText(
"./Icons/arrow_up_white.svg", buttonText,
id);
4200 input.onclick =
function (e) {
4202 var inputName = e.target.id;
4203 inputName = inputName.replace(
'__pp:',
'');
4204 inputName = inputName.replace(
'_text',
'');
4205 inputName = inputName.replace(
'_img',
'');
4206 console.log(
'Clicked traversal button:', inputName);
4208 console.log(
'Jump to node:', linkNode.title);
4212 node.setDirtyCanvas(
true,
true);
4216 elem = document.createElement(
"div");
4217 elem.className =
"row px-1 py-0";
4219 input.id =
"__pp:" + inputName;
4221 var label = document.createElement(
"div");
4223 label.className =
"col-4 px-1 py-0 col-form-label-sm text-end";
4224 label.style = this.fontSizeStyle;
4225 label.innerHTML = uiName;
4226 label.for = input.id;
4227 elem.appendChild(label);
4230 if (useFormControl) {
4231 input.classList.add(
"form-control");
4233 input.classList.add(
"form-control-sm");
4236 input.disabled =
true;
4238 var propvalue = document.createElement(
"div");
4239 propvalue.className =
"col p-1";
4240 propvalue.appendChild(input);
4242 elem.appendChild(propvalue);
4248 targetNodes[i] = node;
4249 let targetNode = targetNodes[i];
4250 let propertyKey = inputName;
4252 var
property = targetNode.properties[inputName];
4253 if (property ==
null) {
4255 var subgraph = targetNode.subgraph;
4258 var subNode = subgraph.findNodeByTitle(inputName);
4260 targetNodes[i] = subNode;
4262 property = targetNodes[i].properties[
'in'];
4267 if (property ==
null) {
4268 console.log(
'Update: Cannot find property value for input:', inputName);
4274 if (defaultgeomprop)
4281 if (!node.showDefaultValueInputs && !isNodeGraph)
4283 let isDefault = node.isDefaultValue(inputName);
4291 elem = document.createElement(
"div");
4292 elem.className =
"row px-1 py-0";
4295 var input_btn =
null;
4296 let input_slider =
null;
4297 var colorspace_unit_btn =
null;
4298 var useFormControl =
true;
4301 if (colorspace.length > 0) {
4305 colorspace_unit_btn = this.createColorSpaceInput(colorSpaces, colorspace);
4306 let theNode = targetNodes[i];
4307 colorspace_unit_btn.onchange =
function (e) {
4309 theNode.setPropertyInfo(inputName,
'colorspace', e.target.value);
4312 else if (units.length > 0 && property_info.unittype) {
4314 colorspace_unit_btn = this.createUnitsInput(targetUnits, property_info.unittype, units);
4315 let theNode = targetNodes[i];
4316 colorspace_unit_btn.onchange =
function (e) {
4317 theNode.setPropertyInfo(inputName,
'unit', e.target.value);
4321 let proptype = nodeInput.type;
4322 if (proptype ==
'float' || proptype ==
'integer') {
4323 var isFloat = proptype ==
'float';
4325 input = document.createElement(
"input");
4326 input.id = propertyKey +
'_box';
4327 input.style = this.fontSizeStyle;
4328 input.type =
'number';
4329 input.classList.add(
"form-control",
"form-control-sm",
"ps-0");
4330 input.setAttribute(
'propertyKey', propertyKey);
4332 input_slider = document.createElement(
"input");
4333 input_slider.id = propertyKey +
'_slider';
4335 input_slider.type =
'range';
4336 input_slider.classList.add(
'form-range',
'custom-slider',
'pe-0');
4337 input_slider.setAttribute(
'propertyKey', propertyKey);
4343 input.min = Math.min(property, 0);
4351 input.max = Math.max(property*3, 10.0);
4354 input.max = Math.max(property*3, 100);
4359 input_slider.min = input.min;
4360 input_slider.max = input.max;
4362 input.step = (input.max - input.min) / 100.0;
4363 input_slider.step = input.step;
4366 input_slider.step = 1;
4370 input.value = input_slider.value = property;
4380 let theSlider = input_slider;
4381 let theNode = targetNodes[i];
4382 input_slider.onchange =
function (e) {
4383 var pi = e.target.getAttribute(
'propertyKey');
4384 var val = parseFloat(e.target.value);
4385 theNode.setProperty(pi, val);
4388 input_slider.oninput =
function(e) {
4389 var pi = e.target.getAttribute(
'propertyKey');
4390 var val = parseFloat(e.target.value);
4391 theNode.setProperty(pi, val);
4392 theBox.value = e.target.value;
4395 input.onchange =
function (e) {
4396 var pi = e.target.getAttribute(
'propertyKey');
4397 var val = parseFloat(e.target.value);
4398 theNode.setProperty(pi, val);
4401 input.oninput =
function(e) {
4402 var pi = e.target.getAttribute(
'propertyKey');
4403 var val = parseFloat(e.target.value);
4404 theNode.setProperty(pi, val);
4405 theSlider.value = e.target.value;
4408 else if (proptype ==
'string' || proptype ==
'filename') {
4409 input = document.createElement(
"input");
4410 input.style = this.fontSizeStyle;
4411 input.type =
"text";
4412 if (proptype ==
'filename') {
4413 var curImage = property;
4414 this.updateImagePreview(curImage);
4416 input_btn = document.createElement(
"button");
4417 input_btn.classList.add(
"btn",
"btn-sm",
"btn-outline-secondary");
4418 input_btn.innerHTML =
"+";
4419 input_btn.setAttribute(
'propertyKey', propertyKey);
4420 var fileId =
"__pp:" + inputName;
4421 let theNode = targetNodes[i];
4422 input_btn.onclick =
function (e) {
4423 var pi = e.target.getAttribute(
'propertyKey');
4432 if (property_info && property_info.enum) {
4437 input = document.createElement(
"select");
4438 input.style = this.fontSizeStyle;
4439 input.classList.add(
"form-control",
"form-control-sm");
4441 input.setAttribute(
'propertyKey', propertyKey);
4442 let theNode = targetNodes[i];
4443 let enums = property_info.enum;
4444 for (let j = 0; j < enums.length; j++) {
4445 let option = document.createElement(
"option");
4446 option.value = enums[j];
4447 option.text = enums[j];
4450 input.value = property;
4451 input.setAttribute(
'propertyKey', propertyKey);
4452 input.onchange =
function (e) {
4453 var pi = e.target.getAttribute(
'propertyKey');
4454 theNode.setProperty(pi, e.target.value);
4460 if (property_info && !property_info.enm) {
4461 input.value = property;
4462 input.setAttribute(
'propertyKey', propertyKey);
4463 let theNode = targetNodes[i];
4464 let isFilename = proptype ==
'filename';
4466 input.onchange =
function (e) {
4467 var pi = e.target.getAttribute(
'propertyKey');
4469 theNode.setProperty(pi, e.target.value);
4472 that.updateImagePreview(e.target.value);
4480 else if (proptype ==
'boolean') {
4482 input = document.createElement(
"input");
4483 input.style = this.fontSizeStyle;
4484 input.type =
"checkbox";
4485 input.classList =
"form-check-input";
4486 useFormControl =
false;
4487 input.checked = property;
4488 input.setAttribute(
'propertyKey', propertyKey);
4489 let theNode = targetNodes[i];
4490 input.onchange =
function (e) {
4491 var pi = e.target.getAttribute(
'propertyKey');
4493 theNode.setProperty(pi, e.target.checked);
4498 else if (proptype ==
'vector2' || proptype ==
'vector3' || proptype ==
'vector4')
4501 var vector_size = [
'vector2',
'vector3',
'vector4'].indexOf(proptype) + 2;
4502 input = document.createElement(
"div");
4503 useFormControl =
false;
4505 input.className =
"row py-1 ps-4 pe-0";
4507 for (let v=0; v<vector_size; v++)
4510 let subinput = document.createElement(
"input");
4511 subinput.style = this.fontSizeStyle;
4512 subinput.type =
'number';
4513 subinput.classList.add(
"form-control");
4514 subinput.classList.add(
"form-control-sm");
4515 subinput.setAttribute(
'propertyKey', propertyKey);
4517 let subinput_slider = document.createElement(
"input");
4518 subinput_slider.id = propertyKey +
'_slider';
4519 subinput_slider.type =
'range';
4520 subinput_slider.classList.add(
'form-range',
'custom-slider',
'pe-0');
4521 subinput_slider.setAttribute(
'propertyKey', propertyKey);
4524 subinput.min = uimin[v];
4527 subinput.min = Math.min(property[v]*3, 0);
4530 subinput.max = uimax[v];
4533 subinput.max = Math.max(property[v]*3, 10.0);
4536 subinput_slider.min = subinput.min;
4537 subinput_slider.max = subinput.max;
4538 subinput.step = (subinput.max - subinput.min) / 100.0;
4539 subinput_slider.step = subinput.step;
4541 subinput.value = subinput_slider.value =
property[v];
4543 let theNode = targetNodes[i];
4544 let vector_index = v;
4545 let theBox = subinput;
4546 let theSlider = subinput_slider;
4547 theBox.onchange =
function (e) {
4548 let pi = e.target.getAttribute(
'propertyKey');
4549 let value = parseFloat(e.target.value);
4550 let newValue = theNode.properties[pi].map(item => item);
4551 newValue[vector_index] = value;
4552 theNode.setProperty(pi, newValue);
4556 theBox.oninput =
function(e) {
4557 let pi = e.target.getAttribute(
'propertyKey');
4558 let value = parseFloat(e.target.value);
4559 let newValue = theNode.properties[pi].map(item => item);
4560 newValue[vector_index] = value;
4561 theNode.setProperty(pi, newValue);
4562 theSlider.value = e.target.value;
4565 theSlider.onchange =
function (e) {
4566 let pi = e.target.getAttribute(
'propertyKey');
4567 let value = parseFloat(e.target.value);
4568 let newValue = theNode.properties[pi].map(item => item);
4569 newValue[vector_index] = value;
4570 theNode.setProperty(pi, newValue);
4573 theSlider.oninput =
function(e) {
4574 let pi = e.target.getAttribute(
'propertyKey');
4575 let value = parseFloat(e.target.value);
4576 let newValue = theNode.properties[pi].map(item => item);
4577 newValue[vector_index] = value;
4578 theNode.setProperty(pi, newValue);
4579 theBox.value = e.target.value;
4583 let propvalue_slider = document.createElement(
"div");
4584 propvalue_slider.className =
"col p-0";
4585 propvalue_slider.appendChild(subinput_slider);
4587 let propvalue_box = document.createElement(
"div");
4588 propvalue_box.className =
"col p-0";
4589 propvalue_box.appendChild(subinput);
4591 let input_row = document.createElement(
"div");
4592 input_row.className =
"row p-0";
4593 input_row.appendChild(propvalue_slider);
4594 input_row.appendChild(propvalue_box);
4596 input.appendChild(input_row);
4599 else if (proptype ==
'color3' || proptype ==
'color4') {
4600 input = document.createElement(
"input");
4601 input.type =
"color";
4603 if (property.length == 4) {
4604 input.value = this.rgbToHex([ property[0], property[1], property[2] ]);
4607 input.value = this.rgbToHex(property);
4609 input.setAttribute(
'propertyKey', propertyKey);
4610 let theNode = targetNodes[i];
4611 input.onchange =
function (e) {
4613 var hex = e.target.value;
4616 rgb[0] = parseInt(hex.substring(1, 3), 16) / 255.0;
4617 rgb[0] = parseFloat(rgb[0].toFixed(fprecision));
4618 rgb[1] = parseInt(hex.substring(3, 5), 16) / 255.0;
4619 rgb[1] = parseFloat(rgb[1].toFixed(fprecision));
4620 rgb[2] = parseInt(hex.substring(5, 7), 16) / 255.0;
4621 rgb[2] = parseFloat(rgb[2].toFixed(fprecision));
4622 if (proptype ==
'color4')
4625 var pi = e.target.getAttribute(
'propertyKey');
4626 theNode.setProperty(pi, rgb);
4628 let func =
function (e) {
4630 var hex = e.target.value;
4631 let rgb = [0, 0, 0];
4633 rgb[0] = parseInt(hex.substring(1, 3), 16) / 255.0;
4634 rgb[0] = parseFloat(rgb[0].toFixed(fprecision));
4635 rgb[1] = parseInt(hex.substring(3, 5), 16) / 255.0;
4636 rgb[1] = parseFloat(rgb[1].toFixed(fprecision));
4637 rgb[2] = parseInt(hex.substring(5, 7), 16) / 255.0;
4638 rgb[2] = parseFloat(rgb[2].toFixed(fprecision));
4639 if (proptype ==
'color4')
4642 var pi = e.target.getAttribute(
'propertyKey');
4643 theNode.setProperty(pi, rgb);
4645 input.onchange = func;
4646 input.oninput = func;
4649 input = document.createElement(
"input");
4650 input.style = this.fontSizeStyle;
4651 input.type =
"text";
4652 input.value = property;
4653 let propertyKey = inputName;
4654 let theNode = targetNodes[i];
4655 input.onchange =
function (e) {
4656 theNode.setProperty(propertyKey, e.target.value);
4662 input.id =
"__pp:" + inputName;
4665 var label = document.createElement(
"div");
4666 label.className =
"col-4 p-0 col-form-label-sm text-end";
4667 label.style = this.fontSizeStyle;
4668 label.innerHTML = uiName;
4669 label.for = input.id;
4670 elem.appendChild(label);
4673 if (useFormControl) {
4674 input.classList.add(
"form-control");
4676 input.classList.add(
"form-control-sm");
4679 input.disabled =
true;
4681 var propvalue = document.createElement(
"div");
4682 propvalue.className =
"col py-0";
4685 propvalue.classList.add(
'ps-1');
4687 propvalue.appendChild(input);
4690 var propbutton = document.createElement(
"div");
4691 propbutton.className =
"col-2 py-0";
4693 propbutton.appendChild(input_btn);
4694 elem.appendChild(propbutton);
4696 if (colorspace_unit_btn) {
4698 var propbutton = document.createElement(
"div");
4699 propbutton.className =
"col col-form-label-sm";
4700 var details = document.createElement(
"details");
4701 var summary = document.createElement(
'summary')
4702 summary.style = this.fontSizeStyle;
4703 if (colorspace.length > 0)
4704 summary.innerHTML =
"Colorspace";
4705 else if (targetUnits.length > 0)
4706 summary.innerHTML =
"Units";
4707 details.appendChild(summary);
4708 details.appendChild(colorspace_unit_btn);
4709 propbutton.appendChild(details);
4710 propvalue.appendChild(propbutton);
4715 var propvalue_slider = document.createElement(
"div");
4716 propvalue_slider.className =
"col py-0 pe-0";
4717 propvalue_slider.appendChild(input_slider);
4718 elem.appendChild(propvalue_slider);
4721 elem.appendChild(propvalue);
4726 if (current_details) {
4728 current_details.appendChild(elem);
4730 if (current_details.parentElement ==
null) {
4731 propertypanelcontent.appendChild(current_details);
4735 propertypanelcontent.appendChild(elem);
4743 let output_details =
null;
4744 let nodeOutputs =
null;
4748 if (!inUnselectedNodeGraph)
4749 nodeOutputs = node.outputs;
4750 for (let k in nodeOutputs) {
4751 let nodeOutput = nodeOutputs[k];
4753 let nodeOutputLinks = nodeOutput.links;
4754 for (let j in nodeOutputLinks) {
4755 let link_id = nodeOutputLinks[j];
4762 if (!output_details) {
4763 output_details = document.createElement(
"details");
4764 output_details.id =
"pp::outputs";
4765 output_details.open =
true;
4766 output_details.classList.add(
'w-100',
'p-1',
'border',
'border-secondary',
'rounded',
'my-1');
4767 first_details =
false;
4768 var summary = document.createElement(
'summary');
4770 summary.style = this.fontSizeStyle;
4771 summary.innerHTML =
"<b>Outputs</b>"
4774 output_details.appendChild(summary);
4779 const targetNode =
graphcanvas.graph.getNodeById(link.target_id);
4783 let targetSlot = link.target_slot;
4784 let targetInput = targetNode.inputs[targetSlot];
4787 let downstreamLink = targetNode.title +
'.' + targetInput.name;
4789 let
id =
"__pp:" + nodeOutput.name;
4790 let buttonText = downstreamLink;
4792 if (buttonText.length > TRUNC_TEXT) {
4793 buttonText = buttonText.substring(0, TRUNC_TEXT) +
"...";
4795 let output = this.createButtonWithImageAndText(
"./Icons/arrow_down_white.svg", buttonText,
id);
4797 output.onclick =
function (e) {
4799 var inputName = e.target.id;
4800 inputName = inputName.replace(
'__pp:',
'');
4801 inputName = inputName.replace(
'_text',
'');
4802 inputName = inputName.replace(
'_img',
'');
4803 console.log(
'Clicked traversal button:', inputName);
4805 console.log(
'Jump to node:', targetNode.title);
4809 node.setDirtyCanvas(
true,
true);
4813 elem = document.createElement(
"div");
4814 elem.className =
"row px-1 py-0";
4816 let outputName = nodeOutput.name;
4817 output.id =
"__pp:" + outputName;
4819 var label = document.createElement(
"div");
4821 label.className =
"col-4 px-1 py-0 col-form-label-sm text-end";
4822 label.style = this.fontSizeStyle;
4823 label.innerHTML = outputName;
4824 label.for = nodeOutput.id;
4825 elem.appendChild(label);
4828 if (useFormControl) {
4829 output.classList.add(
"form-control");
4831 output.classList.add(
"form-control-sm");
4834 output.disabled =
true;
4836 var propvalue = document.createElement(
"div");
4837 propvalue.className =
"col p-1";
4838 propvalue.appendChild(output);
4840 elem.appendChild(propvalue);
4842 output_details.appendChild(elem);
4845 console.log(`- Node with ID ${link.target_id} not found.`);
4846 console.log(
'--- Available nodes:',
graphcanvas.graph._nodes_by_id);
4849 console.log(`- Link with ID ${link_id} not found.`);
4854 if (output_details) {
4855 propertypanelcontent.appendChild(output_details);
4867 initializeLiteGraph(canvas, readOnly =
false) {
4869 graph =
new LiteGraph.LGraph();
4884 graphcanvas.default_connection_color_byTypeOff = {
4923 let parentGraph =
'';
4926 parentGraph =
graphcanvas.graph._subgraph_node.title;
4936 let parentGraph =
'';
4939 parentGraph =
graphcanvas.graph._subgraph_node.title;
4947 LGraphNode.prototype.setPropertyInfo =
function(property, propertyInfo, value)
4951 if (this.properties_info) {
4952 for (var i = 0; i < this.properties_info.length; ++i) {
4953 if (this.properties_info[i].name == property) {
4954 info = this.properties_info[i];
4960 if (info && info[propertyInfo])
4962 if (this.onPropertyInfoChanged)
4964 this.onPropertyInfoChanged(property, propertyInfo, value, info[propertyInfo]);
4966 info[propertyInfo] = value;
4970 console.warning(
'Failed to set property: ', property,
'. info: ', propertyInfo,
'. Value: ', value,
'. Infos: ', this.properties_info);
4975 LGraphNode.prototype.isDefaultValue =
function(property)
4980 if (this.properties[property] ==
null)
4982 console.warn(
'> Property value does not exist:', property);
4986 if (this.getInputLink(property))
4991 if (this.properties_info !=
null)
4993 for (let i = 0; i < this.properties_info.length; ++i) {
4994 if (this.properties_info[i].name == property) {
4995 info = this.properties_info[i];
5001 if (info !=
null && info.default_value !=
null)
5003 let property_string = this.properties[property];
5004 let default_value_string = info.default_value;
5005 let isDefault =
false;
5006 if (Array.isArray(default_value_string)) {
5007 default_value_string = default_value_string.map(String);
5008 property_string = property_string.map(String);
5009 isDefault = (JSON.stringify(default_value_string) == JSON.stringify(property_string));
5013 isDefault = (default_value_string == property_string);
5019 console.warn(
'> Default value does not exist for:', property);
5028 this.monitor.monitorGraph(
graph,
true);
5036 console.log(
'> Read only mode: ', readOnly);
5052 graph.ctrl_shift_v_paste_connect_unselected_outputs =
true;
5058 canvas.addEventListener(
"keydown",
function (e) {
5059 if (e.key ===
"f") {
5065 canvas.addEventListener(
"keydown",
function (e) {
5066 if (e.key ===
"l") {
5073 var context = canvas.getContext(
'2d');
5075 function drawstart(event) {
5078 console.log(
'>>>>>>>>>>> draw start');
5082 function drawmove(event) {
5086 console.log(
'>>>>>>>>>>> draw move');
5089 function drawend(event) {
5092 console.log(
'>>>>>>>>>>> draw move');
5096 function touchstart(event) {
5097 drawstart(event.touches[0]);
5100 function touchmove(event) {
5101 drawmove(event.touches[0]);
5105 function touchend(event) {
5106 drawend(event.changedTouches[0]);
5124 var haveSelected =
false;
5125 for (var s in selected) {
5126 haveSelected =
true;
5129 console.log(
'Center nodes:', selected,
'. Have selected:', haveSelected);
5137 LiteGraph.searchbox_extras = [];
5138 var nodeTypes = LiteGraph.registered_node_types;
5139 for (var typeName in nodeTypes) {
5140 if (typeName !==
"graph/subgraph") {
5141 console.log(
'Removing node type:', LiteGraph.getNodeType(typeName));
5142 LiteGraph.unregisterNodeType(typeName);
5150 collapseNode(node, collapse) {
5151 if (node.constructor.collapsable ===
false) {
5154 if (node.flags.collapsed != collapse) {
5155 node.flags.collapsed = collapse;
5164 collapseExpandNodes(collapse) {
5169 var modified =
false;
5170 if (selected_nodes) {
5171 for (var i in selected_nodes) {
5172 var node = selected_nodes[i];
5174 if (this.collapseNode(node, collapse))
5179 var nodes = curGraph._nodes;
5180 for (var i in nodes) {
5181 var node = nodes[i];
5182 if (this.collapseNode(node, collapse))
5189 graph.setDirtyCanvas(
true,
true);
5203 pasteFromClipboard() {
5210 extractNodeGraph() {
5212 if (selected.length == 0) {
5213 console.log(
'No nodes selected.');
5217 var subgraphsSelected = []
5218 for (var i in selected) {
5219 var node = selected[i];
5220 if (node.type ==
'graph/subgraph') {
5221 subgraphsSelected.push(node);
5224 if (subgraphsSelected.length == 0) {
5225 console.log(
'No subgraphs selected.');
5230 var subGraph = subgraphsSelected[0];
5231 var subGraphNodes = subGraph.subgraph._nodes;
5232 for (var i in subGraphNodes) {
5233 var node = subGraphNodes[i];
5254 this.debugOutput(
'Cannot create nest subgraphs.', 1);
5260 if (selected.length == 0) {
5261 console.log(
'No nodes selected.');
5269 var node = LiteGraph.createNode(
'graph/subgraph');
5277 node.subgraph.arrange(80);
5285 displayNodeTypes() {
5287 var nodeTypesListUpdater = this.ui.nodeTypesListUpdater;
5288 if (!nodeTypesListUpdater) {
5293 var nodeTypes = LiteGraph.registered_node_types;
5294 nodeTypesListUpdater(nodeTypes);
5306 initialize(canvas, ui, monitor, materialFilename, readOnly =
false) {
5310 console.log(
'Set custom monitor:', monitor.getName());
5312 this.monitor = monitor;
5313 this.initializeLiteGraph(canvas, readOnly);
5315 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.
getRenderer()
Get the renderer for the monitor.
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 ...
loadFromString(extension, fileContents, fileName, auto_arrange, rerender=false)
Load graph editor from a string.
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.
loadFromZip(extension, file, fileName, editor, auto_arrange, rerender=false)
loadDefinitionsFromFile()
Load MaterialX document containing node definitions from a file.
isArray(_type)
Determines if the specified type is an array type.
initialize(editor, materialFilename)
Initialize the MaterialX handler for the given editor.
createLiteGraphDefinitions(doc, debug, addInputOutputs, definitionsList, libraryPrefix='mtlx', editor, icon='')
Creates LiteGraph node definitions based on the MaterialX document.
buildMetaData(colorSpace, unit, unitType, uiname, uimin, uimax, uifolder, _type)
Builds and returns metadata for a node based on the provided parameters.
loadMaterialXLibraries(stdlib)
Load MaterialX definition libraries.
writeGraphToDocument(mltxgraph, graph, graphWriteOptions)
Writes the graph to the specified MaterialX document.
buildGraphFromDoc(doc, editor, auto_arrange)
Builds the LiteGraph graph from the specified MaterialX document.
saveGraphToFile(extension, graph, graphWriteOptions)
Saves the graph to a file with the specified extension.
loadMaterialX()
Load in the MaterialX library.
saveGraphToString(extension, graph, graphWriteOptions)
Saves the graph to a string in the specified format.
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.