├── public ├── js │ ├── textures │ │ ├── .gitkeep │ │ └── minecraft │ │ │ ├── painterlypack.txt │ │ │ ├── atlas.png │ │ │ ├── dirt.png │ │ │ ├── grass.png │ │ │ └── grass_dirt.png │ ├── loaders │ │ ├── ctm │ │ │ ├── CTMWorker.js │ │ │ ├── license │ │ │ │ ├── OpenCTM.txt │ │ │ │ ├── js-lzma.txt │ │ │ │ └── js-openctm.txt │ │ │ └── CTMLoader.js │ │ ├── SVGLoader.js │ │ ├── VTKLoader.js │ │ ├── BabylonLoader.js │ │ ├── collada │ │ │ ├── AnimationHandler.js │ │ │ └── KeyFrameAnimation.js │ │ ├── PDBLoader.js │ │ ├── gltf │ │ │ ├── glTFAnimation.js │ │ │ └── glTFLoaderUtils.js │ │ ├── OBJLoader2.js │ │ ├── OBJLoader.js │ │ ├── DDSLoader.js │ │ ├── AssimpJSONLoader.js │ │ ├── OBJMTLLoader.js │ │ └── MTLLoader.js │ ├── libs │ │ ├── system.min.js │ │ ├── stats.min.js │ │ └── tween.min.js │ ├── effects │ │ ├── CrosseyedEffect.js │ │ ├── StereoEffect.js │ │ ├── ParallaxBarrierEffect.js │ │ ├── AnaglyphEffect.js │ │ ├── AsciiEffect.js │ │ ├── VREffect.js │ │ └── OculusRiftEffect.js │ ├── controls │ │ ├── OculusControls.js │ │ ├── PointerLockControls.js │ │ ├── DK2Controls.js │ │ ├── EditorControls.js │ │ ├── FirstPersonControls.js │ │ ├── FlyControls.js │ │ ├── OrbitControls.js │ │ └── PathControls.js │ ├── Detector.js │ ├── ImprovedNoise.js │ └── app.js ├── models │ ├── readme.txt │ └── monster.jpg ├── index.css └── views │ └── demo.jade ├── .gitignore ├── docs ├── demo.jpg ├── mpu6050.jpg └── oculus-vr.jpg ├── bower.json ├── package.json └── index.js /public/js/textures/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/models/readme.txt: -------------------------------------------------------------------------------- 1 | Model from http://www.3drt.com/downloads.htm 2 | -------------------------------------------------------------------------------- /public/js/textures/minecraft/painterlypack.txt: -------------------------------------------------------------------------------- 1 | http://painterlypack.net/ 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | src/Debug 2 | src/build 3 | build/* 4 | *.user 5 | .idea 6 | bower_components -------------------------------------------------------------------------------- /docs/demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/oculus-nodejs-threejs-example/HEAD/docs/demo.jpg -------------------------------------------------------------------------------- /docs/mpu6050.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/oculus-nodejs-threejs-example/HEAD/docs/mpu6050.jpg -------------------------------------------------------------------------------- /docs/oculus-vr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/oculus-nodejs-threejs-example/HEAD/docs/oculus-vr.jpg -------------------------------------------------------------------------------- /public/models/monster.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/oculus-nodejs-threejs-example/HEAD/public/models/monster.jpg -------------------------------------------------------------------------------- /public/js/textures/minecraft/atlas.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/oculus-nodejs-threejs-example/HEAD/public/js/textures/minecraft/atlas.png -------------------------------------------------------------------------------- /public/js/textures/minecraft/dirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/oculus-nodejs-threejs-example/HEAD/public/js/textures/minecraft/dirt.png -------------------------------------------------------------------------------- /public/js/textures/minecraft/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/oculus-nodejs-threejs-example/HEAD/public/js/textures/minecraft/grass.png -------------------------------------------------------------------------------- /public/js/textures/minecraft/grass_dirt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phodal/oculus-nodejs-threejs-example/HEAD/public/js/textures/minecraft/grass_dirt.png -------------------------------------------------------------------------------- /public/index.css: -------------------------------------------------------------------------------- 1 | 2 | body { 3 | color: rgb(168, 148, 0); 4 | font-family: Monospace; 5 | font-size: 13px; 6 | text-align: center; 7 | background-color: #000000; 8 | margin: 0px; 9 | overflow: hidden; 10 | } -------------------------------------------------------------------------------- /public/js/loaders/ctm/CTMWorker.js: -------------------------------------------------------------------------------- 1 | importScripts( "lzma.js", "ctm.js" ); 2 | 3 | self.onmessage = function( event ) { 4 | 5 | var files = []; 6 | 7 | for ( var i = 0; i < event.data.offsets.length; i ++ ) { 8 | 9 | var stream = new CTM.Stream( event.data.data ); 10 | stream.offset = event.data.offsets[ i ]; 11 | 12 | files[ i ] = new CTM.File( stream ); 13 | 14 | } 15 | 16 | self.postMessage( files ); 17 | self.close(); 18 | 19 | } 20 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vr", 3 | "homepage": "https://github.com/phodal/vr", 4 | "authors": [ 5 | "Fengda HUANG " 6 | ], 7 | "description": "", 8 | "main": "", 9 | "moduleType": [], 10 | "license": "MIT", 11 | "ignore": [ 12 | "**/.*", 13 | "node_modules", 14 | "bower_components", 15 | "test", 16 | "tests" 17 | ], 18 | "dependencies": { 19 | "three.js": "~0.73.0", 20 | "jquery": "~2.1.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vr", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/phodal/vr.git" 12 | }, 13 | "author": "", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/phodal/vr/issues" 17 | }, 18 | "homepage": "https://github.com/phodal/vr#readme", 19 | "dependencies": { 20 | "express": "^4.13.3", 21 | "jade": "^1.11.0", 22 | "ws": "^0.8.0" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /public/js/loaders/SVGLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | * @author zz85 / http://joshuakoo.com/ 4 | */ 5 | 6 | THREE.SVGLoader = function ( manager ) { 7 | 8 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 9 | 10 | }; 11 | 12 | THREE.SVGLoader.prototype = { 13 | 14 | constructor: THREE.MaterialLoader, 15 | 16 | load: function ( url, onLoad, onProgress, onError ) { 17 | 18 | var parser = new DOMParser(); 19 | 20 | var loader = new THREE.XHRLoader(); 21 | loader.setCrossOrigin( this.crossOrigin ); 22 | loader.load( url, function ( svgString ) { 23 | 24 | var doc = parser.parseFromString( svgString, 'image/svg+xml' ); // application/xml 25 | 26 | onLoad( doc.firstChild ); 27 | 28 | } ); 29 | 30 | } 31 | }; -------------------------------------------------------------------------------- /public/js/loaders/ctm/license/OpenCTM.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009-2010 Marcus Geelnard 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 16 | 2. Altered source versions must be plainly marked as such, and must not 17 | be misrepresented as being the original software. 18 | 19 | 3. This notice may not be removed or altered from any source 20 | distribution. 21 | -------------------------------------------------------------------------------- /public/views/demo.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | 6 | link(href='index.css', rel='stylesheet') 7 | script(src='./js/libs/jquery.min.js') 8 | script(src='./js/libs/three.min.js') 9 | 10 | script(src='./js/ImprovedNoise.js') 11 | script(src='./js/Detector.js') 12 | 13 | script(src='./js/effects/OculusRiftEffect.js') 14 | script(src='./js/effects/AsciiEffect.js') 15 | script(src='./js/effects/AnaglyphEffect.js') 16 | 17 | script(src='./js/controls/FirstPersonControls.js') 18 | script(src='./js/controls/DK2Controls.js') 19 | 20 | script(src='./js/loaders/collada/Animation.js') 21 | script(src='./js/loaders/collada/AnimationHandler.js') 22 | script(src='./js/loaders/collada/KeyFrameAnimation.js') 23 | 24 | script(src='./js/loaders/ColladaLoader.js') 25 | 26 | script(src='./js/libs/stats.min.js') 27 | body 28 | div(id="container") 29 | 30 | script(src='./js/app.js') 31 | -------------------------------------------------------------------------------- /public/js/loaders/ctm/license/js-lzma.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Juan Mellado 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /public/js/loaders/ctm/license/js-openctm.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011 Juan Mellado 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /public/js/libs/system.min.js: -------------------------------------------------------------------------------- 1 | // system.js - http://github.com/mrdoob/system.js 2 | 'use strict';var System={browser:function(){var a=navigator.userAgent;return/Arora/i.test(a)?"Arora":/Chrome/i.test(a)?"Chrome":/Epiphany/i.test(a)?"Epiphany":/Firefox/i.test(a)?"Firefox":/Mobile(\/.*)? Safari/i.test(a)?"Mobile Safari":/MSIE/i.test(a)?"Internet Explorer":/Midori/i.test(a)?"Midori":/Opera/.test(a)?"Opera":/Safari/i.test(a)?"Safari":!1}(),os:function(){var a=navigator.userAgent;return/Android/i.test(a)?"Android":/CrOS/i.test(a)?"Chrome OS":/iP[ao]d|iPhone/i.test(a)?"iOS":/Linux/i.test(a)? 3 | "Linux":/Mac OS/i.test(a)?"Mac OS":/windows/i.test(a)?"Windows":!1}(),support:{canvas:!!window.CanvasRenderingContext2D,localStorage:function(){try{return!!window.localStorage.getItem}catch(a){return!1}}(),file:!!window.File&&!!window.FileReader&&!!window.FileList&&!!window.Blob,fileSystem:!!window.requestFileSystem||!!window.webkitRequestFileSystem,getUserMedia:!!window.navigator.getUserMedia||!!window.navigator.webkitGetUserMedia||!!window.navigator.mozGetUserMedia||!!window.navigator.msGetUserMedia, 4 | requestAnimationFrame:!!window.mozRequestAnimationFrame||!!window.webkitRequestAnimationFrame||!!window.oRequestAnimationFrame||!!window.msRequestAnimationFrame,sessionStorage:function(){try{return!!window.sessionStorage.getItem}catch(a){return!1}}(),webgl:function(){try{return!!window.WebGLRenderingContext&&!!document.createElement("canvas").getContext("experimental-webgl")}catch(a){return!1}}(),worker:!!window.Worker}}; 5 | -------------------------------------------------------------------------------- /public/js/effects/CrosseyedEffect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | */ 4 | 5 | THREE.CrosseyedEffect = function ( renderer ) { 6 | 7 | // API 8 | 9 | this.separation = 10; 10 | 11 | // internals 12 | 13 | var _width, _height; 14 | 15 | var _cameraL = new THREE.PerspectiveCamera(); 16 | _cameraL.target = new THREE.Vector3(); 17 | 18 | var _cameraR = new THREE.PerspectiveCamera(); 19 | _cameraR.target = new THREE.Vector3(); 20 | 21 | // initialization 22 | 23 | renderer.autoClear = false; 24 | 25 | this.setSize = function ( width, height ) { 26 | 27 | _width = width / 2; 28 | _height = height; 29 | 30 | renderer.setSize( width, height ); 31 | 32 | }; 33 | 34 | this.render = function ( scene, camera ) { 35 | 36 | // left 37 | 38 | _cameraL.fov = camera.fov; 39 | _cameraL.aspect = 0.5 * camera.aspect; 40 | _cameraL.near = camera.near; 41 | _cameraL.far = camera.far; 42 | _cameraL.updateProjectionMatrix(); 43 | 44 | _cameraL.position.copy( camera.position ); 45 | _cameraL.target.copy( camera.target ); 46 | _cameraL.translateX( this.separation ); 47 | _cameraL.lookAt( _cameraL.target ); 48 | 49 | // right 50 | 51 | _cameraR.near = camera.near; 52 | _cameraR.far = camera.far; 53 | 54 | _cameraR.projectionMatrix = _cameraL.projectionMatrix; 55 | 56 | _cameraR.position.copy( camera.position ); 57 | _cameraR.target.copy( camera.target ); 58 | _cameraR.translateX( - this.separation ); 59 | _cameraR.lookAt( _cameraR.target ); 60 | 61 | // 62 | 63 | renderer.clear(); 64 | 65 | renderer.setViewport( 0, 0, _width, _height ); 66 | renderer.render( scene, _cameraL ); 67 | 68 | renderer.setViewport( _width, 0, _width, _height ); 69 | renderer.render( scene, _cameraR, false ); 70 | 71 | }; 72 | 73 | }; 74 | -------------------------------------------------------------------------------- /public/js/controls/OculusControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author possan / http://possan.se/ 3 | * 4 | * Oculus headtracking control 5 | * - use together with the oculus-rest project to get headtracking 6 | * coordinates from the rift: http://github.com/possan/oculus-rest 7 | */ 8 | 9 | THREE.OculusControls = function ( object ) { 10 | 11 | this.object = object; 12 | this.target = new THREE.Vector3( 0, 0, 0 ); 13 | 14 | this.headquat = new THREE.Quaternion(); 15 | this.freeze = false; 16 | 17 | this.loadAjaxJSON = function ( url, callback ) { 18 | var xhr = new XMLHttpRequest(); 19 | xhr.onreadystatechange = function () { 20 | if ( xhr.readyState === xhr.DONE ) { 21 | if ( xhr.status === 200 || xhr.status === 0 ) { 22 | if ( xhr.responseText ) { 23 | var json = JSON.parse( xhr.responseText ); 24 | callback( json ); 25 | } 26 | } 27 | } 28 | }; 29 | xhr.open( "GET", url, true ); 30 | xhr.withCredentials = false; 31 | xhr.send( null ); 32 | }; 33 | 34 | this.gotCoordinates = function( r ) { 35 | this.headquat.set(r.quat.x, r.quat.y, r.quat.z, r.quat.w); 36 | this.queuePoll(); 37 | }; 38 | 39 | this.pollOnce = function() { 40 | this.loadAjaxJSON('http://localhost:3000', bind(this, this.gotCoordinates)); 41 | }; 42 | 43 | this.queuePoll = function() { 44 | setTimeout(bind(this, this.pollOnce), 10); 45 | }; 46 | 47 | this.update = function( delta ) { 48 | if ( this.freeze ) { 49 | return; 50 | } 51 | 52 | this.object.quaternion.multiply(this.headquat); 53 | }; 54 | 55 | function bind( scope, fn ) { 56 | return function () { 57 | fn.apply( scope, arguments ); 58 | }; 59 | } 60 | 61 | this.connect = function() { 62 | this.queuePoll(); 63 | }; 64 | }; 65 | -------------------------------------------------------------------------------- /public/js/effects/StereoEffect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | * @authod mrdoob / http://mrdoob.com/ 4 | * @authod arodic / http://aleksandarrodic.com/ 5 | */ 6 | 7 | THREE.StereoEffect = function ( renderer ) { 8 | 9 | // API 10 | 11 | this.separation = 3; 12 | 13 | // internals 14 | 15 | var _width, _height; 16 | 17 | var _position = new THREE.Vector3(); 18 | var _quaternion = new THREE.Quaternion(); 19 | var _scale = new THREE.Vector3(); 20 | 21 | var _cameraL = new THREE.PerspectiveCamera(); 22 | var _cameraR = new THREE.PerspectiveCamera(); 23 | 24 | // initialization 25 | 26 | renderer.autoClear = false; 27 | 28 | this.setSize = function ( width, height ) { 29 | 30 | _width = width / 2; 31 | _height = height; 32 | 33 | renderer.setSize( width, height ); 34 | 35 | }; 36 | 37 | this.render = function ( scene, camera ) { 38 | 39 | scene.updateMatrixWorld(); 40 | 41 | if ( camera.parent === undefined ) camera.updateMatrixWorld(); 42 | 43 | camera.matrixWorld.decompose( _position, _quaternion, _scale ); 44 | 45 | // left 46 | 47 | _cameraL.fov = camera.fov; 48 | _cameraL.aspect = 0.5 * camera.aspect; 49 | _cameraL.near = camera.near; 50 | _cameraL.far = camera.far; 51 | _cameraL.updateProjectionMatrix(); 52 | 53 | _cameraL.position.copy( _position ); 54 | _cameraL.quaternion.copy( _quaternion ); 55 | _cameraL.translateX( - this.separation ); 56 | 57 | // right 58 | 59 | _cameraR.near = camera.near; 60 | _cameraR.far = camera.far; 61 | _cameraR.projectionMatrix = _cameraL.projectionMatrix; 62 | 63 | _cameraR.position.copy( _position ); 64 | _cameraR.quaternion.copy( _quaternion ); 65 | _cameraR.translateX( this.separation ); 66 | 67 | // 68 | 69 | renderer.setViewport( 0, 0, _width * 2, _height ); 70 | renderer.clear(); 71 | 72 | renderer.setViewport( 0, 0, _width, _height ); 73 | renderer.render( scene, _cameraL ); 74 | 75 | renderer.setViewport( _width, 0, _width, _height ); 76 | renderer.render( scene, _cameraR ); 77 | 78 | }; 79 | 80 | }; 81 | -------------------------------------------------------------------------------- /public/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 () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )(), 10 | workers: !! window.Worker, 11 | fileapi: window.File && window.FileReader && window.FileList && window.Blob, 12 | 13 | getWebGLErrorMessage: function () { 14 | 15 | var element = document.createElement( 'div' ); 16 | element.id = 'webgl-error-message'; 17 | element.style.fontFamily = 'monospace'; 18 | element.style.fontSize = '13px'; 19 | element.style.fontWeight = 'normal'; 20 | element.style.textAlign = 'center'; 21 | element.style.background = '#fff'; 22 | element.style.color = '#000'; 23 | element.style.padding = '1.5em'; 24 | element.style.width = '400px'; 25 | element.style.margin = '5em auto 0'; 26 | 27 | if ( ! this.webgl ) { 28 | 29 | element.innerHTML = window.WebGLRenderingContext ? [ 30 | 'Your graphics card does not seem to support WebGL.
', 31 | 'Find out how to get it here.' 32 | ].join( '\n' ) : [ 33 | 'Your browser does not seem to support WebGL.
', 34 | 'Find out how to get it here.' 35 | ].join( '\n' ); 36 | 37 | } 38 | 39 | return element; 40 | 41 | }, 42 | 43 | addGetWebGLMessage: function ( parameters ) { 44 | 45 | var parent, id, element; 46 | 47 | parameters = parameters || {}; 48 | 49 | parent = parameters.parent !== undefined ? parameters.parent : document.body; 50 | id = parameters.id !== undefined ? parameters.id : 'oldie'; 51 | 52 | element = Detector.getWebGLErrorMessage(); 53 | element.id = id; 54 | 55 | parent.appendChild( element ); 56 | 57 | } 58 | 59 | }; 60 | -------------------------------------------------------------------------------- /public/js/libs/stats.min.js: -------------------------------------------------------------------------------- 1 | // stats.js - http://github.com/mrdoob/stats.js 2 | var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; 3 | i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div"); 4 | k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display= 5 | "block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:11,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height= 6 | a+"px",m=b,r=0);return b},update:function(){l=this.end()}}}; 7 | -------------------------------------------------------------------------------- /public/js/ImprovedNoise.js: -------------------------------------------------------------------------------- 1 | // http://mrl.nyu.edu/~perlin/noise/ 2 | 3 | var ImprovedNoise = function () { 4 | 5 | var p = [151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10, 6 | 23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87, 7 | 174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211, 8 | 133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208, 9 | 89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5, 10 | 202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119, 11 | 248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232, 12 | 178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249, 13 | 14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205, 14 | 93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180]; 15 | 16 | for (var i=0; i < 256 ; i++) { 17 | 18 | p[256+i] = p[i]; 19 | 20 | } 21 | 22 | function fade(t) { 23 | 24 | return t * t * t * (t * (t * 6 - 15) + 10); 25 | 26 | } 27 | 28 | function lerp(t, a, b) { 29 | 30 | return a + t * (b - a); 31 | 32 | } 33 | 34 | function grad(hash, x, y, z) { 35 | 36 | var h = hash & 15; 37 | var u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z; 38 | return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v); 39 | 40 | } 41 | 42 | return { 43 | 44 | noise: function (x, y, z) { 45 | 46 | var floorX = ~~x, floorY = ~~y, floorZ = ~~z; 47 | 48 | var X = floorX & 255, Y = floorY & 255, Z = floorZ & 255; 49 | 50 | x -= floorX; 51 | y -= floorY; 52 | z -= floorZ; 53 | 54 | var xMinus1 = x -1, yMinus1 = y - 1, zMinus1 = z - 1; 55 | 56 | var u = fade(x), v = fade(y), w = fade(z); 57 | 58 | var A = p[X]+Y, AA = p[A]+Z, AB = p[A+1]+Z, B = p[X+1]+Y, BA = p[B]+Z, BB = p[B+1]+Z; 59 | 60 | return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), 61 | grad(p[BA], xMinus1, y, z)), 62 | lerp(u, grad(p[AB], x, yMinus1, z), 63 | grad(p[BB], xMinus1, yMinus1, z))), 64 | lerp(v, lerp(u, grad(p[AA+1], x, y, zMinus1), 65 | grad(p[BA+1], xMinus1, y, z-1)), 66 | lerp(u, grad(p[AB+1], x, yMinus1, zMinus1), 67 | grad(p[BB+1], xMinus1, yMinus1, zMinus1)))); 68 | 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /public/js/loaders/VTKLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.VTKLoader = function () {}; 6 | 7 | THREE.VTKLoader.prototype = { 8 | 9 | constructor: THREE.VTKLoader, 10 | 11 | load: function ( url, callback ) { 12 | 13 | var scope = this; 14 | var request = new XMLHttpRequest(); 15 | 16 | request.addEventListener( 'load', function ( event ) { 17 | 18 | var geometry = scope.parse( event.target.responseText ); 19 | 20 | scope.dispatchEvent( { type: 'load', content: geometry } ); 21 | 22 | if ( callback ) callback( geometry ); 23 | 24 | }, false ); 25 | 26 | request.addEventListener( 'progress', function ( event ) { 27 | 28 | scope.dispatchEvent( { type: 'progress', loaded: event.loaded, total: event.total } ); 29 | 30 | }, false ); 31 | 32 | request.addEventListener( 'error', function () { 33 | 34 | scope.dispatchEvent( { type: 'error', message: 'Couldn\'t load URL [' + url + ']' } ); 35 | 36 | }, false ); 37 | 38 | request.open( 'GET', url, true ); 39 | request.send( null ); 40 | 41 | }, 42 | 43 | parse: function ( data ) { 44 | 45 | var geometry = new THREE.Geometry(); 46 | 47 | var vertex = function ( x, y, z ) { 48 | 49 | geometry.vertices.push( new THREE.Vector3( x, y, z ) ); 50 | 51 | } 52 | 53 | var face3 = function ( a, b, c ) { 54 | 55 | geometry.faces.push( new THREE.Face3( a, b, c ) ); 56 | 57 | } 58 | 59 | var pattern, result; 60 | 61 | // float float float 62 | 63 | pattern = /([\+|\-]?[\d]+[\.]*[\d|\-|e]*)[ ]+([\+|\-]?[\d]+[\.]*[\d|\-|e]*)[ ]+([\+|\-]?[\d]+[\.]*[\d|\-|e]*)/g; 64 | 65 | while ( ( result = pattern.exec( data ) ) !== null ) { 66 | 67 | // ["1.0 2.0 3.0", "1.0", "2.0", "3.0"] 68 | 69 | vertex( parseFloat( result[ 1 ] ), parseFloat( result[ 2 ] ), parseFloat( result[ 3 ] ) ); 70 | 71 | } 72 | 73 | // 3 int int int 74 | 75 | pattern = /3[ ]+([\d]+)[ ]+([\d]+)[ ]+([\d]+)/g; 76 | 77 | while ( ( result = pattern.exec( data ) ) !== null ) { 78 | 79 | // ["3 1 2 3", "1", "2", "3"] 80 | 81 | face3( parseInt( result[ 1 ] ), parseInt( result[ 2 ] ), parseInt( result[ 3 ] ) ); 82 | 83 | } 84 | 85 | // 4 int int int int 86 | 87 | pattern = /4[ ]+([\d]+)[ ]+([\d]+)[ ]+([\d]+)[ ]+([\d]+)/g; 88 | 89 | while ( ( result = pattern.exec( data ) ) !== null ) { 90 | 91 | // ["4 1 2 3 4", "1", "2", "3", "4"] 92 | 93 | face3( parseInt( result[ 1 ] ), parseInt( result[ 2 ] ), parseInt( result[ 4 ] ) ); 94 | face3( parseInt( result[ 2 ] ), parseInt( result[ 3 ] ), parseInt( result[ 4 ] ) ); 95 | 96 | } 97 | 98 | geometry.computeFaceNormals(); 99 | geometry.computeVertexNormals(); 100 | geometry.computeBoundingSphere(); 101 | 102 | return geometry; 103 | 104 | } 105 | 106 | }; 107 | 108 | THREE.EventDispatcher.prototype.apply( THREE.VTKLoader.prototype ); 109 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var hmd = require("node-hmd"), 2 | express = require("express"), 3 | http = require("http").createServer(), 4 | WebSocketServer = require('ws').Server, 5 | path = require('path'); 6 | 7 | // Create HMD manager object 8 | console.info("Attempting to load node-hmd driver: oculusrift"); 9 | var manager = hmd.createManager("oculusrift"); 10 | if (typeof(manager) === "undefined") { 11 | console.error("Unable to load driver: oculusrift"); 12 | process.exit(1); 13 | } 14 | // Instantiate express server 15 | var app = express(); 16 | app.set('port', process.env.PORT || 3000); 17 | 18 | app.use(express.static(path.join(__dirname + '/', 'public'))); 19 | app.set('views', path.join(__dirname + '/public/', 'views')); 20 | app.set('view engine', 'jade'); 21 | 22 | app.get('/demo', function (req, res) { 23 | 'use strict'; 24 | res.render('demo', { 25 | title: 'Home' 26 | }); 27 | }); 28 | 29 | 30 | app.get("/", function (req, res) { 31 | res.header('Access-Control-Allow-Origin', '*'); 32 | res.header('Access-Control-Allow-Methods', 'GET'); 33 | res.header('Access-Control-Allow-Headers', 'Content-Type'); 34 | 35 | res.json({quat: manager.getDeviceQuatSync(), position: manager.getDevicePositionSync()}) 36 | }); 37 | 38 | app.get("/supported", function (req, res) { 39 | res.json(hmd.getSupportedDevices()); 40 | }); 41 | 42 | app.get("/info", function (req, res) { 43 | manager.getDeviceInfo(function (err, deviceInfo) { 44 | res.json(deviceInfo); 45 | }); 46 | }); 47 | 48 | app.get("/orientation", function (req, res) { 49 | res.header('Access-Control-Allow-Origin', '*'); 50 | res.header('Access-Control-Allow-Methods', 'GET'); 51 | res.header('Access-Control-Allow-Headers', 'Content-Type'); 52 | 53 | manager.getDeviceOrientation(function (err, deviceOrientation) { 54 | res.json(deviceOrientation); 55 | }); 56 | }); 57 | 58 | 59 | // Attach socket.io listener to the server 60 | var wss = new WebSocketServer({server: http}); 61 | var id = 1; 62 | 63 | wss.on('open', function open() { 64 | console.log('connected'); 65 | }); 66 | 67 | // On socket connection set up event emitters to automatically push the HMD orientation data 68 | wss.on("connection", function (ws) { 69 | function emitOrientation() { 70 | id = id + 1; 71 | var deviceQuat = manager.getDeviceQuatSync(); 72 | var devicePosition = manager.getDevicePositionSync(); 73 | 74 | var data = JSON.stringify({ 75 | id: id, 76 | quat: deviceQuat, 77 | position: devicePosition 78 | }); 79 | 80 | ws.send(data, function (error) { 81 | //it's a bug of websocket, see in https://github.com/websockets/ws/issues/337 82 | }); 83 | } 84 | 85 | var orientation = setInterval(emitOrientation, 1000); 86 | 87 | ws.on("message", function (data) { 88 | clearInterval(orientation); 89 | orientation = setInterval(emitOrientation, data); 90 | }); 91 | 92 | ws.on("close", function () { 93 | setTimeout(null, 500); 94 | clearInterval(orientation); 95 | console.log("disconnect"); 96 | }); 97 | }); 98 | 99 | // Launch express server 100 | http.on('request', app); 101 | http.listen(3000, function () { 102 | console.log("Express server listening on port 3000"); 103 | }); 104 | -------------------------------------------------------------------------------- /public/js/controls/PointerLockControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.PointerLockControls = function ( camera ) { 6 | 7 | var scope = this; 8 | 9 | var pitchObject = new THREE.Object3D(); 10 | pitchObject.add( camera ); 11 | 12 | var yawObject = new THREE.Object3D(); 13 | yawObject.position.y = 10; 14 | yawObject.add( pitchObject ); 15 | 16 | var moveForward = false; 17 | var moveBackward = false; 18 | var moveLeft = false; 19 | var moveRight = false; 20 | 21 | var isOnObject = false; 22 | var canJump = false; 23 | 24 | var velocity = new THREE.Vector3(); 25 | 26 | var PI_2 = Math.PI / 2; 27 | 28 | var onMouseMove = function ( event ) { 29 | 30 | if ( scope.enabled === false ) return; 31 | 32 | var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0; 33 | var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0; 34 | 35 | yawObject.rotation.y -= movementX * 0.002; 36 | pitchObject.rotation.x -= movementY * 0.002; 37 | 38 | pitchObject.rotation.x = Math.max( - PI_2, Math.min( PI_2, pitchObject.rotation.x ) ); 39 | 40 | }; 41 | 42 | var onKeyDown = function ( event ) { 43 | 44 | switch ( event.keyCode ) { 45 | 46 | case 38: // up 47 | case 87: // w 48 | moveForward = true; 49 | break; 50 | 51 | case 37: // left 52 | case 65: // a 53 | moveLeft = true; break; 54 | 55 | case 40: // down 56 | case 83: // s 57 | moveBackward = true; 58 | break; 59 | 60 | case 39: // right 61 | case 68: // d 62 | moveRight = true; 63 | break; 64 | 65 | case 32: // space 66 | if ( canJump === true ) velocity.y += 10; 67 | canJump = false; 68 | break; 69 | 70 | } 71 | 72 | }; 73 | 74 | var onKeyUp = function ( event ) { 75 | 76 | switch( event.keyCode ) { 77 | 78 | case 38: // up 79 | case 87: // w 80 | moveForward = false; 81 | break; 82 | 83 | case 37: // left 84 | case 65: // a 85 | moveLeft = false; 86 | break; 87 | 88 | case 40: // down 89 | case 83: // a 90 | moveBackward = false; 91 | break; 92 | 93 | case 39: // right 94 | case 68: // d 95 | moveRight = false; 96 | break; 97 | 98 | } 99 | 100 | }; 101 | 102 | document.addEventListener( 'mousemove', onMouseMove, false ); 103 | document.addEventListener( 'keydown', onKeyDown, false ); 104 | document.addEventListener( 'keyup', onKeyUp, false ); 105 | 106 | this.enabled = false; 107 | 108 | this.getObject = function () { 109 | 110 | return yawObject; 111 | 112 | }; 113 | 114 | this.isOnObject = function ( boolean ) { 115 | 116 | isOnObject = boolean; 117 | canJump = boolean; 118 | 119 | }; 120 | 121 | this.update = function ( delta ) { 122 | 123 | if ( scope.enabled === false ) return; 124 | 125 | delta *= 0.1; 126 | 127 | velocity.x += ( - velocity.x ) * 0.08 * delta; 128 | velocity.z += ( - velocity.z ) * 0.08 * delta; 129 | 130 | velocity.y -= 0.25 * delta; 131 | 132 | if ( moveForward ) velocity.z -= 0.12 * delta; 133 | if ( moveBackward ) velocity.z += 0.12 * delta; 134 | 135 | if ( moveLeft ) velocity.x -= 0.12 * delta; 136 | if ( moveRight ) velocity.x += 0.12 * delta; 137 | 138 | if ( isOnObject === true ) { 139 | 140 | velocity.y = Math.max( 0, velocity.y ); 141 | 142 | } 143 | 144 | yawObject.translateX( velocity.x ); 145 | yawObject.translateY( velocity.y ); 146 | yawObject.translateZ( velocity.z ); 147 | 148 | if ( yawObject.position.y < 10 ) { 149 | 150 | velocity.y = 0; 151 | yawObject.position.y = 10; 152 | 153 | canJump = true; 154 | 155 | } 156 | 157 | }; 158 | 159 | }; 160 | -------------------------------------------------------------------------------- /public/js/controls/DK2Controls.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2014 Lars Ivar Hatledal 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | */ 13 | 14 | THREE.DK2Controls = function (camera) { 15 | 16 | this.camera = camera; 17 | this.ws; 18 | this.sensorData; 19 | this.lastId = -1; 20 | 21 | this.controller = new THREE.Object3D(); 22 | 23 | this.headPos = new THREE.Vector3(); 24 | this.headQuat = new THREE.Quaternion(); 25 | 26 | this.translationSpeed = 20000; 27 | 28 | this.wasd = { 29 | left: false, 30 | up: false, 31 | right: false, 32 | down: false 33 | }; 34 | 35 | var that = this; 36 | var ws = new WebSocket("ws://localhost:3000/"); 37 | ws.onopen = function () { 38 | console.log("### Connected ####"); 39 | }; 40 | 41 | ws.onmessage = function (evt) { 42 | var message = evt.data; 43 | try { 44 | that.sensorData = JSON.parse(message); 45 | } catch (err) { 46 | console.log(message); 47 | } 48 | }; 49 | 50 | ws.onclose = function () { 51 | console.log("### Closed ####"); 52 | }; 53 | 54 | 55 | this.onKeyDown = function (event) { 56 | switch (event.keyCode) { 57 | case 87: //W 58 | this.wasd.up = true; 59 | break; 60 | case 83: //S 61 | this.wasd.down = true; 62 | break; 63 | case 68: //D 64 | this.wasd.right = true; 65 | break; 66 | case 65: //A 67 | this.wasd.left = true; 68 | break; 69 | } 70 | }; 71 | 72 | this.onKeyUp = function (event) { 73 | switch (event.keyCode) { 74 | case 87: //W 75 | this.wasd.up = false; 76 | break; 77 | case 83: //S 78 | this.wasd.down = false; 79 | break; 80 | case 68: //D 81 | this.wasd.right = false; 82 | break; 83 | case 65: //A 84 | this.wasd.left = false; 85 | break; 86 | } 87 | }; 88 | 89 | 90 | this.update = function (delta) { 91 | 92 | var sensorData = this.sensorData; 93 | if (sensorData) { 94 | var id = sensorData.id; 95 | if (id > this.lastId) { 96 | this.headPos.set(sensorData.position.x * 10 - 0.4, sensorData.position.y * 10 + 1.75, sensorData.position.z * 10 + 10); 97 | this.headQuat.set(sensorData.quat.x, sensorData.quat.y, sensorData.quat.z, sensorData.quat.w); 98 | 99 | this.camera.setRotationFromQuaternion(this.headQuat); 100 | this.controller.setRotationFromMatrix(this.camera.matrix); 101 | } 102 | this.lastId = id; 103 | } 104 | 105 | if (this.wasd.up) { 106 | this.controller.translateZ(-this.translationSpeed * delta); 107 | } 108 | 109 | if (this.wasd.down) { 110 | this.controller.translateZ(this.translationSpeed * delta); 111 | } 112 | 113 | if (this.wasd.right) { 114 | this.controller.translateX(this.translationSpeed * delta); 115 | } 116 | 117 | if (this.wasd.left) { 118 | this.controller.translateX(-this.translationSpeed * delta); 119 | } 120 | 121 | this.camera.position.addVectors(this.controller.position, this.headPos); 122 | 123 | if (this.camera.position.y < -10) { 124 | this.camera.position.y = -10; 125 | } 126 | 127 | if (ws) { 128 | if (ws.readyState === 1) { 129 | ws.send("get\n"); 130 | } 131 | } 132 | 133 | }; 134 | 135 | window.addEventListener('keydown', this.onKeyDown.bind(this), false); 136 | window.addEventListener('keyup', this.onKeyUp.bind(this), false); 137 | 138 | function bind(scope, fn) { 139 | 140 | return function () { 141 | 142 | fn.apply(scope, arguments); 143 | 144 | }; 145 | 146 | }; 147 | 148 | }; -------------------------------------------------------------------------------- /public/js/libs/tween.min.js: -------------------------------------------------------------------------------- 1 | // tween.js - http://github.com/sole/tween.js 2 | 'use strict';var TWEEN=TWEEN||function(){var a=[];return{REVISION:"7",getAll:function(){return a},removeAll:function(){a=[]},add:function(c){a.push(c)},remove:function(c){c=a.indexOf(c);-1!==c&&a.splice(c,1)},update:function(c){if(0===a.length)return!1;for(var b=0,d=a.length,c=void 0!==c?c:Date.now();b(a*=2)?0.5*a*a:-0.5*(--a*(a-2)-1)}},Cubic:{In:function(a){return a*a*a},Out:function(a){return--a*a*a+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*a:0.5*((a-=2)*a*a+2)}},Quartic:{In:function(a){return a*a*a*a},Out:function(a){return 1- --a*a*a*a},InOut:function(a){return 1>(a*=2)?0.5*a*a*a*a:-0.5*((a-=2)*a*a*a-2)}},Quintic:{In:function(a){return a*a*a* 7 | a*a},Out:function(a){return--a*a*a*a*a+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*a*a*a:0.5*((a-=2)*a*a*a*a+2)}},Sinusoidal:{In:function(a){return 1-Math.cos(a*Math.PI/2)},Out:function(a){return Math.sin(a*Math.PI/2)},InOut:function(a){return 0.5*(1-Math.cos(Math.PI*a))}},Exponential:{In:function(a){return 0===a?0:Math.pow(1024,a-1)},Out:function(a){return 1===a?1:1-Math.pow(2,-10*a)},InOut:function(a){return 0===a?0:1===a?1:1>(a*=2)?0.5*Math.pow(1024,a-1):0.5*(-Math.pow(2,-10*(a-1))+2)}},Circular:{In:function(a){return 1- 8 | Math.sqrt(1-a*a)},Out:function(a){return Math.sqrt(1- --a*a)},InOut:function(a){return 1>(a*=2)?-0.5*(Math.sqrt(1-a*a)-1):0.5*(Math.sqrt(1-(a-=2)*a)+1)}},Elastic:{In:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return-(b*Math.pow(2,10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4))},Out:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return b*Math.pow(2,-10*a)*Math.sin((a-c)* 9 | 2*Math.PI/0.4)+1},InOut:function(a){var c,b=0.1;if(0===a)return 0;if(1===a)return 1;!b||1>b?(b=1,c=0.1):c=0.4*Math.asin(1/b)/(2*Math.PI);return 1>(a*=2)?-0.5*b*Math.pow(2,10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4):0.5*b*Math.pow(2,-10*(a-=1))*Math.sin((a-c)*2*Math.PI/0.4)+1}},Back:{In:function(a){return a*a*(2.70158*a-1.70158)},Out:function(a){return--a*a*(2.70158*a+1.70158)+1},InOut:function(a){return 1>(a*=2)?0.5*a*a*(3.5949095*a-2.5949095):0.5*((a-=2)*a*(3.5949095*a+2.5949095)+2)}},Bounce:{In:function(a){return 1- 10 | TWEEN.Easing.Bounce.Out(1-a)},Out:function(a){return a<1/2.75?7.5625*a*a:a<2/2.75?7.5625*(a-=1.5/2.75)*a+0.75:a<2.5/2.75?7.5625*(a-=2.25/2.75)*a+0.9375:7.5625*(a-=2.625/2.75)*a+0.984375},InOut:function(a){return 0.5>a?0.5*TWEEN.Easing.Bounce.In(2*a):0.5*TWEEN.Easing.Bounce.Out(2*a-1)+0.5}}}; 11 | TWEEN.Interpolation={Linear:function(a,c){var b=a.length-1,d=b*c,e=Math.floor(d),f=TWEEN.Interpolation.Utils.Linear;return 0>c?f(a[0],a[1],d):1b?b:e+1],d-e)},Bezier:function(a,c){var b=0,d=a.length-1,e=Math.pow,f=TWEEN.Interpolation.Utils.Bernstein,h;for(h=0;h<=d;h++)b+=e(1-c,d-h)*e(c,h)*a[h]*f(d,h);return b},CatmullRom:function(a,c){var b=a.length-1,d=b*c,e=Math.floor(d),f=TWEEN.Interpolation.Utils.CatmullRom;return a[0]===a[b]?(0>c&&(e=Math.floor(d=b*(1+c))),f(a[(e- 12 | 1+b)%b],a[e],a[(e+1)%b],a[(e+2)%b],d-e)):0>c?a[0]-(f(a[0],a[0],a[1],a[1],-d)-a[0]):1 1.00 ) {", 64 | 65 | " gl_FragColor = texture2D( mapLeft, uv );", 66 | 67 | " } else {", 68 | 69 | " gl_FragColor = texture2D( mapRight, uv );", 70 | 71 | " }", 72 | 73 | "}" 74 | 75 | ].join("\n") 76 | 77 | } ); 78 | 79 | var mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), _material ); 80 | _scene.add( mesh ); 81 | 82 | this.setSize = function ( width, height ) { 83 | 84 | _renderTargetL = new THREE.WebGLRenderTarget( width, height, _params ); 85 | _renderTargetR = new THREE.WebGLRenderTarget( width, height, _params ); 86 | 87 | _material.uniforms[ "mapLeft" ].value = _renderTargetL; 88 | _material.uniforms[ "mapRight" ].value = _renderTargetR; 89 | 90 | renderer.setSize( width, height ); 91 | 92 | }; 93 | 94 | /* 95 | * Renderer now uses an asymmetric perspective projection 96 | * (http://paulbourke.net/miscellaneous/stereographics/stereorender/). 97 | * 98 | * Each camera is offset by the eye seperation and its projection matrix is 99 | * also skewed asymetrically back to converge on the same projection plane. 100 | * Added a focal length parameter to, this is where the parallax is equal to 0. 101 | */ 102 | 103 | this.render = function ( scene, camera ) { 104 | 105 | scene.updateMatrixWorld(); 106 | 107 | if ( camera.parent === undefined ) camera.updateMatrixWorld(); 108 | 109 | var hasCameraChanged = ( _aspect !== camera.aspect ) || ( _near !== camera.near ) || ( _far !== camera.far ) || ( _fov !== camera.fov ); 110 | 111 | if ( hasCameraChanged ) { 112 | 113 | _aspect = camera.aspect; 114 | _near = camera.near; 115 | _far = camera.far; 116 | _fov = camera.fov; 117 | 118 | var projectionMatrix = camera.projectionMatrix.clone(); 119 | var eyeSep = focalLength / 30 * 0.5; 120 | var eyeSepOnProjection = eyeSep * _near / focalLength; 121 | var ymax = _near * Math.tan( THREE.Math.degToRad( _fov * 0.5 ) ); 122 | var xmin, xmax; 123 | 124 | // translate xOffset 125 | 126 | eyeRight.elements[12] = eyeSep; 127 | eyeLeft.elements[12] = -eyeSep; 128 | 129 | // for left eye 130 | 131 | xmin = -ymax * _aspect + eyeSepOnProjection; 132 | xmax = ymax * _aspect + eyeSepOnProjection; 133 | 134 | projectionMatrix.elements[0] = 2 * _near / ( xmax - xmin ); 135 | projectionMatrix.elements[8] = ( xmax + xmin ) / ( xmax - xmin ); 136 | 137 | _cameraL.projectionMatrix.copy( projectionMatrix ); 138 | 139 | // for right eye 140 | 141 | xmin = -ymax * _aspect - eyeSepOnProjection; 142 | xmax = ymax * _aspect - eyeSepOnProjection; 143 | 144 | projectionMatrix.elements[0] = 2 * _near / ( xmax - xmin ); 145 | projectionMatrix.elements[8] = ( xmax + xmin ) / ( xmax - xmin ); 146 | 147 | _cameraR.projectionMatrix.copy( projectionMatrix ); 148 | 149 | } 150 | 151 | _cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); 152 | _cameraL.position.copy( camera.position ); 153 | _cameraL.near = camera.near; 154 | _cameraL.far = camera.far; 155 | 156 | renderer.render( scene, _cameraL, _renderTargetL, true ); 157 | 158 | _cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); 159 | _cameraR.position.copy( camera.position ); 160 | _cameraR.near = camera.near; 161 | _cameraR.far = camera.far; 162 | 163 | renderer.render( scene, _cameraR, _renderTargetR, true ); 164 | 165 | _scene.updateMatrixWorld(); 166 | 167 | renderer.render( _scene, _camera ); 168 | 169 | }; 170 | 171 | }; 172 | -------------------------------------------------------------------------------- /public/js/effects/AnaglyphEffect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | * @author marklundin / http://mark-lundin.com/ 4 | * @author alteredq / http://alteredqualia.com/ 5 | */ 6 | 7 | THREE.AnaglyphEffect = function ( renderer, width, height ) { 8 | 9 | var eyeRight = new THREE.Matrix4(); 10 | var eyeLeft = new THREE.Matrix4(); 11 | var focalLength = 125; 12 | var _aspect, _near, _far, _fov; 13 | 14 | var _cameraL = new THREE.PerspectiveCamera(); 15 | _cameraL.matrixAutoUpdate = false; 16 | 17 | var _cameraR = new THREE.PerspectiveCamera(); 18 | _cameraR.matrixAutoUpdate = false; 19 | 20 | var _camera = new THREE.OrthographicCamera( -1, 1, 1, - 1, 0, 1 ); 21 | 22 | var _scene = new THREE.Scene(); 23 | 24 | var _params = { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat }; 25 | 26 | if ( width === undefined ) width = 512; 27 | if ( height === undefined ) height = 512; 28 | 29 | var _renderTargetL = new THREE.WebGLRenderTarget( width, height, _params ); 30 | var _renderTargetR = new THREE.WebGLRenderTarget( width, height, _params ); 31 | 32 | var _material = new THREE.ShaderMaterial( { 33 | 34 | uniforms: { 35 | 36 | "mapLeft": { type: "t", value: _renderTargetL }, 37 | "mapRight": { type: "t", value: _renderTargetR } 38 | 39 | }, 40 | 41 | vertexShader: [ 42 | 43 | "varying vec2 vUv;", 44 | 45 | "void main() {", 46 | 47 | " vUv = vec2( uv.x, uv.y );", 48 | " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", 49 | 50 | "}" 51 | 52 | ].join("\n"), 53 | 54 | fragmentShader: [ 55 | 56 | "uniform sampler2D mapLeft;", 57 | "uniform sampler2D mapRight;", 58 | "varying vec2 vUv;", 59 | 60 | "void main() {", 61 | 62 | " vec4 colorL, colorR;", 63 | " vec2 uv = vUv;", 64 | 65 | " colorL = texture2D( mapLeft, uv );", 66 | " colorR = texture2D( mapRight, uv );", 67 | 68 | // http://3dtv.at/Knowhow/AnaglyphComparison_en.aspx 69 | 70 | " gl_FragColor = vec4( colorL.g * 0.7 + colorL.b * 0.3, colorR.g, colorR.b, colorL.a + colorR.a ) * 1.1;", 71 | 72 | "}" 73 | 74 | ].join("\n") 75 | 76 | } ); 77 | 78 | var mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), _material ); 79 | _scene.add( mesh ); 80 | 81 | this.setSize = function ( width, height ) { 82 | 83 | if ( _renderTargetL ) _renderTargetL.dispose(); 84 | if ( _renderTargetR ) _renderTargetR.dispose(); 85 | _renderTargetL = new THREE.WebGLRenderTarget( width, height, _params ); 86 | _renderTargetR = new THREE.WebGLRenderTarget( width, height, _params ); 87 | 88 | _material.uniforms[ "mapLeft" ].value = _renderTargetL; 89 | _material.uniforms[ "mapRight" ].value = _renderTargetR; 90 | 91 | renderer.setSize( width, height ); 92 | 93 | }; 94 | 95 | /* 96 | * Renderer now uses an asymmetric perspective projection 97 | * (http://paulbourke.net/miscellaneous/stereographics/stereorender/). 98 | * 99 | * Each camera is offset by the eye seperation and its projection matrix is 100 | * also skewed asymetrically back to converge on the same projection plane. 101 | * Added a focal length parameter to, this is where the parallax is equal to 0. 102 | */ 103 | 104 | this.render = function ( scene, camera ) { 105 | 106 | scene.updateMatrixWorld(); 107 | 108 | if ( camera.parent === undefined ) camera.updateMatrixWorld(); 109 | 110 | var hasCameraChanged = ( _aspect !== camera.aspect ) || ( _near !== camera.near ) || ( _far !== camera.far ) || ( _fov !== camera.fov ); 111 | 112 | if ( hasCameraChanged ) { 113 | 114 | _aspect = camera.aspect; 115 | _near = camera.near; 116 | _far = camera.far; 117 | _fov = camera.fov; 118 | 119 | var projectionMatrix = camera.projectionMatrix.clone(); 120 | var eyeSep = focalLength / 30 * 0.5; 121 | var eyeSepOnProjection = eyeSep * _near / focalLength; 122 | var ymax = _near * Math.tan( THREE.Math.degToRad( _fov * 0.5 ) ); 123 | var xmin, xmax; 124 | 125 | // translate xOffset 126 | 127 | eyeRight.elements[12] = eyeSep; 128 | eyeLeft.elements[12] = -eyeSep; 129 | 130 | // for left eye 131 | 132 | xmin = -ymax * _aspect + eyeSepOnProjection; 133 | xmax = ymax * _aspect + eyeSepOnProjection; 134 | 135 | projectionMatrix.elements[0] = 2 * _near / ( xmax - xmin ); 136 | projectionMatrix.elements[8] = ( xmax + xmin ) / ( xmax - xmin ); 137 | 138 | _cameraL.projectionMatrix.copy( projectionMatrix ); 139 | 140 | // for right eye 141 | 142 | xmin = -ymax * _aspect - eyeSepOnProjection; 143 | xmax = ymax * _aspect - eyeSepOnProjection; 144 | 145 | projectionMatrix.elements[0] = 2 * _near / ( xmax - xmin ); 146 | projectionMatrix.elements[8] = ( xmax + xmin ) / ( xmax - xmin ); 147 | 148 | _cameraR.projectionMatrix.copy( projectionMatrix ); 149 | 150 | } 151 | 152 | _cameraL.matrixWorld.copy( camera.matrixWorld ).multiply( eyeLeft ); 153 | _cameraL.position.copy( camera.position ); 154 | _cameraL.near = camera.near; 155 | _cameraL.far = camera.far; 156 | 157 | renderer.render( scene, _cameraL, _renderTargetL, true ); 158 | 159 | _cameraR.matrixWorld.copy( camera.matrixWorld ).multiply( eyeRight ); 160 | _cameraR.position.copy( camera.position ); 161 | _cameraR.near = camera.near; 162 | _cameraR.far = camera.far; 163 | 164 | renderer.render( scene, _cameraR, _renderTargetR, true ); 165 | 166 | renderer.render( _scene, _camera ); 167 | 168 | }; 169 | 170 | this.dispose = function() { 171 | if ( _renderTargetL ) _renderTargetL.dispose(); 172 | if ( _renderTargetR ) _renderTargetR.dispose(); 173 | } 174 | 175 | }; 176 | -------------------------------------------------------------------------------- /public/js/loaders/collada/AnimationHandler.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mikael emtinger / http://gomo.se/ 3 | */ 4 | 5 | THREE.AnimationHandler = { 6 | 7 | LINEAR: 0, 8 | CATMULLROM: 1, 9 | CATMULLROM_FORWARD: 2, 10 | 11 | // 12 | 13 | add: function () { 14 | 15 | console.warn( 'THREE.AnimationHandler.add() has been deprecated.' ); 16 | 17 | }, 18 | get: function () { 19 | 20 | console.warn( 'THREE.AnimationHandler.get() has been deprecated.' ); 21 | 22 | }, 23 | remove: function () { 24 | 25 | console.warn( 'THREE.AnimationHandler.remove() has been deprecated.' ); 26 | 27 | }, 28 | 29 | // 30 | 31 | animations: [], 32 | 33 | init: function ( data ) { 34 | 35 | if ( data.initialized === true ) return data; 36 | 37 | // loop through all keys 38 | 39 | for ( var h = 0; h < data.hierarchy.length; h ++ ) { 40 | 41 | for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { 42 | 43 | // remove minus times 44 | 45 | if ( data.hierarchy[ h ].keys[ k ].time < 0 ) { 46 | 47 | data.hierarchy[ h ].keys[ k ].time = 0; 48 | 49 | } 50 | 51 | // create quaternions 52 | 53 | if ( data.hierarchy[ h ].keys[ k ].rot !== undefined && 54 | ! ( data.hierarchy[ h ].keys[ k ].rot instanceof THREE.Quaternion ) ) { 55 | 56 | var quat = data.hierarchy[ h ].keys[ k ].rot; 57 | data.hierarchy[ h ].keys[ k ].rot = new THREE.Quaternion().fromArray( quat ); 58 | 59 | } 60 | 61 | } 62 | 63 | // prepare morph target keys 64 | 65 | if ( data.hierarchy[ h ].keys.length && data.hierarchy[ h ].keys[ 0 ].morphTargets !== undefined ) { 66 | 67 | // get all used 68 | 69 | var usedMorphTargets = {}; 70 | 71 | for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { 72 | 73 | for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { 74 | 75 | var morphTargetName = data.hierarchy[ h ].keys[ k ].morphTargets[ m ]; 76 | usedMorphTargets[ morphTargetName ] = - 1; 77 | 78 | } 79 | 80 | } 81 | 82 | data.hierarchy[ h ].usedMorphTargets = usedMorphTargets; 83 | 84 | 85 | // set all used on all frames 86 | 87 | for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { 88 | 89 | var influences = {}; 90 | 91 | for ( var morphTargetName in usedMorphTargets ) { 92 | 93 | for ( var m = 0; m < data.hierarchy[ h ].keys[ k ].morphTargets.length; m ++ ) { 94 | 95 | if ( data.hierarchy[ h ].keys[ k ].morphTargets[ m ] === morphTargetName ) { 96 | 97 | influences[ morphTargetName ] = data.hierarchy[ h ].keys[ k ].morphTargetsInfluences[ m ]; 98 | break; 99 | 100 | } 101 | 102 | } 103 | 104 | if ( m === data.hierarchy[ h ].keys[ k ].morphTargets.length ) { 105 | 106 | influences[ morphTargetName ] = 0; 107 | 108 | } 109 | 110 | } 111 | 112 | data.hierarchy[ h ].keys[ k ].morphTargetsInfluences = influences; 113 | 114 | } 115 | 116 | } 117 | 118 | 119 | // remove all keys that are on the same time 120 | 121 | for ( var k = 1; k < data.hierarchy[ h ].keys.length; k ++ ) { 122 | 123 | if ( data.hierarchy[ h ].keys[ k ].time === data.hierarchy[ h ].keys[ k - 1 ].time ) { 124 | 125 | data.hierarchy[ h ].keys.splice( k, 1 ); 126 | k --; 127 | 128 | } 129 | 130 | } 131 | 132 | 133 | // set index 134 | 135 | for ( var k = 0; k < data.hierarchy[ h ].keys.length; k ++ ) { 136 | 137 | data.hierarchy[ h ].keys[ k ].index = k; 138 | 139 | } 140 | 141 | } 142 | 143 | data.initialized = true; 144 | 145 | return data; 146 | 147 | }, 148 | 149 | parse: function ( root ) { 150 | 151 | var parseRecurseHierarchy = function ( root, hierarchy ) { 152 | 153 | hierarchy.push( root ); 154 | 155 | for ( var c = 0; c < root.children.length; c ++ ) 156 | parseRecurseHierarchy( root.children[ c ], hierarchy ); 157 | 158 | }; 159 | 160 | // setup hierarchy 161 | 162 | var hierarchy = []; 163 | 164 | if ( root instanceof THREE.SkinnedMesh ) { 165 | 166 | for ( var b = 0; b < root.skeleton.bones.length; b ++ ) { 167 | 168 | hierarchy.push( root.skeleton.bones[ b ] ); 169 | 170 | } 171 | 172 | } else { 173 | 174 | parseRecurseHierarchy( root, hierarchy ); 175 | 176 | } 177 | 178 | return hierarchy; 179 | 180 | }, 181 | 182 | play: function ( animation ) { 183 | 184 | if ( this.animations.indexOf( animation ) === - 1 ) { 185 | 186 | this.animations.push( animation ); 187 | 188 | } 189 | 190 | }, 191 | 192 | stop: function ( animation ) { 193 | 194 | var index = this.animations.indexOf( animation ); 195 | 196 | if ( index !== - 1 ) { 197 | 198 | this.animations.splice( index, 1 ); 199 | 200 | } 201 | 202 | }, 203 | 204 | update: function ( deltaTimeMS ) { 205 | 206 | for ( var i = 0; i < this.animations.length; i ++ ) { 207 | 208 | this.animations[ i ].resetBlendWeights(); 209 | 210 | } 211 | 212 | for ( var i = 0; i < this.animations.length; i ++ ) { 213 | 214 | this.animations[ i ].update( deltaTimeMS ); 215 | 216 | } 217 | 218 | } 219 | 220 | }; -------------------------------------------------------------------------------- /public/js/loaders/PDBLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | */ 4 | 5 | THREE.PDBLoader = function ( manager ) { 6 | 7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 8 | 9 | }; 10 | 11 | THREE.PDBLoader.prototype = { 12 | 13 | constructor: THREE.PDBLoader, 14 | 15 | load: function ( url, onLoad ) { 16 | 17 | var scope = this; 18 | 19 | var loader = new THREE.XHRLoader( scope.manager ); 20 | loader.setCrossOrigin( this.crossOrigin ); 21 | loader.load( url, function ( text ) { 22 | 23 | var json = scope.parsePDB( text ); 24 | scope.createModel( json, onLoad ); 25 | 26 | } ); 27 | 28 | }, 29 | 30 | // Based on CanvasMol PDB parser 31 | 32 | parsePDB: function ( text ) { 33 | 34 | function trim( text ) { 35 | 36 | return text.replace(/^\s\s*/, '').replace(/\s\s*$/, ''); 37 | 38 | } 39 | 40 | function capitalize( text ) { 41 | 42 | return text.charAt(0).toUpperCase() + text.substr(1).toLowerCase(); 43 | 44 | } 45 | 46 | function hash( s, e ) { 47 | 48 | return "s" + Math.min( s, e ) + "e" + Math.max( s, e ); 49 | 50 | } 51 | 52 | function parseBond( start, length ) { 53 | 54 | var eatom = parseInt( lines[ i ].substr( start, length ) ); 55 | 56 | if( eatom ) { 57 | 58 | var h = hash( satom, eatom ); 59 | 60 | if ( bhash[ h ] == undefined ) { 61 | 62 | bonds.push( [ satom - 1, eatom - 1, 1 ] ); 63 | bhash[ h ] = bonds.length - 1; 64 | 65 | } else { 66 | 67 | // doesn't really work as almost all PDBs 68 | // have just normal bonds appearing multiple 69 | // times instead of being double/triple bonds 70 | // bonds[bhash[h]][2] += 1; 71 | 72 | } 73 | 74 | } 75 | 76 | } 77 | 78 | var CPK = {"h":[255,255,255],"he":[217,255,255],"li":[204,128,255],"be":[194,255,0],"b":[255,181,181],"c":[144,144,144],"n":[48,80,248],"o":[255,13,13],"f":[144,224,80],"ne":[179,227,245],"na":[171,92,242],"mg":[138,255,0],"al":[191,166,166],"si":[240,200,160],"p":[255,128,0],"s":[255,255,48],"cl":[31,240,31],"ar":[128,209,227],"k":[143,64,212],"ca":[61,255,0],"sc":[230,230,230],"ti":[191,194,199],"v":[166,166,171],"cr":[138,153,199],"mn":[156,122,199],"fe":[224,102,51],"co":[240,144,160],"ni":[80,208,80],"cu":[200,128,51],"zn":[125,128,176],"ga":[194,143,143],"ge":[102,143,143],"as":[189,128,227],"se":[255,161,0],"br":[166,41,41],"kr":[92,184,209],"rb":[112,46,176],"sr":[0,255,0],"y":[148,255,255],"zr":[148,224,224],"nb":[115,194,201],"mo":[84,181,181],"tc":[59,158,158],"ru":[36,143,143],"rh":[10,125,140],"pd":[0,105,133],"ag":[192,192,192],"cd":[255,217,143],"in":[166,117,115],"sn":[102,128,128],"sb":[158,99,181],"te":[212,122,0],"i":[148,0,148],"xe":[66,158,176],"cs":[87,23,143],"ba":[0,201,0],"la":[112,212,255],"ce":[255,255,199],"pr":[217,255,199],"nd":[199,255,199],"pm":[163,255,199],"sm":[143,255,199],"eu":[97,255,199],"gd":[69,255,199],"tb":[48,255,199],"dy":[31,255,199],"ho":[0,255,156],"er":[0,230,117],"tm":[0,212,82],"yb":[0,191,56],"lu":[0,171,36],"hf":[77,194,255],"ta":[77,166,255],"w":[33,148,214],"re":[38,125,171],"os":[38,102,150],"ir":[23,84,135],"pt":[208,208,224],"au":[255,209,35],"hg":[184,184,208],"tl":[166,84,77],"pb":[87,89,97],"bi":[158,79,181],"po":[171,92,0],"at":[117,79,69],"rn":[66,130,150],"fr":[66,0,102],"ra":[0,125,0],"ac":[112,171,250],"th":[0,186,255],"pa":[0,161,255],"u":[0,143,255],"np":[0,128,255],"pu":[0,107,255],"am":[84,92,242],"cm":[120,92,227],"bk":[138,79,227],"cf":[161,54,212],"es":[179,31,212],"fm":[179,31,186],"md":[179,13,166],"no":[189,13,135],"lr":[199,0,102],"rf":[204,0,89],"db":[209,0,79],"sg":[217,0,69],"bh":[224,0,56],"hs":[230,0,46],"mt":[235,0,38], 79 | "ds":[235,0,38],"rg":[235,0,38],"cn":[235,0,38],"uut":[235,0,38],"uuq":[235,0,38],"uup":[235,0,38],"uuh":[235,0,38],"uus":[235,0,38],"uuo":[235,0,38]}; 80 | 81 | 82 | var atoms = []; 83 | var bonds = []; 84 | var histogram = {}; 85 | 86 | var bhash = {}; 87 | 88 | var lines = text.split( "\n" ); 89 | 90 | var x, y, z, e; 91 | 92 | for( var i = 0, il = lines.length; i < il; ++ i ) { 93 | 94 | if( lines[i].substr(0,4)=="ATOM" || lines[i].substr(0,6)=="HETATM" ) { 95 | 96 | x = parseFloat( lines[i].substr(30,7) ); 97 | y = parseFloat( lines[i].substr(38,7) ); 98 | z = parseFloat( lines[i].substr(46,7) ); 99 | 100 | e = trim( lines[i].substr(76,2) ).toLowerCase(); 101 | 102 | if ( e=="" ) e = trim(lines[i].substr(12,2)).toLowerCase(); 103 | atoms.push( [ x,y,z, CPK[e], capitalize(e) ] ); 104 | 105 | if (histogram[e]==undefined) histogram[e] = 1; 106 | else histogram[e] += 1; 107 | 108 | } else if(lines[i].substr(0,6)=="CONECT") { 109 | 110 | var satom = parseInt( lines[i].substr(6,5) ); 111 | 112 | parseBond(11,5); 113 | parseBond(16,5); 114 | parseBond(21,5); 115 | parseBond(26,5); 116 | 117 | } 118 | 119 | } 120 | 121 | return { "ok": true, "atoms": atoms, "bonds": bonds, "histogram": histogram }; 122 | 123 | }, 124 | 125 | createModel: function ( json, callback ) { 126 | 127 | var scope = this, 128 | geometryAtoms = new THREE.Geometry(), 129 | geometryBonds = new THREE.Geometry(); 130 | 131 | geometryAtoms.elements = []; 132 | 133 | var atoms = json.atoms; 134 | var bonds = json.bonds; 135 | 136 | for ( var i = 0; i < atoms.length; i ++ ) { 137 | 138 | var atom = atoms[ i ]; 139 | 140 | var x = atom[ 0 ]; 141 | var y = atom[ 1 ]; 142 | var z = atom[ 2 ]; 143 | 144 | var position = new THREE.Vector3( x, y, z ); 145 | geometryAtoms.vertices.push( position ); 146 | 147 | var r = atom[ 3 ][ 0 ] / 255; 148 | var g = atom[ 3 ][ 1 ] / 255; 149 | var b = atom[ 3 ][ 2 ] / 255; 150 | 151 | var color = new THREE.Color(); 152 | color.setRGB( r, g, b ); 153 | 154 | geometryAtoms.colors.push( color ); 155 | 156 | geometryAtoms.elements.push( atom[ 4 ] ); 157 | 158 | } 159 | 160 | for ( var i = 0; i < bonds.length; i ++ ) { 161 | 162 | var bond = bonds[ i ]; 163 | 164 | var start = bond[ 0 ]; 165 | var end = bond[ 1 ]; 166 | 167 | var vertex1 = geometryAtoms.vertices[ start ]; 168 | var vertex2 = geometryAtoms.vertices[ end ]; 169 | 170 | geometryBonds.vertices.push( vertex1.clone() ); 171 | geometryBonds.vertices.push( vertex2.clone() ); 172 | 173 | } 174 | 175 | callback( geometryAtoms, geometryBonds, json ); 176 | 177 | } 178 | 179 | } 180 | 181 | -------------------------------------------------------------------------------- /public/js/loaders/ctm/CTMLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Loader for CTM encoded models generated by OpenCTM tools: 3 | * http://openctm.sourceforge.net/ 4 | * 5 | * Uses js-openctm library by Juan Mellado 6 | * http://code.google.com/p/js-openctm/ 7 | * 8 | * @author alteredq / http://alteredqualia.com/ 9 | */ 10 | 11 | THREE.CTMLoader = function ( showStatus ) { 12 | 13 | THREE.Loader.call( this, showStatus ); 14 | 15 | }; 16 | 17 | THREE.CTMLoader.prototype = Object.create( THREE.Loader.prototype ); 18 | 19 | // Load multiple CTM parts defined in JSON 20 | 21 | THREE.CTMLoader.prototype.loadParts = function( url, callback, parameters ) { 22 | 23 | parameters = parameters || {}; 24 | 25 | var scope = this; 26 | 27 | var xhr = new XMLHttpRequest(); 28 | 29 | var basePath = parameters.basePath ? parameters.basePath : this.extractUrlBase( url ); 30 | 31 | xhr.onreadystatechange = function() { 32 | 33 | if ( xhr.readyState === 4 ) { 34 | 35 | if ( xhr.status === 200 || xhr.status === 0 ) { 36 | 37 | var jsonObject = JSON.parse( xhr.responseText ); 38 | 39 | var materials = [], geometries = [], counter = 0; 40 | 41 | function callbackFinal( geometry ) { 42 | 43 | counter += 1; 44 | 45 | geometries.push( geometry ); 46 | 47 | if ( counter === jsonObject.offsets.length ) { 48 | 49 | callback( geometries, materials ); 50 | 51 | } 52 | 53 | } 54 | 55 | 56 | // init materials 57 | 58 | for ( var i = 0; i < jsonObject.materials.length; i ++ ) { 59 | 60 | materials[ i ] = scope.createMaterial( jsonObject.materials[ i ], basePath ); 61 | 62 | } 63 | 64 | // load joined CTM file 65 | 66 | var partUrl = basePath + jsonObject.data; 67 | var parametersPart = { useWorker: parameters.useWorker, offsets: jsonObject.offsets }; 68 | scope.load( partUrl, callbackFinal, parametersPart ); 69 | 70 | } 71 | 72 | } 73 | 74 | } 75 | 76 | xhr.open( "GET", url, true ); 77 | xhr.setRequestHeader( "Content-Type", "text/plain" ); 78 | xhr.send( null ); 79 | 80 | }; 81 | 82 | // Load CTMLoader compressed models 83 | // - parameters 84 | // - url (required) 85 | // - callback (required) 86 | 87 | THREE.CTMLoader.prototype.load = function( url, callback, parameters ) { 88 | 89 | parameters = parameters || {}; 90 | 91 | var scope = this; 92 | 93 | var offsets = parameters.offsets !== undefined ? parameters.offsets : [ 0 ]; 94 | 95 | var xhr = new XMLHttpRequest(), 96 | callbackProgress = null; 97 | 98 | var length = 0; 99 | 100 | xhr.onreadystatechange = function() { 101 | 102 | if ( xhr.readyState === 4 ) { 103 | 104 | if ( xhr.status === 200 || xhr.status === 0 ) { 105 | 106 | var binaryData = new Uint8Array(xhr.response); 107 | 108 | var s = Date.now(); 109 | 110 | if ( parameters.useWorker ) { 111 | 112 | var worker = new Worker( "js/loaders/ctm/CTMWorker.js" ); 113 | 114 | worker.onmessage = function( event ) { 115 | 116 | var files = event.data; 117 | 118 | for ( var i = 0; i < files.length; i ++ ) { 119 | 120 | var ctmFile = files[ i ]; 121 | 122 | var e1 = Date.now(); 123 | // console.log( "CTM data parse time [worker]: " + (e1-s) + " ms" ); 124 | 125 | scope.createModel( ctmFile, callback ); 126 | 127 | var e = Date.now(); 128 | console.log( "model load time [worker]: " + (e-e1) + " ms, total: " + (e-s)); 129 | 130 | } 131 | 132 | 133 | }; 134 | 135 | worker.postMessage( { "data": binaryData, "offsets": offsets } ); 136 | 137 | } else { 138 | 139 | for ( var i = 0; i < offsets.length; i ++ ) { 140 | 141 | var stream = new CTM.Stream( binaryData ); 142 | stream.offset = offsets[ i ]; 143 | 144 | var ctmFile = new CTM.File( stream ); 145 | 146 | scope.createModel( ctmFile, callback ); 147 | 148 | } 149 | 150 | //var e = Date.now(); 151 | //console.log( "CTM data parse time [inline]: " + (e-s) + " ms" ); 152 | 153 | } 154 | 155 | } else { 156 | 157 | console.error( "Couldn't load [" + url + "] [" + xhr.status + "]" ); 158 | 159 | } 160 | 161 | } else if ( xhr.readyState === 3 ) { 162 | 163 | if ( callbackProgress ) { 164 | 165 | if ( length === 0 ) { 166 | 167 | length = xhr.getResponseHeader( "Content-Length" ); 168 | 169 | } 170 | 171 | callbackProgress( { total: length, loaded: xhr.responseText.length } ); 172 | 173 | } 174 | 175 | } else if ( xhr.readyState === 2 ) { 176 | 177 | length = xhr.getResponseHeader( "Content-Length" ); 178 | 179 | } 180 | 181 | } 182 | 183 | xhr.open( "GET", url, true ); 184 | xhr.responseType = "arraybuffer"; 185 | 186 | xhr.send( null ); 187 | 188 | }; 189 | 190 | 191 | THREE.CTMLoader.prototype.createModel = function ( file, callback ) { 192 | 193 | var Model = function () { 194 | 195 | THREE.BufferGeometry.call( this ); 196 | 197 | this.materials = []; 198 | 199 | var indices = file.body.indices, 200 | positions = file.body.vertices, 201 | normals = file.body.normals; 202 | 203 | var uvs, colors; 204 | 205 | var uvMaps = file.body.uvMaps; 206 | 207 | if ( uvMaps !== undefined && uvMaps.length > 0 ) { 208 | 209 | uvs = uvMaps[ 0 ].uv; 210 | 211 | } 212 | 213 | var attrMaps = file.body.attrMaps; 214 | 215 | if ( attrMaps !== undefined && attrMaps.length > 0 && attrMaps[ 0 ].name === 'Color' ) { 216 | 217 | colors = attrMaps[ 0 ].attr; 218 | 219 | } 220 | 221 | this.addAttribute( 'index', new THREE.BufferAttribute( indices, 1 ) ); 222 | this.addAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) ); 223 | 224 | if ( normals !== undefined ) { 225 | 226 | this.addAttribute( 'normal', new THREE.BufferAttribute( normals, 3 ) ); 227 | 228 | } 229 | 230 | if ( uvs !== undefined ) { 231 | 232 | this.addAttribute( 'uv', new THREE.BufferAttribute( uvs, 2 ) ); 233 | 234 | } 235 | 236 | if ( colors !== undefined ) { 237 | 238 | this.addAttribute( 'color', new THREE.BufferAttribute( colors, 4 ) ); 239 | 240 | } 241 | 242 | } 243 | 244 | Model.prototype = Object.create( THREE.BufferGeometry.prototype ); 245 | 246 | var geometry = new Model(); 247 | 248 | geometry.computeOffsets(); 249 | 250 | // compute vertex normals if not present in the CTM model 251 | if ( geometry.attributes[ "normal" ] === undefined ) { 252 | geometry.computeVertexNormals(); 253 | } 254 | 255 | callback( geometry ); 256 | 257 | }; 258 | -------------------------------------------------------------------------------- /public/js/loaders/gltf/glTFAnimation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tony Parisi / http://www.tonyparisi.com/ 3 | */ 4 | 5 | THREE.glTFAnimator = ( function () { 6 | 7 | var animators = []; 8 | 9 | return { 10 | add : function(animator) 11 | { 12 | animators.push(animator); 13 | }, 14 | 15 | remove: function(animator) 16 | { 17 | 18 | var i = animators.indexOf(animator); 19 | 20 | if ( i !== -1 ) { 21 | animators.splice( i, 1 ); 22 | } 23 | }, 24 | 25 | update : function() 26 | { 27 | for (i = 0; i < animators.length; i++) 28 | { 29 | animators[i].update(); 30 | } 31 | }, 32 | }; 33 | })(); 34 | 35 | // Construction/initialization 36 | THREE.glTFAnimation = function(interps) 37 | { 38 | this.running = false; 39 | this.loop = false; 40 | this.duration = 0; 41 | this.startTime = 0; 42 | this.interps = []; 43 | 44 | if (interps) 45 | { 46 | this.createInterpolators(interps); 47 | } 48 | } 49 | 50 | THREE.glTFAnimation.prototype.createInterpolators = function(interps) 51 | { 52 | var i, len = interps.length; 53 | for (i = 0; i < len; i++) 54 | { 55 | var interp = new THREE.glTFInterpolator(interps[i]); 56 | this.interps.push(interp); 57 | this.duration = Math.max(this.duration, interp.duration); 58 | } 59 | } 60 | 61 | // Start/stop 62 | THREE.glTFAnimation.prototype.play = function() 63 | { 64 | if (this.running) 65 | return; 66 | 67 | this.startTime = Date.now(); 68 | this.running = true; 69 | THREE.glTFAnimator.add(this); 70 | } 71 | 72 | THREE.glTFAnimation.prototype.stop = function() 73 | { 74 | this.running = false; 75 | THREE.glTFAnimator.remove(this); 76 | } 77 | 78 | // Update - drive key frame evaluation 79 | THREE.glTFAnimation.prototype.update = function() 80 | { 81 | if (!this.running) 82 | return; 83 | 84 | var now = Date.now(); 85 | var deltat = (now - this.startTime) / 1000; 86 | var t = deltat % this.duration; 87 | var nCycles = Math.floor(deltat / this.duration); 88 | 89 | if (nCycles >= 1 && !this.loop) 90 | { 91 | this.running = false; 92 | var i, len = this.interps.length; 93 | for (i = 0; i < len; i++) 94 | { 95 | this.interps[i].interp(this.duration); 96 | } 97 | this.stop(); 98 | return; 99 | } 100 | else 101 | { 102 | var i, len = this.interps.length; 103 | for (i = 0; i < len; i++) 104 | { 105 | this.interps[i].interp(t); 106 | } 107 | } 108 | } 109 | 110 | //Interpolator class 111 | //Construction/initialization 112 | THREE.glTFInterpolator = function(param) 113 | { 114 | this.keys = param.keys; 115 | this.values = param.values; 116 | this.count = param.count; 117 | this.type = param.type; 118 | this.path = param.path; 119 | this.isRot = false; 120 | 121 | var node = param.target; 122 | node.updateMatrix(); 123 | node.matrixAutoUpdate = true; 124 | this.targetNode = node; 125 | 126 | switch (param.path) { 127 | case "translation" : 128 | this.target = node.position; 129 | this.originalValue = node.position.clone(); 130 | break; 131 | case "rotation" : 132 | this.target = node.quaternion; 133 | this.originalValue = node.quaternion.clone(); 134 | this.isRot = true; 135 | break; 136 | case "scale" : 137 | this.target = node.scale; 138 | this.originalValue = node.scale.clone(); 139 | break; 140 | } 141 | 142 | this.duration = this.keys[this.count - 1]; 143 | 144 | this.vec1 = new THREE.Vector3; 145 | this.vec2 = new THREE.Vector3; 146 | this.vec3 = new THREE.Vector3; 147 | this.quat1 = new THREE.Quaternion; 148 | this.quat2 = new THREE.Quaternion; 149 | this.quat3 = new THREE.Quaternion; 150 | } 151 | 152 | //Interpolation and tweening methods 153 | THREE.glTFInterpolator.prototype.interp = function(t) 154 | { 155 | var i, j; 156 | if (t == this.keys[0]) 157 | { 158 | if (this.isRot) { 159 | this.quat3.set(this.values[0], this.values[1], this.values[2], this.values[3]); 160 | } 161 | else { 162 | this.vec3.set(this.values[0], this.values[1], this.values[2]); 163 | } 164 | } 165 | else if (t < this.keys[0]) 166 | { 167 | if (this.isRot) { 168 | this.quat1.set(this.originalValue.x, 169 | this.originalValue.y, 170 | this.originalValue.z, 171 | this.originalValue.w); 172 | this.quat2.set(this.values[0], 173 | this.values[1], 174 | this.values[2], 175 | this.values[3]); 176 | THREE.Quaternion.slerp(this.quat1, this.quat2, this.quat3, t / this.keys[0]); 177 | } 178 | else { 179 | this.vec3.set(this.originalValue.x, 180 | this.originalValue.y, 181 | this.originalValue.z); 182 | this.vec2.set(this.values[0], 183 | this.values[1], 184 | this.values[2]); 185 | 186 | this.vec3.lerp(this.vec2, t / this.keys[0]); 187 | } 188 | } 189 | else if (t >= this.keys[this.count - 1]) 190 | { 191 | if (this.isRot) { 192 | this.quat3.set(this.values[(this.count - 1) * 4], 193 | this.values[(this.count - 1) * 4 + 1], 194 | this.values[(this.count - 1) * 4 + 2], 195 | this.values[(this.count - 1) * 4 + 3]); 196 | } 197 | else { 198 | this.vec3.set(this.values[(this.count - 1) * 3], 199 | this.values[(this.count - 1) * 3 + 1], 200 | this.values[(this.count - 1) * 3 + 2]); 201 | } 202 | } 203 | else 204 | { 205 | for (i = 0; i < this.count - 1; i++) 206 | { 207 | var key1 = this.keys[i]; 208 | var key2 = this.keys[i + 1]; 209 | 210 | if (t >= key1 && t <= key2) 211 | { 212 | if (this.isRot) { 213 | this.quat1.set(this.values[i * 4], 214 | this.values[i * 4 + 1], 215 | this.values[i * 4 + 2], 216 | this.values[i * 4 + 3]); 217 | this.quat2.set(this.values[(i + 1) * 4], 218 | this.values[(i + 1) * 4 + 1], 219 | this.values[(i + 1) * 4 + 2], 220 | this.values[(i + 1) * 4 + 3]); 221 | THREE.Quaternion.slerp(this.quat1, this.quat2, this.quat3, (t - key1) / (key2 - key1)); 222 | } 223 | else { 224 | this.vec3.set(this.values[i * 3], 225 | this.values[i * 3 + 1], 226 | this.values[i * 3 + 2]); 227 | this.vec2.set(this.values[(i + 1) * 3], 228 | this.values[(i + 1) * 3 + 1], 229 | this.values[(i + 1) * 3 + 2]); 230 | 231 | this.vec3.lerp(this.vec2, (t - key1) / (key2 - key1)); 232 | } 233 | } 234 | } 235 | } 236 | 237 | if (this.target) 238 | { 239 | this.copyValue(this.target); 240 | } 241 | } 242 | 243 | THREE.glTFInterpolator.prototype.copyValue = function(target) { 244 | 245 | if (this.isRot) { 246 | target.copy(this.quat3); 247 | } 248 | else { 249 | target.copy(this.vec3); 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /public/js/app.js: -------------------------------------------------------------------------------- 1 | if ( ! Detector.webgl ) { 2 | Detector.addGetWebGLMessage(); 3 | document.getElementById( 'container' ).innerHTML = ""; 4 | } 5 | var container, stats, effect, oculusControl; 6 | var camera, scene, renderer; 7 | var mesh; 8 | var worldWidth = 128, worldDepth = 128, 9 | worldHalfWidth = worldWidth / 2, worldHalfDepth = worldDepth / 2, 10 | data = generateHeight( worldWidth, worldDepth ); 11 | var clock = new THREE.Clock(); 12 | 13 | init(); 14 | animate(); 15 | 16 | function init() { 17 | container = document.getElementById( 'container' ); 18 | 19 | camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 20000 ); 20 | camera.position.y = getY( worldHalfWidth, worldHalfDepth ) * 100 + 100; 21 | 22 | oculusControl = new THREE.DK2Controls( camera ); 23 | 24 | scene = new THREE.Scene(); 25 | // sides 26 | var matrix = new THREE.Matrix4(); 27 | var pxGeometry = new THREE.PlaneBufferGeometry( 100, 100 ); 28 | pxGeometry.attributes.uv.array[ 1 ] = 0.5; 29 | pxGeometry.attributes.uv.array[ 3 ] = 0.5; 30 | pxGeometry.rotateY( Math.PI / 2 ); 31 | pxGeometry.translate( 50, 0, 0 ); 32 | 33 | var nxGeometry = new THREE.PlaneBufferGeometry( 100, 100 ); 34 | nxGeometry.attributes.uv.array[ 1 ] = 0.5; 35 | nxGeometry.attributes.uv.array[ 3 ] = 0.5; 36 | nxGeometry.rotateY( - Math.PI / 2 ); 37 | nxGeometry.translate( - 50, 0, 0 ); 38 | 39 | var pyGeometry = new THREE.PlaneBufferGeometry( 100, 100 ); 40 | pyGeometry.attributes.uv.array[ 5 ] = 0.5; 41 | pyGeometry.attributes.uv.array[ 7 ] = 0.5; 42 | pyGeometry.rotateX( - Math.PI / 2 ); 43 | pyGeometry.translate( 0, 50, 0 ); 44 | 45 | var pzGeometry = new THREE.PlaneBufferGeometry( 100, 100 ); 46 | pzGeometry.attributes.uv.array[ 1 ] = 0.5; 47 | pzGeometry.attributes.uv.array[ 3 ] = 0.5; 48 | pzGeometry.translate( 0, 0, 50 ); 49 | 50 | var nzGeometry = new THREE.PlaneBufferGeometry( 100, 100 ); 51 | nzGeometry.attributes.uv.array[ 1 ] = 0.5; 52 | nzGeometry.attributes.uv.array[ 3 ] = 0.5; 53 | nzGeometry.rotateY( Math.PI ); 54 | nzGeometry.translate( 0, 0, -50 ); 55 | // 56 | // BufferGeometry cannot be merged yet. 57 | var tmpGeometry = new THREE.Geometry(); 58 | var pxTmpGeometry = new THREE.Geometry().fromBufferGeometry( pxGeometry ); 59 | var nxTmpGeometry = new THREE.Geometry().fromBufferGeometry( nxGeometry ); 60 | var pyTmpGeometry = new THREE.Geometry().fromBufferGeometry( pyGeometry ); 61 | var pzTmpGeometry = new THREE.Geometry().fromBufferGeometry( pzGeometry ); 62 | var nzTmpGeometry = new THREE.Geometry().fromBufferGeometry( nzGeometry ); 63 | for ( var z = 0; z < worldDepth; z ++ ) { 64 | for ( var x = 0; x < worldWidth; x ++ ) { 65 | var h = getY( x, z ); 66 | matrix.makeTranslation( 67 | x * 100 - worldHalfWidth * 100, 68 | h * 100, 69 | z * 100 - worldHalfDepth * 100 70 | ); 71 | var px = getY( x + 1, z ); 72 | var nx = getY( x - 1, z ); 73 | var pz = getY( x, z + 1 ); 74 | var nz = getY( x, z - 1 ); 75 | tmpGeometry.merge( pyTmpGeometry, matrix ); 76 | if ( ( px !== h && px !== h + 1 ) || x === 0 ) { 77 | tmpGeometry.merge( pxTmpGeometry, matrix ); 78 | } 79 | if ( ( nx !== h && nx !== h + 1 ) || x === worldWidth - 1 ) { 80 | tmpGeometry.merge( nxTmpGeometry, matrix ); 81 | } 82 | if ( ( pz !== h && pz !== h + 1 ) || z === worldDepth - 1 ) { 83 | tmpGeometry.merge( pzTmpGeometry, matrix ); 84 | } 85 | if ( ( nz !== h && nz !== h + 1 ) || z === 0 ) { 86 | tmpGeometry.merge( nzTmpGeometry, matrix ); 87 | } 88 | } 89 | } 90 | 91 | var geometry = new THREE.BufferGeometry().fromGeometry( tmpGeometry ); 92 | geometry.computeBoundingSphere(); 93 | 94 | var texture = THREE.ImageUtils.loadTexture( './js/textures/minecraft/atlas.png' ); 95 | texture.magFilter = THREE.NearestFilter; 96 | texture.minFilter = THREE.LinearMipMapLinearFilter; 97 | 98 | var mesh = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { map: texture } ) ); 99 | scene.add( mesh ); 100 | var ambientLight = new THREE.AmbientLight( 0xcccccc ); 101 | scene.add( ambientLight ); 102 | var directionalLight = new THREE.DirectionalLight( 0xffffff, 2 ); 103 | directionalLight.position.set( 1, 1, 0.5 ).normalize(); 104 | scene.add( directionalLight ); 105 | 106 | renderer = new THREE.WebGLRenderer(); 107 | renderer.setClearColor( 0xbfd1e5 ); 108 | renderer.setPixelRatio( window.devicePixelRatio ); 109 | renderer.setSize( window.innerWidth, window.innerHeight ); 110 | 111 | effect = new THREE.OculusRiftEffect(renderer, { worldScale: 100 }); 112 | effect.setSize(window.innerWidth, window.innerHeight); 113 | 114 | container.innerHTML = ""; 115 | container.appendChild( renderer.domElement ); 116 | stats = new Stats(); 117 | stats.domElement.style.position = 'absolute'; 118 | stats.domElement.style.top = '0px'; 119 | container.appendChild( stats.domElement ); 120 | // 121 | window.addEventListener( 'resize', onWindowResize, false ); 122 | 123 | } 124 | 125 | function onWindowResize() { 126 | camera.aspect = window.innerWidth / window.innerHeight; 127 | camera.updateProjectionMatrix(); 128 | 129 | effect.setSize(window.innerWidth, window.innerHeight); 130 | } 131 | 132 | function generateHeight( width, height ) { 133 | var data = [], perlin = new ImprovedNoise(), 134 | size = width * height, quality = 2, z = Math.random() * 100; 135 | for ( var j = 0; j < 4; j ++ ) { 136 | if ( j === 0 ) for ( var i = 0; i < size; i ++ ) data[ i ] = 0; 137 | for ( var i = 0; i < size; i ++ ) { 138 | var x = i % width, y = ( i / width ) | 0; 139 | data[ i ] += perlin.noise( x / quality, y / quality, z ) * quality; 140 | } 141 | quality *= 4; 142 | } 143 | return data; 144 | } 145 | function getY( x, z ) { 146 | return ( data[ x + z * worldWidth ] * 0.2 ) | 0; 147 | } 148 | // 149 | function animate() { 150 | requestAnimationFrame( animate ); 151 | render(); 152 | stats.update(); 153 | } 154 | function render() { 155 | oculusControl.update( clock.getDelta() ); 156 | THREE.AnimationHandler.update( clock.getDelta() * 100 ); 157 | 158 | camera.useQuaternion = true; 159 | camera.matrixWorldNeedsUpdate = true; 160 | 161 | effect.render(scene, camera); 162 | } -------------------------------------------------------------------------------- /public/js/loaders/collada/KeyFrameAnimation.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mikael emtinger / http://gomo.se/ 3 | * @author mrdoob / http://mrdoob.com/ 4 | * @author alteredq / http://alteredqualia.com/ 5 | * @author khang duong 6 | * @author erik kitson 7 | */ 8 | 9 | THREE.KeyFrameAnimation = function ( data ) { 10 | 11 | this.root = data.node; 12 | this.data = THREE.AnimationHandler.init( data ); 13 | this.hierarchy = THREE.AnimationHandler.parse( this.root ); 14 | this.currentTime = 0; 15 | this.timeScale = 0.001; 16 | this.isPlaying = false; 17 | this.isPaused = true; 18 | this.loop = true; 19 | 20 | // initialize to first keyframes 21 | 22 | for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { 23 | 24 | var keys = this.data.hierarchy[ h ].keys, 25 | sids = this.data.hierarchy[ h ].sids, 26 | obj = this.hierarchy[ h ]; 27 | 28 | if ( keys.length && sids ) { 29 | 30 | for ( var s = 0; s < sids.length; s ++ ) { 31 | 32 | var sid = sids[ s ], 33 | next = this.getNextKeyWith( sid, h, 0 ); 34 | 35 | if ( next ) { 36 | 37 | next.apply( sid ); 38 | 39 | } 40 | 41 | } 42 | 43 | obj.matrixAutoUpdate = false; 44 | this.data.hierarchy[ h ].node.updateMatrix(); 45 | obj.matrixWorldNeedsUpdate = true; 46 | 47 | } 48 | 49 | } 50 | 51 | }; 52 | 53 | THREE.KeyFrameAnimation.prototype = { 54 | 55 | constructor: THREE.KeyFrameAnimation, 56 | 57 | play: function ( startTime ) { 58 | 59 | this.currentTime = startTime !== undefined ? startTime : 0; 60 | 61 | if ( this.isPlaying === false ) { 62 | 63 | this.isPlaying = true; 64 | 65 | // reset key cache 66 | 67 | var h, hl = this.hierarchy.length, 68 | object, 69 | node; 70 | 71 | for ( h = 0; h < hl; h ++ ) { 72 | 73 | object = this.hierarchy[ h ]; 74 | node = this.data.hierarchy[ h ]; 75 | 76 | if ( node.animationCache === undefined ) { 77 | 78 | node.animationCache = {}; 79 | node.animationCache.prevKey = null; 80 | node.animationCache.nextKey = null; 81 | node.animationCache.originalMatrix = object.matrix; 82 | 83 | } 84 | 85 | var keys = this.data.hierarchy[ h ].keys; 86 | 87 | if ( keys.length ) { 88 | 89 | node.animationCache.prevKey = keys[ 0 ]; 90 | node.animationCache.nextKey = keys[ 1 ]; 91 | 92 | this.startTime = Math.min( keys[ 0 ].time, this.startTime ); 93 | this.endTime = Math.max( keys[ keys.length - 1 ].time, this.endTime ); 94 | 95 | } 96 | 97 | } 98 | 99 | this.update( 0 ); 100 | 101 | } 102 | 103 | this.isPaused = false; 104 | 105 | THREE.AnimationHandler.play( this ); 106 | 107 | }, 108 | 109 | stop: function () { 110 | 111 | this.isPlaying = false; 112 | this.isPaused = false; 113 | 114 | THREE.AnimationHandler.stop( this ); 115 | 116 | // reset JIT matrix and remove cache 117 | 118 | for ( var h = 0; h < this.data.hierarchy.length; h ++ ) { 119 | 120 | var obj = this.hierarchy[ h ]; 121 | var node = this.data.hierarchy[ h ]; 122 | 123 | if ( node.animationCache !== undefined ) { 124 | 125 | var original = node.animationCache.originalMatrix; 126 | 127 | original.copy( obj.matrix ); 128 | obj.matrix = original; 129 | 130 | delete node.animationCache; 131 | 132 | } 133 | 134 | } 135 | 136 | }, 137 | 138 | update: function ( delta ) { 139 | 140 | if ( this.isPlaying === false ) return; 141 | 142 | this.currentTime += delta * this.timeScale; 143 | 144 | // 145 | 146 | var duration = this.data.length; 147 | 148 | if ( this.loop === true && this.currentTime > duration ) { 149 | 150 | this.currentTime %= duration; 151 | 152 | } 153 | 154 | this.currentTime = Math.min( this.currentTime, duration ); 155 | 156 | for ( var h = 0, hl = this.hierarchy.length; h < hl; h ++ ) { 157 | 158 | var object = this.hierarchy[ h ]; 159 | var node = this.data.hierarchy[ h ]; 160 | 161 | var keys = node.keys, 162 | animationCache = node.animationCache; 163 | 164 | 165 | if ( keys.length ) { 166 | 167 | var prevKey = animationCache.prevKey; 168 | var nextKey = animationCache.nextKey; 169 | 170 | if ( nextKey.time <= this.currentTime ) { 171 | 172 | while ( nextKey.time < this.currentTime && nextKey.index > prevKey.index ) { 173 | 174 | prevKey = nextKey; 175 | nextKey = keys[ prevKey.index + 1 ]; 176 | 177 | } 178 | 179 | animationCache.prevKey = prevKey; 180 | animationCache.nextKey = nextKey; 181 | 182 | } 183 | 184 | if ( nextKey.time >= this.currentTime ) { 185 | 186 | prevKey.interpolate( nextKey, this.currentTime ); 187 | 188 | } else { 189 | 190 | prevKey.interpolate( nextKey, nextKey.time ); 191 | 192 | } 193 | 194 | this.data.hierarchy[ h ].node.updateMatrix(); 195 | object.matrixWorldNeedsUpdate = true; 196 | 197 | } 198 | 199 | } 200 | 201 | }, 202 | 203 | getNextKeyWith: function ( sid, h, key ) { 204 | 205 | var keys = this.data.hierarchy[ h ].keys; 206 | key = key % keys.length; 207 | 208 | for ( ; key < keys.length; key ++ ) { 209 | 210 | if ( keys[ key ].hasTarget( sid ) ) { 211 | 212 | return keys[ key ]; 213 | 214 | } 215 | 216 | } 217 | 218 | return keys[ 0 ]; 219 | 220 | }, 221 | 222 | getPrevKeyWith: function ( sid, h, key ) { 223 | 224 | var keys = this.data.hierarchy[ h ].keys; 225 | key = key >= 0 ? key : key + keys.length; 226 | 227 | for ( ; key >= 0; key -- ) { 228 | 229 | if ( keys[ key ].hasTarget( sid ) ) { 230 | 231 | return keys[ key ]; 232 | 233 | } 234 | 235 | } 236 | 237 | return keys[ keys.length - 1 ]; 238 | 239 | } 240 | 241 | }; -------------------------------------------------------------------------------- /public/js/controls/EditorControls.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.EditorControls = function ( object, domElement ) { 9 | 10 | domElement = ( domElement !== undefined ) ? domElement : document; 11 | 12 | // API 13 | 14 | this.enabled = true; 15 | 16 | // internals 17 | 18 | var scope = this; 19 | var vector = new THREE.Vector3(); 20 | 21 | var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2 }; 22 | var state = STATE.NONE; 23 | 24 | var center = new THREE.Vector3(); 25 | var normalMatrix = new THREE.Matrix3(); 26 | 27 | // events 28 | 29 | var changeEvent = { type: 'change' }; 30 | 31 | this.focus = function ( target ) { 32 | 33 | center.getPositionFromMatrix( target.matrixWorld ); 34 | object.lookAt( center ); 35 | 36 | scope.dispatchEvent( changeEvent ); 37 | 38 | }; 39 | 40 | this.pan = function ( distance ) { 41 | 42 | normalMatrix.getNormalMatrix( object.matrix ); 43 | 44 | distance.applyMatrix3( normalMatrix ); 45 | distance.multiplyScalar( vector.copy( center ).sub( object.position ).length() * 0.001 ); 46 | 47 | object.position.add( distance ); 48 | center.add( distance ); 49 | 50 | scope.dispatchEvent( changeEvent ); 51 | 52 | }; 53 | 54 | this.zoom = function ( distance ) { 55 | 56 | normalMatrix.getNormalMatrix( object.matrix ); 57 | 58 | distance.applyMatrix3( normalMatrix ); 59 | distance.multiplyScalar( vector.copy( center ).sub( object.position ).length() * 0.001 ); 60 | 61 | object.position.add( distance ); 62 | 63 | scope.dispatchEvent( changeEvent ); 64 | 65 | }; 66 | 67 | this.rotate = function ( delta ) { 68 | 69 | vector.copy( object.position ).sub( center ); 70 | 71 | var theta = Math.atan2( vector.x, vector.z ); 72 | var phi = Math.atan2( Math.sqrt( vector.x * vector.x + vector.z * vector.z ), vector.y ); 73 | 74 | theta += delta.x; 75 | phi += delta.y; 76 | 77 | var EPS = 0.000001; 78 | 79 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 80 | 81 | var radius = vector.length(); 82 | 83 | vector.x = radius * Math.sin( phi ) * Math.sin( theta ); 84 | vector.y = radius * Math.cos( phi ); 85 | vector.z = radius * Math.sin( phi ) * Math.cos( theta ); 86 | 87 | object.position.copy( center ).add( vector ); 88 | 89 | object.lookAt( center ); 90 | 91 | scope.dispatchEvent( changeEvent ); 92 | 93 | }; 94 | 95 | // mouse 96 | 97 | function onMouseDown( event ) { 98 | 99 | if ( scope.enabled === false ) return; 100 | 101 | event.preventDefault(); 102 | 103 | if ( event.button === 0 ) { 104 | 105 | state = STATE.ROTATE; 106 | 107 | } else if ( event.button === 1 ) { 108 | 109 | state = STATE.ZOOM; 110 | 111 | } else if ( event.button === 2 ) { 112 | 113 | state = STATE.PAN; 114 | 115 | } 116 | 117 | document.addEventListener( 'mousemove', onMouseMove, false ); 118 | document.addEventListener( 'mouseup', onMouseUp, false ); 119 | 120 | } 121 | 122 | function onMouseMove( event ) { 123 | 124 | if ( scope.enabled === false ) return; 125 | 126 | event.preventDefault(); 127 | 128 | var movementX = event.movementX || event.webkitMovementX || event.mozMovementX || event.oMovementX || 0; 129 | var movementY = event.movementY || event.webkitMovementY || event.mozMovementY || event.oMovementY || 0; 130 | 131 | if ( state === STATE.ROTATE ) { 132 | 133 | scope.rotate( new THREE.Vector3( - movementX * 0.005, - movementY * 0.005, 0 ) ); 134 | 135 | } else if ( state === STATE.ZOOM ) { 136 | 137 | scope.zoom( new THREE.Vector3( 0, 0, movementY ) ); 138 | 139 | } else if ( state === STATE.PAN ) { 140 | 141 | scope.pan( new THREE.Vector3( - movementX, movementY, 0 ) ); 142 | 143 | } 144 | 145 | } 146 | 147 | function onMouseUp( event ) { 148 | 149 | if ( scope.enabled === false ) return; 150 | 151 | document.removeEventListener( 'mousemove', onMouseMove, false ); 152 | document.removeEventListener( 'mouseup', onMouseUp, false ); 153 | 154 | state = STATE.NONE; 155 | 156 | } 157 | 158 | function onMouseWheel( event ) { 159 | 160 | if ( scope.enabled === false ) return; 161 | 162 | var delta = 0; 163 | 164 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 165 | 166 | delta = - event.wheelDelta; 167 | 168 | } else if ( event.detail ) { // Firefox 169 | 170 | delta = event.detail * 10; 171 | 172 | } 173 | 174 | scope.zoom( new THREE.Vector3( 0, 0, delta ) ); 175 | 176 | } 177 | 178 | domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 179 | domElement.addEventListener( 'mousedown', onMouseDown, false ); 180 | domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 181 | domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 182 | 183 | // touch 184 | 185 | var touch = new THREE.Vector3(); 186 | var prevTouch = new THREE.Vector3(); 187 | var prevDistance = null; 188 | 189 | function touchStart( event ) { 190 | 191 | if ( scope.enabled === false ) return; 192 | 193 | var touches = event.touches; 194 | 195 | switch ( touches.length ) { 196 | 197 | case 2: 198 | var dx = touches[ 0 ].pageX - touches[ 1 ].pageX; 199 | var dy = touches[ 0 ].pageY - touches[ 1 ].pageY; 200 | prevDistance = Math.sqrt( dx * dx + dy * dy ); 201 | break; 202 | 203 | } 204 | 205 | prevTouch.set( touches[ 0 ].pageX, touches[ 0 ].pageY, 0 ); 206 | 207 | } 208 | 209 | function touchMove( event ) { 210 | 211 | if ( scope.enabled === false ) return; 212 | 213 | event.preventDefault(); 214 | event.stopPropagation(); 215 | 216 | var touches = event.touches; 217 | 218 | touch.set( touches[ 0 ].pageX, touches[ 0 ].pageY, 0 ); 219 | 220 | switch ( touches.length ) { 221 | 222 | case 1: 223 | scope.rotate( touch.sub( prevTouch ).multiplyScalar( - 0.005 ) ); 224 | break; 225 | 226 | case 2: 227 | var dx = touches[ 0 ].pageX - touches[ 1 ].pageX; 228 | var dy = touches[ 0 ].pageY - touches[ 1 ].pageY; 229 | var distance = Math.sqrt( dx * dx + dy * dy ); 230 | scope.zoom( new THREE.Vector3( 0, 0, prevDistance - distance ) ); 231 | prevDistance = distance; 232 | break; 233 | 234 | case 3: 235 | scope.pan( touch.sub( prevTouch ).setX( - touch.x ) ); 236 | break; 237 | 238 | } 239 | 240 | prevTouch.set( touches[ 0 ].pageX, touches[ 0 ].pageY, 0 ); 241 | 242 | } 243 | 244 | domElement.addEventListener( 'touchstart', touchStart, false ); 245 | domElement.addEventListener( 'touchmove', touchMove, false ); 246 | 247 | }; 248 | 249 | THREE.EditorControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 250 | -------------------------------------------------------------------------------- /public/js/controls/FirstPersonControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | * @author alteredq / http://alteredqualia.com/ 4 | * @author paulirish / http://paulirish.com/ 5 | */ 6 | 7 | THREE.FirstPersonControls = function ( object, domElement ) { 8 | 9 | this.object = object; 10 | this.target = new THREE.Vector3( 0, 0, 0 ); 11 | 12 | this.domElement = ( domElement !== undefined ) ? domElement : document; 13 | 14 | this.movementSpeed = 1.0; 15 | this.lookSpeed = 0.005; 16 | 17 | this.lookVertical = true; 18 | this.autoForward = false; 19 | // this.invertVertical = false; 20 | 21 | this.activeLook = true; 22 | 23 | this.heightSpeed = false; 24 | this.heightCoef = 1.0; 25 | this.heightMin = 0.0; 26 | this.heightMax = 1.0; 27 | 28 | this.constrainVertical = false; 29 | this.verticalMin = 0; 30 | this.verticalMax = Math.PI; 31 | 32 | this.autoSpeedFactor = 0.0; 33 | 34 | this.mouseX = 0; 35 | this.mouseY = 0; 36 | 37 | this.lat = 0; 38 | this.lon = 0; 39 | this.phi = 0; 40 | this.theta = 0; 41 | 42 | this.moveForward = false; 43 | this.moveBackward = false; 44 | this.moveLeft = false; 45 | this.moveRight = false; 46 | this.freeze = false; 47 | 48 | this.mouseDragOn = false; 49 | 50 | this.viewHalfX = 0; 51 | this.viewHalfY = 0; 52 | 53 | if ( this.domElement !== document ) { 54 | 55 | this.domElement.setAttribute( 'tabindex', -1 ); 56 | 57 | } 58 | 59 | // 60 | 61 | this.handleResize = function () { 62 | 63 | if ( this.domElement === document ) { 64 | 65 | this.viewHalfX = window.innerWidth / 2; 66 | this.viewHalfY = window.innerHeight / 2; 67 | 68 | } else { 69 | 70 | this.viewHalfX = this.domElement.offsetWidth / 2; 71 | this.viewHalfY = this.domElement.offsetHeight / 2; 72 | 73 | } 74 | 75 | }; 76 | 77 | this.onMouseDown = function ( event ) { 78 | 79 | if ( this.domElement !== document ) { 80 | 81 | this.domElement.focus(); 82 | 83 | } 84 | 85 | event.preventDefault(); 86 | event.stopPropagation(); 87 | 88 | if ( this.activeLook ) { 89 | 90 | switch ( event.button ) { 91 | 92 | case 0: this.moveForward = true; break; 93 | case 2: this.moveBackward = true; break; 94 | 95 | } 96 | 97 | } 98 | 99 | this.mouseDragOn = true; 100 | 101 | }; 102 | 103 | this.onMouseUp = function ( event ) { 104 | 105 | event.preventDefault(); 106 | event.stopPropagation(); 107 | 108 | if ( this.activeLook ) { 109 | 110 | switch ( event.button ) { 111 | 112 | case 0: this.moveForward = false; break; 113 | case 2: this.moveBackward = false; break; 114 | 115 | } 116 | 117 | } 118 | 119 | this.mouseDragOn = false; 120 | 121 | }; 122 | 123 | this.onMouseMove = function ( event ) { 124 | 125 | if ( this.domElement === document ) { 126 | 127 | this.mouseX = event.pageX - this.viewHalfX; 128 | this.mouseY = event.pageY - this.viewHalfY; 129 | 130 | } else { 131 | 132 | this.mouseX = event.pageX - this.domElement.offsetLeft - this.viewHalfX; 133 | this.mouseY = event.pageY - this.domElement.offsetTop - this.viewHalfY; 134 | 135 | } 136 | 137 | }; 138 | 139 | this.onKeyDown = function ( event ) { 140 | 141 | //event.preventDefault(); 142 | 143 | switch ( event.keyCode ) { 144 | 145 | case 38: /*up*/ 146 | case 87: /*W*/ this.moveForward = true; break; 147 | 148 | case 37: /*left*/ 149 | case 65: /*A*/ this.moveLeft = true; break; 150 | 151 | case 40: /*down*/ 152 | case 83: /*S*/ this.moveBackward = true; break; 153 | 154 | case 39: /*right*/ 155 | case 68: /*D*/ this.moveRight = true; break; 156 | 157 | case 82: /*R*/ this.moveUp = true; break; 158 | case 70: /*F*/ this.moveDown = true; break; 159 | 160 | case 81: /*Q*/ this.freeze = !this.freeze; break; 161 | 162 | } 163 | 164 | }; 165 | 166 | this.onKeyUp = function ( event ) { 167 | 168 | switch( event.keyCode ) { 169 | 170 | case 38: /*up*/ 171 | case 87: /*W*/ this.moveForward = false; break; 172 | 173 | case 37: /*left*/ 174 | case 65: /*A*/ this.moveLeft = false; break; 175 | 176 | case 40: /*down*/ 177 | case 83: /*S*/ this.moveBackward = false; break; 178 | 179 | case 39: /*right*/ 180 | case 68: /*D*/ this.moveRight = false; break; 181 | 182 | case 82: /*R*/ this.moveUp = false; break; 183 | case 70: /*F*/ this.moveDown = false; break; 184 | 185 | } 186 | 187 | }; 188 | 189 | this.update = function( delta ) { 190 | 191 | if ( this.freeze ) { 192 | 193 | return; 194 | 195 | } 196 | 197 | if ( this.heightSpeed ) { 198 | 199 | var y = THREE.Math.clamp( this.object.position.y, this.heightMin, this.heightMax ); 200 | var heightDelta = y - this.heightMin; 201 | 202 | this.autoSpeedFactor = delta * ( heightDelta * this.heightCoef ); 203 | 204 | } else { 205 | 206 | this.autoSpeedFactor = 0.0; 207 | 208 | } 209 | 210 | var actualMoveSpeed = delta * this.movementSpeed; 211 | 212 | if ( this.moveForward || ( this.autoForward && !this.moveBackward ) ) this.object.translateZ( - ( actualMoveSpeed + this.autoSpeedFactor ) ); 213 | if ( this.moveBackward ) this.object.translateZ( actualMoveSpeed ); 214 | 215 | if ( this.moveLeft ) this.object.translateX( - actualMoveSpeed ); 216 | if ( this.moveRight ) this.object.translateX( actualMoveSpeed ); 217 | 218 | if ( this.moveUp ) this.object.translateY( actualMoveSpeed ); 219 | if ( this.moveDown ) this.object.translateY( - actualMoveSpeed ); 220 | 221 | var actualLookSpeed = delta * this.lookSpeed; 222 | 223 | if ( !this.activeLook ) { 224 | 225 | actualLookSpeed = 0; 226 | 227 | } 228 | 229 | var verticalLookRatio = 1; 230 | 231 | if ( this.constrainVertical ) { 232 | 233 | verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin ); 234 | 235 | } 236 | 237 | this.lon += this.mouseX * actualLookSpeed; 238 | if( this.lookVertical ) this.lat -= this.mouseY * actualLookSpeed * verticalLookRatio; 239 | 240 | this.lat = Math.max( - 85, Math.min( 85, this.lat ) ); 241 | this.phi = THREE.Math.degToRad( 90 - this.lat ); 242 | 243 | this.theta = THREE.Math.degToRad( this.lon ); 244 | 245 | if ( this.constrainVertical ) { 246 | 247 | this.phi = THREE.Math.mapLinear( this.phi, 0, Math.PI, this.verticalMin, this.verticalMax ); 248 | 249 | } 250 | 251 | var targetPosition = this.target, 252 | position = this.object.position; 253 | 254 | targetPosition.x = position.x + 100 * Math.sin( this.phi ) * Math.cos( this.theta ); 255 | targetPosition.y = position.y + 100 * Math.cos( this.phi ); 256 | targetPosition.z = position.z + 100 * Math.sin( this.phi ) * Math.sin( this.theta ); 257 | 258 | this.object.lookAt( targetPosition ); 259 | 260 | }; 261 | 262 | 263 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 264 | 265 | this.domElement.addEventListener( 'mousemove', bind( this, this.onMouseMove ), false ); 266 | this.domElement.addEventListener( 'mousedown', bind( this, this.onMouseDown ), false ); 267 | this.domElement.addEventListener( 'mouseup', bind( this, this.onMouseUp ), false ); 268 | this.domElement.addEventListener( 'keydown', bind( this, this.onKeyDown ), false ); 269 | this.domElement.addEventListener( 'keyup', bind( this, this.onKeyUp ), false ); 270 | 271 | function bind( scope, fn ) { 272 | 273 | return function () { 274 | 275 | fn.apply( scope, arguments ); 276 | 277 | }; 278 | 279 | }; 280 | 281 | this.handleResize(); 282 | 283 | }; 284 | -------------------------------------------------------------------------------- /public/js/effects/AsciiEffect.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @author zz85 / https://github.com/zz85 3 | * 4 | * Ascii generation is based on http://www.nihilogic.dk/labs/jsascii/ 5 | * Maybe more about this later with a blog post at http://lab4games.net/zz85/blog 6 | * 7 | * 16 April 2012 - @blurspline 8 | */ 9 | 10 | THREE.AsciiEffect = function ( renderer, charSet, options ) { 11 | 12 | // its fun to create one your own! 13 | 14 | charSet = ( charSet === undefined ) ? ' .:-=+*#%@' : charSet; 15 | 16 | // ' .,:;=|iI+hHOE#`$'; 17 | // darker bolder character set from https://github.com/saw/Canvas-ASCII-Art/ 18 | // ' .\'`^",:;Il!i~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$'.split(''); 19 | 20 | if ( !options ) options = {}; 21 | 22 | // Some ASCII settings 23 | 24 | var bResolution = !options['resolution'] ? 0.15 : options['resolution']; // Higher for more details 25 | var iScale = !options['scale'] ? 1 : options['scale']; 26 | var bColor = !options['color'] ? false : options['color']; // nice but slows down rendering! 27 | var bAlpha = !options['alpha'] ? false : options['alpha']; // Transparency 28 | var bBlock = !options['block'] ? false : options['block']; // blocked characters. like good O dos 29 | var bInvert = !options['invert'] ? false : options['invert']; // black is white, white is black 30 | 31 | var strResolution = 'low'; 32 | 33 | var width, height; 34 | 35 | var domElement = document.createElement('div'); 36 | domElement.style.cursor = 'default'; 37 | 38 | var oAscii = document.createElement("table"); 39 | domElement.appendChild( oAscii ); 40 | 41 | var iWidth, iHeight; 42 | var oImg; 43 | 44 | this.setSize = function ( w, h ) { 45 | 46 | width = w; 47 | height = h; 48 | 49 | renderer.setSize( w, h ); 50 | 51 | initAsciiSize(); 52 | 53 | }; 54 | 55 | 56 | this.render = function ( scene, camera ) { 57 | 58 | renderer.render( scene, camera ); 59 | asciifyImage( renderer, oAscii ); 60 | 61 | }; 62 | 63 | this.domElement = domElement; 64 | 65 | 66 | // Throw in ascii library from http://www.nihilogic.dk/labs/jsascii/jsascii.js 67 | 68 | /* 69 | * jsAscii 0.1 70 | * Copyright (c) 2008 Jacob Seidelin, jseidelin@nihilogic.dk, http://blog.nihilogic.dk/ 71 | * MIT License [http://www.nihilogic.dk/licenses/mit-license.txt] 72 | */ 73 | 74 | function initAsciiSize() { 75 | 76 | iWidth = Math.round( width * fResolution ); 77 | iHeight = Math.round( height * fResolution ); 78 | 79 | oCanvas.width = iWidth; 80 | oCanvas.height = iHeight; 81 | // oCanvas.style.display = "none"; 82 | // oCanvas.style.width = iWidth; 83 | // oCanvas.style.height = iHeight; 84 | 85 | oImg = renderer.domElement; 86 | 87 | if ( oImg.style.backgroundColor ) { 88 | 89 | oAscii.rows[0].cells[0].style.backgroundColor = oImg.style.backgroundColor; 90 | oAscii.rows[0].cells[0].style.color = oImg.style.color; 91 | 92 | } 93 | 94 | oAscii.cellSpacing = 0; 95 | oAscii.cellPadding = 0; 96 | 97 | var oStyle = oAscii.style; 98 | oStyle.display = "inline"; 99 | oStyle.width = Math.round(iWidth/fResolution*iScale) + "px"; 100 | oStyle.height = Math.round(iHeight/fResolution*iScale) + "px"; 101 | oStyle.whiteSpace = "pre"; 102 | oStyle.margin = "0px"; 103 | oStyle.padding = "0px"; 104 | oStyle.letterSpacing = fLetterSpacing + "px"; 105 | oStyle.fontFamily = strFont; 106 | oStyle.fontSize = fFontSize + "px"; 107 | oStyle.lineHeight = fLineHeight + "px"; 108 | oStyle.textAlign = "left"; 109 | oStyle.textDecoration = "none"; 110 | } 111 | 112 | 113 | var aDefaultCharList = (" .,:;i1tfLCG08@").split(""); 114 | var aDefaultColorCharList = (" CGO08@").split(""); 115 | var strFont = "courier new, monospace"; 116 | 117 | var oCanvasImg = renderer.domElement; 118 | 119 | var oCanvas = document.createElement("canvas"); 120 | if (!oCanvas.getContext) { 121 | return; 122 | } 123 | 124 | var oCtx = oCanvas.getContext("2d"); 125 | if (!oCtx.getImageData) { 126 | return; 127 | } 128 | 129 | var aCharList = (bColor ? aDefaultColorCharList : aDefaultCharList); 130 | 131 | if (charSet) aCharList = charSet; 132 | 133 | var fResolution = 0.5; 134 | 135 | switch ( strResolution ) { 136 | 137 | case "low" : fResolution = 0.25; break; 138 | case "medium" : fResolution = 0.5; break; 139 | case "high" : fResolution = 1; break; 140 | 141 | } 142 | 143 | if ( bResolution ) fResolution = bResolution; 144 | 145 | // Setup dom 146 | 147 | var fFontSize = (2/fResolution)*iScale; 148 | var fLineHeight = (2/fResolution)*iScale; 149 | 150 | // adjust letter-spacing for all combinations of scale and resolution to get it to fit the image width. 151 | 152 | var fLetterSpacing = 0; 153 | 154 | if ( strResolution == "low" ) { 155 | 156 | switch (iScale) { 157 | case 1 : fLetterSpacing = -1; break; 158 | case 2 : 159 | case 3 : fLetterSpacing = -2.1; break; 160 | case 4 : fLetterSpacing = -3.1; break; 161 | case 5 : fLetterSpacing = -4.15; break; 162 | } 163 | 164 | } 165 | 166 | if ( strResolution == "medium" ) { 167 | 168 | switch (iScale) { 169 | case 1 : fLetterSpacing = 0; break; 170 | case 2 : fLetterSpacing = -1; break; 171 | case 3 : fLetterSpacing = -1.04; break; 172 | case 4 : 173 | case 5 : fLetterSpacing = -2.1; break; 174 | } 175 | 176 | } 177 | 178 | if ( strResolution == "high" ) { 179 | 180 | switch (iScale) { 181 | case 1 : 182 | case 2 : fLetterSpacing = 0; break; 183 | case 3 : 184 | case 4 : 185 | case 5 : fLetterSpacing = -1; break; 186 | } 187 | 188 | } 189 | 190 | 191 | // can't get a span or div to flow like an img element, but a table works? 192 | 193 | 194 | // convert img element to ascii 195 | 196 | function asciifyImage( canvasRenderer, oAscii ) { 197 | 198 | oCtx.clearRect( 0, 0, iWidth, iHeight ); 199 | oCtx.drawImage( oCanvasImg, 0, 0, iWidth, iHeight ); 200 | var oImgData = oCtx.getImageData(0, 0, iWidth, iHeight).data; 201 | 202 | // Coloring loop starts now 203 | var strChars = ""; 204 | 205 | // console.time('rendering'); 206 | 207 | for (var y=0;y" + strThisChar + ""; 250 | } else { 251 | strChars += strThisChar; 252 | } 253 | } 254 | strChars += "
"; 255 | } 256 | 257 | oAscii.innerHTML = "" + strChars + ""; 258 | 259 | // console.timeEnd('rendering'); 260 | 261 | // return oAscii; 262 | } 263 | 264 | // end modified asciifyImage block 265 | 266 | }; 267 | -------------------------------------------------------------------------------- /public/js/controls/FlyControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author James Baicoianu / http://www.baicoianu.com/ 3 | */ 4 | 5 | THREE.FlyControls = function ( object, domElement ) { 6 | 7 | this.object = object; 8 | 9 | this.domElement = ( domElement !== undefined ) ? domElement : document; 10 | if ( domElement ) this.domElement.setAttribute( 'tabindex', -1 ); 11 | 12 | // API 13 | 14 | this.movementSpeed = 1.0; 15 | this.rollSpeed = 0.005; 16 | 17 | this.dragToLook = false; 18 | this.autoForward = false; 19 | 20 | // disable default target object behavior 21 | 22 | this.object.useQuaternion = true; 23 | 24 | // internals 25 | 26 | this.tmpQuaternion = new THREE.Quaternion(); 27 | 28 | this.mouseStatus = 0; 29 | 30 | this.moveState = { up: 0, down: 0, left: 0, right: 0, forward: 0, back: 0, pitchUp: 0, pitchDown: 0, yawLeft: 0, yawRight: 0, rollLeft: 0, rollRight: 0 }; 31 | this.moveVector = new THREE.Vector3( 0, 0, 0 ); 32 | this.rotationVector = new THREE.Vector3( 0, 0, 0 ); 33 | 34 | this.handleEvent = function ( event ) { 35 | 36 | if ( typeof this[ event.type ] == 'function' ) { 37 | 38 | this[ event.type ]( event ); 39 | 40 | } 41 | 42 | }; 43 | 44 | this.keydown = function( event ) { 45 | 46 | if ( event.altKey ) { 47 | 48 | return; 49 | 50 | } 51 | 52 | //event.preventDefault(); 53 | 54 | switch ( event.keyCode ) { 55 | 56 | case 16: /* shift */ this.movementSpeedMultiplier = .1; break; 57 | 58 | case 87: /*W*/ this.moveState.forward = 1; break; 59 | case 83: /*S*/ this.moveState.back = 1; break; 60 | 61 | case 65: /*A*/ this.moveState.left = 1; break; 62 | case 68: /*D*/ this.moveState.right = 1; break; 63 | 64 | case 82: /*R*/ this.moveState.up = 1; break; 65 | case 70: /*F*/ this.moveState.down = 1; break; 66 | 67 | case 38: /*up*/ this.moveState.pitchUp = 1; break; 68 | case 40: /*down*/ this.moveState.pitchDown = 1; break; 69 | 70 | case 37: /*left*/ this.moveState.yawLeft = 1; break; 71 | case 39: /*right*/ this.moveState.yawRight = 1; break; 72 | 73 | case 81: /*Q*/ this.moveState.rollLeft = 1; break; 74 | case 69: /*E*/ this.moveState.rollRight = 1; break; 75 | 76 | } 77 | 78 | this.updateMovementVector(); 79 | this.updateRotationVector(); 80 | 81 | }; 82 | 83 | this.keyup = function( event ) { 84 | 85 | switch( event.keyCode ) { 86 | 87 | case 16: /* shift */ this.movementSpeedMultiplier = 1; break; 88 | 89 | case 87: /*W*/ this.moveState.forward = 0; break; 90 | case 83: /*S*/ this.moveState.back = 0; break; 91 | 92 | case 65: /*A*/ this.moveState.left = 0; break; 93 | case 68: /*D*/ this.moveState.right = 0; break; 94 | 95 | case 82: /*R*/ this.moveState.up = 0; break; 96 | case 70: /*F*/ this.moveState.down = 0; break; 97 | 98 | case 38: /*up*/ this.moveState.pitchUp = 0; break; 99 | case 40: /*down*/ this.moveState.pitchDown = 0; break; 100 | 101 | case 37: /*left*/ this.moveState.yawLeft = 0; break; 102 | case 39: /*right*/ this.moveState.yawRight = 0; break; 103 | 104 | case 81: /*Q*/ this.moveState.rollLeft = 0; break; 105 | case 69: /*E*/ this.moveState.rollRight = 0; break; 106 | 107 | } 108 | 109 | this.updateMovementVector(); 110 | this.updateRotationVector(); 111 | 112 | }; 113 | 114 | this.mousedown = function( event ) { 115 | 116 | if ( this.domElement !== document ) { 117 | 118 | this.domElement.focus(); 119 | 120 | } 121 | 122 | event.preventDefault(); 123 | event.stopPropagation(); 124 | 125 | if ( this.dragToLook ) { 126 | 127 | this.mouseStatus ++; 128 | 129 | } else { 130 | 131 | switch ( event.button ) { 132 | 133 | case 0: this.moveState.forward = 1; break; 134 | case 2: this.moveState.back = 1; break; 135 | 136 | } 137 | 138 | this.updateMovementVector(); 139 | 140 | } 141 | 142 | }; 143 | 144 | this.mousemove = function( event ) { 145 | 146 | if ( !this.dragToLook || this.mouseStatus > 0 ) { 147 | 148 | var container = this.getContainerDimensions(); 149 | var halfWidth = container.size[ 0 ] / 2; 150 | var halfHeight = container.size[ 1 ] / 2; 151 | 152 | this.moveState.yawLeft = - ( ( event.pageX - container.offset[ 0 ] ) - halfWidth ) / halfWidth; 153 | this.moveState.pitchDown = ( ( event.pageY - container.offset[ 1 ] ) - halfHeight ) / halfHeight; 154 | 155 | this.updateRotationVector(); 156 | 157 | } 158 | 159 | }; 160 | 161 | this.mouseup = function( event ) { 162 | 163 | event.preventDefault(); 164 | event.stopPropagation(); 165 | 166 | if ( this.dragToLook ) { 167 | 168 | this.mouseStatus --; 169 | 170 | this.moveState.yawLeft = this.moveState.pitchDown = 0; 171 | 172 | } else { 173 | 174 | switch ( event.button ) { 175 | 176 | case 0: this.moveState.forward = 0; break; 177 | case 2: this.moveState.back = 0; break; 178 | 179 | } 180 | 181 | this.updateMovementVector(); 182 | 183 | } 184 | 185 | this.updateRotationVector(); 186 | 187 | }; 188 | 189 | this.update = function( delta ) { 190 | 191 | var moveMult = delta * this.movementSpeed; 192 | var rotMult = delta * this.rollSpeed; 193 | 194 | this.object.translateX( this.moveVector.x * moveMult ); 195 | this.object.translateY( this.moveVector.y * moveMult ); 196 | this.object.translateZ( this.moveVector.z * moveMult ); 197 | 198 | this.tmpQuaternion.set( this.rotationVector.x * rotMult, this.rotationVector.y * rotMult, this.rotationVector.z * rotMult, 1 ).normalize(); 199 | this.object.quaternion.multiply( this.tmpQuaternion ); 200 | 201 | // expose the rotation vector for convenience 202 | this.object.rotation.setEulerFromQuaternion( this.object.quaternion, this.object.eulerOrder ); 203 | 204 | 205 | }; 206 | 207 | this.updateMovementVector = function() { 208 | 209 | var forward = ( this.moveState.forward || ( this.autoForward && !this.moveState.back ) ) ? 1 : 0; 210 | 211 | this.moveVector.x = ( -this.moveState.left + this.moveState.right ); 212 | this.moveVector.y = ( -this.moveState.down + this.moveState.up ); 213 | this.moveVector.z = ( -forward + this.moveState.back ); 214 | 215 | //console.log( 'move:', [ this.moveVector.x, this.moveVector.y, this.moveVector.z ] ); 216 | 217 | }; 218 | 219 | this.updateRotationVector = function() { 220 | 221 | this.rotationVector.x = ( -this.moveState.pitchDown + this.moveState.pitchUp ); 222 | this.rotationVector.y = ( -this.moveState.yawRight + this.moveState.yawLeft ); 223 | this.rotationVector.z = ( -this.moveState.rollRight + this.moveState.rollLeft ); 224 | 225 | //console.log( 'rotate:', [ this.rotationVector.x, this.rotationVector.y, this.rotationVector.z ] ); 226 | 227 | }; 228 | 229 | this.getContainerDimensions = function() { 230 | 231 | if ( this.domElement != document ) { 232 | 233 | return { 234 | size : [ this.domElement.offsetWidth, this.domElement.offsetHeight ], 235 | offset : [ this.domElement.offsetLeft, this.domElement.offsetTop ] 236 | }; 237 | 238 | } else { 239 | 240 | return { 241 | size : [ window.innerWidth, window.innerHeight ], 242 | offset : [ 0, 0 ] 243 | }; 244 | 245 | } 246 | 247 | }; 248 | 249 | function bind( scope, fn ) { 250 | 251 | return function () { 252 | 253 | fn.apply( scope, arguments ); 254 | 255 | }; 256 | 257 | }; 258 | 259 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 260 | 261 | this.domElement.addEventListener( 'mousemove', bind( this, this.mousemove ), false ); 262 | this.domElement.addEventListener( 'mousedown', bind( this, this.mousedown ), false ); 263 | this.domElement.addEventListener( 'mouseup', bind( this, this.mouseup ), false ); 264 | 265 | this.domElement.addEventListener( 'keydown', bind( this, this.keydown ), false ); 266 | this.domElement.addEventListener( 'keyup', bind( this, this.keyup ), false ); 267 | 268 | this.updateMovementVector(); 269 | this.updateRotationVector(); 270 | 271 | }; 272 | -------------------------------------------------------------------------------- /public/js/effects/VREffect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author dmarcos / https://github.com/dmarcos 3 | * 4 | * It handles stereo rendering 5 | * If mozGetVRDevices and getVRDevices APIs are not available it gracefuly falls back to a 6 | * regular renderer 7 | * 8 | * The HMD supported is the Oculus DK1 and The Web API doesn't currently allow 9 | * to query for the display resolution (only the chrome API allows it). 10 | * The dimensions of the screen are temporarly hardcoded (1280 x 800). 11 | * 12 | * For VR mode to work it has to be used with the Oculus enabled builds of Firefox or Chrome: 13 | * 14 | * Firefox: 15 | * 16 | * OSX: http://people.mozilla.com/~vladimir/vr/firefox-33.0a1.en-US.mac.dmg 17 | * WIN: http://people.mozilla.com/~vladimir/vr/firefox-33.0a1.en-US.win64-x86_64.zip 18 | * 19 | * Chrome builds: 20 | * 21 | * https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list 22 | * 23 | */ 24 | THREE.VREffect = function ( renderer, done ) { 25 | this._renderer = renderer; 26 | 27 | this._init = function() { 28 | var self = this; 29 | if ( !navigator.mozGetVRDevices && !navigator.getVRDevices ) { 30 | if ( done ) { 31 | done("Your browser is not VR Ready"); 32 | } 33 | return; 34 | } 35 | if ( navigator.getVRDevices ) { 36 | navigator.getVRDevices().then( gotVRDevices ); 37 | } else { 38 | navigator.mozGetVRDevices( gotVRDevices ); 39 | } 40 | function gotVRDevices( devices ) { 41 | var vrHMD; 42 | var error; 43 | for ( var i = 0; i < devices.length; ++i ) { 44 | if ( devices[i] instanceof HMDVRDevice ) { 45 | vrHMD = devices[i]; 46 | self._vrHMD = vrHMD; 47 | self.leftEyeTranslation = vrHMD.getEyeTranslation( "left" ); 48 | self.rightEyeTranslation = vrHMD.getEyeTranslation( "right" ); 49 | self.leftEyeFOV = vrHMD.getRecommendedEyeFieldOfView( "left" ); 50 | self.rightEyeFOV = vrHMD.getRecommendedEyeFieldOfView( "right" ); 51 | break; // We keep the first we encounter 52 | } 53 | } 54 | if ( done ) { 55 | if ( !vrHMD ) { 56 | error = 'HMD not available'; 57 | } 58 | done( error ); 59 | } 60 | } 61 | }; 62 | 63 | this._init(); 64 | 65 | this.render = function ( scene, camera ) { 66 | var renderer = this._renderer; 67 | var vrHMD = this._vrHMD; 68 | renderer.enableScissorTest( false ); 69 | // VR render mode if HMD is available 70 | if ( vrHMD ) { 71 | this.renderStereo.apply( this, arguments ); 72 | return; 73 | } 74 | // Regular render mode if not HMD 75 | renderer.render.apply( this._renderer , arguments ); 76 | }; 77 | 78 | this.renderStereo = function( scene, camera, renderTarget, forceClear ) { 79 | var cameraLeft; 80 | var cameraRight; 81 | var leftEyeTranslation = this.leftEyeTranslation; 82 | var rightEyeTranslation = this.rightEyeTranslation; 83 | var renderer = this._renderer; 84 | var rendererWidth = renderer.domElement.width / renderer.devicePixelRatio; 85 | var rendererHeight = renderer.domElement.height / renderer.devicePixelRatio; 86 | var eyeDivisionLine = rendererWidth / 2; 87 | renderer.enableScissorTest( true ); 88 | renderer.clear(); 89 | 90 | // Grab camera matrix from user. 91 | // This is interpreted as the head base. 92 | if ( camera.matrixAutoUpdate ) { 93 | camera.updateMatrix(); 94 | } 95 | var eyeWorldMatrix = camera.matrixWorld.clone(); 96 | 97 | cameraLeft = camera.clone(); 98 | cameraRight = camera.clone(); 99 | cameraLeft.projectionMatrix = this.FovToProjection( this.leftEyeFOV ); 100 | cameraRight.projectionMatrix = this.FovToProjection( this.rightEyeFOV ); 101 | cameraLeft.position.add(new THREE.Vector3( 102 | leftEyeTranslation.x, leftEyeTranslation.y, leftEyeTranslation.z) ); 103 | cameraRight.position.add(new THREE.Vector3( 104 | rightEyeTranslation.x, rightEyeTranslation.y, rightEyeTranslation.z) ); 105 | 106 | // render left eye 107 | renderer.setViewport( 0, 0, eyeDivisionLine, rendererHeight ); 108 | renderer.setScissor( 0, 0, eyeDivisionLine, rendererHeight ); 109 | renderer.render( scene, cameraLeft ); 110 | 111 | // render right eye 112 | renderer.setViewport( eyeDivisionLine, 0, eyeDivisionLine, rendererHeight ); 113 | renderer.setScissor( eyeDivisionLine, 0, eyeDivisionLine, rendererHeight ); 114 | renderer.render( scene, cameraRight ); 115 | 116 | }; 117 | 118 | this.setFullScreen = function( enable ) { 119 | var renderer = this._renderer; 120 | var vrHMD = this._vrHMD; 121 | var canvasOriginalSize = this._canvasOriginalSize; 122 | if (!vrHMD) { 123 | return; 124 | } 125 | // If state doesn't change we do nothing 126 | if ( enable === this._fullScreen ) { 127 | return; 128 | } 129 | this._fullScreen = !!enable; 130 | 131 | // VR Mode disabled 132 | if ( !enable ) { 133 | // Restores canvas original size 134 | renderer.setSize( canvasOriginalSize.width, canvasOriginalSize.height ); 135 | return; 136 | } 137 | // VR Mode enabled 138 | this._canvasOriginalSize = { 139 | width: renderer.domElement.width, 140 | height: renderer.domElement.height 141 | }; 142 | // Hardcoded Rift display size 143 | renderer.setSize( 1280, 800, false ); 144 | this.startFullscreen(); 145 | }; 146 | 147 | this.startFullscreen = function() { 148 | var self = this; 149 | var renderer = this._renderer; 150 | var vrHMD = this._vrHMD; 151 | var canvas = renderer.domElement; 152 | var fullScreenChange = 153 | canvas.mozRequestFullScreen? 'mozfullscreenchange' : 'webkitfullscreenchange'; 154 | 155 | document.addEventListener( fullScreenChange, onFullScreenChanged, false ); 156 | function onFullScreenChanged() { 157 | if ( !document.mozFullScreenElement && !document.webkitFullScreenElement ) { 158 | self.setFullScreen( false ); 159 | } 160 | } 161 | if ( canvas.mozRequestFullScreen ) { 162 | canvas.mozRequestFullScreen( { vrDisplay: vrHMD } ); 163 | } else { 164 | canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } ); 165 | } 166 | }; 167 | 168 | this.FovToNDCScaleOffset = function( fov ) { 169 | var pxscale = 2.0 / (fov.leftTan + fov.rightTan); 170 | var pxoffset = (fov.leftTan - fov.rightTan) * pxscale * 0.5; 171 | var pyscale = 2.0 / (fov.upTan + fov.downTan); 172 | var pyoffset = (fov.upTan - fov.downTan) * pyscale * 0.5; 173 | return { scale: [pxscale, pyscale], offset: [pxoffset, pyoffset] }; 174 | }; 175 | 176 | this.FovPortToProjection = function( fov, rightHanded /* = true */, zNear /* = 0.01 */, zFar /* = 10000.0 */ ) 177 | { 178 | rightHanded = rightHanded === undefined ? true : rightHanded; 179 | zNear = zNear === undefined ? 0.01 : zNear; 180 | zFar = zFar === undefined ? 10000.0 : zFar; 181 | 182 | var handednessScale = rightHanded ? -1.0 : 1.0; 183 | 184 | // start with an identity matrix 185 | var mobj = new THREE.Matrix4(); 186 | var m = mobj.elements; 187 | 188 | // and with scale/offset info for normalized device coords 189 | var scaleAndOffset = this.FovToNDCScaleOffset(fov); 190 | 191 | // X result, map clip edges to [-w,+w] 192 | m[0*4+0] = scaleAndOffset.scale[0]; 193 | m[0*4+1] = 0.0; 194 | m[0*4+2] = scaleAndOffset.offset[0] * handednessScale; 195 | m[0*4+3] = 0.0; 196 | 197 | // Y result, map clip edges to [-w,+w] 198 | // Y offset is negated because this proj matrix transforms from world coords with Y=up, 199 | // but the NDC scaling has Y=down (thanks D3D?) 200 | m[1*4+0] = 0.0; 201 | m[1*4+1] = scaleAndOffset.scale[1]; 202 | m[1*4+2] = -scaleAndOffset.offset[1] * handednessScale; 203 | m[1*4+3] = 0.0; 204 | 205 | // Z result (up to the app) 206 | m[2*4+0] = 0.0; 207 | m[2*4+1] = 0.0; 208 | m[2*4+2] = zFar / (zNear - zFar) * -handednessScale; 209 | m[2*4+3] = (zFar * zNear) / (zNear - zFar); 210 | 211 | // W result (= Z in) 212 | m[3*4+0] = 0.0; 213 | m[3*4+1] = 0.0; 214 | m[3*4+2] = handednessScale; 215 | m[3*4+3] = 0.0; 216 | 217 | mobj.transpose(); 218 | 219 | return mobj; 220 | }; 221 | 222 | this.FovToProjection = function( fov, rightHanded /* = true */, zNear /* = 0.01 */, zFar /* = 10000.0 */ ) 223 | { 224 | var fovPort = { 225 | upTan: Math.tan(fov.upDegrees * Math.PI / 180.0), 226 | downTan: Math.tan(fov.downDegrees * Math.PI / 180.0), 227 | leftTan: Math.tan(fov.leftDegrees * Math.PI / 180.0), 228 | rightTan: Math.tan(fov.rightDegrees * Math.PI / 180.0) 229 | }; 230 | return this.FovPortToProjection(fovPort, rightHanded, zNear, zFar); 231 | }; 232 | 233 | }; -------------------------------------------------------------------------------- /public/js/loaders/gltf/glTFLoaderUtils.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Tony Parisi / http://www.tonyparisi.com/ 3 | */ 4 | 5 | THREE.GLTFLoaderUtils = Object.create(Object, { 6 | 7 | // errors 8 | MISSING_DESCRIPTION: { value: "MISSING_DESCRIPTION" }, 9 | INVALID_PATH: { value: "INVALID_PATH" }, 10 | INVALID_TYPE: { value: "INVALID_TYPE" }, 11 | XMLHTTPREQUEST_STATUS_ERROR: { value: "XMLHTTPREQUEST_STATUS_ERROR" }, 12 | NOT_FOUND: { value: "NOT_FOUND" }, 13 | // misc constants 14 | ARRAY_BUFFER: { value: "ArrayBuffer" }, 15 | 16 | _streams : { value:{}, writable: true }, 17 | 18 | _streamsStatus: { value: {}, writable: true }, 19 | 20 | _resources: { value: {}, writable: true }, 21 | 22 | _resourcesStatus: { value: {}, writable: true }, 23 | 24 | // initialization 25 | init: { 26 | value: function() { 27 | this._streams = {}; 28 | this._streamsStatus = {}; 29 | this._resources = {}; 30 | this._resourcesStatus = {}; 31 | } 32 | }, 33 | 34 | //manage entries 35 | _containsResource: { 36 | enumerable: false, 37 | value: function(resourceID) { 38 | return this._resources[resourceID] ? true : false; 39 | } 40 | }, 41 | 42 | _storeResource: { 43 | enumerable: false, 44 | value: function(resourceID, resource) { 45 | if (!resourceID) { 46 | console.log("ERROR: entry does not contain id, cannot store"); 47 | return; 48 | } 49 | 50 | if (this._containsResource[resourceID]) { 51 | console.log("WARNING: resource:"+resourceID+" is already stored, overriding"); 52 | } 53 | 54 | this._resources[resourceID] = resource; 55 | } 56 | }, 57 | 58 | _getResource: { 59 | enumerable: false, 60 | value: function(resourceID) { 61 | return this._resources[resourceID]; 62 | } 63 | }, 64 | 65 | _loadStream: { 66 | value: function(path, type, delegate) { 67 | var self = this; 68 | 69 | if (!type) { 70 | delegate.handleError(THREE.GLTFLoaderUtils.INVALID_TYPE, null); 71 | return; 72 | } 73 | 74 | if (!path) { 75 | delegate.handleError(THREE.GLTFLoaderUtils.INVALID_PATH); 76 | return; 77 | } 78 | 79 | var xhr = new XMLHttpRequest(); 80 | xhr.open('GET', path, true); 81 | xhr.responseType = (type === this.ARRAY_BUFFER) ? "arraybuffer" : "text"; 82 | 83 | //if this is not specified, 1 "big blob" scenes fails to load. 84 | xhr.setRequestHeader("If-Modified-Since", "Sat, 01 Jan 1970 00:00:00 GMT"); 85 | xhr.addEventListener( 'load', function ( event ) { 86 | delegate.streamAvailable(path, xhr.response); 87 | }, false ); 88 | xhr.addEventListener( 'error', function ( event ) { 89 | delegate.handleError(THREE.GLTFLoaderUtils.XMLHTTPREQUEST_STATUS_ERROR, xhr.status); 90 | }, false ); 91 | xhr.send(null); 92 | } 93 | }, 94 | 95 | send: { value: 0, writable: true }, 96 | requested: { value: 0, writable: true }, 97 | 98 | _handleRequest: { 99 | value: function(request) { 100 | var resourceStatus = this._resourcesStatus[request.id]; 101 | if (resourceStatus) 102 | { 103 | this._resourcesStatus[request.id]++; 104 | } 105 | else 106 | { 107 | this._resourcesStatus[request.id] = 1; 108 | } 109 | 110 | var streamStatus = this._streamsStatus[request.path]; 111 | if (streamStatus && streamStatus.status === "loading" ) 112 | { 113 | streamStatus.requests.push(request); 114 | return; 115 | } 116 | 117 | this._streamsStatus[request.path] = { status : "loading", requests : [request] }; 118 | 119 | var self = this; 120 | var processResourceDelegate = {}; 121 | 122 | processResourceDelegate.streamAvailable = function(path, res_) { 123 | var streamStatus = self._streamsStatus[path]; 124 | var requests = streamStatus.requests; 125 | requests.forEach( function(req_) { 126 | var subArray = res_.slice(req_.range[0], req_.range[1]); 127 | var convertedResource = req_.delegate.convert(subArray, req_.ctx); 128 | self._storeResource(req_.id, convertedResource); 129 | req_.delegate.resourceAvailable(convertedResource, req_.ctx); 130 | --self._resourcesStatus[req_.id]; 131 | 132 | }, this); 133 | 134 | delete self._streamsStatus[path]; 135 | 136 | }; 137 | 138 | processResourceDelegate.handleError = function(errorCode, info) { 139 | request.delegate.handleError(errorCode, info); 140 | } 141 | 142 | this._loadStream(request.path, request.type, processResourceDelegate); 143 | } 144 | }, 145 | 146 | 147 | _elementSizeForGLType: { 148 | value: function(glType) { 149 | switch (glType) { 150 | case WebGLRenderingContext.FLOAT : 151 | return Float32Array.BYTES_PER_ELEMENT; 152 | case WebGLRenderingContext.UNSIGNED_BYTE : 153 | return Uint8Array.BYTES_PER_ELEMENT; 154 | case WebGLRenderingContext.UNSIGNED_SHORT : 155 | return Uint16Array.BYTES_PER_ELEMENT; 156 | case WebGLRenderingContext.FLOAT_VEC2 : 157 | return Float32Array.BYTES_PER_ELEMENT * 2; 158 | case WebGLRenderingContext.FLOAT_VEC3 : 159 | return Float32Array.BYTES_PER_ELEMENT * 3; 160 | case WebGLRenderingContext.FLOAT_VEC4 : 161 | return Float32Array.BYTES_PER_ELEMENT * 4; 162 | case WebGLRenderingContext.FLOAT_MAT3 : 163 | return Float32Array.BYTES_PER_ELEMENT * 9; 164 | case WebGLRenderingContext.FLOAT_MAT4 : 165 | return Float32Array.BYTES_PER_ELEMENT * 16; 166 | default: 167 | return null; 168 | } 169 | } 170 | }, 171 | 172 | _handleWrappedBufferViewResourceLoading: { 173 | value: function(wrappedBufferView, delegate, ctx) { 174 | var bufferView = wrappedBufferView.bufferView; 175 | var buffer = bufferView.buffer; 176 | var byteOffset = wrappedBufferView.byteOffset + bufferView.description.byteOffset; 177 | var range = [byteOffset , (this._elementSizeForGLType(wrappedBufferView.type) * wrappedBufferView.count) + byteOffset]; 178 | 179 | this._handleRequest({ "id" : wrappedBufferView.id, 180 | "range" : range, 181 | "type" : buffer.description.type, 182 | "path" : buffer.description.path, 183 | "delegate" : delegate, 184 | "ctx" : ctx }, null); 185 | } 186 | }, 187 | 188 | getBuffer: { 189 | 190 | value: function(wrappedBufferView, delegate, ctx) { 191 | 192 | var savedBuffer = this._getResource(wrappedBufferView.id); 193 | if (savedBuffer) { 194 | return savedBuffer; 195 | } else { 196 | this._handleWrappedBufferViewResourceLoading(wrappedBufferView, delegate, ctx); 197 | } 198 | 199 | return null; 200 | } 201 | }, 202 | 203 | getFile: { 204 | 205 | value: function(request, delegate, ctx) { 206 | 207 | request.delegate = delegate; 208 | request.ctx = ctx; 209 | 210 | this._handleRequest({ "id" : request.id, 211 | "path" : request.path, 212 | "range" : [0], 213 | "type" : "text", 214 | "delegate" : delegate, 215 | "ctx" : ctx }, null); 216 | 217 | return null; 218 | } 219 | }, 220 | }); 221 | -------------------------------------------------------------------------------- /public/js/effects/OculusRiftEffect.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author troffmo5 / http://github.com/troffmo5 3 | * 4 | * Effect to render the scene in stereo 3d side by side with lens distortion. 5 | * It is written to be used with the Oculus Rift (http://www.oculusvr.com/) but 6 | * it works also with other HMD using the same technology 7 | */ 8 | 9 | THREE.OculusRiftEffect = function ( renderer, options ) { 10 | // worldFactor indicates how many units is 1 meter 11 | var worldFactor = (options && options.worldFactor) ? options.worldFactor: 1.0; 12 | 13 | // Specific HMD parameters 14 | var HMD = (options && options.HMD) ? options.HMD: { 15 | // Parameters from the Oculus Rift DK1 16 | hResolution: 1920, 17 | vResolution: 1080, 18 | hScreenSize: 0.12576, 19 | vScreenSize: 0.07074, 20 | interpupillaryDistance: 0.0635, 21 | lensSeparationDistance: 0.0635, 22 | eyeToScreenDistance: 0.041, 23 | distortionK : [1.0, 0.22, 0.24, 0.0], 24 | chromaAbParameter: [ 0.996, -0.004, 1.014, 0.0] 25 | }; 26 | 27 | // Perspective camera 28 | var pCamera = new THREE.PerspectiveCamera(); 29 | pCamera.matrixAutoUpdate = false; 30 | pCamera.target = new THREE.Vector3(); 31 | 32 | // Orthographic camera 33 | var oCamera = new THREE.OrthographicCamera( -1, 1, 1, -1, 1, 1000 ); 34 | oCamera.position.z = 1; 35 | 36 | // pre-render hooks 37 | this.preLeftRender = function() {}; 38 | this.preRightRender = function() {}; 39 | 40 | renderer.autoClear = false; 41 | var emptyColor = new THREE.Color("black"); 42 | 43 | // Render target 44 | var RTParams = { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBAFormat }; 45 | var renderTarget = new THREE.WebGLRenderTarget( 960, 1080, RTParams ); 46 | var RTMaterial = new THREE.ShaderMaterial( { 47 | uniforms: { 48 | "texid": { type: "t", value: renderTarget }, 49 | "scale": { type: "v2", value: new THREE.Vector2(1.0,1.0) }, 50 | "scaleIn": { type: "v2", value: new THREE.Vector2(1.0,1.0) }, 51 | "lensCenter": { type: "v2", value: new THREE.Vector2(0.0,0.0) }, 52 | "hmdWarpParam": { type: "v4", value: new THREE.Vector4(1.0,0.0,0.0,0.0) }, 53 | "chromAbParam": { type: "v4", value: new THREE.Vector4(1.0,0.0,0.0,0.0) } 54 | }, 55 | vertexShader: [ 56 | "varying vec2 vUv;", 57 | "void main() {", 58 | " vUv = uv;", 59 | " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", 60 | "}" 61 | ].join("\n"), 62 | 63 | fragmentShader: [ 64 | "uniform vec2 scale;", 65 | "uniform vec2 scaleIn;", 66 | "uniform vec2 lensCenter;", 67 | "uniform vec4 hmdWarpParam;", 68 | 'uniform vec4 chromAbParam;', 69 | "uniform sampler2D texid;", 70 | "varying vec2 vUv;", 71 | "void main()", 72 | "{", 73 | " vec2 uv = (vUv*2.0)-1.0;", // range from [0,1] to [-1,1] 74 | " vec2 theta = (uv-lensCenter)*scaleIn;", 75 | " float rSq = theta.x*theta.x + theta.y*theta.y;", 76 | " vec2 rvector = theta*(hmdWarpParam.x + hmdWarpParam.y*rSq + hmdWarpParam.z*rSq*rSq + hmdWarpParam.w*rSq*rSq*rSq);", 77 | ' vec2 rBlue = rvector * (chromAbParam.z + chromAbParam.w * rSq);', 78 | " vec2 tcBlue = (lensCenter + scale * rBlue);", 79 | " tcBlue = (tcBlue+1.0)/2.0;", // range from [-1,1] to [0,1] 80 | " if (any(bvec2(clamp(tcBlue, vec2(0.0,0.0), vec2(1.0,1.0))-tcBlue))) {", 81 | " gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);", 82 | " return;}", 83 | " vec2 tcGreen = lensCenter + scale * rvector;", 84 | " tcGreen = (tcGreen+1.0)/2.0;", // range from [-1,1] to [0,1] 85 | " vec2 rRed = rvector * (chromAbParam.x + chromAbParam.y * rSq);", 86 | " vec2 tcRed = lensCenter + scale * rRed;", 87 | " tcRed = (tcRed+1.0)/2.0;", // range from [-1,1] to [0,1] 88 | " gl_FragColor = vec4(texture2D(texid, tcRed).r, texture2D(texid, tcGreen).g, texture2D(texid, tcBlue).b, 1);", 89 | "}" 90 | ].join("\n") 91 | } ); 92 | 93 | var mesh = new THREE.Mesh( new THREE.PlaneGeometry( 2, 2 ), RTMaterial ); 94 | 95 | // Final scene 96 | var finalScene = new THREE.Scene(); 97 | finalScene.add( oCamera ); 98 | finalScene.add( mesh ); 99 | 100 | var left = {}, right = {}; 101 | var distScale = 1.0; 102 | this.setHMD = function(v) { 103 | HMD = v; 104 | // Compute aspect ratio and FOV 105 | var aspect = HMD.hResolution / (2*HMD.vResolution); 106 | 107 | // Fov is normally computed with: 108 | // THREE.Math.radToDeg( 2*Math.atan2(HMD.vScreenSize,2*HMD.eyeToScreenDistance) ); 109 | // But with lens distortion it is increased (see Oculus SDK Documentation) 110 | var r = -1.0 - (4 * (HMD.hScreenSize/4 - HMD.lensSeparationDistance/2) / HMD.hScreenSize); 111 | distScale = (HMD.distortionK[0] + HMD.distortionK[1] * Math.pow(r,2) + HMD.distortionK[2] * Math.pow(r,4) + HMD.distortionK[3] * Math.pow(r,6)); 112 | var fov = THREE.Math.radToDeg(2*Math.atan2(HMD.vScreenSize*distScale, 2*HMD.eyeToScreenDistance)); 113 | 114 | // Compute camera projection matrices 115 | var proj = (new THREE.Matrix4()).makePerspective( fov, aspect, 0.3, 10000 ); 116 | var h = 4 * (HMD.hScreenSize/4 - HMD.interpupillaryDistance/2) / HMD.hScreenSize; 117 | left.proj = ((new THREE.Matrix4()).makeTranslation( h, 0.0, 0.0 )).multiply(proj); 118 | right.proj = ((new THREE.Matrix4()).makeTranslation( -h, 0.0, 0.0 )).multiply(proj); 119 | 120 | // Compute camera transformation matrices 121 | left.tranform = (new THREE.Matrix4()).makeTranslation( -worldFactor * HMD.interpupillaryDistance/2, 0.0, 0.0 ); 122 | right.tranform = (new THREE.Matrix4()).makeTranslation( worldFactor * HMD.interpupillaryDistance/2, 0.0, 0.0 ); 123 | 124 | // Compute Viewport 125 | left.viewport = [0, 0, HMD.hResolution/2, HMD.vResolution]; 126 | right.viewport = [HMD.hResolution/2, 0, HMD.hResolution/2, HMD.vResolution]; 127 | 128 | // Distortion shader parameters 129 | var lensShift = 4 * (HMD.hScreenSize/4 - HMD.lensSeparationDistance/2) / HMD.hScreenSize; 130 | left.lensCenter = new THREE.Vector2(lensShift, 0.0); 131 | right.lensCenter = new THREE.Vector2(-lensShift, 0.0); 132 | 133 | RTMaterial.uniforms['hmdWarpParam'].value = new THREE.Vector4(HMD.distortionK[0], HMD.distortionK[1], HMD.distortionK[2], HMD.distortionK[3]); 134 | RTMaterial.uniforms['chromAbParam'].value = new THREE.Vector4(HMD.chromaAbParameter[0], HMD.chromaAbParameter[1], HMD.chromaAbParameter[2], HMD.chromaAbParameter[3]); 135 | RTMaterial.uniforms['scaleIn'].value = new THREE.Vector2(1.0,1.0/aspect); 136 | RTMaterial.uniforms['scale'].value = new THREE.Vector2(1.0/distScale, 1.0*aspect/distScale); 137 | 138 | // Create render target 139 | if ( renderTarget ) renderTarget.dispose(); 140 | renderTarget = new THREE.WebGLRenderTarget( HMD.hResolution*distScale/2, HMD.vResolution*distScale, RTParams ); 141 | RTMaterial.uniforms[ "texid" ].value = renderTarget; 142 | 143 | } 144 | this.getHMD = function() {return HMD}; 145 | 146 | this.setHMD(HMD); 147 | 148 | this.setSize = function ( width, height ) { 149 | left.viewport = [width/2 - HMD.hResolution/2, height/2 - HMD.vResolution/2, HMD.hResolution/2, HMD.vResolution]; 150 | right.viewport = [width/2, height/2 - HMD.vResolution/2, HMD.hResolution/2, HMD.vResolution]; 151 | 152 | renderer.setSize( width, height ); 153 | }; 154 | 155 | this.render = function ( scene, camera ) { 156 | var cc = renderer.getClearColor().clone(); 157 | 158 | // Clear 159 | renderer.setClearColor(emptyColor); 160 | renderer.clear(); 161 | renderer.setClearColor(cc); 162 | 163 | // camera parameters 164 | if (camera.matrixAutoUpdate) camera.updateMatrix(); 165 | 166 | // Render left 167 | this.preLeftRender(); 168 | 169 | pCamera.projectionMatrix.copy(left.proj); 170 | 171 | pCamera.matrix.copy(camera.matrix).multiply(left.tranform); 172 | pCamera.matrixWorldNeedsUpdate = true; 173 | 174 | renderer.setViewport(left.viewport[0], left.viewport[1], left.viewport[2], left.viewport[3]); 175 | 176 | RTMaterial.uniforms['lensCenter'].value = left.lensCenter; 177 | renderer.render( scene, pCamera, renderTarget, true ); 178 | 179 | renderer.render( finalScene, oCamera ); 180 | 181 | // Render right 182 | this.preRightRender(); 183 | 184 | pCamera.projectionMatrix.copy(right.proj); 185 | 186 | pCamera.matrix.copy(camera.matrix).multiply(right.tranform); 187 | pCamera.matrixWorldNeedsUpdate = true; 188 | 189 | renderer.setViewport(right.viewport[0], right.viewport[1], right.viewport[2], right.viewport[3]); 190 | 191 | RTMaterial.uniforms['lensCenter'].value = right.lensCenter; 192 | 193 | renderer.render( scene, pCamera, renderTarget, true ); 194 | renderer.render( finalScene, oCamera ); 195 | 196 | }; 197 | 198 | this.dispose = function() { 199 | if ( RTMaterial ) { 200 | RTMaterial.dispose(); 201 | } 202 | if ( renderTarget ) { 203 | renderTarget.dispose(); 204 | } 205 | }; 206 | 207 | }; 208 | -------------------------------------------------------------------------------- /public/js/controls/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 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 38 | 39 | // internals 40 | 41 | var scope = this; 42 | 43 | var EPS = 0.000001; 44 | var PIXELS_PER_ROUND = 1800; 45 | 46 | var rotateStart = new THREE.Vector2(); 47 | var rotateEnd = new THREE.Vector2(); 48 | var rotateDelta = new THREE.Vector2(); 49 | 50 | var zoomStart = new THREE.Vector2(); 51 | var zoomEnd = new THREE.Vector2(); 52 | var zoomDelta = new THREE.Vector2(); 53 | 54 | var phiDelta = 0; 55 | var thetaDelta = 0; 56 | var scale = 1; 57 | 58 | var lastPosition = new THREE.Vector3(); 59 | 60 | var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2 }; 61 | var state = STATE.NONE; 62 | 63 | // events 64 | 65 | var changeEvent = { type: 'change' }; 66 | 67 | 68 | this.rotateLeft = function ( angle ) { 69 | 70 | if ( angle === undefined ) { 71 | 72 | angle = getAutoRotationAngle(); 73 | 74 | } 75 | 76 | thetaDelta -= angle; 77 | 78 | }; 79 | 80 | this.rotateRight = function ( angle ) { 81 | 82 | if ( angle === undefined ) { 83 | 84 | angle = getAutoRotationAngle(); 85 | 86 | } 87 | 88 | thetaDelta += angle; 89 | 90 | }; 91 | 92 | this.rotateUp = function ( angle ) { 93 | 94 | if ( angle === undefined ) { 95 | 96 | angle = getAutoRotationAngle(); 97 | 98 | } 99 | 100 | phiDelta -= angle; 101 | 102 | }; 103 | 104 | this.rotateDown = function ( angle ) { 105 | 106 | if ( angle === undefined ) { 107 | 108 | angle = getAutoRotationAngle(); 109 | 110 | } 111 | 112 | phiDelta += angle; 113 | 114 | }; 115 | 116 | this.zoomIn = function ( zoomScale ) { 117 | 118 | if ( zoomScale === undefined ) { 119 | 120 | zoomScale = getZoomScale(); 121 | 122 | } 123 | 124 | scale /= zoomScale; 125 | 126 | }; 127 | 128 | this.zoomOut = function ( zoomScale ) { 129 | 130 | if ( zoomScale === undefined ) { 131 | 132 | zoomScale = getZoomScale(); 133 | 134 | } 135 | 136 | scale *= zoomScale; 137 | 138 | }; 139 | 140 | this.pan = function ( distance ) { 141 | 142 | distance.transformDirection( this.object.matrix ); 143 | distance.multiplyScalar( scope.userPanSpeed ); 144 | 145 | this.object.position.add( distance ); 146 | this.center.add( distance ); 147 | 148 | }; 149 | 150 | this.update = function () { 151 | 152 | var position = this.object.position; 153 | var offset = position.clone().sub( this.center ); 154 | 155 | // angle from z-axis around y-axis 156 | 157 | var theta = Math.atan2( offset.x, offset.z ); 158 | 159 | // angle from y-axis 160 | 161 | var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 162 | 163 | if ( this.autoRotate ) { 164 | 165 | this.rotateLeft( getAutoRotationAngle() ); 166 | 167 | } 168 | 169 | theta += thetaDelta; 170 | phi += phiDelta; 171 | 172 | // restrict phi to be between desired limits 173 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 174 | 175 | // restrict phi to be betwee EPS and PI-EPS 176 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 177 | 178 | var radius = offset.length() * scale; 179 | 180 | // restrict radius to be between desired limits 181 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 182 | 183 | offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 184 | offset.y = radius * Math.cos( phi ); 185 | offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 186 | 187 | position.copy( this.center ).add( offset ); 188 | 189 | this.object.lookAt( this.center ); 190 | 191 | thetaDelta = 0; 192 | phiDelta = 0; 193 | scale = 1; 194 | 195 | if ( lastPosition.distanceTo( this.object.position ) > 0 ) { 196 | 197 | this.dispatchEvent( changeEvent ); 198 | 199 | lastPosition.copy( this.object.position ); 200 | 201 | } 202 | 203 | }; 204 | 205 | 206 | function getAutoRotationAngle() { 207 | 208 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 209 | 210 | } 211 | 212 | function getZoomScale() { 213 | 214 | return Math.pow( 0.95, scope.userZoomSpeed ); 215 | 216 | } 217 | 218 | function onMouseDown( event ) { 219 | 220 | if ( scope.enabled === false ) return; 221 | if ( scope.userRotate === false ) return; 222 | 223 | event.preventDefault(); 224 | 225 | if ( event.button === 0 ) { 226 | 227 | state = STATE.ROTATE; 228 | 229 | rotateStart.set( event.clientX, event.clientY ); 230 | 231 | } else if ( event.button === 1 ) { 232 | 233 | state = STATE.ZOOM; 234 | 235 | zoomStart.set( event.clientX, event.clientY ); 236 | 237 | } else if ( event.button === 2 ) { 238 | 239 | state = STATE.PAN; 240 | 241 | } 242 | 243 | document.addEventListener( 'mousemove', onMouseMove, false ); 244 | document.addEventListener( 'mouseup', onMouseUp, false ); 245 | 246 | } 247 | 248 | function onMouseMove( event ) { 249 | 250 | if ( scope.enabled === false ) return; 251 | 252 | event.preventDefault(); 253 | 254 | if ( state === STATE.ROTATE ) { 255 | 256 | rotateEnd.set( event.clientX, event.clientY ); 257 | rotateDelta.subVectors( rotateEnd, rotateStart ); 258 | 259 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / PIXELS_PER_ROUND * scope.userRotateSpeed ); 260 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / PIXELS_PER_ROUND * scope.userRotateSpeed ); 261 | 262 | rotateStart.copy( rotateEnd ); 263 | 264 | } else if ( state === STATE.ZOOM ) { 265 | 266 | zoomEnd.set( event.clientX, event.clientY ); 267 | zoomDelta.subVectors( zoomEnd, zoomStart ); 268 | 269 | if ( zoomDelta.y > 0 ) { 270 | 271 | scope.zoomIn(); 272 | 273 | } else { 274 | 275 | scope.zoomOut(); 276 | 277 | } 278 | 279 | zoomStart.copy( zoomEnd ); 280 | 281 | } else if ( state === STATE.PAN ) { 282 | 283 | var movementX = event.movementX || event.mozMovementX || event.webkitMovementX || 0; 284 | var movementY = event.movementY || event.mozMovementY || event.webkitMovementY || 0; 285 | 286 | scope.pan( new THREE.Vector3( - movementX, movementY, 0 ) ); 287 | 288 | } 289 | 290 | } 291 | 292 | function onMouseUp( event ) { 293 | 294 | if ( scope.enabled === false ) return; 295 | if ( scope.userRotate === false ) return; 296 | 297 | document.removeEventListener( 'mousemove', onMouseMove, false ); 298 | document.removeEventListener( 'mouseup', onMouseUp, false ); 299 | 300 | state = STATE.NONE; 301 | 302 | } 303 | 304 | function onMouseWheel( event ) { 305 | 306 | if ( scope.enabled === false ) return; 307 | if ( scope.userZoom === false ) return; 308 | 309 | var delta = 0; 310 | 311 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9 312 | 313 | delta = event.wheelDelta; 314 | 315 | } else if ( event.detail ) { // Firefox 316 | 317 | delta = - event.detail; 318 | 319 | } 320 | 321 | if ( delta > 0 ) { 322 | 323 | scope.zoomOut(); 324 | 325 | } else { 326 | 327 | scope.zoomIn(); 328 | 329 | } 330 | 331 | } 332 | 333 | function onKeyDown( event ) { 334 | 335 | if ( scope.enabled === false ) return; 336 | if ( scope.userPan === false ) return; 337 | 338 | switch ( event.keyCode ) { 339 | 340 | case scope.keys.UP: 341 | scope.pan( new THREE.Vector3( 0, 1, 0 ) ); 342 | break; 343 | case scope.keys.BOTTOM: 344 | scope.pan( new THREE.Vector3( 0, - 1, 0 ) ); 345 | break; 346 | case scope.keys.LEFT: 347 | scope.pan( new THREE.Vector3( - 1, 0, 0 ) ); 348 | break; 349 | case scope.keys.RIGHT: 350 | scope.pan( new THREE.Vector3( 1, 0, 0 ) ); 351 | break; 352 | } 353 | 354 | } 355 | 356 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 357 | this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 358 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 359 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 360 | this.domElement.addEventListener( 'keydown', onKeyDown, false ); 361 | 362 | }; 363 | 364 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 365 | -------------------------------------------------------------------------------- /public/js/loaders/OBJLoader2.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.OBJLoader = function ( manager ) { 6 | 7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 8 | 9 | }; 10 | 11 | THREE.OBJLoader.prototype = { 12 | 13 | constructor: THREE.OBJLoader, 14 | 15 | load: function ( url, onLoad, onProgress, onError ) { 16 | 17 | var scope = this; 18 | 19 | var loader = new THREE.XHRLoader( scope.manager ); 20 | loader.setCrossOrigin( this.crossOrigin ); 21 | loader.load( url, function ( text ) { 22 | 23 | onLoad( scope.parse( text ) ); 24 | 25 | } ); 26 | 27 | }, 28 | 29 | parse: function ( text ) { 30 | 31 | var object, objects = []; 32 | var geometry, material, mesh; 33 | 34 | function parseVertexIndex( value ) { 35 | 36 | var index = parseInt( value ); 37 | 38 | return ( index >= 0 ? index - 1 : index + vertices.length / 3 ) * 3; 39 | 40 | } 41 | 42 | function parseNormalIndex( value ) { 43 | 44 | var index = parseInt( value ); 45 | 46 | return ( index >= 0 ? index - 1 : index + normals.length / 3 ) * 3; 47 | 48 | } 49 | 50 | function parseUVIndex( value ) { 51 | 52 | var index = parseInt( value ); 53 | 54 | return ( index >= 0 ? index - 1 : index + uvs.length / 2 ) * 2; 55 | 56 | } 57 | 58 | function addVertex( a, b, c ) { 59 | 60 | geometry.vertices.push( 61 | vertices[ a ], vertices[ a + 1 ], vertices[ a + 2 ], 62 | vertices[ b ], vertices[ b + 1 ], vertices[ b + 2 ], 63 | vertices[ c ], vertices[ c + 1 ], vertices[ c + 2 ] 64 | ); 65 | 66 | } 67 | 68 | function addNormal( a, b, c ) { 69 | 70 | geometry.normals.push( 71 | normals[ a ], normals[ a + 1 ], normals[ a + 2 ], 72 | normals[ b ], normals[ b + 1 ], normals[ b + 2 ], 73 | normals[ c ], normals[ c + 1 ], normals[ c + 2 ] 74 | ); 75 | 76 | } 77 | 78 | function addUV( a, b, c ) { 79 | 80 | geometry.uvs.push( 81 | uvs[ a ], uvs[ a + 1 ], 82 | uvs[ b ], uvs[ b + 1 ], 83 | uvs[ c ], uvs[ c + 1 ] 84 | ); 85 | 86 | } 87 | 88 | function handleFace( a, b, c, d ) { 89 | 90 | var ia = parseVertexIndex( a ); 91 | var ib = parseVertexIndex( b ); 92 | var ic = parseVertexIndex( c ); 93 | 94 | if ( d === undefined ) { 95 | 96 | addVertex( ia, ib, ic ); 97 | 98 | } else { 99 | 100 | var id = parseVertexIndex( d ); 101 | 102 | addVertex( ia, ib, id ); 103 | addVertex( ib, ic, id ); 104 | 105 | } 106 | 107 | } 108 | 109 | function handleUV( a, b, c, d ) { 110 | 111 | var ia = parseUVIndex( a ); 112 | var ib = parseUVIndex( b ); 113 | var ic = parseUVIndex( c ); 114 | 115 | if ( d === undefined ) { 116 | 117 | addUV( ia, ib, ic ); 118 | 119 | } else { 120 | 121 | var id = parseUVIndex( d ); 122 | 123 | addUV( ia, ib, id ); 124 | addUV( ib, ic, id ); 125 | 126 | } 127 | 128 | } 129 | 130 | function handleNormal( a, b, c, d ) { 131 | 132 | var ia = parseNormalIndex( a ); 133 | var ib = parseNormalIndex( b ); 134 | var ic = parseNormalIndex( c ); 135 | 136 | if ( d === undefined ) { 137 | 138 | addNormal( ia, ib, ic ); 139 | 140 | } else { 141 | 142 | var id = parseNormalIndex( d ); 143 | 144 | addNormal( ia, ib, id ); 145 | addNormal( ib, ic, id ); 146 | 147 | } 148 | 149 | } 150 | 151 | // create mesh if no objects in text 152 | 153 | if ( /^o /gm.test( text ) === false ) { 154 | 155 | geometry = { 156 | vertices: [], 157 | normals: [], 158 | uvs: [] 159 | }; 160 | 161 | material = { 162 | name: '' 163 | }; 164 | 165 | object = { 166 | name: '', 167 | geometry: geometry, 168 | material: material 169 | }; 170 | 171 | objects.push( object ); 172 | 173 | } 174 | 175 | var vertices = []; 176 | var normals = []; 177 | var uvs = []; 178 | 179 | // v float float float 180 | 181 | var vertex_pattern = /v( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 182 | 183 | // vn float float float 184 | 185 | var normal_pattern = /vn( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 186 | 187 | // vt float float 188 | 189 | var uv_pattern = /vt( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 190 | 191 | // f vertex vertex vertex ... 192 | 193 | var face_pattern1 = /f( +-?\d+)( +-?\d+)( +-?\d+)( +-?\d+)?/; 194 | 195 | // f vertex/uv vertex/uv vertex/uv ... 196 | 197 | var face_pattern2 = /f( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))?/; 198 | 199 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... 200 | 201 | var face_pattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/; 202 | 203 | // f vertex//normal vertex//normal vertex//normal ... 204 | 205 | var face_pattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/ 206 | 207 | // 208 | 209 | var lines = text.split( '\n' ); 210 | 211 | for ( var i = 0; i < lines.length; i ++ ) { 212 | 213 | var line = lines[ i ]; 214 | line = line.trim(); 215 | 216 | var result; 217 | 218 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) { 219 | 220 | continue; 221 | 222 | } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) { 223 | 224 | // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 225 | 226 | vertices.push( 227 | parseFloat( result[ 1 ] ), 228 | parseFloat( result[ 2 ] ), 229 | parseFloat( result[ 3 ] ) 230 | ); 231 | 232 | } else if ( ( result = normal_pattern.exec( line ) ) !== null ) { 233 | 234 | // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 235 | 236 | normals.push( 237 | parseFloat( result[ 1 ] ), 238 | parseFloat( result[ 2 ] ), 239 | parseFloat( result[ 3 ] ) 240 | ); 241 | 242 | } else if ( ( result = uv_pattern.exec( line ) ) !== null ) { 243 | 244 | // ["vt 0.1 0.2", "0.1", "0.2"] 245 | 246 | uvs.push( 247 | parseFloat( result[ 1 ] ), 248 | parseFloat( result[ 2 ] ) 249 | ); 250 | 251 | } else if ( ( result = face_pattern1.exec( line ) ) !== null ) { 252 | 253 | // ["f 1 2 3", "1", "2", "3", undefined] 254 | 255 | handleFace( result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ] ); 256 | 257 | } else if ( ( result = face_pattern2.exec( line ) ) !== null ) { 258 | 259 | // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] 260 | 261 | handleFace( result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ); 262 | handleUV( result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ); 263 | 264 | } else if ( ( result = face_pattern3.exec( line ) ) !== null ) { 265 | 266 | // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined] 267 | 268 | handleFace( result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ] ); 269 | handleUV( result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ] ); 270 | handleNormal( result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] ); 271 | 272 | } else if ( ( result = face_pattern4.exec( line ) ) !== null ) { 273 | 274 | // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] 275 | 276 | handleFace( result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ); 277 | handleNormal( result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ); 278 | 279 | } else if ( /^o /.test( line ) ) { 280 | 281 | geometry = { 282 | vertices: [], 283 | normals: [], 284 | uvs: [] 285 | }; 286 | 287 | material = { 288 | name: '' 289 | }; 290 | 291 | object = { 292 | name: line.substring( 2 ).trim(), 293 | geometry: geometry, 294 | material: material 295 | }; 296 | 297 | objects.push( object ) 298 | 299 | } else if ( /^g /.test( line ) ) { 300 | 301 | // group 302 | 303 | } else if ( /^usemtl /.test( line ) ) { 304 | 305 | // material 306 | 307 | material.name = line.substring( 7 ).trim(); 308 | 309 | } else if ( /^mtllib /.test( line ) ) { 310 | 311 | // mtl file 312 | 313 | } else if ( /^s /.test( line ) ) { 314 | 315 | // smooth shading 316 | 317 | } else { 318 | 319 | // console.log( "THREE.OBJLoader: Unhandled line " + line ); 320 | 321 | } 322 | 323 | } 324 | 325 | var container = new THREE.Object3D(); 326 | 327 | for ( var i = 0, l = objects.length; i < l; i ++ ) { 328 | 329 | var object = objects[ i ]; 330 | 331 | var geometry = new THREE.Geometry2( object.geometry.vertices.length / 3 ); 332 | geometry.vertices.set( object.geometry.vertices ); 333 | geometry.normals.set( object.geometry.normals ); 334 | geometry.uvs.set( object.geometry.uvs ); 335 | 336 | var material = new THREE.MeshLambertMaterial(); 337 | material.name = object.material.name; 338 | 339 | var mesh = new THREE.Mesh( geometry, material ); 340 | mesh.name = object.name; 341 | 342 | container.add( mesh ); 343 | 344 | } 345 | 346 | return container; 347 | 348 | } 349 | 350 | }; -------------------------------------------------------------------------------- /public/js/loaders/OBJLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.OBJLoader = function ( manager ) { 6 | 7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 8 | 9 | }; 10 | 11 | THREE.OBJLoader.prototype = { 12 | 13 | constructor: THREE.OBJLoader, 14 | 15 | load: function ( url, onLoad, onProgress, onError ) { 16 | 17 | var scope = this; 18 | 19 | var loader = new THREE.XHRLoader( scope.manager ); 20 | loader.setCrossOrigin( this.crossOrigin ); 21 | loader.load( url, function ( text ) { 22 | 23 | onLoad( scope.parse( text ) ); 24 | 25 | } ); 26 | 27 | }, 28 | 29 | parse: function ( text ) { 30 | 31 | function vector( x, y, z ) { 32 | 33 | return new THREE.Vector3( parseFloat( x ), parseFloat( y ), parseFloat( z ) ); 34 | 35 | } 36 | 37 | function uv( u, v ) { 38 | 39 | return new THREE.Vector2( parseFloat( u ), parseFloat( v ) ); 40 | 41 | } 42 | 43 | function face3( a, b, c, normals ) { 44 | 45 | return new THREE.Face3( a, b, c, normals ); 46 | 47 | } 48 | 49 | var object = new THREE.Object3D(); 50 | var geometry, material, mesh; 51 | 52 | function parseVertexIndex( index ) { 53 | 54 | index = parseInt( index ); 55 | 56 | return index >= 0 ? index - 1 : index + vertices.length; 57 | 58 | } 59 | 60 | function parseNormalIndex( index ) { 61 | 62 | index = parseInt( index ); 63 | 64 | return index >= 0 ? index - 1 : index + normals.length; 65 | 66 | } 67 | 68 | function parseUVIndex( index ) { 69 | 70 | index = parseInt( index ); 71 | 72 | return index >= 0 ? index - 1 : index + uvs.length; 73 | 74 | } 75 | 76 | function add_face( a, b, c, normals_inds ) { 77 | 78 | if ( normals_inds === undefined ) { 79 | 80 | geometry.faces.push( face3( 81 | vertices[ parseVertexIndex( a ) ] - 1, 82 | vertices[ parseVertexIndex( b ) ] - 1, 83 | vertices[ parseVertexIndex( c ) ] - 1 84 | ) ); 85 | 86 | } else { 87 | 88 | geometry.faces.push( face3( 89 | vertices[ parseVertexIndex( a ) ] - 1, 90 | vertices[ parseVertexIndex( b ) ] - 1, 91 | vertices[ parseVertexIndex( c ) ] - 1, 92 | [ 93 | normals[ parseNormalIndex( normals_inds[ 0 ] ) ].clone(), 94 | normals[ parseNormalIndex( normals_inds[ 1 ] ) ].clone(), 95 | normals[ parseNormalIndex( normals_inds[ 2 ] ) ].clone() 96 | ] 97 | ) ); 98 | 99 | } 100 | 101 | } 102 | 103 | function add_uvs( a, b, c ) { 104 | 105 | geometry.faceVertexUvs[ 0 ].push( [ 106 | uvs[ parseUVIndex( a ) ].clone(), 107 | uvs[ parseUVIndex( b ) ].clone(), 108 | uvs[ parseUVIndex( c ) ].clone() 109 | ] ); 110 | 111 | } 112 | 113 | function handle_face_line(faces, uvs, normals_inds) { 114 | 115 | if ( faces[ 3 ] === undefined ) { 116 | 117 | add_face( faces[ 0 ], faces[ 1 ], faces[ 2 ], normals_inds ); 118 | 119 | if ( uvs !== undefined && uvs.length > 0 ) { 120 | 121 | add_uvs( uvs[ 0 ], uvs[ 1 ], uvs[ 2 ] ); 122 | 123 | } 124 | 125 | } else { 126 | 127 | if ( normals_inds !== undefined && normals_inds.length > 0 ) { 128 | 129 | add_face( faces[ 0 ], faces[ 1 ], faces[ 3 ], [ normals_inds[ 0 ], normals_inds[ 1 ], normals_inds[ 3 ] ] ); 130 | add_face( faces[ 1 ], faces[ 2 ], faces[ 3 ], [ normals_inds[ 1 ], normals_inds[ 2 ], normals_inds[ 3 ] ] ); 131 | 132 | } else { 133 | 134 | add_face( faces[ 0 ], faces[ 1 ], faces[ 3 ] ); 135 | add_face( faces[ 1 ], faces[ 2 ], faces[ 3 ] ); 136 | 137 | } 138 | 139 | if ( uvs !== undefined && uvs.length > 0 ) { 140 | 141 | add_uvs( uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ); 142 | add_uvs( uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ); 143 | 144 | } 145 | 146 | } 147 | 148 | } 149 | 150 | // create mesh if no objects in text 151 | 152 | if ( /^o /gm.test( text ) === false ) { 153 | 154 | geometry = new THREE.Geometry(); 155 | material = new THREE.MeshLambertMaterial(); 156 | mesh = new THREE.Mesh( geometry, material ); 157 | object.add( mesh ); 158 | 159 | } 160 | 161 | var vertices = []; 162 | var normals = []; 163 | var uvs = []; 164 | 165 | // v float float float 166 | 167 | var vertex_pattern = /v( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 168 | 169 | // vn float float float 170 | 171 | var normal_pattern = /vn( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 172 | 173 | // vt float float 174 | 175 | var uv_pattern = /vt( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 176 | 177 | // f vertex vertex vertex ... 178 | 179 | var face_pattern1 = /f( +-?\d+)( +-?\d+)( +-?\d+)( +-?\d+)?/; 180 | 181 | // f vertex/uv vertex/uv vertex/uv ... 182 | 183 | var face_pattern2 = /f( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+))?/; 184 | 185 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... 186 | 187 | var face_pattern3 = /f( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))( +(-?\d+)\/(-?\d+)\/(-?\d+))?/; 188 | 189 | // f vertex//normal vertex//normal vertex//normal ... 190 | 191 | var face_pattern4 = /f( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))( +(-?\d+)\/\/(-?\d+))?/ 192 | 193 | // fixes 194 | 195 | text = text.replace( /\\\r\n/g, '' ); // handles line continuations \ 196 | 197 | var lines = text.split( '\n' ); 198 | 199 | for ( var i = 0; i < lines.length; i ++ ) { 200 | 201 | var line = lines[ i ]; 202 | line = line.trim(); 203 | 204 | var result; 205 | 206 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) { 207 | 208 | continue; 209 | 210 | } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) { 211 | 212 | // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 213 | 214 | vertices.push( 215 | geometry.vertices.push( 216 | vector( 217 | result[ 1 ], result[ 2 ], result[ 3 ] 218 | ) 219 | ) 220 | ); 221 | 222 | } else if ( ( result = normal_pattern.exec( line ) ) !== null ) { 223 | 224 | // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 225 | 226 | normals.push( 227 | vector( 228 | result[ 1 ], result[ 2 ], result[ 3 ] 229 | ) 230 | ); 231 | 232 | } else if ( ( result = uv_pattern.exec( line ) ) !== null ) { 233 | 234 | // ["vt 0.1 0.2", "0.1", "0.2"] 235 | 236 | uvs.push( 237 | uv( 238 | result[ 1 ], result[ 2 ] 239 | ) 240 | ); 241 | 242 | } else if ( ( result = face_pattern1.exec( line ) ) !== null ) { 243 | 244 | // ["f 1 2 3", "1", "2", "3", undefined] 245 | 246 | handle_face_line( 247 | [ result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ] ] 248 | ); 249 | 250 | } else if ( ( result = face_pattern2.exec( line ) ) !== null ) { 251 | 252 | // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] 253 | 254 | handle_face_line( 255 | [ result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ], //faces 256 | [ result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ] //uv 257 | ); 258 | 259 | } else if ( ( result = face_pattern3.exec( line ) ) !== null ) { 260 | 261 | // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined] 262 | 263 | handle_face_line( 264 | [ result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ] ], //faces 265 | [ result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ] ], //uv 266 | [ result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] ] //normal 267 | ); 268 | 269 | } else if ( ( result = face_pattern4.exec( line ) ) !== null ) { 270 | 271 | // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] 272 | 273 | handle_face_line( 274 | [ result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ], //faces 275 | [ ], //uv 276 | [ result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ] //normal 277 | ); 278 | 279 | } else if ( /^o /.test( line ) ) { 280 | 281 | geometry = new THREE.Geometry(); 282 | material = new THREE.MeshLambertMaterial(); 283 | 284 | mesh = new THREE.Mesh( geometry, material ); 285 | mesh.name = line.substring( 2 ).trim(); 286 | object.add( mesh ); 287 | 288 | } else if ( /^g /.test( line ) ) { 289 | 290 | // group 291 | 292 | } else if ( /^usemtl /.test( line ) ) { 293 | 294 | // material 295 | 296 | material.name = line.substring( 7 ).trim(); 297 | 298 | } else if ( /^mtllib /.test( line ) ) { 299 | 300 | // mtl file 301 | 302 | } else if ( /^s /.test( line ) ) { 303 | 304 | // smooth shading 305 | 306 | } else { 307 | 308 | // console.log( "THREE.OBJLoader: Unhandled line " + line ); 309 | 310 | } 311 | 312 | } 313 | 314 | var children = object.children; 315 | 316 | for ( var i = 0, l = children.length; i < l; i ++ ) { 317 | 318 | var geometry = children[ i ].geometry; 319 | 320 | geometry.computeFaceNormals(); 321 | geometry.computeBoundingSphere(); 322 | 323 | } 324 | 325 | return object; 326 | 327 | } 328 | 329 | }; 330 | -------------------------------------------------------------------------------- /public/js/loaders/DDSLoader.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | THREE.DDSLoader = function () {}; 6 | 7 | THREE.DDSLoader.prototype = { 8 | 9 | constructor: THREE.DDSLoader, 10 | 11 | load: function ( url, onLoad, onError ) { 12 | 13 | var scope = this; 14 | 15 | var images = []; 16 | 17 | var texture = new THREE.CompressedTexture(); 18 | texture.image = images; 19 | 20 | // no flipping for cube textures 21 | // (also flipping doesn't work for compressed textures ) 22 | 23 | texture.flipY = false; 24 | 25 | // can't generate mipmaps for compressed textures 26 | // mips must be embedded in DDS files 27 | 28 | texture.generateMipmaps = false; 29 | 30 | if ( url instanceof Array ) { 31 | 32 | var loaded = 0; 33 | 34 | var loader = new THREE.XHRLoader(); 35 | loader.setResponseType( 'arraybuffer' ); 36 | 37 | var loadTexture = function ( i ) { 38 | 39 | loader.load( url[ i ], function ( buffer ) { 40 | 41 | var dds = scope.parse( buffer, true ); 42 | 43 | images[ i ] = { 44 | width: dds.width, 45 | height: dds.height, 46 | format: dds.format, 47 | mipmaps: dds.mipmaps 48 | } 49 | 50 | loaded += 1; 51 | 52 | if ( loaded === 6 ) { 53 | 54 | texture.format = dds.format; 55 | texture.needsUpdate = true; 56 | 57 | if ( onLoad ) onLoad( texture ); 58 | 59 | } 60 | 61 | } ); 62 | 63 | } 64 | 65 | for ( var i = 0, il = url.length; i < il; ++ i ) { 66 | 67 | loadTexture( i ); 68 | 69 | } 70 | 71 | } else { 72 | 73 | // compressed cubemap texture stored in a single DDS file 74 | 75 | var loader = new THREE.XHRLoader(); 76 | loader.setResponseType( 'arraybuffer' ); 77 | loader.load( url, function ( buffer ) { 78 | 79 | var dds = scope.parse( buffer, true ); 80 | 81 | if ( dds.isCubemap ) { 82 | 83 | var faces = dds.mipmaps.length / dds.mipmapCount; 84 | 85 | for ( var f = 0; f < faces; f ++ ) { 86 | 87 | images[ f ] = { mipmaps : [] }; 88 | 89 | for ( var i = 0; i < dds.mipmapCount; i ++ ) { 90 | 91 | images[ f ].mipmaps.push( dds.mipmaps[ f * dds.mipmapCount + i ] ); 92 | images[ f ].format = dds.format; 93 | images[ f ].width = dds.width; 94 | images[ f ].height = dds.height; 95 | 96 | } 97 | 98 | } 99 | 100 | } else { 101 | 102 | texture.image.width = dds.width; 103 | texture.image.height = dds.height; 104 | texture.mipmaps = dds.mipmaps; 105 | 106 | } 107 | 108 | texture.format = dds.format; 109 | texture.needsUpdate = true; 110 | 111 | if ( onLoad ) onLoad( texture ); 112 | 113 | } ); 114 | 115 | } 116 | 117 | return texture; 118 | 119 | }, 120 | 121 | parse: function ( buffer, loadMipmaps ) { 122 | 123 | var dds = { mipmaps: [], width: 0, height: 0, format: null, mipmapCount: 1 }; 124 | 125 | // Adapted from @toji's DDS utils 126 | // https://github.com/toji/webgl-texture-utils/blob/master/texture-util/dds.js 127 | 128 | // All values and structures referenced from: 129 | // http://msdn.microsoft.com/en-us/library/bb943991.aspx/ 130 | 131 | var DDS_MAGIC = 0x20534444; 132 | 133 | var DDSD_CAPS = 0x1, 134 | DDSD_HEIGHT = 0x2, 135 | DDSD_WIDTH = 0x4, 136 | DDSD_PITCH = 0x8, 137 | DDSD_PIXELFORMAT = 0x1000, 138 | DDSD_MIPMAPCOUNT = 0x20000, 139 | DDSD_LINEARSIZE = 0x80000, 140 | DDSD_DEPTH = 0x800000; 141 | 142 | var DDSCAPS_COMPLEX = 0x8, 143 | DDSCAPS_MIPMAP = 0x400000, 144 | DDSCAPS_TEXTURE = 0x1000; 145 | 146 | var DDSCAPS2_CUBEMAP = 0x200, 147 | DDSCAPS2_CUBEMAP_POSITIVEX = 0x400, 148 | DDSCAPS2_CUBEMAP_NEGATIVEX = 0x800, 149 | DDSCAPS2_CUBEMAP_POSITIVEY = 0x1000, 150 | DDSCAPS2_CUBEMAP_NEGATIVEY = 0x2000, 151 | DDSCAPS2_CUBEMAP_POSITIVEZ = 0x4000, 152 | DDSCAPS2_CUBEMAP_NEGATIVEZ = 0x8000, 153 | DDSCAPS2_VOLUME = 0x200000; 154 | 155 | var DDPF_ALPHAPIXELS = 0x1, 156 | DDPF_ALPHA = 0x2, 157 | DDPF_FOURCC = 0x4, 158 | DDPF_RGB = 0x40, 159 | DDPF_YUV = 0x200, 160 | DDPF_LUMINANCE = 0x20000; 161 | 162 | function fourCCToInt32( value ) { 163 | 164 | return value.charCodeAt(0) + 165 | (value.charCodeAt(1) << 8) + 166 | (value.charCodeAt(2) << 16) + 167 | (value.charCodeAt(3) << 24); 168 | 169 | } 170 | 171 | function int32ToFourCC( value ) { 172 | 173 | return String.fromCharCode( 174 | value & 0xff, 175 | (value >> 8) & 0xff, 176 | (value >> 16) & 0xff, 177 | (value >> 24) & 0xff 178 | ); 179 | } 180 | 181 | function loadARGBMip( buffer, dataOffset, width, height ) { 182 | var dataLength = width*height*4; 183 | var srcBuffer = new Uint8Array( buffer, dataOffset, dataLength ); 184 | var byteArray = new Uint8Array( dataLength ); 185 | var dst = 0; 186 | var src = 0; 187 | for ( var y = 0; y < height; y++ ) { 188 | for ( var x = 0; x < width; x++ ) { 189 | var b = srcBuffer[src]; src++; 190 | var g = srcBuffer[src]; src++; 191 | var r = srcBuffer[src]; src++; 192 | var a = srcBuffer[src]; src++; 193 | byteArray[dst] = r; dst++; //r 194 | byteArray[dst] = g; dst++; //g 195 | byteArray[dst] = b; dst++; //b 196 | byteArray[dst] = a; dst++; //a 197 | } 198 | } 199 | return byteArray; 200 | } 201 | 202 | var FOURCC_DXT1 = fourCCToInt32("DXT1"); 203 | var FOURCC_DXT3 = fourCCToInt32("DXT3"); 204 | var FOURCC_DXT5 = fourCCToInt32("DXT5"); 205 | 206 | var headerLengthInt = 31; // The header length in 32 bit ints 207 | 208 | // Offsets into the header array 209 | 210 | var off_magic = 0; 211 | 212 | var off_size = 1; 213 | var off_flags = 2; 214 | var off_height = 3; 215 | var off_width = 4; 216 | 217 | var off_mipmapCount = 7; 218 | 219 | var off_pfFlags = 20; 220 | var off_pfFourCC = 21; 221 | var off_RGBBitCount = 22; 222 | var off_RBitMask = 23; 223 | var off_GBitMask = 24; 224 | var off_BBitMask = 25; 225 | var off_ABitMask = 26; 226 | 227 | var off_caps = 27; 228 | var off_caps2 = 28; 229 | var off_caps3 = 29; 230 | var off_caps4 = 30; 231 | 232 | // Parse header 233 | 234 | var header = new Int32Array( buffer, 0, headerLengthInt ); 235 | 236 | if ( header[ off_magic ] !== DDS_MAGIC ) { 237 | 238 | console.error( 'THREE.DDSLoader.parse: Invalid magic number in DDS header.' ); 239 | return dds; 240 | 241 | } 242 | 243 | if ( ! header[ off_pfFlags ] & DDPF_FOURCC ) { 244 | 245 | console.error( 'THREE.DDSLoader.parse: Unsupported format, must contain a FourCC code.' ); 246 | return dds; 247 | 248 | } 249 | 250 | var blockBytes; 251 | 252 | var fourCC = header[ off_pfFourCC ]; 253 | 254 | var isRGBAUncompressed = false; 255 | 256 | switch ( fourCC ) { 257 | 258 | case FOURCC_DXT1: 259 | 260 | blockBytes = 8; 261 | dds.format = THREE.RGB_S3TC_DXT1_Format; 262 | break; 263 | 264 | case FOURCC_DXT3: 265 | 266 | blockBytes = 16; 267 | dds.format = THREE.RGBA_S3TC_DXT3_Format; 268 | break; 269 | 270 | case FOURCC_DXT5: 271 | 272 | blockBytes = 16; 273 | dds.format = THREE.RGBA_S3TC_DXT5_Format; 274 | break; 275 | 276 | default: 277 | 278 | if( header[off_RGBBitCount] ==32 279 | && header[off_RBitMask]&0xff0000 280 | && header[off_GBitMask]&0xff00 281 | && header[off_BBitMask]&0xff 282 | && header[off_ABitMask]&0xff000000 ) { 283 | isRGBAUncompressed = true; 284 | blockBytes = 64; 285 | dds.format = THREE.RGBAFormat; 286 | } else { 287 | console.error( 'THREE.DDSLoader.parse: Unsupported FourCC code ', int32ToFourCC( fourCC ) ); 288 | return dds; 289 | } 290 | } 291 | 292 | dds.mipmapCount = 1; 293 | 294 | if ( header[ off_flags ] & DDSD_MIPMAPCOUNT && loadMipmaps !== false ) { 295 | 296 | dds.mipmapCount = Math.max( 1, header[ off_mipmapCount ] ); 297 | 298 | } 299 | 300 | //TODO: Verify that all faces of the cubemap are present with DDSCAPS2_CUBEMAP_POSITIVEX, etc. 301 | 302 | dds.isCubemap = header[ off_caps2 ] & DDSCAPS2_CUBEMAP ? true : false; 303 | 304 | dds.width = header[ off_width ]; 305 | dds.height = header[ off_height ]; 306 | 307 | var dataOffset = header[ off_size ] + 4; 308 | 309 | // Extract mipmaps buffers 310 | 311 | var width = dds.width; 312 | var height = dds.height; 313 | 314 | var faces = dds.isCubemap ? 6 : 1; 315 | 316 | for ( var face = 0; face < faces; face ++ ) { 317 | 318 | for ( var i = 0; i < dds.mipmapCount; i ++ ) { 319 | 320 | if( isRGBAUncompressed ) { 321 | var byteArray = loadARGBMip( buffer, dataOffset, width, height ); 322 | var dataLength = byteArray.length; 323 | } else { 324 | var dataLength = Math.max( 4, width ) / 4 * Math.max( 4, height ) / 4 * blockBytes; 325 | var byteArray = new Uint8Array( buffer, dataOffset, dataLength ); 326 | } 327 | 328 | var mipmap = { "data": byteArray, "width": width, "height": height }; 329 | dds.mipmaps.push( mipmap ); 330 | 331 | dataOffset += dataLength; 332 | 333 | width = Math.max( width * 0.5, 1 ); 334 | height = Math.max( height * 0.5, 1 ); 335 | 336 | } 337 | 338 | width = dds.width; 339 | height = dds.height; 340 | 341 | } 342 | 343 | return dds; 344 | 345 | } 346 | 347 | }; 348 | -------------------------------------------------------------------------------- /public/js/controls/PathControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | */ 4 | 5 | THREE.PathControls = function ( object, domElement ) { 6 | 7 | this.object = object; 8 | this.domElement = ( domElement !== undefined ) ? domElement : document; 9 | 10 | this.id = "PathControls" + THREE.PathControlsIdCounter ++; 11 | 12 | // API 13 | 14 | this.duration = 10 * 1000; // milliseconds 15 | this.waypoints = []; 16 | 17 | this.useConstantSpeed = true; 18 | this.resamplingCoef = 50; 19 | 20 | this.debugPath = new THREE.Object3D(); 21 | this.debugDummy = new THREE.Object3D(); 22 | 23 | this.animationParent = new THREE.Object3D(); 24 | 25 | this.lookSpeed = 0.005; 26 | this.lookVertical = true; 27 | this.lookHorizontal = true; 28 | this.verticalAngleMap = { srcRange: [ 0, 2 * Math.PI ], dstRange: [ 0, 2 * Math.PI ] }; 29 | this.horizontalAngleMap = { srcRange: [ 0, 2 * Math.PI ], dstRange: [ 0, 2 * Math.PI ] }; 30 | 31 | // internals 32 | 33 | this.target = new THREE.Object3D(); 34 | 35 | this.mouseX = 0; 36 | this.mouseY = 0; 37 | 38 | this.lat = 0; 39 | this.lon = 0; 40 | 41 | this.phi = 0; 42 | this.theta = 0; 43 | 44 | var PI2 = Math.PI * 2; 45 | 46 | this.viewHalfX = 0; 47 | this.viewHalfY = 0; 48 | 49 | if ( this.domElement !== document ) { 50 | 51 | this.domElement.setAttribute( 'tabindex', -1 ); 52 | 53 | } 54 | 55 | // methods 56 | 57 | this.handleResize = function () { 58 | 59 | if ( this.domElement === document ) { 60 | 61 | this.viewHalfX = window.innerWidth / 2; 62 | this.viewHalfY = window.innerHeight / 2; 63 | 64 | } else { 65 | 66 | this.viewHalfX = this.domElement.offsetWidth / 2; 67 | this.viewHalfY = this.domElement.offsetHeight / 2; 68 | 69 | } 70 | 71 | }; 72 | 73 | this.update = function ( delta ) { 74 | 75 | var srcRange, dstRange; 76 | 77 | if( this.lookHorizontal ) this.lon += this.mouseX * this.lookSpeed * delta; 78 | if( this.lookVertical ) this.lat -= this.mouseY * this.lookSpeed * delta; 79 | 80 | this.lon = Math.max( 0, Math.min( 360, this.lon ) ); 81 | this.lat = Math.max( - 85, Math.min( 85, this.lat ) ); 82 | 83 | this.phi = THREE.Math.degToRad( 90 - this.lat ); 84 | this.theta = THREE.Math.degToRad( this.lon ); 85 | 86 | this.phi = normalize_angle_rad( this.phi ); 87 | 88 | // constrain vertical look angle 89 | 90 | srcRange = this.verticalAngleMap.srcRange; 91 | dstRange = this.verticalAngleMap.dstRange; 92 | 93 | var tmpPhi = THREE.Math.mapLinear( this.phi, srcRange[ 0 ], srcRange[ 1 ], dstRange[ 0 ], dstRange[ 1 ] ); 94 | var tmpPhiFullRange = dstRange[ 1 ] - dstRange[ 0 ]; 95 | var tmpPhiNormalized = ( tmpPhi - dstRange[ 0 ] ) / tmpPhiFullRange; 96 | 97 | this.phi = QuadraticEaseInOut( tmpPhiNormalized ) * tmpPhiFullRange + dstRange[ 0 ]; 98 | 99 | // constrain horizontal look angle 100 | 101 | srcRange = this.horizontalAngleMap.srcRange; 102 | dstRange = this.horizontalAngleMap.dstRange; 103 | 104 | var tmpTheta = THREE.Math.mapLinear( this.theta, srcRange[ 0 ], srcRange[ 1 ], dstRange[ 0 ], dstRange[ 1 ] ); 105 | var tmpThetaFullRange = dstRange[ 1 ] - dstRange[ 0 ]; 106 | var tmpThetaNormalized = ( tmpTheta - dstRange[ 0 ] ) / tmpThetaFullRange; 107 | 108 | this.theta = QuadraticEaseInOut( tmpThetaNormalized ) * tmpThetaFullRange + dstRange[ 0 ]; 109 | 110 | var targetPosition = this.target.position, 111 | position = this.object.position; 112 | 113 | targetPosition.x = 100 * Math.sin( this.phi ) * Math.cos( this.theta ); 114 | targetPosition.y = 100 * Math.cos( this.phi ); 115 | targetPosition.z = 100 * Math.sin( this.phi ) * Math.sin( this.theta ); 116 | 117 | this.object.lookAt( this.target.position ); 118 | 119 | }; 120 | 121 | this.onMouseMove = function ( event ) { 122 | 123 | if ( this.domElement === document ) { 124 | 125 | this.mouseX = event.pageX - this.viewHalfX; 126 | this.mouseY = event.pageY - this.viewHalfY; 127 | 128 | } else { 129 | 130 | this.mouseX = event.pageX - this.domElement.offsetLeft - this.viewHalfX; 131 | this.mouseY = event.pageY - this.domElement.offsetTop - this.viewHalfY; 132 | 133 | } 134 | 135 | }; 136 | 137 | // utils 138 | 139 | function normalize_angle_rad( a ) { 140 | 141 | var b = a % PI2; 142 | return b >= 0 ? b : b + PI2; 143 | 144 | }; 145 | 146 | function distance( a, b ) { 147 | 148 | var dx = a[ 0 ] - b[ 0 ], 149 | dy = a[ 1 ] - b[ 1 ], 150 | dz = a[ 2 ] - b[ 2 ]; 151 | 152 | return Math.sqrt( dx * dx + dy * dy + dz * dz ); 153 | 154 | }; 155 | 156 | function QuadraticEaseInOut ( k ) { 157 | 158 | if ( ( k *= 2 ) < 1 ) return 0.5 * k * k; 159 | return - 0.5 * ( --k * ( k - 2 ) - 1 ); 160 | 161 | }; 162 | 163 | function bind( scope, fn ) { 164 | 165 | return function () { 166 | 167 | fn.apply( scope, arguments ); 168 | 169 | }; 170 | 171 | }; 172 | 173 | function initAnimationPath( parent, spline, name, duration ) { 174 | 175 | var animationData = { 176 | 177 | name: name, 178 | fps: 0.6, 179 | length: duration, 180 | 181 | hierarchy: [] 182 | 183 | }; 184 | 185 | var i, 186 | parentAnimation, childAnimation, 187 | path = spline.getControlPointsArray(), 188 | sl = spline.getLength(), 189 | pl = path.length, 190 | t = 0, 191 | first = 0, 192 | last = pl - 1; 193 | 194 | parentAnimation = { parent: -1, keys: [] }; 195 | parentAnimation.keys[ first ] = { time: 0, pos: path[ first ], rot: [ 0, 0, 0, 1 ], scl: [ 1, 1, 1 ] }; 196 | parentAnimation.keys[ last ] = { time: duration, pos: path[ last ], rot: [ 0, 0, 0, 1 ], scl: [ 1, 1, 1 ] }; 197 | 198 | for ( i = 1; i < pl - 1; i++ ) { 199 | 200 | // real distance (approximation via linear segments) 201 | 202 | t = duration * sl.chunks[ i ] / sl.total; 203 | 204 | // equal distance 205 | 206 | //t = duration * ( i / pl ); 207 | 208 | // linear distance 209 | 210 | //t += duration * distance( path[ i ], path[ i - 1 ] ) / sl.total; 211 | 212 | parentAnimation.keys[ i ] = { time: t, pos: path[ i ] }; 213 | 214 | } 215 | 216 | animationData.hierarchy[ 0 ] = parentAnimation; 217 | 218 | THREE.AnimationHandler.add( animationData ); 219 | 220 | return new THREE.Animation( parent, name, THREE.AnimationHandler.CATMULLROM_FORWARD, false ); 221 | 222 | }; 223 | 224 | 225 | function createSplineGeometry( spline, n_sub ) { 226 | 227 | var i, index, position, 228 | geometry = new THREE.Geometry(); 229 | 230 | for ( i = 0; i < spline.points.length * n_sub; i ++ ) { 231 | 232 | index = i / ( spline.points.length * n_sub ); 233 | position = spline.getPoint( index ); 234 | 235 | geometry.vertices[ i ] = new THREE.Vector3( position.x, position.y, position.z ); 236 | 237 | } 238 | 239 | return geometry; 240 | 241 | }; 242 | 243 | function createPath( parent, spline ) { 244 | 245 | var lineGeo = createSplineGeometry( spline, 10 ), 246 | particleGeo = createSplineGeometry( spline, 10 ), 247 | lineMat = new THREE.LineBasicMaterial( { color: 0xff0000, linewidth: 3 } ), 248 | lineObj = new THREE.Line( lineGeo, lineMat ), 249 | particleObj = new THREE.ParticleSystem( particleGeo, new THREE.ParticleBasicMaterial( { color: 0xffaa00, size: 3 } ) ); 250 | 251 | lineObj.scale.set( 1, 1, 1 ); 252 | parent.add( lineObj ); 253 | 254 | particleObj.scale.set( 1, 1, 1 ); 255 | parent.add( particleObj ); 256 | 257 | var waypoint, 258 | geo = new THREE.SphereGeometry( 1, 16, 8 ), 259 | mat = new THREE.MeshBasicMaterial( { color: 0x00ff00 } ); 260 | 261 | for ( var i = 0; i < spline.points.length; i ++ ) { 262 | 263 | waypoint = new THREE.Mesh( geo, mat ); 264 | waypoint.position.copy( spline.points[ i ] ); 265 | parent.add( waypoint ); 266 | 267 | } 268 | 269 | }; 270 | 271 | this.init = function ( ) { 272 | 273 | // constructor 274 | 275 | this.spline = new THREE.Spline(); 276 | this.spline.initFromArray( this.waypoints ); 277 | 278 | if ( this.useConstantSpeed ) { 279 | 280 | this.spline.reparametrizeByArcLength( this.resamplingCoef ); 281 | 282 | } 283 | 284 | if ( this.createDebugDummy ) { 285 | 286 | var dummyParentMaterial = new THREE.MeshLambertMaterial( { color: 0x0077ff } ), 287 | dummyChildMaterial = new THREE.MeshLambertMaterial( { color: 0x00ff00 } ), 288 | dummyParentGeo = new THREE.CubeGeometry( 10, 10, 20 ), 289 | dummyChildGeo = new THREE.CubeGeometry( 2, 2, 10 ); 290 | 291 | this.animationParent = new THREE.Mesh( dummyParentGeo, dummyParentMaterial ); 292 | 293 | var dummyChild = new THREE.Mesh( dummyChildGeo, dummyChildMaterial ); 294 | dummyChild.position.set( 0, 10, 0 ); 295 | 296 | this.animation = initAnimationPath( this.animationParent, this.spline, this.id, this.duration ); 297 | 298 | this.animationParent.add( this.object ); 299 | this.animationParent.add( this.target ); 300 | this.animationParent.add( dummyChild ); 301 | 302 | } else { 303 | 304 | this.animation = initAnimationPath( this.animationParent, this.spline, this.id, this.duration ); 305 | this.animationParent.add( this.target ); 306 | this.animationParent.add( this.object ); 307 | 308 | } 309 | 310 | if ( this.createDebugPath ) { 311 | 312 | createPath( this.debugPath, this.spline ); 313 | 314 | } 315 | 316 | this.domElement.addEventListener( 'mousemove', bind( this, this.onMouseMove ), false ); 317 | 318 | }; 319 | 320 | this.handleResize(); 321 | 322 | }; 323 | 324 | THREE.PathControlsIdCounter = 0; 325 | -------------------------------------------------------------------------------- /public/js/loaders/AssimpJSONLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Alexander Gessler / http://www.greentoken.de/ 3 | * https://github.com/acgessler 4 | * 5 | * Loader for models imported with Open Asset Import Library (http://assimp.sf.net) 6 | * through assimp2json (https://github.com/acgessler/assimp2json). 7 | * 8 | * Supports any input format that assimp supports, including 3ds, obj, dae, blend, 9 | * fbx, x, ms3d, lwo (and many more). 10 | * 11 | * See webgl_loader_assimp2json example. 12 | */ 13 | 14 | THREE.AssimpJSONLoader = function ( manager ) { 15 | 16 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 17 | }; 18 | 19 | THREE.AssimpJSONLoader.prototype = { 20 | 21 | constructor: THREE.AssimpJSONLoader, 22 | 23 | texturePath : '', 24 | 25 | load: function ( url, onLoad, onProgress, onError, texturePath ) { 26 | 27 | var scope = this; 28 | 29 | this.texturePath = texturePath && ( typeof texturePath === "string" ) ? texturePath : this.extractUrlBase( url ); 30 | 31 | var loader = new THREE.XHRLoader( this.manager ); 32 | loader.setCrossOrigin( this.crossOrigin ); 33 | loader.load( url, function ( text ) { 34 | var scene = scope.parse( JSON.parse( text ) ); 35 | onLoad( scene ); 36 | } ); 37 | }, 38 | 39 | setCrossOrigin: function ( value ) { 40 | this.crossOrigin = value; 41 | }, 42 | 43 | extractUrlBase: function ( url ) { // from three/src/loaders/Loader.js 44 | var parts = url.split( '/' ); 45 | parts.pop(); 46 | return ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/'; 47 | }, 48 | 49 | parse: function ( json ) { 50 | var meshes = this.parseList ( json.meshes, this.parseMesh ); 51 | var materials = this.parseList ( json.materials, this.parseMaterial ); 52 | return this.parseObject( json, json.rootnode, meshes, materials ); 53 | }, 54 | 55 | parseList : function(json, handler) { 56 | var meshes = new Array(json.length); 57 | for(var i = 0; i < json.length; ++i) { 58 | meshes[i] = handler.call(this, json[i]); 59 | } 60 | return meshes; 61 | }, 62 | 63 | parseMesh : function(json) { 64 | var vertex, geometry, i, e, in_data, src; 65 | 66 | 67 | geometry = new THREE.Geometry(); 68 | 69 | // read vertex positions 70 | for(in_data = json.vertices, i = 0, e = in_data.length; i < e; ) { 71 | geometry.vertices.push( new THREE.Vector3( in_data[ i++ ], in_data[ i++ ], in_data[ i++ ] ) ); 72 | } 73 | 74 | // read faces 75 | var cnt = 0; 76 | for(in_data = json.faces, i = 0, e = in_data.length; i < e; ++i) { 77 | face = new THREE.Face3(); 78 | src = in_data[i]; 79 | face.a = src[0]; 80 | face.b = src[1]; 81 | face.c = src[2]; 82 | 83 | face.materialIndex = 0; //json.materialindex; 84 | geometry.faces.push(face); 85 | } 86 | 87 | // read texture coordinates - three.js attaches them to its faces 88 | json.texturecoords = json.texturecoords || []; 89 | for(i = 0, e = json.texturecoords.length; i < e; ++i) { 90 | 91 | function convertTextureCoords(in_uv, out_faces, out_vertex_uvs) { 92 | var i, e, face, a, b, c; 93 | 94 | for(i = 0, e = out_faces.length; i < e; ++i) { 95 | face = out_faces[i]; 96 | a = face.a * 2; 97 | b = face.b * 2; 98 | c = face.c * 2; 99 | out_vertex_uvs.push([ 100 | new THREE.Vector2( in_uv[ a ], in_uv[ a + 1 ] ), 101 | new THREE.Vector2( in_uv[ b ], in_uv[ b + 1 ] ), 102 | new THREE.Vector2( in_uv[ c ], in_uv[ c + 1 ] ) 103 | ]); 104 | } 105 | } 106 | 107 | convertTextureCoords(json.texturecoords[i], geometry.faces, geometry.faceVertexUvs[i]); 108 | } 109 | 110 | // read normals - three.js also attaches them to its faces 111 | if(json.normals) { 112 | 113 | function convertNormals(in_nor, out_faces) { 114 | var i, e, face, a, b, c; 115 | 116 | for(i = 0, e = out_faces.length; i < e; ++i) { 117 | face = out_faces[i]; 118 | a = face.a * 3; 119 | b = face.b * 3; 120 | c = face.c * 3; 121 | face.vertexNormals = [ 122 | new THREE.Vector3( in_nor[ a ], in_nor[ a + 1 ], in_nor[ a + 2 ] ), 123 | new THREE.Vector3( in_nor[ b ], in_nor[ b + 1 ], in_nor[ b + 2 ] ), 124 | new THREE.Vector3( in_nor[ c ], in_nor[ c + 1 ], in_nor[ c + 2 ] ) 125 | ]; 126 | } 127 | } 128 | 129 | convertNormals(json.normals, geometry.faces); 130 | } 131 | 132 | // read vertex colors - three.js also attaches them to its faces 133 | if(json.colors && json.colors[0]) { 134 | 135 | function convertColors(in_color, out_faces) { 136 | var i, e, face, a, b, c; 137 | 138 | function makeColor(start) { 139 | var col = new THREE.Color( ); 140 | col.setRGB( arr[0], arr[1], arr[2] ); 141 | // TODO: what about alpha? 142 | return col; 143 | } 144 | 145 | for(i = 0, e = out_faces.length; i < e; ++i) { 146 | face = out_faces[i]; 147 | a = face.a * 4; 148 | b = face.b * 4; 149 | c = face.c * 4; 150 | face.vertexColors = [ 151 | makeColor( a ), 152 | makeColor( b ), 153 | makeColor( c ) 154 | ]; 155 | } 156 | } 157 | 158 | convertColors(json.colors[0], geometry.faces); 159 | } 160 | 161 | 162 | //geometry.computeFaceNormals(); 163 | //geometry.computeVertexNormals(); 164 | //geometry.computeTangents(); 165 | geometry.computeBoundingSphere(); 166 | 167 | // TODO: tangents 168 | return geometry; 169 | }, 170 | 171 | parseMaterial : function(json) { 172 | var mat = null, 173 | scope = this, i, prop, has_textures = [], 174 | 175 | init_props = { 176 | shading : THREE.SmoothShading 177 | }; 178 | 179 | function toColor(value_arr) { 180 | var col = new THREE.Color(); 181 | col.setRGB(value_arr[0],value_arr[1],value_arr[2]); 182 | return col; 183 | } 184 | 185 | function defaultTexture() { 186 | var im = new Image(); 187 | im.width = 1; 188 | im.height = 1; 189 | return new THREE.Texture(im); 190 | } 191 | 192 | for (var i in json.properties) { 193 | prop = json.properties[i]; 194 | 195 | if(prop.key === '$tex.file') { 196 | // prop.semantic gives the type of the texture 197 | // 1: diffuse 198 | // 2: specular mao 199 | // 5: height map (bumps) 200 | // 6: normal map 201 | // more values (i.e. emissive, environment) are known by assimp and may be relevant 202 | if(prop.semantic === 1 || prop.semantic === 5 || prop.semantic === 6 || prop.semantic === 2) { 203 | (function(semantic) { 204 | var loader = new THREE.TextureLoader(scope.manager), 205 | keyname; 206 | 207 | if(semantic === 1) { 208 | keyname = 'map'; 209 | } 210 | else if(semantic === 5) { 211 | keyname = 'bumpMap'; 212 | } 213 | else if(semantic === 6) { 214 | keyname = 'normalMap'; 215 | } 216 | else if(semantic === 2) { 217 | keyname = 'specularMap'; 218 | } 219 | 220 | has_textures.push(keyname); 221 | 222 | loader.setCrossOrigin(this.crossOrigin); 223 | var material_url = scope.texturePath + '/' + prop.value 224 | material_url = material_url.replace(/\\/g, '/'); 225 | loader.load(material_url, function(tex) { 226 | if(tex) { 227 | // TODO: read texture settings from assimp. 228 | // Wrapping is the default, though. 229 | tex.wrapS = tex.wrapT = THREE.RepeatWrapping; 230 | 231 | mat[keyname] = tex; 232 | mat.needsUpdate = true; 233 | } 234 | }); 235 | })(prop.semantic); 236 | } 237 | } 238 | else if(prop.key === '?mat.name') { 239 | init_props.name = prop.value; 240 | } 241 | else if(prop.key === '$clr.diffuse') { 242 | init_props.color = toColor(prop.value); 243 | } 244 | else if(prop.key === '$clr.specular') { 245 | init_props.specular = toColor(prop.value); 246 | } 247 | else if(prop.key === '$clr.ambient') { 248 | init_props.ambient = toColor(prop.value); 249 | } 250 | else if(prop.key === '$clr.emissive') { 251 | init_props.emissive = toColor(prop.value); 252 | } 253 | else if(prop.key === '$mat.shadingm') { 254 | // aiShadingMode_Flat 255 | if (prop.value === 1) { 256 | init_props.shading = THREE.FlatShading; 257 | } 258 | } 259 | else if (prop.key === '$mat.shininess') { 260 | init_props.shininess = prop.value; 261 | } 262 | } 263 | 264 | if(!init_props.ambient) { 265 | init_props.ambient = init_props.color; 266 | } 267 | 268 | // note: three.js does not like it when a texture is added after the geometry 269 | // has been rendered once, see http://stackoverflow.com/questions/16531759/. 270 | // for this reason we fill all slots upfront with default textures 271 | if(has_textures.length) { 272 | for(i = has_textures.length-1; i >= 0; --i) { 273 | init_props[has_textures[i]] = defaultTexture(); 274 | } 275 | } 276 | 277 | mat = new THREE.MeshPhongMaterial( init_props ); 278 | return mat; 279 | }, 280 | 281 | parseObject : function(json, node, meshes, materials) { 282 | var obj = new THREE.Object3D() 283 | , i 284 | , idx 285 | ; 286 | 287 | obj.name = node.name || ""; 288 | obj.matrix = new THREE.Matrix4().fromArray(node.transformation).transpose(); 289 | obj.matrix.decompose( obj.position, obj.quaternion, obj.scale ); 290 | 291 | for(i = 0; node.meshes && i < node.meshes.length; ++i) { 292 | idx = node.meshes[i]; 293 | obj.add(new THREE.Mesh( meshes[idx], materials[json.meshes[idx].materialindex] )); 294 | } 295 | 296 | for(i = 0; node.children && i < node.children.length; ++i) { 297 | obj.add(this.parseObject(json, node.children[i], meshes, materials)); 298 | } 299 | 300 | return obj; 301 | }, 302 | }; 303 | 304 | 305 | -------------------------------------------------------------------------------- /public/js/loaders/OBJMTLLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Loads a Wavefront .obj file with materials 3 | * 4 | * @author mrdoob / http://mrdoob.com/ 5 | * @author angelxuanchang 6 | */ 7 | 8 | THREE.OBJMTLLoader = function () {}; 9 | 10 | THREE.OBJMTLLoader.prototype = { 11 | 12 | constructor: THREE.OBJMTLLoader, 13 | 14 | load: function ( url, mtlurl, onLoad, onProgress, onError ) { 15 | 16 | var scope = this; 17 | 18 | var mtlLoader = new THREE.MTLLoader( url.substr( 0, url.lastIndexOf( "/" ) + 1 ) ); 19 | mtlLoader.load( mtlurl, function ( materials ) { 20 | 21 | var materialsCreator = materials; 22 | materialsCreator.preload(); 23 | 24 | var loader = new THREE.XHRLoader( scope.manager ); 25 | loader.setCrossOrigin( this.crossOrigin ); 26 | loader.load( url, function ( text ) { 27 | 28 | var object = scope.parse( text ); 29 | 30 | object.traverse( function ( object ) { 31 | 32 | if ( object instanceof THREE.Mesh ) { 33 | 34 | if ( object.material.name ) { 35 | 36 | var material = materialsCreator.create( object.material.name ); 37 | 38 | if ( material ) object.material = material; 39 | 40 | } 41 | 42 | } 43 | 44 | } ); 45 | 46 | onLoad( object ); 47 | 48 | } ); 49 | 50 | } ); 51 | 52 | }, 53 | 54 | /** 55 | * Parses loaded .obj file 56 | * @param data - content of .obj file 57 | * @param mtllibCallback - callback to handle mtllib declaration (optional) 58 | * @return {THREE.Object3D} - Object3D (with default material) 59 | */ 60 | 61 | parse: function ( data, mtllibCallback ) { 62 | 63 | function vector( x, y, z ) { 64 | 65 | return new THREE.Vector3( x, y, z ); 66 | 67 | } 68 | 69 | function uv( u, v ) { 70 | 71 | return new THREE.Vector2( u, v ); 72 | 73 | } 74 | 75 | function face3( a, b, c, normals ) { 76 | 77 | return new THREE.Face3( a, b, c, normals ); 78 | 79 | } 80 | 81 | var face_offset = 0; 82 | 83 | function meshN( meshName, materialName ) { 84 | 85 | if ( vertices.length > 0 ) { 86 | 87 | geometry.vertices = vertices; 88 | 89 | geometry.mergeVertices(); 90 | geometry.computeFaceNormals(); 91 | geometry.computeBoundingSphere(); 92 | 93 | object.add( mesh ); 94 | 95 | geometry = new THREE.Geometry(); 96 | mesh = new THREE.Mesh( geometry, material ); 97 | 98 | } 99 | 100 | if ( meshName !== undefined ) mesh.name = meshName; 101 | 102 | if ( materialName !== undefined ) { 103 | 104 | material = new THREE.MeshLambertMaterial(); 105 | material.name = materialName; 106 | 107 | mesh.material = material; 108 | 109 | } 110 | 111 | } 112 | 113 | var group = new THREE.Object3D(); 114 | var object = group; 115 | 116 | var geometry = new THREE.Geometry(); 117 | var material = new THREE.MeshLambertMaterial(); 118 | var mesh = new THREE.Mesh( geometry, material ); 119 | 120 | var vertices = []; 121 | var normals = []; 122 | var uvs = []; 123 | 124 | function add_face( a, b, c, normals_inds ) { 125 | 126 | if ( normals_inds === undefined ) { 127 | 128 | geometry.faces.push( face3( 129 | parseInt( a ) - (face_offset + 1), 130 | parseInt( b ) - (face_offset + 1), 131 | parseInt( c ) - (face_offset + 1) 132 | ) ); 133 | 134 | } else { 135 | 136 | geometry.faces.push( face3( 137 | parseInt( a ) - (face_offset + 1), 138 | parseInt( b ) - (face_offset + 1), 139 | parseInt( c ) - (face_offset + 1), 140 | [ 141 | normals[ parseInt( normals_inds[ 0 ] ) - 1 ].clone(), 142 | normals[ parseInt( normals_inds[ 1 ] ) - 1 ].clone(), 143 | normals[ parseInt( normals_inds[ 2 ] ) - 1 ].clone() 144 | ] 145 | ) ); 146 | 147 | } 148 | 149 | } 150 | 151 | function add_uvs( a, b, c ) { 152 | 153 | geometry.faceVertexUvs[ 0 ].push( [ 154 | uvs[ parseInt( a ) - 1 ].clone(), 155 | uvs[ parseInt( b ) - 1 ].clone(), 156 | uvs[ parseInt( c ) - 1 ].clone() 157 | ] ); 158 | 159 | } 160 | 161 | function handle_face_line(faces, uvs, normals_inds) { 162 | 163 | if ( faces[ 3 ] === undefined ) { 164 | 165 | add_face( faces[ 0 ], faces[ 1 ], faces[ 2 ], normals_inds ); 166 | 167 | if (!(uvs === undefined) && uvs.length > 0) { 168 | add_uvs( uvs[ 0 ], uvs[ 1 ], uvs[ 2 ] ); 169 | } 170 | 171 | } else { 172 | 173 | if (!(normals_inds === undefined) && normals_inds.length > 0) { 174 | 175 | add_face( faces[ 0 ], faces[ 1 ], faces[ 3 ], [ normals_inds[ 0 ], normals_inds[ 1 ], normals_inds[ 3 ] ]); 176 | add_face( faces[ 1 ], faces[ 2 ], faces[ 3 ], [ normals_inds[ 1 ], normals_inds[ 2 ], normals_inds[ 3 ] ]); 177 | 178 | } else { 179 | 180 | add_face( faces[ 0 ], faces[ 1 ], faces[ 3 ]); 181 | add_face( faces[ 1 ], faces[ 2 ], faces[ 3 ]); 182 | 183 | } 184 | 185 | if (!(uvs === undefined) && uvs.length > 0) { 186 | 187 | add_uvs( uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ); 188 | add_uvs( uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ); 189 | 190 | } 191 | 192 | } 193 | 194 | } 195 | 196 | 197 | // v float float float 198 | 199 | var vertex_pattern = /v( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 200 | 201 | // vn float float float 202 | 203 | var normal_pattern = /vn( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 204 | 205 | // vt float float 206 | 207 | var uv_pattern = /vt( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 208 | 209 | // f vertex vertex vertex ... 210 | 211 | var face_pattern1 = /f( +\d+)( +\d+)( +\d+)( +\d+)?/; 212 | 213 | // f vertex/uv vertex/uv vertex/uv ... 214 | 215 | var face_pattern2 = /f( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))?/; 216 | 217 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... 218 | 219 | var face_pattern3 = /f( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))?/; 220 | 221 | // f vertex//normal vertex//normal vertex//normal ... 222 | 223 | var face_pattern4 = /f( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))?/ 224 | 225 | // 226 | 227 | var lines = data.split( "\n" ); 228 | 229 | for ( var i = 0; i < lines.length; i ++ ) { 230 | 231 | var line = lines[ i ]; 232 | line = line.trim(); 233 | 234 | var result; 235 | 236 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) { 237 | 238 | continue; 239 | 240 | } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) { 241 | 242 | // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 243 | 244 | vertices.push( vector( 245 | parseFloat( result[ 1 ] ), 246 | parseFloat( result[ 2 ] ), 247 | parseFloat( result[ 3 ] ) 248 | ) ); 249 | 250 | } else if ( ( result = normal_pattern.exec( line ) ) !== null ) { 251 | 252 | // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 253 | 254 | normals.push( vector( 255 | parseFloat( result[ 1 ] ), 256 | parseFloat( result[ 2 ] ), 257 | parseFloat( result[ 3 ] ) 258 | ) ); 259 | 260 | } else if ( ( result = uv_pattern.exec( line ) ) !== null ) { 261 | 262 | // ["vt 0.1 0.2", "0.1", "0.2"] 263 | 264 | uvs.push( uv( 265 | parseFloat( result[ 1 ] ), 266 | parseFloat( result[ 2 ] ) 267 | ) ); 268 | 269 | } else if ( ( result = face_pattern1.exec( line ) ) !== null ) { 270 | 271 | // ["f 1 2 3", "1", "2", "3", undefined] 272 | 273 | handle_face_line([ result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ] ]); 274 | 275 | } else if ( ( result = face_pattern2.exec( line ) ) !== null ) { 276 | 277 | // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] 278 | 279 | handle_face_line( 280 | [ result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ], //faces 281 | [ result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ] //uv 282 | ); 283 | 284 | } else if ( ( result = face_pattern3.exec( line ) ) !== null ) { 285 | 286 | // ["f 1/1/1 2/2/2 3/3/3", " 1/1/1", "1", "1", "1", " 2/2/2", "2", "2", "2", " 3/3/3", "3", "3", "3", undefined, undefined, undefined, undefined] 287 | 288 | handle_face_line( 289 | [ result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ] ], //faces 290 | [ result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ] ], //uv 291 | [ result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] ] //normal 292 | ); 293 | 294 | } else if ( ( result = face_pattern4.exec( line ) ) !== null ) { 295 | 296 | // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] 297 | 298 | handle_face_line( 299 | [ result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ], //faces 300 | [ ], //uv 301 | [ result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ] //normal 302 | ); 303 | 304 | } else if ( /^o /.test( line ) ) { 305 | 306 | // object 307 | 308 | meshN(); 309 | face_offset = face_offset + vertices.length; 310 | vertices = []; 311 | object = new THREE.Object3D(); 312 | object.name = line.substring( 2 ).trim(); 313 | group.add( object ); 314 | 315 | } else if ( /^g /.test( line ) ) { 316 | 317 | // group 318 | 319 | meshN( line.substring( 2 ).trim(), undefined ); 320 | 321 | } else if ( /^usemtl /.test( line ) ) { 322 | 323 | // material 324 | 325 | meshN( undefined, line.substring( 7 ).trim() ); 326 | 327 | } else if ( /^mtllib /.test( line ) ) { 328 | 329 | // mtl file 330 | 331 | if ( mtllibCallback ) { 332 | 333 | var mtlfile = line.substring( 7 ); 334 | mtlfile = mtlfile.trim(); 335 | mtllibCallback( mtlfile ); 336 | 337 | } 338 | 339 | } else if ( /^s /.test( line ) ) { 340 | 341 | // Smooth shading 342 | 343 | } else { 344 | 345 | console.log( "THREE.OBJMTLLoader: Unhandled line " + line ); 346 | 347 | } 348 | 349 | } 350 | 351 | //Add last object 352 | meshN(undefined, undefined); 353 | 354 | return group; 355 | 356 | } 357 | 358 | }; 359 | 360 | THREE.EventDispatcher.prototype.apply( THREE.OBJMTLLoader.prototype ); 361 | -------------------------------------------------------------------------------- /public/js/loaders/MTLLoader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Loads a Wavefront .mtl file specifying materials 3 | * 4 | * @author angelxuanchang 5 | */ 6 | 7 | THREE.MTLLoader = function( baseUrl, options, crossOrigin ) { 8 | 9 | this.baseUrl = baseUrl; 10 | this.options = options; 11 | this.crossOrigin = crossOrigin; 12 | 13 | }; 14 | 15 | THREE.MTLLoader.prototype = { 16 | 17 | constructor: THREE.MTLLoader, 18 | 19 | load: function ( url, onLoad, onProgress, onError ) { 20 | 21 | var scope = this; 22 | 23 | var loader = new THREE.XHRLoader(); 24 | loader.setCrossOrigin( this.crossOrigin ); 25 | loader.load( url, function ( text ) { 26 | 27 | onLoad( scope.parse( text ) ); 28 | 29 | } ); 30 | 31 | }, 32 | 33 | /** 34 | * Parses loaded MTL file 35 | * @param text - Content of MTL file 36 | * @return {THREE.MTLLoader.MaterialCreator} 37 | */ 38 | parse: function ( text ) { 39 | 40 | var lines = text.split( "\n" ); 41 | var info = {}; 42 | var delimiter_pattern = /\s+/; 43 | var materialsInfo = {}; 44 | 45 | for ( var i = 0; i < lines.length; i ++ ) { 46 | 47 | var line = lines[ i ]; 48 | line = line.trim(); 49 | 50 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) { 51 | 52 | // Blank line or comment ignore 53 | continue; 54 | 55 | } 56 | 57 | var pos = line.indexOf( ' ' ); 58 | 59 | var key = ( pos >= 0 ) ? line.substring( 0, pos ) : line; 60 | key = key.toLowerCase(); 61 | 62 | var value = ( pos >= 0 ) ? line.substring( pos + 1 ) : ""; 63 | value = value.trim(); 64 | 65 | if ( key === "newmtl" ) { 66 | 67 | // New material 68 | 69 | info = { name: value }; 70 | materialsInfo[ value ] = info; 71 | 72 | } else if ( info ) { 73 | 74 | if ( key === "ka" || key === "kd" || key === "ks" ) { 75 | 76 | var ss = value.split( delimiter_pattern, 3 ); 77 | info[ key ] = [ parseFloat( ss[0] ), parseFloat( ss[1] ), parseFloat( ss[2] ) ]; 78 | 79 | } else { 80 | 81 | info[ key ] = value; 82 | 83 | } 84 | 85 | } 86 | 87 | } 88 | 89 | var materialCreator = new THREE.MTLLoader.MaterialCreator( this.baseUrl, this.options ); 90 | materialCreator.setMaterials( materialsInfo ); 91 | return materialCreator; 92 | 93 | } 94 | 95 | }; 96 | 97 | /** 98 | * Create a new THREE-MTLLoader.MaterialCreator 99 | * @param baseUrl - Url relative to which textures are loaded 100 | * @param options - Set of options on how to construct the materials 101 | * side: Which side to apply the material 102 | * THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide 103 | * wrap: What type of wrapping to apply for textures 104 | * THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping 105 | * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255 106 | * Default: false, assumed to be already normalized 107 | * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's 108 | * Default: false 109 | * invertTransparency: If transparency need to be inverted (inversion is needed if d = 0 is fully opaque) 110 | * Default: false (d = 1 is fully opaque) 111 | * @constructor 112 | */ 113 | 114 | THREE.MTLLoader.MaterialCreator = function( baseUrl, options ) { 115 | 116 | this.baseUrl = baseUrl; 117 | this.options = options; 118 | this.materialsInfo = {}; 119 | this.materials = {}; 120 | this.materialsArray = []; 121 | this.nameLookup = {}; 122 | 123 | this.side = ( this.options && this.options.side )? this.options.side: THREE.FrontSide; 124 | this.wrap = ( this.options && this.options.wrap )? this.options.wrap: THREE.RepeatWrapping; 125 | 126 | }; 127 | 128 | THREE.MTLLoader.MaterialCreator.prototype = { 129 | 130 | constructor: THREE.MTLLoader.MaterialCreator, 131 | 132 | setMaterials: function( materialsInfo ) { 133 | 134 | this.materialsInfo = this.convert( materialsInfo ); 135 | this.materials = {}; 136 | this.materialsArray = []; 137 | this.nameLookup = {}; 138 | 139 | }, 140 | 141 | convert: function( materialsInfo ) { 142 | 143 | if ( !this.options ) return materialsInfo; 144 | 145 | var converted = {}; 146 | 147 | for ( var mn in materialsInfo ) { 148 | 149 | // Convert materials info into normalized form based on options 150 | 151 | var mat = materialsInfo[ mn ]; 152 | 153 | var covmat = {}; 154 | 155 | converted[ mn ] = covmat; 156 | 157 | for ( var prop in mat ) { 158 | 159 | var save = true; 160 | var value = mat[ prop ]; 161 | var lprop = prop.toLowerCase(); 162 | 163 | switch ( lprop ) { 164 | 165 | case 'kd': 166 | case 'ka': 167 | case 'ks': 168 | 169 | // Diffuse color (color under white light) using RGB values 170 | 171 | if ( this.options && this.options.normalizeRGB ) { 172 | 173 | value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ]; 174 | 175 | } 176 | 177 | if ( this.options && this.options.ignoreZeroRGBs ) { 178 | 179 | if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 1 ] === 0 ) { 180 | 181 | // ignore 182 | 183 | save = false; 184 | 185 | } 186 | } 187 | 188 | break; 189 | 190 | case 'd': 191 | 192 | // According to MTL format (http://paulbourke.net/dataformats/mtl/): 193 | // d is dissolve for current material 194 | // factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent) 195 | 196 | if ( this.options && this.options.invertTransparency ) { 197 | 198 | value = 1 - value; 199 | 200 | } 201 | 202 | break; 203 | 204 | default: 205 | 206 | break; 207 | } 208 | 209 | if ( save ) { 210 | 211 | covmat[ lprop ] = value; 212 | 213 | } 214 | 215 | } 216 | 217 | } 218 | 219 | return converted; 220 | 221 | }, 222 | 223 | preload: function () { 224 | 225 | for ( var mn in this.materialsInfo ) { 226 | 227 | this.create( mn ); 228 | 229 | } 230 | 231 | }, 232 | 233 | getIndex: function( materialName ) { 234 | 235 | return this.nameLookup[ materialName ]; 236 | 237 | }, 238 | 239 | getAsArray: function() { 240 | 241 | var index = 0; 242 | 243 | for ( var mn in this.materialsInfo ) { 244 | 245 | this.materialsArray[ index ] = this.create( mn ); 246 | this.nameLookup[ mn ] = index; 247 | index ++; 248 | 249 | } 250 | 251 | return this.materialsArray; 252 | 253 | }, 254 | 255 | create: function ( materialName ) { 256 | 257 | if ( this.materials[ materialName ] === undefined ) { 258 | 259 | this.createMaterial_( materialName ); 260 | 261 | } 262 | 263 | return this.materials[ materialName ]; 264 | 265 | }, 266 | 267 | createMaterial_: function ( materialName ) { 268 | 269 | // Create material 270 | 271 | var mat = this.materialsInfo[ materialName ]; 272 | var params = { 273 | 274 | name: materialName, 275 | side: this.side 276 | 277 | }; 278 | 279 | for ( var prop in mat ) { 280 | 281 | var value = mat[ prop ]; 282 | 283 | switch ( prop.toLowerCase() ) { 284 | 285 | // Ns is material specular exponent 286 | 287 | case 'kd': 288 | 289 | // Diffuse color (color under white light) using RGB values 290 | 291 | params[ 'diffuse' ] = new THREE.Color().fromArray( value ); 292 | 293 | break; 294 | 295 | case 'ka': 296 | 297 | // Ambient color (color under shadow) using RGB values 298 | 299 | params[ 'ambient' ] = new THREE.Color().fromArray( value ); 300 | 301 | break; 302 | 303 | case 'ks': 304 | 305 | // Specular color (color when light is reflected from shiny surface) using RGB values 306 | params[ 'specular' ] = new THREE.Color().fromArray( value ); 307 | 308 | break; 309 | 310 | case 'map_kd': 311 | 312 | // Diffuse texture map 313 | 314 | params[ 'map' ] = this.loadTexture( this.baseUrl + value ); 315 | params[ 'map' ].wrapS = this.wrap; 316 | params[ 'map' ].wrapT = this.wrap; 317 | 318 | break; 319 | 320 | case 'ns': 321 | 322 | // The specular exponent (defines the focus of the specular highlight) 323 | // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000. 324 | 325 | params['shininess'] = value; 326 | 327 | break; 328 | 329 | case 'd': 330 | 331 | // According to MTL format (http://paulbourke.net/dataformats/mtl/): 332 | // d is dissolve for current material 333 | // factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent) 334 | 335 | if ( value < 1 ) { 336 | 337 | params['transparent'] = true; 338 | params['opacity'] = value; 339 | 340 | } 341 | 342 | break; 343 | 344 | default: 345 | break; 346 | 347 | } 348 | 349 | } 350 | 351 | if ( params[ 'diffuse' ] ) { 352 | 353 | if ( !params[ 'ambient' ]) params[ 'ambient' ] = params[ 'diffuse' ]; 354 | params[ 'color' ] = params[ 'diffuse' ]; 355 | 356 | } 357 | 358 | this.materials[ materialName ] = new THREE.MeshPhongMaterial( params ); 359 | return this.materials[ materialName ]; 360 | 361 | }, 362 | 363 | 364 | loadTexture: function ( url, mapping, onLoad, onError ) { 365 | 366 | var texture; 367 | var loader = THREE.Loader.Handlers.get( url ); 368 | 369 | if ( loader !== null ) { 370 | 371 | texture = loader.load( url, onLoad ); 372 | 373 | } else { 374 | 375 | texture = new THREE.Texture(); 376 | 377 | loader = new THREE.ImageLoader(); 378 | loader.crossOrigin = this.crossOrigin; 379 | loader.load( url, function ( image ) { 380 | 381 | texture.image = THREE.MTLLoader.ensurePowerOfTwo_( image ); 382 | texture.needsUpdate = true; 383 | 384 | if ( onLoad ) onLoad( texture ); 385 | 386 | } ); 387 | 388 | } 389 | 390 | texture.mapping = mapping; 391 | 392 | return texture; 393 | 394 | } 395 | 396 | }; 397 | 398 | THREE.MTLLoader.ensurePowerOfTwo_ = function ( image ) { 399 | 400 | if ( ! THREE.Math.isPowerOfTwo( image.width ) || ! THREE.Math.isPowerOfTwo( image.height ) ) { 401 | 402 | var canvas = document.createElement( "canvas" ); 403 | canvas.width = THREE.MTLLoader.nextHighestPowerOfTwo_( image.width ); 404 | canvas.height = THREE.MTLLoader.nextHighestPowerOfTwo_( image.height ); 405 | 406 | var ctx = canvas.getContext("2d"); 407 | ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); 408 | return canvas; 409 | 410 | } 411 | 412 | return image; 413 | 414 | }; 415 | 416 | THREE.MTLLoader.nextHighestPowerOfTwo_ = function( x ) { 417 | 418 | --x; 419 | 420 | for ( var i = 1; i < 32; i <<= 1 ) { 421 | 422 | x = x | x >> i; 423 | 424 | } 425 | 426 | return x + 1; 427 | 428 | }; 429 | 430 | THREE.EventDispatcher.prototype.apply( THREE.MTLLoader.prototype ); 431 | --------------------------------------------------------------------------------