forked from: ocean

by Ludd forked from ocean (diff: 1307)
♥0 | Line 412 | Modified 2011-07-06 16:06:05 | MIT License
play

ActionScript3 source code

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

// forked from zonnbe's ocean
package  {
    import flash.display.Sprite;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.TriangleCulling;
    import flash.display.Graphics;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Vector3D;
    import flash.geom.Point;
    import flash.geom.PerspectiveProjection;
    import flash.geom.Utils3D;
    import flash.geom.Matrix3D;
    import flash.geom.Matrix;
    import flash.utils.getTimer;
    import net.hires.debug.Stats;
    
    [SWF(frameRate='60',width='465',height='465',backgroundColor='0xFFFFFF')]
    public class oceanBeta extends Sprite
    {
        //world
        private var gravity:Number = 9.81;
        private var fftDir:int = 0;

        private var oceanSizeX:Number = 32;
        private var oceanSizeY:Number = 32;
        private var oceanGridSize:Number = 1;
        private var oceanWindScaleX:Number = 0.1;
        private var oceanWindScaleY:Number = 0.1;
        private var oceanLambda:Number = 1.2;

        //map
        private var oceanMapSizeX:int = oceanSizeX+0;
        private var oceanMapSizeY:int = oceanSizeY+0;
        private var heights:Vector.<Vector.<Complex>> = new Vector.<Vector.<Complex>>(); //Vector.<Complex> = new Vector.<Complex>();
        private var horizontals:Vector.<Vector.<Vector.<Number>>> = new Vector.<Vector.<Vector.<Number>>>();
        
        private var bmpd:BitmapData = new BitmapData(oceanSizeX, oceanSizeY, false, 0x0);
        private var oreal:Vector.<Number> = new Vector.<Number>();
        private var oimag:Vector.<Number> = new Vector.<Number>();
        
        private var fft:FFT = new FFT(oceanSizeX);
        
        // surgery
        private var vertices:Vector.<Number> = new Vector.<Number>();
        private var uvs:Vector.<Number> = new Vector.<Number>();
        private var indices:Vector.<int> = new Vector.<int>();
        private var normals:Vector.<Vector3D> = new Vector.<Vector3D>();
        
        private var gridSizeX:Number = 25;
        private var gridSizeY:Number = 25;
        
        private var _vertexNormal:Vector.<Vector3D> = new Vector.<Vector3D>();
       
        private var viewport:Sprite = new Sprite();
        private var _projector:PerspectiveProjection;
        private var _projectionMatrix:Matrix3D;


        
        private var cp:Point = new Point();
        
        private var _vertexOnWorld:Vector.<Number> = new Vector.<Number>();
        private var _vout:Vector.<Number> = new Vector.<Number>();
        
        public function oceanBeta()
        {
            cp.x = stage.stageWidth / 2;
            cp.y = stage.stageHeight / 2;
            viewport.x = cp.x;
            viewport.y = cp.y;
          
            _projector  = new PerspectiveProjection();
            _projectionMatrix = _projector.toMatrix3D();
            
            var args1:Array = ["linear", [0, 0x043055], [1, 0], [0, 240]];
            var args2:Array = ["linear", [0, 0x043055], [1, 1], [0, 255]];
         
            with(addChild(new Bitmap(bmpd)))
            {
                scaleX = scaleY = 3; x = (stage.stageWidth - width) / 2; y = stage.stageHeight - height;
            }
            boot();
        }
        
        private function boot():void
        {
            var i:int, j:int, k:int;
            for(j = 0; j < oceanMapSizeY; j++)
            {
                heights.push(new Vector.<Complex>());
                horizontals.push(new Vector.<Vector.<Number>>());
                
                for(i = 0; i < oceanMapSizeX; i++)
                {
                    vertices.push(i*gridSizeX-(gridSizeX*oceanMapSizeX)/2, 0, j*gridSizeY-(gridSizeY*oceanMapSizeY)/2);
                    uvs.push(Number(0.0), Number(0.0), Number(0.0));
                    normals.push(new Vector3D());
                    
                    heights[j].push(new Complex());
                    horizontals[j].push(new Vector.<Number>);
                    
                    oreal.push(Number(0.0));
                    oimag.push(Number(0.0));

                    for (k = 0; k < 4; k++)
                    {
                        horizontals[j][i].push(Number(0.0));
                    }
                }
            }
          
            init();
        }
        
        private function init():void
        {
            var invSqrt2:Number = 1/Math.sqrt(2);
            var aGlobal:Number = 0.001;
            var rootOfPhillips:Number = 0.0;
            var horizontal:Vector.<Number> = new Vector.<Number>(2,true);
            var gv:Vector.<Number> = new Vector.<Number>(2,true);
            var winds:Vector.<Number> = new Vector.<Number>(2,true);

            winds[0] = 100*oceanWindScaleX;
            winds[1] = 100*oceanWindScaleY;
        
            var i:int, j:int;
        
            for (i = 0; i < oceanSizeY; i++)
            {
                for (j = 0; j < oceanSizeX; j++)
                {
                    horizontal[0]=horizontals[i][j][0]=2.0*Math.PI*(i-0.5*(oceanSizeX))/((oceanSizeX)*2.0);
                    horizontal[1]=horizontals[i][j][1]=2.0*Math.PI*(j-0.5*(oceanSizeY))/((oceanSizeY)*2.0);
        
                    horizontals[i][j][3]=horizontals[i][j][0]*horizontals[i][j][0]+horizontals[i][j][1]*horizontals[i][j][1];
                    horizontals[i][j][2]=Math.sqrt(horizontals[i][j][3]);
        
                    gauss(gv);

                    rootOfPhillips=Math.sqrt(phillips(aGlobal,horizontal,winds));
                
                    heights[i][j].real=invSqrt2*gv[0]*rootOfPhillips;
                    heights[i][j].imag=invSqrt2*gv[1]*rootOfPhillips;
                }
            }
            stage.addEventListener(Event.ENTER_FRAME, processing);
           
        }
        
        private function processing(e:Event):void
        {
            oceanSimulating(getTimer()/180*Math.PI/10);
            
            bmpd.lock();
            bmpd.fillRect(bmpd.rect, 0x0);
            
            var i:int, j:int;
            var h:int = 0;
            var c:uint = 0;
            for(j = 0; j < oceanMapSizeY; j++)
            {
                for(i = 0; i < oceanMapSizeX; i++)
                {
                    vertices[(i + j * oceanMapSizeX) * 3 + 1] = oreal[i + j * oceanMapSizeX]*3;
                    h = (oreal[i + j * oceanMapSizeX]+10)/40 * 256;
                    if (h < 0) h = 0;
                    if (h > 255) h = 255;
                    c = h;
                    c += h << 8;
                    c += h << 16;
                    c += 0xFF000000;
                    bmpd.setPixel32(i,j,c);
                }
            }
            bmpd.unlock();
            
        }
        
        private function gauss(work:Vector.<Number>):void
        {
            var x1:Number;
            var x2:Number;
            var w:Number;
        
            do 
            {
                x1 = 2.0 * Math.random() - 1.0;
                x2 = 2.0 * Math.random() - 1.0;
                w = x1 * x1 + x2 * x2; 
            } while ( w >= 1.0 );
        
            w = Math.sqrt( (-2.0 * Math.log( w ) ) / w );
        
            work[0] = x1 * w;
            work[1] = x2 * w;
        }

        private function phillips(a:Number, k:Vector.<Number>, wind:Vector.<Number>):Number
        {
            var k2:Number;
            var v2:Number;
            var EL:Number;
            var Phk:Number;
        
            k2 = k[0]*k[0]+k[1]*k[1];             
            v2 = wind[0]*wind[0]+wind[1]*wind[1];
        
            EL = v2 / gravity;                 
        
            if (k2==0)
            {
                return 0;
            }
            else
            {
                Phk = a*(Math.exp(-1/(k2*(EL)*(EL)))/(k2*k2))*((k[0]*wind[0]+k[1]*wind[1])*(k[0]*wind[0]+k[1]*wind[1])/(k2*v2))*Math.exp(-Math.sqrt(k2)*1.0);
                return Phk;
            }
        }
        
        private function oceanSimulating(timeStep:Number):void
        {
            var i:int, j:int;
            var kvector:Vector.<Number> = new Vector.<Number>(2,true);
            var klength:Number = 0;
            var wkt:Number = 0;
            var yHalf:int = (oceanSizeY)/2 + 1;
    
            var yLine:int = 0;
            var yLineMirr:int = 0;
            var kNegIndex:int = 0;
            var ri:int = 0;
            var rj:int = 0;
            
            for (i = 0; i < yHalf; ++i)
            {
                yLine = i*(oceanSizeY);
                yLineMirr = (((oceanSizeY)-i)% (oceanSizeY))*(oceanSizeY);
    
                for (j = 0; j < (oceanSizeX); ++j)
                {
                    ri = (oceanSizeX) - i - 1;
                    rj = (oceanSizeX) - j - 1;
                    
                    kvector[0]=horizontals[i][j][0];
                    kvector[1]=horizontals[i][j][1];
    
                    klength=horizontals[i][j][2];
    
                    wkt = Math.sqrt(klength * gravity) * timeStep;
    
                    kNegIndex = yLineMirr*(oceanSizeY) + (((oceanSizeY)-j)% (oceanSizeY));
    
                    oreal[j+i*oceanSizeX]=
                        heights[i][j].real*Math.cos(wkt) + heights[i][j].imag*Math.sin(wkt)
                        + heights[ri][rj].real*Math.cos(wkt)
                        - heights[ri][rj].imag*Math.sin(wkt);

                    oimag[j+i*oceanSizeX]=        
                        heights[i][j].imag*Math.cos(wkt) + heights[i][j].real*Math.sin(wkt)
                        - heights[ri][rj].imag*Math.cos(wkt)
                        - heights[ri][rj].real*Math.sin(wkt);

                    if (i != yHalf-1)        
                    {
                        oimag[rj+ri*oceanSizeX]=
                            heights[i][j].real*Math.cos(wkt) + heights[i][j].imag*Math.sin(wkt)
                            + heights[ri][rj].real*Math.cos(wkt)
                            - heights[ri][rj].imag*Math.sin(wkt);
    
                        oreal[rj+ri*oceanSizeX]=
                            heights[i][j].imag*Math.cos(wkt) + heights[i][j].real*Math.sin(wkt)
                            - heights[ri][rj].imag*Math.cos(wkt)
                            - heights[ri][rj].real*Math.sin(wkt);
                    }
                }
            }

            fft.fft2d(oreal,oimag);

            for (j = 0; j < oceanSizeY; j++)
            {
                for (i = 0; i < oceanSizeX; i++)
                {
                    oreal[i+j*oceanSizeX] *= Number(Math.pow(-1.0,i+j));
                }
            }
        }
    }
}

