├── LICENSE ├── README.md ├── img ├── clouds_down.jpg ├── clouds_east.jpg ├── clouds_north.jpg ├── clouds_south.jpg ├── clouds_up.jpg ├── clouds_west.jpg ├── grimmnight_down.jpg ├── grimmnight_east.jpg ├── grimmnight_north.jpg ├── grimmnight_south.jpg ├── grimmnight_up.jpg ├── grimmnight_west.jpg ├── interstellar_down.jpg ├── interstellar_east.jpg ├── interstellar_north.jpg ├── interstellar_south.jpg ├── interstellar_up.jpg ├── interstellar_west.jpg ├── miramar_down.jpg ├── miramar_east.jpg ├── miramar_north.jpg ├── miramar_south.jpg ├── miramar_up.jpg ├── miramar_west.jpg ├── mountains.png ├── sky_down.jpg ├── sky_east.jpg ├── sky_north.jpg ├── sky_south.jpg ├── sky_up.jpg ├── sky_west.jpg ├── sunset_down.jpg ├── sunset_east.jpg ├── sunset_north.jpg ├── sunset_south.jpg ├── sunset_up.jpg ├── sunset_west.jpg ├── violent_days_down.jpg ├── violent_days_east.jpg ├── violent_days_north.jpg ├── violent_days_south.jpg ├── violent_days_up.jpg ├── violent_days_west.jpg └── water-drop.png ├── index.html ├── js ├── controls │ └── OrbitControls.js ├── demo.js ├── effects │ ├── MirrorRenderer.js │ └── Ocean.js ├── libs │ ├── dat.gui.min.js │ ├── messg.css │ ├── messg.min.js │ └── three.min.js ├── loaders │ ├── MTLLoader.js │ └── OBJMTLLoader.js └── shaders │ ├── CloudShader2.js │ ├── FFTOceanShader.js │ ├── OceanShader.js │ ├── RainShader.js │ ├── ScreenSpaceShader.js │ └── sky4.js ├── models └── BlackPearl │ ├── BlackPearl.mtl │ ├── BlackPearl.obj │ ├── Map__5_Tiles.jpg │ ├── Map__5_Tiles.tga │ ├── Wood_floor_ffs062.jpg │ ├── dark_teak.jpg │ ├── dark_wood_planks.jpg │ ├── flag.jpg │ └── sails1.jpg ├── sound ├── rain.mp3 └── waves.mp3 └── visual ├── day_ocean_fft.jpg ├── fft-ocean_night.jpg ├── fft-ocean_night_far.jpg ├── night_ocean_fft.jpg ├── screen_space_256.jpg ├── screen_space_64.jpg ├── screen_space_full.jpg └── sunset_ocean_fft.jpg /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jérémy Bouny 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A trip under the moonlight 2 | ========= 3 | 4 | Fast Fourier Transform ocean rendering for Three.js 5 | 6 | This demo shows a way to render realistic scenes in WebGL. 7 | 8 | You can travel by using the keyboard and the environment can be selected. You can also explore the working of the scene by typing 'h'. 9 | 10 | Live demo: https://jbouny.github.io/fft-ocean/ 11 | 12 | ### Screenshots 13 | 14 | ![Alt text](/visual/night_ocean_fft.jpg "Ocean rendering in a night environment") 15 | 16 | ![Alt text](/visual/sunset_ocean_fft.jpg "Ocean rendering in a sunset environment") 17 | 18 | ![Alt text](/visual/day_ocean_fft.jpg "Ocean rendering in a day environment") 19 | 20 | ### Features 21 | 22 | - Ocean rendering is based on the generation of a displacement map and a normal map applied with vertex and fragment shader 23 | - Ocean mesh is computed in screen space 24 | - Clouds and rain come from Three.js community 25 | 26 | ### Screen space grid 27 | 28 | The screen space working is in fact simple: 29 | 30 | - Add a grid in the scene, anywhere 31 | - The grid must always be seen by the camera, or the vertex shader will not be applied 32 | - In the vertex shader, the grid is then put in front of the camera in order to fill the entire screen 33 | - In the vertex shader, the grid is projected on a 3d plane (just change the depth) 34 | 35 | A complete explanation can be find here: http://habib.wikidot.com/projected-grid-ocean-shader-full-html-version 36 | 37 | So, results are here. 38 | 39 | With a 64*64 grid: 40 | 41 | ![Alt text](/visual/screen_space_64.jpg "Screen space 64*64 grid") 42 | 43 | With a 256*256 grid: 44 | 45 | ![Alt text](/visual/screen_space_256.jpg "Screen space 256*256 grid") 46 | 47 | 48 | 49 | 50 | ### Acknowledgments 51 | 52 | - @mrdoob and the three.js community for the library 53 | - @zz85 for the cloud shader 54 | - @dli for the original WebGL version of the fft rendering http://david.li/waves/ 55 | - Aleksandr Albert for the three.js version and improvements www.routter.co.tt 56 | - Hipshot for the skyboxes http://www.quake3world.com/forum/viewtopic.php?t=9242 57 | - Kevin Boone for the 3d Black Pearl model 58 | -------------------------------------------------------------------------------- /img/clouds_down.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/clouds_down.jpg -------------------------------------------------------------------------------- /img/clouds_east.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/clouds_east.jpg -------------------------------------------------------------------------------- /img/clouds_north.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/clouds_north.jpg -------------------------------------------------------------------------------- /img/clouds_south.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/clouds_south.jpg -------------------------------------------------------------------------------- /img/clouds_up.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/clouds_up.jpg -------------------------------------------------------------------------------- /img/clouds_west.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/clouds_west.jpg -------------------------------------------------------------------------------- /img/grimmnight_down.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/grimmnight_down.jpg -------------------------------------------------------------------------------- /img/grimmnight_east.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/grimmnight_east.jpg -------------------------------------------------------------------------------- /img/grimmnight_north.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/grimmnight_north.jpg -------------------------------------------------------------------------------- /img/grimmnight_south.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/grimmnight_south.jpg -------------------------------------------------------------------------------- /img/grimmnight_up.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/grimmnight_up.jpg -------------------------------------------------------------------------------- /img/grimmnight_west.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/grimmnight_west.jpg -------------------------------------------------------------------------------- /img/interstellar_down.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/interstellar_down.jpg -------------------------------------------------------------------------------- /img/interstellar_east.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/interstellar_east.jpg -------------------------------------------------------------------------------- /img/interstellar_north.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/interstellar_north.jpg -------------------------------------------------------------------------------- /img/interstellar_south.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/interstellar_south.jpg -------------------------------------------------------------------------------- /img/interstellar_up.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/interstellar_up.jpg -------------------------------------------------------------------------------- /img/interstellar_west.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/interstellar_west.jpg -------------------------------------------------------------------------------- /img/miramar_down.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/miramar_down.jpg -------------------------------------------------------------------------------- /img/miramar_east.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/miramar_east.jpg -------------------------------------------------------------------------------- /img/miramar_north.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/miramar_north.jpg -------------------------------------------------------------------------------- /img/miramar_south.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/miramar_south.jpg -------------------------------------------------------------------------------- /img/miramar_up.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/miramar_up.jpg -------------------------------------------------------------------------------- /img/miramar_west.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/miramar_west.jpg -------------------------------------------------------------------------------- /img/mountains.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/mountains.png -------------------------------------------------------------------------------- /img/sky_down.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/sky_down.jpg -------------------------------------------------------------------------------- /img/sky_east.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/sky_east.jpg -------------------------------------------------------------------------------- /img/sky_north.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/sky_north.jpg -------------------------------------------------------------------------------- /img/sky_south.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/sky_south.jpg -------------------------------------------------------------------------------- /img/sky_up.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/sky_up.jpg -------------------------------------------------------------------------------- /img/sky_west.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/sky_west.jpg -------------------------------------------------------------------------------- /img/sunset_down.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/sunset_down.jpg -------------------------------------------------------------------------------- /img/sunset_east.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/sunset_east.jpg -------------------------------------------------------------------------------- /img/sunset_north.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/sunset_north.jpg -------------------------------------------------------------------------------- /img/sunset_south.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/sunset_south.jpg -------------------------------------------------------------------------------- /img/sunset_up.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/sunset_up.jpg -------------------------------------------------------------------------------- /img/sunset_west.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/sunset_west.jpg -------------------------------------------------------------------------------- /img/violent_days_down.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/violent_days_down.jpg -------------------------------------------------------------------------------- /img/violent_days_east.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/violent_days_east.jpg -------------------------------------------------------------------------------- /img/violent_days_north.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/violent_days_north.jpg -------------------------------------------------------------------------------- /img/violent_days_south.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/violent_days_south.jpg -------------------------------------------------------------------------------- /img/violent_days_up.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/violent_days_up.jpg -------------------------------------------------------------------------------- /img/violent_days_west.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/violent_days_west.jpg -------------------------------------------------------------------------------- /img/water-drop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/img/water-drop.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | A trip under the moonlight 7 | 55 | 56 | 57 | 58 | 59 |
60 | 69 |
70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 164 | 165 | -------------------------------------------------------------------------------- /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 | * @author erich666 / http://erichaines.com 7 | */ 8 | /*global THREE, console */ 9 | 10 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains 11 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is 12 | // supported. 13 | // 14 | // Orbit - left mouse / touch: one finger move 15 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish 16 | // Pan - right mouse, or arrow keys / touch: three finter swipe 17 | 18 | THREE.OrbitControls = function ( object, domElement ) { 19 | 20 | this.object = object; 21 | this.domElement = ( domElement !== undefined ) ? domElement : document; 22 | 23 | // API 24 | 25 | // Set to false to disable this control 26 | this.enabled = true; 27 | 28 | // "target" sets the location of focus, where the control orbits around 29 | // and where it pans with respect to. 30 | this.target = new THREE.Vector3(); 31 | 32 | // center is old, deprecated; use "target" instead 33 | this.center = this.target; 34 | 35 | // This option actually enables dollying in and out; left as "zoom" for 36 | // backwards compatibility 37 | this.noZoom = false; 38 | this.zoomSpeed = 1.0; 39 | 40 | // Limits to how far you can dolly in and out ( PerspectiveCamera only ) 41 | this.minDistance = 0; 42 | this.maxDistance = Infinity; 43 | 44 | // Limits to how far you can zoom in and out ( OrthographicCamera only ) 45 | this.minZoom = 0; 46 | this.maxZoom = Infinity; 47 | 48 | // Set to true to disable this control 49 | this.noRotate = false; 50 | this.rotateSpeed = 1.0; 51 | 52 | // Set to true to disable this control 53 | this.noPan = false; 54 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push 55 | 56 | // Set to true to automatically rotate around the target 57 | this.autoRotate = false; 58 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60 59 | 60 | // How far you can orbit vertically, upper and lower limits. 61 | // Range is 0 to Math.PI radians. 62 | this.minPolarAngle = 0; // radians 63 | this.maxPolarAngle = Math.PI; // radians 64 | 65 | // How far you can orbit horizontally, upper and lower limits. 66 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ]. 67 | this.minAzimuthAngle = - Infinity; // radians 68 | this.maxAzimuthAngle = Infinity; // radians 69 | 70 | // Set to true to disable use of the keys 71 | this.noKeys = false; 72 | 73 | // The four arrow keys 74 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 }; 75 | 76 | // Mouse buttons 77 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT }; 78 | 79 | //////////// 80 | // internals 81 | 82 | var scope = this; 83 | 84 | var EPS = 0.000001; 85 | 86 | var rotateStart = new THREE.Vector2(); 87 | var rotateEnd = new THREE.Vector2(); 88 | var rotateDelta = new THREE.Vector2(); 89 | 90 | var panStart = new THREE.Vector2(); 91 | var panEnd = new THREE.Vector2(); 92 | var panDelta = new THREE.Vector2(); 93 | var panOffset = new THREE.Vector3(); 94 | 95 | var offset = new THREE.Vector3(); 96 | 97 | var dollyStart = new THREE.Vector2(); 98 | var dollyEnd = new THREE.Vector2(); 99 | var dollyDelta = new THREE.Vector2(); 100 | 101 | var theta; 102 | var phi; 103 | var phiDelta = 0; 104 | var thetaDelta = 0; 105 | var scale = 1; 106 | var pan = new THREE.Vector3(); 107 | 108 | var lastPosition = new THREE.Vector3(); 109 | var lastQuaternion = new THREE.Quaternion(); 110 | 111 | var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 }; 112 | 113 | var state = STATE.NONE; 114 | 115 | // for reset 116 | 117 | this.target0 = this.target.clone(); 118 | this.position0 = this.object.position.clone(); 119 | this.zoom0 = this.object.zoom; 120 | 121 | // so camera.up is the orbit axis 122 | 123 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) ); 124 | var quatInverse = quat.clone().inverse(); 125 | 126 | // events 127 | 128 | var changeEvent = { type: 'change' }; 129 | var startEvent = { type: 'start' }; 130 | var endEvent = { type: 'end' }; 131 | 132 | this.rotateLeft = function ( angle ) { 133 | 134 | if ( angle === undefined ) { 135 | 136 | angle = getAutoRotationAngle(); 137 | 138 | } 139 | 140 | thetaDelta -= angle; 141 | 142 | }; 143 | 144 | this.rotateUp = function ( angle ) { 145 | 146 | if ( angle === undefined ) { 147 | 148 | angle = getAutoRotationAngle(); 149 | 150 | } 151 | 152 | phiDelta -= angle; 153 | 154 | }; 155 | 156 | // pass in distance in world space to move left 157 | this.panLeft = function ( distance ) { 158 | 159 | var te = this.object.matrix.elements; 160 | 161 | // get X column of matrix 162 | panOffset.set( te[ 0 ], te[ 1 ], te[ 2 ] ); 163 | panOffset.multiplyScalar( - distance ); 164 | 165 | pan.add( panOffset ); 166 | 167 | }; 168 | 169 | // pass in distance in world space to move up 170 | this.panUp = function ( distance ) { 171 | 172 | var te = this.object.matrix.elements; 173 | 174 | // get Y column of matrix 175 | panOffset.set( te[ 4 ], te[ 5 ], te[ 6 ] ); 176 | panOffset.multiplyScalar( distance ); 177 | 178 | pan.add( panOffset ); 179 | 180 | }; 181 | 182 | // pass in x,y of change desired in pixel space, 183 | // right and down are positive 184 | this.pan = function ( deltaX, deltaY ) { 185 | 186 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 187 | 188 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 189 | 190 | // perspective 191 | var position = scope.object.position; 192 | var offset = position.clone().sub( scope.target ); 193 | var targetDistance = offset.length(); 194 | 195 | // half of the fov is center to top of screen 196 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 ); 197 | 198 | // we actually don't use screenWidth, since perspective camera is fixed to screen height 199 | scope.panLeft( 2 * deltaX * targetDistance / element.clientHeight ); 200 | scope.panUp( 2 * deltaY * targetDistance / element.clientHeight ); 201 | 202 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 203 | 204 | // orthographic 205 | scope.panLeft( deltaX * (scope.object.right - scope.object.left) / element.clientWidth ); 206 | scope.panUp( deltaY * (scope.object.top - scope.object.bottom) / element.clientHeight ); 207 | 208 | } else { 209 | 210 | // camera neither orthographic or perspective 211 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' ); 212 | 213 | } 214 | 215 | }; 216 | 217 | this.dollyIn = function ( dollyScale ) { 218 | 219 | if ( dollyScale === undefined ) { 220 | 221 | dollyScale = getZoomScale(); 222 | 223 | } 224 | 225 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 226 | 227 | scale /= dollyScale; 228 | 229 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 230 | 231 | scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom * dollyScale ) ); 232 | scope.object.updateProjectionMatrix(); 233 | scope.dispatchEvent( changeEvent ); 234 | 235 | } else { 236 | 237 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 238 | 239 | } 240 | 241 | }; 242 | 243 | this.dollyOut = function ( dollyScale ) { 244 | 245 | if ( dollyScale === undefined ) { 246 | 247 | dollyScale = getZoomScale(); 248 | 249 | } 250 | 251 | if ( scope.object instanceof THREE.PerspectiveCamera ) { 252 | 253 | scale *= dollyScale; 254 | 255 | } else if ( scope.object instanceof THREE.OrthographicCamera ) { 256 | 257 | scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / dollyScale ) ); 258 | scope.object.updateProjectionMatrix(); 259 | scope.dispatchEvent( changeEvent ); 260 | 261 | } else { 262 | 263 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' ); 264 | 265 | } 266 | 267 | }; 268 | 269 | this.update = function () { 270 | 271 | var position = this.object.position; 272 | 273 | offset.copy( position ).sub( this.target ); 274 | 275 | // rotate offset to "y-axis-is-up" space 276 | offset.applyQuaternion( quat ); 277 | 278 | // angle from z-axis around y-axis 279 | 280 | theta = Math.atan2( offset.x, offset.z ); 281 | 282 | // angle from y-axis 283 | 284 | phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y ); 285 | 286 | if ( this.autoRotate && state === STATE.NONE ) { 287 | 288 | this.rotateLeft( getAutoRotationAngle() ); 289 | 290 | } 291 | 292 | theta += thetaDelta; 293 | phi += phiDelta; 294 | 295 | // restrict theta to be between desired limits 296 | theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) ); 297 | 298 | // restrict phi to be between desired limits 299 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) ); 300 | 301 | // restrict phi to be betwee EPS and PI-EPS 302 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) ); 303 | 304 | var radius = offset.length() * scale; 305 | 306 | // restrict radius to be between desired limits 307 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) ); 308 | 309 | // move target to panned location 310 | this.target.add( pan ); 311 | 312 | offset.x = radius * Math.sin( phi ) * Math.sin( theta ); 313 | offset.y = radius * Math.cos( phi ); 314 | offset.z = radius * Math.sin( phi ) * Math.cos( theta ); 315 | 316 | // rotate offset back to "camera-up-vector-is-up" space 317 | offset.applyQuaternion( quatInverse ); 318 | 319 | position.copy( this.target ).add( offset ); 320 | 321 | this.object.lookAt( this.target ); 322 | 323 | thetaDelta = 0; 324 | phiDelta = 0; 325 | scale = 1; 326 | pan.set( 0, 0, 0 ); 327 | 328 | // update condition is: 329 | // min(camera displacement, camera rotation in radians)^2 > EPS 330 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8 331 | 332 | if ( lastPosition.distanceToSquared( this.object.position ) > EPS 333 | || 8 * (1 - lastQuaternion.dot(this.object.quaternion)) > EPS ) { 334 | 335 | this.dispatchEvent( changeEvent ); 336 | 337 | lastPosition.copy( this.object.position ); 338 | lastQuaternion.copy (this.object.quaternion ); 339 | 340 | } 341 | 342 | }; 343 | 344 | 345 | this.reset = function () { 346 | 347 | state = STATE.NONE; 348 | 349 | this.target.copy( this.target0 ); 350 | this.object.position.copy( this.position0 ); 351 | this.object.zoom = this.zoom0; 352 | 353 | this.object.updateProjectionMatrix(); 354 | this.dispatchEvent( changeEvent ); 355 | 356 | this.update(); 357 | 358 | }; 359 | 360 | this.getPolarAngle = function () { 361 | 362 | return phi; 363 | 364 | }; 365 | 366 | this.getAzimuthalAngle = function () { 367 | 368 | return theta 369 | 370 | }; 371 | 372 | function getAutoRotationAngle() { 373 | 374 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed; 375 | 376 | } 377 | 378 | function getZoomScale() { 379 | 380 | return Math.pow( 0.95, scope.zoomSpeed ); 381 | 382 | } 383 | 384 | function onMouseDown( event ) { 385 | 386 | if ( scope.enabled === false ) return; 387 | event.preventDefault(); 388 | 389 | if ( event.button === scope.mouseButtons.ORBIT ) { 390 | if ( scope.noRotate === true ) return; 391 | 392 | state = STATE.ROTATE; 393 | 394 | rotateStart.set( event.clientX, event.clientY ); 395 | 396 | } else if ( event.button === scope.mouseButtons.ZOOM ) { 397 | if ( scope.noZoom === true ) return; 398 | 399 | state = STATE.DOLLY; 400 | 401 | dollyStart.set( event.clientX, event.clientY ); 402 | 403 | } else if ( event.button === scope.mouseButtons.PAN ) { 404 | if ( scope.noPan === true ) return; 405 | 406 | state = STATE.PAN; 407 | 408 | panStart.set( event.clientX, event.clientY ); 409 | 410 | } 411 | 412 | if ( state !== STATE.NONE ) { 413 | document.addEventListener( 'mousemove', onMouseMove, false ); 414 | document.addEventListener( 'mouseup', onMouseUp, false ); 415 | scope.dispatchEvent( startEvent ); 416 | } 417 | 418 | } 419 | 420 | function onMouseMove( event ) { 421 | 422 | if ( scope.enabled === false ) return; 423 | 424 | event.preventDefault(); 425 | 426 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 427 | 428 | if ( state === STATE.ROTATE ) { 429 | 430 | if ( scope.noRotate === true ) return; 431 | 432 | rotateEnd.set( event.clientX, event.clientY ); 433 | rotateDelta.subVectors( rotateEnd, rotateStart ); 434 | 435 | // rotating across whole screen goes 360 degrees around 436 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 437 | 438 | // rotating up and down along whole screen attempts to go 360, but limited to 180 439 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 440 | 441 | rotateStart.copy( rotateEnd ); 442 | 443 | } else if ( state === STATE.DOLLY ) { 444 | 445 | if ( scope.noZoom === true ) return; 446 | 447 | dollyEnd.set( event.clientX, event.clientY ); 448 | dollyDelta.subVectors( dollyEnd, dollyStart ); 449 | 450 | if ( dollyDelta.y > 0 ) { 451 | 452 | scope.dollyIn(); 453 | 454 | } else if ( dollyDelta.y < 0 ) { 455 | 456 | scope.dollyOut(); 457 | 458 | } 459 | 460 | dollyStart.copy( dollyEnd ); 461 | 462 | } else if ( state === STATE.PAN ) { 463 | 464 | if ( scope.noPan === true ) return; 465 | 466 | panEnd.set( event.clientX, event.clientY ); 467 | panDelta.subVectors( panEnd, panStart ); 468 | 469 | scope.pan( panDelta.x, panDelta.y ); 470 | 471 | panStart.copy( panEnd ); 472 | 473 | } 474 | 475 | if ( state !== STATE.NONE ) scope.update(); 476 | 477 | } 478 | 479 | function onMouseUp( /* event */ ) { 480 | 481 | if ( scope.enabled === false ) return; 482 | 483 | document.removeEventListener( 'mousemove', onMouseMove, false ); 484 | document.removeEventListener( 'mouseup', onMouseUp, false ); 485 | scope.dispatchEvent( endEvent ); 486 | state = STATE.NONE; 487 | 488 | } 489 | 490 | function onMouseWheel( event ) { 491 | 492 | if ( scope.enabled === false || scope.noZoom === true || state !== STATE.NONE ) return; 493 | 494 | event.preventDefault(); 495 | event.stopPropagation(); 496 | 497 | var delta = 0; 498 | 499 | if ( event.wheelDelta !== undefined ) { // WebKit / Opera / Explorer 9 500 | 501 | delta = event.wheelDelta; 502 | 503 | } else if ( event.detail !== undefined ) { // Firefox 504 | 505 | delta = - event.detail; 506 | 507 | } 508 | 509 | if ( delta > 0 ) { 510 | 511 | scope.dollyOut(); 512 | 513 | } else if ( delta < 0 ) { 514 | 515 | scope.dollyIn(); 516 | 517 | } 518 | 519 | scope.update(); 520 | scope.dispatchEvent( startEvent ); 521 | scope.dispatchEvent( endEvent ); 522 | 523 | } 524 | 525 | function onKeyDown( event ) { 526 | 527 | if ( scope.enabled === false || scope.noKeys === true || scope.noPan === true ) return; 528 | 529 | switch ( event.keyCode ) { 530 | 531 | case scope.keys.UP: 532 | scope.pan( 0, scope.keyPanSpeed ); 533 | scope.update(); 534 | break; 535 | 536 | case scope.keys.BOTTOM: 537 | scope.pan( 0, - scope.keyPanSpeed ); 538 | scope.update(); 539 | break; 540 | 541 | case scope.keys.LEFT: 542 | scope.pan( scope.keyPanSpeed, 0 ); 543 | scope.update(); 544 | break; 545 | 546 | case scope.keys.RIGHT: 547 | scope.pan( - scope.keyPanSpeed, 0 ); 548 | scope.update(); 549 | break; 550 | 551 | } 552 | 553 | } 554 | 555 | function touchstart( event ) { 556 | 557 | if ( scope.enabled === false ) return; 558 | 559 | switch ( event.touches.length ) { 560 | 561 | case 1: // one-fingered touch: rotate 562 | 563 | if ( scope.noRotate === true ) return; 564 | 565 | state = STATE.TOUCH_ROTATE; 566 | 567 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 568 | break; 569 | 570 | case 2: // two-fingered touch: dolly 571 | 572 | if ( scope.noZoom === true ) return; 573 | 574 | state = STATE.TOUCH_DOLLY; 575 | 576 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 577 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 578 | var distance = Math.sqrt( dx * dx + dy * dy ); 579 | dollyStart.set( 0, distance ); 580 | break; 581 | 582 | case 3: // three-fingered touch: pan 583 | 584 | if ( scope.noPan === true ) return; 585 | 586 | state = STATE.TOUCH_PAN; 587 | 588 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 589 | break; 590 | 591 | default: 592 | 593 | state = STATE.NONE; 594 | 595 | } 596 | 597 | if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent ); 598 | 599 | } 600 | 601 | function touchmove( event ) { 602 | 603 | if ( scope.enabled === false ) return; 604 | 605 | event.preventDefault(); 606 | event.stopPropagation(); 607 | 608 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement; 609 | 610 | switch ( event.touches.length ) { 611 | 612 | case 1: // one-fingered touch: rotate 613 | 614 | if ( scope.noRotate === true ) return; 615 | if ( state !== STATE.TOUCH_ROTATE ) return; 616 | 617 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 618 | rotateDelta.subVectors( rotateEnd, rotateStart ); 619 | 620 | // rotating across whole screen goes 360 degrees around 621 | scope.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed ); 622 | // rotating up and down along whole screen attempts to go 360, but limited to 180 623 | scope.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed ); 624 | 625 | rotateStart.copy( rotateEnd ); 626 | 627 | scope.update(); 628 | break; 629 | 630 | case 2: // two-fingered touch: dolly 631 | 632 | if ( scope.noZoom === true ) return; 633 | if ( state !== STATE.TOUCH_DOLLY ) return; 634 | 635 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX; 636 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY; 637 | var distance = Math.sqrt( dx * dx + dy * dy ); 638 | 639 | dollyEnd.set( 0, distance ); 640 | dollyDelta.subVectors( dollyEnd, dollyStart ); 641 | 642 | if ( dollyDelta.y > 0 ) { 643 | 644 | scope.dollyOut(); 645 | 646 | } else if ( dollyDelta.y < 0 ) { 647 | 648 | scope.dollyIn(); 649 | 650 | } 651 | 652 | dollyStart.copy( dollyEnd ); 653 | 654 | scope.update(); 655 | break; 656 | 657 | case 3: // three-fingered touch: pan 658 | 659 | if ( scope.noPan === true ) return; 660 | if ( state !== STATE.TOUCH_PAN ) return; 661 | 662 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY ); 663 | panDelta.subVectors( panEnd, panStart ); 664 | 665 | scope.pan( panDelta.x, panDelta.y ); 666 | 667 | panStart.copy( panEnd ); 668 | 669 | scope.update(); 670 | break; 671 | 672 | default: 673 | 674 | state = STATE.NONE; 675 | 676 | } 677 | 678 | } 679 | 680 | function touchend( /* event */ ) { 681 | 682 | if ( scope.enabled === false ) return; 683 | 684 | scope.dispatchEvent( endEvent ); 685 | state = STATE.NONE; 686 | 687 | } 688 | 689 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false ); 690 | this.domElement.addEventListener( 'mousedown', onMouseDown, false ); 691 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false ); 692 | this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox 693 | 694 | this.domElement.addEventListener( 'touchstart', touchstart, false ); 695 | this.domElement.addEventListener( 'touchend', touchend, false ); 696 | this.domElement.addEventListener( 'touchmove', touchmove, false ); 697 | 698 | window.addEventListener( 'keydown', onKeyDown, false ); 699 | 700 | // force an update at start 701 | this.update(); 702 | 703 | }; 704 | 705 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype ); 706 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls; -------------------------------------------------------------------------------- /js/demo.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author jbouny / https://github.com/fft-ocean 3 | */ 4 | 5 | var DEMO = 6 | { 7 | ms_Renderer : null, 8 | ms_Camera : null, 9 | ms_Scene : null, 10 | ms_Controls : null, 11 | ms_Ocean : null, 12 | ms_Environment : "night", 13 | ms_Raining : false, 14 | 15 | ms_Commands : { 16 | states : { 17 | up : false, 18 | right : false, 19 | down : false, 20 | left : false 21 | }, 22 | movements : { 23 | speed : 0.0, 24 | angle : 0.0 25 | } 26 | }, 27 | 28 | Initialize : function () { 29 | 30 | this.ms_Renderer = new THREE.WebGLRenderer(); 31 | this.ms_Renderer.context.getExtension( 'OES_texture_float' ); 32 | this.ms_Renderer.context.getExtension( 'OES_texture_float_linear' ); 33 | this.ms_Renderer.setClearColor( 0x000000 ); 34 | 35 | document.body.appendChild( this.ms_Renderer.domElement ); 36 | 37 | this.ms_Scene = new THREE.Scene(); 38 | 39 | this.ms_GroupShip = new THREE.Object3D(); 40 | this.ms_BlackPearlShip = new THREE.Object3D(); 41 | this.ms_Scene.add( this.ms_GroupShip ); 42 | this.ms_GroupShip.add( this.ms_BlackPearlShip ); 43 | 44 | this.ms_Camera = new THREE.PerspectiveCamera( 55.0, WINDOW.ms_Width / WINDOW.ms_Height, 0.5, 1000000 ); 45 | this.ms_Camera.position.set( 0, 350, 800 ); 46 | this.ms_Camera.lookAt( new THREE.Vector3() ); 47 | this.ms_BlackPearlShip.add( this.ms_Camera ); 48 | 49 | // Initialize Orbit control 50 | this.ms_Controls = new THREE.OrbitControls( this.ms_Camera, this.ms_Renderer.domElement ); 51 | this.ms_Controls.userPan = false; 52 | this.ms_Controls.target.set( 0, 100.0, 0 ); 53 | this.ms_Controls.noKeys = true; 54 | this.ms_Controls.userPanSpeed = 0; 55 | this.ms_Controls.minDistance = 0; 56 | this.ms_Controls.maxDistance = 20000.0; 57 | this.ms_Controls.minPolarAngle = 0; 58 | this.ms_Controls.maxPolarAngle = Math.PI * 0.75; 59 | 60 | this.InitializeSound(); 61 | this.InitializeLoader(); 62 | this.InitializeScene(); 63 | 64 | this.InitGui(); 65 | this.InitCommands(); 66 | 67 | }, 68 | 69 | InitializeSound : function InitializeSound() { 70 | 71 | var initSound = function initSound( url ) { 72 | 73 | if ( window.HTMLAudioElement ) { 74 | 75 | var sound = new Audio(''); 76 | 77 | if ( sound.canPlayType( 'audio/mp3' ) ) { 78 | 79 | var sound = new Audio( url ); 80 | 81 | sound.addEventListener( 'ended', function() { 82 | this.currentTime = 0; 83 | this.play(); 84 | }, false ); 85 | 86 | return sound; 87 | 88 | } 89 | 90 | } 91 | 92 | }; 93 | 94 | this.ms_soundWaves = initSound( 'sound/waves.mp3' ); 95 | this.ms_soundRain = initSound( 'sound/rain.mp3' ); 96 | 97 | this.ms_soundWaves.play(); 98 | 99 | }, 100 | 101 | InitializeLoader : function InitializeLoader() { 102 | 103 | this.ms_Loader = new THREE.LoadingManager(); 104 | 105 | var log = function( message, type, timeout ) { 106 | console.log( message ); 107 | messg( message, type, timeout ); 108 | } 109 | 110 | var delay = 1500; 111 | this.ms_Loader.onProgress = function( item, loaded, total ) { 112 | log( 'Loaded ' + loaded + '/' + total + ':' + item, 'info', delay ); 113 | }; 114 | this.ms_Loader.onLoad = function () { 115 | log( 'Loaded.', 'success', delay ); 116 | }; 117 | this.ms_Loader.onError = function () { 118 | log( 'Loading error.', 'error', delay ); 119 | }; 120 | 121 | 122 | this.ms_ImageLoader = new THREE.ImageLoader( this.ms_Loader ); 123 | 124 | }, 125 | 126 | InitializeScene : function InitializeScene() { 127 | 128 | // Add light 129 | this.ms_MainDirectionalLight = new THREE.DirectionalLight( 0xffffff, 1.5 ); 130 | this.ms_MainDirectionalLight.position.set( -0.2, 0.5, 1 ); 131 | this.ms_Scene.add( this.ms_MainDirectionalLight ); 132 | 133 | // Add Black Pearl 134 | var loader = new THREE.OBJMTLLoader( this.ms_Loader ); 135 | this.ms_BlackPearl = null; 136 | loader.load( 'models/BlackPearl/BlackPearl.obj', 'models/BlackPearl/BlackPearl.mtl', function ( object ) { 137 | object.position.y = 20.0; 138 | if( object.children ) { 139 | for( child in object.children ) { 140 | object.children[child].material.side = THREE.DoubleSide; 141 | } 142 | } 143 | 144 | DEMO.ms_BlackPearlShip.add( object ); 145 | DEMO.ms_BlackPearl = object; 146 | } ); 147 | 148 | // Add rain 149 | { 150 | var size = 128; 151 | var rainTexture = new THREE.Texture(); 152 | rainTexture.generateMipmaps = false; 153 | rainTexture.magFilter = THREE.LinearFilter; 154 | rainTexture.minFilter = THREE.LinearFilter; 155 | this.ms_ImageLoader.load( 'img/water-drop.png', function ( image ) { 156 | rainTexture.image = image; 157 | rainTexture.needsUpdate = true; 158 | } ); 159 | 160 | var rainShader = THREE.ShaderLib['rain']; 161 | 162 | var rainMaterial = new THREE.ShaderMaterial({ 163 | fragmentShader: rainShader.fragmentShader, 164 | vertexShader: rainShader.vertexShader, 165 | uniforms: rainShader.uniforms, 166 | transparent: true, 167 | depthWrite: false 168 | }); 169 | rainMaterial.uniforms.texture.value = rainTexture; 170 | 171 | this.ms_RainGeometry = new THREE.Geometry(); 172 | for ( i = 0; i < 100; i++ ) 173 | { 174 | var vertex = new THREE.Vector3(); 175 | vertex.x = Math.random() * 2.0 * size - size; 176 | vertex.y = Math.random() * 2.0 * size - size; 177 | vertex.z = Math.random() * size - size * 0.5; 178 | this.ms_RainGeometry.vertices.push( vertex ); 179 | } 180 | this.ms_Rain = new THREE.Points( this.ms_RainGeometry, rainMaterial ); 181 | this.ms_Camera.add( this.ms_Rain ); 182 | this.ms_Rain.position.setZ( - size * 0.75 ) ; 183 | } 184 | 185 | // Initialize Clouds 186 | this.ms_CloudShader = new CloudShader( this.ms_Renderer, 512 ); 187 | this.ms_CloudShader.cloudMesh.scale.multiplyScalar( 4.0 ); 188 | this.ms_Scene.add( this.ms_CloudShader.cloudMesh ); 189 | 190 | // Initialize Ocean 191 | var gsize = 512; 192 | var res = 512; 193 | var gres = 256; 194 | var origx = -gsize / 2; 195 | var origz = -gsize / 2; 196 | this.ms_Ocean = new THREE.Ocean( this.ms_Renderer, this.ms_Camera, this.ms_Scene, 197 | { 198 | INITIAL_SIZE : 200.0, 199 | INITIAL_WIND : [ 10.0, 10.0 ], 200 | INITIAL_CHOPPINESS : 3.6, 201 | CLEAR_COLOR : [ 1.0, 1.0, 1.0, 0.0 ], 202 | SUN_DIRECTION : this.ms_MainDirectionalLight.position.clone(), 203 | OCEAN_COLOR: new THREE.Vector3( 0.35, 0.4, 0.45 ), 204 | SKY_COLOR: new THREE.Vector3( 10.0, 13.0, 15.0 ), 205 | EXPOSURE : 0.15, 206 | GEOMETRY_RESOLUTION: gres, 207 | GEOMETRY_SIZE : gsize, 208 | RESOLUTION : res 209 | } ); 210 | 211 | this.LoadSkyBox(); 212 | this.LoadMountains(); 213 | }, 214 | 215 | InitGui : function InitGui() { 216 | 217 | // Initialize UI 218 | var gui = new dat.GUI(); 219 | dat.GUI.toggleHide(); 220 | 221 | gui.add( this.ms_Ocean, "size", 10, 2000 ).onChange( function( v ) { 222 | this.object.size = v; 223 | this.object.changed = true; 224 | } ); 225 | gui.add( this.ms_Ocean.materialSpectrum.uniforms.u_choppiness, "value", 0.1, 8 ).name( "choppiness" ); 226 | gui.add( this.ms_Ocean, "windX", -50, 50 ).onChange( function ( v ) { 227 | this.object.windX = v; 228 | this.object.changed = true; 229 | } ); 230 | gui.add( this.ms_Ocean, "windY", -50, 50 ).onChange( function ( v ) { 231 | this.object.windY = v; 232 | this.object.changed = true; 233 | } ); 234 | gui.add( this.ms_Ocean, "exposure", 0.0, 0.5 ).onChange( function ( v ) { 235 | this.object.exposure = v; 236 | this.object.changed = true; 237 | } ); 238 | gui.add( DEMO.ms_Ocean.materialOcean, "wireframe" ); 239 | 240 | var demo = this; 241 | 242 | $( '#env-selector > ul > li[key="' + this.ms_Environment + '"]' ).addClass( 'selected' ); 243 | $( '#env-selector > ul > li' ).click( function() { 244 | demo.UpdateEnvironment( $( this ).attr('key') ); 245 | 246 | $( '#env-selector > ul > li' ).removeClass( 'selected' ); 247 | $( this ).addClass( 'selected' ); 248 | } ).each( function() { 249 | $( this ).html( '' + $( this ).html() + '' ); 250 | } ) ; 251 | 252 | }, 253 | 254 | InitCommands : function InitCommands() { 255 | 256 | var LEFT = 37, 257 | UP = 38, 258 | RIGHT = 39, 259 | DOWN = 40; 260 | 261 | var keyHandler = function keyHandler( action ) { 262 | return function( event ) { 263 | var key = event.which; 264 | if( key >= LEFT && key <= DOWN ) { 265 | switch( key ) { 266 | case UP : DEMO.ms_Commands.states.up = action ; break ; 267 | case RIGHT : DEMO.ms_Commands.states.right = action ; break ; 268 | case DOWN : DEMO.ms_Commands.states.down = action ; break ; 269 | case LEFT : DEMO.ms_Commands.states.left = action ; break ; 270 | } 271 | } 272 | } 273 | } 274 | 275 | $( document ).keydown( keyHandler( true ) ); 276 | $( document ).keyup( keyHandler( false ) ); 277 | 278 | }, 279 | 280 | LoadMountains : function LoadSkyBox() { 281 | 282 | var demo = this; 283 | 284 | var mountainTexture = new THREE.Texture(); 285 | mountainTexture.generateMipmaps = false; 286 | mountainTexture.magFilter = THREE.LinearFilter; 287 | mountainTexture.minFilter = THREE.LinearFilter; 288 | this.ms_ImageLoader.load( 'img/mountains.png', function ( image ) { 289 | mountainTexture.image = image; 290 | mountainTexture.needsUpdate = true; 291 | } ); 292 | 293 | 294 | var mountainsMaterial = new THREE.MeshBasicMaterial( { 295 | map: mountainTexture, 296 | transparent: true, 297 | side: THREE.BackSide, 298 | depthWrite: false 299 | } ); 300 | 301 | var addMountain = function addMountain( size ) { 302 | 303 | var moutains = new THREE.Mesh( 304 | new THREE.CylinderGeometry( size, size, 35000, 32, 1, true ), 305 | mountainsMaterial 306 | ); 307 | moutains.position.y = 10000; 308 | demo.ms_Scene.add( moutains ); 309 | 310 | } ; 311 | 312 | // Add twice with different size in order to avoid some artifacts on the reflection 313 | addMountain( 120000 ); 314 | addMountain( 150000 ); 315 | 316 | // Add a black cylinder to hide the skybox under the water 317 | var cylinder = new THREE.Mesh( 318 | new THREE.CylinderGeometry( 150000, 150000, 150000, 32, 1, true ), 319 | new THREE.MeshBasicMaterial( { color: new THREE.Color( 1, 1, 1 ), side: THREE.BackSide } ) 320 | ); 321 | cylinder.position.y = -80000; 322 | demo.ms_Scene.add( cylinder ); 323 | 324 | }, 325 | 326 | LoadSkyBox : function LoadSkyBox() { 327 | 328 | var cubeShader = THREE.ShaderLib['cube']; 329 | 330 | var skyBoxMaterial = new THREE.ShaderMaterial( { 331 | fragmentShader: cubeShader.fragmentShader, 332 | vertexShader: cubeShader.vertexShader, 333 | uniforms: cubeShader.uniforms, 334 | side: THREE.BackSide 335 | } ); 336 | 337 | this.ms_SkyBox = new THREE.Mesh( 338 | new THREE.BoxGeometry( 450000, 450000, 450000 ), 339 | skyBoxMaterial 340 | ); 341 | 342 | this.ms_Scene.add( this.ms_SkyBox ); 343 | 344 | // https://stackoverflow.com/questions/3552944/how-to-get-the-anchor-from-the-url-using-jquery 345 | var url = window.location.href, idx = url.indexOf("#"); 346 | var anchor = idx != -1 ? url.substring(idx+1) : null; 347 | var environmentParameter = anchor; 348 | 349 | if( environmentParameter !== null ) { 350 | this.ms_Environment = environmentParameter; 351 | } 352 | 353 | this.UpdateEnvironment( this.ms_Environment ); 354 | 355 | }, 356 | 357 | UpdateEnvironment : function UpdateEnvironment( key ) { 358 | 359 | var textureName = ''; 360 | var textureExt = ".jpg"; 361 | var directionalLightPosition = null; 362 | var directionalLightColor = null; 363 | var raining = false; 364 | 365 | switch( key ) { 366 | case 'night': 367 | textureName = 'grimmnight'; 368 | directionalLightPosition = new THREE.Vector3( -0.3, 0.3, 1 ); 369 | directionalLightColor = new THREE.Color( 1, 1, 1 ); 370 | raining = true; 371 | break; 372 | case 'morning': 373 | textureName = 'clouds'; 374 | directionalLightPosition = new THREE.Vector3( -1, 0.5, 0.8 ); 375 | directionalLightColor = new THREE.Color( 1, 0.95, 0.8 ); 376 | break; 377 | case 'day': 378 | textureName = 'sky'; 379 | directionalLightPosition = new THREE.Vector3( -0.5, 0.5, -0.6 ); 380 | directionalLightColor = new THREE.Color( 1, 0.95, 0.9 ); 381 | break; 382 | case 'cloudy': 383 | textureName = 'miramar'; 384 | directionalLightPosition = new THREE.Vector3( 0.3, 1.0, 0.5 ); 385 | directionalLightColor = new THREE.Color( 0.9, 0.95, 1 ); 386 | raining = true; 387 | break; 388 | case 'sunset': 389 | textureName = 'sunset'; 390 | directionalLightPosition = new THREE.Vector3( -0.7, 0.2, -1 ); 391 | directionalLightColor = new THREE.Color( 1, 0.8, 0.5 ); 392 | break; 393 | case 'interstellar': 394 | textureName = 'interstellar'; 395 | directionalLightPosition = new THREE.Vector3( -0.7, 1.0, -0.4 ); 396 | directionalLightColor = new THREE.Color( 0.8, 1.0, 0.95 ); 397 | break; 398 | case 'apocalypse': 399 | textureName = 'violent_days'; 400 | directionalLightPosition = new THREE.Vector3( 1, 0.3, 1 ); 401 | directionalLightColor = new THREE.Color( 1, 0.85, 0.3 ); 402 | break; 403 | default: 404 | return; 405 | }; 406 | 407 | this.ms_Environment = key; 408 | this.ms_Raining = raining; 409 | this.ms_MainDirectionalLight.position.copy( directionalLightPosition ); 410 | this.ms_MainDirectionalLight.color.copy( directionalLightColor ); 411 | this.ms_Ocean.materialOcean.uniforms.u_sunDirection.value.copy( this.ms_MainDirectionalLight.position ); 412 | if ( raining ) { 413 | this.ms_soundRain.play(); 414 | } 415 | else { 416 | this.ms_soundRain.pause(); 417 | } 418 | 419 | var sources = [ 420 | 'img/' + textureName + '_west' + textureExt, 421 | 'img/' + textureName + '_east' + textureExt, 422 | 'img/' + textureName + '_up' + textureExt, 423 | 'img/' + textureName + '_down' + textureExt, 424 | 'img/' + textureName + '_south' + textureExt, 425 | 'img/' + textureName + '_north' + textureExt 426 | ]; 427 | var images = []; 428 | 429 | var cubeMap = new THREE.CubeTexture( images ); 430 | cubeMap.flipY = false; 431 | 432 | var imageLoader = this.ms_ImageLoader; 433 | var loaded = 0; 434 | var loadTexture = function ( i ) { 435 | imageLoader.load( sources[ i ], function ( image ) { 436 | cubeMap.images[ i ] = image; 437 | loaded ++; 438 | if ( loaded === 6 ) { 439 | cubeMap.needsUpdate = true; 440 | } 441 | } ); 442 | 443 | } 444 | 445 | for ( var i = 0, il = sources.length; i < il; ++ i ) { 446 | loadTexture( i ); 447 | } 448 | 449 | cubeMap.format = THREE.RGBFormat; 450 | cubeMap.generateMipmaps = false; 451 | cubeMap.magFilter = THREE.LinearFilter; 452 | cubeMap.minFilter = THREE.LinearFilter; 453 | 454 | this.ms_SkyBox.material.uniforms['tCube'].value = cubeMap; 455 | }, 456 | 457 | Display : function () { 458 | 459 | this.ms_Renderer.render( this.ms_Scene, this.ms_Camera ); 460 | 461 | }, 462 | 463 | Update : function () { 464 | 465 | // Update camera position 466 | if( this.ms_Camera.position.y < 0.0 ) { 467 | this.ms_Camera.position.y = 2.0; 468 | } 469 | 470 | // Update black ship displacements 471 | this.UpdateCommands(); 472 | this.ms_GroupShip.rotation.y += this.ms_Commands.movements.angle; 473 | this.ms_BlackPearlShip.rotation.z = -this.ms_Commands.movements.angle * 10.0; 474 | this.ms_BlackPearlShip.rotation.x = this.ms_Commands.movements.speed * 0.1; 475 | var shipDisplacement = (new THREE.Vector3(0, 0, -1)).applyEuler(this.ms_GroupShip.rotation).multiplyScalar( 10.0 * this.ms_Commands.movements.speed ); 476 | this.ms_GroupShip.position.add( shipDisplacement ); 477 | 478 | var currentTime = new Date().getTime(); 479 | this.ms_Ocean.deltaTime = ( currentTime - lastTime ) / 1000 || 0.0; 480 | lastTime = currentTime; 481 | 482 | // Update black ship movements 483 | if( this.ms_BlackPearl !== null ) 484 | { 485 | var animationRatio = 1.0 + this.ms_Commands.movements.speed * 1.0; 486 | this.ms_BlackPearl.rotation.y = Math.cos( currentTime * 0.0008 ) * 0.05 - 0.025; 487 | this.ms_BlackPearl.rotation.x = Math.sin( currentTime * 0.001154 + 0.78 ) * 0.1 + 0.05; 488 | } 489 | 490 | // Update rain 491 | if( this.ms_Raining ) { 492 | var seed = 1; 493 | var fastRandom = function fastRandom() { 494 | // https://stackoverflow.com/questions/521295/javascript-random-seeds 495 | var x = Math.sin( seed++ ) * 10000; 496 | return x - Math.floor( x ); 497 | } 498 | for( i in this.ms_RainGeometry.vertices ) 499 | { 500 | var speed = 4.0; 501 | this.ms_RainGeometry.vertices[i].y -= fastRandom() * speed + speed; 502 | if( this.ms_RainGeometry.vertices[i].y < -50 ) 503 | this.ms_RainGeometry.vertices[i].y = 50; 504 | } 505 | this.ms_Rain.rotation.set( -this.ms_Camera.rotation.x, -this.ms_Camera.rotation.y, -this.ms_Camera.rotation.z, "ZYX" ); 506 | this.ms_RainGeometry.verticesNeedUpdate = true; 507 | } 508 | 509 | // Render ocean reflection 510 | this.ms_Camera.remove( this.ms_Rain ); 511 | this.ms_Ocean.render(); 512 | if( this.ms_Raining ) 513 | this.ms_Camera.add( this.ms_Rain ); 514 | 515 | // Updade clouds 516 | this.ms_CloudShader.update(); 517 | 518 | // Update ocean data 519 | this.ms_Ocean.update(); 520 | 521 | this.ms_Controls.update(); 522 | this.Display(); 523 | 524 | }, 525 | 526 | UpdateCommands : function UpdateCommands() { 527 | 528 | var states = this.ms_Commands.states; 529 | 530 | // Update speed 531 | var targetSpeed = 0.0; 532 | if( states.up ) { 533 | targetSpeed = 1.0; 534 | } 535 | else if( states.down ) { 536 | targetSpeed = -0.5; 537 | } 538 | var curSpeed = this.ms_Commands.movements.speed ; 539 | this.ms_Commands.movements.speed = curSpeed + ( targetSpeed - curSpeed ) * 0.02; 540 | 541 | // Update angle 542 | var targetAngle = 0.0; 543 | if( states.left ) { 544 | targetAngle = Math.PI * 0.005; 545 | } 546 | else if( states.right ) { 547 | targetAngle = -Math.PI * 0.005; 548 | } 549 | if( states.down ) { 550 | targetAngle *= -1.0; 551 | } 552 | 553 | var curAngle = this.ms_Commands.movements.angle ; 554 | this.ms_Commands.movements.angle = curAngle + ( targetAngle - curAngle ) * 0.02; 555 | 556 | }, 557 | 558 | Resize : function ( inWidth, inHeight ) { 559 | 560 | this.ms_Camera.aspect = inWidth / inHeight; 561 | this.ms_Camera.updateProjectionMatrix(); 562 | this.ms_Renderer.setSize( inWidth, inHeight ); 563 | this.Display(); 564 | 565 | } 566 | }; 567 | -------------------------------------------------------------------------------- /js/effects/MirrorRenderer.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | THREE.MirrorRenderer = function (renderer, camera, scene, options) { 4 | 5 | THREE.Object3D.call(this); 6 | this.name = 'mirror_' + this.id; 7 | 8 | function optionalParameter (value, defaultValue) { 9 | return value !== undefined ? value : defaultValue; 10 | }; 11 | 12 | options = options || {}; 13 | 14 | this.matrixNeedsUpdate = true; 15 | 16 | var width = optionalParameter(options.textureWidth, 512); 17 | var height = optionalParameter(options.textureHeight, 512); 18 | this.clipBias = optionalParameter(options.clipBias, 0.0); 19 | 20 | this.renderer = renderer; 21 | this.scene = scene; 22 | this.mirrorPlane = new THREE.Plane(); 23 | this.normal = new THREE.Vector3(0, 0, 1); 24 | this.cameraWorldPosition = new THREE.Vector3(); 25 | this.rotationMatrix = new THREE.Matrix4(); 26 | this.lookAtPosition = new THREE.Vector3(0, 0, -1); 27 | this.clipPlane = new THREE.Vector4(); 28 | 29 | if ( camera instanceof THREE.PerspectiveCamera ) { 30 | this.camera = camera; 31 | } 32 | else { 33 | this.camera = new THREE.PerspectiveCamera(); 34 | console.log(this.name + ': camera is not a Perspective Camera!') 35 | } 36 | 37 | this.textureMatrix = new THREE.Matrix4(); 38 | 39 | this.mirrorCamera = this.camera.clone(); 40 | 41 | this.mesh = new THREE.Object3D(); 42 | 43 | this.texture = new THREE.WebGLRenderTarget(width, height); 44 | this.tempTexture = new THREE.WebGLRenderTarget(width, height); 45 | 46 | if ( !THREE.Math.isPowerOfTwo(width) || !THREE.Math.isPowerOfTwo(height) ) { 47 | this.texture.generateMipmaps = false; 48 | this.tempTexture.generateMipmaps = false; 49 | } 50 | 51 | this.updateTextureMatrix(); 52 | this.render(); 53 | }; 54 | 55 | THREE.MirrorRenderer.prototype = Object.create(THREE.Object3D.prototype); 56 | 57 | THREE.MirrorRenderer.prototype.renderWithMirror = function (otherMirror) { 58 | 59 | // update the mirror matrix to mirror the current view 60 | this.updateTextureMatrix(); 61 | this.matrixNeedsUpdate = false; 62 | 63 | // set the camera of the other mirror so the mirrored view is the reference view 64 | var tempCamera = otherMirror.camera; 65 | otherMirror.camera = this.mirrorCamera; 66 | 67 | // render the other mirror in temp texture 68 | otherMirror.render(true); 69 | otherMirror.material.uniforms.mirrorSampler.value = otherMirror.tempTexture; 70 | 71 | // render the current mirror 72 | this.render(); 73 | this.matrixNeedsUpdate = true; 74 | 75 | // restore material and camera of other mirror 76 | otherMirror.material.uniforms.mirrorSampler.value = otherMirror.texture; 77 | otherMirror.camera = tempCamera; 78 | 79 | // restore texture matrix of other mirror 80 | otherMirror.updateTextureMatrix(); 81 | }; 82 | 83 | THREE.MirrorRenderer.prototype.updateTextureMatrix = function () { 84 | 85 | if ( this.parent != undefined ) { 86 | this.mesh = this.parent; 87 | } 88 | 89 | function sign(x) { return x ? x < 0 ? -1 : 1 : 0; } 90 | 91 | this.updateMatrixWorld(); 92 | this.camera.updateMatrixWorld(); 93 | 94 | this.cameraWorldPosition.setFromMatrixPosition(this.camera.matrixWorld); 95 | 96 | this.rotationMatrix.extractRotation(this.matrixWorld); 97 | 98 | this.normal = (new THREE.Vector3(0, 1, 0)).applyEuler(this.mesh.rotation); 99 | var cameraLookAt = (new THREE.Vector3(0, 0, 1)).applyEuler(this.camera.rotation); 100 | if ( this.normal.dot(cameraLookAt) < 0 ) { 101 | var meshNormal = (new THREE.Vector3(0, 0, 1)).applyEuler(this.mesh.rotation); 102 | this.normal.reflect(meshNormal); 103 | } 104 | 105 | var view = this.mesh.position.clone().sub(this.cameraWorldPosition); 106 | //view.y -= 0.1; 107 | view.reflect(this.normal).negate(); 108 | view.add(this.mesh.position); 109 | 110 | this.rotationMatrix.extractRotation(this.camera.matrixWorld); 111 | 112 | this.lookAtPosition.set(0, 0, -1); 113 | this.lookAtPosition.applyMatrix4(this.rotationMatrix); 114 | this.lookAtPosition.add(this.cameraWorldPosition); 115 | 116 | var target = this.mesh.position.clone().sub(this.lookAtPosition); 117 | target.reflect(this.normal).negate(); 118 | target.add(this.mesh.position); 119 | 120 | this.up.set(0, -1, 0); 121 | this.up.applyMatrix4(this.rotationMatrix); 122 | this.up.reflect(this.normal).negate(); 123 | 124 | this.mirrorCamera.position.copy(view); 125 | this.mirrorCamera.up = this.up; 126 | this.mirrorCamera.lookAt(target); 127 | this.mirrorCamera.aspect = this.camera.aspect; 128 | 129 | this.mirrorCamera.updateProjectionMatrix(); 130 | this.mirrorCamera.updateMatrixWorld(); 131 | this.mirrorCamera.matrixWorldInverse.getInverse(this.mirrorCamera.matrixWorld); 132 | 133 | // Update the texture matrix 134 | this.textureMatrix.set(0.5, 0.0, 0.0, 0.5, 135 | 0.0, 0.5, 0.0, 0.5, 136 | 0.0, 0.0, 0.5, 0.5, 137 | 0.0, 0.0, 0.0, 1.0); 138 | this.textureMatrix.multiply(this.mirrorCamera.projectionMatrix); 139 | this.textureMatrix.multiply(this.mirrorCamera.matrixWorldInverse); 140 | 141 | // Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html 142 | // Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf 143 | this.mirrorPlane.setFromNormalAndCoplanarPoint(this.normal, this.mesh.position); 144 | this.mirrorPlane.applyMatrix4(this.mirrorCamera.matrixWorldInverse); 145 | 146 | this.clipPlane.set(this.mirrorPlane.normal.x, this.mirrorPlane.normal.y, this.mirrorPlane.normal.z, this.mirrorPlane.constant); 147 | 148 | var q = new THREE.Vector4(); 149 | var projectionMatrix = this.mirrorCamera.projectionMatrix; 150 | 151 | q.x = (sign(this.clipPlane.x) + projectionMatrix.elements[8]) / projectionMatrix.elements[0]; 152 | q.y = (sign(this.clipPlane.y) + projectionMatrix.elements[9]) / projectionMatrix.elements[5]; 153 | q.z = -1.0; 154 | q.w = (1.0 + projectionMatrix.elements[10]) / projectionMatrix.elements[14]; 155 | 156 | // Calculate the scaled plane vector 157 | var c = new THREE.Vector4(); 158 | c = this.clipPlane.multiplyScalar(2.0 / this.clipPlane.dot(q)); 159 | 160 | // Replacing the third row of the projection matrix 161 | projectionMatrix.elements[2] = c.x; 162 | projectionMatrix.elements[6] = c.y; 163 | projectionMatrix.elements[10] = c.z + 1.0 - this.clipBias; 164 | projectionMatrix.elements[14] = c.w; 165 | 166 | var worldCoordinates = new THREE.Vector3(); 167 | worldCoordinates.setFromMatrixPosition(this.camera.matrixWorld); 168 | this.eye = worldCoordinates; 169 | }; 170 | 171 | THREE.MirrorRenderer.prototype.render = function (isTempTexture) { 172 | 173 | if ( this.matrixNeedsUpdate ) { 174 | this.updateTextureMatrix(); 175 | } 176 | 177 | this.matrixNeedsUpdate = true; 178 | 179 | // Render the mirrored view of the current scene into the target texture 180 | if ( this.scene !== undefined && this.scene instanceof THREE.Scene ) { 181 | var renderTexture = (isTempTexture !== undefined && isTempTexture)? this.tempTexture : this.texture; 182 | this.renderer.render(this.scene, this.mirrorCamera, renderTexture, true); 183 | } 184 | 185 | }; -------------------------------------------------------------------------------- /js/effects/Ocean.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author jbouny / https://github.com/fft-ocean 3 | * 4 | * Based on: 5 | * @author Aleksandr Albert / http://www.routter.co.tt 6 | */ 7 | 8 | THREE.Ocean = function (renderer, camera, scene, options) { 9 | 10 | // flag used to trigger parameter changes 11 | this.changed = true; 12 | this.initial = true; 13 | 14 | // Assign required parameters as object properties 15 | this.oceanCamera = new THREE.OrthographicCamera(); //camera.clone(); 16 | this.oceanCamera.position.z = 1; 17 | this.renderer = renderer; 18 | this.renderer.clearColor( 0xffffff ); 19 | 20 | this.scene = new THREE.Scene(); 21 | 22 | // Enable necessary extensions 23 | this.renderer.context.getExtension('OES_texture_float'); 24 | this.renderer.context.getExtension('OES_texture_float_linear'); 25 | 26 | // Create mirror rendering 27 | this.mirror = new THREE.MirrorRenderer( renderer, camera, scene ) ; 28 | this.mirror.position.y = -10.0; 29 | 30 | // Assign optional parameters as variables and object properties 31 | function optionalParameter(value, defaultValue) { 32 | return value !== undefined ? value : defaultValue; 33 | }; 34 | function optionalParameterArray(value, index, defaultValue) { 35 | return value !== undefined ? value[index] : defaultValue; 36 | }; 37 | options = options || {}; 38 | this.sunDirection = optionalParameter(options.SUN_DIRECTION, new THREE.Vector3(-1.0, 1.0, 1.0 )); 39 | this.oceanColor = optionalParameter(options.OCEAN_COLOR, new THREE.Vector3(0.004, 0.016, 0.047)); 40 | this.skyColor = optionalParameter(options.SKY_COLOR, new THREE.Vector3(3.2, 9.6, 12.8)); 41 | this.exposure = optionalParameter(options.EXPOSURE, 0.35); 42 | this.geometryResolution = optionalParameter(options.GEOMETRY_RESOLUTION, 32); 43 | this.geometrySize = optionalParameter(options.GEOMETRY_SIZE, 2000); 44 | this.resolution = optionalParameter(options.RESOLUTION, 64); 45 | this.floatSize = optionalParameter(options.SIZE_OF_FLOAT, 4); 46 | this.windX = optionalParameterArray(options.INITIAL_WIND, 0, 10.0), 47 | this.windY = optionalParameterArray(options.INITIAL_WIND, 1, 10.0), 48 | this.size = optionalParameter(options.INITIAL_SIZE, 250.0), 49 | this.choppiness = optionalParameter(options.INITIAL_CHOPPINESS, 1.5); 50 | 51 | this.matrixNeedsUpdate = false; 52 | 53 | // Setup framebuffer pipeline 54 | var BaseParams = { 55 | format: THREE.RGBAFormat, 56 | stencilBuffer: false, 57 | depthBuffer: false, 58 | premultiplyAlpha: false, 59 | type: THREE.FloatType 60 | }; 61 | var LinearClampParams = JSON.parse(JSON.stringify(BaseParams)); 62 | LinearClampParams.minFilter = LinearClampParams.magFilter = THREE.LinearFilter ; 63 | LinearClampParams.wrapS = LinearClampParams.wrapT = THREE.ClampToEdgeWrapping ; 64 | 65 | var NearestClampParams = JSON.parse(JSON.stringify(BaseParams)); 66 | NearestClampParams.minFilter = NearestClampParams.magFilter = THREE.NearestFilter ; 67 | NearestClampParams.wrapS = NearestClampParams.wrapT = THREE.ClampToEdgeWrapping ; 68 | 69 | var NearestRepeatParams = JSON.parse(JSON.stringify(BaseParams)); 70 | NearestRepeatParams.minFilter = NearestRepeatParams.magFilter = THREE.NearestFilter ; 71 | NearestRepeatParams.wrapS = NearestRepeatParams.wrapT = THREE.RepeatWrapping ; 72 | 73 | var LinearRepeatParams = JSON.parse(JSON.stringify(BaseParams)); 74 | LinearRepeatParams.minFilter = LinearRepeatParams.magFilter = THREE.LinearFilter ; 75 | LinearRepeatParams.wrapS = LinearRepeatParams.wrapT = THREE.RepeatWrapping ; 76 | 77 | this.initialSpectrumFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, NearestRepeatParams); 78 | this.spectrumFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, NearestClampParams); 79 | this.pingPhaseFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, NearestClampParams); 80 | this.pongPhaseFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, NearestClampParams); 81 | this.pingTransformFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, NearestClampParams); 82 | this.pongTransformFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, NearestClampParams); 83 | this.displacementMapFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, LinearRepeatParams); 84 | this.normalMapFramebuffer = new THREE.WebGLRenderTarget(this.resolution, this.resolution, LinearRepeatParams); 85 | 86 | // Define shaders and constant uniforms 87 | //////////////////////////////////////// 88 | 89 | // 0 - The vertex shader used in all of the simulation steps 90 | var fullscreeenVertexShader = THREE.ShaderLib["ocean_sim_vertex"]; 91 | 92 | // 1 - Horizontal wave vertices used for FFT 93 | var oceanHorizontalShader = THREE.ShaderLib["ocean_subtransform"]; 94 | var oceanHorizontalUniforms = THREE.UniformsUtils.clone(oceanHorizontalShader.uniforms); 95 | this.materialOceanHorizontal = new THREE.ShaderMaterial({ 96 | uniforms: oceanHorizontalUniforms, 97 | vertexShader: fullscreeenVertexShader.vertexShader, 98 | fragmentShader: "#define HORIZONTAL \n" + oceanHorizontalShader.fragmentShader 99 | }); 100 | this.materialOceanHorizontal.uniforms.u_transformSize = { type: "f", value: this.resolution }; 101 | this.materialOceanHorizontal.uniforms.u_subtransformSize = { type: "f", value: null }; 102 | this.materialOceanHorizontal.uniforms.u_input = { type: "t", value: null }; 103 | this.materialOceanHorizontal.depthTest = false; 104 | 105 | // 2 - Vertical wave vertices used for FFT 106 | var oceanVerticalShader = THREE.ShaderLib["ocean_subtransform"]; 107 | var oceanVerticalUniforms = THREE.UniformsUtils.clone(oceanVerticalShader.uniforms); 108 | this.materialOceanVertical = new THREE.ShaderMaterial({ 109 | uniforms: oceanVerticalUniforms, 110 | vertexShader: fullscreeenVertexShader.vertexShader, 111 | fragmentShader: oceanVerticalShader.fragmentShader 112 | }); 113 | this.materialOceanVertical.uniforms.u_transformSize = { type: "f", value: this.resolution }; 114 | this.materialOceanVertical.uniforms.u_subtransformSize = { type: "f", value: null }; 115 | this.materialOceanVertical.uniforms.u_input = { type: "t", value: null }; 116 | this.materialOceanVertical.depthTest = false; 117 | 118 | // 3 - Initial spectrum used to generate height map 119 | var initialSpectrumShader = THREE.ShaderLib["ocean_initial_spectrum"]; 120 | var initialSpectrumUniforms = THREE.UniformsUtils.clone(initialSpectrumShader.uniforms); 121 | this.materialInitialSpectrum = new THREE.ShaderMaterial({ 122 | uniforms: initialSpectrumUniforms, 123 | vertexShader: fullscreeenVertexShader.vertexShader, 124 | fragmentShader:initialSpectrumShader.fragmentShader 125 | }); 126 | this.materialInitialSpectrum.uniforms.u_wind = { type: "v2", value: new THREE.Vector2() }; 127 | this.materialInitialSpectrum.uniforms.u_resolution = { type: "f", value: this.resolution }; 128 | this.materialInitialSpectrum.depthTest = false; 129 | 130 | // 4 - Phases used to animate heightmap 131 | var phaseShader = THREE.ShaderLib["ocean_phase"]; 132 | var phaseUniforms = THREE.UniformsUtils.clone(phaseShader.uniforms); 133 | this.materialPhase = new THREE.ShaderMaterial({ 134 | uniforms: phaseUniforms, 135 | vertexShader: fullscreeenVertexShader.vertexShader, 136 | fragmentShader: phaseShader.fragmentShader 137 | }); 138 | this.materialPhase.uniforms.u_resolution = { type: "f", value: this.resolution }; 139 | this.materialPhase.depthTest = false; 140 | 141 | // 5 - Shader used to update spectrum 142 | var spectrumShader = THREE.ShaderLib["ocean_spectrum"]; 143 | var spectrumUniforms = THREE.UniformsUtils.clone(spectrumShader.uniforms); 144 | this.materialSpectrum = new THREE.ShaderMaterial({ 145 | uniforms: spectrumUniforms, 146 | vertexShader: fullscreeenVertexShader.vertexShader, 147 | fragmentShader: spectrumShader.fragmentShader 148 | }); 149 | this.materialSpectrum.uniforms.u_initialSpectrum = { type: "t", value: null }; 150 | this.materialSpectrum.uniforms.u_resolution = { type: "f", value: this.resolution }; 151 | this.materialSpectrum.uniforms.u_choppiness.value = this.choppiness ; 152 | this.materialSpectrum.depthTest = false; 153 | 154 | // 6 - Shader used to update spectrum normals 155 | var normalShader = THREE.ShaderLib["ocean_normals"]; 156 | var normalUniforms = THREE.UniformsUtils.clone(normalShader.uniforms); 157 | this.materialNormal = new THREE.ShaderMaterial({ 158 | uniforms: normalUniforms, 159 | vertexShader: fullscreeenVertexShader.vertexShader, 160 | fragmentShader: normalShader.fragmentShader 161 | }); 162 | this.materialNormal.uniforms.u_displacementMap = { type: "t", value: null }; 163 | this.materialNormal.uniforms.u_resolution = { type: "f", value: this.resolution }; 164 | this.materialNormal.depthTest = false; 165 | 166 | // 7 - Shader used to update normals 167 | var oceanShader = THREE.ShaderLib["ocean_main"]; 168 | var oceanUniforms = THREE.UniformsUtils.clone(oceanShader.uniforms); 169 | var vertexShaderOcean = oceanShader.vertexShader; 170 | { 171 | var gl = renderer.getContext(); 172 | if ( gl.getParameter(gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS) === 0 ) { 173 | vertexShaderOcean = oceanShader.vertexShaderNoTexLookup; 174 | } 175 | } 176 | this.materialOcean = new THREE.ShaderMaterial({ 177 | uniforms: oceanUniforms, 178 | vertexShader: vertexShaderOcean, 179 | fragmentShader: oceanShader.fragmentShader, 180 | side: THREE.FrontSide, 181 | wireframe: false 182 | }); 183 | //this.materialOcean.wireframe = true; 184 | this.materialOcean.uniforms.u_geometrySize = { type: "f", value: this.resolution }; 185 | this.materialOcean.uniforms.u_displacementMap = { type: "t", value: this.displacementMapFramebuffer }; 186 | this.materialOcean.uniforms.u_reflection = { type: "t", value: this.mirror.texture }; 187 | this.materialOcean.uniforms.u_mirrorMatrix = { type: "m4", value: this.mirror.textureMatrix }; 188 | this.materialOcean.uniforms.u_normalMap = { type: "t", value: this.normalMapFramebuffer }; 189 | this.materialOcean.uniforms.u_oceanColor = { type: "v3", value: this.oceanColor }; 190 | this.materialOcean.uniforms.u_skyColor = { type: "v3", value: this.skyColor }; 191 | this.materialOcean.uniforms.u_sunDirection = { type: "v3", value: this.sunDirection }; 192 | this.materialOcean.uniforms.u_exposure = { type: "f", value: this.exposure }; 193 | 194 | // Disable blending to prevent default premultiplied alpha values 195 | this.materialOceanHorizontal.blending = 0; 196 | this.materialOceanVertical.blending = 0; 197 | this.materialInitialSpectrum.blending = 0; 198 | this.materialPhase.blending = 0; 199 | this.materialSpectrum.blending = 0; 200 | this.materialNormal.blending = 0; 201 | this.materialOcean.blending = 0; 202 | 203 | // Create the simulation plane 204 | this.screenQuad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ) ); 205 | this.scene.add(this.screenQuad); 206 | 207 | // Initialise spectrum data 208 | this.generateSeedPhaseTexture(); 209 | 210 | // Generate the ocean mesh 211 | this.generateMesh(); 212 | this.mirror.mesh = this.oceanMesh; 213 | camera.add( this.oceanMesh ); 214 | 215 | }; 216 | 217 | THREE.Ocean.prototype.generateMesh = function () { 218 | 219 | var geometry = new THREE.PlaneBufferGeometry( 1, 1, this.geometryResolution, this.geometryResolution ); 220 | this.oceanMesh = new THREE.Mesh( geometry, this.materialOcean ); 221 | 222 | }; 223 | 224 | THREE.Ocean.prototype.update = function () { 225 | 226 | this.overrideMaterial = this.materialOcean; 227 | if ( this.changed ) { 228 | this.materialOcean.uniforms.u_size.value = this.size; 229 | this.materialOcean.uniforms.u_exposure.value = this.exposure; 230 | this.changed = false; 231 | } 232 | this.materialOcean.uniforms.u_normalMap.value = this.normalMapFramebuffer ; 233 | this.materialOcean.uniforms.u_displacementMap.value = this.displacementMapFramebuffer ; 234 | this.materialOcean.depthTest = true; 235 | 236 | }; 237 | 238 | THREE.Ocean.prototype.render = function () { 239 | 240 | this.scene.overrideMaterial = null; 241 | 242 | if (this.changed) 243 | this.renderInitialSpectrum(); 244 | 245 | this.mirror.render(); 246 | this.renderWavePhase(); 247 | this.renderSpectrum(); 248 | this.renderSpectrumFFT(); 249 | this.renderNormalMap(); 250 | this.scene.overrideMaterial = null; 251 | 252 | }; 253 | 254 | THREE.Ocean.prototype.generateSeedPhaseTexture = function() { 255 | 256 | // Setup the seed texture 257 | this.pingPhase = true; 258 | var phaseArray = new window.Float32Array(this.resolution * this.resolution * 4); 259 | for (var i = 0; i < this.resolution; i++) { 260 | for (var j = 0; j < this.resolution; j++) { 261 | phaseArray[i * this.resolution * 4 + j * 4] = Math.random() * 2.0 * Math.PI; 262 | phaseArray[i * this.resolution * 4 + j * 4 + 1] = 0.0; 263 | phaseArray[i * this.resolution * 4 + j * 4 + 2] = 0.0; 264 | phaseArray[i * this.resolution * 4 + j * 4 + 3] = 0.0; 265 | } 266 | } 267 | 268 | this.pingPhaseTexture = new THREE.DataTexture(phaseArray, this.resolution, this.resolution, THREE.RGBAFormat); 269 | this.pingPhaseTexture.minFilter = THREE.NearestFilter; 270 | this.pingPhaseTexture.magFilter = THREE.NearestFilter; 271 | this.pingPhaseTexture.wrapS = THREE.ClampToEdgeWrapping; 272 | this.pingPhaseTexture.wrapT = THREE.ClampToEdgeWrapping; 273 | this.pingPhaseTexture.type = THREE.FloatType; 274 | this.pingPhaseTexture.needsUpdate = true; 275 | 276 | }; 277 | 278 | THREE.Ocean.prototype.renderInitialSpectrum = function () { 279 | 280 | this.scene.overrideMaterial = this.materialInitialSpectrum; 281 | this.materialInitialSpectrum.uniforms.u_wind.value.set( this.windX, this.windY ); 282 | this.materialInitialSpectrum.uniforms.u_size.value = this.size; 283 | this.renderer.render(this.scene, this.oceanCamera, this.initialSpectrumFramebuffer, true); 284 | 285 | }; 286 | 287 | THREE.Ocean.prototype.renderWavePhase = function () { 288 | 289 | this.scene.overrideMaterial = this.materialPhase; 290 | this.screenQuad.material = this.materialPhase; 291 | if (this.initial) { 292 | this.materialPhase.uniforms.u_phases.value = this.pingPhaseTexture; 293 | this.initial = false; 294 | }else { 295 | this.materialPhase.uniforms.u_phases.value = this.pingPhase ? this.pingPhaseFramebuffer : this.pongPhaseFramebuffer; 296 | } 297 | this.materialPhase.uniforms.u_deltaTime.value = this.deltaTime; 298 | this.materialPhase.uniforms.u_size.value = this.size; 299 | this.renderer.render(this.scene, this.oceanCamera, this.pingPhase ? this.pongPhaseFramebuffer : this.pingPhaseFramebuffer); 300 | this.pingPhase = !this.pingPhase; 301 | 302 | }; 303 | 304 | THREE.Ocean.prototype.renderSpectrum = function () { 305 | 306 | this.scene.overrideMaterial = this.materialSpectrum; 307 | this.materialSpectrum.uniforms.u_initialSpectrum.value = this.initialSpectrumFramebuffer; 308 | this.materialSpectrum.uniforms.u_phases.value = this.pingPhase ? this.pingPhaseFramebuffer : this.pongPhaseFramebuffer; 309 | //this.materialSpectrum.uniforms.u_choppiness.value = this.choppiness ; 310 | this.materialSpectrum.uniforms.u_size.value = this.size ; 311 | this.renderer.render(this.scene, this.oceanCamera, this.spectrumFramebuffer); 312 | 313 | }; 314 | 315 | THREE.Ocean.prototype.renderSpectrumFFT = function() { 316 | 317 | // GPU FFT using Stockham formulation 318 | var iterations = Math.log2( this.resolution ) * 2; // log2 319 | 320 | this.scene.overrideMaterial = this.materialOceanHorizontal; 321 | var subtransformProgram = this.materialOceanHorizontal; 322 | 323 | // Processus 0-N 324 | // material = materialOceanHorizontal 325 | // 0 : material( spectrumFramebuffer ) > pingTransformFramebuffer 326 | 327 | // i%2==0 : material( pongTransformFramebuffer ) > pingTransformFramebuffer 328 | // i%2==1 : material( pingTransformFramebuffer ) > pongTransformFramebuffer 329 | 330 | // i == N/2 : material = materialOceanVertical 331 | 332 | // i%2==0 : material( pongTransformFramebuffer ) > pingTransformFramebuffer 333 | // i%2==1 : material( pingTransformFramebuffer ) > pongTransformFramebuffer 334 | 335 | // N-1 : materialOceanVertical( pingTransformFramebuffer / pongTransformFramebuffer ) > displacementMapFramebuffer 336 | 337 | var frameBuffer; 338 | var inputBuffer; 339 | 340 | for (var i = 0; i < iterations; i++) { 341 | if (i === 0) { 342 | inputBuffer = this.spectrumFramebuffer; 343 | frameBuffer = this.pingTransformFramebuffer ; 344 | } 345 | else if (i === iterations - 1) { 346 | inputBuffer = ((iterations % 2 === 0)? this.pingTransformFramebuffer : this.pongTransformFramebuffer) ; 347 | frameBuffer = this.displacementMapFramebuffer ; 348 | } 349 | else if (i % 2 === 1) { 350 | inputBuffer = this.pingTransformFramebuffer; 351 | frameBuffer = this.pongTransformFramebuffer ; 352 | } 353 | else { 354 | inputBuffer = this.pongTransformFramebuffer; 355 | frameBuffer = this.pingTransformFramebuffer ; 356 | } 357 | 358 | if (i === iterations / 2) { 359 | subtransformProgram = this.materialOceanVertical; 360 | this.scene.overrideMaterial = this.materialOceanVertical; 361 | } 362 | 363 | subtransformProgram.uniforms.u_input.value = inputBuffer; 364 | 365 | subtransformProgram.uniforms.u_subtransformSize.value = Math.pow(2, (i % (iterations / 2) + 1 )); 366 | this.renderer.render(this.scene, this.oceanCamera, frameBuffer); 367 | } 368 | 369 | }; 370 | 371 | THREE.Ocean.prototype.renderNormalMap = function () { 372 | 373 | this.scene.overrideMaterial = this.materialNormal; 374 | if (this.changed) this.materialNormal.uniforms.u_size.value = this.size; 375 | this.materialNormal.uniforms.u_displacementMap.value = this.displacementMapFramebuffer; 376 | this.renderer.render(this.scene, this.oceanCamera, this.normalMapFramebuffer, true); 377 | 378 | }; 379 | -------------------------------------------------------------------------------- /js/libs/dat.gui.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * dat-gui JavaScript Controller Library 3 | * http://code.google.com/p/dat-gui 4 | * 5 | * Copyright 2011 Data Arts Team, Google Creative Lab 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | */ 13 | var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(f,a){a=a||document;var d=a.createElement("link");d.type="text/css";d.rel="stylesheet";d.href=f;a.getElementsByTagName("head")[0].appendChild(d)},inject:function(f,a){a=a||document;var d=document.createElement("style");d.type="text/css";d.innerHTML=f;a.getElementsByTagName("head")[0].appendChild(d)}}}(); 14 | dat.utils.common=function(){var f=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(d){this.each(a.call(arguments,1),function(a){for(var c in a)this.isUndefined(a[c])||(d[c]=a[c])},this);return d},defaults:function(d){this.each(a.call(arguments,1),function(a){for(var c in a)this.isUndefined(d[c])&&(d[c]=a[c])},this);return d},compose:function(){var d=a.call(arguments);return function(){for(var e=a.call(arguments),c=d.length-1;0<=c;c--)e=[d[c].apply(this,e)];return e[0]}}, 15 | each:function(a,e,c){if(a)if(f&&a.forEach&&a.forEach===f)a.forEach(e,c);else if(a.length===a.length+0)for(var b=0,p=a.length;bthis.__max&&(a=this.__max);void 0!==this.__step&&0!=a%this.__step&&(a=Math.round(a/this.__step)*this.__step);return e.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__impliedStep=this.__step=a;this.__precision=d(a);return this}});return e}(dat.controllers.Controller,dat.utils.common); 29 | dat.controllers.NumberControllerBox=function(f,a,d){var e=function(c,b,f){function q(){var a=parseFloat(n.__input.value);d.isNaN(a)||n.setValue(a)}function l(a){var b=u-a.clientY;n.setValue(n.getValue()+b*n.__impliedStep);u=a.clientY}function r(){a.unbind(window,"mousemove",l);a.unbind(window,"mouseup",r)}this.__truncationSuspended=!1;e.superclass.call(this,c,b,f);var n=this,u;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",q);a.bind(this.__input, 30 | "blur",function(){q();n.__onFinishChange&&n.__onFinishChange.call(n,n.getValue())});a.bind(this.__input,"mousedown",function(b){a.bind(window,"mousemove",l);a.bind(window,"mouseup",r);u=b.clientY});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&(n.__truncationSuspended=!0,this.blur(),n.__truncationSuspended=!1)});this.updateDisplay();this.domElement.appendChild(this.__input)};e.superclass=f;d.extend(e.prototype,f.prototype,{updateDisplay:function(){var a=this.__input,b;if(this.__truncationSuspended)b= 31 | this.getValue();else{b=this.getValue();var d=Math.pow(10,this.__precision);b=Math.round(b*d)/d}a.value=b;return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common); 32 | dat.controllers.NumberControllerSlider=function(f,a,d,e,c){function b(a,b,c,e,d){return e+(a-b)/(c-b)*(d-e)}var p=function(c,e,d,f,u){function A(c){c.preventDefault();var e=a.getOffset(k.__background),d=a.getWidth(k.__background);k.setValue(b(c.clientX,e.left,e.left+d,k.__min,k.__max));return!1}function g(){a.unbind(window,"mousemove",A);a.unbind(window,"mouseup",g);k.__onFinishChange&&k.__onFinishChange.call(k,k.getValue())}p.superclass.call(this,c,e,{min:d,max:f,step:u});var k=this;this.__background= 33 | document.createElement("div");this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(b){a.bind(window,"mousemove",A);a.bind(window,"mouseup",g);A(b)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};p.superclass=f;p.useDefaultStyles=function(){d.inject(c)};e.extend(p.prototype,f.prototype,{updateDisplay:function(){var a= 34 | (this.getValue()-this.__min)/(this.__max-this.__min);this.__foreground.style.width=100*a+"%";return p.superclass.prototype.updateDisplay.call(this)}});return p}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,"/**\n * dat-gui JavaScript Controller Library\n * http://code.google.com/p/dat-gui\n *\n * Copyright 2011 Data Arts Team, Google Creative Lab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n */\n\n.slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}"); 35 | dat.controllers.FunctionController=function(f,a,d){var e=function(c,b,d){e.superclass.call(this,c,b);var f=this;this.__button=document.createElement("div");this.__button.innerHTML=void 0===d?"Fire":d;a.bind(this.__button,"click",function(a){a.preventDefault();f.fire();return!1});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};e.superclass=f;d.extend(e.prototype,f.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.getValue().call(this.object); 36 | this.__onFinishChange&&this.__onFinishChange.call(this,this.getValue())}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); 37 | dat.controllers.BooleanController=function(f,a,d){var e=function(c,b){e.superclass.call(this,c,b);var d=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){d.setValue(!d.__prev)},!1);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};e.superclass=f;d.extend(e.prototype,f.prototype,{setValue:function(a){a=e.superclass.prototype.setValue.call(this,a);this.__onFinishChange&& 38 | this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){!0===this.getValue()?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=!0):this.__checkbox.checked=!1;return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); 39 | dat.color.toString=function(f){return function(a){if(1==a.a||f.isUndefined(a.a)){for(a=a.hex.toString(16);6>a.length;)a="0"+a;return"#"+a}return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common); 40 | dat.color.interpret=function(f,a){var d,e,c=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:f},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:f},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); 41 | return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:f},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:f}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return 3!= 42 | a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return 4!=a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(b){return a.isNumber(b.r)&&a.isNumber(b.g)&&a.isNumber(b.b)&&a.isNumber(b.a)?{space:"RGB",r:b.r,g:b.g,b:b.b,a:b.a}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(b){return a.isNumber(b.r)&& 43 | a.isNumber(b.g)&&a.isNumber(b.b)?{space:"RGB",r:b.r,g:b.g,b:b.b}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)&&a.isNumber(b.a)?{space:"HSV",h:b.h,s:b.s,v:b.v,a:b.a}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)?{space:"HSV",h:b.h,s:b.s,v:b.v}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){e=!1; 44 | var b=1\n\n Here\'s the new load parameter for your GUI\'s constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n \n
\n \n
\n\n', 71 | ".dg {\n /** Clear list styles */\n /* Auto-place container */\n /* Auto-placed GUI's */\n /* Line items that don't contain folders. */\n /** Folder names */\n /** Hides closed items */\n /** Controller row */\n /** Name-half (left) */\n /** Controller-half (right) */\n /** Controller placement */\n /** Shorter number boxes when slider is present. */\n /** Ensure the entire boolean and function row shows a hand */ }\n .dg ul {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n clear: both; }\n .dg.ac {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 0;\n z-index: 0; }\n .dg:not(.ac) .main {\n /** Exclude mains in ac so that we don't hide close button */\n overflow: hidden; }\n .dg.main {\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear; }\n .dg.main.taller-than-window {\n overflow-y: auto; }\n .dg.main.taller-than-window .close-button {\n opacity: 1;\n /* TODO, these are style notes */\n margin-top: -1px;\n border-top: 1px solid #2c2c2c; }\n .dg.main ul.closed .close-button {\n opacity: 1 !important; }\n .dg.main:hover .close-button,\n .dg.main .close-button.drag {\n opacity: 1; }\n .dg.main .close-button {\n /*opacity: 0;*/\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear;\n border: 0;\n position: absolute;\n line-height: 19px;\n height: 20px;\n /* TODO, these are style notes */\n cursor: pointer;\n text-align: center;\n background-color: #000; }\n .dg.main .close-button:hover {\n background-color: #111; }\n .dg.a {\n float: right;\n margin-right: 15px;\n overflow-x: hidden; }\n .dg.a.has-save > ul {\n margin-top: 27px; }\n .dg.a.has-save > ul.closed {\n margin-top: 0; }\n .dg.a .save-row {\n position: fixed;\n top: 0;\n z-index: 1002; }\n .dg li {\n -webkit-transition: height 0.1s ease-out;\n -o-transition: height 0.1s ease-out;\n -moz-transition: height 0.1s ease-out;\n transition: height 0.1s ease-out; }\n .dg li:not(.folder) {\n cursor: auto;\n height: 27px;\n line-height: 27px;\n overflow: hidden;\n padding: 0 4px 0 5px; }\n .dg li.folder {\n padding: 0;\n border-left: 4px solid rgba(0, 0, 0, 0); }\n .dg li.title {\n cursor: pointer;\n margin-left: -4px; }\n .dg .closed li:not(.title),\n .dg .closed ul li,\n .dg .closed ul li > * {\n height: 0;\n overflow: hidden;\n border: 0; }\n .dg .cr {\n clear: both;\n padding-left: 3px;\n height: 27px; }\n .dg .property-name {\n cursor: default;\n float: left;\n clear: left;\n width: 40%;\n overflow: hidden;\n text-overflow: ellipsis; }\n .dg .c {\n float: left;\n width: 60%; }\n .dg .c input[type=text] {\n border: 0;\n margin-top: 4px;\n padding: 3px;\n width: 100%;\n float: right; }\n .dg .has-slider input[type=text] {\n width: 30%;\n /*display: none;*/\n margin-left: 0; }\n .dg .slider {\n float: left;\n width: 66%;\n margin-left: -5px;\n margin-right: 0;\n height: 19px;\n margin-top: 4px; }\n .dg .slider-fg {\n height: 100%; }\n .dg .c input[type=checkbox] {\n margin-top: 9px; }\n .dg .c select {\n margin-top: 5px; }\n .dg .cr.function,\n .dg .cr.function .property-name,\n .dg .cr.function *,\n .dg .cr.boolean,\n .dg .cr.boolean * {\n cursor: pointer; }\n .dg .selector {\n display: none;\n position: absolute;\n margin-left: -9px;\n margin-top: 23px;\n z-index: 10; }\n .dg .c:hover .selector,\n .dg .selector.drag {\n display: block; }\n .dg li.save-row {\n padding: 0; }\n .dg li.save-row .button {\n display: inline-block;\n padding: 0px 6px; }\n .dg.dialogue {\n background-color: #222;\n width: 460px;\n padding: 15px;\n font-size: 13px;\n line-height: 15px; }\n\n/* TODO Separate style and structure */\n#dg-new-constructor {\n padding: 10px;\n color: #222;\n font-family: Monaco, monospace;\n font-size: 10px;\n border: 0;\n resize: none;\n box-shadow: inset 1px 1px 1px #888;\n word-wrap: break-word;\n margin: 12px 0;\n display: block;\n width: 440px;\n overflow-y: scroll;\n height: 100px;\n position: relative; }\n\n#dg-local-explain {\n display: none;\n font-size: 11px;\n line-height: 17px;\n border-radius: 3px;\n background-color: #333;\n padding: 8px;\n margin-top: 10px; }\n #dg-local-explain code {\n font-size: 10px; }\n\n#dat-gui-save-locally {\n display: none; }\n\n/** Main type */\n.dg {\n color: #eee;\n font: 11px 'Lucida Grande', sans-serif;\n text-shadow: 0 -1px 0 #111;\n /** Auto place */\n /* Controller row,
  • */\n /** Controllers */ }\n .dg.main {\n /** Scrollbar */ }\n .dg.main::-webkit-scrollbar {\n width: 5px;\n background: #1a1a1a; }\n .dg.main::-webkit-scrollbar-corner {\n height: 0;\n display: none; }\n .dg.main::-webkit-scrollbar-thumb {\n border-radius: 5px;\n background: #676767; }\n .dg li:not(.folder) {\n background: #1a1a1a;\n border-bottom: 1px solid #2c2c2c; }\n .dg li.save-row {\n line-height: 25px;\n background: #dad5cb;\n border: 0; }\n .dg li.save-row select {\n margin-left: 5px;\n width: 108px; }\n .dg li.save-row .button {\n margin-left: 5px;\n margin-top: 1px;\n border-radius: 2px;\n font-size: 9px;\n line-height: 7px;\n padding: 4px 4px 5px 4px;\n background: #c5bdad;\n color: #fff;\n text-shadow: 0 1px 0 #b0a58f;\n box-shadow: 0 -1px 0 #b0a58f;\n cursor: pointer; }\n .dg li.save-row .button.gears {\n background: #c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;\n height: 7px;\n width: 8px; }\n .dg li.save-row .button:hover {\n background-color: #bab19e;\n box-shadow: 0 -1px 0 #b0a58f; }\n .dg li.folder {\n border-bottom: 0; }\n .dg li.title {\n padding-left: 16px;\n background: black url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;\n cursor: pointer;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2); }\n .dg .closed li.title {\n background-image: url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==); }\n .dg .cr.boolean {\n border-left: 3px solid #806787; }\n .dg .cr.function {\n border-left: 3px solid #e61d5f; }\n .dg .cr.number {\n border-left: 3px solid #2fa1d6; }\n .dg .cr.number input[type=text] {\n color: #2fa1d6; }\n .dg .cr.string {\n border-left: 3px solid #1ed36f; }\n .dg .cr.string input[type=text] {\n color: #1ed36f; }\n .dg .cr.function:hover, .dg .cr.boolean:hover {\n background: #111; }\n .dg .c input[type=text] {\n background: #303030;\n outline: none; }\n .dg .c input[type=text]:hover {\n background: #3c3c3c; }\n .dg .c input[type=text]:focus {\n background: #494949;\n color: #fff; }\n .dg .c .slider {\n background: #303030;\n cursor: ew-resize; }\n .dg .c .slider-fg {\n background: #2fa1d6; }\n .dg .c .slider:hover {\n background: #3c3c3c; }\n .dg .c .slider:hover .slider-fg {\n background: #44abda; }\n", 72 | dat.controllers.factory=function(f,a,d,e,c,b,p){return function(q,l,r,n){var u=q[l];if(p.isArray(r)||p.isObject(r))return new f(q,l,r);if(p.isNumber(u))return p.isNumber(r)&&p.isNumber(n)?new d(q,l,r,n):new a(q,l,{min:r,max:n});if(p.isString(u))return new e(q,l);if(p.isFunction(u))return new c(q,l,"");if(p.isBoolean(u))return new b(q,l)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(f,a,d){var e= 73 | function(c,b){function d(){f.setValue(f.__input.value)}e.superclass.call(this,c,b);var f=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",d);a.bind(this.__input,"change",d);a.bind(this.__input,"blur",function(){f.__onFinishChange&&f.__onFinishChange.call(f,f.getValue())});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};e.superclass=f;d.extend(e.prototype, 74 | f.prototype,{updateDisplay:function(){a.isActive(this.__input)||(this.__input.value=this.getValue());return e.superclass.prototype.updateDisplay.call(this)}});return e}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController, 75 | dat.controllers.ColorController=function(f,a,d,e,c){function b(a,b,d,e){a.style.background="";c.each(l,function(c){a.style.cssText+="background: "+c+"linear-gradient("+b+", "+d+" 0%, "+e+" 100%); "})}function p(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"; 76 | a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var q=function(f,n){function u(b){v(b);a.bind(window,"mousemove",v);a.bind(window, 77 | "mouseup",l)}function l(){a.unbind(window,"mousemove",v);a.unbind(window,"mouseup",l)}function g(){var a=e(this.value);!1!==a?(t.__color.__state=a,t.setValue(t.__color.toOriginal())):this.value=t.__color.toString()}function k(){a.unbind(window,"mousemove",w);a.unbind(window,"mouseup",k)}function v(b){b.preventDefault();var c=a.getWidth(t.__saturation_field),d=a.getOffset(t.__saturation_field),e=(b.clientX-d.left+document.body.scrollLeft)/c;b=1-(b.clientY-d.top+document.body.scrollTop)/c;1 78 | b&&(b=0);1e&&(e=0);t.__color.v=b;t.__color.s=e;t.setValue(t.__color.toOriginal());return!1}function w(b){b.preventDefault();var c=a.getHeight(t.__hue_field),d=a.getOffset(t.__hue_field);b=1-(b.clientY-d.top+document.body.scrollTop)/c;1b&&(b=0);t.__color.h=360*b;t.setValue(t.__color.toOriginal());return!1}q.superclass.call(this,f,n);this.__color=new d(this.getValue());this.__temp=new d(0);var t=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement,!1); 79 | this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input=document.createElement("input"); 80 | this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){13===a.keyCode&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(b){a.addClass(this,"drag").bind(window,"mouseup",function(b){a.removeClass(t.__selector,"drag")})});var y=document.createElement("div");c.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"});c.extend(this.__field_knob.style, 81 | {position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(.5>this.__color.v?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});c.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});c.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});c.extend(y.style,{width:"100%",height:"100%", 82 | background:"none"});b(y,"top","rgba(0,0,0,0)","#000");c.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});p(this.__hue_field);c.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",u);a.bind(this.__field_knob,"mousedown",u);a.bind(this.__hue_field,"mousedown",function(b){w(b);a.bind(window, 83 | "mousemove",w);a.bind(window,"mouseup",k)});this.__saturation_field.appendChild(y);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};q.superclass=f;c.extend(q.prototype,f.prototype,{updateDisplay:function(){var a=e(this.getValue());if(!1!==a){var f=!1; 84 | c.each(d.COMPONENTS,function(b){if(!c.isUndefined(a[b])&&!c.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return f=!0,{}},this);f&&c.extend(this.__color.__state,a)}c.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var l=.5>this.__color.v||.5a&&(a+=1);return{h:360*a,s:c/b,v:b/255}},rgb_to_hex:function(a,d,e){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,d);return a=this.hex_with_component(a,0,e)},component_from_hex:function(a,d){return a>>8*d&255},hex_with_component:function(a,d,e){return e<<(f=8*d)|a&~(255<.*<\/script>)/gim,""),this.delay=i,this.exist=!1,this.element=document.createElement("div"),this.element.innerHTML=a,this.element=this.element.children[0],this.element.style.display=y,this.element.style.opacity=m,this.element.style.transition=["all",n.speed/1e3+"s","ease-in-out"].join(" "),this.element.className+=[" ",h,"-",this.type].join(""),this.element.id=this.id,this.element.setAttribute("role",this.type),this.buttons=this.element.children[0],this.content=this.element.children[1],this.content.innerHTML=this.text,l.appendChild(this.element);var o=this;setTimeout(function(){o.buttons.children.length||r.bind(o.element,"click",function(){o.hide()})},n.speed)}function o(){var e=p;c.reverse(d,function(t){t.exist&&(t.element.style[n.position]=e+"px",e+=t.element.offsetHeight+p)})}try{var r=e("event")}catch(s){var r=e("component-event")}var c=e("ea"),u=e("uniquid"),a=e("./template.html"),f=["default","success","info","warning","error"],l=document.getElementsByTagName("body")[0],h="messg",d={},p=10,m="0.0",v="1.0",y="none",x="block";t.exports=n,n.speed=250,n.position="top",n.flow=!0,t.exports.set=function(e,t){"object"==typeof e?c(e,function(e,t){n[t]=e}):t&&(n[e]=t)},c(f,function(e){t.exports[e]=function(t,i){return n(t,e,i)}}),i.prototype.show=function(){this.exist=!0,this.element.style.display=x;var e=this;setTimeout(function(){e.element.style.opacity=v},50),this.delay&&setTimeout(function(){e.hide()},e.delay+n.speed),o()},i.prototype.hide=function(e){if("function"==typeof e)return this.fn=e,this;this.exist=!1,this.element.style.opacity=m;var t=this;setTimeout(function(){t.fn&&t.fn(),t.element.style.display=y,l.removeChild(t.element),delete d[t.id]},n.speed),o()},i.prototype.button=function(e,t){var n=document.createElement("button");n.innerHTML=e,this.buttons.appendChild(n),o();var i=this;return r.bind(n,"click","function"==typeof t?function(){t(e.toLowerCase()),i.hide()}:function(){i.hide()}),this}},{event:2,"component-event":2,ea:3,uniquid:4,"./template.html":5}],2:[function(e,t,n){var i=window.addEventListener?"addEventListener":"attachEvent",o=window.removeEventListener?"removeEventListener":"detachEvent",r="addEventListener"!==i?"on":"";n.bind=function(e,t,n,o){return e[i](r+t,n,o||!1),n},n.unbind=function(e,t,n,i){return e[o](r+t,n,i||!1),n}},{}],3:[function(e,t){"use strict";function n(e,t,n){if("function"==typeof n)switch(i(t)){case"array":return s[e](t,n);case"object":return c[e](t,n)}}function i(e){return Object.prototype.toString.call(e).replace(/\[\w+\s(\w+)\]/i,"$1").toLowerCase()}function o(e,t){return o.each(e,t)}var r=Object.prototype.hasOwnProperty,s={},c={};s.each=function(e,t){for(var n=0;n=0;n--)t(e[n],n)},c.each=function(e,t){for(var n in e)r.call(e,n)&&t(e[n],n)},c.reverse=function(e,t){var n=[];for(var i in e)r.call(e,i)&&n.push(i);for(var o=n.length-1;o>=0;o--)t(e[n[o]],n[o])},o.each=function(e,t){return n("each",e,t)},o.reverse=function(e,t){return n("reverse",e,t)},t.exports=o},{}],4:[function(e,t){"use strict";t.exports=function(e){var t=parseInt([(new Date).valueOf(),(1e6*Math.random()).toFixed()].join("")).toString(36);return[e||"",t].join("")}},{}],5:[function(e,t){t.exports='
    \n
    \n
    \n
    \n'},{}]},{},{1:""})); -------------------------------------------------------------------------------- /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 | }, onProgress, onError ); 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.crossOrigin = this.crossOrigin 91 | materialCreator.setMaterials( materialsInfo ); 92 | return materialCreator; 93 | 94 | } 95 | 96 | }; 97 | 98 | /** 99 | * Create a new THREE-MTLLoader.MaterialCreator 100 | * @param baseUrl - Url relative to which textures are loaded 101 | * @param options - Set of options on how to construct the materials 102 | * side: Which side to apply the material 103 | * THREE.FrontSide (default), THREE.BackSide, THREE.DoubleSide 104 | * wrap: What type of wrapping to apply for textures 105 | * THREE.RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping 106 | * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255 107 | * Default: false, assumed to be already normalized 108 | * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's 109 | * Default: false 110 | * invertTransparency: If transparency need to be inverted (inversion is needed if d = 0 is fully opaque) 111 | * Default: false (d = 1 is fully opaque) 112 | * @constructor 113 | */ 114 | 115 | THREE.MTLLoader.MaterialCreator = function( baseUrl, options ) { 116 | 117 | this.baseUrl = baseUrl; 118 | this.options = options; 119 | this.materialsInfo = {}; 120 | this.materials = {}; 121 | this.materialsArray = []; 122 | this.nameLookup = {}; 123 | 124 | this.side = ( this.options && this.options.side ) ? this.options.side : THREE.FrontSide; 125 | this.wrap = ( this.options && this.options.wrap ) ? this.options.wrap : THREE.RepeatWrapping; 126 | 127 | }; 128 | 129 | THREE.MTLLoader.MaterialCreator.prototype = { 130 | 131 | constructor: THREE.MTLLoader.MaterialCreator, 132 | 133 | setMaterials: function( materialsInfo ) { 134 | 135 | this.materialsInfo = this.convert( materialsInfo ); 136 | this.materials = {}; 137 | this.materialsArray = []; 138 | this.nameLookup = {}; 139 | 140 | }, 141 | 142 | convert: function( materialsInfo ) { 143 | 144 | if ( !this.options ) return materialsInfo; 145 | 146 | var converted = {}; 147 | 148 | for ( var mn in materialsInfo ) { 149 | 150 | // Convert materials info into normalized form based on options 151 | 152 | var mat = materialsInfo[ mn ]; 153 | 154 | var covmat = {}; 155 | 156 | converted[ mn ] = covmat; 157 | 158 | for ( var prop in mat ) { 159 | 160 | var save = true; 161 | var value = mat[ prop ]; 162 | var lprop = prop.toLowerCase(); 163 | 164 | switch ( lprop ) { 165 | 166 | case 'kd': 167 | case 'ka': 168 | case 'ks': 169 | 170 | // Diffuse color (color under white light) using RGB values 171 | 172 | if ( this.options && this.options.normalizeRGB ) { 173 | 174 | value = [ value[ 0 ] / 255, value[ 1 ] / 255, value[ 2 ] / 255 ]; 175 | 176 | } 177 | 178 | if ( this.options && this.options.ignoreZeroRGBs ) { 179 | 180 | if ( value[ 0 ] === 0 && value[ 1 ] === 0 && value[ 1 ] === 0 ) { 181 | 182 | // ignore 183 | 184 | save = false; 185 | 186 | } 187 | } 188 | 189 | break; 190 | 191 | case 'd': 192 | 193 | // According to MTL format (http://paulbourke.net/dataformats/mtl/): 194 | // d is dissolve for current material 195 | // factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent) 196 | 197 | if ( this.options && this.options.invertTransparency ) { 198 | 199 | value = 1 - value; 200 | 201 | } 202 | 203 | break; 204 | 205 | default: 206 | 207 | break; 208 | } 209 | 210 | if ( save ) { 211 | 212 | covmat[ lprop ] = value; 213 | 214 | } 215 | 216 | } 217 | 218 | } 219 | 220 | return converted; 221 | 222 | }, 223 | 224 | preload: function () { 225 | 226 | for ( var mn in this.materialsInfo ) { 227 | 228 | this.create( mn ); 229 | 230 | } 231 | 232 | }, 233 | 234 | getIndex: function( materialName ) { 235 | 236 | return this.nameLookup[ materialName ]; 237 | 238 | }, 239 | 240 | getAsArray: function() { 241 | 242 | var index = 0; 243 | 244 | for ( var mn in this.materialsInfo ) { 245 | 246 | this.materialsArray[ index ] = this.create( mn ); 247 | this.nameLookup[ mn ] = index; 248 | index ++; 249 | 250 | } 251 | 252 | return this.materialsArray; 253 | 254 | }, 255 | 256 | create: function ( materialName ) { 257 | 258 | if ( this.materials[ materialName ] === undefined ) { 259 | 260 | this.createMaterial_( materialName ); 261 | 262 | } 263 | 264 | return this.materials[ materialName ]; 265 | 266 | }, 267 | 268 | createMaterial_: function ( materialName ) { 269 | 270 | // Create material 271 | 272 | var mat = this.materialsInfo[ materialName ]; 273 | var params = { 274 | 275 | name: materialName, 276 | side: this.side 277 | 278 | }; 279 | 280 | for ( var prop in mat ) { 281 | 282 | var value = mat[ prop ]; 283 | 284 | switch ( prop.toLowerCase() ) { 285 | 286 | // Ns is material specular exponent 287 | 288 | case 'kd': 289 | 290 | // Diffuse color (color under white light) using RGB values 291 | 292 | params[ 'diffuse' ] = new THREE.Color().fromArray( value ); 293 | 294 | break; 295 | 296 | case 'ka': 297 | 298 | // Ambient color (color under shadow) using RGB values 299 | 300 | break; 301 | 302 | case 'ks': 303 | 304 | // Specular color (color when light is reflected from shiny surface) using RGB values 305 | params[ 'specular' ] = new THREE.Color().fromArray( value ); 306 | 307 | break; 308 | 309 | case 'map_kd': 310 | 311 | // Diffuse texture map 312 | 313 | params[ 'map' ] = this.loadTexture( this.baseUrl + value ); 314 | params[ 'map' ].wrapS = this.wrap; 315 | params[ 'map' ].wrapT = this.wrap; 316 | 317 | break; 318 | 319 | case 'ns': 320 | 321 | // The specular exponent (defines the focus of the specular highlight) 322 | // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000. 323 | 324 | params['shininess'] = value; 325 | 326 | break; 327 | 328 | case 'd': 329 | 330 | // According to MTL format (http://paulbourke.net/dataformats/mtl/): 331 | // d is dissolve for current material 332 | // factor of 1.0 is fully opaque, a factor of 0 is fully dissolved (completely transparent) 333 | 334 | if ( value < 1 ) { 335 | 336 | params['transparent'] = true; 337 | params['opacity'] = value; 338 | 339 | } 340 | 341 | break; 342 | 343 | case 'map_bump': 344 | case 'bump': 345 | 346 | // Bump texture map 347 | 348 | if ( params[ 'bumpMap' ] ) break; // Avoid loading twice. 349 | 350 | params[ 'bumpMap' ] = this.loadTexture( this.baseUrl + value ); 351 | params[ 'bumpMap' ].wrapS = this.wrap; 352 | params[ 'bumpMap' ].wrapT = this.wrap; 353 | 354 | break; 355 | 356 | default: 357 | break; 358 | 359 | } 360 | 361 | } 362 | 363 | if ( params[ 'diffuse' ] ) { 364 | 365 | params[ 'color' ] = params[ 'diffuse' ]; 366 | 367 | } 368 | 369 | this.materials[ materialName ] = new THREE.MeshPhongMaterial( params ); 370 | return this.materials[ materialName ]; 371 | 372 | }, 373 | 374 | 375 | loadTexture: function ( url, mapping, onLoad, onError ) { 376 | 377 | var texture; 378 | var loader = THREE.Loader.Handlers.get( url ); 379 | 380 | if ( loader !== null ) { 381 | 382 | texture = loader.load( url, onLoad ); 383 | 384 | } else { 385 | 386 | texture = new THREE.Texture(); 387 | 388 | loader = new THREE.ImageLoader(); 389 | loader.crossOrigin = this.crossOrigin; 390 | loader.load( url, function ( image ) { 391 | 392 | texture.image = THREE.MTLLoader.ensurePowerOfTwo_( image ); 393 | texture.needsUpdate = true; 394 | 395 | if ( onLoad ) onLoad( texture ); 396 | 397 | } ); 398 | 399 | } 400 | 401 | if ( mapping !== undefined ) texture.mapping = mapping; 402 | 403 | return texture; 404 | 405 | } 406 | 407 | }; 408 | 409 | THREE.MTLLoader.ensurePowerOfTwo_ = function ( image ) { 410 | 411 | if ( ! THREE.Math.isPowerOfTwo( image.width ) || ! THREE.Math.isPowerOfTwo( image.height ) ) { 412 | 413 | var canvas = document.createElement( "canvas" ); 414 | canvas.width = THREE.MTLLoader.nextHighestPowerOfTwo_( image.width ); 415 | canvas.height = THREE.MTLLoader.nextHighestPowerOfTwo_( image.height ); 416 | 417 | var ctx = canvas.getContext("2d"); 418 | ctx.drawImage( image, 0, 0, image.width, image.height, 0, 0, canvas.width, canvas.height ); 419 | return canvas; 420 | 421 | } 422 | 423 | return image; 424 | 425 | }; 426 | 427 | THREE.MTLLoader.nextHighestPowerOfTwo_ = function( x ) { 428 | 429 | -- x; 430 | 431 | for ( var i = 1; i < 32; i <<= 1 ) { 432 | 433 | x = x | x >> i; 434 | 435 | } 436 | 437 | return x + 1; 438 | 439 | }; 440 | 441 | THREE.EventDispatcher.prototype.apply( THREE.MTLLoader.prototype ); -------------------------------------------------------------------------------- /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 ( manager ) { 9 | 10 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager; 11 | 12 | }; 13 | 14 | THREE.OBJMTLLoader.prototype = { 15 | 16 | constructor: THREE.OBJMTLLoader, 17 | 18 | load: function ( url, mtlurl, onLoad, onProgress, onError ) { 19 | 20 | var scope = this; 21 | 22 | var mtlLoader = new THREE.MTLLoader( url.substr( 0, url.lastIndexOf( "/" ) + 1 ) ); 23 | mtlLoader.crossOrigin = scope.crossOrigin; 24 | mtlLoader.load( mtlurl, function ( materials ) { 25 | 26 | var materialsCreator = materials; 27 | materialsCreator.preload(); 28 | 29 | var loader = new THREE.XHRLoader( scope.manager ); 30 | loader.setCrossOrigin( scope.crossOrigin ); 31 | loader.load( url, function ( text ) { 32 | 33 | var object = scope.parse( text ); 34 | 35 | object.traverse( function ( object ) { 36 | 37 | if ( object instanceof THREE.Mesh ) { 38 | 39 | if ( object.material.name ) { 40 | 41 | var material = materialsCreator.create( object.material.name ); 42 | 43 | if ( material ) object.material = material; 44 | 45 | } 46 | 47 | } 48 | 49 | } ); 50 | 51 | onLoad( object ); 52 | 53 | }, onProgress, onError ); 54 | 55 | }, onProgress, onError ); 56 | 57 | }, 58 | 59 | /** 60 | * Parses loaded .obj file 61 | * @param data - content of .obj file 62 | * @param mtllibCallback - callback to handle mtllib declaration (optional) 63 | * @return {THREE.Object3D} - Object3D (with default material) 64 | */ 65 | 66 | parse: function ( data, mtllibCallback ) { 67 | 68 | function vector( x, y, z ) { 69 | 70 | return new THREE.Vector3( x, y, z ); 71 | 72 | } 73 | 74 | function uv( u, v ) { 75 | 76 | return new THREE.Vector2( u, v ); 77 | 78 | } 79 | 80 | function face3( a, b, c, normals ) { 81 | 82 | return new THREE.Face3( a, b, c, normals ); 83 | 84 | } 85 | 86 | var face_offset = 0; 87 | 88 | function meshN( meshName, materialName ) { 89 | 90 | if ( vertices.length > 0 ) { 91 | 92 | geometry.vertices = vertices; 93 | 94 | geometry.mergeVertices(); 95 | geometry.computeFaceNormals(); 96 | geometry.computeBoundingSphere(); 97 | 98 | object.add( mesh ); 99 | 100 | geometry = new THREE.Geometry(); 101 | mesh = new THREE.Mesh( geometry, material ); 102 | 103 | } 104 | 105 | if ( meshName !== undefined ) mesh.name = meshName; 106 | 107 | if ( materialName !== undefined ) { 108 | 109 | material = new THREE.MeshLambertMaterial(); 110 | material.name = materialName; 111 | 112 | mesh.material = material; 113 | 114 | } 115 | 116 | } 117 | 118 | var group = new THREE.Group(); 119 | var object = group; 120 | 121 | var geometry = new THREE.Geometry(); 122 | var material = new THREE.MeshLambertMaterial(); 123 | var mesh = new THREE.Mesh( geometry, material ); 124 | 125 | var vertices = []; 126 | var normals = []; 127 | var uvs = []; 128 | 129 | function add_face( a, b, c, normals_inds ) { 130 | 131 | if ( normals_inds === undefined ) { 132 | 133 | geometry.faces.push( face3( 134 | parseInt( a ) - (face_offset + 1), 135 | parseInt( b ) - (face_offset + 1), 136 | parseInt( c ) - (face_offset + 1) 137 | ) ); 138 | 139 | } else { 140 | 141 | geometry.faces.push( face3( 142 | parseInt( a ) - (face_offset + 1), 143 | parseInt( b ) - (face_offset + 1), 144 | parseInt( c ) - (face_offset + 1), 145 | [ 146 | normals[ parseInt( normals_inds[ 0 ] ) - 1 ].clone(), 147 | normals[ parseInt( normals_inds[ 1 ] ) - 1 ].clone(), 148 | normals[ parseInt( normals_inds[ 2 ] ) - 1 ].clone() 149 | ] 150 | ) ); 151 | 152 | } 153 | 154 | } 155 | 156 | function add_uvs( a, b, c ) { 157 | 158 | geometry.faceVertexUvs[ 0 ].push( [ 159 | uvs[ parseInt( a ) - 1 ].clone(), 160 | uvs[ parseInt( b ) - 1 ].clone(), 161 | uvs[ parseInt( c ) - 1 ].clone() 162 | ] ); 163 | 164 | } 165 | 166 | function handle_face_line(faces, uvs, normals_inds) { 167 | 168 | if ( faces[ 3 ] === undefined ) { 169 | 170 | add_face( faces[ 0 ], faces[ 1 ], faces[ 2 ], normals_inds ); 171 | 172 | if (!(uvs === undefined) && uvs.length > 0) { 173 | add_uvs( uvs[ 0 ], uvs[ 1 ], uvs[ 2 ] ); 174 | } 175 | 176 | } else { 177 | 178 | if (!(normals_inds === undefined) && normals_inds.length > 0) { 179 | 180 | add_face( faces[ 0 ], faces[ 1 ], faces[ 3 ], [ normals_inds[ 0 ], normals_inds[ 1 ], normals_inds[ 3 ] ]); 181 | add_face( faces[ 1 ], faces[ 2 ], faces[ 3 ], [ normals_inds[ 1 ], normals_inds[ 2 ], normals_inds[ 3 ] ]); 182 | 183 | } else { 184 | 185 | add_face( faces[ 0 ], faces[ 1 ], faces[ 3 ]); 186 | add_face( faces[ 1 ], faces[ 2 ], faces[ 3 ]); 187 | 188 | } 189 | 190 | if (!(uvs === undefined) && uvs.length > 0) { 191 | 192 | add_uvs( uvs[ 0 ], uvs[ 1 ], uvs[ 3 ] ); 193 | add_uvs( uvs[ 1 ], uvs[ 2 ], uvs[ 3 ] ); 194 | 195 | } 196 | 197 | } 198 | 199 | } 200 | 201 | 202 | // v float float float 203 | 204 | var vertex_pattern = /v( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 205 | 206 | // vn float float float 207 | 208 | var normal_pattern = /vn( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 209 | 210 | // vt float float 211 | 212 | var uv_pattern = /vt( +[\d|\.|\+|\-|e]+)( +[\d|\.|\+|\-|e]+)/; 213 | 214 | // f vertex vertex vertex ... 215 | 216 | var face_pattern1 = /f( +\d+)( +\d+)( +\d+)( +\d+)?/; 217 | 218 | // f vertex/uv vertex/uv vertex/uv ... 219 | 220 | var face_pattern2 = /f( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))( +(\d+)\/(\d+))?/; 221 | 222 | // f vertex/uv/normal vertex/uv/normal vertex/uv/normal ... 223 | 224 | var face_pattern3 = /f( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))( +(\d+)\/(\d+)\/(\d+))?/; 225 | 226 | // f vertex//normal vertex//normal vertex//normal ... 227 | 228 | var face_pattern4 = /f( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))( +(\d+)\/\/(\d+))?/ 229 | 230 | // 231 | 232 | var lines = data.split( "\n" ); 233 | 234 | for ( var i = 0; i < lines.length; i ++ ) { 235 | 236 | var line = lines[ i ]; 237 | line = line.trim(); 238 | 239 | var result; 240 | 241 | if ( line.length === 0 || line.charAt( 0 ) === '#' ) { 242 | 243 | continue; 244 | 245 | } else if ( ( result = vertex_pattern.exec( line ) ) !== null ) { 246 | 247 | // ["v 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 248 | 249 | vertices.push( vector( 250 | parseFloat( result[ 1 ] ), 251 | parseFloat( result[ 2 ] ), 252 | parseFloat( result[ 3 ] ) 253 | ) ); 254 | 255 | } else if ( ( result = normal_pattern.exec( line ) ) !== null ) { 256 | 257 | // ["vn 1.0 2.0 3.0", "1.0", "2.0", "3.0"] 258 | 259 | normals.push( vector( 260 | parseFloat( result[ 1 ] ), 261 | parseFloat( result[ 2 ] ), 262 | parseFloat( result[ 3 ] ) 263 | ) ); 264 | 265 | } else if ( ( result = uv_pattern.exec( line ) ) !== null ) { 266 | 267 | // ["vt 0.1 0.2", "0.1", "0.2"] 268 | 269 | uvs.push( uv( 270 | parseFloat( result[ 1 ] ), 271 | parseFloat( result[ 2 ] ) 272 | ) ); 273 | 274 | } else if ( ( result = face_pattern1.exec( line ) ) !== null ) { 275 | 276 | // ["f 1 2 3", "1", "2", "3", undefined] 277 | 278 | handle_face_line([ result[ 1 ], result[ 2 ], result[ 3 ], result[ 4 ] ]); 279 | 280 | } else if ( ( result = face_pattern2.exec( line ) ) !== null ) { 281 | 282 | // ["f 1/1 2/2 3/3", " 1/1", "1", "1", " 2/2", "2", "2", " 3/3", "3", "3", undefined, undefined, undefined] 283 | 284 | handle_face_line( 285 | [ result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ], //faces 286 | [ result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ] //uv 287 | ); 288 | 289 | } else if ( ( result = face_pattern3.exec( line ) ) !== null ) { 290 | 291 | // ["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] 292 | 293 | handle_face_line( 294 | [ result[ 2 ], result[ 6 ], result[ 10 ], result[ 14 ] ], //faces 295 | [ result[ 3 ], result[ 7 ], result[ 11 ], result[ 15 ] ], //uv 296 | [ result[ 4 ], result[ 8 ], result[ 12 ], result[ 16 ] ] //normal 297 | ); 298 | 299 | } else if ( ( result = face_pattern4.exec( line ) ) !== null ) { 300 | 301 | // ["f 1//1 2//2 3//3", " 1//1", "1", "1", " 2//2", "2", "2", " 3//3", "3", "3", undefined, undefined, undefined] 302 | 303 | handle_face_line( 304 | [ result[ 2 ], result[ 5 ], result[ 8 ], result[ 11 ] ], //faces 305 | [ ], //uv 306 | [ result[ 3 ], result[ 6 ], result[ 9 ], result[ 12 ] ] //normal 307 | ); 308 | 309 | } else if ( /^o /.test( line ) ) { 310 | 311 | // object 312 | 313 | meshN(); 314 | face_offset = face_offset + vertices.length; 315 | vertices = []; 316 | object = new THREE.Object3D(); 317 | object.name = line.substring( 2 ).trim(); 318 | group.add( object ); 319 | 320 | } else if ( /^g /.test( line ) ) { 321 | 322 | // group 323 | 324 | meshN( line.substring( 2 ).trim(), undefined ); 325 | 326 | } else if ( /^usemtl /.test( line ) ) { 327 | 328 | // material 329 | 330 | meshN( undefined, line.substring( 7 ).trim() ); 331 | 332 | } else if ( /^mtllib /.test( line ) ) { 333 | 334 | // mtl file 335 | 336 | if ( mtllibCallback ) { 337 | 338 | var mtlfile = line.substring( 7 ); 339 | mtlfile = mtlfile.trim(); 340 | mtllibCallback( mtlfile ); 341 | 342 | } 343 | 344 | } else if ( /^s /.test( line ) ) { 345 | 346 | // Smooth shading 347 | 348 | } else { 349 | 350 | console.log( "THREE.OBJMTLLoader: Unhandled line " + line ); 351 | 352 | } 353 | 354 | } 355 | 356 | //Add last object 357 | meshN(undefined, undefined); 358 | 359 | return group; 360 | 361 | } 362 | 363 | }; 364 | 365 | THREE.EventDispatcher.prototype.apply( THREE.OBJMTLLoader.prototype ); -------------------------------------------------------------------------------- /js/shaders/CloudShader2.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | /** 4 | * Cloud shader by @blurspline / http://github.com/zz85 5 | * Refactored by @jbouny 6 | */ 7 | 8 | THREE.ShaderLib['cloud'] = { 9 | uniforms: { 10 | 'texture': { type: 't', value: null}, 11 | 'time': { type: 'f', value: 1 }, 12 | 'sharp': { type: 'f', value: 0.9 }, 13 | 'cover': { type: 'f', value: 0.5 }, 14 | 'clouds': { type: 'f', value: 1 }, 15 | 'depth': { type: 'f', value: 0 } 16 | }, 17 | vertexShader: [ 18 | 'uniform sampler2D texture;', 19 | 'uniform float time;', 20 | 'varying vec2 vUv;', 21 | 22 | 'void main()', 23 | '{', 24 | 'vUv = uv;', 25 | ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', 26 | '}' 27 | ].join('\n'), 28 | fragmentShader: [ 29 | // Cloud shader by @blurspline / http://github.com/zz85 30 | // uses a simple fractal noise used a generated value-noise texture 31 | 32 | // references 33 | // 1. value noise 34 | // 2. iq clouds 35 | // 3. cloud papers 36 | 'uniform float time;', 37 | 38 | 'uniform float depth;', 39 | 'uniform float sharp;', // magnify the intensity of clouds 40 | // 1 = dull ( more clouds), 0 = fuzzy ( less clouds) 41 | 'uniform float cover;', // 0 = less clouds, 1 = more clouds 42 | // substraction factor 43 | 'uniform float clouds;', // opacity 44 | 'uniform sampler2D texture;', 45 | 'varying vec2 vUv;', 46 | 47 | // multi-chanel noise lookup 48 | 'vec3 noise3(vec2 p) {', 49 | ' return texture2D(texture, p).xyz;', 50 | '}', 51 | 52 | 'vec3 fNoise(vec2 uv) {', 53 | ' vec3 f = vec3(0.);', 54 | ' float scale = 1.;', 55 | ' for (int i=0; i<5; i++) {', 56 | ' scale *= 2.;', 57 | ' f += noise3(uv * scale) / scale;', 58 | ' }', 59 | ' return f;', 60 | '}', 61 | 62 | 'void main(void)', 63 | '{', 64 | ' vec2 uv = vUv;', 65 | 66 | // Formula: varience (smaller -> bigger cover) + speed (time) * direction 67 | // normal thick clouds 68 | ' vec3 ff1 = fNoise(uv * 0.01 + time * 0.00015 * vec2(-1., 1.));', 69 | 70 | // fast small clouds 71 | ' vec3 ff2 = fNoise(uv * 0.1 + time * 0.0005 * vec2(1., 1.));', 72 | 73 | // Different combinations of mixing 74 | ' float t = ff1.x * 0.9 + ff1.y * 0.15;', 75 | ' t = t * 0.99 + ff2.x * 0.01;', 76 | 77 | ' float o = clamp ( length(uv * 2.0 - vec2(1., 1.)), 0., 1. );', 78 | 79 | // applies more transparency to horizon for 80 | // to create illusion of distant clouds 81 | ' o = 1. - o * o * o * o;', 82 | 83 | // multiply by more cloud transparency 84 | ' o -= (1. - t) * 0.95;', // factor clouds opacity based on cloud cover 85 | // 1 t = 1 o 86 | // depending on where this is placed, it will affect darkness / opacity of clouds 87 | ' t = max(t - (1. - cover), 0.);', // low cut off point 88 | // magnify or add layers! 89 | // cloud power magnifer 90 | ' t = 1. - pow(1. - sharp, t);', // . 0.999999 (response curve from linear to exponiential brigtness) 91 | ' t = min(t * 1.9, 1.0);', // clamp to 1.0 92 | 93 | // Other effects 94 | 95 | ' if (depth > 0.) {', 96 | // tweak thresholds 97 | ' if (o < 0.4 && t < 0.8) discard;', 98 | 99 | ' gl_FragData[ 0 ] = vec4(gl_FragCoord.z, 1., 1., 1.);', 100 | ' }', 101 | ' else {', 102 | ' gl_FragData[ 0 ] = vec4(t, t, t, o );', 103 | 104 | ' }', 105 | '}' 106 | ].join('\n') 107 | }; 108 | 109 | function CloudShader( renderer, noiseSize ) { 110 | 111 | noiseSize = noiseSize || 256; 112 | 113 | var cloudShader = THREE.ShaderLib['cloud'] ; 114 | 115 | // Generate random noise texture 116 | var size = noiseSize * noiseSize; 117 | var data = new Uint8Array( 4 * size ); 118 | 119 | for ( var i = 0; i < size * 4; i ++ ) { 120 | data[ i ] = Math.random() * 255 | 0; 121 | } 122 | 123 | var dt = new THREE.DataTexture( data, noiseSize, noiseSize, THREE.RGBAFormat ); 124 | dt.wrapS = THREE.RepeatWrapping; 125 | dt.wrapT = THREE.RepeatWrapping; 126 | dt.magFilter = THREE.LinearFilter; 127 | dt.minFilter = THREE.LinearFilter; 128 | dt.needsUpdate = true; 129 | 130 | cloudShader.uniforms.texture.value = dt; 131 | 132 | var noiseMaterial = new THREE.ShaderMaterial({ 133 | vertexShader: cloudShader.vertexShader, 134 | fragmentShader: cloudShader.fragmentShader, 135 | uniforms: cloudShader.uniforms, 136 | side: THREE.DoubleSide, 137 | transparent: true 138 | }); 139 | 140 | this.noiseMaterial = noiseMaterial; 141 | 142 | var scamera, sscene, smesh; 143 | 144 | scamera = new THREE.Camera(); 145 | 146 | scamera.position.z = 1; 147 | 148 | sscene = new THREE.Scene(); 149 | 150 | smesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), noiseMaterial ); 151 | 152 | sscene.add(smesh); 153 | 154 | function SkyDome(i, j) { 155 | i -= 0.5; 156 | j -= 0.5; 157 | 158 | var r2 = i * i * 4 + j * j * 4; 159 | var scale = 100000; 160 | 161 | return new THREE.Vector3( 162 | i * 20 * scale, 163 | (1 - r2) * 5 * scale, 164 | j * 20 * scale 165 | ).multiplyScalar(0.05); 166 | 167 | }; 168 | 169 | this.cloudMesh = new THREE.Mesh( 170 | new THREE.ParametricGeometry(SkyDome, 5, 5), 171 | noiseMaterial 172 | ); 173 | 174 | var performance = window.performance || Date; 175 | 176 | function update() { 177 | noiseMaterial.uniforms.time.value = performance.now() / 1000; 178 | } 179 | this.update = update; 180 | 181 | this.depthOnly = function(v) { 182 | noiseMaterial.uniforms.depth.value = v ? 1 : 0; 183 | }; 184 | 185 | } 186 | -------------------------------------------------------------------------------- /js/shaders/FFTOceanShader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Original work: 3 | * @author David Li / http://david.li/waves/ 4 | * 5 | * Three.js version: 6 | * @author Aleksandr Albert / http://www.routter.co.tt 7 | * 8 | * Modified: 9 | * @author jbouny / https://github.com/fft-ocean 10 | */ 11 | 12 | // Author: Aleksandr Albert 13 | // Website: www.routter.co.tt 14 | 15 | // Description: A deep water ocean shader set 16 | // based on an implementation of a Tessendorf Waves 17 | // originally presented by David Li ( www.david.li/waves ) 18 | 19 | // The general method is to apply shaders to simulation Framebuffers 20 | // and then sample these framebuffers when rendering the ocean mesh 21 | 22 | // The set uses 7 shaders: 23 | 24 | // -- Simulation shaders 25 | // [1] ocean_sim_vertex -> Vertex shader used to set up a 2x2 simulation plane centered at (0,0) 26 | // [2] ocean_subtransform -> Fragment shader used to subtransform the mesh (generates the displacement map) 27 | // [3] ocean_initial_spectrum -> Fragment shader used to set intitial wave frequency at a texel coordinate 28 | // [4] ocean_phase -> Fragment shader used to set wave phase at a texel coordinate 29 | // [5] ocean_spectrum -> Fragment shader used to set current wave frequency at a texel coordinate 30 | // [6] ocean_normal -> Fragment shader used to set face normals at a texel coordinate 31 | 32 | // -- Rendering Shader 33 | // [7] ocean_main -> Vertex and Fragment shader used to create the final render 34 | 35 | 36 | THREE.ShaderLib['ocean_sim_vertex'] = { 37 | varying: { 38 | "vUV": { type: "v2" } 39 | }, 40 | vertexShader: [ 41 | 'varying vec2 vUV;', 42 | 43 | 'void main (void) {', 44 | 'vUV = position.xy * 0.5 + 0.5;', 45 | 'gl_Position = vec4(position, 1.0 );', 46 | '}' 47 | ].join('\n') 48 | }; 49 | THREE.ShaderLib['ocean_subtransform'] = { 50 | uniforms: { 51 | "u_input": { type: "t", value: null }, 52 | "u_transformSize": { type: "f", value: 512.0 }, 53 | "u_subtransformSize": { type: "f", value: 250.0 } 54 | }, 55 | varying: { 56 | "vUV": { type: "v2" } 57 | }, 58 | fragmentShader: [ 59 | //GPU FFT using a Stockham formulation 60 | 61 | 'const float PI = 3.14159265359;', 62 | 63 | 'uniform sampler2D u_input;', 64 | 'uniform float u_transformSize;', 65 | 'uniform float u_subtransformSize;', 66 | 67 | 'varying vec2 vUV;', 68 | 69 | 'vec2 multiplyComplex (vec2 a, vec2 b) {', 70 | 'return vec2(a[0] * b[0] - a[1] * b[1], a[1] * b[0] + a[0] * b[1]);', 71 | '}', 72 | 73 | 'void main (void) {', 74 | '#ifdef HORIZONTAL', 75 | 'float index = vUV.x * u_transformSize - 0.5;', 76 | '#else', 77 | 'float index = vUV.y * u_transformSize - 0.5;', 78 | '#endif', 79 | 80 | 'float evenIndex = floor(index / u_subtransformSize) * (u_subtransformSize * 0.5) + mod(index, u_subtransformSize * 0.5);', 81 | 82 | //transform two complex sequences simultaneously 83 | '#ifdef HORIZONTAL', 84 | 'vec4 even = texture2D(u_input, vec2(evenIndex + 0.5, gl_FragCoord.y) / u_transformSize).rgba;', 85 | 'vec4 odd = texture2D(u_input, vec2(evenIndex + u_transformSize * 0.5 + 0.5, gl_FragCoord.y) / u_transformSize).rgba;', 86 | '#else', 87 | 'vec4 even = texture2D(u_input, vec2(gl_FragCoord.x, evenIndex + 0.5) / u_transformSize).rgba;', 88 | 'vec4 odd = texture2D(u_input, vec2(gl_FragCoord.x, evenIndex + u_transformSize * 0.5 + 0.5) / u_transformSize).rgba;', 89 | '#endif', 90 | 91 | 'float twiddleArgument = -2.0 * PI * (index / u_subtransformSize);', 92 | 'vec2 twiddle = vec2(cos(twiddleArgument), sin(twiddleArgument));', 93 | 94 | 'vec2 outputA = even.xy + multiplyComplex(twiddle, odd.xy);', 95 | 'vec2 outputB = even.zw + multiplyComplex(twiddle, odd.zw);', 96 | 97 | 'gl_FragColor = vec4(outputA, outputB);', 98 | '}' 99 | ].join('\n') 100 | }; 101 | THREE.ShaderLib['ocean_initial_spectrum'] = { 102 | uniforms: { 103 | "u_wind": { type: "v2", value: new THREE.Vector2(10.0, 10.0) }, 104 | "u_resolution": { type: "f", value: 512.0 }, 105 | "u_size": { type: "f", value: 250.0 }, 106 | }, 107 | fragmentShader: [ 108 | 109 | 'const float PI = 3.14159265359;', 110 | 'const float G = 9.81;', 111 | 'const float KM = 370.0;', 112 | 'const float CM = 0.23;', 113 | 114 | 'uniform vec2 u_wind;', 115 | 'uniform float u_resolution;', 116 | 'uniform float u_size;', 117 | 118 | 'float square (float x) {', 119 | 'return x * x;', 120 | '}', 121 | 122 | 'float omega (float k) {', 123 | 'return sqrt(G * k * (1.0 + square(k / KM)));', 124 | '}', 125 | 126 | 'float tanh (float x) {', 127 | 'return (1.0 - exp(-2.0 * x)) / (1.0 + exp(-2.0 * x));', 128 | '}', 129 | 130 | 'void main (void) {', 131 | 'vec2 coordinates = gl_FragCoord.xy - 0.5;', 132 | 133 | 'float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;', 134 | 'float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;', 135 | 136 | 'vec2 K = (2.0 * PI * vec2(n, m)) / u_size;', 137 | 'float k = length(K);', 138 | 139 | 'float l_wind = length(u_wind);', 140 | 141 | 'float Omega = 0.84;', 142 | 'float kp = G * square(Omega / l_wind);', 143 | 144 | 'float c = omega(k) / k;', 145 | 'float cp = omega(kp) / kp;', 146 | 147 | 'float Lpm = exp(-1.25 * square(kp / k));', 148 | 'float gamma = 1.7;', 149 | 'float sigma = 0.08 * (1.0 + 4.0 * pow(Omega, -3.0));', 150 | 'float Gamma = exp(-square(sqrt(k / kp) - 1.0) / 2.0 * square(sigma));', 151 | 'float Jp = pow(gamma, Gamma);', 152 | 'float Fp = Lpm * Jp * exp(-Omega / sqrt(10.0) * (sqrt(k / kp) - 1.0));', 153 | 'float alphap = 0.006 * sqrt(Omega);', 154 | 'float Bl = 0.5 * alphap * cp / c * Fp;', 155 | 156 | 'float z0 = 0.000037 * square(l_wind) / G * pow(l_wind / cp, 0.9);', 157 | 'float uStar = 0.41 * l_wind / log(10.0 / z0);', 158 | 'float alpham = 0.01 * ((uStar < CM) ? (1.0 + log(uStar / CM)) : (1.0 + 3.0 * log(uStar / CM)));', 159 | 'float Fm = exp(-0.25 * square(k / KM - 1.0));', 160 | 'float Bh = 0.5 * alpham * CM / c * Fm * Lpm;', 161 | 162 | 'float a0 = log(2.0) / 4.0;', 163 | 'float am = 0.13 * uStar / CM;', 164 | 'float Delta = tanh(a0 + 4.0 * pow(c / cp, 2.5) + am * pow(CM / c, 2.5));', 165 | 166 | 'float cosPhi = dot(normalize(u_wind), normalize(K));', 167 | 168 | 'float S = (1.0 / (2.0 * PI)) * pow(k, -4.0) * (Bl + Bh) * (1.0 + Delta * (2.0 * cosPhi * cosPhi - 1.0));', 169 | 170 | 'float dk = 2.0 * PI / u_size;', 171 | 'float h = sqrt(S / 2.0) * dk;', 172 | 173 | 'if (K.x == 0.0 && K.y == 0.0) {', 174 | 'h = 0.0;', //no DC term 175 | '}', 176 | 'gl_FragColor = vec4(h, 0.0, 0.0, 0.0);', 177 | '}' 178 | ].join('\n') 179 | }; 180 | THREE.ShaderLib['ocean_phase'] = { 181 | uniforms: { 182 | "u_phases": { type: "t", value: null }, 183 | "u_deltaTime": { type: "f", value: null }, 184 | "u_resolution": { type: "f", value: null }, 185 | "u_size": { type: "f", value: null }, 186 | }, 187 | varying: { 188 | "vUV": { type: "v2" } 189 | }, 190 | fragmentShader: [ 191 | 192 | 'const float PI = 3.14159265359;', 193 | 'const float G = 9.81;', 194 | 'const float KM = 370.0;', 195 | 196 | 'varying vec2 vUV;', 197 | 198 | 'uniform sampler2D u_phases;', 199 | 'uniform float u_deltaTime;', 200 | 'uniform float u_resolution;', 201 | 'uniform float u_size;', 202 | 203 | 'float omega (float k) {', 204 | 'return sqrt(G * k * (1.0 + k * k / KM * KM));', 205 | '}', 206 | 207 | 'void main (void) {', 208 | 'vec2 coordinates = gl_FragCoord.xy - 0.5;', 209 | 'float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;', 210 | 'float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;', 211 | 'vec2 waveVector = (2.0 * PI * vec2(n, m)) / u_size;', 212 | 213 | 'float phase = texture2D(u_phases, vUV).r;', 214 | 'float deltaPhase = omega(length(waveVector)) * u_deltaTime;', 215 | 'phase = mod(phase + deltaPhase, 2.0 * PI);', 216 | 217 | 'gl_FragColor = vec4(phase, 0.0, 0.0, 0.0);', 218 | '}' 219 | ].join('\n') 220 | }; 221 | THREE.ShaderLib['ocean_spectrum'] = { 222 | uniforms: { 223 | "u_size": { type: "f", value: null }, 224 | "u_resolution": { type: "f", value: null }, 225 | "u_choppiness": { type: "f", value: null }, 226 | "u_phases": { type: "t", value: null }, 227 | "u_initialSpectrum": { type: "t", value: null }, 228 | }, 229 | varying: { 230 | "vUV": { type: "v2" } 231 | }, 232 | fragmentShader: [ 233 | 234 | 'const float PI = 3.14159265359;', 235 | 'const float G = 9.81;', 236 | 'const float KM = 370.0;', 237 | 238 | 'varying vec2 vUV;', 239 | 240 | 'uniform float u_size;', 241 | 'uniform float u_resolution;', 242 | 'uniform float u_choppiness;', 243 | 'uniform sampler2D u_phases;', 244 | 'uniform sampler2D u_initialSpectrum;', 245 | 246 | 'vec2 multiplyComplex (vec2 a, vec2 b) {', 247 | 'return vec2(a[0] * b[0] - a[1] * b[1], a[1] * b[0] + a[0] * b[1]);', 248 | '}', 249 | 250 | 'vec2 multiplyByI (vec2 z) {', 251 | 'return vec2(-z[1], z[0]);', 252 | '}', 253 | 254 | 'float omega (float k) {', 255 | 'return sqrt(G * k * (1.0 + k * k / KM * KM));', 256 | '}', 257 | 258 | 'void main (void) {', 259 | 'vec2 coordinates = gl_FragCoord.xy - 0.5;', 260 | 'float n = (coordinates.x < u_resolution * 0.5) ? coordinates.x : coordinates.x - u_resolution;', 261 | 'float m = (coordinates.y < u_resolution * 0.5) ? coordinates.y : coordinates.y - u_resolution;', 262 | 'vec2 waveVector = (2.0 * PI * vec2(n, m)) / u_size;', 263 | 264 | 'float phase = texture2D(u_phases, vUV).r;', 265 | 'vec2 phaseVector = vec2(cos(phase), sin(phase));', 266 | 267 | 'vec2 h0 = texture2D(u_initialSpectrum, vUV).rg;', 268 | 'vec2 h0Star = texture2D(u_initialSpectrum, vec2(1.0 - vUV + 1.0 / u_resolution)).rg;', 269 | 'h0Star.y *= -1.0;', 270 | 271 | 'vec2 h = multiplyComplex(h0, phaseVector) + multiplyComplex(h0Star, vec2(phaseVector.x, -phaseVector.y));', 272 | 273 | 'vec2 hX = -multiplyByI(h * (waveVector.x / length(waveVector))) * u_choppiness;', 274 | 'vec2 hZ = -multiplyByI(h * (waveVector.y / length(waveVector))) * u_choppiness;', 275 | 276 | //no DC term 277 | 'if (waveVector.x == 0.0 && waveVector.y == 0.0) {', 278 | 'h = vec2(0.0);', 279 | 'hX = vec2(0.0);', 280 | 'hZ = vec2(0.0);', 281 | '}', 282 | 283 | 'gl_FragColor = vec4(hX + multiplyByI(h), hZ);', 284 | '}' 285 | ].join('\n') 286 | }; 287 | THREE.ShaderLib['ocean_normals'] = { 288 | uniforms: { 289 | "u_displacementMap": { type: "t", value: null }, 290 | "u_resolution": { type: "f", value: null }, 291 | "u_size": { type: "f", value: null }, 292 | }, 293 | varying: { 294 | "vUV": { type: "v2" } 295 | }, 296 | fragmentShader: [ 297 | 298 | 'varying vec2 vUV;', 299 | 300 | 'uniform sampler2D u_displacementMap;', 301 | 'uniform float u_resolution;', 302 | 'uniform float u_size;', 303 | 304 | 'void main (void) {', 305 | 'float texel = 1.0 / u_resolution;', 306 | 'float texelSize = u_size / u_resolution;', 307 | 308 | 'vec3 center = texture2D(u_displacementMap, vUV).rgb;', 309 | 'vec3 right = vec3(texelSize, 0.0, 0.0) + texture2D(u_displacementMap, vUV + vec2(texel, 0.0)).rgb - center;', 310 | 'vec3 left = vec3(-texelSize, 0.0, 0.0) + texture2D(u_displacementMap, vUV + vec2(-texel, 0.0)).rgb - center;', 311 | 'vec3 top = vec3(0.0, 0.0, -texelSize) + texture2D(u_displacementMap, vUV + vec2(0.0, -texel)).rgb - center;', 312 | 'vec3 bottom = vec3(0.0, 0.0, texelSize) + texture2D(u_displacementMap, vUV + vec2(0.0, texel)).rgb - center;', 313 | 314 | 'vec3 topRight = cross(right, top);', 315 | 'vec3 topLeft = cross(top, left);', 316 | 'vec3 bottomLeft = cross(left, bottom);', 317 | 'vec3 bottomRight = cross(bottom, right);', 318 | 319 | 'gl_FragColor = vec4(normalize(topRight + topLeft + bottomLeft + bottomRight), 1.0);', 320 | '}' 321 | ].join('\n') 322 | }; 323 | 324 | THREE.UniformsLib[ "oceanfft" ] = { 325 | 326 | "u_displacementMap": { type: "t", value: null }, 327 | "u_reflection": { type: "t", value: null }, 328 | "u_normalMap": { type: "t", value: null }, 329 | "u_geometrySize": { type: "f", value: null }, 330 | "u_size": { type: "f", value: null }, 331 | "u_mirrorMatrix": { type: "m4", value: null }, 332 | "u_cameraPosition": { type: "v3", value: null }, 333 | "u_skyColor": { type: "v3", value: null }, 334 | "u_oceanColor": { type: "v3", value: null }, 335 | "u_sunDirection": { type: "v3", value: null }, 336 | "u_exposure": { type: "f", value: null }, 337 | 338 | }, 339 | 340 | THREE.ShaderChunk[ "oceanfft_pars_vertex" ] = [ 341 | 342 | 'uniform sampler2D u_displacementMap;', 343 | 'uniform float u_geometrySize;', 344 | 'uniform float u_size;', 345 | 346 | ].join('\n'); 347 | 348 | THREE.ShaderChunk[ "oceanfft_vertex" ] = [ 349 | 350 | 'vec3 displacement = texture2D( u_displacementMap, worldPosition.xz * 0.002 ).rgb * ( u_geometrySize / u_size );', 351 | 'vec4 oceanfftWorldPosition = worldPosition + vec4( displacement, 0.0 );', 352 | 353 | ].join('\n'); 354 | 355 | THREE.ShaderChunk[ "oceanfft_pars_fragment" ] = [ 356 | 357 | ].join('\n'); 358 | 359 | THREE.ShaderChunk[ "oceanfft_fragment" ] = [ 360 | 361 | ].join('\n'); 362 | -------------------------------------------------------------------------------- /js/shaders/OceanShader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author jbouny / https://github.com/fft-ocean 3 | */ 4 | 5 | 6 | THREE.ShaderLib['ocean_main'] = { 7 | uniforms: THREE.UniformsLib[ "oceanfft" ], 8 | 9 | vertexShader: [ 10 | 'precision highp float;', 11 | 12 | 'varying vec3 vWorldPosition;', 13 | 'varying vec4 vReflectCoordinates;', 14 | 15 | 'uniform mat4 u_mirrorMatrix;', 16 | 17 | THREE.ShaderChunk[ "screenplane_pars_vertex" ], 18 | THREE.ShaderChunk[ "oceanfft_pars_vertex" ], 19 | 20 | 'void main (void) {', 21 | THREE.ShaderChunk[ "screenplane_vertex" ], 22 | 23 | 'vec4 worldPosition = screenPlaneWorldPosition;', 24 | 25 | THREE.ShaderChunk[ "oceanfft_vertex" ], 26 | 27 | 'vWorldPosition = oceanfftWorldPosition.xyz;', 28 | 'vReflectCoordinates = u_mirrorMatrix * oceanfftWorldPosition;', 29 | 30 | 'gl_Position = projectionMatrix * viewMatrix * oceanfftWorldPosition;', 31 | '}' 32 | ].join('\n'), 33 | 34 | vertexShaderNoTexLookup: [ 35 | 'precision highp float;', 36 | 37 | 'varying vec3 vWorldPosition;', 38 | 'varying vec4 vReflectCoordinates;', 39 | 40 | 'uniform mat4 u_mirrorMatrix;', 41 | 42 | THREE.ShaderChunk[ "screenplane_pars_vertex" ], 43 | THREE.ShaderChunk[ "oceanfft_pars_vertex" ], 44 | 45 | 'void main (void) {', 46 | THREE.ShaderChunk[ "screenplane_vertex" ], 47 | 48 | 'vWorldPosition = screenPlaneWorldPosition.xyz;', 49 | 'vReflectCoordinates = u_mirrorMatrix * screenPlaneWorldPosition;', 50 | 51 | 'gl_Position = projectionMatrix * viewMatrix * screenPlaneWorldPosition;', 52 | '}' 53 | ].join('\n'), 54 | 55 | fragmentShader: [ 56 | 'varying vec3 vWorldPosition;', 57 | 'varying vec4 vReflectCoordinates;', 58 | 59 | 'uniform sampler2D u_reflection;', 60 | 'uniform sampler2D u_normalMap;', 61 | 'uniform vec3 u_oceanColor;', 62 | 'uniform vec3 u_sunDirection;', 63 | 'uniform float u_exposure;', 64 | 65 | 'vec3 hdr (vec3 color, float exposure) {', 66 | 'return 1.0 - exp(-color * exposure);', 67 | '}', 68 | 69 | THREE.ShaderChunk["screenplane_pars_fragment"], 70 | 71 | 'void main (void) {', 72 | 'vec3 normal = texture2D( u_normalMap, vWorldPosition.xz * 0.002 ).rgb;', 73 | 'vec3 view = normalize( vCamPosition - vWorldPosition );', 74 | 75 | // Compute the specular factor 76 | 'vec3 reflection = normalize( reflect( -u_sunDirection, normal ) );', 77 | 'float specularFactor = pow( max( 0.0, dot( view, reflection ) ), 500.0 ) * 20.0;', 78 | 79 | // Get reflection color 80 | 'vec3 distortion = 200.0 * normal * vec3( 1.0, 0.0, 0.1 );', 81 | 'vec3 reflectionColor = texture2DProj( u_reflection, vReflectCoordinates.xyz + distortion ).xyz;', 82 | 83 | // Smooth the normal following the distance 84 | 'float distanceRatio = min( 1.0, log( 1.0 / length( vCamPosition - vWorldPosition ) * 3000.0 + 1.0 ) );', 85 | 'distanceRatio *= distanceRatio;', 86 | 'distanceRatio = distanceRatio * 0.7 + 0.3;', 87 | //'distanceRatio = 1.0;', 88 | 'normal = ( distanceRatio * normal + vec3( 0.0, 1.0 - distanceRatio, 0.0 ) ) * 0.5;', 89 | 'normal /= length( normal );', 90 | 91 | // Compute the fresnel ratio 92 | 'float fresnel = pow( 1.0 - dot( normal, view ), 2.0 );', 93 | 94 | // Compute the sky reflection and the water color 95 | 'float skyFactor = ( fresnel + 0.2 ) * 10.0;', 96 | 'vec3 waterColor = ( 1.0 - fresnel ) * u_oceanColor;', 97 | 98 | // Compute the final color 99 | 'vec3 color = ( skyFactor + specularFactor + waterColor ) * reflectionColor + waterColor * 0.5 ;', 100 | 'color = hdr( color, u_exposure );', 101 | 102 | 'gl_FragColor = vec4( color, 1.0 );', 103 | '}' 104 | ].join('\n') 105 | }; -------------------------------------------------------------------------------- /js/shaders/RainShader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author jbouny / https://github.com/fft-ocean 3 | */ 4 | 5 | THREE.ShaderLib['rain'] = { 6 | 7 | uniforms: { 8 | texture: { type: 't', value: null } 9 | }, 10 | 11 | vertexShader: [ 12 | 'attribute vec3 color;', 13 | 14 | 'varying vec3 vColor;', 15 | 16 | 'void main() {', 17 | ' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );', 18 | 19 | ' vColor = color;', 20 | 21 | ' gl_PointSize = 50.0 * abs( modelViewMatrix[1].y );', 22 | ' gl_Position = projectionMatrix * mvPosition;', 23 | '}' 24 | ].join( '\n' ), 25 | 26 | fragmentShader: [ 27 | 'uniform sampler2D texture;', 28 | 29 | 'varying vec3 vColor;', 30 | 31 | 'void main() {', 32 | ' vec4 startColor = vec4( vColor, 1.0 );', 33 | ' vec4 finalColor;', 34 | ' gl_FragColor = texture2D( texture, gl_PointCoord );', 35 | '}' 36 | ].join( '\n' ) 37 | 38 | }; -------------------------------------------------------------------------------- /js/shaders/ScreenSpaceShader.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author jbouny / https://github.com/fft-ocean 3 | */ 4 | 5 | THREE.ShaderChunk["screenplane_pars_vertex"] = [ 6 | 7 | 'const float infinite = 150000.0;', 8 | 'const float screenScale = 1.2;', 9 | 'const vec3 groundNormal = vec3( 0.0, 1.0, 0.0 );', 10 | 'const float groundHeight = 0.0;', 11 | 12 | 'varying vec3 vCamPosition;', 13 | 14 | 'vec3 interceptPlane( in vec3 source, in vec3 dir, in vec3 normal, float height )', 15 | '{', 16 | // Compute the distance between the source and the surface, following a ray, then return the intersection 17 | // http://www.cs.rpi.edu/~cutler/classes/advancedgraphics/S09/lectures/11_ray_tracing.pdf 18 | ' float distance = ( - height - dot( normal, source ) ) / dot( normal, dir );', 19 | ' if( distance < 0.0 )', 20 | ' return source + dir * distance;', 21 | ' else ', 22 | ' return - ( vec3( source.x, height, source.z ) + vec3( dir.x, height, dir.z ) * infinite );', 23 | '}', 24 | 25 | 'mat3 getRotation()', 26 | '{', 27 | // Extract the 3x3 rotation matrix from the 4x4 view matrix 28 | ' return mat3( ', 29 | ' viewMatrix[0].xyz,', 30 | ' viewMatrix[1].xyz,', 31 | ' viewMatrix[2].xyz', 32 | ' );', 33 | '}', 34 | 35 | 'vec3 getCameraPos( in mat3 rotation )', 36 | '{', 37 | // Xc = R * Xw + t 38 | // c = - R.t() * t <=> c = - t.t() * R 39 | ' return - viewMatrix[3].xyz * rotation;', 40 | '}', 41 | 42 | 'vec2 getImagePlan()', 43 | '{', 44 | // Extracting aspect and focal from projection matrix: 45 | // P = | e 0 0 0 | 46 | // | 0 e/(h/w) 0 0 | 47 | // | 0 0 . . | 48 | // | 0 0 -1 0 | 49 | ' float focal = projectionMatrix[0].x;', 50 | ' float aspect = projectionMatrix[1].y;', 51 | 52 | // Fix coordinate aspect and scale 53 | ' return vec2( ( uv.x - 0.5 ) * screenScale * aspect, ( uv.y - 0.5 ) * screenScale * focal );', 54 | '}', 55 | 56 | 'vec3 getCamRay( in mat3 rotation, in vec2 screenUV )', 57 | '{', 58 | // Compute camera ray then rotate it in order to get it in world coordinate 59 | ' return vec3( screenUV.x, screenUV.y, projectionMatrix[0].x ) * rotation;', 60 | '}', 61 | 62 | 'vec3 computeProjectedPosition()', 63 | '{', 64 | // Extract camera position and rotation from the model view matrix 65 | ' mat3 cameraRotation = getRotation();', 66 | ' vec3 camPosition = getCameraPos( cameraRotation );', 67 | ' vCamPosition = camPosition;', 68 | 69 | // Return the intersection between the camera ray and a given plane 70 | ' if( camPosition.y < groundHeight )', 71 | ' return vec3( 0.0, 0.0, 0.0 );', 72 | 73 | // Extract coordinate of the vertex on the image plan 74 | ' vec2 screenUV = getImagePlan() ;', 75 | 76 | // Compute the ray from camera to world 77 | ' vec3 ray = getCamRay( cameraRotation, screenUV );', 78 | 79 | ' vec3 finalPos = interceptPlane( camPosition, ray, groundNormal, groundHeight );', 80 | 81 | ' float distance = length( finalPos );', 82 | ' if( distance > infinite )', 83 | ' finalPos *= infinite / distance;', 84 | 85 | ' return finalPos;', 86 | '}' 87 | 88 | ].join('\n'); 89 | 90 | THREE.ShaderChunk["screenplane_vertex"] = [ 91 | 'vec4 screenPlaneWorldPosition = vec4( computeProjectedPosition(), 1.0 );', 92 | ].join('\n'); 93 | 94 | THREE.ShaderChunk["screenplane_pars_fragment"] = [ 95 | 'varying vec3 vCamPosition;' 96 | ].join('\n'); -------------------------------------------------------------------------------- /js/shaders/sky4.js: -------------------------------------------------------------------------------- 1 | /* 2 | Based on "A Practical Analytic Model for Daylight" 3 | aka The Preetham Model, the de facto standard analytic skydome model 4 | http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf 5 | 6 | First implemented by Simon Wallner http://www.simonwallner.at/projects/atmospheric-scattering 7 | 8 | Improved by Martin Upitis http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR 9 | 10 | Three.js integration by zz85 http://twitter.com/blurspline 11 | Refactored by @jbouny 12 | 13 | */ 14 | 15 | 'uniform sampler2D skySampler;', 16 | 'uniform vec3 sunPosition;', 17 | 'varying vec3 vWorldPosition;', 18 | 'varying vec2 vUv;', 19 | 20 | 'vec3 cameraPos = vec3(0., 0., 0.);', 21 | 22 | 'uniform float luminance;', 23 | 'uniform float turbidity;', 24 | 'uniform float reileigh;', 25 | 'uniform float mieCoefficient;', 26 | 'uniform float mieDirectionalG;', 27 | 28 | 29 | 'vec3 sunDirection = normalize(sunPosition);', 30 | 'float reileighCoefficient = reileigh;', 31 | 32 | // constants for atmospheric scattering 33 | 'const float e = 2.71828182845904523536028747135266249775724709369995957;', 34 | 'const float pi = 3.141592653589793238462643383279502884197169;', 35 | 36 | 'const float n = 1.0003;', // refractive index of air 37 | 'const float N = 2.545E25;', // number of molecules per unit volume for air at 38 | // 288.15K and 1013mb (sea level -45 celsius) 39 | 'const float pn = 0.035;', // depolatization factor for standard air 40 | 41 | // wavelength of used primaries, according to preetham 42 | 'const vec3 lambda = vec3(680E-9, 550E-9, 450E-9);', 43 | 44 | // mie stuff 45 | // K coefficient for the primaries 46 | 'const vec3 K = vec3(0.686, 0.678, 0.666);', 47 | 'const float v = 4.0;', 48 | 49 | // optical length at zenith for molecules 50 | 'const float rayleighZenithLength = 8.4E3;', 51 | 'const float mieZenithLength = 1.25E3;', 52 | 'const vec3 up = vec3(0.0, 1.0, 0.0);', 53 | 54 | 'const float EE = 1000.0;', 55 | 'const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;', 56 | // 66 arc seconds -> degrees, and the cosine of that 57 | // earth shadow hack 58 | 'const float cutoffAngle = pi/1.95;', 59 | 'const float steepness = 1.5;', 60 | 61 | 'vec3 totalRayleigh(vec3 lambda)', 62 | '{', 63 | ' 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));', 64 | '}', 65 | 66 | 'float rayleighPhase(float cosTheta)', 67 | '{ ', 68 | ' return (3.0 / (16.0*pi)) * (1.0 + pow(cosTheta, 2.0));', 69 | // return (1.0 / (3.0*pi)) * (1.0 + pow(cosTheta, 2.0)); 70 | // return (3.0 / 4.0) * (1.0 + pow(cosTheta, 2.0)); 71 | '}', 72 | 73 | 'vec3 totalMie(vec3 lambda, vec3 K, float T)', 74 | '{', 75 | ' float c = (0.2 * T ) * 10E-18;', 76 | ' return 0.434 * c * pi * pow((2.0 * pi) / lambda, vec3(v - 2.0)) * K;', 77 | '}', 78 | 79 | 'float hgPhase(float cosTheta, float g)', 80 | '{', 81 | ' 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));', 82 | '}', 83 | 84 | 'float sunIntensity(float zenithAngleCos)', 85 | '{', 86 | ' return EE * max(0.0, 1.0 - exp(-((cutoffAngle - acos(zenithAngleCos))/steepness)));', 87 | '}', 88 | 89 | // float logLuminance(vec3 c) 90 | // { 91 | // return log(c.r * 0.2126 + c.g * 0.7152 + c.b * 0.0722); 92 | // } 93 | 94 | // Filmic ToneMapping http://filmicgames.com/archives/75 95 | 'float A = 0.15;', 96 | 'float B = 0.50;', 97 | 'float C = 0.10;', 98 | 'float D = 0.20;', 99 | 'float E = 0.02;', 100 | 'float F = 0.30;', 101 | 'float W = 1000.0;', 102 | 103 | 'vec3 Uncharted2Tonemap(vec3 x)', 104 | '{', 105 | ' return ((x*(A*x+C*B)+D*E)/(x*(A*x+B)+D*F))-E/F;', 106 | '}', 107 | 108 | 'void main() ', 109 | '{', 110 | ' float sunfade = 1.0-clamp(1.0-exp((sunPosition.y/450000.0)),0.0,1.0);', 111 | 112 | // luminance = 1.0 ;// vWorldPosition.y / 450000. + 0.5; //sunPosition.y / 450000. * 1. + 0.5; 113 | 114 | // gl_FragColor = vec4(sunfade, sunfade, sunfade, 1.0); 115 | ' ', 116 | ' reileighCoefficient = reileighCoefficient - (1.0* (1.0-sunfade));', 117 | ' ', 118 | ' float sunE = sunIntensity(dot(sunDirection, up));', 119 | 120 | // extinction (absorbtion + out scattering) 121 | // rayleigh coefficients 122 | ' vec3 betaR = totalRayleigh(lambda) * reileighCoefficient;', 123 | 124 | // mie coefficients 125 | ' vec3 betaM = totalMie(lambda, K, turbidity) * mieCoefficient;', 126 | 127 | // optical length' 128 | // cutoff angle at 90 to avoid singularity in next formula. 129 | ' float zenithAngle = acos(max(0.0, dot(up, normalize(vWorldPosition - cameraPos))));', 130 | ' float sR = rayleighZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));', 131 | ' float sM = mieZenithLength / (cos(zenithAngle) + 0.15 * pow(93.885 - ((zenithAngle * 180.0) / pi), -1.253));', 132 | 133 | // combined extinction factor 134 | ' vec3 Fex = exp(-(betaR * sR + betaM * sM));', 135 | 136 | // in scattering 137 | ' float cosTheta = dot(normalize(vWorldPosition - cameraPos), sunDirection);', 138 | 139 | ' float rPhase = rayleighPhase(cosTheta*0.5+0.5);', 140 | ' vec3 betaRTheta = betaR * rPhase;', 141 | 142 | ' float mPhase = hgPhase(cosTheta, mieDirectionalG);', 143 | ' vec3 betaMTheta = betaM * mPhase;', 144 | 145 | ' vec3 Lin = pow(sunE * ((betaRTheta + betaMTheta) / (betaR + betaM)) * (1.0 - Fex),vec3(1.5));', 146 | ' 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));', 147 | 148 | //nightsky 149 | ' vec3 direction = normalize(vWorldPosition - cameraPos);', 150 | ' float theta = acos(direction.y); // elevation --> y-axis, [-pi/2, pi/2]', 151 | ' float phi = atan(direction.z, direction.x); // azimuth --> x-axis [-pi/2, pi/2]', 152 | ' vec2 uv = vec2(phi, theta) / vec2(2.0*pi, pi) + vec2(0.5, 0.0);', 153 | // vec3 L0 = texture2D(skySampler, uv).rgb+0.1 * Fex; 154 | ' vec3 L0 = vec3(0.1) * Fex;', 155 | 156 | // composition + solar disc 157 | //if (cosTheta > sunAngularDiameterCos) 158 | ' float sundisk = smoothstep(sunAngularDiameterCos,sunAngularDiameterCos+0.00002,cosTheta);', 159 | // if (normalize(vWorldPosition - cameraPos).y>0.0) 160 | ' L0 += (sunE * 19000.0 * Fex)*sundisk;', 161 | 162 | ' vec3 whiteScale = 1.0/Uncharted2Tonemap(vec3(W));', 163 | 164 | ' vec3 texColor = (Lin+L0); ', 165 | ' texColor *= 0.04 ;', 166 | ' texColor += vec3(0.0,0.001,0.0025)*0.3;', 167 | 168 | ' float g_fMaxLuminance = 1.0;', 169 | ' float fLumScaled = 0.1 / luminance; ', 170 | ' float fLumCompressed = (fLumScaled * (1.0 + (fLumScaled / (g_fMaxLuminance * g_fMaxLuminance)))) / (1.0 + fLumScaled); ', 171 | 172 | ' float ExposureBias = fLumCompressed;', 173 | 174 | ' vec3 curr = Uncharted2Tonemap((log2(2.0/pow(luminance,4.0)))*texColor);', 175 | ' vec3 color = curr*whiteScale;', 176 | 177 | ' vec3 retColor = pow(color,vec3(1.0/(1.2+(1.2*sunfade))));', 178 | 179 | 180 | ' gl_FragColor.rgb = retColor;', 181 | ' ', 182 | ' gl_FragColor.a = 1.0;', 183 | '}' -------------------------------------------------------------------------------- /models/BlackPearl/BlackPearl.mtl: -------------------------------------------------------------------------------- 1 | # 2 | # Wavefront material file 3 | # Converted by Meshlab Group 4 | # 5 | 6 | newmtl material_0 7 | Ka 0.200000 0.200000 0.200000 8 | Kd 0.75 0.75 0.75 9 | Ks 1.000000 1.000000 1.000000 10 | Tr 0.000000 11 | illum 2 12 | Ns 0.000000 13 | map_Kd .\flag.jpg 14 | 15 | newmtl material_1 16 | Ka 0.200000 0.200000 0.200000 17 | Kd 0.75 0.75 0.75 18 | Ks 0.41 0.37 0.34 19 | Tr 0.000000 20 | illum 2 21 | Ns 0.000000 22 | map_Kd .\dark_teak.jpg 23 | 24 | newmtl material_2 25 | Ka 0.200000 0.200000 0.200000 26 | Kd 0.75 0.75 0.75 27 | Ks 0.41 0.37 0.34 28 | Tr 0.000000 29 | illum 2 30 | Ns 0.000000 31 | map_Kd .\dark_teak.jpg 32 | 33 | newmtl material_3 34 | Ka 0.200000 0.200000 0.200000 35 | Kd 0.75 0.75 0.75 36 | Ks 1.000000 1.000000 1.000000 37 | Tr 0.000000 38 | illum 2 39 | Ns 0.000000 40 | map_Kd .\dark_wood_planks.jpg 41 | 42 | newmtl material_4 43 | Ka 0.200000 0.200000 0.200000 44 | Kd 0.5 0.5 0.5 45 | Ks 0.2 0.2 0.2 46 | Tr 0.000000 47 | illum 2 48 | Ns 0.000000 49 | 50 | newmtl material_5 51 | Ka 0.200000 0.200000 0.200000 52 | Kd 0.75 0.75 0.75 53 | Ks 0.41 0.37 0.34 54 | Tr 0.000000 55 | illum 2 56 | Ns 0.000000 57 | map_Kd .\Wood_floor_ffs062.jpg 58 | 59 | newmtl material_6 60 | Ka 0.200000 0.200000 0.200000 61 | Kd 0.75 0.75 0.75 62 | Ks 1.000000 1.000000 1.000000 63 | Tr 0.000000 64 | illum 2 65 | Ns 0.000000 66 | map_Kd .\Map__5_Tiles.jpg 67 | 68 | newmtl material_7 69 | Ka 0.200000 0.200000 0.200000 70 | Kd 0.3 0.25 0.22 71 | Ks 0.3 0.25 0.22 72 | Tr 0.000000 73 | illum 2 74 | Ns 0.000000 75 | 76 | newmtl material_8 77 | Ka 0.200000 0.200000 0.200000 78 | Kd 0.75 0.75 0.75 79 | Ks 0.8 0.76 0.74 80 | Tr 0.000000 81 | illum 2 82 | Ns 0.000000 83 | map_Kd .\sails1.jpg 84 | 85 | -------------------------------------------------------------------------------- /models/BlackPearl/Map__5_Tiles.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/models/BlackPearl/Map__5_Tiles.jpg -------------------------------------------------------------------------------- /models/BlackPearl/Map__5_Tiles.tga: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/models/BlackPearl/Map__5_Tiles.tga -------------------------------------------------------------------------------- /models/BlackPearl/Wood_floor_ffs062.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/models/BlackPearl/Wood_floor_ffs062.jpg -------------------------------------------------------------------------------- /models/BlackPearl/dark_teak.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/models/BlackPearl/dark_teak.jpg -------------------------------------------------------------------------------- /models/BlackPearl/dark_wood_planks.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/models/BlackPearl/dark_wood_planks.jpg -------------------------------------------------------------------------------- /models/BlackPearl/flag.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/models/BlackPearl/flag.jpg -------------------------------------------------------------------------------- /models/BlackPearl/sails1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/models/BlackPearl/sails1.jpg -------------------------------------------------------------------------------- /sound/rain.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/sound/rain.mp3 -------------------------------------------------------------------------------- /sound/waves.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/sound/waves.mp3 -------------------------------------------------------------------------------- /visual/day_ocean_fft.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/visual/day_ocean_fft.jpg -------------------------------------------------------------------------------- /visual/fft-ocean_night.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/visual/fft-ocean_night.jpg -------------------------------------------------------------------------------- /visual/fft-ocean_night_far.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/visual/fft-ocean_night_far.jpg -------------------------------------------------------------------------------- /visual/night_ocean_fft.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/visual/night_ocean_fft.jpg -------------------------------------------------------------------------------- /visual/screen_space_256.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/visual/screen_space_256.jpg -------------------------------------------------------------------------------- /visual/screen_space_64.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/visual/screen_space_64.jpg -------------------------------------------------------------------------------- /visual/screen_space_full.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/visual/screen_space_full.jpg -------------------------------------------------------------------------------- /visual/sunset_ocean_fft.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jbouny/fft-ocean/930c09676a7a86dd9b811a69907987704c55de96/visual/sunset_ocean_fft.jpg --------------------------------------------------------------------------------