Terrain with Ridged Perlin Noise

by greentec
color map from: http://libnoise.sourceforge.net/tutorials/tutorial3.html

slope algorithm from:
http://andywoodruff.com/blog/shaded-relief-in-as3/
♥0 | Line 226 | Modified 2016-11-30 22:36:04 | MIT License
play

ActionScript3 source code

/**
 * Copyright greentec ( http://wonderfl.net/user/greentec )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/QbXN
 */

package 
{
    import com.bit101.components.PushButton;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.ColorTransform;
    
    /**
     * ...
     * @author ypc
     */
    [SWF(width = 465, height = 465)]
    public class Main extends Sprite 
    {
        public var noiseBitmapData:BitmapData;
        public var bitmapData:BitmapData;
        public var heightMapData:BitmapData;
        public var screenMapData:BitmapData;
        public var seed:uint = 0;
        public var noiseType:int = 3;
        
        public var terrainColorDict:Array = [0x000080, 0x0000ff, 0x0080ff, 0xf0f040, 0x20a000, 0xe0e000, 0x808080, 0xffffff];
        public var terrainColorNum:Array = [0, 0.125, 0.5, 0.53125, 0.5625, 0.6875, 0.875, 1];
        
        public var _width:int = 220;
        public var _height:int = 220;
        
        public function Main():void 
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            
            noiseBitmapData = new BitmapData(_width, _height, false, 0x0);
            bitmapData = new BitmapData(_width, _height, false, 0x0);
            heightMapData = new BitmapData(_width, _height, false, 0x0);
            screenMapData = new BitmapData(_width, _height, true, 0x0);
            
            var bitmap0:Bitmap;
            bitmap0 = new Bitmap(noiseBitmapData);
            addChild(bitmap0);
           
            var bitmap:Bitmap;
            bitmap = new Bitmap(bitmapData);
            bitmap.x = _width;
            addChild(bitmap);
            
            var bitmap2:Bitmap;
            bitmap2 = new Bitmap(heightMapData);
            bitmap2.y = _height;

            addChild(bitmap2);

           
            var bitmap3:Bitmap;
            bitmap3 = new Bitmap(screenMapData);
            bitmap3.x = _width;
            bitmap3.y = _height;
            addChild(bitmap3);
            
            
            var perlinButton:PushButton = new PushButton(this, 0, 440, "Perlin Noise", function(e:Event):void {
                noiseType = 1;
                ridgedPerlin();
            });
            perlinButton.width = 465 / 3;
            perlinButton.height = 25;
            var ridgedPerlinButton:PushButton = new PushButton(this, perlinButton.x + perlinButton.width, perlinButton.y, "Ridged Perlin", function(e:Event):void {
                noiseType = 2;
                ridgedPerlin();
            });
            ridgedPerlinButton.width = 465 / 3;
            ridgedPerlinButton.height = 25;
            var reverseRidgedPerlinButton:PushButton = new PushButton(this, ridgedPerlinButton.x + ridgedPerlinButton.width, ridgedPerlinButton.y, "Ridged Perlin (Reverse)", function(e:Event):void {
                noiseType = 3;
                ridgedPerlin();
            });
            reverseRidgedPerlinButton.width = 465 / 3;
            reverseRidgedPerlinButton.height = 25;
            
            
            ridgedPerlin();
            
            stage.addEventListener(MouseEvent.CLICK, ridgedPerlin);
        }
        
        public function ridgedPerlin(e:Event = null):void
        {
            seed = Math.random() * uint.MAX_VALUE;
            
            noiseBitmapData.perlinNoise(_width / 2.0, _height / 2.0, 7, seed, false, true, 7, true);
            
            var i:int;
            var j:int;
            var k:int;
            var t:Number;
            var num:Number;
            var color:uint;
            
            for (i = 0; i < _width; i += 1)
            {
                for (j = 0; j < _height; j += 1)
                {
                    color = noiseBitmapData.getPixel(i, j);
                    color = color & 0xff;
                    num = color / 255.0;
                    
                    if (noiseType > 1)
                    {
                        num *= 2;
                        num -= 1;
                        num = Math.abs(num);
                        
                        num = 1 - num;
                        num *= num * num * num;
                        
                        if (noiseType == 3)
                        {
                            num = 1 - num;
                        }
                    }
                    
                    color = num * 255;
                    
                    noiseBitmapData.setPixel(i, j, color << 16 | color << 8 | color);
                }
            }
            
            for (i = 0; i < _width; i += 1)
            {
                for (j = 0; j < _height; j += 1)
                {
                    color = noiseBitmapData.getPixel(i, j);
                    color = color & 0xff;
                    num = color / 255.0;
                    
                    for (k = 0; k < terrainColorNum.length - 1; k += 1)
                    {
                        if (terrainColorNum[k] <= num && terrainColorNum[k+1] >= num)
                        {
                            t = (num - terrainColorNum[k]) / (terrainColorNum[k + 1] - terrainColorNum[k]);
                            color = interpolateColor(terrainColorDict[k], terrainColorDict[k + 1], t);
                        }
                    }
                    
                    bitmapData.setPixel(i, j, color);
                }
            }
            
            
            var slx:Number;
            var sly:Number;
            var sl0:Number;
            var top:Number;
            var bottom:Number;
            var left:Number;
            var right:Number;
            
            var phi:Number;
            var azimuth:Number;
            
            var sunElev:Number = Math.PI * .25;
            var sunAzimuth:Number = 1.75 * Math.PI;
            var L:Number;
            
            for (i = 0; i < _width; i += 1)
            {
                for (j = 0; j < _height; j += 1)
                {
                    //slope
                    top = bitmapData.getPixel(i, Math.max(j - 1, 0)) & 0xff;
                    left = bitmapData.getPixel(Math.max(i - 1, 0), j) & 0xff;
                    right = bitmapData.getPixel(Math.min(i + 1, _width - 1), j) & 0xff;
                    bottom = bitmapData.getPixel(i, Math.min(j + 1, height - 1)) & 0xff;
                    
                    slx = (right - left) / 2.0;
                    sly = (bottom - top) / 2.0;
                    sl0 = Math.sqrt(slx * slx + sly * sly);
                    
                    
                    //aspect
                    phi = Math.acos(slx / sl0);
                    
                    if (sl0 == 0)
                    {
                        phi = 0;
                    }
                    
                    if (slx > 0)
                    {
                        if (sly > 0)
                        {
                            azimuth = phi + 1.5 * Math.PI;
                        }
                        else if (sly < 0)
                        {
                            azimuth = 1.5 * Math.PI - phi;
                        }
                        else
                        {
                            phi = 1.5 * Math.PI;
                        }
                    }
                    else if (slx < 0)
                    {
                        if (sly < 0)
                        {
                            azimuth = phi + .5 * Math.PI;
                        }
                        else if (sly > 0)
                        {
                            azimuth = .5 * Math.PI - phi;
                        }
                        else
                        {
                            azimuth = .5 * Math.PI;
                        }
                    }
                    else
                    {
                        if (sly < 0)
                        {
                            azimuth = Math.PI;
                        }
                        else if (sly > 0)
                        {
                            azimuth = 0;
                        }
                    }
                    
                    
                    //reflectance
                    L = Math.cos(azimuth - sunAzimuth) * Math.cos(Math.PI * .5 - Math.atan(sl0)) * Math.cos(sunElev) + Math.sin(Math.PI * .5 - Math.atan(sl0)) * Math.sin(sunElev);
                    
                    color = 255 * L;
                    
                    if (color < 0)
                    {
                        color = 0;
                    }
                    if (color > 255)
                    {
                        color = 255;
                    }
                    
                    heightMapData.setPixel(i, j, color << 16 | color << 8 | color);
                }
            }
            
            
            screenMapData.fillRect(screenMapData.rect, 0x00ffffff);
            
            screenMapData.draw(bitmapData);
            
            var alphaColT:ColorTransform = new ColorTransform(1, 1, 1, 0.1, 0, 0, 0, 0);
            //screenMapData.draw(heightMapData, null, alphaColT, "overlay");
            screenMapData.draw(heightMapData, null, alphaColT, "add");
            
            
        }
        
        public static function interpolateColor(fromColor:uint, toColor:uint, progress:Number):uint
        {
            var q:Number = 1-progress;
            var fromR:uint = (fromColor >> 16) & 0xFF;
            var fromG:uint = (fromColor >>  8) & 0xFF;
            var fromB:uint =  fromColor        & 0xFF;
            var toR:uint = (toColor >> 16) & 0xFF;
            var toG:uint = (toColor >>  8) & 0xFF;
            var toB:uint =  toColor        & 0xFF;
            var resultR:uint = fromR*q + toR*progress;
            var resultG:uint = fromG*q + toG*progress;
            var resultB:uint = fromB*q + toB*progress;
            var resultColor:uint = resultR << 16 | resultG << 8 | resultB;
            return resultColor;
        }
        
    }
    
}