@xviewer.js/debug 1.0.3 → 1.0.4-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/module.js CHANGED
@@ -1,9 +1,11 @@
1
- import { Texture, Material, MeshBasicMaterial, MeshStandardMaterial, MeshPhysicalMaterial, Scene, OrthographicCamera, Sprite, Vector3, ShaderMaterial, Box3, Box3Helper } from 'three';
2
- import { property, ObjectInstance, Mount, PropertyManager, Component, BoxProjection } from '@xviewer.js/core';
1
+ import { Object3D, Texture, Vector4, Material, MeshBasicMaterial, MeshStandardMaterial, MeshPhysicalMaterial, Mesh, PlaneGeometry, ShaderMaterial, DoubleSide, Camera } from 'three';
2
+ import { GLTFExporter } from 'three/examples/jsm/Addons';
3
+ import * as TextureUtils from 'three/examples/jsm/utils/WebGLTextureUtils';
4
+ import { property, ObjectInstance, Component, PropertyManager } from '@xviewer.js/core';
3
5
 
4
- var e=[],t=[];function n(n,r){if(n&&"undefined"!=typeof document){var a,s=!0===r.prepend?"prepend":"append",d=!0===r.singleTag,i="string"==typeof r.container?document.querySelector(r.container):document.getElementsByTagName("head")[0];if(d){var u=e.indexOf(i);-1===u&&(u=e.push(i)-1,t[u]={}),a=t[u]&&t[u][s]?t[u][s]:t[u][s]=c();}else a=c();65279===n.charCodeAt(0)&&(n=n.substring(1)),a.styleSheet?a.styleSheet.cssText+=n:a.appendChild(document.createTextNode(n));}function c(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),r.attributes)for(var t=Object.keys(r.attributes),n=0;n<t.length;n++)e.setAttribute(t[n],r.attributes[t[n]]);var a="prepend"===s?"afterbegin":"beforeend";return i.insertAdjacentElement(a,e),e}}
6
+ var e=[],t=[];function n(n,r){if(n&&"undefined"!=typeof document){var a,s=true===r.prepend?"prepend":"append",d=true===r.singleTag,i="string"==typeof r.container?document.querySelector(r.container):document.getElementsByTagName("head")[0];if(d){var u=e.indexOf(i);-1===u&&(u=e.push(i)-1,t[u]={}),a=t[u]&&t[u][s]?t[u][s]:t[u][s]=c();}else a=c();65279===n.charCodeAt(0)&&(n=n.substring(1)),a.styleSheet?a.styleSheet.cssText+=n:a.appendChild(document.createTextNode(n));}function c(){var e=document.createElement("style");if(e.setAttribute("type","text/css"),r.attributes)for(var t=Object.keys(r.attributes),n=0;n<t.length;n++)e.setAttribute(t[n],r.attributes[t[n]]);var a="prepend"===s?"afterbegin":"beforeend";return i.insertAdjacentElement(a,e),e}}
5
7
 
