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

voxel3d

Get Adobe Flash player
by lizhi 11 Dec 2012
/**
 * Copyright lizhi ( http://wonderfl.net/user/lizhi )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/9v5N
 */

// 地表渲染
//
// OK.. 我知道 voxel 指的别的什么... 但是许多人使用 "voxel" 这个
// 名字来表示一种渲染技术.我写这个来解释 newvox4 渲染的基本思想;
// newvox4 写的很糟糕 (名字里有个 4 是因为是继续到第四次的实验)
// 而且它是用 pascal + asm 写的. 自从我得到了一些说明的请求, 我
// 便决定用 C 写一个渲染内核, 希望它能更容易被理解. 这段程序仅仅
// 是基本的地表 (没有天空等) 而且只支持键盘,但是我想你可以自己想
// 得到应该怎样写其它的部分
//
// I'm releasing this code to the public domain for free... and as it's
// probably really obvious there's no warranty of any kind on it.
// You can do whatever you want with this source; however a credit in any
// program that uses part of this code would be really appreciated :)
//
// 欢迎给予任何评价和建议 :)
//
//                                  Andrea "6502" Griffini, programmer
//                                         agriff@ix.netcom.com
//                                      http://vv.val.net/~agriffini
//
// 译者 注:
// 我在学习 3D 地表的生成算法时有幸拜读了这段程序,深受启发.
// 原来的程序已经是很清晰了, 但作者还是加入了少许优化. 我将仅有的
// 优化也去掉了 ;) 想使程序更容易被理解. 大多数英文注解被我用汉语
// 从写了. 并在许多地方描述的更详细. 希望能对大家有更多的帮助 :-)
// btw, 原来的程序是用 Watcom C 写的, 云风用 Djgpp 重写了
// 这个程序写的极为清晰, 所以它没有过多的效果修饰, 如果你能花上一定
// 时间 (云风花了 10 分钟 ;) 读懂它, 就可以进一步的增加边缘平滑, 背景
// 图案等等效果. 这些云风在自己的程序里都加上了, 并对程序做了许多优化,
// 但是为了让大家更容易的读懂, 还是在这里保留了程序的原貌.
//
// 有任何问题, 欢迎和我讨论 :)
//                                   云风  cloudwu@163.net
//                               http://www.nease.net/~cloudwu
//
//
//as3 code @author sliz http://game-develop.net/blog/
//
//
package {
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.filters.BlurFilter;
    import flash.utils.ByteArray;
    import net.hires.debug.Stats;
    [SWF(frameRate=60)]
    public class Newvoxc extends Sprite {
        private var W:int = 465;
        private var H:int = 465;
        private var HP1:int = H - 1;
        
        private var CMap:Vector.<Vector.<uint>> = createArray(256, 256); //[256][256];     // 色彩值数组
        private var HMap:Vector.<Vector.<uint>> = createArray(256, 256); //=[256][256];     // 地表高度数组
        private var Video:ByteArray=new ByteArray //[320*200];     // 屏幕缓冲区


        private var lasty:Vector.<uint> = new Vector.<uint>(W); //[320],         // 画在指定列上的最后一个点
        private var lastc:Vector.<uint> = new Vector.<uint>(W);  //[320];         // 最后一点的颜色
        
        private var FOV:Number = Math.PI / 4; // 45 度宽的视角

        private var view:Bitmap = new Bitmap(new BitmapData(W, H, false, 0xff0000));

        private var ss:Number, sa:Number, a:Number, s:Number;
        private var x0:int, y0:int;
        
        private var input:Input;

        public function Newvoxc() {
            addChild(view);
            input = new Input(stage);
            var i:int, k:int;
            //
            // 计算地图高度
            //
            ComputeMap();
            //
            // 主循环
            //
            //   a     = 角度
            //   x0,y0 = 当前坐标
            //   s     = 固定速度
            //   ss    = 当前向前/向后的速度
            //   sa    = 旋转角速度
            //
            a = 0;
            k = x0 = y0 = 0;
            s = 4096;
            ss = 8090;
            sa = 0.1;

            addEventListener(Event.ENTER_FRAME, update);
            addChild(new Stats);
        }

        private function update(e:Event):void {
            if (input.left) {
                sa -= 0.005;
            }else if(input.right){
                sa += 0.005;
            }else {
                sa = 0;
            }
            if (input.up) {
                ss += s;
            }else if(input.down){
                ss -= s;
            }
            a += sa;
            
            
            //
            // 刷新位置/角度
            //
            x0 += ss * Math.cos(a);
            y0 += ss * Math.sin(a);
            //
            // 画一帧
            //
            View(x0, y0, a+(mouseX-stage.stageWidth/2)*4/stage.stageWidth);
        }

        //
        // 将值限制在 0..255 之间
        //
        private function Clamp(x:int):int {
            return Math.max(0, Math.min(x, 255));
        }

        //
        // 取 x 的低字节位, 即对 255 取模 (HMap 和 CMap 都是 256 x 256 的数组)
        //
        private function L(x:int):int {
            return x & 0xff;
        }

        private function createArray(x:int, y:int):Vector.<Vector.<uint>> {
            var arr:Vector.<Vector.<uint>> = new Vector.<Vector.<uint>>(x);
            for (var i:int = 0; i < x; i++){
                arr[i]=new Vector.<uint>(y);
            }
            return arr;
        }

        private function rand():int {
            return 2147483647 * Math.random();
        }

        //
        // 地表高度和色彩表的计算
        //
        private function ComputeMap():void {

            var p:int, i:int, j:int, k:int, k2:int, p2:int;

            //
            // 从一个平坦的地表开始
            //
            HMap[0][0] = 128;
            for (p = 256; p > 1; p = p2){
                p2 = p / 2;
                k = p * 8 + 20;
                k2 = k / 2;
                for (i = 0; i < 256; i += p){
                    for (j = 0; j < 256; j += p){
                        var a:int, b:int, c:int, d:int;
                        a = HMap[i][j]; //   a:::::::b
                        b = HMap[L(i + p)][j]; //   :::::::::    以 a,b,c,d
                        c = HMap[i][L(j + p)]; //   :::::::::    为角的区域
                        d = HMap[L(i + p)][L(j + p)]; //   c:::::::d
                        HMap[i][L(j + p2)] = //  在 a,c 中点,以a,c平均高度为基准
                            Clamp(((a + c) >> 1) + (rand() % k - k2)); //  产生一随机的高度
                        HMap[L(i + p2)][L(j + p2)] = //  在 a,b,c,d 区域中心,以平均高度
                            Clamp(((a + b + c + d) >> 2) + (rand() % k - k2)); // 为基准,产生一随机高度
                        HMap[L(i + p2)][j] = //  在 a,b 中点,以a,b平均高度为基准
                            Clamp(((a + b) >> 1) + (rand() % k - k2)); //  产生一随机的高度
                    }
                }
            }
            //
            // 平滑处理
            //
            for (k = 0; k < 3; k++)
                for (i = 0; i < 256; i++)
                    for (j = 0; j < 256; j++){
                        HMap[i][j] = (HMap[L(i + 1)][j] + HMap[i][L(j + 1)] + //将前后左右,四个点取
                            HMap[L(i - 1)][j] + HMap[i][L(j - 1)]) / 4; //平均值,这样做平滑
                    }
            //
            // 颜色计算 (地表高度的衍生物)
            //
            for (i = 0; i < 256; i++)
                for (j = 0; j < 256; j++){
                    k = 128 + (HMap[L(i + 1)][L(j + 1)] - HMap[i][j]) * 4;
                    CMap[i][j] = Clamp(k); // 以坡度决定灰度
                }
        }

        //
        // 画地表的一个"部分"; 它能画出距离视点一定远处的图象
        // 使用 lasty 数组中保存的上次画过的位置, 保正了这个部分不会
        // 覆盖掉以前画的部分. x0,y0 和 x1,y1 和 xy 坐标描述
        // 地表的高度, hy 是视点的高度, s 是由距离决定的比例因子.
        // x0,y0,x1,y1 是 16.16 的定点数,
        // 比例因子是 16.8 的定点值.
        //
        private function Line(x0:int, y0:int, x1:int, y1:int, hy:int, s:int):void {
            var i:int, sx:int, sy:int;
            // 计算 xy 速度
            sx = (x1 - x0) / W;
            sy = (y1 - y0) / W;
            for (i = 0; i < W; i++){
                var c:int, y:int, h:int, u0:int, v0:int, u1:int, v1:int, a:int, b:int, h0:int, h1:int, h2:int, h3:int;
                //
                // 计算 xy 坐标; a 和 b 将被定位于
                // 一个 (0..255)(0..255) 的区间里面.
                //
                u0 = (x0 >> 16)&0xff;
                a = (x0 >> 8)&0xff;
                v0 = (y0 >> 16)&0xff;
                b = (y0 >> 8)&0xff;
                u1 = (u0 + 1)&0xff;
                v1 = (v0 + 1)&0xff;
                //
                // 由周围 4 个点来决定里面的高度
                //
                h0 = HMap[v0][u0];
                h2 = HMap[v1][u0];
                h1 = HMap[v0][u1];
                h3 = HMap[v1][u1];

                h0 = (h0 << 8) + a * (h1 - h0);
                h2 = (h2 << 8) + a * (h3 - h2);
                h = ((h0 << 8) + b * (h2 - h0)) >> 16;
                //
                // 由周围 4 个点来决定里面的颜色 (颜色值是 16.16 的定点数)
                //
                h0 = CMap[v0][u0];
                h2 = CMap[v1][u0];
                h1 = CMap[v0][u1];
                h3 = CMap[v1][u1];
                
                h0 = (h0 << 8) + a * (h1 - h0);
                h2 = (h2 << 8) + a * (h3 - h2);
                c = ((h0 << 8) + b * (h2 - h0));
                //
                // 使用比例因子计算屏幕高度
                //
                y = (((h - hy) * s) >> 11) + 100;
                //
                // 画一列
                //
                if (y < (a = lasty[i])){
                    var sc:int, cc:int;
                    if (lastc[i] == -1)
                        lastc[i] = c;
                    sc = (c - lastc[i]) / (a - y);
                    cc = lastc[i];
                    if (a > HP1){
                        b -= (a - HP1) * W;
                        cc += (a - HP1) * sc;
                        a = HP1;
                    }
                    if (y < 0)
                        y = 0;
                    
                    while (y < a){
                        var bc:uint = cc >>17;
                        var bi:int = 4*(a * W + i);
                        Video[bi] = bc; 
                        Video[++bi] = bc; 
                        Video[++bi] = bc; 
                        Video[bi] = bc; 
                        cc += sc;
                        b -= W;
                        a--;
                    }
                    lasty[i] = y;
                }
                lastc[i] = c;
                //
                // 进一步计算下一个 xy 坐标
                //
                x0 += sx;
                y0 += sy;
            }
        }


        //
        // 画出从点 x0,y0 (16.16) 以 a 角 看到的图象
        //
        private function View(x0:int, y0:int, aa:Number):void {
            var d:int;
            var a:int, b:int, h:int, u0:int, v0:int, u1:int, v1:int, h0:int, h1:int, h2:int, h3:int;
            //
            // 清除屏幕缓冲
            //
            // memset(Video,0,320*200);
            Video.clear();
            Video.length = W * H*4;
            //
            // 初始化 last-y 和 last-color 数组
            //
            for (d = 0; d < W; d++){
                lasty[d] = H;
                lastc[d] = -1;
            }
            //
            // 计算视点高度变量
            //
            // 计算 xy 坐标; a 和 b 将被定位于
            // 一个 (0..255)(0..255) 的区间里面.
            //
            u0 = (x0 >> 16) & 0xFF;
            a = (x0 >> 8) & 255;
            v0 = (y0 >> 16) & 0xFF;
            b = (y0 >> 8) & 255;
            u1 = (u0 + 1) & 0xFF;
            v1 = (v0 + 1) & 0xFF;
            //
            // 由周围 4 个点来决定里面的高度
            //
            h0 = HMap[v0][u0];
            h2 = HMap[v1][u0];
            h1 = HMap[v0][u1];
            h3 = HMap[v1][u1];

            h0 = (h0 << 8) + a * (h1 - h0);
            h2 = (h2 << 8) + a * (h3 - h2);
            h = ((h0 << 8) + b * (h2 - h0)) >> 16;
            //
            // 无覆盖的由近及远画地表
            //
           var sx0:int = 65536 * Math.cos(aa - FOV);
           var sy0:int = 65536 * Math.sin(aa - FOV);
           var sx1:int = 65536 * Math.cos(aa + FOV);
           var sy1:int = 65536 * Math.sin(aa + FOV);
           var hp30:uint = Math.min(60,h - mouseY);
           
            for (d = 0; d < 200; d +=1+(d>>6)){
                Line(x0 + d * sx0, y0 + d * sy0,
                     x0 + d * sx1, y0 + d * sy1, hp30, 25600 / (d+1));
            }
            //
            // 将最终图象 blit 到屏幕
            //
            //_movedatal(_my_ds(), (unsigned)Video, _dos_ds, 0xa0000,
            //              16000); //320*200/4
            view.bitmapData.lock();
            view.bitmapData.setPixels(view.bitmapData.rect, Video);
            view.bitmapData.unlock();
        }
    }

}

