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

3D Rendering Engine

Arrow keys to rotate the cube. Can add also render a uv map, but took out some of the code.
Get Adobe Flash player
by bennett.yeates 23 May 2015
    Embed
/**
 * Copyright bennett.yeates ( http://wonderfl.net/user/bennett.yeates )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/oNMK
 */

package

{

    import flash.display.Bitmap;

    import flash.display.BitmapData;

    import flash.display.Sprite;

    import flash.events.Event;

    import flash.events.KeyboardEvent;

    import flash.ui.Keyboard;

    

    /**

     * ...

     * @author Bennett Yeates

     * 

     * */

    [SWF(frameRate=30, backgroundColor=0x0)]

    public class Main extends Sprite

    {

        /*=========================================================================================

        VARS

        =========================================================================================*/

        // =============================

        // PUBLIC

        // =============================

        public var square:Object3D;

       

        // =============================

        // PROTECTED

        // =============================

        protected var _renderer:Renderer;

        protected var _canvas:BitmapData;

        

        // =============================

        // PRIVATE

        // =============================

        // list of key presses

        private static var _keys:Object;

        

        // =============================

        // CONST

        // =============================

        private static const GRID_LINE_WIDTH:int = 1;

        private static const GRID_LINE_COLOR:int = 0xFFFFFF;

        private static const GRID_COLUMN_SIZE:int = 25;

        private static const GRID_ROW_SIZE:int = 25;

        private static const MOVE_PIXELS_BY:int = 10;



        /*=========================================================================================

        CONSTRUCTOR

        =========================================================================================*/

        public function Main()

        {

            _canvas = new BitmapData( stage.stageWidth, stage.stageHeight, false, 0 );

            

            // comment this out if grid lines are not wanted

            //createGrid();

            _keys = {};



            addChild( new Bitmap( _canvas ) );



            _renderer = new Renderer( _canvas );



            addEventListener( Event.ENTER_FRAME, update );

            stage.addEventListener( KeyboardEvent.KEY_DOWN, onKeyDown );

            stage.addEventListener( KeyboardEvent.KEY_UP, onKeyUp );



            var mesh:CubeMesh = new CubeMesh();

            mesh.setUVData();



            square = new Object3D( mesh );

            square.scale( 50, 50, 1 );

            square.translate( stage.stageWidth/2, stage.stageHeight/2, 0 );

            _renderer.renderObject( square );

        }

        

        protected function onKeyDown( e:KeyboardEvent ):void

        {

            _keys[ e.keyCode ] = true;

        }

        

        protected function onKeyUp( e:KeyboardEvent ):void

        {

            _keys[ e.keyCode ] = false;

        }



        protected function update(event:Event):void

        {

            var doRedraw:Boolean;

            for ( var key:String in _keys )

            {

                if ( _keys[ key ] === true )

                {

                    switch( int(key) )

                    {

                        case Keyboard.DOWN:

                            square.rotationX += MOVE_PIXELS_BY;

                            doRedraw = true;

                            break;



                        case Keyboard.UP:

                            square.rotationX -= MOVE_PIXELS_BY;

                            doRedraw = true;

                            break;



                        case Keyboard.LEFT:

                            square.rotationY -= MOVE_PIXELS_BY;

                            doRedraw = true;

                            break;



                        case Keyboard.RIGHT:

                            square.rotationY += MOVE_PIXELS_BY;

                            doRedraw = true;

                            break;



                    }

                }

            }



            if ( doRedraw )

            {

                square.updateTransformVertices();

                redraw();

            }

        }



        private function redraw():void

        {

            _renderer.clear();

            _renderer.renderObject( square );

        }

        

        private function createGrid():void

        {

            var grid:Sprite = new Sprite();

            grid.graphics.lineStyle(GRID_LINE_WIDTH, GRID_LINE_COLOR);

           

            var columns:int = stage.stageWidth / GRID_COLUMN_SIZE;

            var rows:int = stage.stageWidth / GRID_ROW_SIZE;

            

            for ( var i:int = 0; i < columns; ++i )

            {

                grid.graphics.drawRect( i*GRID_COLUMN_SIZE, 0, GRID_LINE_WIDTH, stage.stageHeight );

            }

            

            for ( i = 0; i < rows; ++i )

            {

                grid.graphics.drawRect( 0, i*GRID_ROW_SIZE, stage.stageWidth, GRID_LINE_WIDTH );

            }

            

            _canvas.draw( grid )

        }

    }

}