6
- var css = "@charset \"UTF-8\";\n.lil-gui {\n font-family: var(--font-family);\n font-size: var(--font-size);\n line-height: 1;\n font-weight: normal;\n font-style: normal;\n text-align: left;\n background-color: var(--background-color);\n color: var(--text-color);\n user-select: none;\n -webkit-user-select: none;\n touch-action: manipulation;\n --background-color: #1f1f1f;\n --text-color: #ebebeb;\n --title-background-color: #111111;\n --title-text-color: #ebebeb;\n --widget-color: #424242;\n --hover-color: #4f4f4f;\n --focus-color: #595959;\n --number-color: #2cc9ff;\n --string-color: #a2db3c;\n --font-size: 11px;\n --input-font-size: 11px;\n --font-family: -apple-system,\n BlinkMacSystemFont,\n \"Segoe UI\",\n Roboto,\n Arial,\n sans-serif;\n --font-family-mono: Menlo,\n Monaco,\n Consolas,\n \"Droid Sans Mono\",\n monospace;\n --padding: 4px;\n --spacing: 4px;\n --widget-height: 20px;\n --title-height: calc(var(--widget-height) + var(--spacing) * 1.25);\n --name-width: 40%;\n --slider-knob-width: 2px;\n --slider-input-width: 27%;\n --color-input-width: 27%;\n --slider-input-min-width: 45px;\n --color-input-min-width: 45px;\n --folder-indent: 7px;\n --widget-padding: 0 0 0 3px;\n --widget-border-radius: 2px;\n --checkbox-size: calc(0.75 * var(--widget-height));\n --scrollbar-width: 5px;\n}\n.lil-gui, .lil-gui * {\n box-sizing: border-box;\n margin: 0;\n padding: 0;\n}\n.lil-gui.root {\n width: var(--width, 245px);\n display: flex;\n flex-direction: column;\n}\n.lil-gui.root > .title {\n background: var(--title-background-color);\n color: var(--title-text-color);\n}\n.lil-gui.root > .children {\n overflow-x: hidden;\n overflow-y: auto;\n}\n.lil-gui.root > .children::-webkit-scrollbar {\n width: var(--scrollbar-width);\n height: var(--scrollbar-width);\n background: var(--background-color);\n}\n.lil-gui.root > .children::-webkit-scrollbar-thumb {\n border-radius: var(--scrollbar-width);\n background: var(--focus-color);\n}\n@media (pointer: coarse) {\n .lil-gui.allow-touch-styles, .lil-gui.allow-touch-styles .lil-gui {\n --widget-height: 28px;\n --padding: 6px;\n --spacing: 6px;\n --font-size: 13px;\n --input-font-size: 16px;\n --folder-indent: 10px;\n --scrollbar-width: 7px;\n --slider-input-min-width: 50px;\n --color-input-min-width: 65px;\n }\n}\n.lil-gui.force-touch-styles, .lil-gui.force-touch-styles .lil-gui {\n --widget-height: 28px;\n --padding: 6px;\n --spacing: 6px;\n --font-size: 13px;\n --input-font-size: 16px;\n --folder-indent: 10px;\n --scrollbar-width: 7px;\n --slider-input-min-width: 50px;\n --color-input-min-width: 65px;\n}\n.lil-gui.autoPlace {\n max-height: 100%;\n position: fixed;\n top: 0;\n right: 15px;\n z-index: 1001;\n}\n\n.lil-gui .controller {\n display: flex;\n align-items: center;\n padding: 0 var(--padding);\n margin: var(--spacing) 0;\n}\n.lil-gui .controller.disabled {\n opacity: 0.5;\n}\n.lil-gui .controller.disabled, .lil-gui .controller.disabled * {\n pointer-events: none !important;\n}\n.lil-gui .controller > .name {\n min-width: var(--name-width);\n flex-shrink: 0;\n white-space: pre;\n padding-right: var(--spacing);\n line-height: var(--widget-height);\n}\n.lil-gui .controller .widget {\n position: relative;\n display: flex;\n align-items: center;\n width: 100%;\n min-height: var(--widget-height);\n}\n.lil-gui .controller.string input {\n color: var(--string-color);\n}\n.lil-gui .controller.boolean .widget {\n cursor: pointer;\n}\n.lil-gui .controller.color .display {\n width: 100%;\n height: var(--widget-height);\n border-radius: var(--widget-border-radius);\n position: relative;\n}\n@media (hover: hover) {\n .lil-gui .controller.color .display:hover:before {\n content: \" \";\n display: block;\n position: absolute;\n border-radius: var(--widget-border-radius);\n border: 1px solid rgba(255, 255, 255, 0.6);\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n }\n}\n.lil-gui .controller.color input[type=color] {\n opacity: 0;\n width: 100%;\n height: 100%;\n cursor: pointer;\n}\n.lil-gui .controller.color input[type=text] {\n margin-left: var(--spacing);\n font-family: var(--font-family-mono);\n min-width: var(--color-input-min-width);\n width: var(--color-input-width);\n flex-shrink: 0;\n}\n.lil-gui .controller.option select {\n opacity: 0;\n position: absolute;\n width: 100%;\n max-width: 100%;\n}\n.lil-gui .controller.option .display {\n position: relative;\n pointer-events: none;\n border-radius: var(--widget-border-radius);\n height: var(--widget-height);\n line-height: var(--widget-height);\n max-width: 100%;\n overflow: hidden;\n word-break: break-all;\n padding-left: 0.55em;\n padding-right: 1.75em;\n background: var(--widget-color);\n}\n@media (hover: hover) {\n .lil-gui .controller.option .display.focus {\n background: var(--focus-color);\n }\n}\n.lil-gui .controller.option .display.active {\n background: var(--focus-color);\n}\n.lil-gui .controller.option .display:after {\n font-family: \"lil-gui\";\n content: \"↕\";\n position: absolute;\n top: 0;\n right: 0;\n bottom: 0;\n padding-right: 0.375em;\n}\n.lil-gui .controller.option .widget,\n.lil-gui .controller.option select {\n cursor: pointer;\n}\n@media (hover: hover) {\n .lil-gui .controller.option .widget:hover .display {\n background: var(--hover-color);\n }\n}\n.lil-gui .controller.number input {\n color: var(--number-color);\n}\n.lil-gui .controller.number.hasSlider input {\n margin-left: var(--spacing);\n width: var(--slider-input-width);\n min-width: var(--slider-input-min-width);\n flex-shrink: 0;\n}\n.lil-gui .controller.number .slider {\n width: 100%;\n height: var(--widget-height);\n background-color: var(--widget-color);\n border-radius: var(--widget-border-radius);\n padding-right: var(--slider-knob-width);\n overflow: hidden;\n cursor: ew-resize;\n touch-action: pan-y;\n}\n@media (hover: hover) {\n .lil-gui .controller.number .slider:hover {\n background-color: var(--hover-color);\n }\n}\n.lil-gui .controller.number .slider.active {\n background-color: var(--focus-color);\n}\n.lil-gui .controller.number .slider.active .fill {\n opacity: 0.95;\n}\n.lil-gui .controller.number .fill {\n height: 100%;\n border-right: var(--slider-knob-width) solid var(--number-color);\n box-sizing: content-box;\n}\n.lil-gui .controller.texture canvas {\n width: 100%;\n border-radius: var(--widget-border-radius);\n}\n.lil-gui .controller.texture .group {\n display: flex;\n position: relative;\n width: 100%;\n}\n.lil-gui .controller.texture .info {\n position: absolute;\n display: flex;\n gap: 11px;\n}\n.lil-gui .controller.texture .label {\n position: absolute;\n background-color: rgba(0, 0, 0, 0.6745098039);\n display: \"flex\";\n padding: 2px;\n border-radius: var(--widget-border-radius);\n}\n.lil-gui .controller.texture .block {\n position: absolute;\n left: 50%;\n transform: translateX(-50%);\n margin-top: 2px;\n width: var(--font-size);\n height: var(--font-size);\n border-radius: var(--font-size);\n background-color: white;\n cursor: pointer;\n}\n.lil-gui .controller.texture .block.select {\n background-color: var(--string-color);\n}\n.lil-gui .controller.image img {\n width: 100%;\n}\n.lil-gui .controller.image canvas {\n width: 100%;\n}\n.lil-gui .controller.vector input {\n color: var(--number-color);\n}\n.lil-gui .controller.vector .fill {\n height: 100%;\n margin-left: var(--spacing);\n box-sizing: content-box;\n}\n\n.lil-gui-dragging .lil-gui {\n --hover-color: var(--widget-color);\n}\n.lil-gui-dragging * {\n cursor: ew-resize !important;\n}\n\n.lil-gui-dragging.lil-gui-vertical * {\n cursor: ns-resize !important;\n}\n\n.lil-gui .title {\n height: var(--title-height);\n line-height: calc(var(--title-height) - 4px);\n font-weight: 600;\n padding: 0 var(--padding);\n -webkit-tap-highlight-color: transparent;\n cursor: pointer;\n outline: none;\n text-decoration-skip: objects;\n}\n.lil-gui .title:before {\n font-family: \"lil-gui\";\n content: \"▾\";\n padding-right: 2px;\n display: inline-block;\n}\n.lil-gui .title:active {\n background: var(--title-background-color);\n opacity: 0.75;\n}\n@media (hover: hover) {\n body:not(.lil-gui-dragging) .lil-gui .title:hover {\n background: var(--title-background-color);\n opacity: 0.85;\n }\n .lil-gui .title:focus {\n text-decoration: underline var(--focus-color);\n }\n}\n.lil-gui.root > .title:focus {\n text-decoration: none !important;\n}\n.lil-gui.closed > .title:before {\n content: \"▸\";\n}\n.lil-gui.closed > .children {\n transform: translateY(-7px);\n opacity: 0;\n}\n.lil-gui.closed:not(.transition) > .children {\n display: none;\n}\n.lil-gui.transition > .children {\n transition-duration: 300ms;\n transition-property: height, opacity, transform;\n transition-timing-function: cubic-bezier(0.2, 0.6, 0.35, 1);\n overflow: hidden;\n pointer-events: none;\n}\n.lil-gui .children:empty:before {\n content: \"Empty\";\n padding: 0 var(--padding);\n margin: var(--spacing) 0;\n display: block;\n height: var(--widget-height);\n font-style: italic;\n line-height: var(--widget-height);\n opacity: 0.5;\n}\n.lil-gui.root > .children > .lil-gui > .title {\n border: 0 solid var(--widget-color);\n border-width: 1px 0;\n transition: border-color 300ms;\n}\n.lil-gui.root > .children > .lil-gui.closed > .title {\n border-bottom-color: transparent;\n}\n.lil-gui + .controller {\n border-top: 1px solid var(--widget-color);\n margin-top: 0;\n padding-top: var(--spacing);\n}\n.lil-gui .lil-gui .lil-gui > .title {\n border: none;\n}\n.lil-gui .lil-gui .lil-gui > .children {\n border: none;\n margin-left: var(--folder-indent);\n border-left: 2px solid var(--widget-color);\n}\n.lil-gui .lil-gui .controller {\n border: none;\n}\n\n.lil-gui input {\n -webkit-tap-highlight-color: transparent;\n border: 0;\n outline: none;\n font-family: var(--font-family);\n font-size: var(--input-font-size);\n border-radius: var(--widget-border-radius);\n height: var(--widget-height);\n background: var(--widget-color);\n color: var(--text-color);\n width: 100%;\n}\n@media (hover: hover) {\n .lil-gui input:hover {\n background: var(--hover-color);\n }\n .lil-gui input:active {\n background: var(--focus-color);\n }\n}\n.lil-gui input:disabled {\n opacity: 1;\n}\n.lil-gui input[type=text],\n.lil-gui input[type=number] {\n padding: var(--widget-padding);\n}\n.lil-gui input[type=text]:focus,\n.lil-gui input[type=number]:focus {\n background: var(--focus-color);\n}\n.lil-gui input::-webkit-outer-spin-button,\n.lil-gui input::-webkit-inner-spin-button {\n -webkit-appearance: none;\n margin: 0;\n}\n.lil-gui input[type=number] {\n -moz-appearance: textfield;\n}\n.lil-gui input[type=checkbox] {\n appearance: none;\n -webkit-appearance: none;\n height: var(--checkbox-size);\n width: var(--checkbox-size);\n border-radius: var(--widget-border-radius);\n text-align: center;\n cursor: pointer;\n}\n.lil-gui input[type=checkbox]:checked:before {\n font-family: \"lil-gui\";\n content: \"✓\";\n font-size: var(--checkbox-size);\n line-height: var(--checkbox-size);\n}\n@media (hover: hover) {\n .lil-gui input[type=checkbox]:focus {\n box-shadow: inset 0 0 0 1px var(--focus-color);\n }\n}\n.lil-gui button {\n -webkit-tap-highlight-color: transparent;\n outline: none;\n cursor: pointer;\n font-family: var(--font-family);\n font-size: var(--font-size);\n color: var(--text-color);\n width: 100%;\n height: var(--widget-height);\n text-transform: none;\n background: var(--widget-color);\n border-radius: var(--widget-border-radius);\n border: 1px solid var(--widget-color);\n text-align: center;\n line-height: calc(var(--widget-height) - 4px);\n}\n@media (hover: hover) {\n .lil-gui button:hover {\n background: var(--hover-color);\n border-color: var(--hover-color);\n }\n .lil-gui button:focus {\n border-color: var(--focus-color);\n }\n}\n.lil-gui button:active {\n background: var(--focus-color);\n}\n\n@font-face {\n font-family: \"lil-gui\";\n src: url(\"data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAUsAAsAAAAACJwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAAH4AAADAImwmYE9TLzIAAAGIAAAAPwAAAGBKqH5SY21hcAAAAcgAAAD0AAACrukyyJBnbHlmAAACvAAAAF8AAACEIZpWH2hlYWQAAAMcAAAAJwAAADZfcj2zaGhlYQAAA0QAAAAYAAAAJAC5AHhobXR4AAADXAAAABAAAABMAZAAAGxvY2EAAANsAAAAFAAAACgCEgIybWF4cAAAA4AAAAAeAAAAIAEfABJuYW1lAAADoAAAASIAAAIK9SUU/XBvc3QAAATEAAAAZgAAAJCTcMc2eJxVjbEOgjAURU+hFRBK1dGRL+ALnAiToyMLEzFpnPz/eAshwSa97517c/MwwJmeB9kwPl+0cf5+uGPZXsqPu4nvZabcSZldZ6kfyWnomFY/eScKqZNWupKJO6kXN3K9uCVoL7iInPr1X5baXs3tjuMqCtzEuagm/AAlzQgPAAB4nGNgYRBlnMDAysDAYM/gBiT5oLQBAwuDJAMDEwMrMwNWEJDmmsJwgCFeXZghBcjlZMgFCzOiKOIFAB71Bb8AeJy1kjFuwkAQRZ+DwRAwBtNQRUGKQ8OdKCAWUhAgKLhIuAsVSpWz5Bbkj3dEgYiUIszqWdpZe+Z7/wB1oCYmIoboiwiLT2WjKl/jscrHfGg/pKdMkyklC5Zs2LEfHYpjcRoPzme9MWWmk3dWbK9ObkWkikOetJ554fWyoEsmdSlt+uR0pCJR34b6t/TVg1SY3sYvdf8vuiKrpyaDXDISiegp17p7579Gp3p++y7HPAiY9pmTibljrr85qSidtlg4+l25GLCaS8e6rRxNBmsnERunKbaOObRz7N72ju5vdAjYpBXHgJylOAVsMseDAPEP8LYoUHicY2BiAAEfhiAGJgZWBgZ7RnFRdnVJELCQlBSRlATJMoLV2DK4glSYs6ubq5vbKrJLSbGrgEmovDuDJVhe3VzcXFwNLCOILB/C4IuQ1xTn5FPilBTj5FPmBAB4WwoqAHicY2BkYGAA4sk1sR/j+W2+MnAzpDBgAyEMQUCSg4EJxAEAwUgFHgB4nGNgZGBgSGFggJMhDIwMqEAYAByHATJ4nGNgAIIUNEwmAABl3AGReJxjYAACIQYlBiMGJ3wQAEcQBEV4nGNgZGBgEGZgY2BiAAEQyQWEDAz/wXwGAAsPATIAAHicXdBNSsNAHAXwl35iA0UQXYnMShfS9GPZA7T7LgIu03SSpkwzYTIt1BN4Ak/gKTyAeCxfw39jZkjymzcvAwmAW/wgwHUEGDb36+jQQ3GXGot79L24jxCP4gHzF/EIr4jEIe7wxhOC3g2TMYy4Q7+Lu/SHuEd/ivt4wJd4wPxbPEKMX3GI5+DJFGaSn4qNzk8mcbKSR6xdXdhSzaOZJGtdapd4vVPbi6rP+cL7TGXOHtXKll4bY1Xl7EGnPtp7Xy2n00zyKLVHfkHBa4IcJ2oD3cgggWvt/V/FbDrUlEUJhTn/0azVWbNTNr0Ens8de1tceK9xZmfB1CPjOmPH4kitmvOubcNpmVTN3oFJyjzCvnmrwhJTzqzVj9jiSX911FjeAAB4nG3HMRKCMBBA0f0giiKi4DU8k0V2GWbIZDOh4PoWWvq6J5V8If9NVNQcaDhyouXMhY4rPTcG7jwYmXhKq8Wz+p762aNaeYXom2n3m2dLTVgsrCgFJ7OTmIkYbwIbC6vIB7WmFfAAAA==\") format(\"woff\");\n}\n.curve-editor {\n display: flex;\n flex-direction: column;\n gap: 5px;\n background-color: #303030;\n outline: 1px solid #000000;\n padding: 5px;\n width: 100%;\n}\n.curve-editor .button-medium {\n background-color: #545454;\n min-width: var(--widget-height);\n min-height: var(--widget-height);\n height: 100%;\n outline: 1px solid #3c3c3c;\n display: flex;\n justify-content: center;\n align-items: center;\n color: #fff;\n font-size: var(--input-font-size, 11px);\n}\n.curve-editor .button-medium.selected {\n background-color: #4772b3;\n pointer-events: none;\n}\n.curve-editor .button-medium:hover {\n background-color: #656565;\n cursor: default;\n}\n.curve-editor .curve-top {\n display: flex;\n width: 100%;\n}\n.curve-editor .curve-panel {\n width: 100%;\n height: 90px;\n outline: 1px solid #646464;\n}\n.curve-editor .curve-point-panel {\n display: flex;\n align-items: center;\n width: 100%;\n height: var(--widget-height);\n}\n.curve-editor .curve-point-panel input {\n background-color: #545454;\n height: 100%;\n width: 100%;\n color: #fff;\n border: 0;\n outline: 1px solid #3c3c3c;\n text-align: center;\n font-size: var(--input-font-size, 11px);\n}";
8
+ var css = "/* lil-gui CSS - Main entry point */\n\n/* Import all CSS modules */\n/* Base styles and CSS variables for lil-gui */\n/* Font face for icons */\n@font-face {\n\tfont-family: 'lil-gui';\n\tsrc: url('data:application/font-woff;charset=utf-8;base64,d09GRgABAAAAAAUsAAsAAAAACJwAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABHU1VCAAABCAAAAH4AAADAImwmYE9TLzIAAAGIAAAAPwAAAGBKqH5SY21hcAAAAcgAAAD0AAACrukyyJBnbHlmAAACvAAAAF8AAACEIZpWH2hlYWQAAAMcAAAAJwAAADZfcj2zaGhlYQAAA0QAAAAYAAAAJAC5AHhobXR4AAADXAAAABAAAABMAZAAAGxvY2EAAANsAAAAFAAAACgCEgIybWF4cAAAA4AAAAAeAAAAIAEfABJuYW1lAAADoAAAASIAAAIK9SUU/XBvc3QAAATEAAAAZgAAAJCTcMc2eJxVjbEOgjAURU+hFRBK1dGRL+ALnAiToyMLEzFpnPz/eAshwSa97517c/MwwJmeB9kwPl+0cf5+uGPZXsqPu4nvZabcSZldZ6kfyWnomFY/eScKqZNWupKJO6kXN3K9uCVoL7iInPr1X5baXs3tjuMqCtzEuagm/AAlzQgPAAB4nGNgYRBlnMDAysDAYM/gBiT5oLQBAwuDJAMDEwMrMwNWEJDmmsJwgCFeXZghBcjlZMgFCzOiKOIFAB71Bb8AeJy1kjFuwkAQRZ+DwRAwBtNQRUGKQ8OdKCAWUhAgKLhIuAsVSpWz5Bbkj3dEgYiUIszqWdpZe+Z7/wB1oCYmIoboiwiLT2WjKl/jscrHfGg/pKdMkyklC5Zs2LEfHYpjcRoPzme9MWWmk3dWbK9ObkWkikOetJ554fWyoEsmdSlt+uR0pCJR34b6t/TVg1SY3sYvdf8vuiKrpyaDXDISiegp17p7579Gp3p++y7HPAiY9pmTibljrr85qSidtlg4+l25GLCaS8e6rRxNBmsnERunKbaOObRz7N72ju5vdAjYpBXHgJylOAVsMseDAPEP8LYoUHicY2BiAAEfhiAGJgZWBgZ7RnFRdnVJELCQlBSRlATJMoLV2DK4glSYs6ubq5vbKrJLSbGrgEmovDuDJVhe3VzcXFwNLCOILB/C4IuQ1xTn5FPilBTj5FPmBAB4WwoqAHicY2BkYGAA4sk1sR/j+W2+MnAzpDBgAyEMQUCSg4EJxAEAwUgFHgB4nGNgZGBgSGFggJMhDIwMqEAYAByHATJ4nGNgAIIUNEwmAABl3AGReJxjYAACIQYlBiMGJ3wQAEcQBEV4nGNgZGBgEGZgY2BiAAEQyQWEDAz/wXwGAAsPATIAAHicXdBNSsNAHAXwl35iA0UQXYnMShfS9GPZA7T7LgIu03SSpkwzYTIt1BN4Ak/gKTyAeCxfw39jZkjymzcvAwmAW/wgwHUEGDb36+jQQ3GXGot79L24jxCP4gHzF/EIr4jEIe7wxhOC3g2TMYy4Q7+Lu/SHuEd/ivt4wJd4wPxbPEKMX3GI5+DJFGaSn4qNzk8mcbKSR6xdXdhSzaOZJGtdapd4vVPbi6rP+cL7TGXOHtXKll4bY1Xl7EGnPtp7Xy2n00zyKLVHfkHBa4IcJ2oD3cgggWvt/V/FbDrUlEUJhTn/0azVWbNTNr0Ens8de1tceK9xZmfB1CPjOmPH4kitmvOubcNpmVTN3oFJyjzCvnmrwhJTzqzVj9jiSX911FjeAAB4nG3HMRKCMBBA0f0giiKi4DU8k0V2GWbIZDOh4PoWWvq6J5V8If9NVNQcaDhyouXMhY4rPTcG7jwYmXhKq8Wz+p762aNaeYXom2n3m2dLTVgsrCgFJ7OTmIkYbwIbC6vIB7WmFfAAAA==') format('woff');\n}\n/* CSS Variables - Unified Theme */\n:root {\n\t/* Colors */\n\t--bg-primary: #2a2a2a;\n\t--bg-secondary: #1e1e1e;\n\t--bg-tertiary: #323232;\n\t--bg-widget: #3c3c3c;\n\t--bg-hover: #4a4a4a;\n\t--bg-focus: #4d90fe;\n\t--bg-active: #4d90fe;\n\t--bg-selected: #185fbd;\n\n\t--text-primary: #d9d9d9;\n\t--text-muted: #a0a0a0;\n\t--text-number: #d9d9d9;\n\t--text-string: #a2db3c;\n\t--text-warning: #ff8c00;\n\t--text-error: #ff6b6b;\n\t--text-success: #4caf50;\n\n\t/* Typography */\n\t--font-size: 12px;\n\t--font-size-input: 12px;\n\t--font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;\n\t--font-family-mono: Menlo, Monaco, Consolas, 'Droid Sans Mono', monospace;\n\n\t/* Layout */\n\t--padding: 4px;\n\t--spacing: 4px;\n\t--widget-height: 20px;\n\t--title-height: 25px;\n\t--name-width: 40%;\n\t--border-radius: 2px;\n\n\t/* Components */\n\t--slider-knob-width: 2px;\n\t--slider-input-width: 27%;\n\t--color-input-width: 27%;\n\t--slider-input-min-width: 45px;\n\t--color-input-min-width: 65px;\n\t--folder-indent: 7px;\n\t--checkbox-size: 15px;\n\t--scrollbar-width: 5px;\n\n\t/* Effects */\n\t--text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8);\n\t--text-shadow-light: 0 1px 1px rgba(0, 0, 0, 0.6);\n}\n/* Touch device overrides */\n@media (pointer: coarse) {\n\t:root {\n\t\t--widget-height: 28px;\n\t\t--padding: 6px;\n\t\t--spacing: 6px;\n\t\t--font-size: 13px;\n\t\t--font-size-input: 16px;\n\t\t--folder-indent: 10px;\n\t\t--scrollbar-width: 7px;\n\t\t--slider-input-min-width: 50px;\n\t\t--color-input-min-width: 65px;\n\t}\n}\n/* Base styles */\n.lil-gui {\n\tfont-family: var(--font-family);\n\tfont-size: var(--font-size);\n\tline-height: 1;\n\tfont-weight: normal;\n\tfont-style: normal;\n\ttext-align: left;\n\tbackground-color: var(--bg-primary);\n\tcolor: var(--text-primary);\n\tuser-select: none;\n\t-webkit-user-select: none;\n\ttouch-action: manipulation;\n\ttext-shadow: var(--text-shadow);\n}\n.lil-gui,\n.lil-gui * {\n\tbox-sizing: border-box;\n\tmargin: 0;\n\tborder-radius: var(--border-radius);\n\tpadding: 1px 0;\n}\n/* Root container */\n.lil-gui.root {\n\twidth: var(--width, 245px);\n\tdisplay: flex;\n\tflex-direction: column;\n}\n.lil-gui.root>.title {\n\tbackground: var(--bg-secondary);\n\tcolor: var(--text-primary);\n\tmargin: 1px 2px;\n}\n.lil-gui.root>.children {\n\toverflow-x: hidden;\n\toverflow-y: auto;\n\tpadding: 0 2px 0;\n\tmargin: 0;\n}\n/* Scrollbar styles */\n.lil-gui.root>.children::-webkit-scrollbar,\n.scene-tree-container::-webkit-scrollbar {\n\twidth: var(--scrollbar-width);\n\theight: var(--scrollbar-width);\n\tbackground: var(--bg-primary);\n}\n.lil-gui.root>.children::-webkit-scrollbar-thumb,\n.scene-tree-container::-webkit-scrollbar-thumb {\n\tborder-radius: var(--scrollbar-width);\n\tbackground: var(--bg-focus);\n}\n.scene-tree-container::-webkit-scrollbar-thumb:hover {\n\tbackground: var(--text-primary);\n}\n/* Auto placement */\n.lil-gui.autoPlace {\n\tmax-height: 100%;\n\tposition: fixed;\n\ttop: 0;\n\tright: 15px;\n\tz-index: 1001;\n}\n/* Title styles */\n.lil-gui .title {\n\theight: var(--title-height);\n\tline-height: calc(var(--title-height) - 4px);\n\tfont-weight: normal;\n\tfont-size: var(--font-size);\n\tpadding: 0 var(--padding);\n\t-webkit-tap-highlight-color: transparent;\n\tcursor: pointer;\n\toutline: none;\n\ttext-decoration-skip: objects;\n\ttext-shadow: var(--text-shadow);\n}\n.lil-gui .title:before {\n\tfont-family: 'lil-gui';\n\tcontent: '▾';\n\tpadding-right: 2px;\n\tdisplay: inline-block;\n}\n/* Title states */\n.lil-gui .title:hover,\n.lil-gui .title:focus,\n.lil-gui .title:active {\n\tbackground: var(--bg-hover);\n\ttext-decoration: none;\n}\n.lil-gui .title:active {\n\topacity: 0.75;\n}\n/* Closed state */\n.lil-gui.closed>.title:before {\n\tcontent: '▸';\n}\n.lil-gui.closed>.children {\n\ttransform: translateY(-7px);\n\topacity: 0;\n}\n.lil-gui.closed:not(.transition)>.children {\n\tdisplay: none;\n}\n/* Transition state */\n.lil-gui.transition>.children {\n\ttransition: height 300ms, opacity 300ms, transform 300ms;\n\ttransition-timing-function: cubic-bezier(0.2, 0.6, 0.35, 1);\n\toverflow: hidden;\n\tpointer-events: none;\n}\n/* Empty children */\n.lil-gui .children:empty:before {\n\tcontent: 'Empty';\n\tpadding: 0 var(--padding);\n\tmargin: var(--spacing) 0;\n\tdisplay: block;\n\theight: var(--widget-height);\n\tfont-style: italic;\n\tline-height: var(--widget-height);\n\topacity: 0.5;\n}\n/* Dragging state */\n.lil-gui-dragging .lil-gui {\n\t--bg-hover: var(--bg-widget);\n}\n.lil-gui-dragging * {\n\tcursor: ew-resize !important;\n}\n.lil-gui-dragging.lil-gui-vertical * {\n\tcursor: ns-resize !important;\n}\n/* Controller styles for lil-gui */\n/* Base controller */\n.lil-gui .controller {\n\tdisplay: flex;\n\talign-items: center;\n\tpadding: 0 var(--padding);\n\tmargin: 0;\n}\n.lil-gui .controller.disabled {\n\topacity: 0.5;\n\tpointer-events: none !important;\n}\n.lil-gui .controller > .name {\n\tmin-width: var(--name-width);\n\tflex-shrink: 0;\n\twhite-space: pre;\n\tpadding-right: var(--spacing);\n\tline-height: var(--widget-height);\n\ttext-shadow: var(--text-shadow-light);\n}\n.lil-gui .controller .widget {\n\tposition: relative;\n\tdisplay: flex;\n\talign-items: center;\n\twidth: 100%;\n\tmin-height: var(--widget-height);\n}\n/* String controller */\n.lil-gui .controller.string input {\n\tcolor: var(--text-string);\n}\n/* Boolean controller */\n.lil-gui .controller.boolean .widget {\n\tcursor: pointer;\n}\n/* Color controller */\n.lil-gui .controller.color .display {\n\twidth: 100%;\n\theight: var(--widget-height);\n\tborder-radius: var(--border-radius);\n\tposition: relative;\n}\n.lil-gui .controller.color .display:hover:before {\n\tcontent: ' ';\n\tdisplay: block;\n\tposition: absolute;\n\tborder-radius: var(--border-radius);\n\tborder: 1px solid #fff9;\n\ttop: 0;\n\tright: 0;\n\tbottom: 0;\n\tleft: 0;\n}\n.lil-gui .controller.color input[type='color'] {\n\topacity: 0;\n\twidth: 100%;\n\theight: 100%;\n\tcursor: pointer;\n}\n.lil-gui .controller.color input[type='text'] {\n\tmargin-left: var(--spacing);\n\tfont-family: var(--font-family-mono);\n\tmin-width: var(--color-input-min-width);\n\twidth: var(--color-input-width);\n\tflex-shrink: 0;\n}\n/* Option controller */\n.lil-gui .controller.option select {\n\topacity: 0;\n\tposition: absolute;\n\twidth: 100%;\n\tmax-width: 100%;\n}\n.lil-gui .controller.option .display {\n\tposition: relative;\n\tpointer-events: none;\n\tborder-radius: var(--border-radius);\n\theight: var(--widget-height);\n\tline-height: var(--widget-height);\n\tmax-width: 100%;\n\toverflow: hidden;\n\tword-break: break-all;\n\tpadding-left: 0.55em;\n\tpadding-right: 1.75em;\n\tbackground: var(--bg-widget);\n\ttext-shadow: var(--text-shadow);\n}\n.lil-gui .controller.option .display.focus,\n.lil-gui .controller.option .display.active {\n\tbackground: var(--bg-focus);\n}\n.lil-gui .controller.option .display:after {\n\tfont-family: 'lil-gui';\n\tcontent: '↕';\n\tposition: absolute;\n\ttop: 0;\n\tright: 0;\n\tbottom: 0;\n\tpadding-right: 0.375em;\n}\n.lil-gui .controller.option .widget,\n.lil-gui .controller.option select {\n\tcursor: pointer;\n}\n.lil-gui .controller.option .widget:hover .display {\n\tbackground: var(--bg-hover);\n}\n/* Number controller */\n.lil-gui .controller.number input {\n\tcolor: var(--text-number);\n}\n.lil-gui .controller.number.hasSlider input {\n\tmargin-left: var(--spacing);\n\twidth: var(--slider-input-width);\n\tmin-width: var(--slider-input-min-width);\n\tflex-shrink: 0;\n}\n.lil-gui .controller.number .slider {\n\twidth: 100%;\n\theight: var(--widget-height);\n\tbackground-color: var(--bg-widget);\n\tborder-radius: var(--border-radius);\n\tpadding-right: var(--slider-knob-width);\n\toverflow: hidden;\n\tcursor: ew-resize;\n\ttouch-action: pan-y;\n}\n.lil-gui .controller.number .slider:hover {\n\tbackground-color: var(--bg-hover);\n}\n.lil-gui .controller.number .slider.active {\n\tbackground-color: var(--bg-active);\n}\n.lil-gui .controller.number .slider.active .fill {\n\topacity: 0.95;\n}\n.lil-gui .controller.number .fill {\n\theight: 100%;\n\tborder-right: var(--slider-knob-width) solid var(--text-number);\n\tbox-sizing: content-box;\n}\n/* Texture controller */\n.lil-gui .controller.texture canvas {\n\twidth: 100%;\n\tborder-radius: var(--border-radius);\n}\n.lil-gui .controller.texture .group {\n\tdisplay: flex;\n\tposition: relative;\n\twidth: 100%;\n}\n.lil-gui .controller.texture .info {\n\tposition: absolute;\n\tdisplay: flex;\n\tgap: 11px;\n}\n.lil-gui .controller.texture .label {\n\tposition: absolute;\n\tbackground-color: #000000ac;\n\tdisplay: flex;\n\tpadding: 2px;\n\tborder-radius: var(--border-radius);\n}\n.lil-gui .controller.texture .block {\n\tposition: absolute;\n\tleft: 50%;\n\ttransform: translateX(-50%);\n\tmargin-top: 2px;\n\twidth: var(--font-size);\n\theight: var(--font-size);\n\tborder-radius: var(--font-size);\n\tbackground-color: white;\n\tcursor: pointer;\n}\n.lil-gui .controller.texture .block.select {\n\tbackground-color: var(--text-string);\n}\n/* Image controller */\n.lil-gui .controller.image img,\n.lil-gui .controller.image canvas {\n\twidth: 100%;\n}\n/* Vector controller */\n.lil-gui .controller.vector input {\n\tcolor: var(--text-number);\n}\n.lil-gui .controller.vector .fill {\n\theight: 100%;\n\tmargin-left: var(--spacing);\n\tbox-sizing: content-box;\n}\n/* Input and form control styles for lil-gui */\n/* Base input styles */\n.lil-gui input {\n\t-webkit-tap-highlight-color: transparent;\n\tborder: 0;\n\toutline: none;\n\tfont-family: var(--font-family);\n\tfont-size: var(--font-size-input);\n\tborder-radius: var(--border-radius);\n\theight: var(--widget-height);\n\tbackground: var(--bg-widget);\n\tcolor: var(--text-primary);\n\twidth: 100%;\n\ttext-shadow: var(--text-shadow-light);\n}\n.lil-gui input:hover {\n\tbackground: var(--bg-hover);\n}\n.lil-gui input:focus {\n\tbackground: var(--bg-focus);\n}\n.lil-gui input:disabled {\n\topacity: 1;\n}\n/* Text and number inputs */\n.lil-gui input[type='text'],\n.lil-gui input[type='number'] {\n\tpadding: 0 0 0 3px;\n}\n/* Hide number spinners */\n.lil-gui input::-webkit-outer-spin-button,\n.lil-gui input::-webkit-inner-spin-button {\n\t-webkit-appearance: none;\n\tmargin: 0;\n}\n.lil-gui input[type='number'] {\n\t-moz-appearance: textfield;\n}\n/* Checkbox */\n.lil-gui input[type='checkbox'] {\n\tappearance: none;\n\t-webkit-appearance: none;\n\theight: var(--checkbox-size);\n\twidth: var(--checkbox-size);\n\tborder-radius: var(--border-radius);\n\ttext-align: center;\n\tcursor: pointer;\n}\n.lil-gui input[type='checkbox']:checked:before {\n\tfont-family: 'lil-gui';\n\tcontent: '✓';\n\tfont-size: var(--checkbox-size);\n\tline-height: var(--checkbox-size);\n}\n.lil-gui input[type='checkbox']:focus {\n\tbox-shadow: inset 0 0 0 1px var(--bg-active);\n}\n/* Button */\n.lil-gui button {\n\t-webkit-tap-highlight-color: transparent;\n\toutline: none;\n\tcursor: pointer;\n\tfont-family: var(--font-family);\n\tfont-size: var(--font-size);\n\tcolor: var(--text-primary);\n\twidth: 100%;\n\theight: var(--widget-height);\n\ttext-transform: none;\n\tbackground: var(--bg-widget);\n\tborder-radius: var(--border-radius);\n\tborder: 1px solid var(--bg-widget);\n\ttext-align: center;\n\tline-height: calc(var(--widget-height) - 4px);\n\ttext-shadow: var(--text-shadow);\n}\n.lil-gui button:hover {\n\tbackground: var(--bg-hover);\n\tborder-color: var(--bg-hover);\n}\n.lil-gui button:focus {\n\tborder-color: var(--bg-active);\n}\n.lil-gui button:active {\n\tbackground: var(--bg-active);\n}\n/* Curve editor styles for lil-gui */\n.curve-editor {\n\tdisplay: flex;\n\tflex-direction: column;\n\tgap: 5px;\n\tbackground-color: var(--bg-tertiary);\n\toutline: 1px solid #000000;\n\tpadding: 5px;\n\twidth: 100%;\n}\n.curve-editor .button-medium {\n\tbackground-color: #545454;\n\tmin-width: var(--widget-height);\n\tmin-height: var(--widget-height);\n\theight: 100%;\n\toutline: 1px solid var(--bg-widget);\n\tdisplay: flex;\n\tjustify-content: center;\n\talign-items: center;\n\tcolor: #fff;\n\tfont-size: var(--font-size-input);\n}\n.curve-editor .button-medium.selected {\n\tbackground-color: var(--bg-active);\n\tpointer-events: none;\n}\n.curve-editor .button-medium:hover {\n\tbackground-color: #656565;\n\tcursor: default;\n}\n.curve-editor .curve-top {\n\tdisplay: flex;\n\twidth: 100%;\n}\n.curve-editor .curve-panel {\n\twidth: 100%;\n\theight: 90px;\n\toutline: 1px solid #646464;\n}\n.curve-editor .curve-point-panel {\n\tdisplay: flex;\n\talign-items: center;\n\twidth: 100%;\n\theight: var(--widget-height);\n}\n.curve-editor .curve-point-panel input {\n\tbackground-color: #545454;\n\theight: 100%;\n\twidth: 100%;\n\tcolor: #fff;\n\tborder: 0;\n\toutline: 1px solid var(--bg-widget);\n\ttext-align: center;\n\tfont-size: var(--font-size-input);\n}\n/* Scene Tree Controller Styles */\n/* Base styles for scene tree */\n.scene-tree-container,\n.scene-tree-controller {\n\twidth: 100%;\n}\n.scene-tree-container {\n\theight: 100%;\n\toverflow-y: auto;\n\toverflow-x: hidden;\n\tposition: relative;\n\tpadding: 0;\n}\n.scene-tree-controller {\n\tfont-family: var(--font-family);\n\tfont-size: var(--font-size);\n\tmax-height: 300px;\n\toverflow-y: auto;\n\tborder: none;\n\tpadding: 0 !important;\n}\n/* Scene tree node */\n.scene-tree-controller .scene-tree-node {\n\ttransition: background-color 200ms ease;\n\tborder-radius: 0;\n\tpadding: 2px var(--padding);\n\tmargin: 0;\n\tdisplay: flex;\n\talign-items: center;\n\tcursor: pointer;\n\tuser-select: none;\n\tmin-height: 24px;\n\twidth: 100%;\n\tbox-sizing: border-box;\n\tposition: relative;\n\theight: var(--title-height);\n\tline-height: calc(var(--title-height) - 4px);\n}\n/* Alternating background colors */\n.scene-tree-controller .scene-tree-node.even-row {\n\tbackground-color: var(--bg-primary);\n}\n.scene-tree-controller .scene-tree-node.odd-row {\n\tbackground-color: var(--bg-tertiary);\n}\n.scene-tree-controller .scene-tree-node:hover {\n\tbackground-color: var(--bg-hover);\n}\n.scene-tree-controller .scene-tree-node.selected {\n\tbackground-color: var(--bg-active);\n\tcolor: #ffffff;\n}\n/* Expand button */\n.scene-tree-controller .scene-tree-node .expand-btn {\n\tcursor: pointer;\n\tuser-select: none;\n\ttransition: all 300ms;\n\tdisplay: inline-block;\n\theight: 16px;\n\tflex-shrink: 0;\n\tfont-family: 'lil-gui';\n\tfont-size: var(--font-size);\n\tline-height: 1;\n\tcolor: var(--text-primary);\n\ttext-align: center;\n\topacity: 0.8;\n}\n.scene-tree-controller .scene-tree-node .expand-btn:hover {\n\topacity: 1;\n}\n/* Visibility button */\n.scene-tree-controller .scene-tree-node .visibility-btn {\n\tcursor: pointer;\n\tuser-select: none;\n\ttransition: all 300ms;\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: center;\n\twidth: 16px;\n\theight: 16px;\n\tborder-radius: var(--border-radius);\n\tmargin-left: auto;\n\tflex-shrink: 0;\n\topacity: 0.8;\n}\n.scene-tree-controller .scene-tree-node .visibility-btn:hover {\n\tbackground-color: var(--bg-secondary);\n\topacity: 0.8;\n}\n.scene-tree-controller .scene-tree-node .visibility-btn svg {\n\twidth: 16px;\n\theight: 16px;\n\tfill: var(--text-primary);\n}\n.scene-tree-controller .scene-tree-node .visibility-btn.hidden svg {\n\topacity: 0.6;\n}\n.scene-tree-controller .scene-tree-node .visibility-btn:hover {\n\topacity: 1;\n}\n/* Node info */\n.scene-tree-controller .scene-tree-node .node-info {\n\tflex: 1;\n\toverflow: hidden;\n\ttext-overflow: ellipsis;\n\twhite-space: nowrap;\n\tcolor: var(--text-primary);\n\tfont-size: var(--font-size);\n\tmargin-right: var(--spacing);\n\tmin-width: 0;\n\tpadding-left: 0;\n\ttext-shadow: var(--text-shadow);\n}\n/* Context Menu Styles - Blender-inspired */\n.scene-tree-context-menu {\n\tposition: fixed;\n\tbackground: #202020;\n\tborder-radius: 2px;\n\tbox-shadow: 0 4px 20px rgba(0, 0, 0, 0.6), 0 0 0 1px rgba(255, 255, 255, 0.05);\n\tz-index: 10000;\n\tmin-width: 100px;\n\tpadding: 4px;\n\tfont-family: var(--font-family);\n\tfont-size: var(--font-size);\n\tcolor: #e0e0e0;\n}\n.scene-tree-context-menu .context-menu-item {\n\theight: 20px;\n\tline-height: 20px;\n\tpadding: 0 16px;\n\tcursor: pointer;\n\tdisplay: flex;\n\talign-items: center;\n\tcolor: #e0e0e0;\n\ttransition: all 0.1s ease;\n\tuser-select: none;\n\t-webkit-tap-highlight-color: transparent;\n\toutline: none;\n\ttext-decoration-skip: objects;\n\tfont-weight: 400;\n\tborder-radius: 2px;\n}\n.scene-tree-context-menu .context-menu-item:hover {\n\tbackground: #3a3a3a;\n\tcolor: #ffffff;\n}\n.scene-tree-context-menu .context-menu-item:active {\n\tbackground: #4a4a4a;\n\tcolor: #ffffff;\n}\n/* Context menu separator */\n.scene-tree-context-menu .context-menu-separator {\n\theight: 1px;\n\tbackground-color: #404040;\n\tmargin: 4px 0;\n}\n/* Disabled state for paste when clipboard is empty */\n.scene-tree-context-menu .context-menu-item.disabled {\n\topacity: 0.4;\n\tcursor: not-allowed;\n\tcolor: #808080;\n}\n.scene-tree-context-menu .context-menu-item.disabled:hover,\n.scene-tree-context-menu .context-menu-item.disabled:focus,\n.scene-tree-context-menu .context-menu-item.disabled:active {\n\tbackground: transparent;\n\tcolor: #808080;\n}\n\n\n";
7
9
  n(css,{});
