Beautifl - Flash Gallery

Preview

Desert Generator
mrdoob 2009年8月27日 All rights reserved
?
Terrain raycaster (optimised)  
by Mr.doob (http://mrdoob.com)  
 
Click for a new terrain
      /*
 * Terrain raycaster (optimised)
 * by Mr.doob (http://mrdoob.com)
 *
 * Click for a new terrain
 */

package  
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.Sprite;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;    

    [SWF(backgroundColor="#000000", frameRate="60")] 
    public class Main extends Sprite 
    {
        private var precision : int = 1;
        
        private var canvas : BitmapData;
        private var canvas_width : int = 465;
        private var canvas_height : int = 465;
        private var canvas_width_half : int = canvas_width / 2;
        private var canvas_height_half : int = canvas_height / 2;    
        
        private var terrain_size : int = 1024;    
        
        private var light : Point3D;
        
        private var diffuseColor : Color;
        private var ambientColor : Color;
        private var fogColor : Color;
        private var skyColor : Color;
        
        private var pixelRect : Rectangle;
        private var lineRect : Rectangle;
        
        private var xx : int = 0;
        
        private var terrainH : BitmapData;
        private var fractaliser : BitmapData;
        
        private var camera_height : Number;
        private var prev_t : Number = 0.1;
        
        public function Main()
        {
            addEventListener( Event.ADDED_TO_STAGE, onAddedToStage );
        }
        
        private function onAddedToStage( e : Event ) : void
        {
            stage.scaleMode = StageScaleMode.NO_SCALE;
            
            canvas = new BitmapData( canvas_width, canvas_height, false, 0 );
            var sprite : Sprite = new Sprite();
            sprite.buttonMode = true;
            sprite.addChild( new Bitmap( canvas ) );
            addChild( sprite );

            pixelRect = new Rectangle();
            lineRect = new Rectangle(0, 0, 1, canvas_height);
            
            terrainH = new BitmapData(terrain_size, terrain_size, false, 0);
            fractaliser = new BitmapData(terrain_size, terrain_size, false, 0);
            
            diffuseColor = new Color( 0x6f2d17 );
            ambientColor = new Color( 0x070300 );
            fogColor = new Color( 0x51547d );
            skyColor = new Color( 0xbbcde3 );

            init();
            
            stage.addEventListener(MouseEvent.CLICK, init);
            addEventListener(Event.ENTER_FRAME, render);
        }
        
        private function init( e : Event = null ) : void
        {
            xx = 0;
            precision = 16;
            
            generateTerrain();
            light = new Point3D( (Math.random() - 0.5) * 2, Math.random(), (Math.random() - 0.5) * 2 );            
            // Need to sort this out to avoid camera inside sand
            //camera_height = f(canvas_width_half,canvas_width_half);
        }
        
        private function generateTerrain() : void
        {
            terrainH.perlinNoise(500, 500, 1, Math.random() * 100, true, false, 7, true);
            
            var i : int = 0;
            
            while(i++ < 3)
            {
                fractaliser.perlinNoise(500, 500, 1, Math.random() * 100, true, false, 7, true);
                terrainH.draw(fractaliser, null, null, BlendMode.DIFFERENCE);
            }
        }
        
        private function render( e : Event ) : void
        {
            canvas.lock();
            
            for (var yy : int = canvas_height; yy >= 0; yy -= precision)
            {
                 var ray : Ray = new Ray();
                ray.origin = new Point3D( canvas_width_half, canvas_height_half, canvas_width_half);
                ray.direction = Point3D.sub( new Point3D(xx, canvas_height - yy, 0 ), ray.origin);
                ray.direction.normalise();

                pixelRect.x = xx;
                pixelRect.y = yy;
                pixelRect.width = precision;
                pixelRect.height = precision;
                
                if (castRay(ray))
                {
                    canvas.fillRect(pixelRect, terrainColor(ray) );
                }
                else
                {
                    pixelRect.y = 0;
                    pixelRect.height = yy + precision;
                    canvas.fillRect(pixelRect, skyColor.getHex());
                    break;
                }
                
                prev_t = 0.1;        
            }
            
            lineRect.x = xx + precision;
            canvas.fillRect(lineRect, 0xffffff );                

            if ((xx += precision) > canvas_width)
            {
                if (precision > 1)
                    precision /= 2;
                else
                    init();                

                xx = 0;
            }
            
            canvas.unlock();
        }    
        
        private function f( x : Number, z : Number) : Number
        {
            x = Math.abs((x - 250) * 1000) % terrain_size;
            z = Math.abs((z - 250) * 1000) % terrain_size;
            
            var f : Number = ((0x0000ff & terrainH.getPixel(x, z)) / 0xff) * .3;

            return f - .1 + canvas_height_half;
        }
        
        private function castRay( ray : Ray ) : Boolean
        {
            var delt : Number = 0.01;
            var mint : Number = prev_t; //0.1;
            var maxt : Number = 50.0;
            
            for( var t : Number = mint; t < maxt; t += delt )
            {
                var p : Point3D = Point3D.add(ray.origin, Point3D.scalar(ray.direction, t));
                
                if( p.y < f( p.x, p.z ) )
                {
                    prev_t = ray.t = t - 0.5 * delt;
                    return true;
                }
                
                delt = 0.01 * t;   
            }
            return false;
        }
        
        private function terrainColor( ray : Ray ) : Number
        {    
            var p : Point3D = Point3D.add(ray.origin, Point3D.scalar(ray.direction, ray.t));
            var n : Point3D = getNormal(p);
            var dot : Number = Point3D.dot(n, light);
            
            var c : Color = new Color(0x000000);
                        
            c.addRGB( diffuseColor );
            c.addNumber( dot * 0xFF );
            c.addRGB( ambientColor );
            c.mixRGB( fogColor, ray.t * 0.2);
                        
            return c.getHex();
        }
        
        private function getNormal( p : Point3D ) : Point3D
        {
            var eps : Number = 0.01;
            var normal : Point3D = new Point3D( f(p.x-eps,p.z) - f(p.x+eps,p.z),
                                                2 * eps, 
                                                f(p.x,p.z-eps) - f(p.x,p.z+eps) );
            normal.normalise();
            return normal;
        }        
    }
}

    
class Ray 
{
    public var origin : Point3D;
    public var direction : Point3D;
    public var t : Number;
}

