Beautifl - Flash Gallery

Thumbnail : 地形生成+光源計算
地形生成+光源計算
Nao_u 2009-12-27 MIT License

再生するにはFlash Playerが必要です。デスクトップのブラウザでご覧ください。

// forked from Nao_u's テクスチャ生成実験2
// 
// 地形生成+光源計算
//
// マウスで光源移動
// クリックで別パターンを生成
//
//
package {     
    import flash.display.Sprite;     
    import flash.events.*;     
    [SWF(width="465", height="465", backgroundColor="0xFFFFFF", frameRate="15")]      
        
    public class FlashTest extends Sprite {     
        public function FlashTest() {     
            Main = this;     
            initialize();     
            stage.addEventListener(Event.ENTER_FRAME,update);      
            stage.addEventListener(MouseEvent.MOUSE_DOWN, function (event:MouseEvent):void{ Text.text = "生成中...しばらくお待ちください"; Cnt=0 });       
        }     
    }     
}             

import flash.display.Bitmap; 
import flash.display.BitmapData; 
import flash.display.Sprite;      
import flash.display.BlendMode;
import flash.events.*
import flash.text.TextField;     
import flash.geom.*;
import flash.geom.Vector3D; 
import flash.utils.getTimer;
import flash.filters.ColorMatrixFilter; 
var Main:Sprite;     
var SCREEN_W:Number = 465;
var SCREEN_H:Number = 465;
var Text:TextField    
var View: Bitmap; 
var View2: Bitmap; 
var BmpData: BitmapData; 
var NorData: BitmapData; 
var DispData: BitmapData; 
var Front:Sprite;

var Scale:int = 1;
var BITMAP_W:int = SCREEN_W/Scale;
var BITMAP_H:int = SCREEN_H/Scale;
var Cnt:int;
var Frequency:Number = 48/Scale;
var Base:Number = 0;
var Type:int = 0;
var bAbs:Boolean = false;

function initialize():void{     
    BmpData = new BitmapData(BITMAP_W, BITMAP_H, false, 0xffffff); 
    NorData = new BitmapData(BITMAP_W, BITMAP_H, false, 0xffffff); 
    DispData = new BitmapData(BITMAP_W, BITMAP_H, false, 0xffffff); 
    View = new Bitmap(BmpData); 
    View.scaleX = Scale;
    View.scaleY = Scale;
    Main.addChild(View);      

    View2 = new Bitmap(DispData); 
    View2.scaleX = Scale;
    View2.scaleY = Scale;

    Front = new Sprite();
    Front.blendMode = BlendMode.MULTIPLY;
    Front.blendMode = BlendMode.NORMAL;
    Front.addChild(View2);
    Main.addChild(Front);

    Text = new TextField();     
    Text.text = "生成中...しばらくお待ちください";   
    Text.autoSize = "left";
    Main.addChild(Text);      
}

function resetTexture():void{     
    BmpData.lock(); 
    for( var x:int=0; x<BITMAP_W; x++ ){ 
        for( var y:int=0; y<BITMAP_H; y++ ){ 
            var col:int = 0xffffff;
            BmpData.setPixel(x, y, col); 
        }
    } 
    BmpData.unlock(); 
}


function update(e :Event):void{     
    Cnt++;
    if( Cnt == 1 ){
        resetTexture();
    }
    if( Cnt == 2 ){
        var time:int = getTimer(); 
        createTexture(Type%4,Frequency, Base, bAbs);
        var endTime:int = getTimer() - time;
        Text.text = "生成時間:" + endTime + "[ms]";   

        Frequency = 48/Scale - 151.0 * rnd( Frequency*4, Frequency*2 );
        Base = 100/Scale + 500.0 * rnd( Frequency*4, Frequency*3 );
    }
    updateLight();


}  

