// forked from mrdoob's Terrain raycaster /* * Terrain raycaster * by Mr.doob (http://mrdoob.com) * * Click for a new terrain */ package { import flash.display.Bitmap; import flash.display.BitmapData; 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 seed : Number; private var precision : int; 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 light : Point3D; private var diffuseColor : Color; private var ambientColor : Color; private var skyColor : Color; private var pixelRect : Rectangle; private var lineRect : Rectangle; private var yy : int = 0; 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(); addChild( sprite ); sprite.addChild( new Bitmap( canvas ) ); sprite.buttonMode = true; stage.addEventListener(MouseEvent.CLICK, init); lineRect = new Rectangle(0, 0, canvas_width, 1); init(); addEventListener(Event.ENTER_FRAME, renderLine); } private function init( e : Event = null ) : void { yy = 0; precision = 16; seed = Math.random() * 2 + 0.1; diffuseColor = new Color( 0x303030 * Math.random() ); ambientColor = new Color( 0x101010 * Math.random() ); skyColor = new Color( 0xffffff * Math.random() ); light = new Point3D( (Math.random() - 0.5) * 2, (Math.random() - 0.5) * 2, (Math.random() - 0.5) * 2 ); } private function renderLine( e : Event ) : void { pixelRect = new Rectangle(0, 0, precision, precision); canvas.lock(); for (var xx : int = 0; xx < canvas_width; xx += 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; if (castRay(ray)) canvas.fillRect(pixelRect, terrainColor(ray) ); else canvas.fillRect(pixelRect, skyColor.getHex() ); } lineRect.y = yy + precision; canvas.fillRect(lineRect, 0xffffff ); if ((yy += precision) > canvas_height) { if (precision > 1) precision /= 2; else init(); yy = 0; } canvas.unlock(); } private function f( x : Number, z : Number) : Number { var f : Number; f = seed * Math.sin(seed * x) * Math.cos(seed * z); f += seed * Math.sin(2 * seed * x) * Math.cos(2 * seed * z); f += seed * 0.2 * Math.sin(5 * seed * x) * Math.cos(5 * seed * z); f -= seed * 0.1 * Math.sin(10 * seed * x) * Math.cos(10 * seed * z); return f - 1 + canvas_height_half; } private function castRay( ray : Ray ) : Boolean { var delt : Number = 0.01; var mint : Number = 0.1; var maxt : Number = 20.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 ) ) { 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( skyColor, ray.t / 20); 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 = ( (0x00ff00 & hex) >> 8 ); b = ( 0x0000ff & 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; } } forked from: Terrain raycaster