In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: forked from: Desert Generator

Terrain raycaster (optimised)
* by Mr.doob (http://mrdoob.com)
*
* Click for a new terrain
// forked from mik's forked from: Desert Generator
// forked from mrdoob's Desert Generator
/*
 * 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);
						
			for (var i:int = 0; i < 4; i++) {
        			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();
			
		 	var ray : Ray = new Ray();
			ray.origin = new Point3D( canvas_width_half, canvas_height_half, canvas_width_half);
                        ray.direction = new Point3D();
			for (var yy : int = canvas_height; yy >= 0; yy -= precision) {
				ray.direction.x = xx;
				ray.direction.y = canvas_height - yy;
				ray.direction.z = 0;
				ray.direction.sub(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)) * (0.3 / 255);

			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;
                    var p : Point3D = new Point3D();
		    for (var t : Number = mint; t < maxt; delt = 0.01 * t, t += delt) {
                        ray.getPoint(p, t);
                        if ( p.y < f( p.x, p.z ) ) {
			    prev_t = ray.t = t - 0.5 * delt;
		            return true;
		        }
        	    }
		    return false;
		}
		
		private function terrainColor( ray : Ray ) : Number
		{	
			var p : Point3D = ray.point();
			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;

        public function point():Point3D {
            var p:Point3D = new Point3D();
            p.set(origin);
            p.madd(direction, t);
            return p;
        }
        public function getPoint(p:Point3D, t:Number):void {
            p.set(origin);
            p.madd(direction, t);
        }
}

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 set(p1:Point3D): void {
            x = p1.x; y = p1.y; z = p1.z;
        }	
	public function normalise() : void {
		var idist : Number = 1.0 / Math.sqrt( (x * x)+(y * y)+(z * z) );
		x *= idist;
		y *= idist;
		z *= idist;
	}
	public function add(p1:Point3D):void {
            x += p1.x;
            y += p1.y;
            z += p1.z;
        }
	public function sub(p1:Point3D):void {
            x -= p1.x;
            y -= p1.y;
            z -= p1.z;
        }
	public function madd(p1:Point3D, val:Number):void {
            x += p1.x * val;
            y += p1.y * val;
            z += p1.z * val;
        }
        public function scalar(val:Number):void {
            x *= val;
            y *= val;
            z *= val;
        }
	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 += (colour.r - r) * amount;
		g += (colour.g - g) * amount;
		b += (colour.b - b) * amount;
	}		
		
	public function multiplyNumber( value : Number ) : void
	{
		r *= value;
		g *= value;
		b *= value;
	}
}