154export
function initializeNodeEditor(materialFilename, geometryId, customRenderer, user_icon_map =
null, sampleFiles =
null,
157 "_default_":
"./Icons/materialx_logo.webp",
158 "_default_graph_":
"./Icons/nodegraph_white.svg"
161 let geometryValues = [
'teapot',
'shaderball',
'sphere',
'plane',
'cube',
'cylinder',
'donut',
'twist',
'_loadFromFile_']
165 for (var key in user_icon_map) {
166 my_icon_map[key] = user_icon_map[key];
171 function uriExists(uri) {
175 return Promise.resolve(
true);
177 return Promise.resolve(
false);
181 console.log(
'Error checking URI:', error);
182 return Promise.resolve(
false);
187 function renderableItemUpdater(renderableItems) {
188 let renderableItemSelect = document.getElementById(
'renderableItem');
189 if (renderableItemSelect) {
191 const TRUNCATION_LENGTH = 12;
193 while (renderableItemSelect.firstChild) {
194 renderableItemSelect.removeChild(renderableItemSelect.firstChild);
196 for (let i = 0; i < renderableItems.length; i++) {
197 let item = renderableItems[i];
198 let option = document.createElement(
'option');
202 if (uiItem.length > 20)
203 uiItem = uiItem.substring(0, 20) +
'...';
204 option.text = uiItem;
205 renderableItemSelect.appendChild(option);
212 function consoleLog(text, severity, clear =
null) {
213 if (severity === 2) {
214 text =
'> Error: ' + text
216 else if (severity === 1) {
217 text =
'> Warning: ' + text
224 let console_area = document.getElementById(
'console_area');
227 console_area.value = text +
'\n';
230 console_area.value = console_area.value + text +
'\n';
233 console_area.scrollTop = console_area.scrollHeight;
262 function createMenuItem(text, filename) {
263 let menuItem = document.createElement(
'li');
264 menuItem.className =
'dropdown-item';
265 menuItem.innerText = text;
266 menuItem.onclick =
function () {
267 console.log(
'Load library file:', filename);
270 let dropdownMenu = document.getElementById(
'libraryDropdown');
271 dropdownMenu.classList.remove(
'show');
282 function createSubMenu(title, auto_close =
false) {
284 let li = document.createElement(
'li');
285 li.className =
"dropend dropdown";
288 let subMenu = document.createElement(
'a');
289 subMenu.className =
"dropdown-item dropdown-toggle";
290 subMenu.setAttribute(
'data-bs-toggle',
"dropdown");
292 subMenu.setAttribute(
'data-bs-auto-close',
'outside');
294 subMenu.setAttribute(
'aria-expanded',
'false');
295 subMenu.setAttribute(
'aria-haspopup',
'true');
296 subMenu.innerHTML = title;
297 li.appendChild(subMenu);
308 function createLibraryMenu(sampleFiles, libraryDropdown) {
309 for (let key in sampleFiles) {
311 let li = createSubMenu(key,
true);
312 libraryDropdown.appendChild(li);
314 let value = sampleFiles[key];
317 if (typeof value ===
'string') {
318 let value = sampleFiles[key];
319 li.appendChild(createMenuItem(key, value));
321 else if (typeof value ===
'object') {
323 let subMenuList = document.createElement(
'ul');
324 subMenuList.className =
'dropdown-menu';
325 subMenuList.id = key;
330 if (typeof value[key] ===
'string') {
331 subMenuList.appendChild(createMenuItem(key, value[key]));
334 else if (typeof value[key] ===
'object') {
337 let sli = createSubMenu(key,
false);
338 subMenuList.appendChild(sli);
340 let ssubMenuList = document.createElement(
'ul');
341 ssubMenuList.className =
'dropdown-menu';
342 for (let skey in value[key]) {
343 ssubMenuList.appendChild(createMenuItem(skey, (value[key])[skey]));
345 sli.appendChild(ssubMenuList);
349 li.appendChild(subMenuList);
356 if (sampleFiles && libraryDropdown) {
357 createLibraryMenu(sampleFiles, libraryDropdown);
361 let selectGeometryUI =
false;
362 if (customRenderer) {
364 let geometryURL = geometryId;
365 if (geometryId.length > 0 && geometryValues.includes(geometryId)) {
366 geometryURL =
'Geometry/' + geometryId +
'.glb';
367 selectGeometryUI =
true;
369 var viewer = customRenderer.initialize(materialFilename, geometryURL, readOnly);
370 console.log(
'Setup renderer:', viewer);
373 let preview_panel = document.getElementById(
"preview_panel");
376 preview_panel.style.display =
'none';
385 function displayNodeTypes(nodeTypes) {
387 var nodeList = document.getElementById(
'nodeTypesList');
393 while (nodeList.firstChild) {
394 nodeList.removeChild(nodeList.firstChild);
398 for (var typeName in nodeTypes) {
400 var rowItem = document.createElement(
"tr");
402 var cellItem = document.createElement(
"td");
403 cellItem.textContent = typeName;
404 rowItem.appendChild(cellItem);
406 cellItem = document.createElement(
"td");
407 var nodeDefString =
'<None>';
408 var nodeDefName = nodeTypes[typeName].nodedef_name;
409 var nodeDefNode = nodeTypes[typeName].nodedef_node
410 var nodeDefHref = nodeTypes[typeName].nodedef_href;
413 var link = document.createElement(
"a");
414 link.target =
"_blank";
415 link.href = nodeDefHref;
416 link.textContent = nodeDefNode +
" ( " + nodeDefName +
" )";
417 cellItem.appendChild(link);
420 cellItem.textContent = nodeDefName;
424 cellItem.textContent = nodeDefString;
426 rowItem.appendChild(cellItem);
428 nodeList.appendChild(rowItem);
433 var cmeditor = setupXMLSyntax();
434 var cmeditor2 = setupJavascriptSyntax();
435 var cmeditor3 = setupGLTFSyntax();
442 function docDisplayUpdater(contents) {
444 cmeditor.setValue(contents);
452 function gltfDisplayUpdater(contents) {
453 if (!contents || contents.length == 0) {
457 cmeditor3.setValue(contents);
459 console.log(contents);
467 function jsDefinitionsDisplayUpdater(contents) {
469 cmeditor2.setValue(contents);
473 var canvas = document.getElementById(
'mygraphcanvas');
475 consoleLogger: consoleLog,
476 nodeTypesListUpdater: displayNodeTypes,
477 renderableItemUpdater: renderableItemUpdater,
478 documentDisplayUpdater: docDisplayUpdater,
479 gltfDocumentDisplayUpdater: gltfDisplayUpdater,
480 definitionsDisplayUpdater: jsDefinitionsDisplayUpdater,
481 propertypanel_content: document.getElementById(
'propertypanel_content'),
482 propertypanel_icon: document.getElementById(
'propertypanel_icon'),
483 icon_map: my_icon_map,
488 monitor.setRenderer(customRenderer);
489 editor.initialize(canvas, ui, monitor, materialFilename, readOnly);
495 function addUIHandlers() {
497 var saveCanvasButton = document.getElementById(
'captureGraph');
498 if (saveCanvasButton) {
499 saveCanvasButton.addEventListener(
'click',
function () {
500 var canvas = document.getElementById(
'mygraphcanvas');
501 var dataURL = canvas.toDataURL(
'image/png');
502 var link = document.createElement(
'a');
504 link.download =
'graph_capture.png';
510 var auto_arrange_size = 80;
513 var loadMaterialXDocumentFromFile = document.getElementById(
'loadMaterialXDocumentFromFile');
514 if (loadMaterialXDocumentFromFile) {
515 loadMaterialXDocumentFromFile.addEventListener(
'click',
function () {
516 editor.loadGraphFromFile(
'mtlx', auto_arrange_size,
true);
522 var loadMaterialXDocumentFromZip = document.getElementById(
'loadMaterialXDocumentFromZip');
523 if (loadMaterialXDocumentFromZip) {
524 loadMaterialXDocumentFromZip.addEventListener(
'click',
function () {
525 editor.loadGraphFromFile(
'zip', auto_arrange_size,
true);
531 var texAreaNumber = 0;
532 var loadMaterialXDocumentFromText = document.getElementById(
'loadMaterialXDocumentFromText');
533 if (loadMaterialXDocumentFromText) {
534 loadMaterialXDocumentFromText.addEventListener(
'click',
function () {
535 var mtlxdoc = document.getElementById(
'mtlxdoc').value;
537 if (mtlxdoc.length > 0) {
538 var name =
'MaterialXGraph' + texAreaNumber++;
539 editor.loadGraphFromString(
'mtlx', mtlxdoc, name, auto_arrange_size,
true);
546 var loadMaterialXDefinitions = document.getElementById(
'loadMaterialXDefinitions');
547 if (loadMaterialXDefinitions) {
548 loadMaterialXDefinitions.addEventListener(
'click',
function () {
549 editor.loadDefinitionsFromFile(
'mtlx');
554 var clearGraphButton = document.getElementById(
'clearGraph');
555 if (clearGraphButton) {
556 clearGraphButton.addEventListener(
'click',
function () {
563 var saveMaterialXGraph = document.getElementById(
'saveMaterialXGraph');
564 if (saveMaterialXGraph) {
565 saveMaterialXGraph.addEventListener(
'click',
function () {
566 var sl = document.getElementById(
'writeCustomLibs').checked;
567 var sp = document.getElementById(
'saveNodePositions').checked;
569 var graphWriteOptions = { writeCustomLibs: sl, saveNodePositions: sp, writeOutputs: wo };
570 editor.saveGraphToFile(
'mtlx', graphWriteOptions);
575 var saveMaterialXGraphText = document.getElementById(
'saveMaterialXGraphText');
576 if (saveMaterialXGraphText) {
577 saveMaterialXGraphText.addEventListener(
'click',
function () {
583 var searchGraph = document.getElementById(
'searchGraph');
585 searchGraph.addEventListener(
'click',
function () {
586 var search = document.getElementById(
'searchGraphText').value;
587 editor.searchGraph(search);
592 var openSubgraph = document.getElementById(
'openSubgraph');
594 openSubgraph.addEventListener(
'click',
function () {
595 editor.openSubgraph();
600 var closeSubgraph = document.getElementById(
'closeSubgraph');
602 closeSubgraph.addEventListener(
'click',
function () {
603 editor.closeSubgraph();
608 var resetView = document.getElementById(
'resetView');
610 resetView.addEventListener(
'click',
function () {
616 var arrangeGraphButton = document.getElementById(
'arrangeGraph');
617 if (arrangeGraphButton) {
618 arrangeGraphButton.addEventListener(
'click',
function () {
619 editor.arrangeGraph();
624 var centerNodeButton = document.getElementById(
'centerNode');
625 if (centerNodeButton) {
626 centerNodeButton.addEventListener(
'click',
function () {
632 var collapseNodesButton = document.getElementById(
'collapseNodes');
633 if (collapseNodesButton) {
634 collapseNodesButton.addEventListener(
'click',
function () {
635 editor.collapseExpandNodes(
true);
638 var expandNodesButton = document.getElementById(
'expandNodes');
639 if (expandNodesButton) {
640 expandNodesButton.addEventListener(
'click',
function () {
641 editor.collapseExpandNodes(
false);
646 var selectNodesButton = document.getElementById(
'selectNodes');
647 if (selectNodesButton) {
648 selectNodesButton.addEventListener(
'click',
function () {
649 editor.selectNodes();
654 var copySelectedButton = document.getElementById(
'copySelected');
655 if (copySelectedButton) {
656 copySelectedButton.addEventListener(
'click',
function () {
657 editor.copyToClipboard();
662 var pasteSelectedButton = document.getElementById(
'pasteSelected');
663 if (pasteSelectedButton) {
664 pasteSelectedButton.addEventListener(
'click',
function () {
665 editor.pasteFromClipboard();
670 var createNodeGraphButton = document.getElementById(
'createNodeGraph');
671 if (createNodeGraphButton) {
672 createNodeGraphButton.addEventListener(
'click',
function () {
673 editor.createNodeGraph();
678 var extractNodeGraphButton = document.getElementById(
'extractNodeGraph');
679 if (extractNodeGraphButton) {
680 extractNodeGraphButton.addEventListener(
'click',
function () {
681 editor.extractNodeGraph();
699 var xmlToGraph = document.getElementById(
'xmltograph');
701 xmlToGraph.addEventListener(
'click',
function () {
702 var name =
'MaterialXGraph' + texAreaNumber++;
703 var mtlxdoc = document.getElementById(
'mtlxdoc').value;
704 editor.loadGraphFromString(
'mtlx', mtlxdoc,
'MaterialXGraph', auto_arrange_size,
true);
709 function updateRenderableItemUI() {
710 let renderableItems = editor.findRenderableItems();
711 renderableItemUpdater(renderableItems);
714 function saveToStringUI() {
715 var cl = document.getElementById(
'writeCustomLibs').checked;
716 var sp = document.getElementById(
'saveNodePositions').checked;
718 var graphWriteOptions = { writeCustomLibs: cl, saveNodePositions: sp, writeOutputs: wo };
719 console.log(
'Save with options: ', graphWriteOptions);
720 var result = editor.saveGraphToString(
'mtlx', graphWriteOptions);
722 cmeditor.setValue(result[0]);
724 if (customRenderer) {
725 customRenderer.setSourceColorSpace(editor.getSourceColorSpace());
726 customRenderer.setTargetDistanceUnit(editor.getTargetDistanceUnit());
727 customRenderer.updateMaterialFromText(result[0]);
728 updateRenderableItemUI();
733 var graphtoxml = document.getElementById(
'graphtoxml');
735 graphtoxml.addEventListener(
'click',
function () {
740 let graphtoxml2 = document.getElementById(
'graphtoxml2');
742 graphtoxml2.addEventListener(
'click',
function () {
744 graphtoxml2.classList.remove(
'btn-outline-warning');
745 graphtoxml2.classList.add(
'btn-outline-secondary');
750 var graphtogltf = document.getElementById(
'graphtogltf');
752 graphtogltf.addEventListener(
'click',
function () {
753 var graphWriteOptions = { writeCustomLibs:
false, saveNodePositions:
false, writeOutputs:
true };
754 var result = editor.saveGraphToString(
'gltf', graphWriteOptions);
755 gltfDisplayUpdater(result[0]);
757 consoleLog(result[1], 1,
false);
763 var gltftograph = document.getElementById(
'gltftograph');
765 gltftograph.addEventListener(
'click',
function () {
766 var gltfdoc = document.getElementById(
'gltfgraph').value;
767 if (gltfdoc.length > 0) {
768 editor.loadGraphFromString(
'gltf', gltfdoc,
'GLTFGraph', auto_arrange_size,
true);
775 let turntableEnabledUI = document.getElementById(
'turntableEnabled');
776 if (turntableEnabledUI) {
777 turntableEnabledUI.addEventListener(
'click', (e) => {
779 turntableEnabledUI.classList.toggle(
'btn-secondary');
781 customRenderer.toggleTurntable();
786 let disableRenderingUI = document.getElementById(
'disableRendering');
787 if (disableRenderingUI) {
788 disableRenderingUI.addEventListener(
'click', (e) => {
790 disableRenderingUI.classList.toggle(
'btn-danger');
792 customRenderer.toggleRendering();
797 let toggleBackgroundTextureUI = document.getElementById(
'toggleBackgroundTexture');
798 if (toggleBackgroundTextureUI) {
799 toggleBackgroundTextureUI.addEventListener(
'click', (e) => {
800 toggleBackgroundTextureUI.classList.toggle(
'btn-primary');
802 customRenderer.toggleBackgroundTexture();
807 let resetCameraUI = document.getElementById(
'resetCamera');
809 resetCameraUI.addEventListener(
'click', (e) => {
810 if (customRenderer) {
811 customRenderer.resetCamera();
817 function loadFromMenu(e) {
818 var uiItem = e.target.value;
819 if (uiItem ==
'_loadFromFile_') {
821 var fileInput = document.createElement(
'input');
822 fileInput.type =
'file';
823 fileInput.accept =
'.glb';
825 fileInput.onchange =
function(event) {
826 var file =
event.target.files[0];
828 var fileURL = URL.createObjectURL(file);
830 customRenderer.setRenderGeometry(fileURL);
831 console.log(
'Change geometry to:', fileURL,
'from file:', file.name);
838 var geometryURL = uiItem.toLowerCase().replace(/\s/g,
'');
839 var geometryPath =
'Geometry/' + geometryURL +
'.glb';
840 console.log(
'Change geometry to:', geometryPath);
842 customRenderer.setRenderGeometry(geometryPath);
847 let geometryItemSelect = document.getElementById(
'loadGeometry');
848 if (geometryItemSelect) {
851 var geometryItems = [
'Teapot',
'Shader Ball',
'Sphere',
'Plane',
'Cube',
'Cylinder',
'Donut',
'Twist',
'Custom...'];
852 for (var i = 0; i < geometryItems.length; i++) {
853 var option = document.createElement(
'option');
854 option.value = geometryValues[i];
855 option.text = geometryItems[i];
856 geometryItemSelect.appendChild(option);
860 geometryItemSelect.addEventListener(
'change', (e) => {
862 if (e.target.value ==
'_loadFromFile_')
863 e.target.value =
'Custom Geometry'
867 if (selectGeometryUI) {
869 geometryItemSelect.value = geometryId;
874 let renderableItemSelect = document.getElementById(
'renderableItem');
875 if (renderableItemSelect) {
876 renderableItemSelect.addEventListener(
'change', (e) => {
877 let index = e.target.value;
880 customRenderer.setRenderMaterial(index);
881 editor.searchGraph(index);
887 var canvas = document.getElementById(
'mygraphcanvas');
888 var canvasContainer = document.getElementById(
'canvasContainer');
889 var colContainer = document.getElementById(
'colContainer');
892 var observer =
new ResizeObserver(
function (entries) {
898 var parent = canvas.parentNode;
899 let newWidth = parent.offsetWidth;
900 let newHeight = parent.offsetHeight;
907 console.log(
'Resize node graph canvas to:', newWidth, newHeight);
908 editor.setDirty(newWidth, newHeight);
914 observer.observe(canvasContainer);
918 function setupGLTFSyntax() {
921 const gltfTextArea = document.getElementById(
'gltfgraph');
923 cmeditor = CodeMirror.fromTextArea(gltfTextArea, {
924 mode:
'application/json',
931 const initialGLTF =
'';
932 gltfTextArea.value = initialGLTF;
933 cmeditor.setValue(initialGLTF);
936 cmeditor.on(
'change', (e) => {
937 gltfTextArea.value = cmeditor.getValue();
940 var pasteButton = document.getElementById(
'gltfgraph_paste');
942 addPasteHandler(pasteButton, cmeditor);
948 function setupJavascriptSyntax() {
950 const elem = document.getElementById(
'mtlxlib');
954 let cmeditor = CodeMirror.fromTextArea(elem, {
955 mode:
'application/javascript',
963 cmeditor.setValue(
'');
966 cmeditor.on(
'change', () => {
967 elem.value = cmeditor.getValue();
974 function setupXMLSyntax() {
976 const materialXTextArea = document.getElementById(
'mtlxdoc');
977 let cmeditor = CodeMirror.fromTextArea(materialXTextArea, {
978 mode:
'application/xml',
985 const initialXML =
'';
986 materialXTextArea.value = initialXML;
987 cmeditor.setValue(initialXML);
990 cmeditor.on(
'change', (e) => {
991 materialXTextArea.value = cmeditor.getValue();
994 var pasteButton = document.getElementById(
'mtlxdoc_paste');
996 addPasteHandler(pasteButton, cmeditor);