├── .PhysicsRenderer.js.swp
├── PhysicsRenderer.js
├── README.md
├── assests
├── GeosansLight.ttf
└── note.mp3
├── examples
├── .collisions.html.swp
├── .curl.html.swp
├── collisions.html
├── curl.html
├── flocking.html
└── gravity.html
├── index.html
├── lib
├── .ShaderLoader.js.swp
├── ShaderLoader.js
├── TrackballControls.js
├── jquery.min.js
├── three.js
└── underscore.js
└── shaders
├── .fs-lookup.glsl.swp
├── .simplex.glsl.swp
├── .ss-collisions.glsl.swp
├── .ss-curl.glsl.swo
├── .ss-curl.glsl.swp
├── .ss-text.glsl.swp
├── .vs-lookup.glsl.swp
├── curl.glsl
├── fs-lookup.glsl
├── fs-lookupFront.glsl
├── rand.glsl
├── simplex.glsl
├── ss-collisions.glsl
├── ss-curl.glsl
├── ss-curlFront.glsl
├── ss-flocking.glsl
├── ss-gravity.glsl
├── ss-text.glsl
├── vs-lookup.glsl
└── vs-lookupFront.glsl
/.PhysicsRenderer.js.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/.PhysicsRenderer.js.swp
--------------------------------------------------------------------------------
/PhysicsRenderer.js:
--------------------------------------------------------------------------------
1 | function PhysicsRenderer( size , shader , renderer ){
2 |
3 | // First Make sure everything Works
4 | this.checkCompatibility( renderer );
5 | this.renderer = renderer;
6 |
7 | this.size = size || 128;
8 | this.s2 = size * size;
9 |
10 | this.renderer = renderer;
11 |
12 | this.clock = new THREE.Clock();
13 |
14 | this.resolution = new THREE.Vector2( this.size , this.size );
15 |
16 |
17 |
18 | // Sets up our render targets
19 | this.rt_1 = new THREE.WebGLRenderTarget( this.size, this.size, {
20 | minFilter: THREE.NearestFilter,
21 | magFilter: THREE.NearestFilter,
22 | format: THREE.RGBAFormat,
23 | type:THREE.FloatType,
24 | stencilBuffer: false
25 | });
26 |
27 | this.rt_2 = this.rt_1.clone();
28 | this.rt_3 = this.rt_1.clone();
29 |
30 | this.counter = 0;
31 |
32 | this.debugScene = this.createDebugScene();
33 | this.texturePassProgram = this.createTexturePassProgram();
34 |
35 | // WHERE THE MAGIC HAPPENS
36 | this.simulation = this.createSimulationProgram( shader );
37 | this.material = this.simulation;
38 |
39 | this.boundTextures = [];
40 |
41 | /*
42 |
43 | GPGPU Utilities
44 | From Sporel by Mr.Doob
45 | @author mrdoob / http://www.mrdoob.com
46 |
47 | */
48 |
49 | this.camera = new THREE.OrthographicCamera( - 0.5, 0.5, 0.5, - 0.5, 0, 1 );
50 | this.scene = new THREE.Scene();
51 | this.mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 1, 1 ) );
52 | this.scene.add( this.mesh );
53 |
54 | }
55 |
56 | PhysicsRenderer.prototype.checkCompatibility = function( renderer ){
57 |
58 | var gl = renderer.context;
59 |
60 | if ( gl.getExtension( "OES_texture_float" ) === null ) {
61 | this.onError( "No Float Textures");
62 | return;
63 | }
64 |
65 | if ( gl.getParameter( gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS ) === 0 ) {
66 | this.onError( "Vert Shader Textures don't work");
67 | return;
68 | }
69 |
70 | }
71 |
72 | PhysicsRenderer.prototype.onError = function( e ){
73 | console.log( e );
74 | }
75 |
76 | PhysicsRenderer.prototype.createDebugScene= function(){
77 |
78 | var debugScene = new THREE.Object3D();
79 | debugScene.position.z = 0;
80 |
81 | var geo = new THREE.PlaneBufferGeometry( 100 , 100 );
82 |
83 | var debugMesh = new THREE.Mesh( geo , new THREE.MeshBasicMaterial({
84 | map: this.rt_1
85 | }));
86 | debugMesh.position.set( -105 , 0 , 0 );
87 |
88 | debugScene.add( debugMesh );
89 |
90 | var debugMesh = new THREE.Mesh( geo , new THREE.MeshBasicMaterial({
91 | map: this.rt_2
92 | }));
93 | debugMesh.position.set( 0 , 0 , 0 );
94 | debugScene.add( debugMesh );
95 |
96 | var debugMesh = new THREE.Mesh( geo , new THREE.MeshBasicMaterial({
97 | map: this.rt_3
98 | }));
99 | debugMesh.position.set( 105, 0 , 0 );
100 | debugScene.add( debugMesh );
101 |
102 | return debugScene;
103 |
104 | }
105 |
106 | PhysicsRenderer.prototype.removeDebugScene = function( scene ){
107 | scene.remove( this.debugScene );
108 | }
109 |
110 | PhysicsRenderer.prototype.addDebugScene = function( scene ){
111 | scene.add( this.debugScene );
112 | }
113 |
114 |
115 | PhysicsRenderer.prototype.createTexturePassProgram = function(){
116 |
117 | var uniforms = {
118 | texture:{ type:"t" , value:null },
119 | }
120 |
121 | var texturePassShader = new THREE.ShaderMaterial({
122 | uniforms:uniforms,
123 | vertexShader:this.VSPass,
124 | fragmentShader:this.FSPass,
125 | });
126 |
127 | return texturePassShader;
128 |
129 | }
130 |
131 | PhysicsRenderer.prototype.createSimulationProgram = function(sim){
132 |
133 | this.simulationUniforms = {
134 | t_oPos:{ type:"t" , value:null },
135 | t_pos:{ type:"t" , value:null },
136 | resolution: { type:"v2" , value: this.resolution }
137 | }
138 |
139 |
140 | var program = new THREE.ShaderMaterial({
141 |
142 | uniforms:this.simulationUniforms,
143 | vertexShader:this.VSPass,
144 | fragmentShader:sim
145 |
146 | });
147 |
148 | return program;
149 |
150 | }
151 |
152 |
153 | PhysicsRenderer.prototype.VSPass = [
154 | "varying vec2 vUv;",
155 | "void main() {",
156 | " vUv = uv;",
157 | " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
158 | "}"
159 | ].join("\n");
160 |
161 | PhysicsRenderer.prototype.FSPass = [
162 | "uniform sampler2D texture;",
163 | "varying vec2 vUv;",
164 | "void main() {",
165 | " vec4 c = texture2D( texture , vUv );",
166 | " gl_FragColor = c ;",
167 | "}"
168 | ].join("\n");
169 |
170 |
171 | PhysicsRenderer.prototype.update = function(){
172 |
173 | var flipFlop = this.counter % 3;
174 |
175 | if( flipFlop == 0 ){
176 |
177 | this.simulation.uniforms.t_oPos.value = this.rt_1;
178 | this.simulation.uniforms.t_pos.value = this.rt_2;
179 |
180 | this.pass( this.simulation, this.rt_3 );
181 |
182 | this.ooOutput = this.rt_1;
183 | this.oOutput = this.rt_2;
184 | this.output = this.rt_3;
185 |
186 | }else if( flipFlop == 1 ){
187 |
188 | this.simulation.uniforms.t_oPos.value = this.rt_2;
189 | this.simulation.uniforms.t_pos.value = this.rt_3;
190 |
191 | this.pass( this.simulation , this.rt_1 );
192 |
193 | this.ooOutput = this.rt_2;
194 | this.oOutput = this.rt_3;
195 | this.output = this.rt_1;
196 |
197 |
198 | }else if( flipFlop == 2 ){
199 |
200 | this.simulation.uniforms.t_oPos.value = this.rt_3;
201 | this.simulation.uniforms.t_pos.value = this.rt_1;
202 |
203 | this.pass( this.simulation , this.rt_2 );
204 |
205 | this.ooOutput = this.rt_3;
206 | this.oOutput = this.rt_1;
207 | this.output = this.rt_2;
208 |
209 | }
210 |
211 | this.counter ++;
212 |
213 | this.bindTextures();
214 |
215 | }
216 |
217 | // Some GPGPU Utilities author: @mrdoob
218 | PhysicsRenderer.prototype.render = function ( scene, camera, target ) {
219 | renderer.render( scene, camera, target, false );
220 | };
221 |
222 | PhysicsRenderer.prototype.pass = function ( shader , target ) {
223 | this.mesh.material = shader;
224 | this.renderer.render( this.scene, this.camera, target, false );
225 | };
226 |
227 | PhysicsRenderer.prototype.out = function ( shader ) {
228 | this.mesh.material = shader.material;
229 | this.renderer.render( this.scene, this.camera );
230 | };
231 |
232 | // Used if he have uniforms we want to update!
233 | PhysicsRenderer.prototype.setUniforms = function( uniforms ){
234 |
235 | for( var propt in uniforms ){
236 |
237 | this.simulation.uniforms[ propt ] = uniforms[ propt]
238 |
239 | }
240 |
241 | // Have to make sure that these always remain!
242 | this.simulation.uniforms.t_pos = { type:"t" , value:null };
243 | this.simulation.uniforms.t_oPos = { type:"t" , value:null };
244 | this.simulation.uniforms.resolution = { type:"v2" , value:this.resolution };
245 |
246 | console.log( this.simulation.uniforms );
247 |
248 | }
249 |
250 | PhysicsRenderer.prototype.setUniform = function( name , u ){
251 | this.simulation.uniforms[name] = u;
252 | }
253 |
254 | // resets the render targets to the from position
255 | PhysicsRenderer.prototype.reset = function( texture ){
256 |
257 | this.texture = texture;
258 | this.texturePassProgram.uniforms.texture.value = texture;
259 |
260 | this.pass( this.texturePassProgram , this.rt_1 );
261 | this.pass( this.texturePassProgram , this.rt_2 );
262 | this.pass( this.texturePassProgram , this.rt_3 );
263 |
264 | }
265 |
266 | PhysicsRenderer.prototype.passTexture = function( t1 , t2 ){
267 |
268 | this.texturePassProgram.uniforms.texture.value = t1;
269 | this.pass( this.texturePassProgram , t2 );
270 |
271 | }
272 |
273 |
274 | // resets the render targets to the from position
275 | PhysicsRenderer.prototype.resetRand = function( size , alpha ){
276 |
277 | var size = size || 100;
278 | var data = new Float32Array( this.s2 * 4 );
279 |
280 | for( var i =0; i < data.length; i++ ){
281 |
282 | //console.log('ss');
283 | data[ i ] = (Math.random() - .5 ) * size;
284 |
285 | if( alpha && i % 4 ===3 ){
286 | data[i] = 0;
287 | }
288 |
289 | }
290 |
291 | var texture = new THREE.DataTexture(
292 | data,
293 | this.size,
294 | this.size,
295 | THREE.RGBAFormat,
296 | THREE.FloatType
297 | );
298 |
299 | texture.minFilter = THREE.NearestFilter,
300 | texture.magFilter = THREE.NearestFilter,
301 |
302 | texture.needsUpdate = true;
303 |
304 | this.reset( texture);
305 |
306 | }
307 |
308 |
309 |
310 | PhysicsRenderer.prototype.addBoundTexture = function( uniform , value ){
311 | this.boundTextures.push( [ uniform , value ] );
312 | }
313 |
314 | PhysicsRenderer.prototype.bindTextures = function(){
315 |
316 | for( var i = 0; i < this.boundTextures.length; i++ ){
317 |
318 | var uniform = this.boundTextures[i][0];
319 | var textureToBind = this.boundTextures[i][1];
320 |
321 | uniform.value = this[ textureToBind ];
322 |
323 |
324 | }
325 |
326 | }
327 |
328 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # PhysicsRenderer
2 | - Check it out on [Github] !!!!!
3 | - Contact me via [TWITTER]!!!!!
4 |
5 | ### Introduction
6 |
7 | An awesome lil helper to help you do gpgpu calculations! Inspired by a few awesome projects including:
8 |
9 | - [Sporel] - An awesome demo by @mrdoob
10 | - [ThreeJS GPGPU Examples] - A great example of flocking by @BlurSpline
11 | - [Soulwire's GPGPU Particles] - Prettiness by the one and only @soulwire
12 |
13 | Using this tool ( or previous, uglier versions of it ) I've made a whole smorgasbord of projects, including, but most definitely not limited to:
14 |
15 | - [Needs]
16 | - [Flow]
17 | - [NVS]
18 | - [We Over]
19 | - [Bees]
20 | - [DRAGONFISH]
21 | - [Huldra]
22 | - [Diamonds]
23 |
24 | Because GPGPU Calculations are not something many developers are familiar with, I'd like to talk a bit about them, including pros / cons , limitations, etc, but if you want to skip straight to the good stuff ( AKA Code ), feel free to dive into the following examples. This definitely is a subject that staring at code really helps with:
25 |
26 | - [Curl Noise]
27 | - [Collisions]
28 | - [Gravity]
29 | - [Flocking]
30 |
31 | Also, at any point in time, jump straight over to the [Github] for the code! With all of this being said, lets start talking about how to use the Physics Renderer! If you want to know a bit more about how it actually works, check out the BACKGROUND section, but hopefully it is easy enough that you shouldn't *have* to know how crazy the gpgpu really gets!
32 |
33 |
34 | ###Caveats
35 |
36 | First things first, The PhysicsRenderer requires the use of Floating Point Textures. This is not something that is available ( *YET* ) in phones, so if you are trying to make a mobile friendly site, please run while you still can!
37 |
38 | Second, GPU performance varies dramatically from computer to computer, so make sure you have multiple machines, or multiple friends with multiple machines, to test on!
39 |
40 | Third, This specific renderer uses 2 positions textures to get a velocity, and interpolates based on that. Its much more efficient than having a seperate pass for position and velocity, but makes other parts more difficult, such as collisions. I have plans to add different types of render pases into this ( 3 old positions , pos / vel , etc ), but have not done so yet! If you have any good ideas of how to do this, let me know on [TWITTER]
41 |
42 | Fourth, for most of these examples, I will be using another Utility that I have written: [ShaderLoader] , because its a cool tool, and I'm too lazy to make things from scratch, but rest assured that ANY shader you write can still be used in this tool!
43 |
44 |
45 |
46 | #Usage
47 |
48 | ##Boring Setup Stuff:
49 |
50 | ###Including Scripts
51 |
52 | As usual, the first thing you need to do is include the script
53 | ```javascript
54 |
55 | ```
56 |
57 | ###Initializing the PhysicsRenderer
58 |
59 | Within the 'Initialization' phase of your application, create the renderer. To do this you will need 3 pieces of information:
60 |
61 | - Size of Simulation ( actual size is this number squared )
62 | - The Simulation Shader
63 | - WebGL Renderer
64 |
65 | ##### Size
66 | The size that will be passed into the PhysicsRenderer is not actually the number of particles, but rather the width / height of the texture to be used. This means that the actual number of positions in the simulation will be Size * Size . Also, for the sake of older GPUs please try and keep this number a Power of 2 , AKA: 2 , 4 , 8 , 16 , 32 , 64 , 128 , 256 , 512 , 1024 ( that corresponds to 1048576 different position calculations BTW.... )
67 |
68 | ##### Simulation Shader
69 | This is the soul of the physics renderer. You can think of it as the 'Kernel' that you are passing into to the GPU to be run. I will go into more depth about what this is later, but for now, just remember that is is a Fragment Shader passed in as a text string.
70 |
71 | ##### WebGL Renderer
72 | This will just be the Three.js Renderer you use for everything else, so nothing fancy hear, just make sure you declare it before you create the PhysicsRenderer.
73 |
74 | Putting all these together the intialization looks a lil something like this:
75 |
76 | ```javascript
77 | var size = 128;
78 | var simulationShader = "SIMULATION FRAGMENT SHADER"
79 | renderer = new THREE.WebGLRenderer();
80 |
81 | physicsRenderer = new PhysicsRenderer( size , simulationShader , renderer );
82 | ```
83 |
84 | ###Updating the Renderer
85 | Once its been initialized, all we have to do to use the renderer is update it in our animation loop, like so
86 | ```javascript
87 | physicsRenderer.update();
88 | ```
89 |
90 | If you do only these things, nothing is going to happen, and infact, you may get some errors, so lets dig a bit further and talk about what the simulation shader is going to look like:
91 |
92 | ##Simulation Shader:
93 | The simulation shader is the most important part of this tool, and everything else about the tool is just trying to get it to run. It will be written in GLSL, so we'll break it down by sections of the glsl program
94 |
95 | ### Uniforms
96 |
97 | The first thing we will have to do is include the proper uniforms. In our case there are 3 that are mandatory: The Current Position Texture , The Old Positition Texture, and the resolution of simulation. Declaring them looks like so:
98 |
99 | ```glsl
100 | uniform sampler2D t_pos; // Current Position Texture
101 | uniform sampler2D t_oPos; // Old Position Texture
102 | uniform vec2 resolution; // Resolution of simulation
103 | ```
104 |
105 | *ALWAYS PUT THESE IN YOUR PROGRAM!!!*
106 |
107 | ### Main
108 |
109 | Within the 'main' function, we use these uniforms to do our proper physics! To do this, we first need to get a 'UV' position that will tell us where to look up in the texture to get our position, than we will use this uv to get our positions, got through the process of applying forces, and than at the very end, color the pixel accordingly
110 |
111 | ##### UV
112 |
113 | The resolution uniform is vital for this set , because it gives us exactly where in the texture our current position lies. We get this uv by doing the following:
114 |
115 | ```glsl
116 | vec2 uv = gl_FragCoord.xy / resolution;
117 | ```
118 |
119 | ##### Positions
120 | next we use our uv to look up the correct positions
121 |
122 | ```glsl
123 | vec4 oPos = texture2D( t_oPos , uv );
124 | vec4 pos = texture2D( t_pos , uv );
125 | ```
126 |
127 | #####Velocity
128 | We can determine velocity from these two positions:
129 | ```glsl
130 | vec3 vel = pos.xyz - oPos.xyz;
131 | ```
132 |
133 | #####Force
134 | This is the section of the program which can be all you! For now, I'll show you the most basic example: Fake Gravity.
135 |
136 | ```glsl
137 | vec3 force = vec3( 0. , -1. , 0. );
138 | ```
139 |
140 | #####Getting New Position
141 | Using position, velocity and force, we can than get a new position, like so:
142 | ```glsl
143 | vel += force;
144 | vec3 newPos = pos.xyz + vel;
145 | ```
146 |
147 | #####Assigning New Position
148 | Now that we've got a new position, all we have to do is assign it:
149 | ```glsl
150 | gl_FragColor = vec4( newPos , 1. );
151 | ```
152 |
153 | #####Putting it all together:
154 | ```glsl
155 | uniform sampler2D t_pos;
156 | uniform sampler2D t_oPos;
157 | uniform vec2 resolution;
158 |
159 | void main(){
160 |
161 | vec2 uv = gl_FragCoord.xy / resolution;
162 |
163 | vec4 oPos = texture2D( t_oPos , uv );
164 | vec4 pos = texture2D( t_pos , uv );
165 |
166 | vec3 vel = pos.xyz - oPos.xyz;
167 |
168 | vec3 force = vec3( 0. , -1. , 0. );
169 |
170 | vel += force;
171 | vec3 newPos = pos.xyz + vel;
172 |
173 | gl_FragColor = vec4( newPos , 1. );
174 |
175 | }
176 | ```
177 |
178 | #####Going Further
179 | The above is just about the MOST basic example possible, but theres so many other fun things you can do! Add dampenign to the velocity, make it so particles respawn somewhere else, etc. etc. etc. Check out the examples to see all the weird ways you can make points move!!!
180 |
181 |
182 | ##Using the Output
183 | Now that we've discussed how to create the PhysicsRenderer, and pass in a simulation shader that will do a bunch of awesome calculations for us, We need to know how to use it. This will require a few things: Creating a geometry that knows how to use the output textures, creating a Material that knows how to use the output textures, and binding the output texture.
184 |
185 | ##### Creating a Geometry
186 | Just like in the simulation shader, where we created a uv by using the gl_FragCoord, we will make a geometry where the position corresponds to a position in a texture, rather than an actual position in 3D Space. We do this like so:
187 |
188 | ```javascript
189 | function createLookupGeometry( size ){
190 |
191 | var geo = new THREE.BufferGeometry();
192 | var positions = new Float32Array( size * size * 3 );
193 |
194 | for ( var i = 0, j = 0, l = positions.length / 3; i < l; i ++, j += 3 ) {
195 |
196 | positions[ j ] = ( i % size ) / size;
197 | positions[ j + 1 ] = Math.floor( i / size ) / size;
198 |
199 | }
200 |
201 | var posA = new THREE.BufferAttribute( positions , 3 );
202 | geo.addAttribute( 'position', posA );
203 |
204 | return geo;
205 |
206 | }
207 | ```
208 |
209 | Right now, this is wasting the z data of the position, but consider that another constraint to play with!
210 |
211 | ##### Creating a Material
212 | Next We have to create a material that can use all of our data and create something meaningful on the screen. For right now, I will create the simplest possible material, but rest assured that some [REALLY WEIRD MATERIALS] can be made...
213 |
214 | Lets break the material down into its seperate parts: The Uniforms, The Vertex Shader, and The Fragment Shader
215 |
216 | ######Uniforms
217 | The Uniforms will be like any other set of shader uniforms, with One mandatory addition, the positions texture that will come from the simulation shader. Because of this, the most basic uniforms will look like this:
218 | ```javascript
219 | var uniforms = {
220 | t_pos: { type:"t" , value: null }
221 | }
222 | ```
223 |
224 | ######Vertex Shader
225 | The vertex shader will do nothing but use the position of the geometry to look up into the positions texture, and than place the particle based on this information:
226 | ```glsl
227 | uniform sampler2D t_pos;
228 |
229 | void main(){
230 |
231 | vec4 pos = texture2D( t_pos , position.xy );
232 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos.xyz , 1. );
233 |
234 | }
235 | ```
236 |
237 | ######Fragment Shader
238 | The fragment shader than will look like any other fragment shader. All of the magic has already been done in the vertex shader:
239 | ```glsl
240 | void main(){
241 | gl_FragColor = vec4( 1. );
242 | }
243 | ```
244 |
245 | ##### Bringing it into THREE
246 | We than bring all of this information into THREE by doing the following:
247 |
248 | ```javascript
249 |
250 | var geometry = createLookupGeometry( size );
251 | var material = new THREE.ShaderMaterial({
252 | uniforms:uniforms,
253 | vertexShader:vertexShader,
254 | fragmentShader:fragmentShader
255 | });
256 |
257 | var particles = new THREE.PointCloud( geometry, material );
258 | particles.frustumCulled = false;
259 |
260 | scene.add( particles );
261 |
262 | ```
263 | Notice the line:
264 | ```javscript
265 | particles.frustumCulled = false;
266 | ```
267 | This is because all the particles will ahve positions that are not their true positions, so three.js may cull them, even though they should still be visible
268 |
269 | ##### Binding the texture
270 | The last thing we need to do is bind the output of the PhysicsRenderer, so that it is used by our particle system. Luckily this is only a single line, and the PhysicsRenderer takes care of the rest:
271 | ```javascript
272 | physicsRenderer.addBoundTexture( uniforms.t_pos , 'output' );
273 | ```
274 |
275 | doing this will make sure that whenever physicsRenderer.update is called, it will make sure that its output is assigned to the value of the uniform that is passed in!
276 |
277 | ## Other Helpful Functions
278 |
279 | Although the above is all you need to get rolling, there are some very helpful functions to give you additional functionality
280 |
281 | #### Assigning Uniforms
282 |
283 | Digging all the way into the Physics Renderer to set a uniform is pretty annoying, so here are some other ways to set uniforms
284 |
285 | ###### Setting a single uniform
286 | Set a single uniform with whatever name you want!
287 | ```javascript
288 | var uniforms ={
289 | time:{ type:"f" , value:0 },
290 | dT:{ type:"f" , value:0 },
291 | }
292 |
293 | physicsRenderer.setUniform( 'nameInSimulationShader' , uniforms.dT );
294 | ```
295 |
296 | ###### Setting Multiple Uniforms
297 | Set all uniforms from another set of unifoms
298 | ```javascript
299 | var uniforms ={
300 | time:{ type:"f" , value:0 },
301 | dT:{ type:"f" , value:0 },
302 | }
303 |
304 | physicsRenderer.setUniforms( uniforms );
305 | ```
306 | Keep in mind, that because the PhysicsRenderer always needs t_pos , t_oPos , and resolution, even if you try to set these via this method, the PhysicsRenderer will override them!
307 |
308 | ####Reseting Positions
309 | You may want to place the particles at a certain place to start, because they will currently start all at [0,0,0]. This makes pretty much every simulation kindof boring, because you will only see 1 point... Because of this there are multiply ways to set positions:
310 |
311 | ######Reseting position randomly
312 | The easiest way to get a quick feel for a simulation is to reset the positions randomly. This is done with a 1-liner
313 | ```javascript
314 | // Resets positions in a random box of side length 5
315 | physicsRenderer.resetRand( 5 );
316 | ```
317 |
318 | ######Reseting Positions with another texture
319 | You can also create a specific texture with position information and reset it this way. Altough the creation of the texture might be a bit more than one line, the call to reset using a texture is only:
320 | ```javascript
321 | var texture = createPositionTexture(size);
322 | physicsRenderer.reset( texture );
323 | ```
324 | Just for the sake of completeness, here's a sample 'createPositionTexture' function:
325 | ```javascript
326 | function createPositionTexture(size){
327 |
328 | var data = new Float32Array( size * size * 4 );
329 |
330 | for( var i =0; i < data.length; i++ ){
331 |
332 | //makes some weird sin based positions
333 | data[ i ] = Math.sin( i*.1 ) * 30;
334 |
335 | }
336 |
337 | var texture = new THREE.DataTexture(
338 | data,
339 | this.size,
340 | this.size,
341 | THREE.RGBAFormat,
342 | THREE.FloatType
343 | );
344 |
345 | texture.minFilter = THREE.NearestFilter,
346 | texture.magFilter = THREE.NearestFilter,
347 |
348 | texture.needsUpdate = true;
349 |
350 | return texture;
351 |
352 |
353 | }
354 | ```
355 |
356 |
357 | #### Adding a debugScene
358 | Sometimes things might not be going right, and you want to see the actual data textures, or things are going right, and you want to see the data textures. They can look [REALLY COOL]. To do this, just call:
359 | ```javascript
360 | physicsRenderer.addDebugScene( scene );
361 | ```
362 | You can change the scale of this scene ( and probably will have to ), my playing with the THREE.Object3D which is physicsRenderer.debugScene. like so:
363 | ```javascript
364 | physicsRenderer.debugScene.scale.multiplyScalar( .1 );
365 | ```
366 |
367 | ## YOU MADE IT DOWN HERE!
368 | Thats alot of reading you've just done. Why don't you go play with some examples now, or let me know on [TWITTER] why everything I've said is wrong! If you want to keep learnign about the GPU, keep reading on for a bit of background!
369 |
370 | #Background
371 |
372 | ### What are GPGPU Calculations ?!??!
373 |
374 | The first thing you need to understand about the physics renderer is how it actually works! Well you don't actually, but its reallly reallly cool, so stay with me!
375 |
376 | Your screen has alot of pixels right? And for a graphics programs, each one of these pixels needs to be told what color it should be. That is a WHOLE bunch of pixels. More than 5 million on my computer, and maybe even more on yours!
377 |
378 | Your GPU is in charge of doing all the calculations that tell you what color to make these pixels, and it is EXTREMELY good at doing so. It does this a bunch of different threads, and doing all the calculations in parrallel. This is a dramatic simplification. If you really want to nerd out, check out this article on [GPU Architecture].
379 |
380 | Now, although things like [WebCL] are coming to the browser at some point in time, and there is actually a [WebCL Extension for Firefox], General Purpose ( meaning anything not vertex / fragment shader based ) calculations done on the GPU can be a bit of a beast to work with. But WHY ?!?!?
381 |
382 | ### Tricking Your Computer
383 |
384 | To do GPGPU ( General Purpose GPU ) calculations in WebGl, we have to use only the tools given to us. In the case of WebGL, thats vertex and fragment shaders. However, the output of these is a vec4 that represents a color. In WebGL, the GPU reallly likes doing colors, but everything else is a bit of a stretch.
385 |
386 | All this means though, is that we let the computer do colors, but use them for different purposes! By simply pretending that Red, Green and Blue values are actually X , Y and Z values, we get to tell the computer we are coloring pixels, when we are actually doing physics!!! ( insert evil laughter here!!! )
387 |
388 |
389 |
390 | [Github]:http://github.com/cabbibo/PhysicsRenderer/
391 |
392 | [Sporel]:http://mrdoob.com/#/153/sporel
393 | [ThreeJS GPGPU Examples]: threejs.org/examples/#webgl_gpgpu_birds
394 | [Soulwire's GPGPU Particles]: http://soulwire.co.uk/experiments/webgl-gpu-particles/
395 |
396 | [Flow]:http://cabbi.bo/flow
397 | [Needs]:http://cabbi.bo/Needs/
398 | [We Over]:http://wom.bs/audioSketches/weOver/
399 | [Bees]:http://cabbi.bo/Bees/
400 | [DRAGONFISH]:http://cabbi.bo/DRAGONFISH/
401 | [NVS]:http://cabbi.bo/nvs/
402 | [Huldra]:http://cabbi.bo/huldra/
403 | [Diamonds]:http://cabbi.bo/diamonds
404 |
405 | [Curl Noise]:http://cabbi.bo/PhysicsRenderer/examples/curl.html
406 | [Collisions]:http://cabbi.bo/PhysicsRenderer/examples/collisions.html
407 | [Gravity]:http://cabbi.bo/PhysicsRenderer/examples/gravity.html
408 | [Flocking]:http://cabbi.bo/PhysicsRenderer/examples/flocking.html
409 | [Springs]:http://cabbi.bo/PhysicsRenderer/examples/springs.html
410 | [Text]:http://cabbi.bo/PhysicsRenderer/examples/text.html
411 |
412 |
413 | [GPU Architecture]:ftp://download.nvidia.com/developer/GPU_Gems_2/GPU_Gems2_ch30.pdf
414 | [WebCL]:https://www.khronos.org/webcl/
415 | [WebCL Extension for Firefox]:http://webcl.nokiaresearch.com/
416 |
417 | [ShaderLoader]:http://cabbi.bo/ShaderLoader
418 | [TWITTER]:http://twitter.com/cabbibo
419 |
420 | [REALLY WEIRD MATERIALS]:http://cabbi.bo/beacon
421 | [REALLY COOL]:https://twitter.com/Cabbibo/status/554516882854645761/photo/1
422 |
--------------------------------------------------------------------------------
/assests/GeosansLight.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/assests/GeosansLight.ttf
--------------------------------------------------------------------------------
/assests/note.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/assests/note.mp3
--------------------------------------------------------------------------------
/examples/.collisions.html.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/examples/.collisions.html.swp
--------------------------------------------------------------------------------
/examples/.curl.html.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/examples/.curl.html.swp
--------------------------------------------------------------------------------
/examples/collisions.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
172 |
173 |
174 |
175 |
--------------------------------------------------------------------------------
/examples/curl.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
135 |
136 |
137 |
138 |
--------------------------------------------------------------------------------
/examples/flocking.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/examples/gravity.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
138 |
139 |
140 |
141 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
42 |
43 |
44 |
45 |
46 |
50 |
51 |
PhysicsRenderer
52 |
53 | - Check it out on Github !!!!!
54 | - Contact me via TWITTER!!!!!
55 |
56 |
Introduction
57 |
An awesome lil helper to help you do gpgpu calculations! Inspired by a few awesome projects including:
58 |
63 |
Using this tool ( or previous, uglier versions of it ) I've made a whole smorgasbord of projects, including, but most definitely not limited to:
64 |
74 |
Because GPGPU Calculations are not something many developers are familiar with, I'd like to talk a bit about them, including pros / cons , limitations, etc, but if you want to skip straight to the good stuff ( AKA Code ), feel free to dive into the following examples. This definitely is a subject that staring at code really helps with:
75 |
81 |
Also, at any point in time, jump straight over to the Github for the code! With all of this being said, lets start talking about how to use the Physics Renderer! If you want to know a bit more about how it actually works, check out the BACKGROUND section, but hopefully it is easy enough that you shouldn't have to know how crazy the gpgpu really gets!
82 |
Caveats
83 |
First things first, The PhysicsRenderer requires the use of Floating Point Textures. This is not something that is available ( YET ) in phones, so if you are trying to make a mobile friendly site, please run while you still can!
84 |
Second, GPU performance varies dramatically from computer to computer, so make sure you have multiple machines, or multiple friends with multiple machines, to test on!
85 |
Third, This specific renderer uses 2 positions textures to get a velocity, and interpolates based on that. Its much more efficient than having a seperate pass for position and velocity, but makes other parts more difficult, such as collisions. I have plans to add different types of render pases into this ( 3 old positions , pos / vel , etc ), but have not done so yet! If you have any good ideas of how to do this, let me know on TWITTER
86 |
Fourth, for most of these examples, I will be using another Utility that I have written: ShaderLoader , because its a cool tool, and I'm too lazy to make things from scratch, but rest assured that ANY shader you write can still be used in this tool!
87 |
Usage
88 |
Boring Setup Stuff:
89 |
Including Scripts
90 |
As usual, the first thing you need to do is include the script
91 |
<script src="PATH/TO/PhysicsRenderer.js"></script>
92 |
93 |
Initializing the PhysicsRenderer
94 |
Within the 'Initialization' phase of your application, create the renderer. To do this you will need 3 pieces of information:
95 |
96 | - Size of Simulation ( actual size is this number squared )
97 | - The Simulation Shader
98 | - WebGL Renderer
99 |
100 |
Size
101 |
The size that will be passed into the PhysicsRenderer is not actually the number of particles, but rather the width / height of the texture to be used. This means that the actual number of positions in the simulation will be Size * Size . Also, for the sake of older GPUs please try and keep this number a Power of 2 , AKA: 2 , 4 , 8 , 16 , 32 , 64 , 128 , 256 , 512 , 1024 ( that corresponds to 1048576 different position calculations BTW.... )
102 |
Simulation Shader
103 |
This is the soul of the physics renderer. You can think of it as the 'Kernel' that you are passing into to the GPU to be run. I will go into more depth about what this is later, but for now, just remember that is is a Fragment Shader passed in as a text string.
104 |
WebGL Renderer
105 |
This will just be the Three.js Renderer you use for everything else, so nothing fancy hear, just make sure you declare it before you create the PhysicsRenderer.
106 |
Putting all these together the intialization looks a lil something like this:
107 |
var size = 128;
108 | var simulationShader = "SIMULATION FRAGMENT SHADER"
109 | renderer = new THREE.WebGLRenderer();
110 |
111 | physicsRenderer = new PhysicsRenderer( size , simulationShader , renderer );
112 |
113 |
Updating the Renderer
114 |
Once its been initialized, all we have to do to use the renderer is update it in our animation loop, like so
115 |
physicsRenderer.update();
116 |
117 |
If you do only these things, nothing is going to happen, and infact, you may get some errors, so lets dig a bit further and talk about what the simulation shader is going to look like:
118 |
Simulation Shader:
119 |
The simulation shader is the most important part of this tool, and everything else about the tool is just trying to get it to run. It will be written in GLSL, so we'll break it down by sections of the glsl program
120 |
121 |
The first thing we will have to do is include the proper uniforms. In our case there are 3 that are mandatory: The Current Position Texture , The Old Positition Texture, and the resolution of simulation. Declaring them looks like so:
122 |
uniform sampler2D t_pos;
123 | uniform sampler2D t_oPos;
124 | uniform vec2 resolution;
125 |
126 |
ALWAYS PUT THESE IN YOUR PROGRAM!!!
127 |
Main
128 |
Within the 'main' function, we use these uniforms to do our proper physics! To do this, we first need to get a 'UV' position that will tell us where to look up in the texture to get our position, than we will use this uv to get our positions, got through the process of applying forces, and than at the very end, color the pixel accordingly
129 |
UV
130 |
The resolution uniform is vital for this set , because it gives us exactly where in the texture our current position lies. We get this uv by doing the following:
131 |
vec2 uv = gl_FragCoord.xy / resolution;
132 |
133 |
Positions
134 |
next we use our uv to look up the correct positions
135 |
vec4 oPos = texture2D( t_oPos , uv );
136 | vec4 pos = texture2D( t_pos , uv );
137 |
138 |
Velocity
139 |
We can determine velocity from these two positions:
140 |
vec3 vel = pos.xyz - oPos.xyz;
141 |
142 |
Force
143 |
This is the section of the program which can be all you! For now, I'll show you the most basic example: Fake Gravity.
144 |
vec3 force = vec3( 0. , -1. , 0. );
145 |
146 |
Getting New Position
147 |
Using position, velocity and force, we can than get a new position, like so:
148 |
vel += force;
149 | vec3 newPos = pos.xyz + vel;
150 |
151 |
Assigning New Position
152 |
Now that we've got a new position, all we have to do is assign it:
153 |
gl_FragColor = vec4( newPos , 1. );
154 |
155 |
Putting it all together:
156 |
uniform sampler2D t_pos;
157 | uniform sampler2D t_oPos;
158 | uniform vec2 resolution;
159 |
160 | void main(){
161 |
162 | vec2 uv = gl_FragCoord.xy / resolution;
163 |
164 | vec4 oPos = texture2D( t_oPos , uv );
165 | vec4 pos = texture2D( t_pos , uv );
166 |
167 | vec3 vel = pos.xyz - oPos.xyz;
168 |
169 | vec3 force = vec3( 0. , -1. , 0. );
170 |
171 | vel += force;
172 | vec3 newPos = pos.xyz + vel;
173 |
174 | gl_FragColor = vec4( newPos , 1. );
175 |
176 | }
177 |
178 |
Going Further
179 |
The above is just about the MOST basic example possible, but theres so many other fun things you can do! Add dampenign to the velocity, make it so particles respawn somewhere else, etc. etc. etc. Check out the examples to see all the weird ways you can make points move!!!
180 |
Using the Output
181 |
Now that we've discussed how to create the PhysicsRenderer, and pass in a simulation shader that will do a bunch of awesome calculations for us, We need to know how to use it. This will require a few things: Creating a geometry that knows how to use the output textures, creating a Material that knows how to use the output textures, and binding the output texture.
182 |
Creating a Geometry
183 |
Just like in the simulation shader, where we created a uv by using the gl_FragCoord, we will make a geometry where the position corresponds to a position in a texture, rather than an actual position in 3D Space. We do this like so:
184 |
function createLookupGeometry( size ){
185 |
186 | var geo = new THREE.BufferGeometry();
187 | var positions = new Float32Array( size * size * 3 );
188 |
189 | for ( var i = 0, j = 0, l = positions.length / 3; i < l; i ++, j += 3 ) {
190 |
191 | positions[ j ] = ( i % size ) / size;
192 | positions[ j + 1 ] = Math.floor( i / size ) / size;
193 |
194 | }
195 |
196 | var posA = new THREE.BufferAttribute( positions , 3 );
197 | geo.addAttribute( 'position', posA );
198 |
199 | return geo;
200 |
201 | }
202 |
203 |
Right now, this is wasting the z data of the position, but consider that another constraint to play with!
204 |
Creating a Material
205 |
Next We have to create a material that can use all of our data and create something meaningful on the screen. For right now, I will create the simplest possible material, but rest assured that some REALLY WEIRD MATERIALS can be made...
206 |
Lets break the material down into its seperate parts: The Uniforms, The Vertex Shader, and The Fragment Shader
207 |
208 |
The Uniforms will be like any other set of shader uniforms, with One mandatory addition, the positions texture that will come from the simulation shader. Because of this, the most basic uniforms will look like this:
209 |
var uniforms = {
210 | t_pos: { type:"t" , value: null }
211 | }
212 |
213 |
Vertex Shader
214 |
The vertex shader will do nothing but use the position of the geometry to look up into the positions texture, and than place the particle based on this information:
215 |
uniform sampler2D t_pos;
216 |
217 | void main(){
218 |
219 | vec4 pos = texture2D( t_pos , position.xy );
220 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos.xyz , 1. );
221 |
222 | }
223 |
224 |
Fragment Shader
225 |
The fragment shader than will look like any other fragment shader. All of the magic has already been done in the vertex shader:
226 |
void main(){
227 | gl_FragColor = vec4( 1. );
228 | }
229 |
230 |
Bringing it into THREE
231 |
We than bring all of this information into THREE by doing the following:
232 |
233 | var geometry = createLookupGeometry( size );
234 | var material = new THREE.ShaderMaterial({
235 | uniforms:uniforms,
236 | vertexShader:vertexShader,
237 | fragmentShader:fragmentShader
238 | });
239 |
240 | var particles = new THREE.PointCloud( geometry, material );
241 | particles.frustumCulled = false;
242 |
243 | scene.add( particles );
244 |
245 |
Notice the line:
246 |
particles.frustumCulled = false;
247 |
248 |
This is because all the particles will ahve positions that are not their true positions, so three.js may cull them, even though they should still be visible
249 |
Binding the texture
250 |
The last thing we need to do is bind the output of the PhysicsRenderer, so that it is used by our particle system. Luckily this is only a single line, and the PhysicsRenderer takes care of the rest:
251 |
physicsRenderer.addBoundTexture( uniforms.t_pos , 'output' );
252 |
253 |
doing this will make sure that whenever physicsRenderer.update is called, it will make sure that its output is assigned to the value of the uniform that is passed in!
254 |
Other Helpful Functions
255 |
Although the above is all you need to get rolling, there are some very helpful functions to give you additional functionality
256 |
257 |
Digging all the way into the Physics Renderer to set a uniform is pretty annoying, so here are some other ways to set uniforms
258 |
259 |
Set a single uniform with whatever name you want!
260 |
var uniforms ={
261 | time:{ type:"f" , value:0 },
262 | dT:{ type:"f" , value:0 },
263 | }
264 |
265 | physicsRenderer.setUniform( 'nameInSimulationShader' , uniforms.dT );
266 |
267 |
268 |
Set all uniforms from another set of unifoms
269 |
var uniforms ={
270 | time:{ type:"f" , value:0 },
271 | dT:{ type:"f" , value:0 },
272 | }
273 |
274 | physicsRenderer.setUniforms( uniforms );
275 |
276 |
Keep in mind, that because the PhysicsRenderer always needs t_pos , t_oPos , and resolution, even if you try to set these via this method, the PhysicsRenderer will override them!
277 |
Reseting Positions
278 |
You may want to place the particles at a certain place to start, because they will currently start all at [0,0,0]. This makes pretty much every simulation kindof boring, because you will only see 1 point... Because of this there are multiply ways to set positions:
279 |
Reseting position randomly
280 |
The easiest way to get a quick feel for a simulation is to reset the positions randomly. This is done with a 1-liner
281 |
282 | physicsRenderer.resetRand( 5 );
283 |
284 |
Reseting Positions with another texture
285 |
You can also create a specific texture with position information and reset it this way. Altough the creation of the texture might be a bit more than one line, the call to reset using a texture is only:
286 |
var texture = createPositionTexture(size);
287 | physicsRenderer.reset( texture );
288 |
289 |
Just for the sake of completeness, here's a sample 'createPositionTexture' function:
290 |
function createPositionTexture(size){
291 |
292 | var data = new Float32Array( size * size * 4 );
293 |
294 | for( var i =0; i < data.length; i++ ){
295 |
296 |
297 | data[ i ] = Math.sin( i*.1 ) * 30;
298 |
299 | }
300 |
301 | var texture = new THREE.DataTexture(
302 | data,
303 | this.size,
304 | this.size,
305 | THREE.RGBAFormat,
306 | THREE.FloatType
307 | );
308 |
309 | texture.minFilter = THREE.NearestFilter,
310 | texture.magFilter = THREE.NearestFilter,
311 |
312 | texture.needsUpdate = true;
313 |
314 | return texture;
315 |
316 |
317 | }
318 |
319 |
Adding a debugScene
320 |
Sometimes things might not be going right, and you want to see the actual data textures, or things are going right, and you want to see the data textures. They can look REALLY COOL. To do this, just call:
321 |
physicsRenderer.addDebugScene( scene );
322 |
323 |
You can change the scale of this scene ( and probably will have to ), my playing with the THREE.Object3D which is physicsRenderer.debugScene. like so:
324 |
physicsRenderer.debugScene.scale.multiplyScalar( .1 );
325 |
326 |
YOU MADE IT DOWN HERE!
327 |
Thats alot of reading you've just done. Why don't you go play with some examples now, or let me know on TWITTER why everything I've said is wrong! If you want to keep learnign about the GPU, keep reading on for a bit of background!
328 |
Background
329 |
What are GPGPU Calculations ?!??!
330 |
The first thing you need to understand about the physics renderer is how it actually works! Well you don't actually, but its reallly reallly cool, so stay with me!
331 |
Your screen has alot of pixels right? And for a graphics programs, each one of these pixels needs to be told what color it should be. That is a WHOLE bunch of pixels. More than 5 million on my computer, and maybe even more on yours!
332 |
Your GPU is in charge of doing all the calculations that tell you what color to make these pixels, and it is EXTREMELY good at doing so. It does this a bunch of different threads, and doing all the calculations in parrallel. This is a dramatic simplification. If you really want to nerd out, check out this article on GPU Architecture.
333 |
Now, although things like WebCL are coming to the browser at some point in time, and there is actually a WebCL Extension for Firefox, General Purpose ( meaning anything not vertex / fragment shader based ) calculations done on the GPU can be a bit of a beast to work with. But WHY ?!?!?
334 |
Tricking Your Computer
335 |
To do GPGPU ( General Purpose GPU ) calculations in WebGl, we have to use only the tools given to us. In the case of WebGL, thats vertex and fragment shaders. However, the output of these is a vec4 that represents a color. In WebGL, the GPU reallly likes doing colors, but everything else is a bit of a stretch.
336 |
All this means though, is that we let the computer do colors, but use them for different purposes! By simply pretending that Red, Green and Blue values are actually X , Y and Z values, we get to tell the computer we are coloring pixels, when we are actually doing physics!!! ( insert evil laughter here!!! )
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
--------------------------------------------------------------------------------
/lib/.ShaderLoader.js.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/lib/.ShaderLoader.js.swp
--------------------------------------------------------------------------------
/lib/ShaderLoader.js:
--------------------------------------------------------------------------------
1 |
2 | // Shader Loader will Load any shader you want,
3 | // And be able to add in large functions ( such as noise )
4 | // with regex inside the shader
5 | function ShaderLoader( pathToShaders , pathToChunks ){
6 |
7 | this.fragmentShaders = {};
8 | this.vertexShaders = {};
9 | this.simulationShaders = {};
10 |
11 | this.fs = this.fragmentShaders;
12 | this.vs = this.vertexShaders;
13 | this.ss = this.simulationShaders;
14 |
15 | this.pathToShaders = pathToShaders || "/" ;
16 | this.pathToChunks = pathToChunks || pathToShaders;
17 |
18 | this.shaderChunks = {};
19 |
20 | this.shadersLoaded = 0;
21 | this.shadersToLoad = 0;
22 |
23 | }
24 |
25 |
26 |
27 | /*
28 |
29 | Loads in a shader chunk when told to by
30 | onShaderLoaded.
31 |
32 | it is important to know that the title of the
33 | chunk needs to be the same as the reference in the shader
34 |
35 | AKA, if I use:
36 |
37 | $simplexNoise
38 |
39 | I will need to create a file in the pathToChunks directory
40 | called
41 |
42 | simplexNoise.js
43 |
44 |
45 | */
46 | ShaderLoader.prototype.loadShaderChunk = function( type ){
47 |
48 | var path = this.pathToChunks + "/" + type + ".glsl";
49 |
50 | var self = this;
51 | $.ajax({
52 | url:path,
53 | dataType:'text',
54 | context:{
55 | title:type,
56 | path: path
57 | },
58 | complete: function( r ){
59 | self.onChunkLoaded( r.responseText , this.title );
60 | },
61 | error:function( r ){
62 | console.log( 'ERROR: Unable to Load Shader' + this.path );
63 | self.onChunkLoaded( " NO SHADER LOADED " , this.title );
64 | }
65 | });
66 |
67 | }
68 |
69 | ShaderLoader.prototype.onChunkLoaded = function( chunk , title ){
70 |
71 | this.shaderChunks[title] = chunk;
72 |
73 | }
74 |
75 | /*
76 |
77 | This function Loads a shader with whatever title/
78 | type we prefer.
79 |
80 | */
81 | ShaderLoader.prototype.load = function( shader , title , type ){
82 |
83 | var self = this;
84 |
85 | this._beginLoad( shader , title , type );
86 |
87 |
88 | // request the file over AJAX
89 | $.ajax({
90 | url: self.pathToShaders +"/" + shader + ".glsl" ,
91 | dataType: 'text',
92 | context: {
93 | type: type
94 | },
95 | complete: function(r){
96 | self.onShaderLoaded( r.responseText , title , this.type );
97 | }
98 | });
99 |
100 | }
101 |
102 | /*
103 |
104 | Once a Shader is loaded, check to see if there are any extra chunks
105 | we need to find and pull in.
106 |
107 | Will recall itself, until the chunk has been loaded in
108 |
109 | */
110 | ShaderLoader.prototype.onShaderLoaded = function( shaderText , title , type ){
111 |
112 | var finalShader = shaderText;
113 |
114 | var readyToLoad = true;
115 |
116 |
117 | var array = finalShader.split( "$" );
118 |
119 | for( var i = 1; i < array.length; i++ ){
120 |
121 | var chunkName = array[i].split("\n")[0];
122 |
123 | if( this.shaderChunks[chunkName] ){
124 |
125 | var tmpShader = finalShader.split( "$" + chunkName );
126 |
127 | finalShader = tmpShader.join( this.shaderChunks[chunkName] );
128 |
129 | }else{
130 |
131 | readyToLoad = false;
132 | this.loadShaderChunk( chunkName );
133 |
134 | }
135 |
136 | }
137 |
138 | if( readyToLoad ){
139 |
140 | if( type == 'vertex' ){
141 | this.vertexShaders[ title ] = finalShader;
142 | }else if( type == 'fragment' ){
143 | this.fragmentShaders[ title ] = finalShader;
144 | }else if( type == 'simulation' ){
145 | this.simulationShaders[ title ] = finalShader;
146 | }
147 |
148 | this._endLoad( finalShader , title , type );
149 |
150 | }else{
151 |
152 | var self = this;
153 | setTimeout( function(){
154 | self.onShaderLoaded( finalShader , title , type )
155 | }, 300 );
156 |
157 | }
158 |
159 | }
160 |
161 |
162 | // might add something later...
163 | ShaderLoader.prototype._beginLoad = function( shader , title , type ){
164 | this.shadersToLoad ++;
165 | this.beginLoad( shader , title , type );
166 | }
167 |
168 | ShaderLoader.prototype._endLoad = function( shaderText , title , type ){
169 | this.shadersLoaded ++;
170 |
171 | if( this.shadersLoaded == this.shadersToLoad ){
172 | this.shaderSetLoaded();
173 | }
174 |
175 | this.endLoad( shaderText , title , type );
176 |
177 | }
178 |
179 |
180 | ShaderLoader.prototype.setValue = function( shader , name , value ){
181 |
182 | //console.log( name , value );
183 |
184 | var a = '@'+name;
185 | //console.log( a );
186 |
187 | var replaced = false;
188 |
189 | var newStr = shader.replace( a , function(token){replaced = true; return value;});
190 |
191 | console.log( 'replaced' , replaced );
192 | return newStr;
193 |
194 | }
195 |
196 | ShaderLoader.prototype.shaderSetLoaded = function(){}
197 | ShaderLoader.prototype.endLoad = function(){}
198 | ShaderLoader.prototype.beginLoad = function(){}
199 |
--------------------------------------------------------------------------------
/lib/TrackballControls.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @author Eberhard Graether / http://egraether.com/
3 | * @author Mark Lundin / http://mark-lundin.com
4 | */
5 |
6 | THREE.TrackballControls = function ( object, domElement ) {
7 |
8 | var _this = this;
9 | var STATE = { NONE: -1, ROTATE: 0, ZOOM: 1, PAN: 2, TOUCH_ROTATE: 3, TOUCH_ZOOM: 4, TOUCH_PAN: 5 };
10 |
11 | this.object = object;
12 | this.domElement = ( domElement !== undefined ) ? domElement : document;
13 |
14 | // API
15 |
16 | this.enabled = true;
17 |
18 | this.screen = { left: 0, top: 0, width: 0, height: 0 };
19 |
20 | this.rotateSpeed = 1.0;
21 | this.zoomSpeed = 1.2;
22 | this.panSpeed = 0.3;
23 |
24 | this.noRotate = false;
25 | this.noZoom = false;
26 | this.noPan = false;
27 | this.noRoll = false;
28 |
29 | this.staticMoving = false;
30 | this.dynamicDampingFactor = 0.2;
31 |
32 | this.minDistance = 0;
33 | this.maxDistance = Infinity;
34 |
35 | this.keys = [ 65 /*A*/, 83 /*S*/, 68 /*D*/ ];
36 |
37 | // internals
38 |
39 | this.target = new THREE.Vector3();
40 |
41 | var EPS = 0.000001;
42 |
43 | var lastPosition = new THREE.Vector3();
44 |
45 | var _state = STATE.NONE,
46 | _prevState = STATE.NONE,
47 |
48 | _eye = new THREE.Vector3(),
49 |
50 | _rotateStart = new THREE.Vector3(),
51 | _rotateEnd = new THREE.Vector3(),
52 |
53 | _zoomStart = new THREE.Vector2(),
54 | _zoomEnd = new THREE.Vector2(),
55 |
56 | _touchZoomDistanceStart = 0,
57 | _touchZoomDistanceEnd = 0,
58 |
59 | _panStart = new THREE.Vector2(),
60 | _panEnd = new THREE.Vector2();
61 |
62 | // for reset
63 |
64 | this.target0 = this.target.clone();
65 | this.position0 = this.object.position.clone();
66 | this.up0 = this.object.up.clone();
67 |
68 | // events
69 |
70 | var changeEvent = { type: 'change' };
71 | var startEvent = { type: 'start'};
72 | var endEvent = { type: 'end'};
73 |
74 |
75 | // methods
76 |
77 | this.handleResize = function () {
78 |
79 | if ( this.domElement === document ) {
80 |
81 | this.screen.left = 0;
82 | this.screen.top = 0;
83 | this.screen.width = window.innerWidth;
84 | this.screen.height = window.innerHeight;
85 |
86 | } else {
87 |
88 | var box = this.domElement.getBoundingClientRect();
89 | // adjustments come from similar code in the jquery offset() function
90 | var d = this.domElement.ownerDocument.documentElement;
91 | this.screen.left = box.left + window.pageXOffset - d.clientLeft;
92 | this.screen.top = box.top + window.pageYOffset - d.clientTop;
93 | this.screen.width = box.width;
94 | this.screen.height = box.height;
95 |
96 | }
97 |
98 | };
99 |
100 | this.handleEvent = function ( event ) {
101 |
102 | if ( typeof this[ event.type ] == 'function' ) {
103 |
104 | this[ event.type ]( event );
105 |
106 | }
107 |
108 | };
109 |
110 | this.getMouseOnScreen = function ( pageX, pageY, vector ) {
111 |
112 | return vector.set(
113 | ( pageX - _this.screen.left ) / _this.screen.width,
114 | ( pageY - _this.screen.top ) / _this.screen.height
115 | );
116 |
117 | };
118 |
119 | this.getMouseProjectionOnBall = (function(){
120 |
121 | var objectUp = new THREE.Vector3(),
122 | mouseOnBall = new THREE.Vector3();
123 |
124 |
125 | return function ( pageX, pageY, projection ) {
126 |
127 | mouseOnBall.set(
128 | ( pageX - _this.screen.width * 0.5 - _this.screen.left ) / (_this.screen.width*.5),
129 | ( _this.screen.height * 0.5 + _this.screen.top - pageY ) / (_this.screen.height*.5),
130 | 0.0
131 | );
132 |
133 | var length = mouseOnBall.length();
134 |
135 | if ( _this.noRoll ) {
136 |
137 | if ( length < Math.SQRT1_2 ) {
138 |
139 | mouseOnBall.z = Math.sqrt( 1.0 - length*length );
140 |
141 | } else {
142 |
143 | mouseOnBall.z = .5 / length;
144 |
145 | }
146 |
147 | } else if ( length > 1.0 ) {
148 |
149 | mouseOnBall.normalize();
150 |
151 | } else {
152 |
153 | mouseOnBall.z = Math.sqrt( 1.0 - length * length );
154 |
155 | }
156 |
157 | _eye.copy( _this.object.position ).sub( _this.target );
158 |
159 | projection.copy( _this.object.up ).setLength( mouseOnBall.y )
160 | projection.add( objectUp.copy( _this.object.up ).cross( _eye ).setLength( mouseOnBall.x ) );
161 | projection.add( _eye.setLength( mouseOnBall.z ) );
162 |
163 | return projection;
164 | }
165 |
166 | }());
167 |
168 | this.rotateCamera = (function(){
169 |
170 | var axis = new THREE.Vector3(),
171 | quaternion = new THREE.Quaternion();
172 |
173 |
174 | return function () {
175 |
176 | var angle = Math.acos( _rotateStart.dot( _rotateEnd ) / _rotateStart.length() / _rotateEnd.length() );
177 |
178 | if ( angle ) {
179 |
180 | axis.crossVectors( _rotateStart, _rotateEnd ).normalize();
181 |
182 | angle *= _this.rotateSpeed;
183 |
184 | quaternion.setFromAxisAngle( axis, -angle );
185 |
186 | _eye.applyQuaternion( quaternion );
187 | _this.object.up.applyQuaternion( quaternion );
188 |
189 | _rotateEnd.applyQuaternion( quaternion );
190 |
191 | if ( _this.staticMoving ) {
192 |
193 | _rotateStart.copy( _rotateEnd );
194 |
195 | } else {
196 |
197 | quaternion.setFromAxisAngle( axis, angle * ( _this.dynamicDampingFactor - 1.0 ) );
198 | _rotateStart.applyQuaternion( quaternion );
199 |
200 | }
201 |
202 | }
203 | }
204 |
205 | }());
206 |
207 | this.zoomCamera = function () {
208 |
209 | if ( _state === STATE.TOUCH_ZOOM ) {
210 |
211 | var factor = _touchZoomDistanceStart / _touchZoomDistanceEnd;
212 | _touchZoomDistanceStart = _touchZoomDistanceEnd;
213 | _eye.multiplyScalar( factor );
214 |
215 | } else {
216 |
217 | var factor = 1.0 + ( _zoomEnd.y - _zoomStart.y ) * _this.zoomSpeed;
218 |
219 | if ( factor !== 1.0 && factor > 0.0 ) {
220 |
221 | _eye.multiplyScalar( factor );
222 |
223 | if ( _this.staticMoving ) {
224 |
225 | _zoomStart.copy( _zoomEnd );
226 |
227 | } else {
228 |
229 | _zoomStart.y += ( _zoomEnd.y - _zoomStart.y ) * this.dynamicDampingFactor;
230 |
231 | }
232 |
233 | }
234 |
235 | }
236 |
237 | };
238 |
239 | this.panCamera = (function(){
240 |
241 | var mouseChange = new THREE.Vector2(),
242 | objectUp = new THREE.Vector3(),
243 | pan = new THREE.Vector3();
244 |
245 | return function () {
246 |
247 | mouseChange.copy( _panEnd ).sub( _panStart );
248 |
249 | if ( mouseChange.lengthSq() ) {
250 |
251 | mouseChange.multiplyScalar( _eye.length() * _this.panSpeed );
252 |
253 | pan.copy( _eye ).cross( _this.object.up ).setLength( mouseChange.x );
254 | pan.add( objectUp.copy( _this.object.up ).setLength( mouseChange.y ) );
255 |
256 | _this.object.position.add( pan );
257 | _this.target.add( pan );
258 |
259 | if ( _this.staticMoving ) {
260 |
261 | _panStart.copy( _panEnd );
262 |
263 | } else {
264 |
265 | _panStart.add( mouseChange.subVectors( _panEnd, _panStart ).multiplyScalar( _this.dynamicDampingFactor ) );
266 |
267 | }
268 |
269 | }
270 | }
271 |
272 | }());
273 |
274 | this.checkDistances = function () {
275 |
276 | if ( !_this.noZoom || !_this.noPan ) {
277 |
278 | if ( _eye.lengthSq() > _this.maxDistance * _this.maxDistance ) {
279 |
280 | _this.object.position.addVectors( _this.target, _eye.setLength( _this.maxDistance ) );
281 |
282 | }
283 |
284 | if ( _eye.lengthSq() < _this.minDistance * _this.minDistance ) {
285 |
286 | _this.object.position.addVectors( _this.target, _eye.setLength( _this.minDistance ) );
287 |
288 | }
289 |
290 | }
291 |
292 | };
293 |
294 | this.update = function () {
295 |
296 | _eye.subVectors( _this.object.position, _this.target );
297 |
298 | if ( !_this.noRotate ) {
299 |
300 | _this.rotateCamera();
301 |
302 | }
303 |
304 | if ( !_this.noZoom ) {
305 |
306 | _this.zoomCamera();
307 |
308 | }
309 |
310 | if ( !_this.noPan ) {
311 |
312 | _this.panCamera();
313 |
314 | }
315 |
316 | _this.object.position.addVectors( _this.target, _eye );
317 |
318 | _this.checkDistances();
319 |
320 | _this.object.lookAt( _this.target );
321 |
322 | if ( lastPosition.distanceToSquared( _this.object.position ) > EPS ) {
323 |
324 | _this.dispatchEvent( changeEvent );
325 |
326 | lastPosition.copy( _this.object.position );
327 |
328 | }
329 |
330 | };
331 |
332 | this.reset = function () {
333 |
334 | _state = STATE.NONE;
335 | _prevState = STATE.NONE;
336 |
337 | _this.target.copy( _this.target0 );
338 | _this.object.position.copy( _this.position0 );
339 | _this.object.up.copy( _this.up0 );
340 |
341 | _eye.subVectors( _this.object.position, _this.target );
342 |
343 | _this.object.lookAt( _this.target );
344 |
345 | _this.dispatchEvent( changeEvent );
346 |
347 | lastPosition.copy( _this.object.position );
348 |
349 | };
350 |
351 | // listeners
352 |
353 | function keydown( event ) {
354 |
355 | if ( _this.enabled === false ) return;
356 |
357 | window.removeEventListener( 'keydown', keydown );
358 |
359 | _prevState = _state;
360 |
361 | if ( _state !== STATE.NONE ) {
362 |
363 | return;
364 |
365 | } else if ( event.keyCode === _this.keys[ STATE.ROTATE ] && !_this.noRotate ) {
366 |
367 | _state = STATE.ROTATE;
368 |
369 | } else if ( event.keyCode === _this.keys[ STATE.ZOOM ] && !_this.noZoom ) {
370 |
371 | _state = STATE.ZOOM;
372 |
373 | } else if ( event.keyCode === _this.keys[ STATE.PAN ] && !_this.noPan ) {
374 |
375 | _state = STATE.PAN;
376 |
377 | }
378 |
379 | }
380 |
381 | function keyup( event ) {
382 |
383 | if ( _this.enabled === false ) return;
384 |
385 | _state = _prevState;
386 |
387 | window.addEventListener( 'keydown', keydown, false );
388 |
389 | }
390 |
391 | function mousedown( event ) {
392 |
393 | if ( _this.enabled === false ) return;
394 |
395 | event.preventDefault();
396 | event.stopPropagation();
397 |
398 | if ( _state === STATE.NONE ) {
399 |
400 | _state = event.button;
401 |
402 | }
403 |
404 | if ( _state === STATE.ROTATE && !_this.noRotate ) {
405 |
406 | _this.getMouseProjectionOnBall( event.pageX, event.pageY, _rotateStart );
407 | _rotateEnd.copy(_rotateStart)
408 |
409 | } else if ( _state === STATE.ZOOM && !_this.noZoom ) {
410 |
411 | _this.getMouseOnScreen( event.pageX, event.pageY, _zoomStart );
412 | _zoomEnd.copy(_zoomStart);
413 |
414 | } else if ( _state === STATE.PAN && !_this.noPan ) {
415 |
416 | _this.getMouseOnScreen( event.pageX, event.pageY, _panStart );
417 | _panEnd.copy(_panStart)
418 |
419 | }
420 |
421 | document.addEventListener( 'mousemove', mousemove, false );
422 | document.addEventListener( 'mouseup', mouseup, false );
423 | _this.dispatchEvent( startEvent );
424 |
425 |
426 | }
427 |
428 | function mousemove( event ) {
429 |
430 | if ( _this.enabled === false ) return;
431 |
432 | event.preventDefault();
433 | event.stopPropagation();
434 |
435 | if ( _state === STATE.ROTATE && !_this.noRotate ) {
436 |
437 | _this.getMouseProjectionOnBall( event.pageX, event.pageY, _rotateEnd );
438 |
439 | } else if ( _state === STATE.ZOOM && !_this.noZoom ) {
440 |
441 | _this.getMouseOnScreen( event.pageX, event.pageY, _zoomEnd );
442 |
443 | } else if ( _state === STATE.PAN && !_this.noPan ) {
444 |
445 | _this.getMouseOnScreen( event.pageX, event.pageY, _panEnd );
446 |
447 | }
448 |
449 | }
450 |
451 | function mouseup( event ) {
452 |
453 | if ( _this.enabled === false ) return;
454 |
455 | event.preventDefault();
456 | event.stopPropagation();
457 |
458 | _state = STATE.NONE;
459 |
460 | document.removeEventListener( 'mousemove', mousemove );
461 | document.removeEventListener( 'mouseup', mouseup );
462 | _this.dispatchEvent( endEvent );
463 |
464 | }
465 |
466 | function mousewheel( event ) {
467 |
468 | if ( _this.enabled === false ) return;
469 |
470 | event.preventDefault();
471 | event.stopPropagation();
472 |
473 | var delta = 0;
474 |
475 | if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
476 |
477 | delta = event.wheelDelta / 40;
478 |
479 | } else if ( event.detail ) { // Firefox
480 |
481 | delta = - event.detail / 3;
482 |
483 | }
484 |
485 | _zoomStart.y += delta * 0.01;
486 | _this.dispatchEvent( startEvent );
487 | _this.dispatchEvent( endEvent );
488 |
489 | }
490 |
491 | function touchstart( event ) {
492 |
493 | if ( _this.enabled === false ) return;
494 |
495 | switch ( event.touches.length ) {
496 |
497 | case 1:
498 | _state = STATE.TOUCH_ROTATE;
499 | _rotateEnd.copy( _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateStart ));
500 | break;
501 |
502 | case 2:
503 | _state = STATE.TOUCH_ZOOM;
504 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
505 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
506 | _touchZoomDistanceEnd = _touchZoomDistanceStart = Math.sqrt( dx * dx + dy * dy );
507 | break;
508 |
509 | case 3:
510 | _state = STATE.TOUCH_PAN;
511 | _panEnd.copy( _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panStart ));
512 | break;
513 |
514 | default:
515 | _state = STATE.NONE;
516 |
517 | }
518 | _this.dispatchEvent( startEvent );
519 |
520 |
521 | }
522 |
523 | function touchmove( event ) {
524 |
525 | if ( _this.enabled === false ) return;
526 |
527 | event.preventDefault();
528 | event.stopPropagation();
529 |
530 | switch ( event.touches.length ) {
531 |
532 | case 1:
533 | _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateEnd );
534 | break;
535 |
536 | case 2:
537 | var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
538 | var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
539 | _touchZoomDistanceEnd = Math.sqrt( dx * dx + dy * dy )
540 | break;
541 |
542 | case 3:
543 | _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panEnd );
544 | break;
545 |
546 | default:
547 | _state = STATE.NONE;
548 |
549 | }
550 |
551 | }
552 |
553 | function touchend( event ) {
554 |
555 | if ( _this.enabled === false ) return;
556 |
557 | switch ( event.touches.length ) {
558 |
559 | case 1:
560 | _rotateStart.copy( _this.getMouseProjectionOnBall( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _rotateEnd ));
561 | break;
562 |
563 | case 2:
564 | _touchZoomDistanceStart = _touchZoomDistanceEnd = 0;
565 | break;
566 |
567 | case 3:
568 | _panStart.copy( _this.getMouseOnScreen( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY, _panEnd ));
569 | break;
570 |
571 | }
572 |
573 | _state = STATE.NONE;
574 | _this.dispatchEvent( endEvent );
575 |
576 | }
577 |
578 | this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
579 |
580 | this.domElement.addEventListener( 'mousedown', mousedown, false );
581 |
582 | this.domElement.addEventListener( 'mousewheel', mousewheel, false );
583 | this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox
584 |
585 | this.domElement.addEventListener( 'touchstart', touchstart, false );
586 | this.domElement.addEventListener( 'touchend', touchend, false );
587 | this.domElement.addEventListener( 'touchmove', touchmove, false );
588 |
589 | window.addEventListener( 'keydown', keydown, false );
590 | window.addEventListener( 'keyup', keyup, false );
591 |
592 | this.handleResize();
593 |
594 | // force an update at start
595 | this.update();
596 |
597 | };
598 |
599 | THREE.TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
600 |
--------------------------------------------------------------------------------
/lib/underscore.js:
--------------------------------------------------------------------------------
1 | // Underscore.js 1.5.2
2 | // http://underscorejs.org
3 | // (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
4 | // Underscore may be freely distributed under the MIT license.
5 |
6 | (function() {
7 |
8 | // Baseline setup
9 | // --------------
10 |
11 | // Establish the root object, `window` in the browser, or `exports` on the server.
12 | var root = this;
13 |
14 | // Save the previous value of the `_` variable.
15 | var previousUnderscore = root._;
16 |
17 | // Establish the object that gets returned to break out of a loop iteration.
18 | var breaker = {};
19 |
20 | // Save bytes in the minified (but not gzipped) version:
21 | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
22 |
23 | // Create quick reference variables for speed access to core prototypes.
24 | var
25 | push = ArrayProto.push,
26 | slice = ArrayProto.slice,
27 | concat = ArrayProto.concat,
28 | toString = ObjProto.toString,
29 | hasOwnProperty = ObjProto.hasOwnProperty;
30 |
31 | // All **ECMAScript 5** native function implementations that we hope to use
32 | // are declared here.
33 | var
34 | nativeForEach = ArrayProto.forEach,
35 | nativeMap = ArrayProto.map,
36 | nativeReduce = ArrayProto.reduce,
37 | nativeReduceRight = ArrayProto.reduceRight,
38 | nativeFilter = ArrayProto.filter,
39 | nativeEvery = ArrayProto.every,
40 | nativeSome = ArrayProto.some,
41 | nativeIndexOf = ArrayProto.indexOf,
42 | nativeLastIndexOf = ArrayProto.lastIndexOf,
43 | nativeIsArray = Array.isArray,
44 | nativeKeys = Object.keys,
45 | nativeBind = FuncProto.bind;
46 |
47 | // Create a safe reference to the Underscore object for use below.
48 | var _ = function(obj) {
49 | if (obj instanceof _) return obj;
50 | if (!(this instanceof _)) return new _(obj);
51 | this._wrapped = obj;
52 | };
53 |
54 | // Export the Underscore object for **Node.js**, with
55 | // backwards-compatibility for the old `require()` API. If we're in
56 | // the browser, add `_` as a global object via a string identifier,
57 | // for Closure Compiler "advanced" mode.
58 | if (typeof exports !== 'undefined') {
59 | if (typeof module !== 'undefined' && module.exports) {
60 | exports = module.exports = _;
61 | }
62 | exports._ = _;
63 | } else {
64 | root._ = _;
65 | }
66 |
67 | // Current version.
68 | _.VERSION = '1.5.2';
69 |
70 | // Collection Functions
71 | // --------------------
72 |
73 | // The cornerstone, an `each` implementation, aka `forEach`.
74 | // Handles objects with the built-in `forEach`, arrays, and raw objects.
75 | // Delegates to **ECMAScript 5**'s native `forEach` if available.
76 | var each = _.each = _.forEach = function(obj, iterator, context) {
77 | if (obj == null) return;
78 | if (nativeForEach && obj.forEach === nativeForEach) {
79 | obj.forEach(iterator, context);
80 | } else if (obj.length === +obj.length) {
81 | for (var i = 0, length = obj.length; i < length; i++) {
82 | if (iterator.call(context, obj[i], i, obj) === breaker) return;
83 | }
84 | } else {
85 | var keys = _.keys(obj);
86 | for (var i = 0, length = keys.length; i < length; i++) {
87 | if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return;
88 | }
89 | }
90 | };
91 |
92 | // Return the results of applying the iterator to each element.
93 | // Delegates to **ECMAScript 5**'s native `map` if available.
94 | _.map = _.collect = function(obj, iterator, context) {
95 | var results = [];
96 | if (obj == null) return results;
97 | if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
98 | each(obj, function(value, index, list) {
99 | results.push(iterator.call(context, value, index, list));
100 | });
101 | return results;
102 | };
103 |
104 | var reduceError = 'Reduce of empty array with no initial value';
105 |
106 | // **Reduce** builds up a single result from a list of values, aka `inject`,
107 | // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
108 | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
109 | var initial = arguments.length > 2;
110 | if (obj == null) obj = [];
111 | if (nativeReduce && obj.reduce === nativeReduce) {
112 | if (context) iterator = _.bind(iterator, context);
113 | return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
114 | }
115 | each(obj, function(value, index, list) {
116 | if (!initial) {
117 | memo = value;
118 | initial = true;
119 | } else {
120 | memo = iterator.call(context, memo, value, index, list);
121 | }
122 | });
123 | if (!initial) throw new TypeError(reduceError);
124 | return memo;
125 | };
126 |
127 | // The right-associative version of reduce, also known as `foldr`.
128 | // Delegates to **ECMAScript 5**'s native `reduceRight` if available.
129 | _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
130 | var initial = arguments.length > 2;
131 | if (obj == null) obj = [];
132 | if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
133 | if (context) iterator = _.bind(iterator, context);
134 | return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
135 | }
136 | var length = obj.length;
137 | if (length !== +length) {
138 | var keys = _.keys(obj);
139 | length = keys.length;
140 | }
141 | each(obj, function(value, index, list) {
142 | index = keys ? keys[--length] : --length;
143 | if (!initial) {
144 | memo = obj[index];
145 | initial = true;
146 | } else {
147 | memo = iterator.call(context, memo, obj[index], index, list);
148 | }
149 | });
150 | if (!initial) throw new TypeError(reduceError);
151 | return memo;
152 | };
153 |
154 | // Return the first value which passes a truth test. Aliased as `detect`.
155 | _.find = _.detect = function(obj, iterator, context) {
156 | var result;
157 | any(obj, function(value, index, list) {
158 | if (iterator.call(context, value, index, list)) {
159 | result = value;
160 | return true;
161 | }
162 | });
163 | return result;
164 | };
165 |
166 | // Return all the elements that pass a truth test.
167 | // Delegates to **ECMAScript 5**'s native `filter` if available.
168 | // Aliased as `select`.
169 | _.filter = _.select = function(obj, iterator, context) {
170 | var results = [];
171 | if (obj == null) return results;
172 | if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
173 | each(obj, function(value, index, list) {
174 | if (iterator.call(context, value, index, list)) results.push(value);
175 | });
176 | return results;
177 | };
178 |
179 | // Return all the elements for which a truth test fails.
180 | _.reject = function(obj, iterator, context) {
181 | return _.filter(obj, function(value, index, list) {
182 | return !iterator.call(context, value, index, list);
183 | }, context);
184 | };
185 |
186 | // Determine whether all of the elements match a truth test.
187 | // Delegates to **ECMAScript 5**'s native `every` if available.
188 | // Aliased as `all`.
189 | _.every = _.all = function(obj, iterator, context) {
190 | iterator || (iterator = _.identity);
191 | var result = true;
192 | if (obj == null) return result;
193 | if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
194 | each(obj, function(value, index, list) {
195 | if (!(result = result && iterator.call(context, value, index, list))) return breaker;
196 | });
197 | return !!result;
198 | };
199 |
200 | // Determine if at least one element in the object matches a truth test.
201 | // Delegates to **ECMAScript 5**'s native `some` if available.
202 | // Aliased as `any`.
203 | var any = _.some = _.any = function(obj, iterator, context) {
204 | iterator || (iterator = _.identity);
205 | var result = false;
206 | if (obj == null) return result;
207 | if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
208 | each(obj, function(value, index, list) {
209 | if (result || (result = iterator.call(context, value, index, list))) return breaker;
210 | });
211 | return !!result;
212 | };
213 |
214 | // Determine if the array or object contains a given value (using `===`).
215 | // Aliased as `include`.
216 | _.contains = _.include = function(obj, target) {
217 | if (obj == null) return false;
218 | if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
219 | return any(obj, function(value) {
220 | return value === target;
221 | });
222 | };
223 |
224 | // Invoke a method (with arguments) on every item in a collection.
225 | _.invoke = function(obj, method) {
226 | var args = slice.call(arguments, 2);
227 | var isFunc = _.isFunction(method);
228 | return _.map(obj, function(value) {
229 | return (isFunc ? method : value[method]).apply(value, args);
230 | });
231 | };
232 |
233 | // Convenience version of a common use case of `map`: fetching a property.
234 | _.pluck = function(obj, key) {
235 | return _.map(obj, function(value){ return value[key]; });
236 | };
237 |
238 | // Convenience version of a common use case of `filter`: selecting only objects
239 | // containing specific `key:value` pairs.
240 | _.where = function(obj, attrs, first) {
241 | if (_.isEmpty(attrs)) return first ? void 0 : [];
242 | return _[first ? 'find' : 'filter'](obj, function(value) {
243 | for (var key in attrs) {
244 | if (attrs[key] !== value[key]) return false;
245 | }
246 | return true;
247 | });
248 | };
249 |
250 | // Convenience version of a common use case of `find`: getting the first object
251 | // containing specific `key:value` pairs.
252 | _.findWhere = function(obj, attrs) {
253 | return _.where(obj, attrs, true);
254 | };
255 |
256 | // Return the maximum element or (element-based computation).
257 | // Can't optimize arrays of integers longer than 65,535 elements.
258 | // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797)
259 | _.max = function(obj, iterator, context) {
260 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
261 | return Math.max.apply(Math, obj);
262 | }
263 | if (!iterator && _.isEmpty(obj)) return -Infinity;
264 | var result = {computed : -Infinity, value: -Infinity};
265 | each(obj, function(value, index, list) {
266 | var computed = iterator ? iterator.call(context, value, index, list) : value;
267 | computed > result.computed && (result = {value : value, computed : computed});
268 | });
269 | return result.value;
270 | };
271 |
272 | // Return the minimum element (or element-based computation).
273 | _.min = function(obj, iterator, context) {
274 | if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
275 | return Math.min.apply(Math, obj);
276 | }
277 | if (!iterator && _.isEmpty(obj)) return Infinity;
278 | var result = {computed : Infinity, value: Infinity};
279 | each(obj, function(value, index, list) {
280 | var computed = iterator ? iterator.call(context, value, index, list) : value;
281 | computed < result.computed && (result = {value : value, computed : computed});
282 | });
283 | return result.value;
284 | };
285 |
286 | // Shuffle an array, using the modern version of the
287 | // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
288 | _.shuffle = function(obj) {
289 | var rand;
290 | var index = 0;
291 | var shuffled = [];
292 | each(obj, function(value) {
293 | rand = _.random(index++);
294 | shuffled[index - 1] = shuffled[rand];
295 | shuffled[rand] = value;
296 | });
297 | return shuffled;
298 | };
299 |
300 | // Sample **n** random values from a collection.
301 | // If **n** is not specified, returns a single random element.
302 | // The internal `guard` argument allows it to work with `map`.
303 | _.sample = function(obj, n, guard) {
304 | if (n == null || guard) {
305 | if (obj.length !== +obj.length) obj = _.values(obj);
306 | return obj[_.random(obj.length - 1)];
307 | }
308 | return _.shuffle(obj).slice(0, Math.max(0, n));
309 | };
310 |
311 | // An internal function to generate lookup iterators.
312 | var lookupIterator = function(value) {
313 | return _.isFunction(value) ? value : function(obj){ return obj[value]; };
314 | };
315 |
316 | // Sort the object's values by a criterion produced by an iterator.
317 | _.sortBy = function(obj, value, context) {
318 | var iterator = lookupIterator(value);
319 | return _.pluck(_.map(obj, function(value, index, list) {
320 | return {
321 | value: value,
322 | index: index,
323 | criteria: iterator.call(context, value, index, list)
324 | };
325 | }).sort(function(left, right) {
326 | var a = left.criteria;
327 | var b = right.criteria;
328 | if (a !== b) {
329 | if (a > b || a === void 0) return 1;
330 | if (a < b || b === void 0) return -1;
331 | }
332 | return left.index - right.index;
333 | }), 'value');
334 | };
335 |
336 | // An internal function used for aggregate "group by" operations.
337 | var group = function(behavior) {
338 | return function(obj, value, context) {
339 | var result = {};
340 | var iterator = value == null ? _.identity : lookupIterator(value);
341 | each(obj, function(value, index) {
342 | var key = iterator.call(context, value, index, obj);
343 | behavior(result, key, value);
344 | });
345 | return result;
346 | };
347 | };
348 |
349 | // Groups the object's values by a criterion. Pass either a string attribute
350 | // to group by, or a function that returns the criterion.
351 | _.groupBy = group(function(result, key, value) {
352 | (_.has(result, key) ? result[key] : (result[key] = [])).push(value);
353 | });
354 |
355 | // Indexes the object's values by a criterion, similar to `groupBy`, but for
356 | // when you know that your index values will be unique.
357 | _.indexBy = group(function(result, key, value) {
358 | result[key] = value;
359 | });
360 |
361 | // Counts instances of an object that group by a certain criterion. Pass
362 | // either a string attribute to count by, or a function that returns the
363 | // criterion.
364 | _.countBy = group(function(result, key) {
365 | _.has(result, key) ? result[key]++ : result[key] = 1;
366 | });
367 |
368 | // Use a comparator function to figure out the smallest index at which
369 | // an object should be inserted so as to maintain order. Uses binary search.
370 | _.sortedIndex = function(array, obj, iterator, context) {
371 | iterator = iterator == null ? _.identity : lookupIterator(iterator);
372 | var value = iterator.call(context, obj);
373 | var low = 0, high = array.length;
374 | while (low < high) {
375 | var mid = (low + high) >>> 1;
376 | iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
377 | }
378 | return low;
379 | };
380 |
381 | // Safely create a real, live array from anything iterable.
382 | _.toArray = function(obj) {
383 | if (!obj) return [];
384 | if (_.isArray(obj)) return slice.call(obj);
385 | if (obj.length === +obj.length) return _.map(obj, _.identity);
386 | return _.values(obj);
387 | };
388 |
389 | // Return the number of elements in an object.
390 | _.size = function(obj) {
391 | if (obj == null) return 0;
392 | return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
393 | };
394 |
395 | // Array Functions
396 | // ---------------
397 |
398 | // Get the first element of an array. Passing **n** will return the first N
399 | // values in the array. Aliased as `head` and `take`. The **guard** check
400 | // allows it to work with `_.map`.
401 | _.first = _.head = _.take = function(array, n, guard) {
402 | if (array == null) return void 0;
403 | return (n == null) || guard ? array[0] : slice.call(array, 0, n);
404 | };
405 |
406 | // Returns everything but the last entry of the array. Especially useful on
407 | // the arguments object. Passing **n** will return all the values in
408 | // the array, excluding the last N. The **guard** check allows it to work with
409 | // `_.map`.
410 | _.initial = function(array, n, guard) {
411 | return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
412 | };
413 |
414 | // Get the last element of an array. Passing **n** will return the last N
415 | // values in the array. The **guard** check allows it to work with `_.map`.
416 | _.last = function(array, n, guard) {
417 | if (array == null) return void 0;
418 | if ((n == null) || guard) {
419 | return array[array.length - 1];
420 | } else {
421 | return slice.call(array, Math.max(array.length - n, 0));
422 | }
423 | };
424 |
425 | // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
426 | // Especially useful on the arguments object. Passing an **n** will return
427 | // the rest N values in the array. The **guard**
428 | // check allows it to work with `_.map`.
429 | _.rest = _.tail = _.drop = function(array, n, guard) {
430 | return slice.call(array, (n == null) || guard ? 1 : n);
431 | };
432 |
433 | // Trim out all falsy values from an array.
434 | _.compact = function(array) {
435 | return _.filter(array, _.identity);
436 | };
437 |
438 | // Internal implementation of a recursive `flatten` function.
439 | var flatten = function(input, shallow, output) {
440 | if (shallow && _.every(input, _.isArray)) {
441 | return concat.apply(output, input);
442 | }
443 | each(input, function(value) {
444 | if (_.isArray(value) || _.isArguments(value)) {
445 | shallow ? push.apply(output, value) : flatten(value, shallow, output);
446 | } else {
447 | output.push(value);
448 | }
449 | });
450 | return output;
451 | };
452 |
453 | // Flatten out an array, either recursively (by default), or just one level.
454 | _.flatten = function(array, shallow) {
455 | return flatten(array, shallow, []);
456 | };
457 |
458 | // Return a version of the array that does not contain the specified value(s).
459 | _.without = function(array) {
460 | return _.difference(array, slice.call(arguments, 1));
461 | };
462 |
463 | // Produce a duplicate-free version of the array. If the array has already
464 | // been sorted, you have the option of using a faster algorithm.
465 | // Aliased as `unique`.
466 | _.uniq = _.unique = function(array, isSorted, iterator, context) {
467 | if (_.isFunction(isSorted)) {
468 | context = iterator;
469 | iterator = isSorted;
470 | isSorted = false;
471 | }
472 | var initial = iterator ? _.map(array, iterator, context) : array;
473 | var results = [];
474 | var seen = [];
475 | each(initial, function(value, index) {
476 | if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
477 | seen.push(value);
478 | results.push(array[index]);
479 | }
480 | });
481 | return results;
482 | };
483 |
484 | // Produce an array that contains the union: each distinct element from all of
485 | // the passed-in arrays.
486 | _.union = function() {
487 | return _.uniq(_.flatten(arguments, true));
488 | };
489 |
490 | // Produce an array that contains every item shared between all the
491 | // passed-in arrays.
492 | _.intersection = function(array) {
493 | var rest = slice.call(arguments, 1);
494 | return _.filter(_.uniq(array), function(item) {
495 | return _.every(rest, function(other) {
496 | return _.indexOf(other, item) >= 0;
497 | });
498 | });
499 | };
500 |
501 | // Take the difference between one array and a number of other arrays.
502 | // Only the elements present in just the first array will remain.
503 | _.difference = function(array) {
504 | var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
505 | return _.filter(array, function(value){ return !_.contains(rest, value); });
506 | };
507 |
508 | // Zip together multiple lists into a single array -- elements that share
509 | // an index go together.
510 | _.zip = function() {
511 | var length = _.max(_.pluck(arguments, "length").concat(0));
512 | var results = new Array(length);
513 | for (var i = 0; i < length; i++) {
514 | results[i] = _.pluck(arguments, '' + i);
515 | }
516 | return results;
517 | };
518 |
519 | // Converts lists into objects. Pass either a single array of `[key, value]`
520 | // pairs, or two parallel arrays of the same length -- one of keys, and one of
521 | // the corresponding values.
522 | _.object = function(list, values) {
523 | if (list == null) return {};
524 | var result = {};
525 | for (var i = 0, length = list.length; i < length; i++) {
526 | if (values) {
527 | result[list[i]] = values[i];
528 | } else {
529 | result[list[i][0]] = list[i][1];
530 | }
531 | }
532 | return result;
533 | };
534 |
535 | // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
536 | // we need this function. Return the position of the first occurrence of an
537 | // item in an array, or -1 if the item is not included in the array.
538 | // Delegates to **ECMAScript 5**'s native `indexOf` if available.
539 | // If the array is large and already in sort order, pass `true`
540 | // for **isSorted** to use binary search.
541 | _.indexOf = function(array, item, isSorted) {
542 | if (array == null) return -1;
543 | var i = 0, length = array.length;
544 | if (isSorted) {
545 | if (typeof isSorted == 'number') {
546 | i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted);
547 | } else {
548 | i = _.sortedIndex(array, item);
549 | return array[i] === item ? i : -1;
550 | }
551 | }
552 | if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
553 | for (; i < length; i++) if (array[i] === item) return i;
554 | return -1;
555 | };
556 |
557 | // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
558 | _.lastIndexOf = function(array, item, from) {
559 | if (array == null) return -1;
560 | var hasIndex = from != null;
561 | if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
562 | return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
563 | }
564 | var i = (hasIndex ? from : array.length);
565 | while (i--) if (array[i] === item) return i;
566 | return -1;
567 | };
568 |
569 | // Generate an integer Array containing an arithmetic progression. A port of
570 | // the native Python `range()` function. See
571 | // [the Python documentation](http://docs.python.org/library/functions.html#range).
572 | _.range = function(start, stop, step) {
573 | if (arguments.length <= 1) {
574 | stop = start || 0;
575 | start = 0;
576 | }
577 | step = arguments[2] || 1;
578 |
579 | var length = Math.max(Math.ceil((stop - start) / step), 0);
580 | var idx = 0;
581 | var range = new Array(length);
582 |
583 | while(idx < length) {
584 | range[idx++] = start;
585 | start += step;
586 | }
587 |
588 | return range;
589 | };
590 |
591 | // Function (ahem) Functions
592 | // ------------------
593 |
594 | // Reusable constructor function for prototype setting.
595 | var ctor = function(){};
596 |
597 | // Create a function bound to a given object (assigning `this`, and arguments,
598 | // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
599 | // available.
600 | _.bind = function(func, context) {
601 | var args, bound;
602 | if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
603 | if (!_.isFunction(func)) throw new TypeError;
604 | args = slice.call(arguments, 2);
605 | return bound = function() {
606 | if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
607 | ctor.prototype = func.prototype;
608 | var self = new ctor;
609 | ctor.prototype = null;
610 | var result = func.apply(self, args.concat(slice.call(arguments)));
611 | if (Object(result) === result) return result;
612 | return self;
613 | };
614 | };
615 |
616 | // Partially apply a function by creating a version that has had some of its
617 | // arguments pre-filled, without changing its dynamic `this` context.
618 | _.partial = function(func) {
619 | var args = slice.call(arguments, 1);
620 | return function() {
621 | return func.apply(this, args.concat(slice.call(arguments)));
622 | };
623 | };
624 |
625 | // Bind all of an object's methods to that object. Useful for ensuring that
626 | // all callbacks defined on an object belong to it.
627 | _.bindAll = function(obj) {
628 | var funcs = slice.call(arguments, 1);
629 | if (funcs.length === 0) throw new Error("bindAll must be passed function names");
630 | each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
631 | return obj;
632 | };
633 |
634 | // Memoize an expensive function by storing its results.
635 | _.memoize = function(func, hasher) {
636 | var memo = {};
637 | hasher || (hasher = _.identity);
638 | return function() {
639 | var key = hasher.apply(this, arguments);
640 | return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
641 | };
642 | };
643 |
644 | // Delays a function for the given number of milliseconds, and then calls
645 | // it with the arguments supplied.
646 | _.delay = function(func, wait) {
647 | var args = slice.call(arguments, 2);
648 | return setTimeout(function(){ return func.apply(null, args); }, wait);
649 | };
650 |
651 | // Defers a function, scheduling it to run after the current call stack has
652 | // cleared.
653 | _.defer = function(func) {
654 | return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
655 | };
656 |
657 | // Returns a function, that, when invoked, will only be triggered at most once
658 | // during a given window of time. Normally, the throttled function will run
659 | // as much as it can, without ever going more than once per `wait` duration;
660 | // but if you'd like to disable the execution on the leading edge, pass
661 | // `{leading: false}`. To disable execution on the trailing edge, ditto.
662 | _.throttle = function(func, wait, options) {
663 | var context, args, result;
664 | var timeout = null;
665 | var previous = 0;
666 | options || (options = {});
667 | var later = function() {
668 | previous = options.leading === false ? 0 : new Date;
669 | timeout = null;
670 | result = func.apply(context, args);
671 | };
672 | return function() {
673 | var now = new Date;
674 | if (!previous && options.leading === false) previous = now;
675 | var remaining = wait - (now - previous);
676 | context = this;
677 | args = arguments;
678 | if (remaining <= 0) {
679 | clearTimeout(timeout);
680 | timeout = null;
681 | previous = now;
682 | result = func.apply(context, args);
683 | } else if (!timeout && options.trailing !== false) {
684 | timeout = setTimeout(later, remaining);
685 | }
686 | return result;
687 | };
688 | };
689 |
690 | // Returns a function, that, as long as it continues to be invoked, will not
691 | // be triggered. The function will be called after it stops being called for
692 | // N milliseconds. If `immediate` is passed, trigger the function on the
693 | // leading edge, instead of the trailing.
694 | _.debounce = function(func, wait, immediate) {
695 | var timeout, args, context, timestamp, result;
696 | return function() {
697 | context = this;
698 | args = arguments;
699 | timestamp = new Date();
700 | var later = function() {
701 | var last = (new Date()) - timestamp;
702 | if (last < wait) {
703 | timeout = setTimeout(later, wait - last);
704 | } else {
705 | timeout = null;
706 | if (!immediate) result = func.apply(context, args);
707 | }
708 | };
709 | var callNow = immediate && !timeout;
710 | if (!timeout) {
711 | timeout = setTimeout(later, wait);
712 | }
713 | if (callNow) result = func.apply(context, args);
714 | return result;
715 | };
716 | };
717 |
718 | // Returns a function that will be executed at most one time, no matter how
719 | // often you call it. Useful for lazy initialization.
720 | _.once = function(func) {
721 | var ran = false, memo;
722 | return function() {
723 | if (ran) return memo;
724 | ran = true;
725 | memo = func.apply(this, arguments);
726 | func = null;
727 | return memo;
728 | };
729 | };
730 |
731 | // Returns the first function passed as an argument to the second,
732 | // allowing you to adjust arguments, run code before and after, and
733 | // conditionally execute the original function.
734 | _.wrap = function(func, wrapper) {
735 | return function() {
736 | var args = [func];
737 | push.apply(args, arguments);
738 | return wrapper.apply(this, args);
739 | };
740 | };
741 |
742 | // Returns a function that is the composition of a list of functions, each
743 | // consuming the return value of the function that follows.
744 | _.compose = function() {
745 | var funcs = arguments;
746 | return function() {
747 | var args = arguments;
748 | for (var i = funcs.length - 1; i >= 0; i--) {
749 | args = [funcs[i].apply(this, args)];
750 | }
751 | return args[0];
752 | };
753 | };
754 |
755 | // Returns a function that will only be executed after being called N times.
756 | _.after = function(times, func) {
757 | return function() {
758 | if (--times < 1) {
759 | return func.apply(this, arguments);
760 | }
761 | };
762 | };
763 |
764 | // Object Functions
765 | // ----------------
766 |
767 | // Retrieve the names of an object's properties.
768 | // Delegates to **ECMAScript 5**'s native `Object.keys`
769 | _.keys = nativeKeys || function(obj) {
770 | if (obj !== Object(obj)) throw new TypeError('Invalid object');
771 | var keys = [];
772 | for (var key in obj) if (_.has(obj, key)) keys.push(key);
773 | return keys;
774 | };
775 |
776 | // Retrieve the values of an object's properties.
777 | _.values = function(obj) {
778 | var keys = _.keys(obj);
779 | var length = keys.length;
780 | var values = new Array(length);
781 | for (var i = 0; i < length; i++) {
782 | values[i] = obj[keys[i]];
783 | }
784 | return values;
785 | };
786 |
787 | // Convert an object into a list of `[key, value]` pairs.
788 | _.pairs = function(obj) {
789 | var keys = _.keys(obj);
790 | var length = keys.length;
791 | var pairs = new Array(length);
792 | for (var i = 0; i < length; i++) {
793 | pairs[i] = [keys[i], obj[keys[i]]];
794 | }
795 | return pairs;
796 | };
797 |
798 | // Invert the keys and values of an object. The values must be serializable.
799 | _.invert = function(obj) {
800 | var result = {};
801 | var keys = _.keys(obj);
802 | for (var i = 0, length = keys.length; i < length; i++) {
803 | result[obj[keys[i]]] = keys[i];
804 | }
805 | return result;
806 | };
807 |
808 | // Return a sorted list of the function names available on the object.
809 | // Aliased as `methods`
810 | _.functions = _.methods = function(obj) {
811 | var names = [];
812 | for (var key in obj) {
813 | if (_.isFunction(obj[key])) names.push(key);
814 | }
815 | return names.sort();
816 | };
817 |
818 | // Extend a given object with all the properties in passed-in object(s).
819 | _.extend = function(obj) {
820 | each(slice.call(arguments, 1), function(source) {
821 | if (source) {
822 | for (var prop in source) {
823 | obj[prop] = source[prop];
824 | }
825 | }
826 | });
827 | return obj;
828 | };
829 |
830 | // Return a copy of the object only containing the whitelisted properties.
831 | _.pick = function(obj) {
832 | var copy = {};
833 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
834 | each(keys, function(key) {
835 | if (key in obj) copy[key] = obj[key];
836 | });
837 | return copy;
838 | };
839 |
840 | // Return a copy of the object without the blacklisted properties.
841 | _.omit = function(obj) {
842 | var copy = {};
843 | var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
844 | for (var key in obj) {
845 | if (!_.contains(keys, key)) copy[key] = obj[key];
846 | }
847 | return copy;
848 | };
849 |
850 | // Fill in a given object with default properties.
851 | _.defaults = function(obj) {
852 | each(slice.call(arguments, 1), function(source) {
853 | if (source) {
854 | for (var prop in source) {
855 | if (obj[prop] === void 0) obj[prop] = source[prop];
856 | }
857 | }
858 | });
859 | return obj;
860 | };
861 |
862 | // Create a (shallow-cloned) duplicate of an object.
863 | _.clone = function(obj) {
864 | if (!_.isObject(obj)) return obj;
865 | return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
866 | };
867 |
868 | // Invokes interceptor with the obj, and then returns obj.
869 | // The primary purpose of this method is to "tap into" a method chain, in
870 | // order to perform operations on intermediate results within the chain.
871 | _.tap = function(obj, interceptor) {
872 | interceptor(obj);
873 | return obj;
874 | };
875 |
876 | // Internal recursive comparison function for `isEqual`.
877 | var eq = function(a, b, aStack, bStack) {
878 | // Identical objects are equal. `0 === -0`, but they aren't identical.
879 | // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
880 | if (a === b) return a !== 0 || 1 / a == 1 / b;
881 | // A strict comparison is necessary because `null == undefined`.
882 | if (a == null || b == null) return a === b;
883 | // Unwrap any wrapped objects.
884 | if (a instanceof _) a = a._wrapped;
885 | if (b instanceof _) b = b._wrapped;
886 | // Compare `[[Class]]` names.
887 | var className = toString.call(a);
888 | if (className != toString.call(b)) return false;
889 | switch (className) {
890 | // Strings, numbers, dates, and booleans are compared by value.
891 | case '[object String]':
892 | // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
893 | // equivalent to `new String("5")`.
894 | return a == String(b);
895 | case '[object Number]':
896 | // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for
897 | // other numeric values.
898 | return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
899 | case '[object Date]':
900 | case '[object Boolean]':
901 | // Coerce dates and booleans to numeric primitive values. Dates are compared by their
902 | // millisecond representations. Note that invalid dates with millisecond representations
903 | // of `NaN` are not equivalent.
904 | return +a == +b;
905 | // RegExps are compared by their source patterns and flags.
906 | case '[object RegExp]':
907 | return a.source == b.source &&
908 | a.global == b.global &&
909 | a.multiline == b.multiline &&
910 | a.ignoreCase == b.ignoreCase;
911 | }
912 | if (typeof a != 'object' || typeof b != 'object') return false;
913 | // Assume equality for cyclic structures. The algorithm for detecting cyclic
914 | // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
915 | var length = aStack.length;
916 | while (length--) {
917 | // Linear search. Performance is inversely proportional to the number of
918 | // unique nested structures.
919 | if (aStack[length] == a) return bStack[length] == b;
920 | }
921 | // Objects with different constructors are not equivalent, but `Object`s
922 | // from different frames are.
923 | var aCtor = a.constructor, bCtor = b.constructor;
924 | if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
925 | _.isFunction(bCtor) && (bCtor instanceof bCtor))) {
926 | return false;
927 | }
928 | // Add the first object to the stack of traversed objects.
929 | aStack.push(a);
930 | bStack.push(b);
931 | var size = 0, result = true;
932 | // Recursively compare objects and arrays.
933 | if (className == '[object Array]') {
934 | // Compare array lengths to determine if a deep comparison is necessary.
935 | size = a.length;
936 | result = size == b.length;
937 | if (result) {
938 | // Deep compare the contents, ignoring non-numeric properties.
939 | while (size--) {
940 | if (!(result = eq(a[size], b[size], aStack, bStack))) break;
941 | }
942 | }
943 | } else {
944 | // Deep compare objects.
945 | for (var key in a) {
946 | if (_.has(a, key)) {
947 | // Count the expected number of properties.
948 | size++;
949 | // Deep compare each member.
950 | if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
951 | }
952 | }
953 | // Ensure that both objects contain the same number of properties.
954 | if (result) {
955 | for (key in b) {
956 | if (_.has(b, key) && !(size--)) break;
957 | }
958 | result = !size;
959 | }
960 | }
961 | // Remove the first object from the stack of traversed objects.
962 | aStack.pop();
963 | bStack.pop();
964 | return result;
965 | };
966 |
967 | // Perform a deep comparison to check if two objects are equal.
968 | _.isEqual = function(a, b) {
969 | return eq(a, b, [], []);
970 | };
971 |
972 | // Is a given array, string, or object empty?
973 | // An "empty" object has no enumerable own-properties.
974 | _.isEmpty = function(obj) {
975 | if (obj == null) return true;
976 | if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
977 | for (var key in obj) if (_.has(obj, key)) return false;
978 | return true;
979 | };
980 |
981 | // Is a given value a DOM element?
982 | _.isElement = function(obj) {
983 | return !!(obj && obj.nodeType === 1);
984 | };
985 |
986 | // Is a given value an array?
987 | // Delegates to ECMA5's native Array.isArray
988 | _.isArray = nativeIsArray || function(obj) {
989 | return toString.call(obj) == '[object Array]';
990 | };
991 |
992 | // Is a given variable an object?
993 | _.isObject = function(obj) {
994 | return obj === Object(obj);
995 | };
996 |
997 | // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
998 | each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
999 | _['is' + name] = function(obj) {
1000 | return toString.call(obj) == '[object ' + name + ']';
1001 | };
1002 | });
1003 |
1004 | // Define a fallback version of the method in browsers (ahem, IE), where
1005 | // there isn't any inspectable "Arguments" type.
1006 | if (!_.isArguments(arguments)) {
1007 | _.isArguments = function(obj) {
1008 | return !!(obj && _.has(obj, 'callee'));
1009 | };
1010 | }
1011 |
1012 | // Optimize `isFunction` if appropriate.
1013 | if (typeof (/./) !== 'function') {
1014 | _.isFunction = function(obj) {
1015 | return typeof obj === 'function';
1016 | };
1017 | }
1018 |
1019 | // Is a given object a finite number?
1020 | _.isFinite = function(obj) {
1021 | return isFinite(obj) && !isNaN(parseFloat(obj));
1022 | };
1023 |
1024 | // Is the given value `NaN`? (NaN is the only number which does not equal itself).
1025 | _.isNaN = function(obj) {
1026 | return _.isNumber(obj) && obj != +obj;
1027 | };
1028 |
1029 | // Is a given value a boolean?
1030 | _.isBoolean = function(obj) {
1031 | return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
1032 | };
1033 |
1034 | // Is a given value equal to null?
1035 | _.isNull = function(obj) {
1036 | return obj === null;
1037 | };
1038 |
1039 | // Is a given variable undefined?
1040 | _.isUndefined = function(obj) {
1041 | return obj === void 0;
1042 | };
1043 |
1044 | // Shortcut function for checking if an object has a given property directly
1045 | // on itself (in other words, not on a prototype).
1046 | _.has = function(obj, key) {
1047 | return hasOwnProperty.call(obj, key);
1048 | };
1049 |
1050 | // Utility Functions
1051 | // -----------------
1052 |
1053 | // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
1054 | // previous owner. Returns a reference to the Underscore object.
1055 | _.noConflict = function() {
1056 | root._ = previousUnderscore;
1057 | return this;
1058 | };
1059 |
1060 | // Keep the identity function around for default iterators.
1061 | _.identity = function(value) {
1062 | return value;
1063 | };
1064 |
1065 | // Run a function **n** times.
1066 | _.times = function(n, iterator, context) {
1067 | var accum = Array(Math.max(0, n));
1068 | for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
1069 | return accum;
1070 | };
1071 |
1072 | // Return a random integer between min and max (inclusive).
1073 | _.random = function(min, max) {
1074 | if (max == null) {
1075 | max = min;
1076 | min = 0;
1077 | }
1078 | return min + Math.floor(Math.random() * (max - min + 1));
1079 | };
1080 |
1081 | // List of HTML entities for escaping.
1082 | var entityMap = {
1083 | escape: {
1084 | '&': '&',
1085 | '<': '<',
1086 | '>': '>',
1087 | '"': '"',
1088 | "'": '''
1089 | }
1090 | };
1091 | entityMap.unescape = _.invert(entityMap.escape);
1092 |
1093 | // Regexes containing the keys and values listed immediately above.
1094 | var entityRegexes = {
1095 | escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
1096 | unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
1097 | };
1098 |
1099 | // Functions for escaping and unescaping strings to/from HTML interpolation.
1100 | _.each(['escape', 'unescape'], function(method) {
1101 | _[method] = function(string) {
1102 | if (string == null) return '';
1103 | return ('' + string).replace(entityRegexes[method], function(match) {
1104 | return entityMap[method][match];
1105 | });
1106 | };
1107 | });
1108 |
1109 | // If the value of the named `property` is a function then invoke it with the
1110 | // `object` as context; otherwise, return it.
1111 | _.result = function(object, property) {
1112 | if (object == null) return void 0;
1113 | var value = object[property];
1114 | return _.isFunction(value) ? value.call(object) : value;
1115 | };
1116 |
1117 | // Add your own custom functions to the Underscore object.
1118 | _.mixin = function(obj) {
1119 | each(_.functions(obj), function(name) {
1120 | var func = _[name] = obj[name];
1121 | _.prototype[name] = function() {
1122 | var args = [this._wrapped];
1123 | push.apply(args, arguments);
1124 | return result.call(this, func.apply(_, args));
1125 | };
1126 | });
1127 | };
1128 |
1129 | // Generate a unique integer id (unique within the entire client session).
1130 | // Useful for temporary DOM ids.
1131 | var idCounter = 0;
1132 | _.uniqueId = function(prefix) {
1133 | var id = ++idCounter + '';
1134 | return prefix ? prefix + id : id;
1135 | };
1136 |
1137 | // By default, Underscore uses ERB-style template delimiters, change the
1138 | // following template settings to use alternative delimiters.
1139 | _.templateSettings = {
1140 | evaluate : /<%([\s\S]+?)%>/g,
1141 | interpolate : /<%=([\s\S]+?)%>/g,
1142 | escape : /<%-([\s\S]+?)%>/g
1143 | };
1144 |
1145 | // When customizing `templateSettings`, if you don't want to define an
1146 | // interpolation, evaluation or escaping regex, we need one that is
1147 | // guaranteed not to match.
1148 | var noMatch = /(.)^/;
1149 |
1150 | // Certain characters need to be escaped so that they can be put into a
1151 | // string literal.
1152 | var escapes = {
1153 | "'": "'",
1154 | '\\': '\\',
1155 | '\r': 'r',
1156 | '\n': 'n',
1157 | '\t': 't',
1158 | '\u2028': 'u2028',
1159 | '\u2029': 'u2029'
1160 | };
1161 |
1162 | var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
1163 |
1164 | // JavaScript micro-templating, similar to John Resig's implementation.
1165 | // Underscore templating handles arbitrary delimiters, preserves whitespace,
1166 | // and correctly escapes quotes within interpolated code.
1167 | _.template = function(text, data, settings) {
1168 | var render;
1169 | settings = _.defaults({}, settings, _.templateSettings);
1170 |
1171 | // Combine delimiters into one regular expression via alternation.
1172 | var matcher = new RegExp([
1173 | (settings.escape || noMatch).source,
1174 | (settings.interpolate || noMatch).source,
1175 | (settings.evaluate || noMatch).source
1176 | ].join('|') + '|$', 'g');
1177 |
1178 | // Compile the template source, escaping string literals appropriately.
1179 | var index = 0;
1180 | var source = "__p+='";
1181 | text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
1182 | source += text.slice(index, offset)
1183 | .replace(escaper, function(match) { return '\\' + escapes[match]; });
1184 |
1185 | if (escape) {
1186 | source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
1187 | }
1188 | if (interpolate) {
1189 | source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
1190 | }
1191 | if (evaluate) {
1192 | source += "';\n" + evaluate + "\n__p+='";
1193 | }
1194 | index = offset + match.length;
1195 | return match;
1196 | });
1197 | source += "';\n";
1198 |
1199 | // If a variable is not specified, place data values in local scope.
1200 | if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
1201 |
1202 | source = "var __t,__p='',__j=Array.prototype.join," +
1203 | "print=function(){__p+=__j.call(arguments,'');};\n" +
1204 | source + "return __p;\n";
1205 |
1206 | try {
1207 | render = new Function(settings.variable || 'obj', '_', source);
1208 | } catch (e) {
1209 | e.source = source;
1210 | throw e;
1211 | }
1212 |
1213 | if (data) return render(data, _);
1214 | var template = function(data) {
1215 | return render.call(this, data, _);
1216 | };
1217 |
1218 | // Provide the compiled function source as a convenience for precompilation.
1219 | template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
1220 |
1221 | return template;
1222 | };
1223 |
1224 | // Add a "chain" function, which will delegate to the wrapper.
1225 | _.chain = function(obj) {
1226 | return _(obj).chain();
1227 | };
1228 |
1229 | // OOP
1230 | // ---------------
1231 | // If Underscore is called as a function, it returns a wrapped object that
1232 | // can be used OO-style. This wrapper holds altered versions of all the
1233 | // underscore functions. Wrapped objects may be chained.
1234 |
1235 | // Helper function to continue chaining intermediate results.
1236 | var result = function(obj) {
1237 | return this._chain ? _(obj).chain() : obj;
1238 | };
1239 |
1240 | // Add all of the Underscore functions to the wrapper object.
1241 | _.mixin(_);
1242 |
1243 | // Add all mutator Array functions to the wrapper.
1244 | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
1245 | var method = ArrayProto[name];
1246 | _.prototype[name] = function() {
1247 | var obj = this._wrapped;
1248 | method.apply(obj, arguments);
1249 | if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
1250 | return result.call(this, obj);
1251 | };
1252 | });
1253 |
1254 | // Add all accessor Array functions to the wrapper.
1255 | each(['concat', 'join', 'slice'], function(name) {
1256 | var method = ArrayProto[name];
1257 | _.prototype[name] = function() {
1258 | return result.call(this, method.apply(this._wrapped, arguments));
1259 | };
1260 | });
1261 |
1262 | _.extend(_.prototype, {
1263 |
1264 | // Start chaining a wrapped Underscore object.
1265 | chain: function() {
1266 | this._chain = true;
1267 | return this;
1268 | },
1269 |
1270 | // Extracts the result from a wrapped and chained object.
1271 | value: function() {
1272 | return this._wrapped;
1273 | }
1274 |
1275 | });
1276 |
1277 | }).call(this);
1278 |
--------------------------------------------------------------------------------
/shaders/.fs-lookup.glsl.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/shaders/.fs-lookup.glsl.swp
--------------------------------------------------------------------------------
/shaders/.simplex.glsl.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/shaders/.simplex.glsl.swp
--------------------------------------------------------------------------------
/shaders/.ss-collisions.glsl.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/shaders/.ss-collisions.glsl.swp
--------------------------------------------------------------------------------
/shaders/.ss-curl.glsl.swo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/shaders/.ss-curl.glsl.swo
--------------------------------------------------------------------------------
/shaders/.ss-curl.glsl.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/shaders/.ss-curl.glsl.swp
--------------------------------------------------------------------------------
/shaders/.ss-text.glsl.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/shaders/.ss-text.glsl.swp
--------------------------------------------------------------------------------
/shaders/.vs-lookup.glsl.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cabbibo/PhysicsRenderer/c09cbce5e7678ebc3a37cead4348b1d40ca00ee3/shaders/.vs-lookup.glsl.swp
--------------------------------------------------------------------------------
/shaders/curl.glsl:
--------------------------------------------------------------------------------
1 | // Using @eddietree implementation from
2 | // His brilliant demo 'Artifacts'
3 |
4 | vec3 snoiseVec3( vec3 x ){
5 |
6 | float s = snoise(vec3( x ));
7 | float s1 = snoise(vec3( x.y - 19.1 , x.z + 33.4 , x.x + 47.2 ));
8 | float s2 = snoise(vec3( x.z + 74.2 , x.x - 124.5 , x.y + 99.4 ));
9 | vec3 c = vec3( s , s1 , s2 );
10 | return c;
11 |
12 | }
13 |
14 |
15 | vec3 curlNoise( vec3 p ){
16 |
17 | const float e = 1e-1;
18 | vec3 dx = vec3( e , 0.0 , 0.0 );
19 | vec3 dy = vec3( 0.0 , e , 0.0 );
20 | vec3 dz = vec3( 0.0 , 0.0 , e );
21 |
22 | vec3 p_x0 = snoiseVec3( p - dx );
23 | vec3 p_x1 = snoiseVec3( p + dx );
24 | vec3 p_y0 = snoiseVec3( p - dy );
25 | vec3 p_y1 = snoiseVec3( p + dy );
26 | vec3 p_z0 = snoiseVec3( p - dz );
27 | vec3 p_z1 = snoiseVec3( p + dz );
28 |
29 | float x = p_y1.z - p_y0.z - p_z1.y + p_z0.y;
30 | float y = p_z1.x - p_z0.x - p_x1.z + p_x0.z;
31 | float z = p_x1.y - p_x0.y - p_y1.x + p_y0.x;
32 |
33 | const float divisor = 1.0 / ( 2.0 * e );
34 | return normalize( vec3( x , y , z ) * divisor );
35 | }
36 |
--------------------------------------------------------------------------------
/shaders/fs-lookup.glsl:
--------------------------------------------------------------------------------
1 | void main(){
2 |
3 | gl_FragColor = vec4( 1. );
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/shaders/fs-lookupFront.glsl:
--------------------------------------------------------------------------------
1 | varying vec3 vPos;
2 | void main(){
3 |
4 | gl_FragColor = vec4( normalize( vPos ) * .5 + .5 , 1. );
5 |
6 | }
7 |
--------------------------------------------------------------------------------
/shaders/rand.glsl:
--------------------------------------------------------------------------------
1 | float rand(vec2 co){
2 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
3 | }
4 |
--------------------------------------------------------------------------------
/shaders/simplex.glsl:
--------------------------------------------------------------------------------
1 | //
2 | // Description : Array and textureless GLSL 2D simplex noise function.
3 | // Author : Ian McEwan, Ashima Arts.
4 | // Maintainer : ijm
5 | // Lastmod : 20110822 (ijm)
6 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved.
7 | // Distributed under the MIT License. See LICENSE file.
8 | // https://github.com/ashima/webgl-noise
9 | //
10 |
11 | vec3 mod289(vec3 x) {
12 | return x - floor(x * (1.0 / 289.0)) * 289.0;
13 | }
14 |
15 | vec2 mod289(vec2 x) {
16 | return x - floor(x * (1.0 / 289.0)) * 289.0;
17 | }
18 |
19 | vec3 permute(vec3 x) {
20 | return mod289(((x*34.0)+1.0)*x);
21 | }
22 |
23 | float snoise(vec2 v)
24 | {
25 | const vec4 C = vec4(0.211324865405187, // (3.0-sqrt(3.0))/6.0
26 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0)
27 | -0.577350269189626, // -1.0 + 2.0 * C.x
28 | 0.024390243902439); // 1.0 / 41.0
29 | // First corner
30 | vec2 i = floor(v + dot(v, C.yy) );
31 | vec2 x0 = v - i + dot(i, C.xx);
32 |
33 | // Other corners
34 | vec2 i1;
35 | //i1.x = step( x0.y, x0.x ); // x0.x > x0.y ? 1.0 : 0.0
36 | //i1.y = 1.0 - i1.x;
37 | i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
38 | // x0 = x0 - 0.0 + 0.0 * C.xx ;
39 | // x1 = x0 - i1 + 1.0 * C.xx ;
40 | // x2 = x0 - 1.0 + 2.0 * C.xx ;
41 | vec4 x12 = x0.xyxy + C.xxzz;
42 | x12.xy -= i1;
43 |
44 | // Permutations
45 | i = mod289(i); // Avoid truncation effects in permutation
46 | vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 ))
47 | + i.x + vec3(0.0, i1.x, 1.0 ));
48 |
49 | vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), dot(x12.zw,x12.zw)), 0.0);
50 | m = m*m ;
51 | m = m*m ;
52 |
53 | // Gradients: 41 points uniformly over a line, mapped onto a diamond.
54 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287)
55 |
56 | vec3 x = 2.0 * fract(p * C.www) - 1.0;
57 | vec3 h = abs(x) - 0.5;
58 | vec3 ox = floor(x + 0.5);
59 | vec3 a0 = x - ox;
60 |
61 | // Normalise gradients implicitly by scaling m
62 | // Approximation of: m *= inversesqrt( a0*a0 + h*h );
63 | m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h );
64 |
65 | // Compute final noise value at P
66 | vec3 g;
67 | g.x = a0.x * x0.x + h.x * x0.y;
68 | g.yz = a0.yz * x12.xz + h.yz * x12.yw;
69 | return 130.0 * dot(m, g);
70 | }
71 |
72 | //
73 | // Description : Array and textureless GLSL 2D/3D/4D simplex
74 | // noise functions.
75 | // Author : Ian McEwan, Ashima Arts.
76 | // Maintainer : ijm
77 | // Lastmod : 20110822 (ijm)
78 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved.
79 | // Distributed under the MIT License. See LICENSE file.
80 | // https://github.com/ashima/webgl-noise
81 | //
82 |
83 |
84 | vec4 mod289(vec4 x) {
85 | return x - floor(x * (1.0 / 289.0)) * 289.0;
86 | }
87 |
88 | vec4 permute(vec4 x) {
89 | return mod289(((x*34.0)+1.0)*x);
90 | }
91 |
92 | vec4 taylorInvSqrt(vec4 r)
93 | {
94 | return 1.79284291400159 - 0.85373472095314 * r;
95 | }
96 |
97 | float snoise(vec3 v)
98 | {
99 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
100 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
101 |
102 | // First corner
103 | vec3 i = floor(v + dot(v, C.yyy) );
104 | vec3 x0 = v - i + dot(i, C.xxx) ;
105 |
106 | // Other corners
107 | vec3 g = step(x0.yzx, x0.xyz);
108 | vec3 l = 1.0 - g;
109 | vec3 i1 = min( g.xyz, l.zxy );
110 | vec3 i2 = max( g.xyz, l.zxy );
111 |
112 | // x0 = x0 - 0.0 + 0.0 * C.xxx;
113 | // x1 = x0 - i1 + 1.0 * C.xxx;
114 | // x2 = x0 - i2 + 2.0 * C.xxx;
115 | // x3 = x0 - 1.0 + 3.0 * C.xxx;
116 | vec3 x1 = x0 - i1 + C.xxx;
117 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
118 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
119 |
120 | // Permutations
121 | i = mod289(i);
122 | vec4 p = permute( permute( permute(
123 | i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
124 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
125 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
126 |
127 | // Gradients: 7x7 points over a square, mapped onto an octahedron.
128 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
129 | float n_ = 0.142857142857; // 1.0/7.0
130 | vec3 ns = n_ * D.wyz - D.xzx;
131 |
132 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
133 |
134 | vec4 x_ = floor(j * ns.z);
135 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
136 |
137 | vec4 x = x_ *ns.x + ns.yyyy;
138 | vec4 y = y_ *ns.x + ns.yyyy;
139 | vec4 h = 1.0 - abs(x) - abs(y);
140 |
141 | vec4 b0 = vec4( x.xy, y.xy );
142 | vec4 b1 = vec4( x.zw, y.zw );
143 |
144 | //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
145 | //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
146 | vec4 s0 = floor(b0)*2.0 + 1.0;
147 | vec4 s1 = floor(b1)*2.0 + 1.0;
148 | vec4 sh = -step(h, vec4(0.0));
149 |
150 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
151 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
152 |
153 | vec3 p0 = vec3(a0.xy,h.x);
154 | vec3 p1 = vec3(a0.zw,h.y);
155 | vec3 p2 = vec3(a1.xy,h.z);
156 | vec3 p3 = vec3(a1.zw,h.w);
157 |
158 | //Normalise gradients
159 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
160 | p0 *= norm.x;
161 | p1 *= norm.y;
162 | p2 *= norm.z;
163 | p3 *= norm.w;
164 |
165 | // Mix final noise value
166 | vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
167 | m = m * m;
168 | return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
169 | dot(p2,x2), dot(p3,x3) ) );
170 | }
171 |
172 |
173 | //
174 | // Description : Array and textureless GLSL 2D/3D/4D simplex
175 | // noise functions.
176 | // Author : Ian McEwan, Ashima Arts.
177 | // Maintainer : ijm
178 | // Lastmod : 20110822 (ijm)
179 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved.
180 | // Distributed under the MIT License. See LICENSE file.
181 | // https://github.com/ashima/webgl-noise
182 | //
183 |
184 | float mod289(float x) {
185 | return x - floor(x * (1.0 / 289.0)) * 289.0; }
186 |
187 | float permute(float x) {
188 | return mod289(((x*34.0)+1.0)*x);
189 | }
190 |
191 |
192 | float taylorInvSqrt(float r)
193 | {
194 | return 1.79284291400159 - 0.85373472095314 * r;
195 | }
196 |
197 | vec4 grad4(float j, vec4 ip)
198 | {
199 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0);
200 | vec4 p,s;
201 |
202 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0;
203 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz);
204 | s = vec4(lessThan(p, vec4(0.0)));
205 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www;
206 |
207 | return p;
208 | }
209 |
210 | float snoise(vec4 v)
211 | {
212 | const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4
213 | 0.276393202250021, // 2 * G4
214 | 0.414589803375032, // 3 * G4
215 | -0.447213595499958); // -1 + 4 * G4
216 |
217 | // (sqrt(5) - 1)/4 = F4, used once below
218 | #define F4 0.309016994374947451
219 |
220 | // First corner
221 | vec4 i = floor(v + dot(v, vec4(F4)) );
222 | vec4 x0 = v - i + dot(i, C.xxxx);
223 |
224 | // Other corners
225 |
226 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI)
227 | vec4 i0;
228 | vec3 isX = step( x0.yzw, x0.xxx );
229 | vec3 isYZ = step( x0.zww, x0.yyz );
230 | // i0.x = dot( isX, vec3( 1.0 ) );
231 | i0.x = isX.x + isX.y + isX.z;
232 | i0.yzw = 1.0 - isX;
233 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) );
234 | i0.y += isYZ.x + isYZ.y;
235 | i0.zw += 1.0 - isYZ.xy;
236 | i0.z += isYZ.z;
237 | i0.w += 1.0 - isYZ.z;
238 |
239 | // i0 now contains the unique values 0,1,2,3 in each channel
240 | vec4 i3 = clamp( i0, 0.0, 1.0 );
241 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 );
242 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 );
243 |
244 | // x0 = x0 - 0.0 + 0.0 * C.xxxx
245 | // x1 = x0 - i1 + 1.0 * C.xxxx
246 | // x2 = x0 - i2 + 2.0 * C.xxxx
247 | // x3 = x0 - i3 + 3.0 * C.xxxx
248 | // x4 = x0 - 1.0 + 4.0 * C.xxxx
249 | vec4 x1 = x0 - i1 + C.xxxx;
250 | vec4 x2 = x0 - i2 + C.yyyy;
251 | vec4 x3 = x0 - i3 + C.zzzz;
252 | vec4 x4 = x0 + C.wwww;
253 |
254 | // Permutations
255 | i = mod289(i);
256 | float j0 = permute( permute( permute( permute(i.w) + i.z) + i.y) + i.x);
257 | vec4 j1 = permute( permute( permute( permute (
258 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 ))
259 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 ))
260 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 ))
261 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 ));
262 |
263 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope
264 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289.
265 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ;
266 |
267 | vec4 p0 = grad4(j0, ip);
268 | vec4 p1 = grad4(j1.x, ip);
269 | vec4 p2 = grad4(j1.y, ip);
270 | vec4 p3 = grad4(j1.z, ip);
271 | vec4 p4 = grad4(j1.w, ip);
272 |
273 | // Normalise gradients
274 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
275 | p0 *= norm.x;
276 | p1 *= norm.y;
277 | p2 *= norm.z;
278 | p3 *= norm.w;
279 | p4 *= taylorInvSqrt(dot(p4,p4));
280 |
281 | // Mix contributions from the five corners
282 | vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0);
283 | vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0);
284 | m0 = m0 * m0;
285 | m1 = m1 * m1;
286 | return 49.0 * ( dot(m0*m0, vec3( dot( p0, x0 ), dot( p1, x1 ), dot( p2, x2 )))
287 | + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ;
288 |
289 | }
290 |
--------------------------------------------------------------------------------
/shaders/ss-collisions.glsl:
--------------------------------------------------------------------------------
1 |
2 | const int colliders = @COLLIDERS;
3 |
4 | uniform sampler2D t_oPos;
5 | uniform sampler2D t_pos;
6 |
7 | uniform float dT;
8 | uniform float radius;
9 | uniform vec3 colliderPositions[ colliders ];
10 |
11 | uniform vec2 resolution;
12 |
13 | $rand
14 |
15 | void main(){
16 |
17 | vec2 uv = gl_FragCoord.xy / resolution;
18 | vec4 oPos = texture2D( t_oPos , uv );
19 | vec4 pos = texture2D( t_pos , uv );
20 |
21 | float life = pos.w;
22 |
23 | vec3 vel = pos.xyz - oPos.xyz;
24 |
25 |
26 | life -= .01 * ( rand( uv ) + .1 );
27 |
28 | if( life > 1. ){
29 |
30 | vel = vec3( 0. );
31 | float r = (rand( uv * 100. )-.5) * 100.;
32 | float r2 = (rand( uv * 50. )-.5) * 100.;
33 | pos.xyz = vec3( r , 100. , r2 );
34 | life = .99;
35 |
36 | }
37 |
38 | if( life < 0. ){
39 |
40 | life = 1.1;
41 | vel = vec3( 0. );
42 | float r = (rand( uv * 100. )-.5) * 100.;
43 | float r2 = (rand( uv * 50. )-.5) * 100.;
44 | pos.xyz = vec3( r , 100. , r2 );
45 |
46 |
47 | }
48 |
49 |
50 | vel += vec3( 0. , -.002 , 0. );
51 |
52 |
53 | for( int i = 0; i < colliders; i++ ){
54 |
55 | vec3 dif = colliderPositions[ i ] - pos.xyz;
56 |
57 | if( length( dif ) < radius ){
58 |
59 | vel -= normalize(dif) * .1;
60 |
61 | }
62 |
63 | }
64 |
65 |
66 | vel *= .99; // dampening
67 |
68 | vec3 p = pos.xyz + vel;
69 |
70 | gl_FragColor = vec4( p , life );
71 |
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/shaders/ss-curl.glsl:
--------------------------------------------------------------------------------
1 | uniform sampler2D t_oPos;
2 | uniform sampler2D t_pos;
3 |
4 | uniform float dT;
5 | uniform float noiseSize;
6 | uniform vec2 resolution;
7 |
8 | varying vec2 vUv;
9 |
10 | $simplex
11 | $curl
12 |
13 |
14 | float rand(vec2 co){
15 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
16 | }
17 |
18 |
19 | void main(){
20 |
21 | vec2 uv = gl_FragCoord.xy / resolution;
22 | vec4 oPos = texture2D( t_oPos , uv );
23 | vec4 pos = texture2D( t_pos , uv );
24 |
25 | vec3 vel = pos.xyz - oPos.xyz;
26 |
27 | vec3 curl = curlNoise( pos.xyz * noiseSize );
28 |
29 | vel += curl * .01;
30 | vel *= .97; // dampening
31 |
32 | vec3 p = pos.xyz + vel;
33 |
34 |
35 | gl_FragColor = vec4( p , 1. );
36 |
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/shaders/ss-curlFront.glsl:
--------------------------------------------------------------------------------
1 | uniform sampler2D t_oPos;
2 | uniform sampler2D t_pos;
3 |
4 | uniform float dT;
5 | uniform float noiseSize;
6 | uniform vec2 resolution;
7 |
8 | varying vec2 vUv;
9 |
10 | $simplex
11 | $curl
12 |
13 |
14 | float rand(vec2 co){
15 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
16 | }
17 |
18 |
19 | void main(){
20 |
21 | vec2 uv = gl_FragCoord.xy / resolution;
22 | vec4 oPos = texture2D( t_oPos , uv );
23 | vec4 pos = texture2D( t_pos , uv );
24 |
25 | vec3 vel = pos.xyz - oPos.xyz;
26 |
27 | vec3 curl = curlNoise( pos.xyz * noiseSize );
28 |
29 | vel += curl * .0001;
30 | vel *= .97; // dampening
31 |
32 | vec3 p = pos.xyz + vel;
33 |
34 |
35 | gl_FragColor = vec4( p , 1. );
36 |
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/shaders/ss-flocking.glsl:
--------------------------------------------------------------------------------
1 | // Based off: http://threejs.org/examples/#webgl_gpgpu_birds
2 |
3 | uniform vec2 resolution;
4 | uniform float time;
5 | uniform float testing;
6 | uniform float dT; // about 0.016
7 | uniform float seperationDistance; // 20
8 | uniform float alignmentDistance; // 40
9 | uniform float cohesionDistance; //
10 | uniform float maxVel;
11 | uniform float centerPower;
12 | uniform float forceMultiplier;
13 |
14 | uniform vec3 predator;
15 | uniform float predatorRepelPower;
16 | uniform float predatorRepelRadius;
17 | uniform float centerForce;
18 |
19 | uniform sampler2D t_pos;
20 | uniform sampler2D t_oPos;
21 |
22 | const int width = @SIZE;
23 | const int height = width;
24 |
25 | const float PI = 3.141592653589793;
26 | const float PI_2 = PI * 2.0;
27 | // const float VISION = PI * 0.55;
28 |
29 | float zoneRadius = 40.0;
30 | float zoneRadiusSquared = zoneRadius * zoneRadius;
31 |
32 | float separationThresh = 0.45;
33 | float alignmentThresh = 0.65;
34 |
35 |
36 | const float UPPER_BOUNDS = 400.0;
37 | const float LOWER_BOUNDS = -UPPER_BOUNDS;
38 |
39 | float rand(vec2 co){
40 | return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
41 | }
42 |
43 | void main() {
44 |
45 | zoneRadius = seperationDistance + alignmentDistance + cohesionDistance;
46 | separationThresh = seperationDistance / zoneRadius;
47 | alignmentThresh = ( seperationDistance + alignmentDistance ) / zoneRadius;
48 | zoneRadiusSquared = zoneRadius * zoneRadius;
49 |
50 |
51 | vec2 uv = gl_FragCoord.xy / resolution.xy;
52 | vec3 fPos, fVel;
53 |
54 | vec4 sample = texture2D( t_pos, uv );
55 | vec3 pos = sample.xyz;
56 | float whichCoral = sample.w;
57 | vec3 oPos = texture2D( t_oPos, uv ).xyz;
58 |
59 | vec3 vel = pos - oPos;
60 |
61 | vec3 force = vec3( 0. );
62 |
63 | float dist;
64 | vec3 dir; // direction
65 | float distSquared;
66 |
67 | float seperationSquared = seperationDistance * seperationDistance;
68 | float cohesionSquared = cohesionDistance * cohesionDistance;
69 |
70 | float f;
71 | float percent;
72 |
73 |
74 | // Attract flocks to the center
75 | vec3 central = predator;
76 | dir = pos - central;
77 | dist = length( dir );
78 | //dir.y *= 2.5;
79 | force -= normalize( dir ) * dist * dist * centerPower * .0001;
80 |
81 | // Moves Flock from center
82 | if( dist < predatorRepelRadius ){
83 |
84 | force += normalize( dir ) * predatorRepelPower;
85 |
86 |
87 | }
88 |
89 |
90 | // Checking information of other birds
91 | // This is something that Older GPUs REALLLY hate!
92 |
93 | for ( int y = 0; y < height; y++ ) {
94 | for ( int x = 0; x < width; x++ ) {
95 | if ( float(x) == gl_FragCoord.x && float(y) == gl_FragCoord.y ) continue;
96 |
97 | vec2 lookup = vec2( float(x) / resolution.x, float(y) / resolution.y ) ;
98 | fPos = texture2D( t_pos, lookup ).xyz;
99 |
100 | dir = fPos - pos;
101 | dist = length(dir);
102 | distSquared = dist * dist;
103 |
104 | if ( dist > 0.0 && distSquared < zoneRadiusSquared ) {
105 |
106 | percent = distSquared / zoneRadiusSquared;
107 |
108 | if ( percent < separationThresh ) { // low
109 |
110 | // Separation - Move apart for comfort
111 | f = (separationThresh / percent - 1.0);
112 | force -= normalize(dir) * f;
113 |
114 | } else if ( percent < alignmentThresh ) { // high
115 |
116 | // Alignment - fly the same direction
117 | float threshDelta = alignmentThresh - separationThresh;
118 | float adjustedPercent = ( percent - separationThresh ) / threshDelta;
119 |
120 |
121 | vec3 oFPos = texture2D( t_oPos, lookup ).xyz;
122 |
123 | fVel = fPos - oFPos;
124 |
125 | f = ( 0.5 - cos( adjustedPercent * PI_2 ) * 0.5 + 0.5 );
126 | force += normalize(fVel) * f;
127 |
128 | } else {
129 |
130 | // Attraction / Cohesion - move closer
131 | float threshDelta = 1.0 - alignmentThresh;
132 | float adjustedPercent = ( percent - alignmentThresh ) / threshDelta;
133 |
134 | f = ( 0.5 - ( cos( adjustedPercent * PI_2 ) * -0.5 + 0.5 ) );
135 |
136 | force += normalize(dir) * f;
137 |
138 | }
139 |
140 | }
141 |
142 | }
143 |
144 | }
145 |
146 | vel += force * forceMultiplier * dT;
147 |
148 | vel *= .95; // dampening
149 |
150 | // Speed Limits
151 | if ( length( vel ) > maxVel ) {
152 | vel = normalize( vel ) * maxVel;
153 | }
154 |
155 | gl_FragColor = vec4( pos + vel, 1.);
156 |
157 | }
158 |
--------------------------------------------------------------------------------
/shaders/ss-gravity.glsl:
--------------------------------------------------------------------------------
1 | uniform sampler2D t_oPos;
2 | uniform sampler2D t_pos;
3 |
4 | uniform vec2 resolution;
5 |
6 | uniform float dT;
7 | uniform vec3 centerPos;
8 |
9 | void main(){
10 |
11 | vec2 uv = gl_FragCoord.xy / resolution;
12 | vec4 oPos = texture2D( t_oPos , uv );
13 | vec4 pos = texture2D( t_pos , uv );
14 |
15 | vec3 vel = pos.xyz - oPos.xyz;
16 |
17 | vec3 force = vec3( 0. );
18 |
19 | vec3 dif = pos.xyz - centerPos;
20 |
21 | force -= length( dif ) * length( dif ) * normalize( dif ) * .01;
22 |
23 |
24 | vel += force * dT;
25 |
26 | vec3 p = pos.xyz + vel;
27 |
28 |
29 | gl_FragColor = vec4( p , 1. );
30 |
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/shaders/ss-text.glsl:
--------------------------------------------------------------------------------
1 | uniform sampler2D t_to;
2 | uniform sampler2D t_oPos;
3 | uniform sampler2D t_pos;
4 |
5 | uniform float timer;
6 | uniform vec3 speed;
7 | uniform mat4 cameraMat;
8 | uniform vec3 cameraPos;
9 | uniform vec3 offsetPos;
10 | uniform vec3 handPos;
11 | uniform float alive;
12 | uniform float repelForce;
13 | uniform float distToCam;
14 | uniform float noiseSize;
15 |
16 | uniform vec3 pagePos;
17 |
18 | uniform vec3 gRepelPos[ 4 ];
19 | uniform vec3 repelPos[ 20 ];
20 |
21 | varying vec2 vUv;
22 |
23 | $simplex
24 | $curl
25 |
26 | void main(){
27 |
28 | vec4 oPos = texture2D( t_oPos , vUv );
29 | vec4 pos = texture2D( t_pos , vUv );
30 | vec4 to = texture2D( t_to , vUv );
31 |
32 | vec2 offset = vec2( timer * 100. , timer * 10. );
33 | float displace = 1. * snoise( (to.xy + offset ) * .001 );
34 | //float displace = snoise( to.xy * .1 );
35 |
36 | // vec4 rotPos = vec4( to.x , to.y , -5.
37 |
38 | //vec3 newTo = ( cameraMat * vec4( to.xyz-cameraPos , 1. ) ).xyz; //+ vec3( 0. , 0. , displace * 20. );
39 |
40 | //vec3 newTo = (cameraMat * vec4( cameraPos , 1. ) ).xyz;
41 | //vec3 newTo = (cameraMat * vec4( 0.0 , 0.0 , 1.0 , 1.)).xyz;
42 |
43 | vec3 newDir = (cameraMat * vec4( 0. , 0. , 1. , 0. )).xyz;
44 |
45 | normalize( newDir );
46 | vec3 newTo1 = ( cameraMat * vec4( to.xyz , 0. ) ).xyz;
47 | vec3 newTo = newTo1 + (cameraPos+offsetPos) - newDir * distToCam - 20. * newDir * displace;
48 | /* vec3 newP = cameraPos - offsetPos;
49 | newTo = -(normalize(cameraPos.xyz) * distToCam ) + (cameraMat * vec4( to.xyz + newP , 1. )).xyz;*/
50 |
51 | //newTo += cameraPos;
52 | vec3 vel = pos.xyz - oPos.xyz;
53 | vec3 dif = newTo.xyz - pos.xyz;
54 |
55 |
56 | //vec3 vel
57 |
58 | // dif.y += speed.y * 1.;
59 |
60 |
61 | vel += dif * .2 ;
62 |
63 | /* vec3 toHand = pos.xyz-handPos;
64 | float distToHand = length( toHand );
65 | vel += normalize(toHand) * 1000. / (distToHand);*/
66 |
67 | for( int i = 0; i < 20; i++ ){
68 | //if( repelPos[i] != vec3( 0. , 0. , 0. )){
69 | vec3 toFriend = (repelPos[i] - pos.xyz) + pagePos;
70 | vel -= normalize(toFriend) * repelForce / (length( toFriend * toFriend));
71 | //}
72 | }
73 |
74 | for( int i = 0; i < 4; i++ ){
75 | //if( repelPos[i] != vec3( 0. , 0. , 0. )){
76 | vec3 toFriend = gRepelPos[i] - pos.xyz;
77 | vel -= normalize(toFriend) * repelForce / (length( toFriend * toFriend));
78 | //}
79 | }
80 |
81 |
82 |
83 | //vel.y += abs( displace ) * speed.y;
84 | //vel.y += (((displace * .4)+.5)/5.) * ( speed.y ) ;
85 | //vel.y += (((abs(displace) * .2)+.1)/3.) * speed.y;
86 |
87 |
88 | vec3 newPos = vec3(1.);
89 |
90 | if( alive < 1.5){
91 |
92 | if( alive > .5 ){
93 |
94 | newPos = pos.xyz + vel * (( displace *displace+ 2.)/10.);
95 |
96 | }else{
97 |
98 | vec3 curlForce = curlNoise( pos.xyz * noiseSize );
99 | newPos = pos.xyz - curlForce * 1.1;
100 |
101 | }
102 |
103 | }else{
104 |
105 | newPos = newTo.xyz;
106 |
107 | }
108 | //newPos.z = displace * 5.;
109 |
110 | gl_FragColor= vec4( newPos , displace * 10. );
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/shaders/vs-lookup.glsl:
--------------------------------------------------------------------------------
1 | uniform sampler2D t_pos;
2 |
3 | void main(){
4 |
5 | vec4 pos = texture2D( t_pos , position.xy );
6 |
7 | vec3 dif = cameraPosition - pos.xyz;
8 |
9 | gl_PointSize = min( 5. , 50. / length( dif ));
10 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos.xyz , 1. );
11 |
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/shaders/vs-lookupFront.glsl:
--------------------------------------------------------------------------------
1 | uniform sampler2D t_pos;
2 |
3 | varying vec3 vPos;
4 | void main(){
5 |
6 | vec4 pos = texture2D( t_pos , position.xy );
7 |
8 | vec3 dif = cameraPosition - pos.xyz;
9 |
10 | gl_PointSize = min( 5. , 50. / length( dif ));
11 |
12 | vPos = ( modelMatrix * vec4( pos.xyz , 1. ) ).xyz;
13 |
14 | gl_Position = projectionMatrix * modelViewMatrix * vec4( pos.xyz , 1. );
15 |
16 |
17 | }
18 |
--------------------------------------------------------------------------------