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

3次元座標で描画コマンドを指定して、レンダラーが頂点座標を変換してベクター描画するようなものを目指しました。
Get Adobe Flash player
by Nicolas 31 Jan 2011
    Embed
/**
 * Copyright Nicolas ( http://wonderfl.net/user/Nicolas )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/rHtL
 */

package {
    import flash.display.Sprite;
    import flash.events.Event;
    //本当はBasicViewを継承させる
    public class Main extends Sprite 
    {
        
        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);
            
            //継承できなかったので仕方なく…/////////////////////////////////
            var b:BasicView = addChild( new BasicView() ) as BasicView;
            var camera:Camera3D = b.camera;
            var viewport:Viewport3D = b.viewport;
            var scene:Scene3D = b.scene;
            var startRendering:Function = b.startRendering;
            ////////////////////////////////////////////////////////////
            
            camera.x = 0;
            camera.y = 100;
            camera.z = -1000;
            
            var obj:DisplayObjectContainer3D = new DisplayObjectContainer3D();
            var g:Graphics3D = obj.graphics;
            g.beginFill(0xCCCCCC);
            g.lineStyle(3, 0x000000);
            g.moveTo(100, 30, 100);
            g.lineTo(100, 30, -100);
            g.lineTo(-100, 30, -100);
            g.lineTo( -100, 30, 100);
            g.lineTo(100, 30, 100);
            g.endFill();
            
            var child:DisplayObject3D = new DisplayObject3D();
            g = child.graphics;
            g.beginFill(0x0000CC);
            g.lineStyle(3, 0x000000);
            g.moveTo(100, 0, 100);
            g.lineTo(100, 0, -100);
            g.lineTo(-100, 0, -100);
            g.lineTo( -100, 0, 100);
            g.lineTo(100, 0, 100);
            g.endFill();
            
            obj.addChild(child);
            
            child.x = 200;
            child.rotationX = 90;
            
            scene.addChild(obj);
            startRendering();
            
            addEventListener(Event.ENTER_FRAME, function(e:Event):void {
                
                child.rotationX++;
                
                var theta:Number = mouseY / 100;
                var phi:Number = mouseX / 100;
                if (theta <= 0) theta = Math.PI * 0.01;
                if (theta > Math.PI) theta = Math.PI * 0.99;
                
                camera.x =  -1000 * Math.sin(theta) * Math.cos(phi);
                camera.y = -1000 * Math.cos(theta);
                camera.z =  1000 * Math.sin(theta) * Math.sin(phi);
            });
            
        }
        
    }
}

import flash.display.Sprite;
import flash.display.Graphics;
import flash.events.Event;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
import flash.geom.Utils3D;

class BasicView extends Sprite
    {
        public var camera:Camera3D = new Camera3D();
        public var renderer:Renderer = new Renderer();
        public var scene:Scene3D = new Scene3D();
        public var viewport:Viewport3D = new Viewport3D();
        
        public function BasicView() 
        {
            addChild(viewport);
        }
        
        public function startRendering():void 
        {
            addEventListener(Event.ENTER_FRAME, singleRender);
        }
        
        public function stopRendering():void 
        {
            removeEventListener(Event.ENTER_FRAME, singleRender);
        }
        
        public function singleRender(e:Event = null):void
        {
            renderer.renderScene(scene, camera, viewport);
        }
    }

class Scene3D implements IContainer
    {
        private var children:Vector.<IDisplayObject3D> = new Vector.<IDisplayObject3D>();
        public var vertices:Vector.<Number> = new Vector.<Number>();
        public var commands:Vector.<IGraphicsCommand> = new Vector.<IGraphicsCommand>();
        
        public function Scene3D() 
        {
        }
        
        public function addChild(child:DisplayObject3D):DisplayObject3D
        {
            child._parent = this;
            children.push(child as IDisplayObject3D);
            return child;
        }
        
        public function removeChild(child:DisplayObject3D):DisplayObject3D
        {
            child._parent = null;
            var index:int = children.indexOf(child as IDisplayObject3D);
            if (index != -1) 
            {
                return children.splice(index, 1)[0] as DisplayObject3D;
            }
            else 
            {
                throw ArgumentError("引数のIDisplayObject3Dは、このIContainerオブジェクトの子ではありません。");
                return null;
            }
        }
        
        public function getChildren():Vector.<IDisplayObject3D>
        {
            return children.concat();
        }
        
        public function get numChildren():int 
        {
            return children.length;
        }
    }

