forked from: Particle Fluid + WaterFilter

by bradsedito forked from Particle Fluid + WaterFilter (diff: 17)
♥0 | Line 306 | Modified 2011-03-02 17:03:08 | MIT License
play

Related images

ActionScript3 source code

/**
 * Copyright bradsedito ( http://wonderfl.net/user/bradsedito )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/zvCt
 */

package {
    import flash.system.LoaderContext;
    import flash.net.URLRequest;
    import flash.utils.*;
    import flash.text.*;
    import flash.filters.*;
    import flash.geom.*;
    import flash.events.*;
    import flash.display.*;
    import net.hires.debug.Stats;
    import mx.utils.Base64Decoder;
    [SWF(frameRate = 60)]
    public class SPH extends Sprite {
        public static const GRAVITY:Number = 0.025;//重力
        public static const RANGE:Number = 7;//影響半径
        public static const RANGE2:Number = RANGE * RANGE;//影響半径の二乗
        public static const DENSITY:Number = 0.2;//流体の密度
        public static const PRESSURE:Number = 2;//圧力係数
        public static const VISCOSITY:Number = 0.09;//粘性係数
        public static const NUM_GRIDS:int = 65;//グリッド数( 465 / RANGE)
        public static const INV_GRID_SIZE:Number = 1/ (465 / NUM_GRIDS);//グリッドサイズの逆数(≒ 1 / RANGE)
        public static const IMG_URL:String = "http://wonderfl.net/static/tmp/related_images/b81d5aeef64984c170b2889044ca03efeccbb04cm";  
        public static const SHADER_DATA:String = "pQEAAACkCwB3YXRlckZpbHRlcqAMbmFtZXNwYWNlAEFJRgCgDHZlbmRvcgBBZG9iZVN5c3RlbXMAoAh2ZXJzaW9uAAIAoAxkZXNjcmlwdGlvbgB3YXRlcgChAQIAAAxfT3V0Q29vcmQAoQEBAAACZGlzdGFuY2UAogFtaW5WYWx1ZQAAAAAAogFkZWZhdWx0VmFsdWUAQsgAAKEBAQAAAWhlaWdodACiAWRlZmF1bHRWYWx1ZQBAQAAAowAEYmFja0ltYWdlAKMBBHdhdGVySW1hZ2UAoQIEAQAPZHN0ADICAIAAAAAAMgIAQL+AAAAdAgAxAAAQAAECADECABAAMAMA8QIAsAEdAgDzAwAbAAMCAPMAAP8AHQMA8wIAGwAyAgCAAAAAADICAEA/gAAAHQIAMQAAEAABAgAxAgAQADAEAPECALABHQIA8wQAGwADAgDzAAD/AB0EAPMCABsAHQIAgAMAAAABAgCAAwBAAB0CAEACAAAAAQIAQAMAgAAdAgCAAgBAAAMCAIADAMAAHQIAQAIAAAAdAgCABAAAAAECAIAEAEAAHQIAIAIAAAABAgAgBACAAB0CAIACAIAAAwIAgAQAwAAdAgAgAgAAAB0CAIACAIAAAgIAgAIAQAAdAgAQAgAAADIFAIC/gAAAMgUAQAAAAAAdBQAxAAAQAAEFADEFABAAMAYA8QUAsAEdBQDzBgAbAAMFAPMAAP8AHQYA8wUAGwAyBQCAP4AAADIFAEAAAAAAHQUAMQAAEAABBQAxBQAQADAHAPEFALABHQUA8wcAGwADBQDzAAD/AB0HAPMFABsAHQIAgAYAAAABAgCABgBAAB0FAIACAAAAAQUAgAYAgAAdAgCABQAAAAMCAIAGAMAAHQUAgAIAAAAdAgCABwAAAAECAIAHAEAAHQUAQAIAAAABBQBABwCAAB0CAIAFAEAAAwIAgAcAwAAdBQBAAgAAAB0CAIAFAEAAAgIAgAUAAAAdBQAgAgAAAB0CAIACAEAAAQIAgAIAgAAdBQAQAgAAAAEFABAFAAAAHQIAgAUAwAABAgCABQBAADIFABBAgAAABAgAgAUAwAADCACAAgAAAB0CAIAIAAAAAQIAgAAAgAAdBQAQAgAAAB0CAIAFAIAAAwIAgAUAwAAdCACAAgAAAB0CAIACAMAAAwIAgAUAwAAdCABAAgAAAB0IADEAABAAAQgAMQgAEAAxCQDxCACwAB0BAPMJABsA"
/*
<languageVersion:1.0;>
kernel waterFilter
<namespace:"AIF";vendor:"AdobeSystems";version:2;description:"water";>{
    parameter float distance
    <minValue:0.0;defaultValue:100.0;>;
    parameter float height<defaultValue:3.0;>;
    input image4 backImage;
    input image4 waterImage;
    output pixel4 dst;void evaluatePixel(){
        float4 a=sampleNearest(waterImage,outCoord()+float2(0,-1))*height;
        float4 b=sampleNearest(waterImage,outCoord()+float2(0,1))*height;
        float a2=(a[0]+a[1]+a[2])*a[3];
        float b2=(b[0]+b[1]+b[2])*b[3];
        float v=b2-a2;
        float4 l=sampleNearest(waterImage,outCoord()+float2(-1,0))*height;
        float4 r=sampleNearest(waterImage,outCoord()+float2(1,0))*height;
        float l2=(l[0]+l[1]+l[2])*l[3];
        float r2=(r[0]+r[1]+r[2])*r[3];
        float h=r2-l2;
        
        float distance2=(a2+b2+l2+r2)/4.0+distance;
        dst=sample(backImage,outCoord()+float2(h*distance2,v*distance2));
    }
}}
*/
        private var shader:Shader;
        private var shaderFilter:ShaderFilter;
        private var img:BitmapData;
        private var bitmap:Bitmap;
        private var background:Loader;
        private var particles:Vector.<Particle>;
        private var numParticles:uint;
        private var neighbors:Vector.<Neighbor>;
        private var numNeighbors:uint;
        private var color:ColorTransform;
        private var filter:BlurFilter;
        private var count:int;
        private var press:Boolean;
        private var labP:TextField;
        private var grids:Vector.<Vector.<Grid>>;
        
        public function SPH() {
            initialize();
        }
        
        private function initialize():void {
            color = new ColorTransform(1,1,1,1.5,0,0,0,-256)
            particles = new Vector.<Particle>();
            numParticles = 0;
            neighbors = new Vector.<Neighbor>();
            numNeighbors = 0;
            grids = new Vector.<Vector.<Grid>>(NUM_GRIDS, true);
            for(var i:int = 0; i < NUM_GRIDS; i++) {
                grids[i] = new Vector.<Grid>(NUM_GRIDS, true);
                for(var j:int = 0; j < NUM_GRIDS; j++)
                    grids[i][j] = new Grid();
            }
            count = 0;
            var decoder:Base64Decoder = new Base64Decoder ;
            decoder.decode( SHADER_DATA );
            var ba:ByteArray = decoder.toByteArray();
            shader = new Shader( ba );
            
            background = new Loader();
            background.load( new URLRequest(IMG_URL), new LoaderContext(true) );
            addChild(background);
            background.width = stage.stageWidth;
            background.height = stage.stageHeight;
            
            img = new BitmapData(465, 465, true, 0);
            addChild( new Bitmap(img) ).alpha = 0.5;
            shader.data.height.value = [0.5];
            shader.data.distance.value = [600];
            shader.data.waterImage.input = img;
            shaderFilter = new ShaderFilter( shader );
            background.filters = [ shaderFilter ];
            
            addEventListener(Event.ENTER_FRAME, frame);
            labP = new TextField();
            labP.selectable = false;
            var tf:TextFormat = new TextFormat();
            tf.color = 0xffffff;
            tf.size = 12;
            tf.font = "MS UI Gothic";
            labP.defaultTextFormat = tf;
            labP.width = 256;
            labP.x = 70;
            var st:Stats = new Stats();
            st.alpha = 0.75;
            addChild(st);
            addChild(labP);
            
            
            stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {press = true;});
            stage.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void {press = false;});
        }

        private function frame(e:Event):void {
            if(press)
                pour();
            move();
            img.lock();
            img.colorTransform(img.rect, color);
            draw();
            img.unlock();
            
            shader.data.waterImage.input = img;
            shaderFilter = new ShaderFilter( shader );
            background.filters = [ shaderFilter ];
            
            labP.text = "Particle : " + numParticles;
        }

        private function draw():void {
            var w:Number = Sphere.W/2;
            var h:Number = Sphere.H/2;
            for(var i:int = 0; i < numParticles; i++) {
                var p:Particle = particles[i];
                img.draw( sphere, new Matrix(1,0,0,1,p.x-w,p.y-h) );
            }
        } 

        private function pour():void {
            if(count % 3 == 0)
                for(var i:int = -4; i <= 4; i++) {
                     particles[numParticles++] = new Particle(mouseX + i * RANGE * 0.8, 5);
                     particles[numParticles - 1].vy = 3;
                }
        }

        private function move():void {
            count++;
            var i:int;
            var j:int;
            var dist:Number;
            var p:Particle;
            updateGrids();
            findNeighbors();
            calcPressure();
            calcForce();
            for(i = 0; i < numParticles; i++) {
                p = particles[i];
                p.move();
            }
        }

        private function updateGrids():void {
            var i:int;
            var j:int;
            for(i = 0; i < NUM_GRIDS; i++)
                for(j = 0; j < NUM_GRIDS; j++)
                    grids[i][j].clear();
            for(i = 0; i < numParticles; i++) {
                var p:Particle = particles[i];
                p.fx = p.fy = p.density = 0;
                p.gx = p.x * INV_GRID_SIZE;
                p.gy = p.y * INV_GRID_SIZE;
                if(p.gx < 0)
                    p.gx = 0;
                if(p.gy < 0)
                    p.gy = 0;
                if(p.gx > NUM_GRIDS - 1)
                    p.gx = NUM_GRIDS - 1;
                if(p.gy > NUM_GRIDS - 1)
                    p.gy = NUM_GRIDS - 1;
                grids[p.gx][p.gy].add(p);
            }
        }

        private function findNeighbors():void {
            numNeighbors = 0;
            for(var i:int = 0; i < numParticles; i++) {
                var p:Particle = particles[i];
                var xMin:Boolean = p.gx != 0;
                var xMax:Boolean = p.gx != NUM_GRIDS - 1;
                var yMin:Boolean = p.gy != 0;
                var yMax:Boolean = p.gy != NUM_GRIDS - 1;
                findNeighborsInGrid(p, grids[p.gx][p.gy]);
                if(xMin) findNeighborsInGrid(p, grids[p.gx - 1][p.gy]);
                if(xMax) findNeighborsInGrid(p, grids[p.gx + 1][p.gy]);
                if(yMin) findNeighborsInGrid(p, grids[p.gx][p.gy - 1]);
                if(yMax) findNeighborsInGrid(p, grids[p.gx][p.gy + 1]);
                if(xMin && yMin) findNeighborsInGrid(p, grids[p.gx - 1][p.gy - 1]);
                if(xMin && yMax) findNeighborsInGrid(p, grids[p.gx - 1][p.gy + 1]);
                if(xMax && yMin) findNeighborsInGrid(p, grids[p.gx + 1][p.gy - 1]);
                if(xMax && yMax) findNeighborsInGrid(p, grids[p.gx + 1][p.gy + 1]);
            }
        }

        private function findNeighborsInGrid(pi:Particle, g:Grid):void {
            for(var j:int = 0; j < g.numParticles; j++) {
                var pj:Particle = g.particles[j];
                if(pi == pj)
                    continue;
                var distance:Number = (pi.x - pj.x) * (pi.x - pj.x) + (pi.y - pj.y) * (pi.y - pj.y);
                if(distance < RANGE2) {
                    if(neighbors.length == numNeighbors)
                        neighbors[numNeighbors] = new Neighbor();
                    neighbors[numNeighbors++].setParticle(pi, pj);
                }
            }
        }

        private function calcPressure():void {
            for(var i:int = 0; i < numParticles; i++) {
                var p:Particle = particles[i];
                if(p.density < DENSITY)
                    p.density = DENSITY;
                p.pressure = p.density - DENSITY;
            }
        }

        private function calcForce():void {
            var i:int;
            for(i = 0; i < numNeighbors; i++) {
                var n:Neighbor = neighbors[i];
                n.calcForce();
            }
        }
    }
}
import flash.geom.Point;
import flash.filters.BlurFilter;
import flash.display.BitmapData;

