├── tmp └── .gitignore ├── online ├── assets │ ├── data │ │ └── .gitignore │ ├── js │ │ └── .gitignore │ ├── img │ │ ├── perlin-512.png │ │ ├── waternormals.jpg │ │ ├── star_preview3.png │ │ ├── skybox3-hr │ │ │ ├── skybox_0.png │ │ │ ├── skybox_1.png │ │ │ ├── skybox_2.png │ │ │ ├── skybox_3.png │ │ │ ├── skybox_4.png │ │ │ └── skybox_5.png │ │ └── skybox3-lr │ │ │ ├── skybox_0.png │ │ │ ├── skybox_1.png │ │ │ ├── skybox_2.png │ │ │ ├── skybox_3.png │ │ │ ├── skybox_4.png │ │ │ └── skybox_5.png │ └── src │ │ ├── config │ │ └── WebVRConfig.js │ │ ├── vendor │ │ ├── modernizr │ │ │ └── modernizr-custom.js │ │ ├── three │ │ │ └── examples │ │ │ │ └── js │ │ │ │ └── controls │ │ │ │ └── MouseControls.js │ │ ├── zz85 │ │ │ └── SkyShader.js │ │ └── charliehoey │ │ │ └── GPUParticleSystem.js │ │ └── index.js ├── .htaccess ├── bower.json ├── index.html ├── package.json └── webpack.config.js ├── .gitignore ├── scripts ├── setup.py └── csv-to-json.py └── README.md /tmp/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /online/assets/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /online/assets/js/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /online/assets/img/perlin-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/perlin-512.png -------------------------------------------------------------------------------- /online/assets/img/waternormals.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/waternormals.jpg -------------------------------------------------------------------------------- /online/assets/img/star_preview3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/star_preview3.png -------------------------------------------------------------------------------- /online/assets/img/skybox3-hr/skybox_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/skybox3-hr/skybox_0.png -------------------------------------------------------------------------------- /online/assets/img/skybox3-hr/skybox_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/skybox3-hr/skybox_1.png -------------------------------------------------------------------------------- /online/assets/img/skybox3-hr/skybox_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/skybox3-hr/skybox_2.png -------------------------------------------------------------------------------- /online/assets/img/skybox3-hr/skybox_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/skybox3-hr/skybox_3.png -------------------------------------------------------------------------------- /online/assets/img/skybox3-hr/skybox_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/skybox3-hr/skybox_4.png -------------------------------------------------------------------------------- /online/assets/img/skybox3-hr/skybox_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/skybox3-hr/skybox_5.png -------------------------------------------------------------------------------- /online/assets/img/skybox3-lr/skybox_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/skybox3-lr/skybox_0.png -------------------------------------------------------------------------------- /online/assets/img/skybox3-lr/skybox_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/skybox3-lr/skybox_1.png -------------------------------------------------------------------------------- /online/assets/img/skybox3-lr/skybox_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/skybox3-lr/skybox_2.png -------------------------------------------------------------------------------- /online/assets/img/skybox3-lr/skybox_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/skybox3-lr/skybox_3.png -------------------------------------------------------------------------------- /online/assets/img/skybox3-lr/skybox_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/skybox3-lr/skybox_4.png -------------------------------------------------------------------------------- /online/assets/img/skybox3-lr/skybox_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UXVirtual/night-sky/HEAD/online/assets/img/skybox3-lr/skybox_5.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | scripts/CSVToJSON.egg-info 4 | scripts/dist 5 | online/assets/js/data.json 6 | online/bower_components 7 | -------------------------------------------------------------------------------- /online/.htaccess: -------------------------------------------------------------------------------- 1 | AddEncoding gzip .gz 2 | 3 | RewriteEngine on 4 | RewriteCond %{HTTP:Accept-encoding} gzip 5 | RewriteCond %{HTTP_USER_AGENT} !Konqueror 6 | RewriteCond %{REQUEST_FILENAME}.gz -f 7 | RewriteRule ^(.*)\.js$ $1.js.gz [QSA,L] -------------------------------------------------------------------------------- /scripts/setup.py: -------------------------------------------------------------------------------- 1 | #to setup CSV To JSON run: `python setup.py install` 2 | 3 | from setuptools import setup 4 | 5 | setup( 6 | name="CSVToJSON", 7 | version="0.1", 8 | install_requires=[ 9 | "pandas" 10 | ] 11 | ) -------------------------------------------------------------------------------- /online/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "olympics-vr-prototype", 3 | "description": "A prototype of the night sky VR functionality.", 4 | "main": "index.js", 5 | "authors": [ 6 | "Michael Andrew (michael.andrew@gladeye.co.nz)" 7 | ], 8 | "moduleType": [], 9 | "homepage": "", 10 | "private": true, 11 | "ignore": [ 12 | "**/.*", 13 | "node_modules", 14 | "bower_components", 15 | "test", 16 | "tests" 17 | ], 18 | "dependencies": { 19 | "ocean": "https://github.com/jbouny/ocean.git", 20 | "modernizr": "~3.3.1" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /online/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Night Sky Viewer 7 | 8 | 9 | 10 | 11 | 12 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /online/assets/src/config/WebVRConfig.js: -------------------------------------------------------------------------------- 1 | window.WebVRConfig = { 2 | /** 3 | * webvr-polyfill configuration 4 | */ 5 | 6 | // Forces availability of VR mode. 7 | FORCE_ENABLE_VR: false, // Default: false. 8 | // Complementary filter coefficient. 0 for accelerometer, 1 for gyro. 9 | K_FILTER: 0.98, // Default: 0.98. 10 | // How far into the future to predict during fast motion. 11 | PREDICTION_TIME_S: 0.040, // Default: 0.040 (in seconds). 12 | // Flag to disable touch panner. In case you have your own touch controls 13 | TOUCH_PANNER_DISABLED: false, // Default: false. 14 | // Enable yaw panning only, disabling roll and pitch. This can be useful for 15 | // panoramas with nothing interesting above or below. 16 | YAW_ONLY: false, // Default: false. 17 | MOUSE_KEYBOARD_CONTROLS_DISABLED: false, 18 | 19 | /** 20 | * webvr-boilerplate configuration 21 | */ 22 | // Forces distortion in VR mode. 23 | FORCE_DISTORTION: false, // Default: false. 24 | // Override the distortion background color. 25 | // DISTORTION_BGCOLOR: {x: 1, y: 0, z: 0, w: 1}, // Default: (0,0,0,1). 26 | // Prevent distortion from happening. 27 | PREVENT_DISTORTION: false, // Default: false. 28 | // Show eye centers for debugging. 29 | SHOW_EYE_CENTERS: false, // Default: false. 30 | // Prevent the online DPDB from being fetched. 31 | NO_DPDB_FETCH: true // Default: false. 32 | }; -------------------------------------------------------------------------------- /online/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "olympics-vr-prototype", 3 | "version": "0.1.1", 4 | "description": "A prototype of the night sky VR functionality.", 5 | "main": "index.js", 6 | "dependencies": { 7 | "dat-gui": "^0.5.0", 8 | "exports-loader": "^0.6.3", 9 | "fpsmeter": "git+https://github.com/darsain/fpsmeter.git#v0.3.1", 10 | "imports-loader": "^0.6.5", 11 | "jquery": "^2.2.1", 12 | "jquery-modal": "^0.6.1", 13 | "mobile-detect": "^1.3.1", 14 | "perfnow": "^0.2.0", 15 | "rollup": "^0.25.4", 16 | "stats.js": "^0.0.14", 17 | "three": "^0.75.0", 18 | "three-loaders-collada": "^1.0.1", 19 | "three-text2d": "^0.2.2", 20 | "tiny-popup": "^0.1.11", 21 | "webvr-boilerplate": "^0.3.7", 22 | "webvr-polyfill": "^0.2.7" 23 | }, 24 | "devDependencies": { 25 | "babel-core": "^6.6.0", 26 | "babel-loader": "^6.2.4", 27 | "babel-preset-es2015": "^6.6.0", 28 | "babel-preset-react": "^6.5.0", 29 | "css-loader": "^0.23.1", 30 | "expose-loader": "^0.7.1", 31 | "json-loader": "^0.5.4", 32 | "style-loader": "^0.13.0", 33 | "webpack": "^1.13.1", 34 | "webpack-dev-server": "^1.14.1" 35 | }, 36 | "scripts": { 37 | "test": "echo \"Error: no test specified\" && exit 1" 38 | }, 39 | "repository": { 40 | "type": "git", 41 | "url": "https://git.gladeye.org/michael/olympics-app-prototype.git" 42 | }, 43 | "author": "Michael Andrew (michael.andrew@gladeye.co.nz)" 44 | } 45 | -------------------------------------------------------------------------------- /online/webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var path = require("path"); 3 | 4 | module.exports = { 5 | entry: "./assets/src/index.js", 6 | output: { 7 | path: './assets/js', 8 | 9 | filename: "bundle.js" 10 | }, 11 | module: { 12 | loaders: [ 13 | { 14 | test: /\.jsx?$/, 15 | exclude: /(node_modules|bower_components)/, 16 | loader: 'babel', // 'babel-loader' is also a legal name to reference 17 | query: { 18 | presets: ['react', 'es2015'] 19 | } 20 | }, 21 | { test: /\.css$/, loader: "style-loader!css-loader" } 22 | ] 23 | }, 24 | resolve: [ 25 | { 26 | root: [path.join(__dirname, "bower_components")] 27 | } 28 | ], 29 | plugins: [ 30 | new webpack.ProvidePlugin({ 31 | THREE: "three", 32 | "window.THREE": "three" 33 | }), 34 | new webpack.ResolverPlugin( 35 | new webpack.ResolverPlugin.DirectoryDescriptionFilePlugin(".bower.json", ["main"]) 36 | ), 37 | new webpack.ProvidePlugin({ 38 | $: "jquery", 39 | jQuery: "jquery", 40 | "window.jQuery": "jquery" 41 | }), 42 | new webpack.ProvidePlugin({ 43 | 'performance': 'imports?this=>global!exports?global.performance!perfnow' 44 | }) 45 | ] 46 | }; 47 | 48 | -------------------------------------------------------------------------------- /scripts/csv-to-json.py: -------------------------------------------------------------------------------- 1 | # 2 | # CSV To JSON Converson Script 3 | # 4 | # @author Michael Andrew (michael.andrew@gladeye.co.nz) 5 | # @usage python csv-to-json.py ../tmp/data.csv ../online/assets/data/data.json 6 | # 7 | 8 | import csv 9 | import json 10 | import sys 11 | import urllib2 12 | import gzip 13 | import pandas 14 | import numpy as np 15 | 16 | URL = "https://github.com/astronexus/HYG-Database/raw/master/hygdata_v3.csv" #url to star data csv on GitHub 17 | csvFilePath = sys.argv[1] #destination path of downloaded csv file 18 | jsonFilePath = sys.argv[2] #destination path of converted json file 19 | 20 | print "Downloading star data CSV... This may take a few minutes..." 21 | with open(csvFilePath,'wb') as f: 22 | f.write(urllib2.urlopen(URL).read()) 23 | f.close() 24 | print "Download completed. Converting target columns from CSV to GZipped JSON..." 25 | 26 | jsonfileGzipped = gzip.open(jsonFilePath+'.gz', 'wb') 27 | jsonfile = open(jsonFilePath, 'w') 28 | 29 | #pandas.set_option('display.precision', 15) 30 | 31 | colnames = ['id', 'proper', 'mag', 'absmag', 'dist', 'x', 'y', 'z', 'spect', 'con', 'bf', 'gl'] 32 | data = pandas.read_csv(csvFilePath, engine='c', usecols=colnames, low_memory=False, delimiter = ',', skip_blank_lines = True, index_col = 0, header = 0, float_precision = 10, dtype={'id': np.int64, 'proper': str, 'dist': np.float64, 'x': np.float64,'y': np.float64, 'z': np.float64, 'spect': str, 'mag': np.float64, 'absmag': np.float64, 'con': str, 'bf': str, 'gl': str}) 33 | 34 | print data.dtypes 35 | 36 | myJSON = data.to_json(path_or_buf = None, orient = 'records', date_format = 'epoch', double_precision = 10, force_ascii = False, date_unit = 'ms') 37 | 38 | 39 | jsonfileGzipped.write(myJSON) 40 | jsonfile.write(myJSON) 41 | jsonfile.close() 42 | jsonfileGzipped.close() 43 | print "Done!" 44 | -------------------------------------------------------------------------------- /online/assets/src/vendor/modernizr/modernizr-custom.js: -------------------------------------------------------------------------------- 1 | /*! modernizr 3.3.1 (Custom Build) | MIT * 2 | * http://modernizr.com/download/?-devicemotion_deviceorientation-geolocation-webgl-webglextensions-addtest-mq-setclasses !*/ 3 | !function(e,n,t){function o(e,n){return typeof e===n}function i(){var e,n,t,i,a,s,r;for(var l in c)if(c.hasOwnProperty(l)){if(e=[],n=c[l],n.name&&(e.push(n.name.toLowerCase()),n.options&&n.options.aliases&&n.options.aliases.length))for(t=0;t= 10000000 indicates missing or dubious (e.g., negative) parallax data in Hipparcos. 124 | * `pmra`, `pmdec` - The star's proper motion in right ascension and declination, in milliarcseconds per year. 125 | * `rv` - The star's radial velocity in km/sec, where known. 126 | * `mag` - The star's apparent visual magnitude. 127 | * `absmag` - The star's absolute visual magnitude (its apparent magnitude from a distance of 10 parsecs). 128 | * `spect` - The star's spectral type, if known. 129 | * `ci` - The star's color index (blue magnitude - visual magnitude), where known. 130 | * `x`, `y` ,`z` - The Cartesian coordinates of the star, in a system based on the equatorial coordinates as seen from 131 | Earth. +X is in the direction of the vernal equinox (at epoch 2000), +Z towards the north celestial pole, and +Y in 132 | the direction of R.A. 6 hours, declination 0 degrees. 133 | * `vx`, `vy`, `vz` - The Cartesian velocity components of the star, in the same coordinate system described 134 | immediately above. They are determined from the proper motion and the radial velocity (when known). The velocity 135 | unit is parsecs per year; these are small values (around 1 millionth of a parsec per year), but they enormously 136 | simplify calculations using parsecs as base units for celestial mapping. 137 | * `rarad`, `decrad`, `pmrarad`, `prdecrad` - The positions in radians, and proper motions in radians per year. 138 | * `bayer` - The Bayer designation as a distinct value 139 | * `flam` - The Flamsteed number as a distinct value 140 | * `con` - The standard constellation abbreviation 141 | * `comp`, `comp_primary`, `base` - Identifies a star in a multiple star system. comp = ID of companion star, 142 | comp_primary = ID of primary star for this component, and base = catalog ID or name for this multi-star system. 143 | Currently only used for Gliese stars. 144 | * `lum` - Star's luminosity as a multiple of Solar luminosity. 145 | * `var` - Star's standard variable star designation, when known. 146 | * `var_min`, `var_max`: Star's approximate magnitude range, for variables. This value is based on the Hp magnitudes 147 | for the range in the original Hipparcos catalog, adjusted to the V magnitude scale to match the "mag" field. 148 | 149 | ## Tools 150 | 151 | * [Three.js Inspector](https://chrome.google.com/webstore/detail/threejs-inspector/dnhjfclbfhcbcdfpjaeacomhbdfjbebi/related?hl=en) 152 | 153 | ## Examples 154 | 155 | * [Three JS Point Cloud Experiment](http://codepen.io/seanseansean/pen/EaBZEY) 156 | 157 | * http://www.html5rocks.com/en/tutorials/casestudies/100000stars/ 158 | 159 | * http://stars.chromeexperiments.com/ 160 | 161 | * https://jbouny.github.io/ocean/demo/ 162 | 163 | ## Reference 164 | 165 | * [What Are Celestial Coordinates](http://www.skyandtelescope.com/astronomy-resources/what-are-celestial-coordinates/) 166 | * [Scientific Visualization Studio](http://svs.gsfc.nasa.gov/cgi-bin/details.cgi?aid=3895) 167 | * [Planetary Annihilation Skyboxes](http://www.superouman.net/planetary-annihilation-skyboxes.php) 168 | * [How to Find the Milky Way](http://www.lonelyspeck.com/how-to-find-the-milky-way/) 169 | * [THREE.js VRControls Integration How To Move In The Scene](http://stackoverflow.com/questions/30511524/three-js-vrcontrols-integration-how-to-move-in-the-scene) 170 | -------------------------------------------------------------------------------- /online/assets/src/vendor/three/examples/js/controls/MouseControls.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author dmarcos / http://github.com/dmarcos 3 | * 4 | * This controls allow to change the orientation of the camera using the mouse 5 | */ 6 | 7 | var Util = require('webvr-polyfill/src/util'); 8 | 9 | THREE.MouseControls = function ( object, domElement ) { 10 | 11 | var scope = this; 12 | 13 | 14 | 15 | var changeEvent = { type: 'change' }; 16 | var startEvent = { type: 'start' }; 17 | var endEvent = { type: 'end' }; 18 | 19 | 20 | var PI_2 = Math.PI / 2; 21 | var mouseQuat = { 22 | x: new THREE.Quaternion(), 23 | y: new THREE.Quaternion() 24 | }; 25 | var object = object; 26 | //var xVector = (Util.isIOS()) ? new THREE.Vector3( 0, 0, 1 ) : new THREE.Vector3( 1, 0, 0 ); //on IOS we have to invert the X axis, so we need to correct it here otherwise it will show up as a Z rotation 27 | //var yVector = new THREE.Vector3( 0, 1, 0 ); 28 | 29 | var rotateStart = new THREE.Vector2(); 30 | var rotateEnd = new THREE.Vector2(); 31 | var rotateDelta = new THREE.Vector2(); 32 | 33 | var STATE = { NONE : - 1, ROTATE : 0, TOUCH_ROTATE : 3 }; 34 | 35 | var state = STATE.NONE; 36 | 37 | // Set to false to disable rotating 38 | this.enableRotate = true; 39 | this.rotateSpeed = 1.0; 40 | 41 | this.domElement = ( domElement !== undefined ) ? domElement : document; 42 | 43 | // Mouse buttons 44 | this.mouseButtons = { ROTATE: THREE.MOUSE.LEFT }; 45 | 46 | this.enabled = true; 47 | 48 | this.orientation = { 49 | x: 0, 50 | y: 0, 51 | }; 52 | 53 | function onMouseDown( event ) { 54 | 55 | if ( scope.enabled === false ) return; 56 | 57 | event.preventDefault(); 58 | 59 | if ( event.button === scope.mouseButtons.ROTATE ) { 60 | 61 | console.log('mouse down'); 62 | 63 | if ( scope.enableRotate === false ) return; 64 | 65 | handleMouseDownRotate( event ); 66 | 67 | state = STATE.ROTATE; 68 | 69 | console.log(state) 70 | 71 | } 72 | 73 | if ( state !== STATE.NONE ) { 74 | 75 | scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); 76 | scope.domElement.addEventListener( 'mouseup', onMouseUp, false ); 77 | scope.domElement.addEventListener( 'mouseout', onMouseUp, false ); 78 | 79 | scope.dispatchEvent( startEvent ); 80 | 81 | } 82 | 83 | } 84 | 85 | function onMouseMove( event ) { 86 | 87 | if ( scope.enabled === false ) return; 88 | 89 | event.preventDefault(); 90 | 91 | if ( state === STATE.ROTATE ) { 92 | 93 | if ( scope.enableRotate === false ) return; 94 | 95 | handleMouseMoveRotate( event ); 96 | 97 | } 98 | 99 | } 100 | 101 | function onMouseUp( event ) { 102 | 103 | if ( scope.enabled === false ) return; 104 | 105 | handleMouseUp( event ); 106 | 107 | document.removeEventListener( 'mousemove', onMouseMove, false ); 108 | document.removeEventListener( 'mouseup', onMouseUp, false ); 109 | document.removeEventListener( 'mouseout', onMouseUp, false ); 110 | 111 | scope.dispatchEvent( endEvent ); 112 | 113 | state = STATE.NONE; 114 | 115 | } 116 | 117 | 118 | 119 | function onTouchStart( event ) { 120 | 121 | if ( scope.enabled === false ) return; 122 | 123 | console.log('touchstart'); 124 | 125 | switch ( event.touches.length ) { 126 | 127 | case 1: // one-fingered touch: rotate 128 | 129 | if ( scope.enableRotate === false ) return; 130 | 131 | handleTouchStartRotate( event ); 132 | 133 | state = STATE.TOUCH_ROTATE; 134 | 135 | break; 136 | 137 | default: 138 | 139 | state = STATE.NONE; 140 | 141 | } 142 | 143 | if ( state !== STATE.NONE ) { 144 | 145 | scope.dispatchEvent( startEvent ); 146 | 147 | } 148 | 149 | } 150 | 151 | function onTouchMove( event ) { 152 | 153 | if ( scope.enabled === false ) return; 154 | 155 | event.preventDefault(); 156 | event.stopPropagation(); 157 | 158 | switch ( event.touches.length ) { 159 | 160 | case 1: // one-fingered touch: rotate 161 | 162 | if ( scope.enableRotate === false ) return; 163 | if ( state !== STATE.TOUCH_ROTATE ) return; // is this needed?... 164 | 165 | handleTouchMoveRotate( event ); 166 | 167 | break; 168 | 169 | default: 170 | 171 | state = STATE.NONE; 172 | 173 | } 174 | 175 | } 176 | 177 | function onTouchEnd( event ) { 178 | 179 | if ( scope.enabled === false ) return; 180 | 181 | handleTouchEnd( event ); 182 | 183 | scope.dispatchEvent( endEvent ); 184 | 185 | state = STATE.NONE; 186 | 187 | } 188 | 189 | function onContextMenu( event ) { 190 | 191 | event.preventDefault(); 192 | 193 | } 194 | 195 | function handleTouchStartRotate( event ) { 196 | 197 | //console.log( 'handleTouchStartRotate' ); 198 | 199 | rotateStart.set(event.touches[0].pageX, event.touches[0].pageY); 200 | 201 | } 202 | 203 | function handleTouchEnd( event ) { 204 | 205 | console.log( 'handleTouchEnd' ); 206 | 207 | } 208 | 209 | function handleTouchMoveRotate( event ) { 210 | 211 | console.log( 'handleTouchMoveRotate' ); 212 | 213 | 214 | 215 | rotateEnd.set(event.touches[0].pageX, event.touches[0].pageY); 216 | rotateDelta.subVectors(rotateEnd, rotateStart); 217 | rotateStart.copy(rotateEnd); 218 | 219 | // On iOS, direction is inverted. 220 | if (Util.isIOS()) { 221 | //rotateDelta.x *= -1; 222 | } 223 | 224 | var orientation = scope.orientation; 225 | 226 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 227 | orientation.x += 2 * Math.PI * rotateDelta.y / element.clientWidth * scope.rotateSpeed; 228 | 229 | orientation.y += 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed; 230 | 231 | //orientation.x = Math.max( - PI_2, Math.min( PI_2, orientation.x ) ); 232 | 233 | scope.update(); 234 | 235 | } 236 | 237 | function handleMouseMoveRotate( event ) { 238 | 239 | console.log( 'handleMouseMoveRotate' ); 240 | 241 | rotateEnd.set( event.clientX, event.clientY ); 242 | rotateDelta.subVectors( rotateEnd, rotateStart ); 243 | 244 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 245 | 246 | // rotating across whole screen goes 360 degrees around 247 | /*rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 248 | 249 | // rotating up and down along whole screen attempts to go 360, but limited to 180 250 | rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 251 | 252 | rotateStart.copy( rotateEnd );*/ 253 | 254 | rotateStart.copy( rotateEnd ); 255 | 256 | var orientation = scope.orientation; 257 | 258 | var movementX = rotateDelta.x; 259 | var movementY = rotateDelta.y; 260 | 261 | orientation.y += movementX * 0.0025; 262 | orientation.x += movementY * 0.0025; 263 | 264 | orientation.x = Math.max( - PI_2, Math.min( PI_2, orientation.x ) ); 265 | 266 | scope.update(); 267 | 268 | } 269 | 270 | function handleMouseDownRotate( event ) { 271 | 272 | console.log( 'handleMouseDownRotate' ); 273 | 274 | rotateStart.set( event.clientX, event.clientY ); 275 | 276 | } 277 | 278 | function handleMouseUp( event ) { 279 | 280 | console.log( 'handleMouseUp' ); 281 | 282 | } 283 | 284 | this.update = function() { 285 | 286 | if ( scope.enabled === false ) return; 287 | 288 | //TODO: fix this so that rotation is corrected when the parent rotation changes. Currently X and Z axis flip 289 | //when the dollyCamera is rotated in a different direction using the gyro 290 | 291 | mouseQuat.x.setFromEuler(new THREE.Euler(scope.orientation.x, 0, 0)); 292 | 293 | mouseQuat.y.setFromEuler(new THREE.Euler(0, scope.orientation.y, 0)); 294 | 295 | //mouseQuat.x.setFromAxisAngle( xVector, scope.orientation.x ); 296 | //mouseQuat.y.setFromAxisAngle( yVector, scope.orientation.y ); 297 | object.quaternion.copy( mouseQuat.y ).multiply( mouseQuat.x ); 298 | return; 299 | 300 | }; 301 | 302 | this.dispose = function() { 303 | 304 | scope.domElement.removeEventListener( 'mousedown', onMouseDown, false ); 305 | scope.domElement.removeEventListener( 'contextmenu', onContextMenu, false ); 306 | 307 | document.removeEventListener( 'mousemove', onMouseMove, false ); 308 | document.removeEventListener( 'mouseup', onMouseUp, false ); 309 | document.removeEventListener( 'mouseout', onMouseUp, false ); 310 | 311 | 312 | } 313 | 314 | //scope.domElement.addEventListener( 'mousemove', onMouseMove, false ); 315 | scope.domElement.addEventListener( 'mousedown', onMouseDown, false ); 316 | scope.domElement.addEventListener( 'touchstart', onTouchStart, false ); 317 | scope.domElement.addEventListener( 'touchend', onTouchEnd, false ); 318 | scope.domElement.addEventListener( 'touchmove', onTouchMove, false ); 319 | scope.domElement.addEventListener( 'contextmenu', onContextMenu, false ); 320 | 321 | }; 322 | 323 | THREE.MouseControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 324 | THREE.MouseControls.prototype.constructor = THREE.MouseControls; 325 | -------------------------------------------------------------------------------- /online/assets/src/vendor/zz85/SkyShader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author zz85 / https://github.com/zz85 3 | * 4 | * Based on "A Practical Analytic Model for Daylight" 5 | * aka The Preetham Model, the de facto standard analytic skydome model 6 | * http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf 7 | * 8 | * First implemented by Simon Wallner 9 | * http://www.simonwallner.at/projects/atmospheric-scattering 10 | * 11 | * Improved by Martin Upitis 12 | * http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR 13 | * 14 | * Three.js integration by zz85 http://twitter.com/blurspline 15 | */ 16 | 17 | THREE.ShaderLib[ 'sky' ] = { 18 | 19 | uniforms: { 20 | 21 | luminance: { type: "f", value: 1 }, 22 | turbidity: { type: "f", value: 2 }, 23 | reileigh: { type: "f", value: 1 }, 24 | mieCoefficient: { type: "f", value: 0.005 }, 25 | mieDirectionalG: { type: "f", value: 0.8 }, 26 | sunPosition: { type: "v3", value: new THREE.Vector3() } 27 | 28 | }, 29 | 30 | vertexShader: [ 31 | 32 | "varying vec3 vWorldPosition;", 33 | 34 | "void main() {", 35 | 36 | "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", 37 | "vWorldPosition = worldPosition.xyz;", 38 | 39 | "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", 40 | 41 | "}", 42 | 43 | ].join( "\n" ), 44 | 45 | fragmentShader: [ 46 | 47 | "uniform sampler2D skySampler;", 48 | "uniform vec3 sunPosition;", 49 | "varying vec3 vWorldPosition;", 50 | 51 | "vec3 cameraPos = vec3(0., 0., 0.);", 52 | "// uniform sampler2D sDiffuse;", 53 | "// const float turbidity = 10.0; //", 54 | "// const float reileigh = 2.; //", 55 | "// const float luminance = 1.0; //", 56 | "// const float mieCoefficient = 0.005;", 57 | "// const float mieDirectionalG = 0.8;", 58 | 59 | "uniform float luminance;", 60 | "uniform float turbidity;", 61 | "uniform float reileigh;", 62 | "uniform float mieCoefficient;", 63 | "uniform float mieDirectionalG;", 64 | 65 | "// constants for atmospheric scattering", 66 | "const float e = 2.71828182845904523536028747135266249775724709369995957;", 67 | "const float pi = 3.141592653589793238462643383279502884197169;", 68 | 69 | "const float n = 1.0003; // refractive index of air", 70 | "const float N = 2.545E25; // number of molecules per unit volume for air at", 71 | "// 288.15K and 1013mb (sea level -45 celsius)", 72 | "const float pn = 0.035; // depolatization factor for standard air", 73 | 74 | "// wavelength of used primaries, according to preetham", 75 | "const vec3 lambda = vec3(680E-9, 550E-9, 450E-9);", 76 | 77 | "// mie stuff", 78 | "// K coefficient for the primaries", 79 | "const vec3 K = vec3(0.686, 0.678, 0.666);", 80 | "const float v = 4.0;", 81 | 82 | "// optical length at zenith for molecules", 83 | "const float rayleighZenithLength = 8.4E3;", 84 | "const float mieZenithLength = 1.25E3;", 85 | "const vec3 up = vec3(0.0, 1.0, 0.0);", 86 | 87 | "const float EE = 1000.0;", 88 | "const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;", 89 | "// 66 arc seconds -> degrees, and the cosine of that", 90 | 91 | "// earth shadow hack", 92 | "const float cutoffAngle = pi/1.95;", 93 | "const float steepness = 1.5;", 94 | 95 | 96 | "vec3 totalRayleigh(vec3 lambda)", 97 | "{", 98 | "return (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn));", 99 | "}", 100 | 101 | // see http://blenderartists.org/forum/showthread.php?321110-Shaders-and-Skybox-madness 102 | "// A simplied version of the total Reayleigh scattering to works on browsers that use ANGLE", 103 | "vec3 simplifiedRayleigh()", 104 | "{", 105 | "return 0.0005 / vec3(94, 40, 18);", 106 | // return 0.00054532832366 / (3.0 * 2.545E25 * pow(vec3(680E-9, 550E-9, 450E-9), vec3(4.0)) * 6.245); 107 | "}", 108 | 109 | "float rayleighPhase(float cosTheta)", 110 | "{ ", 111 | "return (3.0 / (16.0*pi)) * (1.0 + pow(cosTheta, 2.0));", 112 | "// return (1.0 / (3.0*pi)) * (1.0 + pow(cosTheta, 2.0));", 113 | "// return (3.0 / 4.0) * (1.0 + pow(cosTheta, 2.0));", 114 | "}", 115 | 116 | "vec3 totalMie(vec3 lambda, vec3 K, float T)", 117 | "{", 118 | "float c = (0.2 * T ) * 10E-18;", 119 | "return 0.434 * c * pi * pow((2.0 * pi) / lambda, vec3(v - 2.0)) * K;", 120 | "}", 121 | 122 | "float hgPhase(float cosTheta, float g)", 123 | "{", 124 | "return (1.0 / (4.0*pi)) * ((1.0 - pow(g, 2.0)) / pow(1.0 - 2.0*g*cosTheta + pow(g, 2.0), 1.5));", 125 | "}", 126 | 127 | "float sunIntensity(float zenithAngleCos)", 128 | "{", 129 | "return EE * max(0.0, 1.0 - exp(-((cutoffAngle - acos(zenithAngleCos))/steepness)));", 130 | "}", 131 | 132 | "// float logLuminance(vec3 c)", 133 | "// {", 134 | "// return log(c.r * 0.2126 + c.g * 0.7152 + c.b * 0.0722);", 135 | "// }", 136 | 137 | "// Filmic ToneMapping http://filmicgames.com/archives/75", 138 | "float A = 0.15;", 139 | "float B = 0.50;", 140 | "float C = 0.10;", 141 | "float D = 0.20;", 142 | "float E = 0.02;", 143 | "float F = 0.30;", 144 | "float W = 1000.0;", 145 | 146 | "vec3 Uncharted2Tonemap(vec3 x)", 147 | "{", 148 | "return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;", 149 | "}", 150 | 151 | 152 | "void main() ", 153 | "{", 154 | "float sunfade = 1.0-clamp(1.0-exp((sunPosition.y/450000.0)),0.0,1.0);", 155 | 156 | "// luminance = 1.0 ;// vWorldPosition.y / 450000. + 0.5; //sunPosition.y / 450000. * 1. + 0.5;", 157 | 158 | "// gl_FragColor = vec4(sunfade, sunfade, sunfade, 1.0);", 159 | 160 | "float reileighCoefficient = reileigh - (1.0* (1.0-sunfade));", 161 | 162 | "vec3 sunDirection = normalize(sunPosition);", 163 | 164 | "float sunE = sunIntensity(dot(sunDirection, up));", 165 | 166 | "// extinction (absorbtion + out scattering) ", 167 | "// rayleigh coefficients", 168 | 169 | // "vec3 betaR = totalRayleigh(lambda) * reileighCoefficient;", 170 | "vec3 betaR = simplifiedRayleigh() * reileighCoefficient;", 171 | 172 | "// mie coefficients", 173 | "vec3 betaM = totalMie(lambda, K, turbidity) * mieCoefficient;", 174 | 175 | "// optical length", 176 | "// cutoff angle at 90 to avoid singularity in next formula.", 177 | "float zenithAngle = acos(max(0.0, dot(up, normalize(vWorldPosition - cameraPos))));", 178 | "float sR = rayleighZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));", 179 | "float sM = mieZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));", 180 | 181 | 182 | 183 | "// combined extinction factor ", 184 | "vec3 Fex = exp(-(betaR * sR + betaM * sM));", 185 | 186 | "// in scattering", 187 | "float cosTheta = dot(normalize(vWorldPosition - cameraPos), sunDirection);", 188 | 189 | "float rPhase = rayleighPhase(cosTheta*0.5+0.5);", 190 | "vec3 betaRTheta = betaR * rPhase;", 191 | 192 | "float mPhase = hgPhase(cosTheta, mieDirectionalG);", 193 | "vec3 betaMTheta = betaM * mPhase;", 194 | 195 | 196 | "vec3 Lin = pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * (1.0 - Fex),vec3(1.5));", 197 | "Lin *= mix(vec3(1.0),pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * Fex,vec3(1.0/2.0)),clamp(pow(1.0-dot(up, sunDirection),5.0),0.0,1.0));", 198 | 199 | "//nightsky", 200 | "vec3 direction = normalize(vWorldPosition - cameraPos);", 201 | "float theta = acos(direction.y); // elevation --> y-axis, [-pi/2, pi/2]", 202 | "float phi = atan(direction.z, direction.x); // azimuth --> x-axis [-pi/2, pi/2]", 203 | "vec2 uv = vec2(phi, theta) / vec2(2.0*pi, pi) + vec2(0.5, 0.0);", 204 | "// vec3 L0 = texture2D(skySampler, uv).rgb+0.1 * Fex;", 205 | "vec3 L0 = vec3(0.1) * Fex;", 206 | 207 | "// composition + solar disc", 208 | "//if (cosTheta > sunAngularDiameterCos)", 209 | "float sundisk = smoothstep(sunAngularDiameterCos,sunAngularDiameterCos+0.00002,cosTheta);", 210 | "// if (normalize(vWorldPosition - cameraPos).y>0.0)", 211 | "L0 += (sunE * 19000.0 * Fex)*sundisk;", 212 | 213 | 214 | "vec3 whiteScale = 1.0/Uncharted2Tonemap(vec3(W));", 215 | 216 | "vec3 texColor = (Lin+L0); ", 217 | "texColor *= 0.04 ;", 218 | "texColor += vec3(0.0,0.001,0.0025)*0.3;", 219 | 220 | "float g_fMaxLuminance = 1.0;", 221 | "float fLumScaled = 0.1 / luminance; ", 222 | "float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (g_fMaxLuminance * g_fMaxLuminance)))) / (1.0 + fLumScaled); ", 223 | 224 | "float ExposureBias = fLumCompressed;", 225 | 226 | "vec3 curr = Uncharted2Tonemap((log2(2.0/pow(luminance,4.0)))*texColor);", 227 | "vec3 color = curr*whiteScale;", 228 | 229 | "vec3 retColor = pow(color,vec3(1.0/(1.2+(1.2*sunfade))));", 230 | 231 | 232 | "gl_FragColor.rgb = retColor;", 233 | 234 | "gl_FragColor.a = 1.0;", 235 | "}", 236 | 237 | ].join( "\n" ) 238 | 239 | }; 240 | 241 | THREE.Sky = function () { 242 | 243 | var skyShader = THREE.ShaderLib[ "sky" ]; 244 | var skyUniforms = THREE.UniformsUtils.clone( skyShader.uniforms ); 245 | 246 | var skyMat = new THREE.ShaderMaterial( { 247 | fragmentShader: skyShader.fragmentShader, 248 | vertexShader: skyShader.vertexShader, 249 | uniforms: skyUniforms, 250 | side: THREE.BackSide 251 | } ); 252 | 253 | var skyGeo = new THREE.SphereBufferGeometry( 450000, 32, 15 ); 254 | var skyMesh = new THREE.Mesh( skyGeo, skyMat ); 255 | 256 | 257 | // Expose variables 258 | this.mesh = skyMesh; 259 | this.uniforms = skyUniforms; 260 | 261 | }; 262 | -------------------------------------------------------------------------------- /online/assets/src/vendor/charliehoey/GPUParticleSystem.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GPU Particle System 3 | * @author flimshaw - Charlie Hoey - http://charliehoey.com 4 | * 5 | * A simple to use, general purpose GPU system. Particles are spawn-and-forget with 6 | * several options available, and do not require monitoring or cleanup after spawning. 7 | * Because the paths of all particles are completely deterministic once spawned, the scale 8 | * and direction of time is also variable. 9 | * 10 | * Currently uses a static wrapping perlin noise texture for turbulence, and a small png texture for 11 | * particles, but adding support for a particle texture atlas or changing to a different type of turbulence 12 | * would be a fairly light day's work. 13 | * 14 | * Shader and javascript packing code derrived from several Stack Overflow examples. 15 | * 16 | */ 17 | 18 | import THREE from 'three' 19 | 20 | THREE.GPUParticleSystem = function(options) { 21 | 22 | var self = this; 23 | var options = options || {}; 24 | 25 | // parse options and use defaults 26 | self.PARTICLE_COUNT = options.maxParticles || 1000000; 27 | self.PARTICLE_CONTAINERS = options.containerCount || 1; 28 | self.PARTICLES_PER_CONTAINER = Math.ceil(self.PARTICLE_COUNT / self.PARTICLE_CONTAINERS); 29 | self.PARTICLE_CURSOR = 0; 30 | self.time = 0; 31 | 32 | 33 | // Custom vertex and fragement shader 34 | var GPUParticleShader = { 35 | 36 | vertexShader: [ 37 | 38 | 'precision highp float;', 39 | 'const vec4 bitSh = vec4(256. * 256. * 256., 256. * 256., 256., 1.);', 40 | 'const vec4 bitMsk = vec4(0.,vec3(1./256.0));', 41 | 'const vec4 bitShifts = vec4(1.) / bitSh;', 42 | 43 | '#define FLOAT_MAX 1.70141184e38', 44 | '#define FLOAT_MIN 1.17549435e-38', 45 | 46 | 'lowp vec4 encode_float(highp float v) {', 47 | 'highp float av = abs(v);', 48 | 49 | '//Handle special cases', 50 | 'if(av < FLOAT_MIN) {', 51 | 'return vec4(0.0, 0.0, 0.0, 0.0);', 52 | '} else if(v > FLOAT_MAX) {', 53 | 'return vec4(127.0, 128.0, 0.0, 0.0) / 255.0;', 54 | '} else if(v < -FLOAT_MAX) {', 55 | 'return vec4(255.0, 128.0, 0.0, 0.0) / 255.0;', 56 | '}', 57 | 58 | 'highp vec4 c = vec4(0,0,0,0);', 59 | 60 | '//Compute exponent and mantissa', 61 | 'highp float e = floor(log2(av));', 62 | 'highp float m = av * pow(2.0, -e) - 1.0;', 63 | 64 | //Unpack mantissa 65 | 'c[1] = floor(128.0 * m);', 66 | 'm -= c[1] / 128.0;', 67 | 'c[2] = floor(32768.0 * m);', 68 | 'm -= c[2] / 32768.0;', 69 | 'c[3] = floor(8388608.0 * m);', 70 | 71 | '//Unpack exponent', 72 | 'highp float ebias = e + 127.0;', 73 | 'c[0] = floor(ebias / 2.0);', 74 | 'ebias -= c[0] * 2.0;', 75 | 'c[1] += floor(ebias) * 128.0;', 76 | 77 | '//Unpack sign bit', 78 | 'c[0] += 128.0 * step(0.0, -v);', 79 | 80 | '//Scale back to range', 81 | 'return c / 255.0;', 82 | '}', 83 | 84 | 'vec4 pack(const in float depth)', 85 | '{', 86 | 'const vec4 bit_shift = vec4(256.0*256.0*256.0, 256.0*256.0, 256.0, 1.0);', 87 | 'const vec4 bit_mask = vec4(0.0, 1.0/256.0, 1.0/256.0, 1.0/256.0);', 88 | 'vec4 res = mod(depth*bit_shift*vec4(255), vec4(256))/vec4(255);', 89 | 'res -= res.xxyz * bit_mask;', 90 | 'return res;', 91 | '}', 92 | 93 | 'float unpack(const in vec4 rgba_depth)', 94 | '{', 95 | 'const vec4 bit_shift = vec4(1.0/(256.0*256.0*256.0), 1.0/(256.0*256.0), 1.0/256.0, 1.0);', 96 | 'float depth = dot(rgba_depth, bit_shift);', 97 | 'return depth;', 98 | '}', 99 | 100 | 'uniform float uTime;', 101 | 'uniform float uScale;', 102 | 'uniform sampler2D tNoise;', 103 | 104 | 'attribute vec4 particlePositionsStartTime;', 105 | 'attribute vec4 particleVelColSizeLife;', 106 | 107 | 'varying vec4 vColor;', 108 | 'varying float lifeLeft;', 109 | 110 | 'void main() {', 111 | 112 | '// unpack things from our attributes', 113 | 'vColor = encode_float( particleVelColSizeLife.y );', 114 | 115 | '// convert our velocity back into a value we can use', 116 | 'vec4 velTurb = encode_float( particleVelColSizeLife.x );', 117 | 'vec3 velocity = vec3( velTurb.xyz );', 118 | 'float turbulence = velTurb.w;', 119 | 120 | 'vec3 newPosition;', 121 | 122 | 'float timeElapsed = uTime - particlePositionsStartTime.a;', 123 | 124 | 'lifeLeft = 1. - (timeElapsed / particleVelColSizeLife.w);', 125 | 126 | 'gl_PointSize = ( uScale * particleVelColSizeLife.z ) * lifeLeft;', 127 | 128 | 'velocity.x = ( velocity.x - .5 ) * 3.;', 129 | 'velocity.y = ( velocity.y - .5 ) * 3.;', 130 | 'velocity.z = ( velocity.z - .5 ) * 3.;', 131 | 132 | 'newPosition = particlePositionsStartTime.xyz + ( velocity * 10. ) * ( uTime - particlePositionsStartTime.a );', 133 | 134 | 'vec3 noise = texture2D( tNoise, vec2( newPosition.x * .015 + (uTime * .05), newPosition.y * .02 + (uTime * .015) )).rgb;', 135 | 'vec3 noiseVel = ( noise.rgb - .5 ) * 30.;', 136 | 137 | 'newPosition = mix(newPosition, newPosition + vec3(noiseVel * ( turbulence * 5. ) ), (timeElapsed / particleVelColSizeLife.a) );', 138 | 139 | 'if( velocity.y > 0. && velocity.y < .05 ) {', 140 | 'lifeLeft = 0.;', 141 | '}', 142 | 143 | 'if( velocity.x < -1.45 ) {', 144 | 'lifeLeft = 0.;', 145 | '}', 146 | 147 | 'if( timeElapsed > 0. ) {', 148 | 'gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 );', 149 | '} else {', 150 | 'gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', 151 | 'lifeLeft = 0.;', 152 | 'gl_PointSize = 0.;', 153 | '}', 154 | '}' 155 | 156 | ].join("\n"), 157 | 158 | fragmentShader: [ 159 | 160 | 'float scaleLinear(float value, vec2 valueDomain) {', 161 | 'return (value - valueDomain.x) / (valueDomain.y - valueDomain.x);', 162 | '}', 163 | 164 | 'float scaleLinear(float value, vec2 valueDomain, vec2 valueRange) {', 165 | 'return mix(valueRange.x, valueRange.y, scaleLinear(value, valueDomain));', 166 | '}', 167 | 168 | 'varying vec4 vColor;', 169 | 'varying float lifeLeft;', 170 | 171 | 'uniform sampler2D tSprite;', 172 | 173 | 'void main() {', 174 | 175 | 'float alpha = 0.;', 176 | 177 | 'if( lifeLeft > .995 ) {', 178 | 'alpha = scaleLinear( lifeLeft, vec2(1., .995), vec2(0., 1.));//mix( 0., 1., ( lifeLeft - .95 ) * 100. ) * .75;', 179 | '} else {', 180 | 'alpha = lifeLeft * .75;', 181 | '}', 182 | 183 | 'vec4 tex = texture2D( tSprite, gl_PointCoord );', 184 | 185 | 'gl_FragColor = vec4( vColor.rgb * tex.a, alpha * tex.a );', 186 | '}' 187 | 188 | ].join("\n") 189 | 190 | }; 191 | 192 | // preload a million random numbers 193 | self.rand = []; 194 | 195 | for (var i = 1e5; i > 0; i--) { 196 | self.rand.push(Math.random() - .5); 197 | } 198 | 199 | self.random = function() { 200 | return ++i >= self.rand.length ? self.rand[i = 1] : self.rand[i]; 201 | } 202 | 203 | var textureLoader = new THREE.TextureLoader(); 204 | 205 | self.particleNoiseTex = textureLoader.load("assets/img/perlin-512.png"); 206 | self.particleNoiseTex.wrapS = self.particleNoiseTex.wrapT = THREE.RepeatWrapping; 207 | 208 | self.particleSpriteTex = textureLoader.load("assets/img/particle2.png"); 209 | self.particleSpriteTex.wrapS = self.particleSpriteTex.wrapT = THREE.RepeatWrapping; 210 | 211 | self.particleShaderMat = new THREE.ShaderMaterial({ 212 | transparent: true, 213 | depthWrite: false, 214 | uniforms: { 215 | "uTime": { 216 | type: "f", 217 | value: 0.0 218 | }, 219 | "uScale": { 220 | type: "f", 221 | value: 1.0 222 | }, 223 | "tNoise": { 224 | type: "t", 225 | value: self.particleNoiseTex 226 | }, 227 | "tSprite": { 228 | type: "t", 229 | value: self.particleSpriteTex 230 | } 231 | }, 232 | blending: THREE.AdditiveBlending, 233 | vertexShader: GPUParticleShader.vertexShader, 234 | fragmentShader: GPUParticleShader.fragmentShader 235 | }); 236 | 237 | // define defaults for all values 238 | self.particleShaderMat.defaultAttributeValues.particlePositionsStartTime = [0, 0, 0, 0]; 239 | self.particleShaderMat.defaultAttributeValues.particleVelColSizeLife = [0, 0, 0, 0]; 240 | 241 | self.particleContainers = []; 242 | 243 | 244 | // extend Object3D 245 | THREE.Object3D.apply(this, arguments); 246 | 247 | this.init = function() { 248 | 249 | for (var i = 0; i < self.PARTICLE_CONTAINERS; i++) { 250 | 251 | var c = new THREE.GPUParticleContainer(self.PARTICLES_PER_CONTAINER, self); 252 | self.particleContainers.push(c); 253 | self.add(c); 254 | 255 | } 256 | 257 | } 258 | 259 | this.spawnParticle = function(options) { 260 | 261 | self.PARTICLE_CURSOR++; 262 | if (self.PARTICLE_CURSOR >= self.PARTICLE_COUNT) { 263 | self.PARTICLE_CURSOR = 1; 264 | } 265 | 266 | var currentContainer = self.particleContainers[Math.floor(self.PARTICLE_CURSOR / self.PARTICLES_PER_CONTAINER)]; 267 | 268 | currentContainer.spawnParticle(options); 269 | 270 | } 271 | 272 | this.update = function(time) { 273 | for (var i = 0; i < self.PARTICLE_CONTAINERS; i++) { 274 | 275 | self.particleContainers[i].update(time); 276 | 277 | } 278 | }; 279 | 280 | this.init(); 281 | 282 | } 283 | 284 | THREE.GPUParticleSystem.prototype = Object.create(THREE.Object3D.prototype); 285 | THREE.GPUParticleSystem.prototype.constructor = THREE.GPUParticleSystem; 286 | 287 | 288 | // Subclass for particle containers, allows for very large arrays to be spread out 289 | THREE.GPUParticleContainer = function(maxParticles, particleSystem) { 290 | 291 | var self = this; 292 | self.PARTICLE_COUNT = maxParticles || 100000; 293 | self.PARTICLE_CURSOR = 0; 294 | self.time = 0; 295 | self.DPR = window.devicePixelRatio; 296 | self.GPUParticleSystem = particleSystem; 297 | 298 | var particlesPerArray = Math.floor(self.PARTICLE_COUNT / self.MAX_ATTRIBUTES); 299 | 300 | // extend Object3D 301 | THREE.Object3D.apply(this, arguments); 302 | 303 | // construct a couple small arrays used for packing variables into floats etc 304 | var UINT8_VIEW = new Uint8Array(4) 305 | var FLOAT_VIEW = new Float32Array(UINT8_VIEW.buffer) 306 | 307 | function decodeFloat(x, y, z, w) { 308 | UINT8_VIEW[0] = Math.floor(w) 309 | UINT8_VIEW[1] = Math.floor(z) 310 | UINT8_VIEW[2] = Math.floor(y) 311 | UINT8_VIEW[3] = Math.floor(x) 312 | return FLOAT_VIEW[0] 313 | } 314 | 315 | function componentToHex(c) { 316 | var hex = c.toString(16); 317 | return hex.length == 1 ? "0" + hex : hex; 318 | } 319 | 320 | function rgbToHex(r, g, b) { 321 | return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); 322 | } 323 | 324 | function hexToRgb(hex) { 325 | var r = hex >> 16; 326 | var g = (hex & 0x00FF00) >> 8; 327 | var b = hex & 0x0000FF; 328 | 329 | if (r > 0) r--; 330 | if (g > 0) g--; 331 | if (b > 0) b--; 332 | 333 | return [r, g, b]; 334 | }; 335 | 336 | self.particles = []; 337 | self.deadParticles = []; 338 | self.particlesAvailableSlot = []; 339 | 340 | // create a container for particles 341 | self.particleUpdate = false; 342 | 343 | // Shader Based Particle System 344 | self.particleShaderGeo = new THREE.BufferGeometry(); 345 | 346 | // new hyper compressed attributes 347 | self.particleVertices = new Float32Array(self.PARTICLE_COUNT * 3); // position 348 | self.particlePositionsStartTime = new Float32Array(self.PARTICLE_COUNT * 4); // position 349 | self.particleVelColSizeLife = new Float32Array(self.PARTICLE_COUNT * 4); 350 | 351 | for (var i = 0; i < self.PARTICLE_COUNT; i++) { 352 | self.particlePositionsStartTime[i * 4 + 0] = 100; //x 353 | self.particlePositionsStartTime[i * 4 + 1] = 0; //y 354 | self.particlePositionsStartTime[i * 4 + 2] = 0.0; //z 355 | self.particlePositionsStartTime[i * 4 + 3] = 0.0; //startTime 356 | 357 | self.particleVertices[i * 3 + 0] = 0; //x 358 | self.particleVertices[i * 3 + 1] = 0; //y 359 | self.particleVertices[i * 3 + 2] = 0.0; //z 360 | 361 | self.particleVelColSizeLife[i * 4 + 0] = decodeFloat(128, 128, 0, 0); //vel 362 | self.particleVelColSizeLife[i * 4 + 1] = decodeFloat(0, 254, 0, 254); //color 363 | self.particleVelColSizeLife[i * 4 + 2] = 1.0; //size 364 | self.particleVelColSizeLife[i * 4 + 3] = 0.0; //lifespan 365 | } 366 | 367 | self.particleShaderGeo.addAttribute('position', new THREE.BufferAttribute(self.particleVertices, 3)); 368 | self.particleShaderGeo.addAttribute('particlePositionsStartTime', new THREE.BufferAttribute(self.particlePositionsStartTime, 4).setDynamic(true)); 369 | self.particleShaderGeo.addAttribute('particleVelColSizeLife', new THREE.BufferAttribute(self.particleVelColSizeLife, 4).setDynamic(true)); 370 | 371 | self.posStart = self.particleShaderGeo.getAttribute('particlePositionsStartTime') 372 | self.velCol = self.particleShaderGeo.getAttribute('particleVelColSizeLife'); 373 | 374 | self.particleShaderMat = self.GPUParticleSystem.particleShaderMat; 375 | 376 | this.init = function() { 377 | self.particleSystem = new THREE.Points(self.particleShaderGeo, self.particleShaderMat); 378 | self.particleSystem.frustumCulled = false; 379 | this.add(self.particleSystem); 380 | }; 381 | 382 | var options = {}, 383 | position = new THREE.Vector3(), 384 | velocity = new THREE.Vector3(), 385 | positionRandomness = 0., 386 | velocityRandomness = 0., 387 | color = 0xffffff, 388 | colorRandomness = 0., 389 | turbulence = 0., 390 | lifetime = 0., 391 | size = 0., 392 | sizeRandomness = 0., 393 | i; 394 | 395 | var maxVel = 2; 396 | var maxSource = 250; 397 | this.offset = 0; 398 | this.count = 0; 399 | 400 | this.spawnParticle = function(options) { 401 | 402 | options = options || {}; 403 | 404 | // setup reasonable default values for all arguments 405 | position = options.position !== undefined ? position.copy(options.position) : position.set(0., 0., 0.); 406 | velocity = options.velocity !== undefined ? velocity.copy(options.velocity) : velocity.set(0., 0., 0.); 407 | positionRandomness = options.positionRandomness !== undefined ? options.positionRandomness : 0.0; 408 | velocityRandomness = options.velocityRandomness !== undefined ? options.velocityRandomness : 0.0; 409 | color = options.color !== undefined ? options.color : 0xffffff; 410 | colorRandomness = options.colorRandomness !== undefined ? options.colorRandomness : 1.0; 411 | turbulence = options.turbulence !== undefined ? options.turbulence : 1.0; 412 | lifetime = options.lifetime !== undefined ? options.lifetime : 5.0; 413 | size = options.size !== undefined ? options.size : 10; 414 | sizeRandomness = options.sizeRandomness !== undefined ? options.sizeRandomness : 0.0, 415 | smoothPosition = options.smoothPosition !== undefined ? options.smoothPosition : false; 416 | 417 | if (self.DPR !== undefined) size *= self.DPR; 418 | 419 | i = self.PARTICLE_CURSOR; 420 | 421 | self.posStart.array[i * 4 + 0] = position.x + ((particleSystem.random()) * positionRandomness); // - ( velocity.x * particleSystem.random() ); //x 422 | self.posStart.array[i * 4 + 1] = position.y + ((particleSystem.random()) * positionRandomness); // - ( velocity.y * particleSystem.random() ); //y 423 | self.posStart.array[i * 4 + 2] = position.z + ((particleSystem.random()) * positionRandomness); // - ( velocity.z * particleSystem.random() ); //z 424 | self.posStart.array[i * 4 + 3] = self.time + (particleSystem.random() * 2e-2); //startTime 425 | 426 | if (smoothPosition === true) { 427 | self.posStart.array[i * 4 + 0] += -(velocity.x * particleSystem.random()); //x 428 | self.posStart.array[i * 4 + 1] += -(velocity.y * particleSystem.random()); //y 429 | self.posStart.array[i * 4 + 2] += -(velocity.z * particleSystem.random()); //z 430 | } 431 | 432 | var velX = velocity.x + (particleSystem.random()) * velocityRandomness; 433 | var velY = velocity.y + (particleSystem.random()) * velocityRandomness; 434 | var velZ = velocity.z + (particleSystem.random()) * velocityRandomness; 435 | 436 | // convert turbulence rating to something we can pack into a vec4 437 | var turbulence = Math.floor(turbulence * 254); 438 | 439 | // clamp our value to between 0. and 1. 440 | velX = Math.floor(maxSource * ((velX - -maxVel) / (maxVel - -maxVel))); 441 | velY = Math.floor(maxSource * ((velY - -maxVel) / (maxVel - -maxVel))); 442 | velZ = Math.floor(maxSource * ((velZ - -maxVel) / (maxVel - -maxVel))); 443 | 444 | self.velCol.array[i * 4 + 0] = decodeFloat(velX, velY, velZ, turbulence); //vel 445 | 446 | var rgb = hexToRgb(color); 447 | 448 | for (var c = 0; c < rgb.length; c++) { 449 | rgb[c] = Math.floor(rgb[c] + ((particleSystem.random()) * colorRandomness) * 254); 450 | if (rgb[c] > 254) rgb[c] = 254; 451 | if (rgb[c] < 0) rgb[c] = 0; 452 | } 453 | 454 | self.velCol.array[i * 4 + 1] = decodeFloat(rgb[0], rgb[1], rgb[2], 254); //color 455 | self.velCol.array[i * 4 + 2] = size + (particleSystem.random()) * sizeRandomness; //size 456 | self.velCol.array[i * 4 + 3] = lifetime; //lifespan 457 | 458 | if (this.offset == 0) { 459 | this.offset = self.PARTICLE_CURSOR; 460 | } 461 | 462 | self.count++; 463 | 464 | self.PARTICLE_CURSOR++; 465 | 466 | if (self.PARTICLE_CURSOR >= self.PARTICLE_COUNT) { 467 | self.PARTICLE_CURSOR = 0; 468 | } 469 | 470 | self.particleUpdate = true; 471 | 472 | } 473 | 474 | this.update = function(time) { 475 | 476 | self.time = time; 477 | self.particleShaderMat.uniforms['uTime'].value = time; 478 | 479 | this.geometryUpdate(); 480 | 481 | }; 482 | 483 | this.geometryUpdate = function() { 484 | if (self.particleUpdate == true) { 485 | self.particleUpdate = false; 486 | 487 | // if we can get away with a partial buffer update, do so 488 | if (self.offset + self.count < self.PARTICLE_COUNT) { 489 | self.posStart.updateRange.offset = self.velCol.updateRange.offset = self.offset * 4; 490 | self.posStart.updateRange.count = self.velCol.updateRange.count = self.count * 4; 491 | } else { 492 | self.posStart.updateRange.offset = 0; 493 | self.posStart.updateRange.count = self.velCol.updateRange.count = (self.PARTICLE_COUNT * 4); 494 | } 495 | 496 | self.posStart.needsUpdate = true; 497 | self.velCol.needsUpdate = true; 498 | 499 | self.offset = 0; 500 | self.count = 0; 501 | } 502 | } 503 | 504 | this.init(); 505 | 506 | } 507 | 508 | THREE.GPUParticleContainer.prototype = Object.create(THREE.Object3D.prototype); 509 | THREE.GPUParticleContainer.prototype.constructor = THREE.GPUParticleContainer; 510 | -------------------------------------------------------------------------------- /online/assets/src/index.js: -------------------------------------------------------------------------------- 1 | import { SpriteText2D, textAlign } from 'three-text2d' 2 | 3 | import './config/WebVRConfig' 4 | 5 | import 'webvr-polyfill/src/main' 6 | 7 | import 'webvr-boilerplate' 8 | import dat from 'dat-gui' 9 | 10 | import './vendor/three/examples/js/controls/MouseControls' 11 | import 'three/examples/js/controls/VRControls' 12 | import 'three/examples/js/effects/VREffect' 13 | 14 | import './vendor/charliehoey/GPUParticleSystem' 15 | 16 | import 'fpsmeter/dist/fpsmeter' 17 | 18 | import 'jquery-modal' 19 | 20 | import 'jquery-modal/jquery.modal.css' 21 | 22 | import MobileDetect from 'mobile-detect' 23 | 24 | import 'perfnow' 25 | 26 | import Stats from 'stats.js' 27 | 28 | import './vendor/modernizr/modernizr-custom' 29 | 30 | import '../../bower_components/ocean/water-material.js' 31 | 32 | 33 | 34 | var sky, sphere, sphere2, moonLightDebugSphere, sphereContainer, lightDirDebugSphere, cameraContainer, skyboxContainer, skyboxContainer2, scene, renderer, camera, dollyCam; 35 | var starData; 36 | var ms_Water; 37 | var skyBox; 38 | var meter, stats; 39 | var cameraFOV = 45; 40 | 41 | var moonScale = 15; 42 | 43 | var waterTextureSize = 512; 44 | 45 | var aMeshMirror; 46 | 47 | var moonLightDirection = new THREE.Vector3(0,0,0); 48 | 49 | var controls; 50 | 51 | var spriteCount = 0; 52 | 53 | 54 | 55 | var parameters = 56 | { 57 | rotX: 34, //don't touch - defines rotation of star container to skybox! 58 | rotY: 32, //don't touch - defines rotation of star container to skybox! 59 | rotZ: 60, //don't touch - defines rotation of star container to skybox! 60 | lightDirX: 0, 61 | lightDirY: 0, 62 | lightDirZ: -1000, 63 | 64 | moonLightDirX: -50, 65 | moonLightDirY: -500, 66 | moonLightDirZ: 100, 67 | 68 | rotXFloor: 270, //defines rotation of floor to sphereContainer 69 | rotYFloor: 0, //defines rotation of floor to sphereContainer 70 | rotZFloor: 0, //defines rotation of floor to sphereContainer 71 | floorOffset: -10, //defines offset of floor to sphereContainer 72 | cameraContainerRotX: 0, //defines default rotation of camera 73 | cameraContainerRotY: 160, //defines default rotation of camera - linked to compass direction 74 | cameraContainerRotZ: 0, //defines default rotation of camera 75 | cameraOffset: 0, //set to 0 to hide water, set to 1 to show 76 | sphereRotX: 0, //defines default rotation of sphere used to point to north and south celestial poles 77 | sphereRotY: 0, //defines default rotation of sphere used to point to north and south celestial poles - if the camera is added to `sphere` and the Y axis is rotated around the sky will appear to rotate around the north / south points 78 | sphereRotZ: 0, //defines default rotation of sphere used to point to north and south celestial poles 79 | sphere2RotX: 0, //defines default rotation of sphere used to point to north and south celestial poles 80 | sphere2RotY: 0, //defines default rotation of sphere used to point to north and south celestial poles 81 | sphere2RotZ: 0, //defines default rotation of sphere used to point to north and south celestial poles 82 | sphereContainerRotX: 0, //defines default rotation of sphereContainer. Controls global orientation of camera 83 | sphereContainerRotY: 0, //defines default rotation of sphereContainer. Controls global orientation of camera 84 | sphereContainerRotZ: 0, //defines default rotation of sphereContainer. Controls global orientation of camera 85 | 86 | skyboxContainerRotX: 0, 87 | skyboxContainerRotY: 0, //adjust Y value to rotate sky around fixed point - linked to time of day 88 | skyboxContainerRotZ: 0, 89 | 90 | skyboxContainer2RotX: 235, //ajust X value to rotate sky into position according to lat 91 | skyboxContainer2RotY: 0, 92 | skyboxContainer2RotZ: 175, //ajust X value to rotate sky into position according to long 93 | 94 | skyboxRotX: 54, 95 | skyboxRotY: 326, 96 | skyboxRotZ: 347 97 | }; 98 | 99 | var defaultWaterSide = THREE.FrontSide; 100 | 101 | var debugOn = false; 102 | 103 | var pointClouds = []; 104 | 105 | var spriteContainer; 106 | 107 | var md = new MobileDetect(window.navigator.userAgent); 108 | 109 | var androidVersion = md.versionStr('Android'); 110 | 111 | var iOSVersion = md.versionStr('iOS') 112 | 113 | 114 | var oldAndroid = (androidVersion !== null && cmpVersions(androidVersion,'5', '.') < 0); 115 | 116 | var oldIOS = (iOSVersion !== null && cmpVersions(iOSVersion,'9', '_') < 0); 117 | 118 | var canHandleOrientation = false; 119 | 120 | (function() { 121 | 122 | var originalGetExtensionFunction = WebGLRenderingContext.prototype.getExtension; 123 | 124 | // start with this array empty. Once you know which extensions 125 | // the app is requesting you can then selectively add them here 126 | // to figure out which ones are required. 127 | var extensionToReject = [ 128 | "OES_texture_float", 129 | "OES_texture_float_linear", 130 | ]; 131 | 132 | WebGLRenderingContext.prototype.getExtension = function() { 133 | var name = arguments[0]; 134 | if (extensionToReject.indexOf(name) >= 0) { 135 | return null; 136 | } 137 | var ext = originalGetExtensionFunction.apply(this, arguments); 138 | return ext; 139 | }; 140 | 141 | }()); 142 | 143 | window.addEventListener("compassneedscalibration", function(event) { 144 | 145 | }, true); 146 | 147 | 148 | 149 | function checkCanHandleOrientation(){ 150 | 151 | if (window.DeviceOrientationEvent) { 152 | window.addEventListener("deviceorientation", handleOrientation, false); 153 | } 154 | 155 | function handleOrientation(event){ 156 | canHandleOrientation = (event !== null); // event will be either null or with event data 157 | } 158 | } 159 | 160 | function cmpVersions (a, b, delimeter) { 161 | var i, l, diff, segmentsA, segmentsB; 162 | 163 | segmentsA = a.replace(/(\.0+)+$/, '').split(delimeter); 164 | segmentsB = b.replace(/(\.0+)+$/, '').split(delimeter); 165 | l = Math.min(segmentsA.length, segmentsB.length); 166 | 167 | for (i = 0; i < l; i++) { 168 | diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10); 169 | if (diff !== 0) { 170 | return diff; 171 | } 172 | } 173 | return segmentsA.length - segmentsB.length; 174 | } 175 | 176 | function initFPSMeter(){ 177 | // Meter will be attached to `document.body` with all default options. 178 | meter = new FPSMeter({ 179 | interval: 100, // Update interval in milliseconds. 180 | smoothing: 10, // Spike smoothing strength. 1 means no smoothing. 181 | show: 'fps', // Whether to show 'fps', or 'ms' = frame duration in milliseconds. 182 | toggleOn: 'click', // Toggle between show 'fps' and 'ms' on this event. 183 | decimals: 1, // Number of decimals in FPS number. 1 = 59.9, 2 = 59.94, ... 184 | maxFps: 60, // Max expected FPS value. 185 | threshold: 100, // Minimal tick reporting interval in milliseconds. 186 | 187 | // Meter position 188 | position: 'absolute', // Meter position. 189 | zIndex: 10, // Meter Z index. 190 | left: '5px', // Meter left offset. 191 | top: '5px', // Meter top offset. 192 | right: 'auto', // Meter right offset. 193 | bottom: 'auto', // Meter bottom offset. 194 | margin: '0 0 0 0', // Meter margin. Helps with centering the counter when left: 50%; 195 | 196 | // Theme 197 | theme: 'dark', // Meter theme. Build in: 'dark', 'light', 'transparent', 'colorful'. 198 | heat: 1, // Allow themes to use coloring by FPS heat. 0 FPS = red, maxFps = green. 199 | 200 | // Graph 201 | graph: 1, // Whether to show history graph. 202 | history: 20 // How many history states to show in a graph. 203 | }); 204 | } 205 | 206 | function loadSkyBox() { 207 | var path = "assets/img/skybox3-hr/"; 208 | var format = '.png'; 209 | var urls = [ 210 | path + 'skybox_0' + format, path + 'skybox_1' + format, 211 | path + 'skybox_2' + format, path + 'skybox_3' + format, 212 | path + 'skybox_4' + format, path + 'skybox_5' + format 213 | ]; 214 | 215 | //skybox is now no longer using shaders and has actual geometry instead so we can rotate it 216 | var skyGeometry = new THREE.CubeGeometry( 100000, 100000, 100000 ); 217 | 218 | var materialArray = []; 219 | for (var i = 0; i < 6; i++) 220 | materialArray.push( new THREE.MeshBasicMaterial({ 221 | map: new THREE.TextureLoader().load(urls[i]), 222 | side: THREE.BackSide 223 | })); 224 | var skyMaterial = new THREE.MeshFaceMaterial( materialArray ); 225 | skyBox = new THREE.Mesh( skyGeometry, skyMaterial ); 226 | 227 | skyboxContainer = new THREE.Object3D(); 228 | 229 | skyboxContainer2 = new THREE.Object3D(); 230 | 231 | scene.add(skyboxContainer2); 232 | 233 | skyboxContainer2.add(skyboxContainer); 234 | 235 | skyboxContainer.add(skyBox); 236 | 237 | skyboxContainer2.add( sphere2 ); 238 | 239 | skyBox.add(sphereContainer); 240 | 241 | if(debugOn){ 242 | 243 | 244 | var gui = new dat.GUI(); 245 | 246 | gui.add( parameters, 'rotX' ).min(0).max(359).step(1).name('Stars RotX'); 247 | gui.add( parameters, 'rotY' ).min(0).max(359).step(1).name('Stars RotY'); 248 | gui.add( parameters, 'rotZ' ).min(0).max(359).step(1).name('Stars RotZ'); 249 | 250 | gui.add( parameters, 'lightDirX' ).min(-2000).max(2000).step(50).name('LightDir X'); 251 | gui.add( parameters, 'lightDirY' ).min(-2000).max(2000).step(50).name('LightDir Y'); 252 | gui.add( parameters, 'lightDirZ' ).min(-2000).max(2000).step(50).name('LightDir Z'); 253 | 254 | gui.add( parameters, 'rotXFloor' ).min(0).max(359).step(1).name('Floor RotX'); 255 | gui.add( parameters, 'rotYFloor' ).min(0).max(359).step(1).name('Floor RotY'); 256 | gui.add( parameters, 'rotZFloor' ).min(0).max(359).step(1).name('Floor RotZ'); 257 | 258 | gui.add( parameters, 'floorOffset' ).min(-200).max(200).step(5).name('Floor Offset'); 259 | 260 | gui.add( parameters, 'cameraContainerRotX' ).min(0).max(359).step(1).name('Camera RotX'); 261 | gui.add( parameters, 'cameraContainerRotY' ).min(0).max(359).step(1).name('Camera RotY'); 262 | gui.add( parameters, 'cameraContainerRotZ' ).min(0).max(359).step(1).name('Camera RotZ'); 263 | 264 | gui.add( parameters, 'cameraOffset' ).min(-100).max(100).step(1).name('Camera Offset'); 265 | 266 | 267 | gui.add( parameters, 'sphereRotX' ).min(0).max(359).step(1).name('Sphere RotX'); 268 | gui.add( parameters, 'sphereRotY' ).min(0).max(359).step(1).name('Sphere RotY'); 269 | gui.add( parameters, 'sphereRotZ' ).min(0).max(359).step(1).name('Sphere RotZ'); 270 | 271 | gui.add( parameters, 'sphere2RotX' ).min(0).max(359).step(1).name('Sphere2 RotX'); 272 | gui.add( parameters, 'sphere2RotY' ).min(0).max(359).step(1).name('Sphere2 RotY'); 273 | gui.add( parameters, 'sphere2RotZ' ).min(0).max(359).step(1).name('Sphere2 RotZ'); 274 | 275 | gui.add( parameters, 'skyboxRotX' ).min(0).max(359).step(1).name('Skybox RotX'); 276 | gui.add( parameters, 'skyboxRotY' ).min(0).max(359).step(1).name('Skybox RotY'); 277 | gui.add( parameters, 'skyboxRotZ' ).min(0).max(359).step(1).name('Skybox RotZ'); 278 | 279 | gui.add( parameters, 'skyboxContainerRotX' ).min(0).max(359).step(1).name('SkyboxCon RotX'); 280 | gui.add( parameters, 'skyboxContainerRotY' ).min(0).max(359).step(1).name('SkyboxCon RotY'); 281 | gui.add( parameters, 'skyboxContainerRotZ' ).min(0).max(359).step(1).name('SkyboxCon RotZ'); 282 | 283 | gui.add( parameters, 'skyboxContainer2RotX' ).min(0).max(359).step(1).name('SkyboxCon2 RotX'); 284 | gui.add( parameters, 'skyboxContainer2RotY' ).min(0).max(359).step(1).name('SkyboxCon2 RotY'); 285 | gui.add( parameters, 'skyboxContainer2RotZ' ).min(0).max(359).step(1).name('SkyboxCon2 RotZ'); 286 | 287 | gui.open(); 288 | } 289 | 290 | 291 | } 292 | 293 | function addBasicGroundPlane(){ 294 | var geometry = new THREE.PlaneGeometry( 1500, 1500, 1, 1 ); 295 | var material = new THREE.MeshBasicMaterial( {color: 0x181818, side: THREE.FrontSide} ); 296 | aMeshMirror = new THREE.Mesh( geometry, material ); 297 | } 298 | 299 | function initScene(){ 300 | 301 | // Setup three.js WebGL renderer. Note: Antialiasing is a big performance hit. 302 | // Only enable it if you actually need to. 303 | renderer = new THREE.WebGLRenderer({antialias: false, alpha: false}); //performance hits if antialias or alpha used 304 | renderer.setPixelRatio(window.devicePixelRatio); 305 | 306 | // Append the canvas element created by the renderer to document body element. 307 | document.body.appendChild(renderer.domElement); 308 | 309 | 310 | //var starCount = 10000; 311 | var normalizeRadius = 500; 312 | var pointCloudCount = 3; 313 | var distanceScale = 1; //keep this at 1 now that we are normalizing star distance 314 | var starMagnitudes = 6; //number of visible star magnitude buckets 315 | var starMagnitudeScaleFactor = 4; //higher number = smaller stars 316 | var starSpriteSize = 5; //scaling factor of star sprites for near stars that make up major constellations 317 | 318 | // Create a three.js scene. 319 | scene = new THREE.Scene(); 320 | 321 | window.scene = scene; //export as window.scene so the THREE.js inspector can access it 322 | 323 | cameraContainer = new THREE.Object3D(); 324 | 325 | // Create a three.js camera. 326 | camera = new THREE.PerspectiveCamera(cameraFOV, window.innerWidth / window.innerHeight, 0.1, 2000000); 327 | dollyCam = new THREE.PerspectiveCamera(); 328 | 329 | dollyCam.add(camera); 330 | 331 | cameraContainer.add(dollyCam); 332 | 333 | // Apply VR headset positional data to camera. 334 | 335 | controls = new THREE.VRControls(camera); 336 | 337 | // Apply VR stereo rendering to renderer. 338 | var effect = new THREE.VREffect(renderer); 339 | effect.setSize(window.innerWidth, window.innerHeight); 340 | 341 | var loader = new THREE.TextureLoader(); 342 | 343 | // Add light 344 | var directionalLight = new THREE.DirectionalLight(0xffff55, 1); 345 | directionalLight.position.set(parameters.lightDirX, parameters.lightDirY, parameters.lightDirZ); 346 | 347 | 348 | if(oldAndroid || oldIOS){ 349 | addBasicGroundPlane(); 350 | }else{ 351 | // Load textures 352 | var waterNormals = new THREE.TextureLoader().load('assets/img/waternormals.jpg'); 353 | waterNormals.wrapS = waterNormals.wrapT = THREE.RepeatWrapping; 354 | 355 | 356 | // Create the water effect - use THREE.BackSide or THREE.FrontSide depending on the default rotation of the cameraContainer 357 | ms_Water = new THREE.Water(renderer, camera, scene, { 358 | textureWidth: waterTextureSize, 359 | textureHeight: waterTextureSize, 360 | waterNormals: waterNormals, 361 | alpha: 1.0, 362 | sunDirection: directionalLight.position.normalize(), 363 | sunColor: 0xffffff, 364 | waterColor: 0x001e0f, 365 | distortionScale: 50.0, 366 | //side: (parameters.cameraContainerRotY == 180) ? THREE.FrontSide : THREE.BackSide 367 | side: defaultWaterSide 368 | }); 369 | aMeshMirror = new THREE.Mesh( 370 | new THREE.PlaneBufferGeometry(1500, 1500, 10, 10), 371 | ms_Water.material 372 | ); 373 | 374 | aMeshMirror.add(ms_Water); 375 | } 376 | 377 | 378 | sphereContainer = new THREE.Object3D; 379 | 380 | var geometry = new THREE.SphereGeometry( 5, 32, 32 ); 381 | 382 | if(debugOn){ 383 | var material = new THREE.MeshBasicMaterial( {color: 0xffff00, wireframe: true, opacity: 0.05, transparent: true} ); 384 | 385 | sphere = new THREE.Mesh( geometry, material ); 386 | }else{ 387 | sphere = new THREE.Object3D; 388 | } 389 | 390 | 391 | 392 | if(debugOn){ 393 | 394 | var material2 = new THREE.MeshBasicMaterial( {color: 0xffffff, wireframe: true, opacity: 0.05, transparent: true} ); 395 | 396 | sphere2 = new THREE.Mesh( geometry, material2 ); 397 | }else{ 398 | sphere2 = new THREE.Object3D; 399 | } 400 | 401 | var moonMaterial = new THREE.MeshBasicMaterial( {color: 0xffffff, wireframe: false, opacity: 1, transparent: false/*, map: moonTexture*/} ); 402 | 403 | moonLightDebugSphere = new THREE.Mesh( geometry, moonMaterial ); 404 | 405 | 406 | moonLightDebugSphere.position.set(parameters.moonLightDirX,parameters.moonLightDirY,parameters.moonLightDirZ); 407 | 408 | 409 | lightDirDebugSphere = new THREE.Mesh( geometry, moonMaterial ); 410 | 411 | 412 | lightDirDebugSphere.position.set(parameters.lightDirX,parameters.lightDirY,parameters.lightDirZ); 413 | 414 | lightDirDebugSphere.rotation.set(0,180,0); 415 | lightDirDebugSphere.scale.set(moonScale,moonScale,moonScale); 416 | 417 | 418 | scene.add(lightDirDebugSphere); 419 | 420 | sphereContainer.add( sphere ); 421 | 422 | scene.add(aMeshMirror); //reflections don't work correctly unless aMeshMirror added to scene 423 | scene.add(cameraContainer); 424 | aMeshMirror.add(directionalLight); //reflections don't work correctly unless light added to scene 425 | 426 | 427 | loader.load('assets/img/star_preview3.png', onStarTextureLoaded); 428 | 429 | function onStarTextureLoaded(texture){ 430 | initStars(texture); 431 | } 432 | 433 | function centerObject(obj){ 434 | var box = new THREE.Box3().setFromObject( obj ); 435 | box.center( obj.position ); // this re-sets the mesh position 436 | obj.position.multiplyScalar( - 1 ); 437 | } 438 | 439 | 440 | // Create a VR manager helper to enter and exit VR mode. 441 | var params = { 442 | hideButton: true, // Default: false. 443 | isUndistorted: true // Default: false. 444 | }; 445 | var manager = new WebVRManager(renderer, effect, params); 446 | 447 | function initStars(texture){ 448 | var x, y, z; 449 | 450 | //generate point cloud geometry 451 | 452 | var pointCloudGeometries = new Array(pointCloudCount); 453 | 454 | spriteContainer = new THREE.Object3D(); 455 | skyBox.add(spriteContainer); 456 | 457 | for(var k = 0; k < pointCloudCount; k++){ 458 | pointCloudGeometries[k] = new Array(starMagnitudes); 459 | for(var a = 0; a < starMagnitudes; a++){ 460 | pointCloudGeometries[k][a] = new THREE.Geometry(); 461 | } 462 | } 463 | 464 | for(var l = 0; l < starData.count; l++){ 465 | 466 | x = starData.x[l]*distanceScale; 467 | y = starData.y[l]*distanceScale; 468 | z = starData.z[l]*distanceScale; 469 | 470 | //normalize distance of stars, but keep apparent position in sky - we'll use the mag value to determine size later 471 | x = x * normalizeRadius/starData.dist[l]; 472 | y = y * normalizeRadius/starData.dist[l]; 473 | z = z * normalizeRadius/starData.dist[l]; 474 | 475 | var targetPointCloudGeometry; 476 | var doInsertPoint = true; 477 | var doInsertSprite = false; 478 | 479 | var starLabel = (starData.proper[l] !== null) ? starData.proper[l] : (starData.bf[l] !== null) ? starData.bf[l] : (starData.gl[l] !== null) ? starData.gl[l] : null; 480 | 481 | 482 | if(starLabel !== null && starData.mag[l] < 4){ 483 | 484 | doInsertSprite = true; 485 | 486 | if(debugOn){ 487 | 488 | 489 | if(starLabel !== null){ 490 | var sprite = new SpriteText2D(starLabel, { align: new THREE.Vector2(0, 0), font: '10px Arial', fillStyle: '#ffffff' , antialias: false }) 491 | sprite.position.set(x,y,z); 492 | spriteContainer.add(sprite); 493 | 494 | sprite.lookAt(camera.position); 495 | 496 | } 497 | 498 | } 499 | } 500 | 501 | //assign points to geometries with specific colors - use first letter of spect value to define color 502 | //determine which color bucket the star should go into 503 | if(starData.spect[l] !== null){ 504 | switch(starData.spect[l].charAt(0)){ 505 | case "O": 506 | targetPointCloudGeometry = pointCloudGeometries[0]; 507 | break; 508 | case "B": 509 | targetPointCloudGeometry = pointCloudGeometries[1]; 510 | break; 511 | case "A": 512 | targetPointCloudGeometry = pointCloudGeometries[2]; 513 | break; 514 | case "F": 515 | targetPointCloudGeometry = pointCloudGeometries[3]; 516 | break; 517 | case "G": 518 | targetPointCloudGeometry = pointCloudGeometries[4]; 519 | break; 520 | case "K": 521 | targetPointCloudGeometry = pointCloudGeometries[5]; 522 | break; 523 | case "M": 524 | targetPointCloudGeometry = pointCloudGeometries[6]; 525 | break; 526 | } 527 | }else{ 528 | targetPointCloudGeometry = pointCloudGeometries[2]; 529 | } 530 | 531 | targetPointCloudGeometry = pointCloudGeometries[2]; 532 | 533 | //determine which size bucket the star should go into 534 | 535 | var targetSize; 536 | 537 | if (starData.mag[l] < 0) { 538 | targetSize = 0; 539 | } else if (starData.mag[l] >= 0 && starData.mag[l] < 1) { 540 | targetSize = 1; 541 | } else if (starData.mag[l] >= 1 && starData.mag[l] < 2) { 542 | targetSize = 2; 543 | } else if (starData.mag[l] >= 2 && starData.mag[l] < 3) { 544 | targetSize = 3; 545 | } else if (starData.mag[l] >= 3 && starData.mag[l] < 4) { 546 | targetSize = 4; 547 | } else if (starData.mag[l] >= 4 && starData.mag[l] < 5) { 548 | targetSize = 5; 549 | } else if (starData.mag[l] >= 5 && starData.mag[l] < 6) { 550 | targetSize = 6; 551 | doInsertPoint = false; 552 | } else if (starData.mag[l] >= 6 && starData.mag[l] < 7) { 553 | targetSize = 7; 554 | doInsertPoint = false; 555 | } else if (starData.mag[l] >= 7 && starData.mag[l] < 16) { 556 | doInsertPoint = false; 557 | } else { 558 | doInsertPoint = false; 559 | } 560 | 561 | if(doInsertPoint && typeof targetPointCloudGeometry !== 'undefined' && typeof targetPointCloudGeometry[targetSize] !== 'undefined'){ 562 | targetPointCloudGeometry[targetSize].vertices.push(new THREE.Vector3(x,y,z)); 563 | } 564 | 565 | if(doInsertSprite && doInsertPoint){ 566 | var material = new THREE.SpriteMaterial( { map: texture, color: 0xffffff, fog: false, depthTest: true} ); 567 | 568 | spriteCount++; 569 | 570 | 571 | var sprite = new THREE.Sprite( material ); 572 | sprite.position.set(x,y,z); 573 | spriteContainer.add( sprite ); 574 | sprite.lookAt(camera.position); 575 | 576 | 577 | sprite.scale.set((starMagnitudes-starData.mag[l])*starSpriteSize,(starMagnitudes-starData.mag[l])*starSpriteSize,(starMagnitudes-starData.mag[l])*starSpriteSize); 578 | 579 | } 580 | } 581 | 582 | console.log("Stars generated: ",spriteCount); 583 | 584 | 585 | for(var j = 0; j < pointCloudCount; j++){ 586 | 587 | var color; 588 | 589 | switch(j){ 590 | case 0: 591 | color = 0x0000FF; 592 | break; 593 | case 1: 594 | color = 0xADD8E6; 595 | break; 596 | case 2: 597 | color = 0xFFFFFF; 598 | break; 599 | case 3: 600 | color = 0xFFFFE0; 601 | break; 602 | case 4: 603 | color = 0xFFFF00; 604 | break; 605 | case 5: 606 | color = 0xFFA500; 607 | break; 608 | case 6: 609 | color = 0xFF0000; 610 | break; 611 | case 7: 612 | color = 0x663399; 613 | break; 614 | } 615 | 616 | for(var m = 0; m < starMagnitudes; m++){ 617 | var material = new THREE.PointsMaterial({ 618 | color: color, 619 | size: (starMagnitudes-m+1)/starMagnitudeScaleFactor, 620 | depthTest: false, transparent : false 621 | //wireframe property not supported on PointsMaterial 622 | }); 623 | 624 | var pointCloud = new THREE.Points(pointCloudGeometries[j][m], material); 625 | 626 | pointClouds.push(pointCloud); 627 | 628 | centerObject(pointCloud); 629 | } 630 | } 631 | } 632 | 633 | function onWindowResize() { 634 | 635 | camera.aspect = window.innerWidth / window.innerHeight; 636 | camera.updateProjectionMatrix(); 637 | 638 | renderer.setSize( window.innerWidth, window.innerHeight ); 639 | 640 | render(); 641 | 642 | } 643 | 644 | // Request animation frame loop function 645 | var lastRender = 0; 646 | function animate(timestamp) { 647 | 648 | requestAnimationFrame(animate); 649 | 650 | if(typeof meter !== 'undefined'){ 651 | meter.tickStart(); 652 | } 653 | 654 | if(typeof stats !== 'undefined'){ 655 | stats.begin(); 656 | } 657 | 658 | lastRender = timestamp; 659 | 660 | //dont render water if it doesnt exist 661 | if(typeof ms_Water !== 'undefined'){ 662 | 663 | if(ms_Water !== null){ 664 | 665 | ms_Water.material.uniforms.time.value += 1.0 / 60.0; 666 | ms_Water.render(); 667 | 668 | } 669 | 670 | } 671 | 672 | // Update VR headset position and apply to camera. 673 | 674 | if(typeof controls !== 'undefined'/* && typeof dollyControls !== 'undefined'*/){ 675 | 676 | controls.update(); 677 | } 678 | 679 | 680 | 681 | for(var i = 0; i < pointClouds.length; i++){ 682 | // rotate the skybox around its axis 683 | 684 | pointClouds[i].rotation.set(parameters.rotX * Math.PI / 180,parameters.rotY * Math.PI / 180,parameters.rotZ * Math.PI / 180); 685 | } 686 | 687 | if(typeof cameraContainer !== 'undefined'){ 688 | cameraContainer.rotation.set(parameters.cameraContainerRotX * Math.PI / 180,parameters.cameraContainerRotY * Math.PI / 180,parameters.cameraContainerRotZ * Math.PI / 180); 689 | camera.position.set(0,parameters.cameraOffset,0); 690 | 691 | } 692 | 693 | 694 | if(typeof spriteContainer !== 'undefined'){ 695 | spriteContainer.rotation.set(parameters.rotX * Math.PI / 180,parameters.rotY * Math.PI / 180,parameters.rotZ * Math.PI / 180); 696 | } 697 | 698 | if(typeof aMeshMirror !== 'undefined' && aMeshMirror !== null){ 699 | aMeshMirror.rotation.set(parameters.rotXFloor * Math.PI / 180,parameters.rotYFloor * Math.PI / 180,parameters.rotZFloor * Math.PI / 180); 700 | 701 | sphereContainer.rotation.set(parameters.sphereContainerRotX * Math.PI / 180,parameters.sphereContainerRotY * Math.PI / 180,parameters.sphereContainerRotZ * Math.PI / 180); 702 | sphere.rotation.set(parameters.sphereRotX * Math.PI / 180,parameters.sphereRotY * Math.PI / 180,parameters.sphereRotZ * Math.PI / 180); 703 | sphere2.rotation.set(parameters.sphere2RotX * Math.PI / 180,parameters.sphere2RotY * Math.PI / 180,parameters.sphere2RotZ * Math.PI / 180); 704 | 705 | 706 | aMeshMirror.position.set(0,parameters.floorOffset,0); 707 | 708 | sphereContainer.rotation.set(parameters.sphereContainerRotX * Math.PI / 180,parameters.sphereContainerRotY * Math.PI / 180,parameters.sphereContainerRotZ * Math.PI / 180); 709 | sphere.rotation.set(parameters.sphereRotX * Math.PI / 180,parameters.sphereRotY * Math.PI / 180,parameters.sphereRotZ * Math.PI / 180); 710 | sphere2.rotation.set(parameters.sphere2RotX * Math.PI / 180,parameters.sphere2RotY * Math.PI / 180,parameters.sphere2RotZ * Math.PI / 180); 711 | 712 | 713 | } 714 | 715 | if(typeof skyBox !== 'undefined'){ 716 | skyBox.rotation.set(parameters.skyboxRotX * Math.PI / 180,parameters.skyboxRotY * Math.PI / 180,parameters.skyboxRotZ * Math.PI / 180); 717 | 718 | skyboxContainer.rotation.set(parameters.skyboxContainerRotX * Math.PI / 180,parameters.skyboxContainerRotY * Math.PI / 180,parameters.skyboxContainerRotZ * Math.PI / 180); 719 | 720 | skyboxContainer2.rotation.set(parameters.skyboxContainer2RotX * Math.PI / 180,parameters.skyboxContainer2RotY * Math.PI / 180,parameters.skyboxContainer2RotZ * Math.PI / 180); 721 | } 722 | 723 | if(typeof ms_Water !== 'undefined' && typeof directionalLight !== 'undefined'){ 724 | 725 | 726 | moonLightDirection.set(parameters.moonLightDirX,parameters.moonLightDirY,parameters.moonLightDirZ); 727 | 728 | moonLightDebugSphere.position.set(parameters.moonLightDirX,parameters.moonLightDirY,parameters.moonLightDirZ); 729 | 730 | directionalLight.position.set(parameters.lightDirX,parameters.lightDirY,parameters.lightDirZ); 731 | lightDirDebugSphere.position.set(parameters.lightDirX,parameters.lightDirY,parameters.lightDirZ); 732 | ms_Water.material.uniforms.sunDirection.value = directionalLight.position.normalize(); 733 | } 734 | 735 | render(timestamp); 736 | 737 | 738 | if(typeof meter !== 'undefined') { 739 | 740 | meter.tick(); 741 | 742 | } 743 | 744 | if(typeof stats !== 'undefined'){ 745 | stats.end(); 746 | } 747 | 748 | } 749 | 750 | function render(timestamp) { 751 | 752 | manager.render(scene, camera, timestamp); 753 | 754 | } 755 | 756 | // Kick off animation loop 757 | onWindowResize(); 758 | animate(performance ? performance.now() : Date.now(),true); 759 | 760 | // Reset the position sensor when 'z' pressed. 761 | function onKey(event) { 762 | if (typeof controls !== 'undefined' && event.keyCode == 90) { // z 763 | controls.resetSensor(); 764 | } 765 | } 766 | 767 | window.addEventListener('keydown', onKey, true); 768 | } 769 | 770 | 771 | function loadStarData(){ 772 | 773 | $.getJSON( 'assets/data/data.json', {}, function(data){ 774 | 775 | //init typed arrays for star data 776 | var n = data.length; 777 | 778 | starData = { 779 | dist: new Float64Array(n), 780 | proper: new Array(n), 781 | x: new Float64Array(n), 782 | y: new Float64Array(n), 783 | z: new Float64Array(n), 784 | spect: new Array(n), 785 | mag: new Float64Array(n), 786 | count: n, 787 | absmag: new Float64Array(n), 788 | con: new Array(n), 789 | bf: new Array(n), 790 | gl: new Array(n) 791 | }; 792 | 793 | //populated typed arrays with star data 794 | var i = 0; 795 | while (i < n ) { 796 | starData.dist[i] = data[i].dist; 797 | 798 | starData.proper[i] = data[i].proper; 799 | starData.x[i] = data[i].x; 800 | starData.y[i] = data[i].y; 801 | starData.z[i] = data[i].z; 802 | starData.spect[i] = data[i].spect; 803 | starData.mag[i] = data[i].mag; 804 | starData.absmag[i] = data[i].absmag; 805 | starData.con[i] = data[i].con; 806 | starData.bf[i] = data[i].bf; 807 | starData.gl[i] = data[i].gl; 808 | 809 | i++; 810 | } 811 | 812 | Modernizr.addTest('highres', function() { 813 | // for opera 814 | var ratio = '2.99/2'; 815 | // for webkit 816 | var num = '1.499'; 817 | var mqs = [ 818 | 'only screen and (-o-min-device-pixel-ratio:' + ratio + ')', 819 | 'only screen and (min--moz-device-pixel-ratio:' + num + ')', 820 | 'only screen and (-webkit-min-device-pixel-ratio:' + num + ')', 821 | 'only screen and (min-device-pixel-ratio:' + num + ')' 822 | ]; 823 | var isHighRes = false; 824 | 825 | // loop through vendors, checking non-prefixed first 826 | for (var i = mqs.length - 1; i >= 0; i--) { 827 | isHighRes = Modernizr.mq( mqs[i] ); 828 | // if found one, return early 829 | if ( isHighRes ) { 830 | return isHighRes; 831 | } 832 | } 833 | // not highres 834 | return isHighRes; 835 | }); 836 | 837 | //rendering appears to be partially broken on iOS 8 on latest version of three.js iOS 9 has about 90% market 838 | //share so we can recommend users update to that version 839 | //TODO: add three-orbit-controls library to allow mouse controls to be used by default on devices that don't 840 | //support motion controls 841 | 842 | //TODO: move these checks out into a first-run function along with the FPS test to determin if device is capable 843 | //of running the experience. Magnometer should be optional as it is only required to get the correct 844 | //orientation, but user should be warned that their direction won't be accurate 845 | 846 | checkCanHandleOrientation(); 847 | 848 | initScene(); 849 | 850 | loadSkyBox(); 851 | 852 | initFPSMeter(); 853 | }); 854 | } 855 | 856 | 857 | 858 | $(document).ready(function(){ 859 | loadStarData(); 860 | }); 861 | 862 | --------------------------------------------------------------------------------