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

Flocking test with 3D models

Currently not optimized. Runs under Ash framework code. Only up to 100 clones at the moment to run smoothly on most video cards . Once SkinClonesContainer is up, can have up to 500 clones estimated. Code based off http://wonderfl.net/c/fd6Z
/**
 * Copyright Glidias ( http://wonderfl.net/user/Glidias )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/yrvl
 */

package
{
    import alternativa.engine3d.core.Object3D;
    import alternativa.engine3d.loaders.ParserA3D;
    import alternativa.engine3d.materials.FillMaterial;
    import alternativa.engine3d.materials.StandardMaterial;
    import alternativa.engine3d.materials.TextureMaterial;
    import alternativa.engine3d.objects.Skin;
    import alternativa.engine3d.primitives.Plane;
    import alternativa.engine3d.resources.BitmapTextureResource;
    import flash.display.Bitmap;
    import flash.events.MouseEvent;
    import flash.system.Security;
    import flash.system.SecurityDomain;


    import flash.events.IEventDispatcher;
    import flash.events.KeyboardEvent;
    import flash.system.ApplicationDomain;
    import flash.system.LoaderContext;
    import flash.ui.Keyboard;
    import flash.utils.ByteArray;
    import flash.utils.Dictionary;

    import flash.display.DisplayObject;    
    import flash.display.MovieClip;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.geom.Vector3D;

    /**
     * I have not implemented a SkinClonesContainer class yet. That would actually allow you to have 5x more animated models running about!
     * @author Glenn Ko 
     */
    public class TestFlocking3D extends MovieClip
    {
        public var engine:Engine;
        public var ticker:FrameTickProvider;
        
        public static const WORLD_SCALE:Number = 8;
                
        private  var WORLD_WIDTH:Number = 600*WORLD_SCALE;
        private  var WORLD_HEIGHT:Number = 400*WORLD_SCALE;
        
        private static const NUMBOIDS:int = 100;
        static public const MIN_SPEED:Number = 4*WORLD_SCALE;
        static public const MAX_SPEED:Number = 32*WORLD_SCALE;
        static public const TURN_RATIO:Number = 0.5;
        static public const MIN_DIST:Number = 65*WORLD_SCALE;
        static public const SENSE_DIST:Number = 200*WORLD_SCALE;
        static public const DEFAULT_ROT_X:Number = Math.PI * .5; // for boid

        
        private var _skin:Skin;
        private var _animManager:AnimationManager;
        private var rootContainer:Object3D = new Object3D();
        public var myAssets:Assets;
        
        
        
        public function TestFlocking3D() 
        {
            super();
            Wonderfl.disable_capture();
            
            ReflectUtil.registerComponents([Pos,Rot,Vel,Flocking,Object3D,IAnimatable]);
            
            myAssets = new Assets();
            if (myAssets.MECH_SKIN != null) {
                init();
            }
            else {
                myAssets.addEventListener(Event.COMPLETE, init);
                var domain:SecurityDomain = loaderInfo.url.indexOf("file://") >= 0 ? null : SecurityDomain.currentDomain;
                if (domain != null) {
                    Security.loadPolicyFile("http://glidias.uphero.com/crossdomain.xml");    
                }
                myAssets.load("http://glidias.uphero.com/flockmech.swf", "tests.flocking", new LoaderContext(true, null,domain));
            }
            
        }
        
        private function init(e:Event=null):void {
            
            engine = new Engine();
            
            engine.addSystem( new FlockingSystem(), 0 );
            engine.addSystem( new AnimationSystem(), 1 );
            //engine.addSystem( new DisplayObjectRenderingSystem(this), 1);
            
            ticker = new FrameTickProvider(stage);
            ticker.add(tick);
            
            stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
            
            MechStance.RANGE = 1 / MAX_SPEED;
            addChild( _template3d = new Template());
            _template3d.settings.cameraSpeed *= 4;
    
            _template3d.settings.cameraSpeedMultiplier *= 2;
            _template3d.addEventListener(Template.VIEW_CREATE, onReady3D);
    
            //startGame();
        }
        
    
        private var _template3d:Template;
        private function onReady3D(e:Event):void 
        {
            
            
            (e.currentTarget as IEventDispatcher).removeEventListener(e.type, onReady3D);
            engine.addSystem( new RenderingSystem(rootContainer), 2 );
            
            var child:Object3D = _template3d.scene.addChild( new Plane( WORLD_WIDTH, WORLD_HEIGHT, 1, 1, false, false, null, new FillMaterial(0x222222, 1) ) );
            child.x += WORLD_WIDTH * .5;
            child.y += WORLD_HEIGHT * .5;
            //rootContainer.rotationZ = Math.PI;

            var parser:ParserA3D = new ParserA3D();
            parser.parse(new myAssets.MECH_KAYRATH());
            var skin:Skin = findSkin(parser.objects);
            
            var standardMaterial:StandardMaterial = new StandardMaterial( new BitmapTextureResource(new myAssets.MECH_SKIN().bitmapData), _template3d.normalResource);
            skin.geometry.calculateNormals();
            skin.geometry.calculateTangents(0);
            skin.rotationX = Math.PI * .5;
            skin.rotationZ = Math.PI*.5;
            
            standardMaterial.specularPower = 0;
            skin.boundBox = null;
            skin.setMaterialToAllSurfaces(standardMaterial);
            //_template3d.scene.addChild(skin); 
            _skin = skin;
            
            _animManager = new AnimationManager();
            var animBytes:ByteArray =  new myAssets.MECH_ANIMS();
            animBytes.uncompress();
            _animManager.readExternal(animBytes );
            
            _template3d.cameraController.setObjectPos(new Vector3D(WORLD_WIDTH * .5, WORLD_HEIGHT * .5, 100));
            //_template3d.camera.transformChanged = true;
            //_template3d.cameraController.update();
            //throw new Error(_animManager.animClips[0].name);
            
            _template3d.scene.addChild(rootContainer);
            _template3d.uploadResources(_template3d.scene.getResources(true));
            _template3d.uploadResources(skin.getResources());
            
            startGame();
        }
        
        private function startGame():void 
        {
            createBoids();
            ticker.start();
        }
        private function findSkin(objects:Vector.<Object3D>):Skin {
            for each(var obj:Object3D in objects) {
                if (obj is Skin) return obj as Skin;
            }
            throw new Error("Could not find skin:");
            return null;
        }
        
        
        private var playing:Boolean = true;
        
        private function onKeyDown(e:KeyboardEvent):void 
        {
            var kc:uint = e.keyCode;
            if (kc === Keyboard.P) {
                playing = !playing;
                if (playing) ticker.start()
                else ticker.stop();
            }
            else if (kc === Keyboard.F6) {
                _template3d.takeScreenshot(screenieMethod);
            }
            else if (kc === Keyboard.F7) {
                _scrnie=_template3d.takeScreenshot(screenieMethod2);
            }
        }
        
          private function screenieMethod():Boolean 
        {
        //    Wonderfl.capture(); //
            return true;
        
        }
        
         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;
        }
        
        
        private function tick(time:Number):void 
        {
            engine.update(time);
            
            _template3d.cameraController.update();
            _template3d.camera.render(_template3d.stage3D); // onRenderTick();
        }
        
        
        private function createBoids():void 
        {
            
             var tmp:Number = 2.0 * Math.PI / NUMBOIDS;
             
                var tmpw:int = WORLD_WIDTH / 2 , tmph:int = WORLD_HEIGHT / 2;
                var flockSettings:FlockSettings = Flocking.createFlockSettings(MIN_DIST,SENSE_DIST,0,0,tmpw*2, tmph*2, MIN_SPEED, MAX_SPEED, TURN_RATIO);
              
            for (var i:int = 0; i < NUMBOIDS; ++i) {
                 const ph:Number = i * tmp;
                var pos:Pos = new Pos(  tmpw + ((i % 4) * 0.2 + 0.3) * tmpw * Math.cos(ph), tmph + ((i % 4) * 0.2 + 0.3) * tmph * Math.sin(ph));
                
                
                var vel:Vel = new Vel( ((i%4)*(-4) + 16) * Math.cos(ph + Math.PI / 6 * (1+i%4) * (Math.random() - 0.5)),  ((i%4)*(-4) + 16) * Math.sin(ph + Math.PI / 6 * (1+i%4) * (Math.random() - 0.5)));
                
                var rot:Rot = new Rot(0, 0, Math.random() * 2 * Math.PI);
                

                
                var entity:Entity = new Entity().add(pos).add(rot).add(vel).add( new Flocking().setup(flockSettings )) ;
                
                //entity.add( new BoidGraphic(), DisplayObject);
                var obj:Object3D = new Object3D();
                var skin:Skin = _skin.clone() as Skin;
                obj.addChild(skin);

                entity.add( new MechStance( _animManager.cloneFor(skin), vel, skin ), IAnimatable).add(obj);
                
                
                engine.addEntity(entity);
            }
        }
        
        
        
    
    }
        

}



    /**
     * ...
     * @author Glenn Ko
     */
    
    import alternativa.engine3d.alternativa3d;
    import alternativa.engine3d.primitives.GeoSphere;
    import alternativa.engine3d.controllers.SimpleObjectController;
    import alternativa.engine3d.core.Camera3D;
    import alternativa.engine3d.core.Object3D;
    import alternativa.engine3d.core.Resource;
    import alternativa.engine3d.core.View;
    import alternativa.engine3d.core.VertexAttributes;
    import alternativa.engine3d.core.Transform3D;
    import alternativa.engine3d.resources.BitmapTextureResource;
    import flash.display.Bitmap;
    import flash.display.BitmapData;

    import alternativa.engine3d.objects.Mesh;
    import alternativa.engine3d.objects.Surface;
    import alternativa.engine3d.objects.Joint;
    import alternativa.engine3d.lights.AmbientLight;
    import alternativa.engine3d.lights.DirectionalLight;
    import alternativa.engine3d.resources.Geometry;
    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 flash.utils.Dictionary;
    import flash.geom.Vector3D;
    import flash.utils.Dictionary;
    import flash.geom.*;


    class Template extends Sprite {
        public static const VIEW_CREATE:String = 'view_create'
        
        public var stage3D:Stage3D
        public var camera:Camera3D
        public var scene:Object3D
        public var cameraController:SimpleObjectController;
        public var objectController:SimpleObjectController;
        public var controlObject:Object3D;
        
        protected var directionalLight:DirectionalLight;
        protected var ambientLight:AmbientLight;   
        
        public var bitmapResource:BitmapTextureResource;
        public var normalResource:BitmapTextureResource;
        
        public var settings:TemplateSettings = new TemplateSettings();
        public var renderId:int = 0;
        
        public function Template() {
            
            
            addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        protected function init(e:Event = null):void 
        {
             var diffuseMap:BitmapData = new BitmapData(16, 16, false, 0xFF6666);
            var normalMap:BitmapData = new BitmapData(16, 16, false, 0x8080FF);

            bitmapResource = new BitmapTextureResource(diffuseMap);
            normalResource = new BitmapTextureResource(normalMap);
            
            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();
        }
        
            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() ) {
                stage.removeChild(child);
             }
          return child;
     }
     
        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);
            camera.view.backgroundColor  = settings.viewBackgroundColor;
            
            //Cameraをコントロールする場合は、CameraControlerの作成
            cameraController = new SimpleObjectController(stage, camera, settings.cameraSpeed, settings.cameraSpeedMultiplier, settings.cameraSensitivity);
            //cameraController.mouseSensitivity = 0;
            //cameraController.unbindAll();
        
            
            //Cameraの位置調整
            cameraController.setObjectPosXYZ(0, -300, 0);
            cameraController.lookAtXYZ(0, 0, 0);
            
            //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;
            
        
            //コントロールオブジェクトの作成
            controlObject = new Object3D()
            scene.addChild(controlObject);
            dispatchEvent(new Event(VIEW_CREATE));
            
            
            stage.addEventListener(Event.RESIZE, onStageResize);
            
            
        }
        
        private function onStageResize(e:Event):void 
        {
            camera.view.width = stage.stageWidth;
            camera.view.height = stage.stageHeight;
        }
        
        public function startRendering():void {
            uploadResources ( scene.getResources(true) );
            
            //オブジェクト用のコントローラー(マウス操作)
            objectController = new SimpleObjectController(stage, controlObject, 100);
            objectController.mouseSensitivity = 0.2;
         
            //レンダリング
            camera.render(stage3D);
            
            
            addEventListener(Event.ENTER_FRAME, onRenderTick);
        }
        
        public function uploadResources(vec:Vector.<Resource>):void {
             for each (var resource:Resource in vec) {
                //trace(resource)
                resource.upload(stage3D.context3D);
            }
        }
        
        


        
        public function onRenderTick(e:Event):void {
            cameraController.update()
            camera.render(stage3D);
        
        }
    }