8
10
 
9
11
  class Controller {
@@ -317,13 +319,7 @@ class OptionController extends Controller {
317
319
  this.$select.setAttribute('aria-labelledby', this.$name.id);
318
320
  this.$display = document.createElement('div');
319
321
  this.$display.classList.add('display');
320
- this._values = Array.isArray(options) ? options : Object.values(options);
321
- this._names = Array.isArray(options) ? options : Object.keys(options);
322
- this._names.forEach((name)=>{
323
- const $option = document.createElement('option');
324
- $option.innerHTML = name;
325
- this.$select.appendChild($option);
326
- });
322
+ this.options(options);
327
323
  this.$select.addEventListener('change', ()=>{
328
324
  this.setValue(this._values[this.$select.selectedIndex]);
329
325
  this._callOnFinishChange();
@@ -881,10 +877,7 @@ class ColorController extends Controller {
881
877
  return this;
882
878
  }
883
879
  constructor(parent, object, property, rgbScale){
884
- super(parent, object, property, 'color');
885
- this._textFocused = false;
886
- this._initialValueHexString = "";
887
- this._rgbScale = 1;
880
+ super(parent, object, property, 'color'), this._textFocused = false, this._initialValueHexString = "", this._rgbScale = 1;
888
881
  this.$input = document.createElement('input');
889
882
  this.$input.setAttribute('type', 'color');
890
883
  this.$input.setAttribute('tabindex', "-1");
@@ -1040,13 +1033,7 @@ class UINumber extends UIElement {
1040
1033
  return this;
1041
1034
  }
1042
1035
  constructor(value, min, max, step){
1043
- super(document.createElement('input'));
1044
- this.value = 0;
1045
- this.min = -Infinity;
1046
- this.max = Infinity;
1047
- this.step = 1;
1048
- this.nudge = 1;
1049
- this.unit = "";
1036
+ super(document.createElement('input')), this.value = 0, this.min = -Infinity, this.max = Infinity, this.step = 1, this.nudge = 1, this.unit = "";
1050
1037
  this.dom.style.cursor = 'ns-resize';
1051
1038
  this.dom.value = '0.00';
1052
1039
  this.min = min != null ? min : this.min;
@@ -1704,7 +1691,7 @@ class TextureController extends Controller {
1704
1691
  canvas.title = texture.sourceFile || "";
1705
1692
  canvas.height = canvas.width / image.width * image.height;
1706
1693
  if (this.getImage) {
1707
- const w = 256, h = 256 / image.width * image.height;
1694
+ const w = 256, h = Math.floor(256 / image.width * image.height);
1708
1695
  context.drawImage(this.getImage(texture, w, h), 0, 0, w * 1.5, h * 1.5, 0, 0, canvas.width, canvas.height); //why 1.5 ?
1709
1696
  }
1710
1697
  } else {
@@ -1716,8 +1703,7 @@ class TextureController extends Controller {
1716
1703
  return this;
1717
1704
  }
1718
1705
  constructor(parent, object, property){
1719
- super(parent, object, property, "texture");
1720
- this._autoRefresh = false;
1706
+ super(parent, object, property, "texture"), this._autoRefresh = false;
1721
1707
  const group = this.$widget.appendChild(document.createElement("div"));
1722
1708
  group.classList.add("group");
1723
1709
  this.$canvas = group.appendChild(document.createElement("canvas"));
@@ -1736,6 +1722,486 @@ class TextureController extends Controller {
1736
1722
  }
1737
1723
  }
1738
1724
 
1725
+ class SceneTreeController extends Controller {
1726
+ _setupDOM() {
1727
+ // Hide the name element since we don't need it for scene tree
1728
+ this.$name.style.display = 'none';
1729
+ this.$treeContainer = document.createElement('div');
1730
+ this.$treeContainer.classList.add('scene-tree-container');
1731
+ this.$widget.appendChild(this.$treeContainer);
1732
+ }
1733
+ _createContextMenu() {
1734
+ this._contextMenu = document.createElement('div');
1735
+ this._contextMenu.classList.add('scene-tree-context-menu');
1736
+ this._contextMenu.style.display = 'none';
1737
+ const menuItems = [
1738
+ {
1739
+ label: 'Export',
1740
+ action: 'export'
1741
+ },
1742
+ {
1743
+ label: 'Copy',
1744
+ action: 'copy'
1745
+ },
1746
+ {
1747
+ label: 'Paste',
1748
+ action: 'paste'
1749
+ }
1750
+ ];
1751
+ menuItems.forEach((item)=>{
1752
+ const menuItem = document.createElement('div');
1753
+ menuItem.classList.add('context-menu-item');
1754
+ menuItem.dataset.action = item.action;
1755
+ menuItem.textContent = item.label;
1756
+ menuItem.addEventListener('click', ()=>this._handleContextMenuAction(item.action));
1757
+ this._contextMenu.appendChild(menuItem);
1758
+ });
1759
+ document.body.appendChild(this._contextMenu);
1760
+ }
1761
+ _setupGlobalEventListeners() {
1762
+ // Hide context menu when clicking outside
1763
+ document.addEventListener('click', (e)=>{
1764
+ if (this._contextMenu && !this._contextMenu.contains(e.target)) {
1765
+ this._hideContextMenu();
1766
+ }
1767
+ });
1768
+ // Hide context menu on escape key
1769
+ document.addEventListener('keydown', (e)=>{
1770
+ if (e.key === 'Escape') {
1771
+ this._hideContextMenu();
1772
+ }
1773
+ });
1774
+ }
1775
+ _showContextMenu(x, y, target) {
1776
+ if (!this._contextMenu) return;
1777
+ this._contextMenuTarget = target;
1778
+ this._contextMenu.style.display = 'block';
1779
+ // Update paste menu item state based on clipboard
1780
+ this._updateContextMenuState();
1781
+ // Position the menu
1782
+ const rect = this._contextMenu.getBoundingClientRect();
1783
+ const viewportWidth = window.innerWidth;
1784
+ const viewportHeight = window.innerHeight;
1785
+ // Adjust position if menu would go off-screen
1786
+ let adjustedX = x;
1787
+ let adjustedY = y;
1788
+ if (x + rect.width > viewportWidth) {
1789
+ adjustedX = x - rect.width;
1790
+ }
1791
+ if (y + rect.height > viewportHeight) {
1792
+ adjustedY = y - rect.height;
1793
+ }
1794
+ this._contextMenu.style.left = `${adjustedX}px`;
1795
+ this._contextMenu.style.top = `${adjustedY}px`;
1796
+ }
1797
+ _updateContextMenuState() {
1798
+ if (!this._contextMenu) return;
1799
+ const pasteItem = this._contextMenu.querySelector('[data-action="paste"]');
1800
+ if (pasteItem) {
1801
+ if (this._clipboardData) {
1802
+ pasteItem.classList.remove('disabled');
1803
+ pasteItem.style.cursor = 'pointer';
1804
+ } else {
1805
+ pasteItem.classList.add('disabled');
1806
+ pasteItem.style.cursor = 'not-allowed';
1807
+ }
1808
+ }
1809
+ }
1810
+ _hideContextMenu() {
1811
+ if (this._contextMenu) {
1812
+ this._contextMenu.style.display = 'none';
1813
+ }
1814
+ this._contextMenuTarget = null;
1815
+ }
1816
+ _handleContextMenuAction(action) {
1817
+ if (!this._contextMenuTarget) return;
1818
+ switch(action){
1819
+ case 'export':
1820
+ this._exportObject(this._contextMenuTarget);
1821
+ break;
1822
+ case 'copy':
1823
+ this._copyObject(this._contextMenuTarget);
1824
+ break;
1825
+ case 'paste':
1826
+ if (this._clipboardData) {
1827
+ this._pasteObject(this._contextMenuTarget);
1828
+ }
1829
+ break;
1830
+ }
1831
+ this._hideContextMenu();
1832
+ }
1833
+ _exportObject(node) {
1834
+ try {
1835
+ new GLTFExporter().setTextureUtils(TextureUtils).parse(node.object, (result)=>{
1836
+ if (result instanceof ArrayBuffer) {
1837
+ const blob = new Blob([
1838
+ result
1839
+ ], {
1840
+ type: 'application/octet-stream'
1841
+ });
1842
+ const url = URL.createObjectURL(blob);
1843
+ const link = document.createElement('a');
1844
+ link.href = url;
1845
+ link.download = `${node.name || node.type}.glb`;
1846
+ link.click();
1847
+ }
1848
+ }, (err)=>{
1849
+ console.log('An error happened during parsing', err);
1850
+ }, {
1851
+ trs: false,
1852
+ binary: true,
1853
+ maxTextureSize: 4096
1854
+ });
1855
+ console.log(`Exported object: ${node.name || node.type}`);
1856
+ } catch (error) {
1857
+ console.error('Failed to export object:', error);
1858
+ }
1859
+ }
1860
+ _copyObject(node) {
1861
+ try {
1862
+ const copyData = this._serializeObject(node.object);
1863
+ this._clipboardData = copyData;
1864
+ // Also copy to system clipboard if possible
1865
+ if (navigator.clipboard && navigator.clipboard.writeText) {
1866
+ navigator.clipboard.writeText(JSON.stringify(copyData, null, 2));
1867
+ }
1868
+ console.log(`Copied object: ${node.name || node.type}`);
1869
+ } catch (error) {
1870
+ console.error('Failed to copy object:', error);
1871
+ }
1872
+ }
1873
+ _pasteObject(targetNode) {
1874
+ if (!this._clipboardData) {
1875
+ console.warn('No object data in clipboard');
1876
+ return;
1877
+ }
1878
+ try {
1879
+ // Create a new object based on clipboard data
1880
+ const newObject = this._deserializeObject(this._clipboardData);
1881
+ if (newObject) {
1882
+ // Add to the target node's parent
1883
+ const parent = this._findParentNode(targetNode);
1884
+ if (parent) {
1885
+ parent.object.add(newObject);
1886
+ this.refresh();
1887
+ console.log(`Pasted object to: ${parent.name || parent.type}`);
1888
+ }
1889
+ }
1890
+ } catch (error) {
1891
+ console.error('Failed to paste object:', error);
1892
+ }
1893
+ }
1894
+ _serializeObject(obj) {
1895
+ const serialized = {
1896
+ type: obj.type,
1897
+ name: obj.name,
1898
+ uuid: obj.uuid,
1899
+ visible: obj.visible,
1900
+ position: obj.position.toArray(),
1901
+ rotation: obj.rotation.toArray(),
1902
+ scale: obj.scale.toArray(),
1903
+ userData: obj.userData
1904
+ };
1905
+ // Add children if any
1906
+ if (obj.children.length > 0) {
1907
+ serialized.children = obj.children.map((child)=>this._serializeObject(child));
1908
+ }
1909
+ return serialized;
1910
+ }
1911
+ _deserializeObject(data) {
1912
+ try {
1913
+ // This is a simplified deserialization
1914
+ // In a real implementation, you'd want to handle different object types properly
1915
+ const obj = new Object3D();
1916
+ obj.name = data.name || data.type;
1917
+ obj.visible = data.visible !== undefined ? data.visible : true;
1918
+ if (data.position) {
1919
+ obj.position.set(data.position[0], data.position[1], data.position[2]);
1920
+ }
1921
+ if (data.rotation) {
1922
+ obj.rotation.set(data.rotation[0], data.rotation[1], data.rotation[2]);
1923
+ }
1924
+ if (data.scale) {
1925
+ obj.scale.set(data.scale[0], data.scale[1], data.scale[2]);
1926
+ }
1927
+ if (data.userData) {
1928
+ obj.userData = {
1929
+ ...data.userData
1930
+ };
1931
+ }
1932
+ return obj;
1933
+ } catch (error) {
1934
+ console.error('Failed to deserialize object:', error);
1935
+ return null;
1936
+ }
1937
+ }
1938
+ _findParentNode(node) {
1939
+ const findParent = (current, target)=>{
1940
+ if (current.children.includes(target)) {
1941
+ return current;
1942
+ }
1943
+ for (const child of current.children){
1944
+ const result = findParent(child, target);
1945
+ if (result) return result;
1946
+ }
1947
+ return null;
1948
+ };
1949
+ return findParent(this._rootNode, node);
1950
+ }
1951
+ _buildSceneTree(scene) {
1952
+ const buildNode = (obj)=>{
1953
+ const children = [];
1954
+ for(let i = 0; i < obj.children.length; i++){
1955
+ children.push(buildNode(obj.children[i]));
1956
+ }
1957
+ return {
1958
+ object: obj,
1959
+ name: obj.name || obj.type,
1960
+ type: obj.type,
1961
+ visible: obj.visible,
1962
+ children: children,
1963
+ expanded: false
1964
+ };
1965
+ };
1966
+ return buildNode(scene);
1967
+ }
1968
+ _renderTree() {
1969
+ this.$treeContainer.innerHTML = '';
1970
+ this._visibleNodeCount = 0; // Reset counter for each render
1971
+ this._renderNode(this._rootNode, 0);
1972
+ }
1973
+ _renderNode(node, depth) {
1974
+ const nodeElement = document.createElement('div');
1975
+ nodeElement.classList.add('scene-tree-node');
1976
+ // Remove marginLeft from the node itself to keep background full width
1977
+ // Add alternating background colors using theme colors
1978
+ // Use a simple counter that increments for each visible node
1979
+ const isEvenRow = this._visibleNodeCount % 2 === 0;
1980
+ nodeElement.classList.add(isEvenRow ? 'even-row' : 'odd-row');
1981
+ this._visibleNodeCount++;
1982
+ // Create a content wrapper for indentation
1983
+ const contentWrapper = document.createElement('div');
1984
+ contentWrapper.style.marginLeft = `${depth * 7}px`;
1985
+ contentWrapper.style.display = 'flex';
1986
+ contentWrapper.style.alignItems = 'center';
1987
+ contentWrapper.style.width = '100%';
1988
+ // Expand/collapse button
1989
+ if (node.children.length > 0) {
1990
+ const expandBtn = document.createElement('span');
1991
+ expandBtn.classList.add('expand-btn');
1992
+ expandBtn.classList.add(node.expanded ? 'expanded' : 'collapsed');
1993
+ expandBtn.textContent = node.expanded ? '▾' : '▸';
1994
+ expandBtn.addEventListener('click', (e)=>{
1995
+ e.stopPropagation();
1996
+ node.expanded = !node.expanded;
1997
+ this._renderTree();
1998
+ });
1999
+ contentWrapper.appendChild(expandBtn);
2000
+ }
2001
+ // Node name and type
2002
+ const nodeInfo = document.createElement('span');
2003
+ nodeInfo.textContent = `${node.name} (${node.type})`;
2004
+ nodeInfo.classList.add('node-info');
2005
+ contentWrapper.appendChild(nodeInfo);
2006
+ // Visibility toggle (moved after node name)
2007
+ const visibilityBtn = document.createElement('span');
2008
+ visibilityBtn.classList.add('visibility-btn');
2009
+ visibilityBtn.classList.add(node.visible ? 'visible' : 'hidden');
2010
+ visibilityBtn.innerHTML = node.visible ? '<svg viewBox="0 0 24 24" width="16" height="16"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>' : '<svg viewBox="0 0 24 24" width="16" height="16"><path d="M12 7c2.76 0 5 2.24 5 5 0 .65-.13 1.26-.36 1.83l2.92 2.92c1.51-1.26 2.7-2.89 3.43-4.75-1.73-4.39-6-7.5-11-7.5-1.4 0-2.74.25-3.98.7l2.16 2.16C10.74 7.13 11.35 7 12 7zM2 4.27l2.28 2.28.46.46C3.08 8.3 1.78 10.02 1 12c1.73 4.39 6 7.5 11 7.5 1.55 0 3.03-.3 4.38-.84l.42.42L19.73 22 21 20.73 3.27 3 2 4.27zM7.53 9.8l1.55 1.55c-.05.21-.08.43-.08.65 0 1.66 1.34 3 3 3 .22 0 .44-.03.65-.08l1.55 1.55c-.67.33-1.41.53-2.2.53-2.76 0-5-2.24-5-5 0-.79.2-1.53.53-2.2zm4.31-.78l3.15 3.15.02-.16c0-1.66-1.34-3-3-3l-.17.01z"/></svg>';
2011
+ visibilityBtn.addEventListener('click', (e)=>{
2012
+ e.stopPropagation();
2013
+ node.object.visible = !node.object.visible;
2014
+ node.visible = node.object.visible;
2015
+ this._renderTree();
2016
+ });
2017
+ contentWrapper.appendChild(visibilityBtn);
2018
+ // Add content wrapper to node
2019
+ nodeElement.appendChild(contentWrapper);
2020
+ // Selection indicator
2021
+ if (this._selectedObject === node.object) {
2022
+ nodeElement.classList.add('selected');
2023
+ }
2024
+ // Click to select
2025
+ nodeElement.addEventListener('click', ()=>{
2026
+ this._selectObject(node.object);
2027
+ });
2028
+ // Right-click for context menu
2029
+ nodeElement.addEventListener('contextmenu', (e)=>{
2030
+ e.preventDefault();
2031
+ e.stopPropagation();
2032
+ this._showContextMenu(e.clientX, e.clientY, node);
2033
+ });
2034
+ this.$treeContainer.appendChild(nodeElement);
2035
+ // Render children if expanded
2036
+ if (node.expanded) {
2037
+ node.children.forEach((child)=>{
2038
+ this._renderNode(child, depth + 1);
2039
+ });
2040
+ }
2041
+ }
2042
+ _selectObject(object) {
2043
+ this._selectedObject = object;
2044
+ this._renderTree();
2045
+ if (this._onObjectSelect) {
2046
+ this._onObjectSelect(object);
2047
+ }
2048
+ }
2049
+ onObjectSelect(callback) {
2050
+ this._onObjectSelect = callback;
2051
+ return this;
2052
+ }
2053
+ refresh() {
2054
+ this._rootNode = this._buildSceneTree(this._scene);
2055
+ this._renderTree();
2056
+ }
2057
+ getSelectedObject() {
2058
+ return this._selectedObject;
2059
+ }
2060
+ expandAll() {
2061
+ const expandNode = (node)=>{
2062
+ node.expanded = true;
2063
+ node.children.forEach(expandNode);
2064
+ };
2065
+ expandNode(this._rootNode);
2066
+ this._renderTree();
2067
+ }
2068
+ collapseAll() {
2069
+ const collapseNode = (node)=>{
2070
+ node.expanded = false;
2071
+ node.children.forEach(collapseNode);
2072
+ };
2073
+ collapseNode(this._rootNode);
2074
+ this._renderTree();
2075
+ }
2076
+ update() {
2077
+ // Always update visibility states (lightweight operation)
2078
+ this._updateVisibilityStates(this._rootNode);
2079
+ // Only check for structural changes periodically to avoid performance issues
2080
+ if (this._autoUpdate && this._shouldCheckForChanges()) {
2081
+ this._checkForStructuralChanges();
2082
+ }
2083
+ }
2084
+ _shouldCheckForChanges() {
2085
+ const now = performance.now();
2086
+ if (now - this._lastUpdateTime > this._updateInterval) {
2087
+ this._lastUpdateTime = now;
2088
+ return true;
2089
+ }
2090
+ return false;
2091
+ }
2092
+ setAutoUpdate(enabled) {
2093
+ this._autoUpdate = enabled;
2094
+ }
2095
+ setUpdateInterval(intervalMs) {
2096
+ this._updateInterval = intervalMs;
2097
+ }
2098
+ getUpdateInterval() {
2099
+ return this._updateInterval;
2100
+ }
2101
+ getAutoUpdate() {
2102
+ return this._autoUpdate;
2103
+ }
2104
+ forceUpdate() {
2105
+ this._rootNode = this._buildSceneTree(this._scene);
2106
+ this._renderTree();
2107
+ }
2108
+ _checkForStructuralChanges() {
2109
+ // Use lightweight change detection instead of rebuilding the entire tree
2110
+ if (this._hasSceneStructureChanged()) {
2111
+ this._rebuildSceneTree();
2112
+ }
2113
+ }
2114
+ _hasSceneStructureChanged() {
2115
+ // Quick check for common changes without rebuilding the tree
2116
+ return this._checkObjectCount() || this._checkObjectReferences();
2117
+ }
2118
+ _checkObjectCount() {
2119
+ // Check if the total number of objects has changed
2120
+ const currentCount = this._countObjects(this._scene);
2121
+ if (currentCount !== this._cachedObjectCount) {
2122
+ this._cachedObjectCount = currentCount;
2123
+ return true;
2124
+ }
2125
+ return false;
2126
+ }
2127
+ _checkObjectReferences() {
2128
+ // Check if any object references have changed (objects added/removed)
2129
+ return this._scene.children.length !== this._rootNode.children.length || this._hasChildrenChanged(this._scene, this._rootNode);
2130
+ }
2131
+ _hasChildrenChanged(sceneObj, treeNode) {
2132
+ if (sceneObj.children.length !== treeNode.children.length) return true;
2133
+ for(let i = 0; i < sceneObj.children.length; i++){
2134
+ const sceneChild = sceneObj.children[i];
2135
+ const treeChild = treeNode.children[i];
2136
+ if (sceneChild !== treeChild.object) return true;
2137
+ if (this._hasChildrenChanged(sceneChild, treeChild)) return true;
2138
+ }
2139
+ return false;
2140
+ }
2141
+ _countObjects(obj) {
2142
+ let count = 1;
2143
+ for(let i = 0; i < obj.children.length; i++){
2144
+ count += this._countObjects(obj.children[i]);
2145
+ }
2146
+ return count;
2147
+ }
2148
+ _rebuildSceneTree() {
2149
+ // Only rebuild when necessary
2150
+ const currentStructure = this._buildSceneTree(this._scene);
2151
+ this._preserveExpandedStates(this._rootNode, currentStructure);
2152
+ this._rootNode = currentStructure;
2153
+ this._cachedObjectCount = this._countObjects(this._scene);
2154
+ this._renderTree();
2155
+ }
2156
+ _hasStructureChanged(oldNode, newNode) {
2157
+ if (oldNode.children.length !== newNode.children.length) return true;
2158
+ if (oldNode.object !== newNode.object) return true;
2159
+ for(let i = 0; i < oldNode.children.length; i++){
2160
+ if (this._hasStructureChanged(oldNode.children[i], newNode.children[i])) {
2161
+ return true;
2162
+ }
2163
+ }
2164
+ return false;
2165
+ }
2166
+ _preserveExpandedStates(oldNode, newNode) {
2167
+ // Try to preserve expanded states by matching object references
2168
+ if (oldNode.object === newNode.object) {
2169
+ newNode.expanded = oldNode.expanded;
2170
+ }
2171
+ for(let i = 0; i < Math.min(oldNode.children.length, newNode.children.length); i++){
2172
+ this._preserveExpandedStates(oldNode.children[i], newNode.children[i]);
2173
+ }
2174
+ }
2175
+ _updateVisibilityStates(node) {
2176
+ node.visible = node.object.visible;
2177
+ node.children.forEach((child)=>this._updateVisibilityStates(child));
2178
+ }
2179
+ destroy() {
2180
+ super.destroy();
2181
+ this._onObjectSelect = null;
2182
+ // Clean up context menu
2183
+ if (this._contextMenu && this._contextMenu.parentNode) {
2184
+ this._contextMenu.parentNode.removeChild(this._contextMenu);
2185
+ }
2186
+ this._contextMenu = null;
2187
+ this._contextMenuTarget = null;
2188
+ this._clipboardData = null;
2189
+ }
2190
+ constructor(parent, scene){
2191
+ super(parent, scene, 'sceneTree', 'scene-tree-controller', 'div'), this._selectedObject = null, this._onObjectSelect = null, this._autoUpdate = true, this._cachedObjectCount = 0, this._lastUpdateTime = 0, this._updateInterval = 1000 // Only check for changes every 1 second
2192
+ , this._visibleNodeCount = 0 // Counter for alternating background colors
2193
+ , // Context menu properties
2194
+ this._contextMenu = null, this._contextMenuTarget = null, this._clipboardData = null;
2195
+ this._scene = scene;
2196
+ this._rootNode = this._buildSceneTree(scene);
2197
+ this._cachedObjectCount = this._countObjects(scene);
2198
+ this._setupDOM();
2199
+ this._createContextMenu();
2200
+ this._renderTree();
2201
+ this._setupGlobalEventListeners();
2202
+ }
2203
+ }
2204
+
1739
2205
  class GUI {
1740
2206
  /**
1741
2207
  * Adds a controller to the GUI, inferring controller type using the `typeof` operator.
@@ -1810,6 +2276,13 @@ class GUI {
1810
2276
  addCurve(object, property) {
1811
2277
  return new CurveController(this, object, property);
1812
2278
  }
2279
+ /**
2280
+ * Adds a scene tree controller to display Three.js scene hierarchy.
2281
+ * @param {Scene} scene The Three.js scene to display
2282
+ * @returns {SceneTreeController}
2283
+ */ addSceneTree(scene) {
2284
+ return new SceneTreeController(this, scene);
2285
+ }
1813
2286
  /**
1814
2287
  * Adds a folder to the GUI, which is just another GUI. This method returns
1815
2288
  * the nested GUI so you can add controllers to it.
@@ -1819,8 +2292,6 @@ class GUI {
1819
2292
  * folder.add( position, 'y' );
1820
2293
  * folder.add( position, 'z' );
1821
2294
  *
1822
- * @param {string} title Name to display in the folder's title bar.
1823
- * @param {string} id Id
1824
2295
  * @returns {GUI}
1825
2296
  */ addFolder(title, id = title) {
1826
2297
  const folder = new GUI({
@@ -2116,6 +2587,25 @@ class GUI {
2116
2587
  child.update();
2117
2588
  }
2118
2589
  }
2590
+ /**
2591
+ * Sets this folder as active and deactivates all other folders
2592
+ */ setActive() {
2593
+ // Only root GUI can manage active states
2594
+ if (!this.parent) return;
2595
+ // Remove active class from ALL folders in the entire GUI tree using DOM query
2596
+ const allActiveFolders = document.querySelectorAll('.lil-gui.active');
2597
+ allActiveFolders.forEach((folder)=>{
2598
+ folder.classList.remove('active');
2599
+ });
2600
+ // Add active class to this folder
2601
+ this.domElement.classList.add('active');
2602
+ }
2603
+ /**
2604
+ * Gets the currently active folder
2605
+ */ getActiveFolder() {
2606
+ if (!this.parent) return null;
2607
+ return this.parent.folders.find((folder)=>folder.domElement.classList.contains('active')) || null;
2608
+ }
2119
2609
  constructor({ parent, container, width, autoPlace = parent === undefined, title = 'Controls', id = title, closeFolders = false, touchStyles = true } = {}){
2120
2610
  this.children = [];
2121
2611
  this.controllers = [];
@@ -2137,6 +2627,8 @@ class GUI {
2137
2627
  this.$title.setAttribute('aria-expanded', "true");
2138
2628
  this.$title.setAttribute('tabindex', "0");
2139
2629
  this.$title.addEventListener('click', ()=>{
2630
+ // Set this folder as active and deactivate others
2631
+ this.setActive();
2140
2632
  this.openAnimated(this._closed);
2141
2633
  });
2142
2634
  this.$title.addEventListener('keydown', (e)=>{
@@ -2195,7 +2687,7 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
2195
2687
  OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
2196
2688
  PERFORMANCE OF THIS SOFTWARE.
2197
2689
  ***************************************************************************** */
2198
- /* global Reflect, Promise, SuppressedError, Symbol */
2690
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
2199
2691
 
2200
2692
 
2201
2693
  function __decorate(decorators, target, key, desc) {
@@ -2351,67 +2843,74 @@ __decorate([
2351
2843
  property
2352
2844
  ], ViewerHelper.prototype, "targetFrameRate", null);
2353
2845
 
2354
- class Inspector extends Mount {
2846
+ const vert_Sprite = `
2847
+ uniform vec4 u_transform;
2848
+ uniform vec4 u_resolution;
2849
+ varying vec2 v_uv;
2850
+
2851
+ void main() {
2852
+ vec2 pos = u_transform.xy + u_transform.zw * 0.5 + position.xy * u_transform.zw;
2853
+ vec2 ndc = pos * u_resolution.zw * 2. - 1.;
2854
+ gl_Position = vec4(ndc, 0., 1.);
2855
+ v_uv = uv;
2856
+ }
2857
+ `;
2858
+ const frag_Sprite = `
2859
+ uniform sampler2D u_texture;
2860
+ varying vec2 v_uv;
2861
+
2862
+ void main() {
2863
+ gl_FragColor = texture2D(u_texture, v_uv);
2864
+ }
2865
+ `;
2866
+ class Inspector extends Component {
2355
2867
  onLoad() {
2356
2868
  const { width, height } = this.viewer;
2357
- this._scene = new Scene();
2358
- this._camera = new OrthographicCamera(0, width, height, 0, -1, 1);
2869
+ this._spriteUniforms.u_resolution.value.set(width, height, 1 / width, 1 / height);
2870
+ this._sprite = new Mesh(new PlaneGeometry(1, 1, 1, 1), new ShaderMaterial({
2871
+ side: DoubleSide,
2872
+ vertexShader: vert_Sprite,
2873
+ fragmentShader: frag_Sprite,
2874
+ uniforms: this._spriteUniforms
2875
+ }));
2876
+ this._sprite.frustumCulled = false;
2877
+ this._camera.viewport = new Vector4(0, 0, 256, 256);
2359
2878
  this._gui = new GUI({
2360
2879
  width: 310,
2361
2880
  title: "Inspector"
2362
2881
  }).close();
2882
+ this._tree = this._gui.addSceneTree(this.viewer.scene);
2363
2883
  this._gui.addFolder("Viewer").close().hide();
2364
2884
  this._gui.addFolder("Component").close().hide();
2365
2885
  this._gui.addFolder("Material").close().hide();
2366
2886
  this._gui.addFolder("Other").close().hide();
2367
2887
  this._inspect(new ViewerHelper(this.viewer));
2368
- this._addSprite();
2369
2888
  }
2370
2889
  onDestroy() {
2890
+ if (this._tree) {
2891
+ this._tree.destroy();
2892
+ }
2371
2893
  this._gui.destroy();
2372
2894
  this._refCntMap.clear();
2373
2895
  this._targetMap.clear();
2374
2896
  }
2375
2897
  update(dt) {
2376
2898
  this._gui.update();
2899
+ if (this._tree) {
2900
+ this._tree.update();
2901
+ }
2377
2902
  this._updateFolders();
2378
2903
  }
2379
2904
  resize(width, height) {
2380
- this._camera.left = 0;
2381
- this._camera.right = width;
2382
- this._camera.top = height;
2383
- this._camera.bottom = 0;
2384
- this._camera.updateProjectionMatrix();
2385
- }
2386
- _addSprite() {
2387
- const viewer = this.viewer;
2388
- this._sprite = viewer.add(Sprite, {
2389
- parent: this._scene,
2390
- scale: new Vector3(256, 256, 1),
2391
- position: new Vector3(viewer.width / 2, viewer.height / 2),
2392
- visible: false
2393
- });
2394
- }
2395
- _renderScene() {
2396
- const { renderer } = this.viewer;
2397
- const autoClearDepth = renderer.autoClearDepth;
2398
- const autoClearColor = renderer.autoClearColor;
2399
- renderer.autoClearDepth = true;
2400
- renderer.autoClearColor = false;
2401
- renderer.render(this._scene, this._camera);
2402
- renderer.autoClearDepth = autoClearDepth;
2403
- renderer.autoClearColor = autoClearColor;
2404
- return renderer.domElement;
2905
+ this._spriteUniforms.u_resolution.value.set(width, height, 1 / width, 1 / height);
2405
2906
  }
2406
2907
  _renderSprite(texture, w, h) {
2407
- this._sprite.visible = true;
2408
- this._sprite.scale.set(w, h, 1);
2409
- this._sprite.position.set(w / 2, this._camera.top - h / 2, 0);
2410
- this._sprite.material.map = texture;
2411
- this._sprite.material.needsUpdate = true;
2412
- const dom = this._renderScene();
2413
- this._sprite.visible = false;
2414
- return dom;
2908
+ const resolution = this._spriteUniforms.u_resolution.value;
2909
+ this._camera.viewport.set(0, resolution.y - h, w, h);
2910
+ this._spriteUniforms.u_transform.value.set(0, resolution.y - h, w, h);
2911
+ this._spriteUniforms.u_texture.value = texture;
2912
+ this.viewer.render(null, this._sprite, this._camera, 0, 0);
2913
+ return this.viewer.canvas;
2415
2914
  }
2416
2915
  _updateFolders() {
2417
2916
  const refCntMap = this._refCntMap;
@@ -2420,8 +2919,14 @@ class Inspector extends Mount {
2420
2919
  if (refCntMap.get(v) === undefined) refCntMap.set(v, 1);
2421
2920
  };
2422
2921
  refCntMap.forEach((_, k, map)=>map.set(k, -1));
2423
- this.viewer.traverseMaterials(setState);
2424
- this.viewer.traverseComponents(setState);
2922
+ const { scene, componentManager } = this.viewer;
2923
+ scene.traverse((item)=>{
2924
+ if (item.material) {
2925
+ if (Array.isArray(item.material)) item.material.forEach(setState);
2926
+ else setState(item.material);
2927
+ }
2928
+ });
2929
+ componentManager.traverseComponents(setState);
2425
2930
  refCntMap.forEach((v, k, map)=>{
2426
2931
  if (v === 1) {
2427
2932
  this._inspect(k);
@@ -2497,12 +3002,16 @@ class Inspector extends Mount {
2497
3002
  this._addGUI(folder, this._getPropertiesList(value), target, k);
2498
3003
  }
2499
3004
  if (Array.isArray(value)) {
2500
- this._addGUI(folder, [
2501
- value.map((_, i)=>[
2502
- i.toString(),
2503
- {}
2504
- ])
2505
- ], value, k);
3005
+ if (prop.flat) {
3006
+ value.forEach((v)=>this._addGUI(folder, this._getPropertiesList(v), v, v.constructor.name));
3007
+ } else {
3008
+ this._addGUI(folder, [
3009
+ value.map((_, i)=>[
3010
+ i.toString(),
3011
+ {}
3012
+ ])
3013
+ ], value, k);
3014
+ }
2506
3015
  } else {
2507
3016
  const type = typeof value;
2508
3017
  let ins = null;
@@ -2540,11 +3049,20 @@ class Inspector extends Mount {
2540
3049
  }
2541
3050
  }
2542
3051
  constructor(...args){
2543
- super(...args);
2544
- this._refCntMap = new Map();
2545
- this._targetMap = new Map();
3052
+ super(...args), this._camera = new Camera(), this._sprite = null, this._spriteUniforms = {
3053
+ u_texture: {
3054
+ value: null
3055
+ },
3056
+ u_transform: {
3057
+ value: new Vector4()
3058
+ },
3059
+ u_resolution: {
3060
+ value: new Vector4()
3061
+ }
3062
+ }, this._refCntMap = new Map(), this._targetMap = new Map();
2546
3063
  }
2547
3064
  }
3065
+ Inspector.viewport = new Vector4();
2548
3066
  function targetName(target) {
2549
3067
  return target.name || target.constructor.name || target.type;
2550
3068
  }
@@ -2922,7 +3440,7 @@ var Stats$1 = function() {
2922
3440
  };
2923
3441
  };
2924
3442
 
2925
- class Stats extends Mount {
3443
+ class Stats extends Component {
2926
3444
  onLoad() {
2927
3445
  this._stats.addPanel(this._dcPanel);
2928
3446
  this._stats.addPanel(this._texPanel);
@@ -2943,34 +3461,9 @@ class Stats extends Mount {
2943
3461
  this._stats.update();
2944
3462
  }
2945
3463
  constructor(...args){
2946
- super(...args);
2947
- this._stats = new Stats$1();
2948
- this._dcPanel = new Panel('DC', '#ff8', '#221');
2949
- this._triPanel = new Panel("TRI", '#ff8', '#221');
2950
- this._texPanel = new Panel('TEX', '#ff8', '#221');
2951
- this._prgPanel = new Panel('PRG', '#ff8', '#221');
2952
- }
2953
- }
2954
-
2955
- class BoxProjectionHelper extends Mount {
2956
- onLoad() {
2957
- this._box3 = new Box3();
2958
- this._target = this.viewer.mount(BoxProjection);
2959
- this._helper = this.viewer.add(new Box3Helper(this._box3));
2960
- }
2961
- onEnable() {
2962
- this._helper.visible = true;
2963
- }
2964
- onDisable() {
2965
- this._helper.visible = false;
2966
- }
2967
- update(dt) {
2968
- const { center, boxMin, boxMax } = this._target;
2969
- this._box3.min.copy(center).add(boxMin);
2970
- this._box3.max.copy(center).add(boxMax);
2971
- this._helper.updateMatrixWorld();
3464
+ super(...args), this._stats = new Stats$1(), this._dcPanel = new Panel('DC', '#ff8', '#221'), this._triPanel = new Panel("TRI", '#ff8', '#221'), this._texPanel = new Panel('TEX', '#ff8', '#221'), this._prgPanel = new Panel('PRG', '#ff8', '#221');
2972
3465
  }
2973
3466
  }
2974
3467
 
2975
- export { BoxProjectionHelper, GUI, Inspector, Stats };
3468
+ export { GUI, Inspector, Stats };
2976
3469
  //# sourceMappingURL=module.js.map