// テクスチャ生成
function createTexture(Type:int, frequency:Number, base:Number, bAbs:Boolean):void{     
    var func:Function;
    switch( Type ){
        case 0: func = Noise0; break;
    }

    BmpData.lock(); 
    NorData.lock(); 
    for( var x:int=0; x<BITMAP_W; x++ ){ 
        for( var y:int=0; y<BITMAP_H; y++ ){ 
            var col:int;
            var ret:int;
            var a:int;
            ret = func( x+base, y+base, frequency, 0.25, 5, bAbs, base);// * mul;
            a = ret >> 24;
            col = a + (a<<8) + (a<<16);
            BmpData.setPixel(x, y, col); 
            NorData.setPixel(x, y, ret); 
        }
    } 
    BmpData.unlock(); 
    NorData.unlock(); 

    // 高さに応じて色変換
      var paletteR: Array = new Array(256); 
      var paletteG: Array = new Array(256); 
      var paletteB: Array = new Array(256); 
      var threshold: int = 134; 
      var val:int; 
        var r:Number, g:Number, b:Number, t:Number;
        var r0:Number, g0:Number, b0:Number;
        var r1:Number, g1:Number, b1:Number;
        var h0:Number = 0.06;
        var h1:Number = 0.01;
        var h2:Number = -0.001;
        for (var i:int=0; i<256; i++) { 
                if (i >= threshold) { 
                    r = (i - threshold) / (256 - threshold); 
                    if (r > h0) { 
                        t = (r - h0) * 1.0 / (1.0 - h0);
                        r0 = 1.2;
                        g0 = 1.2;
                        b0 = 1.25;
                        r1 = 0.3;
                        g1 = 0.25;
                        b1 = 0.175;
                    } else if (r > h1) { 
                        t = (r - h1) * 1.0 / (h0 - h1);
                        r0 = 0.2;
                        g0 = 0.275;
                        b0 = 0.1;
                        r1 = 0.12;
                        g1 = 0.15;
                        b1 = 0.09;
                    } else { 
                        t = (r - h2) * 1.0 / (h1 - h2);
                        r0 = 0.07;
                        g0 = 0.09;
                        b0 = 0.085;
                        r1 = 0.3;
                        g1 = 0.3;
                        b1 = 0.5;
                    } 
                    r = interpolate( r1, r0, t ) * 255;
                    g = linearInterpolate( g1, g0, t ) * 255;
                    b = interpolate( b1, b0, t ) * 255;
                    if( r > 255 ) r = 255;
                    if( g > 255 ) g = 255;
                    if( b > 255 ) b = 255;
                    paletteR[i] = r<<16; 
                    paletteG[i] = g<<8; 
                    paletteB[i] = b; 
                } else { 
                    r = i / threshold; 
                    paletteR[i] = 1; 
                    paletteG[i] = Math.pow(r, 5) * 0x66 << 8; 
                    paletteB[i] = (r * 0.5 + 0.5) * 0xFF; 
                } 
      } 
     BmpData.paletteMap(BmpData, new Rectangle(0,0,BmpData.width,BmpData.height), new Point(0,0), paletteR, paletteG, paletteB, null); 
    updateLight();
}

var mx:int = 0;
var my:int = 0;

// ライト計算
function updateLight():void{
    var light:Vector3D = new Vector3D(); 
    mx += (Main.mouseX - mx) * 0.45;
    my += (Main.mouseY - my) * 0.45;
    if( mx < 0 ) mx = 0;
    if( my < 0 ) my = 0;
    if( mx > SCREEN_W ) mx = SCREEN_W;
    if( my > SCREEN_H ) my = SCREEN_H;
    light.x = -10 + mx / Scale;
    light.y = -10 + my / Scale;
    light.z = 120;
    var lx:Number, ly:Number, lz:Number;

    DispData.lock(); 
    for( var x:int=0; x<BITMAP_W; x++ ){ 
        for( var y:int=0; y<BITMAP_H; y++ ){ 
            lx = x - light.x;
            ly = y - light.y;
            lz = light.z;
            var l:Number = 1.0 / Math.sqrt(lx*lx+ly*ly+lz*lz);
            lx *= l;
            ly *= l;
            lz *= l;
            var lt:Number = (l*150);
 
            var col:int = NorData.getPixel(x, y);
            var r:int = (col & 0xff0000)>>16;
            var g:int = (col & 0x00ff00)>>8;
            var b:int = (col & 0x0000ff);
            var colb:int = BmpData.getPixel(x, y);
            var br:int = (colb & 0xff0000)>>16;
            var bg:int = (colb & 0x00ff00)>>8;
            var bb:int = (colb & 0x0000ff);
            var fr:Number = (r - 128) * (1/128);
            var fg:Number = (g - 128) * (1/128);
            var fb:Number = (b - 128) * (1/128);
            var dot:Number = fr * lx + fg * ly + fb * lz;
            dot += 0.5;
            dot *= lt;
            if( dot < 0.0 ) dot = 0.0;
            dot += 0.4;
            var or:int = dot * br;
            var og:int = dot * bg * 0.98;
            var ob:int = dot * bb * 0.85;
            if( or > 255 ) or = 255;
            if( og > 255 ) og = 255;
            if( ob > 255 ) ob = 255;
            DispData.setPixel(x, y, ob + (og<<8) + (or<<16)); 
        }
    } 
    DispData.unlock(); 

}

