├── README.md
├── images
├── door_left.png
├── door_right.png
├── floor.jpg
└── gz4.png
├── index.html
├── js
├── Detector.js
├── MTLLoader.js
├── OBJLoader.js
├── OrbitControls.js
├── ThreeBSP.js
├── jquery-1.9.1.js
├── onEvent.js
└── three.js
├── obj
├── camera.mtl
├── camera_left.obj
├── camera_right.obj
├── fire.mtl
└── fire.obj
└── video
├── 4.mp4
└── 5.mp4
/README.md:
--------------------------------------------------------------------------------
1 | # 库房建模示例
2 |
3 | ### 技术交流群:1018373095
4 |
5 | ### 使用three.js进行库房建模,包含货柜,摄像头,灭火器等,其中点击摄像头可以播放视频。
6 | 直接下载运行即可,具体内容请看代码,都有详细注释,不明白的可以提issues。
7 |
8 |
9 |
10 | ### 开源不易,且行且珍惜,您的star✨是我继续完善的动力,谢谢!
11 |
--------------------------------------------------------------------------------
/images/door_left.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhuGYao/threejs_room_model/30e6c46942eaf5bd8646a267cfdb3025784a2f9a/images/door_left.png
--------------------------------------------------------------------------------
/images/door_right.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhuGYao/threejs_room_model/30e6c46942eaf5bd8646a267cfdb3025784a2f9a/images/door_right.png
--------------------------------------------------------------------------------
/images/floor.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhuGYao/threejs_room_model/30e6c46942eaf5bd8646a267cfdb3025784a2f9a/images/floor.jpg
--------------------------------------------------------------------------------
/images/gz4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ZhuGYao/threejs_room_model/30e6c46942eaf5bd8646a267cfdb3025784a2f9a/images/gz4.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 仓库布局
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
26 |
29 |
32 |
35 |
36 |
516 |
517 |
518 |
519 |
--------------------------------------------------------------------------------
/js/Detector.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author alteredq / http://alteredqualia.com/
3 | * @author mr.doob / http://mrdoob.com/
4 | */
5 |
6 | var Detector = {
7 |
8 | canvas: !! window.CanvasRenderingContext2D,
9 | webgl: ( function () {
10 |
11 | try {
12 |
13 | var canvas = document.createElement( 'canvas' ); return !! ( window.WebGLRenderingContext && ( canvas.getContext( 'webgl' ) || canvas.getContext( 'experimental-webgl' ) ) );
14 |
15 | } catch ( e ) {
16 |
17 | return false;
18 |
19 | }
20 |
21 | } )(),
22 | workers: !! window.Worker,
23 | fileapi: window.File && window.FileReader && window.FileList && window.Blob,
24 |
25 | getWebGLErrorMessage: function () {
26 |
27 | var element = document.createElement( 'div' );
28 | element.id = 'webgl-error-message';
29 | element.style.fontFamily = 'monospace';
30 | element.style.fontSize = '13px';
31 | element.style.fontWeight = 'normal';
32 | element.style.textAlign = 'center';
33 | element.style.background = '#fff';
34 | element.style.color = '#000';
35 | element.style.padding = '1.5em';
36 | element.style.width = '400px';
37 | element.style.margin = '5em auto 0';
38 |
39 | if ( ! this.webgl ) {
40 |
41 | element.innerHTML = window.WebGLRenderingContext ? [
42 | 'Your graphics card does not seem to support WebGL.
',
43 | 'Find out how to get it here.'
44 | ].join( '\n' ) : [
45 | 'Your browser does not seem to support WebGL.
',
46 | 'Find out how to get it here.'
47 | ].join( '\n' );
48 |
49 | }
50 |
51 | return element;
52 |
53 | },
54 |
55 | addGetWebGLMessage: function ( parameters ) {
56 |
57 | var parent, id, element;
58 |
59 | parameters = parameters || {};
60 |
61 | parent = parameters.parent !== undefined ? parameters.parent : document.body;
62 | id = parameters.id !== undefined ? parameters.id : 'oldie';
63 |
64 | element = Detector.getWebGLErrorMessage();
65 | element.id = id;
66 |
67 | parent.appendChild( element );
68 |
69 | }
70 |
71 | };
72 |
73 | // browserify support
74 | if ( typeof module === 'object' ) {
75 |
76 | module.exports = Detector;
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/js/MTLLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Loads a Wavefront .mtl file specifying materials
3 | *
4 | * @author angelxuanchang
5 | */
6 |
7 | THREE.MTLLoader = function ( manager ) {
8 |
9 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
10 |
11 | };
12 |
13 | THREE.MTLLoader.prototype = {
14 |
15 | constructor: THREE.MTLLoader,
16 |
17 | /**
18 | * Loads and parses a MTL asset from a URL.
19 | *
20 | * @param {String} url - URL to the MTL file.
21 | * @param {Function} [onLoad] - Callback invoked with the loaded object.
22 | * @param {Function} [onProgress] - Callback for download progress.
23 | * @param {Function} [onError] - Callback for download errors.
24 | *
25 | * @see setPath setTexturePath
26 | *
27 | * @note In order for relative texture references to resolve correctly
28 | * you must call setPath and/or setTexturePath explicitly prior to load.
29 | */
30 | load: function ( url, onLoad, onProgress, onError ) {
31 |
32 | var scope = this;
33 |
34 | var loader = new THREE.FileLoader( this.manager );
35 | loader.setPath( this.path );
36 | loader.load( url, function ( text ) {
37 |
38 | onLoad( scope.parse( text ) );
39 |
40 | }, onProgress, onError );
41 |
42 | },
43 |
44 | /**
45 | * Set base path for resolving references.
46 | * If set this path will be prepended to each loaded and found reference.
47 | *
48 | * @see setTexturePath
49 | * @param {String} path
50 | *
51 | * @example
52 | * mtlLoader.setPath( 'assets/obj/' );
53 | * mtlLoader.load( 'my.mtl', ... );
54 | */
55 | setPath: function ( path ) {
56 |
57 | this.path = path;
58 |
59 | },
60 |
61 | /**
62 | * Set base path for resolving texture references.
63 | * If set this path will be prepended found texture reference.
64 | * If not set and setPath is, it will be used as texture base path.
65 | *
66 | * @see setPath
67 | * @param {String} path
68 | *
69 | * @example
70 | * mtlLoader.setPath( 'assets/obj/' );
71 | * mtlLoader.setTexturePath( 'assets/textures/' );
72 | * mtlLoader.load( 'my.mtl', ... );
73 | */
74 | setTexturePath: function ( path ) {
75 |
76 | this.texturePath = path;
77 |
78 | },
79 |
80 | setBaseUrl: function ( path ) {
81 |
82 | console.warn( 'THREE.MTLLoader: .setBaseUrl() is deprecated. Use .setTexturePath( path ) for texture path or .setPath( path ) for general base path instead.' );
83 |
84 | this.setTexturePath( path );
85 |
86 | },
87 |
88 | setCrossOrigin: function ( value ) {
89 |
90 | this.crossOrigin = value;
91 |
92 | },
93 |
94 | setMaterialOptions: function ( value ) {
95 |
96 | this.materialOptions = value;
97 |
98 | },
99 |
100 | /**
101 | * Parses a MTL file.
102 | *
103 | * @param {String} text - Content of MTL file
104 | * @return {THREE.MTLLoader.MaterialCreator}
105 | *
106 | * @see setPath setTexturePath
107 | *
108 | * @note In order for relative texture references to resolve correctly
109 | * you must call setPath and/or setTexturePath explicitly prior to parse.
110 | */
111 | parse: function ( text ) {
112 |
113 | var lines = text.split( '\n' );
114 | var info = {};
115 | var delimiter_pattern = /\s+/;
116 | var materialsInfo = {};
117 |
118 | for ( var i = 0; i < lines.length; i ++ ) {
119 |
120 | var line = lines[ i ];
121 | line = line.trim();
122 |
123 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) {
124 |
125 | // Blank line or comment ignore
126 | continue;
127 |
128 | }
129 |
130 | var pos = line.indexOf( ' ' );
131 |
132 | var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line;
133 | key = key.toLowerCase();
134 |
135 | var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : '';
136 | value = value.trim();
137 |
138 | if ( key === 'newmtl' ) {
139 |
140 | // New material
141 |
142 | info = { name: value };
143 | materialsInfo[ value ] = info;
144 |
145 | } else if ( info ) {
146 |
147 | if ( key === 'ka' || key === 'kd' || key === 'ks' ) {
148 |
149 | var ss = value.split( delimiter_pattern, 3 );
150 | info[ key ] = [ parseFloat( ss[ 0 ] ), parseFloat( ss[ 1 ] ), parseFloat( ss[ 2 ] ) ];
151 |
152 | } else {
153 |
154 | info[ key ] = value;
155 |
156 | }
157 |
158 | }
159 |
160 | }
161 |
162 | var materialCreator = new THREE.MTLLoader.MaterialCreator( this.texturePath || this.path, this.materialOptions );
163 | materialCreator.setCrossOrigin( this.crossOrigin );
164 | materialCreator.setManager( this.manager );
165 | materialCreator.setMaterials( materialsInfo );
166 | return materialCreator;
167 |
168 | }
169 |
170 | };
171 |
172 | /**
173 | * Create a new THREE-MTLLoader.MaterialCreator
174 | * @param baseUrl - Url relative to which textures are loaded
175 | * @param options - Set of options on how to construct the materials
176 | * side: Which side to apply the material
177 | * THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide
178 | * wrap: What type of wrapping to apply for textures
179 | * THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
180 | * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
181 | * Default: false, assumed to be already normalized
182 | * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
183 | * Default: false
184 | * @constructor
185 | */
186 |
187 | THREE.MTLLoader.MaterialCreator = function ( baseUrl, options ) {
188 |
189 | this.baseUrl = baseUrl || '';
190 | this.options = options;
191 | this.materialsInfo = {};
192 | this.materials = {};
193 | this.materialsArray = [];
194 | this.nameLookup = {};
195 |
196 | this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide;
197 | this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping;
198 |
199 | };
200 |
201 | THREE.MTLLoader.MaterialCreator.prototype = {
202 |
203 | constructor: THREE.MTLLoader.MaterialCreator,
204 |
205 | crossOrigin: 'Anonymous',
206 |
207 | setCrossOrigin: function ( value ) {
208 |
209 | this.crossOrigin = value;
210 |
211 | },
212 |
213 | setManager: function ( value ) {
214 |
215 | this.manager = value;
216 |
217 | },
218 |
219 | setMaterials: function ( materialsInfo ) {
220 |
221 | this.materialsInfo = this.convert( materialsInfo );
222 | this.materials = {};
223 | this.materialsArray = [];
224 | this.nameLookup = {};
225 |
226 | },
227 |
228 | convert: function ( materialsInfo ) {
229 |
230 | if ( ! this.options ) return materialsInfo;
231 |
232 | var converted = {};
233 |
234 | for ( var mn in materialsInfo ) {
235 |
236 | // Convert materials info into normalized form based on options
237 |
238 | var mat = materialsInfo[ mn ];
239 |
240 | var covmat = {};
241 |
242 | converted[ mn ] = covmat;
243 |
244 | for ( var prop in mat ) {
245 |
246 | var save = true;
247 | var value = mat[ prop ];
248 | var lprop = prop.toLowerCase();
249 |
250 | switch ( lprop ) {
251 |
252 | case 'kd':
253 | case 'ka':
254 | case 'ks':
255 |
256 | // Diffuse color (color under white light) using RGB values
257 |
258 | if ( this.options && this.options.normalizeRGB ) {
259 |
260 | value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ];
261 |
262 | }
263 |
264 | if ( this.options && this.options.ignoreZeroRGBs ) {
265 |
266 | if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 2 ] === 0 ) {
267 |
268 | // ignore
269 |
270 | save = false;
271 |
272 | }
273 |
274 | }
275 |
276 | break;
277 |
278 | default:
279 |
280 | break;
281 |
282 | }
283 |
284 | if ( save ) {
285 |
286 | covmat[ lprop ] = value;
287 |
288 | }
289 |
290 | }
291 |
292 | }
293 |
294 | return converted;
295 |
296 | },
297 |
298 | preload: function () {
299 |
300 | for ( var mn in this.materialsInfo ) {
301 |
302 | this.create( mn );
303 |
304 | }
305 |
306 | },
307 |
308 | getIndex: function ( materialName ) {
309 |
310 | return this.nameLookup[ materialName ];
311 |
312 | },
313 |
314 | getAsArray: function () {
315 |
316 | var index = 0;
317 |
318 | for ( var mn in this.materialsInfo ) {
319 |
320 | this.materialsArray[ index ] = this.create( mn );
321 | this.nameLookup[ mn ] = index;
322 | index ++;
323 |
324 | }
325 |
326 | return this.materialsArray;
327 |
328 | },
329 |
330 | create: function ( materialName ) {
331 |
332 | if ( this.materials[ materialName ] === undefined ) {
333 |
334 | this.createMaterial_( materialName );
335 |
336 | }
337 |
338 | return this.materials[ materialName ];
339 |
340 | },
341 |
342 | createMaterial_: function ( materialName ) {
343 |
344 | // Create material
345 |
346 | var scope = this;
347 | var mat = this.materialsInfo[ materialName ];
348 | var params = {
349 |
350 | name: materialName,
351 | side: this.side
352 |
353 | };
354 |
355 | function resolveURL( baseUrl, url ) {
356 |
357 | if ( typeof url !== 'string' || url === '' )
358 | return '';
359 |
360 | // Absolute URL
361 | if ( /^https?:\/\//i.test( url ) ) return url;
362 |
363 | return baseUrl + url;
364 |
365 | }
366 |
367 | function setMapForType( mapType, value ) {
368 |
369 | if ( params[ mapType ] ) return; // Keep the first encountered texture
370 |
371 | var texParams = scope.getTextureParams( value, params );
372 | var map = scope.loadTexture( resolveURL( scope.baseUrl, texParams.url ) );
373 |
374 | map.repeat.copy( texParams.scale );
375 | map.offset.copy( texParams.offset );
376 |
377 | map.wrapS = scope.wrap;
378 | map.wrapT = scope.wrap;
379 |
380 | params[ mapType ] = map;
381 |
382 | }
383 |
384 | for ( var prop in mat ) {
385 |
386 | var value = mat[ prop ];
387 | var n;
388 |
389 | if ( value === '' ) continue;
390 |
391 | switch ( prop.toLowerCase() ) {
392 |
393 | // Ns is material specular exponent
394 |
395 | case 'kd':
396 |
397 | // Diffuse color (color under white light) using RGB values
398 |
399 | params.color = new THREE.Color().fromArray( value );
400 |
401 | break;
402 |
403 | case 'ks':
404 |
405 | // Specular color (color when light is reflected from shiny surface) using RGB values
406 | params.specular = new THREE.Color().fromArray( value );
407 |
408 | break;
409 |
410 | case 'map_kd':
411 |
412 | // Diffuse texture map
413 |
414 | setMapForType( "map", value );
415 |
416 | break;
417 |
418 | case 'map_ks':
419 |
420 | // Specular map
421 |
422 | setMapForType( "specularMap", value );
423 |
424 | break;
425 |
426 | case 'norm':
427 |
428 | setMapForType( "normalMap", value );
429 |
430 | break;
431 |
432 | case 'map_bump':
433 | case 'bump':
434 |
435 | // Bump texture map
436 |
437 | setMapForType( "bumpMap", value );
438 |
439 | break;
440 |
441 | case 'ns':
442 |
443 | // The specular exponent (defines the focus of the specular highlight)
444 | // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
445 |
446 | params.shininess = parseFloat( value );
447 |
448 | break;
449 |
450 | case 'd':
451 | n = parseFloat(value);
452 |
453 | if ( n < 1 ) {
454 |
455 | params.opacity = n;
456 | params.transparent = true;
457 |
458 | }
459 |
460 | break;
461 |
462 | case 'tr':
463 | n = parseFloat(value);
464 |
465 | if ( n > 0 ) {
466 |
467 | params.opacity = 1 - n;
468 | params.transparent = true;
469 |
470 | }
471 |
472 | break;
473 |
474 | default:
475 | break;
476 |
477 | }
478 |
479 | }
480 |
481 | this.materials[ materialName ] = new THREE.MeshPhongMaterial( params );
482 | return this.materials[ materialName ];
483 |
484 | },
485 |
486 | getTextureParams: function ( value, matParams ) {
487 |
488 | var texParams = {
489 |
490 | scale: new THREE.Vector2( 1, 1 ),
491 | offset: new THREE.Vector2( 0, 0 )
492 |
493 | };
494 |
495 | var items = value.split( /\s+/ );
496 | var pos;
497 |
498 | pos = items.indexOf( '-bm' );
499 |
500 | if ( pos >= 0 ) {
501 |
502 | matParams.bumpScale = parseFloat( items[ pos + 1 ] );
503 | items.splice( pos, 2 );
504 |
505 | }
506 |
507 | pos = items.indexOf( '-s' );
508 |
509 | if ( pos >= 0 ) {
510 |
511 | texParams.scale.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
512 | items.splice( pos, 4 ); // we expect 3 parameters here!
513 |
514 | }
515 |
516 | pos = items.indexOf( '-o' );
517 |
518 | if ( pos >= 0 ) {
519 |
520 | texParams.offset.set( parseFloat( items[ pos + 1 ] ), parseFloat( items[ pos + 2 ] ) );
521 | items.splice( pos, 4 ); // we expect 3 parameters here!
522 |
523 | }
524 |
525 | texParams.url = items.join( ' ' ).trim();
526 | return texParams;
527 |
528 | },
529 |
530 | loadTexture: function ( url, mapping, onLoad, onProgress, onError ) {
531 |
532 | var texture;
533 | var loader = THREE.Loader.Handlers.get( url );
534 | var manager = ( this.manager !== undefined ) ? this.manager : THREE.DefaultLoadingManager;
535 |
536 | if ( loader === null ) {
537 |
538 | loader = new THREE.TextureLoader( manager );
539 |
540 | }
541 |
542 | if ( loader.setCrossOrigin ) loader.setCrossOrigin( this.crossOrigin );
543 | texture = loader.load( url, onLoad, onProgress, onError );
544 |
545 | if ( mapping !== undefined ) texture.mapping = mapping;
546 |
547 | return texture;
548 |
549 | }
550 |
551 | };
552 |
--------------------------------------------------------------------------------
/js/OBJLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.OBJLoader = ( function () {
6 |
7 | // o object_name | g group_name
8 | var object_pattern = /^[og]\s*(.+)?/;
9 | // mtllib file_reference
10 | var material_library_pattern = /^mtllib /;
11 | // usemtl material_name
12 | var material_use_pattern = /^usemtl /;
13 |
14 | function ParserState() {
15 |
16 | var state = {
17 | objects : [],
18 | object : {},
19 |
20 | vertices : [],
21 | normals : [],
22 | uvs : [],
23 |
24 | materialLibraries : [],
25 |
26 | startObject: function ( name, fromDeclaration ) {
27 |
28 | // If the current object (initial from reset) is not from a g/o declaration in the parsed
29 | // file. We need to use it for the first parsed g/o to keep things in sync.
30 | if ( this.object && this.object.fromDeclaration === false ) {
31 |
32 | this.object.name = name;
33 | this.object.fromDeclaration = ( fromDeclaration !== false );
34 | return;
35 |
36 | }
37 |
38 | var previousMaterial = ( this.object && typeof this.object.currentMaterial === 'function' ? this.object.currentMaterial() : undefined );
39 |
40 | if ( this.object && typeof this.object._finalize === 'function' ) {
41 |
42 | this.object._finalize( true );
43 |
44 | }
45 |
46 | this.object = {
47 | name : name || '',
48 | fromDeclaration : ( fromDeclaration !== false ),
49 |
50 | geometry : {
51 | vertices : [],
52 | normals : [],
53 | uvs : []
54 | },
55 | materials : [],
56 | smooth : true,
57 |
58 | startMaterial: function ( name, libraries ) {
59 |
60 | var previous = this._finalize( false );
61 |
62 | // New usemtl declaration overwrites an inherited material, except if faces were declared
63 | // after the material, then it must be preserved for proper MultiMaterial continuation.
64 | if ( previous && ( previous.inherited || previous.groupCount <= 0 ) ) {
65 |
66 | this.materials.splice( previous.index, 1 );
67 |
68 | }
69 |
70 | var material = {
71 | index : this.materials.length,
72 | name : name || '',
73 | mtllib : ( Array.isArray( libraries ) && libraries.length > 0 ? libraries[ libraries.length - 1 ] : '' ),
74 | smooth : ( previous !== undefined ? previous.smooth : this.smooth ),
75 | groupStart : ( previous !== undefined ? previous.groupEnd : 0 ),
76 | groupEnd : -1,
77 | groupCount : -1,
78 | inherited : false,
79 |
80 | clone: function ( index ) {
81 | var cloned = {
82 | index : ( typeof index === 'number' ? index : this.index ),
83 | name : this.name,
84 | mtllib : this.mtllib,
85 | smooth : this.smooth,
86 | groupStart : 0,
87 | groupEnd : -1,
88 | groupCount : -1,
89 | inherited : false
90 | };
91 | cloned.clone = this.clone.bind(cloned);
92 | return cloned;
93 | }
94 | };
95 |
96 | this.materials.push( material );
97 |
98 | return material;
99 |
100 | },
101 |
102 | currentMaterial: function () {
103 |
104 | if ( this.materials.length > 0 ) {
105 | return this.materials[ this.materials.length - 1 ];
106 | }
107 |
108 | return undefined;
109 |
110 | },
111 |
112 | _finalize: function ( end ) {
113 |
114 | var lastMultiMaterial = this.currentMaterial();
115 | if ( lastMultiMaterial && lastMultiMaterial.groupEnd === -1 ) {
116 |
117 | lastMultiMaterial.groupEnd = this.geometry.vertices.length / 3;
118 | lastMultiMaterial.groupCount = lastMultiMaterial.groupEnd - lastMultiMaterial.groupStart;
119 | lastMultiMaterial.inherited = false;
120 |
121 | }
122 |
123 | // Ignore objects tail materials if no face declarations followed them before a new o/g started.
124 | if ( end && this.materials.length > 1 ) {
125 |
126 | for ( var mi = this.materials.length - 1; mi >= 0; mi-- ) {
127 | if ( this.materials[ mi ].groupCount <= 0 ) {
128 | this.materials.splice( mi, 1 );
129 | }
130 | }
131 |
132 | }
133 |
134 | // Guarantee at least one empty material, this makes the creation later more straight forward.
135 | if ( end && this.materials.length === 0 ) {
136 |
137 | this.materials.push({
138 | name : '',
139 | smooth : this.smooth
140 | });
141 |
142 | }
143 |
144 | return lastMultiMaterial;
145 |
146 | }
147 | };
148 |
149 | // Inherit previous objects material.
150 | // Spec tells us that a declared material must be set to all objects until a new material is declared.
151 | // If a usemtl declaration is encountered while this new object is being parsed, it will
152 | // overwrite the inherited material. Exception being that there was already face declarations
153 | // to the inherited material, then it will be preserved for proper MultiMaterial continuation.
154 |
155 | if ( previousMaterial && previousMaterial.name && typeof previousMaterial.clone === 'function' ) {
156 |
157 | var declared = previousMaterial.clone( 0 );
158 | declared.inherited = true;
159 | this.object.materials.push( declared );
160 |
161 | }
162 |
163 | this.objects.push( this.object );
164 |
165 | },
166 |
167 | finalize: function () {
168 |
169 | if ( this.object && typeof this.object._finalize === 'function' ) {
170 |
171 | this.object._finalize( true );
172 |
173 | }
174 |
175 | },
176 |
177 | parseVertexIndex: function ( value, len ) {
178 |
179 | var index = parseInt( value, 10 );
180 | return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
181 |
182 | },
183 |
184 | parseNormalIndex: function ( value, len ) {
185 |
186 | var index = parseInt( value, 10 );
187 | return ( index >= 0 ? index - 1 : index + len / 3 ) * 3;
188 |
189 | },
190 |
191 | parseUVIndex: function ( value, len ) {
192 |
193 | var index = parseInt( value, 10 );
194 | return ( index >= 0 ? index - 1 : index + len / 2 ) * 2;
195 |
196 | },
197 |
198 | addVertex: function ( a, b, c ) {
199 |
200 | var src = this.vertices;
201 | var dst = this.object.geometry.vertices;
202 |
203 | dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
204 | dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
205 | dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
206 |
207 | },
208 |
209 | addVertexLine: function ( a ) {
210 |
211 | var src = this.vertices;
212 | var dst = this.object.geometry.vertices;
213 |
214 | dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
215 |
216 | },
217 |
218 | addNormal: function ( a, b, c ) {
219 |
220 | var src = this.normals;
221 | var dst = this.object.geometry.normals;
222 |
223 | dst.push( src[ a + 0 ], src[ a + 1 ], src[ a + 2 ] );
224 | dst.push( src[ b + 0 ], src[ b + 1 ], src[ b + 2 ] );
225 | dst.push( src[ c + 0 ], src[ c + 1 ], src[ c + 2 ] );
226 |
227 | },
228 |
229 | addUV: function ( a, b, c ) {
230 |
231 | var src = this.uvs;
232 | var dst = this.object.geometry.uvs;
233 |
234 | dst.push( src[ a + 0 ], src[ a + 1 ] );
235 | dst.push( src[ b + 0 ], src[ b + 1 ] );
236 | dst.push( src[ c + 0 ], src[ c + 1 ] );
237 |
238 | },
239 |
240 | addUVLine: function ( a ) {
241 |
242 | var src = this.uvs;
243 | var dst = this.object.geometry.uvs;
244 |
245 | dst.push( src[ a + 0 ], src[ a + 1 ] );
246 |
247 | },
248 |
249 | addFace: function ( a, b, c, ua, ub, uc, na, nb, nc ) {
250 |
251 | var vLen = this.vertices.length;
252 |
253 | var ia = this.parseVertexIndex( a, vLen );
254 | var ib = this.parseVertexIndex( b, vLen );
255 | var ic = this.parseVertexIndex( c, vLen );
256 |
257 | this.addVertex( ia, ib, ic );
258 |
259 | if ( ua !== undefined ) {
260 |
261 | var uvLen = this.uvs.length;
262 |
263 | ia = this.parseUVIndex( ua, uvLen );
264 | ib = this.parseUVIndex( ub, uvLen );
265 | ic = this.parseUVIndex( uc, uvLen );
266 |
267 | this.addUV( ia, ib, ic );
268 |
269 | }
270 |
271 | if ( na !== undefined ) {
272 |
273 | // Normals are many times the same. If so, skip function call and parseInt.
274 | var nLen = this.normals.length;
275 | ia = this.parseNormalIndex( na, nLen );
276 |
277 | ib = na === nb ? ia : this.parseNormalIndex( nb, nLen );
278 | ic = na === nc ? ia : this.parseNormalIndex( nc, nLen );
279 |
280 | this.addNormal( ia, ib, ic );
281 |
282 | }
283 |
284 | },
285 |
286 | addLineGeometry: function ( vertices, uvs ) {
287 |
288 | this.object.geometry.type = 'Line';
289 |
290 | var vLen = this.vertices.length;
291 | var uvLen = this.uvs.length;
292 |
293 | for ( var vi = 0, l = vertices.length; vi < l; vi ++ ) {
294 |
295 | this.addVertexLine( this.parseVertexIndex( vertices[ vi ], vLen ) );
296 |
297 | }
298 |
299 | for ( var uvi = 0, l = uvs.length; uvi < l; uvi ++ ) {
300 |
301 | this.addUVLine( this.parseUVIndex( uvs[ uvi ], uvLen ) );
302 |
303 | }
304 |
305 | }
306 |
307 | };
308 |
309 | state.startObject( '', false );
310 |
311 | return state;
312 |
313 | }
314 |
315 | //
316 |
317 | function OBJLoader( manager ) {
318 |
319 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
320 |
321 | this.materials = null;
322 |
323 | };
324 |
325 | OBJLoader.prototype = {
326 |
327 | constructor: OBJLoader,
328 |
329 | load: function ( url, onLoad, onProgress, onError ) {
330 |
331 | var scope = this;
332 |
333 | var loader = new THREE.FileLoader( scope.manager );
334 | loader.setPath( this.path );
335 | loader.load( url, function ( text ) {
336 |
337 | onLoad( scope.parse( text ) );
338 |
339 | }, onProgress, onError );
340 |
341 | },
342 |
343 | setPath: function ( value ) {
344 |
345 | this.path = value;
346 |
347 | },
348 |
349 | setMaterials: function ( materials ) {
350 |
351 | this.materials = materials;
352 |
353 | return this;
354 |
355 | },
356 |
357 | parse: function ( text ) {
358 |
359 | console.time( 'OBJLoader' );
360 |
361 | var state = new ParserState();
362 |
363 | if ( text.indexOf( '\r\n' ) !== - 1 ) {
364 |
365 | // This is faster than String.split with regex that splits on both
366 | text = text.replace( /\r\n/g, '\n' );
367 |
368 | }
369 |
370 | if ( text.indexOf( '\\\n' ) !== - 1) {
371 |
372 | // join lines separated by a line continuation character (\)
373 | text = text.replace( /\\\n/g, '' );
374 |
375 | }
376 |
377 | var lines = text.split( '\n' );
378 | var line = '', lineFirstChar = '';
379 | var lineLength = 0;
380 | var result = [];
381 |
382 | // Faster to just trim left side of the line. Use if available.
383 | var trimLeft = ( typeof ''.trimLeft === 'function' );
384 |
385 | for ( var i = 0, l = lines.length; i < l; i ++ ) {
386 |
387 | line = lines[ i ];
388 |
389 | line = trimLeft ? line.trimLeft() : line.trim();
390 |
391 | lineLength = line.length;
392 |
393 | if ( lineLength === 0 ) continue;
394 |
395 | lineFirstChar = line.charAt( 0 );
396 |
397 | // @todo invoke passed in handler if any
398 | if ( lineFirstChar === '#' ) continue;
399 |
400 | if ( lineFirstChar === 'v' ) {
401 |
402 | var data = line.split( /\s+/ );
403 |
404 | switch ( data[ 0 ] ) {
405 |
406 | case 'v':
407 | state.vertices.push(
408 | parseFloat( data[ 1 ] ),
409 | parseFloat( data[ 2 ] ),
410 | parseFloat( data[ 3 ] )
411 | );
412 | break;
413 | case 'vn':
414 | state.normals.push(
415 | parseFloat( data[ 1 ] ),
416 | parseFloat( data[ 2 ] ),
417 | parseFloat( data[ 3 ] )
418 | );
419 | break;
420 | case 'vt':
421 | state.uvs.push(
422 | parseFloat( data[ 1 ] ),
423 | parseFloat( data[ 2 ] )
424 | );
425 | break;
426 | }
427 |
428 | } else if ( lineFirstChar === 'f' ) {
429 |
430 | var lineData = line.substr( 1 ).trim();
431 | var vertexData = lineData.split( /\s+/ );
432 | var faceVertices = [];
433 |
434 | // Parse the face vertex data into an easy to work with format
435 |
436 | for ( var j = 0, jl = vertexData.length; j < jl; j ++ ) {
437 |
438 | var vertex = vertexData[ j ];
439 |
440 | if ( vertex.length > 0 ) {
441 |
442 | var vertexParts = vertex.split( '/' );
443 | faceVertices.push( vertexParts );
444 |
445 | }
446 |
447 | }
448 |
449 | // Draw an edge between the first vertex and all subsequent vertices to form an n-gon
450 |
451 | var v1 = faceVertices[ 0 ];
452 |
453 | for ( var j = 1, jl = faceVertices.length - 1; j < jl; j ++ ) {
454 |
455 | var v2 = faceVertices[ j ];
456 | var v3 = faceVertices[ j + 1 ];
457 |
458 | state.addFace(
459 | v1[ 0 ], v2[ 0 ], v3[ 0 ],
460 | v1[ 1 ], v2[ 1 ], v3[ 1 ],
461 | v1[ 2 ], v2[ 2 ], v3[ 2 ]
462 | );
463 |
464 | }
465 |
466 | } else if ( lineFirstChar === 'l' ) {
467 |
468 | var lineParts = line.substring( 1 ).trim().split( " " );
469 | var lineVertices = [], lineUVs = [];
470 |
471 | if ( line.indexOf( "/" ) === - 1 ) {
472 |
473 | lineVertices = lineParts;
474 |
475 | } else {
476 |
477 | for ( var li = 0, llen = lineParts.length; li < llen; li ++ ) {
478 |
479 | var parts = lineParts[ li ].split( "/" );
480 |
481 | if ( parts[ 0 ] !== "" ) lineVertices.push( parts[ 0 ] );
482 | if ( parts[ 1 ] !== "" ) lineUVs.push( parts[ 1 ] );
483 |
484 | }
485 |
486 | }
487 | state.addLineGeometry( lineVertices, lineUVs );
488 |
489 | } else if ( ( result = object_pattern.exec( line ) ) !== null ) {
490 |
491 | // o object_name
492 | // or
493 | // g group_name
494 |
495 | // WORKAROUND: https://bugs.chromium.org/p/v8/issues/detail?id=2869
496 | // var name = result[ 0 ].substr( 1 ).trim();
497 | var name = ( " " + result[ 0 ].substr( 1 ).trim() ).substr( 1 );
498 |
499 | state.startObject( name );
500 |
501 | } else if ( material_use_pattern.test( line ) ) {
502 |
503 | // material
504 |
505 | state.object.startMaterial( line.substring( 7 ).trim(), state.materialLibraries );
506 |
507 | } else if ( material_library_pattern.test( line ) ) {
508 |
509 | // mtl file
510 |
511 | state.materialLibraries.push( line.substring( 7 ).trim() );
512 |
513 | } else if ( lineFirstChar === 's' ) {
514 |
515 | result = line.split( ' ' );
516 |
517 | // smooth shading
518 |
519 | // @todo Handle files that have varying smooth values for a set of faces inside one geometry,
520 | // but does not define a usemtl for each face set.
521 | // This should be detected and a dummy material created (later MultiMaterial and geometry groups).
522 | // This requires some care to not create extra material on each smooth value for "normal" obj files.
523 | // where explicit usemtl defines geometry groups.
524 | // Example asset: examples/models/obj/cerberus/Cerberus.obj
525 |
526 | /*
527 | * http://paulbourke.net/dataformats/obj/
528 | * or
529 | * http://www.cs.utah.edu/~boulos/cs3505/obj_spec.pdf
530 | *
531 | * From chapter "Grouping" Syntax explanation "s group_number":
532 | * "group_number is the smoothing group number. To turn off smoothing groups, use a value of 0 or off.
533 | * Polygonal elements use group numbers to put elements in different smoothing groups. For free-form
534 | * surfaces, smoothing groups are either turned on or off; there is no difference between values greater
535 | * than 0."
536 | */
537 | if ( result.length > 1 ) {
538 |
539 | var value = result[ 1 ].trim().toLowerCase();
540 | state.object.smooth = ( value !== '0' && value !== 'off' );
541 |
542 | } else {
543 |
544 | // ZBrush can produce "s" lines #11707
545 | state.object.smooth = true;
546 |
547 | }
548 | var material = state.object.currentMaterial();
549 | if ( material ) material.smooth = state.object.smooth;
550 |
551 | } else {
552 |
553 | // Handle null terminated files without exception
554 | if ( line === '\0' ) continue;
555 |
556 | throw new Error( "Unexpected line: '" + line + "'" );
557 |
558 | }
559 |
560 | }
561 |
562 | state.finalize();
563 |
564 | var container = new THREE.Group();
565 | container.materialLibraries = [].concat( state.materialLibraries );
566 |
567 | for ( var i = 0, l = state.objects.length; i < l; i ++ ) {
568 |
569 | var object = state.objects[ i ];
570 | var geometry = object.geometry;
571 | var materials = object.materials;
572 | var isLine = ( geometry.type === 'Line' );
573 |
574 | // Skip o/g line declarations that did not follow with any faces
575 | if ( geometry.vertices.length === 0 ) continue;
576 |
577 | var buffergeometry = new THREE.BufferGeometry();
578 |
579 | buffergeometry.addAttribute( 'position', new THREE.BufferAttribute( new Float32Array( geometry.vertices ), 3 ) );
580 |
581 | if ( geometry.normals.length > 0 ) {
582 |
583 | buffergeometry.addAttribute( 'normal', new THREE.BufferAttribute( new Float32Array( geometry.normals ), 3 ) );
584 |
585 | } else {
586 |
587 | buffergeometry.computeVertexNormals();
588 |
589 | }
590 |
591 | if ( geometry.uvs.length > 0 ) {
592 |
593 | buffergeometry.addAttribute( 'uv', new THREE.BufferAttribute( new Float32Array( geometry.uvs ), 2 ) );
594 |
595 | }
596 |
597 | // Create materials
598 |
599 | var createdMaterials = [];
600 |
601 | for ( var mi = 0, miLen = materials.length; mi < miLen ; mi++ ) {
602 |
603 | var sourceMaterial = materials[ mi ];
604 | var material = undefined;
605 |
606 | if ( this.materials !== null ) {
607 |
608 | material = this.materials.create( sourceMaterial.name );
609 |
610 | // mtl etc. loaders probably can't create line materials correctly, copy properties to a line material.
611 | if ( isLine && material && ! ( material instanceof THREE.LineBasicMaterial ) ) {
612 |
613 | var materialLine = new THREE.LineBasicMaterial();
614 | materialLine.copy( material );
615 | material = materialLine;
616 |
617 | }
618 |
619 | }
620 |
621 | if ( ! material ) {
622 |
623 | material = ( ! isLine ? new THREE.MeshPhongMaterial() : new THREE.LineBasicMaterial() );
624 | material.name = sourceMaterial.name;
625 |
626 | }
627 |
628 | material.flatShading = sourceMaterial.smooth ? false : true;
629 |
630 | createdMaterials.push(material);
631 |
632 | }
633 |
634 | // Create mesh
635 |
636 | var mesh;
637 |
638 | if ( createdMaterials.length > 1 ) {
639 |
640 | for ( var mi = 0, miLen = materials.length; mi < miLen ; mi++ ) {
641 |
642 | var sourceMaterial = materials[ mi ];
643 | buffergeometry.addGroup( sourceMaterial.groupStart, sourceMaterial.groupCount, mi );
644 |
645 | }
646 |
647 | mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, createdMaterials ) : new THREE.LineSegments( buffergeometry, createdMaterials ) );
648 |
649 | } else {
650 |
651 | mesh = ( ! isLine ? new THREE.Mesh( buffergeometry, createdMaterials[ 0 ] ) : new THREE.LineSegments( buffergeometry, createdMaterials[ 0 ] ) );
652 | }
653 |
654 | mesh.name = object.name;
655 |
656 | container.add( mesh );
657 |
658 | }
659 |
660 | console.timeEnd( 'OBJLoader' );
661 |
662 | return container;
663 |
664 | }
665 |
666 | };
667 |
668 | return OBJLoader;
669 |
670 | } )();
671 |
--------------------------------------------------------------------------------
/js/OrbitControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | */
7 |
8 | THREE.OrbitControls = function ( object, domElement ) {
9 |
10 | this.object = object;
11 | this.domElement = ( domElement !== undefined ) ? domElement : document;
12 |
13 | // API
14 |
15 | this.enabled = true;
16 |
17 | this.center = new THREE.Vector3();
18 |
19 | this.userZoom = true;
20 | this.userZoomSpeed = 1.0;
21 |
22 | this.userRotate = true;
23 | this.userRotateSpeed = 1.0;
24 |
25 | this.userPan = true;
26 | this.userPanSpeed = 2.0;
27 |
28 | this.autoRotate = false;
29 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
30 |
31 | this.minPolarAngle = 0; // radians
32 | this.maxPolarAngle = Math.PI; // radians
33 |
34 | this.minDistance = 0;
35 | this.maxDistance = Infinity;
36 |
37 | // 65 /*A*/, 83 /*S*/, 68 /*D*/
38 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40, ROTATE: 65, ZOOM: 83, PAN: 68 };
39 |
40 | // internals
41 |
42 | var scope = this;
43 |
44 | var EPS = 0.000001;
45 | var PIXELS_PER_ROUND = 1800;
46 |
47 | var rotateStart = new THREE.Vector2();
48 | var rotateEnd = new THREE.Vector2();
49 | var rotateDelta = new THREE.Vector2();
50 |
51 | var zoomStart = new THREE.Vector2();
52 | var zoomEnd = new THREE.Vector2();
53 | var zoomDelta = new THREE.Vector2();
54 |
55 | var phiDelta = 0;
56 | var thetaDelta = 0;
57 | var scale = 1;
58 |
59 | var lastPosition = new THREE.Vector3();
60 |
61 | var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2 };
62 | var state = STATE.NONE;
63 |
64 | // events
65 |
66 | var changeEvent = { type: 'change' };
67 |
68 |
69 | this.rotateLeft = function ( angle ) {
70 |
71 | if ( angle === undefined ) {
72 |
73 | angle = getAutoRotationAngle();
74 |
75 | }
76 |
77 | thetaDelta -= angle;
78 |
79 | };
80 |
81 | this.rotateRight = function ( angle ) {
82 |
83 | if ( angle === undefined ) {
84 |
85 | angle = getAutoRotationAngle();
86 |
87 | }
88 |
89 | thetaDelta += angle;
90 |
91 | };
92 |
93 | this.rotateUp = function ( angle ) {
94 |
95 | if ( angle === undefined ) {
96 |
97 | angle = getAutoRotationAngle();
98 |
99 | }
100 |
101 | phiDelta -= angle;
102 |
103 | };
104 |
105 | this.rotateDown = function ( angle ) {
106 |
107 | if ( angle === undefined ) {
108 |
109 | angle = getAutoRotationAngle();
110 |
111 | }
112 |
113 | phiDelta += angle;
114 |
115 | };
116 |
117 | this.zoomIn = function ( zoomScale ) {
118 |
119 | if ( zoomScale === undefined ) {
120 |
121 | zoomScale = getZoomScale();
122 |
123 | }
124 |
125 | scale /= zoomScale;
126 |
127 | };
128 |
129 | this.zoomOut = function ( zoomScale ) {
130 |
131 | if ( zoomScale === undefined ) {
132 |
133 | zoomScale = getZoomScale();
134 |
135 | }
136 |
137 | scale *= zoomScale;
138 |
139 | };
140 |
141 | this.pan = function ( distance ) {
142 |
143 | distance.transformDirection( this.object.matrix );
144 | distance.multiplyScalar( scope.userPanSpeed );
145 |
146 | this.object.position.add( distance );
147 | this.center.add( distance );
148 |
149 | };
150 |
151 | this.update = function () {
152 |
153 | var position = this.object.position;
154 | var offset = position.clone().sub( this.center );
155 |
156 | // angle from z-axis around y-axis
157 |
158 | var theta = Math.atan2( offset.x, offset.z );
159 |
160 | // angle from y-axis
161 |
162 | var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
163 |
164 | if ( this.autoRotate ) {
165 |
166 | this.rotateLeft( getAutoRotationAngle() );
167 |
168 | }
169 |
170 | theta += thetaDelta;
171 | phi += phiDelta;
172 |
173 | // restrict phi to be between desired limits
174 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
175 |
176 | // restrict phi to be betwee EPS and PI-EPS
177 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
178 |
179 | var radius = offset.length() * scale;
180 |
181 | // restrict radius to be between desired limits
182 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
183 |
184 | offset.x = radius * Math.sin( phi ) * Math.sin( theta );
185 | offset.y = radius * Math.cos( phi );
186 | offset.z = radius * Math.sin( phi ) * Math.cos( theta );
187 |
188 | position.copy( this.center ).add( offset );
189 |
190 | this.object.lookAt( this.center );
191 |
192 | thetaDelta = 0;
193 | phiDelta = 0;
194 | scale = 1;
195 |
196 | if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
197 |
198 | this.dispatchEvent( changeEvent );
199 |
200 | lastPosition.copy( this.object.position );
201 |
202 | }
203 |
204 | };
205 |
206 |
207 | function getAutoRotationAngle() {
208 |
209 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
210 |
211 | }
212 |
213 | function getZoomScale() {
214 |
215 | return Math.pow( 0.95, scope.userZoomSpeed );
216 |
217 | }
218 |
219 | function onMouseDown( event ) {
220 |
221 | if ( scope.enabled === false ) return;
222 | if ( scope.userRotate === false ) return;
223 |
224 | event.preventDefault();
225 |
226 | if ( state === STATE.NONE )
227 | {
228 | if ( event.button === 0 )
229 | state = STATE.ROTATE;
230 | if ( event.button === 1 )
231 | state = STATE.ZOOM;
232 | if ( event.button === 2 )
233 | state = STATE.PAN;
234 | }
235 |
236 |
237 | if ( state === STATE.ROTATE ) {
238 |
239 | //state = STATE.ROTATE;
240 |
241 | rotateStart.set( event.clientX, event.clientY );
242 |
243 | } else if ( state === STATE.ZOOM ) {
244 |
245 | //state = STATE.ZOOM;
246 |
247 | zoomStart.set( event.clientX, event.clientY );
248 |
249 | } else if ( state === STATE.PAN ) {
250 |
251 | //state = STATE.PAN;
252 |
253 | }
254 |
255 | document.addEventListener( 'mousemove', onMouseMove, false );
256 | document.addEventListener( 'mouseup', onMouseUp, false );
257 |
258 | }
259 |
260 | function onMouseMove( event ) {
261 |
262 | if ( scope.enabled === false ) return;
263 |
264 | event.preventDefault();
265 |
266 |
267 |
268 | if ( state === STATE.ROTATE ) {
269 |
270 | rotateEnd.set( event.clientX, event.clientY );
271 | rotateDelta.subVectors( rotateEnd, rotateStart );
272 |
273 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed );
274 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed );
275 |
276 | rotateStart.copy( rotateEnd );
277 |
278 | } else if ( state === STATE.ZOOM ) {
279 |
280 | zoomEnd.set( event.clientX, event.clientY );
281 | zoomDelta.subVectors( zoomEnd, zoomStart );
282 |
283 | if ( zoomDelta.y > 0 ) {
284 |
285 | scope.zoomIn();
286 |
287 | } else {
288 |
289 | scope.zoomOut();
290 |
291 | }
292 |
293 | zoomStart.copy( zoomEnd );
294 |
295 | } else if ( state === STATE.PAN ) {
296 |
297 | var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0;
298 | var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0;
299 |
300 | scope.pan( new THREE.Vector3( - movementX, movementY, 0 ) );
301 |
302 | }
303 |
304 | }
305 |
306 | function onMouseUp( event ) {
307 |
308 | if ( scope.enabled === false ) return;
309 | if ( scope.userRotate === false ) return;
310 |
311 | document.removeEventListener( 'mousemove', onMouseMove, false );
312 | document.removeEventListener( 'mouseup', onMouseUp, false );
313 |
314 | state = STATE.NONE;
315 |
316 | }
317 |
318 | function onMouseWheel( event ) {
319 |
320 | if ( scope.enabled === false ) return;
321 | if ( scope.userZoom === false ) return;
322 |
323 | var delta = 0;
324 |
325 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
326 |
327 | delta = event.wheelDelta;
328 |
329 | } else if ( event.detail ) { // Firefox
330 |
331 | delta = - event.detail;
332 |
333 | }
334 |
335 | if ( delta > 0 ) {
336 |
337 | scope.zoomOut();
338 |
339 | } else {
340 |
341 | scope.zoomIn();
342 |
343 | }
344 |
345 | }
346 |
347 | function onKeyDown( event ) {
348 |
349 | if ( scope.enabled === false ) return;
350 | if ( scope.userPan === false ) return;
351 |
352 | switch ( event.keyCode ) {
353 |
354 | /*case scope.keys.UP:
355 | scope.pan( new THREE.Vector3( 0, 1, 0 ) );
356 | break;
357 | case scope.keys.BOTTOM:
358 | scope.pan( new THREE.Vector3( 0, - 1, 0 ) );
359 | break;
360 | case scope.keys.LEFT:
361 | scope.pan( new THREE.Vector3( - 1, 0, 0 ) );
362 | break;
363 | case scope.keys.RIGHT:
364 | scope.pan( new THREE.Vector3( 1, 0, 0 ) );
365 | break;
366 | */
367 | case scope.keys.ROTATE:
368 | state = STATE.ROTATE;
369 | break;
370 | case scope.keys.ZOOM:
371 | state = STATE.ZOOM;
372 | break;
373 | case scope.keys.PAN:
374 | state = STATE.PAN;
375 | break;
376 |
377 | }
378 |
379 | }
380 |
381 | function onKeyUp( event ) {
382 |
383 | switch ( event.keyCode ) {
384 |
385 | case scope.keys.ROTATE:
386 | case scope.keys.ZOOM:
387 | case scope.keys.PAN:
388 | state = STATE.NONE;
389 | break;
390 | }
391 |
392 | }
393 |
394 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
395 | this.domElement.addEventListener( 'mousedown', onMouseDown, false );
396 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
397 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
398 | window.addEventListener( 'keydown', onKeyDown, false );
399 | window.addEventListener( 'keyup', onKeyUp, false );
400 |
401 | };
402 |
403 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
404 |
--------------------------------------------------------------------------------
/js/ThreeBSP.js:
--------------------------------------------------------------------------------
1 | // Generated by CoffeeScript 1.6.3
2 | (function() {
3 | var BACK, COPLANAR, EPSILON, FRONT, SPANNING, returning,
4 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; },
5 | __slice = [].slice,
6 | __hasProp = {}.hasOwnProperty,
7 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
8 |
9 | EPSILON = 1e-5;
10 |
11 | COPLANAR = 0;
12 |
13 | FRONT = 1;
14 |
15 | BACK = 2;
16 |
17 | SPANNING = 3;
18 |
19 | returning = function(value, fn) {
20 | fn();
21 | return value;
22 | };
23 |
24 | window.ThreeBSP = (function() {
25 | function ThreeBSP(treeIsh, matrix) {
26 | this.matrix = matrix;
27 | this.intersect = __bind(this.intersect, this);
28 | this.union = __bind(this.union, this);
29 | this.subtract = __bind(this.subtract, this);
30 | this.toGeometry = __bind(this.toGeometry, this);
31 | this.toMesh = __bind(this.toMesh, this);
32 | this.toTree = __bind(this.toTree, this);
33 | if (this.matrix == null) {
34 | this.matrix = new THREE.Matrix4();
35 | }
36 | this.tree = this.toTree(treeIsh);
37 | }
38 |
39 | ThreeBSP.prototype.toTree = function(treeIsh) {
40 | var face, geometry, i, polygons, _fn, _i, _len, _ref,
41 | _this = this;
42 | if (treeIsh instanceof ThreeBSP.Node) {
43 | return treeIsh;
44 | }
45 | polygons = [];
46 | geometry = treeIsh instanceof THREE.Geometry ? treeIsh : treeIsh instanceof THREE.Mesh ? (treeIsh.updateMatrix(), this.matrix = treeIsh.matrix.clone(), treeIsh.geometry) : void 0;
47 | _ref = geometry.faces;
48 | _fn = function(face, i) {
49 | var faceVertexUvs, idx, polygon, vIndex, vName, vertex, _j, _len1, _ref1, _ref2;
50 | faceVertexUvs = (_ref1 = geometry.faceVertexUvs) != null ? _ref1[0][i] : void 0;
51 | if (faceVertexUvs == null) {
52 | faceVertexUvs = [new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2(), new THREE.Vector2()];
53 | }
54 | polygon = new ThreeBSP.Polygon();
55 | _ref2 = ['a', 'b', 'c', 'd'];
56 | for (vIndex = _j = 0, _len1 = _ref2.length; _j < _len1; vIndex = ++_j) {
57 | vName = _ref2[vIndex];
58 | if ((idx = face[vName]) != null) {
59 | vertex = geometry.vertices[idx];
60 | vertex = new ThreeBSP.Vertex(vertex.x, vertex.y, vertex.z, face.vertexNormals[0], new THREE.Vector2(faceVertexUvs[vIndex].x, faceVertexUvs[vIndex].y));
61 | vertex.applyMatrix4(_this.matrix);
62 | polygon.vertices.push(vertex);
63 | }
64 | }
65 | return polygons.push(polygon.calculateProperties());
66 | };
67 | for (i = _i = 0, _len = _ref.length; _i < _len; i = ++_i) {
68 | face = _ref[i];
69 | _fn(face, i);
70 | }
71 | return new ThreeBSP.Node(polygons);
72 | };
73 |
74 | ThreeBSP.prototype.toMesh = function(material) {
75 | var geometry, mesh,
76 | _this = this;
77 | if (material == null) {
78 | material = new THREE.MeshNormalMaterial();
79 | }
80 | geometry = this.toGeometry();
81 | return returning((mesh = new THREE.Mesh(geometry, material)), function() {
82 | mesh.position.setFromMatrixPosition(_this.matrix);
83 | return mesh.rotation.setFromRotationMatrix(_this.matrix);
84 | });
85 | };
86 |
87 | ThreeBSP.prototype.toGeometry = function() {
88 | var geometry, matrix,
89 | _this = this;
90 | matrix = new THREE.Matrix4().getInverse(this.matrix);
91 | return returning((geometry = new THREE.Geometry()), function() {
92 | var face, idx, polyVerts, polygon, v, vertUvs, verts, _i, _len, _ref, _results;
93 | _ref = _this.tree.allPolygons();
94 | _results = [];
95 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
96 | polygon = _ref[_i];
97 | polyVerts = (function() {
98 | var _j, _len1, _ref1, _results1;
99 | _ref1 = polygon.vertices;
100 | _results1 = [];
101 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
102 | v = _ref1[_j];
103 | _results1.push(v.clone().applyMatrix4(matrix));
104 | }
105 | return _results1;
106 | })();
107 | _results.push((function() {
108 | var _j, _ref1, _results1;
109 | _results1 = [];
110 | for (idx = _j = 2, _ref1 = polyVerts.length; 2 <= _ref1 ? _j < _ref1 : _j > _ref1; idx = 2 <= _ref1 ? ++_j : --_j) {
111 | verts = [polyVerts[0], polyVerts[idx - 1], polyVerts[idx]];
112 | vertUvs = (function() {
113 | var _k, _len1, _ref2, _ref3, _results2;
114 | _results2 = [];
115 | for (_k = 0, _len1 = verts.length; _k < _len1; _k++) {
116 | v = verts[_k];
117 | _results2.push(new THREE.Vector2((_ref2 = v.uv) != null ? _ref2.x : void 0, (_ref3 = v.uv) != null ? _ref3.y : void 0));
118 | }
119 | return _results2;
120 | })();
121 | face = (function(func, args, ctor) {
122 | ctor.prototype = func.prototype;
123 | var child = new ctor, result = func.apply(child, args);
124 | return Object(result) === result ? result : child;
125 | })(THREE.Face3, __slice.call((function() {
126 | var _k, _len1, _results2;
127 | _results2 = [];
128 | for (_k = 0, _len1 = verts.length; _k < _len1; _k++) {
129 | v = verts[_k];
130 | _results2.push(geometry.vertices.push(v) - 1);
131 | }
132 | return _results2;
133 | })()).concat([polygon.normal.clone()]), function(){});
134 | geometry.faces.push(face);
135 | _results1.push(geometry.faceVertexUvs[0].push(vertUvs));
136 | }
137 | return _results1;
138 | })());
139 | }
140 | return _results;
141 | });
142 | };
143 |
144 | ThreeBSP.prototype.subtract = function(other) {
145 | var them, us, _ref;
146 | _ref = [this.tree.clone(), other.tree.clone()], us = _ref[0], them = _ref[1];
147 | us.invert().clipTo(them);
148 | them.clipTo(us).invert().clipTo(us).invert();
149 | return new ThreeBSP(us.build(them.allPolygons()).invert(), this.matrix);
150 | };
151 |
152 | ThreeBSP.prototype.union = function(other) {
153 | var them, us, _ref;
154 | _ref = [this.tree.clone(), other.tree.clone()], us = _ref[0], them = _ref[1];
155 | us.clipTo(them);
156 | them.clipTo(us).invert().clipTo(us).invert();
157 | return new ThreeBSP(us.build(them.allPolygons()), this.matrix);
158 | };
159 |
160 | ThreeBSP.prototype.intersect = function(other) {
161 | var them, us, _ref;
162 | _ref = [this.tree.clone(), other.tree.clone()], us = _ref[0], them = _ref[1];
163 | them.clipTo(us.invert()).invert().clipTo(us.clipTo(them));
164 | return new ThreeBSP(us.build(them.allPolygons()).invert(), this.matrix);
165 | };
166 |
167 | return ThreeBSP;
168 |
169 | })();
170 |
171 | ThreeBSP.Vertex = (function(_super) {
172 | __extends(Vertex, _super);
173 |
174 | function Vertex(x, y, z, normal, uv) {
175 | this.normal = normal != null ? normal : new THREE.Vector3();
176 | this.uv = uv != null ? uv : new THREE.Vector2();
177 | this.interpolate = __bind(this.interpolate, this);
178 | this.lerp = __bind(this.lerp, this);
179 | Vertex.__super__.constructor.call(this, x, y, z);
180 | }
181 |
182 | Vertex.prototype.clone = function() {
183 | return new ThreeBSP.Vertex(this.x, this.y, this.z, this.normal.clone(), this.uv.clone());
184 | };
185 |
186 | Vertex.prototype.lerp = function(v, alpha) {
187 | var _this = this;
188 | return returning(Vertex.__super__.lerp.apply(this, arguments), function() {
189 | _this.uv.add(v.uv.clone().sub(_this.uv).multiplyScalar(alpha));
190 | return _this.normal.lerp(v, alpha);
191 | });
192 | };
193 |
194 | Vertex.prototype.interpolate = function() {
195 | var args, _ref;
196 | args = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
197 | return (_ref = this.clone()).lerp.apply(_ref, args);
198 | };
199 |
200 | return Vertex;
201 |
202 | })(THREE.Vector3);
203 |
204 | ThreeBSP.Polygon = (function() {
205 | function Polygon(vertices, normal, w) {
206 | this.vertices = vertices != null ? vertices : [];
207 | this.normal = normal;
208 | this.w = w;
209 | this.subdivide = __bind(this.subdivide, this);
210 | this.tessellate = __bind(this.tessellate, this);
211 | this.classifySide = __bind(this.classifySide, this);
212 | this.classifyVertex = __bind(this.classifyVertex, this);
213 | this.invert = __bind(this.invert, this);
214 | this.clone = __bind(this.clone, this);
215 | this.calculateProperties = __bind(this.calculateProperties, this);
216 | if (this.vertices.length) {
217 | this.calculateProperties();
218 | }
219 | }
220 |
221 | Polygon.prototype.calculateProperties = function() {
222 | var _this = this;
223 | return returning(this, function() {
224 | var a, b, c, _ref;
225 | _ref = _this.vertices, a = _ref[0], b = _ref[1], c = _ref[2];
226 | _this.normal = b.clone().sub(a).cross(c.clone().sub(a)).normalize();
227 | return _this.w = _this.normal.clone().dot(a);
228 | });
229 | };
230 |
231 | Polygon.prototype.clone = function() {
232 | var v;
233 | return new ThreeBSP.Polygon((function() {
234 | var _i, _len, _ref, _results;
235 | _ref = this.vertices;
236 | _results = [];
237 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
238 | v = _ref[_i];
239 | _results.push(v.clone());
240 | }
241 | return _results;
242 | }).call(this), this.normal.clone(), this.w);
243 | };
244 |
245 | Polygon.prototype.invert = function() {
246 | var _this = this;
247 | return returning(this, function() {
248 | _this.normal.multiplyScalar(-1);
249 | _this.w *= -1;
250 | return _this.vertices.reverse();
251 | });
252 | };
253 |
254 | Polygon.prototype.classifyVertex = function(vertex) {
255 | var side;
256 | side = this.normal.dot(vertex) - this.w;
257 | switch (false) {
258 | case !(side < -EPSILON):
259 | return BACK;
260 | case !(side > EPSILON):
261 | return FRONT;
262 | default:
263 | return COPLANAR;
264 | }
265 | };
266 |
267 | Polygon.prototype.classifySide = function(polygon) {
268 | var back, front, tally, v, _i, _len, _ref, _ref1,
269 | _this = this;
270 | _ref = [0, 0], front = _ref[0], back = _ref[1];
271 | tally = function(v) {
272 | switch (_this.classifyVertex(v)) {
273 | case FRONT:
274 | return front += 1;
275 | case BACK:
276 | return back += 1;
277 | }
278 | };
279 | _ref1 = polygon.vertices;
280 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
281 | v = _ref1[_i];
282 | tally(v);
283 | }
284 | if (front > 0 && back === 0) {
285 | return FRONT;
286 | }
287 | if (front === 0 && back > 0) {
288 | return BACK;
289 | }
290 | if ((front === back && back === 0)) {
291 | return COPLANAR;
292 | }
293 | return SPANNING;
294 | };
295 |
296 | Polygon.prototype.tessellate = function(poly) {
297 | var b, count, f, i, j, polys, t, ti, tj, v, vi, vj, _i, _len, _ref, _ref1, _ref2,
298 | _this = this;
299 | _ref = {
300 | f: [],
301 | b: [],
302 | count: poly.vertices.length
303 | }, f = _ref.f, b = _ref.b, count = _ref.count;
304 | if (this.classifySide(poly) !== SPANNING) {
305 | return [poly];
306 | }
307 | _ref1 = poly.vertices;
308 | for (i = _i = 0, _len = _ref1.length; _i < _len; i = ++_i) {
309 | vi = _ref1[i];
310 | vj = poly.vertices[(j = (i + 1) % count)];
311 | _ref2 = (function() {
312 | var _j, _len1, _ref2, _results;
313 | _ref2 = [vi, vj];
314 | _results = [];
315 | for (_j = 0, _len1 = _ref2.length; _j < _len1; _j++) {
316 | v = _ref2[_j];
317 | _results.push(this.classifyVertex(v));
318 | }
319 | return _results;
320 | }).call(this), ti = _ref2[0], tj = _ref2[1];
321 | if (ti !== BACK) {
322 | f.push(vi);
323 | }
324 | if (ti !== FRONT) {
325 | b.push(vi);
326 | }
327 | if ((ti | tj) === SPANNING) {
328 | t = (this.w - this.normal.dot(vi)) / this.normal.dot(vj.clone().sub(vi));
329 | v = vi.interpolate(vj, t);
330 | f.push(v);
331 | b.push(v);
332 | }
333 | }
334 | return returning((polys = []), function() {
335 | if (f.length >= 3) {
336 | polys.push(new ThreeBSP.Polygon(f));
337 | }
338 | if (b.length >= 3) {
339 | return polys.push(new ThreeBSP.Polygon(b));
340 | }
341 | });
342 | };
343 |
344 | Polygon.prototype.subdivide = function(polygon, coplanar_front, coplanar_back, front, back) {
345 | var poly, side, _i, _len, _ref, _results;
346 | _ref = this.tessellate(polygon);
347 | _results = [];
348 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
349 | poly = _ref[_i];
350 | side = this.classifySide(poly);
351 | switch (side) {
352 | case FRONT:
353 | _results.push(front.push(poly));
354 | break;
355 | case BACK:
356 | _results.push(back.push(poly));
357 | break;
358 | case COPLANAR:
359 | if (this.normal.dot(poly.normal) > 0) {
360 | _results.push(coplanar_front.push(poly));
361 | } else {
362 | _results.push(coplanar_back.push(poly));
363 | }
364 | break;
365 | default:
366 | throw new Error("BUG: Polygon of classification " + side + " in subdivision");
367 | }
368 | }
369 | return _results;
370 | };
371 |
372 | return Polygon;
373 |
374 | })();
375 |
376 | ThreeBSP.Node = (function() {
377 | Node.prototype.clone = function() {
378 | var node,
379 | _this = this;
380 | return returning((node = new ThreeBSP.Node()), function() {
381 | var p, _ref, _ref1, _ref2;
382 | node.divider = (_ref = _this.divider) != null ? _ref.clone() : void 0;
383 | node.polygons = (function() {
384 | var _i, _len, _ref1, _results;
385 | _ref1 = this.polygons;
386 | _results = [];
387 | for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
388 | p = _ref1[_i];
389 | _results.push(p.clone());
390 | }
391 | return _results;
392 | }).call(_this);
393 | node.front = (_ref1 = _this.front) != null ? _ref1.clone() : void 0;
394 | return node.back = (_ref2 = _this.back) != null ? _ref2.clone() : void 0;
395 | });
396 | };
397 |
398 | function Node(polygons) {
399 | this.clipTo = __bind(this.clipTo, this);
400 | this.clipPolygons = __bind(this.clipPolygons, this);
401 | this.invert = __bind(this.invert, this);
402 | this.allPolygons = __bind(this.allPolygons, this);
403 | this.isConvex = __bind(this.isConvex, this);
404 | this.build = __bind(this.build, this);
405 | this.clone = __bind(this.clone, this);
406 | this.polygons = [];
407 | if ((polygons != null) && polygons.length) {
408 | this.build(polygons);
409 | }
410 | }
411 |
412 | Node.prototype.build = function(polygons) {
413 | var _this = this;
414 | return returning(this, function() {
415 | var poly, polys, side, sides, _i, _len, _results;
416 | sides = {
417 | front: [],
418 | back: []
419 | };
420 | if (_this.divider == null) {
421 | _this.divider = polygons[0].clone();
422 | }
423 | for (_i = 0, _len = polygons.length; _i < _len; _i++) {
424 | poly = polygons[_i];
425 | _this.divider.subdivide(poly, _this.polygons, _this.polygons, sides.front, sides.back);
426 | }
427 | _results = [];
428 | for (side in sides) {
429 | if (!__hasProp.call(sides, side)) continue;
430 | polys = sides[side];
431 | if (polys.length) {
432 | if (_this[side] == null) {
433 | _this[side] = new ThreeBSP.Node();
434 | }
435 | _results.push(_this[side].build(polys));
436 | } else {
437 | _results.push(void 0);
438 | }
439 | }
440 | return _results;
441 | });
442 | };
443 |
444 | Node.prototype.isConvex = function(polys) {
445 | var inner, outer, _i, _j, _len, _len1;
446 | for (_i = 0, _len = polys.length; _i < _len; _i++) {
447 | inner = polys[_i];
448 | for (_j = 0, _len1 = polys.length; _j < _len1; _j++) {
449 | outer = polys[_j];
450 | if (inner !== outer && outer.classifySide(inner) !== BACK) {
451 | return false;
452 | }
453 | }
454 | }
455 | return true;
456 | };
457 |
458 | Node.prototype.allPolygons = function() {
459 | var _ref, _ref1;
460 | return this.polygons.slice().concat(((_ref1 = this.front) != null ? _ref1.allPolygons() : void 0) || []).concat(((_ref = this.back) != null ? _ref.allPolygons() : void 0) || []);
461 | };
462 |
463 | Node.prototype.invert = function() {
464 | var _this = this;
465 | return returning(this, function() {
466 | var flipper, poly, _i, _j, _len, _len1, _ref, _ref1, _ref2;
467 | _ref = _this.polygons;
468 | for (_i = 0, _len = _ref.length; _i < _len; _i++) {
469 | poly = _ref[_i];
470 | poly.invert();
471 | }
472 | _ref1 = [_this.divider, _this.front, _this.back];
473 | for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
474 | flipper = _ref1[_j];
475 | if (flipper != null) {
476 | flipper.invert();
477 | }
478 | }
479 | return _ref2 = [_this.back, _this.front], _this.front = _ref2[0], _this.back = _ref2[1], _ref2;
480 | });
481 | };
482 |
483 | Node.prototype.clipPolygons = function(polygons) {
484 | var back, front, poly, _i, _len;
485 | if (!this.divider) {
486 | return polygons.slice();
487 | }
488 | front = [];
489 | back = [];
490 | for (_i = 0, _len = polygons.length; _i < _len; _i++) {
491 | poly = polygons[_i];
492 | this.divider.subdivide(poly, front, back, front, back);
493 | }
494 | if (this.front) {
495 | front = this.front.clipPolygons(front);
496 | }
497 | if (this.back) {
498 | back = this.back.clipPolygons(back);
499 | }
500 | return front.concat(this.back ? back : []);
501 | };
502 |
503 | Node.prototype.clipTo = function(node) {
504 | var _this = this;
505 | return returning(this, function() {
506 | var _ref, _ref1;
507 | _this.polygons = node.clipPolygons(_this.polygons);
508 | if ((_ref = _this.front) != null) {
509 | _ref.clipTo(node);
510 | }
511 | return (_ref1 = _this.back) != null ? _ref1.clipTo(node) : void 0;
512 | });
513 | };
514 |
515 | return Node;
516 |
517 | })();
518 |
519 | }).call(this);
520 |
--------------------------------------------------------------------------------
/js/onEvent.js:
--------------------------------------------------------------------------------
1 | (function (definition) {
2 | "use strict";
3 | if (!THREE) {
4 | throw new Error("This module is dependent from 'three.js,add this file first.");
5 | }
6 | // CommonJS
7 | if (typeof exports === "object" && typeof module === "object") {
8 | module.exports = definition(THREE);
9 |
10 | // RequireJS
11 | } else if (typeof define === "function" && define.amd) {
12 | define(definition);
13 |
14 | //