class Camera3D
    {
        public var pos:Vector3D;
        public var up:Vector3D;
        public var at:Vector3D;
        public var fov:Number = 60;
        public var near:Number = 100;
        public var far:Number = 500;
        
        public function get x():Number { return pos.x }
        public function set x(value:Number):void { pos.x = value }
        public function get y():Number { return pos.y }
        public function set y(value:Number):void { pos.y = value }
        public function get z():Number { return pos.z }
        public function set z(value:Number):void { pos.z = value }
        
        public function Camera3D() 
        {
            pos = new Vector3D(0, 0, -1000);
            at = new Vector3D(0, 0, 0);
            up = new Vector3D(0, 1, 0);
        }
    }

class Viewport3D extends Sprite
    {
        public function get viewportWidth():Number { return _viewportWidth; }
        public function set viewportWidth(value:Number):void 
        {
            _viewportWidth = value;
            setViewport(_viewportWidth, _viewportHeight);
        }
        private var _viewportWidth:Number;
        
        public function get viewportHeight():Number { return _viewportHeight; }
        public function set viewportHeight(value:Number):void 
        {
            _viewportHeight = value;
            setViewport(_viewportWidth, _viewportHeight);
        }
        private var _viewportHeight:Number;
        
        public function Viewport3D(w:Number = 465, h:Number = 465) 
        {
            _viewportWidth = w;
            _viewportHeight = h;
            setViewport(w, h);
        }
        
        private function setViewport(w:Number, h:Number):void
        {
            x = w / 2;
            y = h / 2;
        }
    }

class Renderer
    {
        private var matrix:Matrix3D = new Matrix3D();
        private var unitMatrixRawData:Vector.<Number> = Vector.<Number>([
            1, 0, 0, 0, 
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        ]);
        private var viewMatrix:Matrix3D;
        private var projectionMatrix:Matrix3D;
        
        public function Renderer() 
        {
        }
        
        public function renderScene(scene:Scene3D, camera:Camera3D, viewport:Viewport3D):void
        {
            viewport.graphics.clear();
            //ビュー変換行列
            viewMatrix = getViewMatrix(camera);
            //プロジェクション変換行列
            projectionMatrix = getProjectionMatrix(camera, viewport);
            //ワールド変換行列以外の行列の統合
            matrix.rawData = unitMatrixRawData;
            matrix.append(viewMatrix);
            matrix.append(projectionMatrix);
            matrix.appendScale(viewport.viewportWidth, viewport.viewportHeight, 1);
            
            //子を探して、最終的な変換行列を計算して、変換後のz座標を計算して、配列に入れる。
            //せっかく変換行列を計算したので、射影変換もここでやっとく。
            //子がコンテナだったらその子を探す、というように、この関数を再帰的に繰り返す。
            var children:Vector.<IDisplayObject3D>;
            var mat:Matrix3D;
            var objects:Array = [];
            searchAndSetDepth(scene);
            function searchAndSetDepth(container:IContainer):void
            {
                children = container.getChildren();    //子の配列を取得
                for each (var child:IDisplayObject3D in children)
                {
                    mat = matrix.clone();                                                                            
                    var obj:DisplayObject3D = child as DisplayObject3D;
                    mat.prepend(obj.getWorldMatrix());                                       //ワールド変換行列を統合
                    obj.depth = mat.transformVector(new Vector3D(obj.x, obj.y, obj.z)).z;    //変換後のz座標を計算
                    obj.graphics.project(mat);                                               //射影変換
                    objects.push(obj)                                                        //配列に入れる
                    if (obj is IContainer) searchAndSetDepth(obj as IContainer);
                }
            }
            
            //Zソート
            objects.sortOn("depth", Array.NUMERIC | Array.DESCENDING);
            
            //描画
            for each (var o:DisplayObject3D in objects)
            {
                o.graphics.render(viewport);
            }
        }
        
        private function getViewMatrix(camera:Camera3D):Matrix3D
        {
            var xAxis:Vector3D;
            var yAxis:Vector3D;
            var zAxis:Vector3D;
            var eye:Vector3D = camera.pos;
            var at:Vector3D = camera.at;
            var up:Vector3D = camera.up;
            //「カメラの座標→ターゲット」の向きの基本ベクトルがz軸
            //zAxis = at.subtract(eye);
            zAxis = new Vector3D(at.x - eye.x, at.y - eye.y, at.z - eye.z)
            zAxis.normalize();
            //「上方向の基準ベクトルとz軸を含む平面」に垂直な向きの基本ベクトルがx軸
            xAxis = up.crossProduct(zAxis);
            xAxis.normalize();
            //x軸とz軸に垂直な向きの基本ベクトルがy軸。(基本ベクトル同士の外積なので長さは1⇒正規化は必要ない)
            yAxis = zAxis.crossProduct(xAxis);
            yAxis.negate();
            var mat:Matrix3D = new Matrix3D();
            mat.rawData = Vector.<Number>([
                xAxis.x, yAxis.x, zAxis.x, 0,
                xAxis.y, yAxis.y, zAxis.y, 0,
                xAxis.z, yAxis.z, zAxis.z, 0,
                -xAxis.dotProduct(eye), -yAxis.dotProduct(eye), -zAxis.dotProduct(eye), 1
            ])
            return mat;
        }
        
        private function getProjectionMatrix(camera:Camera3D, viewport:Viewport3D):Matrix3D
        {
            var mat:Matrix3D = new Matrix3D();
            var aspectRatio:Number = viewport.viewportWidth / viewport.viewportHeight;
            var sy:Number = 1 / Math.tan(camera.fov * 0.5 * Math.PI / 180);
            var sx:Number = sy / aspectRatio;
            var sz:Number = camera.far / (camera.far - camera.near);
            mat.rawData = Vector.<Number>([
                sx, 0, 0, 0,
                0, sy, 0, 0,
                0, 0, sz, 1,
                0, 0, -sz * camera.near, 0
            ]);
            return mat;
        }
    }

