├── .gitignore ├── CHANGELOG.md ├── DEVELOPMENT.md ├── LICENSE ├── README.md ├── assets ├── icon_128.png └── three-devtools-architecture.svg ├── examples ├── attributions.md ├── dynamic.html ├── large-data.html ├── materials.html ├── modules │ ├── EquirectangularToCubeGenerator.js │ ├── RGBELoader.js │ └── TextureUtils.js ├── objects.html ├── scenes.html └── textures │ ├── marble │ ├── marble_01_AO_1k.jpg │ ├── marble_01_diff_1k.jpg │ ├── marble_01_disp_1k.jpg │ ├── marble_01_nor_1k.jpg │ ├── marble_01_rough_1k.jpg │ └── marble_01_spec_1k.jpg │ └── studio_small_03_1k.hdr ├── manifest.json ├── package-lock.json ├── package.json ├── scripts ├── build-deps.sh ├── build-dist.sh ├── build-source.sh ├── genlicenses.sh └── version.js ├── src ├── app │ ├── ContentBridge.js │ ├── assets │ │ ├── chromeSelect.png │ │ ├── chromeSelect_2x.png │ │ ├── largeIcons.png │ │ ├── largeIcons_2x.png │ │ ├── mediumIcons.png │ │ ├── mediumIcons_2x.png │ │ ├── smallIcons.png │ │ └── smallIcons_2x.png │ ├── common-elements │ │ ├── AccordionViewElement.js │ │ ├── DevtoolsButtonElement.js │ │ ├── DevtoolsIconButtonElement.js │ │ ├── DevtoolsIconElement.js │ │ ├── DevtoolsMessageElement.js │ │ ├── IconElement.js │ │ ├── NumberInputElement.js │ │ └── TreeItemElement.js │ ├── constants.js │ ├── data │ │ ├── geometry.js │ │ ├── lights.js │ │ ├── material-blending.js │ │ ├── material-common.js │ │ ├── material-line.js │ │ ├── material-pbr.js │ │ ├── material-polygon-offset.js │ │ ├── material-shared.js │ │ ├── materials.js │ │ ├── object-renderable.js │ │ ├── object-transform.js │ │ ├── objects.js │ │ ├── renderers.js │ │ └── textures.js │ ├── elements │ │ ├── AppElement.js │ │ ├── ImagePreviewElement.js │ │ ├── ParametersViewElement.js │ │ ├── RendererViewElement.js │ │ ├── ResourcesViewElement.js │ │ ├── SceneViewElement.js │ │ ├── TabBarElement.js │ │ ├── TitleBarElement.js │ │ ├── shared-styles │ │ │ └── chrome-select.js │ │ └── values │ │ │ ├── EnumValueElement.js │ │ │ ├── KeyValueElement.js │ │ │ ├── MaterialValueElement.js │ │ │ └── TextureValueElement.js │ ├── fonts │ │ ├── fa-regular-400.eot │ │ ├── fa-regular-400.svg │ │ ├── fa-regular-400.ttf │ │ ├── fa-regular-400.woff │ │ ├── fa-regular-400.woff2 │ │ ├── fa-solid-900.eot │ │ ├── fa-solid-900.svg │ │ ├── fa-solid-900.ttf │ │ ├── fa-solid-900.woff │ │ └── fa-solid-900.woff2 │ ├── index.html │ ├── index.js │ ├── injection.js │ ├── styles │ │ ├── app.css │ │ ├── devtools.css │ │ ├── fontawesome.css │ │ └── inspectorCommon.css │ └── utils.js ├── content │ ├── DevToolsScene.js │ ├── EntityCache.js │ ├── ThreeDevTools.js │ ├── TransformControls.js │ ├── three.js │ ├── toJSON.js │ └── utils.js └── extension │ ├── background.html │ ├── background.js │ ├── contentScript.js │ ├── devtools.html │ └── devtools.js └── web_modules ├── @egjs └── agent.js ├── common ├── lit-html-513f48c9.js └── lit-html-d6553ed1.js ├── import-map.json ├── licenses ├── agent_LICENSE ├── lit-element_LICENSE ├── lit-html_LICENSE └── three_LICENSE ├── lit-element.js ├── lit-html └── directives │ └── if-defined.js ├── three.js ├── three ├── examples │ └── jsm │ │ ├── geometries │ │ └── TeapotBufferGeometry.js │ │ ├── loaders │ │ └── GLTFLoader.js │ │ └── pmrem │ │ ├── PMREMCubeUVPacker.js │ │ └── PMREMGenerator.js └── src │ └── constants.js └── webextension-polyfill └── dist └── browser-polyfill.js /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | **/*.sw* 4 | .DS_Store 5 | .idea 6 | *.iml 7 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 0.4.0 (2020/7/27) 2 | 3 | ### New 4 | 5 | * Added inspector view in the rendering panel to debug WebGLRenderers. 6 | 7 | ### Changes 8 | 9 | * Improve styling for the rendering panel. 10 | * Add parameters for lights. 11 | * Fixed int and radian parameters in the parameters view. 12 | * Parameters view will now display readonly parameters. 13 | * Added and updated parameters for many material types. 14 | * Added tooltips to properties in parameters view. 15 | * InstancedBufferGeometry now correctly identified as Geometry. 16 | * SkinnedMesh and InstancedMesh now correctly identified as Mesh. 17 | * Inspecting a CompressedTexture should no longer throw an error. 18 | 19 | ## 0.3.1 20 | 21 | * Fix the setting of boolean properties from an entity's parameter view. 22 | 23 | ## 0.3.0 24 | 25 | * Add several views to filter entities: Scene graph, geometry, materials, textures, and renderer. 26 | * Improved heuristics on displaying all available entities in overviews, rather than needing to refresh often. 27 | * Significant improvement to scenes with large assets/data (textures, geometry). 28 | * Entity dependencies are now shown as links in the parameter view -- select a Mesh's material, or geometry, for example. 29 | * Add ability to modify vectors in the parameters view, like position and scale. 30 | 31 | ## 0.2.1 32 | 33 | * Rejoice! Extensions now support [major.minor.patch] format in versions. 34 | * Display three.js revision found in content to console 35 | * Display correct extension version in console 36 | 37 | ## 0.2 38 | 39 | * Refresh button to get latest changes of a scene 40 | * Fixed issues in debugging scenes with unserializable objects (InterleavedBufferAttribute, WebGLRenderTarget, DataTexture, ParametricGeometry) 41 | * Style improvements (@jacobcoughenour) 42 | * Dark theme support (@jacobcoughenour) 43 | 44 | ## 0.1 45 | 46 | Initial Release 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Jordan Santell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # three-devtools 2 | 3 | **three-devtools** is a web extension that allows inspection of three.js content. 4 | 5 | > ## 🚨 Status: Experimental 6 | > **three-devtools** is very much in an alpha/experimentation stage. Use at your own risk. Follow the [Baseline Milestone](https://github.com/jsantell/three-devtools/milestone/1) for issues and considerations that need to be solved in order for some stability. 7 | 8 | ## Installing 9 | 10 | The alpha version of the developer tools can be installed as a web extension on [Firefox Add-ons/AMO](https://addons.mozilla.org/en-US/firefox/addon/three-js-developer-tools/) and for Chrome via [Chrome Web Store](https://chrome.google.com/webstore/detail/threejs-developer-tools/ebpnegggocnnhleeicgljbedjkganaek). See [DEVELOPMENT.md](DEVELOPMENT.md) for local installation. 11 | 12 | ## Current API 13 | 14 | This API has not been thought out at all, but this will register your 15 | THREE.Scene and THREE.Renderer to be observed by the tools. 16 | 17 | ```js 18 | // Observe a scene or a renderer 19 | if (typeof __THREE_DEVTOOLS__ !== 'undefined') { 20 | __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent('observe', { detail: scene })); 21 | __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent('observe', { detail: renderer })); 22 | } 23 | ``` 24 | 25 | ## Development 26 | 27 | Architecture & development notes can be found in [DEVELOPMENT.md](DEVELOPMENT.md). 28 | -------------------------------------------------------------------------------- /assets/icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/assets/icon_128.png -------------------------------------------------------------------------------- /examples/attributions.md: -------------------------------------------------------------------------------- 1 | textures/studio_small_03_1k.hdr 2 | https://hdrihaven.com/hdri/download.php?h=studio_small_03 3 | 4 | textures/marble/* 5 | https://texturehaven.com/tex/?t=marble_01 6 | -------------------------------------------------------------------------------- /examples/dynamic.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /examples/large-data.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/materials.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /examples/modules/EquirectangularToCubeGenerator.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Richard M. / https://github.com/richardmonette 3 | * @author WestLangley / http://github.com/WestLangley 4 | */ 5 | import * as THREE from '../../web_modules/three.js'; 6 | 7 | const CubemapGenerator = function ( renderer ) { 8 | 9 | this.renderer = renderer; 10 | 11 | }; 12 | 13 | CubemapGenerator.prototype.fromEquirectangular = function ( texture, options ) { 14 | 15 | options = options || {}; 16 | 17 | var scene = new THREE.Scene(); 18 | 19 | var shader = { 20 | 21 | uniforms: { 22 | tEquirect: { value: null }, 23 | }, 24 | 25 | vertexShader: 26 | 27 | ` 28 | varying vec3 vWorldDirection; 29 | 30 | //include 31 | vec3 transformDirection( in vec3 dir, in mat4 matrix ) { 32 | 33 | return normalize( ( matrix * vec4( dir, 0.0 ) ).xyz ); 34 | 35 | } 36 | 37 | void main() { 38 | 39 | vWorldDirection = transformDirection( position, modelMatrix ); 40 | 41 | #include 42 | #include 43 | 44 | } 45 | `, 46 | 47 | fragmentShader: 48 | 49 | ` 50 | uniform sampler2D tEquirect; 51 | 52 | varying vec3 vWorldDirection; 53 | 54 | //include 55 | #define RECIPROCAL_PI 0.31830988618 56 | #define RECIPROCAL_PI2 0.15915494 57 | 58 | void main() { 59 | 60 | vec3 direction = normalize( vWorldDirection ); 61 | 62 | vec2 sampleUV; 63 | 64 | sampleUV.y = asin( clamp( direction.y, - 1.0, 1.0 ) ) * RECIPROCAL_PI + 0.5; 65 | 66 | sampleUV.x = atan( direction.z, direction.x ) * RECIPROCAL_PI2 + 0.5; 67 | 68 | gl_FragColor = texture2D( tEquirect, sampleUV ); 69 | 70 | } 71 | ` 72 | }; 73 | 74 | var material = new THREE.ShaderMaterial( { 75 | 76 | type: 'CubemapFromEquirect', 77 | 78 | uniforms: THREE.UniformsUtils.clone( shader.uniforms ), 79 | vertexShader: shader.vertexShader, 80 | fragmentShader: shader.fragmentShader, 81 | side: THREE.BackSide, 82 | blending: THREE.NoBlending 83 | 84 | } ); 85 | 86 | material.uniforms.tEquirect.value = texture; 87 | 88 | var mesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 5, 5, 5 ), material ); 89 | 90 | scene.add( mesh ); 91 | 92 | var resolution = options.resolution || 512; 93 | 94 | var params = { 95 | type: texture.type, 96 | format: texture.format, 97 | encoding: texture.encoding, 98 | generateMipmaps: ( options.generateMipmaps !== undefined ) ? options.generateMipmaps : texture.generateMipmaps, 99 | minFilter: ( options.minFilter !== undefined ) ? options.minFilter : texture.minFilter, 100 | magFilter: ( options.magFilter !== undefined ) ? options.magFilter : texture.magFilter 101 | }; 102 | 103 | var camera = new THREE.CubeCamera( 1, 10, resolution, params ); 104 | 105 | camera.update( this.renderer, scene ); 106 | 107 | mesh.geometry.dispose(); 108 | mesh.material.dispose(); 109 | 110 | return camera.renderTarget; 111 | 112 | }; 113 | 114 | // 115 | 116 | export default ( function () { 117 | 118 | var camera = new THREE.PerspectiveCamera( 90, 1, 0.1, 10 ); 119 | var scene = new THREE.Scene(); 120 | var boxMesh = new THREE.Mesh( new THREE.BoxBufferGeometry( 1, 1, 1 ), getShader() ); 121 | boxMesh.material.side = THREE.BackSide; 122 | scene.add( boxMesh ); 123 | 124 | var EquirectangularToCubeGenerator = function ( sourceTexture, options ) { 125 | 126 | options = options || {}; 127 | 128 | this.sourceTexture = sourceTexture; 129 | this.resolution = options.resolution || 512; 130 | 131 | this.views = [ 132 | { t: [ 1, 0, 0 ], u: [ 0, - 1, 0 ] }, 133 | { t: [ - 1, 0, 0 ], u: [ 0, - 1, 0 ] }, 134 | { t: [ 0, 1, 0 ], u: [ 0, 0, 1 ] }, 135 | { t: [ 0, - 1, 0 ], u: [ 0, 0, - 1 ] }, 136 | { t: [ 0, 0, 1 ], u: [ 0, - 1, 0 ] }, 137 | { t: [ 0, 0, - 1 ], u: [ 0, - 1, 0 ] }, 138 | ]; 139 | 140 | var params = { 141 | format: options.format || this.sourceTexture.format, 142 | magFilter: this.sourceTexture.magFilter, 143 | minFilter: this.sourceTexture.minFilter, 144 | type: options.type || this.sourceTexture.type, 145 | generateMipmaps: this.sourceTexture.generateMipmaps, 146 | anisotropy: this.sourceTexture.anisotropy, 147 | encoding: this.sourceTexture.encoding 148 | }; 149 | 150 | this.renderTarget = new THREE.WebGLRenderTargetCube( this.resolution, this.resolution, params ); 151 | 152 | }; 153 | 154 | EquirectangularToCubeGenerator.prototype = { 155 | 156 | constructor: EquirectangularToCubeGenerator, 157 | 158 | update: function ( renderer ) { 159 | 160 | var currentRenderTarget = renderer.getRenderTarget(); 161 | 162 | boxMesh.material.uniforms.equirectangularMap.value = this.sourceTexture; 163 | 164 | for ( var i = 0; i < 6; i ++ ) { 165 | 166 | var v = this.views[ i ]; 167 | 168 | camera.position.set( 0, 0, 0 ); 169 | camera.up.set( v.u[ 0 ], v.u[ 1 ], v.u[ 2 ] ); 170 | camera.lookAt( v.t[ 0 ], v.t[ 1 ], v.t[ 2 ] ); 171 | 172 | renderer.setRenderTarget( this.renderTarget, i ); 173 | renderer.clear(); 174 | renderer.render( scene, camera ); 175 | 176 | } 177 | 178 | renderer.setRenderTarget( currentRenderTarget ); 179 | 180 | return this.renderTarget.texture; 181 | 182 | }, 183 | 184 | dispose: function () { 185 | 186 | this.renderTarget.dispose(); 187 | 188 | } 189 | 190 | }; 191 | 192 | function getShader() { 193 | 194 | var shaderMaterial = new THREE.ShaderMaterial( { 195 | 196 | uniforms: { 197 | "equirectangularMap": { value: null }, 198 | }, 199 | 200 | vertexShader: 201 | "varying vec3 localPosition;\n\ 202 | \n\ 203 | void main() {\n\ 204 | localPosition = position;\n\ 205 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\n\ 206 | }", 207 | 208 | fragmentShader: 209 | "#include \n\ 210 | varying vec3 localPosition;\n\ 211 | uniform sampler2D equirectangularMap;\n\ 212 | \n\ 213 | vec2 EquirectangularSampleUV(vec3 v) {\n\ 214 | vec2 uv = vec2(atan(v.z, v.x), asin(v.y));\n\ 215 | uv *= vec2(0.1591, 0.3183); // inverse atan\n\ 216 | uv += 0.5;\n\ 217 | return uv;\n\ 218 | }\n\ 219 | \n\ 220 | void main() {\n\ 221 | vec2 uv = EquirectangularSampleUV(normalize(localPosition));\n\ 222 | gl_FragColor = texture2D(equirectangularMap, uv);\n\ 223 | }", 224 | 225 | blending: THREE.NoBlending 226 | 227 | } ); 228 | 229 | shaderMaterial.type = 'EquirectangularToCubeGenerator'; 230 | 231 | return shaderMaterial; 232 | 233 | } 234 | 235 | return EquirectangularToCubeGenerator; 236 | 237 | } )(); 238 | -------------------------------------------------------------------------------- /examples/modules/TextureUtils.js: -------------------------------------------------------------------------------- 1 | import {Cache, CubeTexture, EventDispatcher, GammaEncoding, NearestFilter, RGBEEncoding, TextureLoader} from '../../web_modules/three.js'; 2 | 3 | import EquirectangularToCubeGenerator from './EquirectangularToCubeGenerator.js'; 4 | import { PMREMCubeUVPacker } from '../../web_modules/three/examples/jsm/pmrem/PMREMCubeUVPacker.js'; 5 | import { PMREMGenerator } from '../../web_modules/three/examples/jsm/pmrem/PMREMGenerator.js'; 6 | import RGBELoader from './RGBELoader.js'; 7 | 8 | const loader = new RGBELoader(); 9 | 10 | export default (async (renderer, url) => { 11 | const texture = await new Promise(resolve => loader.load(url, resolve)); 12 | texture.encoding = RGBEEncoding; 13 | texture.minFilter = NearestFilter; 14 | texture.magFilter = NearestFilter; 15 | texture.flipY = true; 16 | 17 | const cubemapGenerator = new EquirectangularToCubeGenerator(texture, { 18 | resolution: 1024, 19 | }); 20 | const cubemapTexture = cubemapGenerator.update(renderer); 21 | const pmremGenerator = new PMREMGenerator(cubemapTexture); 22 | pmremGenerator.update(renderer); 23 | const pmremPacker = new PMREMCubeUVPacker(pmremGenerator.cubeLods); 24 | pmremPacker.update(renderer); 25 | 26 | texture.dispose(); 27 | pmremGenerator.dispose(); 28 | pmremPacker.dispose(); 29 | 30 | return pmremPacker.CubeUVRenderTarget.texture; 31 | }); 32 | -------------------------------------------------------------------------------- /examples/objects.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /examples/scenes.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /examples/textures/marble/marble_01_AO_1k.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/examples/textures/marble/marble_01_AO_1k.jpg -------------------------------------------------------------------------------- /examples/textures/marble/marble_01_diff_1k.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/examples/textures/marble/marble_01_diff_1k.jpg -------------------------------------------------------------------------------- /examples/textures/marble/marble_01_disp_1k.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/examples/textures/marble/marble_01_disp_1k.jpg -------------------------------------------------------------------------------- /examples/textures/marble/marble_01_nor_1k.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/examples/textures/marble/marble_01_nor_1k.jpg -------------------------------------------------------------------------------- /examples/textures/marble/marble_01_rough_1k.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/examples/textures/marble/marble_01_rough_1k.jpg -------------------------------------------------------------------------------- /examples/textures/marble/marble_01_spec_1k.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/examples/textures/marble/marble_01_spec_1k.jpg -------------------------------------------------------------------------------- /examples/textures/studio_small_03_1k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/examples/textures/studio_small_03_1k.hdr -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Three.js Developer Tools", 3 | "version": "0.4.0", 4 | "description": "Developer tools for 3D library three.js.", 5 | "devtools_page": "src/extension/devtools.html", 6 | "background": { 7 | "page": "src/extension/background.html" 8 | }, 9 | "icons": { 10 | "128": "assets/icon_128.png" 11 | }, 12 | "content_scripts": [ 13 | { 14 | "matches": [ 15 | "http://*/*", 16 | "https://*/*" 17 | ], 18 | "js": [ 19 | "src/extension/contentScript.js" 20 | ], 21 | "run_at": "document_start" 22 | } 23 | ], 24 | "permissions": [ 25 | "storage", 26 | "webNavigation", 27 | "http://*/*", 28 | "https://*/*" 29 | ], 30 | "web_accessible_resources": [ 31 | "src/content/*.js" 32 | ], 33 | "manifest_version": 2, 34 | "author": "Jordan Santell", 35 | "browser_specific_settings": { 36 | "gecko": { 37 | "id": "three-devtools@jsantell.com" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "three-devtools", 3 | "private": true, 4 | "version": "0.4.0", 5 | "description": "three.js developer tools web extension", 6 | "scripts": { 7 | "build:source": "./scripts/build-source.sh", 8 | "build:deps": "./scripts/build-deps.sh", 9 | "build:dist": "./scripts/build-dist.sh", 10 | "build:dist:chrome": "./scripts/build-dist.sh chrome", 11 | "serve": "http-server . -c-1", 12 | "version": "node ./scripts/version.js && git add *.json", 13 | "postversion": "git push && git push --tags" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/jsantell/three-devtools.git" 18 | }, 19 | "author": "Jordan Santell ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/jsantell/three-devtools/issues" 23 | }, 24 | "homepage": "https://github.com/jsantell/three-devtools#readme", 25 | "dependencies": { 26 | "@egjs/agent": "2.1.5", 27 | "lit-element": "2.2.1", 28 | "lit-html": "1.1.2", 29 | "three": "0.137.0", 30 | "webextension-polyfill": "0.4.0" 31 | }, 32 | "devDependencies": { 33 | "http-server": "^14.1.1", 34 | "json": "10.0.0" 35 | }, 36 | "@pika/web": { 37 | "webDependencies": [ 38 | "webextension-polyfill/dist/browser-polyfill.js", 39 | "lit-element", 40 | "lit-html/directives/if-defined.js", 41 | "@egjs/agent", 42 | "three", 43 | "three/src/constants.js", 44 | "three/examples/jsm/loaders/GLTFLoader.js", 45 | "three/examples/jsm/geometries/TeapotBufferGeometry.js" 46 | ] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /scripts/build-deps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | npm install 4 | npx @pika/web --dest web_modules/ 5 | ./scripts/genlicenses.sh 6 | 7 | # Copy over the non-module form of three.js for injection 8 | # in the content. This beautiful hack fakes a CommonJS 9 | # environment so that: 10 | # 1) No global scope pollution or clobbering content's version of three.js 11 | # 2) Can be stringified and lazily injected along with the other content 12 | # side objects (src/content/). Need access to the full context in order 13 | # for Function.prototype.toString to work. 14 | # 3) Content-side devtools code can use its own private instance of three.js 15 | # to do things like injecting a bounding box visual. 16 | echo 'export default () => {' > src/content/three.js 17 | echo ' var exports = {};' >> src/content/three.js 18 | echo ' var module = { exports };' >> src/content/three.js 19 | cat node_modules/three/build/three.min.js >> src/content/three.js 20 | echo ' return exports;' >> src/content/three.js 21 | echo '};' >> src/content/three.js 22 | 23 | # Replace all instances of __THREE_DEVTOOLS__ in 24 | # the internal three.js so we're not recursively 25 | # complicating things, for example, we get a 'registration' 26 | # event from the internal version and can't determine 27 | # whether its the internal version, or a content version 28 | # that should be debugged. 29 | sed -i -e 's/__THREE_DEVTOOLS__/__INTERNAL_THREE_DEVTOOLS__/g' src/content/three.js 30 | 31 | # Copy over the non-modified version of TransformControls 32 | echo 'export default (THREE) => {' > src/content/TransformControls.js 33 | cat node_modules/three/examples/js/controls/TransformControls.js >> src/content/TransformControls.js 34 | echo '};' >> src/content/TransformControls.js 35 | -------------------------------------------------------------------------------- /scripts/build-dist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | rm -rf three-devtools 4 | mkdir three-devtools 5 | 6 | cp -r src three-devtools 7 | cp -r web_modules three-devtools 8 | cp -r assets three-devtools 9 | cp package.json three-devtools 10 | cp manifest.json three-devtools 11 | cp README.md three-devtools 12 | cp LICENSE three-devtools 13 | 14 | if [ ! -z "$1" ] && [ $1 == 'chrome' ] ; 15 | then 16 | echo "Building Chrome variation." 17 | cat manifest.json | \ 18 | json -e 'delete this.browser_specific_settings' > \ 19 | three-devtools/manifest.json 20 | fi 21 | 22 | npx web-ext build \ 23 | --source-dir three-devtools \ 24 | --artifacts-dir dist \ 25 | --overwrite-dest 26 | 27 | rm -r three-devtools 28 | -------------------------------------------------------------------------------- /scripts/build-source.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Zips up the necessary files and docs, sans dependencies from 4 | # npm, to build the add-on, necessary for AMO's reviewal of code 5 | # since @pika/web transforms the source code. 6 | 7 | zip -r dist/three-devtools-source.zip \ 8 | assets examples src scripts web_modules \ 9 | manifest.json package.json package-lock.json \ 10 | LICENSE DEVELOPMENT.md README.md 11 | -------------------------------------------------------------------------------- /scripts/genlicenses.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | mkdir -p web_modules/licenses 4 | cp node_modules/@egjs/agent/LICENSE web_modules/licenses/agent_LICENSE 5 | cp node_modules/three/LICENSE web_modules/licenses/three_LICENSE 6 | cp node_modules/lit-html/LICENSE web_modules/licenses/lit-html_LICENSE 7 | cp node_modules/lit-element/LICENSE web_modules/licenses/lit-element_LICENSE 8 | -------------------------------------------------------------------------------- /scripts/version.js: -------------------------------------------------------------------------------- 1 | #!/bin/node 2 | 3 | // Called after the version as been incremented 4 | // via `npm version [major | minor | patch]` 5 | // https://docs.npmjs.com/cli/version 6 | const fs = require('fs'); 7 | const path = require('path'); 8 | const packageJSON = require('../package.json'); 9 | const version = packageJSON.version; 10 | const manifestJSON = require('../manifest.json'); 11 | 12 | manifestJSON.version = version; 13 | 14 | const manifestJSONPath = path.join(__dirname, '..', 'manifest.json'); 15 | const manifestString = JSON.stringify(manifestJSON, null, 2); 16 | console.log(`Updating manifest.json with version ${version}`); 17 | fs.writeFileSync(manifestJSONPath, manifestString); 18 | -------------------------------------------------------------------------------- /src/app/ContentBridge.js: -------------------------------------------------------------------------------- 1 | import injection from './injection.js'; 2 | import { isUUID } from './utils.js'; 3 | const $db = Symbol('db'); 4 | const $sceneGraphs = Symbol('sceneGraphs'); 5 | const $overviews = Symbol('overviews'); 6 | const $update = Symbol('update'); 7 | const $onMessage = Symbol('onMessage'); 8 | const $log = Symbol('log'); 9 | const $eval = Symbol('eval'); 10 | const $dispatchToContent = Symbol('dispatchToContent'); 11 | const $renderers = Symbol('renderers'); 12 | const $renderingInfo = Symbol('renderingInfo'); 13 | 14 | const VERBOSE_CONTENT_BRIDGE = false; 15 | 16 | export default class ContentBridge extends EventTarget { 17 | /** 18 | * Events: 19 | * 'load' 20 | * 'entity-update' { entity, uuid } 21 | * 'scene-graph-update' { uuid, graph } 22 | * 'overview-update' { type, entities } 23 | * 'rendering-info-update' {} 24 | * 'renderer-update' { renderer, id } 25 | */ 26 | constructor() { 27 | super(); 28 | 29 | this[$db] = new Map(); 30 | this[$overviews] = new Map(); 31 | this[$sceneGraphs] = new Map(); 32 | this[$renderers] = new Map(); 33 | this[$renderingInfo] = new Map(); 34 | 35 | this.port = browser.runtime.connect({ 36 | name: 'three-devtools', 37 | }); 38 | 39 | // Notify background port that the tools panel is open 40 | this.port.postMessage({ 41 | name: 'connect', 42 | tabId: browser.devtools.inspectedWindow.tabId, 43 | }); 44 | 45 | this.port.onDisconnect.addListener(request => { 46 | console.error('disconnected from background', request); 47 | }); 48 | 49 | this.port.onMessage.addListener(e => this[$onMessage](e)); 50 | 51 | document.addEventListener('keydown', e => { 52 | let mode, space; 53 | switch (e.key) { 54 | case 'q': space = true; break; 55 | case 'w': mode = 'translate'; break; 56 | case 'e': mode = 'rotate'; break; 57 | case 'r': mode = 'scale'; break; 58 | } 59 | 60 | if (mode || space) { 61 | this[$dispatchToContent]('_transform-controls-update', { mode, space }); 62 | } 63 | }, { passive: true }) 64 | } 65 | 66 | reload() { 67 | browser.devtools.inspectedWindow.reload(); 68 | } 69 | 70 | getEntity(uuid) { 71 | return /renderer/.test(uuid) ? this[$renderers].get(uuid) : this[$db].get(uuid); 72 | } 73 | 74 | getEntityAndDependencies(rootUUID) { 75 | const data = {}; 76 | const uuids = [rootUUID]; 77 | while (uuids.length) { 78 | const uuid = uuids.shift(); 79 | const entity = this.getEntity(uuid); 80 | // In renderers case, uuid is an id. 81 | // @TODO should probably abstract away the differences 82 | // between UUID and synthesized renderer ids. 83 | if (entity && !data[uuid]) { 84 | data[uuid] = entity; 85 | 86 | // entities can have several dependencies, like textures on materials, 87 | // or a Mesh's geometry 88 | for (let value of Object.values(entity)) { 89 | if (isUUID(value)) { 90 | uuids.push(value); 91 | } 92 | } 93 | } 94 | } 95 | 96 | return data; 97 | } 98 | 99 | getRenderingInfo(uuid) { 100 | return this[$renderingInfo].get(uuid); 101 | } 102 | 103 | getResourcesOverview(type) { 104 | return this[$overviews].get(type); 105 | } 106 | 107 | getSceneGraph(uuid) { 108 | return this[$sceneGraphs].get(uuid); 109 | } 110 | 111 | updateProperty(uuid, property, value, dataType) { 112 | const object = this.getEntity(uuid); 113 | this[$dispatchToContent]('entity-update', { 114 | uuid, 115 | property, 116 | value, 117 | dataType, 118 | }); 119 | 120 | // Updating property won't trigger a data flush, instead update 121 | // the local state so that elements' values are in sync with their 122 | // HTML input state, important when switching between different items 123 | // with LitElement. 124 | 125 | object[property] = value; 126 | this[$update](object); 127 | } 128 | 129 | /** 130 | * Request latest data from content for the object 131 | * with UUID. 132 | */ 133 | requestEntity(uuid) { 134 | this[$dispatchToContent]('_request-entity', { uuid }); 135 | } 136 | 137 | requestOverview(type) { 138 | this[$dispatchToContent]('_request-overview', { type }); 139 | } 140 | 141 | requestSceneGraph(uuid) { 142 | this[$dispatchToContent]('_request-scene-graph', { uuid }); 143 | } 144 | 145 | requestRenderingInfo(uuid) { 146 | this[$dispatchToContent]('_request-rendering-info', { uuid }); 147 | } 148 | 149 | select(uuid) { 150 | if (!uuid) { 151 | return; 152 | } 153 | this[$dispatchToContent]('select', { uuid }); 154 | } 155 | 156 | [$onMessage](request) { 157 | const { id, type, data } = request; 158 | 159 | this[$log]('>>', type, data); 160 | switch (type) { 161 | case 'error': 162 | this.dispatchEvent(new CustomEvent('error', { 163 | detail: data, 164 | })); 165 | break; 166 | case 'register': 167 | this.revision = data.revision; 168 | this[$eval](`console.log("three-devtools: debugging three.js r${this.revision}")`); 169 | break; 170 | case 'committed': 171 | this[$db].clear(); 172 | this[$overviews].clear(); 173 | this[$sceneGraphs].clear(); 174 | this[$renderers].clear(); 175 | this[$renderingInfo].clear(); 176 | 177 | this[$eval](injection); 178 | this.dispatchEvent(new CustomEvent('load')); 179 | break; 180 | case 'observe': 181 | this.dispatchEvent(new CustomEvent('observe', { 182 | detail: { 183 | uuids: data.uuids, 184 | }, 185 | })); 186 | break; 187 | case 'scene-graph': 188 | this[$sceneGraphs].set(data.uuid, data.graph); 189 | this.dispatchEvent(new CustomEvent('scene-graph-update', { 190 | detail: { 191 | uuid: data.uuid, 192 | graph: data.graph, 193 | }, 194 | })); 195 | break; 196 | case 'overview': 197 | this[$overviews].set(data.type, data.entities); 198 | this.dispatchEvent(new CustomEvent('overview-update', { 199 | detail: { 200 | type: data.type, 201 | entities: data.entities, 202 | }, 203 | })); 204 | break; 205 | case 'rendering-info': 206 | this.dispatchEvent(new CustomEvent('rendering-info-update', { 207 | detail: data, 208 | })); 209 | this[$renderingInfo].set(data.uuid, data); 210 | break; 211 | case 'entity': 212 | if (data.type === 'renderer') { 213 | this[$renderers].set(data.uuid, data); 214 | this.dispatchEvent(new CustomEvent('renderer-update', { 215 | detail: { 216 | renderer: data, 217 | uuid: data.uuid, 218 | }, 219 | })); 220 | } else if (Array.isArray(data)) { 221 | for (let entity of data) { 222 | this[$update](entity); 223 | } 224 | } 225 | break; 226 | } 227 | } 228 | 229 | [$update](entity) { 230 | this[$db].set(entity.uuid, entity); 231 | this.dispatchEvent(new CustomEvent('entity-update', { 232 | detail: { 233 | entity, 234 | uuid: entity.uuid, 235 | }, 236 | })); 237 | } 238 | 239 | [$dispatchToContent](type, detail) { 240 | this[$eval](` 241 | __THREE_DEVTOOLS__.dispatchEvent(new CustomEvent('${type}', { 242 | detail: ${JSON.stringify(detail)}, 243 | }));` 244 | ); 245 | } 246 | 247 | _contentLog(string) { 248 | this[$eval](`console.log("${string}")`); 249 | } 250 | 251 | async [$eval](string) { 252 | this[$log]('EVAL', string); 253 | const [result, error] = await browser.devtools.inspectedWindow.eval(string); 254 | if (error) { 255 | console.warn(error); 256 | } 257 | return result; 258 | } 259 | 260 | [$log](...message) { 261 | if (VERBOSE_CONTENT_BRIDGE) { 262 | console.log('ContentBridge:', ...message); 263 | } 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /src/app/assets/chromeSelect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/assets/chromeSelect.png -------------------------------------------------------------------------------- /src/app/assets/chromeSelect_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/assets/chromeSelect_2x.png -------------------------------------------------------------------------------- /src/app/assets/largeIcons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/assets/largeIcons.png -------------------------------------------------------------------------------- /src/app/assets/largeIcons_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/assets/largeIcons_2x.png -------------------------------------------------------------------------------- /src/app/assets/mediumIcons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/assets/mediumIcons.png -------------------------------------------------------------------------------- /src/app/assets/mediumIcons_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/assets/mediumIcons_2x.png -------------------------------------------------------------------------------- /src/app/assets/smallIcons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/assets/smallIcons.png -------------------------------------------------------------------------------- /src/app/assets/smallIcons_2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/assets/smallIcons_2x.png -------------------------------------------------------------------------------- /src/app/common-elements/AccordionViewElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../web_modules/lit-element.js' 2 | 3 | const $onVisibilityToggle = Symbol('onVisibilityToggle'); 4 | const $onClick = Symbol('onClick'); 5 | 6 | export default class AccordionViewElement extends LitElement { 7 | static get properties() { 8 | return { 9 | open: {type: Boolean, reflect: true}, 10 | } 11 | } 12 | 13 | render() { 14 | return html` 15 | 90 |
92 | 93 | 94 |
95 | 96 | 97 | `; 98 | } 99 | 100 | [$onVisibilityToggle](e) { 101 | e.stopPropagation(); 102 | this.open = !this.open; 103 | } 104 | 105 | [$onClick](e) { 106 | e.stopPropagation(); 107 | this.open = !this.open; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/app/common-elements/DevtoolsButtonElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../web_modules/lit-element.js' 2 | 3 | export default class DevtoolsButtonElement extends LitElement { 4 | render() { 5 | return html` 6 | 21 | 22 | `; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/app/common-elements/DevtoolsIconButtonElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../web_modules/lit-element.js' 2 | 3 | export default class DevtoolsIconButtonElement extends LitElement { 4 | static get properties() { 5 | return { 6 | "icon": { type: String, reflect: true } 7 | } 8 | } 9 | 10 | render() { 11 | return html` 12 | 36 | 39 | `; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/app/common-elements/DevtoolsIconElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../web_modules/lit-element.js' 2 | 3 | const icons = { 4 | "close": { sheet: "large", position: "-84px 0px" }, 5 | "visibility": { sheet: "large", position: "28px 0px" }, 6 | "add": { sheet: "large", position: "0px -24px" }, 7 | "check": { sheet: "large", position: "-54px -24px" }, 8 | "shadow": { sheet: "large", position: "0px -48px" }, 9 | "camera": { sheet: "large", position: "-28px -48px" }, 10 | "settings": { sheet: "large", position: "56px -48px" }, 11 | "undo": { sheet: "large", position: "28px -48px" }, 12 | "edit": { sheet: "large", position: "0px -96px" }, 13 | "filter": { sheet: "large", position: "-56px -96px" }, 14 | "menu": { sheet: "large", position: "-56px -120px" }, 15 | "search": { sheet: "large", position: "28px -120px" }, 16 | "refresh": { sheet: "large", position: "-84px 48px" }, 17 | }; 18 | 19 | const sizes = { 20 | "large": "width: 28px; height: 24px" 21 | } 22 | 23 | export default class DevtoolsIconElement extends LitElement { 24 | static get properties() { 25 | return { 26 | "icon": { type: String, reflect: true } 27 | } 28 | } 29 | 30 | render() { 31 | const icon = icons[this.icon || "refresh"]; 32 | return html` 33 | 45 | `; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/app/common-elements/DevtoolsMessageElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../web_modules/lit-element.js' 2 | 3 | export default class DevtoolsMessageElement extends LitElement { 4 | render() { 5 | return html` 6 | 25 |
26 | 27 |
28 | `; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/app/common-elements/IconElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, svg, html } from '../../../web_modules/lit-element.js' 2 | 3 | // cube 4 | // cubes 5 | // flask (experiments) 6 | // globe-africa, globe-americas, globe-asia, globe-europe (scene graph?) 7 | // sitemap (scene graph?) 8 | // sliders-h 9 | // vr-cardboard 10 | // dice-d20, dice-d6 11 | // sync 12 | // magic 13 | // lightbulb (lights) 14 | // search 15 | // box/box-open (grouping?) 16 | // brush (material?) 17 | // chess-board (textures) 18 | // eye (visibility) 19 | export default class IconElement extends LitElement { 20 | static get properties() { 21 | return { 22 | "icon": { type: String, }, 23 | "fill": { type: Boolean, } 24 | } 25 | } 26 | 27 | render() { 28 | const iconName = `${this.icon}${this.fill ? '' : '-outline'}`; 29 | return html` 30 | 31 | 45 | 46 | `; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/app/common-elements/NumberInputElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../web_modules/lit-element.js'; 2 | //import { LitElement, html } from '../examples/web_modules/lit-element.js' 3 | 4 | const $onActivate = Symbol('onActivate'); 5 | 6 | /** 7 | * A LitElement form of A-Frame Inspector's number widget 8 | * https://github.com/aframevr/aframe-inspector/blob/master/src/components/widgets/NumberWidget.js 9 | */ 10 | class NumberInputElement extends LitElement { 11 | static get properties() { 12 | return { 13 | min: { type: Number }, 14 | max: { type: Number }, 15 | step: { type: Number }, 16 | value: { type: Number }, 17 | precision: { type: Number }, 18 | } 19 | } 20 | 21 | constructor() { 22 | super(); 23 | this.min = -Infinity; 24 | this.max = Infinity; 25 | this.value = 0; 26 | this.precision = 3; 27 | this.step = 1; 28 | 29 | this.onMouseMove = this.onMouseMove.bind(this); 30 | this.onMouseUp = this.onMouseUp.bind(this); 31 | }; 32 | 33 | firstUpdated() { 34 | this.distance = 0; 35 | this.onMouseDownValue = 0; 36 | this.prevPointer = [0, 0]; 37 | this.$input = this.shadowRoot.querySelector('input'); 38 | this.setValue(this.value); 39 | this.onBlur(); 40 | } 41 | 42 | onMouseMove(event) { 43 | const currentValue = parseFloat(this.value); 44 | const pointer = [event.clientX, event.clientY]; 45 | const delta = 46 | pointer[0] - this.prevPointer[0] - (pointer[1] - this.prevPointer[1]); 47 | this.distance += delta; 48 | 49 | // Add minimum tolerance to reduce unintentional drags when clicking on input. 50 | if (Math.abs(delta) <= 2) { 51 | return; 52 | } 53 | 54 | let value = this.onMouseDownValue; 55 | const accel = event.shiftKey ? 10 : 1; 56 | value = parseInt(this.distance / 2) * accel * this.step; 57 | value += currentValue; 58 | value = Math.min(this.max, Math.max(this.min, value)); 59 | if (currentValue !== value) { 60 | this.setValue(value); 61 | } 62 | this.prevPointer = [event.clientX, event.clientY]; 63 | } 64 | 65 | onMouseDown(event) { 66 | event.preventDefault(); 67 | this.distance = 0; 68 | this.onMouseDownValue = this.value; 69 | this.prevPointer = [event.clientX, event.clientY]; 70 | document.addEventListener('mousemove', this.onMouseMove, false); 71 | document.addEventListener('mouseup', this.onMouseUp, false); 72 | }; 73 | 74 | onMouseUp(event) { 75 | document.removeEventListener('mousemove', this.onMouseMove, false); 76 | document.removeEventListener('mouseup', this.onMouseUp, false); 77 | 78 | if (Math.abs(this.distance) < 2) { 79 | this.$input.focus(); 80 | this.$input.select(); 81 | } 82 | }; 83 | 84 | setValue(value) { 85 | if (value === this.value) return; 86 | 87 | if (value !== undefined) { 88 | if (this.precision === 0) { 89 | value = parseInt(value); 90 | } else { 91 | value = parseFloat(value); 92 | } 93 | 94 | if (value < this.min) { 95 | value = this.min; 96 | } 97 | if (value > this.max) { 98 | value = this.max; 99 | } 100 | 101 | this.value = value; 102 | this.dispatchEvent(new CustomEvent('change', { 103 | detail: { 104 | value: parseFloat(value.toFixed(5)), 105 | }, 106 | bubbles: true, 107 | composed: true, 108 | })); 109 | } 110 | } 111 | 112 | shouldUpdate(changed) { 113 | // This will be triggered typically when the element is changed directly with 114 | // element.setAttribute. 115 | if (changed.has('value') && changed.get('value') !== this.value) { 116 | return true; 117 | } 118 | return true; 119 | } 120 | 121 | onBlur() { 122 | this.setValue(parseFloat(this.$input.value)); 123 | } 124 | 125 | onChange(e) { 126 | this.setValue(e.target.value); 127 | }; 128 | 129 | onKeyDown(event) { 130 | event.stopPropagation(); 131 | 132 | // enter. 133 | if (event.keyCode === 13) { 134 | this.setValue(parseFloat(this.$input.value)); 135 | this.$input.blur(); 136 | return; 137 | } 138 | 139 | // up. 140 | if (event.keyCode === 38) { 141 | this.setValue(parseFloat(this.value) + this.step); 142 | return; 143 | } 144 | 145 | // down. 146 | if (event.keyCode === 40) { 147 | this.setValue(parseFloat(this.value) - this.step); 148 | return; 149 | } 150 | }; 151 | 152 | render() { 153 | const displayValue = this.value.toFixed(this.precision); 154 | return html` 155 | 163 | 172 | `; 173 | } 174 | } 175 | export default NumberInputElement; 176 | -------------------------------------------------------------------------------- /src/app/constants.js: -------------------------------------------------------------------------------- 1 | export const MaterialTypes = [ 2 | 'Material', 3 | 'LineBasicMaterial', 4 | 'LineDashedMaterial', 5 | 'MeshBasicMaterial', 6 | 'MeshDepthMaterial', 7 | 'MeshDistanceMaterial', 8 | 'MeshLambertMaterial', 9 | 'MeshMatcapMaterial', 10 | 'MeshNormalMaterial', 11 | 'MeshPhongMaterial', 12 | 'MeshPhysicalMaterial', 13 | 'MeshStandardMaterial', 14 | 'MeshToonMaterial', 15 | 'PointsMaterial', 16 | 'RawShaderMaterial', 17 | 'ShaderMaterial', 18 | 'ShadowMaterial', 19 | 'SpriteMaterial' 20 | ]; 21 | 22 | export const ObjectTypes = [ 23 | 'Mesh', 24 | 'Line', 25 | 'LineLoop', 26 | 'LineSegments', 27 | 'AmbientLight', 28 | 'DirectionalLight', 29 | 'HemisphereLight', 30 | 'PointLight', 31 | 'RectAreaLight', 32 | 'SpotLight', 33 | 'ArrowHelper', 34 | 'AxesHelper', 35 | 'BoxHelper', 36 | 'Box3Helper', 37 | 'CameraHelper', 38 | 'DirectionalLightHelper', 39 | 'FaceNormalsHelper', 40 | 'GridHelper', 41 | 'PolarGridHelper', 42 | 'PositionalAudioHelper', 43 | 'HemisphereLightHelper', 44 | 'PlaneHelper', 45 | 'PointLightHelper', 46 | 'RectAreaLightHelper', 47 | 'SkeletonHelper', 48 | 'SpotLightHelper', 49 | 'VertexNormalsHelper', 50 | 'Skeleton', 51 | 'Bone', 52 | 'Group' 53 | ]; 54 | 55 | // Defaults go first, and applied if undefined 56 | const ConstantTypes = { 57 | mapping: [ 58 | 'UVMapping', 59 | 'CubeReflectionMapping', 60 | 'CubeRefractionMapping', 61 | 'EquirectangularReflectionMapping', 62 | 'EquirectangularRefractionMapping', 63 | 'SphericalReflectionMapping', 64 | 'CubeUVReflectionMapping', 65 | 'CubeUVRefractionMapping', 66 | ], 67 | drawMode: [ 68 | 'TrianglesDrawMode', 69 | 'TriangleStripDrawMode', 70 | 'TriangleFanDrawMode' 71 | ], 72 | side: [ 73 | 'FrontSide', 74 | 'BackSide', 75 | 'DoubleSide' 76 | ], 77 | colors: [ 78 | 'NoColors', 79 | 'FaceColors', 80 | 'VertexColors' 81 | ], 82 | blending:[ 83 | 'NormalBlending', 84 | 'NoBlending', 85 | 'AdditiveBlending', 86 | 'SubtractiveBlending', 87 | 'MultiplyBlending', 88 | 'CustomBlending' 89 | ], 90 | blendEquation: [ 91 | 'AddEquation', 92 | 'SubtractEquation', 93 | 'ReverseSubtractEquation', 94 | 'MinEquation', 95 | 'MaxEquation' 96 | ], 97 | blendDst: [ 98 | 'OneMinusSrcAlphaFactor', 99 | 'ZeroFactor', 100 | 'OneFactor', 101 | 'SrcColorFactor', 102 | 'OneMinusSrcColorFactor', 103 | 'SrcAlphaFactor', 104 | 'DstAlphaFactor', 105 | 'OneMinusDstAlphaFactor', 106 | 'DstColorFactor', 107 | 'OneMinusDstColorFactor' 108 | ], 109 | blendSrc: [ 110 | 'OneMinusSrcAlphaFactor', 111 | 'ZeroFactor', 112 | 'OneFactor', 113 | 'SrcColorFactor', 114 | 'OneMinusSrcColorFactor', 115 | 'SrcAlphaFactor', 116 | 'DstAlphaFactor', 117 | 'OneMinusDstAlphaFactor', 118 | 'DstColorFactor', 119 | 'OneMinusDstColorFactor', 120 | 'SrcAlphaSaturateFactor' 121 | ], 122 | depthFunc: [ 123 | 'LessEqualDepth', 124 | 'NeverDepth', 125 | 'AlwaysDepth', 126 | 'LessDepth', 127 | 'GreaterEqualDepth', 128 | 'GreaterDepth', 129 | 'NotEqualDepth' 130 | ], 131 | combine: [ 132 | 'MultiplyOperation', 133 | 'MixOperation', 134 | 'AddOperation', 135 | ], 136 | vertexColors: [ 137 | 'NoColors', 138 | 'VertexColors', 139 | 'FaceColors', 140 | ], 141 | normalMapType: [ 142 | 'TangentSpaceNormalMap', 143 | 'ObjectSpaceNormalMap', 144 | ], 145 | encoding: [ 146 | 'LinearEncoding', 147 | 'sRGBEncoding', 148 | 'GammaEncoding', 149 | 'RGBEEncoding', 150 | 'LogLuvEncoding', 151 | 'RGBM7Encoding', 152 | 'RGBM16Encoding', 153 | 'RGBDEncoding', 154 | 'BasicDepthPacking', 155 | 'RGBADepthPacking' 156 | ], 157 | format: [ 158 | 'RGBAFormat', 159 | 'AlphaFormat', 160 | 'RGBFormat', 161 | 'LuminanceFormat', 162 | 'LuminanceAlphaFormat', 163 | 'RGBEFormat', 164 | 'DepthFormat', 165 | 'DepthStencilFormat' 166 | ], 167 | type: [ 168 | 'UnsignedByteType', 169 | 'ByteType', 170 | 'ShortType', 171 | 'UnsignedShortType', 172 | 'IntType', 173 | 'UnsignedIntType', 174 | 'FloatType', 175 | 'HalfFloatType', 176 | 'UnsignedShort4444Type', 177 | 'UnsignedShort5551Type', 178 | 'UnsignedShort565Type', 179 | 'UnsignedInt248Type', 180 | ], 181 | wrapping: [ 182 | 'ClampToEdgeWrapping', 183 | 'RepeatWrapping', 184 | 'MirroredRepeatWrapping', 185 | ], 186 | magFilter: [ 187 | 'LinearFilter', 188 | 'NearestFilter', 189 | ], 190 | minFilter: [ 191 | 'LinearMipMapLinearFilter', 192 | 'NearestFilter', 193 | 'NearestMipMapNearestFilter', 194 | 'NearestMipMapLinearFilter', 195 | 'LinearFilter', 196 | 'LinearMipMapNearestFilter', 197 | ], 198 | toneMapping: [ 199 | 'NoToneMapping', 200 | 'LinearToneMapping', 201 | 'ReinhardToneMapping', 202 | 'Uncharted2ToneMapping', 203 | 'CineonToneMapping', 204 | 'ACESFilmicToneMapping', 205 | ], 206 | shadowMap: [ 207 | 'BasicShadowMap', 208 | 'PCFShadowMap', 209 | 'PCFSoftShadowMap', 210 | 'VSMShadowMap', 211 | ], 212 | }; 213 | 214 | // Copy some over since the constant type is found 215 | // by property name. 216 | ConstantTypes.shadowSide = ConstantTypes.side; 217 | ConstantTypes.blendSrcAlpha = ['null', ...ConstantTypes.blendSrc]; 218 | ConstantTypes.blendDstAlpha = ['null', ...ConstantTypes.blendDst]; 219 | ConstantTypes.blendEquationAlpha = ['null', ...ConstantTypes.blendEquation]; 220 | // Change default (first in order) when encoding is used as `depthPacking` 221 | ConstantTypes.depthPacking = [...ConstantTypes.encoding]; 222 | ConstantTypes.depthPacking[ConstantTypes.depthPacking.indexOf('BasicDepthPacking')] = 223 | ConstantTypes.depthPacking[0]; 224 | ConstantTypes.depthPacking[0] = 'BasicDepthPacking'; 225 | 226 | export { ConstantTypes }; 227 | -------------------------------------------------------------------------------- /src/app/data/geometry.js: -------------------------------------------------------------------------------- 1 | const BufferAttributes = (entity) => { 2 | const attributes = entity.data && entity.data.attributes; 3 | if (!attributes) { 4 | return; 5 | } 6 | const attrType = 'array'; // { name, itemSize, type, array, normalized } 7 | const attrs = Object.keys(attributes).map(key => { 8 | return { 9 | name: key, 10 | type: attrType, 11 | prop: `data.attributes.${key}` 12 | } 13 | }); 14 | return { 15 | name: 'Attributes', 16 | type: 'group', 17 | props: attrs, 18 | } 19 | }; 20 | 21 | // boundingBox 22 | // boundingSphere 23 | const Geometry = { 24 | type: 'geometry', 25 | props: [{ 26 | name: 'Vertices', 27 | type: 'array', 28 | prop: 'data.vertices', 29 | }, { 30 | name: 'Colors', 31 | type: 'array', 32 | prop: 'colors', 33 | }, { 34 | name: 'Faces', 35 | type: 'array', 36 | prop: 'faces', 37 | }, { 38 | name: 'Faces Vertex UVs', 39 | type: 'array', 40 | prop: 'faceVertexUvs', 41 | }, { 42 | name: 'Morph Targets', 43 | type: 'array', // { name: ..., vertices: [new Vector3()]} 44 | prop: 'morphTargets', 45 | }, { 46 | name: 'Morph Normals', 47 | type: 'array', // { name: ..., normals: [new Vector3()]} 48 | prop: 'morphNormals', 49 | }, { 50 | name: 'Skin Weights', 51 | type: 'array', 52 | prop: 'skinWeights', 53 | }, { 54 | name: 'Skin Indices', 55 | type: 'array', 56 | prop: 'skinIndices', 57 | }, { 58 | name: 'Line Distances', 59 | type: 'array', 60 | prop: 'faceVertexUvs', 61 | }] 62 | } 63 | 64 | // boundingBox 65 | // boundingSphere 66 | // morphAttributes 67 | const bufferGeometryProps = [{ 68 | name: 'Index', 69 | type: 'attribute', 70 | prop: 'data.index', 71 | }, { 72 | name: 'Groups', 73 | type: 'array', // { start, count, materialIndex } 74 | prop: 'data.groups', 75 | }, 76 | /* 77 | { 78 | name: 'Draw Range', 79 | type: 'group', 80 | props: [{ 81 | name: 'Start', 82 | type: 'number', 83 | prop: 'drawRange.start', 84 | default: 0, 85 | }, { 86 | name: 'Count', 87 | type: 'number', 88 | prop: 'drawRange.count', 89 | default: Infinity, 90 | }] 91 | }, 92 | */ 93 | { 94 | name: 'Morph Targets Relative', 95 | type: 'boolean', 96 | prop: 'data.morphTargetsRelative', 97 | default: false, 98 | }]; 99 | 100 | const BufferGeometry = { 101 | type: 'geometry', 102 | props: [ 103 | ...bufferGeometryProps 104 | ], 105 | BufferAttributes, 106 | } 107 | 108 | const InstancedBufferGeometry = { 109 | type: 'geometry', 110 | props: [ 111 | ...bufferGeometryProps, { 112 | name: 'Instance Count', 113 | type: 'int', 114 | prop: 'instanceCount', 115 | } 116 | ], 117 | BufferAttributes, 118 | } 119 | 120 | export default { 121 | BufferGeometry: BufferGeometry, 122 | Geometry: Geometry, 123 | 124 | InstancedBufferGeometry: InstancedBufferGeometry, 125 | 126 | BoxBufferGeometry: BufferGeometry, 127 | BoxGeometry: Geometry, 128 | CircleBufferGeometry: BufferGeometry, 129 | CircleGeometry: Geometry, 130 | ConeBufferGeometry: BufferGeometry, 131 | ConeGeometry: Geometry, 132 | CylinderBufferGeometry: BufferGeometry, 133 | CylinderGeometry: Geometry, 134 | DodecahedronBufferGeometry: BufferGeometry, 135 | DodecahedronGeometry: Geometry, 136 | EdgesGeometry: Geometry, 137 | ExtrudeBufferGeometry: BufferGeometry, 138 | ExtrudeGeometry: Geometry, 139 | IcosahedronBufferGeometry: BufferGeometry, 140 | IcosahedronGeometry: Geometry, 141 | LatheBufferGeometry: BufferGeometry, 142 | LatheGeometry: Geometry, 143 | OctahedronBufferGeometry: BufferGeometry, 144 | OctahedronGeometry: Geometry, 145 | ParametricBufferGeometry: BufferGeometry, 146 | ParametricGeometry: Geometry, 147 | PlaneBufferGeometry: BufferGeometry, 148 | PlaneGeometry: Geometry, 149 | PolyhedronBufferGeometry: BufferGeometry, 150 | PolyhedronGeometry: Geometry, 151 | RingBufferGeometry: BufferGeometry, 152 | RingGeometry: Geometry, 153 | ShapeBufferGeometry: BufferGeometry, 154 | ShapeGeometry: Geometry, 155 | SphereBufferGeometry: BufferGeometry, 156 | SphereGeometry: Geometry, 157 | TetrahedronBufferGeometry: BufferGeometry, 158 | TetrahedronGeometry: Geometry, 159 | TextBufferGeometry: BufferGeometry, 160 | TextGeometry: Geometry, 161 | TorusBufferGeometry: BufferGeometry, 162 | TorusGeometry: Geometry, 163 | TorusKnotBufferGeometry: BufferGeometry, 164 | TorusKnotGeometry: Geometry, 165 | TubeBufferGeometry: BufferGeometry, 166 | TubeGeometry: Geometry, 167 | WireframeGeometry: Geometry, 168 | } 169 | -------------------------------------------------------------------------------- /src/app/data/lights.js: -------------------------------------------------------------------------------- 1 | import transform from './object-transform.js'; 2 | import renderable from './object-renderable.js'; 3 | 4 | const base = [{ 5 | name: 'Color', 6 | prop: 'color', 7 | type: 'color', 8 | }, { 9 | name: 'Intensity', 10 | prop: 'intensity', 11 | type: 'number', 12 | }]; 13 | 14 | const objectProps = [ 15 | transform, 16 | renderable, 17 | ]; 18 | 19 | const castShadow = { 20 | name: 'Cast Shadow', 21 | prop: 'castShadow', 22 | type: 'boolean', 23 | default: false, 24 | }; 25 | 26 | const target = { 27 | name: 'Target', 28 | prop: 'target', 29 | type: 'vec3', 30 | }; 31 | 32 | const decay = { 33 | name: 'Decay', 34 | prop: 'decay', 35 | type: 'number', 36 | default: 1, 37 | }; 38 | const distance = { 39 | name: 'Distance', 40 | prop: 'distance', 41 | type: 'number', 42 | default: 0, 43 | }; 44 | const power = { 45 | name: 'Power', 46 | prop: 'power', 47 | type: 'number', 48 | default: Math.PI * 4, 49 | }; 50 | 51 | const Light = { 52 | type: 'light', 53 | props: [ 54 | ...base, 55 | ...objectProps 56 | ], 57 | } 58 | 59 | export default { 60 | Light: Light, 61 | AmbientLight: Light, 62 | DirectionalLight: { 63 | type: 'object', 64 | props: [ 65 | ...base, 66 | castShadow, 67 | target, 68 | ...objectProps, 69 | ], 70 | }, 71 | HemisphereLight: { 72 | type: 'object', 73 | props: [ 74 | ...base, 75 | castShadow, 76 | { 77 | name: 'Ground Color', 78 | prop: 'groundColor', 79 | type: 'color' 80 | }, 81 | ...objectProps, 82 | ], 83 | }, 84 | PointLight: { 85 | type: 'object', 86 | props: [ 87 | ...base, 88 | castShadow, 89 | decay, 90 | distance, 91 | power, 92 | ...objectProps, 93 | ], 94 | }, 95 | RectAreaLight: { 96 | type: 'object', 97 | props: [ 98 | ...base, 99 | castShadow, 100 | { 101 | name: 'Width', 102 | prop: 'width', 103 | type: 'number' 104 | }, { 105 | name: 'Height', 106 | prop: 'height', 107 | type: 'number' 108 | }, 109 | ...objectProps, 110 | ], 111 | }, 112 | SpotLight: { 113 | type: 'object', 114 | props: [ 115 | ...base, 116 | castShadow, 117 | { 118 | name: 'Angle', 119 | prop: 'angle', 120 | type: 'angle', 121 | default: Math.PI/3, 122 | min: 0, 123 | max: Math.PI/2, 124 | }, 125 | decay, 126 | distance, 127 | power, 128 | { 129 | name: 'Penumbra', 130 | prop: 'penumbra', 131 | type: 'number', 132 | default: 0, 133 | min: 0, 134 | max: 1, 135 | }, 136 | target, 137 | ...objectProps, 138 | ], 139 | }, 140 | }; 141 | -------------------------------------------------------------------------------- /src/app/data/material-blending.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'Blending', 3 | type: 'group', 4 | props: [{ 5 | name: 'Blending', 6 | prop: 'blending', 7 | type: 'enum', 8 | }, { 9 | name: 'Alpha Test', 10 | prop: 'alphaTest', 11 | type: 'number', 12 | default: 0, 13 | }, { 14 | name: 'Source', 15 | prop: 'blendSrc', 16 | type: 'enum', 17 | }, { 18 | name: 'Source Alpha', 19 | prop: 'blendSrcAlpha', 20 | type: 'enum', 21 | default: 0, 22 | }, { 23 | name: 'Destination', 24 | prop: 'blendDst', 25 | type: 'enum', 26 | }, { 27 | name: 'Destination Alpha', 28 | prop: 'blendDstAlpha', 29 | type: 'enum', 30 | default: 0, 31 | }, { 32 | name: 'Blend Equation', 33 | prop: 'blendEquation', 34 | type: 'enum', 35 | }, { 36 | name: 'Blend Equation Alpha', 37 | prop: 'blendEquationAlpha', 38 | type: 'enum', 39 | default: 0, 40 | }, { 41 | name: 'Premultiplied Alpha', 42 | prop: 'premultipliedAlpha', 43 | type: 'boolean', 44 | default: false, 45 | }], 46 | }; 47 | -------------------------------------------------------------------------------- /src/app/data/material-common.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'Properties', 3 | type: 'group', 4 | props: [{ 5 | name: 'Side', 6 | prop: 'side', 7 | type: 'enum', 8 | default: 0, 9 | }, { 10 | name: 'Transparent', 11 | prop: 'transparent', 12 | type: 'boolean', 13 | default: false, 14 | }, { 15 | name: 'Visible', 16 | prop: 'visible', 17 | type: 'boolean', 18 | default: true, 19 | }, { 20 | name: 'Opacity', 21 | prop: 'opacity', 22 | type: 'number', 23 | min: 0, 24 | max: 1, 25 | default: 1, 26 | }, { 27 | name: 'Color Write', 28 | prop: 'colorWrite', 29 | type: 'boolean', 30 | default: true, 31 | }, { 32 | name: 'Depth Func', 33 | prop: 'depthFunc', 34 | type: 'enum', 35 | }, { 36 | name: 'Depth Test', 37 | prop: 'depthTest', 38 | type: 'boolean', 39 | default: true, 40 | }, { 41 | name: 'Depth Write', 42 | prop: 'depthWrite', 43 | type: 'boolean', 44 | default: true, 45 | }, { 46 | name: 'Lights', 47 | prop: 'lights', 48 | type: 'boolean', 49 | default: false, 50 | }, { 51 | name: 'Flat Shading', 52 | prop: 'flatShading', 53 | type: 'boolean', 54 | default: false, 55 | }, { 56 | name: 'Fog', 57 | prop: 'fog', 58 | type: 'boolean', 59 | default: false, 60 | }, { 61 | name: 'Dithering', 62 | prop: 'dithering', 63 | type: 'boolean', 64 | default: false, 65 | }, { 66 | name: 'Clip Intersection', 67 | prop: 'clipIntersection', 68 | type: 'boolean', 69 | default: false, 70 | }, { 71 | name: 'Clip Shadows', 72 | prop: 'clipShadows', 73 | type: 'boolean', 74 | default: false, 75 | }, { 76 | name: 'Shadow Side', 77 | prop: 'shadowSide', 78 | type: 'enum', 79 | default: 0, 80 | }, { 81 | name: 'Tone Mapped', 82 | prop: 'toneMapped', 83 | type: 'boolean', 84 | default: true, 85 | }, { 86 | name: 'Vertex Colors', 87 | prop: 'vertexColors', 88 | type: 'enum', 89 | }, { 90 | name: 'Vertex Tangents', 91 | prop: 'vertexTangents', 92 | type: 'boolean', 93 | default: false, 94 | }] 95 | } 96 | -------------------------------------------------------------------------------- /src/app/data/material-line.js: -------------------------------------------------------------------------------- 1 | export const line = { 2 | name: 'Line Width', 3 | type: 'number', 4 | prop: 'linewidth', 5 | default: 1, 6 | }; 7 | 8 | export const lineDashed = [{ 9 | name: 'Dash Size', 10 | type: 'number', 11 | prop: 'dashSize', 12 | default: 3, 13 | }, { 14 | name: 'Dash Scale', 15 | type: 'number', 16 | prop: 'scale', 17 | default: 1, 18 | }, { 19 | name: 'Gap Size', 20 | type: 'number', 21 | prop: 'gapSize', 22 | default: 1, 23 | }]; 24 | -------------------------------------------------------------------------------- /src/app/data/material-pbr.js: -------------------------------------------------------------------------------- 1 | export const metalness = { 2 | type: 'group', 3 | name: 'Metalness', 4 | props: [{ 5 | name: 'Map', 6 | type: 'texture', 7 | prop: 'metalnessMap', 8 | }, { 9 | name: 'Metalness', 10 | type: 'number', 11 | prop: 'metalness', 12 | min: 0, 13 | max: 1, 14 | }], 15 | } 16 | 17 | export const roughness = { 18 | type: 'group', 19 | name: 'Roughness', 20 | props: [{ 21 | name: 'Roughness Map', 22 | type: 'texture', 23 | prop: 'roughnessMap', 24 | }, { 25 | name: 'Roughness', 26 | type: 'number', 27 | prop: 'roughness', 28 | min: 0, 29 | max: 1, 30 | }], 31 | }; 32 | 33 | export const clearCoat = { 34 | type: 'group', 35 | name: 'Clear Coat', 36 | props: [{ 37 | name: 'Clear Coat', 38 | type: 'number', 39 | prop: 'clearCoat', 40 | min: 0, 41 | max: 1, 42 | default: 0, 43 | }, { 44 | name: 'Clear Coat Roughness', 45 | type: 'number', 46 | prop: 'clearCoatRoughness', 47 | min: 0, 48 | max: 1, 49 | default: 0, 50 | }], 51 | }; 52 | -------------------------------------------------------------------------------- /src/app/data/material-polygon-offset.js: -------------------------------------------------------------------------------- 1 | export default { 2 | type: 'group', 3 | name: 'Polygon Offset', 4 | props: [{ 5 | name: 'Enabled', 6 | type: 'boolean', 7 | prop: 'polygonOffset', 8 | default: false, 9 | }, { 10 | name: 'Offset Factor', 11 | type: 'int', 12 | prop: 'polygonOffsetFactor', 13 | default: 0, 14 | }, { 15 | name: 'Offset Units', 16 | type: 'int', 17 | prop: 'polygonOffsetUnits', 18 | default: 0, 19 | }] 20 | }; 21 | -------------------------------------------------------------------------------- /src/app/data/material-shared.js: -------------------------------------------------------------------------------- 1 | export const color = { 2 | name: 'Color', 3 | prop: 'color', 4 | type: 'color', 5 | }; 6 | 7 | export const gradientMap = { 8 | name: 'Gradient Map', 9 | prop: 'gradientMap', 10 | type: 'texture', 11 | }; 12 | 13 | export const combine = { 14 | name: 'Combine', 15 | prop: 'combine', 16 | type: 'enum', 17 | }; 18 | 19 | export const depthPacking = { 20 | name: 'Depth Packing', 21 | prop: 'depthPacking', 22 | type: 'enum', 23 | }; 24 | 25 | export const alphaMap = { 26 | name: 'Alpha Map', 27 | prop: 'alphaMap', 28 | type: 'texture', 29 | }; 30 | 31 | export const matcap = { 32 | name: 'matcap', 33 | prop: 'matcap', 34 | type: 'texture', 35 | } 36 | 37 | export const normalMap = { 38 | type: 'group', 39 | name: 'Normal Map', 40 | props: [{ 41 | name: 'Map', 42 | type: 'texture', 43 | prop: 'normalMap', 44 | }, { 45 | name: 'Scale', 46 | type: 'vec2', 47 | prop: 'normalScale', 48 | default: [1, 1], 49 | }, { 50 | name: 'Type', 51 | type: 'enum', 52 | prop: 'normalMapType', 53 | }] 54 | }; 55 | 56 | export const bumpMap = { 57 | type: 'group', 58 | name: 'Bump Map', 59 | props: [{ 60 | name: 'Map', 61 | type: 'texture', 62 | prop: 'bumpMap', 63 | }, { 64 | name: 'Scale', 65 | type: 'texture', 66 | prop: 'bumpScale', 67 | default: 1, 68 | }], 69 | }; 70 | 71 | export const distance = { 72 | name: 'Distance', 73 | type: 'group', 74 | props: [{ 75 | name: 'referencePosition', 76 | prop: 'referencePosition', 77 | type: 'vec3', 78 | }, { 79 | name: 'Near Distance', 80 | prop: 'nearDistance', 81 | type: 'number', 82 | }, { 83 | name: 'Far Distance', 84 | prop: 'farDistance', 85 | type: 'number', 86 | }] 87 | } 88 | 89 | export const displacement = { 90 | type: 'group', 91 | name: 'Displacement Map', 92 | props: [{ 93 | name: 'Map', 94 | type: 'texture', 95 | prop: 'displacementMap', 96 | }, { 97 | name: 'Scale', 98 | type: 'number', 99 | prop: 'displacementScale', 100 | min: 0, 101 | default: 1, 102 | }, { 103 | name: 'Bias', 104 | type: 'number', 105 | prop: 'displacementBias', 106 | min: 0, 107 | default: 0, 108 | }] 109 | }; 110 | 111 | export const diffuseMap = { 112 | name: 'Diffuse Map', 113 | prop: 'map', 114 | type: 'texture', 115 | }; 116 | 117 | export const ao = { 118 | name: 'Ambient Occlusion', 119 | type: 'group', 120 | props: [{ 121 | name: 'Map', 122 | prop: 'aoMap', 123 | type: 'texture', 124 | }, { 125 | name: 'Intensity', 126 | prop: 'aoMapIntensity', 127 | type: 'number', 128 | min: 0, 129 | default: 1, 130 | }], 131 | }; 132 | 133 | export const envMap = { 134 | name: 'Environment Map', 135 | type: 'texture', 136 | prop: 'envMap', 137 | }; 138 | 139 | export const envMapIntensity = { 140 | name: 'Environment Intensity', 141 | type: 'number', 142 | prop: 'envMapIntensity', 143 | default: 1, 144 | min: 0, 145 | }; 146 | 147 | export const lightMap = { 148 | name: 'Light Map', 149 | type: 'group', 150 | props: [{ 151 | name: 'Map', 152 | prop: 'lightMap', 153 | type: 'texture', 154 | }, { 155 | name: 'Intensity', 156 | prop: 'lightMapIntensity', 157 | type: 'number', 158 | min: 0, 159 | default: 1, 160 | }], 161 | }; 162 | 163 | export const morphNormals = { 164 | name: 'Morph Normals', 165 | type: 'boolean', 166 | prop: 'morphNormals', 167 | default: false, 168 | } 169 | 170 | export const morphTargets = { 171 | name: 'Morph Targets', 172 | type: 'boolean', 173 | prop: 'morphTargets', 174 | default: false, 175 | } 176 | 177 | export const reflectivity = { 178 | name: 'Reflectivity', 179 | type: 'number', 180 | prop: 'reflectivity', 181 | min: 0, 182 | max: 1, 183 | default: 1, 184 | } 185 | 186 | export const refractionRatio = { 187 | name: 'Refraction Ratio', 188 | type: 'number', 189 | prop: 'refractionRatio', 190 | default: 0.98, 191 | min: 0, 192 | } 193 | 194 | export const skinning = { 195 | name: 'Skinning', 196 | type: 'boolean', 197 | prop: 'skinning', 198 | default: false, 199 | } 200 | 201 | export const specularMap = { 202 | name: 'Specular Map', 203 | type: 'texture', 204 | prop: 'specularMap', 205 | } 206 | 207 | export const specular = { 208 | name: 'Specular', 209 | type: 'group', 210 | props: [{ 211 | name: 'Color', 212 | type: 'color', 213 | prop: 'specular', 214 | }, { 215 | name: 'Map', 216 | type: 'texture', 217 | prop: 'specularMap', 218 | }], 219 | }; 220 | 221 | export const clipping = { 222 | name: 'Clipping', 223 | type: 'boolean', 224 | prop: 'clipping', 225 | default: false, 226 | } 227 | 228 | export const wireframe = { 229 | name: 'Wireframe', 230 | type: 'group', 231 | props: [{ 232 | name: 'Enabled', 233 | type: 'boolean', 234 | prop: 'wireframe', 235 | default: false, 236 | }, { 237 | name: 'Line Width', 238 | type: 'number', 239 | prop: 'wireframeLinewidth', 240 | default: 1, 241 | }, { 242 | name: 'Line Cap', 243 | type: 'string', 244 | prop: 'wireframeLinecap', 245 | default: 'round', 246 | }, { 247 | name: 'Line Join', 248 | type: 'string', 249 | prop: 'wireframeLinejoin', 250 | default: 'round', 251 | }] 252 | } 253 | 254 | export const sizeAttenuation = { 255 | name: 'Attenuation', 256 | type: 'boolean', 257 | prop: 'sizeAttenuation', 258 | default: true, 259 | } 260 | 261 | export const rotation = { 262 | name: 'Rotation', 263 | type: 'radians', 264 | prop: 'rotation', 265 | default: 0, 266 | } 267 | 268 | export const point = { 269 | name: 'Points', 270 | type: 'group', 271 | props: [{ 272 | name: 'Size', 273 | type: 'number', 274 | prop: 'size', 275 | default: 1, 276 | }, { 277 | name: 'Attenuation', 278 | type: 'boolean', 279 | prop: 'sizeAttenuation', 280 | default: true, 281 | }] 282 | } 283 | 284 | export const emissive = { 285 | name: 'Emissive', 286 | type: 'group', 287 | props: [{ 288 | name: 'Color', 289 | type: 'color', 290 | prop: 'emissive', 291 | }, { 292 | name: 'Emissive Map ', 293 | type: 'texture', 294 | prop: 'emissiveMap', 295 | }, { 296 | name: 'Emissive Intensity', 297 | prop: 'emissiveIntensity', 298 | type: 'number', 299 | default: 1, 300 | }] 301 | }; 302 | -------------------------------------------------------------------------------- /src/app/data/materials.js: -------------------------------------------------------------------------------- 1 | import common from './material-common.js'; 2 | import * as prop from './material-shared.js'; 3 | import blending from './material-blending.js'; 4 | import polygonOffset from './material-polygon-offset.js'; 5 | import { line, lineDashed } from './material-line.js'; 6 | import { metalness, roughness, clearCoat } from './material-pbr.js'; 7 | 8 | const standard = [ 9 | { 10 | name: 'Environment', 11 | type: 'group', 12 | props: [prop.envMap, prop.envMapIntensity].map(p => { 13 | return Object.assign({}, p, { 14 | name: p.name.replace(/Environment /, ''), 15 | }); 16 | }), 17 | }, 18 | metalness, 19 | roughness, 20 | prop.normalMap, 21 | prop.bumpMap, 22 | prop.displacement, 23 | prop.ao, 24 | prop.lightMap, 25 | prop.emissive, 26 | ]; 27 | 28 | const base = [ 29 | common, 30 | blending, 31 | polygonOffset 32 | ]; 33 | 34 | const lineBase = [ 35 | // Remove 'side', 'shadowSide' from line materials 36 | Object.assign({}, common, { 37 | props: [...common.props.filter(({ prop }) => ['side', 'shadowSide'].indexOf(prop) === -1)], 38 | }), 39 | blending, 40 | polygonOffset 41 | ]; 42 | 43 | const GenericMaterial = { 44 | type: 'material', 45 | props: base, 46 | } 47 | 48 | export default { 49 | Material: GenericMaterial, 50 | MeshBasicMaterial: { 51 | type: 'material', 52 | props: [ 53 | prop.color, 54 | prop.diffuseMap, 55 | prop.combine, 56 | prop.alphaMap, 57 | prop.envMap, 58 | prop.reflectivity, 59 | prop.refractionRatio, 60 | prop.specularMap, 61 | prop.morphTargets, 62 | prop.skinning, 63 | prop.wireframe, 64 | prop.ao, 65 | prop.lightMap, 66 | ...base 67 | ], 68 | }, 69 | MeshDepthMaterial: { 70 | type: 'material', 71 | props: [ 72 | prop.depthPacking, 73 | prop.diffuseMap, 74 | prop.alphaMap, 75 | prop.wireframe, 76 | prop.displacement, 77 | ...base 78 | ], 79 | }, 80 | MeshDistanceMaterial: { 81 | type: 'material', 82 | props: [ 83 | prop.diffuseMap, 84 | prop.alphaMap, 85 | prop.morphTargets, 86 | prop.skinning, 87 | prop.distance, 88 | prop.displacement, 89 | ...base 90 | ], 91 | }, 92 | MeshLambertMaterial: { 93 | type: 'material', 94 | props: [ 95 | prop.color, 96 | prop.diffuseMap, 97 | prop.alphaMap, 98 | prop.envMap, 99 | prop.reflectivity, 100 | prop.refractionRatio, 101 | prop.morphNormals, 102 | prop.morphTargets, 103 | prop.skinning, 104 | prop.specularMap, 105 | prop.wireframe, 106 | prop.emissive, 107 | prop.ao, 108 | prop.lightMap, 109 | ...base 110 | ], 111 | }, 112 | MeshMatcapMaterial: { 113 | type: 'material', 114 | props: [ 115 | prop.color, 116 | prop.matcap, 117 | prop.diffuseMap, 118 | prop.alphaMap, 119 | prop.morphNormals, 120 | prop.morphTargets, 121 | prop.skinning, 122 | prop.normalMap, 123 | prop.bumpMap, 124 | prop.displacement, 125 | ...base 126 | ], 127 | }, 128 | MeshNormalMaterial: { 129 | type: 'material', 130 | props: [ 131 | prop.morphNormals, 132 | prop.morphTargets, 133 | prop.skinning, 134 | prop.wireframe, 135 | prop.normalMap, 136 | prop.bumpMap, 137 | prop.displacement, 138 | ...base 139 | ], 140 | }, 141 | MeshPhongMaterial: { 142 | type: 'material', 143 | props: [ 144 | prop.color, 145 | prop.alphaMap, 146 | prop.envMap, 147 | prop.diffuseMap, 148 | prop.combine, 149 | prop.refractionRatio, 150 | prop.reflectivity, 151 | prop.morphNormals, 152 | prop.morphTargets, 153 | prop.skinning, 154 | prop.ao, 155 | prop.specular, 156 | prop.lightMap, 157 | prop.wireframe, 158 | prop.emissive, 159 | prop.normalMap, 160 | prop.bumpMap, 161 | prop.displacement, 162 | ...base 163 | ], 164 | }, 165 | MeshPhysicalMaterial: { 166 | type: 'material', 167 | props: [ 168 | prop.diffuseMap, 169 | prop.color, 170 | prop.refractionRatio, 171 | prop.reflectivity, 172 | prop.skinning, 173 | prop.wireframe, 174 | ...standard, 175 | clearCoat, 176 | ...base 177 | ], 178 | }, 179 | MeshStandardMaterial: { 180 | type: 'material', 181 | props: [ 182 | prop.diffuseMap, 183 | prop.color, 184 | prop.refractionRatio, 185 | prop.skinning, 186 | prop.wireframe, 187 | ...standard, 188 | ...base 189 | ], 190 | }, 191 | MeshToonMaterial: { 192 | type: 'material', 193 | props: [ 194 | prop.color, 195 | prop.diffuseMap, 196 | prop.combine, 197 | prop.gradientMap, 198 | prop.alphaMap, 199 | prop.refractionRatio, 200 | prop.reflectivity, 201 | prop.envMap, 202 | prop.morphNormals, 203 | prop.morphTargets, 204 | prop.skinning, 205 | prop.specular, 206 | prop.ao, 207 | prop.lightMap, 208 | prop.wireframe, 209 | prop.emissive, 210 | prop.normalMap, 211 | prop.bumpMap, 212 | prop.displacement, 213 | ...base 214 | ], 215 | }, 216 | PointsMaterial: { 217 | type: 'material', 218 | props: [ 219 | prop.color, 220 | prop.alphaMap, 221 | prop.diffuseMap, 222 | prop.morphTargets, 223 | prop.point, 224 | ...base 225 | ] 226 | }, 227 | RawShaderMaterial: { 228 | type: 'material', 229 | props: [ 230 | prop.clipping, 231 | prop.morphNormals, 232 | prop.morphTargets, 233 | prop.skinning, 234 | prop.wireframe, 235 | ...base 236 | ], 237 | }, 238 | ShaderMaterial: { 239 | type: 'material', 240 | props: [ 241 | prop.clipping, 242 | prop.morphNormals, 243 | prop.morphTargets, 244 | prop.skinning, 245 | prop.wireframe, 246 | ...base 247 | ], 248 | }, 249 | ShadowMaterial: GenericMaterial, 250 | SpriteMaterial: { 251 | type: 'material', 252 | props: [ 253 | prop.color, 254 | prop.diffuseMap, 255 | prop.rotation, 256 | prop.sizeAttenuation, 257 | ...base 258 | ], 259 | }, 260 | LineBasicMaterial: { 261 | type: 'material', 262 | props: [ 263 | prop.color, 264 | line, 265 | ...lineBase 266 | ], 267 | }, 268 | LineDashedMaterial: { 269 | type: 'material', 270 | props: [ 271 | prop.color, 272 | line, 273 | ...lineDashed, 274 | ...lineBase 275 | ], 276 | }, 277 | }; 278 | -------------------------------------------------------------------------------- /src/app/data/object-renderable.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'Rendering', 3 | type: 'group', 4 | props: [{ 5 | name: 'Render Order', 6 | prop: 'renderOrder', 7 | type: 'int', 8 | default: 0, 9 | }, { 10 | name: 'Visible', 11 | prop: 'visible', 12 | type: 'boolean', 13 | default: true, 14 | }, { 15 | name: 'Receive Shadow', 16 | prop: 'receiveShadow', 17 | type: 'boolean', 18 | default: false, 19 | },{ 20 | name: 'Cast Shadow', 21 | prop: 'castShadow', 22 | type: 'boolean', 23 | default: false, 24 | },{ 25 | name: 'Frustum Culled', 26 | prop: 'frustumCulled', 27 | type: 'boolean', 28 | default: true, 29 | }], 30 | } 31 | -------------------------------------------------------------------------------- /src/app/data/object-transform.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'Transform', 3 | type: 'group', 4 | props: [{ 5 | name: 'Position', 6 | prop: 'position', 7 | type: 'vec3', 8 | step: 0.01, 9 | precision: 3, 10 | }, { 11 | name: 'Rotation', 12 | prop: 'rotation', 13 | type: 'vec3', 14 | step: 0.01, 15 | precision: 3, 16 | }, { 17 | name: 'Scale', 18 | prop: 'scale', 19 | type: 'vec3', 20 | step: 0.01, 21 | precision: 3, 22 | }, { 23 | name: 'Matrix Auto Update', 24 | prop: 'matrixAutoUpdate', 25 | type: 'boolean', 26 | default: true, 27 | }] 28 | } 29 | -------------------------------------------------------------------------------- /src/app/data/objects.js: -------------------------------------------------------------------------------- 1 | import transform from './object-transform.js'; 2 | import renderable from './object-renderable.js'; 3 | 4 | const geometry = { 5 | name: 'Geometry', 6 | prop: 'geometry', 7 | type: 'geometry', 8 | } 9 | const material = { 10 | name: 'Material', 11 | prop: 'material', 12 | type: 'material', 13 | } 14 | 15 | const object = [ 16 | transform, 17 | renderable, 18 | ]; 19 | 20 | const GeometryRenderable = { 21 | type: 'object', 22 | props: [ 23 | geometry, 24 | material, 25 | transform, 26 | renderable, 27 | ] 28 | } 29 | 30 | const Scene = { 31 | type: 'scene', 32 | props: object, 33 | } 34 | 35 | const Helper = { 36 | type: 'helper', 37 | props: object, 38 | } 39 | 40 | const Bone = { 41 | type: 'bone', 42 | props: object, 43 | } 44 | 45 | // Does not have a further classification yet 46 | const Object3D = { 47 | type: 'object', 48 | props: object, 49 | } 50 | 51 | export default { 52 | Mesh: GeometryRenderable, 53 | Line: GeometryRenderable, 54 | LineLoop: GeometryRenderable, 55 | LineSegments: GeometryRenderable, 56 | Points: GeometryRenderable, 57 | SkinnedMesh: GeometryRenderable, 58 | InstancedMesh: GeometryRenderable, 59 | 60 | AxesHelper: Helper, 61 | BoxHelper: Helper, 62 | Box3Helper: Helper, 63 | CameraHelper: Helper, 64 | DirectionalLightHelper: Helper, 65 | FaceNormalsHelper: Helper, 66 | GridHelper: Helper, 67 | PolarGridHelper: Helper, 68 | PositionalAudioHelper: Helper, 69 | HemisphereLightHelper: Helper, 70 | PlaneHelper: Helper, 71 | PointLightHelper: Helper, 72 | RectAreaLightHelper: Helper, 73 | SkeletonHelper: Helper, 74 | SpotLightHelper: Helper, 75 | VertexNormalsHelper: Helper, 76 | 77 | Scene: Scene, 78 | 79 | Skeleton: Bone, 80 | Bone: Bone, 81 | 82 | Object3D: Object3D, 83 | Group: Object3D, 84 | PerspectiveCamera: Object3D, 85 | OrthographicCamera: Object3D, 86 | Camera: Object3D, 87 | }; 88 | -------------------------------------------------------------------------------- /src/app/data/renderers.js: -------------------------------------------------------------------------------- 1 | const ToneMapping = { 2 | name: 'Tone Mapping', 3 | type: 'group', 4 | props: [{ 5 | // @TODO 6 | name: 'Type', 7 | prop: 'toneMapping', 8 | type: 'enum', 9 | enumType: 'toneMapping', 10 | }, { 11 | name: 'Exposure', 12 | prop: 'toneMappingExposure', 13 | type: 'number', 14 | default: 1, 15 | }], 16 | }; 17 | 18 | const ShadowMap = { 19 | name: 'Shadow Map', 20 | type: 'group', 21 | props: [{ 22 | name: 'Enabled', 23 | prop: 'shadowMap.enabled', 24 | type: 'boolean', 25 | default: false, 26 | }, { 27 | name: 'Auto Update', 28 | prop: 'shadowMap.autoUpdate', 29 | type: 'boolean', 30 | default: true, 31 | }, { 32 | // @TODO enum 33 | name: 'Shadow Type', 34 | prop: 'shadowMap.type', 35 | type: 'enum', 36 | enumType: 'shadowMap', 37 | }], 38 | }; 39 | 40 | const BufferClearing = { 41 | name: 'Buffer Clearing', 42 | type: 'group', 43 | props: [{ 44 | name: 'Auto Clear', 45 | prop: 'autoClear', 46 | type: 'boolean', 47 | default: true, 48 | }, { 49 | name: 'Auto Clear Color', 50 | prop: 'autoClearColor', 51 | type: 'boolean', 52 | default: true, 53 | }, { 54 | name: 'Auto Clear Depth', 55 | prop: 'autoClearDepth', 56 | type: 'boolean', 57 | default: true, 58 | }, { 59 | name: 'Auto Clear Stencil', 60 | prop: 'autoClearStencil', 61 | type: 'boolean', 62 | default: true, 63 | }] 64 | }; 65 | 66 | 67 | // Does not have a further classification yet 68 | const Capabilities = { 69 | name: 'Capabilities', 70 | type: 'group', 71 | props: [{ 72 | name: 'Is WebGL2', 73 | prop: 'capabilities.isWebGL2', 74 | type: 'boolean', 75 | readonly: true, 76 | }, { 77 | name: 'Precision', 78 | prop: 'capabilities.precision', 79 | type: 'string', 80 | readonly: true, 81 | }, { 82 | name: 'Float Fragment Textures', 83 | prop: 'capabilities.floatFragmentTextures', 84 | type: 'boolean', 85 | readonly: true, 86 | }, { 87 | name: 'Float Vertex Textures', 88 | prop: 'capabilities.floatVertexTextures', 89 | type: 'boolean', 90 | readonly: true, 91 | }, { 92 | name: 'Logarithmic Depth Buffer', 93 | prop: 'capabilities.logarithmicDepthBuffer', 94 | type: 'boolean', 95 | readonly: true, 96 | }, { 97 | name: 'Max Anisotropy', 98 | prop: 'capabilities.maxAnisotropy', 99 | type: 'number', 100 | readonly: true, 101 | }, { 102 | name: 'Max Precision', 103 | prop: 'capabilities.maxPrecision', 104 | type: 'string', 105 | readonly: true, 106 | }, { 107 | name: 'Max Attributes', 108 | prop: 'capabilities.maxAttributes', 109 | type: 'int', 110 | readonly: true, 111 | }, { 112 | name: 'Max Cubemap Size', 113 | prop: 'capabilities.maxCubemapSize', 114 | type: 'int', 115 | readonly: true, 116 | }, { 117 | name: 'Max Fragment Uniforms', 118 | prop: 'capabilities.maxFragmentUniforms', 119 | type: 'int', 120 | readonly: true, 121 | }, { 122 | name: 'Max Texture Size', 123 | prop: 'capabilities.maxTextureSize', 124 | type: 'int', 125 | readonly: true, 126 | }, { 127 | name: 'Max Textures', 128 | prop: 'capabilities.maxTextures', 129 | type: 'int', 130 | readonly: true, 131 | }, { 132 | name: 'Max Varyings', 133 | prop: 'capabilities.maxVaryings', 134 | type: 'int', 135 | readonly: true, 136 | }, { 137 | name: 'Max Vertex Textures', 138 | prop: 'capabilities.maxVertexTextures', 139 | type: 'int', 140 | readonly: true, 141 | }, { 142 | name: 'Max Vertex Uniforms', 143 | prop: 'capabilities.maxVertexUniforms', 144 | type: 'int', 145 | readonly: true, 146 | }, { 147 | name: 'Vertex Textures', 148 | prop: 'capabilities.vertexTextures', 149 | type: 'boolean', 150 | readonly: true, 151 | }] 152 | }; 153 | 154 | // @TODO clippingPlanes 155 | const Clipping = { 156 | name: 'Clipping', 157 | type: 'group', 158 | props: [{ 159 | name: 'Local Clipping', 160 | prop: 'localClippingEnabled', 161 | type: 'boolean', 162 | default: false, 163 | }], 164 | }; 165 | 166 | const Scene = { 167 | name: 'Scene', 168 | type: 'group', 169 | props: [{ 170 | name: 'Sort Objects', 171 | prop: 'sortObjects', 172 | type: 'boolean', 173 | default: true, 174 | }], 175 | }; 176 | 177 | const MorphLimits = { 178 | name: 'Morph Limits', 179 | type: 'group', 180 | props: [{ 181 | name: 'Max Morph Targets', 182 | prop: 'maxMorphTargets', 183 | type: 'int', 184 | default: 8, 185 | min: 0, 186 | max: 8, 187 | }, { 188 | name: 'Max Morph Normals', 189 | prop: 'maxMorphNormals', 190 | type: 'int', 191 | default: 4, 192 | min: 0, 193 | max: 4, 194 | }], 195 | }; 196 | 197 | const WebGLRenderer = { 198 | type: 'renderer', 199 | props: [ 200 | { 201 | name: 'Check Shader Errors', 202 | prop: 'debug.checkShaderErrors', 203 | type: 'boolean', 204 | default: true, 205 | }, { 206 | name: 'Physically Correct Lights', 207 | prop: 'physicallyCorrectLights', 208 | type: 'boolean', 209 | default: false, 210 | }, { 211 | name: 'Gamma', 212 | prop: 'gammaFactor', 213 | type: 'number', 214 | default: 2, 215 | }, { 216 | name: 'Output Encoding', 217 | prop: 'outputEncoding', 218 | type: 'enum', 219 | enumType: 'encoding', 220 | }, 221 | ToneMapping, 222 | ShadowMap, 223 | BufferClearing, 224 | Capabilities, 225 | Clipping, 226 | Scene, 227 | MorphLimits, 228 | ], 229 | }; 230 | 231 | export default { 232 | WebGLRenderer: WebGLRenderer, 233 | WebGL1Renderer: WebGLRenderer, 234 | }; 235 | -------------------------------------------------------------------------------- /src/app/data/textures.js: -------------------------------------------------------------------------------- 1 | const Filters = { 2 | name: 'Filters', 3 | type: 'group', 4 | props: [{ 5 | name: 'Min Filter', 6 | prop: 'minFilter', 7 | type: 'enum', 8 | }, { 9 | name: 'Mag Filter', 10 | prop: 'magFilter', 11 | type: 'enum', 12 | }] 13 | }; 14 | 15 | const Wrapping = object => ({ 16 | name: 'Wrapping', 17 | type: 'group', 18 | // wrapR is currently used with DataTexture3D but not serialized, 19 | // let's not display it for now. 20 | props: [{ 21 | name: 'Wrap S', 22 | prop: 'wrap[0]', 23 | type: 'enum', 24 | enumType: 'wrapping', 25 | }, { 26 | name: 'Wrap T', 27 | prop: 'wrap[1]', 28 | type: 'enum', 29 | enumType: 'wrapping', 30 | }] 31 | }); 32 | 33 | const Transform = { 34 | name: 'Transform', 35 | type: 'group', 36 | props: [{ 37 | name: 'Offset', 38 | prop: 'offset', 39 | type: 'vec2', 40 | }, { 41 | name: 'Repeat', 42 | prop: 'repeat', 43 | type: 'vec2', 44 | }, { 45 | name: 'Rotation', 46 | prop: 'rotation', 47 | type: 'radians', 48 | }, { 49 | name: 'Center', 50 | prop: 'center', 51 | type: 'vec2', 52 | }, { 53 | name: 'matrixAutoUpdate', 54 | prop: 'matrixAutoUpdate', 55 | type: 'boolean', 56 | default: true, 57 | }, { 58 | name: 'Matrix', 59 | prop: 'matrix', 60 | type: 'mat3', 61 | }] 62 | }; 63 | 64 | const Texture = { 65 | type: 'texture', 66 | props: [{ 67 | name: 'Mapping', 68 | prop: 'mapping', 69 | type: 'enum', 70 | }, { 71 | name: 'Encoding', 72 | prop: 'encoding', 73 | type: 'enum', 74 | }, { 75 | name: 'Format', 76 | prop: 'format', 77 | type: 'enum', 78 | }, { 79 | name: 'Byte Type', 80 | prop: 'type', 81 | type: 'enum', 82 | }, { 83 | name: 'Anisotropy', 84 | prop: 'anisotropy', 85 | type: 'number', 86 | min: 0, 87 | }, { 88 | name: 'Flip Y', 89 | prop: 'flipY', 90 | type: 'boolean', 91 | default: true, 92 | }, { 93 | name: 'Generate Mipmaps', 94 | prop: 'generateMipmaps', 95 | type: 'boolean', 96 | default: true, 97 | }, { 98 | name: 'Premultiply Alpha', 99 | prop: 'premultiplyAlpha', 100 | type: 'boolean', 101 | default: false, 102 | }, 103 | Filters, 104 | Wrapping, 105 | Transform, 106 | ] 107 | } 108 | 109 | export default { 110 | Texture: Texture, 111 | CanvasTexture: Texture, 112 | CompressedTexture: Texture, 113 | CubeTexture: Texture, 114 | DataTexture: Texture, 115 | DataTexture2DArray: Texture, 116 | DataTexture3D: Texture, 117 | DepthTexture: Texture, 118 | VideoTexture: Texture, 119 | } 120 | -------------------------------------------------------------------------------- /src/app/elements/ImagePreviewElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../../web_modules/lit-element.js' 2 | 3 | const $onActivate = Symbol('onActivate'); 4 | const $onLoad = Symbol('onLoad'); 5 | 6 | export default class ImagePreviewElement extends LitElement { 7 | static get properties() { 8 | return { 9 | width: { type: Number, reflect: true }, 10 | height: { type: Number, reflect: true }, 11 | } 12 | } 13 | 14 | constructor() { 15 | super(); 16 | this.width = 0; 17 | this.height = 0; 18 | } 19 | 20 | render() { 21 | const image = null;//this.getEntity(); 22 | 23 | if (!image) { 24 | return html`None`; 25 | } 26 | 27 | console.log('render imagepreview',image); 28 | return html` 29 | 43 | 44 |
${this.width + ' x ' + this.height}
45 | `; 46 | } 47 | 48 | [$onLoad](e) { 49 | const image = e.composedPath()[0]; 50 | this.width = image.naturalWidth; 51 | this.height = image.naturalHeight; 52 | } 53 | 54 | [$onActivate](e) { 55 | this.dispatchEvent(new CustomEvent('select-entity', { 56 | detail: { 57 | uuid: this.uuid, 58 | }, 59 | bubbles: true, 60 | composed: true, 61 | })); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/app/elements/ParametersViewElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../web_modules/lit-element.js' 2 | import RendererTypes from '../data/renderers.js'; 3 | import ObjectTypes from '../data/objects.js'; 4 | import LightTypes from '../data/lights.js'; 5 | import MaterialTypes from '../data/materials.js'; 6 | import GeometryTypes from '../data/geometry.js'; 7 | import TextureTypes from '../data/textures.js'; 8 | import { getEntityName } from '../utils.js'; 9 | 10 | // https://stackoverflow.com/a/6491621 11 | const propByString = function(o, s) { 12 | s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties 13 | s = s.replace(/^\./, ''); // strip a leading dot 14 | var a = s.split('.'); 15 | for (var i = 0, n = a.length; i < n; ++i) { 16 | var k = a[i]; 17 | if (k in o) { 18 | o = o[k]; 19 | } else { 20 | return; 21 | } 22 | } 23 | return o; 24 | } 25 | 26 | const $onRefresh = Symbol('onRefresh'); 27 | const CommonProps = { 28 | Type: { 29 | name: 'Type', 30 | type: 'string', 31 | prop: 'baseType', 32 | }, 33 | UUID: { 34 | name: 'UUID', 35 | type: 'string', 36 | prop: 'uuid', 37 | }, 38 | Name: { 39 | name: 'Name', 40 | type: 'string', 41 | prop: 'name', 42 | } 43 | }; 44 | 45 | function propsToElements(entity, elements, props, entities) { 46 | for (let prop of props) { 47 | if (typeof prop === 'function') { 48 | const result = prop(entity); 49 | if (result) { 50 | prop = result; 51 | } else { 52 | continue; 53 | } 54 | } 55 | 56 | if (prop.type === 'group') { 57 | const subProps = []; 58 | propsToElements(entity, subProps, [...prop.props]); 59 | elements.push(html` 60 |
${prop.name}
61 | ${subProps} 62 |
`); 63 | continue; 64 | } else { 65 | const { name, type, prop: propName, enumType, default: def, readonly } = prop; 66 | 67 | let value = propByString(entity, propName); 68 | if (value === undefined) { 69 | value = def; 70 | } 71 | 72 | // For number/int types 73 | let min = 'min' in prop ? prop.min : -Infinity; 74 | let max = 'max' in prop ? prop.max : Infinity; 75 | let step = 'step' in prop ? prop.step : 76 | type === 'int' ? 1 : 0.01; 77 | let precision = 'precision' in prop ? prop.precision : 78 | type === 'int' ? 0 : 3; 79 | 80 | // For object types (geometry, material, texture) 81 | let associatedData = {}; 82 | if (value && entities && ['geometry', 'material', 'texture'].indexOf(prop.type) !== -1) { 83 | associatedData = entities[value] || {}; 84 | } 85 | 86 | elements.push(html` 87 | 100 | `); 101 | } 102 | } 103 | } 104 | 105 | export default class ParametersViewElement extends LitElement { 106 | static get properties() { 107 | return { 108 | uuid: { type: String }, 109 | // Object with uuids as keys to entity data. Most objects will 110 | // only contain the selected entity's data (matching the uuid), 111 | // however things like Mesh will also contain any associated 112 | // material or geometry, etc. 113 | entities: { type: Object }, 114 | } 115 | } 116 | 117 | [$onRefresh](e) { 118 | this.dispatchEvent(new CustomEvent('command', { 119 | detail: { 120 | type: 'refresh', 121 | }, 122 | bubbles: true, 123 | composed: true, 124 | })); 125 | } 126 | 127 | render() { 128 | const entityData = (this.entities && this.entities[this.uuid]) || null; 129 | const entityTitle = entityData ? getEntityName(entityData) : ''; 130 | const elements = []; 131 | 132 | if (entityData) { 133 | let definition = RendererTypes[entityData.baseType] || 134 | ObjectTypes[entityData.baseType] || 135 | LightTypes[entityData.baseType] || 136 | MaterialTypes[entityData.baseType] || 137 | GeometryTypes[entityData.baseType] || 138 | TextureTypes[entityData.baseType]; 139 | if (!definition) { 140 | definition = ObjectTypes.Object3D; 141 | } 142 | 143 | const commonProps = entityData.type === 'renderer' ? [CommonProps.Type, CommonProps.Name] : 144 | [CommonProps.Type, CommonProps.UUID, CommonProps.Name]; 145 | propsToElements(entityData, elements, [...commonProps, ...definition.props], this.entities); 146 | } 147 | 148 | return html` 149 | 189 | 190 | 191 | 192 |
193 | ${elements} 194 |
195 | `; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/app/elements/RendererViewElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../web_modules/lit-element.js' 2 | 3 | const $onPoll = Symbol('onPoll'); 4 | const RENDERER_POLL_INTERVAL = 1000; // ms 5 | 6 | export default class RendererViewElement extends LitElement { 7 | static get properties() { 8 | return { 9 | rendererId: { type: String, }, 10 | renderingInfo: { type: Object, }, 11 | enabled: { type: Boolean, reflect: true }, 12 | }; 13 | } 14 | 15 | constructor() { 16 | super(); 17 | this[$onPoll] = this[$onPoll].bind(this); 18 | } 19 | 20 | disconnectedCallback() { 21 | if (this[$onPoll].timer) { 22 | window.clearInterval(this[$onPoll].timer); 23 | } 24 | super.disconnectedCallback(); 25 | } 26 | 27 | [$onPoll]() { 28 | if (this.rendererId) { 29 | this.dispatchEvent(new CustomEvent('command', { 30 | detail: { 31 | type: 'request-rendering-info', 32 | uuid: this.rendererId, 33 | }, 34 | bubbles: true, 35 | composed: true, 36 | })); 37 | } 38 | } 39 | 40 | shouldUpdate(props) { 41 | if (props.has('enabled')) { 42 | if (this.enabled) { 43 | this[$onPoll].timer = window.setInterval(this[$onPoll], RENDERER_POLL_INTERVAL); 44 | } else { 45 | window.clearInterval(this[$onPoll].timer); 46 | } 47 | } 48 | return true; 49 | } 50 | 51 | render() { 52 | const activeRenderer = this.rendererId; 53 | const hasRenderingInfo = !!this.renderingInfo; 54 | 55 | const info = hasRenderingInfo ? this.renderingInfo.info : { 56 | render: { 57 | frame: 0, 58 | calls: 0, 59 | triangles: 0, 60 | points: 0, 61 | lines: 0, 62 | }, 63 | memory: { 64 | geometries: 0, 65 | textures: 0, 66 | }, 67 | programs: 0, 68 | }; 69 | 70 | return html` 71 | 97 | 98 |
    99 |
  • frame${info.render.frame}
  • 100 |
  • draw calls${info.render.calls}
  • 101 |
  • triangles${info.render.triangles}
  • 102 |
  • points${info.render.points}
  • 103 |
  • lines${info.render.lines}
  • 104 |
