地形生成+光源計算
地形生成+光源計算
マウスで光源移動
クリックで別パターンを生成
/**
* Copyright Nao_u ( http://wonderfl.net/user/Nao_u )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/pVDN
*/
// 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;
}
}