forked from: ArrowLobMeshSet2.as - Looping Fountain of Arrows example

by raa forked from ArrowLobMeshSet2.as - Looping Fountain of Arrows example (diff: 1)
♥0 | Line 2138 | Modified 2016-01-10 08:00:14 | MIT License
play

ActionScript3 source code

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

// forked from Glidias's  ArrowLobMeshSet2.as - Looping Fountain of Arrows example
// forked from Glidias's ArrowLobMeshSet2.as - Fountain of Arrows example
package 
{
    //import alternativa.a3d.objects.ArrowLobMeshSet2;
    import alternativa.engine3d.core.Object3D;
    import alternativa.engine3d.core.Resource;
    import alternativa.engine3d.materials.FillMaterial;
    import alternativa.engine3d.materials.TextureMaterial;
    import alternativa.engine3d.objects.Mesh;
    import alternativa.engine3d.primitives.Plane;
    import com.greensock.TweenLite;
    import flash.display.Bitmap;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.ui.Keyboard;
    import flash.utils.ByteArray;
    import alternativa.engine3d.resources.BitmapTextureResource;
    import flash.display.MovieClip;
    import flash.events.KeyboardEvent;
    import flash.geom.Vector3D;
    import alternativa.engine3d.primitives.*;
    /**
     * 
     * Current code allows batching up to ~60 arrows per draw call! 
     * THat's a lot of arrows for a single draw call!
     * 
     * All you need to do is launch an arrow with a start-position and
     * end position accordingly.
     * 
      Of course, you can use your own custom meshes for the arrows and all.. 
     * 
     * @author Glidias
     */
    
   [SWF(frameRate="60", backgroundColor="#FFFFFF")]
    public class RainOfArrowsProofOfConcept extends MovieClip 
    {
        private var _template3D:MainView3D;
        private var engine:Engine;
        private var ticker:FrameTickProvider;


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

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

            
            // Setup floor and some basic parameters
            
            var plane:Plane = new Plane(4000, 4000, 1, 1, false, false, null, new FillMaterial(0x111111));
        
            plane.z = -1;
            _template3D.scene.addChild(plane);
        
            _template3D.camera.z = 66;
                     spectatorPerson.setObjectPosXYZ(1100, 1400, 133);
            spectatorPerson.lookAt(new Vector3D(0, 0, 0));
            
            uploadResources(_template3D.scene.getResources(true));
            _template3D.render();
            
            // Let's go
    
            ticker = new FrameTickProvider(stage);
            ticker.add(tick);
            ticker.start();
            
            
            
        }
        
        // Do whatever custom environment setups here!
        private function setupCustomEnvironment():void 
        {
               // example visual scene
            var planeFloor:Mesh = new Plane(2048, 2048, 1, 1, false, false, null, new FillMaterial(0xBBBBBB, 1) );
            _template3D.scene.addChild(planeFloor);
            //arenaSpawner.addCrossStage(SpawnerBundle.context3D);
            //SpawnerBundle.uploadResources(planeFloor.getResources(true, null));
            
            var box:Box = new Box(26, 3, 3, 1, 1, 1, false, null);
            arrows = new ArrowLobMeshSet2(box.geometry, new FillMaterial(0xFF0000, 1));
            //arrows.z = 1333;
            arrows.setGravity(266 * 3);
            arrows.idleArrowLifetime = 4;    // idle time before arrow vanishes
            arrows.maxTimeInterval = 1;  // for periodic cleanups
            
            startPosition = new Vector3D(0,0,0);
            endPosition = new Vector3D();
            

            launchProjectileLooping();
            
           // arrows.setPermanentArrows();
            
            _template3D.scene.addChild ( arrows);
            
        
        //throw new Error(planeFloor.geometry.getVertexBuffer(VertexAttributes.POSITION));
            // collision scene (can be something else)
        
            // (Optional) Enforced ground plane collision
            //game.gameStates.thirdPerson.addInstance( new GroundPlaneCollisionSystem(0, true) ).withPriority(SystemPriorities.resolveCollisions);

    

        }
        
        private function launchProjectileLooping():void {
            
            TweenLite.delayedCall(1 / 30, launchProjectileLoop);
        }
        
        
        private function launchProjectileLoop():void {
            
            for (var i:int = 0 ; i < 50; i++) {
                TweenLite.delayedCall(i*.01, launchProjectile);
            }
            TweenLite.delayedCall(1 / 30, launchProjectileLoop);
        }
        
        private function launchProjectile():void 
        {
            var randAng:Number = Math.random() * Math.PI;
                var d:Number = Math.random() * 555 + 1410;
                
                endPosition.x = Math.cos(randAng)*d;
                endPosition.y =  Math.sin(randAng) * d;
                endPosition.z = 0 ;
                arrows.launchNewProjectile( startPosition, endPosition, 777);
        }
        
  private function onKeyDown(e:KeyboardEvent):void 
        {
            var kc:uint = e.keyCode;
            if (kc === Keyboard.F7) {
                _scrnie=_template3D.takeScreenshot(screenieMethod2);
            }
        }
        
       
        
         private function screenieMethod2():Boolean 
        {
            stage.addEventListener(MouseEvent.CLICK, removeScreenie);
            return false;
        }
        private var _scrnie:Bitmap;
          private function removeScreenie(e:Event=null):void {
            if (_scrnie == null) return;
            stage.removeEventListener(MouseEvent.CLICK, removeScreenie);
            _scrnie.parent.removeChild(_scrnie);
            _scrnie = null;
        }
        
        public   function uploadResources(vec:Vector.<Resource>):void {
            var i:int = vec.length;
            while (--i > -1) {
                vec[i].upload(_template3D.stage3D.context3D);
            }
        }
        private var timePassed:Number = 0;
        private var arrows:ArrowLobMeshSet2;
        private var startPosition:Vector3D;
        private var endPosition:Vector3D;
        
        private function tick(time:Number):void 
        {
               timePassed += time;
            
            arrows.update(time);

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

}


//package alternativa.engine3d.utils 
//{
    import alternativa.engine3d.core.VertexStream;
    import alternativa.engine3d.materials.Material;
    import alternativa.engine3d.objects.Mesh;
    import alternativa.engine3d.objects.Surface;
    import alternativa.engine3d.resources.Geometry;
    import flash.utils.ByteArray;
    import flash.utils.Endian;
    import alternativa.engine3d.alternativa3d;
    use namespace alternativa3d;
    
    /**
     * ...
     * @author Glenn Ko
     */
    //public 
    class GeometryUtil 
    {
        public static const ATTRIBUTE:uint = 20;
        
        private static function collectAttributes(geom:Geometry, attributesDict:Vector.<int>, attributesLengths:Vector.<int>):void {
        
            
                

                for each (var stream:VertexStream in geom._vertexStreams) {
                    var prev:int = -1;
                    var attributes:Array = stream.attributes;
                    for each (var attr:int in attributes) {
                        attributesLengths[attr]++;
                        if (attr == prev) continue;
                        attributesDict[attr]++;
                        prev = attr;
                    }
                }
                
            

            
        }
        
        private static function appendGeometry(geometry:Geometry, geom:Geometry, constantsPerMesh:int):void {
            var stream:VertexStream;
            var i:int, j:int;
            var length:uint = geom._vertexStreams.length;
            var numVertices:int = geom._numVertices;
            for (i = 0; i < length; i++) {
                stream = geom._vertexStreams[i];
                var attributes:Array = geometry._vertexStreams[i].attributes;
                var attribtuesLength:int = attributes.length;
                var destStream:VertexStream = geometry._vertexStreams[i];
                var newOffset:int = destStream.data.length;
                destStream.data.position = newOffset;

                stream.data.position = 0;
                var stride:int = stream.attributes.length*4;
                var destStride:int = destStream.attributes.length*4;
                for (j = 0; j < numVertices; j++) {
                    var prev:int = -1;
                    for (var k:int = 0; k < attribtuesLength; k++) {
                        var attr:int = attributes[k];
                        if (attr == ATTRIBUTE) {
                            destStream.data.writeFloat(0);
                            continue;
                        }
                        if (attr != prev) {
                            stream.data.position = geom._attributesOffsets[attr]*4 + stride*j;
                            destStream.data.position = newOffset + geometry._attributesOffsets[attr]*4 + destStride*j;
                        }
                        destStream.data.writeFloat(stream.data.readFloat());
                        prev = attr;
                    }
                }

            }
            geometry._numVertices += geom._numVertices;
            
        }
        
        private static function appendGeom(geometry:Geometry, geom:Geometry, constantsPerMesh:int):void {
                
            
                var vertexOffset:uint;
                var i:int, j:int;
                vertexOffset = geometry._numVertices;
                appendGeometry(geometry, geom, constantsPerMesh);
                
                // Copy indexes
            
                    
                    var indexEnd:uint = geom.numTriangles*3 + 0;
                
                    
                    for (j = 0; j < indexEnd; j++) {
                        geometry._indices.push(geom._indices[j] + vertexOffset);
                        
                    }
                }

        
        private static function getJointGeometry(geom:Geometry, constantsPerMesh:int):Geometry {
            var geometry:Geometry = new Geometry(0);
        
            var numAttributes:int = 32;
            var attributesDict:Vector.<int> = new Vector.<int>(numAttributes, true);
            var attributesLengths:Vector.<int> = new Vector.<int>(numAttributes, true);
             collectAttributes(geom, attributesDict, attributesLengths);

            var attributes:Array = [];
            var i:int;

            for (i = 0; i < numAttributes; i++) {
                if (attributesDict[i] > 0) {
                    attributesLengths[i] = attributesLengths[i]/attributesDict[i];
                }
            }
            for (i = 0; i < numAttributes; i++) {
                if (Number(attributesDict[i]) == 1) {
                    for (var j:int = 0; j < attributesLengths[i]; j++) {
                        attributes.push(i);
                    }

                }
            }
            attributes.push(ATTRIBUTE);
            geometry.addVertexStream(attributes);
        
            //if (root is Mesh) appendMesh(root as Mesh);
            //collectMeshes(root);
            appendGeom(geometry, geom, constantsPerMesh);
            
            return geometry;
        }
        
        // Borrowed from  MeshSetClonesContainer (based off MeshSet).
        /**
         * Allows you to convert a single geometry instance into a single geometry cloned-chain with joint ID indices for draw-batching!
         * @param    geometry    The geometry sample to duplicate
         * @param    cap        The total amount to duplicate! Give a value higher than  1! Usually the predicted maximum amount of instances per draw batch!)
         * @param   constantsPerMesh  The amount of constants being used per mesh clone, used to offset the joint indices accordingly to match the correct starting constant register index.
         */
        public static function createDuplicateGeometry(geometry:Geometry, cap:int, constantsPerMesh:int):Geometry {
            
            geometry  = getJointGeometry(geometry, constantsPerMesh);
    
            //transformProcedure = calculateTransformProcedure(cap);
            //deltaTransformProcedure = calculateDeltaTransformProcedure(cap);
            
            var bytes:ByteArray;
            //throw new Error(geometry.getAttributeValues(VertexAttributes.POSITION))
            // get samples
            var protoJointIndices:Vector.<Number> = geometry.getAttributeValues(ATTRIBUTE);
            var protoNumVertices:int = geometry.numVertices;
            
            //_numVertices = protoNumVertices;
            
            var protoByteArrayStreams:Vector.<ByteArray> = new Vector.<ByteArray>();
            var len:int = geometry._vertexStreams.length;
    
            // copy all geometry bytearray data samples for all vertex streams
            for (var i:int = 0; i < len; i++) {
                protoByteArrayStreams[i] = bytes = new ByteArray();
                bytes.endian = Endian.LITTLE_ENDIAN;
                for (var u:int = 0; u < cap; u++) {
                    bytes.writeBytes( geometry._vertexStreams[i].data );
                }
            }
            
            // paste geometry data for all the vertex streams
            for (i = 0; i < len; i++) {
                bytes =protoByteArrayStreams[i];
                for (u = 0; u < cap; u++) {
                    var data:ByteArray = geometry._vertexStreams[i].data;
                    data.position = data.length;
                    data.writeBytes(bytes, data.length);
                }
            }
            
            // set number of vertices to match new vertex data size
            geometry._numVertices = protoNumVertices * cap;
        
            var indices:Vector.<uint> = geometry.indices;
            // duplicate indices with offsets
            len = indices.length;
            for (i = 1; i < cap; i++) {
                var indexOffset:int = i * protoNumVertices;
                for (u = 0; u < len; u++) {
                    indices.push(indexOffset+ indices[u]);
                }
            }
            geometry.indices = indices;
    
            
            // paste joint attribute values with offsets
            var jointIndices:Vector.<Number> = geometry.getAttributeValues(ATTRIBUTE);
            len = protoJointIndices.length;
            var duplicateMultiplier:Number =  constantsPerMesh;
            
            var totalLen:int = jointIndices.length;
            for (i = len; i < totalLen; i += len) {
                for (u = i; u < i+len; u++) {
                    jointIndices[u] += duplicateMultiplier;
                }
                duplicateMultiplier+=  constantsPerMesh;
            }
            geometry.setAttributeValues(ATTRIBUTE, jointIndices);
            
            return geometry;
        }
        
    }

//}

//package alternativa.a3d.objects 
//{
    import alternativa.engine3d.core.Camera3D;
    import alternativa.engine3d.core.DrawUnit;
    import alternativa.engine3d.core.Light3D;
    import alternativa.engine3d.core.VertexAttributes;
    import alternativa.engine3d.materials.compiler.Linker;
    import alternativa.engine3d.materials.compiler.Procedure;
    import alternativa.engine3d.materials.Material;
    import alternativa.engine3d.objects.Mesh;
    import alternativa.engine3d.objects.Surface;
    import alternativa.engine3d.resources.Geometry;
//    import alternativa.engine3d.utils.GeometryUtil;
    import alternativa.engine3d.alternativa3d;
    import flash.display3D.Context3DVertexBufferFormat;
    import flash.geom.Vector3D;
    import flash.utils.Dictionary;
    use namespace alternativa3d;
    /**
     * Yes,  ~120 arrows/projectiles per draw call! Let thy arrows blot the sun!!
     * We determine arrow position and orientation, from velocity, offset and time...within GPU itself.
     * @author Glenn Ko
     */
    //public
     class ArrowLobMeshSet2 extends Mesh
    {
        private var batchAmount:int;
        

        private var sampleNumTris:int;
        private static var BATCH_AMOUNT:int = 60;
        
        private static var _transformProcedures:Dictionary = new Dictionary();
        private static var _deltaTransformProcedures:Dictionary = new Dictionary();
        
        public var toUpload:Vector.<Number> = new Vector.<Number>();
        public var total:int = 0;
        private static const ATTRIBUTE:int = GeometryUtil.ATTRIBUTE;
        private var constantsPerMesh:int;
        
        public var maxTimeInterval:Number = 4;
        public var gravity:Number = 266;
        public var idleArrowLifetime:Number = 4;
        
        public function adjustArrowLifeSettings(idleTimeBeforeCleanup:Number = 4, registryFlushTime:Number = 4 ):void {
            idleArrowLifetime = idleTimeBeforeCleanup;
            maxTimeInterval = 4;
        }
        
        public function setPermanentArrows():void {
            idleArrowLifetime = Number.MAX_VALUE;
        
            maxTimeInterval = Number.MAX_VALUE;
        }
        
    //    private var freeSlots:Vector.<int> = new Vector.<int>();
        
        
        public function setGravity(val:Number):void {
            gravity = val;
        }
        
        
        
        public function ArrowLobMeshSet2(arrowGeometry:Geometry, material:Material) 
        {
            super();

            batchAmount = BATCH_AMOUNT;
            sampleNumTris = arrowGeometry.numTriangles;
            constantsPerMesh = 2;
            geometry = GeometryUtil.createDuplicateGeometry(arrowGeometry, batchAmount, constantsPerMesh);
            
            mySurface = addSurface( material, 0, sampleNumTris*total  );
            
            transformProcedure = calculateTransformProcedure(batchAmount);
    
            //    deltaTransformProcedure = calculateDeltaTransformProcedure(batchAmount);
        //    setPermanentArrows();
            
            
            
            
            boundBox = null;
        }
        

        private var _remainingTotal:int;
        private var _toUploadAmount:int;
        
        private var timeStamp:Number = 0;
        
        
        alternativa3d function setVertexConstantsFromVector(drawUnit:DrawUnit, firstRegister:int, data:Vector.<Number>, numRegisters:int, fromParticleIndex:int):void {
            
            if (uint(firstRegister) > (128 - numRegisters)) throw new Error("Register index " + firstRegister + " is out of bounds.");
            var vertexConstants:Vector.<Number> = drawUnit.vertexConstants;
            fromParticleIndex *= (constantsPerMesh<<2);
            
            var offset:int = firstRegister << 2;
            if (firstRegister + numRegisters > drawUnit.vertexConstantsRegistersCount) {
                drawUnit.vertexConstantsRegistersCount = firstRegister + numRegisters;
                vertexConstants.length = drawUnit.vertexConstantsRegistersCount << 2;
            }
            
            for (var i:int = fromParticleIndex, len:int = fromParticleIndex + (numRegisters << 2); i < len; i++) {
                vertexConstants[offset] = data[i];
                offset++;
            }
        }
        
        private var particleDrawCount:int = 0;
        
        alternativa3d override function setTransformConstants(drawUnit:DrawUnit, surface:Surface, vertexShader:Linker, camera:Camera3D):void {
            
            
            drawUnit.setVertexBufferAt(vertexShader.getVariableIndex("joint"), geometry.getVertexBuffer(ATTRIBUTE), geometry._attributesOffsets[ATTRIBUTE], Context3DVertexBufferFormat.FLOAT_1);
    
            drawUnit.setVertexConstantsFromNumbers( vertexShader.getVariableIndex("cVars"), -.5 * gravity, idleArrowLifetime, gravity, timeStamp);
            drawUnit.setVertexConstantsFromNumbers( vertexShader.getVariableIndex("cUp"), 0, 0, 1, 0);
            
            //drawUnit.setVertexConstantsFromVector(0, toUploadSpriteData, toUploadNumSprites*NUM_REGISTERS_PER_SPR ); 
            
    
            setVertexConstantsFromVector(drawUnit, 0, toUpload, _toUploadAmount*constantsPerMesh, particleDrawCount  );
    
            //throw new Error(e.message + ":"+_toUploadAmount + ", " + toUploadData.length);
    
    
            
            //if (triCount != surface.numTriangles) {
                surface.numTriangles = _toUploadAmount * sampleNumTris;
            //}
                particleDrawCount += _toUploadAmount;
        }
        
        
        
        public function update(time:Number):void {
            
            timeStamp += time;
            if (timeStamp >= maxTimeInterval) {
                cleanup();
            }
            
        }
        
        private function cleanup():void 
        {
            var capCheck:int = total - 1;
            for (var i:int = 0; i < total; i++) {
                var base:int = i * 8;
                
                var timeOfLaunch:Number = toUpload[base + 3];
                var t:Number = timeStamp - timeOfLaunch;
                
                if ( t - toUpload[base+7]  < idleArrowLifetime ) {  // still within visible state
                    toUpload[base+3] = timeOfLaunch - timeStamp;
                }
                else {  // remove
                    if (i != capCheck ) { // do pop back
                        var count:int = capCheck * 8;
                        toUpload[base++] = toUpload[count++];
                        toUpload[base++] = toUpload[count++];
                        toUpload[base++] = toUpload[count++];
                        toUpload[base++] = toUpload[count++];
                        
                        toUpload[base++] = toUpload[count++];
                        toUpload[base++] = toUpload[count++];
                        toUpload[base++] = toUpload[count++];
                        toUpload[base++] = toUpload[count++];
                        i--;
                    }
                
                    total--;
                    capCheck--;  
                }
            }
            
            timeStamp = 0;
        }
        
        
        
        public function reset():void {
            
            timeStamp = 0;
            toUpload.length = 0;
            total = 0;
            
        }
        
        public function launchNewProjectile(startPosition:Vector3D, endPosition:Vector3D, speed:Number=1044 ):void {
            //launchProjectileAtIndex(total, startPosition, endPosition);
                displace.x = endPosition.x - startPosition.x;
            displace.y = endPosition.y - startPosition.y;
            displace.z = endPosition.z - startPosition.z;
      
           var totalTime:Number =  displace.length / speed;
         
            
            var base:int = total * 8;
            toUpload[base++] = startPosition.x;
            toUpload[base++] =startPosition.y;
            toUpload[base++] = startPosition.z;
            toUpload[base++ ] = timeStamp;
                
            toUpload[base++] = endPosition.x;
            toUpload[base++] =endPosition.y;
            toUpload[base++] =endPosition.z;
            toUpload[base++ ] = totalTime;
            
            total++;
            
        }
        
        public function launchNewProjectileWithTimeSpan(startPosition:Vector3D, endPosition:Vector3D, time:Number=1 ):void {
            //launchProjectileAtIndex(total, startPosition, endPosition);
        
           
            
            var base:int = total * 8;
            toUpload[base++] = startPosition.x;
            toUpload[base++] =startPosition.y;
            toUpload[base++] = startPosition.z;
            toUpload[base++ ] = timeStamp;
                
            toUpload[base++] = endPosition.x;
            toUpload[base++] =endPosition.y;
            toUpload[base++] =endPosition.z;
            toUpload[base++ ] = time;
            
            total++;
            
        }
        
        private var displace:Vector3D = new Vector3D();
        private var mySurface:Surface;
        
    
        
            override alternativa3d function collectDraws(camera:Camera3D, lights:Vector.<Light3D>, lightsLength:int, useShadow:Boolean):void {
                
                _remainingTotal = total;
                
                
                
                particleDrawCount = 0;
                
                if (_remainingTotal < batchAmount) {
                    mySurface = _surfaces[0];
                
                    _toUploadAmount = _remainingTotal;
                
                //    mySurface.numTriangles = _remainingTotal * sampleNumTris;
                    mySurface.material.collectDraws(camera, mySurface, geometry, lights, lightsLength, useShadow, -1);
                    
                }
                else {
                    var count:int = 0;
                    //if (tempUploadData == null) tempUploadData = new Vector.<Number>(batchAmount*constantsPerMesh*4, true);
                
                    //toUploadData = tempUploadData;
                    while (_remainingTotal > 0) {
                        if (count >= _surfaces.length) {
                            _surfaces[count] = _surfaces[0].clone();
                            _surfacesLength = count;
                        }
                        mySurface = _surfaces[count];
                        
                        //fillTempUploadData( total - _remainingTotal, _remainingTotal);
                        //mySurface.numTriangles = _remainingTotal*sampleNumTris;
                        
                        _toUploadAmount = _remainingTotal;
                        if (_toUploadAmount > batchAmount) _toUploadAmount = batchAmount;
                        
                        mySurface.material.collectDraws(camera, mySurface, geometry, lights, lightsLength, useShadow, -1);
                        
                        _remainingTotal -= batchAmount;
                        count++;
                        
                    }
                }
            }
            
            
            
            
        
        // -------------------
        
    
        
        ///*
        private function calculateTransformProcedure(numMeshes:int):Procedure {
            var res:Procedure = _transformProcedures[numMeshes];
            if (res != null) return res;
            res = _transformProcedures[numMeshes] = new Procedure(null, "ArrowLobMeshSet2TransformProcedure");
            res.compileFromArray(["#a0=joint", "#c1=cVars", "#c2=cUp",
            // dummy declarations (can remove once done..)
            //"mov t0, c1",
            //"mov t1, c2",
        //    "mov t1, a0",
        //    "mov t1, i0",
            // ----

                "mov t1, c[a0.x]",    // start position t1
                "add t0.x, c2.z, a0.x",
                "mov t0, c[t0.x]",  // end position t0
                
                "mov t2.w, t0.w", // we need this later, t2.w will be the totalTime of arrow travel
                
                
                // get velocity t0.xyz
                "sub t0.xyz, t0.xyz, t1.xyz",
                "div t0.xyz, t0.xyz, t0.www",
                "mul t0.w, c1.x, t0.w",  
                "sub t0.z, t0.z, t0.w",
                
                // get time t0.w
                "sub t0.w, c1.w, t1.w",  // now, save actual time by subtracting timeStamp - launchTime. 
                "sub t4.w, t0.w, t2.w",  // t - totalTime  // t4 is reserved for visMultiplier
                "slt t4.w, t4.w, c1.y",  //visMmultiplier  1/0
                
                "min t0.w, t0.w, t2.w",  // cap time for rendering of arrow (so arrows stick to the ground..)
                //t = min( t, totalTime)
                //"mov t0.w, c2.x",
                
                "mul t1.w, t0.x, t0.w, ",  // save out velocity.x*t offset
                "add t1.x, t1.x, t1.w",        // and add it to the launch position to get actual x arrow origin position
                "mul t1.w, t0.y, t0.w, ",  // save out velocity.y*t offset
                "add t1.y, t1.y, t1.w",  // and add it to the launch position to get actual y arrow origin position
            
                "mul t1.w, c1.x, t0.w", // save out  -.5*GRAVITY*t*t + velocity.z*t   gravitational offset over time
                "mul t1.w, t1.w, t0.w", 
                
                "add t1.z, t1.z, t1.w",  // and  LHS operand done, ADD it in!!
                
                "mul t1.w, t0.z, t0.w",
                "add t1.z, t1.z, t1.w",    // and RHS operand done, subtract it out to get actual z arrow origin position
                
                ///*  // if considering orientation
                "mul t0.w, c1.z, t0.w",  // multiply gravitaitonal offset over time: GRAVITY*t  
                "sub t0.z, t0.z, t0.w",    // subtract this from the z alt component velocity to get final unnormalized velocity vector
            
                "nrm t0.xyz, t0.xyz",  // normalize the vector to get forward vector of arrow...along X direction! (2d east)
                "mov t3, t0", // no choice, t0 is used, t1 is to hold position,  t2 to hold axes! need to maintain reference to foward vector to allow crs later
                "mul t2.xyz, i0.xxx, t0.xyz",  // extend out position along x offset of vertex
                "add t1.xyz, t1.xyz, t2.xyz",
                "crs t0.xyz, t0.xyz, c2.xyz",  // cross product forward vector with up to get right...along Y direction (2d north)!
                "mul t2.xyz, i0.yyy, t0.xyz",  // extend out position along y offset of vertex
                "add t1.xyz, t1.xyz, t2.xyz",
                "crs t0.xyz, t0.xyz, t3.xyz",  // cross product right vector with forward vector to get actual UP...along Z direction
                "mul t2.xyz, i0.zzz, t0.xyz",  // extend out position along z offset of vertex
                "add t1.xyz, t1.xyz, t2.xyz",
                "mov t1.w, c1.z",    // w property of 1 needed?
                // */
                
        
            // if not considering orientation    
            //"add t1.xyz, t1.xyz, i0.xyz",


                "mul t1.xyz, t1.xyz, t4.www",    
                "mov t1.w, i0.w",
                "mov o0, t1"]);
                
            res.assignConstantsArray(numMeshes * constantsPerMesh);
            
            return res;
        }
        //*/

        /*
        private function calculateDeltaTransformProcedure(numMeshes:int):Procedure {
            var res:Procedure = _deltaTransformProcedures[numMeshes];
            if (res != null) return res;
            res = _deltaTransformProcedures[numMeshes] = new Procedure(null, "ArrowLobMeshSet2DeltaTransformProcedure");
            res.compileFromArray(["#a0=joint", "#c1=cVars", "sub t0, a0.x, c1.x", "m33 o0.xyz, i0, c[t0.x]", "mov o0.w, i0.w"]);
            return res;
        }
        */
        
        
        
    }

//}




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

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

     class MainView3D extends Sprite  {
        private var _stage:Stage;

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

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

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

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

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

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

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

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

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

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

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

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

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

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



//}


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

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





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

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



//package {
    import flash.utils.Dictionary;

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

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

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

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

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

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

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



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

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


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

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

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



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

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

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

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

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

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


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

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

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

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


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

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

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


    class SignalAny extends SignalBase
    {
        protected var classes : Array;

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

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








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

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

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




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



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



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



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

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

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

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

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

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

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

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

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


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

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



// -- Ash framework ends here
    




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

//package alternativa.a3d.controller {

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

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

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

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

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

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

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

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

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

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

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




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

        public static const version:String = "1.1.0";

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

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