├── .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 | 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 | 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 |

Uniforms

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;  // Current Position Texture
123 | uniform sampler2D t_oPos; // Old Position Texture
124 | uniform vec2 resolution;  // Resolution of simulation
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 |
Uniforms
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 |

Assigning Uniforms

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 |
Setting a single uniform
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 |
Setting Multiple Uniforms
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 |
// Resets positions in a random box of side length 5
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 |     //makes some weird sin based positions
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 | --------------------------------------------------------------------------------