// forked from mrdoob's Cube world // 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 = 128; 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 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() - 0.5) * 2, (Math.random() - 0.5) * 2 ); } 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) * 20) % terrain_size; z = Math.abs((z - 250) * 20) % terrain_size; var f : Number = ((0x0000ff & terrainH.getPixel(x, z)) / 0xff) * 4; return f - 0.3 + canvas_height_half; } private function castRay( ray : Ray ) : Boolean { var delt : Number = 0.01; var mint : Number = prev_t; //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 ) ) { 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.005; 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: Cube world