105 | 106 | 107 | 108 |
    109 |
  • geometries${info.memory.geometries || 0}
  • 110 |
  • textures${info.memory.textures || 0}
  • 111 |
  • programs${info.programs || 0}
  • 112 |
113 | `; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/app/elements/ResourcesViewElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../web_modules/lit-element.js' 2 | import { getEntityName } from '../utils.js'; 3 | 4 | const $onTreeItemSelect = Symbol('onTreeItemSelect'); 5 | const $onRefreshClick = Symbol('onRefreshClick'); 6 | 7 | export default class ResourcesViewElement extends LitElement { 8 | static get properties() { 9 | return { 10 | title: { type: String, reflect: true }, 11 | selected: { type: String, reflect: true }, 12 | resources: { type: Array }, 13 | } 14 | } 15 | 16 | connectedCallback() { 17 | super.connectedCallback(); 18 | this.addEventListener('tree-item-select', this[$onTreeItemSelect]); 19 | } 20 | 21 | disconnectedCallback() { 22 | this.removeEventListener('tree-item-select', this[$onTreeItemSelect]); 23 | super.disconnectedCallback(); 24 | } 25 | 26 | render() { 27 | const title = this.title; 28 | const resources = this.resources || []; 29 | 30 | let nodes = resources.map((obj, i) => { 31 | let selected = obj.uuid && this.selected && this.selected === obj.uuid; 32 | let name = getEntityName(obj); 33 | return html` 34 | 39 |
${name}
40 |
41 | `; 42 | }); 43 | 44 | return html` 45 | 68 | 69 | 70 | 71 | 77 | ${nodes} 78 | 79 | 88 | 89 | `; 90 | } 91 | 92 | updated() { 93 | const selected = this.shadowRoot.querySelector('tree-item[selected]'); 94 | if (selected && selected.parentElement) { 95 | selected.parentElement.open = true; 96 | } 97 | } 98 | 99 | [$onRefreshClick](e) { 100 | this.dispatchEvent(new CustomEvent('command', { 101 | detail: { 102 | type: 'refresh', 103 | }, 104 | bubbles: true, 105 | composed: true, 106 | })); 107 | } 108 | 109 | [$onTreeItemSelect](e) { 110 | e.stopPropagation(); 111 | const treeItem = e.composedPath()[0]; 112 | const uuid = treeItem.getAttribute('unique'); 113 | this.dispatchEvent(new CustomEvent('command', { 114 | detail: { 115 | type: 'select-entity', 116 | uuid, 117 | }, 118 | bubbles: true, 119 | composed: true, 120 | })); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/app/elements/SceneViewElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../web_modules/lit-element.js' 2 | import { getEntityName } from '../utils.js'; 3 | import ChromeSelectStyle from './shared-styles/chrome-select.js'; 4 | 5 | const $createSceneGraphNode = Symbol('createSceneGraphNode'); 6 | const $onRefreshClick = Symbol('onRefreshClick'); 7 | const $onSceneSelect = Symbol('onSceneSelect'); 8 | const $onContentUpdate = Symbol('onContentUpdate'); 9 | const $onTreeItemSelect = Symbol('onTreeItemSelect'); 10 | 11 | export default class SceneViewElement extends LitElement { 12 | static get properties() { 13 | return { 14 | graph: { type: Object}, 15 | scenes: { type: Array }, 16 | activeScene: { type: String, }, 17 | activeEntity: { type: String, }, 18 | } 19 | } 20 | 21 | constructor() { 22 | super(); 23 | this[$onRefreshClick] = this[$onRefreshClick].bind(this); 24 | this[$onTreeItemSelect] = this[$onTreeItemSelect].bind(this); 25 | this[$onContentUpdate] = this[$onContentUpdate].bind(this); 26 | } 27 | 28 | connectedCallback() { 29 | super.connectedCallback(); 30 | this.addEventListener('tree-item-select', this[$onTreeItemSelect]); 31 | } 32 | 33 | disconnectedCallback() { 34 | super.disconnectedCallback(); 35 | this.removeEventListener('tree-item-select', this[$onTreeItemSelect]); 36 | } 37 | 38 | render() { 39 | const { activeScene, activeEntity, scenes, graph } = this; 40 | 41 | if (!scenes) { 42 | return html`
`; 43 | } 44 | 45 | let sceneGraphNode; 46 | if (graph && activeScene) { 47 | sceneGraphNode = this[$createSceneGraphNode](graph, activeScene, activeEntity); 48 | } 49 | 50 | return html` 51 | 72 | 73 | 76 | 77 | 78 | ${sceneGraphNode} 79 | `; 80 | } 81 | 82 | [$createSceneGraphNode](graph, uuid, selected, depth=0) { 83 | const obj = graph[uuid]; 84 | 85 | return html` 86 | 96 |
${getEntityName(obj)}
97 | ${obj.children.map(uuid => this[$createSceneGraphNode](graph, uuid, selected, depth + 1))} 98 |
99 | `; 100 | } 101 | 102 | 103 | [$onRefreshClick](e) { 104 | if (this.activeScene) { 105 | this.dispatchEvent(new CustomEvent('command', { 106 | detail: { 107 | type: 'refresh', 108 | }, 109 | bubbles: true, 110 | composed: true, 111 | })); 112 | } 113 | } 114 | 115 | [$onSceneSelect](e) { 116 | this.dispatchEvent(new CustomEvent('command', { 117 | detail: { 118 | type: 'select-scene', 119 | uuid: e.target.value, 120 | }, 121 | bubbles: true, 122 | composed: true, 123 | })); 124 | } 125 | 126 | [$onContentUpdate](e) { 127 | if (this.app.content.getEntityCategory(e.detail.uuid) === 'scene') { 128 | // Maybe the selector should be pulled into its own component, 129 | // this is a bit messy. 130 | this.requestUpdate(); 131 | } 132 | } 133 | 134 | [$onTreeItemSelect](e) { 135 | e.stopPropagation(); 136 | const treeItem = e.composedPath()[0]; 137 | const uuid = treeItem.getAttribute('uuid'); 138 | this.dispatchEvent(new CustomEvent('command', { 139 | detail: { 140 | type: 'select-entity', 141 | uuid, 142 | }, 143 | bubbles: true, 144 | composed: true, 145 | })); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/app/elements/TabBarElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../web_modules/lit-element.js' 2 | 3 | export default class TitleBarElement extends LitElement { 4 | 5 | static get properties() { 6 | return { 7 | } 8 | } 9 | 10 | render() { 11 | 12 | return html` 13 | 43 | 44 | `; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/app/elements/TitleBarElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../web_modules/lit-element.js' 2 | 3 | export default class TitleBarElement extends LitElement { 4 | 5 | static get properties() { 6 | return { 7 | title: {type: String, reflect: true} 8 | } 9 | } 10 | 11 | render() { 12 | 13 | return html` 14 | 46 | 47 | ${this.title} 48 | 49 | 50 | `; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/app/elements/shared-styles/chrome-select.js: -------------------------------------------------------------------------------- 1 | export default ` 2 | /* 3 | * Styling taken from css/inspectorCommon.css from chrome devtools 4 | */ 5 | select { 6 | /* Form elements do not automatically inherit font style from ancestors. */ 7 | font-family: inherit; 8 | font-size: inherit; 9 | } 10 | 11 | .chrome-select { 12 | -webkit-appearance: none; 13 | -webkit-user-select: none; 14 | border: 1px solid rgba(0, 0, 0, 0.2); 15 | border-radius: 2px; 16 | color: #333; 17 | font: inherit; 18 | margin: 0; 19 | outline: none; 20 | padding-right: 20px; 21 | padding-left: 6px; 22 | background-image: -webkit-image-set(url('assets/chromeSelect.png') 1x, url('assets/chromeSelect_2x.png') 2x); 23 | background-color: hsl(0, 0%, 98%); 24 | background-position: right center; 25 | background-repeat: no-repeat; 26 | min-height: 24px; 27 | min-width: 16px; 28 | background-size: 15px; 29 | } 30 | 31 | .chrome-select:enabled:active, 32 | .chrome-select:enabled:focus, 33 | .chrome-select:enabled:hover { 34 | background-color: hsl(0, 0%, 96%); 35 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); 36 | } 37 | 38 | .chrome-select:enabled:active { 39 | background-color: #f2f2f2; 40 | } 41 | 42 | .chrome-select:enabled:focus { 43 | border-color: transparent; 44 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1), 0 0 0 2px rgba(66, 133, 244, 0.4); 45 | } 46 | 47 | .chrome-select:disabled { 48 | opacity: 0.38; 49 | } 50 | 51 | .chrome-select optgroup, 52 | .chrome-select option { 53 | background-color: #EEEEEE; 54 | color: #222; 55 | } 56 | `; 57 | -------------------------------------------------------------------------------- /src/app/elements/values/EnumValueElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../../web_modules/lit-element.js' 2 | import { ConstantTypes } from '../../constants.js'; 3 | import * as constants from '../../../../web_modules/three/src/constants.js'; 4 | import ChromeSelectStyle from '../shared-styles/chrome-select.js'; 5 | 6 | const $onInput = Symbol('onInput'); 7 | 8 | export default class EnumValueElement extends LitElement { 9 | static get properties() { 10 | return { 11 | uuid: { type: String, reflect: true }, 12 | // Type of enum, e.g. "drawMode", "side", "blendMode" 13 | type: { type: String, reflect: true }, 14 | // Numerical value of enum. 15 | value: { type: String, reflect: true }, 16 | } 17 | } 18 | 19 | constructor() { 20 | super(); 21 | this[$onInput] = this[$onInput].bind(this); 22 | } 23 | 24 | render() { 25 | 26 | if (!ConstantTypes[this.type]) { 27 | return html``; 28 | } 29 | 30 | const options = ConstantTypes[this.type].map((c,i) => { 31 | let value = constants[c]; 32 | 33 | // Let 'null' be a special enum that is -1 34 | if (c === 'null') { 35 | value = -1; 36 | } 37 | 38 | if (typeof value !== 'number') { 39 | throw new Error(`invalid constant value for ${c}`); 40 | } 41 | const selected = this.value === undefined ? i === 0 : this.value === value; 42 | return html``; 43 | }); 44 | 45 | return html` 46 | 53 | 54 | `; 55 | } 56 | 57 | [$onInput](e) { 58 | const selected = [...e.target.querySelectorAll('option')].filter(o => o.selected)[0]; 59 | const value = +selected.value; 60 | 61 | if (value !== null) { 62 | this.dispatchEvent(new CustomEvent('change', { 63 | detail: { 64 | value, 65 | }, 66 | bubbles: true, 67 | composed: true, 68 | })); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/app/elements/values/KeyValueElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../../web_modules/lit-element.js' 2 | import { getEntityName, cssStringToHexNumber, hexNumberToCSSString } from '../../utils.js'; 3 | 4 | const $onChange = Symbol('onChange'); 5 | const $onDependencyClick = Symbol('onDependencyClick'); 6 | const anchor = document.createElement('a'); 7 | let kvIterator = 0; 8 | 9 | export default class KeyValueElement extends LitElement { 10 | static get properties() { 11 | return { 12 | uuid: { type: String, reflect: true }, 13 | // @TODO probably should use less generic/collision-y 14 | // attribute names. 15 | keyName: { type: String, reflect: true, attribute: 'key-name'}, 16 | value: { type: String, reflect: true }, 17 | type: { type: String, reflect: true }, 18 | enumType: { type: String, reflect: true, attribute: 'enum-type' }, 19 | property: { type: String, reflect: true }, 20 | readonly: { type: Boolean }, 21 | // For number types only 22 | min: { type: Number, reflect: true }, 23 | max: { type: Number, reflect: true }, 24 | step: { type: Number, reflect: true }, 25 | precision: { type: Number, reflect: true }, 26 | // Optional data for data types (material, geometry) 27 | data: { type: Object }, 28 | } 29 | } 30 | 31 | constructor() { 32 | super(); 33 | // Currently no way to handle a true label/input match 34 | // across shadow boundaries? Can this be handled better? 35 | // https://github.com/whatwg/html/issues/3219 36 | this._id = `key-value-element-${kvIterator++}`; 37 | this.precision = 1; 38 | this.step = 1; 39 | this.min = -Infinity; 40 | this.max = Infinity; 41 | } 42 | 43 | onDataURLClick(e) { 44 | try { 45 | let stringified = JSON.stringify(this.value, null, 2); 46 | let blob = new Blob([stringified], { type: 'application/json' }); 47 | let url = window.URL.createObjectURL(blob); 48 | anchor.setAttribute('href', url); 49 | anchor.setAttribute('target', '_window'); 50 | anchor.click(); 51 | // Clean it up immediately so we're not storing 52 | // large buffers for the lifetime of the tools 53 | window.URL.revokeObjectURL(url); 54 | } catch (e) { 55 | } 56 | e.preventDefault(); 57 | } 58 | 59 | render() { 60 | 61 | let valueElement; 62 | 63 | if (this.value == null) { 64 | valueElement = html``; 65 | } else if (this.readonly) { 66 | valueElement = this.value; 67 | } else { 68 | switch (this.type) { 69 | case 'array': 70 | if (this.value) { 71 | valueElement = html` 72 | this.onDataURLClick(e)}> 73 | array 74 | `; 75 | } 76 | else { 77 | valueElement = html`[]`; 78 | } 79 | break; 80 | case 'enum': 81 | let enumType = this.enumType || this.property; 82 | valueElement = html``; 83 | break; 84 | case 'vec2': 85 | case 'vec3': 86 | case 'vec4': 87 | const arity = this.type === 'vec2' ? 2 : 88 | this.type === 'vec3' ? 3 : 4; 89 | valueElement = [...new Array(arity)].map((_, i) => html``); 98 | break; 99 | case 'image': 100 | case 'texture': 101 | case 'material': 102 | case 'geometry': 103 | if (this.data) { 104 | const name = getEntityName(this.data); 105 | valueElement = html`
${name}
`; 106 | } 107 | break; 108 | case 'color': 109 | valueElement = html``; 110 | break; 111 | case 'boolean': 112 | valueElement = html``; 113 | break; 114 | case 'number': 115 | case 'int': 116 | case 'angle': 117 | valueElement = html``; 125 | break; 126 | case 'string': 127 | valueElement = this.value; 128 | break; 129 | default: 130 | valueElement = this.value; 131 | } 132 | } 133 | 134 | return html` 135 | 182 | 183 |
184 | ${valueElement} 185 |
186 | `; 187 | } 188 | 189 | [$onDependencyClick](e) { 190 | const target = e.composedPath()[0]; 191 | const uuid = target.getAttribute('data-uuid'); 192 | if (uuid !== null) { 193 | this.dispatchEvent(new CustomEvent('command', { detail: { 194 | type: 'select-entity', 195 | uuid, 196 | }, 197 | bubbles: true, 198 | composed: true, 199 | })); 200 | } 201 | } 202 | 203 | [$onChange](e) { 204 | 205 | const target = e.composedPath()[0]; 206 | 207 | let value = null; 208 | let dataType = null; 209 | let property = this.property; 210 | switch (this.type) { 211 | case 'color': 212 | value = target.value ? cssStringToHexNumber(target.value) : 0; 213 | dataType = 'color'; 214 | break; 215 | case 'boolean': 216 | value = !!target.checked; 217 | dataType = 'boolean'; 218 | break; 219 | case 'number': 220 | case 'radians': 221 | dataType = 'number'; 222 | value = target.value; 223 | break; 224 | case 'enum': 225 | dataType = 'number'; 226 | value = e.detail.value; 227 | break; 228 | case 'vec2': 229 | case 'vec3': 230 | case 'vec4': 231 | dataType = this.type; 232 | value = e.detail.value; 233 | // Add 'x', 'y', 'z', 'w' to the property name 234 | property = `${this.property}.${target.getAttribute('axis')}`; 235 | break; 236 | default: 237 | value = target.value; 238 | } 239 | 240 | if (value !== null) { 241 | this.dispatchEvent(new CustomEvent('command', { detail: { 242 | type: 'update-property', 243 | uuid: this.uuid, 244 | property, 245 | dataType, 246 | value, 247 | }, 248 | bubbles: true, 249 | composed: true, 250 | })); 251 | } 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/app/elements/values/MaterialValueElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../../web_modules/lit-element.js' 2 | import { hexNumberToCSSString } from '../../utils.js'; 3 | 4 | const $onActivate = Symbol('onActivate'); 5 | 6 | export default class MaterialValueElement extends LitElement { 7 | static get properties() { 8 | return { 9 | 10 | } 11 | } 12 | 13 | render() { 14 | const material = null; 15 | 16 | if (!material) { 17 | return null; 18 | } 19 | 20 | const color = material.color ? hexNumberToCSSString(material.color) : 'white'; 21 | return html` 22 | 48 |
49 |
50 |
${material.type}
51 |
52 | `; 53 | } 54 | 55 | [$onActivate](e) { 56 | this.app.dispatchEvent(new CustomEvent('select-entity', { detail: { 57 | uuid: this.uuid, 58 | }})); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/app/elements/values/TextureValueElement.js: -------------------------------------------------------------------------------- 1 | import { LitElement, html } from '../../../../web_modules/lit-element.js' 2 | import { getEntityName } from '../../utils.js'; 3 | 4 | const $onActivate = Symbol('onActivate'); 5 | 6 | export default class TextureValueElement extends LitElement { 7 | 8 | static get properties() { 9 | return { 10 | } 11 | } 12 | 13 | render() { 14 | const texture = null; 15 | 16 | if (!texture) { 17 | return null; 18 | } 19 | 20 | const name = getEntityName(texture); 21 | return html` 22 | 48 |
49 |
50 |
${name}
51 |
52 | `; 53 | } 54 | 55 | [$onActivate](e) { 56 | this.app.dispatchEvent(new CustomEvent('select-entity', { detail: { 57 | uuid: this.uuid, 58 | }})); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/app/fonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/fonts/fa-regular-400.eot -------------------------------------------------------------------------------- /src/app/fonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/fonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /src/app/fonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/fonts/fa-regular-400.woff -------------------------------------------------------------------------------- /src/app/fonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/fonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /src/app/fonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/fonts/fa-solid-900.eot -------------------------------------------------------------------------------- /src/app/fonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/fonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /src/app/fonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/fonts/fa-solid-900.woff -------------------------------------------------------------------------------- /src/app/fonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/threejs/three-devtools/f2129475a34d32637cdc8aacea462a1fa40e5d30/src/app/fonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /src/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /src/app/index.js: -------------------------------------------------------------------------------- 1 | import browser from '../../web_modules/webextension-polyfill/dist/browser-polyfill.js'; 2 | import getAgent from '../../web_modules/@egjs/agent.js'; 3 | import AppElement from './elements/AppElement.js'; 4 | 5 | import RendererViewElement from './elements/RendererViewElement.js'; 6 | import SceneViewElement from './elements/SceneViewElement.js'; 7 | import ResourcesViewElement from './elements/ResourcesViewElement.js'; 8 | import ParametersViewElement from './elements/ParametersViewElement.js'; 9 | 10 | import TitleBarElement from './elements/TitleBarElement.js'; 11 | 12 | import ImagePreviewElement from './elements/ImagePreviewElement.js'; 13 | import KeyValueElement from './elements/values/KeyValueElement.js'; 14 | import MaterialValueElement from './elements/values/MaterialValueElement.js'; 15 | import TextureValueElement from './elements/values/TextureValueElement.js'; 16 | import EnumValueElement from './elements/values/EnumValueElement.js'; 17 | import TabBarElement from './elements/TabBarElement.js'; 18 | 19 | import NumberInputElement from './common-elements/NumberInputElement.js'; 20 | import TreeItemElement from './common-elements/TreeItemElement.js'; 21 | import AccordionViewElement from './common-elements/AccordionViewElement.js'; 22 | import DevtoolsMessageElement from './common-elements/DevtoolsMessageElement.js'; 23 | import DevtoolsIconElement from './common-elements/DevtoolsIconElement.js'; 24 | import DevtoolsButtonElement from './common-elements/DevtoolsButtonElement.js'; 25 | import DevtoolsIconButtonElement from './common-elements/DevtoolsIconButtonElement.js'; 26 | import IconElement from './common-elements/IconElement.js'; 27 | 28 | ////// 29 | globalThis.browser = browser; 30 | 31 | window.customElements.define('three-devtools-app', AppElement); 32 | 33 | window.customElements.define('renderer-view', RendererViewElement); 34 | window.customElements.define('scene-view', SceneViewElement); 35 | window.customElements.define('resources-view', ResourcesViewElement); 36 | window.customElements.define('parameters-view', ParametersViewElement); 37 | 38 | window.customElements.define('title-bar', TitleBarElement); 39 | 40 | window.customElements.define('image-preview', ImagePreviewElement); 41 | window.customElements.define('key-value', KeyValueElement); 42 | window.customElements.define('material-value', MaterialValueElement); 43 | window.customElements.define('texture-value', TextureValueElement); 44 | window.customElements.define('enum-value', EnumValueElement); 45 | window.customElements.define('tab-bar', TabBarElement); 46 | 47 | window.customElements.define('number-input', NumberInputElement); 48 | window.customElements.define('tree-item', TreeItemElement); 49 | window.customElements.define('accordion-view', AccordionViewElement); 50 | window.customElements.define('devtools-message', DevtoolsMessageElement); 51 | window.customElements.define('devtools-icon', DevtoolsIconElement); 52 | window.customElements.define('devtools-button', DevtoolsButtonElement); 53 | window.customElements.define('devtools-icon-button', DevtoolsIconButtonElement); 54 | window.customElements.define('x-icon', IconElement); 55 | 56 | function changeTheme(themeName) { 57 | // chrome "default" = firefox "light" 58 | if (themeName === 'default' || themeName === 'light') { 59 | document.documentElement.classList.remove('-theme-with-dark-background'); 60 | // assume dark theme for anything else (including third-party themes) 61 | } else { 62 | document.documentElement.classList.add('-theme-with-dark-background'); 63 | } 64 | } 65 | 66 | // firefox devtools are not reloaded when the theme is changed 67 | // so we have to add a listener for it 68 | if (browser.devtools.panels.onThemeChanged) { 69 | browser.devtools.panels.onThemeChanged.addListener(changeTheme); 70 | } 71 | 72 | // match browser devtools theme setting 73 | changeTheme(browser.devtools.panels.themeName); 74 | 75 | const agent = getAgent(window.navigator.userAgent); 76 | console.log('Parsed agent:', agent); 77 | if (agent.os.name === 'window') { 78 | document.body.classList.add('platform-windows'); 79 | } else if (agent.os.name === 'mac') { 80 | document.body.classList.add('platform-mac'); 81 | } else if (agent.os.name === 'unknown') { 82 | document.body.classList.add('platform-linux'); 83 | } 84 | 85 | window.addEventListener('error', e => { 86 | document.querySelector('three-devtools-app').setError(e.message); 87 | }); 88 | -------------------------------------------------------------------------------- /src/app/injection.js: -------------------------------------------------------------------------------- 1 | import browser from '../../web_modules/webextension-polyfill/dist/browser-polyfill.js'; 2 | import utils from '../content/utils.js'; 3 | import TransformControls from '../content/TransformControls.js'; 4 | import EntityCache from '../content/EntityCache.js'; 5 | import ThreeDevTools from '../content/ThreeDevTools.js'; 6 | import DevToolsScene from '../content/DevToolsScene.js'; 7 | import InstrumentedToJSON from '../content/toJSON.js'; 8 | import THREE from '../content/three.js'; 9 | 10 | const version = browser.runtime.getManifest().version; 11 | const red = 'rgb(255, 137, 137)'; 12 | const green = 'rgb(190, 251, 125)' 13 | const blue = 'rgb(120, 250, 228)'; 14 | 15 | export default ` 16 | 17 | console.log('%c▲%cthree-devtools%cv${version}', 18 | 'font-size:150%; color:${green}; text-shadow: -10px 0px ${red}, 10px 0px ${blue}; padding: 0 15px 0 10px;', 19 | 'font-size: 110%; background-color: #666; color:white; padding: 0 5px;', 20 | 'font-size: 110%; background-color: ${blue}; color:#666; padding: 0 5px;'); 21 | (() => { 22 | 23 | const DEBUG = false; 24 | const utils = (${utils})(); 25 | const THREE = (${THREE})(); 26 | const InstrumentedToJSON = (${InstrumentedToJSON})(); 27 | (${TransformControls})(THREE); 28 | const DevToolsScene = (${DevToolsScene})(THREE); 29 | const EntityCache = (${EntityCache})(); 30 | const devtools = new (${ThreeDevTools})(window.__THREE_DEVTOOLS__); 31 | 32 | window.__THREE_DEVTOOLS__.dispatchEvent(new CustomEvent('devtools-ready')); 33 | })(); 34 | 35 | `; 36 | -------------------------------------------------------------------------------- /src/app/styles/app.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --title-color: var(--tab-selected-fg-color); 3 | --title-background-color: var(--tab-selected-bg-color); 4 | --title-border-color: var(--divider-color); 5 | --view-border-color: var(--divider-color); 6 | 7 | --tree-item-hover-color: inherit; 8 | --tree-item-hover-background-color: rgba(56, 121, 217, 0.1); 9 | --tree-item-selected-color: var(--selection-fg-color); 10 | --tree-item-selected-background-color: var(--selection-bg-color); 11 | --tree-item-hover-selected-color: var(--selection-fg-color); 12 | --tree-item-hover-selected-background-color: var(--selection-bg-color); 13 | --tree-item-indent-per-level: 10px; 14 | --tree-item-row-height: 20px; 15 | --tree-item-border-color: var(--divider-color); 16 | --key-value-height: 30px; 17 | --key-value-divider-position: 40%; 18 | } 19 | 20 | body { 21 | margin: 0; 22 | } 23 | -------------------------------------------------------------------------------- /src/app/styles/devtools.css: -------------------------------------------------------------------------------- 1 | /** 2 | * These are mostly pulled from devtools source code 3 | */ 4 | 5 | :root { 6 | --accent-color: #1a73e8; 7 | --accent-fg-color: #1a73e8; 8 | --accent-color-hover: #3b86e8; 9 | --focus-bg-color: hsl(214, 40%, 92%); 10 | --toolbar-bg-color: #f3f3f3; 11 | --toolbar-hover-bg-color: #eaeaea; 12 | --selection-fg-color: white; 13 | --selection-inactive-fg-color: #5a5a5a; 14 | --selection-inactive-bg-color: #dadada; 15 | --tab-selected-fg-color: #333; 16 | --tab-selected-bg-color: var(--toolbar-bg-color); 17 | --drop-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05), 18 | 0 2px 4px rgba(0, 0, 0, 0.2), 19 | 0 2px 6px rgba(0, 0, 0, 0.1); 20 | --divider-color: #d0d0d0; 21 | --focus-ring-inactive-shadow: 0 0 0 1px #e0e0e0; 22 | --editor-selection-bg-color: #cfe8fc; 23 | --editor-selection-inactive-bg-color: #e0e0e0; 24 | } 25 | 26 | .-theme-with-dark-background { 27 | --accent-color: #0e639c; 28 | --accent-fg-color: #cccccc; 29 | --accent-color-hover: rgb(17, 119, 187); 30 | --focus-bg-color: hsl(214, 19%, 27%); 31 | --toolbar-bg-color: #333333; 32 | --toolbar-hover-bg-color: #202020; 33 | --selection-fg-color: #cdcdcd; 34 | --selection-inactive-fg-color: #cdcdcd; 35 | --selection-inactive-bg-color: hsl(0, 0%, 28%); 36 | --tab-selected-fg-color: #eaeaea; 37 | --tab-selected-bg-color: var(--toolbar-bg-color); 38 | --drop-shadow: 0 0 0 1px rgba(255, 255, 255, 0.2), 39 | 0 2px 4px 2px rgba(0, 0, 0, 0.2), 40 | 0 2px 6px 2px rgba(0, 0, 0, 0.1); 41 | --divider-color: #525252; 42 | --focus-ring-inactive-shadow: 0 0 0 1px #5a5a5a; 43 | --editor-selection-bg-color: hsl(207, 88%, 22%); 44 | --editor-selection-inactive-bg-color: #454545; 45 | } 46 | 47 | :root { 48 | --focus-ring-active-shadow: 0 0 0 1px var(--accent-color); 49 | --selection-bg-color: var(--accent-color); 50 | --divider-border: 1px solid var(--divider-color); 51 | } 52 | 53 | 54 | body { 55 | height: 100%; 56 | width: 100%; 57 | position: relative; 58 | overflow: hidden; 59 | margin: 0; 60 | cursor: default; 61 | font-family: '.SFNSDisplay-Regular', 'Helvetica Neue', 'Lucida Grande', sans-serif; 62 | font-size: 12px; 63 | tab-size: 4; 64 | -webkit-user-select: none; 65 | color: #222; 66 | background: white; 67 | } 68 | 69 | html.-theme-with-dark-background > body { 70 | color: #d5d5d5; 71 | background: #242424; 72 | } 73 | 74 | .platform-linux { 75 | color: rgb(48, 57, 66); 76 | font-family: Roboto, Ubuntu, Arial, sans-serif; 77 | } 78 | 79 | .platform-mac { 80 | color: rgb(48, 57, 66); 81 | font-family: '.SFNSDisplay-Regular', 'Helvetica Neue', 'Lucida Grande', sans-serif; 82 | } 83 | 84 | .platform-windows { 85 | font-family: 'Segoe UI', Tahoma, sans-serif; 86 | } 87 | -------------------------------------------------------------------------------- /src/app/utils.js: -------------------------------------------------------------------------------- 1 | 2 | const uuidRegex = /^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}){1}$/ 3 | export const isUUID = str => { 4 | return typeof str === 'string' && uuidRegex.test(str); 5 | }; 6 | 7 | export const getEntityName = entity => { 8 | return entity.name || entity.baseType; 9 | }; 10 | 11 | // These color conversions should be way more robust.. 12 | // @TODO use THREE.Color 13 | export const hexNumberToCSSString = hex => 14 | `#${("000000" + (hex).toString(16)).slice(-6)}`; 15 | 16 | export const cssStringToHexNumber = string => +`0x${string.substr(1)}`; 17 | 18 | /** 19 | * Operates on a serialized THREE object, 20 | * recursively searching through the objects for a 21 | * matching UUID. 22 | */ 23 | export const getObjectByUUID = (obj, uuid) => { 24 | if (obj.uuid === uuid) { 25 | return obj; 26 | } else if (obj.children && obj.children.length) { 27 | for (let child of obj.children) { 28 | let result = getObjectByUUID(child, uuid); 29 | if (result) { 30 | return result; 31 | } 32 | } 33 | } else { 34 | return null; 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /src/content/DevToolsScene.js: -------------------------------------------------------------------------------- 1 | export default (THREE) => { 2 | 3 | /** 4 | * `THREE` in this context is the devtools own version of three 5 | * injected into this scope only. 6 | */ 7 | return class DevToolsScene extends THREE.Scene { 8 | constructor(target, domElement, camera) { 9 | super(); 10 | this.onSelectedObjectRemove = this.onSelectedObjectRemove.bind(this); 11 | this.name = 'DevToolsScene'; 12 | this.bbHelper = new THREE.BoxHelper(); 13 | window.bbHelper = this.bbHelper; 14 | this.bbHelper.material.depthWrite = false; 15 | this.bbHelper.material.depthTest = false; 16 | this.bbHelper.visible = false; 17 | this.add(this.bbHelper); 18 | 19 | this.target = target; 20 | this.camera = camera; 21 | this.domElement = domElement; 22 | this.transformControls = new THREE.TransformControls(camera, domElement); 23 | this.transformControls.space = 'local'; 24 | this.add(this.transformControls); 25 | 26 | utils.hideObjectFromTools(this); 27 | utils.hideObjectFromTools(this.bbHelper); 28 | utils.hideObjectFromTools(this.transformControls); 29 | 30 | this.transformControls.addEventListener('change', e => { 31 | // Fire an event to __THREE_DEVTOOLS__ so that content 32 | // can handle lazy rendering, indicating a rerender is necessary. 33 | this.target.dispatchEvent(new CustomEvent('visualization-change')); 34 | }); 35 | 36 | this.transformControls.addEventListener('dragging-changed', e => { 37 | this.target.dispatchEvent(new CustomEvent('interaction-change', { 38 | detail: { 39 | active: e.value, 40 | }, 41 | })); 42 | }); 43 | } 44 | 45 | setTransformMode(mode) { 46 | if (mode && this.transformControls.mode !== mode) { 47 | this.transformControls.mode = mode; 48 | } 49 | } 50 | 51 | toggleTransformSpace() { 52 | const space = this.transformControls.space; 53 | this.transformControls.space = space === 'world' ? 'local' : 'world'; 54 | } 55 | 56 | setCamera(camera) { 57 | this.camera = camera; 58 | this.transformControls.camera = camera; 59 | } 60 | 61 | selectObject(object) { 62 | if (this.selectedObject) { 63 | this.selectedObject.removeEventListener('removed', this.onSelectedObjectRemove); 64 | this.transformControls.detach(); 65 | } 66 | 67 | this.selectedObject = object; 68 | this.bbHelper.visible = false; 69 | 70 | if (object && object.isObject3D && !object.isScene) { 71 | this.transformControls.attach(object); 72 | object.addEventListener('removed', this.onSelectedObjectRemove); 73 | } 74 | 75 | if (object) { 76 | const currentBBVersion = this.bbHelper.geometry.attributes.position.version; 77 | this.bbHelper.setFromObject(object); 78 | // Only way to determine if the object's bounding box is empty 79 | // or not without recomputing 80 | if (currentBBVersion !== this.bbHelper.geometry.attributes.position.version) { 81 | this.bbHelper.visible = true; 82 | } 83 | } 84 | 85 | this.target.dispatchEvent(new CustomEvent('visualization-change')); 86 | } 87 | 88 | onBeforeRender() { 89 | if (this.bbHelper.visible) { 90 | this.bbHelper.update(); 91 | } 92 | } 93 | 94 | onSelectedObjectRemove() { 95 | this.selectObject(null); 96 | } 97 | } 98 | }; 99 | -------------------------------------------------------------------------------- /src/content/ThreeDevTools.js: -------------------------------------------------------------------------------- 1 | export default (() => { 2 | 3 | /** 4 | * Supported events: 5 | * 6 | * `scene` 7 | * `renderer` 8 | */ 9 | return class ThreeDevTools { 10 | constructor(target) { 11 | this.USE_RENDER_OVERLAY = false; 12 | this.target = target; 13 | this.scenes = new Set(); 14 | this.renderers = new Set(); 15 | 16 | this.entityCache = new EntityCache(); 17 | this.entitiesRecentlyObserved = new Set(); 18 | 19 | this.devtoolsScene = null; 20 | 21 | this.selected = window.$t = null; 22 | 23 | // These events are dispatched by the extension, or 24 | // by the three.js library itself. Content could 25 | // also listen to these events and respond accordingly. 26 | this.target.addEventListener('observe', e => this.observe(e.detail)); 27 | this.target.addEventListener('register', e => this.register(e.detail && e.detail.revision)); 28 | this.target.addEventListener('select', e => this.select(e.detail && e.detail.uuid)); 29 | // @TODO "update" is too general -- maybe something like "set-property"? 30 | this.target.addEventListener('entity-update', e => this.update(e.detail)); 31 | window.ctor = this.target.constructor; 32 | 33 | // Underscored events are intended to be private. 34 | this.target.addEventListener('_request-rendering-info', e => this.requestRenderingInfo(e.detail && e.detail.uuid)); 35 | this.target.addEventListener('_request-entity', e => this.requestEntity(e.detail && e.detail.uuid)); 36 | this.target.addEventListener('_request-overview', e => this.requestOverview(e.detail && e.detail.type)); 37 | this.target.addEventListener('_request-scene-graph', e => this.requestSceneGraph(e.detail && e.detail.uuid)); 38 | this.target.addEventListener('_transform-controls-update', e => { 39 | if (this.devtoolsScene) { 40 | const { space, mode } = e.detail; 41 | // Space isn't a string, just a truthy value will trigger a toggle 42 | if (space) { 43 | this.devtoolsScene.toggleTransformSpace(space); 44 | } 45 | if (mode) { 46 | this.devtoolsScene.setTransformMode(mode); 47 | } 48 | } 49 | }); 50 | 51 | // The 'visualization-change' event is fired by DevToolsScene, indicating 52 | // something has changed and if not rendering on a RAF loop, a render 53 | // is necessary to render the devtools content. 54 | // Noted here as documentation. 55 | // this.target.addEventListener('visualization-change', () => {}); 56 | 57 | document.addEventListener('keydown', e => { 58 | if (!this.devtoolsScene) { 59 | return; 60 | } 61 | switch (e.key) { 62 | case 'q': this.devtoolsScene.toggleTransformSpace(); break; 63 | case 'w': this.devtoolsScene.setTransformMode('translate'); break; 64 | case 'e': this.devtoolsScene.setTransformMode('rotate'); break; 65 | case 'r': this.devtoolsScene.setTransformMode('scale'); break; 66 | } 67 | }, { passive: true }) 68 | 69 | } 70 | 71 | /** 72 | * API for extension, should not be called by content 73 | */ 74 | 75 | /** 76 | * This is the active object in the devtools viewer. 77 | */ 78 | select(uuid) { 79 | this.log('select', uuid); 80 | const selected = this.entityCache.getEntity(uuid); 81 | 82 | if (selected) { 83 | if (this.devtoolsScene) { 84 | this.devtoolsScene.selectObject(selected); 85 | } 86 | this.selected = window.$t = selected; 87 | } 88 | } 89 | 90 | update({ uuid, property, value, dataType }) { 91 | this.log('update', uuid, property, value, dataType); 92 | const entity = this.entityCache.getEntity(uuid); 93 | 94 | if (!entity) { 95 | return; 96 | } 97 | 98 | const { target, key } = utils.getTargetAndKey(entity, property); 99 | 100 | if (dataType === 'color') { 101 | if (target[key] && target[key].isColor) { 102 | target[key].setHex(value); 103 | } else { 104 | // Use our own loaded version of THREE; using just 105 | // as a color data type, it shouldn't have any conflicts. 106 | target[key] = new Color((value >> 16 & 255) / 255, 107 | (value >> 8 & 255) / 255, 108 | (value & 255) / 255); 109 | } 110 | } 111 | else if (dataType === 'enum') { 112 | target[key] = value === -1 ? null : value; 113 | } 114 | else { 115 | target[key] = value; 116 | } 117 | } 118 | 119 | register(revision) { 120 | this.log('register', arguments[0]); 121 | this.send('register', { 122 | revision, 123 | }); 124 | } 125 | 126 | requestSceneGraph(uuid) { 127 | this.log('requestSceneGraph', uuid); 128 | try { 129 | const data = this.entityCache.getSceneGraph(uuid); 130 | this.send('scene-graph', { 131 | uuid, 132 | graph: data, 133 | }); 134 | } catch (e) { 135 | // Why must this be wrapped in a try/catch 136 | // to report errors? Where's the async?? 137 | console.error(e); 138 | } 139 | } 140 | 141 | requestOverview(type) { 142 | this.log('requestOverview', type); 143 | try { 144 | const data = this.entityCache.getOverview(type); 145 | this.send('overview', { 146 | type, 147 | entities: data, 148 | }); 149 | } catch (e) { 150 | // Why must this be wrapped in a try/catch 151 | // to report errors? Where's the async?? 152 | console.error(e); 153 | } 154 | } 155 | 156 | requestEntity(uuid) { 157 | this.log('requestEntity', uuid); 158 | try { 159 | let data = this.entityCache.getSerializedEntity(uuid); 160 | if (data) { 161 | this.send('entity', data); 162 | } 163 | } catch (e) { 164 | // Why must this be wrapped in a try/catch 165 | // to report errors? Where's the async?? 166 | console.error(e); 167 | } 168 | } 169 | 170 | requestRenderingInfo(uuid) { 171 | this.log('requestRenderingInfo', uuid); 172 | let data = this.entityCache.getRenderingInfo(uuid); 173 | if (data) { 174 | this.send('rendering-info', data); 175 | } 176 | } 177 | 178 | observe(entity) { 179 | this.log('observe', entity); 180 | const uuid = this.entityCache.add(entity); 181 | 182 | if (!uuid) { 183 | this.warn(`${uuid} is unobservable`); 184 | return; 185 | } 186 | 187 | // Fire on next tick; otherwise this is called when the scene is created, 188 | // which won't have any objects. Will have to explore more in #18. 189 | // Batch up multiple scenes added in the same tick. 190 | if (this.entitiesRecentlyObserved.size === 0) { 191 | requestAnimationFrame(() => { 192 | this.send('observe', { 193 | uuids: [...this.entitiesRecentlyObserved], 194 | }); 195 | this.entitiesRecentlyObserved.clear(); 196 | }); 197 | } 198 | 199 | this.entitiesRecentlyObserved.add(uuid); 200 | } 201 | 202 | send(type, data) { 203 | this.log('emitting', type, data); 204 | try { 205 | window.postMessage({ 206 | id: 'three-devtools', 207 | type: type, 208 | data, 209 | }, '*'); 210 | } catch (e) { 211 | if (!data) { 212 | throw e; 213 | } 214 | // If this throws, it could be because of user data not being 215 | // able to be cloned. This will be much slower, but it will work. 216 | console.error('Data could not be cloned; ensure "userData" is serializable.', e); 217 | window.postMessage({ 218 | id: 'three-devtools', 219 | type, 220 | data: JSON.parse(JSON.stringify(data)) 221 | }); 222 | } 223 | } 224 | 225 | log(...message) { 226 | if (DEBUG) { 227 | console.log('%c ThreeDevTools:', 'color:red', ...message); 228 | } 229 | } 230 | 231 | warn(...message) { 232 | if (DEBUG) { 233 | console.warn('%c ThreeDevTools:', 'color:red', ...message); 234 | } 235 | } 236 | 237 | createDevToolsScene(renderer, camera) { 238 | if (this.devtoolsScene) { 239 | return this.devtoolsScene; 240 | } 241 | 242 | this.devtoolsScene = new DevToolsScene(this.target, renderer.domElement, camera); 243 | return this.devtoolsScene; 244 | } 245 | 246 | setActiveRenderer(renderer) { 247 | // Hide the overlay rendering unless 248 | // enabled while some buggy cases are ironed out 249 | if (!this.USE_RENDER_OVERLAY) { 250 | return; 251 | } 252 | const render = renderer.render; 253 | const devtools = this; 254 | 255 | let devtoolsScene; 256 | renderer.render = function (scene, camera) { 257 | const target = renderer.getRenderTarget(); 258 | render.call(this, scene, camera); 259 | 260 | if (!target) { 261 | if (!devtoolsScene) { 262 | devtoolsScene = devtools.createDevToolsScene(renderer, camera); 263 | } 264 | devtoolsScene.setCamera(camera); 265 | 266 | const autoClear = renderer.autoClear; 267 | renderer.autoClear = false; 268 | render.call(renderer, devtoolsScene, camera); 269 | renderer.autoClear = autoClear; 270 | } 271 | }; 272 | } 273 | }; 274 | 275 | })(); 276 | -------------------------------------------------------------------------------- /src/content/toJSON.js: -------------------------------------------------------------------------------- 1 | export default (() => { 2 | 3 | const isDevtoolsSerialization = meta => !!(meta && meta.devtoolsConfig); 4 | const isObject = o => Object.prototype.toString.call(o) === '[object Object]'; 5 | const tempPosition = new THREE.Vector3(); 6 | const tempRotation = new THREE.Quaternion(); 7 | const tempScale = new THREE.Vector3(); 8 | const tempEuler = new THREE.Euler(); 9 | 10 | return function InstrumentedToJSON (meta) { 11 | /** 12 | * The instrumented version of entity's `toJSON` method, 13 | * used to patch because: 14 | * 1) entity does not have toJSON method 15 | * 2) current serialization returns insufficient information 16 | * 3) throws an error on serialization 17 | * 18 | * Additionally, this makes it possible to create 19 | * instrumentation-only options that can be attached 20 | * to the `meta` object, like smarter serialization of images. 21 | */ 22 | 23 | const toJSON = this.constructor && 24 | this.constructor.prototype && 25 | this.constructor.prototype.toJSON; 26 | 27 | // Short circuit if this is something other than the devtools 28 | // serializing. 29 | if (toJSON && !isDevtoolsSerialization(meta)) { 30 | return toJSON.apply(this, arguments); 31 | } 32 | 33 | // Store our custom flags that are passed in via 34 | // the resources. 35 | const serializeChildren = meta && meta.devtoolsConfig.serializeChildren === false ? false : true; 36 | 37 | // First, discover the `baseType`, which for most 38 | // entities in three.js core, this is the same as 39 | // `type` -- however the `type` property can be modified 40 | // by a developer, especially common when extending a 41 | // base class. 42 | // Note, be sure to check subclasses before 43 | // base classes here. 44 | const baseType = utils.getBaseType(this); 45 | 46 | let textureData; 47 | if (this.isTexture) { 48 | // If `image` is a plain object (via DataTexture or WebGLRenderTarget) 49 | // or an array of plain objects (via CompressedTexture) hide it during 50 | // serialization so an attempt to turn it into a data URL doesn't throw. 51 | // Patch for DataTexture.toJSON (https://github.com/mrdoob/three.js/pull/17745) 52 | if (isObject(this.image) || (Array.isArray(this.image) && this.image.some(isObject))) { 53 | textureData = this.image; 54 | this.image = undefined; 55 | } 56 | } 57 | 58 | let children; 59 | if (this.isObject3D && !serializeChildren) { 60 | // Swap out the children array before serialization if 61 | // it's to be avoided. 62 | children = this.children; 63 | this.children = []; 64 | } 65 | 66 | // Call the original serialization method. 67 | // Note that this can fail if the toJSON was manually 68 | // overwritten e.g. not a part of a prototype. Check for its existence 69 | // first since some do not have any toJSON method, 70 | // like WebGLRenderTarget 71 | let data = toJSON ? toJSON.apply(this, arguments) : {}; 72 | 73 | // If an image was hidden to avoid serialization, 74 | // reapply it here 75 | if (textureData) { 76 | this.image = textureData; 77 | } 78 | // Reattach children 79 | if (children) { 80 | this.children = children; 81 | } 82 | 83 | // Parametric geometry cannot be rehydrated 84 | // without introducing a vector for code injection. 85 | // For serialization, stringifying is fine, 86 | // but this geometry is unrehydratable (for good reason, 87 | // as this would then get access to privileged extension code). 88 | // https://github.com/mrdoob/three.js/issues/17381 89 | else if (data.func) { 90 | data.func = this.parameters.func ? 91 | this.parameters.func.toString() : ''; 92 | } 93 | // Render targets shouldn't be in the graph directly 94 | // since their textures must be used instead, but this 95 | // may still occur in `scene.background`, where an attempt 96 | // at serialization occurs and breaks on render targets. 97 | // https://github.com/mrdoob/three.js/pull/16764 98 | else if (this.isWebGLRenderTarget) { 99 | // Would be great to actually serialize out the render target, 100 | // if given a renderer. 101 | // https://github.com/mrdoob/three.js/issues/16762 102 | } 103 | // InterleavedBufferAttributes cannot be serialized, 104 | // so once patched, this will return some very basic 105 | // data. 106 | else if (this.isInterleavedBufferAttribute) { 107 | data.count = this.count; 108 | data.itemSize = this.itemSize; 109 | data.offset = this.offset; 110 | data.normalized = this.normalized; 111 | } 112 | // Handle renderer serialization 113 | else if (baseType === 'WebGLRenderer' || baseType === 'WebGL1Renderer') { 114 | const shadowMap = this.shadowMap; 115 | const capabilities = this.capabilities; 116 | data.name = ('name' in this) ? this.name : ''; 117 | data.physicallyCorrectLights = ('physicallyCorrectLights' in this) ? this.physicallyCorrectLights : false; 118 | data.gammaFactor = ('gammaFactor' in this) ? this.gammaFactor: 2; 119 | data.outputEncoding = ('outputEncoding' in this) ? this.outputEncoding : 0; // default? 120 | data.toneMapping = ('toneMapping' in this) ? this.toneMapping : 0; // default? 121 | data.toneMappingExposure = ('toneMappingExposure' in this) ? this.toneMappingExposure : 1; 122 | data.autoClear = ('autoClear' in this) ? this.autoClear : true; 123 | data.autoClearColor = ('autoClearColor' in this) ? this.autoClearColor : true; 124 | data.autoClearDepth = ('autoClearDepth' in this) ? this.autoClearDepth : true; 125 | data.autoClearStencil = ('autoClearStencil' in this) ? this.autoClearStencil : true; 126 | if (shadowMap) { 127 | data.shadowMap = {}; 128 | data.shadowMap.enabled = ('enabled' in shadowMap) ? shadowMap.enabled : false; 129 | data.shadowMap.autoUpdate = ('autoUpdate' in shadowMap) ? shadowMap.autoUpdate : true; 130 | data.shadowMap.type = ('type' in shadowMap) ? shadowMap.type : 0; // default? 131 | } 132 | if (capabilities) { 133 | data.capabilities = {}; 134 | data.capabilities.isWebGL2 = capabilities.isWebGL2; 135 | data.capabilities.precision = capabilities.precision; 136 | data.capabilities.floatFragmentTextures = capabilities.floatFragmentTextures; 137 | data.capabilities.floatVertexTextures = capabilities.floatVertexTextures; 138 | data.capabilities.logarithmicDepthBuffer = capabilities.logarithmicDepthBuffer; 139 | data.capabilities.maxAnisotropy = capabilities.getMaxAnisotropy(); 140 | data.capabilities.maxPrecision = capabilities.getMaxPrecision(); 141 | data.capabilities.maxAttributes = capabilities.maxAttributes; 142 | data.capabilities.maxCubemapSize = capabilities.maxCubemapSize; 143 | data.capabilities.maxFragmentUniforms = capabilities.maxFragmentUniforms; 144 | data.capabilities.maxTextureSize = capabilities.maxTextureSize; 145 | data.capabilities.maxTextures = capabilities.maxTextures; 146 | data.capabilities.maxVaryings = capabilities.maxVaryings; 147 | data.capabilities.maxVertexTextures = capabilities.maxVertexTextures; 148 | data.capabilities.maxVertexUniforms = capabilities.maxVertexUniforms; 149 | data.capabilities.vertexTextures = capabilities.vertexTextures; 150 | } 151 | } 152 | 153 | if (data.object) { 154 | data.object.baseType = baseType; 155 | 156 | if (this.matrix) { 157 | // Objects should also decompose their matrix for editing 158 | // in the tools 159 | this.matrix.decompose(tempPosition, tempRotation, tempScale); 160 | data.object.position = tempPosition.toArray(); 161 | data.object.rotation = tempEuler.setFromQuaternion(tempRotation).toArray(); 162 | data.object.scale = tempScale.toArray(); 163 | } 164 | } else { 165 | data.baseType = baseType; 166 | } 167 | 168 | return data; 169 | } 170 | }); 171 | -------------------------------------------------------------------------------- /src/content/utils.js: -------------------------------------------------------------------------------- 1 | export default (() => { 2 | 3 | const utils = { 4 | /** 5 | * Find the corresponding target and property given 6 | * an object and a string including object accessor's 7 | * (e.g. "."), like "position.x" to return the 8 | * "position" object as target, and "x" as the key. 9 | * 10 | * @param {THREE.*} entity 11 | * @param {String} property 12 | */ 13 | getTargetAndKey: (entity, property) => { 14 | const path = property.split('.'); 15 | let target = entity; 16 | let key = path.shift(); 17 | while (path.length) { 18 | target = target[key]; 19 | key = path.shift(); 20 | } 21 | return { target, key }; 22 | }, 23 | 24 | hideObjectFromTools: (object) => { 25 | object.userData.fromDevtools = true; 26 | }, 27 | 28 | isHiddenFromTools: (object) => { 29 | return !!(object.userData && object.userData.fromDevtools); 30 | }, 31 | 32 | /** 33 | * Executes `fn` for the passed in object, and all of its dependents. 34 | * Note that an Object3D's children is not considered a dependency, 35 | * unless `options.recursive` is set. 36 | * 37 | * @param {THREE.*} entity 38 | * @param {Function} fn 39 | * @param {Object} options 40 | * @param {Function} options.recursive [false] whether or not an object should recursively iterate over 41 | * Object3D children and their dependencies. 42 | */ 43 | forEachDependency(entity, fn, options={}) { 44 | fn(entity); 45 | 46 | if (entity.isObject3D) { 47 | if (entity.material && entity.material.isMaterial) { 48 | utils.forEachDependency(entity.material, fn, options); 49 | } 50 | if (entity.geometry && (entity.geometry.isGeometry || entity.geometry.isBufferGeometry)) { 51 | utils.forEachDependency(entity.geometry, fn, options); 52 | } 53 | if (entity.isScene && entity.background) { 54 | utils.forEachDependency(entity.background, fn, options); 55 | } 56 | 57 | if (options.recursive && entity.children && entity.children.length > 0) { 58 | for (let child of entity.children) { 59 | utils.forEachDependency(child, fn, options); 60 | } 61 | } 62 | } 63 | else if (entity.isBufferGeometry) { 64 | // handle attributes 65 | } 66 | else if (entity.isMaterial) { 67 | for (let key of Object.keys(entity)) { 68 | // @TODO cache textures used as uniforms here as well 69 | const texture = entity[key]; 70 | if (texture && texture.isTexture) { 71 | utils.forEachDependency(texture, fn, options); 72 | } 73 | } 74 | if (entity.uniforms) { 75 | for (let name of Object.keys(entity.uniforms)) { 76 | const value = entity.uniforms[name].value; 77 | if (value && value.isTexture) { 78 | // What other "dependency" data could a material have? 79 | // more geometry/buffers? 80 | utils.forEachDependency(value, fn, options); 81 | } 82 | } 83 | } 84 | } 85 | else if (entity.isTexture) { 86 | if (entity.image && entity.image.uuid) { 87 | // maybe don't cache images as an entity 88 | //Object.prototype.toString.call(this.image) === '[object Object]'; 89 | } 90 | } 91 | }, 92 | 93 | /** 94 | * For most entities in three.js core, baseType is the same as 95 | * `type` -- however the `type` property can be modified 96 | * by a developer, especially common when extending a 97 | * base class. 98 | * Note, be sure to check subclasses before 99 | * base classes here. 100 | * 101 | * @param {THREE.*} entity 102 | * @return {String} 103 | */ 104 | getBaseType: (entity) => { 105 | let type = 106 | // objects 107 | entity.isScene ? 'Scene' : 108 | entity.isGroup ? 'Group' : 109 | entity.isLOD ? 'LOD' : 110 | entity.isBone ? 'Bone' : 111 | entity.isSkeleton ? 'Skeleton' : 112 | entity.isPoints ? 'Points' : 113 | entity.isSprite ? 'Sprite' : 114 | 115 | entity.isSkinnedMesh ? 'SkinnedMesh' : 116 | entity.isInstancedMesh ? 'InstancedMesh' : 117 | entity.isMesh ? 'Mesh' : 118 | 119 | entity.isLineLoop ? 'LineLoop' : 120 | entity.isLineSegments ? 'LineSegments' : 121 | entity.isLine ? 'Line' : 122 | 123 | // lights 124 | entity.isAmbientLightProbe ? 'AmbientLightProbe' : 125 | entity.isHemisphereLightProbe ? 'HemisphereLightProbe' : 126 | entity.isLightProbe ? 'LightProbe' : 127 | 128 | entity.isAmbientLight ? 'AmbientLight' : 129 | entity.isDirectionalLight ? 'DirectionalLight' : 130 | entity.isHemisphereLight ? 'HemisphereLight' : 131 | entity.isPointLight ? 'PointLight' : 132 | entity.isRectAreaLight ? 'RectAreaLight' : 133 | entity.isSpotLight ? 'SpotLight' : 134 | entity.isLight ? 'Light' : 135 | 136 | // cameras 137 | entity.isArrayCamera ? 'ArrayCamera' : 138 | entity.isPerspectiveCamera ? 'PerspectiveCamera' : 139 | entity.isOrthographicCamera ? 'OrthographicCamera' : 140 | entity.isCubeCamera ? 'CubeCamera' : 141 | entity.isCamera ? 'Camera' : 142 | 143 | entity.isObject3D ? 'Object3D' : 144 | 145 | // geometries only have `type` property containing 146 | // a reference to its type if a preset like Sphere or Plane. 147 | entity.isGeometry ? 'Geometry' : 148 | //@TODO what is DirectGeometry? entity.isDirectGeometry ? 'DirectGeometry' : 149 | entity.isInstancedBufferGeometry ? 'InstancedBufferGeometry' : 150 | entity.isBufferGeometry ? 'BufferGeometry' : 151 | // buffer attributes 152 | entity.isInstancedBufferAttribute ? 'InstancedBufferAttribute' : 153 | entity.isInterleavedBufferAttribute ? 'InterleavedBufferAttribute' : 154 | entity.isBufferAttribute ? 'BufferAttribute' : 155 | 156 | // materials 157 | entity.isLineBasicMaterial ? 'LineBasicMaterial' : 158 | entity.isLineDashedMaterial ? 'LineDashedMaterial' : 159 | entity.isMeshBasicMaterial ? 'MeshBasicMaterial' : 160 | entity.isMeshDepthMaterial ? 'MeshDepthMaterial' : 161 | entity.isMeshDistanceMaterial ? 'MeshDistanceMaterial' : 162 | entity.isMeshLambertMaterial ? 'MeshLambertMaterial' : 163 | entity.isMeshMatcapMaterial ? 'MeshMatcapMaterial' : 164 | entity.isMeshNormalMaterial ? 'MeshNormalMaterial' : 165 | entity.isMeshToonMaterial ? 'MeshToonMaterial' : 166 | entity.isMeshPhongMaterial ? 'MeshPhongMaterial' : 167 | entity.isPointsMaterial ? 'PointsMaterial' : 168 | entity.isShadowMaterial ? 'ShadowMaterial' : 169 | entity.isSpriteMaterial ? 'SpriteMaterial' : 170 | 171 | entity.isMeshPhysicalMaterial ? 'MeshPhysicalMaterial' : 172 | entity.isMeshStandardMaterial ? 'MeshStandardMaterial' : 173 | 174 | entity.isRawShaderMaterial ? 'RawShaderMaterial' : 175 | entity.isShaderMaterial ? 'ShaderMaterial' : 176 | entity.isMaterial ? 'Material' : 177 | 178 | // textures 179 | entity.isCanvasTexture ? 'CanvasTexture' : 180 | entity.isCompressedTexture ? 'CompressedTexture' : 181 | entity.isCubeTexture ? 'CubeTexture' : 182 | entity.isDataTexture2DArray ? 'DataTexture2DArray' : 183 | entity.isDataTexture3D ? 'DataTexture3D' : 184 | entity.isDataTexture ? 'DataTexture' : 185 | entity.isDepthTexture ? 'DepthTexture' : 186 | entity.isVideoTexture ? 'VideoTexture' : 187 | entity.isTexture ? 'Texture' : 188 | 189 | // Not yet supported fully, but tag it accordingly 190 | entity.isWebGLRenderTarget ? 'WebGLRenderTarget' : 191 | 192 | // renderers 193 | // `WebGLRenderer` does not have a boolean prop, 194 | // test for that below. 195 | entity.isWebGL1Renderer ? 'WebGL1Renderer' : 196 | 197 | // If nothing matches... 198 | 'Unknown'; 199 | 200 | if (type === 'Unknown' && typeof entity.render === 'function' && typeof entity.setPixelRatio === 'function') { 201 | type = 'WebGLRenderer'; 202 | } 203 | 204 | return type; 205 | }, 206 | }; 207 | 208 | return utils; 209 | 210 | }); 211 | -------------------------------------------------------------------------------- /src/extension/background.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/extension/background.js: -------------------------------------------------------------------------------- 1 | import browser from '../../web_modules/webextension-polyfill/dist/browser-polyfill.js'; 2 | globalThis.browser = browser; 3 | 4 | const connections = new Map(); 5 | 6 | /** 7 | * When opening the three-devtools panel, store 8 | * a connection to communicate later. 9 | */ 10 | browser.runtime.onConnect.addListener(port => { 11 | let tabId; 12 | const onMessage = (message) => { 13 | tabId = message.tabId; 14 | console.log('onConnect', tabId, message.name); 15 | if (message.name === 'connect') { 16 | connections.set(tabId, port); 17 | } 18 | } 19 | 20 | port.onMessage.addListener(onMessage); 21 | 22 | port.onDisconnect.addListener(port => { 23 | port.onMessage.removeListener(onMessage); 24 | connections.delete(tabId); 25 | }); 26 | }); 27 | 28 | /** 29 | * When receiving a message from content, pass it 30 | * along to the devtools panel. 31 | */ 32 | browser.runtime.onMessage.addListener((request, sender) => { 33 | if (sender.tab) { 34 | const tabId = sender.tab.id; 35 | if (connections.has(tabId)) { 36 | connections.get(tabId).postMessage(request); 37 | } 38 | } 39 | return true; 40 | }); 41 | 42 | /** 43 | * When a page has reloaded; if three-devtools are open, notify 44 | * the devtools panel so it can inject the content-side of the tools. 45 | */ 46 | browser.webNavigation.onCommitted.addListener(({tabId, frameId}) => { 47 | // Only support top-level frame for now 48 | if (frameId !== 0) { 49 | return; 50 | } 51 | console.log('onCommitted', tabId, connections.has(tabId)); 52 | if (connections.has(tabId)) { 53 | connections.get(tabId).postMessage({ 54 | type: 'committed', 55 | id: 'three-devtools', 56 | }); 57 | } 58 | }); 59 | -------------------------------------------------------------------------------- /src/extension/contentScript.js: -------------------------------------------------------------------------------- 1 | // Use `text` instead of `src` to get around Chromium bug of execution order 2 | // when using `src`, resulting in a race condition. 3 | // https://bugs.chromium.org/p/chromium/issues/detail?id=634381#c3 4 | const script = document.createElement('script'); 5 | script.text = ` 6 | (() => { 7 | /** 8 | * This script injected by the installed three.js developer 9 | * tools extension. 10 | * https://github.com/threejs/three-devtools 11 | */ 12 | 13 | const $devtoolsReady = Symbol('devtoolsReady'); 14 | const $backlog = Symbol('backlog'); 15 | 16 | // The __THREE_DEVTOOLS__ target is small and light-weight, and collects 17 | // events triggered until the devtools panel is ready, which is when 18 | // the events are flushed. 19 | const target = new class ThreeDevToolsTarget extends EventTarget { 20 | constructor() { 21 | super(); 22 | this[$devtoolsReady] = false; 23 | this[$backlog] = []; 24 | this.addEventListener('devtools-ready', e => { 25 | this[$devtoolsReady] = true; 26 | for (let event of this[$backlog]) { 27 | this.dispatchEvent(event); 28 | } 29 | }, { once: true }); 30 | } 31 | 32 | dispatchEvent(event) { 33 | if (this[$devtoolsReady] || event.type === 'devtools-ready') { 34 | super.dispatchEvent(event); 35 | } else { 36 | this[$backlog].push(event); 37 | } 38 | } 39 | } 40 | 41 | Object.defineProperty(window, '__THREE_DEVTOOLS__', { 42 | value: target, 43 | }); 44 | })(); 45 | `; 46 | script.onload = () => { 47 | script.parentNode.removeChild(script); 48 | } 49 | (document.head || document.documentElement).appendChild(script); 50 | 51 | window.addEventListener('message', e => { 52 | if (e.source !== window || 53 | typeof e.data !== 'object' || 54 | e.data.id !== 'three-devtools') { 55 | return; 56 | } 57 | 58 | // Don't bring in the 35kb polyfill on every page 59 | // for a single command that doesn't matter if its callback 60 | // promise; handle this manually. 61 | const extRoot = globalThis.chrome ? globalThis.chrome : globalThis.browser; 62 | 63 | try { 64 | extRoot.runtime.sendMessage(e.data); 65 | } catch (error) { 66 | console.error(error); 67 | extRoot.runtime.sendMessage({ 68 | type: 'error', 69 | id: 'three-devtools', 70 | data: error.toString(), 71 | }); 72 | } 73 | }); 74 | -------------------------------------------------------------------------------- /src/extension/devtools.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/extension/devtools.js: -------------------------------------------------------------------------------- 1 | import browser from '../../web_modules/webextension-polyfill/dist/browser-polyfill.js'; 2 | globalThis.browser = browser; 3 | 4 | if (browser.devtools.inspectedWindow.tabId) { 5 | // As of now, only inspect content windows, not when 6 | // debugging a devtools panel for example. 7 | browser.devtools.inspectedWindow.eval(`window.DevToolsAPI`).then(([result,error]) => { 8 | if (!result) { 9 | createPanel(); 10 | } 11 | }); 12 | } 13 | 14 | 15 | async function createPanel() { 16 | // It appears that Chrome treats URLs relative to extension root, 17 | // and Firefox treats it relative to the devtools page. 18 | // Use `/` to circumvent. 19 | const icon = '/assets/icon_128.png'; 20 | const url = '/src/app/index.html'; 21 | const panel = await browser.devtools.panels.create(`three`, icon, url); 22 | } 23 | -------------------------------------------------------------------------------- /web_modules/@egjs/agent.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2017 NAVER Corp. 3 | @egjs/agent project is licensed under the MIT license 4 | 5 | @egjs/agent JavaScript library 6 | 7 | 8 | @version 2.1.5 9 | */ 10 | var win = typeof window !== "undefined" && window || {}; 11 | var navigator = win.navigator; 12 | 13 | var parseRules = { 14 | browser: [{ 15 | criteria: "PhantomJS", 16 | identity: "PhantomJS" 17 | }, { 18 | criteria: /Whale/, 19 | identity: "Whale", 20 | versionSearch: "Whale" 21 | }, { 22 | criteria: /Edge/, 23 | identity: "Edge", 24 | versionSearch: "Edge" 25 | }, { 26 | criteria: /MSIE|Trident|Windows Phone/, 27 | identity: "IE", 28 | versionSearch: "IEMobile|MSIE|rv" 29 | }, { 30 | criteria: /MiuiBrowser/, 31 | identity: "MIUI Browser", 32 | versionSearch: "MiuiBrowser" 33 | }, { 34 | criteria: /SamsungBrowser/, 35 | identity: "Samsung Internet", 36 | versionSearch: "SamsungBrowser" 37 | }, { 38 | criteria: /SAMSUNG /, 39 | identity: "Samsung Internet", 40 | versionSearch: "Version" 41 | }, { 42 | criteria: /Chrome|CriOS/, 43 | identity: "Chrome" 44 | }, { 45 | criteria: /Android/, 46 | identity: "Android Browser", 47 | versionSearch: "Version" 48 | }, { 49 | criteria: /iPhone|iPad/, 50 | identity: "Safari", 51 | versionSearch: "Version" 52 | }, { 53 | criteria: "Apple", 54 | identity: "Safari", 55 | versionSearch: "Version" 56 | }, { 57 | criteria: "Firefox", 58 | identity: "Firefox" 59 | }], 60 | os: [{ 61 | criteria: /Windows Phone/, 62 | identity: "Windows Phone", 63 | versionSearch: "Windows Phone" 64 | }, { 65 | criteria: "Windows 2000", 66 | identity: "Window", 67 | versionAlias: "5.0" 68 | }, { 69 | criteria: /Windows NT/, 70 | identity: "Window", 71 | versionSearch: "Windows NT" 72 | }, { 73 | criteria: /iPhone|iPad/, 74 | identity: "iOS", 75 | versionSearch: "iPhone OS|CPU OS" 76 | }, { 77 | criteria: "Mac", 78 | versionSearch: "OS X", 79 | identity: "MAC" 80 | }, { 81 | criteria: /Android/, 82 | identity: "Android" 83 | }, { 84 | criteria: /Tizen/, 85 | identity: "Tizen" 86 | }, { 87 | criteria: /Web0S/, 88 | identity: "WebOS" 89 | }], 90 | 91 | // Webview check condition 92 | // ios: If has no version information 93 | // Android 5.0 && chrome 40+: Presence of "; wv" in userAgent 94 | // Under android 5.0: Presence of "NAVER" or "Daum" in userAgent 95 | webview: [{ 96 | criteria: /iPhone|iPad/, 97 | browserVersionSearch: "Version", 98 | webviewBrowserVersion: /-1/ 99 | }, { 100 | criteria: /iPhone|iPad|Android/, 101 | webviewToken: /NAVER|DAUM|; wv/ 102 | 103 | }], 104 | defaultString: { 105 | browser: { 106 | version: "-1", 107 | name: "unknown" 108 | }, 109 | os: { 110 | version: "-1", 111 | name: "unknown" 112 | } 113 | } 114 | }; 115 | 116 | function filter(arr, compare) { 117 | var result = []; 118 | 119 | for (var i = 0; i < arr.length; i++) { 120 | compare(arr[i]) && result.push(arr[i]); 121 | } 122 | return result; 123 | } 124 | 125 | function some(arr, compare) { 126 | for (var i = 0; i < arr.length; i++) { 127 | if (compare(arr[i])) { 128 | return true; 129 | } 130 | } 131 | return false; 132 | } 133 | 134 | var UA = void 0; 135 | 136 | function setUa(ua) { 137 | UA = ua; 138 | } 139 | 140 | function isMatched(base, target) { 141 | return target && target.test ? !!target.test(base) : base.indexOf(target) > -1; 142 | } 143 | 144 | function getIdentityStringFromArray(rules, defaultStrings) { 145 | var matchedRule = filter(rules, function (rule) { 146 | return isMatched(UA, rule.criteria); 147 | })[0]; 148 | 149 | return matchedRule && matchedRule.identity || defaultStrings.name; 150 | } 151 | 152 | function getRule(rules, targetIdentity) { 153 | return filter(rules, function (rule) { 154 | var criteria = rule.criteria; 155 | var identityMatched = new RegExp(rule.identity, "i").test(targetIdentity); 156 | 157 | if (criteria ? identityMatched && isMatched(UA, criteria) : identityMatched) { 158 | return true; 159 | } else { 160 | return false; 161 | } 162 | })[0]; 163 | } 164 | 165 | function getBrowserName() { 166 | return getIdentityStringFromArray(parseRules.browser, parseRules.defaultString.browser); 167 | } 168 | 169 | function getBrowserRule(browserName) { 170 | var rule = getRule(parseRules.browser, browserName); 171 | 172 | if (!rule) { 173 | rule = { 174 | criteria: browserName, 175 | versionSearch: browserName, 176 | identity: browserName 177 | }; 178 | } 179 | 180 | return rule; 181 | } 182 | 183 | function extractBrowserVersion(versionToken, ua) { 184 | var browserVersion = parseRules.defaultString.browser.version; 185 | var versionRegexResult = new RegExp("(" + versionToken + ")", "i").exec(ua); 186 | 187 | if (!versionRegexResult) { 188 | return browserVersion; 189 | } 190 | 191 | var versionTokenIndex = versionRegexResult.index; 192 | var verTkn = versionRegexResult[0]; 193 | 194 | if (versionTokenIndex > -1) { 195 | var versionIndex = versionTokenIndex + verTkn.length + 1; 196 | 197 | browserVersion = ua.substring(versionIndex).split(" ")[0].replace(/_/g, ".").replace(/;|\)/g, ""); 198 | } 199 | return browserVersion; 200 | } 201 | 202 | function getBrowserVersion(browserName) { 203 | if (!browserName) { 204 | return undefined; 205 | } 206 | 207 | // console.log(browserRule); 208 | // const versionToken = browserRule ? browserRule.versionSearch : browserName; 209 | var browserRule = getBrowserRule(browserName); 210 | var versionToken = browserRule.versionSearch || browserName; 211 | var browserVersion = extractBrowserVersion(versionToken, UA); 212 | 213 | return browserVersion; 214 | } 215 | 216 | function isWebview() { 217 | var webviewRules = parseRules.webview; 218 | var browserVersion = void 0; 219 | 220 | return some(filter(webviewRules, function (rule) { 221 | return isMatched(UA, rule.criteria); 222 | }), function (rule) { 223 | browserVersion = extractBrowserVersion(rule.browserVersionSearch, UA); 224 | if (isMatched(UA, rule.webviewToken) || isMatched(browserVersion, rule.webviewBrowserVersion)) { 225 | return true; 226 | } else { 227 | return false; 228 | } 229 | }); 230 | } 231 | 232 | function getOSRule(osName) { 233 | return getRule(parseRules.os, osName); 234 | } 235 | 236 | function getOsName() { 237 | return getIdentityStringFromArray(parseRules.os, parseRules.defaultString.os); 238 | } 239 | 240 | function getOsVersion(osName) { 241 | var osRule = getOSRule(osName) || {}; 242 | var defaultOSVersion = parseRules.defaultString.os.version; 243 | var osVersion = void 0; 244 | 245 | if (!osName) { 246 | return undefined; 247 | } 248 | if (osRule.versionAlias) { 249 | return osRule.versionAlias; 250 | } 251 | var osVersionToken = osRule.versionSearch || osName; 252 | var osVersionRegex = new RegExp("(" + osVersionToken + ")\\s([\\d_\\.]+|\\d_0)", "i"); 253 | var osVersionRegexResult = osVersionRegex.exec(UA); 254 | 255 | if (osVersionRegexResult) { 256 | osVersion = osVersionRegex.exec(UA)[2].replace(/_/g, ".").replace(/;|\)/g, ""); 257 | } 258 | return osVersion || defaultOSVersion; 259 | } 260 | 261 | function getOs() { 262 | var name = getOsName(); 263 | var version = getOsVersion(name); 264 | 265 | return { name: name, version: version }; 266 | } 267 | 268 | function getBrowser() { 269 | var name = getBrowserName(); 270 | var version = getBrowserVersion(name); 271 | 272 | return { name: name, version: version, webview: isWebview() }; 273 | } 274 | 275 | function getIsMobile() { 276 | return UA.indexOf("Mobi") !== -1; 277 | } 278 | 279 | /** 280 | * Copyright (c) NAVER Corp. 281 | * egjs-agent projects are licensed under the MIT license 282 | */ 283 | 284 | /** 285 | * @namespace eg.agent 286 | */ 287 | /** 288 | * Extracts browser and operating system information from the user agent string. 289 | * @ko 유저 에이전트 문자열에서 브라우저와 운영체제 정보를 추출한다. 290 | * @function eg.agent#agent 291 | * @param {String} [userAgent=navigator.userAgent] user agent string to parse 파싱할 유저에이전트 문자열 292 | * @return {Object} agentInfo 293 | * @return {Object} agentInfo.os os Operating system information 운영체제 정보 294 | * @return {String} agentInfo.os.name Operating system name (android, ios, window, mac, unknown) 운영체제 이름 (android, ios, window, mac, unknown) 295 | * @return {String} agentInfo.os.version Operating system version 운영체제 버전 296 | * @return {String} agentInfo.browser Browser information 브라우저 정보 297 | * @return {String} agentInfo.browser.name Browser name (safari, chrome, sbrowser, ie, firefox, unknown) 브라우저 이름 (safari, chrome, sbrowser, ie, firefox, unknown) 298 | * @return {String} agentInfo.browser.version Browser version 브라우저 버전 299 | * @return {Boolean} agentInfo.browser.webview Indicates whether the browser is inapp웹뷰 브라우저 여부 300 | * @return {Boolean} agentInfo.isMobile Indicates whether the browser is for mobile모바일 브라우저 여부 301 | * @example 302 | import agent from "@egjs/agent"; 303 | 304 | const {os, browser, isMobile} = agent(); 305 | */ 306 | function agent() { 307 | var ua = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : navigator.userAgent; 308 | 309 | setUa(ua); 310 | 311 | var agentInfo = { 312 | os: getOs(), 313 | browser: getBrowser(), 314 | isMobile: getIsMobile() 315 | }; 316 | 317 | agentInfo.browser.name = agentInfo.browser.name.toLowerCase(); 318 | agentInfo.os.name = agentInfo.os.name.toLowerCase(); 319 | agentInfo.os.version = agentInfo.os.version.toLowerCase(); 320 | 321 | if (agentInfo.os.name === "ios" && agentInfo.browser.webview) { 322 | agentInfo.browser.version = "-1"; 323 | } 324 | 325 | return agentInfo; 326 | } 327 | /** 328 | * Version info string 329 | * @ko 버전정보 문자열 330 | * @name VERSION 331 | * @static 332 | * @type {String} 333 | * @example 334 | * eg.agent.VERSION; // ex) 2.2.0 335 | * @memberof eg.agent 336 | */ 337 | agent.VERSION = "2.1.5"; 338 | 339 | export default agent; 340 | -------------------------------------------------------------------------------- /web_modules/import-map.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "webextension-polyfill/dist/browser-polyfill": "./webextension-polyfill/dist/browser-polyfill.js", 4 | "lit-element": "./lit-element.js", 5 | "lit-html/directives/if-defined": "./lit-html/directives/if-defined.js", 6 | "@egjs/agent": "./@egjs/agent.js", 7 | "three": "./three.js", 8 | "three/src/constants": "./three/src/constants.js", 9 | "three/examples/jsm/loaders/GLTFLoader": "./three/examples/jsm/loaders/GLTFLoader.js", 10 | "three/examples/jsm/geometries/TeapotBufferGeometry": "./three/examples/jsm/geometries/TeapotBufferGeometry.js" 11 | } 12 | } -------------------------------------------------------------------------------- /web_modules/licenses/agent_LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 NAVER Corp. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /web_modules/licenses/lit-element_LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, The Polymer Authors. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /web_modules/licenses/lit-html_LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2017, The Polymer Authors. All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | * Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | * Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /web_modules/licenses/three_LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright © 2010-2019 three.js authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /web_modules/lit-html/directives/if-defined.js: -------------------------------------------------------------------------------- 1 | import { d as directive, A as AttributePart } from '../../common/lit-html-513f48c9.js'; 2 | 3 | /** 4 | * @license 5 | * Copyright (c) 2018 The Polymer Project Authors. All rights reserved. 6 | * This code may only be used under the BSD style license found at 7 | * http://polymer.github.io/LICENSE.txt 8 | * The complete set of authors may be found at 9 | * http://polymer.github.io/AUTHORS.txt 10 | * The complete set of contributors may be found at 11 | * http://polymer.github.io/CONTRIBUTORS.txt 12 | * Code distributed by Google as part of the polymer project is also 13 | * subject to an additional IP rights grant found at 14 | * http://polymer.github.io/PATENTS.txt 15 | */ 16 | /** 17 | * For AttributeParts, sets the attribute if the value is defined and removes 18 | * the attribute if the value is undefined. 19 | * 20 | * For other part types, this directive is a no-op. 21 | */ 22 | const ifDefined = directive((value) => (part) => { 23 | if (value === undefined && part instanceof AttributePart) { 24 | if (value !== part.value) { 25 | const name = part.committer.name; 26 | part.committer.element.removeAttribute(name); 27 | } 28 | } 29 | else { 30 | part.setValue(value); 31 | } 32 | }); 33 | 34 | export { ifDefined }; 35 | -------------------------------------------------------------------------------- /web_modules/three/examples/jsm/pmrem/PMREMCubeUVPacker.js: -------------------------------------------------------------------------------- 1 | import { OrthographicCamera, Scene, ShaderMaterial, Vector3, NoBlending, RGBEEncoding, RGBM16Encoding, LinearFilter, WebGLRenderTarget, CubeUVReflectionMapping, PlaneBufferGeometry, Mesh, BackSide, LinearToneMapping, Vector2 } from '../../../../three.js'; 2 | 3 | /** 4 | * @author Prashant Sharma / spidersharma03 5 | * @author Ben Houston / bhouston, https://clara.io 6 | * 7 | * This class takes the cube lods(corresponding to different roughness values), and creates a single cubeUV 8 | * Texture. The format for a given roughness set of faces is simply:: 9 | * +X+Y+Z 10 | * -X-Y-Z 11 | * For every roughness a mip map chain is also saved, which is essential to remove the texture artifacts due to 12 | * minification. 13 | * Right now for every face a PlaneMesh is drawn, which leads to a lot of geometry draw calls, but can be replaced 14 | * later by drawing a single buffer and by sending the appropriate faceIndex via vertex attributes. 15 | * The arrangement of the faces is fixed, as assuming this arrangement, the sampling function has been written. 16 | */ 17 | 18 | var PMREMCubeUVPacker = ( function () { 19 | 20 | var camera = new OrthographicCamera(); 21 | var scene = new Scene(); 22 | var shader = getShader(); 23 | 24 | var PMREMCubeUVPacker = function ( cubeTextureLods ) { 25 | 26 | this.cubeLods = cubeTextureLods; 27 | var size = cubeTextureLods[ 0 ].width * 4; 28 | 29 | var sourceTexture = cubeTextureLods[ 0 ].texture; 30 | var params = { 31 | format: sourceTexture.format, 32 | magFilter: sourceTexture.magFilter, 33 | minFilter: sourceTexture.minFilter, 34 | type: sourceTexture.type, 35 | generateMipmaps: sourceTexture.generateMipmaps, 36 | anisotropy: sourceTexture.anisotropy, 37 | encoding: ( sourceTexture.encoding === RGBEEncoding ) ? RGBM16Encoding : sourceTexture.encoding 38 | }; 39 | 40 | if ( params.encoding === RGBM16Encoding ) { 41 | 42 | params.magFilter = LinearFilter; 43 | params.minFilter = LinearFilter; 44 | 45 | } 46 | 47 | this.CubeUVRenderTarget = new WebGLRenderTarget( size, size, params ); 48 | this.CubeUVRenderTarget.texture.name = "PMREMCubeUVPacker.cubeUv"; 49 | this.CubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping; 50 | 51 | this.objects = []; 52 | 53 | var geometry = new PlaneBufferGeometry( 1, 1 ); 54 | 55 | var faceOffsets = []; 56 | faceOffsets.push( new Vector2( 0, 0 ) ); 57 | faceOffsets.push( new Vector2( 1, 0 ) ); 58 | faceOffsets.push( new Vector2( 2, 0 ) ); 59 | faceOffsets.push( new Vector2( 0, 1 ) ); 60 | faceOffsets.push( new Vector2( 1, 1 ) ); 61 | faceOffsets.push( new Vector2( 2, 1 ) ); 62 | 63 | var textureResolution = size; 64 | size = cubeTextureLods[ 0 ].width; 65 | 66 | var offset2 = 0; 67 | var c = 4.0; 68 | this.numLods = Math.log( cubeTextureLods[ 0 ].width ) / Math.log( 2 ) - 2; // IE11 doesn't support Math.log2 69 | for ( var i = 0; i < this.numLods; i ++ ) { 70 | 71 | var offset1 = ( textureResolution - textureResolution / c ) * 0.5; 72 | if ( size > 16 ) c *= 2; 73 | var nMips = size > 16 ? 6 : 1; 74 | var mipOffsetX = 0; 75 | var mipOffsetY = 0; 76 | var mipSize = size; 77 | 78 | for ( var j = 0; j < nMips; j ++ ) { 79 | 80 | // Mip Maps 81 | for ( var k = 0; k < 6; k ++ ) { 82 | 83 | // 6 Cube Faces 84 | var material = shader.clone(); 85 | material.uniforms[ 'envMap' ].value = this.cubeLods[ i ].texture; 86 | material.envMap = this.cubeLods[ i ].texture; 87 | material.uniforms[ 'faceIndex' ].value = k; 88 | material.uniforms[ 'mapSize' ].value = mipSize; 89 | 90 | var planeMesh = new Mesh( geometry, material ); 91 | planeMesh.position.x = faceOffsets[ k ].x * mipSize - offset1 + mipOffsetX; 92 | planeMesh.position.y = faceOffsets[ k ].y * mipSize - offset1 + offset2 + mipOffsetY; 93 | planeMesh.material.side = BackSide; 94 | planeMesh.scale.setScalar( mipSize ); 95 | this.objects.push( planeMesh ); 96 | 97 | } 98 | mipOffsetY += 1.75 * mipSize; 99 | mipOffsetX += 1.25 * mipSize; 100 | mipSize /= 2; 101 | 102 | } 103 | offset2 += 2 * size; 104 | if ( size > 16 ) size /= 2; 105 | 106 | } 107 | 108 | }; 109 | 110 | PMREMCubeUVPacker.prototype = { 111 | 112 | constructor: PMREMCubeUVPacker, 113 | 114 | update: function ( renderer ) { 115 | 116 | var size = this.cubeLods[ 0 ].width * 4; 117 | // top and bottom are swapped for some reason? 118 | camera.left = - size * 0.5; 119 | camera.right = size * 0.5; 120 | camera.top = - size * 0.5; 121 | camera.bottom = size * 0.5; 122 | camera.near = 0; 123 | camera.far = 1; 124 | camera.updateProjectionMatrix(); 125 | 126 | for ( var i = 0; i < this.objects.length; i ++ ) { 127 | 128 | scene.add( this.objects[ i ] ); 129 | 130 | } 131 | 132 | var gammaInput = renderer.gammaInput; 133 | var gammaOutput = renderer.gammaOutput; 134 | var toneMapping = renderer.toneMapping; 135 | var toneMappingExposure = renderer.toneMappingExposure; 136 | var currentRenderTarget = renderer.getRenderTarget(); 137 | 138 | renderer.gammaInput = false; 139 | renderer.gammaOutput = false; 140 | renderer.toneMapping = LinearToneMapping; 141 | renderer.toneMappingExposure = 1.0; 142 | renderer.setRenderTarget( this.CubeUVRenderTarget ); 143 | renderer.render( scene, camera ); 144 | 145 | renderer.setRenderTarget( currentRenderTarget ); 146 | renderer.toneMapping = toneMapping; 147 | renderer.toneMappingExposure = toneMappingExposure; 148 | renderer.gammaInput = gammaInput; 149 | renderer.gammaOutput = gammaOutput; 150 | 151 | for ( var i = 0; i < this.objects.length; i ++ ) { 152 | 153 | scene.remove( this.objects[ i ] ); 154 | 155 | } 156 | 157 | }, 158 | 159 | dispose: function () { 160 | 161 | for ( var i = 0, l = this.objects.length; i < l; i ++ ) { 162 | 163 | this.objects[ i ].material.dispose(); 164 | 165 | } 166 | 167 | this.objects[ 0 ].geometry.dispose(); 168 | 169 | } 170 | 171 | }; 172 | 173 | function getShader() { 174 | 175 | var shaderMaterial = new ShaderMaterial( { 176 | 177 | uniforms: { 178 | "faceIndex": { value: 0 }, 179 | "mapSize": { value: 0 }, 180 | "envMap": { value: null }, 181 | "testColor": { value: new Vector3( 1, 1, 1 ) } 182 | }, 183 | 184 | vertexShader: 185 | "precision highp float;\ 186 | varying vec2 vUv;\ 187 | void main() {\ 188 | vUv = uv;\ 189 | gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );\ 190 | }", 191 | 192 | fragmentShader: 193 | "precision highp float;\ 194 | varying vec2 vUv;\ 195 | uniform samplerCube envMap;\ 196 | uniform float mapSize;\ 197 | uniform vec3 testColor;\ 198 | uniform int faceIndex;\ 199 | \ 200 | void main() {\ 201 | vec3 sampleDirection;\ 202 | vec2 uv = vUv;\ 203 | uv = uv * 2.0 - 1.0;\ 204 | uv.y *= -1.0;\ 205 | if(faceIndex == 0) {\ 206 | sampleDirection = normalize(vec3(1.0, uv.y, -uv.x));\ 207 | } else if(faceIndex == 1) {\ 208 | sampleDirection = normalize(vec3(uv.x, 1.0, uv.y));\ 209 | } else if(faceIndex == 2) {\ 210 | sampleDirection = normalize(vec3(uv.x, uv.y, 1.0));\ 211 | } else if(faceIndex == 3) {\ 212 | sampleDirection = normalize(vec3(-1.0, uv.y, uv.x));\ 213 | } else if(faceIndex == 4) {\ 214 | sampleDirection = normalize(vec3(uv.x, -1.0, -uv.y));\ 215 | } else {\ 216 | sampleDirection = normalize(vec3(-uv.x, uv.y, -1.0));\ 217 | }\ 218 | vec4 color = envMapTexelToLinear( textureCube( envMap, sampleDirection ) );\ 219 | gl_FragColor = linearToOutputTexel( color );\ 220 | }", 221 | 222 | blending: NoBlending 223 | 224 | } ); 225 | 226 | shaderMaterial.type = 'PMREMCubeUVPacker'; 227 | 228 | return shaderMaterial; 229 | 230 | } 231 | 232 | 233 | return PMREMCubeUVPacker; 234 | 235 | } )(); 236 | 237 | export { PMREMCubeUVPacker }; 238 | -------------------------------------------------------------------------------- /web_modules/three/src/constants.js: -------------------------------------------------------------------------------- 1 | var REVISION = '112'; 2 | var MOUSE = { LEFT: 0, MIDDLE: 1, RIGHT: 2, ROTATE: 0, DOLLY: 1, PAN: 2 }; 3 | var TOUCH = { ROTATE: 0, PAN: 1, DOLLY_PAN: 2, DOLLY_ROTATE: 3 }; 4 | var CullFaceNone = 0; 5 | var CullFaceBack = 1; 6 | var CullFaceFront = 2; 7 | var CullFaceFrontBack = 3; 8 | var FrontFaceDirectionCW = 0; 9 | var FrontFaceDirectionCCW = 1; 10 | var BasicShadowMap = 0; 11 | var PCFShadowMap = 1; 12 | var PCFSoftShadowMap = 2; 13 | var VSMShadowMap = 3; 14 | var FrontSide = 0; 15 | var BackSide = 1; 16 | var DoubleSide = 2; 17 | var FlatShading = 1; 18 | var SmoothShading = 2; 19 | var NoColors = 0; 20 | var FaceColors = 1; 21 | var VertexColors = 2; 22 | var NoBlending = 0; 23 | var NormalBlending = 1; 24 | var AdditiveBlending = 2; 25 | var SubtractiveBlending = 3; 26 | var MultiplyBlending = 4; 27 | var CustomBlending = 5; 28 | var AddEquation = 100; 29 | var SubtractEquation = 101; 30 | var ReverseSubtractEquation = 102; 31 | var MinEquation = 103; 32 | var MaxEquation = 104; 33 | var ZeroFactor = 200; 34 | var OneFactor = 201; 35 | var SrcColorFactor = 202; 36 | var OneMinusSrcColorFactor = 203; 37 | var SrcAlphaFactor = 204; 38 | var OneMinusSrcAlphaFactor = 205; 39 | var DstAlphaFactor = 206; 40 | var OneMinusDstAlphaFactor = 207; 41 | var DstColorFactor = 208; 42 | var OneMinusDstColorFactor = 209; 43 | var SrcAlphaSaturateFactor = 210; 44 | var NeverDepth = 0; 45 | var AlwaysDepth = 1; 46 | var LessDepth = 2; 47 | var LessEqualDepth = 3; 48 | var EqualDepth = 4; 49 | var GreaterEqualDepth = 5; 50 | var GreaterDepth = 6; 51 | var NotEqualDepth = 7; 52 | var MultiplyOperation = 0; 53 | var MixOperation = 1; 54 | var AddOperation = 2; 55 | var NoToneMapping = 0; 56 | var LinearToneMapping = 1; 57 | var ReinhardToneMapping = 2; 58 | var Uncharted2ToneMapping = 3; 59 | var CineonToneMapping = 4; 60 | var ACESFilmicToneMapping = 5; 61 | 62 | var UVMapping = 300; 63 | var CubeReflectionMapping = 301; 64 | var CubeRefractionMapping = 302; 65 | var EquirectangularReflectionMapping = 303; 66 | var EquirectangularRefractionMapping = 304; 67 | var SphericalReflectionMapping = 305; 68 | var CubeUVReflectionMapping = 306; 69 | var CubeUVRefractionMapping = 307; 70 | var RepeatWrapping = 1000; 71 | var ClampToEdgeWrapping = 1001; 72 | var MirroredRepeatWrapping = 1002; 73 | var NearestFilter = 1003; 74 | var NearestMipmapNearestFilter = 1004; 75 | var NearestMipMapNearestFilter = 1004; 76 | var NearestMipmapLinearFilter = 1005; 77 | var NearestMipMapLinearFilter = 1005; 78 | var LinearFilter = 1006; 79 | var LinearMipmapNearestFilter = 1007; 80 | var LinearMipMapNearestFilter = 1007; 81 | var LinearMipmapLinearFilter = 1008; 82 | var LinearMipMapLinearFilter = 1008; 83 | var UnsignedByteType = 1009; 84 | var ByteType = 1010; 85 | var ShortType = 1011; 86 | var UnsignedShortType = 1012; 87 | var IntType = 1013; 88 | var UnsignedIntType = 1014; 89 | var FloatType = 1015; 90 | var HalfFloatType = 1016; 91 | var UnsignedShort4444Type = 1017; 92 | var UnsignedShort5551Type = 1018; 93 | var UnsignedShort565Type = 1019; 94 | var UnsignedInt248Type = 1020; 95 | var AlphaFormat = 1021; 96 | var RGBFormat = 1022; 97 | var RGBAFormat = 1023; 98 | var LuminanceFormat = 1024; 99 | var LuminanceAlphaFormat = 1025; 100 | var RGBEFormat = RGBAFormat; 101 | var DepthFormat = 1026; 102 | var DepthStencilFormat = 1027; 103 | var RedFormat = 1028; 104 | var RedIntegerFormat = 1029; 105 | var RGFormat = 1030; 106 | var RGIntegerFormat = 1031; 107 | var RGBIntegerFormat = 1032; 108 | var RGBAIntegerFormat = 1033; 109 | 110 | var RGB_S3TC_DXT1_Format = 33776; 111 | var RGBA_S3TC_DXT1_Format = 33777; 112 | var RGBA_S3TC_DXT3_Format = 33778; 113 | var RGBA_S3TC_DXT5_Format = 33779; 114 | var RGB_PVRTC_4BPPV1_Format = 35840; 115 | var RGB_PVRTC_2BPPV1_Format = 35841; 116 | var RGBA_PVRTC_4BPPV1_Format = 35842; 117 | var RGBA_PVRTC_2BPPV1_Format = 35843; 118 | var RGB_ETC1_Format = 36196; 119 | var RGBA_ASTC_4x4_Format = 37808; 120 | var RGBA_ASTC_5x4_Format = 37809; 121 | var RGBA_ASTC_5x5_Format = 37810; 122 | var RGBA_ASTC_6x5_Format = 37811; 123 | var RGBA_ASTC_6x6_Format = 37812; 124 | var RGBA_ASTC_8x5_Format = 37813; 125 | var RGBA_ASTC_8x6_Format = 37814; 126 | var RGBA_ASTC_8x8_Format = 37815; 127 | var RGBA_ASTC_10x5_Format = 37816; 128 | var RGBA_ASTC_10x6_Format = 37817; 129 | var RGBA_ASTC_10x8_Format = 37818; 130 | var RGBA_ASTC_10x10_Format = 37819; 131 | var RGBA_ASTC_12x10_Format = 37820; 132 | var RGBA_ASTC_12x12_Format = 37821; 133 | var LoopOnce = 2200; 134 | var LoopRepeat = 2201; 135 | var LoopPingPong = 2202; 136 | var InterpolateDiscrete = 2300; 137 | var InterpolateLinear = 2301; 138 | var InterpolateSmooth = 2302; 139 | var ZeroCurvatureEnding = 2400; 140 | var ZeroSlopeEnding = 2401; 141 | var WrapAroundEnding = 2402; 142 | var TrianglesDrawMode = 0; 143 | var TriangleStripDrawMode = 1; 144 | var TriangleFanDrawMode = 2; 145 | var LinearEncoding = 3000; 146 | var sRGBEncoding = 3001; 147 | var GammaEncoding = 3007; 148 | var RGBEEncoding = 3002; 149 | var LogLuvEncoding = 3003; 150 | var RGBM7Encoding = 3004; 151 | var RGBM16Encoding = 3005; 152 | var RGBDEncoding = 3006; 153 | var BasicDepthPacking = 3200; 154 | var RGBADepthPacking = 3201; 155 | var TangentSpaceNormalMap = 0; 156 | var ObjectSpaceNormalMap = 1; 157 | 158 | var ZeroStencilOp = 0; 159 | var KeepStencilOp = 7680; 160 | var ReplaceStencilOp = 7681; 161 | var IncrementStencilOp = 7682; 162 | var DecrementStencilOp = 7683; 163 | var IncrementWrapStencilOp = 34055; 164 | var DecrementWrapStencilOp = 34056; 165 | var InvertStencilOp = 5386; 166 | 167 | var NeverStencilFunc = 512; 168 | var LessStencilFunc = 513; 169 | var EqualStencilFunc = 514; 170 | var LessEqualStencilFunc = 515; 171 | var GreaterStencilFunc = 516; 172 | var NotEqualStencilFunc = 517; 173 | var GreaterEqualStencilFunc = 518; 174 | var AlwaysStencilFunc = 519; 175 | 176 | var StaticDrawUsage = 35044; 177 | var DynamicDrawUsage = 35048; 178 | var StreamDrawUsage = 35040; 179 | var StaticReadUsage = 35045; 180 | var DynamicReadUsage = 35049; 181 | var StreamReadUsage = 35041; 182 | var StaticCopyUsage = 35046; 183 | var DynamicCopyUsage = 35050; 184 | var StreamCopyUsage = 35042; 185 | 186 | export { ACESFilmicToneMapping, AddEquation, AddOperation, AdditiveBlending, AlphaFormat, AlwaysDepth, AlwaysStencilFunc, BackSide, BasicDepthPacking, BasicShadowMap, ByteType, CineonToneMapping, ClampToEdgeWrapping, CubeReflectionMapping, CubeRefractionMapping, CubeUVReflectionMapping, CubeUVRefractionMapping, CullFaceBack, CullFaceFront, CullFaceFrontBack, CullFaceNone, CustomBlending, DecrementStencilOp, DecrementWrapStencilOp, DepthFormat, DepthStencilFormat, DoubleSide, DstAlphaFactor, DstColorFactor, DynamicCopyUsage, DynamicDrawUsage, DynamicReadUsage, EqualDepth, EqualStencilFunc, EquirectangularReflectionMapping, EquirectangularRefractionMapping, FaceColors, FlatShading, FloatType, FrontFaceDirectionCCW, FrontFaceDirectionCW, FrontSide, GammaEncoding, GreaterDepth, GreaterEqualDepth, GreaterEqualStencilFunc, GreaterStencilFunc, HalfFloatType, IncrementStencilOp, IncrementWrapStencilOp, IntType, InterpolateDiscrete, InterpolateLinear, InterpolateSmooth, InvertStencilOp, KeepStencilOp, LessDepth, LessEqualDepth, LessEqualStencilFunc, LessStencilFunc, LinearEncoding, LinearFilter, LinearMipMapLinearFilter, LinearMipMapNearestFilter, LinearMipmapLinearFilter, LinearMipmapNearestFilter, LinearToneMapping, LogLuvEncoding, LoopOnce, LoopPingPong, LoopRepeat, LuminanceAlphaFormat, LuminanceFormat, MOUSE, MaxEquation, MinEquation, MirroredRepeatWrapping, MixOperation, MultiplyBlending, MultiplyOperation, NearestFilter, NearestMipMapLinearFilter, NearestMipMapNearestFilter, NearestMipmapLinearFilter, NearestMipmapNearestFilter, NeverDepth, NeverStencilFunc, NoBlending, NoColors, NoToneMapping, NormalBlending, NotEqualDepth, NotEqualStencilFunc, ObjectSpaceNormalMap, OneFactor, OneMinusDstAlphaFactor, OneMinusDstColorFactor, OneMinusSrcAlphaFactor, OneMinusSrcColorFactor, PCFShadowMap, PCFSoftShadowMap, REVISION, RGBADepthPacking, RGBAFormat, RGBAIntegerFormat, RGBA_ASTC_10x10_Format, RGBA_ASTC_10x5_Format, RGBA_ASTC_10x6_Format, RGBA_ASTC_10x8_Format, RGBA_ASTC_12x10_Format, RGBA_ASTC_12x12_Format, RGBA_ASTC_4x4_Format, RGBA_ASTC_5x4_Format, RGBA_ASTC_5x5_Format, RGBA_ASTC_6x5_Format, RGBA_ASTC_6x6_Format, RGBA_ASTC_8x5_Format, RGBA_ASTC_8x6_Format, RGBA_ASTC_8x8_Format, RGBA_PVRTC_2BPPV1_Format, RGBA_PVRTC_4BPPV1_Format, RGBA_S3TC_DXT1_Format, RGBA_S3TC_DXT3_Format, RGBA_S3TC_DXT5_Format, RGBDEncoding, RGBEEncoding, RGBEFormat, RGBFormat, RGBIntegerFormat, RGBM16Encoding, RGBM7Encoding, RGB_ETC1_Format, RGB_PVRTC_2BPPV1_Format, RGB_PVRTC_4BPPV1_Format, RGB_S3TC_DXT1_Format, RGFormat, RGIntegerFormat, RedFormat, RedIntegerFormat, ReinhardToneMapping, RepeatWrapping, ReplaceStencilOp, ReverseSubtractEquation, ShortType, SmoothShading, SphericalReflectionMapping, SrcAlphaFactor, SrcAlphaSaturateFactor, SrcColorFactor, StaticCopyUsage, StaticDrawUsage, StaticReadUsage, StreamCopyUsage, StreamDrawUsage, StreamReadUsage, SubtractEquation, SubtractiveBlending, TOUCH, TangentSpaceNormalMap, TriangleFanDrawMode, TriangleStripDrawMode, TrianglesDrawMode, UVMapping, Uncharted2ToneMapping, UnsignedByteType, UnsignedInt248Type, UnsignedIntType, UnsignedShort4444Type, UnsignedShort5551Type, UnsignedShort565Type, UnsignedShortType, VSMShadowMap, VertexColors, WrapAroundEnding, ZeroCurvatureEnding, ZeroFactor, ZeroSlopeEnding, ZeroStencilOp, sRGBEncoding }; 187 | --------------------------------------------------------------------------------