import flash.display.BitmapData;

import flash.display.Bitmap;

import flash.display.Sprite;

import flash.geom.Point;

import flash.geom.Vector3D;

import flash.geom.Matrix3D;



class VertexData

{

    public var vector:Vector3D;    

    

     // values 0-1 (representing 0-255)

    public var red:Number;

    public var green:Number;

    public var blue:Number;

        

    public var u:Number;

    public var v:Number;

        

    public var uvMap:BitmapData;

    public var clamp:Boolean;

        

    public function VertexData( x:Number=0, y:Number=0, z:Number=0 )

    {

        vector = new Vector3D( x, y, z );

    }

        

    public function setUV( map:Bitmap, u:Number=0, v:Number=0 ):void

    {

        this.u = u;

        this.v = v;

        uvMap = map.bitmapData;

    }

        

    public function setColorData( red:Number=1, green:Number=1, blue:Number=1 ):void

    {

        this.red = red;

        this.green = green;

        this.blue = blue;

    }

        

    /** returns a copy of this vertex instance */

    public function clone():VertexData

    {

        var vertex:VertexData = new VertexData( x, y, z );

        vertex.u = this.u;

        vertex.v = this.v;

        vertex.uvMap = uvMap;

        vertex.clamp = this.clamp;

        vertex.setColorData( red, green, blue );

        return vertex;

    }

        

    public function set x( val:Number ):void

    {

        vector.x = val;

    }

        

    public function set y( val:Number ):void

    {

       vector.y = val;

    }

        

    public function set z( val:Number ):void

    {

        vector.z = val;

    }

        

    public function get x():Number

    {

        return vector.x;

    }

        

    public function get y():Number

    {

        return vector.y;

    }

        

    public function get z():Number

    {

        return vector.z;

    }

        

    /** returns pixel data without alpha */

    public function getUVPixel( up:Number, vp:Number, pixelType:String="getPixel" ):Number

    {

        if ( clamp )

        {

            if ( up > 1 )

            {

                up = 1;

            }

            if ( vp > 1 )

            {

                vp = 1;

            }

        }

        else

        {

            if ( up > 1 )

            {

                up %= 1;

            }

            if ( vp > 1 )

            {

                vp %= 1;

            }

        }

        return uvMap[ pixelType ]( uvMap.width * up, uvMap.height * vp );

    }

}



class CubeMesh

{

    public var triangles:Vector.<Vector.<VertexData>>

    

   public function CubeMesh()

   {

       triangles = new Vector.<Vector.<VertexData>>();

       // =============================

       // FRONT FACE

       // =============================

            

       // top tri

        triangles.push(

           new <VertexData>

           [

               new VertexData( -1, -1, 1 ),

               new VertexData(  1, -1, 1 ),

               new VertexData( -1,  1, 1 )

            ]);



            // bottom tri

            triangles.push(

                new <VertexData>

                [

                    new VertexData( 1, 1, 1 ),

                    triangles[0][2],

                    triangles[0][1]

                ]);



            // =============================

            // LEFT FACE

            // =============================

           triangles.push(

                new <VertexData>

                [

                    new VertexData( -1, -1, -1 ),

                    new VertexData( -1, -1,  1 ),

                    new VertexData( -1,  1, -1 )

                ]);

            // bottom tri

            triangles.push(

                new <VertexData>

                [

                    new VertexData( -1,  1,  1 ),

                    triangles[2][2],

                    triangles[2][1]

                ]);



            // =============================

            // BACK FACE

            // ===================

              triangles.push(

                new <VertexData>

                [

                    new VertexData(  1, -1, -1 ),

                    new VertexData( -1, -1, -1 ),

                    new VertexData(  1,  1, -1 )

                ]);



            // bottom tri

            triangles.push(

                new <VertexData>

                [

                    new VertexData( -1, 1, -1 ),

                    triangles[4][2],

                    triangles[4][1]

                ]);



            // =============================

            // RIGHT FACE

            // =============================

            triangles.push(

                new <VertexData>

                [

                    new VertexData(  1, -1,  1 ),

                    new VertexData(  1, -1, -1 ),

                    new VertexData(  1,  1,  1 )

                ]);



            // bottom tri

            triangles.push(

                new <VertexData>

                [

                    new VertexData( 1, 1, -1 ),

                    triangles[6][2],

                    triangles[6][1]

                ]);



            // =============================

            // TOP FACE

            // =============================

            triangles.push(

                new <VertexData>

                [

                    new VertexData( -1, -1, -1 ),

                    new VertexData(  1, -1, -1 ),

                    new VertexData( -1, -1,  1 )

                ]);



            // bottom tri

            triangles.push(

                new <VertexData>

                [

                    new VertexData( 1, -1, 1 ),

                    triangles[8][2],

                    triangles[8][1]

                ]);

            // =============================

            // BOTTOM FACE

            // =============================

            triangles.push(

                new <VertexData>

                [

                    new VertexData( -1, 1,  1 ),

                    new VertexData(  1, 1,  1 ),

                    new VertexData( -1, 1, -1 )

                ]);



            // bottom tri

            triangles.push(

                new <VertexData>

                [

                    new VertexData( 1, 1, -1 ),

                    triangles[10][2],

                    triangles[10][1]

                ]);

        }



