/* * Gray-Scott * * A solver of the Gray-Scott model of reaction diffusion. * * ©2012 pmneila. * p.mneila at upm.es */ (function(){ // Canvas. var canvas; var canvasQ; var canvasWidth; var canvasHeight; var mMouseX, mMouseY; var mMouseDown = false; var mRenderer; var mScene; var mCamera; var mUniforms; var mColors; var mColorsNeedUpdate = true; var mLastTime = 0; var mTexture1, mTexture2; var mGSMaterial, mScreenMaterial; var mScreenQuad; var mToggled = false; var mMinusOnes = new THREE.Vector2(-1, -1); // Some presets. var presets = [ { feed: 0.031, kill: 0.057 }, { feed: 0.031, kill: 0.058 }, { feed: 0.031, kill: 0.060 }, { feed: 0.033, kill: 0.058 }, { feed: 0.033, kill: 0.059 }, { feed: 0.033, kill: 0.061 }, { feed: 0.035, kill: 0.059 }, { feed: 0.035, kill: 0.060 }, { feed: 0.035, kill: 0.061 } ]; // Configuration. var feed = presets[0].feed; var kill = presets[0].kill; var noAnimation = false; skipWebGL = function(){ var mobile = /Android|Mobile|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); var noWebgl = false; var canvas; var ctx; try { canvas = document.createElement('canvas'); ctx = canvas.getContext('webgl') || canvas.getContext('experimental-webgl'); } catch (e) { console && "log" in console && console.log("No webgl"); } if (ctx == undefined) { noWebgl = true; } if (noWebgl || mobile) { return true; } return false } init = function() { if( skipWebGL() ){ $("canvas").hide(); $("#replacementImage").show() noAnimation = true return } canvasQ = $('#myCanvas'); canvas = canvasQ.get(0); init_controls(); canvas.onmousedown = onMouseDown; canvas.onmouseup = onMouseUp; canvas.onmousemove = onMouseMove; mRenderer = new THREE.WebGLRenderer({canvas: canvas, preserveDrawingBuffer: true}); mScene = new THREE.Scene(); mCamera = new THREE.OrthographicCamera(-0.5, 0.5, 0.5, -0.5, 0, 100); mCamera.position.z = 100; mCamera.scale.x = mCamera.scale.y = 0.99985 ; mScene.add(mCamera); // For debugging purpose window.mCamera = mCamera; mUniforms = { screenWidth: {type: "f", value: undefined}, screenHeight: {type: "f", value: undefined}, tSource: {type: "t", value: undefined}, delta: {type: "f", value: 0.0}, feed: {type: "f", value: feed}, kill: {type: "f", value: kill}, brush: {type: "v2", value: new THREE.Vector2(-10, -10)}, color1: {type: "v4", value: new THREE.Vector4(0, 0, 0.0, 0)}, color2: {type: "v4", value: new THREE.Vector4(0, 1, 0, 0.2)}, color3: {type: "v4", value: new THREE.Vector4(1, 1, 0, 0.21)}, color4: {type: "v4", value: new THREE.Vector4(1, 0, 0, 0.4)}, color5: {type: "v4", value: new THREE.Vector4(1, 1, 1, 0.6)} }; mColors = [mUniforms.color1, mUniforms.color2, mUniforms.color3, mUniforms.color4, mUniforms.color5]; $("#gradient").gradient("setUpdateCallback", onUpdatedColor); mGSMaterial = new THREE.ShaderMaterial({ uniforms: mUniforms, vertexShader: document.getElementById('standardVertexShader').textContent, fragmentShader: document.getElementById('gsFragmentShader').textContent, }); mScreenMaterial = new THREE.ShaderMaterial({ uniforms: mUniforms, vertexShader: document.getElementById('standardVertexShader').textContent, fragmentShader: document.getElementById('screenFragmentShader').textContent, }); var plane = new THREE.PlaneGeometry(1.0, 1.0); mScreenQuad = new THREE.Mesh(plane, mScreenMaterial); mScene.add(mScreenQuad); mColorsNeedUpdate = true; resize(canvas.clientWidth, canvas.clientHeight); render(0); mUniforms.brush.value = new THREE.Vector2(0.5, 0.5); mLastTime = new Date().getTime(); requestAnimationFrame(render); } var resize = function(width, height) { // Set the new shape of canvas. canvasQ.width(width); canvasQ.height(height); // Get the real size of canvas. canvasWidth = canvasQ.width(); canvasHeight = canvasQ.height(); mRenderer.setSize(canvasWidth, canvasHeight); // TODO: Possible memory leak? mTexture1 = new THREE.WebGLRenderTarget(canvasWidth/2, canvasHeight/2, {minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, type: THREE.FloatType}); mTexture2 = new THREE.WebGLRenderTarget(canvasWidth/2, canvasHeight/2, {minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, format: THREE.RGBAFormat, type: THREE.FloatType}); mTexture1.wrapS = THREE.RepeatWrapping; mTexture1.wrapT = THREE.RepeatWrapping; mTexture2.wrapS = THREE.RepeatWrapping; mTexture2.wrapT = THREE.RepeatWrapping; mUniforms.screenWidth.value = canvasWidth/2; mUniforms.screenHeight.value = canvasHeight/2; } var render = function(time) { var dt = (time - mLastTime)/20.0; if(dt > 0.8 || dt<=0) dt = 0.8; mLastTime = time; mScreenQuad.material = mGSMaterial; mUniforms.delta.value = dt; mUniforms.feed.value = feed; mUniforms.kill.value = kill; for(var i=0; i<8; ++i) { if(!mToggled) { mUniforms.tSource.value = mTexture1; mRenderer.render(mScene, mCamera, mTexture2, true); mUniforms.tSource.value = mTexture2; } else { mUniforms.tSource.value = mTexture2; mRenderer.render(mScene, mCamera, mTexture1, true); mUniforms.tSource.value = mTexture1; } mToggled = !mToggled; mUniforms.brush.value = mMinusOnes; } if(mColorsNeedUpdate) updateUniformsColors(); mScreenQuad.material = mScreenMaterial; mRenderer.render(mScene, mCamera); requestAnimationFrame(render); } loadPreset = function(idx) { feed = presets[idx].feed; kill = presets[idx].kill; worldToForm(); } var updateUniformsColors = function() { var values = $("#gradient").gradient("getValuesRGBS"); for(var i=0; i= mWord[currentLetterIndex].length - 1 ) { nexPos = mWord[currentLetterIndex][0] }else{ nexPos = mWord[currentLetterIndex][currentPositionIndex +1] } // Let's get the line to draw X and Y vector components vectX = nexPos[0] - curPos[0] vectY = nexPos[1] - curPos[1] // Let's move along the vector on the line angle = Math.atan2( vectY, vectX ) moveX = Math.cos(angle) * simSpeed moveY = Math.sin(angle) * simSpeed curX += moveX curY += moveY // Draw the point mUniforms.brush.value = new THREE.Vector2(curX/canvasWidth, 1-curY/canvasHeight); // Is line fully drawn? var finishedPos = false if (( moveX > 0 && curX > nexPos[0]) || ( moveX < 0 && curX < nexPos[0]) || ( moveY > 0 && curY > nexPos[1]) || ( moveY < 0 && curY < nexPos[1]) ){ finishedPos = true } // Change position and letter if necessary if( finishedPos ){ // Get new letter if it was the last line if( currentPositionIndex >= mWord[currentLetterIndex].length - 1 ){ currentLetterIndex = currentLetterIndex == (mWord.length - 1) ? 0 : currentLetterIndex + 1 currentPositionIndex = 0 // Randomize preset key = parseInt( Math.random() * presets.length) newPreset = presets[key] feed= newPreset.feed kill = newPreset.kill // mCamera.left = - 0.501 // mCamera.right = 0.501 // Just a new line of the letter }else{ currentPositionIndex++ } // Force new position point curX = mWord[currentLetterIndex][currentPositionIndex][0] curY = mWord[currentLetterIndex][currentPositionIndex][1] } var factor = 0.000002 var positionBoundary = 0.00005 randX = (Math.random() - 0.5 ) * factor; randY = (Math.random() - 0.5 ) * factor; randS = (Math.random() - 0.5 ) * factor * 5 ; if( mCamera.position.x < -positionBoundary ){ mCamera.position.x = -positionBoundary; }else if( mCamera.position.x > positionBoundary ){ mCamera.position.x = positionBoundary; }else if(mCamera.position.y < -positionBoundary ){ mCamera.position.y = -positionBoundary; } else if(mCamera.position.y > positionBoundary ){ mCamera.position.y = positionBoundary; } mCamera.position.x += randX; mCamera.position.y += randY; if( mCamera.scale.x < 0.9997 ){ mCamera.scale.x = 0.9997 }else if(mCamera.scale.x >= 1.0003 ){ mCamera.scale.x = 1.0003; } mCamera.scale.x += randS; mCamera.scale.y += randS ; mCamera.updateProjectionMatrix(); } setInterval( simulateClick, 1 ) })(); $(function(){ $(".close").bind("click",function(){ $(this).parent().toggle(); }) $("#format_show").bind("click",function(){ $("#format").toggle(); }) })