import flash.display.Stage;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
class Input 
{
    private var stage:Stage;
    private var leftKeyCodes:Array = [Keyboard.LEFT,"A".charCodeAt(0)];
    private var rightKeyCodes:Array = [Keyboard.RIGHT,"D".charCodeAt(0)];
    private var downKeyCodes:Array = [Keyboard.DOWN,"S".charCodeAt(0)];
    private var upKeyCodes:Array = [Keyboard.UP, "W".charCodeAt(0)];
    
    public var left:Boolean = false;
    public var right:Boolean = false;
    public var down:Boolean = false;
    public var up:Boolean = false;
    public function Input(stage:Stage) 
    {
        this.stage = stage;
        stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
        stage.addEventListener(KeyboardEvent.KEY_UP, onKeyUp);
    }
    
    private function onKeyUp(e:KeyboardEvent):void 
    {
        if (leftKeyCodes.indexOf(e.keyCode)!=-1) {
            left = false;
        }else if (rightKeyCodes.indexOf(e.keyCode)!=-1) {
            right = false;
        }else if (downKeyCodes.indexOf(e.keyCode)!=-1) {
            down = false;
        }else if (upKeyCodes.indexOf(e.keyCode)!=-1) {
            up = false;
        }
    }
    
    private function onKeyDown(e:KeyboardEvent):void 
    {
        if (leftKeyCodes.indexOf(e.keyCode)!=-1) {
            left = true;
            right = false;
        }else if (rightKeyCodes.indexOf(e.keyCode)!=-1) {
            right = true;
            left = false;
        }else if (downKeyCodes.indexOf(e.keyCode)!=-1) {
            down = true;
            up = false;
        }else if (upKeyCodes.indexOf(e.keyCode)!=-1) {
            up = true;
            left = false;
        }
    }
}