        public function setUVData():void

        {

            for (var i:int = 0; i < triangles.length-1; i += 2)

            {

                // setting a random color for now

                triangles[i][0].setColorData( Math.random(), Math.random(), Math.random() );

                triangles[i][1].setColorData( Math.random(), Math.random(), Math.random() );

                triangles[i][2].setColorData( Math.random(), Math.random(), Math.random() );

                triangles[i+1][0].setColorData( Math.random(), Math.random(), Math.random() );

            }

        }

    }

    

    class Object3D

    {

       /*=========================================================================================

        VAR

        =========================================================================================*/



        // =============================

        // PUBLIC

        // =============================



        public var mesh:CubeMesh;

        

        // =============================

        // PROTECTED

        // =============================

        protected var _x:Number = 0;

        protected var _y:Number = 0;

        protected var _z:Number = 0;

        protected var _rotationX:Number = 0;

        protected var _rotationY:Number = 0;

        protected var _rotationZ:Number = 0;

        protected var _width:Number = 0;

        protected var _height:Number = 0;

        protected var _depth:Number = 0;

        protected var _scaleX:Number = 0;

        protected var _scaleY:Number = 0;

        protected var _scaleZ:Number = 0;

        protected var _transform:Matrix3D;

        protected var _transformVertices:Vector.<Vector.<VertexData>>;



        // =============================

        // PRIVATE

        // =============================

        /*=========================================================================================

        CONSTRUCTOR

        =========================================================================================*/

        public function Object3D( mesh:CubeMesh )

        {

            _transform = new Matrix3D();

            _transformVertices = new Vector.<Vector.<VertexData>>;

            this.mesh = mesh;

            populateTransformVertices();

            setWidthAndHeight();

        }



        private function setWidthAndHeight():void

        {

            var minX:Number = int.MAX_VALUE;

            var maxX:Number = 0;

            var minY:Number = int.MAX_VALUE;

            var maxY:Number = 0;



            for ( var i:int; i < mesh.triangles.length; ++i )

            {

                for ( var j:int=0; j < mesh.triangles[i].length; ++j )

                {

                    minX = mesh.triangles[i][j].x < minX ? mesh.triangles[i][j].x : minX;

                    minY = mesh.triangles[i][j].y < minY ? mesh.triangles[i][j].y : minY;

                    maxX = mesh.triangles[i][j].x > maxX ? mesh.triangles[i][j].x : maxX;

                    maxY = mesh.triangles[i][j].y > maxY ? mesh.triangles[i][j].y : maxY;

                }

            }

            _width = maxX - minX;

            _height = maxY - minY;

        }



        /** scale - scale the transformation matrix */

        public function scale( scaleX:Number, scaleY:Number, scaleZ:Number ):void

        {

            _scaleX = scaleX;

            _scaleY = scaleY;

            _scaleZ = scaleZ;        



            setWidthAndHeight();

            updateTransformVertices();

        }



        /** translate - move the transformation matrix */

        public function translate( xval:Number, yval:Number, zval:Number ):void

        {

            _x = xval;

            _y = yval;

            _z = zval;



            updateTransformVertices();

        }

        

        /** populateTransformVertices - creates the list of vertex data based on the mesh */

        protected function populateTransformVertices():void

        {

            _transformVertices.length = 0;

            for ( var i:int; i < mesh.triangles.length; ++i ) 

            {

                _transformVertices.push( new Vector.<VertexData> );

                

                for ( var j:int = 0; j < mesh.triangles[i].length; ++j ) 

                {

                    _transformVertices[i].push( mesh.triangles[i][j].clone() );

                }

            }

        }

        

        /** updateTransformVertices - updates the vertices according to the transform */

        public function updateTransformVertices():void

        {

            //var pivot:Vector3D = new Vector3D( _x + _width/2, _y + _height/2, _z + _depth/2 );

            populateTransformVertices();

            _transform.identity();

            _transform.appendScale( _scaleX, _scaleY, _scaleZ );

            _transform.appendTranslation( _x, _y, _z );

            _transform.prependRotation( _rotationX, Vector3D.X_AXIS );

            _transform.prependRotation( _rotationY, Vector3D.Y_AXIS );

            _transform.prependRotation( _rotationZ, Vector3D.Z_AXIS );

            

            for ( var i:int; i < _transformVertices.length; ++i ) 

            {

                for ( var j:int = 0; j < _transformVertices[i].length; ++j ) 

                {

                    var vertex:VertexData = _transformVertices[i][j];

                    vertex.vector = _transform.transformVector( vertex.vector );

                    vertex.vector.x = Math.round( vertex.vector.x );

                    vertex.vector.y = Math.round( vertex.vector.y );

                    vertex.vector.z = Math.round( vertex.vector.z );

                }

            }

        }        



        /** returns the list of vertex data according to the transforms */

        public function get triangles():Vector.<Vector.<VertexData>>

        {

            return _transformVertices;

        }

        

        public function get rotationX():Number

        {

            return _rotationX;

        }

        

        public function set rotationX(value:Number):void

        {

            _rotationX = value%360;

        }  



        public function get rotationY():Number

        {

            return _rotationY;

        }

        

        public function set rotationY(value:Number):void

        {

            _rotationY = value%360;

        }

        

        public function get rotationZ():Number

        {

            return _rotationZ;

        }

        

        public function set rotationZ(value:Number):void

        {

            _rotationZ = value%360;

        }



        public function get x():Number

        {

            return _x;

        }



        public function set x(value:Number):void

        {

            _x = value;

        }



        public function get y():Number

        {

            return _y;

        }



        public function set y(value:Number):void

        {

            _y = value;

        }



        public function get z():Number

        {

            return _z;

        }



        public function set z(value:Number):void

        {

            _z = value;

        }



        public function get width():Number

        {

            return _width;

        }



        public function set width(value:Number):void

        {

            _width = value;

        }



        public function get height():Number

        {

            return _height;

        }



        public function set height(value:Number):void

        {

            _height = value;

        }



        public function get depth():Number

        {

            return _depth;

        }



        public function set depth(value:Number):void

        {

            _depth = value;

        }

    }



    class Renderer extends Sprite

    {

        /*=========================================================================================

        VARS

        =========================================================================================*/

        // =============================

        // PUBLIC

        // =============================

        public var canvas:BitmapData;

        public var ambient:Vector3D = new Vector3D( 1, 1, 1 );

           

        /*=========================================================================================

        CONSTRUCTOR

        =========================================================================================*/

        public function Renderer( canvas:BitmapData )

        {        

            this.canvas = canvas;

        }

    

        public function cull( object:Object3D ):Vector.<Vector.<VertexData>>

        {

            var triangles:Vector.<Vector.<VertexData>> = new Vector.<Vector.<VertexData>>;

            for ( var i:int; i < object.triangles.length-1; i += 2 )

            {

                var triangle:Vector.<VertexData> = object.triangles[i];

                var v0:Vector3D = triangle[0].clone().vector;

                var v1:Vector3D = triangle[1].clone().vector;

                var v2:Vector3D = triangle[2].clone().vector;

                

                var n:Vector3D = v1.subtract( v0 ).crossProduct( v2.subtract( v0 ) );

                

                if ( n.z > 0 )

                {

                    triangles.push( triangle );

                    triangles.push( object.triangles[i+1] );

                }

            }

            return triangles;

        }

        

        public function addAmbience( value:Number=0.1 ):void

        {

            ambient.x += value;

            ambient.y += value;

            ambient.z += value;

        }

        

        public function clear():void

        {

            canvas.fillRect( canvas.rect, 0 );

        }



        public function renderObject( object:Object3D ):void

        {

            var triangles:Vector.<Vector.<VertexData>> = cull( object );

            for ( var i:int=0; i < triangles.length; ++i )

            {

                render( triangles[i] );

            }

        }

        

        protected function render( triangle:Vector.<VertexData> ):void

        {

            // not a triangle

            if ( triangle.length != 3 )

            {

                return;

            }



            triangle.sort( sortByHeight );

            

            var firstPass:Boolean;

            

            var sx:Number = triangle[0].x;

            var sy:Number = triangle[0].y;

            var endX:Number = sx;

            var endY:Number = sy;

            var acheight:Number = triangle[2].y - sy;

            var abheight:Number = triangle[1].y - sy;

            

            var acstepX:Number = ( triangle[2].x - sx ) / (triangle[2].y - sy != 0 ? triangle[2].y - sy : 1);

            var abstepX:Number = (triangle[1].x - sx ) / (triangle[1].y - sy != 0 ? triangle[1].y - sy : 1);

            

            var div_ac:Number = acheight == 0 ? 1 : acheight;

            var div_ab:Number = abheight == 0 ? 1 : abheight;



            var stepY:int = triangle[2].y < sy ? -1 : 1;

            

            // a to c vertex steps

            var acstepRed:Number = (triangle[2].red - triangle[0].red) / div_ac;

            var acstepGreen:Number = (triangle[2].green - triangle[0].green) / div_ac;

            var acstepBlue:Number = (triangle[2].blue - triangle[0].blue) / div_ac;

            

            var cvd:VertexData = new VertexData();

            cvd.red = triangle[0].red;

            cvd.green = triangle[0].green;

            cvd.blue = triangle[0].blue;

            

            // ac to b vertex steps

            var abstepRed:Number = (triangle[1].red - triangle[0].red) / div_ab;

            var abstepGreen:Number = (triangle[1].green - triangle[0].green) / div_ab;

            var abstepBlue:Number = (triangle[1].blue - triangle[0].blue) / div_ab;

            

            var bvd:VertexData = new VertexData();

            bvd.red = triangle[0].red;

            bvd.green = triangle[0].green;

            bvd.blue = triangle[0].blue;

            

            var uvstepleft:Point = new Point( (triangle[2].u - triangle[0].u) / div_ac, (triangle[2].v - triangle[0].v) / div_ac );

            var uvleft:Point = new Point( triangle[0].u, triangle[0].v ); 

            

            var uvstepright:Point = new Point( (triangle[1].u - triangle[0].u) / div_ab, (triangle[1].v - triangle[0].v) / div_ab );

            var uvright:Point = new Point( triangle[0].u, triangle[0].v );

            

            while( sy != triangle[2].y )

            {

                sx += acstepX;

                sy += stepY;

                

                cvd.red += acstepRed;

                cvd.green += acstepGreen;

                cvd.blue += acstepBlue;

                

                endX += abstepX;

                endY += stepY;

                

                bvd.red += abstepRed;

                bvd.green += abstepGreen;

                bvd.blue += abstepBlue;

                

                // uv steps

                uvleft.x += uvstepleft.x;

                uvleft.y += uvstepleft.y;

                

                uvright.x += uvstepright.x;

                uvright.y += uvstepright.y;

                

                // rounding this for the loop

                var dist:int = endX < sx ? Math.floor( endX - sx ) : Math.ceil( endX - sx );

                var it:int = dist > 0 ? 1 : -1;

                var dir:int = it;

                var w:int = dist * dir;

                

                // steps across

                var rx:Number = (bvd.red - cvd.red) / w;

                var gx:Number = (bvd.green - cvd.green) / w;

                var bx:Number = (bvd.blue - cvd.blue) / w;

                

                var xvd:VertexData = new VertexData();

                xvd.red = cvd.red;

                xvd.green = cvd.green;

                xvd.blue = cvd.blue;

                

                // steps across uv

                var uvx:Number = (uvright.x - uvleft.x) / w;

                var uvy:Number = (uvright.y - uvleft.y) / w;

                var uv:Point = new Point( uvleft.x, uvleft.y );

                

                var i:int = 0;

                while( i != dist )

                {
                    // for uv map
                    //var color:Number = triangle[0].getUVPixel( uv.x, uv.y );
                    //canvas.setPixel( sx + i, sy, applyAmbience( color ));
                    canvas.setPixel( sx + i, sy, toUint( xvd ));
                    i += it;

                    

                    xvd.red += rx;

                    xvd.green += gx;

                    xvd.blue += bx;

                    

                    uv.x += uvx;

                    uv.y += uvy;

                }



                if ( !firstPass && ((stepY < 0 && sy <= triangle[1].y) || (stepY > 0 && sy >= triangle[1].y)))

                {

                    var h:Number = triangle[2].y - triangle[1].y != 0 ? triangle[2].y - triangle[1].y : 1;

                    h *= stepY;

                    abstepX = ( triangle[2].x - triangle[1].x ) / h;

                    firstPass = true;



                    // this is altered, it's really b-c step

                    abstepRed = ( triangle[2].red - bvd.red ) / h;

                    abstepGreen = ( triangle[2].green - bvd.green ) / h;

                    abstepBlue = ( triangle[2].blue - bvd.blue ) / h;



                    uvstepright = new Point( (triangle[2].u - triangle[1].u ) / h, (triangle[2].v - triangle[1].v) / h );

                    uvright = new Point( triangle[1].u, triangle[1].v );

                }

            }

         }

        

        /** applyAmbience - returns color multiplied by ambient light */

        protected function applyAmbience( color:uint ):uint

        {

            var rgb:Vector3D = toRGB( color );

            // red

            rgb.x *= ambient.x;

            

            // green

            rgb.y *= ambient.y;

            

            // blue

            rgb.z *= ambient.z;

            

            return (int(rgb.x) << 16) + (int(rgb.y) << 8) + int(rgb.z);

        }

        

        /** sorting method for returning vector ordered by y position low to high */

        protected function sortByHeight( a:VertexData, b:VertexData ):Number

        {

            return a.y - b.y;

        }

        

        /** sorting method for returning vector ordered by x position low to high */

        protected function sortByWidth( a:VertexData, b:VertexData ):Number

        {

            return a.x - b.x;

        }

        

        /** sorting method for returning vector ordered by z position low to high */

        protected function sortByZ( a:VertexData, b:VertexData ):Number

        {

            return a.z - b.z;

        }

        

        protected function toRGB( color:uint ):Vector3D

        {

            var r:Number = color >> 16 & 0xFF;

            var g:Number = color >> 8 & 0xFF;

            var b:Number = color & 0xFF;

            return new Vector3D( r, g, b );

        }



        protected function toUint( vertex:VertexData ):uint

        {

            var r:Number = ( vertex.red * 255 ) << 16;

            var g:Number = ( vertex.green * 255 ) << 8;

            var b:Number = vertex.blue * 255;

            return r + g + b;

        }

        

        protected function getWidth( triangle:Vector.<VertexData> ):Number

        {

            triangle.sort( sortByWidth );

            return triangle[2].x - triangle[0].x;

        }

}



    class RGB

    {

        // 0-255 rgb values

        public var red:uint;

        public var blue:uint;

        public var green:uint;  

    

        public function RGB( red:uint=0, green:uint=0, blue:uint=0 )

        {

            this.red = red;

            this.blue = blue;

            this.green = green;

        }

        

        public function toUint():uint

        {

            return red << 16 | green << 8 | blue;

        }

    }