class DisplayObject3D implements IDisplayObject3D
    {
        public var x:Number = 0;
        public var y:Number = 0;
        public var z:Number = 0;
        public var rotationX:Number = 0;
        public var rotationY:Number = 0;
        public var rotationZ:Number = 0;
        public var scaleX:Number = 1;
        public var scaleY:Number = 1;
        public var scaleZ:Number = 1;
        public var graphics:Graphics3D;
        
        public var depth:Number = 0;
        internal var _parent:IContainer;
        public function get parent():IContainer { return _parent; }
        
        private var unitMatrixRawData:Vector.<Number> = Vector.<Number>([
            1, 0, 0, 0,
            0, 1, 0, 0,
            0, 0, 1, 0,
            0, 0, 0, 1
        ])
        
        private var matrix:Matrix3D;
        
        public function DisplayObject3D() 
        {
            graphics = new Graphics3D();
            matrix = new Matrix3D();
        }
        
        public function getWorldMatrix():Matrix3D
        {
            matrix.rawData = unitMatrixRawData;
            matrix.appendScale(scaleX, scaleY, scaleZ);
            if (rotationX != 0) matrix.appendRotation(-rotationX, Vector3D.X_AXIS);
            if (rotationY != 0) matrix.appendRotation(-rotationY, Vector3D.Y_AXIS);
            if (rotationZ != 0) matrix.appendRotation(rotationZ, Vector3D.Z_AXIS);
            matrix.appendTranslation(x, y, z);
            if (_parent is DisplayObjectContainer3D) matrix.append((_parent as DisplayObjectContainer3D).matrix);
            return matrix;
        }
        
    }

class DisplayObjectContainer3D extends DisplayObject3D implements IContainer
    {
        private var children:Vector.<IDisplayObject3D> = new Vector.<IDisplayObject3D>();
        public function DisplayObjectContainer3D() 
        {
            
        }
        
        public function addChild(child:DisplayObject3D):DisplayObject3D
        {
            children.push(child as IDisplayObject3D);
            child._parent = this;
            return child;
        }
        
        public function removeChild(child:DisplayObject3D):DisplayObject3D
        {
            child._parent = null;
            var index:int = children.indexOf(child as IDisplayObject3D);
            if (index != -1) 
            {
                return children.splice(index, 1)[0] as DisplayObject3D;
            }
            else 
            {
                throw ArgumentError("引数のIDisplayObject3Dは、このIContainerオブジェクトの子ではありません。");
                return null;
            }
        }
        
        public function getChildren():Vector.<IDisplayObject3D>
        {
            return children.concat();
        }
        
        public function get numChildren():int 
        {
            return children.length;
        }
    }

class Graphics3D
    {
        public var commands:Vector.<IGraphicsCommand> = new Vector.<IGraphicsCommand>();
        public var path3D:Vector.<Number> = new Vector.<Number>();
        public var path2D:Vector.<Number> = new Vector.<Number>();
        private var path2DIndex:uint = 0;
        private var uvts:Vector.<Number> = new Vector.<Number>();
        
        public function Graphics3D() 
        {
        }
        
        public function lineStyle(thickness:Number, color:uint, alpha:Number = 1):void
        {
            commands.push(new LineStyle(thickness, color, alpha));
        }
        
        public function beginFill(color:uint, alpha:Number = 1):void
        {
            commands.push(new BeginFill(color, alpha));
        }
        
        public function endFill():void
        {
            commands.push(new EndFill());
        }
        
        public function moveTo(x:Number, y:Number, z:Number):void
        {
            path3D.push(x, y, z);
            commands.push(new MoveTo(path2DIndex, path2DIndex + 1));//インデックスのみを各コマンドに記憶させておく。lineToなども同様
            path2DIndex += 2;
        }
        
        public function lineTo(x:Number, y:Number, z:Number):void
        {
            path3D.push(x, y, z);
            commands.push(new LineTo(path2DIndex, path2DIndex + 1));
            path2DIndex += 2;
        }
        
        public function curveTo(cx:Number, cy:Number, cz:Number, ax:Number, ay:Number, az:Number):void
        {
            path3D.push(cx, cy, cz, ax, ay, az);
            commands.push(new CurveTo(path2DIndex, path2DIndex + 1, path2DIndex + 2, path2DIndex + 3));
            path2DIndex += 4;
        }
        
        public function clear():void
        {
            commands = Vector.<IGraphicsCommand>();
            path2D = Vector.<Number>();
            path3D = Vector.<Number>();
            path2DIndex = 0;
        }
        
        public function project(mat:Matrix3D):void
        {
            Utils3D.projectVectors(mat, path3D, path2D, uvts);
        }
        
        public function render(viewport:Viewport3D):void
        {
            for each (var c:GraphicsCommandBase in commands)
            {
                c.target = viewport.graphics;
                if (c is IPath) (c as IPath).getCoordinate(path2D);    //インデックスから射影変換後の座標を取得
                c.draw();
            }
        }
    }
    
