137export
function initializeNodeEditor(materialFilename, geometryId, customRenderer, user_icon_map =
null, sampleFiles =
null,
140 "_default_":
"./Icons/materialx_logo.webp",
141 "_default_graph_":
"./Icons/nodegraph_white.svg"
144 let geometryValues = [
'teapot',
'shaderball',
'sphere',
'plane',
'cube',
'cylinder',
'donut',
'twist',
'_loadFromFile_']
148 for (var key in user_icon_map) {
149 my_icon_map[key] = user_icon_map[key];
154 function uriExists(uri) {
158 return Promise.resolve(
true);
160 return Promise.resolve(
false);
164 console.log(
'Error checking URI:', error);
165 return Promise.resolve(
false);
170 function renderableItemUpdater(renderableItems) {
171 let renderableItemSelect = document.getElementById(
'renderableItem');
172 if (renderableItemSelect) {
174 const TRUNCATION_LENGTH = 12;
176 while (renderableItemSelect.firstChild) {
177 renderableItemSelect.removeChild(renderableItemSelect.firstChild);
179 for (let i = 0; i < renderableItems.length; i++) {
180 let item = renderableItems[i];
181 let option = document.createElement(
'option');
185 if (uiItem.length > 20)
186 uiItem = uiItem.substring(0, 20) +
'...';
187 option.text = uiItem;
188 renderableItemSelect.appendChild(option);
195 function consoleLog(text, severity, clear =
null) {
196 if (severity === 2) {
197 text =
'> Error: ' + text
199 else if (severity === 1) {
200 text =
'> Warning: ' + text
207 let console_area = document.getElementById(
'console_area');
210 console_area.value = text +
'\n';
213 console_area.value = console_area.value + text +
'\n';
216 console_area.scrollTop = console_area.scrollHeight;
245 function createMenuItem(text, filename) {
246 let menuItem = document.createElement(
'li');
247 menuItem.className =
'dropdown-item';
248 menuItem.innerText = text;
249 menuItem.onclick =
function () {
250 console.log(
'Load library file:', filename);
253 let dropdownMenu = document.getElementById(
'libraryDropdown');
254 dropdownMenu.classList.remove(
'show');
265 function createSubMenu(title, auto_close =
false) {
267 let li = document.createElement(
'li');
268 li.className =
"dropend dropdown";
271 let subMenu = document.createElement(
'a');
272 subMenu.className =
"dropdown-item dropdown-toggle";
273 subMenu.setAttribute(
'data-bs-toggle',
"dropdown");
275 subMenu.setAttribute(
'data-bs-auto-close',
'outside');
277 subMenu.setAttribute(
'aria-expanded',
'false');
278 subMenu.setAttribute(
'aria-haspopup',
'true');
279 subMenu.innerHTML = title;
280 li.appendChild(subMenu);
291 function createLibraryMenu(sampleFiles, libraryDropdown) {
292 for (let key in sampleFiles) {
294 let li = createSubMenu(key,
true);
295 libraryDropdown.appendChild(li);
297 let value = sampleFiles[key];
300 if (typeof value ===
'string') {
301 let value = sampleFiles[key];
302 li.appendChild(createMenuItem(key, value));
304 else if (typeof value ===
'object') {
306 let subMenuList = document.createElement(
'ul');
307 subMenuList.className =
'dropdown-menu';
308 subMenuList.id = key;
313 if (typeof value[key] ===
'string') {
314 subMenuList.appendChild(createMenuItem(key, value[key]));
317 else if (typeof value[key] ===
'object') {
320 let sli = createSubMenu(key,
false);
321 subMenuList.appendChild(sli);
323 let ssubMenuList = document.createElement(
'ul');
324 ssubMenuList.className =
'dropdown-menu';
325 for (let skey in value[key]) {
326 ssubMenuList.appendChild(createMenuItem(skey, (value[key])[skey]));
328 sli.appendChild(ssubMenuList);
332 li.appendChild(subMenuList);
339 if (sampleFiles && libraryDropdown) {
340 createLibraryMenu(sampleFiles, libraryDropdown);
344 let selectGeometryUI =
false;
345 if (customRenderer) {
347 let geometryURL = geometryId;
348 if (geometryId.length > 0 && geometryValues.includes(geometryId)) {
349 geometryURL =
'Geometry/' + geometryId +
'.glb';
350 selectGeometryUI =
true;
352 var viewer = customRenderer.initialize(materialFilename, geometryURL, readOnly);
353 console.log(
'Setup renderer:', viewer);
356 let preview_panel = document.getElementById(
"preview_panel");
359 preview_panel.style.display =
'none';
368 function displayNodeTypes(nodeTypes) {
370 var nodeList = document.getElementById(
'nodeTypesList');
376 while (nodeList.firstChild) {
377 nodeList.removeChild(nodeList.firstChild);
381 for (var typeName in nodeTypes) {
383 var rowItem = document.createElement(
"tr");
385 var cellItem = document.createElement(
"td");
386 cellItem.textContent = typeName;
387 rowItem.appendChild(cellItem);
389 cellItem = document.createElement(
"td");
390 var nodeDefString =
'<None>';
391 var nodeDefName = nodeTypes[typeName].nodedef_name;
392 var nodeDefNode = nodeTypes[typeName].nodedef_node
393 var nodeDefHref = nodeTypes[typeName].nodedef_href;
396 var link = document.createElement(
"a");
397 link.target =
"_blank";
398 link.href = nodeDefHref;
399 link.textContent = nodeDefNode +
" ( " + nodeDefName +
" )";
400 cellItem.appendChild(link);
403 cellItem.textContent = nodeDefName;
407 cellItem.textContent = nodeDefString;
409 rowItem.appendChild(cellItem);
411 nodeList.appendChild(rowItem);
416 var cmeditor = setupXMLSyntax();
417 var cmeditor2 = setupJavascriptSyntax();
418 var cmeditor3 = setupGLTFSyntax();
425 function docDisplayUpdater(contents) {
427 cmeditor.setValue(contents);
435 function gltfDisplayUpdater(contents) {
436 if (!contents || contents.length == 0) {
440 cmeditor3.setValue(contents);
442 console.log(contents);
450 function jsDefinitionsDisplayUpdater(contents) {
452 cmeditor2.setValue(contents);
456 var canvas = document.getElementById(
'mygraphcanvas');
458 consoleLogger: consoleLog,
459 nodeTypesListUpdater: displayNodeTypes,
460 renderableItemUpdater: renderableItemUpdater,
461 documentDisplayUpdater: docDisplayUpdater,
462 gltfDocumentDisplayUpdater: gltfDisplayUpdater,
463 definitionsDisplayUpdater: jsDefinitionsDisplayUpdater,
464 propertypanel_content: document.getElementById(
'propertypanel_content'),
465 propertypanel_icon: document.getElementById(
'propertypanel_icon'),
466 icon_map: my_icon_map,
471 monitor.setRenderer(customRenderer);
472 editor.initialize(canvas, ui, monitor, materialFilename, readOnly);
478 function addUIHandlers() {
480 var saveCanvasButton = document.getElementById(
'captureGraph');
481 if (saveCanvasButton) {
482 saveCanvasButton.addEventListener(
'click',
function () {
483 var canvas = document.getElementById(
'mygraphcanvas');
484 var dataURL = canvas.toDataURL(
'image/png');
485 var link = document.createElement(
'a');
487 link.download =
'graph_capture.png';
493 var auto_arrange_size = 80;
496 var loadMaterialXDocumentFromFile = document.getElementById(
'loadMaterialXDocumentFromFile');
497 if (loadMaterialXDocumentFromFile) {
498 loadMaterialXDocumentFromFile.addEventListener(
'click',
function () {
499 editor.loadGraphFromFile(
'mtlx', auto_arrange_size);
505 var loadMaterialXDocumentFromZip = document.getElementById(
'loadMaterialXDocumentFromZip');
506 if (loadMaterialXDocumentFromZip) {
507 loadMaterialXDocumentFromZip.addEventListener(
'click',
function () {
508 editor.loadGraphFromFile(
'zip', auto_arrange_size);
514 var texAreaNumber = 0;
515 var loadMaterialXDocumentFromText = document.getElementById(
'loadMaterialXDocumentFromText');
516 if (loadMaterialXDocumentFromText) {
517 loadMaterialXDocumentFromText.addEventListener(
'click',
function () {
518 var mtlxdoc = document.getElementById(
'mtlxdoc').value;
520 if (mtlxdoc.length > 0) {
521 var name =
'MaterialXGraph' + texAreaNumber++;
522 editor.loadGraphFromString(
'mtlx', mtlxdoc, name, auto_arrange_size);
529 var loadMaterialXDefinitions = document.getElementById(
'loadMaterialXDefinitions');
530 if (loadMaterialXDefinitions) {
531 loadMaterialXDefinitions.addEventListener(
'click',
function () {
532 editor.loadDefinitionsFromFile(
'mtlx');
537 var clearGraphButton = document.getElementById(
'clearGraph');
538 if (clearGraphButton) {
539 clearGraphButton.addEventListener(
'click',
function () {
546 var saveMaterialXGraph = document.getElementById(
'saveMaterialXGraph');
547 if (saveMaterialXGraph) {
548 saveMaterialXGraph.addEventListener(
'click',
function () {
549 var sl = document.getElementById(
'writeCustomLibs').checked;
550 var sp = document.getElementById(
'saveNodePositions').checked;
552 var graphWriteOptions = { writeCustomLibs: sl, saveNodePositions: sp, writeOutputs: wo };
553 editor.saveGraphToFile(
'mtlx', graphWriteOptions);
558 var saveMaterialXGraphText = document.getElementById(
'saveMaterialXGraphText');
559 if (saveMaterialXGraphText) {
560 saveMaterialXGraphText.addEventListener(
'click',
function () {
566 var searchGraph = document.getElementById(
'searchGraph');
568 searchGraph.addEventListener(
'click',
function () {
569 var search = document.getElementById(
'searchGraphText').value;
570 editor.searchGraph(search);
575 var openSubgraph = document.getElementById(
'openSubgraph');
577 openSubgraph.addEventListener(
'click',
function () {
578 editor.openSubgraph();
583 var closeSubgraph = document.getElementById(
'closeSubgraph');
585 closeSubgraph.addEventListener(
'click',
function () {
586 editor.closeSubgraph();
591 var resetView = document.getElementById(
'resetView');
593 resetView.addEventListener(
'click',
function () {
599 var arrangeGraphButton = document.getElementById(
'arrangeGraph');
600 if (arrangeGraphButton) {
601 arrangeGraphButton.addEventListener(
'click',
function () {
602 editor.arrangeGraph();
607 var centerNodeButton = document.getElementById(
'centerNode');
608 if (centerNodeButton) {
609 centerNodeButton.addEventListener(
'click',
function () {
615 var collapseNodesButton = document.getElementById(
'collapseNodes');
616 if (collapseNodesButton) {
617 collapseNodesButton.addEventListener(
'click',
function () {
618 editor.collapseExpandNodes(
true);
621 var expandNodesButton = document.getElementById(
'expandNodes');
622 if (expandNodesButton) {
623 expandNodesButton.addEventListener(
'click',
function () {
624 editor.collapseExpandNodes(
false);
629 var selectNodesButton = document.getElementById(
'selectNodes');
630 if (selectNodesButton) {
631 selectNodesButton.addEventListener(
'click',
function () {
632 editor.selectNodes();
637 var copySelectedButton = document.getElementById(
'copySelected');
638 if (copySelectedButton) {
639 copySelectedButton.addEventListener(
'click',
function () {
640 editor.copyToClipboard();
645 var pasteSelectedButton = document.getElementById(
'pasteSelected');
646 if (pasteSelectedButton) {
647 pasteSelectedButton.addEventListener(
'click',
function () {
648 editor.pasteFromClipboard();
653 var createNodeGraphButton = document.getElementById(
'createNodeGraph');
654 if (createNodeGraphButton) {
655 createNodeGraphButton.addEventListener(
'click',
function () {
656 editor.createNodeGraph();
661 var extractNodeGraphButton = document.getElementById(
'extractNodeGraph');
662 if (extractNodeGraphButton) {
663 extractNodeGraphButton.addEventListener(
'click',
function () {
664 editor.extractNodeGraph();
682 var xmlToGraph = document.getElementById(
'xmltograph');
684 xmlToGraph.addEventListener(
'click',
function () {
685 var name =
'MaterialXGraph' + texAreaNumber++;
686 var mtlxdoc = document.getElementById(
'mtlxdoc').value;
687 editor.loadGraphFromString(
'mtlx', mtlxdoc,
'MaterialXGraph', auto_arrange_size);
692 function updateRenderableItemUI() {
693 let renderableItems = editor.findRenderableItems();
694 renderableItemUpdater(renderableItems);
697 function saveToStringUI() {
698 var cl = document.getElementById(
'writeCustomLibs').checked;
699 var sp = document.getElementById(
'saveNodePositions').checked;
701 var graphWriteOptions = { writeCustomLibs: cl, saveNodePositions: sp, writeOutputs: wo };
702 console.log(
'Save with options: ', graphWriteOptions);
703 var result = editor.saveGraphToString(
'mtlx', graphWriteOptions);
705 cmeditor.setValue(result[0]);
707 if (customRenderer) {
708 customRenderer.setSourceColorSpace(editor.getSourceColorSpace());
709 customRenderer.setTargetDistanceUnit(editor.getTargetDistanceUnit());
710 customRenderer.updateMaterialFromText(result[0]);
711 updateRenderableItemUI();
716 var graphtoxml = document.getElementById(
'graphtoxml');
718 graphtoxml.addEventListener(
'click',
function () {
723 let graphtoxml2 = document.getElementById(
'graphtoxml2');
725 graphtoxml2.addEventListener(
'click',
function () {
727 graphtoxml2.classList.remove(
'btn-outline-warning');
728 graphtoxml2.classList.add(
'btn-outline-secondary');
733 var graphtogltf = document.getElementById(
'graphtogltf');
735 graphtogltf.addEventListener(
'click',
function () {
736 var graphWriteOptions = { writeCustomLibs:
false, saveNodePositions:
false, writeOutputs:
true };
737 var result = editor.saveGraphToString(
'gltf', graphWriteOptions);
738 gltfDisplayUpdater(result[0]);
740 consoleLog(result[1], 1,
false);
746 var gltftograph = document.getElementById(
'gltftograph');
748 gltftograph.addEventListener(
'click',
function () {
749 var gltfdoc = document.getElementById(
'gltfgraph').value;
750 if (gltfdoc.length > 0) {
751 editor.loadGraphFromString(
'gltf', gltfdoc,
'GLTFGraph', auto_arrange_size);
758 let turntableEnabledUI = document.getElementById(
'turntableEnabled');
759 if (turntableEnabledUI) {
760 turntableEnabledUI.addEventListener(
'click', (e) => {
762 turntableEnabledUI.classList.toggle(
'btn-secondary');
764 customRenderer.toggleTurntable();
769 let disableRenderingUI = document.getElementById(
'disableRendering');
770 if (disableRenderingUI) {
771 disableRenderingUI.addEventListener(
'click', (e) => {
773 disableRenderingUI.classList.toggle(
'btn-danger');
775 customRenderer.toggleRendering();
780 let toggleBackgroundTextureUI = document.getElementById(
'toggleBackgroundTexture');
781 if (toggleBackgroundTextureUI) {
782 toggleBackgroundTextureUI.addEventListener(
'click', (e) => {
783 toggleBackgroundTextureUI.classList.toggle(
'btn-primary');
785 customRenderer.toggleBackgroundTexture();
790 let resetCameraUI = document.getElementById(
'resetCamera');
792 resetCameraUI.addEventListener(
'click', (e) => {
793 if (customRenderer) {
794 customRenderer.resetCamera();
800 function loadFromMenu(e) {
801 var uiItem = e.target.value;
802 if (uiItem ==
'_loadFromFile_') {
804 var fileInput = document.createElement(
'input');
805 fileInput.type =
'file';
806 fileInput.accept =
'.glb';
808 fileInput.onchange =
function(event) {
809 var file =
event.target.files[0];
811 var fileURL = URL.createObjectURL(file);
813 customRenderer.setRenderGeometry(fileURL);
814 console.log(
'Change geometry to:', fileURL,
'from file:', file.name);
821 var geometryURL = uiItem.toLowerCase().replace(/\s/g,
'');
822 var geometryPath =
'Geometry/' + geometryURL +
'.glb';
823 console.log(
'Change geometry to:', geometryPath);
825 customRenderer.setRenderGeometry(geometryPath);
830 let geometryItemSelect = document.getElementById(
'loadGeometry');
831 if (geometryItemSelect) {
834 var geometryItems = [
'Teapot',
'Shader Ball',
'Sphere',
'Plane',
'Cube',
'Cylinder',
'Donut',
'Twist',
'Custom...'];
835 for (var i = 0; i < geometryItems.length; i++) {
836 var option = document.createElement(
'option');
837 option.value = geometryValues[i];
838 option.text = geometryItems[i];
839 geometryItemSelect.appendChild(option);
843 geometryItemSelect.addEventListener(
'change', (e) => {
845 if (e.target.value ==
'_loadFromFile_')
846 e.target.value =
'Custom Geometry'
850 if (selectGeometryUI) {
852 geometryItemSelect.value = geometryId;
857 let renderableItemSelect = document.getElementById(
'renderableItem');
858 if (renderableItemSelect) {
859 renderableItemSelect.addEventListener(
'change', (e) => {
860 let index = e.target.value;
863 customRenderer.setRenderMaterial(index);
864 editor.searchGraph(index);
870 var canvas = document.getElementById(
'mygraphcanvas');
871 var canvasContainer = document.getElementById(
'canvasContainer');
872 var colContainer = document.getElementById(
'colContainer');
875 var observer =
new ResizeObserver(
function (entries) {
881 var parent = canvas.parentNode;
882 let newWidth = parent.offsetWidth;
883 let newHeight = parent.offsetHeight;
890 console.log(
'Resize node graph canvas to:', newWidth, newHeight);
891 editor.setDirty(newWidth, newHeight);
897 observer.observe(canvasContainer);
901 function setupGLTFSyntax() {
904 const gltfTextArea = document.getElementById(
'gltfgraph');
906 cmeditor = CodeMirror.fromTextArea(gltfTextArea, {
907 mode:
'application/json',
914 const initialGLTF =
'';
915 gltfTextArea.value = initialGLTF;
916 cmeditor.setValue(initialGLTF);
919 cmeditor.on(
'change', (e) => {
920 gltfTextArea.value = cmeditor.getValue();
923 var pasteButton = document.getElementById(
'gltfgraph_paste');
925 addPasteHandler(pasteButton, cmeditor);
931 function setupJavascriptSyntax() {
933 const elem = document.getElementById(
'mtlxlib');
937 let cmeditor = CodeMirror.fromTextArea(elem, {
938 mode:
'application/javascript',
946 cmeditor.setValue(
'');
949 cmeditor.on(
'change', () => {
950 elem.value = cmeditor.getValue();
957 function setupXMLSyntax() {
959 const materialXTextArea = document.getElementById(
'mtlxdoc');
960 let cmeditor = CodeMirror.fromTextArea(materialXTextArea, {
961 mode:
'application/xml',
968 const initialXML =
'';
969 materialXTextArea.value = initialXML;
970 cmeditor.setValue(initialXML);
973 cmeditor.on(
'change', (e) => {
974 materialXTextArea.value = cmeditor.getValue();
977 var pasteButton = document.getElementById(
'mtlxdoc_paste');
979 addPasteHandler(pasteButton, cmeditor);