├── 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 | 
15 |
16 | 
17 |
18 | 
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 | 
42 |
43 | With a 256*256 grid:
44 |
45 | 
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 |
61 | Night
62 | Morning
63 | Day
64 | Cloudy
65 | Sunset
66 | Interstellar
67 | Apocalypse
68 |
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'},{}]},{},{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
--------------------------------------------------------------------------------