class TemplateSettings {
    public var cameraSpeedMultiplier:Number = 3;
    public var cameraSpeed:Number = 100;
    public var cameraSensitivity:Number = 1;
    public var viewBackgroundColor:uint;
    public function TemplateSettings() {
        
    }
    
}



import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.system.LoaderContext;
import flash.utils.describeType;
import flash.utils.getDefinitionByName;

    /**
     * ...
     * @author Glenn Ko
     */
    class Assets extends Sprite
    {
        //[Embed(source = "../../../bin/skins/mech/animations.ani", mimeType = "application/octet-stream")]
        public var MECH_ANIMS:Class;
        
        //[Embed(source = "../../../bin/skins/mech/mech_kayrath.a3d", mimeType = "application/octet-stream")]
        public var MECH_KAYRATH:Class;
        
        //[Embed(source = "../../../bin/skins/mech/skin.jpg")]
        public var MECH_SKIN:Class;
        
        public function Assets() 
        {
            
        }
        
        private var _loader:ClassLoader; 
        private var packagePrefix:String;
        
        public function load(url:String, packagePath:String, context:LoaderContext):void {
            _loader = new ClassLoader();
            if (packagePath != "") {
                packagePrefix = packagePath+ "::";
            }
            else packagePrefix = null;
            _loader.addEventListener(ClassLoader.CLASS_LOADED, onLoadComplete);
            _loader.load(url, context);
        }
        
        private function onLoadComplete(e:Event):void {
            (e.currentTarget as IEventDispatcher).removeEventListener(e.type, onLoadComplete);
            
            var me:Object = Object(this);
            var variables : XMLList = describeType( me.constructor ).factory.variable;

            var classe:Class = _loader.getClass( packagePrefix + "Assets");
            var refer:Object = new classe();
            
              for each ( var atom:XML in variables )
                {        
                    var componentClass : Class = refer[atom.@name.toString()];            
                    me[atom.@name.toString()] = componentClass;
                    
                }

                dispatchEvent( new Event(Event.COMPLETE));
            }
        }
    



// written by @9re
// MIT License, see http://www.opensource.org/licenses/mit-license.php

    import flash.display.Loader;
    import flash.errors.IllegalOperationError;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.events.ProgressEvent;
    import flash.events.SecurityErrorEvent;
    import flash.net.URLRequest;
    import flash.system.ApplicationDomain;
    import flash.system.LoaderContext;

    class ClassLoader extends EventDispatcher {
        public static var CLASS_LOADED:String = "classLoaded";
        public static var LOAD_ERROR:String = "loadError";
        private var loader:Loader;
        private var swfLib:String;
        private var request:URLRequest;
        private var loadedClass:Class;
        
        public function ClassLoader() {
            loader = new Loader();
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, completeHandler);
            loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
            loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, securityErrorHandler);
        }
        
        public function load(lib:String, context:LoaderContext):void {
            swfLib = lib;
            request = new URLRequest(swfLib);
            loader.load(request, context);
        }
            
        public function getClass(className:String):Class {
            try {
                var c:Class = loader.contentLoaderInfo.applicationDomain.getDefinition(className) as Class;
                return c;
            }
            catch(e:Error) {
                throw new IllegalOperationError(e + className + " definition not found in " + swfLib);
            }
            return null;
        }
        
        private function completeHandler(e:Event):void {
            dispatchEvent(new Event(ClassLoader.CLASS_LOADED));
        }
        
        private function ioErrorHandler(e:Event):void {
            dispatchEvent(new Event(ClassLoader.LOAD_ERROR));
        }
        
        private function securityErrorHandler(e:Event):void {
            dispatchEvent(new Event(ClassLoader.LOAD_ERROR));
        }

    }



    
    
    
    
    
    
//}//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
    
    
    
    
    

     interface IAnimatable {
        function animate(time : Number) : void ;
    }

    class AnimationSystem extends ListIteratingSystem {
        public function AnimationSystem() : void { 
            super(AnimationNode,this.updateNode);
        }
        
        public function updateNode(node : AnimationNode,time : Number) : void {
            node.animatable.animate(time);
        }
        
    }

    
     class AnimationNode extends Node {
        public function AnimationNode() : void {
        }
        
        public var animatable : IAnimatable;

    }



     interface IRenderable {
        function render() : void ;
    }



     class RenderSystem extends System {
        public function RenderSystem() : void { 
            super();
        }
        
        public function onRemovedNode(node : RenderNode) : void {
        }
        
        public function onAddedNode(node : RenderNode) : void {
        }
        
        public override function addToEngine(engine : Engine) : void {
            this.nodeList = engine.getNodeList(RenderNode);
            this.nodeList.nodeAdded.add(this.onAddedNode);
            this.nodeList.nodeRemoved.add(this.onRemovedNode);
        }
        
        public var renderEngine : IRenderable;
        public var nodeList : NodeList;
    }

    
    import alternativa.engine3d.core.Object3D;
    
    class RenderNode extends Node {
        public function RenderNode() : void {
        }
        
        public var rot : Rot;
        public var pos : Pos;
        public var object : Object3D;
        
        
    }
    
    



    import alternativa.engine3d.core.Object3D;
    import alternativa.engine3d.alternativa3d;
    use namespace alternativa3d;
    
    /**
     * ...
     * @author Glenn Ko
     */
    class RenderingSystem extends RenderSystem
    {
        private var scene:Object3D;
    
        public function RenderingSystem(scene:Object3D, renderingSystem:IRenderable=null) 
        {
            this.scene = scene;
            this.renderEngine = renderingSystem;
        }
        
    
        override public function onAddedNode(node:RenderNode):void {
            scene.addChild( node.object );
        }
        
        override public function onRemovedNode(node:RenderNode):void {
            scene.removeChild( node.object);
        }
    
        override public function update(time:Number):void {
            for (var r:RenderNode = nodeList.head as RenderNode; r != null; r = r.next as RenderNode) {
                r.object._x = r.pos.x;
                r.object._y = r.pos.y;
                r.object._z = r.pos.z;
                r.object._rotationX = r.rot.x;
                r.object._rotationY = r.rot.y;
                r.object._rotationZ = r.rot.z;
                r.object.transformChanged = true;
            }
            if (renderEngine != null) renderEngine.render();
        }
        
    }