var sphere:BitmapData = new Sphere; 
class Sphere extends BitmapData{
    static public const W:int = 25; 
    static public const H:int = 25; 
    function Sphere(){
        var w:int = W, h:int = H;
        super( w, h, true, 0 );
        var filter:BlurFilter = new BlurFilter(4,4,1);
        for( var i:int = 0; i < w; i++ ){
            for( var j:int = 0; j < h; j++ ){
                var r:Number = 1-(Math.sqrt((w/2-i)*(w/2-i)+(h/2-j)*(h/2-j)))*2/w
                if(r<0){r=0} 
                setPixel32( i, j, ((r*0xFF) << 24) + 0xFFFFFF )
            }
        }
    }
}

class Particle {
    public var x:Number;
    public var y:Number;
    public var gx:int;
    public var gy:int;
    public var vx:Number;
    public var vy:Number;
    public var fx:Number;
    public var fy:Number;
    public var density:Number;
    public var pressure:Number;
    public function Particle(x:Number, y:Number) {
        this.x = x;
        this.y = y;
        vx = vy = fx = fy = 0;
    }

    public function move():void {
        vy += SPH.GRAVITY;
        vx += fx;
        vy += fy;
        x += vx;
        y += vy;
        if(x < 5)
            vx += (5 - x) * 0.5 - vx * 0.5;
        if(y < 5)
            vy += (5 - y) * 0.5 - vy * 0.5;
        if(x > 460)
            vx += (460 - x) * 0.5 - vx * 0.5;
        if(y > 460)
            vy += (460 - y) * 0.5 - vy * 0.5;
    }
}