// Noiseを生成(回転)
function Noise0( x:Number, y:Number, frequency:Number, presistence:Number, octave:int, bAbs:Boolean, base:Number ):int{
    var height:int;
    var rx:int, ry:int, rz:int;
    presistence *= 2;

    var e:Number = 0.10;
    octave = 7;
    var vec:Vector4D  = createNormal(x/frequency+100, y/frequency+20, e, octave);
    height = 56 * vec.w;
    height = height + 128;

    rx = 128 * vec.x;
    rx = rx + 128;
    ry = 128 * vec.y;
    ry = ry + 128;
    rz = 128 * vec.z;
    rz = rz + 128;

    return (height<<24) + (rx<<16)+ (ry<<8)+ (rz);
}

var sea_height:Number = 0.085;
function createNormal(px:Number, py:Number, e:Number, o:int):Vector4D{
	var a:Number = f(px, py, o);
         var vec:Vector4D = new Vector4D;
         
         if( a < sea_height ) {
            vec.x = f(px*20, py*20, 1);
            vec.y = f(px*30, py*30, 1);
            vec.z = 7;    
            vec.normalize3D();
            vec.z *= 3;    
         }else{
             if( a > sea_height + 0.20 ) a += f(px*30, py*30, 1) * 0.045;
             var q:Number = -0.40;
             vec.x = q*(a-f(px+e, py,  o));
             vec.y = q*(a-f(px,   py+e,o));
             vec.z = e*0.65;
             vec.normalize3D();
         }
         if( a > sea_height + 0.30 ) a += f(px*20, py*20, 2) * 0.105;
	vec.w = a;
	return vec;
}

function f(px:Number, py:Number, o:int):Number{
	var dx:Number=0;
	var dy:Number=0;
	var a:Number=0;
	var b:Number=3;
         var ppx:Number, ppy:Number;
	for(var i:int=0; i<o; i++){
             ppx = px; ppy = py;
	    px = ppx * 1.8  + ppy * 1.4;
             py = ppy * -1.4 + ppx * 1.8;
	    var n:Vector3D = no(0.25*px, 0.25*py);
	    dx += n.y;
             dy += n.z;
             b*=0.5;
	    a+=(b)*n.x/(1+ dx*dx + dy*dy);
	}
	return a;
}

function no(px:Number, py:Number):Vector3D{
	var fx:Number, fy:Number;

         fx=px-Math.floor(px);
         fy=py-Math.floor(py);
	var ux:Number,uy:Number;
         ux=fx*fx*fx*(fx*(fx*6-15)+10);
         uy=fy*fy*fy*(fy*(fy*6-15)+10);
        
         var add:Number = 1.0;
	var a:Number = rnd( px,     py );
	var b:Number = rnd( px+add, py );
	var c:Number = rnd( px,     py+add );
	var d:Number = rnd( px+add, py+add );
         
         var ret:Vector3D = new Vector3D;
         ret.x = a+(b-a)*ux+(c-a)*uy+(a-b-c+d)*ux*uy;
         ret.y = 30*fx*fx*(fx*(fx-2)+1) * ((b-a)+(a-b-c+d)*uy);
         ret.z = 30*fy*fy*(fy*(fy-2)+1) * ((c-a)+(a-b-c+d)*ux);
         return ret;
}

// a から b をcosでなめらかに補間
function interpolate( a:Number, b:Number, t:Number ):Number{
    var ft:Number = t * Math.PI;
    var f:Number = (1.0 - Math.cos( ft )) * 0.5;
    return a * (1.0 - f) + b * f;
}

// a から b を線形補間
function linearInterpolate( a:Number, b:Number, t:Number ):Number{
    return a * (1.0 - t) + b * t;
}

// 入力値( x, y ) に対応した、-1.0 ~ 1.0 の擬似乱数を生成
function rnd( x:int, y:int ):Number{
    x += y * 465 + 789221;    // 465=2次元での横幅に対応
    x = (x>>10) ^ x;
    var ret:int = (( (x * (x * x * 15731 + 789221) + 1376312589) ) / 1000000);
    
    return ((((ret & 0xff) + ((ret & 0xff00)>>8)+ ((ret & 0xff0000)>>16))&0x1ff) / 256) - 1.0;
}

class Vector4D{
    public var x:Number;
    public var y:Number;
    public var z:Number;
    public var w:Number;

    public function normalize3D():void{
        var l:Number = 1.0 / Math.sqrt(x*x+y*y+z*z);
        x *= l;
        y *= l;
        z *= l;
    }
}