// --- Ash-compiled classes


import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;

import flash.display.DisplayObject;
import flash.display.DisplayObjectContainer;
import flash.events.Event;



class DisplayObjectRenderingSystem extends System {
    private var scene:DisplayObjectContainer;
    private var nodeList:NodeList;
    
    
    public function DisplayObjectRenderingSystem(scene:DisplayObjectContainer):void {
        this.scene = scene;
    }
     public function onAddedNode(node:DisplayNode):void {
            scene.addChild( node.object );
    }
    
    override public function addToEngine(engine:Engine ):void {
        super.addToEngine(engine);
        nodeList = engine.getNodeList(DisplayNode);
        nodeList.nodeAdded.add(onAddedNode);
        nodeList.nodeRemoved.add(onRemovedNode);
    }
    
        
         public function onRemovedNode(node:DisplayNode):void {
            scene.removeChild( node.object);
        }
    
        override public function update(time:Number):void {
            const RAD_TO_DEG:Number = 180 / Math.PI;
            for (var r:DisplayNode = nodeList.head as DisplayNode; r != null; r = r.next as DisplayNode) {
                r.object.x = r.pos.x;
                r.object.y = r.pos.y;
                r.object.rotation = r.rot.z * RAD_TO_DEG;
            }
            //if (renderEngine != null) renderEngine.render();
        }
    
}

class DisplayNode extends Node {
    public var object:DisplayObject;
    public var pos:Pos;
    public var rot:Rot;
    


    public function DisplayNode() {
        
    }
    
   
}

    class Vec3 {
        public function Vec3(x : Number = 0,y : Number = 0,z : Number = 0) : void {
            this.x = x;
            this.y = y;
            this.z = z;
        }
        public function toString() : String {
            return "Vec3(" + this.x + ", " + this.y + ", " + this.z + ")";
        }
        public function distanceTo(v : Vec3) : Number {
            var dx : Number = this.x - v.x;
            var dy : Number = this.y - v.y;
            var dz : Number = this.z - v.z;
            return Math.sqrt(dx * dx + dy * dy + dz * dz);
        }
        public function removeComponent(axis : Vec3) : void {
            var scalar : Number = this.dotProduct(axis);
            this.x = this.x - axis.x * scalar;
            this.y = this.y - axis.y * scalar;
            this.z = this.z - axis.z * scalar;
        }
        public function setLength(val : Number) : void {
            var k : Number = val / this.length();
            this.x *= k;
            this.y *= k;
            this.z *= k;
        }
        protected function normalizeWithSquared(squaredLength : Number) : void {
            this.scale(1 / Math.sqrt(squaredLength));
        }
        public function normalize() : void {
            this.scale(1 / this.length());
        }
        public function assignAddition(v1 : Vec3,v2 : Vec3) : void {
            this.x = v1.x + v2.x;
            this.y = v1.y + v2.y;
            this.z = v1.z + v2.z;
        }

        public function copyFrom(source : Vec3) : void {
            this.x = source.x;
            this.y = source.y;
            this.z = source.z;
        }
        public function saveTo(result : Vec3) : void {
            result.x = this.x;
            result.y = this.y;
            result.z = this.z;
        }
        public function set(param1 : Number,param2 : Number,param3 : Number) : void {
            this.x = param1;
            this.y = param2;
            this.z = param3;
        }
        public function reset() : void {
            this.x = this.y = this.z = 0;
        }
        public function transformTransposed3(m : Mat3) : void {
            this.x = m.a * this.x + m.e * this.y + m.i * this.z;
            this.y = m.b * this.x + m.f * this.y + m.j * this.z;
            this.z = m.c * this.x + m.g * this.y + m.k * this.z;
        }
        public function transform3(m : Mat3) : void {
            this.x = m.a * this.x + m.b * this.y + m.c * this.z;
            this.y = m.e * this.x + m.f * this.y + m.g * this.z;
            this.z = m.i * this.x + m.j * this.y + m.k * this.z;
        }
        public function reverse() : void {
            this.x = -this.x;
            this.y = -this.y;
            this.z = -this.z;
        }
        public function scale(k : Number) : void {
            this.x *= k;
            this.y *= k;
            this.z *= k;
        }
        public function diff(a : Vec3,b : Vec3) : void {
            this.x = a.x - b.x;
            this.y = a.y - b.y;
            this.z = a.z - b.z;
        }
        public function sum(a : Vec3,b : Vec3) : void {
            this.x = a.x + b.x;
            this.y = a.y + b.y;
            this.z = a.z + b.z;
        }
        public function subtract(v : Vec3) : void {
            this.x -= v.x;
            this.y -= v.y;
            this.z -= v.z;
        }
        public function addScaled(k : Number,v : Vec3) : void {
            this.x += k * v.x;
            this.y += k * v.y;
            this.z += k * v.z;
        }
        public function add(v : Vec3) : void {
            this.x += v.x;
            this.y += v.y;
            this.z += v.z;
        }
        public function crossProductSet(v : Vec3) : void {
            this.x = this.y * v.z - this.z * v.y;
            this.y = this.z * v.x - this.x * v.z;
            this.z = this.x * v.y - this.y * v.x;
        }
        public function isZeroVector() : Boolean {
            return this.lengthSqr() == 0;
        }
        public function clone() : Vec3 {
            return new Vec3(this.x,this.y,this.z);
        }
        public function crossProduct(v : Vec3) : Vec3 {
            return new Vec3(this.y * v.z - this.z * v.y,this.z * v.x - this.x * v.z,this.x * v.y - this.y * v.x);
        }
        public function dotProduct(v : Vec3) : Number {
            return this.x * v.x + this.y * v.y + this.z * v.z;
        }
        public function lengthSqr() : Number {
            return this.x * this.x + this.y * this.y + this.z * this.z;
        }
        public function length() : Number {
            return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
        }
        public var z : Number;
        public var y : Number;
        public var x : Number;
        static public var ZERO : Vec3 = new Vec3(0,0,0);
        static public var X_AXIS : Vec3 = new Vec3(1,0,0);
        static public var Y_AXIS : Vec3 = new Vec3(0,1,0);
        static public var Z_AXIS : Vec3 = new Vec3(0,0,1);
        static public var RIGHT : Vec3 = new Vec3(1,0,0);
        static public var LEFT : Vec3 = new Vec3(-1,0,0);
        static public var FORWARD : Vec3 = new Vec3(0,1,0);
        static public var BACK : Vec3 = new Vec3(0,-1,0);
        static public var UP : Vec3 = new Vec3(0,0,1);
        static public var DOWN : Vec3 = new Vec3(0,0,-1);
        static public function copy(v : Vec3) : Vec3 {
            return new Vec3(v.x,v.y,v.z);
        }
        static public function createCross(v1 : Vec3,v2 : Vec3) : Vec3 {
            return new Vec3(v1.y * v2.z - v1.z * v2.y,v1.z * v2.x - v1.x * v2.z,v1.x * v2.y - v1.y * v2.x);
        }
        static public function createAdd(v1 : Vec3,v2 : Vec3) : Vec3 {
            return new Vec3(v1.x + v2.x,v1.y + v2.y,v1.z + v2.z);
        }
        static public function createSubtract(v1 : Vec3,v2 : Vec3) : Vec3 {
            return new Vec3(v1.x - v2.x,v1.y - v2.y,v1.z - v2.z);
        }
        static public function createScale(v : Vec3,scaleAmt : *) : Vec3 {
            return new Vec3(v.x * scaleAmt,v.y * scaleAmt,v.z * scaleAmt);
        }
        static public function createProjection(v : Vec3,axis : Vec3) : Vec3 {
            var scalar : Number = Vec3.dot(v,axis);
            return new Vec3(v.x - axis.x * scalar,v.y - axis.y * scalar,v.z - axis.z * scalar);
        }
        static public function dot(v1 : Vec3,v2 : Vec3) : Number {
            return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
        }
        static public function lengthOf(v : Vec3) : Number {
            return Math.sqrt(Vec3.squareLengthOf(v));
        }
        static public function squareLengthOf(v : Vec3) : Number {
            return v.x * v.x + v.y * v.y + v.z * v.z;
        }
        static public function writeCross(v1 : Vec3,v2 : Vec3,output : Vec3) : void {
            output.x = v1.y * v2.z - v1.z * v2.y;
            output.y = v1.z * v2.x - v2.z * v1.x;
            output.z = v1.x * v2.y - v1.y * v2.x;
        }
        static public function writeProjection(v : Vec3,axis : Vec3,output : Vec3) : void {
            var scalar : Number = Vec3.dot(v,axis);
            output.x = v.x - axis.x * scalar;
            output.y = v.y - axis.y * scalar;
            output.z = v.z - axis.z * scalar;
        }
        static public function writeSubtract(output : Vec3,input : Vec3) : void {
            output.x -= input.x;
            output.y -= input.y;
            output.z -= input.z;
        }
        static public function writeAdd(output : Vec3,input : Vec3) : void {
            output.x += input.x;
            output.y += input.y;
            output.z += input.z;
        }
        static public function writeScale(output : Vec3,scaleAmt : Number) : void {
            output.x *= scaleAmt;
            output.y *= scaleAmt;
            output.z *= scaleAmt;
        }
    }