import flash.geom.Vector3D;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.geom.ColorTransform;
import flash.display.BitmapData;
import flash.display.BlendMode;

class Face
{
    public var i0:int, i1:int, i2:int, z:Number, normal:Vector3D = new Vector3D();
    public function Face(a:int = 0, b:int = 0, c:int = 0)
    {
        i0 = a; i1 = b; i2 = c;
    }
}


class Complex
{
    public var real:Number = 0;
    public var imag:Number = 0;
    
    public function Complex(r:Number = 0, i:Number = 0) { real = r; imag = i; }
}

class FFT{
    private var n:int; // データ数
    private var bitrev:Vector.<int>; // ビット反転テーブル
    private var cstb:Vector.<Number>; // 三角関数テーブル
    public static const HPF:String = "high";
    public static const LPF:String = "low";
    public static const BPF:String = "band"

    public function FFT(n:int) {
        if(n != 0 && (n & (n-1)) == 0) {
            this.n = n;
            this.cstb = new Vector.<Number>(n + (n>>2), true);
            this.bitrev = new Vector.<int>(n, true);
            makeCstb();
            makeBitrev();
        }
    }
    // 1D-FFT
    public function fft(re:Vector.<Number>, im:Vector.<Number>, inv:Boolean=false):Boolean {
        if(!inv) {
         return fftCore(re, im, 1);
        } else {
            if(fftCore(re, im, -1)) {
                for(var i:int=0; i<n; i++) {
                    re[i] /= n;
                    im[i] /= n;
                }
            }
        }
        return true;
    }
    // 2D-FFT
    public function fft2d(re:Vector.<Number>, im:Vector.<Number>, inv:Boolean=false):Boolean {
        var tre:Vector.<Number> = new Vector.<Number>(re.length, true);
        var tim:Vector.<Number> = new Vector.<Number>(im.length, true);
        var x:int, y:int;
        if(inv) cvtSpectrum(re, im);
        // x軸方向のFFT
        for(y=0; y<n; y++) {
            for(x=0; x<n; x++) {
                tre[x] = re[x + y*n];
                tim[x] = im[x + y*n];
            }
            if(!inv) fft(tre, tim);
            else fft(tre, tim, true);
            for(x=0; x<n; x++) {
                re[x + y*n] = tre[x];
                im[x + y*n] = tim[x];
            }
        }
        // y軸方向のFFT
        for(x=0; x<n; x++) {
            for(y=0; y<n; y++) {
                tre[y] = re[x + y*n];
                tim[y] = im[x + y*n];
            }
            if(!inv) fft(tre, tim);
            else fft(tre, tim, true);
            for(y=0; y<n; y++) {
                re[x + y*n] = tre[y];
                im[x + y*n] = tim[y];
            }
        }
        if(!inv) cvtSpectrum(re, im);
        return true;
    }
    // 空間周波数フィルタ
    public function applyFilter(re:Vector.<Number>, im:Vector.<Number>, rad:uint, type:String, bandWidth:uint = 0):void {
        var r:int = 0; // 半径
        var n2:int = n>>1;
        for(var y:int=-n2; y<n2; y++) {
            for(var x:int=-n2; x<n2; x++) {
                r = Math.sqrt(x*x + y*y);
                if((type == HPF && r<rad) || (type == LPF && r>rad) || (type == BPF && (r<rad || r>(rad + bandWidth)))) {
                        re[x + n2 + (y + n2)*n] = im[x + n2 + (y + n2)*n] = 0;
                }
            }
        }
    }
    private function fftCore(re:Vector.<Number>, im:Vector.<Number>, sign:int):Boolean {
        var h:int, d:int, wr:Number, wi:Number, ik:int, xr:Number, xi:Number, m:int, tmp:Number;
        for(var l:int=0; l<n; l++) { // ビット反転
            m = bitrev[l];
            if(l<m) {
                tmp = re[l];
                re[l] = re[m];
                re[m] = tmp;
                tmp = im[l];
                im[l] = im[m];
                im[m] = tmp;
            }
        }
        for(var k:int=1; k<n; k<<=1) { // バタフライ演算
            h = 0;
            d = n/(k<<1);
            for(var j:int=0; j<k; j++) {
                wr = cstb[h + (n>>2)];
                wi = sign*cstb[h];
                for(var i:int=j; i<n; i+=(k<<1)) {
                    ik = i+k;
                    xr = wr*re[ik] + wi*im[ik]
                    xi = wr*im[ik] - wi*re[ik];
                    re[ik] = re[i] - xr;
                    re[i] += xr;
                    im[ik] = im[i] - xi;
                    im[i] += xi;
                }
                h += d;
            }
        }
        return true;
    }
    // スペクトル位置並び替え
    private function cvtSpectrum(re:Vector.<Number>, im:Vector.<Number>):void {
        var tmp:Number = 0.0, xn:int = 0, yn:int = 0;
        for(var y:int=0; y<(n>>1); y++) {
            for(var x:int=0; x<(n>>1); x++) {
                xn = x + (n>>1);
                yn = y + (n>>1);
                tmp = re[x + y*n];
                re[x + y*n] = re[xn + yn*n];
                re[xn + yn*n] = tmp;
                tmp = re[x + yn*n];
                re[x + yn*n] = re[xn + y*n];
                re[xn + y*n] = tmp;
                tmp = im[x + y*n];
                im[x + y*n] = im[xn + yn*n];
                im[xn + yn*n] = tmp;
                tmp = im[x + yn*n];
                im[x + yn*n] = im[xn + y*n];
                im[xn + y*n] = tmp;
            }
        }
    }
    // 三角関数テーブル作成
    private function makeCstb():void {
        var n2:int = n>>1, n4:int = n>>2, n8:int = n>>3;
        var t:Number = Math.sin(Math.PI/n);
        var dc:Number = 2*t*t;
        var ds:Number = Math.sqrt(dc*(2 - dc));
        var c:Number = cstb[n4] = 1;
        var s:Number = cstb[0] = 0;
        t = 2*dc;
        for(var i:int=1; i<n8; i++) {
            c -= dc;
            dc += t*c;
            s += ds;
            ds -= t*s;
            cstb[i] = s;
            cstb[n4 - i] = c;
        }
        if(n8 != 0) cstb[n8] = Math.sqrt(0.5);
        for(var j:int=0; j<n4; j++) cstb[n2 - j] = cstb[j];
        for(var k:int=0; k<(n2 + n4); k++) cstb[k + n2] = -cstb[k];
    }
    // ビット反転テーブル作成
    private function makeBitrev():void {
        var i:int = 0, j:int = 0, k:int = 0;
        bitrev[0] = 0;
        while(++i<n) {
            k= n >> 1;
            while(k<=j) {
                j -= k;
                k >>= 1 ;
            }
            j += k;
            bitrev[i] = j;
        }
    }
}