class GraphicsCommandBase implements IGraphicsCommand
    {
        internal var target:Graphics;
        public function GraphicsCommandBase() 
        {
        }
        
        public function draw():void
        {
        }
    }
    
class BeginFill extends GraphicsCommandBase
    {
        public var color:uint;
        public var alpha:Number;
        public function BeginFill(color:uint, alpha:Number) 
        {
            this.color = color;
            this.alpha = alpha;
        }
        
        override public function draw():void
        {
            target.beginFill(color, alpha);
        }
        
    }
    
class EndFill extends GraphicsCommandBase
    {
        public function EndFill() 
        {
        }
        
        override public function draw():void
        {
            target.endFill();
        }
    }
    
class LineStyle extends GraphicsCommandBase
    {
        public var thickness:Number;
        public var color:uint;
        public var alpha:Number;
        public function LineStyle(thickness:Number, color:uint, alpha:Number) 
        {
            this.thickness = thickness;
            this.color = color;
            this.alpha = alpha;
        }
        
        override public function draw():void
        {
            target.lineStyle(thickness, color, alpha);
        }
    }
    
class MoveTo extends GraphicsCommandBase implements IPath
    {
        public var xIndex:uint;
        public var yIndex:uint;
        
        private var x:Number;
        private var y:Number;
        
        public function MoveTo(xIndex:uint, yIndex:uint) 
        {
            this.xIndex = xIndex;
            this.yIndex = yIndex;
        }
        
        override public function draw():void
        {
            target.moveTo(x, y);
        }
        
        public function getCoordinate(verts:Vector.<Number>):void
        {
            x = verts[xIndex];
            y = verts[yIndex];
        }
    }
    
class LineTo extends GraphicsCommandBase implements IPath
    {
        public var xIndex:uint;
        public var yIndex:uint;
        
        private var x:Number;
        private var y:Number;
        
        public function LineTo(xIndex:uint, yIndex:uint) 
        {
            this.xIndex = xIndex;
            this.yIndex = yIndex;
        }
        
        override public function draw():void
        {
            target.lineTo(x, y);
        }
        
        public function getCoordinate(verts:Vector.<Number>):void
        {
            x = verts[xIndex];
            y = verts[yIndex];
        }
    }
    
class CurveTo extends GraphicsCommandBase implements IPath
    {
        public var controlXIndex:uint;
        public var controlYIndex:uint;
        public var anchorXIndex:uint;
        public var anchorYIndex:uint;
        
        private var controlX:Number;
        private var controlY:Number;
        private var anchorX:Number;
        private var anchorY:Number;
        
        public function CurveTo(controlXIndex:uint, controlYIndex:uint, anchorXIndex:uint, anchorYIndex:uint) 
        {
            this.controlXIndex = controlXIndex;
            this.controlYIndex = controlYIndex;
            this.anchorXIndex = anchorXIndex;
            this.anchorYIndex = anchorYIndex;
        }
        
        override public function draw():void
        {
            target.curveTo(controlX, controlY, anchorX, anchorY);
        }
        
        public function getCoordinate(verts:Vector.<Number>):void
        {
            controlX = verts[controlXIndex];
            controlY = verts[controlYIndex];
            anchorX = verts[anchorXIndex];
            anchorY = verts[anchorYIndex];
        }
    }

interface IDisplayObject3D {}    //同じVectorで扱うための飾り

interface IContainer 
    {
        function addChild(child:DisplayObject3D):DisplayObject3D;
        function removeChild(child:DisplayObject3D):DisplayObject3D;
        function getChildren():Vector.<IDisplayObject3D>
        function get numChildren():int;
    }
    
interface IGraphicsCommand
    {
        function draw():void;
    }
    
interface IPath
    {
        function getCoordinate(verts:Vector.<Number>):void;
    }