//package {


    /*public*/ class FlockingNode extends Node {
        public var vel : Vel;
        public var rot : Rot;
        public var pos : Pos;
        public var f : Flocking;
        

    }
//}
//package {
    import flash.geom.Vector3D;
    
    /*public*/ class Vec3Utils {
        static public function copy(v : Vec3) : Vec3 {
            return new Vec3(v.x,v.y,v.z);
        }
        static public function createCross(v1 : Vec3,v2 : Vec3) : Vec3 {
            return new Vec3(v1.y * v2.z - v1.z * v2.y,v1.z * v2.x - v1.x * v2.z,v1.x * v2.y - v1.y * v2.x);
        }
        static public function createAdd(v1 : Vec3,v2 : Vec3) : Vec3 {
            return new Vec3(v1.x + v2.x,v1.y + v2.y,v1.z + v2.z);
        }
        static public function createSubtract(v1 : Vec3,v2 : Vec3) : Vec3 {
            return new Vec3(v1.x - v2.x,v1.y - v2.y,v1.z - v2.z);
        }
        static public function createScale(v : Vec3,scaleAmt : Number) : Vec3 {
            return new Vec3(v.x * scaleAmt,v.y * scaleAmt,v.z * scaleAmt);
        }
        static public function createProjection(v : Vec3,axis : Vec3) : Vec3 {
            var scalar : Number = Vec3Utils.dot(v,axis);
            return new Vec3(v.x - axis.x * scalar,v.y - axis.y * scalar,v.z - axis.z * scalar);
        }
        static public function matchValues(output : Vec3,withValue : Vec3) : void {
            output.x = withValue.x;
            output.y = withValue.y;
            output.z = withValue.z;
        }
        static public function matchValuesVector3D(output : Vec3,withValue : flash.geom.Vector3D) : void {
            output.x = withValue.x;
            output.y = withValue.y;
            output.z = withValue.z;
        }
        static public function dot(v1 : Vec3,v2 : Vec3) : Number {
            return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
        }
        static public function writeCross(v1 : Vec3,v2 : Vec3,output : Vec3) : void {
            output.x = v1.y * v2.z - v1.z * v2.y;
            output.y = v1.z * v2.x - v2.z * v1.x;
            output.z = v1.x * v2.y - v1.y * v2.x;
        }
        static public function writeProjection(v : Vec3,axis : Vec3,output : Vec3) : void {
            var scalar : Number = Vec3Utils.dot(v,axis);
            output.x = v.x - axis.x * scalar;
            output.y = v.y - axis.y * scalar;
            output.z = v.z - axis.z * scalar;
        }
        static public function normalize(v : Vec3) : void {
            var sc : Number = 1 / Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
            v.x *= sc;
            v.y *= sc;
            v.z *= sc;
        }
        static public function subtract(output : Vec3,input : Vec3) : void {
            output.x -= input.x;
            output.y -= input.y;
            output.z -= input.z;
        }
        static public function add(output : Vec3,input : Vec3) : void {
            output.x += input.x;
            output.y += input.y;
            output.z += input.z;
        }
        static public function scale(output : Vec3,scaleAmt : Number) : void {
            output.x *= scaleAmt;
            output.y *= scaleAmt;
            output.z *= scaleAmt;
        }
        static public function writeSubtract(output : Vec3,v1 : Vec3,v2 : Vec3) : void {
            output.x = v1.x - v2.x;
            output.y = v1.y - v2.y;
            output.z = v1.z - v2.z;
        }
        static public function getLength(v : Vec3) : Number {
            return Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
        }
    }
//}
//package {

    /*public*/ class Mat3 {
        public function Mat3(a : Number = 1,b : Number = 0,c : Number = 0,e : Number = 0,f : Number = 1,g : Number = 0,i : Number = 0,j : Number = 0,k : Number = 1) : void { 
            this.a = a;
            this.b = b;
            this.c = c;
            this.e = e;
            this.f = f;
            this.g = g;
            this.i = i;
            this.j = j;
            this.k = k;
        }
        public function toString() : String {
            return "[Mat3 (" + this.a + ", " + this.b + ", " + this.c + "), (" + this.e + ", " + this.f + ", " + this.g + "), (" + this.i + ", " + this.j + ", " + this.k + ")]";
        }
        public function setFromAxisAngle(axis : Vec3,angle : Number) : void {
            var c1 : Number = Math.cos(angle);
            var s : Number = Math.sin(angle);
            var t : Number = 1 - c1;
            var x : Number = axis.x;
            var y : Number = axis.y;
            var z : Number = axis.z;
            this.a = t * x * x + c1;
            this.b = t * x * y - z * s;
            this.c = t * x * z + y * s;
            this.e = t * x * y + z * s;
            this.f = t * y * y + c1;
            this.g = t * y * z - x * s;
            this.i = t * x * z - y * s;
            this.j = t * y * z + x * s;
            this.k = t * z * z + c1;
        }
        public function setRotation(rx : Number,ry : Number,rz : Number) : void {
            var cosX : Number = Math.cos(rx);
            var sinX : Number = Math.sin(rx);
            var cosY : Number = Math.cos(ry);
            var sinY : Number = Math.sin(ry);
            var cosZ : Number = Math.cos(rz);
            var sinZ : Number = Math.sin(rz);
            var cosZsinY : Number = cosZ * sinY;
            var sinZsinY : Number = sinZ * sinY;
            this.a = cosZ * cosY;
            this.b = cosZsinY * sinX - sinZ * cosX;
            this.c = cosZsinY * cosX + sinZ * sinX;
            this.e = sinZ * cosY;
            this.f = sinZsinY * sinX + cosZ * cosX;
            this.g = sinZsinY * cosX - cosZ * sinX;
            this.i = -sinY;
            this.j = cosY * sinX;
            this.k = cosY * cosX;
        }
        public function writeToEulerAngles(angles : Vec3) : void {
            if(-1 < this.i && this.i < 1) {
                angles.x = Math.atan2(this.j,this.k);
                angles.y = -Math.asin(this.i);
                angles.z = Math.atan2(this.e,this.a);
            }
            else {
                angles.x = 0;
                angles.y = ((this.i <= -1)?Math.PI:-Math.PI);
                angles.y *= 0.5;
                angles.z = Math.atan2(-this.b,this.f);
            }
        }
        public function copyFrom(m : Mat3) : void {
            this.a = m.a;
            this.b = m.b;
            this.c = m.c;
            this.e = m.e;
            this.f = m.f;
            this.g = m.g;
            this.i = m.i;
            this.j = m.j;
            this.k = m.k;
        }
        public function toSkewSymmetric(v : Vec3) : void {
            this.a = this.f = this.k = 0;
            this.b = -v.z;
            this.c = v.y;
            this.e = v.z;
            this.g = -v.x;
            this.i = -v.y;
            this.j = v.x;
        }
        public function transpose() : void {
            var tmp : Number = this.b;
            this.b = this.e;
            this.e = tmp;
            tmp = this.c;
            this.c = this.i;
            this.i = tmp;
            tmp = this.g;
            this.g = this.j;
            this.j = tmp;
        }
        public function subtract(m : Mat3) : void {
            this.a -= m.a;
            this.b -= m.b;
            this.c -= m.c;
            this.e -= m.e;
            this.f -= m.f;
            this.g -= m.g;
            this.i -= m.i;
            this.j -= m.j;
            this.k -= m.k;
        }
        public function add(m : Mat3) : void {
            this.a += m.a;
            this.b += m.b;
            this.c += m.c;
            this.e += m.e;
            this.f += m.f;
            this.g += m.g;
            this.i += m.i;
            this.j += m.j;
            this.k += m.k;
        }
        public function prependTransposed(m : Mat3) : void {
            this.a = this.a * m.a + this.b * m.b + this.c * m.c;
            this.b = this.a * m.e + this.b * m.f + this.c * m.g;
            this.c = this.a * m.i + this.b * m.j + this.c * m.k;
            this.e = this.e * m.a + this.f * m.b + this.g * m.c;
            this.f = this.e * m.e + this.f * m.f + this.g * m.g;
            this.g = this.e * m.i + this.f * m.j + this.g * m.k;
            this.i = this.i * m.a + this.j * m.b + this.k * m.c;
            this.j = this.i * m.e + this.j * m.f + this.k * m.g;
            this.k = this.i * m.i + this.j * m.j + this.k * m.k;
        }
        public function prepend(m : Mat3) : void {
            this.a = this.a * m.a + this.b * m.e + this.c * m.i;
            this.b = this.a * m.b + this.b * m.f + this.c * m.j;
            this.c = this.a * m.c + this.b * m.g + this.c * m.k;
            this.e = this.e * m.a + this.f * m.e + this.g * m.i;
            this.f = this.e * m.b + this.f * m.f + this.g * m.j;
            this.g = this.e * m.c + this.f * m.g + this.g * m.k;
            this.i = this.i * m.a + this.j * m.e + this.k * m.i;
            this.j = this.i * m.b + this.j * m.f + this.k * m.j;
            this.k = this.i * m.c + this.j * m.g + this.k * m.k;
        }
        public function append(m : Mat3) : void {
            this.a = m.a * this.a + m.b * this.e + m.c * this.i;
            this.b = m.a * this.b + m.b * this.f + m.c * this.j;
            this.c = m.a * this.c + m.b * this.g + m.c * this.k;
            this.e = m.e * this.a + m.f * this.e + m.g * this.i;
            this.f = m.e * this.b + m.f * this.f + m.g * this.j;
            this.g = m.e * this.c + m.f * this.g + m.g * this.k;
            this.i = m.i * this.a + m.j * this.e + m.k * this.i;
            this.j = m.i * this.b + m.j * this.f + m.k * this.j;
            this.k = m.i * this.c + m.j * this.g + m.k * this.k;
        }
        public function invert_with_determinant(det : Number) : void {
            this.a = (this.f * this.k - this.g * this.j) * det;
            this.b = (this.c * this.g - this.b * this.k) * det;
            this.c = (this.b * this.g - this.c * this.f) * det;
            this.e = (this.g * this.i - this.e * this.k) * det;
            this.f = (this.a * this.k - this.c * this.i) * det;
            this.g = (this.c * this.e - this.a * this.g) * det;
            this.i = (this.e * this.j - this.f * this.i) * det;
            this.j = (this.b * this.i - this.a * this.j) * det;
            this.k = (this.a * this.f - this.b * this.e) * det;
        }
        public function invert() : void {
            this.invert_with_determinant(this.determinant());
        }
        public function transformVec3To3D(vin : Vec3,vout : Vec3) : void {
            vout.x = this.a * vin.x + this.b * vin.y + this.c * vin.z;
            vout.y = this.e * vin.x + this.f * vin.y + this.g * vin.z;
            vout.z = this.i * vin.x + this.j * vin.y + this.k * vin.z;
        }
        public function transformVectorTransposed(vin : Vec3,vout : Vec3) : void {
            vout.x = this.a * vin.x + this.e * vin.y + this.i * vin.z;
            vout.y = this.b * vin.x + this.f * vin.y + this.j * vin.z;
            vout.z = this.c * vin.x + this.g * vin.y + this.k * vin.z;
        }
        public function transformVector(vin : Vec3,vout : Vec3) : void {
            vout.x = this.a * vin.x + this.b * vin.y + this.c * vin.z;
            vout.y = this.e * vin.x + this.f * vin.y + this.g * vin.z;
            vout.z = this.i * vin.x + this.j * vin.y + this.k * vin.z;
        }
        public function clone() : Mat3 {
            return new Mat3(this.a,this.b,this.c,this.e,this.f,this.g,this.i,this.j,this.k);
        }
        public function identity() : void {
            this.a = this.f = this.k = 1;
            this.b = this.c = this.e = this.g = this.i = this.j = 0;
        }
        public function determinant() : Number {
            return 1 / (-this.c * this.f * this.i + this.b * this.g * this.i + this.c * this.e * this.j - this.a * this.g * this.j - this.b * this.e * this.k + this.a * this.f * this.k);
        }
        public var k : Number;
        public var j : Number;
        public var i : Number;
        public var g : Number;
        public var f : Number;
        public var e : Number;
        public var c : Number;
        public var b : Number;
        public var a : Number;
        static public var IDENTITY : Mat3 = new Mat3();
        static public var ZERO : Mat3 = new Mat3(0,0,0,0,0,0,0,0,0);
    }
