├── .gitignore
├── README.md
├── images
├── IMG_9016.jpg
├── bg-1.png
├── bg-2.png
├── waves-1.png
└── waves-2.png
├── index.html
├── js
├── C4DCurveLoader.js
├── OrbitControls.js
├── VRControls.js
├── VREffect.js
├── sechelt.js
├── three.min.js
├── tween.min.js
└── webvr-polyfill.js
├── models
├── flightpath-nov4-bezier.txt
└── scene-nov4.json
└── sounds
├── 235428__allanz10d__calm-ocean-breeze-simulation.ogg
├── 23707__hazure__seagull.ogg
└── 78389__inchadney__seagulls.ogg
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | npm-debug.log
3 | node_modules/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Sechelt
2 |
3 | An animated fly through of an area near Sechelt, British Columbia, Canada. Built with Cinema 4D and Three.js. Best experienced with a virtual-reality head-mounted display and a VR-enabled build of Firefox from [__http://mozvr.com/downloads/__](http://mozvr.com/downloads/).
4 |
5 | View here: [__http://mozvr.github.io/sechelt/__](http://mozvr.github.io/sechelt/)
6 |
--------------------------------------------------------------------------------
/images/IMG_9016.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdoob/sechelt/256f986f261295cc7967e65658d833cfa4f11809/images/IMG_9016.jpg
--------------------------------------------------------------------------------
/images/bg-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdoob/sechelt/256f986f261295cc7967e65658d833cfa4f11809/images/bg-1.png
--------------------------------------------------------------------------------
/images/bg-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdoob/sechelt/256f986f261295cc7967e65658d833cfa4f11809/images/bg-2.png
--------------------------------------------------------------------------------
/images/waves-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdoob/sechelt/256f986f261295cc7967e65658d833cfa4f11809/images/waves-1.png
--------------------------------------------------------------------------------
/images/waves-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdoob/sechelt/256f986f261295cc7967e65658d833cfa4f11809/images/waves-2.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Sechelt
5 |
6 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/js/C4DCurveLoader.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author mrdoob / http://mrdoob.com/
3 | */
4 |
5 | THREE.C4DCurveLoader = function ( manager ) {
6 |
7 | this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
8 |
9 | };
10 |
11 | THREE.C4DCurveLoader.prototype = {
12 |
13 | constructor: THREE.C4DCurveLoader,
14 |
15 | load: function ( url, onLoad, onProgress, onError ) {
16 |
17 | var scope = this;
18 |
19 | var loader = new THREE.XHRLoader( scope.manager );
20 | loader.setCrossOrigin( this.crossOrigin );
21 | loader.load( url, function ( text ) {
22 |
23 | onLoad( scope.parse( text ) );
24 |
25 | } );
26 |
27 | },
28 |
29 | parse: function ( text ) {
30 |
31 | var LinearCurve = function ( points ) {
32 |
33 | // [ point1, point2, point3, point4, ... ]
34 |
35 | var point1 = new THREE.Vector3();
36 | var point2 = new THREE.Vector3();
37 |
38 | return {
39 |
40 | getPointAt: function ( t ) {
41 |
42 | if ( t <= 0 ) return points[ 0 ].clone();
43 | if ( t >= 1 ) return points[ points.length - 1 ].clone();
44 |
45 | var key = t * ( points.length - 1 );
46 | var keyFloor = Math.floor( key );
47 |
48 | var weight = key - keyFloor;
49 |
50 | point1.copy( points[ keyFloor + 0 ] );
51 | point2.copy( points[ keyFloor + 1 ] );
52 |
53 | return new THREE.Vector3().copy( point1 ).lerp( point2, weight );
54 |
55 | },
56 |
57 | getPoints: function () {
58 |
59 | return points;
60 |
61 | }
62 |
63 | }
64 |
65 | };
66 |
67 | var BezierCurve = function ( points ) {
68 |
69 | // [ point1, anchor1b, anchor2a, point2, anchor2b, anchor3a, point3, ... ]
70 |
71 | var point1 = new THREE.Vector3();
72 | var point2 = new THREE.Vector3();
73 | var point3 = new THREE.Vector3();
74 | var point4 = new THREE.Vector3();
75 |
76 | var B1 = function ( t ) { return t * t * t };
77 | var B2 = function ( t ) { return 3 * t * t * ( 1 - t ) };
78 | var B3 = function ( t ) { return 3 * t * ( 1 - t ) * ( 1 - t ) };
79 | var B4 = function ( t ) { return ( 1 - t ) * ( 1 - t ) * ( 1 - t ) };
80 |
81 | return {
82 |
83 | getPointAt: function ( t ) {
84 |
85 | if ( t <= 0 ) return points[ 0 ].clone();
86 | if ( t >= 1 ) return points[ points.length - 1 ].clone();
87 |
88 | var key = t * Math.floor( points.length / 4 );
89 | var keyFloor = Math.floor( key );
90 |
91 | var weight = 1 - ( key - keyFloor );
92 |
93 | point1.copy( points[ keyFloor * 4 + 0 ] ).multiplyScalar( B1( weight ) );
94 | point2.copy( points[ keyFloor * 4 + 1 ] ).multiplyScalar( B2( weight ) );
95 | point3.copy( points[ keyFloor * 4 + 2 ] ).multiplyScalar( B3( weight ) );
96 | point4.copy( points[ keyFloor * 4 + 3 ] ).multiplyScalar( B4( weight ) );
97 |
98 | return new THREE.Vector3().add( point1 ).add( point2 ).add( point3 ).add( point4 );
99 |
100 | },
101 |
102 | getPoints: function () {
103 |
104 | return points;
105 |
106 | },
107 |
108 | toLinearCurve: function ( separation ) {
109 |
110 | var prevPoint = this.getPointAt( 0 );
111 | var newPoints = [ prevPoint ];
112 | var distance = 0;
113 |
114 | for ( var i = 1; i < 10000; i ++ ) {
115 |
116 | var point = this.getPointAt( i / 10000 );
117 | distance += prevPoint.distanceTo( point );
118 |
119 | if ( distance > separation ) {
120 |
121 | newPoints.push( point );
122 | distance = 0;
123 |
124 | }
125 |
126 | prevPoint = point;
127 |
128 | }
129 |
130 | /*
131 | var pointsAmount = points.length / 4;
132 | var segmentSize = 1 / pointsAmount;
133 |
134 | for ( var i = 0, il = points.length; i < il; i += 4 ) {
135 |
136 | distance = 0;
137 |
138 | distance += points[ i + 0 ].distanceTo( points[ i + 1 ] );
139 | distance += points[ i + 1 ].distanceTo( points[ i + 2 ] );
140 | distance += points[ i + 2 ].distanceTo( points[ i + 3 ] );
141 |
142 | var segment = i / il;
143 |
144 | for ( var j = 0, jl = distance / separation; j < jl; j ++ ) {
145 |
146 | var point = this.getPointAt( segment + ( j / jl ) * segmentSize );
147 | array.push( point );
148 |
149 | }
150 |
151 | }
152 | */
153 |
154 | return new LinearCurve( newPoints );
155 |
156 | }
157 |
158 | }
159 |
160 | };
161 |
162 | var lines = text.split( '\r' );
163 |
164 | if ( /Point\tX\tY\tZ\t<- X\t<- Y\t<- Z\tX ->\tY ->\tZ ->/.test( lines[ 0 ] ) ) {
165 |
166 | var points = [];
167 |
168 | for ( var i = 1, l = lines.length - 1; i < l; i ++ ) {
169 |
170 | var parts = lines[ i ].split( '\t' );
171 |
172 | if ( i === 1 ) {
173 |
174 | // first point
175 |
176 | points.push(
177 |
178 | new THREE.Vector3(
179 | parseFloat( parts[ 1 ] ),
180 | parseFloat( parts[ 2 ] ),
181 | parseFloat( parts[ 3 ] )
182 | ),
183 |
184 | new THREE.Vector3(
185 | parseFloat( parts[ 1 ] ) + parseFloat( parts[ 7 ] ),
186 | parseFloat( parts[ 2 ] ) + parseFloat( parts[ 8 ] ),
187 | parseFloat( parts[ 3 ] ) + parseFloat( parts[ 9 ] )
188 | )
189 |
190 | );
191 |
192 | } else if ( i === l - 1 ) {
193 |
194 | // last point
195 |
196 | points.push(
197 |
198 | new THREE.Vector3(
199 | parseFloat( parts[ 1 ] ) + parseFloat( parts[ 4 ] ),
200 | parseFloat( parts[ 2 ] ) + parseFloat( parts[ 5 ] ),
201 | parseFloat( parts[ 3 ] ) + parseFloat( parts[ 6 ] )
202 | ),
203 |
204 | new THREE.Vector3(
205 | parseFloat( parts[ 1 ] ),
206 | parseFloat( parts[ 2 ] ),
207 | parseFloat( parts[ 3 ] )
208 | )
209 |
210 | );
211 |
212 |
213 | } else {
214 |
215 | points.push(
216 |
217 | new THREE.Vector3(
218 | parseFloat( parts[ 1 ] ) + parseFloat( parts[ 4 ] ),
219 | parseFloat( parts[ 2 ] ) + parseFloat( parts[ 5 ] ),
220 | parseFloat( parts[ 3 ] ) + parseFloat( parts[ 6 ] )
221 | ),
222 |
223 | new THREE.Vector3(
224 | parseFloat( parts[ 1 ] ),
225 | parseFloat( parts[ 2 ] ),
226 | parseFloat( parts[ 3 ] )
227 | ),
228 |
229 | new THREE.Vector3(
230 | parseFloat( parts[ 1 ] ),
231 | parseFloat( parts[ 2 ] ),
232 | parseFloat( parts[ 3 ] )
233 | ),
234 |
235 | new THREE.Vector3(
236 | parseFloat( parts[ 1 ] ) + parseFloat( parts[ 7 ] ),
237 | parseFloat( parts[ 2 ] ) + parseFloat( parts[ 8 ] ),
238 | parseFloat( parts[ 3 ] ) + parseFloat( parts[ 9 ] )
239 | )
240 |
241 | );
242 |
243 | }
244 |
245 | }
246 |
247 | return new BezierCurve( points );
248 |
249 | } else if ( /Point\tX\tY\tZ/.test( lines[ 0 ] ) ) {
250 |
251 | var points = [];
252 |
253 | for ( var i = 1; i < lines.length - 1; i ++ ) {
254 |
255 | var parts = lines[ i ].split( '\t' );
256 |
257 | points.push(
258 | new THREE.Vector3(
259 | parseFloat( parts[ 1 ] ),
260 | parseFloat( parts[ 2 ] ),
261 | parseFloat( parts[ 3 ] )
262 | )
263 | );
264 |
265 | }
266 |
267 | return new LinearCurve( points );
268 |
269 | }
270 |
271 | }
272 |
273 | };
274 |
--------------------------------------------------------------------------------
/js/OrbitControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author qiao / https://github.com/qiao
3 | * @author mrdoob / http://mrdoob.com
4 | * @author alteredq / http://alteredqualia.com/
5 | * @author WestLangley / http://github.com/WestLangley
6 | * @author erich666 / http://erichaines.com
7 | */
8 | /*global THREE, console */
9 |
10 | ( function () {
11 |
12 | function OrbitConstraint ( object ) {
13 |
14 | this.object = object;
15 |
16 | // "target" sets the location of focus, where the object orbits around
17 | // and where it pans with respect to.
18 | this.target = new THREE.Vector3();
19 |
20 | // Limits to how far you can dolly in and out ( PerspectiveCamera only )
21 | this.minDistance = 0;
22 | this.maxDistance = Infinity;
23 |
24 | // Limits to how far you can zoom in and out ( OrthographicCamera only )
25 | this.minZoom = 0;
26 | this.maxZoom = Infinity;
27 |
28 | // How far you can orbit vertically, upper and lower limits.
29 | // Range is 0 to Math.PI radians.
30 | this.minPolarAngle = 0; // radians
31 | this.maxPolarAngle = Math.PI; // radians
32 |
33 | // How far you can orbit horizontally, upper and lower limits.
34 | // If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
35 | this.minAzimuthAngle = - Infinity; // radians
36 | this.maxAzimuthAngle = Infinity; // radians
37 |
38 | // Set to true to enable damping (inertia)
39 | // If damping is enabled, you must call controls.update() in your animation loop
40 | this.enableDamping = false;
41 | this.dampingFactor = 0.25;
42 |
43 | ////////////
44 | // internals
45 |
46 | var scope = this;
47 |
48 | var EPS = 0.000001;
49 |
50 | // Current position in spherical coordinate system.
51 | var theta;
52 | var phi;
53 |
54 | // Pending changes
55 | var phiDelta = 0;
56 | var thetaDelta = 0;
57 | var scale = 1;
58 | var panOffset = new THREE.Vector3();
59 | var zoomChanged = false;
60 |
61 | // API
62 |
63 | this.getPolarAngle = function () {
64 |
65 | return phi;
66 |
67 | };
68 |
69 | this.getAzimuthalAngle = function () {
70 |
71 | return theta;
72 |
73 | };
74 |
75 | this.rotateLeft = function ( angle ) {
76 |
77 | thetaDelta -= angle;
78 |
79 | };
80 |
81 | this.rotateUp = function ( angle ) {
82 |
83 | phiDelta -= angle;
84 |
85 | };
86 |
87 | // pass in distance in world space to move left
88 | this.panLeft = function() {
89 |
90 | var v = new THREE.Vector3();
91 |
92 | return function panLeft ( distance ) {
93 |
94 | var te = this.object.matrix.elements;
95 |
96 | // get X column of matrix
97 | v.set( te[ 0 ], te[ 1 ], te[ 2 ] );
98 | v.multiplyScalar( - distance );
99 |
100 | panOffset.add( v );
101 |
102 | };
103 |
104 | }();
105 |
106 | // pass in distance in world space to move up
107 | this.panUp = function() {
108 |
109 | var v = new THREE.Vector3();
110 |
111 | return function panUp ( distance ) {
112 |
113 | var te = this.object.matrix.elements;
114 |
115 | // get Y column of matrix
116 | v.set( te[ 4 ], te[ 5 ], te[ 6 ] );
117 | v.multiplyScalar( distance );
118 |
119 | panOffset.add( v );
120 |
121 | };
122 |
123 | }();
124 |
125 | // pass in x,y of change desired in pixel space,
126 | // right and down are positive
127 | this.pan = function ( deltaX, deltaY, screenWidth, screenHeight ) {
128 |
129 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
130 |
131 | // perspective
132 | var position = scope.object.position;
133 | var offset = position.clone().sub( scope.target );
134 | var targetDistance = offset.length();
135 |
136 | // half of the fov is center to top of screen
137 | targetDistance *= Math.tan( ( scope.object.fov / 2 ) * Math.PI / 180.0 );
138 |
139 | // we actually don't use screenWidth, since perspective camera is fixed to screen height
140 | scope.panLeft( 2 * deltaX * targetDistance / screenHeight );
141 | scope.panUp( 2 * deltaY * targetDistance / screenHeight );
142 |
143 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
144 |
145 | // orthographic
146 | scope.panLeft( deltaX * ( scope.object.right - scope.object.left ) / screenWidth );
147 | scope.panUp( deltaY * ( scope.object.top - scope.object.bottom ) / screenHeight );
148 |
149 | } else {
150 |
151 | // camera neither orthographic or perspective
152 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
153 |
154 | }
155 |
156 | };
157 |
158 | this.dollyIn = function ( dollyScale ) {
159 |
160 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
161 |
162 | scale /= dollyScale;
163 |
164 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
165 |
166 | scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom * dollyScale ) );
167 | scope.object.updateProjectionMatrix();
168 | zoomChanged = true;
169 |
170 | } else {
171 |
172 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
173 |
174 | }
175 |
176 | };
177 |
178 | this.dollyOut = function ( dollyScale ) {
179 |
180 | if ( scope.object instanceof THREE.PerspectiveCamera ) {
181 |
182 | scale *= dollyScale;
183 |
184 | } else if ( scope.object instanceof THREE.OrthographicCamera ) {
185 |
186 | scope.object.zoom = Math.max( this.minZoom, Math.min( this.maxZoom, this.object.zoom / dollyScale ) );
187 | scope.object.updateProjectionMatrix();
188 | zoomChanged = true;
189 |
190 | } else {
191 |
192 | console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - dolly/zoom disabled.' );
193 |
194 | }
195 |
196 | };
197 |
198 | this.update = function() {
199 |
200 | var offset = new THREE.Vector3();
201 |
202 | // so camera.up is the orbit axis
203 | var quat = new THREE.Quaternion().setFromUnitVectors( object.up, new THREE.Vector3( 0, 1, 0 ) );
204 | var quatInverse = quat.clone().inverse();
205 |
206 | var lastPosition = new THREE.Vector3();
207 | var lastQuaternion = new THREE.Quaternion();
208 |
209 | return function () {
210 |
211 | var position = this.object.position;
212 |
213 | offset.copy( position ).sub( this.target );
214 |
215 | // rotate offset to "y-axis-is-up" space
216 | offset.applyQuaternion( quat );
217 |
218 | // angle from z-axis around y-axis
219 |
220 | theta = Math.atan2( offset.x, offset.z );
221 |
222 | // angle from y-axis
223 |
224 | phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
225 |
226 | theta += thetaDelta;
227 | phi += phiDelta;
228 |
229 | // restrict theta to be between desired limits
230 | theta = Math.max( this.minAzimuthAngle, Math.min( this.maxAzimuthAngle, theta ) );
231 |
232 | // restrict phi to be between desired limits
233 | phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
234 |
235 | // restrict phi to be betwee EPS and PI-EPS
236 | phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
237 |
238 | var radius = offset.length() * scale;
239 |
240 | // restrict radius to be between desired limits
241 | radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
242 |
243 | // move target to panned location
244 | this.target.add( panOffset );
245 |
246 | offset.x = radius * Math.sin( phi ) * Math.sin( theta );
247 | offset.y = radius * Math.cos( phi );
248 | offset.z = radius * Math.sin( phi ) * Math.cos( theta );
249 |
250 | // rotate offset back to "camera-up-vector-is-up" space
251 | offset.applyQuaternion( quatInverse );
252 |
253 | position.copy( this.target ).add( offset );
254 |
255 | this.object.lookAt( this.target );
256 |
257 | if ( this.enableDamping === true ) {
258 |
259 | thetaDelta *= ( 1 - this.dampingFactor );
260 | phiDelta *= ( 1 - this.dampingFactor );
261 |
262 | } else {
263 |
264 | thetaDelta = 0;
265 | phiDelta = 0;
266 |
267 | }
268 |
269 | scale = 1;
270 | panOffset.set( 0, 0, 0 );
271 |
272 | // update condition is:
273 | // min(camera displacement, camera rotation in radians)^2 > EPS
274 | // using small-angle approximation cos(x/2) = 1 - x^2 / 8
275 |
276 | if ( zoomChanged ||
277 | lastPosition.distanceToSquared( this.object.position ) > EPS ||
278 | 8 * ( 1 - lastQuaternion.dot( this.object.quaternion ) ) > EPS ) {
279 |
280 | lastPosition.copy( this.object.position );
281 | lastQuaternion.copy( this.object.quaternion );
282 | zoomChanged = false;
283 |
284 | return true;
285 |
286 | }
287 |
288 | return false;
289 |
290 | };
291 |
292 | }();
293 |
294 | };
295 |
296 |
297 | // This set of controls performs orbiting, dollying (zooming), and panning. It maintains
298 | // the "up" direction as +Y, unlike the TrackballControls. Touch on tablet and phones is
299 | // supported.
300 | //
301 | // Orbit - left mouse / touch: one finger move
302 | // Zoom - middle mouse, or mousewheel / touch: two finger spread or squish
303 | // Pan - right mouse, or arrow keys / touch: three finter swipe
304 |
305 | THREE.OrbitControls = function ( object, domElement ) {
306 |
307 | var constraint = new OrbitConstraint( object );
308 |
309 | this.domElement = ( domElement !== undefined ) ? domElement : document;
310 |
311 | // API
312 |
313 | Object.defineProperty( this, 'constraint', {
314 |
315 | get: function() {
316 |
317 | return constraint;
318 |
319 | }
320 |
321 | } );
322 |
323 | this.getPolarAngle = function () {
324 |
325 | return constraint.getPolarAngle();
326 |
327 | };
328 |
329 | this.getAzimuthalAngle = function () {
330 |
331 | return constraint.getAzimuthalAngle();
332 |
333 | };
334 |
335 | // Set to false to disable this control
336 | this.enabled = true;
337 |
338 | // center is old, deprecated; use "target" instead
339 | this.center = this.target;
340 |
341 | // This option actually enables dollying in and out; left as "zoom" for
342 | // backwards compatibility.
343 | // Set to false to disable zooming
344 | this.enableZoom = true;
345 | this.zoomSpeed = 1.0;
346 |
347 | // Set to false to disable rotating
348 | this.enableRotate = true;
349 | this.rotateSpeed = 1.0;
350 |
351 | // Set to false to disable panning
352 | this.enablePan = true;
353 | this.keyPanSpeed = 7.0; // pixels moved per arrow key push
354 |
355 | // Set to true to automatically rotate around the target
356 | // If auto-rotate is enabled, you must call controls.update() in your animation loop
357 | this.autoRotate = false;
358 | this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
359 |
360 | // Set to false to disable use of the keys
361 | this.enableKeys = true;
362 |
363 | // The four arrow keys
364 | this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
365 |
366 | // Mouse buttons
367 | this.mouseButtons = { ORBIT: THREE.MOUSE.LEFT, ZOOM: THREE.MOUSE.MIDDLE, PAN: THREE.MOUSE.RIGHT };
368 |
369 | ////////////
370 | // internals
371 |
372 | var scope = this;
373 |
374 | var rotateStart = new THREE.Vector2();
375 | var rotateEnd = new THREE.Vector2();
376 | var rotateDelta = new THREE.Vector2();
377 |
378 | var panStart = new THREE.Vector2();
379 | var panEnd = new THREE.Vector2();
380 | var panDelta = new THREE.Vector2();
381 |
382 | var dollyStart = new THREE.Vector2();
383 | var dollyEnd = new THREE.Vector2();
384 | var dollyDelta = new THREE.Vector2();
385 |
386 | var STATE = { NONE : - 1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
387 |
388 | var state = STATE.NONE;
389 |
390 | // for reset
391 |
392 | this.target0 = this.target.clone();
393 | this.position0 = this.object.position.clone();
394 | this.zoom0 = this.object.zoom;
395 |
396 | // events
397 |
398 | var changeEvent = { type: 'change' };
399 | var startEvent = { type: 'start' };
400 | var endEvent = { type: 'end' };
401 |
402 | // pass in x,y of change desired in pixel space,
403 | // right and down are positive
404 | function pan( deltaX, deltaY ) {
405 |
406 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
407 |
408 | constraint.pan( deltaX, deltaY, element.clientWidth, element.clientHeight );
409 |
410 | }
411 |
412 | this.update = function () {
413 |
414 | if ( this.autoRotate && state === STATE.NONE ) {
415 |
416 | constraint.rotateLeft( getAutoRotationAngle() );
417 |
418 | }
419 |
420 | if ( constraint.update() === true ) {
421 |
422 | this.dispatchEvent( changeEvent );
423 |
424 | }
425 |
426 | };
427 |
428 | this.reset = function () {
429 |
430 | state = STATE.NONE;
431 |
432 | this.target.copy( this.target0 );
433 | this.object.position.copy( this.position0 );
434 | this.object.zoom = this.zoom0;
435 |
436 | this.object.updateProjectionMatrix();
437 | this.dispatchEvent( changeEvent );
438 |
439 | this.update();
440 |
441 | };
442 |
443 | function getAutoRotationAngle() {
444 |
445 | return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
446 |
447 | }
448 |
449 | function getZoomScale() {
450 |
451 | return Math.pow( 0.95, scope.zoomSpeed );
452 |
453 | }
454 |
455 | function onMouseDown( event ) {
456 |
457 | if ( scope.enabled === false ) return;
458 |
459 | event.preventDefault();
460 |
461 | if ( event.button === scope.mouseButtons.ORBIT ) {
462 |
463 | if ( scope.enableRotate === false ) return;
464 |
465 | state = STATE.ROTATE;
466 |
467 | rotateStart.set( event.clientX, event.clientY );
468 |
469 | } else if ( event.button === scope.mouseButtons.ZOOM ) {
470 |
471 | if ( scope.enableZoom === false ) return;
472 |
473 | state = STATE.DOLLY;
474 |
475 | dollyStart.set( event.clientX, event.clientY );
476 |
477 | } else if ( event.button === scope.mouseButtons.PAN ) {
478 |
479 | if ( scope.enablePan === false ) return;
480 |
481 | state = STATE.PAN;
482 |
483 | panStart.set( event.clientX, event.clientY );
484 |
485 | }
486 |
487 | if ( state !== STATE.NONE ) {
488 |
489 | document.addEventListener( 'mousemove', onMouseMove, false );
490 | document.addEventListener( 'mouseup', onMouseUp, false );
491 | scope.dispatchEvent( startEvent );
492 |
493 | }
494 |
495 | }
496 |
497 | function onMouseMove( event ) {
498 |
499 | if ( scope.enabled === false ) return;
500 |
501 | event.preventDefault();
502 |
503 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
504 |
505 | if ( state === STATE.ROTATE ) {
506 |
507 | if ( scope.enableRotate === false ) return;
508 |
509 | rotateEnd.set( event.clientX, event.clientY );
510 | rotateDelta.subVectors( rotateEnd, rotateStart );
511 |
512 | // rotating across whole screen goes 360 degrees around
513 | constraint.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
514 |
515 | // rotating up and down along whole screen attempts to go 360, but limited to 180
516 | constraint.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
517 |
518 | rotateStart.copy( rotateEnd );
519 |
520 | } else if ( state === STATE.DOLLY ) {
521 |
522 | if ( scope.enableZoom === false ) return;
523 |
524 | dollyEnd.set( event.clientX, event.clientY );
525 | dollyDelta.subVectors( dollyEnd, dollyStart );
526 |
527 | if ( dollyDelta.y > 0 ) {
528 |
529 | constraint.dollyIn( getZoomScale() );
530 |
531 | } else if ( dollyDelta.y < 0 ) {
532 |
533 | constraint.dollyOut( getZoomScale() );
534 |
535 | }
536 |
537 | dollyStart.copy( dollyEnd );
538 |
539 | } else if ( state === STATE.PAN ) {
540 |
541 | if ( scope.enablePan === false ) return;
542 |
543 | panEnd.set( event.clientX, event.clientY );
544 | panDelta.subVectors( panEnd, panStart );
545 |
546 | pan( panDelta.x, panDelta.y );
547 |
548 | panStart.copy( panEnd );
549 |
550 | }
551 |
552 | if ( state !== STATE.NONE ) scope.update();
553 |
554 | }
555 |
556 | function onMouseUp( /* event */ ) {
557 |
558 | if ( scope.enabled === false ) return;
559 |
560 | document.removeEventListener( 'mousemove', onMouseMove, false );
561 | document.removeEventListener( 'mouseup', onMouseUp, false );
562 | scope.dispatchEvent( endEvent );
563 | state = STATE.NONE;
564 |
565 | }
566 |
567 | function onMouseWheel( event ) {
568 |
569 | if ( scope.enabled === false || scope.enableZoom === false || state !== STATE.NONE ) return;
570 |
571 | event.preventDefault();
572 | event.stopPropagation();
573 |
574 | var delta = 0;
575 |
576 | if ( event.wheelDelta !== undefined ) {
577 |
578 | // WebKit / Opera / Explorer 9
579 |
580 | delta = event.wheelDelta;
581 |
582 | } else if ( event.detail !== undefined ) {
583 |
584 | // Firefox
585 |
586 | delta = - event.detail;
587 |
588 | }
589 |
590 | if ( delta > 0 ) {
591 |
592 | constraint.dollyOut( getZoomScale() );
593 |
594 | } else if ( delta < 0 ) {
595 |
596 | constraint.dollyIn( getZoomScale() );
597 |
598 | }
599 |
600 | scope.update();
601 | scope.dispatchEvent( startEvent );
602 | scope.dispatchEvent( endEvent );
603 |
604 | }
605 |
606 | function onKeyDown( event ) {
607 |
608 | if ( scope.enabled === false || scope.enableKeys === false || scope.enablePan === false ) return;
609 |
610 | switch ( event.keyCode ) {
611 |
612 | case scope.keys.UP:
613 | pan( 0, scope.keyPanSpeed );
614 | scope.update();
615 | break;
616 |
617 | case scope.keys.BOTTOM:
618 | pan( 0, - scope.keyPanSpeed );
619 | scope.update();
620 | break;
621 |
622 | case scope.keys.LEFT:
623 | pan( scope.keyPanSpeed, 0 );
624 | scope.update();
625 | break;
626 |
627 | case scope.keys.RIGHT:
628 | pan( - scope.keyPanSpeed, 0 );
629 | scope.update();
630 | break;
631 |
632 | }
633 |
634 | }
635 |
636 | function touchstart( event ) {
637 |
638 | if ( scope.enabled === false ) return;
639 |
640 | switch ( event.touches.length ) {
641 |
642 | case 1: // one-fingered touch: rotate
643 |
644 | if ( scope.enableRotate === false ) return;
645 |
646 | state = STATE.TOUCH_ROTATE;
647 |
648 | rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
649 | break;
650 |
651 | case 2: // two-fingered touch: dolly
652 |
653 | if ( scope.enableZoom === false ) return;
654 |
655 | state = STATE.TOUCH_DOLLY;
656 |
657 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
658 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
659 | var distance = Math.sqrt( dx * dx + dy * dy );
660 | dollyStart.set( 0, distance );
661 | break;
662 |
663 | case 3: // three-fingered touch: pan
664 |
665 | if ( scope.enablePan === false ) return;
666 |
667 | state = STATE.TOUCH_PAN;
668 |
669 | panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
670 | break;
671 |
672 | default:
673 |
674 | state = STATE.NONE;
675 |
676 | }
677 |
678 | if ( state !== STATE.NONE ) scope.dispatchEvent( startEvent );
679 |
680 | }
681 |
682 | function touchmove( event ) {
683 |
684 | if ( scope.enabled === false ) return;
685 |
686 | event.preventDefault();
687 | event.stopPropagation();
688 |
689 | var element = scope.domElement === document ? scope.domElement.body : scope.domElement;
690 |
691 | switch ( event.touches.length ) {
692 |
693 | case 1: // one-fingered touch: rotate
694 |
695 | if ( scope.enableRotate === false ) return;
696 | if ( state !== STATE.TOUCH_ROTATE ) return;
697 |
698 | rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
699 | rotateDelta.subVectors( rotateEnd, rotateStart );
700 |
701 | // rotating across whole screen goes 360 degrees around
702 | constraint.rotateLeft( 2 * Math.PI * rotateDelta.x / element.clientWidth * scope.rotateSpeed );
703 | // rotating up and down along whole screen attempts to go 360, but limited to 180
704 | constraint.rotateUp( 2 * Math.PI * rotateDelta.y / element.clientHeight * scope.rotateSpeed );
705 |
706 | rotateStart.copy( rotateEnd );
707 |
708 | scope.update();
709 | break;
710 |
711 | case 2: // two-fingered touch: dolly
712 |
713 | if ( scope.enableZoom === false ) return;
714 | if ( state !== STATE.TOUCH_DOLLY ) return;
715 |
716 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
717 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
718 | var distance = Math.sqrt( dx * dx + dy * dy );
719 |
720 | dollyEnd.set( 0, distance );
721 | dollyDelta.subVectors( dollyEnd, dollyStart );
722 |
723 | if ( dollyDelta.y > 0 ) {
724 |
725 | constraint.dollyOut( getZoomScale() );
726 |
727 | } else if ( dollyDelta.y < 0 ) {
728 |
729 | constraint.dollyIn( getZoomScale() );
730 |
731 | }
732 |
733 | dollyStart.copy( dollyEnd );
734 |
735 | scope.update();
736 | break;
737 |
738 | case 3: // three-fingered touch: pan
739 |
740 | if ( scope.enablePan === false ) return;
741 | if ( state !== STATE.TOUCH_PAN ) return;
742 |
743 | panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
744 | panDelta.subVectors( panEnd, panStart );
745 |
746 | pan( panDelta.x, panDelta.y );
747 |
748 | panStart.copy( panEnd );
749 |
750 | scope.update();
751 | break;
752 |
753 | default:
754 |
755 | state = STATE.NONE;
756 |
757 | }
758 |
759 | }
760 |
761 | function touchend( /* event */ ) {
762 |
763 | if ( scope.enabled === false ) return;
764 |
765 | scope.dispatchEvent( endEvent );
766 | state = STATE.NONE;
767 |
768 | }
769 |
770 | function contextmenu( event ) {
771 |
772 | event.preventDefault();
773 |
774 | }
775 |
776 | this.dispose = function() {
777 |
778 | this.domElement.removeEventListener( 'contextmenu', contextmenu, false );
779 | this.domElement.removeEventListener( 'mousedown', onMouseDown, false );
780 | this.domElement.removeEventListener( 'mousewheel', onMouseWheel, false );
781 | this.domElement.removeEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
782 |
783 | this.domElement.removeEventListener( 'touchstart', touchstart, false );
784 | this.domElement.removeEventListener( 'touchend', touchend, false );
785 | this.domElement.removeEventListener( 'touchmove', touchmove, false );
786 |
787 | document.removeEventListener( 'mousemove', onMouseMove, false );
788 | document.removeEventListener( 'mouseup', onMouseUp, false );
789 |
790 | window.removeEventListener( 'keydown', onKeyDown, false );
791 |
792 | }
793 |
794 | this.domElement.addEventListener( 'contextmenu', contextmenu, false );
795 |
796 | this.domElement.addEventListener( 'mousedown', onMouseDown, false );
797 | this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
798 | this.domElement.addEventListener( 'MozMousePixelScroll', onMouseWheel, false ); // firefox
799 |
800 | this.domElement.addEventListener( 'touchstart', touchstart, false );
801 | this.domElement.addEventListener( 'touchend', touchend, false );
802 | this.domElement.addEventListener( 'touchmove', touchmove, false );
803 |
804 | window.addEventListener( 'keydown', onKeyDown, false );
805 |
806 | // force an update at start
807 | this.update();
808 |
809 | };
810 |
811 | THREE.OrbitControls.prototype = Object.create( THREE.EventDispatcher.prototype );
812 | THREE.OrbitControls.prototype.constructor = THREE.OrbitControls;
813 |
814 | Object.defineProperties( THREE.OrbitControls.prototype, {
815 |
816 | object: {
817 |
818 | get: function () {
819 |
820 | return this.constraint.object;
821 |
822 | }
823 |
824 | },
825 |
826 | target: {
827 |
828 | get: function () {
829 |
830 | return this.constraint.target;
831 |
832 | },
833 |
834 | set: function ( value ) {
835 |
836 | console.warn( 'THREE.OrbitControls: target is now immutable. Use target.set() instead.' );
837 | this.constraint.target.copy( value );
838 |
839 | }
840 |
841 | },
842 |
843 | minDistance : {
844 |
845 | get: function () {
846 |
847 | return this.constraint.minDistance;
848 |
849 | },
850 |
851 | set: function ( value ) {
852 |
853 | this.constraint.minDistance = value;
854 |
855 | }
856 |
857 | },
858 |
859 | maxDistance : {
860 |
861 | get: function () {
862 |
863 | return this.constraint.maxDistance;
864 |
865 | },
866 |
867 | set: function ( value ) {
868 |
869 | this.constraint.maxDistance = value;
870 |
871 | }
872 |
873 | },
874 |
875 | minZoom : {
876 |
877 | get: function () {
878 |
879 | return this.constraint.minZoom;
880 |
881 | },
882 |
883 | set: function ( value ) {
884 |
885 | this.constraint.minZoom = value;
886 |
887 | }
888 |
889 | },
890 |
891 | maxZoom : {
892 |
893 | get: function () {
894 |
895 | return this.constraint.maxZoom;
896 |
897 | },
898 |
899 | set: function ( value ) {
900 |
901 | this.constraint.maxZoom = value;
902 |
903 | }
904 |
905 | },
906 |
907 | minPolarAngle : {
908 |
909 | get: function () {
910 |
911 | return this.constraint.minPolarAngle;
912 |
913 | },
914 |
915 | set: function ( value ) {
916 |
917 | this.constraint.minPolarAngle = value;
918 |
919 | }
920 |
921 | },
922 |
923 | maxPolarAngle : {
924 |
925 | get: function () {
926 |
927 | return this.constraint.maxPolarAngle;
928 |
929 | },
930 |
931 | set: function ( value ) {
932 |
933 | this.constraint.maxPolarAngle = value;
934 |
935 | }
936 |
937 | },
938 |
939 | minAzimuthAngle : {
940 |
941 | get: function () {
942 |
943 | return this.constraint.minAzimuthAngle;
944 |
945 | },
946 |
947 | set: function ( value ) {
948 |
949 | this.constraint.minAzimuthAngle = value;
950 |
951 | }
952 |
953 | },
954 |
955 | maxAzimuthAngle : {
956 |
957 | get: function () {
958 |
959 | return this.constraint.maxAzimuthAngle;
960 |
961 | },
962 |
963 | set: function ( value ) {
964 |
965 | this.constraint.maxAzimuthAngle = value;
966 |
967 | }
968 |
969 | },
970 |
971 | enableDamping : {
972 |
973 | get: function () {
974 |
975 | return this.constraint.enableDamping;
976 |
977 | },
978 |
979 | set: function ( value ) {
980 |
981 | this.constraint.enableDamping = value;
982 |
983 | }
984 |
985 | },
986 |
987 | dampingFactor : {
988 |
989 | get: function () {
990 |
991 | return this.constraint.dampingFactor;
992 |
993 | },
994 |
995 | set: function ( value ) {
996 |
997 | this.constraint.dampingFactor = value;
998 |
999 | }
1000 |
1001 | },
1002 |
1003 | // backward compatibility
1004 |
1005 | noZoom: {
1006 |
1007 | get: function () {
1008 |
1009 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
1010 | return ! this.enableZoom;
1011 |
1012 | },
1013 |
1014 | set: function ( value ) {
1015 |
1016 | console.warn( 'THREE.OrbitControls: .noZoom has been deprecated. Use .enableZoom instead.' );
1017 | this.enableZoom = ! value;
1018 |
1019 | }
1020 |
1021 | },
1022 |
1023 | noRotate: {
1024 |
1025 | get: function () {
1026 |
1027 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
1028 | return ! this.enableRotate;
1029 |
1030 | },
1031 |
1032 | set: function ( value ) {
1033 |
1034 | console.warn( 'THREE.OrbitControls: .noRotate has been deprecated. Use .enableRotate instead.' );
1035 | this.enableRotate = ! value;
1036 |
1037 | }
1038 |
1039 | },
1040 |
1041 | noPan: {
1042 |
1043 | get: function () {
1044 |
1045 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
1046 | return ! this.enablePan;
1047 |
1048 | },
1049 |
1050 | set: function ( value ) {
1051 |
1052 | console.warn( 'THREE.OrbitControls: .noPan has been deprecated. Use .enablePan instead.' );
1053 | this.enablePan = ! value;
1054 |
1055 | }
1056 |
1057 | },
1058 |
1059 | noKeys: {
1060 |
1061 | get: function () {
1062 |
1063 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
1064 | return ! this.enableKeys;
1065 |
1066 | },
1067 |
1068 | set: function ( value ) {
1069 |
1070 | console.warn( 'THREE.OrbitControls: .noKeys has been deprecated. Use .enableKeys instead.' );
1071 | this.enableKeys = ! value;
1072 |
1073 | }
1074 |
1075 | },
1076 |
1077 | staticMoving : {
1078 |
1079 | get: function () {
1080 |
1081 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1082 | return ! this.constraint.enableDamping;
1083 |
1084 | },
1085 |
1086 | set: function ( value ) {
1087 |
1088 | console.warn( 'THREE.OrbitControls: .staticMoving has been deprecated. Use .enableDamping instead.' );
1089 | this.constraint.enableDamping = ! value;
1090 |
1091 | }
1092 |
1093 | },
1094 |
1095 | dynamicDampingFactor : {
1096 |
1097 | get: function () {
1098 |
1099 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1100 | return this.constraint.dampingFactor;
1101 |
1102 | },
1103 |
1104 | set: function ( value ) {
1105 |
1106 | console.warn( 'THREE.OrbitControls: .dynamicDampingFactor has been renamed. Use .dampingFactor instead.' );
1107 | this.constraint.dampingFactor = value;
1108 |
1109 | }
1110 |
1111 | }
1112 |
1113 | } );
1114 |
1115 | }() );
1116 |
--------------------------------------------------------------------------------
/js/VRControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author dmarcos / https://github.com/dmarcos
3 | * @author mrdoob / http://mrdoob.com
4 | */
5 |
6 | THREE.VRControls = function ( object, onError ) {
7 |
8 | var scope = this;
9 |
10 | var vrInputs = [];
11 |
12 | function filterInvalidDevices( devices ) {
13 |
14 | // Exclude Cardboard position sensor if Oculus exists.
15 |
16 | var oculusDevices = devices.filter( function ( device ) {
17 |
18 | return device.deviceName.toLowerCase().indexOf( 'oculus' ) !== - 1;
19 |
20 | } );
21 |
22 | if ( oculusDevices.length >= 1 ) {
23 |
24 | return devices.filter( function ( device ) {
25 |
26 | return device.deviceName.toLowerCase().indexOf( 'cardboard' ) === - 1;
27 |
28 | } );
29 |
30 | } else {
31 |
32 | return devices;
33 |
34 | }
35 |
36 | }
37 |
38 | function gotVRDevices( devices ) {
39 |
40 | devices = filterInvalidDevices( devices );
41 |
42 | for ( var i = 0; i < devices.length; i ++ ) {
43 |
44 | if ( devices[ i ] instanceof PositionSensorVRDevice ) {
45 |
46 | vrInputs.push( devices[ i ] );
47 |
48 | }
49 |
50 | }
51 |
52 | if ( onError ) onError( 'HMD not available' );
53 |
54 | }
55 |
56 | if ( navigator.getVRDevices ) {
57 |
58 | navigator.getVRDevices().then( gotVRDevices );
59 |
60 | }
61 |
62 | // the Rift SDK returns the position in meters
63 | // this scale factor allows the user to define how meters
64 | // are converted to scene units.
65 |
66 | this.scale = 1;
67 |
68 | this.update = function () {
69 |
70 | for ( var i = 0; i < vrInputs.length; i ++ ) {
71 |
72 | var vrInput = vrInputs[ i ];
73 |
74 | var state = vrInput.getState();
75 |
76 | if ( state.orientation !== null ) {
77 |
78 | object.quaternion.copy( state.orientation );
79 |
80 | }
81 |
82 | if ( state.position !== null ) {
83 |
84 | object.position.copy( state.position ).multiplyScalar( scope.scale );
85 |
86 | }
87 |
88 | }
89 |
90 | };
91 |
92 | this.resetSensor = function () {
93 |
94 | for ( var i = 0; i < vrInputs.length; i ++ ) {
95 |
96 | var vrInput = vrInputs[ i ];
97 |
98 | if ( vrInput.resetSensor !== undefined ) {
99 |
100 | vrInput.resetSensor();
101 |
102 | } else if ( vrInput.zeroSensor !== undefined ) {
103 |
104 | vrInput.zeroSensor();
105 |
106 | }
107 |
108 | }
109 |
110 | };
111 |
112 | this.zeroSensor = function () {
113 |
114 | console.warn( 'THREE.VRControls: .zeroSensor() is now .resetSensor().' );
115 | this.resetSensor();
116 |
117 | };
118 |
119 | this.dispose = function () {
120 |
121 | vrInputs = [];
122 |
123 | };
124 |
125 | };
126 |
--------------------------------------------------------------------------------
/js/VREffect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author dmarcos / https://github.com/dmarcos
3 | * @author mrdoob / http://mrdoob.com
4 | *
5 | * WebVR Spec: http://mozvr.github.io/webvr-spec/webvr.html
6 | *
7 | * Firefox: http://mozvr.com/downloads/
8 | * Chromium: https://drive.google.com/folderview?id=0BzudLt22BqGRbW9WTHMtOWMzNjQ&usp=sharing#list
9 | *
10 | */
11 |
12 | THREE.VREffect = function ( renderer, onError ) {
13 |
14 | var vrHMD;
15 | var eyeTranslationL, eyeFOVL;
16 | var eyeTranslationR, eyeFOVR;
17 |
18 | function gotVRDevices( devices ) {
19 |
20 | for ( var i = 0; i < devices.length; i ++ ) {
21 |
22 | if ( devices[ i ] instanceof HMDVRDevice ) {
23 |
24 | vrHMD = devices[ i ];
25 |
26 | if ( vrHMD.getEyeParameters !== undefined ) {
27 |
28 | var eyeParamsL = vrHMD.getEyeParameters( 'left' );
29 | var eyeParamsR = vrHMD.getEyeParameters( 'right' );
30 |
31 | eyeTranslationL = eyeParamsL.eyeTranslation;
32 | eyeTranslationR = eyeParamsR.eyeTranslation;
33 | eyeFOVL = eyeParamsL.recommendedFieldOfView;
34 | eyeFOVR = eyeParamsR.recommendedFieldOfView;
35 |
36 | } else {
37 |
38 | // TODO: This is an older code path and not spec compliant.
39 | // It should be removed at some point in the near future.
40 | eyeTranslationL = vrHMD.getEyeTranslation( 'left' );
41 | eyeTranslationR = vrHMD.getEyeTranslation( 'right' );
42 | eyeFOVL = vrHMD.getRecommendedEyeFieldOfView( 'left' );
43 | eyeFOVR = vrHMD.getRecommendedEyeFieldOfView( 'right' );
44 |
45 | }
46 |
47 | break; // We keep the first we encounter
48 |
49 | }
50 |
51 | }
52 |
53 | if ( vrHMD === undefined ) {
54 |
55 | if ( onError ) onError( 'HMD not available' );
56 |
57 | }
58 |
59 | }
60 |
61 | if ( navigator.getVRDevices ) {
62 |
63 | navigator.getVRDevices().then( gotVRDevices );
64 |
65 | }
66 |
67 | //
68 |
69 | this.scale = 1;
70 |
71 | this.setSize = function( width, height ) {
72 |
73 | renderer.setSize( width, height );
74 |
75 | };
76 |
77 | // fullscreen
78 |
79 | var isFullscreen = false;
80 |
81 | var canvas = renderer.domElement;
82 | var fullscreenchange = canvas.mozRequestFullScreen ? 'mozfullscreenchange' : 'webkitfullscreenchange';
83 |
84 | document.addEventListener( fullscreenchange, function ( event ) {
85 |
86 | isFullscreen = document.mozFullScreenElement || document.webkitFullscreenElement;
87 |
88 | }, false );
89 |
90 | this.setFullScreen = function ( boolean ) {
91 |
92 | if ( vrHMD === undefined ) return;
93 | if ( isFullscreen === boolean ) return;
94 |
95 | if ( canvas.mozRequestFullScreen ) {
96 |
97 | canvas.mozRequestFullScreen( { vrDisplay: vrHMD } );
98 |
99 | } else if ( canvas.webkitRequestFullscreen ) {
100 |
101 | canvas.webkitRequestFullscreen( { vrDisplay: vrHMD } );
102 |
103 | }
104 |
105 | };
106 |
107 | // render
108 |
109 | var cameraL = new THREE.PerspectiveCamera();
110 | var cameraR = new THREE.PerspectiveCamera();
111 |
112 | this.render = function ( scene, camera ) {
113 |
114 | if ( vrHMD ) {
115 |
116 | var sceneL, sceneR;
117 |
118 | if ( Array.isArray( scene ) ) {
119 |
120 | sceneL = scene[ 0 ];
121 | sceneR = scene[ 1 ];
122 |
123 | } else {
124 |
125 | sceneL = scene;
126 | sceneR = scene;
127 |
128 | }
129 |
130 | var size = renderer.getSize();
131 | size.width /= 2;
132 |
133 | renderer.enableScissorTest( true );
134 | renderer.clear();
135 |
136 | if ( camera.parent === null ) camera.updateMatrixWorld();
137 |
138 | cameraL.projectionMatrix = fovToProjection( eyeFOVL, true, camera.near, camera.far );
139 | cameraR.projectionMatrix = fovToProjection( eyeFOVR, true, camera.near, camera.far );
140 |
141 | camera.matrixWorld.decompose( cameraL.position, cameraL.quaternion, cameraL.scale );
142 | camera.matrixWorld.decompose( cameraR.position, cameraR.quaternion, cameraR.scale );
143 |
144 | cameraL.translateX( eyeTranslationL.x * this.scale );
145 | cameraR.translateX( eyeTranslationR.x * this.scale );
146 |
147 | // render left eye
148 | renderer.setViewport( 0, 0, size.width, size.height );
149 | renderer.setScissor( 0, 0, size.width, size.height );
150 | renderer.render( sceneL, cameraL );
151 |
152 | // render right eye
153 | renderer.setViewport( size.width, 0, size.width, size.height );
154 | renderer.setScissor( size.width, 0, size.width, size.height );
155 | renderer.render( sceneR, cameraR );
156 |
157 | renderer.enableScissorTest( false );
158 |
159 | return;
160 |
161 | }
162 |
163 | // Regular render mode if not HMD
164 |
165 | if ( Array.isArray( scene ) ) scene = scene[ 0 ];
166 |
167 | renderer.render( scene, camera );
168 |
169 | };
170 |
171 | //
172 |
173 | function fovToNDCScaleOffset( fov ) {
174 |
175 | var pxscale = 2.0 / ( fov.leftTan + fov.rightTan );
176 | var pxoffset = ( fov.leftTan - fov.rightTan ) * pxscale * 0.5;
177 | var pyscale = 2.0 / ( fov.upTan + fov.downTan );
178 | var pyoffset = ( fov.upTan - fov.downTan ) * pyscale * 0.5;
179 | return { scale: [ pxscale, pyscale ], offset: [ pxoffset, pyoffset ] };
180 |
181 | }
182 |
183 | function fovPortToProjection( fov, rightHanded, zNear, zFar ) {
184 |
185 | rightHanded = rightHanded === undefined ? true : rightHanded;
186 | zNear = zNear === undefined ? 0.01 : zNear;
187 | zFar = zFar === undefined ? 10000.0 : zFar;
188 |
189 | var handednessScale = rightHanded ? - 1.0 : 1.0;
190 |
191 | // start with an identity matrix
192 | var mobj = new THREE.Matrix4();
193 | var m = mobj.elements;
194 |
195 | // and with scale/offset info for normalized device coords
196 | var scaleAndOffset = fovToNDCScaleOffset( fov );
197 |
198 | // X result, map clip edges to [-w,+w]
199 | m[ 0 * 4 + 0 ] = scaleAndOffset.scale[ 0 ];
200 | m[ 0 * 4 + 1 ] = 0.0;
201 | m[ 0 * 4 + 2 ] = scaleAndOffset.offset[ 0 ] * handednessScale;
202 | m[ 0 * 4 + 3 ] = 0.0;
203 |
204 | // Y result, map clip edges to [-w,+w]
205 | // Y offset is negated because this proj matrix transforms from world coords with Y=up,
206 | // but the NDC scaling has Y=down (thanks D3D?)
207 | m[ 1 * 4 + 0 ] = 0.0;
208 | m[ 1 * 4 + 1 ] = scaleAndOffset.scale[ 1 ];
209 | m[ 1 * 4 + 2 ] = - scaleAndOffset.offset[ 1 ] * handednessScale;
210 | m[ 1 * 4 + 3 ] = 0.0;
211 |
212 | // Z result (up to the app)
213 | m[ 2 * 4 + 0 ] = 0.0;
214 | m[ 2 * 4 + 1 ] = 0.0;
215 | m[ 2 * 4 + 2 ] = zFar / ( zNear - zFar ) * - handednessScale;
216 | m[ 2 * 4 + 3 ] = ( zFar * zNear ) / ( zNear - zFar );
217 |
218 | // W result (= Z in)
219 | m[ 3 * 4 + 0 ] = 0.0;
220 | m[ 3 * 4 + 1 ] = 0.0;
221 | m[ 3 * 4 + 2 ] = handednessScale;
222 | m[ 3 * 4 + 3 ] = 0.0;
223 |
224 | mobj.transpose();
225 |
226 | return mobj;
227 |
228 | }
229 |
230 | function fovToProjection( fov, rightHanded, zNear, zFar ) {
231 |
232 | var DEG2RAD = Math.PI / 180.0;
233 |
234 | var fovPort = {
235 | upTan: Math.tan( fov.upDegrees * DEG2RAD ),
236 | downTan: Math.tan( fov.downDegrees * DEG2RAD ),
237 | leftTan: Math.tan( fov.leftDegrees * DEG2RAD ),
238 | rightTan: Math.tan( fov.rightDegrees * DEG2RAD )
239 | };
240 |
241 | return fovPortToProjection( fovPort, rightHanded, zNear, zFar );
242 |
243 | }
244 |
245 | };
246 |
--------------------------------------------------------------------------------
/js/sechelt.js:
--------------------------------------------------------------------------------
1 | var camera, scene, renderer, effect;
2 | var vrControls;
3 | var sky, water;
4 | var cameraPath;
5 | var dolly;
6 |
7 | var currentTime = null;
8 | var startTime = null;
9 | var gotime = null;
10 | var vrMode = false;
11 | var speed = 1.6;
12 | var prevTime = null;
13 | var deltaTime = 1 / 60; // set reasonable starting delta time
14 |
15 | var colorTop = new THREE.Color(0xdc72aa);
16 | var colorMiddle = new THREE.Color(0xfbdfd3);
17 | var colorBottom = new THREE.Color(0xdc72aa);
18 |
19 | var isMobile = function () {
20 | var check = false;
21 | (function (a) {
22 | if (/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0, 4))) {
23 | check = true;
24 | }
25 | })(navigator.userAgent || navigator.vendor || window.opera);
26 | return check;
27 | };
28 |
29 | var getUrlParameter = function (name) {
30 | name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
31 | var regex = new RegExp('[\\?&]' + name + '=([^]*)');
32 | var results = regex.exec(location.search);
33 | return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
34 | };
35 |
36 | function setupSound() {
37 | var qsSound = getUrlParameter('sound');
38 | if (qsSound === '0' || qsSound === 'off' || qsSound === 'false') {
39 | return;
40 | }
41 |
42 | var listener = new THREE.AudioListener();
43 | camera.add(listener);
44 |
45 | var sound = new THREE.Audio(listener);
46 | sound.load('sounds/78389__inchadney__seagulls.ogg');
47 | sound.position.set(475, 50, 850);
48 | sound.setLoop(true);
49 | sound.setRefDistance(100);
50 | sound.autoplay = true;
51 | scene.add(sound);
52 |
53 | var sound = new THREE.Audio(listener);
54 | sound.load('sounds/23707__hazure__seagull.ogg');
55 | sound.position.set(10, 50, -200);
56 | sound.setLoop(true);
57 | sound.setRefDistance(100);
58 | sound.autoplay = true;
59 | scene.add(sound);
60 |
61 | var sound = new THREE.Audio(listener);
62 | sound.load('sounds/235428__allanz10d__calm-ocean-breeze-simulation.ogg');
63 | sound.position.set(-30, 0, -750);
64 | sound.setLoop(true);
65 | sound.setRefDistance(100);
66 | sound.autoplay = true;
67 | scene.add(sound);
68 | }
69 |
70 | function setupSkybox() {
71 | var geometry = new THREE.SphereGeometry(10000, 64, 32);
72 | var vertices = geometry.vertices;
73 | var faces = geometry.faces;
74 |
75 | for (var i = 0, l = faces.length; i < l; i++) {
76 | var face = faces[i];
77 | var vertex1 = vertices[face.a];
78 | var vertex2 = vertices[face.b];
79 | var vertex3 = vertices[face.c];
80 |
81 | var color1 = colorMiddle.clone();
82 | color1.lerp(vertex1.y > 0 ? colorTop : colorBottom, Math.abs(vertex1.y) / 6000);
83 |
84 | var color2 = colorMiddle.clone();
85 | color2.lerp(vertex2.y > 0 ? colorTop : colorBottom, Math.abs(vertex2.y) / 6000);
86 |
87 | var color3 = colorMiddle.clone();
88 | color3.lerp(vertex3.y > 0 ? colorTop : colorBottom, Math.abs(vertex3.y) / 6000);
89 |
90 | face.vertexColors.push(color1, color2, color3);
91 | }
92 |
93 | var material = new THREE.MeshBasicMaterial({
94 | side: THREE.BackSide,
95 | depthWrite: false,
96 | depthTest: false,
97 | fog: false,
98 | map: new THREE.TextureLoader().load('images/bg-2.png')
99 | });
100 |
101 | sky = new THREE.Mesh(geometry, material);
102 | scene.add(sky);
103 | }
104 |
105 | function setupWaves() {
106 | var geometry = new THREE.Geometry();
107 | var vertices = geometry.vertices;
108 | var faces = geometry.faces;
109 | var vector = new THREE.Vector3();
110 |
111 | for (var i = 0; i < 10000; i++) {
112 | vector.x = Math.random() * 40000 - 20000;
113 | vector.z = Math.random() * 40000 - 20000;
114 |
115 | var size = Math.random() * 10 + 1;
116 | var angle = Math.random() * Math.PI;
117 |
118 | var vertex1 = vector.clone();
119 | vertex1.x += size * Math.cos(angle);
120 | vertex1.z += size * Math.sin(angle);
121 |
122 | angle -= 2;
123 |
124 | var vertex2 = vector.clone();
125 | vertex2.x += size * Math.cos(angle);
126 | vertex2.z += size * Math.sin(angle);
127 |
128 | angle -= 2;
129 |
130 | var vertex3 = vector.clone();
131 | vertex3.x += size * Math.cos(angle);
132 | vertex3.z += size * Math.sin(angle);
133 |
134 | var a = vertices.push(vertex1) - 1;
135 | var b = vertices.push(vertex2) - 1;
136 | var c = vertices.push(vertex3) - 1;
137 |
138 | faces.push(new THREE.Face3(a, b, c));
139 | }
140 |
141 | var material = new THREE.MeshBasicMaterial({
142 | opacity: 0.4,
143 | transparent: true
144 | })
145 |
146 | var mesh = new THREE.Mesh(geometry, material);
147 | mesh.position.y = 2.5;
148 | mesh.renderOrder = 1;
149 | scene.add(mesh);
150 | }
151 |
152 | function setupWater() {
153 | var geometry = new THREE.PlaneBufferGeometry(100000, 100000);
154 | var material = new THREE.MeshBasicMaterial({
155 | color: colorMiddle,
156 | opacity: 0.75,
157 | transparent: true
158 | });
159 | water = new THREE.Mesh(geometry, material);
160 | water.position.y = 0;
161 | water.rotation.x = -Math.PI / 2;
162 | water.renderOrder = 2;
163 | scene.add(water);
164 | }
165 |
166 | function setupLights() {
167 | var directionalLight = new THREE.DirectionalLight(0xffffff, 0.15);
168 | directionalLight.position.set(-1, 1, -1);
169 | scene.add(directionalLight);
170 |
171 | var hemisphereLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.8);
172 | hemisphereLight.position.set(-1, 2, 1.5);
173 | scene.add(hemisphereLight);
174 | }
175 |
176 | function onModelLoaded(model) {
177 | // clone and flip objects for reflection.
178 | reflectObjects = ['land', 'island', 'rocks', 'trees'];
179 |
180 | reflectObjects.forEach(function(name) {
181 | var object = model.getObjectByName(name).children[0];
182 | var reflection = new THREE.Mesh(object.geometry, object.material.clone());
183 | reflection.material.side = THREE.BackSide;
184 | reflection.position.y = 0;
185 | reflection.scale.y = -1;
186 | object.parent.add(reflection);
187 | });
188 |
189 | scene.add(model);
190 | }
191 |
192 | function onWindowResize() {
193 | camera.aspect = window.innerWidth / window.innerHeight;
194 | camera.updateProjectionMatrix();
195 | renderer.setSize(window.innerWidth, window.innerHeight);
196 | }
197 |
198 | function startAnimation() {
199 | onWindowResize();
200 | requestAnimationFrame(animate);
201 | }
202 |
203 | function animate(time) {
204 | if (cameraPath !== undefined) {
205 | if (!startTime) startTime = time;
206 | if (!currentTime) currentTime = time;
207 |
208 | if (prevTime) {
209 | deltaTime = time - prevTime;
210 | }
211 | prevTime = time;
212 |
213 | currentTime += speed * deltaTime;
214 | gotime = TWEEN.Easing.Sinusoidal.InOut(Math.min(currentTime / 70000, 1)) * 0.9999;
215 |
216 | var pointA = cameraPath.getPointAt(gotime);
217 | var pointB = cameraPath.getPointAt(Math.min(gotime + 0.0001, 1));
218 |
219 | pointA.z = -pointA.z;
220 | pointB.z = -pointB.z;
221 |
222 | dolly.position.copy(pointA);
223 | dolly.lookAt(pointB);
224 | dolly.rotateY(Math.PI); // look forward
225 | }
226 |
227 | // move sky and water position with camera dolly
228 | sky.position.copy(dolly.position);
229 |
230 | water.position.x = dolly.position.x;
231 | water.position.z = dolly.position.z;
232 |
233 | if (vrMode) {
234 | effect.render(scene, camera);
235 | } else {
236 | renderer.render(scene, camera);
237 | }
238 | vrControls.update();
239 |
240 | requestAnimationFrame(animate);
241 | }
242 |
243 | function requestFullscreen() {
244 | var el = renderer.domElement;
245 |
246 | if (!isMobile()) {
247 | effect.setFullScreen(true);
248 | return;
249 | }
250 |
251 | if (el.requestFullscreen) {
252 | el.requestFullscreen();
253 | } else if (el.mozRequestFullScreen) {
254 | el.mozRequestFullScreen();
255 | } else if (el.webkitRequestFullscreen) {
256 | el.webkitRequestFullscreen();
257 | }
258 | }
259 |
260 | function onWindowResize() {
261 | camera.aspect = window.innerWidth / window.innerHeight;
262 | camera.updateProjectionMatrix();
263 | if (vrMode) {
264 | effect.setSize(window.innerWidth, window.innerHeight);
265 | } else {
266 | renderer.setSize(window.innerWidth, window.innerHeight);
267 | }
268 | }
269 |
270 | function onFullscreenChange(e) {
271 | var fsElement = document.fullscreenElement ||
272 | document.mozFullScreenElement ||
273 | document.webkitFullscreenElement;
274 |
275 | if (!fsElement) {
276 | vrMode = false;
277 | } else {
278 | // lock screen if mobile
279 | window.screen.orientation.lock('landscape');
280 | }
281 | }
282 |
283 | function init() {
284 | // setup renderer
285 | renderer = new THREE.WebGLRenderer({
286 | antialias: true
287 | });
288 | renderer.autoClear = false;
289 | renderer.setClearColor(0x404040);
290 | document.body.appendChild(renderer.domElement);
291 |
292 | // setup scene
293 | scene = new THREE.Scene();
294 | scene.fog = new THREE.Fog(0xcacfde, 0, 10000);
295 |
296 | // setup dolly that camera rides on.
297 | dolly = new THREE.Group();
298 | dolly.position.set(10000, 10000, 10000);
299 | scene.add(dolly);
300 |
301 | // setup camera
302 | camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 1, 20000);
303 | camera.position.z = 0.0001;
304 | dolly.add(camera);
305 |
306 | // Scene elements
307 | setupSkybox();
308 | setupWaves();
309 | setupWater();
310 | setupSound();
311 | setupLights();
312 |
313 | // load scene
314 | var loader = new THREE.ObjectLoader();
315 | loader.load('models/scene-nov4.json', onModelLoaded)
316 |
317 | // load camera path
318 | var loader = new THREE.C4DCurveLoader();
319 | loader.load('models/flightpath-nov4-bezier.txt', function(curve) {
320 | cameraPath = curve.toLinearCurve(1); // 1 = distance between points
321 | startAnimation();
322 | });
323 |
324 | // effect and controls for VR
325 | effect = new THREE.VREffect(renderer);
326 | vrControls = new THREE.VRControls(camera);
327 |
328 | window.addEventListener('resize', onWindowResize, false);
329 | }
330 |
331 | document.querySelector('#enterVr').addEventListener('click', function() {
332 | vrMode = vrMode ? false : true;
333 | requestFullscreen();
334 | onWindowResize();
335 | });
336 |
337 | document.addEventListener('fullscreenchange', onFullscreenChange);
338 | document.addEventListener('mozfullscreenchange', onFullscreenChange);
339 | window.addEventListener('resize', onWindowResize, false );
340 |
341 | init();
342 |
--------------------------------------------------------------------------------
/js/tween.min.js:
--------------------------------------------------------------------------------
1 | // tween.js v.0.15.0 https://github.com/sole/tween.js
2 | void 0===Date.now&&(Date.now=function(){return(new Date).valueOf()});var TWEEN=TWEEN||function(){var n=[];return{REVISION:"14",getAll:function(){return n},removeAll:function(){n=[]},add:function(t){n.push(t)},remove:function(t){var r=n.indexOf(t);-1!==r&&n.splice(r,1)},update:function(t){if(0===n.length)return!1;var r=0;for(t=void 0!==t?t:"undefined"!=typeof window&&void 0!==window.performance&&void 0!==window.performance.now?window.performance.now():Date.now();rn;n++)E[n].stop()},this.delay=function(n){return s=n,this},this.repeat=function(n){return e=n,this},this.yoyo=function(n){return a=n,this},this.easing=function(n){return l=n,this},this.interpolation=function(n){return p=n,this},this.chain=function(){return E=arguments,this},this.onStart=function(n){return d=n,this},this.onUpdate=function(n){return I=n,this},this.onComplete=function(n){return w=n,this},this.onStop=function(n){return M=n,this},this.update=function(n){var f;if(h>n)return!0;v===!1&&(null!==d&&d.call(t),v=!0);var M=(n-h)/o;M=M>1?1:M;var O=l(M);for(f in i){var m=r[f]||0,N=i[f];N instanceof Array?t[f]=p(N,O):("string"==typeof N&&(N=m+parseFloat(N,10)),"number"==typeof N&&(t[f]=m+(N-m)*O))}if(null!==I&&I.call(t,O),1==M){if(e>0){isFinite(e)&&e--;for(f in u){if("string"==typeof i[f]&&(u[f]=u[f]+parseFloat(i[f],10)),a){var T=u[f];u[f]=i[f],i[f]=T}r[f]=u[f]}return a&&(c=!c),h=n+s,!0}null!==w&&w.call(t);for(var g=0,W=E.length;W>g;g++)E[g].start(n);return!1}return!0}},TWEEN.Easing={Linear:{None:function(n){return n}},Quadratic:{In:function(n){return n*n},Out:function(n){return n*(2-n)},InOut:function(n){return(n*=2)<1?.5*n*n:-.5*(--n*(n-2)-1)}},Cubic:{In:function(n){return n*n*n},Out:function(n){return--n*n*n+1},InOut:function(n){return(n*=2)<1?.5*n*n*n:.5*((n-=2)*n*n+2)}},Quartic:{In:function(n){return n*n*n*n},Out:function(n){return 1- --n*n*n*n},InOut:function(n){return(n*=2)<1?.5*n*n*n*n:-.5*((n-=2)*n*n*n-2)}},Quintic:{In:function(n){return n*n*n*n*n},Out:function(n){return--n*n*n*n*n+1},InOut:function(n){return(n*=2)<1?.5*n*n*n*n*n:.5*((n-=2)*n*n*n*n+2)}},Sinusoidal:{In:function(n){return 1-Math.cos(n*Math.PI/2)},Out:function(n){return Math.sin(n*Math.PI/2)},InOut:function(n){return.5*(1-Math.cos(Math.PI*n))}},Exponential:{In:function(n){return 0===n?0:Math.pow(1024,n-1)},Out:function(n){return 1===n?1:1-Math.pow(2,-10*n)},InOut:function(n){return 0===n?0:1===n?1:(n*=2)<1?.5*Math.pow(1024,n-1):.5*(-Math.pow(2,-10*(n-1))+2)}},Circular:{In:function(n){return 1-Math.sqrt(1-n*n)},Out:function(n){return Math.sqrt(1- --n*n)},InOut:function(n){return(n*=2)<1?-.5*(Math.sqrt(1-n*n)-1):.5*(Math.sqrt(1-(n-=2)*n)+1)}},Elastic:{In:function(n){var t,r=.1,i=.4;return 0===n?0:1===n?1:(!r||1>r?(r=1,t=i/4):t=i*Math.asin(1/r)/(2*Math.PI),-(r*Math.pow(2,10*(n-=1))*Math.sin(2*(n-t)*Math.PI/i)))},Out:function(n){var t,r=.1,i=.4;return 0===n?0:1===n?1:(!r||1>r?(r=1,t=i/4):t=i*Math.asin(1/r)/(2*Math.PI),r*Math.pow(2,-10*n)*Math.sin(2*(n-t)*Math.PI/i)+1)},InOut:function(n){var t,r=.1,i=.4;return 0===n?0:1===n?1:(!r||1>r?(r=1,t=i/4):t=i*Math.asin(1/r)/(2*Math.PI),(n*=2)<1?-.5*r*Math.pow(2,10*(n-=1))*Math.sin(2*(n-t)*Math.PI/i):r*Math.pow(2,-10*(n-=1))*Math.sin(2*(n-t)*Math.PI/i)*.5+1)}},Back:{In:function(n){var t=1.70158;return n*n*((t+1)*n-t)},Out:function(n){var t=1.70158;return--n*n*((t+1)*n+t)+1},InOut:function(n){var t=2.5949095;return(n*=2)<1?.5*n*n*((t+1)*n-t):.5*((n-=2)*n*((t+1)*n+t)+2)}},Bounce:{In:function(n){return 1-TWEEN.Easing.Bounce.Out(1-n)},Out:function(n){return 1/2.75>n?7.5625*n*n:2/2.75>n?7.5625*(n-=1.5/2.75)*n+.75:2.5/2.75>n?7.5625*(n-=2.25/2.75)*n+.9375:7.5625*(n-=2.625/2.75)*n+.984375},InOut:function(n){return.5>n?.5*TWEEN.Easing.Bounce.In(2*n):.5*TWEEN.Easing.Bounce.Out(2*n-1)+.5}}},TWEEN.Interpolation={Linear:function(n,t){var r=n.length-1,i=r*t,u=Math.floor(i),o=TWEEN.Interpolation.Utils.Linear;return 0>t?o(n[0],n[1],i):t>1?o(n[r],n[r-1],r-i):o(n[u],n[u+1>r?r:u+1],i-u)},Bezier:function(n,t){var r,i=0,u=n.length-1,o=Math.pow,e=TWEEN.Interpolation.Utils.Bernstein;for(r=0;u>=r;r++)i+=o(1-t,u-r)*o(t,r)*n[r]*e(u,r);return i},CatmullRom:function(n,t){var r=n.length-1,i=r*t,u=Math.floor(i),o=TWEEN.Interpolation.Utils.CatmullRom;return n[0]===n[r]?(0>t&&(u=Math.floor(i=r*(1+t))),o(n[(u-1+r)%r],n[u],n[(u+1)%r],n[(u+2)%r],i-u)):0>t?n[0]-(o(n[0],n[0],n[1],n[1],-i)-n[0]):t>1?n[r]-(o(n[r],n[r],n[r-1],n[r-1],i-r)-n[r]):o(n[u?u-1:0],n[u],n[u+1>r?r:u+1],n[u+2>r?r:u+2],i-u)},Utils:{Linear:function(n,t,r){return(t-n)*r+n},Bernstein:function(n,t){var r=TWEEN.Interpolation.Utils.Factorial;return r(n)/r(t)/r(n-t)},Factorial:function(){var n=[1];return function(t){var r,i=1;if(n[t])return n[t];for(r=t;r>1;r--)i*=r;return n[t]=i}}(),CatmullRom:function(n,t,r,i,u){var o=.5*(r-n),e=.5*(i-t),a=u*u,f=u*a;return(2*t-2*r+o+e)*f+(-3*t+3*r-2*o-e)*a+o*u+t}}},"undefined"!=typeof module&&module.exports&&(module.exports=TWEEN);
--------------------------------------------------------------------------------
/js/webvr-polyfill.js:
--------------------------------------------------------------------------------
1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o Util.MAX_TIMESTEP) {
416 | console.warn('Invalid timestamps detected. Time step between successive ' +
417 | 'gyroscope sensor samples is very small or not monotonic');
418 | this.previousTimestampS = timestampS;
419 | return;
420 | }
421 | this.accelerometer.set(-accGravity.x, -accGravity.y, -accGravity.z);
422 | this.gyroscope.set(rotRate.alpha, rotRate.beta, rotRate.gamma);
423 |
424 | // With iOS and Firefox Android, rotationRate is reported in degrees,
425 | // so we first convert to radians.
426 | if (this.isIOS || this.isFirefoxAndroid) {
427 | this.gyroscope.multiplyScalar(Math.PI / 180);
428 | }
429 |
430 | this.filter.addAccelMeasurement(this.accelerometer, timestampS);
431 | this.filter.addGyroMeasurement(this.gyroscope, timestampS);
432 |
433 | this.previousTimestampS = timestampS;
434 | };
435 |
436 | FusionPositionSensorVRDevice.prototype.onScreenOrientationChange_ =
437 | function(screenOrientation) {
438 | this.setScreenTransform_();
439 | };
440 |
441 | FusionPositionSensorVRDevice.prototype.setScreenTransform_ = function() {
442 | this.worldToScreenQ.set(0, 0, 0, 1);
443 | switch (window.orientation) {
444 | case 0:
445 | break;
446 | case 90:
447 | this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0, 0, 1), -Math.PI/2);
448 | break;
449 | case -90:
450 | this.worldToScreenQ.setFromAxisAngle(new THREE.Vector3(0, 0, 1), Math.PI/2);
451 | break;
452 | case 180:
453 | // TODO.
454 | break;
455 | }
456 | };
457 |
458 |
459 | module.exports = FusionPositionSensorVRDevice;
460 |
461 | },{"./base.js":1,"./complementary-filter.js":3,"./pose-predictor.js":7,"./three-math.js":9,"./touch-panner.js":10,"./util.js":11}],5:[function(_dereq_,module,exports){
462 | /*
463 | * Copyright 2015 Google Inc. All Rights Reserved.
464 | * Licensed under the Apache License, Version 2.0 (the "License");
465 | * you may not use this file except in compliance with the License.
466 | * You may obtain a copy of the License at
467 | *
468 | * http://www.apache.org/licenses/LICENSE-2.0
469 | *
470 | * Unless required by applicable law or agreed to in writing, software
471 | * distributed under the License is distributed on an "AS IS" BASIS,
472 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
473 | * See the License for the specific language governing permissions and
474 | * limitations under the License.
475 | */
476 | var WebVRPolyfill = _dereq_('./webvr-polyfill.js');
477 |
478 | // Initialize a WebVRConfig just in case.
479 | window.WebVRConfig = window.WebVRConfig || {};
480 | new WebVRPolyfill();
481 |
482 | },{"./webvr-polyfill.js":12}],6:[function(_dereq_,module,exports){
483 | /*
484 | * Copyright 2015 Google Inc. All Rights Reserved.
485 | * Licensed under the Apache License, Version 2.0 (the "License");
486 | * you may not use this file except in compliance with the License.
487 | * You may obtain a copy of the License at
488 | *
489 | * http://www.apache.org/licenses/LICENSE-2.0
490 | *
491 | * Unless required by applicable law or agreed to in writing, software
492 | * distributed under the License is distributed on an "AS IS" BASIS,
493 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
494 | * See the License for the specific language governing permissions and
495 | * limitations under the License.
496 | */
497 | var PositionSensorVRDevice = _dereq_('./base.js').PositionSensorVRDevice;
498 | var THREE = _dereq_('./three-math.js');
499 | var Util = _dereq_('./util.js');
500 |
501 | // How much to rotate per key stroke.
502 | var KEY_SPEED = 0.15;
503 | var KEY_ANIMATION_DURATION = 80;
504 |
505 | // How much to rotate for mouse events.
506 | var MOUSE_SPEED_X = 0.5;
507 | var MOUSE_SPEED_Y = 0.3;
508 |
509 | /**
510 | * A virtual position sensor, implemented using keyboard and
511 | * mouse APIs. This is designed as for desktops/laptops where no Device*
512 | * events work.
513 | */
514 | function MouseKeyboardPositionSensorVRDevice() {
515 | this.deviceId = 'webvr-polyfill:mouse-keyboard';
516 | this.deviceName = 'VR Position Device (webvr-polyfill:mouse-keyboard)';
517 |
518 | // Attach to mouse and keyboard events.
519 | window.addEventListener('keydown', this.onKeyDown_.bind(this));
520 | window.addEventListener('mousemove', this.onMouseMove_.bind(this));
521 | window.addEventListener('mousedown', this.onMouseDown_.bind(this));
522 | window.addEventListener('mouseup', this.onMouseUp_.bind(this));
523 |
524 | this.phi = 0;
525 | this.theta = 0;
526 |
527 | // Variables for keyboard-based rotation animation.
528 | this.targetAngle = null;
529 |
530 | // State variables for calculations.
531 | this.euler = new THREE.Euler();
532 | this.orientation = new THREE.Quaternion();
533 |
534 | // Variables for mouse-based rotation.
535 | this.rotateStart = new THREE.Vector2();
536 | this.rotateEnd = new THREE.Vector2();
537 | this.rotateDelta = new THREE.Vector2();
538 | }
539 | MouseKeyboardPositionSensorVRDevice.prototype = new PositionSensorVRDevice();
540 |
541 | /**
542 | * Returns {orientation: {x,y,z,w}, position: null}.
543 | * Position is not supported for parity with other PositionSensors.
544 | */
545 | MouseKeyboardPositionSensorVRDevice.prototype.getState = function() {
546 | this.euler.set(this.phi, this.theta, 0, 'YXZ');
547 | this.orientation.setFromEuler(this.euler);
548 |
549 | return {
550 | hasOrientation: true,
551 | orientation: this.orientation,
552 | hasPosition: false,
553 | position: null
554 | }
555 | };
556 |
557 | MouseKeyboardPositionSensorVRDevice.prototype.onKeyDown_ = function(e) {
558 | // Track WASD and arrow keys.
559 | if (e.keyCode == 38) { // Up key.
560 | this.animatePhi_(this.phi + KEY_SPEED);
561 | } else if (e.keyCode == 39) { // Right key.
562 | this.animateTheta_(this.theta - KEY_SPEED);
563 | } else if (e.keyCode == 40) { // Down key.
564 | this.animatePhi_(this.phi - KEY_SPEED);
565 | } else if (e.keyCode == 37) { // Left key.
566 | this.animateTheta_(this.theta + KEY_SPEED);
567 | }
568 | };
569 |
570 | MouseKeyboardPositionSensorVRDevice.prototype.animateTheta_ = function(targetAngle) {
571 | this.animateKeyTransitions_('theta', targetAngle);
572 | };
573 |
574 | MouseKeyboardPositionSensorVRDevice.prototype.animatePhi_ = function(targetAngle) {
575 | // Prevent looking too far up or down.
576 | targetAngle = Util.clamp(targetAngle, -Math.PI/2, Math.PI/2);
577 | this.animateKeyTransitions_('phi', targetAngle);
578 | };
579 |
580 | /**
581 | * Start an animation to transition an angle from one value to another.
582 | */
583 | MouseKeyboardPositionSensorVRDevice.prototype.animateKeyTransitions_ = function(angleName, targetAngle) {
584 | // If an animation is currently running, cancel it.
585 | if (this.angleAnimation) {
586 | clearInterval(this.angleAnimation);
587 | }
588 | var startAngle = this[angleName];
589 | var startTime = new Date();
590 | // Set up an interval timer to perform the animation.
591 | this.angleAnimation = setInterval(function() {
592 | // Once we're finished the animation, we're done.
593 | var elapsed = new Date() - startTime;
594 | if (elapsed >= KEY_ANIMATION_DURATION) {
595 | this[angleName] = targetAngle;
596 | clearInterval(this.angleAnimation);
597 | return;
598 | }
599 | // Linearly interpolate the angle some amount.
600 | var percent = elapsed / KEY_ANIMATION_DURATION;
601 | this[angleName] = startAngle + (targetAngle - startAngle) * percent;
602 | }.bind(this), 1000/60);
603 | };
604 |
605 | MouseKeyboardPositionSensorVRDevice.prototype.onMouseDown_ = function(e) {
606 | this.rotateStart.set(e.clientX, e.clientY);
607 | this.isDragging = true;
608 | };
609 |
610 | // Very similar to https://gist.github.com/mrflix/8351020
611 | MouseKeyboardPositionSensorVRDevice.prototype.onMouseMove_ = function(e) {
612 | if (!this.isDragging && !this.isPointerLocked_()) {
613 | return;
614 | }
615 | // Support pointer lock API.
616 | if (this.isPointerLocked_()) {
617 | var movementX = e.movementX || e.mozMovementX || 0;
618 | var movementY = e.movementY || e.mozMovementY || 0;
619 | this.rotateEnd.set(this.rotateStart.x - movementX, this.rotateStart.y - movementY);
620 | } else {
621 | this.rotateEnd.set(e.clientX, e.clientY);
622 | }
623 | // Calculate how much we moved in mouse space.
624 | this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart);
625 | this.rotateStart.copy(this.rotateEnd);
626 |
627 | // Keep track of the cumulative euler angles.
628 | var element = document.body;
629 | this.phi += 2 * Math.PI * this.rotateDelta.y / element.clientHeight * MOUSE_SPEED_Y;
630 | this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * MOUSE_SPEED_X;
631 |
632 | // Prevent looking too far up or down.
633 | this.phi = Util.clamp(this.phi, -Math.PI/2, Math.PI/2);
634 | };
635 |
636 | MouseKeyboardPositionSensorVRDevice.prototype.onMouseUp_ = function(e) {
637 | this.isDragging = false;
638 | };
639 |
640 | MouseKeyboardPositionSensorVRDevice.prototype.isPointerLocked_ = function() {
641 | var el = document.pointerLockElement || document.mozPointerLockElement ||
642 | document.webkitPointerLockElement;
643 | return el !== undefined;
644 | };
645 |
646 | MouseKeyboardPositionSensorVRDevice.prototype.resetSensor = function() {
647 | console.error('Not implemented yet.');
648 | };
649 |
650 | module.exports = MouseKeyboardPositionSensorVRDevice;
651 |
652 | },{"./base.js":1,"./three-math.js":9,"./util.js":11}],7:[function(_dereq_,module,exports){
653 | /*
654 | * Copyright 2015 Google Inc. All Rights Reserved.
655 | * Licensed under the Apache License, Version 2.0 (the "License");
656 | * you may not use this file except in compliance with the License.
657 | * You may obtain a copy of the License at
658 | *
659 | * http://www.apache.org/licenses/LICENSE-2.0
660 | *
661 | * Unless required by applicable law or agreed to in writing, software
662 | * distributed under the License is distributed on an "AS IS" BASIS,
663 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
664 | * See the License for the specific language governing permissions and
665 | * limitations under the License.
666 | */
667 | var THREE = _dereq_('./three-math.js');
668 |
669 | var DEBUG = false;
670 |
671 | /**
672 | * Given an orientation and the gyroscope data, predicts the future orientation
673 | * of the head. This makes rendering appear faster.
674 | *
675 | * Also see: http://msl.cs.uiuc.edu/~lavalle/papers/LavYerKatAnt14.pdf
676 | *
677 | * @param {Number} predictionTimeS time from head movement to the appearance of
678 | * the corresponding image.
679 | */
680 | function PosePredictor(predictionTimeS) {
681 | this.predictionTimeS = predictionTimeS;
682 |
683 | // The quaternion corresponding to the previous state.
684 | this.previousQ = new THREE.Quaternion();
685 | // Previous time a prediction occurred.
686 | this.previousTimestampS = null;
687 |
688 | // The delta quaternion that adjusts the current pose.
689 | this.deltaQ = new THREE.Quaternion();
690 | // The output quaternion.
691 | this.outQ = new THREE.Quaternion();
692 | }
693 |
694 | PosePredictor.prototype.getPrediction = function(currentQ, gyro, timestampS) {
695 | if (!this.previousTimestampS) {
696 | this.previousQ.copy(currentQ);
697 | this.previousTimestampS = timestampS;
698 | return currentQ;
699 | }
700 |
701 | // Calculate axis and angle based on gyroscope rotation rate data.
702 | var axis = new THREE.Vector3();
703 | axis.copy(gyro);
704 | axis.normalize();
705 |
706 | var angularSpeed = gyro.length();
707 |
708 | // If we're rotating slowly, don't do prediction.
709 | if (angularSpeed < THREE.Math.degToRad(20)) {
710 | if (DEBUG) {
711 | console.log('Moving slowly, at %s deg/s: no prediction',
712 | THREE.Math.radToDeg(angularSpeed).toFixed(1));
713 | }
714 | this.outQ.copy(currentQ);
715 | this.previousQ.copy(currentQ);
716 | return this.outQ;
717 | }
718 |
719 | // Get the predicted angle based on the time delta and latency.
720 | var deltaT = timestampS - this.previousTimestampS;
721 | var predictAngle = angularSpeed * this.predictionTimeS;
722 |
723 | this.deltaQ.setFromAxisAngle(axis, predictAngle);
724 | this.outQ.copy(this.previousQ);
725 | this.outQ.multiply(this.deltaQ);
726 |
727 | this.previousQ.copy(currentQ);
728 |
729 | return this.outQ;
730 | };
731 |
732 |
733 | module.exports = PosePredictor;
734 |
735 | },{"./three-math.js":9}],8:[function(_dereq_,module,exports){
736 | function SensorSample(sample, timestampS) {
737 | this.set(sample, timestampS);
738 | };
739 |
740 | SensorSample.prototype.set = function(sample, timestampS) {
741 | this.sample = sample;
742 | this.timestampS = timestampS;
743 | };
744 |
745 | SensorSample.prototype.copy = function(sensorSample) {
746 | this.set(sensorSample.sample, sensorSample.timestampS);
747 | };
748 |
749 | module.exports = SensorSample;
750 |
751 | },{}],9:[function(_dereq_,module,exports){
752 | /*
753 | * A subset of THREE.js, providing mostly quaternion and euler-related
754 | * operations, manually lifted from
755 | * https://github.com/mrdoob/three.js/tree/master/src/math, as of 9c30286b38df039fca389989ff06ea1c15d6bad1
756 | */
757 |
758 | // Only use if the real THREE is not provided.
759 | var THREE = window.THREE || {};
760 |
761 | // If some piece of THREE is missing, fill it in here.
762 | if (!THREE.Quaternion || !THREE.Vector3 || !THREE.Vector2 || !THREE.Euler || !THREE.Math) {
763 | console.log('No THREE.js found.');
764 |
765 |
766 | /*** START Quaternion ***/
767 |
768 | /**
769 | * @author mikael emtinger / http://gomo.se/
770 | * @author alteredq / http://alteredqualia.com/
771 | * @author WestLangley / http://github.com/WestLangley
772 | * @author bhouston / http://exocortex.com
773 | */
774 |
775 | THREE.Quaternion = function ( x, y, z, w ) {
776 |
777 | this._x = x || 0;
778 | this._y = y || 0;
779 | this._z = z || 0;
780 | this._w = ( w !== undefined ) ? w : 1;
781 |
782 | };
783 |
784 | THREE.Quaternion.prototype = {
785 |
786 | constructor: THREE.Quaternion,
787 |
788 | _x: 0,_y: 0, _z: 0, _w: 0,
789 |
790 | get x () {
791 |
792 | return this._x;
793 |
794 | },
795 |
796 | set x ( value ) {
797 |
798 | this._x = value;
799 | this.onChangeCallback();
800 |
801 | },
802 |
803 | get y () {
804 |
805 | return this._y;
806 |
807 | },
808 |
809 | set y ( value ) {
810 |
811 | this._y = value;
812 | this.onChangeCallback();
813 |
814 | },
815 |
816 | get z () {
817 |
818 | return this._z;
819 |
820 | },
821 |
822 | set z ( value ) {
823 |
824 | this._z = value;
825 | this.onChangeCallback();
826 |
827 | },
828 |
829 | get w () {
830 |
831 | return this._w;
832 |
833 | },
834 |
835 | set w ( value ) {
836 |
837 | this._w = value;
838 | this.onChangeCallback();
839 |
840 | },
841 |
842 | set: function ( x, y, z, w ) {
843 |
844 | this._x = x;
845 | this._y = y;
846 | this._z = z;
847 | this._w = w;
848 |
849 | this.onChangeCallback();
850 |
851 | return this;
852 |
853 | },
854 |
855 | copy: function ( quaternion ) {
856 |
857 | this._x = quaternion.x;
858 | this._y = quaternion.y;
859 | this._z = quaternion.z;
860 | this._w = quaternion.w;
861 |
862 | this.onChangeCallback();
863 |
864 | return this;
865 |
866 | },
867 |
868 | setFromEuler: function ( euler, update ) {
869 |
870 | if ( euler instanceof THREE.Euler === false ) {
871 |
872 | throw new Error( 'THREE.Quaternion: .setFromEuler() now expects a Euler rotation rather than a Vector3 and order.' );
873 | }
874 |
875 | // http://www.mathworks.com/matlabcentral/fileexchange/
876 | // 20696-function-to-convert-between-dcm-euler-angles-quaternions-and-euler-vectors/
877 | // content/SpinCalc.m
878 |
879 | var c1 = Math.cos( euler._x / 2 );
880 | var c2 = Math.cos( euler._y / 2 );
881 | var c3 = Math.cos( euler._z / 2 );
882 | var s1 = Math.sin( euler._x / 2 );
883 | var s2 = Math.sin( euler._y / 2 );
884 | var s3 = Math.sin( euler._z / 2 );
885 |
886 | if ( euler.order === 'XYZ' ) {
887 |
888 | this._x = s1 * c2 * c3 + c1 * s2 * s3;
889 | this._y = c1 * s2 * c3 - s1 * c2 * s3;
890 | this._z = c1 * c2 * s3 + s1 * s2 * c3;
891 | this._w = c1 * c2 * c3 - s1 * s2 * s3;
892 |
893 | } else if ( euler.order === 'YXZ' ) {
894 |
895 | this._x = s1 * c2 * c3 + c1 * s2 * s3;
896 | this._y = c1 * s2 * c3 - s1 * c2 * s3;
897 | this._z = c1 * c2 * s3 - s1 * s2 * c3;
898 | this._w = c1 * c2 * c3 + s1 * s2 * s3;
899 |
900 | } else if ( euler.order === 'ZXY' ) {
901 |
902 | this._x = s1 * c2 * c3 - c1 * s2 * s3;
903 | this._y = c1 * s2 * c3 + s1 * c2 * s3;
904 | this._z = c1 * c2 * s3 + s1 * s2 * c3;
905 | this._w = c1 * c2 * c3 - s1 * s2 * s3;
906 |
907 | } else if ( euler.order === 'ZYX' ) {
908 |
909 | this._x = s1 * c2 * c3 - c1 * s2 * s3;
910 | this._y = c1 * s2 * c3 + s1 * c2 * s3;
911 | this._z = c1 * c2 * s3 - s1 * s2 * c3;
912 | this._w = c1 * c2 * c3 + s1 * s2 * s3;
913 |
914 | } else if ( euler.order === 'YZX' ) {
915 |
916 | this._x = s1 * c2 * c3 + c1 * s2 * s3;
917 | this._y = c1 * s2 * c3 + s1 * c2 * s3;
918 | this._z = c1 * c2 * s3 - s1 * s2 * c3;
919 | this._w = c1 * c2 * c3 - s1 * s2 * s3;
920 |
921 | } else if ( euler.order === 'XZY' ) {
922 |
923 | this._x = s1 * c2 * c3 - c1 * s2 * s3;
924 | this._y = c1 * s2 * c3 - s1 * c2 * s3;
925 | this._z = c1 * c2 * s3 + s1 * s2 * c3;
926 | this._w = c1 * c2 * c3 + s1 * s2 * s3;
927 |
928 | }
929 |
930 | if ( update !== false ) this.onChangeCallback();
931 |
932 | return this;
933 |
934 | },
935 |
936 | setFromAxisAngle: function ( axis, angle ) {
937 |
938 | // http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToQuaternion/index.htm
939 |
940 | // assumes axis is normalized
941 |
942 | var halfAngle = angle / 2, s = Math.sin( halfAngle );
943 |
944 | this._x = axis.x * s;
945 | this._y = axis.y * s;
946 | this._z = axis.z * s;
947 | this._w = Math.cos( halfAngle );
948 |
949 | this.onChangeCallback();
950 |
951 | return this;
952 |
953 | },
954 |
955 | setFromRotationMatrix: function ( m ) {
956 |
957 | // http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion/index.htm
958 |
959 | // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
960 |
961 | var te = m.elements,
962 |
963 | m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ],
964 | m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ],
965 | m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ],
966 |
967 | trace = m11 + m22 + m33,
968 | s;
969 |
970 | if ( trace > 0 ) {
971 |
972 | s = 0.5 / Math.sqrt( trace + 1.0 );
973 |
974 | this._w = 0.25 / s;
975 | this._x = ( m32 - m23 ) * s;
976 | this._y = ( m13 - m31 ) * s;
977 | this._z = ( m21 - m12 ) * s;
978 |
979 | } else if ( m11 > m22 && m11 > m33 ) {
980 |
981 | s = 2.0 * Math.sqrt( 1.0 + m11 - m22 - m33 );
982 |
983 | this._w = ( m32 - m23 ) / s;
984 | this._x = 0.25 * s;
985 | this._y = ( m12 + m21 ) / s;
986 | this._z = ( m13 + m31 ) / s;
987 |
988 | } else if ( m22 > m33 ) {
989 |
990 | s = 2.0 * Math.sqrt( 1.0 + m22 - m11 - m33 );
991 |
992 | this._w = ( m13 - m31 ) / s;
993 | this._x = ( m12 + m21 ) / s;
994 | this._y = 0.25 * s;
995 | this._z = ( m23 + m32 ) / s;
996 |
997 | } else {
998 |
999 | s = 2.0 * Math.sqrt( 1.0 + m33 - m11 - m22 );
1000 |
1001 | this._w = ( m21 - m12 ) / s;
1002 | this._x = ( m13 + m31 ) / s;
1003 | this._y = ( m23 + m32 ) / s;
1004 | this._z = 0.25 * s;
1005 |
1006 | }
1007 |
1008 | this.onChangeCallback();
1009 |
1010 | return this;
1011 |
1012 | },
1013 |
1014 | setFromUnitVectors: function () {
1015 |
1016 | // http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final
1017 |
1018 | // assumes direction vectors vFrom and vTo are normalized
1019 |
1020 | var v1, r;
1021 |
1022 | var EPS = 0.000001;
1023 |
1024 | return function ( vFrom, vTo ) {
1025 |
1026 | if ( v1 === undefined ) v1 = new THREE.Vector3();
1027 |
1028 | r = vFrom.dot( vTo ) + 1;
1029 |
1030 | if ( r < EPS ) {
1031 |
1032 | r = 0;
1033 |
1034 | if ( Math.abs( vFrom.x ) > Math.abs( vFrom.z ) ) {
1035 |
1036 | v1.set( - vFrom.y, vFrom.x, 0 );
1037 |
1038 | } else {
1039 |
1040 | v1.set( 0, - vFrom.z, vFrom.y );
1041 |
1042 | }
1043 |
1044 | } else {
1045 |
1046 | v1.crossVectors( vFrom, vTo );
1047 |
1048 | }
1049 |
1050 | this._x = v1.x;
1051 | this._y = v1.y;
1052 | this._z = v1.z;
1053 | this._w = r;
1054 |
1055 | this.normalize();
1056 |
1057 | return this;
1058 |
1059 | }
1060 |
1061 | }(),
1062 |
1063 | inverse: function () {
1064 |
1065 | this.conjugate().normalize();
1066 |
1067 | return this;
1068 |
1069 | },
1070 |
1071 | conjugate: function () {
1072 |
1073 | this._x *= - 1;
1074 | this._y *= - 1;
1075 | this._z *= - 1;
1076 |
1077 | this.onChangeCallback();
1078 |
1079 | return this;
1080 |
1081 | },
1082 |
1083 | dot: function ( v ) {
1084 |
1085 | return this._x * v._x + this._y * v._y + this._z * v._z + this._w * v._w;
1086 |
1087 | },
1088 |
1089 | lengthSq: function () {
1090 |
1091 | return this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w;
1092 |
1093 | },
1094 |
1095 | length: function () {
1096 |
1097 | return Math.sqrt( this._x * this._x + this._y * this._y + this._z * this._z + this._w * this._w );
1098 |
1099 | },
1100 |
1101 | normalize: function () {
1102 |
1103 | var l = this.length();
1104 |
1105 | if ( l === 0 ) {
1106 |
1107 | this._x = 0;
1108 | this._y = 0;
1109 | this._z = 0;
1110 | this._w = 1;
1111 |
1112 | } else {
1113 |
1114 | l = 1 / l;
1115 |
1116 | this._x = this._x * l;
1117 | this._y = this._y * l;
1118 | this._z = this._z * l;
1119 | this._w = this._w * l;
1120 |
1121 | }
1122 |
1123 | this.onChangeCallback();
1124 |
1125 | return this;
1126 |
1127 | },
1128 |
1129 | multiply: function ( q, p ) {
1130 |
1131 | if ( p !== undefined ) {
1132 |
1133 | console.warn( 'THREE.Quaternion: .multiply() now only accepts one argument. Use .multiplyQuaternions( a, b ) instead.' );
1134 | return this.multiplyQuaternions( q, p );
1135 |
1136 | }
1137 |
1138 | return this.multiplyQuaternions( this, q );
1139 |
1140 | },
1141 |
1142 | multiplyQuaternions: function ( a, b ) {
1143 |
1144 | // from http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/code/index.htm
1145 |
1146 | var qax = a._x, qay = a._y, qaz = a._z, qaw = a._w;
1147 | var qbx = b._x, qby = b._y, qbz = b._z, qbw = b._w;
1148 |
1149 | this._x = qax * qbw + qaw * qbx + qay * qbz - qaz * qby;
1150 | this._y = qay * qbw + qaw * qby + qaz * qbx - qax * qbz;
1151 | this._z = qaz * qbw + qaw * qbz + qax * qby - qay * qbx;
1152 | this._w = qaw * qbw - qax * qbx - qay * qby - qaz * qbz;
1153 |
1154 | this.onChangeCallback();
1155 |
1156 | return this;
1157 |
1158 | },
1159 |
1160 | multiplyVector3: function ( vector ) {
1161 |
1162 | console.warn( 'THREE.Quaternion: .multiplyVector3() has been removed. Use is now vector.applyQuaternion( quaternion ) instead.' );
1163 | return vector.applyQuaternion( this );
1164 |
1165 | },
1166 |
1167 | slerp: function ( qb, t ) {
1168 |
1169 | if ( t === 0 ) return this;
1170 | if ( t === 1 ) return this.copy( qb );
1171 |
1172 | var x = this._x, y = this._y, z = this._z, w = this._w;
1173 |
1174 | // http://www.euclideanspace.com/maths/algebra/realNormedAlgebra/quaternions/slerp/
1175 |
1176 | var cosHalfTheta = w * qb._w + x * qb._x + y * qb._y + z * qb._z;
1177 |
1178 | if ( cosHalfTheta < 0 ) {
1179 |
1180 | this._w = - qb._w;
1181 | this._x = - qb._x;
1182 | this._y = - qb._y;
1183 | this._z = - qb._z;
1184 |
1185 | cosHalfTheta = - cosHalfTheta;
1186 |
1187 | } else {
1188 |
1189 | this.copy( qb );
1190 |
1191 | }
1192 |
1193 | if ( cosHalfTheta >= 1.0 ) {
1194 |
1195 | this._w = w;
1196 | this._x = x;
1197 | this._y = y;
1198 | this._z = z;
1199 |
1200 | return this;
1201 |
1202 | }
1203 |
1204 | var halfTheta = Math.acos( cosHalfTheta );
1205 | var sinHalfTheta = Math.sqrt( 1.0 - cosHalfTheta * cosHalfTheta );
1206 |
1207 | if ( Math.abs( sinHalfTheta ) < 0.001 ) {
1208 |
1209 | this._w = 0.5 * ( w + this._w );
1210 | this._x = 0.5 * ( x + this._x );
1211 | this._y = 0.5 * ( y + this._y );
1212 | this._z = 0.5 * ( z + this._z );
1213 |
1214 | return this;
1215 |
1216 | }
1217 |
1218 | var ratioA = Math.sin( ( 1 - t ) * halfTheta ) / sinHalfTheta,
1219 | ratioB = Math.sin( t * halfTheta ) / sinHalfTheta;
1220 |
1221 | this._w = ( w * ratioA + this._w * ratioB );
1222 | this._x = ( x * ratioA + this._x * ratioB );
1223 | this._y = ( y * ratioA + this._y * ratioB );
1224 | this._z = ( z * ratioA + this._z * ratioB );
1225 |
1226 | this.onChangeCallback();
1227 |
1228 | return this;
1229 |
1230 | },
1231 |
1232 | equals: function ( quaternion ) {
1233 |
1234 | return ( quaternion._x === this._x ) && ( quaternion._y === this._y ) && ( quaternion._z === this._z ) && ( quaternion._w === this._w );
1235 |
1236 | },
1237 |
1238 | fromArray: function ( array, offset ) {
1239 |
1240 | if ( offset === undefined ) offset = 0;
1241 |
1242 | this._x = array[ offset ];
1243 | this._y = array[ offset + 1 ];
1244 | this._z = array[ offset + 2 ];
1245 | this._w = array[ offset + 3 ];
1246 |
1247 | this.onChangeCallback();
1248 |
1249 | return this;
1250 |
1251 | },
1252 |
1253 | toArray: function ( array, offset ) {
1254 |
1255 | if ( array === undefined ) array = [];
1256 | if ( offset === undefined ) offset = 0;
1257 |
1258 | array[ offset ] = this._x;
1259 | array[ offset + 1 ] = this._y;
1260 | array[ offset + 2 ] = this._z;
1261 | array[ offset + 3 ] = this._w;
1262 |
1263 | return array;
1264 |
1265 | },
1266 |
1267 | onChange: function ( callback ) {
1268 |
1269 | this.onChangeCallback = callback;
1270 |
1271 | return this;
1272 |
1273 | },
1274 |
1275 | onChangeCallback: function () {},
1276 |
1277 | clone: function () {
1278 |
1279 | return new THREE.Quaternion( this._x, this._y, this._z, this._w );
1280 |
1281 | }
1282 |
1283 | };
1284 |
1285 | THREE.Quaternion.slerp = function ( qa, qb, qm, t ) {
1286 |
1287 | return qm.copy( qa ).slerp( qb, t );
1288 |
1289 | }
1290 |
1291 | /*** END Quaternion ***/
1292 | /*** START Vector2 ***/
1293 | /**
1294 | * @author mrdoob / http://mrdoob.com/
1295 | * @author philogb / http://blog.thejit.org/
1296 | * @author egraether / http://egraether.com/
1297 | * @author zz85 / http://www.lab4games.net/zz85/blog
1298 | */
1299 |
1300 | THREE.Vector2 = function ( x, y ) {
1301 |
1302 | this.x = x || 0;
1303 | this.y = y || 0;
1304 |
1305 | };
1306 |
1307 | THREE.Vector2.prototype = {
1308 |
1309 | constructor: THREE.Vector2,
1310 |
1311 | set: function ( x, y ) {
1312 |
1313 | this.x = x;
1314 | this.y = y;
1315 |
1316 | return this;
1317 |
1318 | },
1319 |
1320 | setX: function ( x ) {
1321 |
1322 | this.x = x;
1323 |
1324 | return this;
1325 |
1326 | },
1327 |
1328 | setY: function ( y ) {
1329 |
1330 | this.y = y;
1331 |
1332 | return this;
1333 |
1334 | },
1335 |
1336 | setComponent: function ( index, value ) {
1337 |
1338 | switch ( index ) {
1339 |
1340 | case 0: this.x = value; break;
1341 | case 1: this.y = value; break;
1342 | default: throw new Error( 'index is out of range: ' + index );
1343 |
1344 | }
1345 |
1346 | },
1347 |
1348 | getComponent: function ( index ) {
1349 |
1350 | switch ( index ) {
1351 |
1352 | case 0: return this.x;
1353 | case 1: return this.y;
1354 | default: throw new Error( 'index is out of range: ' + index );
1355 |
1356 | }
1357 |
1358 | },
1359 |
1360 | copy: function ( v ) {
1361 |
1362 | this.x = v.x;
1363 | this.y = v.y;
1364 |
1365 | return this;
1366 |
1367 | },
1368 |
1369 | add: function ( v, w ) {
1370 |
1371 | if ( w !== undefined ) {
1372 |
1373 | console.warn( 'THREE.Vector2: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
1374 | return this.addVectors( v, w );
1375 |
1376 | }
1377 |
1378 | this.x += v.x;
1379 | this.y += v.y;
1380 |
1381 | return this;
1382 |
1383 | },
1384 |
1385 | addVectors: function ( a, b ) {
1386 |
1387 | this.x = a.x + b.x;
1388 | this.y = a.y + b.y;
1389 |
1390 | return this;
1391 |
1392 | },
1393 |
1394 | addScalar: function ( s ) {
1395 |
1396 | this.x += s;
1397 | this.y += s;
1398 |
1399 | return this;
1400 |
1401 | },
1402 |
1403 | sub: function ( v, w ) {
1404 |
1405 | if ( w !== undefined ) {
1406 |
1407 | console.warn( 'THREE.Vector2: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
1408 | return this.subVectors( v, w );
1409 |
1410 | }
1411 |
1412 | this.x -= v.x;
1413 | this.y -= v.y;
1414 |
1415 | return this;
1416 |
1417 | },
1418 |
1419 | subVectors: function ( a, b ) {
1420 |
1421 | this.x = a.x - b.x;
1422 | this.y = a.y - b.y;
1423 |
1424 | return this;
1425 |
1426 | },
1427 |
1428 | multiply: function ( v ) {
1429 |
1430 | this.x *= v.x;
1431 | this.y *= v.y;
1432 |
1433 | return this;
1434 |
1435 | },
1436 |
1437 | multiplyScalar: function ( s ) {
1438 |
1439 | this.x *= s;
1440 | this.y *= s;
1441 |
1442 | return this;
1443 |
1444 | },
1445 |
1446 | divide: function ( v ) {
1447 |
1448 | this.x /= v.x;
1449 | this.y /= v.y;
1450 |
1451 | return this;
1452 |
1453 | },
1454 |
1455 | divideScalar: function ( scalar ) {
1456 |
1457 | if ( scalar !== 0 ) {
1458 |
1459 | var invScalar = 1 / scalar;
1460 |
1461 | this.x *= invScalar;
1462 | this.y *= invScalar;
1463 |
1464 | } else {
1465 |
1466 | this.x = 0;
1467 | this.y = 0;
1468 |
1469 | }
1470 |
1471 | return this;
1472 |
1473 | },
1474 |
1475 | min: function ( v ) {
1476 |
1477 | if ( this.x > v.x ) {
1478 |
1479 | this.x = v.x;
1480 |
1481 | }
1482 |
1483 | if ( this.y > v.y ) {
1484 |
1485 | this.y = v.y;
1486 |
1487 | }
1488 |
1489 | return this;
1490 |
1491 | },
1492 |
1493 | max: function ( v ) {
1494 |
1495 | if ( this.x < v.x ) {
1496 |
1497 | this.x = v.x;
1498 |
1499 | }
1500 |
1501 | if ( this.y < v.y ) {
1502 |
1503 | this.y = v.y;
1504 |
1505 | }
1506 |
1507 | return this;
1508 |
1509 | },
1510 |
1511 | clamp: function ( min, max ) {
1512 |
1513 | // This function assumes min < max, if this assumption isn't true it will not operate correctly
1514 |
1515 | if ( this.x < min.x ) {
1516 |
1517 | this.x = min.x;
1518 |
1519 | } else if ( this.x > max.x ) {
1520 |
1521 | this.x = max.x;
1522 |
1523 | }
1524 |
1525 | if ( this.y < min.y ) {
1526 |
1527 | this.y = min.y;
1528 |
1529 | } else if ( this.y > max.y ) {
1530 |
1531 | this.y = max.y;
1532 |
1533 | }
1534 |
1535 | return this;
1536 | },
1537 |
1538 | clampScalar: ( function () {
1539 |
1540 | var min, max;
1541 |
1542 | return function ( minVal, maxVal ) {
1543 |
1544 | if ( min === undefined ) {
1545 |
1546 | min = new THREE.Vector2();
1547 | max = new THREE.Vector2();
1548 |
1549 | }
1550 |
1551 | min.set( minVal, minVal );
1552 | max.set( maxVal, maxVal );
1553 |
1554 | return this.clamp( min, max );
1555 |
1556 | };
1557 |
1558 | } )(),
1559 |
1560 | floor: function () {
1561 |
1562 | this.x = Math.floor( this.x );
1563 | this.y = Math.floor( this.y );
1564 |
1565 | return this;
1566 |
1567 | },
1568 |
1569 | ceil: function () {
1570 |
1571 | this.x = Math.ceil( this.x );
1572 | this.y = Math.ceil( this.y );
1573 |
1574 | return this;
1575 |
1576 | },
1577 |
1578 | round: function () {
1579 |
1580 | this.x = Math.round( this.x );
1581 | this.y = Math.round( this.y );
1582 |
1583 | return this;
1584 |
1585 | },
1586 |
1587 | roundToZero: function () {
1588 |
1589 | this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
1590 | this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
1591 |
1592 | return this;
1593 |
1594 | },
1595 |
1596 | negate: function () {
1597 |
1598 | this.x = - this.x;
1599 | this.y = - this.y;
1600 |
1601 | return this;
1602 |
1603 | },
1604 |
1605 | dot: function ( v ) {
1606 |
1607 | return this.x * v.x + this.y * v.y;
1608 |
1609 | },
1610 |
1611 | lengthSq: function () {
1612 |
1613 | return this.x * this.x + this.y * this.y;
1614 |
1615 | },
1616 |
1617 | length: function () {
1618 |
1619 | return Math.sqrt( this.x * this.x + this.y * this.y );
1620 |
1621 | },
1622 |
1623 | normalize: function () {
1624 |
1625 | return this.divideScalar( this.length() );
1626 |
1627 | },
1628 |
1629 | distanceTo: function ( v ) {
1630 |
1631 | return Math.sqrt( this.distanceToSquared( v ) );
1632 |
1633 | },
1634 |
1635 | distanceToSquared: function ( v ) {
1636 |
1637 | var dx = this.x - v.x, dy = this.y - v.y;
1638 | return dx * dx + dy * dy;
1639 |
1640 | },
1641 |
1642 | setLength: function ( l ) {
1643 |
1644 | var oldLength = this.length();
1645 |
1646 | if ( oldLength !== 0 && l !== oldLength ) {
1647 |
1648 | this.multiplyScalar( l / oldLength );
1649 | }
1650 |
1651 | return this;
1652 |
1653 | },
1654 |
1655 | lerp: function ( v, alpha ) {
1656 |
1657 | this.x += ( v.x - this.x ) * alpha;
1658 | this.y += ( v.y - this.y ) * alpha;
1659 |
1660 | return this;
1661 |
1662 | },
1663 |
1664 | equals: function ( v ) {
1665 |
1666 | return ( ( v.x === this.x ) && ( v.y === this.y ) );
1667 |
1668 | },
1669 |
1670 | fromArray: function ( array, offset ) {
1671 |
1672 | if ( offset === undefined ) offset = 0;
1673 |
1674 | this.x = array[ offset ];
1675 | this.y = array[ offset + 1 ];
1676 |
1677 | return this;
1678 |
1679 | },
1680 |
1681 | toArray: function ( array, offset ) {
1682 |
1683 | if ( array === undefined ) array = [];
1684 | if ( offset === undefined ) offset = 0;
1685 |
1686 | array[ offset ] = this.x;
1687 | array[ offset + 1 ] = this.y;
1688 |
1689 | return array;
1690 |
1691 | },
1692 |
1693 | fromAttribute: function ( attribute, index, offset ) {
1694 |
1695 | if ( offset === undefined ) offset = 0;
1696 |
1697 | index = index * attribute.itemSize + offset;
1698 |
1699 | this.x = attribute.array[ index ];
1700 | this.y = attribute.array[ index + 1 ];
1701 |
1702 | return this;
1703 |
1704 | },
1705 |
1706 | clone: function () {
1707 |
1708 | return new THREE.Vector2( this.x, this.y );
1709 |
1710 | }
1711 |
1712 | };
1713 | /*** END Vector2 ***/
1714 | /*** START Vector3 ***/
1715 |
1716 | /**
1717 | * @author mrdoob / http://mrdoob.com/
1718 | * @author *kile / http://kile.stravaganza.org/
1719 | * @author philogb / http://blog.thejit.org/
1720 | * @author mikael emtinger / http://gomo.se/
1721 | * @author egraether / http://egraether.com/
1722 | * @author WestLangley / http://github.com/WestLangley
1723 | */
1724 |
1725 | THREE.Vector3 = function ( x, y, z ) {
1726 |
1727 | this.x = x || 0;
1728 | this.y = y || 0;
1729 | this.z = z || 0;
1730 |
1731 | };
1732 |
1733 | THREE.Vector3.prototype = {
1734 |
1735 | constructor: THREE.Vector3,
1736 |
1737 | set: function ( x, y, z ) {
1738 |
1739 | this.x = x;
1740 | this.y = y;
1741 | this.z = z;
1742 |
1743 | return this;
1744 |
1745 | },
1746 |
1747 | setX: function ( x ) {
1748 |
1749 | this.x = x;
1750 |
1751 | return this;
1752 |
1753 | },
1754 |
1755 | setY: function ( y ) {
1756 |
1757 | this.y = y;
1758 |
1759 | return this;
1760 |
1761 | },
1762 |
1763 | setZ: function ( z ) {
1764 |
1765 | this.z = z;
1766 |
1767 | return this;
1768 |
1769 | },
1770 |
1771 | setComponent: function ( index, value ) {
1772 |
1773 | switch ( index ) {
1774 |
1775 | case 0: this.x = value; break;
1776 | case 1: this.y = value; break;
1777 | case 2: this.z = value; break;
1778 | default: throw new Error( 'index is out of range: ' + index );
1779 |
1780 | }
1781 |
1782 | },
1783 |
1784 | getComponent: function ( index ) {
1785 |
1786 | switch ( index ) {
1787 |
1788 | case 0: return this.x;
1789 | case 1: return this.y;
1790 | case 2: return this.z;
1791 | default: throw new Error( 'index is out of range: ' + index );
1792 |
1793 | }
1794 |
1795 | },
1796 |
1797 | copy: function ( v ) {
1798 |
1799 | this.x = v.x;
1800 | this.y = v.y;
1801 | this.z = v.z;
1802 |
1803 | return this;
1804 |
1805 | },
1806 |
1807 | add: function ( v, w ) {
1808 |
1809 | if ( w !== undefined ) {
1810 |
1811 | console.warn( 'THREE.Vector3: .add() now only accepts one argument. Use .addVectors( a, b ) instead.' );
1812 | return this.addVectors( v, w );
1813 |
1814 | }
1815 |
1816 | this.x += v.x;
1817 | this.y += v.y;
1818 | this.z += v.z;
1819 |
1820 | return this;
1821 |
1822 | },
1823 |
1824 | addScalar: function ( s ) {
1825 |
1826 | this.x += s;
1827 | this.y += s;
1828 | this.z += s;
1829 |
1830 | return this;
1831 |
1832 | },
1833 |
1834 | addVectors: function ( a, b ) {
1835 |
1836 | this.x = a.x + b.x;
1837 | this.y = a.y + b.y;
1838 | this.z = a.z + b.z;
1839 |
1840 | return this;
1841 |
1842 | },
1843 |
1844 | sub: function ( v, w ) {
1845 |
1846 | if ( w !== undefined ) {
1847 |
1848 | console.warn( 'THREE.Vector3: .sub() now only accepts one argument. Use .subVectors( a, b ) instead.' );
1849 | return this.subVectors( v, w );
1850 |
1851 | }
1852 |
1853 | this.x -= v.x;
1854 | this.y -= v.y;
1855 | this.z -= v.z;
1856 |
1857 | return this;
1858 |
1859 | },
1860 |
1861 | subVectors: function ( a, b ) {
1862 |
1863 | this.x = a.x - b.x;
1864 | this.y = a.y - b.y;
1865 | this.z = a.z - b.z;
1866 |
1867 | return this;
1868 |
1869 | },
1870 |
1871 | multiply: function ( v, w ) {
1872 |
1873 | if ( w !== undefined ) {
1874 |
1875 | console.warn( 'THREE.Vector3: .multiply() now only accepts one argument. Use .multiplyVectors( a, b ) instead.' );
1876 | return this.multiplyVectors( v, w );
1877 |
1878 | }
1879 |
1880 | this.x *= v.x;
1881 | this.y *= v.y;
1882 | this.z *= v.z;
1883 |
1884 | return this;
1885 |
1886 | },
1887 |
1888 | multiplyScalar: function ( scalar ) {
1889 |
1890 | this.x *= scalar;
1891 | this.y *= scalar;
1892 | this.z *= scalar;
1893 |
1894 | return this;
1895 |
1896 | },
1897 |
1898 | multiplyVectors: function ( a, b ) {
1899 |
1900 | this.x = a.x * b.x;
1901 | this.y = a.y * b.y;
1902 | this.z = a.z * b.z;
1903 |
1904 | return this;
1905 |
1906 | },
1907 |
1908 | applyEuler: function () {
1909 |
1910 | var quaternion;
1911 |
1912 | return function ( euler ) {
1913 |
1914 | if ( euler instanceof THREE.Euler === false ) {
1915 |
1916 | console.error( 'THREE.Vector3: .applyEuler() now expects a Euler rotation rather than a Vector3 and order.' );
1917 |
1918 | }
1919 |
1920 | if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
1921 |
1922 | this.applyQuaternion( quaternion.setFromEuler( euler ) );
1923 |
1924 | return this;
1925 |
1926 | };
1927 |
1928 | }(),
1929 |
1930 | applyAxisAngle: function () {
1931 |
1932 | var quaternion;
1933 |
1934 | return function ( axis, angle ) {
1935 |
1936 | if ( quaternion === undefined ) quaternion = new THREE.Quaternion();
1937 |
1938 | this.applyQuaternion( quaternion.setFromAxisAngle( axis, angle ) );
1939 |
1940 | return this;
1941 |
1942 | };
1943 |
1944 | }(),
1945 |
1946 | applyMatrix3: function ( m ) {
1947 |
1948 | var x = this.x;
1949 | var y = this.y;
1950 | var z = this.z;
1951 |
1952 | var e = m.elements;
1953 |
1954 | this.x = e[ 0 ] * x + e[ 3 ] * y + e[ 6 ] * z;
1955 | this.y = e[ 1 ] * x + e[ 4 ] * y + e[ 7 ] * z;
1956 | this.z = e[ 2 ] * x + e[ 5 ] * y + e[ 8 ] * z;
1957 |
1958 | return this;
1959 |
1960 | },
1961 |
1962 | applyMatrix4: function ( m ) {
1963 |
1964 | // input: THREE.Matrix4 affine matrix
1965 |
1966 | var x = this.x, y = this.y, z = this.z;
1967 |
1968 | var e = m.elements;
1969 |
1970 | this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ];
1971 | this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ];
1972 | this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ];
1973 |
1974 | return this;
1975 |
1976 | },
1977 |
1978 | applyProjection: function ( m ) {
1979 |
1980 | // input: THREE.Matrix4 projection matrix
1981 |
1982 | var x = this.x, y = this.y, z = this.z;
1983 |
1984 | var e = m.elements;
1985 | var d = 1 / ( e[ 3 ] * x + e[ 7 ] * y + e[ 11 ] * z + e[ 15 ] ); // perspective divide
1986 |
1987 | this.x = ( e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z + e[ 12 ] ) * d;
1988 | this.y = ( e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z + e[ 13 ] ) * d;
1989 | this.z = ( e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z + e[ 14 ] ) * d;
1990 |
1991 | return this;
1992 |
1993 | },
1994 |
1995 | applyQuaternion: function ( q ) {
1996 |
1997 | var x = this.x;
1998 | var y = this.y;
1999 | var z = this.z;
2000 |
2001 | var qx = q.x;
2002 | var qy = q.y;
2003 | var qz = q.z;
2004 | var qw = q.w;
2005 |
2006 | // calculate quat * vector
2007 |
2008 | var ix = qw * x + qy * z - qz * y;
2009 | var iy = qw * y + qz * x - qx * z;
2010 | var iz = qw * z + qx * y - qy * x;
2011 | var iw = - qx * x - qy * y - qz * z;
2012 |
2013 | // calculate result * inverse quat
2014 |
2015 | this.x = ix * qw + iw * - qx + iy * - qz - iz * - qy;
2016 | this.y = iy * qw + iw * - qy + iz * - qx - ix * - qz;
2017 | this.z = iz * qw + iw * - qz + ix * - qy - iy * - qx;
2018 |
2019 | return this;
2020 |
2021 | },
2022 |
2023 | project: function () {
2024 |
2025 | var matrix;
2026 |
2027 | return function ( camera ) {
2028 |
2029 | if ( matrix === undefined ) matrix = new THREE.Matrix4();
2030 |
2031 | matrix.multiplyMatrices( camera.projectionMatrix, matrix.getInverse( camera.matrixWorld ) );
2032 | return this.applyProjection( matrix );
2033 |
2034 | };
2035 |
2036 | }(),
2037 |
2038 | unproject: function () {
2039 |
2040 | var matrix;
2041 |
2042 | return function ( camera ) {
2043 |
2044 | if ( matrix === undefined ) matrix = new THREE.Matrix4();
2045 |
2046 | matrix.multiplyMatrices( camera.matrixWorld, matrix.getInverse( camera.projectionMatrix ) );
2047 | return this.applyProjection( matrix );
2048 |
2049 | };
2050 |
2051 | }(),
2052 |
2053 | transformDirection: function ( m ) {
2054 |
2055 | // input: THREE.Matrix4 affine matrix
2056 | // vector interpreted as a direction
2057 |
2058 | var x = this.x, y = this.y, z = this.z;
2059 |
2060 | var e = m.elements;
2061 |
2062 | this.x = e[ 0 ] * x + e[ 4 ] * y + e[ 8 ] * z;
2063 | this.y = e[ 1 ] * x + e[ 5 ] * y + e[ 9 ] * z;
2064 | this.z = e[ 2 ] * x + e[ 6 ] * y + e[ 10 ] * z;
2065 |
2066 | this.normalize();
2067 |
2068 | return this;
2069 |
2070 | },
2071 |
2072 | divide: function ( v ) {
2073 |
2074 | this.x /= v.x;
2075 | this.y /= v.y;
2076 | this.z /= v.z;
2077 |
2078 | return this;
2079 |
2080 | },
2081 |
2082 | divideScalar: function ( scalar ) {
2083 |
2084 | if ( scalar !== 0 ) {
2085 |
2086 | var invScalar = 1 / scalar;
2087 |
2088 | this.x *= invScalar;
2089 | this.y *= invScalar;
2090 | this.z *= invScalar;
2091 |
2092 | } else {
2093 |
2094 | this.x = 0;
2095 | this.y = 0;
2096 | this.z = 0;
2097 |
2098 | }
2099 |
2100 | return this;
2101 |
2102 | },
2103 |
2104 | min: function ( v ) {
2105 |
2106 | if ( this.x > v.x ) {
2107 |
2108 | this.x = v.x;
2109 |
2110 | }
2111 |
2112 | if ( this.y > v.y ) {
2113 |
2114 | this.y = v.y;
2115 |
2116 | }
2117 |
2118 | if ( this.z > v.z ) {
2119 |
2120 | this.z = v.z;
2121 |
2122 | }
2123 |
2124 | return this;
2125 |
2126 | },
2127 |
2128 | max: function ( v ) {
2129 |
2130 | if ( this.x < v.x ) {
2131 |
2132 | this.x = v.x;
2133 |
2134 | }
2135 |
2136 | if ( this.y < v.y ) {
2137 |
2138 | this.y = v.y;
2139 |
2140 | }
2141 |
2142 | if ( this.z < v.z ) {
2143 |
2144 | this.z = v.z;
2145 |
2146 | }
2147 |
2148 | return this;
2149 |
2150 | },
2151 |
2152 | clamp: function ( min, max ) {
2153 |
2154 | // This function assumes min < max, if this assumption isn't true it will not operate correctly
2155 |
2156 | if ( this.x < min.x ) {
2157 |
2158 | this.x = min.x;
2159 |
2160 | } else if ( this.x > max.x ) {
2161 |
2162 | this.x = max.x;
2163 |
2164 | }
2165 |
2166 | if ( this.y < min.y ) {
2167 |
2168 | this.y = min.y;
2169 |
2170 | } else if ( this.y > max.y ) {
2171 |
2172 | this.y = max.y;
2173 |
2174 | }
2175 |
2176 | if ( this.z < min.z ) {
2177 |
2178 | this.z = min.z;
2179 |
2180 | } else if ( this.z > max.z ) {
2181 |
2182 | this.z = max.z;
2183 |
2184 | }
2185 |
2186 | return this;
2187 |
2188 | },
2189 |
2190 | clampScalar: ( function () {
2191 |
2192 | var min, max;
2193 |
2194 | return function ( minVal, maxVal ) {
2195 |
2196 | if ( min === undefined ) {
2197 |
2198 | min = new THREE.Vector3();
2199 | max = new THREE.Vector3();
2200 |
2201 | }
2202 |
2203 | min.set( minVal, minVal, minVal );
2204 | max.set( maxVal, maxVal, maxVal );
2205 |
2206 | return this.clamp( min, max );
2207 |
2208 | };
2209 |
2210 | } )(),
2211 |
2212 | floor: function () {
2213 |
2214 | this.x = Math.floor( this.x );
2215 | this.y = Math.floor( this.y );
2216 | this.z = Math.floor( this.z );
2217 |
2218 | return this;
2219 |
2220 | },
2221 |
2222 | ceil: function () {
2223 |
2224 | this.x = Math.ceil( this.x );
2225 | this.y = Math.ceil( this.y );
2226 | this.z = Math.ceil( this.z );
2227 |
2228 | return this;
2229 |
2230 | },
2231 |
2232 | round: function () {
2233 |
2234 | this.x = Math.round( this.x );
2235 | this.y = Math.round( this.y );
2236 | this.z = Math.round( this.z );
2237 |
2238 | return this;
2239 |
2240 | },
2241 |
2242 | roundToZero: function () {
2243 |
2244 | this.x = ( this.x < 0 ) ? Math.ceil( this.x ) : Math.floor( this.x );
2245 | this.y = ( this.y < 0 ) ? Math.ceil( this.y ) : Math.floor( this.y );
2246 | this.z = ( this.z < 0 ) ? Math.ceil( this.z ) : Math.floor( this.z );
2247 |
2248 | return this;
2249 |
2250 | },
2251 |
2252 | negate: function () {
2253 |
2254 | this.x = - this.x;
2255 | this.y = - this.y;
2256 | this.z = - this.z;
2257 |
2258 | return this;
2259 |
2260 | },
2261 |
2262 | dot: function ( v ) {
2263 |
2264 | return this.x * v.x + this.y * v.y + this.z * v.z;
2265 |
2266 | },
2267 |
2268 | lengthSq: function () {
2269 |
2270 | return this.x * this.x + this.y * this.y + this.z * this.z;
2271 |
2272 | },
2273 |
2274 | length: function () {
2275 |
2276 | return Math.sqrt( this.x * this.x + this.y * this.y + this.z * this.z );
2277 |
2278 | },
2279 |
2280 | lengthManhattan: function () {
2281 |
2282 | return Math.abs( this.x ) + Math.abs( this.y ) + Math.abs( this.z );
2283 |
2284 | },
2285 |
2286 | normalize: function () {
2287 |
2288 | return this.divideScalar( this.length() );
2289 |
2290 | },
2291 |
2292 | setLength: function ( l ) {
2293 |
2294 | var oldLength = this.length();
2295 |
2296 | if ( oldLength !== 0 && l !== oldLength ) {
2297 |
2298 | this.multiplyScalar( l / oldLength );
2299 | }
2300 |
2301 | return this;
2302 |
2303 | },
2304 |
2305 | lerp: function ( v, alpha ) {
2306 |
2307 | this.x += ( v.x - this.x ) * alpha;
2308 | this.y += ( v.y - this.y ) * alpha;
2309 | this.z += ( v.z - this.z ) * alpha;
2310 |
2311 | return this;
2312 |
2313 | },
2314 |
2315 | cross: function ( v, w ) {
2316 |
2317 | if ( w !== undefined ) {
2318 |
2319 | console.warn( 'THREE.Vector3: .cross() now only accepts one argument. Use .crossVectors( a, b ) instead.' );
2320 | return this.crossVectors( v, w );
2321 |
2322 | }
2323 |
2324 | var x = this.x, y = this.y, z = this.z;
2325 |
2326 | this.x = y * v.z - z * v.y;
2327 | this.y = z * v.x - x * v.z;
2328 | this.z = x * v.y - y * v.x;
2329 |
2330 | return this;
2331 |
2332 | },
2333 |
2334 | crossVectors: function ( a, b ) {
2335 |
2336 | var ax = a.x, ay = a.y, az = a.z;
2337 | var bx = b.x, by = b.y, bz = b.z;
2338 |
2339 | this.x = ay * bz - az * by;
2340 | this.y = az * bx - ax * bz;
2341 | this.z = ax * by - ay * bx;
2342 |
2343 | return this;
2344 |
2345 | },
2346 |
2347 | projectOnVector: function () {
2348 |
2349 | var v1, dot;
2350 |
2351 | return function ( vector ) {
2352 |
2353 | if ( v1 === undefined ) v1 = new THREE.Vector3();
2354 |
2355 | v1.copy( vector ).normalize();
2356 |
2357 | dot = this.dot( v1 );
2358 |
2359 | return this.copy( v1 ).multiplyScalar( dot );
2360 |
2361 | };
2362 |
2363 | }(),
2364 |
2365 | projectOnPlane: function () {
2366 |
2367 | var v1;
2368 |
2369 | return function ( planeNormal ) {
2370 |
2371 | if ( v1 === undefined ) v1 = new THREE.Vector3();
2372 |
2373 | v1.copy( this ).projectOnVector( planeNormal );
2374 |
2375 | return this.sub( v1 );
2376 |
2377 | }
2378 |
2379 | }(),
2380 |
2381 | reflect: function () {
2382 |
2383 | // reflect incident vector off plane orthogonal to normal
2384 | // normal is assumed to have unit length
2385 |
2386 | var v1;
2387 |
2388 | return function ( normal ) {
2389 |
2390 | if ( v1 === undefined ) v1 = new THREE.Vector3();
2391 |
2392 | return this.sub( v1.copy( normal ).multiplyScalar( 2 * this.dot( normal ) ) );
2393 |
2394 | }
2395 |
2396 | }(),
2397 |
2398 | angleTo: function ( v ) {
2399 |
2400 | var theta = this.dot( v ) / ( this.length() * v.length() );
2401 |
2402 | // clamp, to handle numerical problems
2403 |
2404 | return Math.acos( THREE.Math.clamp( theta, - 1, 1 ) );
2405 |
2406 | },
2407 |
2408 | distanceTo: function ( v ) {
2409 |
2410 | return Math.sqrt( this.distanceToSquared( v ) );
2411 |
2412 | },
2413 |
2414 | distanceToSquared: function ( v ) {
2415 |
2416 | var dx = this.x - v.x;
2417 | var dy = this.y - v.y;
2418 | var dz = this.z - v.z;
2419 |
2420 | return dx * dx + dy * dy + dz * dz;
2421 |
2422 | },
2423 |
2424 | setEulerFromRotationMatrix: function ( m, order ) {
2425 |
2426 | console.error( 'THREE.Vector3: .setEulerFromRotationMatrix() has been removed. Use Euler.setFromRotationMatrix() instead.' );
2427 |
2428 | },
2429 |
2430 | setEulerFromQuaternion: function ( q, order ) {
2431 |
2432 | console.error( 'THREE.Vector3: .setEulerFromQuaternion() has been removed. Use Euler.setFromQuaternion() instead.' );
2433 |
2434 | },
2435 |
2436 | getPositionFromMatrix: function ( m ) {
2437 |
2438 | console.warn( 'THREE.Vector3: .getPositionFromMatrix() has been renamed to .setFromMatrixPosition().' );
2439 |
2440 | return this.setFromMatrixPosition( m );
2441 |
2442 | },
2443 |
2444 | getScaleFromMatrix: function ( m ) {
2445 |
2446 | console.warn( 'THREE.Vector3: .getScaleFromMatrix() has been renamed to .setFromMatrixScale().' );
2447 |
2448 | return this.setFromMatrixScale( m );
2449 | },
2450 |
2451 | getColumnFromMatrix: function ( index, matrix ) {
2452 |
2453 | console.warn( 'THREE.Vector3: .getColumnFromMatrix() has been renamed to .setFromMatrixColumn().' );
2454 |
2455 | return this.setFromMatrixColumn( index, matrix );
2456 |
2457 | },
2458 |
2459 | setFromMatrixPosition: function ( m ) {
2460 |
2461 | this.x = m.elements[ 12 ];
2462 | this.y = m.elements[ 13 ];
2463 | this.z = m.elements[ 14 ];
2464 |
2465 | return this;
2466 |
2467 | },
2468 |
2469 | setFromMatrixScale: function ( m ) {
2470 |
2471 | var sx = this.set( m.elements[ 0 ], m.elements[ 1 ], m.elements[ 2 ] ).length();
2472 | var sy = this.set( m.elements[ 4 ], m.elements[ 5 ], m.elements[ 6 ] ).length();
2473 | var sz = this.set( m.elements[ 8 ], m.elements[ 9 ], m.elements[ 10 ] ).length();
2474 |
2475 | this.x = sx;
2476 | this.y = sy;
2477 | this.z = sz;
2478 |
2479 | return this;
2480 | },
2481 |
2482 | setFromMatrixColumn: function ( index, matrix ) {
2483 |
2484 | var offset = index * 4;
2485 |
2486 | var me = matrix.elements;
2487 |
2488 | this.x = me[ offset ];
2489 | this.y = me[ offset + 1 ];
2490 | this.z = me[ offset + 2 ];
2491 |
2492 | return this;
2493 |
2494 | },
2495 |
2496 | equals: function ( v ) {
2497 |
2498 | return ( ( v.x === this.x ) && ( v.y === this.y ) && ( v.z === this.z ) );
2499 |
2500 | },
2501 |
2502 | fromArray: function ( array, offset ) {
2503 |
2504 | if ( offset === undefined ) offset = 0;
2505 |
2506 | this.x = array[ offset ];
2507 | this.y = array[ offset + 1 ];
2508 | this.z = array[ offset + 2 ];
2509 |
2510 | return this;
2511 |
2512 | },
2513 |
2514 | toArray: function ( array, offset ) {
2515 |
2516 | if ( array === undefined ) array = [];
2517 | if ( offset === undefined ) offset = 0;
2518 |
2519 | array[ offset ] = this.x;
2520 | array[ offset + 1 ] = this.y;
2521 | array[ offset + 2 ] = this.z;
2522 |
2523 | return array;
2524 |
2525 | },
2526 |
2527 | fromAttribute: function ( attribute, index, offset ) {
2528 |
2529 | if ( offset === undefined ) offset = 0;
2530 |
2531 | index = index * attribute.itemSize + offset;
2532 |
2533 | this.x = attribute.array[ index ];
2534 | this.y = attribute.array[ index + 1 ];
2535 | this.z = attribute.array[ index + 2 ];
2536 |
2537 | return this;
2538 |
2539 | },
2540 |
2541 | clone: function () {
2542 |
2543 | return new THREE.Vector3( this.x, this.y, this.z );
2544 |
2545 | }
2546 |
2547 | };
2548 | /*** END Vector3 ***/
2549 | /*** START Euler ***/
2550 | /**
2551 | * @author mrdoob / http://mrdoob.com/
2552 | * @author WestLangley / http://github.com/WestLangley
2553 | * @author bhouston / http://exocortex.com
2554 | */
2555 |
2556 | THREE.Euler = function ( x, y, z, order ) {
2557 |
2558 | this._x = x || 0;
2559 | this._y = y || 0;
2560 | this._z = z || 0;
2561 | this._order = order || THREE.Euler.DefaultOrder;
2562 |
2563 | };
2564 |
2565 | THREE.Euler.RotationOrders = [ 'XYZ', 'YZX', 'ZXY', 'XZY', 'YXZ', 'ZYX' ];
2566 |
2567 | THREE.Euler.DefaultOrder = 'XYZ';
2568 |
2569 | THREE.Euler.prototype = {
2570 |
2571 | constructor: THREE.Euler,
2572 |
2573 | _x: 0, _y: 0, _z: 0, _order: THREE.Euler.DefaultOrder,
2574 |
2575 | get x () {
2576 |
2577 | return this._x;
2578 |
2579 | },
2580 |
2581 | set x ( value ) {
2582 |
2583 | this._x = value;
2584 | this.onChangeCallback();
2585 |
2586 | },
2587 |
2588 | get y () {
2589 |
2590 | return this._y;
2591 |
2592 | },
2593 |
2594 | set y ( value ) {
2595 |
2596 | this._y = value;
2597 | this.onChangeCallback();
2598 |
2599 | },
2600 |
2601 | get z () {
2602 |
2603 | return this._z;
2604 |
2605 | },
2606 |
2607 | set z ( value ) {
2608 |
2609 | this._z = value;
2610 | this.onChangeCallback();
2611 |
2612 | },
2613 |
2614 | get order () {
2615 |
2616 | return this._order;
2617 |
2618 | },
2619 |
2620 | set order ( value ) {
2621 |
2622 | this._order = value;
2623 | this.onChangeCallback();
2624 |
2625 | },
2626 |
2627 | set: function ( x, y, z, order ) {
2628 |
2629 | this._x = x;
2630 | this._y = y;
2631 | this._z = z;
2632 | this._order = order || this._order;
2633 |
2634 | this.onChangeCallback();
2635 |
2636 | return this;
2637 |
2638 | },
2639 |
2640 | copy: function ( euler ) {
2641 |
2642 | this._x = euler._x;
2643 | this._y = euler._y;
2644 | this._z = euler._z;
2645 | this._order = euler._order;
2646 |
2647 | this.onChangeCallback();
2648 |
2649 | return this;
2650 |
2651 | },
2652 |
2653 | setFromRotationMatrix: function ( m, order, update ) {
2654 |
2655 | var clamp = THREE.Math.clamp;
2656 |
2657 | // assumes the upper 3x3 of m is a pure rotation matrix (i.e, unscaled)
2658 |
2659 | var te = m.elements;
2660 | var m11 = te[ 0 ], m12 = te[ 4 ], m13 = te[ 8 ];
2661 | var m21 = te[ 1 ], m22 = te[ 5 ], m23 = te[ 9 ];
2662 | var m31 = te[ 2 ], m32 = te[ 6 ], m33 = te[ 10 ];
2663 |
2664 | order = order || this._order;
2665 |
2666 | if ( order === 'XYZ' ) {
2667 |
2668 | this._y = Math.asin( clamp( m13, - 1, 1 ) );
2669 |
2670 | if ( Math.abs( m13 ) < 0.99999 ) {
2671 |
2672 | this._x = Math.atan2( - m23, m33 );
2673 | this._z = Math.atan2( - m12, m11 );
2674 |
2675 | } else {
2676 |
2677 | this._x = Math.atan2( m32, m22 );
2678 | this._z = 0;
2679 |
2680 | }
2681 |
2682 | } else if ( order === 'YXZ' ) {
2683 |
2684 | this._x = Math.asin( - clamp( m23, - 1, 1 ) );
2685 |
2686 | if ( Math.abs( m23 ) < 0.99999 ) {
2687 |
2688 | this._y = Math.atan2( m13, m33 );
2689 | this._z = Math.atan2( m21, m22 );
2690 |
2691 | } else {
2692 |
2693 | this._y = Math.atan2( - m31, m11 );
2694 | this._z = 0;
2695 |
2696 | }
2697 |
2698 | } else if ( order === 'ZXY' ) {
2699 |
2700 | this._x = Math.asin( clamp( m32, - 1, 1 ) );
2701 |
2702 | if ( Math.abs( m32 ) < 0.99999 ) {
2703 |
2704 | this._y = Math.atan2( - m31, m33 );
2705 | this._z = Math.atan2( - m12, m22 );
2706 |
2707 | } else {
2708 |
2709 | this._y = 0;
2710 | this._z = Math.atan2( m21, m11 );
2711 |
2712 | }
2713 |
2714 | } else if ( order === 'ZYX' ) {
2715 |
2716 | this._y = Math.asin( - clamp( m31, - 1, 1 ) );
2717 |
2718 | if ( Math.abs( m31 ) < 0.99999 ) {
2719 |
2720 | this._x = Math.atan2( m32, m33 );
2721 | this._z = Math.atan2( m21, m11 );
2722 |
2723 | } else {
2724 |
2725 | this._x = 0;
2726 | this._z = Math.atan2( - m12, m22 );
2727 |
2728 | }
2729 |
2730 | } else if ( order === 'YZX' ) {
2731 |
2732 | this._z = Math.asin( clamp( m21, - 1, 1 ) );
2733 |
2734 | if ( Math.abs( m21 ) < 0.99999 ) {
2735 |
2736 | this._x = Math.atan2( - m23, m22 );
2737 | this._y = Math.atan2( - m31, m11 );
2738 |
2739 | } else {
2740 |
2741 | this._x = 0;
2742 | this._y = Math.atan2( m13, m33 );
2743 |
2744 | }
2745 |
2746 | } else if ( order === 'XZY' ) {
2747 |
2748 | this._z = Math.asin( - clamp( m12, - 1, 1 ) );
2749 |
2750 | if ( Math.abs( m12 ) < 0.99999 ) {
2751 |
2752 | this._x = Math.atan2( m32, m22 );
2753 | this._y = Math.atan2( m13, m11 );
2754 |
2755 | } else {
2756 |
2757 | this._x = Math.atan2( - m23, m33 );
2758 | this._y = 0;
2759 |
2760 | }
2761 |
2762 | } else {
2763 |
2764 | console.warn( 'THREE.Euler: .setFromRotationMatrix() given unsupported order: ' + order )
2765 |
2766 | }
2767 |
2768 | this._order = order;
2769 |
2770 | if ( update !== false ) this.onChangeCallback();
2771 |
2772 | return this;
2773 |
2774 | },
2775 |
2776 | setFromQuaternion: function () {
2777 |
2778 | var matrix;
2779 |
2780 | return function ( q, order, update ) {
2781 |
2782 | if ( matrix === undefined ) matrix = new THREE.Matrix4();
2783 | matrix.makeRotationFromQuaternion( q );
2784 | this.setFromRotationMatrix( matrix, order, update );
2785 |
2786 | return this;
2787 |
2788 | };
2789 |
2790 | }(),
2791 |
2792 | setFromVector3: function ( v, order ) {
2793 |
2794 | return this.set( v.x, v.y, v.z, order || this._order );
2795 |
2796 | },
2797 |
2798 | reorder: function () {
2799 |
2800 | // WARNING: this discards revolution information -bhouston
2801 |
2802 | var q = new THREE.Quaternion();
2803 |
2804 | return function ( newOrder ) {
2805 |
2806 | q.setFromEuler( this );
2807 | this.setFromQuaternion( q, newOrder );
2808 |
2809 | };
2810 |
2811 | }(),
2812 |
2813 | equals: function ( euler ) {
2814 |
2815 | return ( euler._x === this._x ) && ( euler._y === this._y ) && ( euler._z === this._z ) && ( euler._order === this._order );
2816 |
2817 | },
2818 |
2819 | fromArray: function ( array ) {
2820 |
2821 | this._x = array[ 0 ];
2822 | this._y = array[ 1 ];
2823 | this._z = array[ 2 ];
2824 | if ( array[ 3 ] !== undefined ) this._order = array[ 3 ];
2825 |
2826 | this.onChangeCallback();
2827 |
2828 | return this;
2829 |
2830 | },
2831 |
2832 | toArray: function () {
2833 |
2834 | return [ this._x, this._y, this._z, this._order ];
2835 |
2836 | },
2837 |
2838 | toVector3: function ( optionalResult ) {
2839 |
2840 | if ( optionalResult ) {
2841 |
2842 | return optionalResult.set( this._x, this._y, this._z );
2843 |
2844 | } else {
2845 |
2846 | return new THREE.Vector3( this._x, this._y, this._z );
2847 |
2848 | }
2849 |
2850 | },
2851 |
2852 | onChange: function ( callback ) {
2853 |
2854 | this.onChangeCallback = callback;
2855 |
2856 | return this;
2857 |
2858 | },
2859 |
2860 | onChangeCallback: function () {},
2861 |
2862 | clone: function () {
2863 |
2864 | return new THREE.Euler( this._x, this._y, this._z, this._order );
2865 |
2866 | }
2867 |
2868 | };
2869 | /*** END Euler ***/
2870 | /*** START Math ***/
2871 | /**
2872 | * @author alteredq / http://alteredqualia.com/
2873 | * @author mrdoob / http://mrdoob.com/
2874 | */
2875 |
2876 | THREE.Math = {
2877 |
2878 | generateUUID: function () {
2879 |
2880 | // http://www.broofa.com/Tools/Math.uuid.htm
2881 |
2882 | var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split( '' );
2883 | var uuid = new Array( 36 );
2884 | var rnd = 0, r;
2885 |
2886 | return function () {
2887 |
2888 | for ( var i = 0; i < 36; i ++ ) {
2889 |
2890 | if ( i == 8 || i == 13 || i == 18 || i == 23 ) {
2891 |
2892 | uuid[ i ] = '-';
2893 |
2894 | } else if ( i == 14 ) {
2895 |
2896 | uuid[ i ] = '4';
2897 |
2898 | } else {
2899 |
2900 | if ( rnd <= 0x02 ) rnd = 0x2000000 + ( Math.random() * 0x1000000 ) | 0;
2901 | r = rnd & 0xf;
2902 | rnd = rnd >> 4;
2903 | uuid[ i ] = chars[ ( i == 19 ) ? ( r & 0x3 ) | 0x8 : r ];
2904 |
2905 | }
2906 | }
2907 |
2908 | return uuid.join( '' );
2909 |
2910 | };
2911 |
2912 | }(),
2913 |
2914 | // Clamp value to range
2915 |
2916 | clamp: function ( x, a, b ) {
2917 |
2918 | return ( x < a ) ? a : ( ( x > b ) ? b : x );
2919 |
2920 | },
2921 |
2922 | // Clamp value to range to range
2931 |
2932 | mapLinear: function ( x, a1, a2, b1, b2 ) {
2933 |
2934 | return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
2935 |
2936 | },
2937 |
2938 | // http://en.wikipedia.org/wiki/Smoothstep
2939 |
2940 | smoothstep: function ( x, min, max ) {
2941 |
2942 | if ( x <= min ) return 0;
2943 | if ( x >= max ) return 1;
2944 |
2945 | x = ( x - min ) / ( max - min );
2946 |
2947 | return x * x * ( 3 - 2 * x );
2948 |
2949 | },
2950 |
2951 | smootherstep: function ( x, min, max ) {
2952 |
2953 | if ( x <= min ) return 0;
2954 | if ( x >= max ) return 1;
2955 |
2956 | x = ( x - min ) / ( max - min );
2957 |
2958 | return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
2959 |
2960 | },
2961 |
2962 | // Random float from <0, 1> with 16 bits of randomness
2963 | // (standard Math.random() creates repetitive patterns when applied over larger space)
2964 |
2965 | random16: function () {
2966 |
2967 | return ( 65280 * Math.random() + 255 * Math.random() ) / 65535;
2968 |
2969 | },
2970 |
2971 | // Random integer from interval
2972 |
2973 | randInt: function ( low, high ) {
2974 |
2975 | return Math.floor( this.randFloat( low, high ) );
2976 |
2977 | },
2978 |
2979 | // Random float from interval
2980 |
2981 | randFloat: function ( low, high ) {
2982 |
2983 | return low + Math.random() * ( high - low );
2984 |
2985 | },
2986 |
2987 | // Random float from <-range/2, range/2> interval
2988 |
2989 | randFloatSpread: function ( range ) {
2990 |
2991 | return range * ( 0.5 - Math.random() );
2992 |
2993 | },
2994 |
2995 | degToRad: function () {
2996 |
2997 | var degreeToRadiansFactor = Math.PI / 180;
2998 |
2999 | return function ( degrees ) {
3000 |
3001 | return degrees * degreeToRadiansFactor;
3002 |
3003 | };
3004 |
3005 | }(),
3006 |
3007 | radToDeg: function () {
3008 |
3009 | var radianToDegreesFactor = 180 / Math.PI;
3010 |
3011 | return function ( radians ) {
3012 |
3013 | return radians * radianToDegreesFactor;
3014 |
3015 | };
3016 |
3017 | }(),
3018 |
3019 | isPowerOfTwo: function ( value ) {
3020 |
3021 | return ( value & ( value - 1 ) ) === 0 && value !== 0;
3022 |
3023 | },
3024 |
3025 | nextPowerOfTwo: function ( value ) {
3026 |
3027 | value --;
3028 | value |= value >> 1;
3029 | value |= value >> 2;
3030 | value |= value >> 4;
3031 | value |= value >> 8;
3032 | value |= value >> 16;
3033 | value ++;
3034 |
3035 | return value;
3036 | }
3037 |
3038 | };
3039 |
3040 | /*** END Math ***/
3041 |
3042 | }
3043 |
3044 | module.exports = THREE;
3045 |
3046 | },{}],10:[function(_dereq_,module,exports){
3047 | /*
3048 | * Copyright 2015 Google Inc. All Rights Reserved.
3049 | * Licensed under the Apache License, Version 2.0 (the "License");
3050 | * you may not use this file except in compliance with the License.
3051 | * You may obtain a copy of the License at
3052 | *
3053 | * http://www.apache.org/licenses/LICENSE-2.0
3054 | *
3055 | * Unless required by applicable law or agreed to in writing, software
3056 | * distributed under the License is distributed on an "AS IS" BASIS,
3057 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3058 | * See the License for the specific language governing permissions and
3059 | * limitations under the License.
3060 | */
3061 | var THREE = _dereq_('./three-math.js');
3062 | var Util = _dereq_('./util.js');
3063 |
3064 | var ROTATE_SPEED = 0.5;
3065 | /**
3066 | * Provides a quaternion responsible for pre-panning the scene before further
3067 | * transformations due to device sensors.
3068 | */
3069 | function TouchPanner() {
3070 | window.addEventListener('touchstart', this.onTouchStart_.bind(this));
3071 | window.addEventListener('touchmove', this.onTouchMove_.bind(this));
3072 | window.addEventListener('touchend', this.onTouchEnd_.bind(this));
3073 |
3074 | this.isTouching = false;
3075 | this.rotateStart = new THREE.Vector2();
3076 | this.rotateEnd = new THREE.Vector2();
3077 | this.rotateDelta = new THREE.Vector2();
3078 |
3079 | this.theta = 0;
3080 | this.orientation = new THREE.Quaternion();
3081 | }
3082 |
3083 | TouchPanner.prototype.getOrientation = function() {
3084 | this.orientation.setFromEuler(new THREE.Euler(0, 0, this.theta));
3085 | return this.orientation;
3086 | };
3087 |
3088 | TouchPanner.prototype.resetSensor = function() {
3089 | this.theta = 0;
3090 | };
3091 |
3092 | TouchPanner.prototype.onTouchStart_ = function(e) {
3093 | // Only respond if there is exactly one touch.
3094 | if (e.touches.length != 1) {
3095 | return;
3096 | }
3097 | this.rotateStart.set(e.touches[0].pageX, e.touches[0].pageY);
3098 | this.isTouching = true;
3099 | };
3100 |
3101 | TouchPanner.prototype.onTouchMove_ = function(e) {
3102 | if (!this.isTouching) {
3103 | return;
3104 | }
3105 | this.rotateEnd.set(e.touches[0].pageX, e.touches[0].pageY);
3106 | this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart);
3107 | this.rotateStart.copy(this.rotateEnd);
3108 |
3109 | // On iOS, direction is inverted.
3110 | if (Util.isIOS()) {
3111 | this.rotateDelta.x *= -1;
3112 | }
3113 |
3114 | var element = document.body;
3115 | this.theta += 2 * Math.PI * this.rotateDelta.x / element.clientWidth * ROTATE_SPEED;
3116 | };
3117 |
3118 | TouchPanner.prototype.onTouchEnd_ = function(e) {
3119 | this.isTouching = false;
3120 | };
3121 |
3122 | module.exports = TouchPanner;
3123 |
3124 | },{"./three-math.js":9,"./util.js":11}],11:[function(_dereq_,module,exports){
3125 | /*
3126 | * Copyright 2015 Google Inc. All Rights Reserved.
3127 | * Licensed under the Apache License, Version 2.0 (the "License");
3128 | * you may not use this file except in compliance with the License.
3129 | * You may obtain a copy of the License at
3130 | *
3131 | * http://www.apache.org/licenses/LICENSE-2.0
3132 | *
3133 | * Unless required by applicable law or agreed to in writing, software
3134 | * distributed under the License is distributed on an "AS IS" BASIS,
3135 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3136 | * See the License for the specific language governing permissions and
3137 | * limitations under the License.
3138 | */
3139 | var Util = window.Util || {};
3140 |
3141 | Util.MIN_TIMESTEP = 0.001;
3142 | Util.MAX_TIMESTEP = 1;
3143 |
3144 | Util.clamp = function(value, min, max) {
3145 | return Math.min(Math.max(min, value), max);
3146 | };
3147 |
3148 | Util.isIOS = function() {
3149 | return /iPad|iPhone|iPod/.test(navigator.platform);
3150 | };
3151 |
3152 | Util.isFirefoxAndroid = function() {
3153 | return navigator.userAgent.indexOf('Firefox') !== -1 && navigator.userAgent.indexOf('Android') !== -1;
3154 | }
3155 |
3156 | // Helper method to validate the time steps of sensor timestamps.
3157 | Util.isTimestampDeltaValid = function(timestampDeltaS) {
3158 | if (isNaN(timestampDeltaS)) {
3159 | return false;
3160 | }
3161 | if (timestampDeltaS <= Util.MIN_TIMESTEP) {
3162 | return false;
3163 | }
3164 | if (timestampDeltaS > Util.MAX_TIMESTEP) {
3165 | return false;
3166 | }
3167 | return true;
3168 | }
3169 |
3170 | module.exports = Util;
3171 |
3172 | },{}],12:[function(_dereq_,module,exports){
3173 | /*
3174 | * Copyright 2015 Google Inc. All Rights Reserved.
3175 | * Licensed under the Apache License, Version 2.0 (the "License");
3176 | * you may not use this file except in compliance with the License.
3177 | * You may obtain a copy of the License at
3178 | *
3179 | * http://www.apache.org/licenses/LICENSE-2.0
3180 | *
3181 | * Unless required by applicable law or agreed to in writing, software
3182 | * distributed under the License is distributed on an "AS IS" BASIS,
3183 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3184 | * See the License for the specific language governing permissions and
3185 | * limitations under the License.
3186 | */
3187 |
3188 | var CardboardHMDVRDevice = _dereq_('./cardboard-hmd-vr-device.js');
3189 | //var OrientationPositionSensorVRDevice = require('./orientation-position-sensor-vr-device.js');
3190 | var FusionPositionSensorVRDevice = _dereq_('./fusion-position-sensor-vr-device.js');
3191 | var MouseKeyboardPositionSensorVRDevice = _dereq_('./mouse-keyboard-position-sensor-vr-device.js');
3192 | // Uncomment to add positional tracking via webcam.
3193 | //var WebcamPositionSensorVRDevice = require('./webcam-position-sensor-vr-device.js');
3194 | var HMDVRDevice = _dereq_('./base.js').HMDVRDevice;
3195 | var PositionSensorVRDevice = _dereq_('./base.js').PositionSensorVRDevice;
3196 |
3197 | function WebVRPolyfill() {
3198 | this.devices = [];
3199 |
3200 | if (!this.isWebVRAvailable()) {
3201 | this.enablePolyfill();
3202 | }
3203 | }
3204 |
3205 | WebVRPolyfill.prototype.isWebVRAvailable = function() {
3206 | return ('getVRDevices' in navigator) || ('mozGetVRDevices' in navigator);
3207 | };
3208 |
3209 |
3210 | WebVRPolyfill.prototype.enablePolyfill = function() {
3211 | // Initialize our virtual VR devices.
3212 | if (this.isCardboardCompatible()) {
3213 | this.devices.push(new CardboardHMDVRDevice());
3214 | }
3215 |
3216 | // Polyfill using the right position sensor.
3217 | if (this.isMobile()) {
3218 | //this.devices.push(new OrientationPositionSensorVRDevice());
3219 | this.devices.push(new FusionPositionSensorVRDevice());
3220 | } else {
3221 | if (!WebVRConfig.MOUSE_KEYBOARD_CONTROLS_DISABLED) {
3222 | this.devices.push(new MouseKeyboardPositionSensorVRDevice());
3223 | }
3224 | // Uncomment to add positional tracking via webcam.
3225 | //this.devices.push(new WebcamPositionSensorVRDevice());
3226 | }
3227 |
3228 | // Provide navigator.getVRDevices.
3229 | navigator.getVRDevices = this.getVRDevices.bind(this);
3230 |
3231 | // Provide the CardboardHMDVRDevice and PositionSensorVRDevice objects.
3232 | window.HMDVRDevice = HMDVRDevice;
3233 | window.PositionSensorVRDevice = PositionSensorVRDevice;
3234 | };
3235 |
3236 | WebVRPolyfill.prototype.getVRDevices = function() {
3237 | var devices = this.devices;
3238 | return new Promise(function(resolve, reject) {
3239 | try {
3240 | resolve(devices);
3241 | } catch (e) {
3242 | reject(e);
3243 | }
3244 | });
3245 | };
3246 |
3247 | /**
3248 | * Determine if a device is mobile.
3249 | */
3250 | WebVRPolyfill.prototype.isMobile = function() {
3251 | return /Android/i.test(navigator.userAgent) ||
3252 | /iPhone|iPad|iPod/i.test(navigator.userAgent);
3253 | };
3254 |
3255 | WebVRPolyfill.prototype.isCardboardCompatible = function() {
3256 | // For now, support all iOS and Android devices.
3257 | // Also enable the WebVRConfig.FORCE_VR flag for debugging.
3258 | return this.isMobile() || WebVRConfig.FORCE_ENABLE_VR;
3259 | };
3260 |
3261 | module.exports = WebVRPolyfill;
3262 |
3263 | },{"./base.js":1,"./cardboard-hmd-vr-device.js":2,"./fusion-position-sensor-vr-device.js":4,"./mouse-keyboard-position-sensor-vr-device.js":6}]},{},[5]);
3264 |
--------------------------------------------------------------------------------
/models/flightpath-nov4-bezier.txt:
--------------------------------------------------------------------------------
1 | Point X Y Z <- X <- Y <- Z X -> Y -> Z ->
0 183.622 m 14.806 m -16083.156 m -661.169 m -6.466 m -663.588 m 661.169 m 6.466 m 663.588 m
1 2540.359 m 90.229 m -13305.282 m -365.853 m -6.288 m -672.261 m 365.853 m 6.288 m 672.261 m
2 3773.545 m 132.351 m -9797.836 m -57.914 m 0 m -863.602 m 57.914 m 0 m 863.602 m
3 3296.352 m 90.229 m -6775.34 m 419.272 m 35 m -649.39 m -419.272 m -35 m 649.39 m
4 874.045 m 38.929 m -3981.337 m 410.143 m 0 m -716.977 m -410.143 m 0 m 716.977 m
5 -137.369 m 22.729 m -934.934 m 87.008 m 0 m -939.691 m -87.008 m 0 m 939.691 m
6 503.777 m 23.429 m 3703.947 m -190.279 m -2.334 m -1309.123 m 190.279 m 2.334 m 1309.123 m
7 -164.173 m 4.484 m 7412.884 m 790.094 m 21.436 m -511.481 m -790.094 m -21.436 m 511.481 m
--------------------------------------------------------------------------------
/sounds/235428__allanz10d__calm-ocean-breeze-simulation.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdoob/sechelt/256f986f261295cc7967e65658d833cfa4f11809/sounds/235428__allanz10d__calm-ocean-breeze-simulation.ogg
--------------------------------------------------------------------------------
/sounds/23707__hazure__seagull.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdoob/sechelt/256f986f261295cc7967e65658d833cfa4f11809/sounds/23707__hazure__seagull.ogg
--------------------------------------------------------------------------------
/sounds/78389__inchadney__seagulls.ogg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mrdoob/sechelt/256f986f261295cc7967e65658d833cfa4f11809/sounds/78389__inchadney__seagulls.ogg
--------------------------------------------------------------------------------