Alternativa3D and Ash Framework Template

by Glidias forked from Batched GPU Directional Sprite testing on texture atlas (diff: 1440)
Boilerplate template for WonderFL combining Ash game framework and Alternativa3D engine.
♥0 | Line 1781 | Modified 2014-04-04 03:48:32 | MIT License
play

ActionScript3 source code

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

package 
{
    import alternativa.engine3d.core.Object3D;
    import alternativa.engine3d.core.Resource;
    import alternativa.engine3d.materials.FillMaterial;
    import alternativa.engine3d.materials.TextureMaterial;
    import alternativa.engine3d.primitives.Plane;
    import flash.display.Bitmap;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.ui.Keyboard;
    import flash.utils.ByteArray;
    import alternativa.engine3d.resources.BitmapTextureResource;
    import flash.display.MovieClip;
    import flash.events.KeyboardEvent;
    import flash.geom.Vector3D;
    /**
     * Basic boilerplate test for Alternativa3D and Ash.
     * Use F7 key to freeze camera for taking screenshot.
     * @author Glidias
     */
    public class Alternativa3DAshFLTemplate extends MovieClip 
    {
        private var _template3D:MainView3D;
        private var engine:Engine;
        private var ticker:FrameTickProvider;


        
        public function Alternativa3DAshFLTemplate() 
        {
            engine  = new Engine();
            Wonderfl.disable_capture();
            
            ReflectUtil.registerComponents([ Object3D]);
            
        
    
            addChild( _template3D = new MainView3D() );
            _template3D.onViewCreate.add(onReady3D);
            
                
        
        }
        
        private function onReady3D():void 
        {
            //SpawnerBundle.context3D = _template3D.stage3D.context3D;
             stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
            
            //engine.addSystem( new RenderingSystem(_template3D.scene), 1 );

            
            var spectatorPerson:SimpleObjectController =new SimpleObjectController( 
                        stage, 
                        _template3D.camera, 
                        155,
                        4);
            
                        
        
    
            
            engine.addSystem( spectatorPerson, 2) ;
        
            
            
            setupCustomEnvironment();

            
            // Setup floor and some basic parameters
            
            var plane:Plane = new Plane(4000, 4000, 1, 1, false, false, null, new FillMaterial(0x111111));
        
            plane.z = -1;
            _template3D.scene.addChild(plane);
        
            _template3D.camera.z = 200;
            spectatorPerson.setObjectPosXYZ(0, 0, 200);
            spectatorPerson.lookAtXYZ(0, 500, 0);
            
            uploadResources(_template3D.scene.getResources(true));
            _template3D.render();
            
            // Let's go
    
            ticker = new FrameTickProvider(stage);
            ticker.add(tick);
            ticker.start();
            
            
            
        }
        
        // Do whatever custom environment setups here!
        private function setupCustomEnvironment():void 
        {
            
        }
        
  private function onKeyDown(e:KeyboardEvent):void 
        {
            var kc:uint = e.keyCode;
            if (kc === Keyboard.F7) {
                _scrnie=_template3D.takeScreenshot(screenieMethod2);
            }
        }
        
       
        
         private function screenieMethod2():Boolean 
        {
            stage.addEventListener(MouseEvent.CLICK, removeScreenie);
            return false;
        }
        private var _scrnie:Bitmap;
          private function removeScreenie(e:Event=null):void {
            if (_scrnie == null) return;
            stage.removeEventListener(MouseEvent.CLICK, removeScreenie);
            _scrnie.parent.removeChild(_scrnie);
            _scrnie = null;
        }
        
        public   function uploadResources(vec:Vector.<Resource>):void {
            var i:int = vec.length;
            while (--i > -1) {
                vec[i].upload(_template3D.stage3D.context3D);
            }
        }
        
        private function tick(time:Number):void 
        {
           

       
            engine.update(time);
            _template3D.render();
        }
        
    }

}


    /**
     * Bare bones main view3d class
     * @author Glenn Ko
     */
    import alternativa.engine3d.core.Camera3D;
    import alternativa.engine3d.core.Object3D;
    import alternativa.engine3d.core.View;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Stage;
    //import systems.rendering.IRenderable;

    //import ash.signals.Signal0;
    import alternativa.engine3d.lights.AmbientLight;
    import alternativa.engine3d.lights.DirectionalLight;
    
    import flash.display.Sprite;
    import flash.display.Stage3D;
    
    import flash.display.StageAlign;
    import flash.display.StageQuality;
    import flash.display.StageScaleMode;
    
    import flash.events.Event;
    import alternativa.engine3d.alternativa3d;
    use namespace alternativa3d;

     class MainView3D extends Sprite  {
        private var _stage:Stage;

       public var onViewCreate:Signal0 = new Signal0();
        
        public var stage3D:Stage3D
        public var camera:Camera3D
        public var scene:Object3D
        
        public var directionalLight:DirectionalLight;
        public var ambientLight:AmbientLight;   
        
        public function MainView3D() {
            addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        protected function init(e:Event = null):void 
        {
            _stage = stage;
            removeEventListener(Event.ADDED_TO_STAGE, init);
            
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.quality = StageQuality.HIGH;            
            
            //Stage3Dを用意
            stage3D = stage.stage3Ds[0];
            //Context3Dの生成、呼び出し、初期化
            stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
            stage3D.requestContext3D();
        }
        
        
        private function onContextCreate(e:Event):void {
            stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
            //View3D(表示エリア)の作成
            var view:View = new View(stage.stageWidth, stage.stageHeight);
            view.antiAlias = 4
            addChild(view);
            
            //Scene(コンテナ)の作成
            scene = new Object3D();

            //Camera(カメラ)の作成
            camera = new Camera3D(1, 100000);
            camera.view = view;
            scene.addChild(camera)
           // ..camera.diagram
           
            addChild(camera.diagram);
            view.hideLogo();

            
            //Lightを追加
            ambientLight = new AmbientLight(0xFFFFFF);
            ambientLight.intensity = 0.5;
            scene.addChild(ambientLight);
            
            //Lightを追加
            directionalLight = new DirectionalLight(0xFFFFFF);
            //手前右上から中央へ向けた指向性light
            directionalLight.x = 0;
            directionalLight.y = -100;
            directionalLight.z = -100;
            directionalLight.lookAt(0, 0, 0);
            scene.addChild(directionalLight);
            //directionalLight.visible = false;
            
        
        
            onViewCreate.dispatch();
            
            
            stage.addEventListener(Event.RESIZE, onStageResize);
        }
        
        private function onStageResize(e:Event):void 
        {
            camera.view.width = stage.stageWidth;
            camera.view.height = stage.stageHeight;
        }
        
        
           public function takeScreenshot( method:Function=null) : Bitmap  //width:int, height:int,
            {
              var view:View = camera.view;

              view.renderToBitmap = true;
              camera.render(stage3D);
             var canvas:BitmapData =  view.canvas.clone();
             // var bitmapData:BitmapData = view.canvas.clone();
              view.renderToBitmap = false;
            //  view.width = oldWidth;
            //  view.height = oldHeight;   
                var child:Bitmap = new Bitmap(canvas);
                stage.addChildAt( child,0 );
             // take screenshot here
                 if (method!= null && method() ) {
                    if (child.parent) child.parent.removeChild(child);
                 }
              return child;
        }
        
        
        
        /*
        public function startRendering():void {
            
            addEventListener(Event.ENTER_FRAME, onRenderTick);
        }
        
        private function onRenderTick(e:Event):void 
        {
            render();
        }
        
        public function stopRendering():void {
             removeEventListener(Event.ENTER_FRAME, onRenderTick);
        }
        */
        

        /* INTERFACE systems.rendering.IRenderable */
        
        public function render():void 
        {
            camera.render(stage3D);
        }
        
        public function get viewBackgroundColor():uint 
        {
            return camera.view.backgroundColor;
        }
        
        public function set viewBackgroundColor(value:uint):void 
        {
            camera.view.backgroundColor = value;
        }
        
     
    }
    

   
    
    
    
//}//package {
    //import Engine;
    //import Node;
    //import NodeList;
    //import System;

    //}
//package {
    //import ash.signals.Signal2;

    import flash.utils.Dictionary;
    import flash.utils.getQualifiedClassName;

    
    /*public*/ class Entity
    {
        private static var nameCount : int = 0;
        
        
        private var _name : String;
        
        public var componentAdded : Signal2;
        
        public var componentRemoved : Signal2;
        
        public var nameChanged : Signal2;
        
        public var previous : Entity;
        public var next : Entity;
        public var components : Dictionary;

        
        public function Entity( name : String = "" )
        {
            componentAdded = new Signal2( Entity, Class );
            componentRemoved = new Signal2( Entity, Class );
            nameChanged = new Signal2( Entity, String );
            components = new Dictionary();
            if( name )
            {
                _name = name;
            }
            else
            {
                _name = "_entity" + (++nameCount);
            }
        }
        
        
        public function get name() : String
        {
            return _name;
        }
        public function set name( value : String ) : void
        {
            if( _name != value )
            {
                var previous : String = _name;
                _name = value;
                nameChanged.dispatch( this, previous );
            }
        }

        
        public function add( component : Object, componentClass : Class = null ) : Entity
        {
            if ( !componentClass )
            {
                componentClass = Class( component.constructor );
            }
            if ( components[ componentClass ] )
            {
                remove( componentClass );
            }
            components[ componentClass ] = component;
            componentAdded.dispatch( this, componentClass );
            return this;
        }

        
        public function remove( componentClass : Class ) : *
        {
            var component : * = components[ componentClass ];
            if ( component )
            {
                delete components[ componentClass ];
                componentRemoved.dispatch( this, componentClass );
                return component;
            }
            return null;
        }

        
        public function get( componentClass : Class ) : *
        {
            return components[ componentClass ];
        }
        
        
        public function getAll() : Array
        {
            var componentArray : Array = new Array();
            for each( var component : * in components )
            {
                componentArray.push( component );
            }
            return componentArray;
        }

        
        public function has( componentClass : Class ) : Boolean
        {
            return components[ componentClass ] != null;
        }
    }
//}
//package {
    
    /*public*/ class System
    {
        
        public var previous : System;
        
        public var next : System;
        
        public var priority : int = 0;
        
        
        public function addToEngine( engine : Engine ) : void
        {
            
        }
        
        
        public function removeFromEngine( engine : Engine ) : void
        {
            
        }
        
        
        public function update( time : Number ) : void
        {
            
        }
    }
//}
//package {
    
    /*public*/ class Node
    {
        
        public var entity : Entity;
        
        
        public var previous : *;
        
        
        public var next : *;
    }
//}



//}


    class ListenerNodePool
    {
        private var tail : ListenerNode;
        private var cacheTail : ListenerNode;
        
        public function get():ListenerNode
        {
            if( tail )
            {
                var node : ListenerNode = tail;
                tail = tail.previous;
                node.previous = null;
                return node;
            }
            else
            {
                return new ListenerNode();
            }
        }

        public function dispose( node : ListenerNode ):void
        {
            node.listener = null;
            node.once = false;
            node.next = null;
            node.previous = tail;
            tail = node;
        }
        
        public function cache( node : ListenerNode ) : void
        {
            node.listener = null;
            node.previous = cacheTail;
            cacheTail = node;
        }
        
        public function releaseCache() : void
        {
            while( cacheTail )
            {
                var node : ListenerNode = cacheTail;
                cacheTail = node.previous;
                node.next = null;
                node.previous = tail;
                tail = node;
            }
        }
    }





    class ListenerNode
    {
        public var previous : ListenerNode;
        public var next : ListenerNode;
        public var listener : Function;
        public var once : Boolean;
    }

    
    
    /*public*/ class ListIteratingSystem extends System
    {
        protected var nodeList : NodeList;
        protected var nodeClass : Class;
        protected var nodeUpdateFunction : Function;
        protected var nodeAddedFunction : Function;
        protected var nodeRemovedFunction : Function;
        
        public function ListIteratingSystem( nodeClass : Class, nodeUpdateFunction : Function, nodeAddedFunction : Function = null, nodeRemovedFunction : Function = null )
        {
            this.nodeClass = nodeClass;
            this.nodeUpdateFunction = nodeUpdateFunction;
            this.nodeAddedFunction = nodeAddedFunction;
            this.nodeRemovedFunction = nodeRemovedFunction;
        }
        
        override public function addToEngine( engine : Engine ) : void
        {
            nodeList = engine.getNodeList( nodeClass );
            if( nodeAddedFunction != null )
            {
                for( var node : Node = nodeList.head; node; node = node.next )
                {
                    nodeAddedFunction( node );
                }
                nodeList.nodeAdded.add( nodeAddedFunction );
            }
            if( nodeRemovedFunction != null )
            {
                nodeList.nodeRemoved.add( nodeRemovedFunction );
            }
        }
        
        override public function removeFromEngine( engine : Engine ) : void
        {
            if( nodeAddedFunction != null )
            {
                nodeList.nodeAdded.remove( nodeAddedFunction );
            }
            if( nodeRemovedFunction != null )
            {
                nodeList.nodeRemoved.remove( nodeRemovedFunction );
            }
            nodeList = null;
        }
        
        override public function update( time : Number ) : void
        {
            for( var node : Node = nodeList.head; node; node = node.next )
            {
                nodeUpdateFunction( node, time );
            }
        }
    }
//}
//package {
    //import ash.signals.Signal1;
    
    
    /*public*/ class NodeList
    {
        
        public var head : *;
        
        public var tail : *;
        
        
        public var nodeAdded : Signal1;
        
        public var nodeRemoved : Signal1;
        
        public function NodeList()
        {
            nodeAdded = new Signal1( Node );
            nodeRemoved = new Signal1( Node );
        }
        
        public function add( node : Node ) : void
        {
            if( ! head )
            {
                head = tail = node;
                node.next = node.previous = null;
            }
            else
            {
                tail.next = node;
                node.previous = tail;
                node.next = null;
                tail = node;
            }
            nodeAdded.dispatch( node );
        }
        
        public function remove( node : Node ) : void
        {
            if ( head == node)
            {
                head = head.next;
            }
            if ( tail == node)
            {
                tail = tail.previous;
            }
            
            if (node.previous)
            {
                node.previous.next = node.next;
            }
            
            if (node.next)
            {
                node.next.previous = node.previous;
            }
            nodeRemoved.dispatch( node );
            
        }
        
        public function removeAll() : void
        {
            while( head )
            {
                var node : Node = head;
                head = node.next;
                node.previous = null;
                node.next = null;
                nodeRemoved.dispatch( node );
            }
            tail = null;
        }
        
        
        public function get empty() : Boolean
        {
            return head == null;
        }
        
        
        public function swap( node1 : Node, node2 : Node ) : void
        {
            if( node1.previous == node2 )
            {
                node1.previous = node2.previous;
                node2.previous = node1;
                node2.next = node1.next;
                node1.next  = node2;
            }
            else if( node2.previous == node1 )
            {
                node2.previous = node1.previous;
                node1.previous = node2;
                node1.next = node2.next;
                node2.next  = node1;
            }
            else
            {
                var temp : Node = node1.previous;
                node1.previous = node2.previous;
                node2.previous = temp;
                temp = node1.next;
                node1.next = node2.next;
                node2.next = temp;
            }
            if( head == node1 )
            {
                head = node2;
            }
            else if( head == node2 )
            {
                head = node1;
            }
            if( tail == node1 )
            {
                tail = node2;
            }
            else if( tail == node2 )
            {
                tail = node1;
            }
            if( node1.previous )
            {                            
                node1.previous.next = node1;
            }
            if( node2.previous )
            {
                node2.previous.next = node2;
            }
            if( node1.next )
            {
                node1.next.previous = node1;
            }
            if( node2.next )
            {
                node2.next.previous = node2;
            }
        }
        
        
        public function insertionSort( sortFunction : Function ) : void
        {
            if( head == tail )
            {
                return;
            }
            var remains : Node = head.next;
            for( var node : Node = remains; node; node = remains )
            {
                remains = node.next;
                for( var other : Node = node.previous; other; other = other.previous )
                {
                    if( sortFunction( node, other ) >= 0 )
                    {
                        
                        if( node != other.next )
                        {
                            
                            if ( tail == node)
                            {
                                tail = node.previous;
                            }
                            node.previous.next = node.next;
                            if (node.next)
                            {
                                node.next.previous = node.previous;
                            }
                            
                            node.next = other.next;
                            node.previous = other;
                            node.next.previous = node;
                            other.next = node;
                        }
                        break; 
                    }
                }
                if( !other ) 
                {
                    
                    if ( tail == node)
                    {
                        tail = node.previous;
                    }
                    node.previous.next = node.next;
                    if (node.next)
                    {
                        node.next.previous = node.previous;
                    }
                    
                    node.next = head;
                    head.previous = node;
                    node.previous = null;
                    head = node;
                }
            }
        }
        
        
        public function mergeSort( sortFunction : Function ) : void
        {
            if( head == tail )
            {
                return;
            }
            var lists : Vector.<Node> = new Vector.<Node>;
            
            var start : Node = head;
            var end : Node;
            while( start )
            {
                end = start;
                while( end.next && sortFunction( end, end.next ) <= 0 )
                {
                    end = end.next;
                }
                var next : Node = end.next;
                start.previous = end.next = null;
                lists.push( start );
                start = next;
            }
            
            while( lists.length > 1 )
            {
                lists.push( merge( lists.shift(), lists.shift(), sortFunction ) );
            }
            
            tail = head = lists[0];
            while( tail.next )
            {
                tail = tail.next;    
            }
        }
        
        private function merge( head1 : Node, head2 : Node, sortFunction : Function ) : Node
        {
            var node : Node;
            var head : Node;
            if( sortFunction( head1, head2 ) <= 0 )
            {
                head = node = head1;
                head1 = head1.next;
            }
            else
            {
                head = node = head2;
                head2 = head2.next;
            }
            while( head1 && head2 )
            {
                if( sortFunction( head1, head2 ) <= 0 )
                {
                    node.next = head1;
                    head1.previous = node;
                    node = head1;
                    head1 = head1.next;
                }
                else
                {
                    node.next = head2;
                    head2.previous = node;
                    node = head2;
                    head2 = head2.next;
                }
            }
            if( head1 )
            {
                node.next = head1;
                head1.previous = node;
            }
            else
            {
                node.next = head2;
                head2.previous = node;
            }
            return head;
        }
    }
//}



//package {
    import flash.utils.Dictionary;

    
    /*public*/ class SignalBase
    {
        public var head : ListenerNode;
        public var tail : ListenerNode;
        
        private var nodes : Dictionary;
        private var listenerNodePool : ListenerNodePool;
        private var toAddHead : ListenerNode;
        private var toAddTail : ListenerNode;
        private var dispatching : Boolean;
        private var _numListeners : int = 0;

        public function SignalBase()
        {
            nodes = new Dictionary( true );
            listenerNodePool = new ListenerNodePool();
        }
        
        protected function startDispatch() : void
        {
            dispatching = true;
        }
        
        protected function endDispatch() : void
        {
            dispatching = false;
            if( toAddHead )
            {
                if( !head )
                {
                    head = toAddHead;
                    tail = toAddTail;
                }
                else
                {
                    tail.next = toAddHead;
                    toAddHead.previous = tail;
                    tail = toAddTail;
                }
                toAddHead = null;
                toAddTail = null;
            }
            listenerNodePool.releaseCache();
        }
        
        public function get numListeners() : int
        {
            return _numListeners;
        }

        public function add( listener : Function ) : void
        {
            if( nodes[ listener ] )
            {
                return;
            }
            var node : ListenerNode = listenerNodePool.get();
            node.listener = listener;
            nodes[ listener ] = node;
            addNode( node );
        }
        
        public function addOnce( listener : Function ) : void
        {
            if( nodes[ listener ] )
            {
                return;
            }
            var node : ListenerNode = listenerNodePool.get();
            node.listener = listener;
            node.once = true;
            nodes[ listener ] = node;
            addNode( node );
        }
        
        protected function addNode( node : ListenerNode ) : void
        {
            if( dispatching )
            {
                if( !toAddHead )
                {
                    toAddHead = toAddTail = node;
                }
                else
                {
                    toAddTail.next = node;
                    node.previous = toAddTail;
                    toAddTail = node;
                }
            }
            else
            {
                if ( !head )
                {
                    head = tail = node;
                }
                else
                {
                    tail.next = node;
                    node.previous = tail;
                    tail = node;
                }
            }
            _numListeners++;
        }

        public function remove( listener : Function ) : void
        {
            var node : ListenerNode = nodes[ listener ];
            if ( node )
            {
                if ( head == node)
                {
                    head = head.next;
                }
                if ( tail == node)
                {
                    tail = tail.previous;
                }
                if ( toAddHead == node)
                {
                    toAddHead = toAddHead.next;
                }
                if ( toAddTail == node)
                {
                    toAddTail = toAddTail.previous;
                }
                if (node.previous)
                {
                    node.previous.next = node.next;
                }
                if (node.next)
                {
                    node.next.previous = node.previous;
                }
                delete nodes[ listener ];
                if( dispatching )
                {
                    listenerNodePool.cache( node );
                }
                else
                {
                    listenerNodePool.dispose( node );
                }
                _numListeners--;
            }
        }
        
        public function removeAll() : void
        {
            while( head )
            {
                var node : ListenerNode = head;
                head = head.next;
                delete nodes[ node.listener ];
                listenerNodePool.dispose( node );
                node.previous = null;
                node.next = null;
            }
            tail = null;
            toAddHead = null;
            toAddTail = null;
            _numListeners = 0;
        }
    }
//}

//package {
    //import ash.signals.Signal0;
    import flash.utils.Dictionary;

    
    /*public*/ class Engine
    {
        private var entityNames : Dictionary;
        private var entityList : EntityList;
        private var systemList : SystemList;
        private var families : Dictionary;
        
        
        public var updating : Boolean;
        
        
        public var updateComplete : Signal0;
        
        
        public var familyClass : Class = ComponentMatchingFamily;
        
        public function Engine()
        {
            entityList = new EntityList();
            entityNames = new Dictionary();
            systemList = new SystemList();
            families = new Dictionary();
            updateComplete = new Signal0();
        }
        
        
        public function addEntity( entity : Entity ) : void
        {
            if( entityNames[ entity.name ] )
            {
                throw new Error( "The entity name " + entity.name + " is already in use by another entity." );
            }
            entityList.add( entity );
            entityNames[ entity.name ] = entity;
            entity.componentAdded.add( componentAdded );
            entity.componentRemoved.add( componentRemoved );
            entity.nameChanged.add( entityNameChanged );
            for each( var family : IFamily in families )
            {
                family.newEntity( entity );
            }
        }
        
        
        public function removeEntity( entity : Entity ) : void
        {
            entity.componentAdded.remove( componentAdded );
            entity.componentRemoved.remove( componentRemoved );
            entity.nameChanged.remove( entityNameChanged );
            for each( var family : IFamily in families )
            {
                family.removeEntity( entity );
            }
            delete entityNames[ entity.name ];
            entityList.remove( entity );
        }
        
        private function entityNameChanged( entity : Entity, oldName : String ) : void
        {
            if( entityNames[ oldName ] == entity )
            {
                delete entityNames[ oldName ];
                entityNames[ entity.name ] = entity;
            }
        }
        
        
        public function getEntityByName( name : String ) : Entity
        {
            return entityNames[ name ];
        }
        
        
        public function removeAllEntities() : void
        {
            while( entityList.head )
            {
                removeEntity( entityList.head );
            }
        }
        
        
        public function get entities() : Vector.<Entity>
        {
            var entities : Vector.<Entity> = new Vector.<Entity>();
            for( var entity : Entity = entityList.head; entity; entity = entity.next )
            {
                entities.push( entity );
            }
            return entities;
        }
        
        
        private function componentAdded( entity : Entity, componentClass : Class ) : void
        {
            for each( var family : IFamily in families )
            {
                family.componentAddedToEntity( entity, componentClass );
            }
        }
        
        
        private function componentRemoved( entity : Entity, componentClass : Class ) : void
        {
            for each( var family : IFamily in families )
            {
                family.componentRemovedFromEntity( entity, componentClass );
            }
        }
        
        
        public function getNodeList( nodeClass : Class ) : NodeList
        {
            if( families[nodeClass] )
            {
                return IFamily( families[nodeClass] ).nodeList;
            }
            var family : IFamily = new familyClass( nodeClass, this );
            families[nodeClass] = family;
            for( var entity : Entity = entityList.head; entity; entity = entity.next )
            {
                family.newEntity( entity );
            }
            return family.nodeList;
        }
        
        
        public function releaseNodeList( nodeClass : Class ) : void
        {
            if( families[nodeClass] )
            {
                families[nodeClass].cleanUp();
            }
            delete families[nodeClass];
        }
        
        
        public function addSystem( system : System, priority : int ) : void
        {
            system.priority = priority;
            system.addToEngine( this );
            systemList.add( system );
        }
        
        
        public function getSystem( type : Class ) : System
        {
            return systemList.get( type );
        }
        
        
        public function get systems() : Vector.<System>
        {
            var systems : Vector.<System> = new Vector.<System>();
            for( var system : System = systemList.head; system; system = system.next )
            {
                systems.push( system );
            }
            return systems;
        }
        
        
        public function removeSystem( system : System ) : void
        {
            systemList.remove( system );
            system.removeFromEngine( this );
        }
        
        
        public function removeAllSystems() : void
        {
            while( systemList.head )
            {
                removeSystem( systemList.head );
            }
        }

        
        public function update( time : Number ) : void
        {
            updating = true;
            for( var system : System = systemList.head; system; system = system.next )
            {
                system.update( time );
            }
            updating = false;
            updateComplete.dispatch();
        }
    }



//package {
    
    /*public*/ class Signal0 extends SignalBase
    {
        public function Signal0()
        {
        }

        public function dispatch() : void
        {
            startDispatch();
            var node : ListenerNode;
            for ( node = head; node; node = node.next )
            {
                node.listener();
                if( node.once )
                {
                    remove( node.listener );
                }
            }
            endDispatch();
        }
    }
//}


class ReflectUtil {
    
    private static var CACHE:Dictionary = new Dictionary();
    public static var HACHE_COMPONENTS:Dictionary = new Dictionary();
    
    public static function registerComponents(arrClasses:Array):void {
        var i:int = arrClasses.length;
        while (--i > -1) {
            HACHE_COMPONENTS[getQualifiedClassName(arrClasses[i])] = arrClasses[i]; 
        }
    }
    

    public static function getFields(nodeClass:Class, arrOfClasses:Array):Dictionary {
        if (CACHE[nodeClass]) return CACHE[nodeClass];
        var variables : XMLList = describeType( nodeClass ).factory.variable;

        var components:Dictionary = new Dictionary();
        var i:int = arrOfClasses.length;
        var hash:Object = { };
        while (--i > -1) {
            hash[ getQualifiedClassName(arrOfClasses[i]) ] = arrOfClasses[i];
        }
          for each ( var atom:XML in variables )
            {
                if ( atom.@name != "entity" && atom.@name != "previous" && atom.@name != "next" )
                {
                    var componentClass : Class = hash[ atom.@type.toString()] || HACHE_COMPONENTS[atom.@type.toString()];
                    if (componentClass == null) throw new Error("Could not find component class>" + atom.@type + ", for "+nodeClass);
                    components[componentClass] = atom.@name.toString();
                }
            }
                CACHE[nodeClass] = components;
                return components;
        }
        
    
    }
    



//package {
    import flash.utils.Dictionary;
    import flash.utils.describeType;
    import flash.utils.getDefinitionByName;

    
    /*public*/ class ComponentMatchingFamily implements IFamily
    {
        private var nodes : NodeList;
        private var entities : Dictionary;
        private var nodeClass : Class;
        private var components : Dictionary;
        private var nodePool : NodePool;
        private var engine : Engine;

        
        public function ComponentMatchingFamily( nodeClass : Class, engine : Engine )
        {
            this.nodeClass = nodeClass;
            this.engine = engine;
            init();
        }

        
        private function init() : void
        {
            nodes = new NodeList();
            entities = new Dictionary();
            components = new Dictionary();
            nodePool = new NodePool( nodeClass, components );
            
            nodePool.dispose( nodePool.get() ); 

            try {
            var dict:Dictionary = nodeClass["_getFields"]();
            }
            catch (e:Error) {
                 var variables : XMLList = describeType( nodeClass ).factory.variable;
                for each ( var atom:XML in variables )
                {
                    if ( atom.@name != "entity" && atom.@name != "previous" && atom.@name != "next" )
                    {
                        var componentClass : Class = ReflectUtil.HACHE_COMPONENTS[ atom.@type.toString()];
                        if (componentClass == null) throw new Error("Component class is undefined! "+atom.@type);
                        components[componentClass] = atom.@name.toString();
                    }
                }
                dict = new Dictionary();
            }
            for each(var key:* in dict) {
                components[key] = dict[key];
            }
            
        }
        
        
        public function get nodeList() : NodeList
        {
            return nodes;
        }

        
        public function newEntity( entity : Entity ) : void
        {
            addIfMatch( entity );
        }
        
        
        public function componentAddedToEntity( entity : Entity, componentClass : Class ) : void
        {
            addIfMatch( entity );
        }
        
        
        public function componentRemovedFromEntity( entity : Entity, componentClass : Class ) : void
        {
            if( components[componentClass] )
            {
                removeIfMatch( entity );
            }
        }
        
        
        public function removeEntity( entity : Entity ) : void
        {
            removeIfMatch( entity );
        }
        
        
        private function addIfMatch( entity : Entity ) : void
        {
            if( !entities[entity] )
            {
                var componentClass : *;
                for ( componentClass in components )
                {
                    if ( !entity.has( componentClass ) )
                    {
                        return;
                    }
                }
                var node : Node = nodePool.get();
                node.entity = entity;
                for ( componentClass in components )
                {
                    node[components[componentClass]] = entity.get( componentClass );
                }
                entities[entity] = node;
                nodes.add( node );
            }
        }
        
        
        private function removeIfMatch( entity : Entity ) : void
        {
            if( entities[entity] )
            {
                var node : Node = entities[entity];
                delete entities[entity];
                nodes.remove( node );
                if( engine.updating )
                {
                    nodePool.cache( node );
                    engine.updateComplete.add( releaseNodePoolCache );
                }
                else
                {
                    nodePool.dispose( node );
                }
            }
        }
        
        
        private function releaseNodePoolCache() : void
        {
            engine.updateComplete.remove( releaseNodePoolCache );
            nodePool.releaseCache();
        }
        
        
        public function cleanUp() : void
        {
            for( var node : Node = nodes.head; node; node = node.next )
            {
                delete entities[node.entity];
            }
            nodes.removeAll();
        }
    }
//}


//package {
    
    /*public*/ class Signal2 extends SignalBase
    {
        private var type1 : Class;
        private var type2 : Class;

        public function Signal2( type1 : Class, type2 : Class )
        {
            this.type1 = type1;
            this.type2 = type2;
        }

        public function dispatch( object1 : *, object2 : * ) : void
        {
            startDispatch();
            var node : ListenerNode;
            for ( node = head; node; node = node.next )
            {
                node.listener( object1, object2 );
                if( node.once )
                {
                    remove( node.listener );
                }
            }
            endDispatch();
        }
    }
//}

/*
 * Based on ideas used in Robert Penner's AS3-signals - https://github.com/robertpenner/as3-signals
 */


    
    class Signal3 extends SignalBase
    {
        private var type1 : Class;
        private var type2 : Class;
        private var type3 : Class;

        public function Signal3( type1 : Class, type2 : Class, type3 : Class )
        {
            this.type1 = type1;
            this.type2 = type2;
            this.type3 = type3;
        }

        public function dispatch( object1 : *, object2 : *, object3 : * ) : void
        {
            startDispatch();
            var node : ListenerNode;
            for ( node = head; node; node = node.next )
            {
                node.listener( object1, object2, object3 );
                if( node.once )
                {
                    remove( node.listener );
                }
            }
            endDispatch();
        }
    }
    
    


    class SignalAny extends SignalBase
    {
        protected var classes : Array;

        public function SignalAny( ...classes )
        {
            this.classes = classes;
        }

        public function dispatch( ...objects ) : void
        {
            startDispatch();
            var node : ListenerNode;
            for ( node = head; node; node = node.next )
            {
                node.listener.apply( null, objects );
                if( node.once )
                {
                    remove( node.listener );
                }
            }
            endDispatch();
        }
    }








//package {
    
    /*public*/ class Signal1 extends SignalBase
    {
        private var type : Class;

        public function Signal1( type : Class )
        {
            this.type = type;
        }

        public function dispatch( object : * ) : void
        {
            startDispatch();
            var node : ListenerNode;
            for ( node = head; node; node = node.next )
            {
                node.listener( object );
                if( node.once )
                {
                    remove( node.listener );
                }
            }
            endDispatch();
        }
    }
//}




interface IFamily {
        function get nodeList() : NodeList;
        function newEntity( entity : Entity ) : void;
        function removeEntity( entity : Entity ) : void;
        function componentAddedToEntity( entity : Entity, componentClass : Class ) : void;
        function componentRemovedFromEntity( entity : Entity, componentClass : Class ) : void;
        function cleanUp() : void;
}



     interface ITickProvider
    {
        function get playing() : Boolean;
        
        function add( listener : Function ) : void;
        function remove( listener : Function ) : void;
        
        function start() : void;
        function stop() : void;
    }



    class EntityList
    {
        public var head : Entity;
        public var tail : Entity;
        
        public function add( entity : Entity ) : void
        {
            if( ! head )
            {
                head = tail = entity;
                entity.next = entity.previous = null;
            }
            else
            {
                tail.next = entity;
                entity.previous = tail;
                entity.next = null;
                tail = entity;
            }
        }
        
        public function remove( entity : Entity ) : void
        {
            if ( head == entity)
            {
                head = head.next;
            }
            if ( tail == entity)
            {
                tail = tail.previous;
            }
            
            if (entity.previous)
            {
                entity.previous.next = entity.next;
            }
            
            if (entity.next)
            {
                entity.next.previous = entity.previous;
            }
            // N.B. Don't set node.next and node.previous to null because that will break the list iteration if node is the current node in the iteration.
        }
        
        public function removeAll() : void
        {
            while( head )
            {
                var entity : Entity = head;
                head = head.next;
                entity.previous = null;
                entity.next = null;
            }
            tail = null;
        }
    }



    class SystemList
    {
        public var head : System;
        public var tail : System;
        
        public function add( system : System ) : void
        {
            if( ! head )
            {
                head = tail = system;
                system.next = system.previous = null;
            }
            else
            {
                for( var node : System = tail; node; node = node.previous )
                {
                    if( node.priority <= system.priority )
                    {
                        break;
                    }
                }
                if( node == tail )
                {
                    tail.next = system;
                    system.previous = tail;
                    system.next = null;
                    tail = system;
                }
                else if( !node )
                {
                    system.next = head;
                    system.previous = null;
                    head.previous = system;
                    head = system;
                }
                else
                {
                    system.next = node.next;
                    system.previous = node;
                    node.next.previous = system;
                    node.next = system;
                }
            }
        }
        
        public function remove( system : System ) : void
        {
            if ( head == system)
            {
                head = head.next;
            }
            if ( tail == system)
            {
                tail = tail.previous;
            }
            
            if (system.previous)
            {
                system.previous.next = system.next;
            }
            
            if (system.next)
            {
                system.next.previous = system.previous;
            }
            // N.B. Don't set system.next and system.previous to null because that will break the list iteration if node is the current node in the iteration.
        }
        
        public function removeAll() : void
        {
            while( head )
            {
                var system : System = head;
                head = head.next;
                system.previous = null;
                system.next = null;
            }
            tail = null;
        }
        
        public function get( type : Class ) : System
        {
            for( var system : System = head; system; system = system.next )
            {
                if ( system is type )
                {
                    return system;
                }
            }
            return null;
        }
    }

//package 
//{
    import flash.utils.Dictionary;
    
    class NodePool
    {
        private var tail : Node;
        private var nodeClass : Class;
        private var cacheTail : Node;
        private var components : Dictionary;

        /**
         * Creates a pool for the given node class.
         */
        public function NodePool( nodeClass : Class, components : Dictionary )
        {
            this.nodeClass = nodeClass;
            this.components = components;
        }

        /**
         * Fetches a node from the pool.
         */
        internal function get() : Node
        {
            if ( tail )
            {
                var node : Node = tail;
                tail = tail.previous;
                node.previous = null;
                return node;
            }
            else
            {
                return new nodeClass();
            }
        }

        /**
         * Adds a node to the pool.
         */
        internal function dispose( node : Node ) : void
        {
            for each( var componentName : String in components )
            {
                node[ componentName ] = null;
            }
            node.entity = null;
            
            node.next = null;
            node.previous = tail;
            tail = node;
        }
        
        /**
         * Adds a node to the cache
         */
        internal function cache( node : Node ) : void
        {
            node.previous = cacheTail;
            cacheTail = node;
        }
        
        /**
         * Releases all nodes from the cache into the pool
         */
        internal function releaseCache() : void
        {
            while( cacheTail )
            {
                var node : Node = cacheTail;
                cacheTail = node.previous;
                dispose( node );
            }
        }
    }
//}

//package {
    //import ash.signals.Signal1;
    import flash.display.DisplayObject;
    import flash.events.Event;
    import flash.utils.getTimer;

    
    /*public*/ class FrameTickProvider extends Signal1 implements ITickProvider
    {
        private var displayObject : DisplayObject;
        private var previousTime : Number;
        private var maximumFrameTime : Number;
        private var isPlaying : Boolean = false;
        
        
        public var timeAdjustment : Number = 1;
        
        public function FrameTickProvider( displayObject : DisplayObject, maximumFrameTime : Number = Number.MAX_VALUE )
        {
            super( Number );
            this.displayObject = displayObject;
            this.maximumFrameTime = maximumFrameTime;
        }
        
        public function start() : void
        {
            previousTime = getTimer();
            displayObject.addEventListener( Event.ENTER_FRAME, dispatchTick );
            isPlaying = true;
        }
        
        public function stop() : void
        {
            isPlaying = false;
            displayObject.removeEventListener( Event.ENTER_FRAME, dispatchTick );
        }
        
        private function dispatchTick( event : Event ) : void
        {
            var temp : Number = previousTime;
            previousTime = getTimer();
            var frameTime : Number = ( previousTime - temp ) / 1000;
            if( frameTime > maximumFrameTime )
            {
                frameTime = maximumFrameTime;
            }
            dispatch( frameTime * timeAdjustment );
        }

        public function get playing() : Boolean
        {
            return isPlaying;
        }
    }
//}

//package ash.tick
//{
    import flash.display.DisplayObject;
    import flash.events.Event;


    //public
    class FixedTickProvider extends Signal1 implements ITickProvider
    {
        private var displayObject : DisplayObject;
        private var frameTime : Number;
        private var isPlaying : Boolean = false;
        
        public var timeAdjustment : Number = 1;
        
        public function FixedTickProvider( displayObject : DisplayObject, frameTime : Number )
        {
            super( Number );
            this.displayObject = displayObject;
            this.frameTime = frameTime;
        }
        
        public function start() : void
        {
            displayObject.addEventListener( Event.ENTER_FRAME, dispatchTick );
            isPlaying = true;
        }
        
        public function stop() : void
        {
            isPlaying = false;
            displayObject.removeEventListener( Event.ENTER_FRAME, dispatchTick );
        }
        
        private function dispatchTick( event : Event ) : void
        {
            dispatch( frameTime * timeAdjustment );
        }

        public function get playing() : Boolean
        {
            return isPlaying;
        }
    }
//}



// -- Ash framework ends here
    




    /**
 * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 * If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice.
 * You may add additional accurate notices of copyright ownership.
 *
 * It is desirable to notify that Covered Software was "Powered by AlternativaPlatform" with link to http://www.alternativaplatform.com/ 
 * */

//package alternativa.a3d.controller {

    import alternativa.engine3d.core.Camera3D;
    import alternativa.engine3d.core.Object3D;
    //import ash.core.Engine;
    //import ash.core.System;

    import flash.display.InteractiveObject;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.geom.Matrix3D;
    import flash.geom.Point;
    import flash.geom.Vector3D;
    import flash.ui.Keyboard;
    import flash.utils.getTimer;

    /**
     * Controller for <code>Object3D</code>. Allow to handle the object with a keyboard and mouse.
     *
     * @see alternativa.engine3d.core.Object3D //
     */
    //public
    class SimpleObjectController extends System {
    
        /**
         * Name of action for binding "forward" action.
         */
        public static const ACTION_FORWARD:String = "ACTION_FORWARD";
        
        /**
         * Name of action for binding "back" action.
         */
        public static const ACTION_BACK:String = "ACTION_BACK";
        
        /**
         * Name of action for binding "left" action.
         */
        public static const ACTION_LEFT:String = "ACTION_LEFT";
        
        /**
         * Name of action for binding "right" action.
         */
        public static const ACTION_RIGHT:String = "ACTION_RIGHT";
        
        /**
         * Name of action for binding "up" action.
         */
        public static const ACTION_UP:String = "ACTION_UP";
        
        /**
         * Name of action for binding "down" action.
         */
        public static const ACTION_DOWN:String = "ACTION_DOWN";
        
        /**
         * Name of action for binding "pitch up" action.
         */
        public static const ACTION_PITCH_UP:String = "ACTION_PITCH_UP";
        
        /**
         * Name of action for binding "pitch down" action.
         */
        public static const ACTION_PITCH_DOWN:String = "ACTION_PITCH_DOWN";
        
        /**
         * Name of action for binding "yaw left" action.
         */
        public static const ACTION_YAW_LEFT:String = "ACTION_YAW_LEFT";
        
        /**
         * Name of action for binding "yaw right" action.
         */
        public static const ACTION_YAW_RIGHT:String = "ACTION_YAW_RIGHT";
        
        /**
         * Name of action for binding "accelerate" action.
         */
        public static const ACTION_ACCELERATE:String = "ACTION_ACCELERATE";
        
        /**
         * ИName of action for binding "mouse look" action.
         */
        public static const ACTION_MOUSE_LOOK:String = "ACTION_MOUSE_LOOK";
    
        /**
         * Speed.
         */
        public var speed:Number;
        
        /**
         * Speed multiplier for acceleration mode.
         */
        public var speedMultiplier:Number;
        
        /**
         * Mouse sensitivity.
         */
        public var mouseSensitivity:Number;
        
        /**
         * The maximal slope in the vertical plane in radians.
         */
        public var maxPitch:Number = 1e+22;
        
        /**
         * The minimal slope in the vertical plane in radians.
         */
        public var minPitch:Number = -1e+22;
    
        private var eventSource:InteractiveObject;
        private var _object:Object3D;
    
        private var _up:Boolean;
        private var _down:Boolean;
        private var _forward:Boolean;
        private var _back:Boolean;
        private var _left:Boolean;
        private var _right:Boolean;
        private var _accelerate:Boolean;
    
        private var displacement:Vector3D = new Vector3D();
        private var mousePoint:Point = new Point();
        private var mouseLook:Boolean;
        private var objectTransform:Vector.<Vector3D>;
    
        
    
        /**
         * The hash for binding  names of action and functions. The functions should be at a form are follows:
         * <code>
         *     function(value:Boolean):void
         * </code>
         *
         * <code>value</code> argument defines if bound key pressed down or up.
         */
        private var actionBindings:Object = {};
        
        /**
         * The hash for binding key codes and action names.
         */
        protected var keyBindings:Object = {};
    
        /**
         * Creates a SimpleObjectController object.
         * @param eventSource Source for event listening.
         * @param speed Speed of movement.
         * @param mouseSensitivity Mouse sensitivity, i.e. number of degrees per each pixel of mouse movement.
         */
        public function SimpleObjectController(eventSource:InteractiveObject, object:Object3D, speed:Number, speedMultiplier:Number = 3, mouseSensitivity:Number = 1) {
            this.eventSource = eventSource;
            this.object = object;
            this.speed = speed;
            this.speedMultiplier = speedMultiplier;
            this.mouseSensitivity = mouseSensitivity;
    
            actionBindings[ACTION_FORWARD] = moveForward;
            actionBindings[ACTION_BACK] = moveBack;
            actionBindings[ACTION_LEFT] = moveLeft;
            actionBindings[ACTION_RIGHT] = moveRight;
            actionBindings[ACTION_UP] = moveUp;
            actionBindings[ACTION_DOWN] = moveDown;
            actionBindings[ACTION_ACCELERATE] = accelerate;
    
            setDefaultBindings();
        }
        
        override public function addToEngine(engine:Engine):void {
            enable();
            updateObjectTransform();
        }
        override public function removeFromEngine(engine:Engine):void {
            disable();
        }
    
        /**
         * Enables the controler.
         */
        public function enable():void {
            eventSource.addEventListener(KeyboardEvent.KEY_DOWN, onKey);
            eventSource.addEventListener(KeyboardEvent.KEY_UP, onKey);
            eventSource.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            eventSource.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
        }
    
        /**
         * Disables the controller.
         */
        public function disable():void {
            eventSource.removeEventListener(KeyboardEvent.KEY_DOWN, onKey);
            eventSource.removeEventListener(KeyboardEvent.KEY_UP, onKey);
            eventSource.removeEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            eventSource.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
            stopMouseLook();
        }
    
        private function onMouseDown(e:MouseEvent):void {
            startMouseLook();
        }
    
        private function onMouseUp(e:MouseEvent):void {
            stopMouseLook();
        }
    
        /**
         * Enables mouse look mode.
         */
        public function startMouseLook():void {
            mousePoint.x = eventSource.mouseX;
            mousePoint.y = eventSource.mouseY;
            mouseLook = true;
        }
    
        /**
         * Disables mouse look mode.
         */
        public function stopMouseLook():void {
            mouseLook = false;
        }
    
        private function onKey(e:KeyboardEvent):void {
            var method:Function = keyBindings[e.keyCode];
            if (method != null) method.call(this, e.type == KeyboardEvent.KEY_DOWN);
        }
    
        /**
         * Target of handling.
         */
        public function get object():Object3D {
            return _object;
        }
    
        /**
         * @private
         */
        public function set object(value:Object3D):void {
            _object = value;
            updateObjectTransform();
        }
    
        /**
         * Refreshes controller state from state of handled object. Should be called if object was moved without the controller (i.e. <code>object.x = 100;</code>).
         */
        public function updateObjectTransform():void {
            if (_object != null) objectTransform = _object.matrix.decompose();
        }
    
        /**
         * Calculates and sets new object position.
         */
        override public function update(frameTime:Number):void {
            if (_object == null) return;
    
            
            if (frameTime > 0.1) frameTime = 0.1;
    
            var moved:Boolean = false;
    
            if (mouseLook) {
                var dx:Number = eventSource.mouseX - mousePoint.x;
                var dy:Number = eventSource.mouseY - mousePoint.y;
                mousePoint.x = eventSource.mouseX;
                mousePoint.y = eventSource.mouseY;
                var v:Vector3D = objectTransform[1];
                v.x -= dy*Math.PI/180*mouseSensitivity;
                if (v.x > maxPitch) v.x = maxPitch;
                if (v.x < minPitch) v.x = minPitch;
                v.z -= dx*Math.PI/180*mouseSensitivity;
                moved = true;
            }
    
            displacement.x = _right ? 1 : (_left ? -1 : 0);
            displacement.y = _forward ? 1 : (_back ? -1 : 0);
            displacement.z = _up ? 1 : (_down ? -1 : 0);
            if (displacement.lengthSquared > 0) {
                if (_object is Camera3D) {
                    var tmp:Number = displacement.z;
                    displacement.z = displacement.y;
                    displacement.y = -tmp;
                }
                deltaTransformVector(displacement);
                if (_accelerate) displacement.scaleBy(speedMultiplier*speed*frameTime/displacement.length);
                else displacement.scaleBy(speed*frameTime/displacement.length);
                (objectTransform[0] as Vector3D).incrementBy(displacement);
                moved = true;
            }
    
            if (moved) {
                var m:Matrix3D = new Matrix3D();
                m.recompose(objectTransform);
                _object.matrix = m;
            }
        }
    
        /**
         * Sets object at given position.
         * @param pos The position.
         */
        public function setObjectPos(pos:Vector3D):void {
            if (_object != null) {
                var v:Vector3D = objectTransform[0];
                v.x = pos.x;
                v.y = pos.y;
                v.z = pos.z;
            }
        }
    
        /**
         * Sets object at given position.
         * @param x  X.
         * @param y  Y.
         * @param z  Z.
         */
        public function setObjectPosXYZ(x:Number, y:Number, z:Number):void {
            if (_object != null) {
                var v:Vector3D = objectTransform[0];
                v.x = x;
                v.y = y;
                v.z = z;
            }
        }
    
        /**
         * Sets direction of Z-axis of handled object to pointed at given place. If object is a camera, it will look to this direction.
         * @param point Point to look at.
         */
        public function lookAt(point:Vector3D):void {
            lookAtXYZ(point.x, point.y, point.z);
        }
    
        /**
         * Sets direction of Z-axis of handled object to pointed at given place. If object is a camera, it will look to this direction.
         * @param x  X.
         * @param y  Y.
         * @param z  Z.
         */
        public function lookAtXYZ(x:Number, y:Number, z:Number):void {
            if (_object == null) return;
            var v:Vector3D = objectTransform[0];
            var dx:Number = x - v.x;
            var dy:Number = y - v.y;
            var dz:Number = z - v.z;
            v = objectTransform[1];
            v.x = Math.atan2(dz, Math.sqrt(dx*dx + dy*dy));
            if (_object is Camera3D) v.x -= 0.5*Math.PI;
            v.y = 0;
            v.z = -Math.atan2(dx, dy);
            var m:Matrix3D = _object.matrix;
            m.recompose(objectTransform);
            _object.matrix = m;
        }
    
        private var _vin:Vector.<Number> = new Vector.<Number>(3);
        private var _vout:Vector.<Number> = new Vector.<Number>(3);
    
        private function deltaTransformVector(v:Vector3D):void {
            _vin[0] = v.x;
            _vin[1] = v.y;
            _vin[2] = v.z;
            _object.matrix.transformVectors(_vin, _vout);
            var c:Vector3D = objectTransform[0];
            v.x = _vout[0] - c.x;
            v.y = _vout[1] - c.y;
            v.z = _vout[2] - c.z;
        }
    
        /**
         * Starts and stops move forward according to  <code>true</code> or <code>false</code> was passed.
         * @param value Action switcher.
         */
        public function moveForward(value:Boolean):void {
            _forward = value;
        }

        /**
         * Starts and stops move backward according to  <code>true</code> or <code>false</code> was passed.
         * @param value Action switcher.
         */
        public function moveBack(value:Boolean):void {
            _back = value;
        }

        /**
         * Starts and stops move to left according to  <code>true</code> or <code>false</code> was passed.
         * @param value Action switcher.
         */
        public function moveLeft(value:Boolean):void {
            _left = value;
        }

        /**
         * Starts and stops move to right according to  <code>true</code> or <code>false</code> was passed.
         * @param value Action switcher.
         */
        public function moveRight(value:Boolean):void {
            _right = value;
        }

        /**
         * Starts and stops move up according to  <code>true</code> or <code>false</code> was passed.
         * @param value Action switcher.
         */
        public function moveUp(value:Boolean):void {
            _up = value;
        }

        /**
         * Starts and stops move down according to  <code>true</code> or <code>false</code> was passed.
         * @param value Action switcher.
         */
        public function moveDown(value:Boolean):void {
            _down = value;
        }
    
        /**
         * Switches acceleration mode.
         * @param value <code>true</code> turns acceleration on, <code>false</code> turns off.
         */
        public function accelerate(value:Boolean):void {
            _accelerate = value;
        }

        /**
         * Binds key and action. Only one action can be assigned to one key.
         * @param keyCode Key code.
         * @param action Action name.
         * @see #unbindKey()
         * @see #unbindAll()
         */
        public function bindKey(keyCode:uint, action:String):void {
            var method:Function = actionBindings[action];
            if (method != null) keyBindings[keyCode] = method;
        }

        /**
         * Binds keys and actions. Only one action can be assigned to one key.
         * @param bindings Array which consists of sequence of couples of key code and action. An example are follows: <code> [ keyCode1, action1, keyCode2, action2 ] </code>.
         */
        public function bindKeys(bindings:Array):void {
            for (var i:int = 0; i < bindings.length; i += 2) bindKey(bindings[i], bindings[i + 1]);
        }

        /**
         * Clear binding for given keyCode.
         * @param keyCode Key code.
         * @see #bindKey()
         * @see #unbindAll()
         */
        public function unbindKey(keyCode:uint):void {
            delete keyBindings[keyCode];
        }
    
        /**
         * Clear binding of all keys.
         * @see #bindKey()
         * @see #unbindKey()
         */
        public function unbindAll():void {
            for (var key:String in keyBindings) delete keyBindings[key];
        }
    
        /**
         * Sets default binding.
         * @see #bindKey()
         * @see #unbindKey()
         * @see #unbindAll()
         */
        public function setDefaultBindings():void {
            bindKey(87, ACTION_FORWARD);
            bindKey(83, ACTION_BACK);
            bindKey(65, ACTION_LEFT);
            bindKey(68, ACTION_RIGHT);
            bindKey(69, ACTION_UP);
            bindKey(67, ACTION_DOWN);
            bindKey(Keyboard.SHIFT, ACTION_ACCELERATE);
    
            bindKey(Keyboard.UP, ACTION_FORWARD);
            bindKey(Keyboard.DOWN, ACTION_BACK);
            bindKey(Keyboard.LEFT, ACTION_LEFT);
            bindKey(Keyboard.RIGHT, ACTION_RIGHT);
        }
    
    }
//}




  import flash.utils.ByteArray;
    
    class Base64 {
        
        private static const BASE64_CHARS:String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

        public static const version:String = "1.1.0";

        public static function encode(data:String):String {
            // Convert string to ByteArray
            var bytes:ByteArray = new ByteArray();
            bytes.writeUTFBytes(data);
            
            // Return encoded ByteArray
            return encodeByteArray(bytes);
        }
        
        public static function encodeByteArray(data:ByteArray):String {
            // Initialise output
            var output:String = "";
            
            // Create data and output buffers
            var dataBuffer:Array;
            var outputBuffer:Array = new Array(4);
            
            // Rewind ByteArray
            data.position = 0;
            
            // while there are still bytes to be processed
            while (data.bytesAvailable > 0) {
                // Create new data buffer and populate next 3 bytes from data
                dataBuffer = new Array();
                for (var i:uint = 0; i < 3 && data.bytesAvailable > 0; i++) {
                    dataBuffer[i] = data.readUnsignedByte();
                }
                
                // Convert to data buffer Base64 character positions and 
                // store in output buffer
                outputBuffer[0] = (dataBuffer[0] & 0xfc) >> 2;
                outputBuffer[1] = ((dataBuffer[0] & 0x03) << 4) | ((dataBuffer[1]) >> 4);
                outputBuffer[2] = ((dataBuffer[1] & 0x0f) << 2) | ((dataBuffer[2]) >> 6);
                outputBuffer[3] = dataBuffer[2] & 0x3f;
                
                // If data buffer was short (i.e not 3 characters) then set
                // end character indexes in data buffer to index of '=' symbol.
                // This is necessary because Base64 data is always a multiple of
                // 4 bytes and is basses with '=' symbols.
                for (var j:uint = dataBuffer.length; j < 3; j++) {
                    outputBuffer[j + 1] = 64;
                }
                
                // Loop through output buffer and add Base64 characters to 
                // encoded data string for each character.
                for (var k:uint = 0; k < outputBuffer.length; k++) {
                    output += BASE64_CHARS.charAt(outputBuffer[k]);
                }
            }
            
            // Return encoded data
            return output;
        }
        
        public static function decode(data:String):String {
            // Decode data to ByteArray
            var bytes:ByteArray = decodeToByteArray(data);
            
            // Convert to string and return
            return bytes.readUTFBytes(bytes.length);
        }
        
        public static function decodeToByteArray(data:String):ByteArray {
            // Initialise output ByteArray for decoded data
            var output:ByteArray = new ByteArray();
            
            // Create data and output buffers
            var dataBuffer:Array = new Array(4);
            var outputBuffer:Array = new Array(3);

            // While there are data bytes left to be processed
            for (var i:uint = 0; i < data.length; i += 4) {
                // Populate data buffer with position of Base64 characters for
                // next 4 bytes from encoded data
                for (var j:uint = 0; j < 4 && i + j < data.length; j++) {
                    dataBuffer[j] = BASE64_CHARS.indexOf(data.charAt(i + j));
                }
                  
                  // Decode data buffer back into bytes
                outputBuffer[0] = (dataBuffer[0] << 2) + ((dataBuffer[1] & 0x30) >> 4);
                outputBuffer[1] = ((dataBuffer[1] & 0x0f) << 4) + ((dataBuffer[2] & 0x3c) >> 2);        
                outputBuffer[2] = ((dataBuffer[2] & 0x03) << 6) + dataBuffer[3];
                
                // Add all non-padded bytes in output buffer to decoded data
                for (var k:uint = 0; k < outputBuffer.length; k++) {
                    if (dataBuffer[k+1] == 64) break;
                    output.writeByte(outputBuffer[k]);
                }
            }
            
            // Rewind decoded data ByteArray
            output.position = 0;
            
            // Return decoded data
            return output;
        }
        
        public function Base64() {
            throw new Error("Base64 class is static container only");
        }
        
    }
    

Forked