class Neighbor {
    public var p1:Particle;
    public var p2:Particle;
    public var distance:Number;
    public var nx:Number;
    public var ny:Number;
    public var weight:Number;
    public function Neighbor() {
    }

    public function setParticle(p1:Particle, p2:Particle):void {
        this.distance = distance;
        this.p1 = p1;
        this.p2 = p2;
        nx = p1.x - p2.x;
        ny = p1.y - p2.y;
        distance = Math.sqrt(nx * nx + ny * ny);
        weight = 1 - distance / SPH.RANGE;
        var temp:Number = weight * weight * weight;
        p1.density += temp;
        p2.density += temp;
        temp = 1 / distance;
        nx *= temp;
        ny *= temp;
    }

    public function calcForce():void {
        var pressureWeight:Number = weight * (p1.pressure + p2.pressure) / (p1.density + p2.density) * SPH.PRESSURE;
        var viscosityWeight:Number = weight / (p1.density + p2.density) * SPH.VISCOSITY;
        p1.fx += nx * pressureWeight;
        p1.fy += ny * pressureWeight;
        p2.fx -= nx * pressureWeight;
        p2.fy -= ny * pressureWeight;
        var rvx:Number = p2.vx - p1.vx;
        var rvy:Number = p2.vy - p1.vy;
        p1.fx += rvx * viscosityWeight;
        p1.fy += rvy * viscosityWeight;
        p2.fx -= rvx * viscosityWeight;
        p2.fy -= rvy * viscosityWeight;
    }
}

class Grid {
    public var particles:Vector.<Particle>;
    public var numParticles:uint;
    public function Grid() {
        particles = new Vector.<Particle>;
        numParticles = 0;
    }

    public function clear():void {
        numParticles = 0;
    }

    public function add(p:Particle):void {
        particles[numParticles++] = p;
    }
}