31 | Build a media player class that can be easily integrated, supporting
32 | media playback, SoundCloud streaming and getUserMedia streams, using Web
33 | Audio API.
34 |
35 |
36 | It also uses AnalyserNode to perform an FFT, and can upload the spectrum
37 | to a GPU texture.
38 |
39 |
Can work together with kick detection.
40 |
41 | Play |
42 | Use microphone
43 |
44 |
More details...
45 |
46 |
47 | SoundCloud stream is done via their API, connecting the stream source
48 | to an HTMLAudioElement and creating a MediaElementSource from that.
49 |
50 |
The microphone can be used via navigator.getUserMedia<().
51 |
52 | I didn't have time to implement media playback, it would be done with
53 | MediaElementSource, too.
54 |
55 |
56 | All nodes are connected to an AnalyserNode and to the AudioContext
57 | destination.
58 |
59 |
60 | The visualisation is done in a single draw call of particle-triangles,
61 | with the color from a gradient texture. Positions are updated in CPU,
62 | from the FFT data.
63 |
Using ShaderTexture to create a diffuse texture (using TruchetFlip's implementation of Wang Tiles) and a roughness texture (using the diffuse texture to modulate some noise), then again using the diffuse texture as a height map to turn it into a normal map (using a shader based on NormalMap Online).
21 |
The maps are then used as diffuse, roughness, metalness, bump and normal maps of a THREE.MeshStandardMaterial.
22 |
Credits
23 |
Tiles shader from TruchetFlip by jt from ShaderToy. Heightmap to normal shader adapted from NormalMap Online
An example of building a sphere with dots, making sure there's a uniform density of dots across all the surface. In naive distributions, there's way more points towards the poles than necessary.
19 |
The algorithm accepts different point sizes and differents spacing between points.
20 |
Toggle camera or press Space
21 |
More details...
22 |
23 |
All points are GL_POINTS, using distance to the center and discard to make the look like dots.
>
24 |
There are two buffers, one for vertices and another for colors. Whether there's a dot or not is decided by looking up a mask image loaded in a canvas. The color of the dot is also read from a color texture (earth and clouds), also loaded in their respective canvases. The position of the dot is displaced in the normal direction based on a lookup of a heightmap texture.
25 |
All images are equirectngular panoramas so can be mapped into a sphere. UV spherical coordinates from the sphere surface need to be adapted to be lookup 2D coordinates for the equirectangular panoramas.
Like those ASCII renderers, but with less characters: just 10.
19 |
More details...
20 |
21 |
A simple scene rendered with lights, shadows and MeshStandardMaterial is rendered to a framebuffer.
22 |
That framebuffer is then processed to add some glow, and then a scaled down version is used to feed a shader that will replace every block of 32x32 pixels with a part of a texture that has the numbers 0 to 9, according to the brightness of the block. The number is aditionally shaded by that intensity, so there's more gradients. The texture with numbers has a slight glow preapplied on it.
Simulating the effect of linocut, or wood engraving.
19 |
Drop an image in the page!
20 |
More details...
21 |
22 |
The image is uploaded to a texture, and a fragment shader generates a few curvy lines based on a sinus, and modulates the contrast of those lines with the intensity of the uploaded image, effectively changing the width of the line.
This are the projects created for Codevember 2016 -a challenge for developers creating a creative sketch by day during the whole month of November.
57 |
58 |
Initially I wanted to mix very different browsers technologies: CSS, SVG, WebGL, and not limited to just visual effects. In the end i went with WebGL + three.js, creating a basic boilerplate and starting from scratch every project. Of course as I went, the toolbox grew considerably, with things like ShaderTexture, Odeo and a lot of shaders and helper methods.
59 |
60 |
I've tried to make all sites work in as many platforms as possible, but the time constrains don't allow for a lot of beta testing and bug hunting. Wobbly Earth, for instance, doesn't work on my Nexus 5, but works everywhere else I've tried. I have reports that Lines in a Sphere doesn't go well with MacBook Airs. If you find any issue and want to help debug, tweet me.
61 |
62 |
So this is all I've been able to churn out, between work, two kids, broken eyeglasses... Just don't judge the code too strictly :)
63 |
64 |
Inside info: in all the effects with music (10, 16 and 30) you can open the console and run odeo.playSoundCloud( "whatever_soundcloud_url" ) and you can change the track.
65 |
Even more inside info: in all the effects with FBOS, shadows, etc. you can probably open the console and run helper.show(true) to see the FBOHelper.
66 |
67 |
Source code for all experiments -and discarded ones- is here: codevember-2016
68 |
69 |
70 |
71 |
72 |
73 |
74 |
Procedurally textured torus
75 |
A.k.a. "The decadent golden-plated knot torus"
76 |
77 |
78 |
79 |
80 |
Dotty Earth
81 |
All is full of dots
82 |
83 |
84 |
85 |
86 |
Strange attractors
87 |
Strangely attractive strange attractors
88 |
89 |
90 |
91 |
92 |
Trencadís texture
93 |
A surface shader to make a plane look like glazed porcelain
94 |
95 |
96 |
97 |
98 |
Particle field
99 |
Oldschool plasma moving particles with motion blur
100 |
101 |
102 |
103 |
104 |
Wobbly Earth
105 |
The day the Earth didn't stand still
106 |
107 |
108 |
109 |
110 |
A (very) poor man's GI
111 |
Computing Global Illumination (GI) by rendering the scene from every vertex... four times!
112 |
113 |
114 |
115 |
116 |
Metaballs
117 |
Shiny striped metaballs
118 |
119 |
120 |
121 |
122 |
Street View PBR
123 |
Hacking image-based lighting (IBL) into a material
124 |
125 |
126 |
127 |
128 |
Odeo
129 |
Music visualisation to demo an audio manager
130 |
131 |
132 |
133 |
134 |
Blocky Water
135 |
Oldschool water with lots of AO
136 |
137 |
138 |
139 |
140 |
AO Blocks
141 |
Take a walk on the occluded side
142 |
143 |
144 |
145 |
146 |
Sphere Impostor
147 |
Don't use many triangles when you can use just one!
148 |
149 |
150 |
151 |
152 |
Clock Flag
153 |
See time fly
154 |
155 |
156 |
157 |
158 |
Crystals
159 |
Generative rocks, or minerals, Marie
160 |
161 |
162 |
163 |
164 |
Torus
165 |
Music visualisation simulating a phosphor oscilloscope
166 |
167 |
168 |
169 |
170 |
Conway
171 |
GPGPU Game of Life with AO and shadows
172 |
173 |
174 |
175 |
176 |
Starfield
177 |
It's a very small universe
178 |
179 |
180 |
181 |
182 |
DOF Tunnel
183 |
Long tunnel with lots of postprocessing
184 |
185 |
186 |
187 |
188 |
Lines on a Sphere
189 |
Lines and streaks moving in the surface of a sphere
190 |
191 |
192 |
193 |
194 |
Numerical renderer
195 |
Rendering with numbers (very digital!)
196 |
197 |
198 |
199 |
200 |
Line thickness
201 |
Creating an engraving effect with shaders
202 |
203 |
204 |
205 |
206 |
Emitter
207 |
Weird thing that started as a particle emitter and ended up like Matrix sentinels
208 |
209 |
210 |
211 |
212 |
AO Spheres
213 |
Boids and soft shadows and analytical ambient occlusion
214 |
215 |
216 |
217 |
218 |
Lines
219 |
More lines and feedback effect
220 |
221 |
222 |
223 |
224 |
Volumetric rendering
225 |
March your ray through a cloud
226 |
227 |
228 |
229 |
230 |
Medusa
231 |
Random jellyfish-like objects
232 |
233 |
234 |
235 |
236 |
Greeble AO
237 |
Rotating greeble, with SSAO
238 |
239 |
240 |
241 |
242 |
Bands
243 |
Twisty pieces of sliced floor
244 |
245 |
246 |
247 |
248 |
Triangle tunnel
249 |
A WebGL remake of GMUNK's PYRADICAL
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
309 |
310 |
311 |
312 |
--------------------------------------------------------------------------------
/js/Boids.js:
--------------------------------------------------------------------------------
1 | (function(){
2 |
3 | function Boids( opts ) {
4 |
5 | this.boids = [];
6 | this.opts = opts || {};
7 |
8 | for( var j = 0; j < this.opts.boids; j++ ) {
9 |
10 | this.boids.push({
11 | position: new THREE.Vector3(
12 | Maf.randomInRange( -50, 50 ),
13 | 0,
14 | Maf.randomInRange( -50, 50 )
15 | ),
16 | centerVelocity: new THREE.Vector3(),
17 | alignmentVelocity: new THREE.Vector3(),
18 | velocity: new THREE.Vector3(),
19 | orientation: new THREE.Quaternion( 0, 0, 0, 1 )
20 | })
21 |
22 | }
23 |
24 | }
25 |
26 | var tmpVector = new THREE.Vector3();
27 |
28 | Boids.prototype.update = function() {
29 |
30 | var center = new THREE.Vector3();
31 | for( var i = 0; i < this.boids.length; i++ ) {
32 | center.add( this.boids[ i ].position );
33 | }
34 | center.multiplyScalar( 1 / this.boids.length );
35 |
36 | for( var i = 0; i < this.boids.length; i++ ) {
37 |
38 | tmpVector.copy( center );
39 | tmpVector.sub( this.boids[ i ].position );
40 | tmpVector.normalize();
41 |
42 | this.boids[ i ].centerVelocity.copy( tmpVector );
43 |
44 | this.boids[ i ].alignmentVelocity.set( 0, 0, 0 );
45 |
46 | for( var j = 0; j < this.boids.length; j++ ) {
47 |
48 | tmpVector.copy( this.boids[ j ].position );
49 | var distance = tmpVector.distanceTo( this.boids[ i ].position );
50 |
51 | if( distance < 10 ) {
52 |
53 | tmpVector.copy( this.boids[ j ].position );
54 | tmpVector.sub( this.boids[ i ].position );
55 | this.boids[ i ].alignmentVelocity.add( tmpVector );
56 |
57 | }
58 |
59 | }
60 |
61 | this.boids[ i ].alignmentVelocity.normalize();
62 |
63 | this.boids[ i ].velocity.copy( this.boids[ i ].centerVelocity );
64 | this.boids[ i ].velocity.add( this.boids[ i ].alignmentVelocity );
65 | this.boids[ i ].velocity.multiplyScalar( .1 );
66 |
67 | this.boids[ i ].position.add( this.boids[ i ].velocity );
68 |
69 | }
70 |
71 | }
72 |
73 | window.Boids = Boids;
74 |
75 | })();
76 |
--------------------------------------------------------------------------------
/js/Cloth.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Cloth Simulation using a relaxed constrains solver
3 | */
4 |
5 | // Suggested Readings
6 |
7 | // Advanced Character Physics by Thomas Jakobsen Character
8 | // http://freespace.virgin.net/hugo.elias/models/m_cloth.htm
9 | // http://en.wikipedia.org/wiki/Cloth_modeling
10 | // http://cg.alexandra.dk/tag/spring-mass-system/
11 | // Real-time Cloth Animation http://www.darwin3d.com/gamedev/articles/col0599.pdf
12 |
13 | /* 20 x 10 restDist = 30 --> good config
14 | var DAMPING = 0.07;
15 | var DRAG = 1 - DAMPING;
16 | var MASS = .03;
17 | var restDistance = 30;
18 | var GRAVITY = 981 * 1.4;
19 | */
20 |
21 | var DAMPING = 0.07;
22 | var DRAG = 1 - DAMPING;
23 | var MASS = .05;
24 | var restDistance = 36;
25 | var GRAVITY = 981 * 1.4;;
26 |
27 | var clothProperties = {'damping': DAMPING,'drag': DRAG,'mass': MASS };
28 |
29 | var xSegs = 50; //
30 | var ySegs = 40; //
31 |
32 | var clothFunction = plane(restDistance * xSegs, restDistance * ySegs);
33 |
34 | var cloth = new Cloth(xSegs, ySegs);
35 |
36 | var gravity = new THREE.Vector3( 0, -GRAVITY, 0 ).multiplyScalar(clothProperties.mass);
37 |
38 |
39 | var TIMESTEP = 18 / 1000;
40 | var TIMESTEP_SQ = TIMESTEP * TIMESTEP;
41 |
42 | var pins = [];
43 | var pinOffset = {x:0,y:0,z:0,'animationVel': 1000,'circularAnimation':false};
44 | var pinBend = {'amount':1,'periodicity':1000};
45 |
46 | //var windToggle = true;
47 | var windStrength = 2;
48 | var windForce = new THREE.Vector3(0,0,0);
49 |
50 | var tmpForce = new THREE.Vector3();
51 |
52 | var lastTime;
53 |
54 |
55 | function plane(width, height) {
56 |
57 | return function(u, v) {
58 | var x = (u-0.5) * width;
59 | var y = (v+0.5) * height;
60 | var z = 0;
61 |
62 | return new THREE.Vector3(x, y, z);
63 | };
64 | }
65 |
66 | function Particle(x, y, z, mass) {
67 | this.position = clothFunction(x, y); // position
68 | this.previous = clothFunction(x, y); // previous
69 | this.original = clothFunction(x, y);
70 | this.a = new THREE.Vector3(0, 0, 0); // acceleration
71 | this.mass = mass;
72 | this.invMass = 1 / mass;
73 | this.tmp = new THREE.Vector3();
74 | this.tmp2 = new THREE.Vector3();
75 | }
76 |
77 | // Force -> Acceleration
78 | Particle.prototype.addForce = function(force) {
79 | this.a.add(
80 | this.tmp2.copy(force).multiplyScalar(this.invMass)
81 | );
82 | };
83 |
84 |
85 | // Performs verlet integration
86 | Particle.prototype.integrate = function(timesq) {
87 | var newPos = this.tmp.subVectors(this.position, this.previous);
88 | newPos.multiplyScalar(clothProperties.drag).add(this.position);
89 | newPos.add(this.a.multiplyScalar(timesq));
90 |
91 | this.tmp = this.previous;
92 | this.previous = this.position;
93 | this.position = newPos;
94 |
95 | this.a.set(0, 0, 0);
96 | }
97 |
98 |
99 | var diff = new THREE.Vector3();
100 | var tmpP1 = new THREE.Vector3();
101 |
102 | function satisifyConstrains(p1, p2, distance) {
103 | //Original function....
104 | diff.subVectors(p2.position, p1.position);
105 | var currentDist = diff.length();
106 | if (currentDist==0) return; // prevents division by 0
107 | var correction = diff.multiplyScalar(1 - distance/currentDist);
108 | var correctionHalf = correction.multiplyScalar(0.5);
109 | p1.position.add(correctionHalf);
110 | p2.position.sub(correctionHalf);
111 |
112 |
113 | //var diff = new THREE.Vector3();
114 | /*diff.subVectors(p2.position, p1.position);
115 | var currentDist = diff.length();
116 | if (currentDist==0) return; // prevents division by 0
117 | if (currentDist<15) return;
118 | diff.normalize();
119 |
120 | diff.multiplyScalar(distance);
121 |
122 | tmpP1.set(0,0,0);
123 | tmpP1.add(p1.position);
124 |
125 | tmpP1.add(diff);
126 |
127 | p2.position.copy(tmpP1);*/
128 | }
129 |
130 |
131 | function Cloth(w, h) {
132 | w = w || 10;
133 | h = h || 10;
134 | this.w = w;
135 | this.h = h;
136 |
137 | var particles = [];
138 | var constrains = [];
139 |
140 | var u, v;
141 |
142 | // Create particles
143 | for (v=0;v<=h;v++) {
144 | for (u=0;u<=w;u++) {
145 | //console.log("Create Particles: ",u/w, v/h);
146 | particles.push(
147 | new Particle(u/w, v/h, 0, clothProperties.mass)
148 | );
149 | }
150 | }
151 |
152 | // Structural
153 |
154 | for (v=0;v 1 ) {
45 |
46 | a = 1 - a;
47 | b = 1 - b;
48 |
49 | }
50 |
51 | var c = 1 - a - b;
52 |
53 | point.copy( vectorA );
54 | point.multiplyScalar( a );
55 |
56 | vector.copy( vectorB );
57 | vector.multiplyScalar( b );
58 |
59 | point.add( vector );
60 |
61 | vector.copy( vectorC );
62 | vector.multiplyScalar( c );
63 |
64 | point.add( vector );
65 |
66 | return point;
67 |
68 | };
69 |
70 | }(),
71 |
72 | // Get random point in face (triangle)
73 | // (uniform distribution)
74 |
75 | randomPointInFace: function ( face, geometry ) {
76 |
77 | var vA, vB, vC;
78 |
79 | vA = geometry.vertices[ face.a ];
80 | vB = geometry.vertices[ face.b ];
81 | vC = geometry.vertices[ face.c ];
82 |
83 | return THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC );
84 |
85 | },
86 |
87 | // Get uniformly distributed random points in mesh
88 | // - create array with cumulative sums of face areas
89 | // - pick random number from 0 to total area
90 | // - find corresponding place in area array by binary search
91 | // - get random point in face
92 |
93 | randomPointsInGeometry: function ( geometry, n ) {
94 |
95 | var face, i,
96 | faces = geometry.faces,
97 | vertices = geometry.vertices,
98 | il = faces.length,
99 | totalArea = 0,
100 | cumulativeAreas = [],
101 | vA, vB, vC;
102 |
103 | // precompute face areas
104 |
105 | for ( i = 0; i < il; i ++ ) {
106 |
107 | face = faces[ i ];
108 |
109 | vA = vertices[ face.a ];
110 | vB = vertices[ face.b ];
111 | vC = vertices[ face.c ];
112 |
113 | face._area = THREE.GeometryUtils.triangleArea( vA, vB, vC );
114 |
115 | totalArea += face._area;
116 |
117 | cumulativeAreas[ i ] = totalArea;
118 |
119 | }
120 |
121 | // binary search cumulative areas array
122 |
123 | function binarySearchIndices( value ) {
124 |
125 | function binarySearch( start, end ) {
126 |
127 | // return closest larger index
128 | // if exact number is not found
129 |
130 | if ( end < start )
131 | return start;
132 |
133 | var mid = start + Math.floor( ( end - start ) / 2 );
134 |
135 | if ( cumulativeAreas[ mid ] > value ) {
136 |
137 | return binarySearch( start, mid - 1 );
138 |
139 | } else if ( cumulativeAreas[ mid ] < value ) {
140 |
141 | return binarySearch( mid + 1, end );
142 |
143 | } else {
144 |
145 | return mid;
146 |
147 | }
148 |
149 | }
150 |
151 | var result = binarySearch( 0, cumulativeAreas.length - 1 );
152 | return result;
153 |
154 | }
155 |
156 | // pick random face weighted by face area
157 |
158 | var r, index,
159 | result = [];
160 |
161 | var stats = {};
162 |
163 | for ( i = 0; i < n; i ++ ) {
164 |
165 | r = Math.random() * totalArea;
166 |
167 | index = binarySearchIndices( r );
168 |
169 | result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry );
170 |
171 | if ( ! stats[ index ] ) {
172 |
173 | stats[ index ] = 1;
174 |
175 | } else {
176 |
177 | stats[ index ] += 1;
178 |
179 | }
180 |
181 | }
182 |
183 | return result;
184 |
185 | },
186 |
187 | randomPointsInBufferGeometry: function ( geometry, n ) {
188 |
189 | var i,
190 | vertices = geometry.attributes.position.array,
191 | totalArea = 0,
192 | cumulativeAreas = [],
193 | vA, vB, vC;
194 |
195 | // precompute face areas
196 | vA = new THREE.Vector3();
197 | vB = new THREE.Vector3();
198 | vC = new THREE.Vector3();
199 |
200 | // geometry._areas = [];
201 | var il = vertices.length / 9;
202 |
203 | for ( i = 0; i < il; i ++ ) {
204 |
205 | vA.set( vertices[ i * 9 + 0 ], vertices[ i * 9 + 1 ], vertices[ i * 9 + 2 ] );
206 | vB.set( vertices[ i * 9 + 3 ], vertices[ i * 9 + 4 ], vertices[ i * 9 + 5 ] );
207 | vC.set( vertices[ i * 9 + 6 ], vertices[ i * 9 + 7 ], vertices[ i * 9 + 8 ] );
208 |
209 | area = THREE.GeometryUtils.triangleArea( vA, vB, vC );
210 | totalArea += area;
211 |
212 | cumulativeAreas.push( totalArea );
213 |
214 | }
215 |
216 | // binary search cumulative areas array
217 |
218 | function binarySearchIndices( value ) {
219 |
220 | function binarySearch( start, end ) {
221 |
222 | // return closest larger index
223 | // if exact number is not found
224 |
225 | if ( end < start )
226 | return start;
227 |
228 | var mid = start + Math.floor( ( end - start ) / 2 );
229 |
230 | if ( cumulativeAreas[ mid ] > value ) {
231 |
232 | return binarySearch( start, mid - 1 );
233 |
234 | } else if ( cumulativeAreas[ mid ] < value ) {
235 |
236 | return binarySearch( mid + 1, end );
237 |
238 | } else {
239 |
240 | return mid;
241 |
242 | }
243 |
244 | }
245 |
246 | var result = binarySearch( 0, cumulativeAreas.length - 1 );
247 | return result;
248 |
249 | }
250 |
251 | // pick random face weighted by face area
252 |
253 | var r, index,
254 | result = [];
255 |
256 | for ( i = 0; i < n; i ++ ) {
257 |
258 | r = Math.random() * totalArea;
259 |
260 | index = binarySearchIndices( r );
261 |
262 | // result[ i ] = THREE.GeometryUtils.randomPointInFace( faces[ index ], geometry, true );
263 | vA.set( vertices[ index * 9 + 0 ], vertices[ index * 9 + 1 ], vertices[ index * 9 + 2 ] );
264 | vB.set( vertices[ index * 9 + 3 ], vertices[ index * 9 + 4 ], vertices[ index * 9 + 5 ] );
265 | vC.set( vertices[ index * 9 + 6 ], vertices[ index * 9 + 7 ], vertices[ index * 9 + 8 ] );
266 | result[ i ] = THREE.GeometryUtils.randomPointInTriangle( vA, vB, vC );
267 |
268 | }
269 |
270 | return result;
271 |
272 | },
273 |
274 | // Get triangle area (half of parallelogram)
275 | // http://mathworld.wolfram.com/TriangleArea.html
276 |
277 | triangleArea: function () {
278 |
279 | var vector1 = new THREE.Vector3();
280 | var vector2 = new THREE.Vector3();
281 |
282 | return function ( vectorA, vectorB, vectorC ) {
283 |
284 | vector1.subVectors( vectorB, vectorA );
285 | vector2.subVectors( vectorC, vectorA );
286 | vector1.cross( vector2 );
287 |
288 | return 0.5 * vector1.length();
289 |
290 | };
291 |
292 | }(),
293 |
294 | center: function ( geometry ) {
295 |
296 | console.warn( 'THREE.GeometryUtils: .center() has been moved to Geometry. Use geometry.center() instead.' );
297 | return geometry.center();
298 |
299 | }
300 |
301 | };
302 |
--------------------------------------------------------------------------------
/js/ImprovedNoise.js:
--------------------------------------------------------------------------------
1 | // http://mrl.nyu.edu/~perlin/noise/
2 |
3 | var ImprovedNoise = function () {
4 |
5 | var p = [151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,
6 | 23,190,6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33,88,237,149,56,87,
7 | 174,20,125,136,171,168,68,175,74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,
8 | 133,230,220,105,92,41,55,46,245,40,244,102,143,54,65,25,63,161,1,216,80,73,209,76,132,187,208,
9 | 89,18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,52,217,226,250,124,123,5,
10 | 202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,119,
11 | 248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,129,22,39,253,19,98,108,110,79,113,224,232,
12 | 178,185,112,104,218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,81,51,145,235,249,
13 | 14,239,107,49,192,214,31,181,199,106,157,184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,
14 | 93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180];
15 |
16 | for (var i=0; i < 256 ; i++) {
17 |
18 | p[256+i] = p[i];
19 |
20 | }
21 |
22 | function fade(t) {
23 |
24 | return t * t * t * (t * (t * 6 - 15) + 10);
25 |
26 | }
27 |
28 | function lerp(t, a, b) {
29 |
30 | return a + t * (b - a);
31 |
32 | }
33 |
34 | function grad(hash, x, y, z) {
35 |
36 | var h = hash & 15;
37 | var u = h < 8 ? x : y, v = h < 4 ? y : h == 12 || h == 14 ? x : z;
38 | return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v);
39 |
40 | }
41 |
42 | return {
43 |
44 | noise: function (x, y, z) {
45 |
46 | var floorX = Math.floor(x), floorY = Math.floor(y), floorZ = Math.floor(z);
47 |
48 | var X = floorX & 255, Y = floorY & 255, Z = floorZ & 255;
49 |
50 | x -= floorX;
51 | y -= floorY;
52 | z -= floorZ;
53 |
54 | var xMinus1 = x -1, yMinus1 = y - 1, zMinus1 = z - 1;
55 |
56 | var u = fade(x), v = fade(y), w = fade(z);
57 |
58 | var A = p[X]+Y, AA = p[A]+Z, AB = p[A+1]+Z, B = p[X+1]+Y, BA = p[B]+Z, BB = p[B+1]+Z;
59 |
60 | return lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z),
61 | grad(p[BA], xMinus1, y, z)),
62 | lerp(u, grad(p[AB], x, yMinus1, z),
63 | grad(p[BB], xMinus1, yMinus1, z))),
64 | lerp(v, lerp(u, grad(p[AA+1], x, y, zMinus1),
65 | grad(p[BA+1], xMinus1, y, z-1)),
66 | lerp(u, grad(p[AB+1], x, yMinus1, zMinus1),
67 | grad(p[BB+1], xMinus1, yMinus1, zMinus1))));
68 |
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/js/Kick.js:
--------------------------------------------------------------------------------
1 | var Kick = function ( o, data ) {
2 | o = o || {};
3 | this.frequency = o.frequency !== undefined ? o.frequency : [ 0, 10 ];
4 | this.threshold = o.threshold !== undefined ? o.threshold : 0.3;
5 | this.decay = o.decay !== undefined ? o.decay : 0.02;
6 | this.onKick = o.onKick;
7 | this.offKick = o.offKick;
8 | this.isOn = false;
9 | this.currentThreshold = this.threshold;
10 | this.value = 0;
11 | this.fftData = data;
12 | };
13 |
14 | Kick.prototype = {
15 | on : function () {
16 | this.isOn = true;
17 | return this;
18 | },
19 | off : function () {
20 | this.isOn = false;
21 | return this;
22 | },
23 |
24 | set : function ( o ) {
25 | o = o || {};
26 | this.frequency = o.frequency !== undefined ? o.frequency : this.frequency;
27 | this.threshold = o.threshold !== undefined ? o.threshold : this.threshold;
28 | this.decay = o.decay !== undefined ? o.decay : this.decay;
29 | this.onKick = o.onKick || this.onKick;
30 | this.offKick = o.offKick || this.offKick;
31 | },
32 |
33 | onUpdate : function () {
34 | if ( !this.isOn ) { return; }
35 | var magnitude = this.maxAmplitude( this.frequency, this.fftData );
36 | this.value = magnitude;
37 | if ( magnitude >= this.currentThreshold &&
38 | magnitude >= this.threshold ) {
39 | this.currentThreshold = magnitude;
40 | this.onKick && this.onKick.call( this, magnitude );
41 | } else {
42 | this.offKick && this.offKick.call( this, magnitude );
43 | this.currentThreshold -= this.decay;
44 | }
45 | },
46 | maxAmplitude : function ( frequency, fft ) {
47 | var max = 0;
48 |
49 | // Sloppy array check
50 | if ( !frequency.length ) {
51 | return frequency < fft.length ?
52 | fft[ ~~frequency ] :
53 | null;
54 | }
55 |
56 | for ( var i = frequency[ 0 ], l = frequency[ 1 ]; i <= l; i++ ) {
57 | if ( fft[ i ] > max ) { max = fft[ i ]; }
58 | }
59 | return max;
60 | }
61 | };
62 |
--------------------------------------------------------------------------------
/js/Maf.js:
--------------------------------------------------------------------------------
1 | (function() {
2 |
3 | // Module code from underscore.js
4 |
5 | // Establish the root object, `window` (`self`) in the browser, `global`
6 | // on the server, or `this` in some virtual machines. We use `self`
7 | // instead of `window` for `WebWorker` support.
8 | var root = typeof self == 'object' && self.self === self && self ||
9 | typeof global == 'object' && global.global === global && global ||
10 | this;
11 |
12 | var Maf = function(obj) {
13 | if (obj instanceof Maf ) return obj;
14 | if (!(this instanceof Maf )) return new Maf(obj);
15 | this._wrapped = obj;
16 | };
17 |
18 | // Export the Underscore object for **Node.js**, with
19 | // backwards-compatibility for their old module API. If we're in
20 | // the browser, add `Maf` as a global object.
21 | // (`nodeType` is checked to ensure that `module`
22 | // and `exports` are not HTML elements.)
23 | if (typeof exports != 'undefined' && !exports.nodeType) {
24 | if (typeof module != 'undefined' && !module.nodeType && module.exports) {
25 | exports = module.exports = Maf;
26 | }
27 | exports.Maf = Maf;
28 | } else {
29 | root.Maf = Maf;
30 | }
31 |
32 | // Current version.
33 | Maf.VERSION = '1.0.0';
34 |
35 | Maf.PI = Math.PI;
36 |
37 | // https://www.opengl.org/sdk/docs/man/html/clamp.xhtml
38 |
39 | Maf.clamp = function( v, minVal, maxVal ) {
40 | return Math.min( maxVal, Math.max( minVal, v ) );
41 | };
42 |
43 | // https://www.opengl.org/sdk/docs/man/html/step.xhtml
44 |
45 | Maf.step = function( edge, v ) {
46 | return ( v < edge ) ? 0 : 1;
47 | }
48 |
49 | // https://www.opengl.org/sdk/docs/man/html/smoothstep.xhtml
50 |
51 | Maf.smoothStep = function ( edge0, edge1, v ) {
52 | var t = Maf.clamp( ( v - edge0 ) / ( edge1 - edge0 ), 0.0, 1.0 );
53 | return t * t * ( 3.0 - 2.0 * t );
54 | };
55 |
56 | // http://docs.unity3d.com/ScriptReference/Mathf.html
57 | // http://www.shaderific.com/glsl-functions/
58 | // https://www.opengl.org/sdk/docs/man4/html/
59 | // https://msdn.microsoft.com/en-us/library/windows/desktop/ff471376(v=vs.85).aspx
60 | // http://moutjs.com/docs/v0.11/math.html#map
61 | // https://code.google.com/p/kuda/source/browse/public/js/hemi/utils/mathUtils.js?r=8d581c02651077c4ac3f5fc4725323210b6b13cc
62 |
63 | // Converts from degrees to radians.
64 | Maf.deg2Rad = function( degrees ) {
65 | return degrees * Math.PI / 180;
66 | };
67 |
68 | Maf.toRadians = Maf.deg2Rad;
69 |
70 | // Converts from radians to degrees.
71 | Maf.rad2Deg = function(radians) {
72 | return radians * 180 / Math.PI;
73 | };
74 |
75 | Maf.toDegrees = Maf.rad2Deg;
76 |
77 | Maf.clamp01 = function( v ) {
78 | return Maf.clamp( v, 0, 1 );
79 | };
80 |
81 | // https://www.opengl.org/sdk/docs/man/html/mix.xhtml
82 |
83 | Maf.mix = function( x, y, a ) {
84 | if( a <= 0 ) return x;
85 | if( a >= 1 ) return y;
86 | return x + a * (y - x)
87 | };
88 |
89 | Maf.lerp = Maf.mix;
90 |
91 | Maf.inverseMix = function( a, b, v ) {
92 | return ( v - a ) / ( b - a );
93 | };
94 |
95 | Maf.inverseLerp = Maf.inverseMix;
96 |
97 | Maf.mixUnclamped = function( x, y, a ) {
98 | if( a <= 0 ) return x;
99 | if( a >= 1 ) return y;
100 | return x + a * (y - x)
101 | };
102 |
103 | Maf.lerpUnclamped = Maf.mixUnclamped;
104 |
105 | // https://www.opengl.org/sdk/docs/man/html/fract.xhtml
106 |
107 | Maf.fract = function( v ) {
108 | return v - Math.floor( v );
109 | };
110 |
111 | Maf.frac = Maf.fract;
112 |
113 | // http://stackoverflow.com/questions/4965301/finding-if-a-number-is-a-power-of-2
114 |
115 | Maf.isPowerOfTwo = function( v ) {
116 | return ( ( ( v - 1) & v ) == 0 );
117 | };
118 |
119 | // https://bocoup.com/weblog/find-the-closest-power-of-2-with-javascript
120 |
121 | Maf.closestPowerOfTwo = function( v ) {
122 | return Math.pow( 2, Math.round( Math.log( v ) / Math.log( 2 ) ) );
123 | };
124 |
125 | Maf.nextPowerOfTwo = function( v ) {
126 | return Math.pow( 2, Math.ceil( Math.log( v ) / Math.log( 2 ) ) );
127 | }
128 |
129 | // http://stackoverflow.com/questions/1878907/the-smallest-difference-between-2-angles
130 |
131 | //function mod(a, n) { return a - Math.floor(a/n) * n; }
132 | Maf.mod = function(a, n) { return (a % n + n) % n; }
133 |
134 | Maf.deltaAngle = function( a, b ) {
135 | var d = Maf.mod( b - a, 360 );
136 | if( d > 180 ) d = Math.abs( d - 360 );
137 | return d;
138 | };
139 |
140 | Maf.deltaAngleDeg = Maf.deltaAngle;
141 |
142 | Maf.deltaAngleRad = function( a, b ) {
143 | return Maf.toRadians( Maf.deltaAngle( Maf.toDegrees( a ), Maf.toDegrees( b ) ) );
144 | };
145 |
146 | Maf.lerpAngle = function( a, b, t ) {
147 | var angle = Maf.deltaAngle( a, b );
148 | return Maf.mod( a + Maf.lerp( 0, angle, t ), 360 );
149 | };
150 |
151 | Maf.lerpAngleDeg = Maf.lerpAngle;
152 |
153 | Maf.lerpAngleRad = function( a, b, t ) {
154 | return Maf.toRadians( Maf.lerpAngleDeg( Maf.toDegrees( a ), Maf.toDegrees( b ), t ) );
155 | };
156 |
157 | // http://gamedev.stackexchange.com/questions/74324/gamma-space-and-linear-space-with-shader
158 |
159 | Maf.gammaToLinearSpace = function( v ) {
160 | return Math.pow( v, 2.2 );
161 | };
162 |
163 | Maf.linearToGammaSpace = function( v ) {
164 | return Math.pow( v, 1 / 2.2 );
165 | };
166 |
167 | Maf.map = function( from1, to1, from2, to2, v ) {
168 | return from2 + ( v - from1 ) * ( to2 - from2 ) / ( to1 - from1 );
169 | }
170 |
171 | Maf.scale = Maf.map;
172 |
173 | // http://www.iquilezles.org/www/articles/functions/functions.htm
174 |
175 | Maf.almostIdentity = function( x, m, n ) {
176 |
177 | if( x > m ) return x;
178 |
179 | var a = 2 * n - m;
180 | var b = 2 * m - 3 * n;
181 | var t = x / m;
182 |
183 | return ( a * t + b) * t * t + n;
184 | }
185 |
186 | Maf.impulse = function( k, x ) {
187 | var h = k * x;
188 | return h * Math.exp( 1 - h );
189 | };
190 |
191 | Maf.cubicPulse = function( c, w, x ) {
192 | x = Math.abs( x - c );
193 | if( x > w ) return 0;
194 | x /= w;
195 | return 1 - x * x * ( 3 - 2 * x );
196 | }
197 |
198 | Maf.expStep = function( x, k, n ) {
199 | return Math.exp( -k * Math.pow( x, n ) );
200 | }
201 |
202 | Maf.parabola = function( x, k ) {
203 | return Math.pow( 4 * x * ( 1 - x ), k );
204 | }
205 |
206 | Maf.powerCurve = function( x, a, b ) {
207 | var k = Math.pow( a + b, a + b ) / ( Math.pow( a, a ) * Math.pow( b, b ) );
208 | return k * Math.pow( x, a ) * Math.pow( 1 - x, b );
209 | }
210 |
211 | // http://iquilezles.org/www/articles/smin/smin.htm ?
212 |
213 | Maf.latLonToCartesian = function( lat, lon ) {
214 |
215 | lon += 180;
216 | lat = Maf.clamp( lat, -85, 85 );
217 | var phi = Maf.toRadians( 90 - lat );
218 | var theta = Maf.toRadians( 180 - lon );
219 | var x = Math.sin( phi ) * Math.cos( theta );
220 | var y = Math.cos( phi );
221 | var z = Math.sin( phi ) * Math.sin( theta );
222 |
223 | return { x: x, y: y, z: z }
224 |
225 | }
226 |
227 | Maf.cartesianToLatLon = function( x, y, z ) {
228 | var n = Math.sqrt( x * x + y * y + z * z );
229 | return{ lat: Math.asin( z / n ), lon: Math.atan2( y, x ) };
230 | }
231 |
232 | Maf.randomInRange = function( min, max ) {
233 | return min + Math.random() * ( max - min );
234 | }
235 |
236 | Maf.norm = function( v, minVal, maxVal ) {
237 | return ( v - minVal ) / ( maxVal - minVal );
238 | }
239 |
240 | Maf.hash = function( n ) {
241 | return Maf.fract( (1.0 + Math.cos(n)) * 415.92653);
242 | }
243 |
244 | Maf.noise2d = function( x, y ) {
245 | var xhash = Maf.hash( x * 37.0 );
246 | var yhash = Maf.hash( y * 57.0 );
247 | return Maf.fract( xhash + yhash );
248 | }
249 |
250 | // http://iquilezles.org/www/articles/smin/smin.htm
251 |
252 | Maf.smoothMin = function( a, b, k ) {
253 | var res = Math.exp( -k*a ) + Math.exp( -k*b );
254 | return - Math.log( res )/k;
255 | }
256 |
257 | Maf.smoothMax = function( a, b, k ){
258 | return Math.log( Math.exp(a) + Math.exp(b) )/k;
259 | }
260 |
261 | Maf.almost = function( a, b ) {
262 | return ( Math.abs( a - b ) < .0001 );
263 | }
264 |
265 | }());
--------------------------------------------------------------------------------
/js/MeshCustomMaterial.js:
--------------------------------------------------------------------------------
1 | function MeshCustomMaterial (parameters) {
2 | THREE.MeshStandardMaterial.call( this );
3 | this.uniforms = THREE.UniformsUtils.merge([
4 | THREE.UniformsLib.common,
5 | THREE.UniformsLib.aomap,
6 | THREE.UniformsLib.lightmap,
7 | THREE.UniformsLib.emissivemap,
8 | THREE.UniformsLib.bumpmap,
9 | THREE.UniformsLib.normalmap,
10 | THREE.UniformsLib.displacementmap,
11 | THREE.UniformsLib.roughnessmap,
12 | THREE.UniformsLib.metalnessmap,
13 | THREE.UniformsLib.fog,
14 | THREE.UniformsLib.lights,
15 | parameters.u
16 | ]);
17 |
18 | this.vertexShader = parameters.vertexShader;
19 | this.fragmentShader = parameters.fragmentShader;
20 | this.type = 'MeshCustomMaterial';
21 |
22 | this.setValues(parameters);
23 | }
24 |
25 | MeshCustomMaterial.prototype = Object.create( THREE.MeshStandardMaterial.prototype );
26 | MeshCustomMaterial.prototype.constructor = MeshCustomMaterial;
27 | MeshCustomMaterial.prototype.isMeshStandardMaterial = true;
28 |
29 | MeshCustomMaterial.prototype.copy = function ( source ) {
30 | THREE.MeshStandardMaterial.prototype.copy.call( this, source );
31 | this.uniforms = THREE.UniformsUtils.clone(source.uniforms);
32 | setFlags(this);
33 | return this;
34 | };
35 |
--------------------------------------------------------------------------------
/js/Mirror.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Slayvin / http://slayvin.net
3 | */
4 |
5 | THREE.ShaderLib[ 'mirror' ] = {
6 |
7 | uniforms: {
8 | "mirrorColor": { value: new THREE.Color( 0x7F7F7F ) },
9 | "mirrorSampler": { value: null },
10 | "textureMatrix" : { value: new THREE.Matrix4() }
11 | },
12 |
13 | vertexShader: [
14 |
15 | "uniform mat4 textureMatrix;",
16 |
17 | "varying vec4 mirrorCoord;",
18 |
19 | "void main() {",
20 |
21 | "vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );",
22 | "vec4 worldPosition = modelMatrix * vec4( position, 1.0 );",
23 | "mirrorCoord = textureMatrix * worldPosition;",
24 |
25 | "gl_Position = projectionMatrix * mvPosition;",
26 |
27 | "}"
28 |
29 | ].join( "\n" ),
30 |
31 | fragmentShader: [
32 |
33 | "uniform vec3 mirrorColor;",
34 | "uniform sampler2D mirrorSampler;",
35 |
36 | "varying vec4 mirrorCoord;",
37 |
38 | "float blendOverlay(float base, float blend) {",
39 | "return( base < 0.5 ? ( 2.0 * base * blend ) : (1.0 - 2.0 * ( 1.0 - base ) * ( 1.0 - blend ) ) );",
40 | "}",
41 |
42 | "void main() {",
43 |
44 | "vec4 color = texture2DProj(mirrorSampler, mirrorCoord);",
45 | "color = vec4(blendOverlay(mirrorColor.r, color.r), blendOverlay(mirrorColor.g, color.g), blendOverlay(mirrorColor.b, color.b), 1.0);",
46 |
47 | "gl_FragColor = color;",
48 |
49 | "}"
50 |
51 | ].join( "\n" )
52 |
53 | };
54 |
55 | THREE.Mirror = function ( renderer, camera, options ) {
56 |
57 | THREE.Object3D.call( this );
58 |
59 | this.name = 'mirror_' + this.id;
60 |
61 | options = options || {};
62 |
63 | this.matrixNeedsUpdate = true;
64 |
65 | var width = options.textureWidth !== undefined ? options.textureWidth : 512;
66 | var height = options.textureHeight !== undefined ? options.textureHeight : 512;
67 |
68 | this.clipBias = options.clipBias !== undefined ? options.clipBias : 0.0;
69 |
70 | var mirrorColor = options.color !== undefined ? new THREE.Color( options.color ) : new THREE.Color( 0x7F7F7F );
71 |
72 | this.renderer = renderer;
73 | this.mirrorPlane = new THREE.Plane();
74 | this.normal = new THREE.Vector3( 0, 0, 1 );
75 | this.mirrorWorldPosition = new THREE.Vector3();
76 | this.cameraWorldPosition = new THREE.Vector3();
77 | this.rotationMatrix = new THREE.Matrix4();
78 | this.lookAtPosition = new THREE.Vector3( 0, 0, - 1 );
79 | this.clipPlane = new THREE.Vector4();
80 |
81 | // For debug only, show the normal and plane of the mirror
82 | var debugMode = options.debugMode !== undefined ? options.debugMode : false;
83 |
84 | if ( debugMode ) {
85 |
86 | var arrow = new THREE.ArrowHelper( new THREE.Vector3( 0, 0, 1 ), new THREE.Vector3( 0, 0, 0 ), 10, 0xffff80 );
87 | var planeGeometry = new THREE.Geometry();
88 | planeGeometry.vertices.push( new THREE.Vector3( - 10, - 10, 0 ) );
89 | planeGeometry.vertices.push( new THREE.Vector3( 10, - 10, 0 ) );
90 | planeGeometry.vertices.push( new THREE.Vector3( 10, 10, 0 ) );
91 | planeGeometry.vertices.push( new THREE.Vector3( - 10, 10, 0 ) );
92 | planeGeometry.vertices.push( planeGeometry.vertices[ 0 ] );
93 | var plane = new THREE.Line( planeGeometry, new THREE.LineBasicMaterial( { color: 0xffff80 } ) );
94 |
95 | this.add( arrow );
96 | this.add( plane );
97 |
98 | }
99 |
100 | if ( camera instanceof THREE.PerspectiveCamera ) {
101 |
102 | this.camera = camera;
103 |
104 | } else {
105 |
106 | this.camera = new THREE.PerspectiveCamera();
107 | console.log( this.name + ': camera is not a Perspective Camera!' );
108 |
109 | }
110 |
111 | this.textureMatrix = new THREE.Matrix4();
112 |
113 | this.mirrorCamera = this.camera.clone();
114 | this.mirrorCamera.matrixAutoUpdate = true;
115 |
116 | var parameters = { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBFormat, stencilBuffer: false };
117 |
118 | this.renderTarget = new THREE.WebGLRenderTarget( width, height, parameters );
119 | this.renderTarget2 = new THREE.WebGLRenderTarget( width, height, parameters );
120 |
121 | var mirrorShader = THREE.ShaderLib[ "mirror" ];
122 | var mirrorUniforms = THREE.UniformsUtils.clone( mirrorShader.uniforms );
123 |
124 | this.material = new THREE.ShaderMaterial( {
125 |
126 | fragmentShader: mirrorShader.fragmentShader,
127 | vertexShader: mirrorShader.vertexShader,
128 | uniforms: mirrorUniforms
129 |
130 | } );
131 |
132 | this.material.uniforms.mirrorSampler.value = this.renderTarget.texture;
133 | this.material.uniforms.mirrorColor.value = mirrorColor;
134 | this.material.uniforms.textureMatrix.value = this.textureMatrix;
135 |
136 | if ( ! THREE.Math.isPowerOfTwo( width ) || ! THREE.Math.isPowerOfTwo( height ) ) {
137 |
138 | this.renderTarget.texture.generateMipmaps = false;
139 | this.renderTarget2.texture.generateMipmaps = false;
140 |
141 | }
142 |
143 | this.updateTextureMatrix();
144 | this.render();
145 |
146 | };
147 |
148 | THREE.Mirror.prototype = Object.create( THREE.Object3D.prototype );
149 | THREE.Mirror.prototype.constructor = THREE.Mirror;
150 |
151 | THREE.Mirror.prototype.renderWithMirror = function ( otherMirror ) {
152 |
153 | // update the mirror matrix to mirror the current view
154 | this.updateTextureMatrix();
155 | this.matrixNeedsUpdate = false;
156 |
157 | // set the camera of the other mirror so the mirrored view is the reference view
158 | var tempCamera = otherMirror.camera;
159 | otherMirror.camera = this.mirrorCamera;
160 |
161 | // render the other mirror in temp texture
162 | otherMirror.renderTemp();
163 | otherMirror.material.uniforms.mirrorSampler.value = otherMirror.renderTarget2.texture;
164 |
165 | // render the current mirror
166 | this.render();
167 | this.matrixNeedsUpdate = true;
168 |
169 | // restore material and camera of other mirror
170 | otherMirror.material.uniforms.mirrorSampler.value = otherMirror.renderTarget.texture;
171 | otherMirror.camera = tempCamera;
172 |
173 | // restore texture matrix of other mirror
174 | otherMirror.updateTextureMatrix();
175 |
176 | };
177 |
178 | THREE.Mirror.prototype.updateTextureMatrix = function () {
179 |
180 | this.updateMatrixWorld();
181 | this.camera.updateMatrixWorld();
182 |
183 | this.mirrorWorldPosition.setFromMatrixPosition( this.matrixWorld );
184 | this.cameraWorldPosition.setFromMatrixPosition( this.camera.matrixWorld );
185 |
186 | this.rotationMatrix.extractRotation( this.matrixWorld );
187 |
188 | this.normal.set( 0, 0, 1 );
189 | this.normal.applyMatrix4( this.rotationMatrix );
190 |
191 | var view = this.mirrorWorldPosition.clone().sub( this.cameraWorldPosition );
192 | view.reflect( this.normal ).negate();
193 | view.add( this.mirrorWorldPosition );
194 |
195 | this.rotationMatrix.extractRotation( this.camera.matrixWorld );
196 |
197 | this.lookAtPosition.set( 0, 0, - 1 );
198 | this.lookAtPosition.applyMatrix4( this.rotationMatrix );
199 | this.lookAtPosition.add( this.cameraWorldPosition );
200 |
201 | var target = this.mirrorWorldPosition.clone().sub( this.lookAtPosition );
202 | target.reflect( this.normal ).negate();
203 | target.add( this.mirrorWorldPosition );
204 |
205 | this.up.set( 0, - 1, 0 );
206 | this.up.applyMatrix4( this.rotationMatrix );
207 | this.up.reflect( this.normal ).negate();
208 |
209 | this.mirrorCamera.position.copy( view );
210 | this.mirrorCamera.up = this.up;
211 | this.mirrorCamera.lookAt( target );
212 |
213 | this.mirrorCamera.updateProjectionMatrix();
214 | this.mirrorCamera.updateMatrixWorld();
215 | this.mirrorCamera.matrixWorldInverse.getInverse( this.mirrorCamera.matrixWorld );
216 |
217 | // Update the texture matrix
218 | this.textureMatrix.set( 0.5, 0.0, 0.0, 0.5,
219 | 0.0, 0.5, 0.0, 0.5,
220 | 0.0, 0.0, 0.5, 0.5,
221 | 0.0, 0.0, 0.0, 1.0 );
222 | this.textureMatrix.multiply( this.mirrorCamera.projectionMatrix );
223 | this.textureMatrix.multiply( this.mirrorCamera.matrixWorldInverse );
224 |
225 | // Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html
226 | // Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf
227 | this.mirrorPlane.setFromNormalAndCoplanarPoint( this.normal, this.mirrorWorldPosition );
228 | this.mirrorPlane.applyMatrix4( this.mirrorCamera.matrixWorldInverse );
229 |
230 | this.clipPlane.set( this.mirrorPlane.normal.x, this.mirrorPlane.normal.y, this.mirrorPlane.normal.z, this.mirrorPlane.constant );
231 |
232 | var q = new THREE.Vector4();
233 | var projectionMatrix = this.mirrorCamera.projectionMatrix;
234 |
235 | q.x = ( Math.sign( this.clipPlane.x ) + projectionMatrix.elements[ 8 ] ) / projectionMatrix.elements[ 0 ];
236 | q.y = ( Math.sign( this.clipPlane.y ) + projectionMatrix.elements[ 9 ] ) / projectionMatrix.elements[ 5 ];
237 | q.z = - 1.0;
238 | q.w = ( 1.0 + projectionMatrix.elements[ 10 ] ) / projectionMatrix.elements[ 14 ];
239 |
240 | // Calculate the scaled plane vector
241 | var c = new THREE.Vector4();
242 | c = this.clipPlane.multiplyScalar( 2.0 / this.clipPlane.dot( q ) );
243 |
244 | // Replacing the third row of the projection matrix
245 | projectionMatrix.elements[ 2 ] = c.x;
246 | projectionMatrix.elements[ 6 ] = c.y;
247 | projectionMatrix.elements[ 10 ] = c.z + 1.0 - this.clipBias;
248 | projectionMatrix.elements[ 14 ] = c.w;
249 |
250 | };
251 |
252 | THREE.Mirror.prototype.render = function () {
253 |
254 | if ( this.matrixNeedsUpdate ) this.updateTextureMatrix();
255 |
256 | this.matrixNeedsUpdate = true;
257 |
258 | // Render the mirrored view of the current scene into the target texture
259 | var scene = this;
260 |
261 | while ( scene.parent !== null ) {
262 |
263 | scene = scene.parent;
264 |
265 | }
266 |
267 | if ( scene !== undefined && scene instanceof THREE.Scene ) {
268 |
269 | // We can't render ourself to ourself
270 | var visible = this.material.visible;
271 | this.material.visible = false;
272 |
273 | this.renderer.render( scene, this.mirrorCamera, this.renderTarget, true );
274 |
275 | this.material.visible = visible;
276 |
277 | }
278 |
279 | };
280 |
281 | THREE.Mirror.prototype.renderTemp = function () {
282 |
283 | if ( this.matrixNeedsUpdate ) this.updateTextureMatrix();
284 |
285 | this.matrixNeedsUpdate = true;
286 |
287 | // Render the mirrored view of the current scene into the target texture
288 | var scene = this;
289 |
290 | while ( scene.parent !== null ) {
291 |
292 | scene = scene.parent;
293 |
294 | }
295 |
296 | if ( scene !== undefined && scene instanceof THREE.Scene ) {
297 |
298 | this.renderer.render( scene, this.mirrorCamera, this.renderTarget2, true );
299 |
300 | }
301 |
302 | };
303 |
--------------------------------------------------------------------------------
/js/Odeo.js:
--------------------------------------------------------------------------------
1 | // /ˈôdēˌō/
2 |
3 | (function () {
4 | var AudioContext = window.AudioContext || window.webkitAudioContext;
5 |
6 | function OdeoSoundCloudPlayer(id, odeo) {
7 | this.id = id;
8 | this.odeo = odeo;
9 |
10 | SC.initialize({
11 | client_id: this.id,
12 | });
13 |
14 | this.audio = document.createElement("audio");
15 | this.audio.loop = true;
16 | this.audio.autoplay = true;
17 | this.audio.crossOrigin = "";
18 |
19 | this.songSource = this.odeo.context.createMediaElementSource(this.audio);
20 | this.songSource.connect(this.odeo.analyser);
21 | this.songSource.connect(this.odeo.context.destination);
22 | }
23 |
24 | OdeoSoundCloudPlayer.prototype.getSong = function (songURL) {
25 | SC.resolve(songURL).then(
26 | function (song) {
27 | console.log(song);
28 | //songInfo.innerHTML = '