//}
//package {

    
    /*public*/ class FlockingSystem extends System {
        public function FlockingSystem() : void { 
            super();
            this.relP = new Vec3();
            this.relV = new Vec3();
            this.dist = new Vec3();
            this.hispos = new Vec3();
            this.mypos = new Vec3();
            this.hispos = new Vec3();
            this.collision = new Vec3();
        }
        protected function predictTime(cur : FlockingNode,other : FlockingNode) : Number {
            Vec3Utils.writeSubtract(this.relP,cur.pos,other.pos);
            Vec3Utils.writeSubtract(this.relV,other.vel,cur.vel);
            return this.relV.dotProduct(this.relP) / this.relV.lengthSqr();
        }
        protected function angleBetween(me : Vec3,v : Vec3) : Number {
            var result : Number = Math.atan2(me.y,me.x) - Math.atan2(v.y,v.x);
            if(result < -Math.PI) result += Math.PI * 2;
            if(result > Math.PI) result -= Math.PI * 2;
            return result;
        }
        protected function isAlmostZero(a : Vec3,min : Number = 0.15999) : Boolean {
            return a.lengthSqr() < min;
        }
        protected function getAngle(vec : Vec3) : Number {
            return Math.atan2(vec.y,vec.x);
        }
        protected function sign(arg : Number) : Number {
            return ((arg > 0)?1:((arg < 0)?-1:0));
        }
        public override function update(sec : Number) : void {
            var count : int;
            var count2 : int;
            var cur : FlockingNode = this.nodeList.head;
            var time : Number;
            var collisionLen : Number;
            var curF : Flocking;
            var curS : FlockSettings;
            while(cur != null) {
                count = 0;
                count2 = 0;
                curF = cur.f;
                curS = curF.settings;
                var minTime : Number = curF.minTime;
                var other : FlockingNode;
                curF.separation.reset();
                curF.alignment.reset();
                curF.cohesion.reset();
                curF._aold.copyFrom(curF._a);
                curF._a.reset();
                other = cur.previous;
                while(other != null) {
                    Vec3Utils.writeSubtract(this.dist,other.pos,cur.pos);
                    time = this.predictTime(cur,other);
                    if(this.dist.lengthSqr() < curS.mydistSquared) {
                        curF.angle = Math.abs(this.angleBetween(cur.vel,this.dist));
                        if(curF.angle < 2 / 3 * Math.PI) {
                            this.dist.scale(curS.mydist / this.dist.lengthSqr());
                            curF.separation.subtract(this.dist);
                            curF.alignment.add(other.vel);
                            ++count;
                            curF.angle = Math.abs(this.angleBetween(cur.vel,other.vel));
                            if(curF.angle < Math.PI / 2) {
                                curF.cohesion.add(other.pos);
                                ++count2;
                            }
                        }
                    }
                    if(!(time < 0. || time >= minTime)) {
                        this.mypos.copyFrom(cur.vel);
                        this.mypos.scale(time);
                        this.mypos.add(cur.pos);
                        this.hispos.copyFrom(other.vel);
                        this.hispos.scale(time);
                        this.hispos.add(other.pos);
                        Vec3Utils.writeSubtract(this.collision,this.mypos,this.hispos);
                        collisionLen = this.collision.lengthSqr();
                        if(!(collisionLen >= curS.mindistSquared)) {
                            minTime = time;
                            collisionLen = 1 / Math.sqrt(collisionLen);
                            this.collision.scale(collisionLen);
                            curF._a.copyFrom(this.collision);
                        }
                    }
                    other = other.previous;
                }
                other = cur.next;
                while(other != null) {
                    Vec3Utils.writeSubtract(this.dist,other.pos,cur.pos);
                    time = this.predictTime(cur,other);
                    if(this.dist.lengthSqr() < curS.mindistSquared) {
                        curF.angle = Math.abs(this.angleBetween(cur.vel,this.dist));
                        if(curF.angle < 2 / 3 * Math.PI) {
                            this.dist.scale(curS.mydist / this.dist.lengthSqr());
                            curF.separation.subtract(this.dist);
                            curF.alignment.add(other.vel);
                            ++count;
                            curF.angle = Math.abs(this.angleBetween(cur.vel,other.vel));
                            if(curF.angle < Math.PI / 2) {
                                curF.cohesion.add(other.pos);
                                ++count2;
                            }
                        }
                    }
                    if(!(time < 0. || time >= minTime)) {
                        this.mypos.copyFrom(cur.vel);
                        this.mypos.scale(time);
                        this.mypos.add(cur.pos);
                        this.hispos.copyFrom(other.vel);
                        this.hispos.scale(time);
                        this.hispos.add(other.pos);
                        Vec3Utils.writeSubtract(this.collision,this.mypos,this.hispos);
                        collisionLen = this.collision.lengthSqr();
                        if(!(collisionLen >= curS.mindistSquared)) {
                            minTime = time;
                            collisionLen = 1 / Math.sqrt(collisionLen);
                            this.collision.scale(collisionLen);
                            curF._a.copyFrom(this.collision);
                        }
                    }
                    other = other.next;
                }
                var _a : Vec3 = curF._a;
                if(this.isAlmostZero(_a)) {
                    curF.separation.scale(.5);
                    _a.add(curF.separation);
                    if(count > 0) {
                        curF.alignment.scale(1 / count);
                        curF.alignment.subtract(cur.vel);
                        curF.alignment.scale(1 / (curS.maxspeed * 2));
                        curF.alignment.scale(2);
                        _a.add(curF.alignment);
                    }
                    if(count2 > 0) {
                        curF.cohesion.scale(1 / count2);
                        curF.cohesion.subtract(cur.pos);
                        curF.cohesion.scale(1 / curS.mydist);
                        curF.cohesion.scale(8);
                        _a.add(curF.cohesion);
                    }
                }
                if(this.isAlmostZero(_a) && count == 0) {
                    curF.rangle += this.sign(Math.random() - 0.5) * Math.PI / 36;
                    _a.addScaled(0.44 / Vec3Utils.getLength(cur.vel),cur.vel);
                    _a.x += 0.45 * Math.sin(curF.rangle);
                    _a.y += 0.45 * Math.cos(curF.rangle);
                }
                else curF.rangle = this.getAngle(_a);
                if(cur.pos.x < curS.minx) _a.x += 0.4;
                else if(cur.pos.x > curS.maxx) _a.x -= 0.4;
                if(cur.pos.y < curS.miny) _a.y += 0.4;
                else if(cur.pos.y > curS.maxy) _a.y -= 0.4;
                if(curS.turnAccelRatio > 0) {
                    _a.subtract(curF._aold);
                    var t : Number = _a.length();
                    if(t > 0.0001) _a.scale(curS.turnAccelRatio);
                    if(t >= curS.turnAccelRatio) _a.scale(curS.turnAccelRatio / t);
                    _a.add(curF._aold);
                }
                cur = cur.next;
            }
            cur = this.nodeList.head;
            while(cur != null) {
                curF = cur.f;
                curS = curF.settings;
                Vec3Utils.add(cur.vel,curF._a);
                Vec3Utils.scale(cur.vel,1. / 12);
                cur.rot.z = this.getAngle(cur.vel);
                Vec3Utils.add(cur.pos,cur.vel);
                Vec3Utils.scale(cur.vel,12.);
                var v : Number = Vec3Utils.getLength(cur.vel);
                if(v > curS.maxspeed) Vec3Utils.scale(cur.vel,curS.maxspeed / v);
                else if(v < curS.minspeed) Vec3Utils.scale(cur.vel,curS.minspeed / v);
                else Vec3Utils.scale(cur.vel,0.99);
                cur = cur.next;
            }
        }
        public override function addToEngine(engine : Engine) : void {
            this.nodeList = engine.getNodeList(FlockingNode);
        }
        protected var collision : Vec3;
        protected var mypos : Vec3;
        protected var hispos : Vec3;
        protected var dist : Vec3;
        protected var relV : Vec3;
        protected var relP : Vec3;
        protected var nodeList : NodeList;
        static protected var myr : Number = 2 / 3 * Math.PI;
        static protected var scalerSpeed : Number = 1. / 12;
    }
