├── 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 | //