├── LICENSE
├── README.md
├── about
├── ChromeWebStore_Badge_v2_496x150.png
├── banner-large.jpg
├── banner.jpg
├── demo.gif
├── snapshot-1.jpg
├── snapshot-2.jpg
├── snapshot-3.jpg
├── snapshot-4.jpg
├── snapshot.jpg
├── snapshot.png
└── thumb.png
└── src
├── assets
├── eye.svg
├── format.svg
├── fs.svg
├── fs_exit.svg
└── gears.svg
├── background.js
├── content_script.js
├── devtools.html
├── devtools.js
├── icon_128.png
├── icon_16.png
├── icon_48.png
├── manifest.json
├── panel.html
├── panel.js
└── treeView
├── item.svg
├── minus_square.svg
├── plus_square.svg
├── treeView.css
└── treeView.js
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Jaume Sanchez
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 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Three.js Editor Extension for Google Chrome
2 |
3 | A Chrome DevTools extension to manage any three.js project.
4 |
5 | [Install the extension from the Chrome Store](https://chrome.google.com/webstore/detail/threejs-editor-extension/fbgbekpggeldiacgjkacbkkcbjhmakea/)
6 | [](https://chrome.google.com/webstore/detail/threejs-editor-extension/fbgbekpggeldiacgjkacbkkcbjhmakea/)
7 |
8 | 
9 |
10 | Twin project of [WebGL GLSL Shader Editor Extension for Google Chrome](https://github.com/spite/ShaderEditorExtension)
11 |
12 | ### How to install ###
13 |
14 | While in beta, you can load the extension from disk directly:
15 | - Checkout the repo
16 | - Open Chrome's Extensions page (``Settings / More tools / Extensions``)
17 | - Enable ``Developer Mode``
18 | - Click on ``Load unpacked extension`...
19 | - Select the folder /src in the checked out project
20 |
21 | Alternatively, you can pack the extension yourself and load by dropping the .crx file in the Extensions page.
22 |
23 | ### How to use ###
24 |
25 | - Browse to a page with three.js content (you can find many here http://threejs.org/ or here https://www.chromeexperiments.com/webgl)
26 | - Open DevTools
27 | - Select the ``Three.js Editor`` tab
28 | - The extension needs to instrument ``THREE``, so the inspected tab has to be reloaded with the script injected. Hit the ``Reload`` button
29 | - If you are developing the page, make sure ``THREE`` is global
30 | - The extension will begin to track Scene and other objects allocations
31 | - Select an object to see its properties and change them
32 |
33 | #### License ####
34 |
35 | MIT licensed
36 |
37 | Copyright (C) 2015 Jaume Sanchez Elias, http://www.clicktorelease.com
--------------------------------------------------------------------------------
/about/ChromeWebStore_Badge_v2_496x150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/ChromeWebStore_Badge_v2_496x150.png
--------------------------------------------------------------------------------
/about/banner-large.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/banner-large.jpg
--------------------------------------------------------------------------------
/about/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/banner.jpg
--------------------------------------------------------------------------------
/about/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/demo.gif
--------------------------------------------------------------------------------
/about/snapshot-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/snapshot-1.jpg
--------------------------------------------------------------------------------
/about/snapshot-2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/snapshot-2.jpg
--------------------------------------------------------------------------------
/about/snapshot-3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/snapshot-3.jpg
--------------------------------------------------------------------------------
/about/snapshot-4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/snapshot-4.jpg
--------------------------------------------------------------------------------
/about/snapshot.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/snapshot.jpg
--------------------------------------------------------------------------------
/about/snapshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/snapshot.png
--------------------------------------------------------------------------------
/about/thumb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/about/thumb.png
--------------------------------------------------------------------------------
/src/assets/eye.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/format.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/src/assets/fs.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/fs_exit.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/src/assets/gears.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/background.js:
--------------------------------------------------------------------------------
1 | // chrome.extension calls
2 | var connections = {};
3 |
4 | chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
5 | // console.log('incoming message from injected script');
6 | // console.log(request);
7 |
8 | // Messages from content scripts should have sender.tab set
9 | if (sender.tab) {
10 | var tabId = sender.tab.id;
11 | if (tabId in connections) {
12 | connections[tabId].postMessage(request);
13 | } else {
14 | console.log("Tab not found in connection list.");
15 | }
16 | } else {
17 | console.log("sender.tab not defined.");
18 | }
19 | return true;
20 | });
21 |
22 | chrome.runtime.onConnect.addListener(function(port) {
23 |
24 | // Listen to messages sent from the DevTools page
25 | port.onMessage.addListener(function(request) {
26 | // console.log('incoming message from dev tools page');
27 |
28 | // Register initial connection
29 | if (request.name == 'init') {
30 | connections[request.tabId] = port;
31 |
32 | port.onDisconnect.addListener(function() {
33 | delete connections[request.tabId];
34 | });
35 |
36 | return;
37 | }
38 | });
39 |
40 | });
41 | /*
42 | chrome.webNavigation.onCommitted.addListener( function() {
43 | for( var j in connections ) {
44 | connections[ j ].postMessage( { method: 'onCommitted' } );
45 | }
46 | } );
47 |
48 | chrome.tabs.onUpdated.addListener( function( tabId ) {
49 |
50 | connections[ tabId ].postMessage( { method: 'onUpdated' } );
51 |
52 | } );*/
53 |
54 | chrome.webNavigation.onBeforeNavigate.addListener(function(data)
55 | {
56 | //console.log("onBeforeNavigate: " + data.url + ". Frame: " + data.frameId + ". Tab: " + data.tabId);
57 | });
58 |
59 | chrome.webNavigation.onCommitted.addListener(function(data) {
60 |
61 | //console.log("onCommitted: " + data.url + ". Frame: " + data.frameId + ". Tab: " + data.tabId);
62 |
63 | if( connections[ data.tabId ] ) {
64 | if( data.frameId === 0 ) {
65 | connections[ data.tabId ].postMessage( { method: 'inject' } );
66 | }
67 | }
68 |
69 | });
70 |
71 | chrome.webNavigation.onReferenceFragmentUpdated.addListener(function(data)
72 | {
73 | //console.log("onReferenceFragmentUpdated: " + data.url + ". Frame: " + data.frameId + ". Tab: " + data.tabId);
74 | });
75 |
76 | chrome.webNavigation.onErrorOccurred.addListener(function(data)
77 | {
78 | //console.log("onErrorOccurred: " + data.url + ". Frame: " + data.frameId + ". Tab: " + data.tabId + ". Error: " + data.error);
79 | });
80 |
81 | chrome.webNavigation.onReferenceFragmentUpdated.addListener(function(data)
82 | {
83 | //console.log("onReferenceFragmentUpdated: " + data.url + ". Frame: " + data.frameId + ". Tab: " + data.tabId);
84 | });
85 |
86 | /*chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab)
87 | {
88 | //console.log("tabs.onUpdated: " + changeInfo.url + ". Status: " + changeInfo.status + ". Tab: " + tabId);
89 | });
90 |
91 | chrome.history.onVisited.addListener(function(historyItem)
92 | {
93 | //console.log("history.onVisited: " + historyItem.url);
94 | });*/
95 | /*
96 | chrome.webNavigation.onCompleted.addListener(function(data)
97 | {
98 | //console.log("onCompleted: " + data.url + ". Frame: " + data.frameId + ". Tab: " + data.tabId);
99 | });*/
100 |
--------------------------------------------------------------------------------
/src/content_script.js:
--------------------------------------------------------------------------------
1 | window.addEventListener('message', function(event) {
2 |
3 | //console.log( 'message ', event );
4 |
5 | if (event.source !== window) {
6 | return;
7 | }
8 |
9 | var message = event.data;
10 |
11 | // Only accept messages that we know are ours
12 | if (typeof message !== 'object' || message === null ) {
13 | return;
14 | }
15 |
16 | chrome.runtime.sendMessage(message);
17 | });
--------------------------------------------------------------------------------
/src/devtools.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/src/devtools.js:
--------------------------------------------------------------------------------
1 | // chrome.devtools calls
2 |
3 | chrome.devtools.panels.create( "Three.js Editor",
4 | "icon.png",
5 | "panel.html",
6 | function(panel) {
7 |
8 | // code invoked on panel creation
9 | }
10 | );
11 |
12 | // Create a connection to the background page
13 | var backgroundPageConnection = chrome.runtime.connect({
14 | name: 'panel'
15 | });
16 |
17 | backgroundPageConnection.postMessage({
18 | name: 'init',
19 | tabId: chrome.devtools.inspectedWindow.tabId
20 | });
21 |
22 | backgroundPageConnection.onMessage.addListener(function(msg) {
23 | //console.log( 'devtools.js', msg );
24 | });
--------------------------------------------------------------------------------
/src/icon_128.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/src/icon_128.png
--------------------------------------------------------------------------------
/src/icon_16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/src/icon_16.png
--------------------------------------------------------------------------------
/src/icon_48.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/spite/ThreeJSEditorExtension/472412ee1709bb3317270f1884424eb751d2c9f0/src/icon_48.png
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Three.js Editor Extension",
3 | "version": "1.0.0",
4 | "minimum_chrome_version": "10.0",
5 | "devtools_page": "devtools.html",
6 | "icons": {
7 | "128": "icon_128.png",
8 | "48": "icon_48.png",
9 | "16": "icon_16.png"
10 | },
11 | "description": "Three.js editor extension",
12 | "background": {
13 | "scripts": [
14 | "background.js"
15 | ]
16 | },
17 | "content_scripts": [{
18 | "matches": [""],
19 | "js": ["content_script.js"],
20 | "run_at": "document_end",
21 | "all_frames": true
22 | } ],
23 | "permissions": [
24 | "http://*/*",
25 | "https://*/*",
26 | "webNavigation"
27 | ],
28 | "manifest_version": 2,
29 | "content_security_policy": "default-src 'self' chrome-extension-resource: ; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; connect-src *; frame-src *;",
30 | "web_accessible_resources": [
31 | "*"
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/src/panel.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
39 |
40 |
41 |
42 |
43 |
44 |
Welcome!
45 |
Three.js scene editor extension
v1.0.0 (beta)
46 |
To start tracking three.js scenes,
the extension needs to reload the page.
47 |
48 |
Bugs, ideas and feedback: GitHub page
@thespite | www.clicktorelease.com
49 |
50 |
51 |
Waiting for scenes to be created...
52 |
53 |
54 |
94 |
95 |
Settings
96 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/src/panel.js:
--------------------------------------------------------------------------------
1 | function f() {
2 |
3 | window.__Injected = true;
4 |
5 | //function log() { console.log( arguments ); }
6 | //function error() { console.error( arguments ); }
7 | function log() {}
8 | function error() {}
9 |
10 | function log( msg ) { logMsg( 'LOG: ' + msg )}
11 | function error( msg ) { logMsg( 'ERROR: ' + msg )}
12 |
13 | function logMsg() {
14 |
15 | var args = [];
16 | for( var j = 0; j < arguments.length; j++ ) {
17 | args.push( arguments[ j ] );
18 | }
19 |
20 | window.postMessage( { source: 'ThreejsEditor', method: 'log', arguments: args }, '*');
21 | }
22 |
23 | /* var methods = [
24 | 'createProgram', 'linkProgram', 'useProgram',
25 | 'createShader', 'shaderSource', 'compileShader', 'attachShader', 'detachShader',
26 | 'getUniformLocation',
27 | 'getAttribLocation', 'vertexAttribPointer', 'enableVertexAttribArray', 'bindAttribLocation',
28 | 'bindBuffer'
29 | ];
30 |
31 | this.references = {};
32 | methods.forEach( function( f ) {
33 | this.references[ f ] = WebGLRenderingContext.prototype[ f ];
34 | }.bind ( this ) );*/
35 |
36 | function _h( f, c ) {
37 |
38 | return function() {
39 | var res = f.apply( this, arguments );
40 | res = c.apply( this, [ res, arguments ] ) || res;
41 | return res;
42 | }
43 |
44 | }
45 |
46 | function _h2( f, c ) {
47 |
48 | return function() {
49 | return c.apply( this, arguments );
50 | }
51 |
52 | }
53 |
54 | var generateUUID = ( function() {
55 |
56 | // http://www.broofa.com/Tools/Math.uuid.htm
57 |
58 | var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
59 | var uuid = new Array( 36 );
60 | var rnd = 0, r;
61 |
62 | return function () {
63 |
64 | for ( var i = 0; i < 36; i ++ ) {
65 |
66 | if ( i == 8 || i == 13 || i == 18 || i == 23 ) {
67 |
68 | uuid[ i ] = '-';
69 |
70 | } else if ( i == 14 ) {
71 |
72 | uuid[ i ] = '4';
73 |
74 | } else {
75 |
76 | if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
77 | r = rnd & 0xf;
78 | rnd = rnd >> 4;
79 | uuid[ i ] = chars[ ( i == 19 ) ? ( r & 0x3 ) | 0x8 : r ];
80 |
81 | }
82 | }
83 |
84 | return uuid.join( '' );
85 |
86 | };
87 |
88 | } )();
89 |
90 | function sniffType( object ) {
91 |
92 | for( var j in types ) {
93 | if( object instanceof THREE[ types[ j ] ] ) {
94 | return types[ j ];
95 | }
96 | }
97 |
98 | debugger; // dafuc?
99 |
100 | }
101 |
102 | var objects = {};
103 | var types = [];
104 |
105 | function checkThreeJs() {
106 |
107 | if( window.THREE && window.THREE.REVISION ) {
108 | instrument();
109 | } else {
110 | setTimeout( checkThreeJs, 10 );
111 | }
112 | }
113 |
114 | checkThreeJs();
115 |
116 | function processAddObject( object, parent ) {
117 |
118 | if( !object.uuid ) object.uuid = generateUUID();
119 | addObject( object, parent );
120 |
121 | object.children.forEach( function( child ) {
122 | if( child instanceof THREE.Object3D ) {
123 | processAddObject( child, object );
124 | }
125 | } );
126 |
127 | }
128 |
129 | function processRemoveObject( object, parent ) {
130 |
131 | removeObject( object, parent );
132 |
133 | object.children.forEach( function( child ) {
134 | if( child instanceof THREE.Object3D ) {
135 | processRemoveObject( child, object );
136 | }
137 | } );
138 |
139 | }
140 |
141 | function instrumentRendererRender( renderer ) {
142 |
143 | var oldRender = renderer.render;
144 | renderer.render = function() {
145 | oldRender.apply( renderer, arguments );
146 | var scene = arguments[ 0 ];
147 | var camera = arguments[ 1 ];
148 | window.postMessage( { source: 'ThreejsEditor', method: 'render', sceneId: scene.uuid, cameraId: camera.uuid }, '*');
149 | }
150 |
151 | }
152 |
153 | function instrumentLate() {
154 |
155 | var propertiesToIgnore = [ 'webkitStorageInfo', 'webkitIndexedDB' ];
156 |
157 | for( var j in window ) {
158 | if( propertiesToIgnore.indexOf( j ) >= 0 ) continue;
159 | if( window[ j ] instanceof THREE.WebGLRenderer ) {
160 | logMsg( '++ Existing WebGLRenderer' );
161 | var object = window[ j ];
162 | instrumentRendererRender( object );
163 | }
164 | if( window[ j ] instanceof THREE.Object3D ) {
165 | logMsg( '++ Existing Object3D' );
166 | var object = window[ j ];
167 |
168 | processAddObject( object );
169 | }
170 | }
171 |
172 | }
173 |
174 | function addObject( object, parent ) {
175 |
176 | var type = sniffType( object );
177 | objects[ object.uuid ] = object;
178 |
179 | if( parent && ( type === 'PerspectiveCamera' || type === 'OrthographicCamera' ) ) {
180 | if( sniffType( parent ) === 'Scene' ) {
181 | return;
182 | }
183 | }
184 |
185 | //logMsg( '++ Adding Object ' + type + ' (' + object.uuid + ') (parent ' + ( parent?parent.uuid:'' ) + ')' );
186 | window.postMessage( { source: 'ThreejsEditor', method: 'addObject', id: object.uuid, parentId: parent?parent.uuid:null, type: type, label: type, visible: object.visible }, '*');
187 |
188 | }
189 |
190 | function removeObject( object, parent ) {
191 |
192 | //objects[ object.uuid ] = object;
193 |
194 | var type = sniffType( object );
195 | //logMsg( '++ Removing Object ' + type + ' (' + object.uuid + ') (parent ' + ( parent?parent.uuid:'' ) + ')' );
196 | window.postMessage( { source: 'ThreejsEditor', method: 'removeObject', id: object.uuid, parentId: parent?parent.uuid:null }, '*');
197 |
198 | }
199 |
200 | function extractTypes() {
201 |
202 | for( var j in THREE ) {
203 | if( typeof THREE[ j ] === 'function' ) {
204 | types.unshift( j );
205 | }
206 | }
207 |
208 | }
209 |
210 | function instrument() {
211 |
212 | extractTypes();
213 | logMsg( 'INSTRUMENT LATE' )
214 | instrumentLate();
215 | logMsg( 'DONE' );
216 |
217 | THREE.WebGLRenderer = _h( THREE.WebGLRenderer, function() {
218 | logMsg( '++ NEW WebGLRenderer' );
219 | instrumentRendererRender( this );
220 | } );
221 |
222 | var oldObject3D = THREE.Object3D;
223 | THREE.Object3D = _h( THREE.Object3D, function() {
224 | logMsg( '++ NEW Object3D' );
225 | var object = this;
226 | if( !object.uuid ) object.uuid = generateUUID();
227 | addObject( object );
228 | } );
229 | THREE.Object3D.prototype = oldObject3D.prototype;
230 | for( var j in oldObject3D ) {
231 | if( oldObject3D.hasOwnProperty( j ) ) {
232 | THREE.Object3D[ j ] = oldObject3D[ j ];
233 | }
234 | }
235 |
236 | THREE.Object3D.prototype.add = _h( THREE.Object3D.prototype.add, function() {
237 |
238 | var parent = this;
239 | if( !parent.uuid ) parent.uuid = generateUUID();
240 | for( var j = 0; j < arguments[ 1 ].length; j++ ) {
241 | logMsg( '++ Object3D.add' );
242 | var object = arguments[ 1 ][ j ];
243 | if( !object.uuid ) object.uuid = generateUUID();
244 | processAddObject( object, parent );
245 | }
246 |
247 | } );
248 |
249 | THREE.Object3D.prototype.remove = _h( THREE.Object3D.prototype.remove, function() {
250 |
251 | var parent = this;
252 | for( var j = 0; j < arguments[ 1 ].length; j++ ) {
253 | logMsg( '++ Object3D.remove' );
254 | var object = arguments[ 1 ][ j ];
255 | processRemoveObject( object, parent );
256 | }
257 |
258 | } );
259 |
260 | THREE.WebGLRenderer.prototype.render = _h( THREE.WebGLRenderer.prototype.render, function() {
261 |
262 | processAddObject( object, parent );
263 |
264 | } );
265 |
266 | }
267 |
268 | /*
269 | Object3D: uuid, name, parent, position, rotation, scale, visible, data
270 | Mesh: uuid, name, parent, position, rotation, scale, visible, data, geometry, material
271 |
272 | PointLight: uuid, name, parent, position, intensity, color, distance, decay, visible, data
273 | SpotLight: uuid, name, parent, position, intensity, color, distance, angle, exponent, decay, visible, data
274 | DirectionalLight: uuid, name, parent, position, intensity, color, visible, data
275 | HemisphereLight: uuid, name, parent, position, intensity, color, ground color, visible, data
276 | AmbientLight: uuid, name, parent, position, color, visible, data
277 | PerspectiveCamera: uuid, name, parent, position, rotation, scale, fov, near, far, visible, data
278 | */
279 |
280 | var fields = {
281 | 'uuid': { type: 'ti' },
282 | 'name': { type: 'ti' },
283 | 'position': { type: 'v3' },
284 | 'rotation': { type: 'v3' },
285 | 'scale': { type: 'v3' },
286 | 'visible': { type: 'b' },
287 | 'userData': { type: 'tj' },
288 | 'intensity': { type: 'f' },
289 | 'color': { type: 'c' },
290 | 'groundColor': { type: 'c' },
291 | 'distance': { type: 'f' },
292 | 'angle': { type: 'f' },
293 | 'decay': { type: 'f' },
294 | 'exponent': { type: 'f' },
295 | 'fov': { type: 'f' },
296 | 'near': { type: 'f' },
297 | 'far': { type: 'f' },
298 | 'left': { type: 'f' },
299 | 'right': { type: 'f' },
300 | 'top': { type: 'f' },
301 | 'bottom': { type: 'f' },
302 | 'aspect': { type: 'f' },
303 | 'castShadow': { type: 'b' },
304 | 'receiveShadow': { type: 'b' }
305 | }
306 |
307 | var categories = {
308 | 'object3d': [ 'uuid', 'name', 'visible', 'position', 'rotation', 'scale', 'userData', 'castShadow', 'receiveShadow' ],
309 | 'light': [ 'intensity', 'color', 'groundColor', 'distance', 'angle', 'decay', 'exponent' ],
310 | 'camera': [ 'fov', 'near', 'far', 'left', 'right', 'top', 'bottom', 'aspect' ]
311 | }
312 |
313 | var properties = {
314 | 'Object3D': [ 'uuid', 'name', 'position', 'visible', 'userData', 'castShadow', 'receiveShadow' ],
315 | 'Mesh': [ 'rotation', 'scale' ],
316 | 'PointCloud': [ 'rotation', 'scale' ],
317 | 'PointLight': [ 'intensity', 'color', 'distance', 'decay' ],
318 | 'SpotLight': [ 'intensity', 'color' ],
319 | 'HemisphereLight': [ 'intensity', 'color', 'groundColor' ],
320 | 'DirectionalLight': [ 'intensity', 'color' ],
321 | 'AmbientLight': [ 'color' ],
322 | 'PerspectiveCamera': [ 'rotation', 'scale', 'fov', 'near', 'far', 'aspect' ],
323 | 'OrthographicCamera': [ 'left', 'right', 'top', 'bottom', 'near', 'far' ]
324 | }
325 |
326 | window.UISelect = function( id ) {
327 |
328 | var o = objects[ id ];
329 | var data = {
330 | id: o.id,
331 | };
332 |
333 | var p = [];
334 | for( var j in properties ) {
335 | if( o instanceof THREE[ j ] ) {
336 | logMsg( j );
337 | for( var i in properties[ j ] ) {
338 | var property = properties[ j ][ i ];
339 | var type = fields[ property ].type;
340 | logMsg( property, type );
341 | switch( type ) {
342 | case 'f':
343 | case 't':
344 | case 'ti':
345 | case 'b':
346 | data[ property ] = o[ property ];
347 | break;
348 | case 'tj':
349 | data[ property ] = JSON.stringify( o[ property ] );
350 | break;
351 | case 'v3':
352 | data[ property ] = {
353 | x: o[ property ].x,
354 | y: o[ property ].y,
355 | z: o[ property ].z,
356 | };
357 | break;
358 | case 'c':
359 | data[ property ] = o[ property ].getHexString();
360 | break;
361 | }
362 | //data[ property ][ 'instance' ] = j;
363 | }
364 | }
365 | }
366 |
367 | logMsg( JSON.stringify( data ) );
368 |
369 | window.postMessage( { source: 'ThreejsEditor', method: 'objectSelected', id: id, data: JSON.stringify( data ) }, '*');
370 |
371 | }
372 |
373 | window.ChangeProperty = function( id, data ) {
374 |
375 | logMsg( JSON.stringify( data ) );
376 |
377 | var o = objects[ id ];
378 | var dataFields = data.property.split( '-' );
379 | var v = o;
380 | for( var j = 1; j < dataFields.length; j++ ) {
381 | if( j === dataFields.length - 1 ) {
382 | var f = fields[ dataFields[ j ] ];
383 | if( f && f.type === 'c' ) {
384 | v[ dataFields[ j ] ].set( data.value );
385 | } else {
386 | if( f && f.type === 'tj' ) {
387 | v[ dataFields[ j ] ] = JSON.parse( data.value );
388 | } else {
389 | v[ dataFields[ j ] ] = data.value;
390 | }
391 | }
392 | } else {
393 | v = v[ dataFields[ j ] ];
394 | }
395 | }
396 |
397 | if( dataFields[ 0 ] === 'camera' ) {
398 | o.updateProjectionMatrix();
399 | if( o instanceof THREE.PerspectiveCamera ) {
400 | }
401 | if( o instanceof THREE.OrthographicCamera ) {
402 | }
403 | }
404 |
405 | }
406 |
407 | window.addEventListener( 'load', function() {
408 | window.postMessage( { source: 'ThreejsEditor', method: 'init' }, '*');
409 | window.postMessage( { source: 'ThreejsEditor', method: 'activateFields', fields: JSON.stringify( fields ), categories: JSON.stringify( categories ) }, '*');
410 | } );
411 |
412 | }
413 |
414 | var links = document.querySelectorAll( 'a[rel=external]' );
415 | for( var j = 0; j < links.length; j++ ) {
416 | var a = links[ j ];
417 | a.addEventListener( 'click', function( e ) {
418 | window.open( this.href, '_blank' );
419 | e.preventDefault();
420 | }, false );
421 | }
422 |
423 | var button = document.getElementById( 'reload' ),
424 | container = document.getElementById( 'container' ),
425 | info = document.getElementById( 'info' ),
426 | waiting = document.getElementById( 'waiting' ),
427 | log = document.getElementById( 'log' ),
428 | ul = document.querySelector( '#container ul' ),
429 | treeViewContainer = document.getElementById( 'treeView' );
430 |
431 | var verbose = !true;
432 | if( verbose ) {
433 | log.style.left = '50%';
434 | log.style.display = 'block';
435 | container.style.right= '50%';
436 | log.addEventListener( 'click', function() {
437 | this.textContent = '';
438 | } );
439 | }
440 |
441 | function logMsg() {
442 |
443 | var args = [];
444 | for( var j = 0; j < arguments.length; j++ ) {
445 | args.push( arguments[ j ] );
446 | }
447 | var p = document.createElement( 'p' );
448 | p.textContent = args.join( ' ' );
449 | log.appendChild( p );
450 |
451 | }
452 |
453 | logMsg( 'starting' );
454 |
455 | button.addEventListener( 'click', function( e ) {
456 | chrome.devtools.inspectedWindow.reload( {
457 | ignoreCache: false
458 | } );
459 | } );
460 |
461 | var backgroundPageConnection = chrome.runtime.connect({
462 | name: 'panel'
463 | });
464 |
465 | backgroundPageConnection.postMessage({
466 | name: 'init',
467 | tabId: chrome.devtools.inspectedWindow.tabId
468 | });
469 |
470 | var settings = {
471 | }
472 |
473 | /*var stored = chrome.storage.sync.get( 'highlight', function( i ) {
474 |
475 | logMsg( 'retrieved' );
476 | logMsg( i );
477 | settings.highlight = i;
478 | document.getElementById( 'highlightButton' ).style.opacity = settings.highlight ? 1 : .5;
479 |
480 | } );*/
481 |
482 | var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
483 |
484 | function encodeSource(input) {
485 | var str = String(input);
486 | for (
487 | var block, charCode, idx = 0, map = chars, output = '';
488 | str.charAt(idx | 0) || (map = '=', idx % 1);
489 | output += map.charAt(63 & block >> 8 - idx % 1 * 8)
490 | ) {
491 | charCode = str.charCodeAt(idx += 3/4);
492 | if (charCode > 0xFF) {
493 | throw new InvalidCharacterError("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");
494 | }
495 | block = block << 8 | charCode;
496 | }
497 | return output;
498 | }
499 |
500 | function tearDown() {
501 |
502 | log.textContent = '';
503 | logMsg( '>> tear down' );
504 |
505 | while( treeViewContainer.firstChild ) treeViewContainer.removeChild( treeViewContainer.firstChild );
506 | objects = {};
507 | scenes = {};
508 |
509 | treeView = new TreeView( treeViewContainer );
510 | treeView.onSelect = function( id ) {
511 | logMsg( 'SELECTED ' + id );
512 | chrome.devtools.inspectedWindow.eval( 'UISelect( \'' + id + '\' )' );
513 | }
514 | r = new TreeViewItem( 'Renderer', null ).setVisible( true );
515 | treeView.getRoot().appendChild( r );
516 |
517 | for( var i in categories ){
518 | var el = document.getElementById( i + '-panel' );
519 | el.style.display = 'none';
520 | }
521 |
522 | }
523 |
524 | var objects = {};
525 | var scenes = {};
526 | var treeView = null;
527 | var r = null;
528 | var currentObject = null;
529 |
530 | var panel = {};
531 | var fields = {};
532 | var categories = {};
533 |
534 | function hashCode(str) { // java String#hashCode
535 | var hash = 0;
536 | for (var i = 0; i < str.length; i++) {
537 | hash = str.charCodeAt(i) + ((hash << 5) - hash);
538 | }
539 | return hash;
540 | }
541 |
542 | function intToRGB(i){
543 | var c = (i & 0x00FFFFFF)
544 | .toString(16)
545 | .toUpperCase();
546 |
547 | return "00000".substring(0, 6 - c.length) + c;
548 | }
549 |
550 | function parseFields( ) {
551 |
552 | var category = null;
553 |
554 | for( var j in fields ) {
555 | var t = fields[ j ].type;
556 | for( var i in categories ) {
557 | var c = i;
558 | for( var k in categories[ i ] ) {
559 | if( categories[ i ][ k ] === j ) {
560 | category = c;
561 | }
562 | }
563 | }
564 |
565 | var id = category + '-' + j;
566 | logMsg( j, t, category, id );
567 |
568 | switch( t ) {
569 | case 't':
570 | case 'ti':
571 | case 'f':
572 | panel[ j ] = document.getElementById( id );
573 | ( function( id ) { panel[ j ].addEventListener( 'change', function( e ) {
574 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '\', value: ' + this.value + ' } )' );
575 | } ) } )( id );
576 | break;
577 | case 'tj':
578 | panel[ j ] = document.getElementById( id );
579 | ( function( id ) { panel[ j ].addEventListener( 'change', function( e ) {
580 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '\', value: \'' + this.value + '\' } )' );
581 | } ) } )( id );
582 | break;
583 | case 'c':
584 | panel[ j ] = document.getElementById( id );
585 | ( function( id ) { panel[ j ].addEventListener( 'change', function( e ) {
586 | var c = this.value.toString();
587 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '\', value: \'' + c + '\' } )' );
588 | } ) } )( id );
589 | break;
590 | case 'b':
591 | panel[ j ] = document.getElementById( id );
592 | ( function( id ) { panel[ j ].addEventListener( 'change', function( e ) {
593 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '\', value: ' + this.checked + ' } )' );
594 | } ) } )( id );
595 | break;
596 | case 'v3':
597 | panel[ j + 'x' ] = document.getElementById( id + '-x' );
598 | panel[ j + 'y' ] = document.getElementById( id + '-y' );
599 | panel[ j + 'z' ] = document.getElementById( id + '-z' );
600 | ( function( id ) { panel[ j + 'x' ].addEventListener( 'change', function( e ) {
601 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '-x\', value: ' + this.value + ' } )' );
602 | } ) } )( id );
603 | ( function( id ) { panel[ j + 'y' ].addEventListener( 'change', function( e ) {
604 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '-y\', value: ' + this.value + ' } )' );
605 | } ) } )( id );
606 | ( function( id ) { panel[ j + 'z' ].addEventListener( 'change', function( e ) {
607 | chrome.devtools.inspectedWindow.eval( 'ChangeProperty( \'' + currentObject.id + '\', { property: \'' + id + '-z\', value: ' + this.value + ' } )' );
608 | } ) } )( id );
609 | break;
610 | }
611 |
612 | }
613 |
614 | logMsg( 'PANEL', JSON.stringify( panel ) );
615 |
616 | }
617 |
618 | backgroundPageConnection.onMessage.addListener( function( msg ) {
619 |
620 | switch( msg.method ) {
621 | case 'activateFields':
622 | fields = JSON.parse( msg.fields );
623 | categories = JSON.parse( msg.categories );
624 | parseFields();
625 | break;
626 | case 'inject':
627 | logMsg( '>> inject' );
628 | tearDown();
629 | logMsg( chrome.devtools.inspectedWindow.eval( '(' + f.toString() + ')()' ) );
630 | break;
631 | case 'init':
632 | logMsg( '>> init' );
633 | info.style.display = 'none';
634 | waiting.style.display = 'none';
635 | container.style.display = 'block';
636 | break;
637 | case 'addObject':
638 | //logMsg( '>> ADD OBJECT', JSON.stringify( msg ) );
639 | logMsg( '>> ADD OBJECT', msg.type, msg.id, msg.parentId );
640 |
641 | //logMsg( ' -- OBJECTS RIGHT NOW: ', JSON.stringify( objects ) );
642 | if( objects[ msg.id ] === undefined ) {
643 |
644 | var n = new TreeViewItem( msg.label, msg.id ).setVisible( msg.visible );
645 | data = {
646 | type: msg.type,
647 | node: n
648 | }
649 |
650 | objects[ msg.id ] = {
651 | id: msg.id,
652 | parent: msg.parentId,
653 | data: data
654 | }
655 | //logMsg( '>> ADDED' );
656 | }
657 |
658 | if( msg.parentId ) {
659 | objects[ msg.id ].data.parent = msg.parentId;
660 | }
661 |
662 | if( msg.parentId ) {
663 | //logMsg( '>> CONNECT #', objects[ msg.parentId ], '#', objects[ msg.id ], '#' );
664 | if( objects[ msg.id ].data.node.parentNode ) objects[ msg.id ].data.node.parentNode.removeChild( objects[ msg.id ].data.node );
665 | objects[ msg.parentId ].data.node.appendChild( objects[ msg.id ].data.node );
666 | objects[ msg.id ].parent = msg.parentId;
667 | } else {
668 | if( !objects[ msg.id ].data.node.parentNode ) r.appendChild( objects[ msg.id ].data.node );
669 | }
670 | //logMsg( '>> DONE' );
671 | break;
672 | case 'removeObject':
673 | logMsg( '>> REMOVE OBJECT', msg.id );
674 | //logMsg( ' -- OBJECTS RIGHT NOW: ', JSON.stringify( objects ) );
675 | if( objects[ msg.id ] !== undefined ) {
676 | if( objects[ msg.id ].data.node.parentNode ) objects[ msg.id ].data.node.parentNode.removeChild( objects[ msg.id ].data.node );
677 | objects[ msg.id ] = undefined;
678 | //logMsg( '>> REMOVED' );
679 | } else {
680 | //logMsg( ' -- CACHED' );
681 | }
682 | /*if( msg.parentId ) {
683 | logMsg( '>> CONNECT #', objects[ msg.parentId ], '#', objects[ msg.id ], '#' );
684 | g.setEdge( msg.parentId, msg.id, { lineInterpolate: 'basis', arrowhead: 'normal' } );
685 | objects[ msg.id ].parent = parentId;
686 | }*/
687 | logMsg( '>> DONE' );
688 | break;
689 | case 'objectSelected' :
690 | logMsg( '>>> OBJECT SELECTED' );
691 | currentObject = objects[ msg.id ];
692 | var data = JSON.parse( msg.data );
693 | for( var j in panel ) {
694 | panel[ j ].parentElement.parentElement.style.display = 'none';
695 | }
696 | for( var i in categories ){
697 | var el = document.getElementById( i + '-panel' );
698 | el.style.display = 'none';
699 | logMsg( 'HIDE ' + i );
700 | }
701 |
702 | for( var j in data ) {
703 | if( fields[ j ] != undefined ) {
704 | var type = fields[ j ].type;
705 | logMsg( j, panel[ j ], type );
706 | switch( type ) {
707 | case 't':
708 | case 'ti':
709 | case 'tj':
710 | case 'f':
711 | panel[ j ].value = data[ j ];
712 | panel[ j ].parentElement.parentElement.style.display = 'block';
713 | break;
714 | case 'b':
715 | panel[ j ].checked = data[ j ] === true;
716 | panel[ j ].parentElement.parentElement.style.display = 'block';
717 | break;
718 | case 'c':
719 | panel[ j ].value = '#' + data[ j ];
720 | panel[ j ].parentElement.parentElement.style.display = 'block';
721 | break;
722 | case 'v3':
723 | panel[ j + 'x' ].value = data[ j ].x;
724 | panel[ j + 'y' ].value = data[ j ].y;
725 | panel[ j + 'z' ].value = data[ j ].z;
726 | panel[ j + 'x' ].parentElement.parentElement.style.display = 'block';
727 | panel[ j + 'y' ].parentElement.parentElement.style.display = 'block';
728 | panel[ j + 'z' ].parentElement.parentElement.style.display = 'block';
729 | break;
730 | }
731 | }
732 |
733 | for( var i in categories ){
734 | for (var k in categories[ i ] ) {
735 | if( categories[ i ][ k ] === j ) {
736 | var el = document.getElementById( i + '-panel' );
737 | el.style.display = 'block';
738 | logMsg( 'SHOW ' + i );
739 | }
740 | }
741 | }
742 | }
743 |
744 | break;
745 | case 'render':
746 | /*g.setEdge( msg.cameraId, msg.sceneId, {
747 | lineInterpolate: 'basis',
748 | arrowhead: 'normal',
749 | style: "stroke-dasharray: 5, 5;",
750 | } );*/
751 | break;
752 | case 'log':
753 | logMsg( msg.arguments );
754 | break;
755 | }
756 |
757 | } );
758 |
--------------------------------------------------------------------------------
/src/treeView/item.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/treeView/minus_square.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/treeView/plus_square.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/treeView/treeView.css:
--------------------------------------------------------------------------------
1 | ul.treeView li{ margin-left: 1.2em; position: relative;}
2 | ul.treeView li span.icon{
3 | width: 1em;
4 | height: 1em;
5 | display: block;
6 | float: left;
7 | margin-right: .4em;
8 | margin-top: .2em;
9 | background-size: contain;
10 | background-repeat: no-repeat;
11 | background-position: 50% 50%;
12 | opacity: .6;
13 | position: absolute;
14 | }
15 | ul.treeView li span.button{
16 | background-image: url(minus_square.svg);
17 | cursor: pointer;
18 | }
19 | ul.treeView li span.item{
20 | background-image: url(item.svg);
21 | }
22 | ul.treeView{ list-style-type: none;
23 | -webkit-touch-callout: none;
24 | -webkit-user-select: none;
25 | -khtml-user-select: none;
26 | -moz-user-select: none;
27 | -ms-user-select: none;.7
28 | user-select: none;
29 | line-height: 1.5em;
30 | position: absolute;
31 | left: 0;
32 | top: 0;
33 | }
34 | ul.treeView ul{ list-style-type: none }
35 | ul.treeView > li:first-child{ margin-left: .2em;}
36 | ul.treeView li p{ display: inline; cursor: pointer; margin-left: 1.3em;}
37 | ul.treeView li p:hover{ color: #222;}
38 | ul.treeView li.collapsed{ height: 1.5em; overflow: hidden;}
39 | ul.treeView li.collapsed span.button{ background-image: url(plus_square.svg); }
40 | ul.treeView li{ color: #AAA; }
41 | ul.treeView li.visible{ color: #777;}
42 | ul.treeView .active{ font-weight: bold; color: black;}
43 |
--------------------------------------------------------------------------------
/src/treeView/treeView.js:
--------------------------------------------------------------------------------
1 | function TreeViewItem( label, id ) {
2 |
3 | this.label = label || '';
4 | this.children = [];
5 | this.collapsed = false;
6 | this.id = id || null;
7 |
8 | this.li = document.createElement( 'li' );
9 | var p = document.createElement( 'p' );
10 | p.textContent = this.label;
11 | this.span = document.createElement( 'span' );
12 | this.span.className = 'icon item';
13 | this.li.appendChild( this.span );
14 | this.li.appendChild( p );
15 |
16 | this.ul = null;
17 | this.parent = null;
18 | this.parentNode = null;
19 |
20 | var li = this.li;
21 |
22 | p.addEventListener( 'click', function( e ) {
23 |
24 | this.parent.clear();
25 | p.classList.add( 'active' );
26 |
27 | this.parent.onSelect( this.id );
28 |
29 | e.preventDefault();
30 |
31 | }.bind( this ) );
32 |
33 | this.span.addEventListener( 'click', function( e ) {
34 |
35 | if( this.children.length ) {
36 | li.classList.toggle( 'collapsed' );
37 | }
38 |
39 | e.preventDefault();
40 |
41 | }.bind( this ) );
42 |
43 | }
44 |
45 | TreeViewItem.prototype.createRootNode = function() {
46 |
47 | this.ul = document.createElement( 'ul' );
48 |
49 | }
50 |
51 | TreeViewItem.prototype.render = function() {
52 |
53 |
54 | }
55 |
56 | TreeViewItem.prototype.setVisible = function( boolean ) {
57 |
58 | this.li.classList.toggle( 'visible', boolean );
59 | return this;
60 |
61 | }
62 |
63 | TreeViewItem.prototype.appendChild = function( child ) {
64 |
65 | child.parent = this.parent;
66 | child.parentNode = this;
67 |
68 | if( this.ul === null ) {
69 | this.ul = document.createElement( 'ul' );
70 | this.li.appendChild( this.ul );
71 | }
72 |
73 | this.children.push( child );
74 | this.ul.appendChild( child.li );
75 |
76 | this.span.className = 'icon button';
77 |
78 | }
79 |
80 | TreeViewItem.prototype.removeChild = function( child ) {
81 |
82 | child.parent = null;
83 | child.parentNode = null;
84 |
85 | if( this.ul === null ) {
86 | this.li.removeChild( this.ul );
87 | }
88 |
89 | for( var j = 0; j < this.children.length; j++ ) {
90 | if( this.children[ j ] === child ) {
91 | this.children.splice( j, 1 );
92 | break;
93 | }
94 | }
95 | this.ul.removeChild( child.li );
96 |
97 | if( this.children.length ) {
98 | this.span.className = 'icon item';
99 | }
100 |
101 | }
102 |
103 | function TreeView( base ) {
104 |
105 | this.base = base;
106 | this.root = new TreeViewItem( 'WebGLRenderer');
107 | this.root.createRootNode();
108 | this.root.ul.classList.add( 'treeView' );
109 | this.root.parent = this;
110 | this.base.appendChild( this.root.ul );
111 |
112 | }
113 |
114 | TreeView.prototype.clear = function() {
115 |
116 | var sel = this.root.ul.querySelector( '.active' );
117 | if( sel ) sel.classList.remove( 'active' );
118 |
119 | }
120 |
121 | TreeView.prototype.getRoot = function() {
122 |
123 | return this.root;
124 |
125 | }
126 |
127 | TreeView.prototype.render = function() {
128 |
129 | }
130 |
131 | TreeView.prototype.onSelect = function() {
132 |
133 | }
134 |
--------------------------------------------------------------------------------