//}
//package {

    
    /*public*/ class Pos extends Vec3 {
        public function Pos(x : Number = 0,y : Number = 0,z : Number = 0) : void { 
            super(x,y,z);
        }
    }
//}
//package {

    /*public*/ class Flocking {
        public function Flocking() : void {
        }
        public function setup(flockSettings : FlockSettings) : Flocking {
            this.settings = flockSettings;
            this.rangle = Math.random() * 2 * Math.PI;
            this.minTime = 10.0 / 3;
            this.separation = new Vec3();
            this.alignment = new Vec3();
            this.cohesion = new Vec3();
            this._a = new Vec3();
            this._aold = new Vec3();
            return this;
        }
        public var settings : FlockSettings;
        public var rangle : Number;
        public var minTime : Number;
        public var angle : Number;
        public var _aold : Vec3;
        public var _a : Vec3;
        public var cohesion : Vec3;
        public var alignment : Vec3;
        public var separation : Vec3;
        static public function createFlockSettings(minDist : Number,senseDistance : Number,minx : Number = 0,miny : Number = 0,maxx2 : Number = 400,maxy2 : Number = 400,minspeed : Number = 8,maxspeed : Number = 32,turnAccelRatio : Number = 0) : FlockSettings {
            var me : FlockSettings = new FlockSettings();
            me.minspeed = minspeed;
            me.maxspeed = maxspeed;
            me.turnAccelRatio = turnAccelRatio;
            me.mydist = senseDistance;
            me.mydistSquared = senseDistance * senseDistance;
            me.mindistSquared = minDist * minDist;
            me.maxx = maxx2 - senseDistance;
            me.maxy = maxy2 - senseDistance;
            me.minx = senseDistance;
            me.miny = senseDistance;
            return me;
        }
    }
//}
//package {

    
    /*public*/ class Rot extends Vec3 {
        public function Rot(x : Number = 0,y : Number = 0,z : Number = 0) : void { 
            super(x,y,z);
        }
    }
//}
//package {

    
    /*public*/ class Vel extends Vec3 {
        public function Vel(x : Number = 0,y : Number = 0,z : Number = 0) : void { 
            super(x,y,z);
        }
    }
//}
//package {
    /*public*/ class FlockSettings {
        public function FlockSettings() : void {
        }
        public var turnAccelRatio : Number;
        public var minspeed : Number;
        public var maxspeed : Number;
        public var mydist : Number;
        public var mydistSquared : Number;
        public var mindistSquared : Number;
        public var miny : Number;
        public var minx : Number;
        public var maxy : Number;
        public var maxx : Number;
    }