class Point3D 
{
    public var x : Number;
    public var y : Number;
    public var z : Number;
    
    public function Point3D( x : Number = 0, y : Number = 0, z : Number = 0)
    {
        this.x = x;
        this.y = y;
        this.z = z; 
    }
    
    public function normalise() : void
    {
        var dist : Number = Math.sqrt( (x * x)+(y * y)+(z * z) );
        
        x = x * ( 1.0 / dist );
        y = y * ( 1.0 / dist );
        z = z * ( 1.0 / dist );
    }
    
    public static function add( p1 : Point3D, p2 : Point3D ) : Point3D
    {
        return new Point3D( p1.x + p2.x, p1.y + p2.y, p1.z + p2.z);
    }

    public static function sub( p1 : Point3D, p2 : Point3D ) : Point3D
    {
        return new Point3D( p1.x - p2.x, p1.y - p2.y, p1.z - p2.z);
    }

    public static function scalar( p1 : Point3D, val : Number ) : Point3D
    {
        return new Point3D( p1.x * val, p1.y * val, p1.z * val);
    }
        
    public static function dot( p1 : Point3D, p2 : Point3D ) : Number
    {
        return p1.x * p2.x + p1.y * p2.y + p1.z * p2.z;            
    }
        
    public function clone() : Point3D
    {
        return new Point3D(x, y , z);
    }
        
    public function toString() : String
    {
        return "x: " + x + ", y: " + y + ", z: " + z;
    }
}

class Color 
{
    public var r : int, g : int, b : int;
    
    public function Color( hex : int )
    {
        r = ( ( 0xff0000 & hex ) >> 16 );  
        g = ( ( 0xff00 & hex ) >> 8 );  
        b = ( ( 0xff & hex ) );
    }
        
    public function getHex() : int
    {
        r = ( r > 0xff ) ? 0xff : ( r < 0x00 ) ? 0 : r;
        g = ( g > 0xff ) ? 0xff : ( g < 0x00 ) ? 0 : g;
        b = ( b > 0xff ) ? 0xff : ( b < 0x00 ) ? 0 : b;    
            
        return r << 16 | g << 8 | b << 0;
    }

    public function addNumber( value : int ) : void
    {
        r += value;
        g += value;
        b += value;
    }
        
    public function addRGB( colour : Color ) : void
    {
        r += colour.r;
        g += colour.g;
        b += colour.b;
    }
        
    public function mixRGB( colour : Color, amount : Number ) : void
    {
        amount = (amount > 1) ? 1 : (amount < 0) ? 0 : amount; 
            
        r = (r * (1 - amount)) + (colour.r * amount);
        g = (g * (1 - amount)) + (colour.g * amount);
        b = (b * (1 - amount)) + (colour.b * amount);
    }        
        
    public function multiplyNumber( value : Number ) : void
    {
        r *= value;
        g *= value;
        b *= value;
    }
}