/* Copyright (c) 2015 saharan * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation * files (the "Software"), to deal in the Software * without restriction, including without limitation the rights to use, copy, * modify, merge, * publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to * whom the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or * substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR * PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ package { import com.adobe.utils.*; import flash.display.*; import flash.display3D.*; import flash.display3D.textures.*; import flash.events.*; import flash.net.*; import flash.geom.*; import flash.ui.*; import flash.utils.*; /** * GPU Liquid Timer * @author saharan */ [SWF(width = "384", height = "512", frameRate = "60")] public class GPULiqTimer extends Sprite { private var pmouseX:Number; private var pmouseY:Number; private var pressed:Boolean; private var s3d:Stage3D; private var c3d:Context3D; private var vof:Texture; private var vof2:Texture; private var vofBlurred:Texture; private var vel:Texture; private var vel2:Texture; private var velLimit:Texture; private var prs:Texture; private var prs2:Texture; private var tension:Texture; private var tensionSharp:Texture; private var poissonDataTarget:Texture; private var poissonDataWeight:Texture; private var poissonData:Texture; private var state:Texture; private var massData:Texture; private var flow:Texture; private var flow2:Texture; private var vofTarget:Texture; private var displayShader:ShaderProgram; private var calcMassShader:ShaderProgram; private var addForceShader:ShaderProgram; private var calcDivShader:ShaderProgram; private var calcWeightShader:ShaderProgram; private var calcDenomShader:ShaderProgram; private var normalizePrsShader:ShaderProgram; private var solveShader:ShaderProgram; private var applyPrsShader:ShaderProgram; private var blurVofShader:ShaderProgram; private var scalingShader:ShaderProgram; private var calcSurfaceTensionShader:ShaderProgram; private var calcSurfaceTensionSharpShader:ShaderProgram; private var applySurfaceTensionShader:ShaderProgram; private var advectVofShader:ShaderProgram; private var advectVel1Shader:ShaderProgram; private var advectVel2Shader:ShaderProgram; private var calcDensityShader:ShaderProgram; private var dampPrsCShader:ShaderProgram; private var solveCShader:ShaderProgram; private var calcFlowShader:ShaderProgram; private var calcFlowCoeffShader:ShaderProgram; private var constrainFlowShader:ShaderProgram; private var applyFlowShader:ShaderProgram; private var scaleVofShader:ShaderProgram; private var gravityRev:Boolean; private var go:Boolean = true; private var vofTargetInitialized:Boolean = false; [Embed(source = "wall.gif")] private const WALL:Class; private var vtxB:VertexBuffer3D; private var idxB:IndexBuffer3D; private const texSize:int = 256; private const scrSize:int = 512; private var rotation:Number; private var rotationPrev:int; private var rotationNext:int; private var rotationEasingCount:int; public function GPULiqTimer() { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null): void { removeEventListener(Event.ADDED_TO_STAGE, init); stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:Event):void { pressed = true; go = true; } ); stage.addEventListener(MouseEvent.MOUSE_UP, function(e:Event):void { pressed = false; }); stage.addEventListener(KeyboardEvent.KEY_DOWN, function(e:KeyboardEvent):void { switch (e.keyCode) { case Keyboard.SPACE: if (rotationEasingCount == 0) { rotationEasingCount = 1; gravityRev = !gravityRev; rotationPrev = rotationNext & 1; rotationNext = rotationPrev + 1; } break; } }); s3d = stage.stage3Ds[0]; s3d.addEventListener(Event.CONTEXT3D_CREATE, start); s3d.requestContext3D(Context3DRenderMode.AUTO, Context3DProfile.STANDARD_CONSTRAINED); } private function start(e:Event):void { c3d = s3d.context3D; c3d.enableErrorChecking = true; c3d.configureBackBuffer(scrSize / 4 * 3, scrSize, 0, false); c3d.setCulling(Context3DTriangleFace.NONE); // vof = createTex(texSize); vof2 = createTex(texSize); vofBlurred = createTex(texSize); vel = createTex(texSize); vel2 = createTex(texSize); velLimit = createTex(texSize, true, false); prs = createTex(texSize); prs2 = createTex(texSize); tension = createTex(texSize, true); tensionSharp = createTex(texSize, true); poissonDataTarget = createTex(texSize); poissonDataWeight = createTex(texSize); poissonData = createTex(texSize); state = createTex(texSize); massData = createTex(texSize); flow = createTex(texSize); flow2 = createTex(texSize); vofTarget = createTex(texSize); reset(); idxB = c3d.createIndexBuffer(6); idxB.uploadFromVector(Vector.([ 0, 1, 2, 1, 3, 2, ]), 0, 6); vtxB = c3d.createVertexBuffer(4, 5); vtxB.uploadFromVector(Vector.([ -1, 1, 0, 0, 0, 1, 1, 0, 1, 0, -1, -1, 0, 0, 1, 1, -1, 0, 1, 1, ]), 0, 4); displayShader = new ShaderProgram(c3d, vertexShader, displayFragmentShader); calcMassShader = new ShaderProgram(c3d, vertexShader, calcMassFragmentShader); addForceShader = new ShaderProgram(c3d, vertexShader, addForceFragmentShader); calcDivShader = new ShaderProgram(c3d, vertexShader, calcDivFragmentShader); calcWeightShader = new ShaderProgram(c3d, vertexShader, calcWeightFragmentShader); calcDenomShader = new ShaderProgram(c3d, vertexShader, calcDenomFragmentShader); normalizePrsShader = new ShaderProgram(c3d, vertexShader, normalizePrsFragmentShader); solveShader = new ShaderProgram(c3d, vertexShader, solveFragmentShader); applyPrsShader = new ShaderProgram(c3d, vertexShader, applyPrsFragmentShader); blurVofShader = new ShaderProgram(c3d, vertexShader, blurVofFragmentShader); scalingShader = new ShaderProgram(c3d, vertexShader, scalingFragmentShader); calcSurfaceTensionShader = new ShaderProgram(c3d, vertexShader, calcSurfaceTensionFragmentShader); calcSurfaceTensionSharpShader = new ShaderProgram(c3d, vertexShader, calcSurfaceTensionSharpFragmentShader); applySurfaceTensionShader = new ShaderProgram(c3d, vertexShader, applySurfaceTensionFragmentShader); advectVofShader = new ShaderProgram(c3d, vertexShader, advectVofFragmentShader); advectVel1Shader = new ShaderProgram(c3d, vertexShader, advectVel1FragmentShader); advectVel2Shader = new ShaderProgram(c3d, vertexShader, advectVel2FragmentShader); calcDensityShader = new ShaderProgram(c3d, vertexShader, calcDensityFragmentShader); dampPrsCShader = new ShaderProgram(c3d, vertexShader, dampPrsCFragmentShader); solveCShader = new ShaderProgram(c3d, vertexShader, solveCFragmentShader); calcFlowShader = new ShaderProgram(c3d, vertexShader, calcFlowFragmentShader); calcFlowCoeffShader = new ShaderProgram(c3d, vertexShader, calcFlowCoeffFragmentShader); constrainFlowShader = new ShaderProgram(c3d, vertexShader, constrainFlowFragmentShader); applyFlowShader = new ShaderProgram(c3d, vertexShader, applyFlowFragmentShader); scaleVofShader = new ShaderProgram(c3d, vertexShader, scaleVofFragmentShader); displayShader.ogsl.setVertexBuffer("position", vtxB, 0, Context3DVertexBufferFormat.FLOAT_3); displayShader.ogsl.setVertexBuffer("texCoord", vtxB, 3, Context3DVertexBufferFormat.FLOAT_2); pmouseX = mouseX; pmouseY = mouseY; // var menu:ContextMenuItem = new ContextMenuItem("Reset"); menu.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, function():void { reset(); }); contextMenu = new ContextMenu(); contextMenu.hideBuiltInItems(); contextMenu.customItems = [menu]; // addEventListener(Event.ENTER_FRAME, frame); } private function createTex(texSize:int, transparent:Boolean = false, floatingPoint:Boolean = true):Texture { var bd:BitmapData = new BitmapData(texSize, texSize, true, transparent ? 0 : 0xff000000); var tex:Texture = c3d.createTexture(texSize, texSize, floatingPoint ? Context3DTextureFormat.RGBA_HALF_FLOAT : Context3DTextureFormat.BGRA, true); tex.uploadFromBitmapData(bd); return tex; } private function reset():void { rotation = 0; rotationEasingCount = 0; rotationNext = 0; gravityRev = false; // var bd:BitmapData = Bitmap(new WALL()).bitmapData; var w:int = bd.width; var h:int = bd.height; var blackBD:BitmapData = new BitmapData(w, h, true, 0xff000000); var vofBD:BitmapData = new BitmapData(w, h, true, 0xff000000); var vofBA:ByteArray = new ByteArray(); var stateBD:BitmapData = new BitmapData(w, h, true, 0xff000000); for (var i:int = 0; i < w; i++) { for (var j:int = 0; j < h; j++) { var vofF:Boolean = false; var vofG:Boolean = false; switch (bd.getPixel(j, i) & 0xffffff) { case 0: stateBD.setPixel(j, i, 0xff0000); break; case 0x0000ff: vofF = true; vofBD.setPixel(j, i, 0xff0000); break; case 0xff0000: vofG = true; vofBD.setPixel(j, i, 0x00ff00); break; } vofBA.writeByte(0); vofBA.writeByte(vofG ? 0xff : 0); vofBA.writeByte(vofF ? 0xff : 0); vofBA.writeByte(0); } } // vof.uploadFromBitmapData(vofBD); vof2.uploadFromBitmapData(vofBD); vel.uploadFromBitmapData(blackBD); prs.uploadFromBitmapData(blackBD); state.uploadFromBitmapData(stateBD); // var maskW:int = w - 1; var maskH:int = h - 1; var velLimitBA:ByteArray = new ByteArray(); for (i = 0; i < w; i++) { for (j = 0; j < h; j++) { if ((stateBD.getPixel(j, i) & 0xffffff) != 0) { velLimitBA.writeByte(0); velLimitBA.writeByte(0); velLimitBA.writeByte(0); velLimitBA.writeByte(0); continue; } var l:int = 0; var r:int = 0; var t:int = 0; var b:int = 0; while (t < 2 && stateBD.getPixel(j, i - t - 1 & maskH) == 0) t++; while (b < 2 && stateBD.getPixel(j, i + b + 1 & maskH) == 0) b++; var canExpand:Boolean = true; while (l < 2) { var y:int = i - t; while (y <= i + b) { if (stateBD.getPixel(j - l - 1 & maskW, y & maskH) != 0) { canExpand = false; break; } y++; } if (!canExpand) break; l++; } canExpand = true; while (r < 2) { y = i - t; while (y <= i + b) { if (stateBD.getPixel(j + r + 1 & maskW, y & maskH) != 0) { canExpand = false; break; } y++; } if (!canExpand) break; r++; } velLimitBA.writeByte(t); velLimitBA.writeByte(r); velLimitBA.writeByte(l); velLimitBA.writeByte(b); } } velLimit.uploadFromByteArray(velLimitBA, 0); } private function flipVofTexture():void { var tmp:Texture = vof2; vof2 = vof; vof = tmp; } private function flipVelTexture():void { var tmp:Texture = vel2; vel2 = vel; vel = tmp; } private function flipPrsTexture():void { var tmp:Texture = prs2; prs2 = prs; prs = tmp; } private function ease(t:Number):Number { t *= 2; if (t < 1) return t * t * 0.5; t -= 1; return (t * (2 - t) + 1) * 0.5; } private function frame(e:Event = null): void { if (rotationEasingCount > 0) { rotationEasingCount++; if (rotationEasingCount == 60) { rotation = rotationNext * Math.PI; rotationEasingCount = 0; } else { rotation = (rotationPrev + ease(rotationEasingCount / 60) * (rotationNext - rotationPrev)) * Math.PI; } } calcMass(); //if (!go) return; go = false; addForce(); calcDiv(); preSolve(); solve(solveShader); applyPrs(); advect(); calcMass(); correct(); display(); // pmouseX = mouseX; pmouseY = mouseY; // } private function calcMass():void { var shader:ShaderProgram = calcMassShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(massData); c3d.clear(); shader.setConsts(); shader.setTex("state", state); shader.setTex("vof", vof); c3d.drawTriangles(idxB); shader.setTex("state", null); shader.setTex("vof", null); } private function addForce():void { var shader:ShaderProgram = addForceShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(vel2); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setConst("gravity", 0.05 * -Math.sin(rotation), 0.05 * Math.cos(rotation)); shader.setConst("rotation", rotation); var scrW:Number = scrSize / 4 * 3; var scrH:Number = scrSize; if (pressed) { shader.setConst("mouse", mouseX / scrW, mouseY / scrH, pmouseX / scrW, pmouseY / scrH); } else { shader.setConst("mouse", -1, -1, -1, -1); } shader.setTex("state", state); shader.setTex("massData", massData); shader.setTex("vel", vel); c3d.drawTriangles(idxB); shader.setTex("state", null); shader.setTex("massData", null); shader.setTex("vel", null); flipVelTexture(); } private function calcDiv():void { var shader:ShaderProgram = calcDivShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(poissonDataTarget); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("vel", vel); c3d.drawTriangles(idxB); shader.setTex("vel", null); } private function preSolve():void { calcWeight(); calcDenom(); normalizePrs(); } private function solve(solveShader:ShaderProgram):void { var shader:ShaderProgram = solveShader; c3d.setProgram(shader.pg); for (var i:int = 0; i < 10; i++) { c3d.setRenderToTexture(prs2); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setConst("turn", i); shader.setTex("prs", prs); shader.setTex("poissonData", poissonData); c3d.drawTriangles(idxB); flipPrsTexture(); } shader.setTex("prs", null); shader.setTex("poissonData", null); } private function applyPrs():void { var shader:ShaderProgram = applyPrsShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(vel2); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("poissonData", poissonData); shader.setTex("prs", prs); shader.setTex("vel", vel); c3d.drawTriangles(idxB); shader.setTex("poissonData", null); shader.setTex("prs", null); shader.setTex("vel", null); flipVelTexture(); } private function calcWeight():void { var shader:ShaderProgram = calcWeightShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(poissonDataWeight); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("massData", massData); c3d.drawTriangles(idxB); shader.setTex("massData", null); } private function calcDenom():void { var shader:ShaderProgram = calcDenomShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(poissonData); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("poissonDataTarget", poissonDataTarget); shader.setTex("poissonDataWeight", poissonDataWeight); c3d.drawTriangles(idxB); shader.setTex("poissonDataTarget", null); shader.setTex("poissonDataWeight", null); } private function normalizePrs():void { var shader:ShaderProgram = normalizePrsShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(prs2); c3d.clear(); shader.setConsts(); shader.setConst("sample", 0.25, 0.25); shader.setTex("prs", prs); c3d.drawTriangles(idxB); shader.setTex("prs", null); flipPrsTexture(); } private function advect():void { blurVof(); calcSurfaceTensionSharp(); if (!vofTargetInitialized) { calcAverageVof(vof, vofTarget); vofTargetInitialized = true; } advectVof(); calcSurfaceTension(); applySurfaceTension(); advectVel(); } private function blurVof():void { var shader:ShaderProgram = blurVofShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(vofBlurred); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("state", state); shader.setTex("massData", massData); c3d.drawTriangles(idxB); shader.setTex("state", null); shader.setTex("massData", null); } private function calcSurfaceTension():void { var shader:ShaderProgram = calcSurfaceTensionShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(tension); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("state", state); shader.setTex("vofBlurred", vofBlurred); c3d.drawTriangles(idxB); shader.setTex("state", null); shader.setTex("vofBlurred", null); } private function calcSurfaceTensionSharp():void { var shader:ShaderProgram = calcSurfaceTensionSharpShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(tensionSharp); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("state", state); shader.setTex("massData", massData); c3d.drawTriangles(idxB); shader.setTex("state", null); shader.setTex("massData", null); } private function applySurfaceTension():void { var shader:ShaderProgram = applySurfaceTensionShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(vel2); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("vel", vel); shader.setTex("massData", massData); shader.setTex("tension", tension); c3d.drawTriangles(idxB); shader.setTex("vel", null); shader.setTex("massData", null); shader.setTex("tension", null); flipVelTexture(); } private function advectVof():void { var shader:ShaderProgram = advectVofShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(vof2); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("vel", vel); shader.setTex("velLimit", velLimit); shader.setTex("vof", vof); shader.setTex("tensionSharp", tensionSharp); c3d.drawTriangles(idxB); shader.setTex("vel", null); shader.setTex("velLimit", null); shader.setTex("vof", null); shader.setTex("tensionSharp", null); flipVofTexture(); } private function advectVel():void { var shader:ShaderProgram = advectVel1Shader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(vel2); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("vel", vel); shader.setTex("velLimit", velLimit); c3d.drawTriangles(idxB); shader.setTex("vel", null); shader.setTex("velLimit", null); shader = advectVel2Shader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(vel); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("vel", vel2); c3d.drawTriangles(idxB); shader.setTex("vel", null); } private function correct():void { calcDensity(); preSolve(); dampPrsC(); solve(solveCShader); calcFlow(); calcFlowCoeff(); constrainFlow(); applyFlow(); normalizeVof(); } private function calcDensity():void { var shader:ShaderProgram = calcDensityShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(poissonDataTarget); c3d.clear(); shader.setConsts(); shader.setTex("state", state); shader.setTex("vof", vof); c3d.drawTriangles(idxB); shader.setTex("state", null); shader.setTex("vof", null); } private function dampPrsC():void { var shader:ShaderProgram = dampPrsCShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(prs2); c3d.clear(); shader.setConsts(); shader.setTex("prs", prs); c3d.drawTriangles(idxB); shader.setTex("prs", null); flipPrsTexture(); } private function calcFlow():void { var shader:ShaderProgram = calcFlowShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(flow); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("massData", massData); shader.setTex("poissonData", poissonData); shader.setTex("prs", prs); c3d.drawTriangles(idxB); shader.setTex("massData", null); shader.setTex("poissonData", null); shader.setTex("prs", null); } private function calcFlowCoeff():void { var shader:ShaderProgram = calcFlowCoeffShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(vof2); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("vof", vof); shader.setTex("flow", flow); c3d.drawTriangles(idxB); shader.setTex("vof", null); shader.setTex("flow", null); } private function constrainFlow():void { var shader:ShaderProgram = constrainFlowShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(flow2); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("flow", flow); shader.setTex("coeff", vof2); c3d.drawTriangles(idxB); shader.setTex("flow", null); shader.setTex("coeff", null); } private function applyFlow():void { var shader:ShaderProgram = applyFlowShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(vof2); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setTex("vof", vof); shader.setTex("flow", flow2); c3d.drawTriangles(idxB); shader.setTex("vof", null); shader.setTex("flow", null); flipVofTexture(); } private function normalizeVof():void { calcAverageVof(vof, vel2); scaleVof(); } private function scaleVof():void { var shader:ShaderProgram = scaleVofShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(vof2); c3d.clear(); shader.setConsts(); shader.setTex("vof", vof); shader.setTex("vofAvgCurrent", vel2); shader.setTex("vofAvgTarget", vofTarget); c3d.drawTriangles(idxB); shader.setTex("vof", null); shader.setTex("vofAvgCurrent", null); shader.setTex("vofAvgTarget", null); flipVofTexture(); } private function calcAverageVof(firstSrc:Texture, finalDst:Texture):void { var shader:ShaderProgram = scalingShader; c3d.setProgram(shader.pg); c3d.setRenderToTexture(flow); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setConst("first", 1); shader.setTex("src", firstSrc); c3d.drawTriangles(idxB); var src:Texture = flow; var dst:Texture = flow2; for (var i:int = 0; i < 7; i++) { c3d.setRenderToTexture(i == 6 ? finalDst : dst); c3d.clear(); shader.setConsts(); shader.setConst("delta", 1 / texSize); shader.setConst("first", 0); shader.setTex("src", src); c3d.drawTriangles(idxB); var tmp:Texture = src; src = dst; dst = tmp; } shader.setTex("src", null); } private function display():void { var shader:ShaderProgram = displayShader; c3d.setProgram(shader.pg); c3d.setRenderToBackBuffer(); c3d.clear(); shader.setConsts(); var delta:Number = 1 / texSize; shader.setConst("delta", delta); shader.setConst("rotation", rotation); shader.setConst("color1", 0.5, 0.8, 1.0); shader.setConst("color2", 1.0, 0.8, 0.9); shader.setConst("color3", 0.5, 1.0, 0.8); shader.setConst("color4", 1.0, 0.9, 0.7); shader.setTex("state", state); shader.setTex("massData", massData); shader.setTex("vofBlurred", vofBlurred); c3d.drawTriangles(idxB); shader.setTex("state", null); shader.setTex("massData", null); shader.setTex("vofBlurred", null); c3d.present(); } } } import com.adobe.utils.AGALMiniAssembler; import com.element.oimo.ogsl.OGSL; import flash.display3D.Context3D; import flash.display3D.Program3D; import flash.display3D.textures.Texture; class ShaderProgram { public var ogsl:OGSL; public var pg:Program3D; private var vec:Vector.; public function ShaderProgram(c3d:Context3D, vertexShaderSource:String, fragmentShaderSource:String) { ogsl = new OGSL(); //ogsl.setLogFunction(function(a:String):void { trace(a) } ); ogsl.compile(vertexShaderSource + " " + fragmentShaderSource); ogsl.setContext3D(c3d); pg = new AGALMiniAssembler().assemble2(c3d, 2, ogsl.getVertexAGAL(), ogsl.getFragmentAGAL()); vec = new Vector.(4, true); } public function setConsts():void { ogsl.setDefaultConstants(); } public function setConst(name:String, x:Number, y:Number = 0, z:Number = 0, w:Number = 0):void { ogsl.setDefaultConstants(); vec[0] = x; vec[1] = y; vec[2] = z; vec[3] = w; ogsl.setFragmentConstantsFromVector(name, vec); } public function setTex(name:String, texture:Texture):void { //trace(name + ": " + ogsl.getTextureIndex(name)); ogsl.setTexture(name, texture); } } var vertexShader:String = ; var displayFragmentShader:String = 0.5 || uvTransformed.y < 0 || uvTransformed.y > 1) { diffuse1 = 1; diffuse2 = 1; specular1 = 0; specular2 = 0; duv1 = 0; duv2 = 0; } var bg:vec3 = bgColor(uv1 / delta + (duv1 + duv2) * 2); return diffuse1 * diffuse2 * bg + specular1 + specular2; } function renderLayer(uv:vec2, color:vec3, layer:float, &outDiffuse:vec3, &outDuv:vec2, &outSpecular:float):void { var ratio:float = ratioSmooth(uv, layer); var dVofBlurred:vec2 = calcDVofBlurred(uv, layer); var duvFluid:vec2; var diffuseFluid:vec3; var specularFluid:float; renderFluid(color, dVofBlurred, duvFluid, diffuseFluid, specularFluid); var dWallBlurred:vec2 = calcDWallBlurred(uv, layer); var duvWall:vec2; var diffuseWall:vec3; var specularWall:float; renderWall(dWallBlurred, duvWall, diffuseWall, specularWall); outDiffuse = mix(diffuseWall, color * diffuseFluid, ratio); outDuv = duvFluid * ratio + duvWall * (ratio - 1); outSpecular = specularFluid * ratio + specularWall * (1 - ratio); } function renderFluid(color:vec3, dVofBlurred:vec2, &outDuv:vec2, &outDiffuse:vec3, &outSpecular:float):void { var normal:vec3 = normalize(vec3(-dVofBlurred, 0.1)); var lightDir:vec3 = normalize(vec3(1.0, 3.0, -1.0)); var lightDirRev:vec3 = normalize(vec3(-0.3, -0.7, -2.0)); var lightDirRev2:vec3 = normalize(vec3(1.2, -0.8, -1.0)); var dotLight:vec3; dotLight.x = -dot(lightDir, vec3(0, 0, -1) + 2 * normal.z * normal); dotLight.y = -dot(lightDirRev, normal); dotLight.z = -dot(lightDirRev2, normal); dotLight = max(0, dotLight); var brightness:float = min(1, 0.4 + dotLight.y * 0.6 + max(0, dotLight.z - 0.5) * 1.5); var specular:float = pow(max(0, dotLight.x - 0.5) * 2, 2) + pow(dot(normal.xy, normal.xy), 12) * 0.5; outDuv = -normal.xy * 2.0; outDiffuse = color * brightness; outSpecular = specular; } function renderWall(dWallBlurred:vec2, &outDuv:vec2, &outDiffuse:vec3, &outSpecular:float):void { var normal:vec3 = normalize(vec3(-dWallBlurred, 0.1)); var lightDir1:vec3 = normalize(vec3(0.2, -0.3, -1)); var lightDir2:vec3 = normalize(vec3(0.4, 1.5, -1)); var dotLight1:float = max(0, -dot(normal, lightDir1)); var dotLight2:float = max(0, -dot(normal, lightDir2)); outDuv = -normal.xy * 2.0; outDiffuse = 0.6 + min(1, dotLight1) * 0.4; outSpecular = pow(dotLight2, 12) * 0.6; } function calcDVofBlurred(uv:vec2, layer:float):vec2 { var vofBlurred:float = vofBlurredSmooth(uv, layer); var vofBlurredR:float = vofBlurredSmooth(uv + vec2(delta, 0), layer); var vofBlurredB:float = vofBlurredSmooth(uv + vec2(0, delta), layer); return vec2(vofBlurredR - vofBlurred, vofBlurredB - vofBlurred); } function calcDWallBlurred(uv:vec2, layer:float):vec2 { var wallBlurred:float = wallBlurredSmooth(uv, layer); var wallBlurredR:float = wallBlurredSmooth(uv + vec2(delta, 0), layer); var wallBlurredB:float = wallBlurredSmooth(uv + vec2(0, delta), layer); return vec2(wallBlurredR - wallBlurred, wallBlurredB - wallBlurred); } function vofBlurredSmooth(uv:vec2, layer:float):float { var val:float = tex2D(this.vofBlurred, transformUV(uv, layer), linear).x; val = smoothstep(0, 1, val); val = smoothstep(0, 1, val); return val; } function ratioSmooth(uv:vec2, layer:float):float { var val:float = tex2D(this.massData, transformUV(uv, layer), linear).x; val = smoothstep(0, 1, val); val = smoothstep(0, 1, val); return val; } function stateSmooth(uv:vec2, layer:float):float { var val:float = tex2D(this.state, transformUV(uv, layer), linear).x; val = smoothstep(0, 1, val); val = smoothstep(0, 1, val); return val; } function wallBlurredSmooth(uv:vec2, layer:float):float { uv = transformUV(uv, layer); var x1:float = uv.x - delta; var x2:float = uv.x; var x3:float = uv.x + delta; var y1:float = uv.y - delta; var y2:float = uv.y; var y3:float = uv.y + delta; var sum:float = tex2D(this.state, vec2(x1, y1), linear).x; sum += tex2D(this.state, vec2(x1, y2), linear).x; sum += tex2D(this.state, vec2(x1, y3), linear).x; sum += tex2D(this.state, vec2(x2, y1), linear).x; sum += tex2D(this.state, vec2(x2, y2), linear).x; sum += tex2D(this.state, vec2(x2, y3), linear).x; sum += tex2D(this.state, vec2(x3, y1), linear).x; sum += tex2D(this.state, vec2(x3, y2), linear).x; sum += tex2D(this.state, vec2(x3, y3), linear).x; sum /= 9; sum = smoothstep(0, 1, sum); sum = smoothstep(0, 1, sum); return sum; } function bgColor(uv:vec2):vec3 { var tileSize:float = 16; var tmp:vec2 = floor(uv.xy / tileSize); var checkerTile:float = 0.6 + mod(tmp.x + tmp.y, 2) * 0.2; var tileColor:vec3; if (checkerTile > 0.7) tileColor = vec3(0.8, 0.85, 0.9); else tileColor = vec3(0.6, 0.65, 0.7); return tileColor; } function transformUV(uv:vec2, layer:float):vec2 { var s:float = sin(rotation); var c:float = cos(rotation); var center:vec2; if (layer < 0.5) center = vec2(0.25, 0.5); else center = vec2(0.75, 0.5); uv -= center; uv = vec2(c * uv.x - s * uv.y, s * uv.x + c * uv.y); return uv + center; } } ]]>; var calcMassFragmentShader:String = 0) { output = vec4(0, 1, 0, 1); } else { vof += 0.00001; var fRatio:float = vof.x / (vof.x + vof.y); var gRatio:float = vof.y / (vof.x + vof.y); var fDensity:float = 1.2; var gDensity:float = 1.0; output = vec4(fRatio, gRatio, 1 / (fRatio * fDensity + gRatio * gDensity), 1); } } } ]]>; var addForceFragmentShader:String = 0) { vel.x = 0; } else { vel.x += force.x; } if (state + stateB > 0) { vel.y = 0; } else { vel.y += force.y; } output = vec4(clamp(vel, -2, 2), 0, 1); } function calcDragForce(uv:vec2, layer:float):vec2 { uv /= delta; var mouse:vec2 = transformMouseUV(this.mouse.xy, layer); var pmouse:vec2 = transformMouseUV(this.mouse.zw, layer); var diff:vec2 = mouse - pmouse; var nearest:vec2 = mouse; var diffLen:float = dot(diff, diff); if (diffLen > 0) { var t:float = dot(diff, uv - pmouse) / diffLen; t = clamp(t, 0, 1); nearest = pmouse + t * (mouse - pmouse); } var dist:float = distance(nearest, uv); var w:float = max(0, 1 - dist / 8); w = smoothstep(0, 1, w); w = smoothstep(0, 1, w); w = smoothstep(0, 1, w); return (mouse - pmouse) * 2 * w; } function transformMouseUV(mouse:vec2, layer:float):vec2 { mouse = vec2(mouse.x * 0.75 - 0.125 + 0.5 * layer, mouse.y); return transformUV(mouse, layer) / delta; } function transformUV(uv:vec2, layer:float):vec2 { var s:float = sin(rotation); var c:float = cos(rotation); var center:vec2; if (layer < 0.5) center = vec2(0.25, 0.5); else center = vec2(0.75, 0.5); uv -= center; uv = vec2(c * uv.x - s * uv.y, s * uv.x + c * uv.y); return uv + center; } } ]]>; var calcDivFragmentShader:String = ; var calcWeightFragmentShader:String = 0) tmp = 1 / tmp; var weightX:float = 2 * invMass * invMassR * tmp; tmp = invMass + invMassB; if (tmp > 0) tmp = 1 / tmp; var weightY:float = 2 * invMass * invMassB * tmp; output = vec4(weightX, weightY, 0, 1); } } ]]>; var calcDenomFragmentShader:String = 0) denom = 1 / denom; output = vec4(target, denom, weight.x, weight.y); } } ]]>; var normalizePrsFragmentShader:String = ; var solveFragmentShader:String = 1) { output = vec4(prs, 0, 1); } else { var sor:float = 1.6; var next:float = (prsL * weightL + prsR * weightR + prsT * weightT + prsB * weightB + target) * denom; var diff:float = next - prs.x; output = vec4(prs.x + diff * sor, prs.y, 0, 1); } } } ]]>; var applyPrsFragmentShader:String = ; var blurVofFragmentShader:String = 0) { output = vec4(0, 1, 0, 1); } else { output = vec4(sumRatio / 49, 0, 1); } } } ]]>; var scalingFragmentShader:String = 0) { if (uv.x > 0.5) { output = vec4(0, 0, src.xy); } else { output = vec4(src.xy, 0, 0); } } else { output = src; } } } ]]>; var calcSurfaceTensionFragmentShader:String = 0) surfaceTensionFactor = 0; output = surfaceTension * surfaceTensionFactor; } } ]]>; var calcSurfaceTensionSharpFragmentShader:String = 0) surfaceTensionFactor = 0; output = surfaceTension * surfaceTensionFactor; } } ]]>; var applySurfaceTensionFragmentShader:String = ; var advectVofFragmentShader:String = ; var advectVel1FragmentShader:String = ; var advectVel2FragmentShader:String = ; var calcDensityFragmentShader:String = 0) { output = vec4(0, 0, 0, 1); } else { output = vec4(1 - vof.x - vof.y, 0, 0, 1); } } } ]]>; var dampPrsCFragmentShader:String = ; var solveCFragmentShader:String = 1) { output = vec4(prs, 0, 1); } else { var sor:float = 1.2; var next:float = (prsL * weightL + prsR * weightR + prsT * weightT + prsB * weightB + target) * denom; var diff:float = next - prs.y; output = vec4(prs.x, prs.y + diff * sor, 0, 1); } } } ]]>; var calcFlowFragmentShader:String = 0) { fv.x = px * ratio.x; gv.x = px * ratio.y; } else { fv.x = px * ratioR.x; gv.x = px * ratioR.y; } if (py > 0) { fv.y = py * ratio.x; gv.y = py * ratio.y; } else { fv.y = py * ratioB.x; gv.y = py * ratioB.y; } output = vec4(fv, gv); } } ]]>; var calcFlowCoeffFragmentShader:String = ; var constrainFlowFragmentShader:String = 0) flow.x *= coeff.x; else flow.x *= coeffR.x; if (flow.y > 0) flow.y *= coeff.x; else flow.y *= coeffB.x; if (flow.z > 0) flow.z *= coeff.y; else flow.z *= coeffR.y; if (flow.w > 0) flow.w *= coeff.y; else flow.w *= coeffB.y; output = flow; } } ]]>; var applyFlowFragmentShader:String = ; var scaleVofFragmentShader:String = 0.5) output = vec4(vof * vofAvgScalingFactor.zw, 0, 1); else output = vec4(vof * vofAvgScalingFactor.xy, 0, 1); } } ]]>;