//}


    import alternativa.engine3d.animation.AnimationClip;
    import alternativa.engine3d.animation.AnimationController;
    import alternativa.engine3d.animation.AnimationCouple;
    import alternativa.engine3d.objects.Joint;
    import alternativa.engine3d.objects.Skin;
    import alternativa.engine3d.alternativa3d;
    use namespace alternativa3d;
    
    /**
     * ...
     * @author Glenn Ko
     */
    class MechStance implements IAnimatable 
    {
        private var animManager:AnimationManager;
        private var anim_walk:AnimationClip;
        private var vel:Vel;
        private var controller:AnimationController;
        private var couple:AnimationCouple;
        private var _turretJoint:Joint;
        
        public function MechStance(animManager:AnimationManager, vel:Vel, skin:Skin) 
        {
            this.animManager = animManager;
            this.vel = vel;
            var index:int = animManager.getAnimationIndexByName("walk");
            if (index < 0) index = 0;
            anim_walk = animManager.animClips[index];
            controller = new AnimationController();
            
            
            couple = new AnimationCouple();
            controller.root = couple;
            couple.left = anim_walk;
            couple.right = new AnimationClip();
            _turretJoint = findJointByName(skin._renderedJoints, "Bip01 Spine3");
            

        }
        
        private function findJointByName(joints:Vector.<Joint>, str:String):Joint {
            
            var i:int = joints.length;
            while (--i > -1) {
                if (joints[i].name === str) return joints[i];
            }
            return null;
        }
        
        /* INTERFACE systems.animation.IAnimatable */
        public static var RANGE:Number = 1/44;
        public function animate(time:Number):void 
        {
            var d:Number = Math.sqrt(vel.x * vel.x + vel.y * vel.y + vel.z * vel.z);
            couple.balance = 1 -  d  * RANGE * 1;
            controller.update();
        
        //    anim_walk.update(time, 1);
            //_turretJoint._rotationY += .05;
            //_turretJoint.transformChanged = true;
        }
        
    }

    import alternativa.engine3d.animation.AnimationClip;
    import alternativa.engine3d.animation.AnimationNotify;
    import alternativa.engine3d.animation.AnimationSwitcher;
    import alternativa.engine3d.animation.events.NotifyEvent;
    import alternativa.engine3d.animation.keys.Keyframe;
    import alternativa.engine3d.animation.keys.NumberKey;
    import alternativa.engine3d.animation.keys.NumberTrack;
    import alternativa.engine3d.animation.keys.Track;
    import alternativa.engine3d.animation.keys.TransformKey;
    import alternativa.engine3d.animation.keys.TransformTrack;
    import alternativa.engine3d.core.Object3D;
    import flash.geom.Matrix3D;
    import flash.geom.Vector3D;
    import flash.utils.Dictionary;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.utils.IExternalizable;

    
    import alternativa.engine3d.alternativa3d;
    use namespace alternativa3d;
    
    /**
     * A model blueprint / switcher for animation state 
     * @author Glidias
     */
    class AnimationManager implements IExternalizable
    {
        public var animClips:Vector.<AnimationClip>;
        private var animGroups:Vector.<Vector.<int>>;    
        private var switcher:AnimationSwitcher;
        private var _fixed:Boolean;
        
        public function AnimationManager(animClips:Vector.<AnimationClip>=null, animGroups:Vector.<Vector.<int>>=null, fixed:Boolean=true, animEndLoops:Dictionary=null) 
        {
            animEndLoops = animEndLoops || new Dictionary();
            if (animClips != null) init(animClips, animGroups, fixed);
        }
        
        private function init(animClips:Vector.<AnimationClip>, animGroups:Vector.<Vector.<int>>=null, fixed:Boolean=true):void {
            this.animClips = animClips;
            this.animGroups = animGroups;
        
            // initSwitcher
            var len:int = animClips.length;
            switcher = new AnimationSwitcher();
            for (var i:int = 0; i < len; i++) {
                switcher.addAnimation(animClips[i]);
            }
            
            _fixed = fixed;
        }
        
        
        /* INTERFACE flash.utils.IExternalizable */
        
        public function writeExternal(output:IDataOutput):void 
        {
            output.writeBoolean( _fixed);
            
            
            var i:int;
            var len:int = animClips.length;  
            output.writeByte(len);
            for (i = 0; i < len; i++) {
                writeAnimationClip( animClips[i] , output);
            }
        
        
            output.writeBoolean( animGroups != null);
            if (animGroups != null) writeAnimationGroups(output);

        }
        

        private function writeAnimationGroups(output:IDataOutput):void {
            var len:int = animGroups.length;
            output.writeByte(len);
            for (var i:int = 0; i < len; i++) {
                var anims:Vector.<int> = animGroups[i];
                if (anims == null) {
                    output.writeByte(0);
                    continue;
                }
                var uLen:int = anims.length;
                output.writeByte(uLen);
                for (var u:int=0; u < uLen; u++) {
                    output.writeByte(anims[u]);
                }
            }
        }
        private function readAnimationGroups(input:IDataInput, fixed:Boolean):Vector.<Vector.<int>> {
            var len:int = input.readByte();
            var animGroups:Vector.<Vector.<int>> = new Vector.<Vector.<int>>(len, fixed);
            for (var i:int = 0; i < len; i++) {
                var uLen:int = input.readByte();
                if (uLen <= 0) continue;
                var anims:Vector.<int> = new Vector.<int>(uLen, fixed);
                animGroups[i] = anims;
                for (var u:int=0; u < uLen; u++) {
                    anims[u] = input.readByte();
                }
            }
            return animGroups;
        }
        
        public function readExternal(input:IDataInput):void 
        {
            var fixed:Boolean = input.readBoolean();
            
            var len:int = input.readByte();
            animClips =  new Vector.<AnimationClip>(len);
            for (var i:int = 0; i < len; i++) {
                animClips[i] = readAnimationClip(input);
            }
        
            animGroups = input.readBoolean() ? readAnimationGroups(input, fixed) : null;
        
            init(animClips, animGroups, fixed );
        }
        
        
        // Parse from XML
        
        public static function fromAnimationAndXML(animation:AnimationClip, animXML:XML, animFPS:int=0, tableLookup:Vector.<String>=null):AnimationManager {
            var animManager:AnimationManager;    
            var animList:XMLList = animXML..a;
            if (animFPS == 0) animFPS = animXML.@fps != undefined ? Number(animXML.@fps) || 24 : 24;
            var useAnimLoop:Boolean = animXML.@loop == "true" || animXML.@loop == "false" ? animXML.@loop == "true" : animation.loop;
            var len:int;
            var i:int;

            var animations:Vector.<AnimationClip> = new Vector.<AnimationClip>();
            
            len = animList.length()
            for (i = 0; i < len; i++) {
                var xml:XML = animList[i];
                var sa:Array = xml.@f.split("-");
                var sa_1:Number = Number(sa[0]);
                var sa_2:Number = Number(sa[1]);
                var newAnim:AnimationClip = animation.slice( (sa_1 <= 1 ? 0 : sa_1) / animFPS, sa_2 / animFPS);
                var notifyList:XMLList = xml.n;
                var uLen:int = notifyList.length();
                for (var u:int = 0; u < uLen; u++) {
                    var uXML:XML = notifyList[u];
                    newAnim.addNotify((Number(uXML)-sa_1)/animFPS , uXML.@id);
                }
            //    var useFPS:Number = xml.@fps != undefined ? Number(xml.@fps) : animFPS;
                newAnim.speed = 1;// Math.round(newAnim.length * animFPS) / useFPS; 
                newAnim.name = xml.@id;
                
                newAnim.loop = xml.@loop == "true" || xml.@loop == "false" ? xml.@loop == "true" : useAnimLoop;
                
                //if (newAnim.loop && uLen > 0) new AnimEndLoop(newAnim);  // todo: determine context from which..
                
                newAnim.name = animList[i].@id;
                animations[i] = newAnim;    
            }
            
            animManager = new AnimationManager(animations, null, true);
            animManager.setupAnimGroups(animXML, tableLookup);
            return animManager;
        }
        
        public function cloneAnimation(toClone:AnimationClip):AnimationClip {
            var newAnim:AnimationClip = toClone.clone();
            newAnim.loop = toClone.loop;
            newAnim.speed = toClone.speed;
            newAnim.time = 0;
            return newAnim;
        }
        
        
        public function switchAnim(animation:AnimationClip, time:Number) : void {
            _currentAnim = animation;
            switcher.activate(animation, time);
        }
        
        public function switchAndRestartAnim(animation:AnimationClip, time:Number):void {
            _currentAnim = animation;
            animation.time = 0;
            switcher.activate(animation, time);
        }
        
        
        public function setupAnimGroups(xml:XML, tableLookup:Vector.<String>=null):void {
            var xmlList:XMLList = xml..a.(hasOwnProperty("@g"));
        
            var len:int = xmlList.length();
            if (len == 0) return;
            
            
            var dict:Dictionary = new Dictionary();
            var i:int;
    
            animGroups = tableLookup != null ? new Vector.<Vector.<int>>(tableLookup.length) : new Vector.<Vector.<int>>();
            var intList:Vector.<int>;
            for (i = 0; i < len; i++) {
                xml = xmlList[i];
                var prop:String = xml.@g != undefined ? xml.@g : null;
                if (prop == null) continue;
                intList = dict[prop];
                if (intList == null) {
                    intList = new Vector.<int>();
                    dict[prop] = intList;
                    if (tableLookup == null) animGroups.push(intList)
                    else animGroups[tableLookup.indexOf(prop)] = intList;
                }
                intList.push(getAnimationIndexByName(xml.@id));
            }
            
            len = animGroups.length;
            for (i = 0; i < len; i++ ) {
                intList = animGroups[i];
                if (intList == null) {
                    //animGroups[i] = intList = new <int>[0];
                }
                else intList.fixed = _fixed;
            }
            
            animGroups.fixed = _fixed;
        }
        

        private var _alreadySetup:Boolean = false;
        
        
        public function setupFor(target:Object):void {
            if (_alreadySetup) return;
            _alreadySetup = true;
            var len:int = animClips.length;

            for (var i:int = 0; i < len; i++) {
                var oldAnim:AnimationClip =  animClips[i];
                
                oldAnim.attach(target, true);
            }
        }
        
        public function cloneFor(target:Object):AnimationManager {
            var len:int = animClips.length;
            var newClips:Vector.<AnimationClip> = new Vector.<AnimationClip>(len);
            var dictEndLoops:Dictionary = new Dictionary();
            var newC:AnimationClip;
            for (var i:int = 0; i < len; i++) {
                var oldAnim:AnimationClip =  animClips[i];
                newClips[i] = newC = oldAnim.clone();
                //if (newC.loop != oldAnim.loop) throw new Error("LOOP MISMATCH!");
                newC.loop = oldAnim.loop;
                //if (newC.speed != oldAnim.speed) throw new Error("SPEED MISMATCH!");
                newC.speed = oldAnim.speed;
                var chkNotifiers:Vector.<AnimationNotify> = oldAnim.notifiers;
                var nLen:int = chkNotifiers.length;
                for (var n:int = 0; n < nLen; n++) {
                    var notifier:AnimationNotify = chkNotifiers[n];
        
                    newC.addNotify( notifier.time, notifier.name);
                }
                if (newC.loop && nLen > 0 ) dictEndLoops[newC.name] = new AnimEndLoop(newC);
                newC.time = 0;
                //newC.speed
                newC.attach(target, true);
            }
            return new AnimationManager(newClips, animGroups, _fixed, dictEndLoops);
        }
        
        
        
        
        public static var MANAGERS:Dictionary = new Dictionary();
    
        
        public var _currentAnim:AnimationClip;
        
        public static function getAnimManagerByKey(key:*):AnimationManager {
            return MANAGERS[key] || (new AnimationManager(new <AnimationClip>[],null,false));
        }
        public static function registerAnimManager(key:*, rootInstance:AnimationManager):void {
            MANAGERS[key] = rootInstance;
        }
        
        /**
         * Plays a certain random animation from a group category
         * @param    index    Category index
         * @param    time    The time to transition to animation
         * @return    True if animation is found, or False if no animation found
         */
        public function playGroup(index:int, time:Number):AnimationClip {
            if (animGroups == null) return null;
            var anim:AnimationClip = getAnimationFromGroup(index);
            if (anim == null) return null;
            _currentAnim = anim;
            switcher.activate(anim , time);
            return anim;
        }
        
        public function getAnimationFromGroup(index:int):AnimationClip {
            //if (animGroups == null) throw new Error("IS NULL!");
            var listAnims:Vector.<int> = animGroups[index];
            return listAnims!= null ? animClips[ listAnims[int(Math.random() * listAnims.length)] ] : null;
        }        
        
        public function getAnimationGroup(index:int):Vector.<int> {
            if (animGroups == null || (index >=animGroups.length)) return null;
            return animGroups[index];
        }
        
        public function getAnimationsByGroup(index:int):Vector.<AnimationClip> {
            var vec:Vector.<AnimationClip> = new Vector.<AnimationClip>();
            var animGroups:Vector.<int> =  getAnimationGroup(index);
            if (animGroups == null) return vec;
            var len:int = animGroups.length;
            for (var i:int = i; i < len; i++) {
                vec[i] = animClips[animGroups[i]];
            }
            vec.fixed = _fixed;
            return vec;
        }

        
        public function getAnimationByName(name:String):AnimationClip {
            var i:int = animClips.length; 
            while (--i > -1) {
                var animClip:AnimationClip = animClips[i];
                if (animClip.name === name) return animClip;
            }
            throw new Error("FAILED TO GET ANIMATION!"+name);
            return null;
        }
        
        public function getAnimationIndexByName(name:String):int {
            var i:int = animClips.length;
            while (--i > -1) {
                if (animClips[i].name === name) return i;
            }
            return -1;
        }
        
        public function getSwitcher():AnimationSwitcher 
        {
            return switcher;
        }
        
        public function get fixed():Boolean 
        {
            return _fixed;
        }
        
        public function get currentAnim():AnimationClip 
        {
            return _currentAnim;
        }
        
        
        
        // --Serialization helpers
        
        private function writeNotifiers(vec:Vector.<AnimationNotify>, output:IDataOutput):void {
            var len:int = vec.length;
            output.writeByte(len);
            for (var i:int = 0; i < len; i++) {
                var anim:AnimationNotify = vec[i];
                output.writeFloat(anim.time);
                output.writeObject(anim.name != null ? anim.name : "0");
            };
        }
        
        private function readNotifiers(input:IDataInput, clip:AnimationClip):void {
            var len:int = input.readByte();
            if (len == 0 ) return;
            for (var i:int = 0; i < len; i++) {
                var time:Number = input.readFloat();
                clip.addNotify(time, input.readObject() );
            };
            if (clip.loop) new AnimEndLoop(clip);
        }
        
        private function writeAnimationClip(anim:AnimationClip, output:IDataOutput):void {
                var uLen:int;
                
                output.writeObject(anim.name);
                output.writeBoolean(anim.loop);    
                //output.writeFloat(anim.length);  // speed
            
                
                uLen = anim.numTracks;
                output.writeShort(uLen);
                for (var u:int = 0; u < uLen; u++) {
                    var track:Track = anim.getTrackAt(u);  // is't the case, could be NUmberTrack or various other trakc types
                    output.writeBoolean( track is TransformTrack );
                    
                    if (track is TransformTrack) {
                        writeTransformTrack(track as TransformTrack, output);    
                    }
                    else if (track is NumberTrack) {
                        writeNumberTrack(track as NumberTrack, output);
                    }
                    else  {
                        throw new Error("Could not resolve track type!");
                    }
                }
                
                var notifiers:Vector.<AnimationNotify> = anim.notifiers;
                if (notifiers != null) {
                    writeNotifiers(notifiers, output);
                }
                else output.writeByte(0);
        }    
        
        
        private function readAnimationClip(input:IDataInput):AnimationClip {
            var anim:AnimationClip;
            anim =  new AnimationClip( input.readObject() );
            anim.loop = input.readBoolean();
            //anim.length = input.readFloat();  // speed
            
            
            var uLen:int = input.readShort();
            for (var u:int = 0; u < uLen; u++) {
                anim.addTrack( input.readBoolean() ? readTransformTrack(input) : readNumberTrack(input) );
            }    
            
            readNotifiers(input, anim);
            
            return anim;
        }
        
        private function writeNumberTrack(numberTrack:NumberTrack, output:IDataOutput):void 
        {
            output.writeObject( numberTrack.object );
            output.writeObject( numberTrack.property );
            var key:NumberKey;
            var count:int = 0;
            for (key = numberTrack.keyList; key != null; key = key.next) {
                count++;
            }
            output.writeShort(count);
        
            for ( key = numberTrack.keyList; key != null; key = key.next) {
                output.writeFloat(key._time);
                output.writeFloat(key._value);
            }
        }
        
        private function readNumberTrack(input:IDataInput):NumberTrack {
            var track:NumberTrack = new NumberTrack(input.readObject(), input.readObject() );
            var len:int = input.readShort();
            for (var i:int = 0; i < len; i++) {
                track.addKey(input.readFloat(), input.readFloat() );
            }
            return track;
        }
        
        private function writeTransformTrack(transformTrack:TransformTrack, output:IDataOutput):void 
        {
            
            output.writeObject(transformTrack.object);
            
            var keys:Vector.<Keyframe> = transformTrack.keys;
            if (keys == null) throw new Error("COuld not find keys!");
            var len:int = keys.length;
            output.writeShort(len);
            
            for (var i:int = 0; i < len; i++) {
                var tKey:TransformKey = keys[i] as TransformKey;
                if (tKey == null) throw new Error("COudl not find TransformKey:" + tKey);
                var matrix3D:Matrix3D = tKey.value as Matrix3D;
                if (matrix3D == null) throw new Error("Could not find matrix!");
                
                output.writeFloat( tKey._time);
                
                //writeMatrix3D( matrix3D, output); 
                writeComponentsFromMatrix3D(matrix3D, output);
                //writeKeyComponents(tKey, output);
            }
        }
        
        private function writeKeyComponents(transformKey:TransformKey, output:IDataOutput):void {
            output.writeFloat(transformKey.x);
            output.writeFloat(transformKey.y);
            output.writeFloat(transformKey.z);
            output.writeFloat(transformKey.rotation.x);
            output.writeFloat(transformKey.rotation.y);
            output.writeFloat(transformKey.rotation.z);
        }
        
        private function writeMatrix3D(matrix:Matrix3D, output:IDataOutput):void {
            var data:Vector.<Number> = matrix.rawData;
            output.writeFloat(data[0]);
            output.writeFloat(data[1]);
            output.writeFloat(data[2]);
            output.writeFloat(data[3]);
            output.writeFloat(data[4]);
            output.writeFloat(data[5]);
            output.writeFloat(data[6]);
            output.writeFloat(data[7]);
            output.writeFloat(data[8]);
            output.writeFloat(data[9]);
            output.writeFloat(data[10]);
            output.writeFloat(data[11]);
            output.writeFloat(data[12]);
            output.writeFloat(data[13]);
            output.writeFloat(data[14]);
            output.writeFloat(data[15]);
        }
        
        public function writeComponentsFromMatrix3D(matrix:Matrix3D, output:IDataOutput):void {
            // hmm.. may need to transpose.
            
            var vec:Vector.<Vector3D> = matrix.decompose();
            var v:Vector3D;
            v = vec[0];
            output.writeFloat(v.x);
            output.writeFloat(v.y);
            output.writeFloat(v.z);
            
            v = vec[1];
            output.writeFloat(v.x);
            output.writeFloat(v.y);
            output.writeFloat(v.z);
        }
        
        public function getAnimGroups():Vector.<Vector.<int>> 
        {
            return animGroups;
        }
        
        
        
        private function readMatrix3D(input:IDataInput):Matrix3D {
            var data:Vector.<Number> = new Vector.<Number>(16, true);
            data[0] = input.readFloat();
            data[1] = input.readFloat();
            data[2] = input.readFloat();
            data[3] = input.readFloat();
            data[4] = input.readFloat();
            data[5] = input.readFloat();
            data[6] = input.readFloat();
            data[7] = input.readFloat();
            data[8] = input.readFloat();
            data[9] = input.readFloat();
            data[10] = input.readFloat();
            data[11] = input.readFloat();
            data[12] = input.readFloat();
            data[13] = input.readFloat();
            data[14] = input.readFloat();
            data[15] = input.readFloat();
            return new Matrix3D(data);
        }
        
        private function readTransformTrack(input:IDataInput):TransformTrack {
            var track:TransformTrack = new TransformTrack(input.readObject());
            var len:int = input.readShort();
            for (var i:int = 0; i < len; i++) {
                //track.addKey(input.readFloat(), readMatrix3D(input)  );
                track.addKeyComponents(input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat());
            }
            return track;
        }
        
        
    }


    import alternativa.engine3d.animation.AnimationClip;
    import alternativa.engine3d.animation.AnimationNotify;
    import alternativa.engine3d.animation.events.NotifyEvent;
    import flash.events.Event;
    /**
     * ...
     * @author Glidias
     */
    class AnimEndLoop 
    {
        private var anim:AnimationClip;
        private var notifier:AnimationNotify;
        
        public function AnimEndLoop(anim:AnimationClip) 
        {
            this.anim = anim;
            anim.loop = false;
            notifier = anim.addNotifyAtEnd();
            notifier.addEventListener(NotifyEvent.NOTIFY, resetAnimTime);
        }
        
        private function resetAnimTime(e:NotifyEvent):void {
            anim.time = 0;
        }
        
        public function destroy():void {
            notifier.removeEventListener(NotifyEvent.NOTIFY, resetAnimTime);
        }
        
        
        
    }