In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

forked from: [Alternativa3D] Danboard in the City

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

???????????

??????:
fladdict » ???????????????
http://fladdict.net/blog/2009/05/computer-painting.html

????:
http://www.cap.or.jp/~toukei/kandokoro/html/14/14_2migi.htm

?????????:
http://wonderfl.kayac.com/code/3fb2258386320fe6d2b0fe17d6861e7da700706a

RGB->HSB??:
http://d.hatena.ne.jp/flashrod/20060930#1159622027

WIP: Real-time processed image using Alternativa3D's KDContainer to generate a "growing"  *  city.

Since image is used, can consider using perlin noise or other procedural means to 
generate city layout.

I was thinking a Blade runner style city would be nice.
Would be good to add in geometry/splitting animations (buildings pushing in/out) as part 
of the "growing" effect.

W-S-A-D to fly around with mouse drag look.

@author Glidias

load color for building
farm4.static.flickr.com/3639/3538831894_cca4aabd68.jpg";
/**
 * Copyright yukkuri6 ( http://wonderfl.net/user/yukkuri6 )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/40JX
 */

// forked from gya's [Alternativa3D] Danboard in the City
/**
 * Copyright gya ( http://wonderfl.net/user/gya )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/cGOS
 */

// forked from Glidias's KDImageDanboCity (Test walkaround)
/**
???????????

??????:
fladdict » ???????????????
http://fladdict.net/blog/2009/05/computer-painting.html

????:
http://www.cap.or.jp/~toukei/kandokoro/html/14/14_2migi.htm

?????????:
http://wonderfl.kayac.com/code/3fb2258386320fe6d2b0fe17d6861e7da700706a

RGB->HSB??:
http://d.hatena.ne.jp/flashrod/20060930#1159622027

**/
package
{
    import alternativ7.engine3d.alternativa3d;
    import alternativ7.engine3d.containers.DistanceSortContainer;
    import alternativ7.engine3d.containers.KDContainer;
    import alternativ7.engine3d.controllers.SimpleObjectController;
    import alternativ7.engine3d.core.Camera3D;
    import alternativ7.engine3d.core.View;
    
    import alternativ7.engine3d.core.Debug;
    import alternativ7.engine3d.core.EllipsoidCollider;
    import alternativ7.engine3d.core.Face;
    import alternativ7.engine3d.core.Object3D;
    import alternativ7.engine3d.core.Object3DContainer;
    import alternativ7.engine3d.core.Vertex;
    import alternativ7.engine3d.core.Wrapper;
    import alternativ7.engine3d.materials.FillMaterial;
    import alternativ7.engine3d.materials.Material;
    import alternativ7.engine3d.objects.Mesh;
    import alternativ7.engine3d.objects.SkyBox;
    import alternativ7.engine3d.primitives.Box;
    import alternativ7.engine3d.primitives.Plane;
    
    import com.greensock.easing.Cubic;
    import com.greensock.easing.Elastic;
    import com.greensock.easing.Linear;
    import com.greensock.plugins.HexColorsPlugin;
    import com.greensock.plugins.TweenPlugin;
    import com.greensock.TweenLite;
    
    import flash.display.StageScaleMode;
    import flash.display.StageAlign;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.geom.Vector3D;
    import flash.net.URLRequest;
    import flash.system.LoaderContext;
    import flash.text.TextField;
    import flash.ui.Keyboard;
    import flash.utils.getTimer;

    use namespace alternativa3d;

    /**
     * WIP: Real-time processed image using Alternativa3D's KDContainer to generate a "growing"  *  city.
     * 
     * Since image is used, can consider using perlin noise or other procedural means to 
     * generate city layout.
     * 
     * I was thinking a Blade runner style city would be nice.
     * Would be good to add in geometry/splitting animations (buildings pushing in/out) as part 
     * of the "growing" effect.
     
     *  W-S-A-D to fly around with mouse drag look.
     * 
     * @author Glidias
     */
    
    [SWF(width = "465", height = "465", frameRate = "30", backgroundColor = "#ffffff")]
    
    public class KDImageDanboCity extends Sprite 
    {
        static public const FLOOR_COLOR:uint = 0xAAAAAA;
        static public const SPEC_RADIUS:Number = 32;
        
        // load color for building
        private const IMAGE_URL:String = "http://farm4.static.flickr.com/3639/3538831894_cca4aabd68.jpg";
        //?????????????????????????????????????????
        private const THRESHOLD:Number = 0.1;

        private var fillRectangleArray:Array;
        private var image:Bitmap;
        private var imageData:BitmapData;
        private var _canvas:Sprite;
        
        public static const DUMMY_VECTOR1:Vector3D = new Vector3D();
        public static const DUMMY_VECTOR2:Vector3D = new Vector3D();
        public static const DUMMY_DISPLACE:Vector3D = new Vector3D(0,0,0.1);
        
        private var camera:Camera3D;
        private var kdContainer:KDContainer;
        private var cameraController:SimpleFlyController;
        private var KD_NODE:Class;
        private var _lastKDNode:*;
        private static const WORLD_SCALE:Number = 16;
        private static const INV_255:Number = 1 / 255;
        private static const MAX_HEIGHT:Number = 12000;
        private static const MAX_Z_BOUNDS:Number = 12000 + 99100;
        private static const MAX_Z_0:Number = 0;
        private var rootContainer:Object3DContainer;
        
        private var _tint:Number;
        
        private var textures:Textures;

        
        public function KDImageDanboCity():void 
        {
            this.stage.align = StageAlign.TOP_LEFT;
            this.stage.scaleMode = StageScaleMode.NO_SCALE;

            super();
            NodeUtils.MAX_ALTITUDE = MAX_Z_BOUNDS;
            NodeUtils.MIN_ALTITUDE = MAX_Z_0;
            TweenPlugin.activate( [HexColorsPlugin] );
            
            textures = new Textures();
            
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
      
        }
        
        private function init(e:Event = null):void 
        {
            
            initA3D(); 
            removeEventListener(Event.ADDED_TO_STAGE, init);
            //???????
                        var req:URLRequest = new URLRequest(IMAGE_URL);
                        var loader:Loader = new Loader();
                        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, loadComplete);    
                        loader.load( req, new LoaderContext(true));
                       
        }
        
        private function initA3D():void 
        {
            
            fillA3DBuffers();  // may improve performance for initial run
            
            
            camera = new Camera3D();
            camera.view = new View(stage.stageWidth, stage.stageHeight);
            stage.addEventListener(Event.RESIZE, onStageResize);
            
            addChild(camera.view);
            
            kdContainer = new KDContainer();
            KD_NODE = getKDNodeClass();
            
        
       
            
            rootContainer = new Object3DContainer();
            rootContainer.addChild(camera);
            
            
             cameraController = new SimpleFlyController(new MyEllipsoidCollider(SPEC_RADIUS, SPEC_RADIUS, SPEC_RADIUS), rootContainer, stage, camera, 800, 8);
            
            //camera.addToDebug(Debug.BOUNDS, Box);
            camera.addToDebug(Debug.NODES, KDContainer);
            //camera.addToDebug(Debug.BOUNDS, KDContainer);
    
            stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
            
            
        
        }
        
        private function fillA3DBuffers():void {
            Vertex.collector = Vertex.createList(300);
            Face.collector = createFaceList(100);
            Wrapper.collector = createWrapperList(300);
        }
        
        private function createFaceList(i:int):Face {
            var f:Face = new Face();
            while ( --i > -1) {
                f = f.next = new Face();
            }
            return f;
        }
        
        private function createWrapperList(i:int):Wrapper {
            var f:Wrapper = new Wrapper();
            while ( --i > -1) {
                f = f.next = new Wrapper();
            }
            return f;
        }
        
      
        private var _doGenerateBuildings:Boolean = true;
        private var player:DanboPlayer;
          
        private function onKeyDown(e:KeyboardEvent):void 
        {
            if (e.keyCode === Keyboard.TAB) {
                //camera.debug = !camera.debug;
            }
            if (e.keyCode === Keyboard.BACKSPACE) { 
                _doGenerateBuildings = !_doGenerateBuildings;    
            }
        }
        
        private function getKDNodeClass():Class {
            var dummy:KDContainer = new KDContainer();
            dummy.createTree(new <Object3D>[new Box(8,8,8)]);
            return Object(dummy.root).constructor;
        }
        
        private function onStageResize(e:Event):void 
        {
            camera.view.width = stage.stageWidth;
            camera.view.height = stage.stageHeight;
        }
        
        //??????????
        public function loadComplete(e:Event = null):void 
        {
            e.target.removeEventListener(Event.COMPLETE, loadComplete);
            
            image = e.target.loader.content as Bitmap;
            imageData = image.bitmapData;

            //???????????
            _canvas = new Sprite;
            
            var p:RectanglePiece = new RectanglePiece();
            var node:*;
            var threshold:Number = kdContainer.threshold;
            
            p.x0 = 0;
            p.y0 = 0;
            p.x1 = imageData.width;
            p.y1 = imageData.height;
            p.c = 0;
            
            // Setup root starting
            kdContainer.root = NodeUtils.ROOT = setupNode(p);
            kdContainer.boundMinX = 0;
            kdContainer.boundMinY = 0;
            kdContainer.boundMaxX = p.x1 * WORLD_SCALE;
            kdContainer.boundMaxY = p.y1 * WORLD_SCALE;
            kdContainer.boundMinZ = 0;
            kdContainer.boundMaxZ = MAX_Z_BOUNDS;
            
            
            camera.x = kdContainer.boundMaxX * .5;
            camera.y = kdContainer.boundMaxY * .5;
            camera.z =  MAX_HEIGHT + 63400;
            cameraController.updateObjectTransform();
            cameraController.lookAtXYZ(camera.x, camera.y, 0);
            
            var skybox:SkyBox = new SkyBox(99999999);
            skybox.setMaterialToAllFaces( new FillMaterial(0xEEFEFF) );
            rootContainer.addChild(skybox);
            
            
            var floor:Plane = new Plane(kdContainer.boundMaxX, kdContainer.boundMaxY,1,1,false,false,false,null, new FillMaterial(FLOOR_COLOR) );
            floor.clipping = 2;
            rootContainer.addChild(floor);
            floor.x = kdContainer.boundMaxX * .5;
            floor.y = kdContainer.boundMaxY * .5;
            
        rootContainer.addChild(kdContainer);
            
            //???????????????????
            fillRectangleArray = new Array(p);
            
            addChild(_canvas);
            addEventListener(Event.ENTER_FRAME, onEnterFrame);
            
            TweenLite.delayedCall(1, createPlayer);
        }
        
        private function setupNode(p:RectanglePiece):* {
            var node:*;
            p.node = node =  new KD_NODE();
            node.boundMinX = p.x0 * WORLD_SCALE;
            node.boundMinY = p.y0 * WORLD_SCALE;
            node.boundMinZ = 0;
            node.boundMaxX = p.x1 * WORLD_SCALE;
            node.boundMaxY = p.y1 * WORLD_SCALE;
            node.boundMaxZ = MAX_Z_BOUNDS;
            
            var bNode:*;
            node.positive = bNode =  new KD_NODE();
            bNode.boundMinX = node.boundMinX;
            bNode.boundMinY = node.boundMinY;
            //bNode.boundMinZ = height;  // to be set later
            bNode.boundMaxX = node.boundMaxX;
            bNode.boundMaxY = node.boundMaxY;
            bNode.boundMaxZ = MAX_Z_BOUNDS;
            
            node.negative = bNode =  new KD_NODE();
            bNode.boundMinX = node.boundMinX;
            bNode.boundMinY = node.boundMinY;
            bNode.boundMinZ = 0;
            bNode.boundMaxX = node.boundMaxX;
            bNode.boundMaxY = node.boundMaxY;
            //bNode.boundMaxZ = height;  // to be set later
        

            return node;
        }
        
        

        private function createBuilding(p:RectanglePiece, color:uint, fromHeight:Number, fromColor:uint):void
        {
            var node:* = p.node;
            
            var height:Number = 1 + _tint * MAX_HEIGHT;
            var w:Number = (p.x1 - p.x0) * WORLD_SCALE;
            var h:Number = (p.y1 - p.y0) * WORLD_SCALE;

            // set positive/negative dimensions
            
            node.positive.boundMinZ  = height;
            node.negative.boundMaxZ = height;
            node.axis = 2;
            node.coord = height;
            node.minCoord = height - 0.01;
            node.maxCoord = height + 0.01;        
            
            
             var building:KDBuilding = new KDBuilding(p.x0 * WORLD_SCALE, p.y0 * WORLD_SCALE, w, h, height, color, node);

            node = node.negative;
            node.objectList = building;
            node.objectBoundList = building;

        
    
            building.height = fromHeight;
            TweenLite.to(building, 1.6, { height:height, ease:Cubic.easeInOut } );
                
            (building.faceList.material as FillMaterial).color = fromColor;
            TweenLite.to(building.faceList.material, .4, { hexColors: { color:color }, ease:Linear.easeNone } );
        
        }
        
        private function createPlayer():void {
            var playerDanbord:Danbord = addDanbord();
            //if (Danbord.RENDERER == null) {
            playerDanbord.setupRenderer(camera);
            Danbord.precomputeAll();
            //}
            
            player = new DanboPlayer(playerDanbord, camera, rootContainer, stage);
            var child:DisplayObject = addChild( new CrossHair() );
            child.x = stage.stageWidth * .5;
            child.y = stage.stageHeight * .5;
            

            
        }
        
        
        
        
   private function addDanbord(posX:Number=-1, posY:Number=-1, posZ:Number=-1):Danbord {
            var mySc:Number = Math.random() * 1 + 0.5;
            
            var myDanbord:Danbord = new Danbord(textures);
            myDanbord.x = posX >= 0 ? posX : Math.random() * kdContainer.boundMaxX;
            myDanbord.y = posY >=0 ? posY : Math.random() * kdContainer.boundMaxY;
            //myDanbord.z = posZ >=0 ? posZ : Math.random() * AREA_
            
            myDanbord.scaleX = myDanbord.scaleY = myDanbord.scaleZ = mySc;
          //  container.addChild(myDanbord);
         
            var myKage:Kage = new Kage(textures);
            myKage.x = myDanbord.x
            myKage.y = myDanbord.y
            myKage.scaleX = myKage.scaleY = myKage.scaleZ = mySc;
           // kageContainer.addChild(myKage);
            myKage.calculateBounds();
            myDanbord.boundMinZ = 0;
            myDanbord.boundMaxZ = 288;
            const padd:Number = 1;
            myDanbord.boundMinX = (myKage.boundMinX+padd) * mySc ;
            myDanbord.boundMaxX =( myKage.boundMaxX-padd) * mySc;
            myDanbord.boundMinY = (myKage.boundMinY+padd) * mySc;
            myDanbord.boundMaxY = (myKage.boundMaxY-padd) * mySc;

            myDanbord.kage = myKage;
            
            // find zone for danbord
            myDanbord.node = insertObjectIntoZNode(kdContainer.root, myDanbord);    
            myDanbord.worldBounds  = myDanbord.node.positive.objectBoundList;
            //rootContainer.addChild(myDanbord);
            camera.lookAt(myDanbord.x, myDanbord.y, myDanbord.z);
            cameraController.setObjectPosXYZ(myDanbord.x, myDanbord.y, myDanbord.z);
            //
            //rootContainer.addChild(myDanbord);
                        kdContainer.addChild(myDanbord);
            return myDanbord;
        }
        
        
        
        // Inserts object into single node based on origin position without considering if it's bounds straddles multiple partitions
        public function insertObjectIntoNode(node:*, obj:Object3D):* {
            if (node.negative == null) {
                // leaf object (Should consider if got multiple nodes)??
                if (node.objectList != null) throw new Error("ALREAdy occupied!");
                node.objectList = obj;
                return node;
            }
            var axis:int = node.axis;
            var value:int = axis == 0 ? obj.x : axis == 1? obj.y : obj.z;
            return value >= node.coord ? insertObjectIntoNode(node.positive,obj) : insertObjectIntoNode(node.negative,obj);
        }
        
        // Inserts object into single z-axis split node based on 2d origin position without considering if it's bounds straddles multiple partitions, and placing it in positive half-space region (assuming it's a leaf rooftop zone)
        public function insertObjectIntoZNode(node:*, obj:Object3D):* {
            var axis:int = node.axis;
            var bounds:Object3D;
            if (axis == 2) {
                // leaf object (Should consider if got multiple nodes)??
                if (node.positive.objectList != null) throw new Error("ALREAdy occupied!"+node.positive.objectList);
                //node.positive.objectList = obj;
                node.positive.objectBoundList = bounds = new Object3D();
                obj.z = node.maxCoord;
                bounds.boundMinX = obj.boundMinX*obj.scaleX + obj.x;
                bounds.boundMinY = obj.boundMinY*obj.scaleY + obj.y;
                bounds.boundMinZ = obj.boundMinZ*obj.scaleZ + obj.z;
                bounds.boundMaxX = obj.boundMaxX*obj.scaleX + obj.x;
                bounds.boundMaxY = obj.boundMaxY*obj.scaleY + obj.y;
                bounds.boundMaxZ = obj.boundMaxZ*obj.scaleZ + obj.z;
                return node;
            }
            var value:int = axis != 0 ? obj.y : obj.x;
            return value >= node.coord ? insertObjectIntoZNode(node.positive,obj) : insertObjectIntoZNode(node.negative,obj);
        }
        

        
        

         private var _lastTime:int = 0;
        private static const MS:Number = 1 / 1000;
        //???????
        
        //???
        private function onEnterFrame(e:Event):void 
        {
            var node:*;
            
            

            //??????????
            var curTime:Number = getTimer();
            var timeElapsed:Number = _lastTime != 0 ? curTime-_lastTime : 30;
            timeElapsed *= MS;
            _lastTime = curTime;
            
            
            if (!_doGenerateBuildings) {
                doPreRenderUpdates( timeElapsed);
                camera.transformId++;
                camera.render();
                return;
            }
            
            
            //?????????
            if (fillRectangleArray.length < 1) {
                removeEventListener(Event.ENTER_FRAME, onEnterFrame);
                var tx:TextField = new TextField();
                tx.text = '??';
                tx.textColor = 0xFFFFFF;
                addChild(tx);
            }else {
                //???????????????1?????
                var rect:RectanglePiece = fillRectangleArray.shift();
                var cArray:Array = deviationLogic(rect.x0, rect.y0, rect.x1, rect.y1);
                rect.c = cArray[0];
                rect.color = cArray[1];
                
                var halfWidth:Number = (rect.x1 - rect.x0) * .5;
                var halfHeight:Number = (rect.y1 - rect.y0) * .5;

                // ???????????????????????2????????
                if (rect.c > THRESHOLD && (halfWidth > 2 || halfHeight > 2)) {
                    //??????
                    /*
                    _canvas.graphics.lineStyle(0, 0xAAAAAA);
                    _canvas.graphics.beginFill(cArray[1]);
                    _canvas.graphics.drawRect(rect.x0, rect.y0, (rect.x1 - rect.x0), (rect.y1 - rect.y0));
                    */
                    
                    node  = rect.node;
                    var removeObj:KDBuilding;  // the building object holder
                    
                    if (rect.parent != null ) {
                        if ((removeObj=rect.parent.node.negative.objectList) != null) {
                            rect.parent.node.negative.objectList = null;
                            rect.parent.node.negative.objectBoundList = null;
                            var removeObj2:Danbord = rect.parent.node.positive.objectList; // the rooftop object holder
                            // do main starting cut
                           
                            rect.parent.node.axis = rect.parent.axis;
                            rect.parent.node.coord = rect.parent.coord;
                            rect.parent.node.minCoord = rect.parent.coord - 0.01;
                            rect.parent.node.maxCoord = rect.parent.coord + 0.01;
                            
                            if (rect.positive) {
                                rect.parent.node.positive = node;
                                rect.parent.node.negative = rect.sibling.node;
                            }
                            else {
                                rect.parent.node.negative = node;
                                rect.parent.node.positive = rect.sibling.node;
                            }
                            TweenLite.killTweensOf(removeObj);
                            
                            createBuilding(rect, rect.color, removeObj.height, rect.parent.color);
                            createBuilding(rect.sibling, rect.parent.color, removeObj.height, rect.parent.color);
                            
                            if (removeObj2 != null) {
                            
                            //    rect.parent.node.positive.objectList = null;
                            //    rect.parent.node.positive.objectBoundList = null;
                            }
                            
                        }
                        else {  // fill up remaining branch (either negative or postiive)
                            if (rect.positive) {
                                rect.parent.node.positive = node;
                            }
                            else {
                                rect.parent.node.negative = node;
                            }
                            removeObj = rect.node.negative.objectList;
                            createBuilding(rect, rect.color, removeObj.height, rect.parent.color);
                        }
                    }
                    else { // Root node case!
                        
                        createBuilding(rect, rect.color, 0, FLOOR_COLOR);
                    }
                    
                    
                    //???2??????????????????????
                    
                    var rect0:RectanglePiece = new RectanglePiece();
                    var rect1:RectanglePiece = new RectanglePiece();
                   var randomX:Number = Math.floor(Math.random()*(halfWidth-4)+4); 
                    var randomY:Number = Math.floor(Math.random()*(halfHeight-4)+4);
     
                    
                    // Rather hackish pointers here!
                    rect0.positive = false;
                    rect1.positive = true;
                    rect0.sibling = rect1;
                    rect1.sibling  = rect0;
                    rect0.parent = rect;
                    rect1.parent = rect;
                    
    
                    
                    if (halfWidth > halfHeight) {
                   
                      //  node.axis = 0;
                      // node.coord = (rect.x0 + randomX) * WORLD_SCALE;
                       // node.minCoord = node.coord - kdContainer.threshold;
                      //  node.maxCoord = node.coord + kdContainer.threshold;
                        
                        rect.axis = 0;
                        rect.coord = (rect.x0 + randomX) * WORLD_SCALE;
                        
                        rect0.x0 = rect.x0;   // negative x
                        rect0.y0 = rect.y0;
                        rect0.x1 = rect.x0+randomX;
                        rect0.y1 = rect.y1;
                        fillRectangleArray.push(rect0);
                        
                       // setupNodeExisting(rect0, node.negative); 
                        setupNode(rect0);
                        

                        rect1.x0 = rect.x0+randomX;  // postive x
                        rect1.y0 = rect.y0;
                        rect1.x1 = rect.x1;
                        rect1.y1 = rect.y1;
                        fillRectangleArray.push(rect1);
                        
                   
                     
                        //setupNodeExisting(rect1, node.positive);
                        setupNode(rect1);
                    

                    }else {
                      //  node.axis = 1;
                      //  node.coord = (rect.y0 + randomY) * WORLD_SCALE;
                      //  node.minCoord = node.coord - kdContainer.threshold;
                      //  node.maxCoord = node.coord + kdContainer.threshold;
                      
                          rect.axis = 1;
                        rect.coord = (rect.y0 + randomY) * WORLD_SCALE;

                        rect0.x0 = rect.x0;  // negative y
                        rect0.y0 = rect.y0;
                        rect0.x1 = rect.x1;
                        rect0.y1 = rect.y0+randomY;
                        fillRectangleArray.push(rect0);
                        
                        
                       // setupNodeExisting(rect0, node.negative);
                        setupNode(rect0);

                        rect1.x0 = rect.x0;  //postive y
                        rect1.y0 = rect.y0+randomY;
                        rect1.x1 = rect.x1;
                        rect1.y1 = rect.y1;
                        fillRectangleArray.push(rect1);
                        
                        
                       
                       // setupNodeExisting(rect1, node.positive);
                       setupNode(rect1);
                        
                    }
                }
            }
            
            doPreRenderUpdates(timeElapsed);
            camera.transformId++;
            camera.render();
        }
        
        private function doPreRenderUpdates(t:Number):void 
        {
            if (player == null) updateSpecCamera()
            else {
                player.update(t);
                player.preRender();
            }
        }
        
        private function updateSpecCamera():void {
            cameraController.update();
            var sphere:Vector3D = new Vector3D(camera.x, camera.y, camera.z, SPEC_RADIUS);
            var len:int = KDBuilding.numCollidables;
            var collidables:Vector.<KDBuilding> = KDBuilding.collidables;
            var collidable:KDBuilding;
            var highestZ:Number = 0;
            if (!cameraController.hasMoved) {  
                // do a dummy collision test
                cameraController.collider.getCollision( new Vector3D(camera.x, camera.y, camera.z), DUMMY_DISPLACE ,DUMMY_VECTOR1, DUMMY_VECTOR2, rootContainer);
            }
            
            len = KDBuilding.numCollidables;
            for (var i:int = 0; i < len; i++) {
                collidable = collidables[i];
                if (kdContainer.boundIntersectSphere(sphere,collidable.boundMinX, collidable.boundMinY, collidable.boundMinZ, collidable.boundMaxX, collidable.boundMaxY, collidable.boundMaxZ)  && collidable.boundMaxZ > highestZ) {
                    highestZ = collidable.boundMaxZ;
                }
            }
            
                if ( camera.z < highestZ + SPEC_RADIUS) {

                    cameraController.setObjectPosXYZ(camera.x, camera.y, highestZ + SPEC_RADIUS);
                    camera.z =  highestZ + SPEC_RADIUS;
                }
        
        }
        

        /**
         * ???????????????????
         * @param    x0    ???x??
         * @param    y0    ???y??
         * @param    x1    ???x??
         * @param    y1    ???y??
         * @return    ????????????
         */
        private function deviationLogic(x0:Number,y0:Number,x1:Number,y1:Number):Array {
            var rgb:uint = 0;
            var r:uint = 0;
            var g:uint = 0;
            var b:uint = 0;
            var hsb:Array = new Array();
            var bArray:Array = new Array();
            var br:Number = 0;
            var av:Number = 0;

            //????????
            for (var i:int = x0; i < x1;i++ ) {
                for (var j:int = y0; j < y1; j++ ) {
                    rgb = imageData.getPixel(i, j);
                    r += (rgb >> 16) & 255;
                    g += (rgb >> 8) & 255;
                    b += rgb & 255;
                    hsb = uintRGBtoHSB(rgb);
                    br += hsb[2];
                    bArray.push(hsb[2]);
                }
            }
            av = br / bArray.length;
            r = r / bArray.length;
            g = g / bArray.length;
            b = b / bArray.length;
            rgb = (r << 16) | (g << 8) | (b << 0);
            _tint = (255 - ( 0.21 * r + 0.71 * g + 0.07 * b )) * INV_255;
            //???????
            br = 0;
            for (i = 0; i < bArray.length; i++ ) {
                br += (bArray[i] - av) *(bArray[i] - av);
            }
            return [Math.sqrt(br / bArray.length),rgb];
            
        }
        /**
         * 
         * @param    rgb    RGB??(uint)
         * @return HSB??([0]=hue, [1]=saturation, [2]=brightness)
         */
        private function uintRGBtoHSB(rgb:uint):Array {
            var r:uint = (rgb >> 16) & 255;
            var g:uint = (rgb >> 8) & 255;
            var b:uint = rgb & 255;
            return RGBtoHSB(r, g, b);
        }
        /** RGB??HSB????
         * @param r    ??????(0~255)
         * @param g ??????(0~255)
         * @param b ??????(0~255)
         * @return HSB??([0]=hue, [1]=saturation, [2]=brightness)
         */
        private function RGBtoHSB(r:int, g:int, b:int):Array {
            var cmax:Number = Math.max(r, g, b);
            var cmin:Number = Math.min(r, g, b);
            var brightness:Number = cmax / 255.0;
            var hue:Number = 0;
            var saturation:Number = (cmax != 0) ? (cmax - cmin) / cmax : 0;
            if (saturation != 0) {
                var redc:Number = (cmax - r) / (cmax - cmin);
                var greenc:Number = (cmax - g) / (cmax - cmin);
                var bluec:Number = (cmax - b) / (cmax - cmin);
                if (r == cmax) {
                    hue = bluec - greenc;
                } else if (g == cmax) {
                    hue = 2.0 + redc - bluec;
                } else {
                    hue = 4.0 + greenc - redc;
                }
                hue = hue / 6.0;
                if (hue < 0) {
                    hue = hue + 1.0;
                }
            }
            return [hue, saturation, brightness];
        }
    }    
}
import alternativ7.engine3d.containers.ConflictContainer;
import alternativ7.engine3d.containers.DistanceSortContainer;
import alternativ7.engine3d.containers.KDContainer;
import alternativ7.engine3d.core.Camera3D;
import alternativ7.engine3d.core.Canvas;
import alternativ7.engine3d.core.EllipsoidCollider;
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.core.Object3D;
import alternativ7.engine3d.core.Vertex;
import alternativ7.engine3d.core.VG;
import alternativ7.engine3d.core.Wrapper;
import alternativ7.engine3d.materials.FillMaterial;
import alternativ7.engine3d.objects.Mesh;
import alternativ7.engine3d.primitives.Box;
import alternativ7.engine3d.alternativa3d;
import flash.filters.DropShadowFilter;
import flash.geom.Vector3D;
import flash.utils.Dictionary;

use namespace alternativa3d;

    /**
     * ...
     * @author DefaultUser (Tools -> Custom Arguments...)
     */
    class RectanglePiece 
    {
        public var x0:Number;
        public var y0:Number;
        public var x1:Number;
        public var y1:Number;
        public var c:Number;
        public var node:*;
        public var parent:RectanglePiece;
        public var positive:Boolean;
        public var color:uint;
        public var sibling:RectanglePiece;
        
        // split kd info
        public var axis:int;
        public var coord:Number;
    
        
        public function RectanglePiece() 
        {
             this.x0 = 0;
             this.y0 = 0;
             this.x1 = 0;
             this.x1 = 0;
             this.c = 0;            
        }
        
    }
    
    
class MyEllipsoidCollider extends EllipsoidCollider {
    public function MyEllipsoidCollider(radiusX:Number, radiusY:Number, radiusZ:Number) {
        super(radiusX, radiusY, radiusZ);
    }
    
        override public function calculateDestination (source:Vector3D, displacement:Vector3D, object:Object3D, excludedObjects:Dictionary = null) : Vector3D {
            KDBuilding.numCollidables = 0;
            return super.calculateDestination(source, displacement, object, excludedObjects);
        }


        override public function getCollision (source:Vector3D, displacement:Vector3D, resCollisionPoint:Vector3D, resCollisionPlane:Vector3D, object:Object3D, excludedObjects:Dictionary = null) : Boolean {
            KDBuilding.numCollidables = 0;
            return super.getCollision(source, displacement, resCollisionPoint, resCollisionPlane, object, excludedObjects);
        }
        
        public function $calculateDestination(source:Vector3D, displacement:Vector3D, object:Object3D, excludedObjects:Dictionary = null) : Vector3D {
            return super.calculateDestination(source, displacement, object, excludedObjects);
        }
        
        public function $getCollision (source:Vector3D, displacement:Vector3D, resCollisionPoint:Vector3D, resCollisionPlane:Vector3D, object:Object3D, excludedObjects:Dictionary = null) : Boolean {
            return super.getCollision(source, displacement, resCollisionPoint, resCollisionPlane, object, excludedObjects);
        }
    
}
    
    class KDBuilding extends Mesh
    {
        // TODO: Can consider recycling of KDBuildings
        public static var collidables:Vector.<KDBuilding> = new Vector.<KDBuilding>();
        public static var numCollidables:int = 0;
        
        //public var childrenList:Object3D; 
        public static var DS_SORT:DistanceSortContainer = new DistanceSortContainer();
        public static var CONFLICT:ConflictContainer = new ConflictContainer();
        
        public var node:*;
        
        public function KDBuilding(xPos:Number, yPos:Number, width:Number, length:Number, height:Number, color:uint, node:*) 
        {
            clipping = 2;
            sorting = 0;
            this.node = node;
            
            var v1:Vertex;
            var v2:Vertex;
            var v3:Vertex;
            var v4:Vertex;
                
            var mat:FillMaterial = new FillMaterial(color); //, 1, 0, 0xDDCC66
            var v:Vertex;
            var f:Face;
            var w:Wrapper;

            // Define top roof vertices
            vertexList = v = Vertex.collector || new Vertex(); v.u = 0; v.v = 0;
            Vertex.collector = v.next;
            v.x = xPos;
            v.y = yPos;
            v.z = height;
            v1 = v;
            
            v.next =  v = v.create(); v.u = 0; v.v = 0;
            v.x = xPos + width;
            v.y = yPos;
            v.z = height;
            v2 = v;
            
            v.next =  v = v.create(); v.u = 0; v.v = 0;
            v.x = xPos + width;
            v.y = yPos + length;
            v.z = height;
            v3 = v;
            
            v.next = v =  v.create(); v.u = 0; v.v = 0;
            v.x = xPos;
            v.y = yPos + length;
            v.z = height;
            v4 = v;
            
            var v5:Vertex;
            var v6:Vertex;
            var v7:Vertex;
            var v8:Vertex;
            
            // Define bottom vertices
            v.next = v = v.create(); v.u = 0; v.v = 0;
            v.x = xPos;
            v.y = yPos;
            v.z = 0;
            v5 = v;
            
            v.next =  v = v.create(); v.u = 0; v.v = 0;
            v.x = xPos + width;
            v.y = yPos;
            v.z = 0;
            v6 = v;
            
            v.next =  v = v.create(); v.u = 0; v.v = 0;
            v.x = xPos + width;
            v.y = yPos + length;
            v.z = 0;
            v7 = v;
            
            v.next = v =  v.create(); v.u = 0; v.v = 0;
            v.x = xPos;
            v.y = yPos + length;
            v.z = 0;
            v8 = v;
            
            // top face
            faceList = f = Face.collector || new Face();
            Face.collector = f.next;
            f.material = mat;
            f.wrapper = w = Wrapper.collector || new Wrapper();
            Wrapper.collector = w.next; 
            w.vertex = v1;
            w.next = w = w.create();
            w.vertex = v2;
            w.next = w = w.create();
            w.vertex = v3;
            w.next = w = w.create();
            w.vertex = v4;
            f.normalX = 0;
            f.normalY = 0;
            f.normalZ = 1;
            f.offset = height;
            //f.calculateBestSequenceAndNormal();
            //if (!f.normal.nearEquals(new Vector3D(0,0,1), 0.001)) throw new Error("MISMATCH top!"+f.normal);
            
            // South face
            f.next = f = f.create();
            f.material = mat;
            f.wrapper = w = w.create();
            w.vertex = v5;
            w.next = w = w.create();
            w.vertex = v6;
            w.next = w = w.create();
            w.vertex = v2;
            w.next = w = w.create();
            w.vertex = v1;
            f.normalX = 0;
            f.normalY = -1;
            f.normalZ = 0;
            f.offset = -yPos;
            //f.calculateBestSequenceAndNormal();
            //if (!f.normal.nearEquals(new Vector3D(0,-1,0), 0.001)) throw new Error("MISMATCH south!"+f.normal);
            
            // East Face
            f.next = f = f.create();
            f.material = mat;
            f.wrapper = w = w.create();
            w.vertex = v6;
            w.next = w = w.create();
            w.vertex = v7;
            w.next = w = w.create();
            w.vertex = v3;
            w.next = w = w.create();
            w.vertex = v2;
            f.normalX = 1;
            f.normalY = 0;
            f.normalZ = 0;
            f.offset = xPos + width;
            //f.calculateBestSequenceAndNormal();
            //if (!f.normal.nearEquals(new Vector3D(1,0,0), 0.001)) throw new Error("MISMATCH east!"+f.normal);
                
            // North Face
            f.next = f = f.create();
            f.material = mat;
            f.wrapper = w = w.create();
            w.vertex = v7;
            w.next = w = w.create();
            w.vertex = v8;
            w.next = w = w.create();
            w.vertex = v4;
            w.next = w = w.create();
            w.vertex = v3;
            f.normalX = 0;
            f.normalY = 1;
            f.normalZ = 0;
            f.offset = yPos + length;
            //f.calculateBestSequenceAndNormal();
            //if (!f.normal.nearEquals(new Vector3D(0,1,0), 0.001)) throw new Error("MISMATCH north!"+f.normal);
            
            // West Face
            f.next = f = f.create();
            f.material = mat;
            f.wrapper = w = w.create();
            w.vertex = v8;
            w.next = w = w.create();
            w.vertex = v5;
            w.next = w = w.create();
            w.vertex = v1;
            w.next = w = w.create();
            w.vertex = v4;
            f.normalX = -1;
            f.normalY = 0;
            f.normalZ = 0;
            f.offset = -xPos;
            //f.calculateBestSequenceAndNormal();
            //if (!f.normal.nearEquals(new Vector3D(-1,0,0), 0.001)) throw new Error("MISMATCH west!"+f.normal);
            
            
            // ^^^ note vertex normals not coded in!    
            
            // calculate bounds
            boundMinX = xPos;
            boundMinY = yPos;
            boundMinZ = 0;
            boundMaxX = xPos + width;
            boundMaxY = yPos + length;
            boundMaxZ = height;

            //if (node.axis != 2) throw new Error("SHOULD NOT BE!");
            // for debugging (checking) purposes
            //calculateFacesNormals();
        }
        
        
        // Boiler-plate mesh draw implementation to prevent errors with camera.debug mode due to
        // private classes.
        override alternativa3d function draw(camera:Camera3D, parentCanvas:Canvas):void {

            calculateInverseMatrix();
            // either transformId++; or if camera.transformId used as an incrementing timestamp
            transformId = camera.transformId;  
            
            //if (childrenList != null) drawChildren(camera, parentCanvas);
            
            // shoudl check below bounds if need to draw??
            
            var f:Face = prepareFaces(camera);
            if (f == null) return;
            if (culling > 0) {
                f = camera.clip(f, culling);
                if (f == null) return;
            }
            drawFaces(camera, parentCanvas.getChildCanvas(true, false, this, 1, blendMode, colorTransform, filters), f);
        }
        
        /*
        private function drawChildren(camera:Camera3D, parentCanvas:Canvas):void {
            // draw children on rooftop.
            const DS_SORT:DistanceSortContainer = DS_SORT;
            DS_SORT.childrenList = childrenList;
            DS_SORT.boundMinX = boundMinX;
            DS_SORT.boundMinY = boundMinY;
            DS_SORT.boundMinZ = boundMinZ;
            DS_SORT.boundMaxX = boundMaxX;
            DS_SORT.boundMaxY = boundMaxY;
            DS_SORT.boundMaxZ = boundMaxZ;
            
            if (DS_SORT.transformId != transformId) {
                DS_SORT.ma = ma;
            }
            if (DS_SORT.cullingInCamera(camera, culling) < 0) return;
            DS_SORT.draw(camera, parentCanvas);    
        }
        */
        
        /*
        override alternativa3d function getVG(camera:Camera3D):VG {  // wip
            
            calculateInverseMatrix();
            // either transformId++; or if camera.transformId used as an incrementing timestamp
            //transformId = camera.transformId;
            transformId++;
            var f:Face = prepareFaces(camera);
            if (f == null) return null;
        
            if (culling > 0) {
                
                camera.clip(f, culling);
                if (f == null) return null;
            }
            return VG.create(this, f, 2, 0, false);
            
        }
        */
        
        override alternativa3d function collectPlanes(center:Vector3D, a:Vector3D, b:Vector3D, c:Vector3D, d:Vector3D, collector:Vector.<Face>, excludedObjects:Dictionary=null) : void {
transformId++;
            collidables[numCollidables++] = this;
            super.collectPlanes(center, a, b, c, d, collector, excludedObjects);
        }

        
        public function get height():Number { return vertexList.z; }
        

        public function set height(value:Number):void 
        {
            // todo: investigate... tween should have been killed once axis==2 changes to 0 or 1.
            if (node.axis != 2) return;// throw new Error("SHOudl nto be!");
            var v:Vertex = vertexList;
            v.z = value; v = v.next;
            v.z = value; v = v.next;
            v.z = value; v = v.next;
            v.z = value;
            faceList.offset = value;
            boundMaxZ = value;
            node.negative.boundMaxZ = value;
            node.positive.boundMinZ = value;
            
        }
        
        
        
    }
    

    
    

    import alternativ7.engine3d.controllers.SimpleObjectController;
    import alternativ7.engine3d.core.Camera3D;
    import alternativ7.engine3d.core.EllipsoidCollider;
    import alternativ7.engine3d.core.Object3D;
    import flash.display.InteractiveObject;
    import flash.geom.Vector3D;
    import flash.utils.Dictionary;
    /**
     * ...
     * @author Glenn Ko
     */
    class SimpleFlyController extends SimpleObjectController 
    {
        public var currentPosition:Vector3D;
        public var collider:EllipsoidCollider;
        public var collidable:Object3D;
        
        public var displacement:Vector3D = new Vector3D();
        public var collisionPoint:Vector3D = new Vector3D();
        public var lastPosition:Vector3D  = new Vector3D();
        public var excludedObjects:Dictionary = null;
        public var gotCollision:Boolean;
        public var collisionPlane:Vector3D = new Vector3D();
        public var hasMoved:Boolean = false;
        
        public function SimpleFlyController(collider:EllipsoidCollider, collidable:Object3D, eventSource:InteractiveObject, object:Object3D, speed:Number, speedMultiplier:Number = 3, mouseSensitivity:Number = 1) 
        {
            super(eventSource, object, speed, speedMultiplier, mouseSensitivity);
            this.collider = collider;
            this.collidable = collidable;
        }
        

        override public function update():void {
            var object:Object3D = this.object;
            
            if (object == null) return;
            if (collider && collidable) {
                lastPosition.x = object.x;
                lastPosition.y = object.y;
                lastPosition.z = object.z;
                super.update();
                displacement.x = object.x -  lastPosition.x;
                displacement.y = object.y  - lastPosition.y;
                displacement.z = object.z  - lastPosition.z;
                if (displacement.x * displacement.x + displacement.y * displacement.y + displacement.z * displacement.z == 0) {
                    gotCollision = false;
                    hasMoved = false;
                    return;
                }
                hasMoved = true;
                gotCollision = collider.getCollision(lastPosition, displacement, collisionPoint, collisionPlane, collidable);
                var dest:Vector3D = collider.calculateDestination(lastPosition, displacement, collidable, excludedObjects);
                 // set back frame coherant transform values
                setObjectPosXYZ(dest.x, dest.y, dest.z);
                // refresh values immediately
                object.x = dest.x; 
                object.y = dest.y;
                object.z = dest.z;
                
                displacement.x = dest.x -  lastPosition.x;
                displacement.y = dest.y  - lastPosition.y;
                displacement.z = dest.z  - lastPosition.z;
                
                currentPosition = dest;
                
    
            }
            else {
                super.update();
            }
        }
        
    }


//Texture.as
//????????????
//package {
    import alternativ7.engine3d.containers.BSPContainer;
    import alternativ7.engine3d.containers.ConflictContainer;
    import alternativ7.engine3d.containers.KDContainer;
    import alternativ7.engine3d.core.Camera3D;
    import alternativ7.engine3d.core.Canvas;
    import alternativ7.engine3d.core.Debug;
    import alternativ7.engine3d.core.EllipsoidCollider;
    import alternativ7.engine3d.core.Face;
    import alternativ7.engine3d.core.Object3D;
    import alternativ7.engine3d.core.VG;
    import alternativ7.engine3d.materials.FillMaterial;
    import alternativ7.engine3d.materials.Material;
    import alternativ7.engine3d.objects.Mesh;
    import alternativ7.engine3d.primitives.Box;
    import alternativ7.engine3d.primitives.Sphere;
    import flash.display.FrameLabel;
    import flash.display.Shape;
    import flash.display.Stage;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.geom.Vector3D;
    import flash.system.LoaderContext;
    import flash.ui.Keyboard;
    import flash.utils.Dictionary;
    

    //
    //Alternativa3D
    import alternativ7.engine3d.core.Object3DContainer;
    import alternativ7.engine3d.loaders.MaterialLoader;
    import alternativ7.engine3d.materials.TextureMaterial;
        
    //public 
    class Textures {
        //3D??
        private var loaderContext:LoaderContext;
        private var materialLoader:MaterialLoader;
        
        private var textureMats:Vector.<TextureMaterial>;
        
        public var txMatDanboTop:TextureMaterial;
        public var txMatDanboBottom:TextureMaterial;
        public var txMatDanboFront:TextureMaterial;
        public var txMatDanboLeft:TextureMaterial;
        public var txMatDanboBack:TextureMaterial;
        public var txMatDanboRight:TextureMaterial;
        public var txMatDanboFace:TextureMaterial;
        public var txMatDanboHeadTop:TextureMaterial;
        public var txMatDanboBodyFront:TextureMaterial;
        public var txMatKage:TextureMaterial;
        
        public function Textures():void {
            //??????????
            txMatDanboTop = new TextureMaterial();  txMatDanboTop.name = "top";
            txMatDanboBottom = new TextureMaterial(); txMatDanboBottom.name = "bottom";
            txMatDanboFront = new TextureMaterial(); txMatDanboFront.name = "front";
            txMatDanboLeft = new TextureMaterial(); txMatDanboLeft.name = "left";
            txMatDanboBack = new TextureMaterial(); txMatDanboBack.name = "back";
            txMatDanboRight = new TextureMaterial(); txMatDanboRight.name = "right";
            txMatDanboFace = new TextureMaterial(); txMatDanboFace.name = "face";
            txMatDanboHeadTop = new TextureMaterial(); txMatDanboHeadTop.name = "headTop";
            txMatDanboBodyFront = new TextureMaterial(); txMatDanboBodyFront.name = "bodyFront";
            txMatKage = new TextureMaterial();
            
            txMatDanboTop.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_top.png";
            txMatDanboBottom.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_bottom.png";
            txMatDanboFront.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_front.png";
            txMatDanboLeft.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_left.png";
            txMatDanboBack.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_back.png";
            txMatDanboRight.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_right.png";
            txMatDanboFace.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_face.png";
            txMatDanboHeadTop.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_headtop.png";
            txMatDanboBodyFront.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/danbo_tx_bodyfront.png";
            txMatKage.diffuseMapURL = "http://garena.sakura.ne.jp/wonderfl/danbord/tx_kage.png";
            
            textureMats = new Vector.<TextureMaterial>();
            
            textureMats.push(txMatDanboTop);
            textureMats.push(txMatDanboBottom);
            textureMats.push(txMatDanboFront);
            textureMats.push(txMatDanboLeft);
            textureMats.push(txMatDanboBack);
            textureMats.push(txMatDanboRight);
            textureMats.push(txMatDanboFace);
            textureMats.push(txMatDanboHeadTop);
            textureMats.push(txMatDanboBodyFront);
            textureMats.push(txMatKage);
            //
            //MaterialLoader?????????????????????????
            loaderContext = new LoaderContext(true);
            materialLoader = new MaterialLoader();
            materialLoader.load(textureMats, loaderContext);
        }
    }
    
    
    class DanboPlayer {
        
        private var danbo:Danbord;
        private var scene:Object3D;
        
        public var thirdPerson:OrbitCameraMan;
        
        public var surfaceMovement:SurfaceMovement;
        
        public var jump:Jump;
        
        public var velocity:Vector3D = new Vector3D();
        public var position:Vector3D = new Vector3D();
        public var rotation:Vector3D = new Vector3D();
        public var radius:Vector3D = new Vector3D(16,16,32);
    
        private var /*static const*/ playerMover:PlayerMover = new PlayerMover();
        private var /*static const*/ qPhysics:QPhysics  = new QPhysics(QPhysics.FLAG_GRAVITY);
        
        private var lookAtObject:Object3D = new Object3D();
        public var excludedObjects:Dictionary = new Dictionary();
        private var lookAtOffset:Vector3D = new Vector3D();
        
        private var keyBinder:KeyBinder = new KeyBinder();
        
        private var colliderPosition:Vector3D = new Vector3D();
        
        private var walkSpeed:Number;
        private var runSpeed:Number;
        
        public function calculateMovementSpeeds():void {
            walkSpeed = 1 + (10 * radius.z/400);
            runSpeed = 1 + (30 * radius.z/400);
        }

        
        public function DanboPlayer(danbo:Danbord, camera:Camera3D, scene:Object3D, stage:Stage) {
            this.scene = scene;
            

            // -- Player-Specific stuff for Client
            thirdPerson = new OrbitCameraMan(new OrbitCameraController(camera, lookAtObject, stage, stage, stage, false, false), scene, danbo ); 
            //thirdPerson.controller.easingSeparator  = 12;
            thirdPerson.preferedZoom = 1000;
            thirdPerson.controller.minDistance = 100;
            thirdPerson.controller.maxDistance = 1800;
            //thirdPerson.controller.minAngleLatitude = 5;
            thirdPerson.controller.minAngleLatitude = -85;
            thirdPerson.controller.maxAngleLatidude =75;
            thirdPerson.followAzimuth = true;
            thirdPerson.useFadeDistance = true;
            thirdPerson.maxFadeAlpha = 1;
            
            keyBinder.clear();
            keyBinder.setupStageListeners(stage);
            
            keyBinder.bindKey(Keyboard.W, forward);
            keyBinder.bindKey(Keyboard.S, back);
            keyBinder.bindKey(Keyboard.A, left);
            keyBinder.bindKey(Keyboard.D, right);
            
            keyBinder.bindKey(Keyboard.SPACE, space);
            keyBinder.bindKey(Keyboard.SHIFT, speedChange);
            
           // stage.addEventListener(MouseEvent.MOUSE_WHEEL, thirdPerson.mouseWheelHandler);
            
            
            // -- The below also applies to NPCs or "other players" besides Client
            
            this.danbo = danbo;
            updateRadius();
            
    
            lookAtOffset.y = 0;
            lookAtOffset.z = 200;
            
            surfaceMovement = new SurfaceMovement();
            jump = new Jump(1, 2136);// new Jump(1, 2135);
            
            
            excludedObjects[danbo] = true;
            excludedObjects[danbo.kage] = true;
            
            //danbo.z += 100;
            setupPosition();
            
            // view collision sphere
            /*
            var sphere:Object3D = new Sphere(radius.z, 8, 7, false, new FillMaterial(0xFF0000) );
            sphere.x = position.x;
            sphere.y = position.y;
            sphere.z = position.z;
            (scene as Object3DContainer).addChild( sphere);
            excludedObjects[sphere] = true;
            */
            

            calculateMovementSpeeds();
        }
        

        
        // Client controls
                
        
        public function getZoomRatio():Number {
            return  (thirdPerson.preferedZoom- thirdPerson.controller.minDistance) /  (thirdPerson.controller.maxDistance -  thirdPerson.controller.minDistance);
        }
        
        public function set zoom(ratio:Number):void {
            ratio = 1 - ratio;
            thirdPerson.preferedZoom =   thirdPerson.controller.minDistance + (thirdPerson.controller.maxDistance -  thirdPerson.controller.minDistance) * ratio;
        }
        
        
        
        
        
        private function speedChange(boo:Boolean):void 
        {
            _runMode = !boo;
        }
        
        private function space(boo:Boolean):void 
        {
            if (boo == _space) return;
            _space = boo;
        }
        
        private function right(boo:Boolean):void 
        {
            if (boo == _right) return;
            surfaceMovement.strafe_state += boo ? 1 : -1;
            _right = boo;
        }
        
        private function left(boo:Boolean):void 
        {
            if (boo == _left) return;
            surfaceMovement.strafe_state += boo ? -1 : 1;
            _left = boo;
            
        }
        
        private function back(boo:Boolean):void 
        {
            if (boo == _back) return;
            surfaceMovement.walk_state += boo ? -1 : 1;
            _back = boo;
        }
        
        private function forward(boo:Boolean):void 
        {
            if (boo == _forward) return;
            surfaceMovement.walk_state += boo ? 1 : -1;
            _forward = boo;
        }
        

        
        // Methods
        private function updateRadius():void 
        {
            radius.x = (danbo.boundMaxX - danbo.boundMinX + 12)*danbo.scaleX * .5;
            radius.y = (danbo.boundMaxY - danbo.boundMinY + 12)*danbo.scaleY * .5;
            radius.z = (danbo.boundMaxZ - danbo.boundMinZ + 35)*danbo.scaleZ * .5;
        }
        
        private function setupPosition():void 
        {
            position.x = danbo.x;
            position.y = danbo.y;
            position.z = danbo.z + radius.z;
        }
        
        private function preRenderPosition():void {
            danbo.x = position.x;
            danbo.y = position.y;
            danbo.z = position.z - radius.z;
            
            danbo.kage.x = danbo.x;
            danbo.kage.y = danbo.y;
            //danbo.kage.z = danbo.z;
        }
        
        private var _back:Boolean= false;
        private var _forward:Boolean= false;
        private var _left:Boolean = false;
        private var _right:Boolean = false;
    
        private var _space:Boolean = false;
        private var _runMode:Boolean = true;
        private var maxGroundNormal:Vector3D;
        
        public static const GROUND_NORMAL:Vector3D = new Vector3D();
        

        public function update(t:Number):void {
        
            // Update global environs
            qPhysics.update(t, velocity);
            
            rotation.x = danbo.rotationX;
            rotation.y = danbo.rotationY;
            rotation.z = danbo.rotationZ;

            // Jump
            jump.update(t);
            var gotJump:Boolean = false;
            if (  _space) { //maxGroundNormal != null &&
                gotJump = jump.do_jump(velocity, t);
            }
            
            // Movement along surface (todo: need to pre-determine any potential moving surface contacts based
            // on current kd building node contacts in 2D). If building moves and got current maximim groound normal
            // on building contact,  character should with the platform as well, unless he's jumping?
            var lastPosition:Vector3D = position.clone();
    
            maxGroundNormal = playerMover.queryMove(radius, position, velocity, scene, excludedObjects);  
            var numCollidables:int = KDBuilding.numCollidables;
            var collidables:Vector.<KDBuilding> = KDBuilding.collidables;
            var collidable:KDBuilding;
            var worldBounds:Object3D = danbo.worldBounds;
            
            var highestZ:Number = position.z
            
            // hack atm
        //    /*
            preRenderPosition(); // call this so can check node...(hmm.. shoudl refactor this)
            danbo.checkNode();
            if (numCollidables > 0) {  
                // check buildings in vincity to clamp z position up to positive (rooftop) node
                for ( var i:int = 0; i < numCollidables; i++) {
                    collidable = collidables[i];

                    if (!(worldBounds.boundMinX > collidable.boundMaxX ||
                    worldBounds.boundMinY > collidable.boundMaxY ||
                    worldBounds.boundMaxX < collidable.boundMinX ||
                    worldBounds.boundMaxY < collidable.boundMaxY)) {
                        if (collidable.boundMaxZ + radius.z + 0.01 > highestZ) {
                            highestZ = collidable.boundMaxZ + radius.z + 0.01;
                            maxGroundNormal = GROUND_NORMAL
                        
                            
                        }
                    }
                }
            }
            position.z = highestZ;
            if (danbo.node.axis != 2) migrate(danbo.node);
            if (danbo.node.positive.boundMinZ + radius.z  > highestZ ) {
                position.z = danbo.node.positive.boundMinZ + radius.z;
                maxGroundNormal = GROUND_NORMAL;
            }
        //    */
            
            
            var baseSpeed:Number = _runMode ? runSpeed : walkSpeed;
            surfaceMovement.WALK_SPEED = baseSpeed;
            surfaceMovement.WALKBACK_SPEED = baseSpeed * 1;
            surfaceMovement.STRAFE_SPEED = baseSpeed * 1;
            
            surfaceMovement.update(t, position, rotation, velocity, maxGroundNormal );
            
            if (maxGroundNormal == null) {
                danbo.spZ -= 9.8 / 60;
            //  myDanbord.z += myDanbord.spZ;
            }
            
            // Animations
            danbo.setMotion(  maxGroundNormal == null ? "jump" :
            
            maxGroundNormal != null &&  (surfaceMovement.strafe_state != 0 || surfaceMovement.walk_state != 0) ? 
                    _runMode ?  "dash" : "walk" 
                    
                    
                    :
                    "stand"
            );
            danbo.enterFrameEvent2();
            // Update animations for Client
            /*
            var useLat:Number = thirdPerson.controller.angleLatitude;
            if (useLat > 45 ) useLat = 45;
            if (useLat < 0 ) useLat = 0;
            danbo.danHead.rotationX = -useLat * DEG_TO_RAD * .7;
            */
            

            
        }
        
        private static const DEG_TO_RAD:Number = Math.PI / 180;
        
        
        private function preRenderCallback():void {
            preRenderPosition();
            //  Adjust look at position for Client Camera
            // This is basically a hardcoded way to translate position of lookAtObject from local coordinate space to global coordinate space. 
            //  If not, one would need to uncomment "//   testLook = _followTarget.localToGlobal(testLook);"
            // from OrbitCameraController class.
            lookAtObject.x = lookAtOffset.x +  danbo.danHead.x + danbo.danBody.x;
            lookAtObject.y = lookAtOffset.y +  danbo.danHead.y + danbo.danBody.y;
            lookAtObject.z = lookAtOffset.z + danbo.danHead.z + danbo.danBody.z;
            
            
            
            danbo.composeMatrix();
            lookAtObject.composeAndAppend(danbo);
            
            lookAtObject.x = lookAtObject.md;
            lookAtObject.y = lookAtObject.mh;
            lookAtObject.z = lookAtObject.ml;
        }
        
        private function get pitchRatio():Number {
            return (thirdPerson.controller.angleLatitude - thirdPerson.controller.minAngleLatitude) / (thirdPerson.controller.maxAngleLatidude - thirdPerson.controller.minAngleLatitude);
        }
        
        private function get arcRatio():Number {
            return thirdPerson.controller.angleLatitude < 0 ? -thirdPerson.controller.angleLatitude/thirdPerson.controller.minAngleLatitude : thirdPerson.controller.angleLatitude/thirdPerson.controller.maxAngleLatidude;
        }
        
        private function migrate(node:*):void { // need to migrate down to sub KD node split
            var axis:int = node.axis;
            var value:Number = axis == 0 ? danbo.x : danbo.y;
            node.objectBoundList = null;
            node.objectList = null;
            node = value >= node.coord ? node.positive : node.negative;
            danbo.node = node;
            
            if (node.axis != 2) throw new Error("STILL NEED !");
            node = node.positive;
            node.objectBoundList = danbo.worldBounds;
            node.objectList = danbo;
            
        }
        
        public function preRender():void {
          

            danbo.updateWorldBounds();
            
            // Client view
            
            lookAtOffset.y = 90 * arcRatio;  // adjust so that got crosshair to view at center
                
            // This is stupid, have to call this multiple times to ensure Danbo's rotation transform of lookAtOffset vector appears more accruately without jittering.
            preRenderCallback();
            thirdPerson.update();
            
            preRenderCallback();
            thirdPerson.update();
            
            preRenderCallback();
            thirdPerson.update();
        }

        
    }
    

class PlayerMover {
    
    public var collisionPoint:Vector3D = new Vector3D();
    public var collisionNormal:Vector3D = new Vector3D();
    private static const GRAVITY_DIR:Vector3D = new Vector3D(0, 0, -1);
    public static var COLLIDER:MyEllipsoidCollider = new MyEllipsoidCollider(2, 2, 2);
    
        public function queryMove(radius:Vector3D, position:Vector3D, displacement:Vector3D, scene:Object3D, excludedObjects:Dictionary = null):Vector3D {
            const collider:MyEllipsoidCollider = COLLIDER;
            collider.radiusX = radius.x;
            collider.radiusY = radius.y;
            collider.radiusZ = radius.z;
            
            const collisionPoint:Vector3D = collisionPoint;
            const collisionNormal:Vector3D = collisionNormal;
            
                var dest:Vector3D = collider.calculateDestination(position, displacement, scene, excludedObjects);
                var maxGroundNormal:Vector3D
                if (collider.getCollision(position, displacement, collisionPoint, collisionNormal, scene, excludedObjects) ) {
                    if (collisionNormal.z >= .8) {
                        maxGroundNormal = collisionNormal; // <- shoudl still count this?
                    }
                    else {
                        GRAVITY_DIR.z = displacement.z;
                        if ( collider.$getCollision(dest, GRAVITY_DIR, collisionPoint, collisionNormal, scene, excludedObjects) ) {
                        
                            if (collisionNormal.z >= .8)  {
                                maxGroundNormal =  collisionNormal;
                            }
                    //        collisionEvent.next = groundCollision;
                        }
                        
                    }
                //    p.move.collisions = collisionEvent;
                //    return maxGroundNormal;
                }
                else {
                //    p.move.clearCollisions();
                
                }
                
                position.x = dest.x;
                position.y = dest.y;
                position.z = dest.z;
                if (position.z > NodeUtils.MAX_ALTITUDE - radius.z) position.z = NodeUtils.MAX_ALTITUDE - radius.z;
                if (position.z < NodeUtils.MIN_ALTITUDE + radius.z) position.z = NodeUtils.MIN_ALTITUDE + radius.z;
                
                return maxGroundNormal;
    
        }
        
    
}


    
class Jump 
{
    // State Settings
    public var JUMP_COOLDOWN:Number;
    
    // State for thing
    private var jump_timer:Number;
    private var jump_speed:Number;
    public var enabled:Boolean;  // use this as a master lock to enable/disable jump depending on situation
    
    public function Jump(timeCooldown:Number, jumpSpeed:Number) 
    {
        JUMP_COOLDOWN = timeCooldown;
        jump_speed = jumpSpeed;
        jump_timer = 0;
        enabled = true;
    }
    
    public function update(time:Number ):void {
        jump_timer = jump_timer-time < 0 ? 0 : jump_timer - time;
    }
     
    
    public function do_jump(velocity:Vector3D, time:Number):Boolean {
        if (enabled && this.jump_timer == 0)
        {
            velocity.z += jump_speed * time;
            jump_timer = JUMP_COOLDOWN;         
            return true;
        }
        return false;
    }

}

class SurfaceMovement 
{
    // State settings
    public var WALK_SPEED:Number;
    public var WALKBACK_SPEED:Number;
    public var STRAFE_SPEED:Number;

    // State for thing
    // delta walk/strafe state (-1 for backwards/left, 0 for neither direction, 1 for forwards/right)
    public var walk_state:int; 
    public var strafe_state:int;
    
    public static const WALK_FORWARD:int = 1;
    public static const WALK_STOP:int = 0;
    public static const WALK_BACK:int = -1;

    public static const STRAFE_LEFT:int = -1;
    public static const STRAFE_STOP:int = 0;
    public static const STRAFE_RIGHT:int = 1;
    
    // Normalized forward direction along surface
    public var forwardVec:Vector3D;
    public var rightVec:Vector3D;
    public var friction:Number;

    
    

    public function SurfaceMovement() 
    {
        walk_state = 0;
        strafe_state = 0;
        forwardVec = new Vector3D();
        rightVec = new Vector3D();
        friction = .25;
        
        setWalkSpeeds(16);
        setStrafeSpeed(10);
    }
    
    public function setWalkSpeeds(forwardSpeed:Number, backspeed:Number = -1):void {
        WALK_SPEED = forwardSpeed;
        WALKBACK_SPEED = (backspeed != -1) ? backspeed : forwardSpeed; 
    }
    public function setStrafeSpeed(val:Number):void {
        STRAFE_SPEED = val;
    }
    public function setAllSpeeds(val:Number):void {
        WALK_SPEED = val;
        WALKBACK_SPEED  = val;
        STRAFE_SPEED = val;
    }
        
    public function respond_move_forward():void {
        walk_state = 1;
    }
    public function respond_move_back():void {
        walk_state = -1;
    }
    public function respond_move_stop():void {
        walk_state = 0;
    }
    
    public function respond_strafe_left():void {
        strafe_state = -1;
    }
    public function respond_strafe_right():void {
        strafe_state = 1;
    }
    public function respond_strafe_stop():void {
        strafe_state = 0;
    }

    
    public function update(time:Number, position:Vector3D, rotation:Vector3D, velocity:Vector3D, ground_normal:Vector3D = null):void {
        
        var multiplier:Number;
        
        if (ground_normal != null) { // can walk on ground
            velocity.x *= friction;
            velocity.y *= friction;
            velocity.z *= friction;
            
            /*
             * Math.cos(this.thingBase.azimuth) * Math.cos(this.thingBase.elevation), Math.sin(this.thingBase.azimuth) * Math.cos(this.thingBase.elevation)
             */
            forwardVec.x = -Math.sin(rotation.z); //Math.cos(rotation.z) * Math.cos(rotation.y);  // frm rotation.z azimith
            forwardVec.y = Math.cos(rotation.z);//Math.sin(rotation.z) * Math.cos(rotation.y); // frm rotation.x pitch.  //* Math.cos(rotation.x)
            forwardVec.z = 0;
            if (forwardVec.dotProduct(ground_normal) > 0) {
                multiplier = ground_normal.x * forwardVec.x + ground_normal.y * forwardVec.y + ground_normal.z * forwardVec.z;
                forwardVec.x -= ground_normal.x * multiplier;
                forwardVec.y -= ground_normal.y * multiplier;
                forwardVec.z -= ground_normal.z * multiplier;
            }
            forwardVec.normalize();
            
            if (walk_state != 0 ) {
                multiplier = (walk_state != WALK_BACK) ? WALK_SPEED : -WALKBACK_SPEED;
                velocity.x += forwardVec.x * multiplier;
                velocity.y += forwardVec.y * multiplier;
                velocity.z += forwardVec.z * multiplier;
    
            }
            
            rightVec = forwardVec.crossProduct(ground_normal);  // TODO: inline
            rightVec.normalize();
            if (strafe_state != 0) {
                multiplier = strafe_state != STRAFE_LEFT ? STRAFE_SPEED : -STRAFE_SPEED;
                velocity.x += rightVec.x * multiplier;
                velocity.y += rightVec.y * multiplier;
                velocity.z += rightVec.z * multiplier;
            }
        }
        
    }

}


/**
 * Quake-like physics static controller or stateful component.
 * @author Glidias
 */
class QPhysics 
{
    public static const FLAG_STICKY:int = 8;
    public static const FLAG_DAMPING:int = 4;
    public static const FLAG_BOUNCE:int = 2;
    public static const FLAG_GRAVITY:int = 1;
    
    // State settings
    public var FLAGS:int;
    public var T_BOUNCE:Number;
    public var TAU_DAMP:Number;
    public var N_BOUNCE:Number;

    
    public static var GRAVITY:Number = 40;
    
    public function QPhysics(flags:int=0) {    
    
        FLAGS = flags;
        N_BOUNCE = 0.5;
        T_BOUNCE = 0.9;
        TAU_DAMP = .9;
    }

    public function applyBounce(collisions:CollisionEvent, velocity:Vector3D):void {
        var coll:CollisionEvent = collisions;
        applyBounceWith(velocity, coll.normal, T_BOUNCE, N_BOUNCE);
        coll = coll.next;
        while (coll != null) {
            applyBounceWith(velocity, coll.normal, T_BOUNCE, N_BOUNCE);
            coll = coll.next;
        }
    }
    public static function applyBounceWith(velocity:Vector3D, normal:Vector3D, T_BOUNCE:Number, N_BOUNCE:Number):void {
        var pushBack:Number = normal.dotProduct( velocity);
        var addVel:Vector3D = normal.clone();
        addVel.scaleBy(-pushBack);
        velocity.add(addVel);
        velocity.scaleBy(T_BOUNCE);
        addVel.scaleBy(N_BOUNCE);
        velocity.add(addVel);
    }
    

    
    public function update(time:Number,  velocity:Vector3D, rotation:Vector3D = null, ang_velocity:Vector3D=null):void {
        if (rotation != null) {
            rotation.x += ang_velocity.x * time; // roll
            rotation.y += ang_velocity.y * time; // elevation
            rotation.z += ang_velocity.z * time; // azimuth 
        }
        if ( (FLAGS & FLAG_DAMPING) != 0)
        {
            velocity.scaleBy(Math.exp((-time) / TAU_DAMP)); // need to check on this.
        }
        if ( (FLAGS & FLAG_GRAVITY) != 0)
        {
            velocity.z -= GRAVITY * time;
        }
        
    }

}


class CollisionEvent {
    
    public var collision:Vector3D = new Vector3D();
    public var normal:Vector3D = new Vector3D();
    public var offset:Number;
    public var t:Number;
    
    public static var COLLECTOR:CollisionEvent = new CollisionEvent();
    public var next:CollisionEvent;
    
    public function CollisionEvent() 
    {
    
    }
    
        
    // Pooling, linked list and disposal
    public static function Get(collision:Vector3D, normal:Vector3D, offset:Number, t:Number, geomtype:int):CollisionEvent {
        var c:CollisionEvent = COLLECTOR || (COLLECTOR = new CollisionEvent());
        COLLECTOR = COLLECTOR.next;

        c.next = null;
        
        c.collision.x = collision.x;
        c.collision.y = collision.y;
        c.collision.z = collision.z;
        c.normal.x = normal.x;
        c.normal.y = normal.y;
        c.normal.z = normal.z;
        c.offset = offset;
        c.t = t;
       // c.geomtype = geomtype;
        
        return c;
    }
    

    public function get(collision:Vector3D, normal:Vector3D, offset:Number, t:Number, geomtype:int):CollisionEvent {
        //return Get(collision, normal, offset, t, geomtype);
        var c:CollisionEvent = COLLECTOR || (COLLECTOR = new CollisionEvent());
        COLLECTOR = COLLECTOR.next;

        c.next = null;
        
        c.collision.x = collision.x;
        c.collision.y = collision.y;
        c.collision.z = collision.z;
        c.normal.x = normal.x;
        c.normal.y = normal.y;
        c.normal.z = normal.z;
        c.offset = offset;
        c.t = t;
       // c.geomtype = geomtype;
        
        return c;
    }
    
    
    public function dispose():void {    
        next = COLLECTOR;
        COLLECTOR = this;
    }
    

}




//}
//Danbord.as
//?????????????
//package {
    //Alternativa3D
    import alternativ7.engine3d.core.Object3DContainer;
    import alternativ7.engine3d.materials.TextureMaterial;
    import alternativ7.engine3d.primitives.Box;
    import alternativ7.engine3d.primitives.Plane;
    import alternativ7.engine3d.alternativa3d;
    use namespace alternativa3d;
        
    //public 
    class Danbord extends Object3D {
        private static var AREA_W:uint = 4000;
        private static var AREA_H:uint = 4000;
        private static var FPS:uint = 60;
        //3D??
        private var boxHead:Box;
        private var boxBody:Box;
        private var boxSldL:Box;
        private var boxSldR:Box;
        private var boxArmL:Box;
        private var boxArmR:Box;
        private var boxKneL:Box;
        private var boxKneR:Box;
        private var boxLegL:Box;
        private var boxLegR:Box;
        private var planeSktL:Plane;
        private var planeSktR:Plane;
        private var planeSktF:Plane;
        private var planeSktB:Plane;
        
        public var danHead:Object3DContainer;
        public var danBody:Object3D;
        private var danSldL:Object3D;
        private var danSldR:Object3D;
        private var danArmL:Object3D;
        private var danArmR:Object3D;
        private var danKneL:Object3D;
        private var danKneR:Object3D;
        private var danLegL:Object3D;
        private var danLegR:Object3D;
        private var danSktL:Object3D;
        private var danSktR:Object3D;
        private var danSktF:Object3D;
        private var danSktB:Object3D;
        
        private var danUpper:Object3D;

        private var motionR:Number = 0;
        private var motionType:String = "stand";
        private var motionTypes:Vector.<String>;
        //
        private var textures:Textures;
        
        public var bodyR:Number = 0;
        public var sp:Number = 0;
        public var spZ:Number = 0;
        public var ground:Boolean = true;
        
        public var node:*;
        public var worldBounds:Object3D;
        
        public var kage:Kage;
        
        private var brushes:Vector.<Mesh>;
        
        private static var RENDERER:BSPTreeRendererI;
        
        // Must call this method once to setup renderer!
        public function setupRenderer(camera:Camera3D):void {  
            RENDERER = new BSPTreeRendererI(camera, new BSPXMLNode(getBSPXML(), this, brushes) );
        }
        
        private function calculateTransforms():void {
             calculateInverseMatrix();
             danBody.composeAndAppend(this);
             danUpper.composeAndAppend(danBody);
             
                boxBody.composeAndAppend(danUpper);             boxBody.calculateInverseMatrix();
                danHead.composeAndAppend(danUpper);  
                    boxHead.composeAndAppend(danHead);         boxHead.calculateInverseMatrix();
                danSldL.composeAndAppend(danUpper);
                   boxSldL.composeAndAppend(danSldL);         boxSldL.calculateInverseMatrix();
                   danArmL.composeAndAppend(danSldL);
                        boxArmL.composeAndAppend(danArmL);   boxArmL.calculateInverseMatrix();
                danSldR.composeAndAppend(danUpper);
                    boxSldR.composeAndAppend(danSldR);          boxSldR.calculateInverseMatrix();
                    danArmR.composeAndAppend(danSldR);
                        boxArmR.composeAndAppend(danArmR);   boxArmR.calculateInverseMatrix();
                danKneL.composeAndAppend(danBody);
                    boxKneL.composeAndAppend(danKneL);          boxKneL.calculateInverseMatrix();
                   danLegL.composeAndAppend(danKneL);
                       boxLegL.composeAndAppend(danLegL);      boxLegL.calculateInverseMatrix();
                danKneR.composeAndAppend(danBody);
                    boxKneR.composeAndAppend(danKneR);         boxKneR.calculateInverseMatrix();
                    danLegR.composeAndAppend(danKneR);
                       boxLegR.composeAndAppend(danLegR);     boxLegR.calculateInverseMatrix();
                danSktL.composeAndAppend(danUpper);
                   planeSktL.composeAndAppend(danSktL);      planeSktL.calculateInverseMatrix();
                danSktR.composeAndAppend(danUpper);
                    planeSktR.composeAndAppend(danSktR);     planeSktR.calculateInverseMatrix();
                danSktF.composeAndAppend(danUpper);
                    planeSktF.composeAndAppend(danSktF);     planeSktF.calculateInverseMatrix();
                danSktB.composeAndAppend(danUpper);
                    planeSktB.composeAndAppend(danSktB);     planeSktB.calculateInverseMatrix();
        }
        override alternativa3d function draw(camera:Camera3D, parentCanvas:Canvas):void {
    
                calculateTransforms();
                RENDERER.draw(parentCanvas, brushes, culling);
                
                //Debug.drawBounds(camera, parentCanvas.getChildCanvas(true, false, this), this, boundMinX, boundMinY, boundMinZ, boundMaxX, boundMaxY, boundMaxZ);          
        }
        
        override alternativa3d function getVG(camera:Camera3D):VG {

                calculateTransforms();
               return RENDERER.collectVG(brushes, culling, this);
                
                //Debug.drawBounds(camera, parentCanvas.getChildCanvas(true, false, this), this, boundMinX, boundMinY, boundMinZ, boundMaxX, boundMaxY, boundMaxZ);          
        }
        
        public function getBSPXML():XML {
            boxHead._parent = danHead;
            return <bsp>
                <back>{_(boxHead)}</back>
                <front>
                    <front>
                        <front>{_(planeSktL)}</front>
                        <back>
                            <back>{_(boxSldL)}</back>
                            <front>{_(boxArmL)}</front>
                            {planeOf(boxSldL, "bottom")}
                        </back>
                        {planeOf(planeSktL, "bottom")}
                    </front>
                    <back>
                        <front>
                            <front>{_(planeSktR)}</front>
                            <back>
                                <back>{_(boxSldR)}</back>
                                <front>{_(boxArmR)}</front>
                                {planeOf(boxSldR, "bottom")}
                            </back>
                            {planeOf(planeSktR, "bottom")}
                        </front>
                        <back>
                            <front>{_(planeSktF)}</front>    
                            <back>
                                <front>{_(planeSktB)}</front>
                                <back>
                                    <front>
                                        <front>{_(boxLegL)}</front>
                                        <back>{_(boxLegR)}</back>
                                        {planeOf(boxLegR, "left")}
                                    </front>
                                    <back>{_(boxBody)}</back>
                                    {planeOf(boxBody, "bottom")}
                                </back>
                                {planeOf(boxBody, "back")}
                            </back>
                            {planeOf(boxBody, "bodyFront")}
                        </back>
                        {planeOf(boxBody, "right")}
                    </back>
                    {planeOf(boxBody, "left")}
                </front>
                {planeOf(boxHead, "bottom")}
            </bsp>
        }

    
        /**
         * Gets face from mesh using material id
         * @param    mesh
         * @param    matId
         * @return
         */
        private static function getFaceOf(mesh:Mesh, matId:String):Face {
            var face:Face = mesh.faceList;
            while ( face) {
                if (face.material && face.material.name === matId) {
                
                    return face;
                }
                face = face.next;
            }
            throw new Error("could not find face by matId:"+matId);
            return null;
        }
        
                
        public function planeOf(mesh:Mesh, matId:String, animated:Boolean=true):XML {
            var face:Face = getFaceOf(mesh, matId);
            return <plane animated={animated}>
                <nx>{face.normalX}</nx>
                <ny>{face.normalY}</ny>
                <nz>{face.normalZ}</nz>
                <offset>{face.offset}</offset>
                <brush>{brushes.indexOf(mesh)}</brush>
            </plane>;
        }
        
        /**
         * Gets brush index of brush for bsp xml node
         * @param    searchBrush
         * @return    The brush index xml node
         */
        private function _(searchBrush:Object3D):XML {
            return <brush>{brushes.indexOf(searchBrush)}</brush>;
        }
        
        
    

        
        //????
        public function Danbord(_textures:Textures):void {
            textures = _textures;
            //
            //???????????????(?????????????????????????????)
            
            // 14 dans
            danHead = new Object3DContainer();
            danBody = new Object3D();
            danSldL = new Object3D();
            danSldR = new Object3D() ;
            danArmL = new Object3D();
            danArmR = new Object3D();
            danKneL = new Object3D();
            danKneR = new Object3D();
            danLegL = new Object3D();
            danLegR = new Object3D() ;
            danSktL = new Object3D();
            danSktR = new Object3D();
            danSktF = new Object3D();
            danSktB = new Object3D();
            
            danUpper = new Object3D();
            
            /*
            danBody:?
            -danHead:?
            -danSldL:??
            --danArmL:??
            -danSldR:??
            --danArmR:??
            -boxKneL:??
            --boxLegL:??
            -boxKneR:??
            --boxLegR:??
            -danSktL:?????
            -danSktR:?????
            -danSktF:?????
            -danSktB:?????
            */
            boundMinX = 0;
            boundMaxX = 0;
            boundMinY = 0;
            boundMaxY = 0;
            boundMinZ = 0;
            boundMaxZ = 0;
            
            // 10 box
            //??????
            boxBody = new Box(76, 60, 102, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboBodyFront, textures.txMatDanboBottom, textures.txMatDanboTop);
            boxHead = new Box(160, 102, 102, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFace, textures.txMatDanboBottom, textures.txMatDanboHeadTop);
            boxSldL = new Box(20, 20, 20, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFront, textures.txMatDanboBottom, textures.txMatDanboTop);
            boxSldR = new Box(20, 20, 20, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFront, textures.txMatDanboBottom, textures.txMatDanboTop);
            boxArmL = new Box(106, 26, 26, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFront, textures.txMatDanboBottom, textures.txMatDanboTop);
            boxArmR = new Box(106, 26, 26, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFront, textures.txMatDanboBottom, textures.txMatDanboTop);
            boxKneL = new Box(20, 20, 20, 1, 1, 1, false, false, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboTop);
            boxKneR = new Box(20, 20, 20, 1, 1, 1, false, false, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboBottom, textures.txMatDanboTop);
            boxLegL = new Box(30, 56, 66, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFront, textures.txMatDanboBottom, textures.txMatDanboTop);
            boxLegR = new Box(30, 56, 66, 1, 1, 1, false, false, textures.txMatDanboLeft, textures.txMatDanboRight, textures.txMatDanboBack, textures.txMatDanboFront, textures.txMatDanboBottom, textures.txMatDanboTop);
    
            // 4 plane
            planeSktL = new Plane(60, 30, 1, 1, true, false, false, textures.txMatDanboLeft, textures.txMatDanboBottom);
            planeSktR = new Plane(60, 30, 1, 1, true, false, false, textures.txMatDanboRight, textures.txMatDanboBottom);
            planeSktF = new Plane(76, 30, 1, 1, true, false, false, textures.txMatDanboFront, textures.txMatDanboBottom);
            planeSktB = new Plane(76, 30, 1, 1, true, false, false, textures.txMatDanboBack, textures.txMatDanboBottom);
    
            //???????(?????????????????)
            brushes = new Vector.<Mesh>(14, true);
            brushes[0] = boxBody;  boxBody.sorting = 0;
            brushes[1] = boxHead;  boxHead.sorting = 0;
            brushes[2] = boxSldL;  boxSldL.sorting = 0;
            brushes[3] = boxSldR;  boxSldR.sorting = 0;
            brushes[4] = boxArmL;  boxArmL.sorting = 0;
            brushes[5] = boxArmR;  boxArmR.sorting = 0;
            brushes[6] = boxKneL;  boxKneL.sorting = 0;
            brushes[7] = boxKneR;  boxKneR.sorting = 0;
            brushes[8] = boxLegL;  boxLegL.sorting = 0;
            brushes[9] = boxLegR;  boxLegR.sorting = 0;
            brushes[10] = planeSktL;  planeSktL.sorting = 0;
            brushes[11] = planeSktR;  planeSktR.sorting = 0;
            brushes[12] = planeSktF;  planeSktF.sorting = 0;
            brushes[13] = planeSktB;  planeSktB.sorting = 0;
            
            
            //??·????
            boxBody.z = 137
            boxHead.z = 51;
            boxArmL.x = -63;
            boxArmR.x = 63;
            boxLegL.z = -38;
            boxLegR.z = -38;
            
            danArmL.rotationY = -1.3;
            danArmR.rotationY = 1.3;
            
            planeSktL.rotationX = Math.PI * 0.5;
            planeSktL.rotationZ = Math.PI * 0.5;
            planeSktL.z = -15;
            planeSktR.rotationX = Math.PI * 0.5;
            planeSktR.rotationZ = Math.PI * -0.5;
            planeSktR.z = -15
            planeSktF.rotationX = Math.PI * 0.5;
            planeSktF.rotationZ = Math.PI * 0;
            planeSktF.z = -15
            planeSktB.rotationX = Math.PI * 0.5;
            planeSktB.rotationZ = Math.PI * 1;
            planeSktB.z = -15
            
            danBody.z = 0;
            danHead.z = 188;
            danSldL.x = -48;
            danSldL.z = 178;
            danSldR.x = 48;
            danSldR.z = 178;
            
            danKneL.x = -17;
            danKneL.z = 76;
            danKneR.x = 17;
            danKneR.z = 76;
            
            danSktL.x = -38;
            danSktL.z = 86;
            danSktL.rotationY = 0.5;
            danSktR.x = 38;
            danSktR.z = 86;
            danSktR.rotationY = -0.5;
            danSktF.y = 30;
            danSktF.z = 86;
            danSktF.rotationX = 0.5;
            danSktB.y = -30;
            danSktB.z = 86;
            danSktB.rotationX = -0.5;
            
            //
            motionTypes = new Vector.<String>();
            motionTypes[0] = "stand";
            motionTypes[1] = "walk";
            motionTypes[2] = "dash";
         //   motionTypes[3] = "jump";
            
          //  bodyR = Math.random() * Math.PI * 2;
            //danBody.rotationZ = bodyR;
            //setMotionRandom();
            


        }
        
        public var motionI:int = 0;
        
        //????????
        public function setMotion(_motion:String):void {
            if(motionType != _motion){
                motionType = _motion;
                motionR = 0;
                motionI = 0;
                danBody.rotationX = 0;
                danBody.rotationY = 0;
                switch(_motion) {
                    case "stand":
                        sp = 0;
                        break;
                    case "walk":
                        bodyR = Math.random() * Math.PI * 2;
                       // danBody.rotationZ = bodyR
                        sp = (2 + Math.random() * 2) * scaleX;
                        break;
                    case "dash":
                        bodyR = Math.random() * Math.PI * 2;
                       // danBody.rotationZ = bodyR
                        sp = (6 + Math.random() * 4) * scaleX;
                        break;
                    case "jump":
                        spZ = (8 + Math.random() * 8) * scaleX;
                        ground = false;
                        break;
                    default :
                        break;
                }
            }
        }
        //?????????????(???????)
        public function setMotionRandom(_jump:Boolean = true):void {
            if (_jump) {
                setMotion(motionTypes[Math.floor(Math.random() * motionTypes.length)])
            }else {
                setMotion(motionTypes[Math.floor(Math.random() * (motionTypes.length - 1))])
            }
        }
        
        public static const MOTION_TIMES:Object = {
            stand: 0.03,
            walk: 0.1,
            dash: 0.23
            //jump: 0.23
        }
        
        public static var STAND:DangBoardClip;
        public static var WALK:DangBoardClip;
        public static var DASH:DangBoardClip;
        
        public static function precomputeAll():void {
            STAND = precompute("stand");
            WALK = precompute("walk");
            DASH = precompute("dash");
        }
        
        private static function precompute(motionType:String):DangBoardClip {
            var clip:DangBoardClip = new DangBoardClip();

            var amt:Number = MOTION_TIMES[motionType];
            var units:int = Math.round( 2 * Math.PI / amt );
            clip.motionR = amt = 2 * Math.PI / units;
            
            for (var i:int = 0 ; i < units; i++) {
                var frame:DangBoardFrame =  new DangBoardFrame();
                clip.frames[clip.frameLen++] = frame;
                var motionR:Number = i * amt;
                  switch(motionType) {
                    case "stand":
                        frame.danBody_z = 0;
                        frame.danHead_rotationX = Math.sin(-motionR) * 0.04;
                        frame.danArmL_rotationY = Math.sin(-motionR) * 0.1 + -1.3;
                        frame.danArmR_rotationY = Math.sin(motionR) * 0.1 + 1.3;
                        frame.danSldL_rotationX = 0;
                        frame.danSldR_rotationX = 0;
                        frame.danLegL_rotationX = 0;
                        frame.danLegR_rotationX = 0;
                        frame.danKneL_rotationY = 0;
                        frame.danKneR_rotationY = 0;
                        break;
                    case "walk":
                        frame.danBody_z = Math.sin(motionR * 2) * 3 + 3;
                        frame.danHead_rotationX = Math.cos(motionR*2) * 0.03;
                        frame.danArmL_rotationY = -1.3;
                        frame.danArmR_rotationY = 1.3;
                        frame.danSldL_rotationX = Math.sin(-motionR) * 0.3;
                        frame.danSldR_rotationX = Math.sin(motionR) * 0.3;
                        frame.danLegL_rotationX = Math.sin(motionR) * 0.4;
                        frame.danLegR_rotationX = Math.sin( -motionR) * 0.4;
                        frame.danKneL_rotationY = 0;
                        frame.danKneR_rotationY = 0;
                        break;
                    case "dash":
                        frame.danBody_z = Math.sin(motionR * 2) * 8 + 8;
                        frame.danHead_rotationX = Math.cos(motionR*2) * 0.05;
                        frame.danArmL_rotationY = -1.3;
                        frame.danArmR_rotationY = 1.3;
                        frame.danSldL_rotationX = Math.sin(-motionR) * 0.6;
                        frame.danSldR_rotationX = Math.sin(motionR) * 0.6;
                        frame.danLegL_rotationX = Math.sin(motionR) * 0.6;
                        frame.danLegR_rotationX = Math.sin( -motionR) * 0.6;
                        frame.danKneL_rotationY = 0;
                        frame.danKneR_rotationY = 0;
                        break;
                    /*
                    case "jump":
                        frame.danBody_z = 0;
                        frame.danHead_rotationX = Math.max(Math.min(spZ * 0.1, 0.8), -0.5);
                        frame.danArmL_rotationY = -Math.max(Math.min(spZ * 0.2, 1.4), -0.2);
                        frame.danArmR_rotationY = Math.max(Math.min(spZ * 0.2, 1.4), -0.2);
                        frame.danSldL_rotationX = 0;
                        frame.danSldR_rotationX = 0;
                        frame.danKneL_rotationY = -Math.max(Math.min(spZ * 0.2, 0), -0.2);
                        frame.danKneR_rotationY = Math.max(Math.min(spZ * 0.2, 0), -0.2);
                        frame.danLegL_rotationX = 0;
                        frame.danLegR_rotationX = 0;
                        break;
                    */
                    default :
                        break;
                }
            }
            

            return clip;
        }
        
        
         public function enterFrameEvent2() :void {
        
            //danUpper.rotationZ += .1;
        //    danHead.rotationZ += .1;
         //   danBody.rotationZ = bodyR;
            var clip:DangBoardClip;
            switch(motionType) {
                case "stand": 
                    clip = STAND;
                    danBody.y = 0;
                break;
                case "walk":
                    clip = WALK;
                break;
                case "dash":
                    clip = DASH;
                 
                break;
                    
                case "jump":
                    motionR += 0.23;
                    danBody.z = 0;
                    danHead.rotationX = Math.max(Math.min(spZ * 0.1, 0.8), -0.5);
                    danArmL.rotationY = -Math.max(Math.min(spZ * 0.2, 1.4), -0.2);
                    danArmR.rotationY = Math.max(Math.min(spZ * 0.2, 1.4), -0.2);
                    danSldL.rotationX = 0;
                    danSldR.rotationX = 0;
                    danKneL.rotationY = -Math.max(Math.min(spZ * 0.2, 0), -0.2);
                    danKneR.rotationY = Math.max(Math.min(spZ * 0.2, 0), -0.2);
                    danLegL.rotationX = 0;
                    danLegR.rotationX = 0;
                    
                    return;
                default :
                    return;
                    
            }
            
            
            if (motionI >= clip.frameLen) motionI = 0;
            var frame:DangBoardFrame = clip.frames[motionI];
                    
                    danBody.z = frame.danBody_z;
                    danHead.rotationX = frame.danHead_rotationX;
                    danArmL.rotationY = frame.danArmL_rotationY
                    danArmR.rotationY = frame.danArmR_rotationY
                    danSldL.rotationX = frame.danSldL_rotationX
                    danSldR.rotationX = frame.danSldR_rotationX
                    danKneL.rotationY = frame.danKneL_rotationY
                    danKneR.rotationY = frame.danKneR_rotationY
                    danLegL.rotationX = frame.danLegL_rotationX
                    danLegR.rotationX = frame.danLegR_rotationX
                    
                motionI++;
       
         }
         
         public function updateWorldBounds():void 
         {
            worldBounds.boundMinX = boundMinX*scaleX + x;
            worldBounds.boundMinY = boundMinY*scaleY + y;
            worldBounds.boundMinZ = boundMinZ*scaleZ + z;
            worldBounds.boundMaxX = boundMaxX*scaleX + x;
            worldBounds.boundMaxY = boundMaxY*scaleY + y;
            worldBounds.boundMaxZ = boundMaxZ*scaleZ + z;
         }
         
         public function checkNode():void 
         {
             if ((x > node.boundMaxX ||
                    y > node.boundMaxY ||
                    x < node.boundMinX ||
                    y < node.boundMaxY)) {
                //node = ;    
                var lastNode:* = node;
                node = NodeUtils.find2DNode(x, y);
                if (lastNode != node) {  // todo: adding/removal instead for multiuser
                    //lastNode.positive.objectBoundList = null;
                    //lastNode.positive.objectList = null;
                    
                    
                //    node.positive.objectList = this;
                    //node.positive.objectBoundList = worldBounds;
                }
            }
         }
         
         
        
      
    
    }
//}
//Kage.as
//?
//package {
    //Alternativa3D
    import alternativ7.engine3d.primitives.Plane;
        
    //public 
    class Kage extends Plane {
        
      
        public function Kage(textures:Textures):void {
           super(200, 200, 1, 1, true, false, false, textures.txMatKage, textures.txMatKage);
        }
    }
//}

    import alternativ7.engine3d.primitives.Plane;
        
    //public 
    class NodeUtils  {
        
      
        public static var ROOT:*;
        public static var MAX_ALTITUDE:Number = Number.MAX_VALUE;
        public static var MIN_ALTITUDE:Number = -Number.MAX_VALUE;
        public static function find2DNode(x:Number, y:Number):* {
            return find2DNode_aux(ROOT, x, y);
        }
        
        private static function find2DNode_aux(node:*, x:Number, y:Number):* {
            var axis:int = node.axis;
            var bounds:Object3D;
            if (axis == 2) return node;
            var value:int = axis != 0 ? y : x;
            return value >= node.coord ? find2DNode_aux(node.positive,x,y) : find2DNode_aux(node.negative,x,y);
        }
    }



//package bsp 
//{
    /**
     * Generic cross-platform BSP node construct for any engine
     * @author Glenn Ko
     */
    //public 
    class BSPNode 
    {
        public var front:BSPNode;
        public var back:BSPNode;
        public var brushIndex:int = -1;
        public var normalX:Number;
        public var normalY:Number;
        public var normalZ:Number;
        public var offset:Number;
        
        
        public function BSPNode() 
        {
            
        }
        
        /** Recursively counts the number of nodes in this tree (including this). */
        public function size():int
        {
            var c:int = 1;
            if (front) { 
                 // assumption made both available
                c += front.size();
                c += back.size();  
            }
            return c;
        }
        

    }
        
    
//}


//package bsp 
//{
    import alternativ7.engine3d.core.Object3D;
    import alternativ7.engine3d.alternativa3d;
    import alternativ7.engine3d.core.Object3DContainer;
    use namespace alternativa3d;
    
    /**
     * Custom xml parsing construct of BSPNode for Alternativa3D on animatable brush-based entities
     * @author Glenn Ko
     */
    //public 
    class BSPXMLNode extends BSPNode
    {
        
        
        public function BSPXMLNode(xml:XML, rootBrush:Object3D, brushContext:Vector.<Mesh>) 
        {
            var planeXML:XML;
            
            // Node validation
            //#if debug
            ///*
            if ( xml.hasOwnProperty("front") || xml.hasOwnProperty("back") ) {
                if (!(xml.hasOwnProperty("front") && xml.hasOwnProperty("back"))) {
                    throw new Error("Do not have both front and back!");
                }
                planeXML = xml.hasOwnProperty("plane") ? xml.plane[0] : null;
                if (planeXML == null) throw new Error("Could not find split plane node <plane>!");
                if ( !planeXML.hasOwnProperty("nx") || !planeXML.hasOwnProperty("ny") || !planeXML.hasOwnProperty("nz") || !planeXML.hasOwnProperty("offset") ) {
                    throw new Error("Do not have valid plane variables!");
                }
            }
            else if (!xml.hasOwnProperty("brush")) {
                throw new Error("No leaf node brush reference found!!");
            }
            
            //*/
            //#end
            
            
            if (xml.hasOwnProperty("front")) {  
                planeXML = xml.plane[0];
                normalX = Number(planeXML.nx[0]);    
                normalY = Number(planeXML.ny[0]);
                normalZ = Number(planeXML.nz[0]);
                offset = Number(planeXML.offset[0]);  
                brushIndex = planeXML.hasOwnProperty("brush") ? int(planeXML.brush[0]) : -1;  
                front = new BSPXMLNode(xml.front[0], rootBrush, brushContext);
                back = new BSPXMLNode(xml.back[0], rootBrush, brushContext);
            }
            else {
                brushIndex = int(xml.brush[0]);
            }    
            
            
            
        }
        
        
    }

//}

//package bsp 
//{
    import alternativ7.engine3d.core.Camera3D;
    import alternativ7.engine3d.core.Canvas;
    import alternativ7.engine3d.core.Object3D;
    import alternativ7.engine3d.alternativa3d;
    use namespace alternativa3d;
    /**
     * @author Glenn Ko
     */
    //public 
    class BSPTreeRendererI
    {
        public var rootNode:BSPNode;
        public var camera:Camera3D;
        
        private var stack:Vector.<BSPNode>;
        private var stackResults:Vector.<BSPNode>;
        
        /**
         * 
         * @param    camera        The camera for rendering
         * @param    rootNode    Reference bsp model node to use for rendering.
         */
        public function BSPTreeRendererI(camera:Camera3D, rootNode:BSPNode ) 
        {
            this.camera = camera;
            this.rootNode = rootNode;
            stack = new Vector.<BSPNode>( rootNode.size(), true ); 
            stackResults = new Vector.<BSPNode>( stack.length, true ); 
        }
        
        
        
        public function collectVG(brushContext:Vector.<Mesh>, culling:int, obj:Object3D):VG {
            
            // inline vars
            /*
            var faceList:Face;
            var f:Face;
            var v:Vertex;
            var w:Wrapper;
            var lastF:Face;
            */
            
             // drawing vars
            var brush:Mesh;
        
            // node vars
            var node:BSPNode;
            var top:int = 0;
            var prev:BSPNode;
            var prevResult:BSPNode; 
        
            stack[top++] = rootNode;
            
            var vgList:VG;
            var vg:VG;
            
        
            for (var i:int = 0; i < brushContext.length; i++) {
                brush = brushContext[i];
                brush.clipping = 2
                brush.culling = culling;
                    vg = brush.getVG(camera);
                    if (vg != null) {
                        vg.next = vgList;
                        vgList = vg;
                    }
            }
            
            return vgList;

            // Darn can't inline the below...
            while (top != 0) {
                node = stack[top - 1];
                
                if (!node.front) { // No more children, so spit leaf out for drawing
                    top--;
                    // todo: inline drawing of faces if rootNode is BSPNodeNiva
                    
                    brush = brushContext[node.brushIndex];
                    brush.culling = culling;
                    vg = brush.getVG(camera);
                    if (vg != null) {
                        vg.next = vgList;
                        vgList = vg;
                    }
            /*
                    brush.transformId = camera.transformId;
                    lastF = f = brush.prepareFaces(camera); // todo: inline this
                    for (f = f.processNext; f != null; f = f.processNext) {
                        lastF = f;
                    }
                    if (lastF!=null) {
                        lastF.processNext = faceList;
                        faceList = lastF;
                    }
              */      
                    prev = node;
                    continue;
                }
                 // got children to recurse into     
                if (!prev || prev.front === node || prev.back === node) {  // if curr node is child of previous
                    //brush = node.brushIndex >= 0 ?  brushContext[node.brushIndex] : rootBrush;
                    brush = brushContext[node.brushIndex];
                    stackResults[top - 1] = prevResult =  brush.imd * node.normalX + brush.imh * node.normalY  + brush.iml * node.normalZ > node.offset ? node.front : node.back;
                
                    stack[top++] = prevResult;
                    prev = node;
                    continue;
                }
                else if ( (prevResult = stackResults[top - 1]) === prev ) { // if curr node's initial result was the previous, recurse other side as well
                
                    stack[top++] = prevResult === node.back ? node.front :  node.back;
                    prev = node;
                    continue;
                }    
                top--;
                prev = node;
            }
            
            return vgList;// faceList != null ?  VG.create(obj, faceList, 0, 0, false) : null;
        }
        
        
        /**
         * Draw brushes onto canvas through iterative bsp traversal
         * @param    canvas            The canvas being 
         * @param   rootBrush        Usually the entity being drawn     // rootBrush:Object3D,        
         * @param    brushContext    Any related brushes for the entity to be drawn/referenced 
         *                             for split-planes/leafs.
         */
        public function draw(canvas:Canvas,  brushContext:Vector.<Mesh>, culling:int):void {
            // drawing vars
            var brush:Mesh;
        
            // node vars
            var node:BSPNode;
            var top:int = 0;
            var prev:BSPNode;
            var prevResult:BSPNode; 
        
            stack[top++] = rootNode;

            while (top != 0) {
                node = stack[top - 1];
                
                if (!node.front) { // No more children, so spit leaf out for drawing
                    top--;
                    // todo: inline drawing of faces if rootNode is BSPNodeNiva
                    brush = brushContext[node.brushIndex];
                   // if (brush.cullingInCamera(camera, culling) >= 0) 
                   brush.culling = culling;
                    brush.draw(camera, canvas); 
                    
                    prev = node;
                    continue;
                }
                 // got children to recurse into     
                if (!prev || prev.front === node || prev.back === node) {  // if curr node is child of previous
                    //brush = node.brushIndex >= 0 ?  brushContext[node.brushIndex] : rootBrush;
                    brush = brushContext[node.brushIndex];
                    stackResults[top - 1] = prevResult =  brush.imd * node.normalX + brush.imh * node.normalY  + brush.iml * node.normalZ > node.offset ? node.front : node.back;
                
                    stack[top++] = prevResult;
                    prev = node;
                    continue;
                }
                else if ( (prevResult = stackResults[top - 1]) === prev ) { // if curr node's initial result was the previous, recurse other side as well
                
                    stack[top++] = prevResult === node.back ? node.front :  node.back;
                    prev = node;
                    continue;
                }    
                top--;
                prev = node;
            }
        }

    }

//}


class DangBoardFrame {

    public var danBody_z:Number;
          public var           danHead_rotationX :Number;
          public var           danArmL_rotationY:Number;
           public var          danArmR_rotationY:Number;
          public var           danSldL_rotationX :Number;
           public var          danSldR_rotationX:Number;
           public var          danLegL_rotationX:Number;
            public var        danLegR_rotationX:Number;
            public var         danKneL_rotationY :Number;
          public var           danKneR_rotationY:Number;

}

class DangBoardClip {
    public var motionR:Number;
    public var frameLen:int = 0;
    public var frames:Vector.<DangBoardFrame> = new Vector.<DangBoardFrame>();
}

// -- Other utilities/classes



import flash.utils.Dictionary;
import flash.events.KeyboardEvent;
import flash.display.Stage;

    class KeyBinder {
        protected var _keys : flash.utils.Dictionary;
        public function KeyBinder(stage : flash.display.Stage = null) : void {
            this._keys = new flash.utils.Dictionary();
            if(stage != null) this.setupStageListeners(stage);
        }
        
        public function setupStageListeners(stage : flash.display.Stage) : void {
            stage.addEventListener(flash.events.KeyboardEvent.KEY_UP,this.onKeyUp);
            stage.addEventListener(flash.events.KeyboardEvent.KEY_DOWN,this.onKeyDown);
        }
        
        public function removeStageListeners(stage : flash.display.Stage) : void {
            stage.removeEventListener(flash.events.KeyboardEvent.KEY_UP,this.onKeyUp);
            stage.removeEventListener(flash.events.KeyboardEvent.KEY_DOWN,this.onKeyDown);
        }
        
        protected function onKeyDown(e : flash.events.KeyboardEvent) : void {
            if(this._keys[e.keyCode]) this._keys[e.keyCode](true);
        }
        
        protected function onKeyUp(e : flash.events.KeyboardEvent) : void {
            if(this._keys[e.keyCode]) this._keys[e.keyCode](false);
        }
        
        public function bindKey(key : int,method : *) : void {
            this._keys[key] = method;
        }
        
        public function hasBinding(key : int) : Boolean {
            return this._keys[key] != null;
        }
        
        public function clear() : void {
            this._keys = new flash.utils.Dictionary();
        }
        
    }

    

    import alternativ7.engine3d.core.Camera3D;
    import alternativ7.engine3d.core.Object3D;
    import alternativ7.engine3d.core.RayIntersectionData;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Vector3D;
    import flash.utils.Dictionary;
    import alternativ7.engine3d.alternativa3d;
    use namespace alternativa3d;
    
    /**
     * Does adjustments to orbit controller for chase-cams/follow target in relation to entire environment
     * and various game-types.
     * 
     * @author Glidias
     */
     class OrbitCameraMan 
    {

        private var _followTarget:Object3D;
        public var controller:OrbitCameraController;
        
        protected var _preferedZoom:Number;
        public var maxFadeAlpha:Number = .8;
        public var minFadeAlpha:Number = .2;
        protected var _fadeDistance:Number = 200;
        public var useFadeDistance:Boolean = true;
        
        public var visBufferLength:Number = 0;
        public var preferedAlpha:Number = 1;
        
        public var followAzimuth:Boolean = false;
        public var followPitch:Boolean = false;
        protected static const ORIGIN:Vector3D = new Vector3D();
        protected var scene:Object3D;
        //x:  -45 * (Math.PI / 180)  
        
        public var cameraForward:Vector3D = new Vector3D();
        public var cameraReverse:Vector3D = new Vector3D();
        public var ignoreDict:Dictionary = new Dictionary();
        public var collideOffset:Number = 24;
        
        public var threshold:Number = 0.01;
        
    

        
        public var instant:Boolean = false;
        public var mouseWheelSensitivity:Number = 30;
        
        public function OrbitCameraMan(controller:OrbitCameraController, scene:Object3D, followTarget:Object3D=null) 
        {
            this._followTarget = followTarget || (controller._followTarget);
        
            this.scene = scene;
            this.controller = controller;
            _preferedZoom = controller.getDistance();
            ignoreDict[controller._followTarget] = true;
            controller.minPitch = Math.PI*.5;
        }
        
        /*
             _object.x += -Math.sin(_object.rotationZ) * 120;
            _object.y += Math.cos(_object.rotationZ) * 120;
        
            _object.x += forward.x * 120;
            _object.y += forward.y * 120;
            _object.z += forward.z * 120;
        */
        
        public function mouseWheelHandler(e:MouseEvent):void {
            var _lastLength:Number = controller.getDistance();
            _lastLength -= e.delta * mouseWheelSensitivity;
            if (_lastLength < controller.minDistance)
            {
                _lastLength = controller.minDistance
            }
            else if (_lastLength > controller.maxDistance)
            {
                _lastLength = controller.maxDistance
            }
            
            preferedZoom = _lastLength;
            
        }
        public function update():void {
            controller.update();

            var camera:Camera3D = controller._target;
        
            var camLookAtTarget:Object3D = controller._followTarget;
            if (followAzimuth) _followTarget.rotationZ = camera.rotationZ;
            if (followPitch) _followTarget.rotationX =  camera.rotationX + Math.PI * .5;
            
        
            
            camera.calculateRay(ORIGIN, cameraForward, camera.view._width*.5, camera.view._height*.5);            
        
            cameraReverse.x = -cameraForward.x;
            cameraReverse.y = -cameraForward.y;
            cameraReverse.z = -cameraForward.z;
            
            var data:RayIntersectionData = getBackRayIntersectionData( new Vector3D(camLookAtTarget.x + cameraForward.x*-threshold, camLookAtTarget.y + cameraForward.y*-threshold, camLookAtTarget.z + cameraForward.z*-threshold) );
        
                _followTarget.visible = true;
            var tarDist:Number = _preferedZoom;
            if (data != null) {
                
                //cameraReverse.normalize();
                data.point = data.object.localToGlobal(data.point);
                tarDist = data.point.subtract( new Vector3D(camLookAtTarget.x, camLookAtTarget.y, camLookAtTarget.z) ).length - collideOffset;
            
                if (tarDist > _preferedZoom) tarDist = _preferedZoom;
                if (tarDist < controller.minDistance) {
                    tarDist =  controller.minDistance;
                    
                }
                var limit:Number = (controller.minDistance + visBufferLength);
                _followTarget.visible = tarDist >= limit;
                
                if (useFadeDistance) {
                    limit = (tarDist - limit) / (_preferedZoom - _fadeDistance);
                    if (limit < 1) {
                        limit  = limit < 0 ? 0 : limit;
                        limit = minFadeAlpha + limit * (maxFadeAlpha - minFadeAlpha);
                        _followTarget.alpha = limit;
                    }
                    else _followTarget.alpha = preferedAlpha;
                }
                controller.setDistance(tarDist, instant);
                controller.setObjectPosXYZ(camera.x = camLookAtTarget.x +  cameraReverse.x * tarDist,
                camera.y= camLookAtTarget.y +  cameraReverse.y * tarDist,
                camera.z = camLookAtTarget.z +  cameraReverse.z * tarDist);
            
            }
            else  {
                
                controller.setDistance(tarDist, instant);
                if (useFadeDistance) _followTarget.alpha = preferedAlpha;
            }
            
            
            //*/
            
        }
        
        public function get sceneCollidable():Object3D {
            return scene;
        }
        protected function getBackRayIntersectionData(objPosition:Vector3D):RayIntersectionData 
        {
            return scene.intersectRay( objPosition, cameraReverse, ignoreDict);
        }
        
        public function get preferedZoom():Number 
        {
            return _preferedZoom;
        }
        
        public function set preferedZoom(value:Number):void 
        {
            _preferedZoom = value;
            controller.setDistance(value);
            
        }
        
        public function get fadeDistance():Number 
        {
            return _fadeDistance;
        }
        
        public function set fadeDistance(value:Number):void 
        {
            _fadeDistance = value;

        }
        
    }


    
    

    import alternativ7.engine3d.containers.BSPContainer;
    import alternativ7.engine3d.containers.ConflictContainer;
    import alternativ7.engine3d.containers.DistanceSortContainer;
    import alternativ7.engine3d.containers.KDContainer;
    import alternativ7.engine3d.containers.LODContainer;
    import alternativ7.engine3d.controllers.SimpleObjectController;
    import alternativ7.engine3d.core.Camera3D;
    import alternativ7.engine3d.core.Object3D;
    import alternativ7.engine3d.core.Object3DContainer;
    import alternativ7.engine3d.core.View;
    import flash.events.IEventDispatcher;

    import flash.display.DisplayObjectContainer;
    import flash.display.InteractiveObject;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageQuality;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.geom.Vector3D;
    import flash.ui.Keyboard;
    import flash.ui.Mouse;
    import flash.ui.MouseCursor;

    /**
     * GeoCameraController ? 3D ?????????????????????????????????
     * ??·???????????????
     *
     * @author narutohyper
     * @author clockmaker
     *
     * @see http://wonderfl.net/c/fwPU
     *  @langversion 3.0
     *  @playerversion Flash 10
     *  @playerversion AIR 1.5
     */
    
    class OrbitCameraController extends SimpleObjectController
    {

        //----------------------------------------------------------
        //
        //   Static Property 
        //
        //----------------------------------------------------------

        /** ??????????????????????? */
        public static const ACTION_FORWARD:String = "actionForward";

        /** ????????????????????????? */
        public static const ACTION_BACKWARD:String = "actionBackward";

        /** ???????????????????????0?0.2?????0????????????????? */
        private static const ROUND_VALUE:Number = 0.1;
        
        
        private var _lockRotationZ:Boolean = false;
        

        //----------------------------------------------------------
        //
        //   Constructor 
        //
        //----------------------------------------------------------

        /**
         * ??? GeoCameraController ?????????????
         * @param targetObject    ??????????????????????
         * @param mouseDownEventSource    ?????????????????????????
         * @param mouseUpEventSource    ???????????????????????????? stage ???
         * @param keyEventSource    ?????/??????????????????????????? stage ???
         * @param useKeyControl    ????????????????????
         * @param useMouseWheelControl    ?????????????????????????
         */
        public function OrbitCameraController(
            targetObject:Camera3D,
            followTarget:Object3D,
            mouseDownEventSource:InteractiveObject,
            mouseUpEventSource:InteractiveObject,
            keyEventSource:InteractiveObject,
            useKeyControl:Boolean = true,
            useMouseWheelControl:Boolean = true
            )
        {
            _target = targetObject;
            _followTarget = followTarget;

            super(mouseDownEventSource, targetObject, 0, 3, mouseSensitivity);
            super.mouseSensitivity = 0;
            super.unbindAll();
            super.accelerate(true);

            this._mouseDownEventSource = mouseDownEventSource;
            this._mouseUpEventSource = mouseUpEventSource;
            this._keyEventSource = keyEventSource;

            _mouseDownEventSource.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
            _mouseUpEventSource.addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
    
            // ?????????
            if (useMouseWheelControl)
            {
                _mouseDownEventSource.addEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);
            }

            // ???????
            if (useKeyControl)
            {
                _keyEventSource.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
                _keyEventSource.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
            }
        }
        
        public function reset():void {
            _angleLongitude = 0;
            _lastLongitude = 0;
            _angleLatitude = 0;
        //    _mouseMove = false;
            if (useHandCursor)
                Mouse.cursor = MouseCursor.AUTO;
            
        }

        //----------------------------------------------------------
        //
        //   Property 
        //
        //----------------------------------------------------------

        //--------------------------------------
        // easingSeparator 
        //--------------------------------------

        private var _easingSeparator:Number = 1.5;

        /**
         * ??????????????????????????
         * ??????????????
         * 1?
         * ???????0 ?????? 1 ??????
         * 1 ???????????????????????????
         */
        public function set easingSeparator(value:uint):void
        {
            if (value)
            {
                _easingSeparator = value;
            }
            else
            {
                _easingSeparator = 1;
            }
        }
        /** Camera ?????????????????????? 2000 ??? */
        public var maxDistance:Number = 2000;

        /** Camera ?????????????????????? 200 ??? */
        public var minDistance:Number = 200;

        //--------------------------------------
        // mouseSensitivityX 
        //--------------------------------------

        private var _mouseSensitivityX:Number = -1;

        /**
         * ???? X ?????
         */
        public function set mouseSensitivityX(value:Number):void
        {
            _mouseSensitivityX = value;
        }

        //--------------------------------------
        // mouseSensitivityY 
        //--------------------------------------

        private var _mouseSensitivityY:Number = 1;

        /**
         * ???? Y ?????
         */
        public function set mouseSensitivityY(value:Number):void
        {
            _mouseSensitivityY = value;
        }

        public var multiplyValue:Number = 10;

        //--------------------------------------
        // needsRendering 
        //--------------------------------------

        private var _needsRendering:Boolean;

        /**
         * ????????????????????
         * ???? update() ????????????????????????
         *
         * @see GeoCameraController.update
         */
        public function get needsRendering():Boolean
        {
            return _needsRendering;
        }

        //--------------------------------------
        // pitchSpeed 
        //--------------------------------------

        private var _pitchSpeed:Number = 5;

        /**
         * ??????
         * ????5(px)
         */
        public function set pitchSpeed(value:Number):void
        {
            _pitchSpeed = value
        }

        public var useHandCursor:Boolean = true;

        //--------------------------------------
        // yawSpeed 
        //--------------------------------------

        private var _yawSpeed:Number = 5;

        /**
         * ??????
         * ????5(?)
         */
        public function set yawSpeed(value:Number):void
        {
            _yawSpeed = value * Math.PI / 180;
        }

        /**
         * Zoom????
         * ????5(px)
         */
        public function set zoomSpeed(value:Number):void
        {
            _distanceSpeed = value;
        }
        
        public function get lockRotationZ():Boolean 
        {
            return _lockRotationZ;
        }
        
        public function set lockRotationZ(value:Boolean):void 
        {
            _lockRotationZ = value;
        }
        
        public function get followTarget():Object3D 
        {
            return _followTarget;
        }
        
        public function get angleLatitude():Number 
        {
            return _angleLatitude;
        }
        
        public function set angleLatitude(value:Number):void 
        {
            _angleLatitude = value;
            _lastLatitude = value;
        }
        
        public function get angleLongitude():Number 
        {
            return _angleLongitude;
        }
        
        public function set angleLongitude(value:Number):void 
        {
            _angleLongitude = value;
            _lastLongitude = value;
        }
        
        public function get minAngleLatitude():Number 
        {
            return _minAngleLatitude;
        }
        
        public function set minAngleLatitude(value:Number):void 
        {
            _minAngleLatitude = value;
        }
        
        public function get maxAngleLatidude():Number 
        {
            return _maxAngleLatidude;
        }
        
        public function set maxAngleLatidude(value:Number):void 
        {
            _maxAngleLatidude = value;
        }
        


        private var _minAngleLatitude:Number = -Number.MAX_VALUE;
        private var _maxAngleLatidude:Number = Number.MAX_VALUE;

        private var _angleLatitude:Number = 0;
        private var _angleLongitude:Number = 0;
        private var _distanceSpeed:Number = 5;
        private var _keyEventSource:InteractiveObject;
        private var _lastLatitude:Number = 0;
        private var _lastLength:Number = 700;
        private var _lastLongitude:Number = _angleLongitude;
        private var _lastLookAtX:Number = _lookAtX;
        private var _lastLookAtY:Number = _lookAtY;
        private var _lastLookAtZ:Number = _lookAtZ;
        private var _length:Number = 700;
        private var _lookAtX:Number = 0;
        private var _lookAtY:Number = 0;
        private var _lookAtZ:Number = 0;
        private var _mouseDownEventSource:InteractiveObject;
        private var _mouseMove:Boolean;
        private var _mouseUpEventSource:InteractiveObject;
        private var _mouseX:Number;
        private var _mouseY:Number;
        private var _oldLatitude:Number;
        private var _oldLongitude:Number;
        private var _pitchDown:Boolean;
        private var _pitchUp:Boolean;
        private var _taregetDistanceValue:Number = 0;
        private var _taregetPitchValue:Number = 0;
        private var _taregetYawValue:Number = 0;
        public var _target:Camera3D
        private var _yawLeft:Boolean;
        private var _yawRight:Boolean;
        private var _zoomIn:Boolean;
        private var _zoomOut:Boolean;
        public var _followTarget:Object3D;

        //----------------------------------------------------------
        //
        //   Function 
        //
        //----------------------------------------------------------

        /**
         * ?????????????????
         */
        public function bindBasicKey():void
        {
            bindKey(Keyboard.LEFT, SimpleObjectController.ACTION_YAW_LEFT);
            bindKey(Keyboard.RIGHT, SimpleObjectController.ACTION_YAW_RIGHT);
            bindKey(Keyboard.DOWN, SimpleObjectController.ACTION_PITCH_DOWN);
            bindKey(Keyboard.UP, SimpleObjectController.ACTION_PITCH_UP);
            bindKey(Keyboard.PAGE_UP, OrbitCameraController.ACTION_BACKWARD);
            bindKey(Keyboard.PAGE_DOWN, OrbitCameraController.ACTION_FORWARD);
        }

        /** ?????????? */
        public function pitchUp():void
        {
            _taregetPitchValue = _pitchSpeed * multiplyValue;
        }

        /** ?????????? */
        public function pitchDown():void
        {
            _taregetPitchValue = _pitchSpeed * -multiplyValue;
        }

        /** ?????????? */
        public function yawLeft():void
        {
            _taregetYawValue = _yawSpeed * multiplyValue;
        }

        /** ?????????? */
        public function yawRight():void
        {
            _taregetYawValue = _yawSpeed * -multiplyValue;
        }

        /** ????????????? */
        public function moveForeward():void
        {
            _taregetDistanceValue -= _distanceSpeed * multiplyValue;
        }

        /** ???????????? */
        public function moveBackward():void
        {
            _taregetDistanceValue += _distanceSpeed * multiplyValue;
        }

        /**
         * @inheritDoc
         */
        override public function bindKey(keyCode:uint, action:String):void
        {
            switch (action)
            {
                case ACTION_FORWARD:
                    keyBindings[keyCode] = toggleForward;
                    break
                case ACTION_BACKWARD:
                    keyBindings[keyCode] = toggleBackward;
                    break
                case ACTION_YAW_LEFT:
                    keyBindings[keyCode] = toggleYawLeft;
                    break
                case ACTION_YAW_RIGHT:
                    keyBindings[keyCode] = toggleYawRight;
                    break
                case ACTION_PITCH_DOWN:
                    keyBindings[keyCode] = togglePitchDown;
                    break
                case ACTION_PITCH_UP:
                    keyBindings[keyCode] = togglePitchUp;
                    break
            }
            //super.bindKey(keyCode, action)
        }

        /**
         * ??????????????????
         * @param value    true ???????????????
         */
        public function togglePitchDown(value:Boolean):void
        {
            _pitchDown = value
        }

        /**
         * ??????????????????
         * @param value    true ???????????????
         */
        public function togglePitchUp(value:Boolean):void
        {
            _pitchUp = value
        }

        /**
         * ??????????????????
         * @param value    true ???????????????
         */
        public function toggleYawLeft(value:Boolean):void
        {
            _yawLeft = value;
        }

        /**
         * ??????????????????
         * @param value    true ???????????????
         */
        public function toggleYawRight(value:Boolean):void
        {
            _yawRight = value;
        }

        /**
         * ???????????????????
         * @param value    true ???????????????
         */
        public function toggleForward(value:Boolean):void
        {
            _zoomIn = value;
        }

        /**
         * ??????????????????????
         * @param value    true ???????????????
         */
        public function toggleBackward(value:Boolean):void
        {
            _zoomOut = value;
        }

        private var testLook:Vector3D = new Vector3D();
        /**
         * @inheritDoc
         */
        override public function update():void
        {
            const RADIAN:Number = Math.PI / 180;
            var oldAngleLatitude:Number = _angleLatitude;
            var oldAngleLongitude:Number = _angleLongitude;
            var oldLengh:Number = _lastLength;

            //CameraZoom??
            if (_zoomIn)
            {
                _lastLength -= _distanceSpeed;
            }
            else if (_zoomOut)
            {
                _lastLength += _distanceSpeed;
            }

            // ?????
            if (_taregetDistanceValue != 0)
            {
                _lastLength += _taregetDistanceValue;
                _taregetDistanceValue = 0;
            }

            if (_lastLength < minDistance)
            {
                _lastLength = minDistance;
            }
            else if (_lastLength > maxDistance)
            {
                _lastLength = maxDistance;
            }
            if (_lastLength - _length)
            {
                _length += (_lastLength - _length) / _easingSeparator;
            }

            if (Math.abs(_lastLength - _length) < ROUND_VALUE)
            {
                _length = _lastLength;
            }

            //Camera????
            if (_mouseMove)
            {
                _lastLongitude = _oldLongitude + (_mouseDownEventSource.mouseX - _mouseX) * _mouseSensitivityX
            }
            else if (_yawLeft)
            {
                _lastLongitude += _yawSpeed;
            }
            else if (_yawRight)
            {
                _lastLongitude -= _yawSpeed;
            }

            if (_taregetYawValue)
            {
                _lastLongitude += _taregetYawValue;
                _taregetYawValue = 0;
            }

            if (_lastLongitude - _angleLongitude)
            {
                _angleLongitude += (_lastLongitude - _angleLongitude) / _easingSeparator;
            }

            if (Math.abs(_lastLatitude - _angleLatitude) < ROUND_VALUE)
            {
                _angleLatitude = _lastLatitude;
            }

            //CameraZ????
            if (_mouseMove)
            {
                _lastLatitude = _oldLatitude + (_mouseDownEventSource.mouseY - _mouseY) * _mouseSensitivityY;
            }
            else if (_pitchDown)
            {
                _lastLatitude -= _pitchSpeed;
            }
            else if (_pitchUp)
            {
                _lastLatitude += _pitchSpeed;
            }

            if (_taregetPitchValue)
            {
                _lastLatitude += _taregetPitchValue;
                _taregetPitchValue = 0;
            }

            _lastLatitude = Math.max(-89.9, Math.min(_lastLatitude, 89.9));

            if (_lastLatitude - _angleLatitude)
            {
                _angleLatitude += (_lastLatitude - _angleLatitude) / _easingSeparator;
            }
            if (Math.abs(_lastLongitude - _angleLongitude) < ROUND_VALUE)
            {
                _angleLongitude = _lastLongitude;
            }
            
            
            if (_angleLatitude < _minAngleLatitude) _angleLatitude = _minAngleLatitude;
            if (_angleLatitude > _maxAngleLatidude) _angleLatitude = _maxAngleLatidude;
            

            var vec3d:Vector3D = this.translateGeoCoords(_angleLatitude, _angleLongitude, _length);
                testLook.x = _followTarget.x;
                testLook.y = _followTarget.y;
                testLook.z = _followTarget.z;
            //    testLook = _followTarget.localToGlobal(testLook);
    
            _target.x = testLook.x + vec3d.x;
            _target.y = testLook.y + vec3d.y;
            _target.z = testLook.z + vec3d.z;

            //lookAt??
            if (_lastLookAtX - _lookAtX)
            {
                _lookAtX += (_lastLookAtX - _lookAtX) / _easingSeparator;
            }

            if (_lastLookAtY - _lookAtY)
            {
                _lookAtY += (_lastLookAtY - _lookAtY) / _easingSeparator;
            }

            if (_lastLookAtZ - _lookAtZ)
            {
                _lookAtZ += (_lastLookAtZ - _lookAtZ) / _easingSeparator;
            }
            
        

            //super.update()
            updateObjectTransform();
            
            
            
            lookAtXYZ(_lookAtX + testLook.x, _lookAtY + testLook.y, _lookAtZ + testLook.z);
            
    

            _needsRendering = oldAngleLatitude != _angleLatitude
                || oldAngleLongitude != _angleLongitude
                || oldLengh != _length;
        }

        /** @inheritDoc */
        override public function startMouseLook():void
        {
            // ??
        }

        /** @inheritDoc */
        override public function stopMouseLook():void
        {
            // ??
        }

        /**
         * Camera????????????
         * lookAt?lookAtXYZ????????
         * @param x
         * @param y
         * @param z
         * @param immediate    true?????????????
         */
        public function lookAtPosition(x:Number, y:Number, z:Number, immediate:Boolean = false):void
        {
            if (immediate)
            {
                _lookAtX = x
                _lookAtY = y
                _lookAtZ = z
            }
            _lastLookAtX = x
            _lastLookAtY = y
            _lastLookAtZ = z
        }

        /**
         * ?????????
         * @param value    0??????????(lookAtPosition)???
         * @param immediate    true?????????????
         */
        public function setLongitude(value:Number, immediate:Boolean = false):void
        {
            if (immediate)
            {
                _angleLongitude = value;
            }
            _lastLongitude = value;
        }

        /**
         * ?????????
         * @param value    0??????????(lookAtPosition)???
         * @param immediate    true?????????????
         */
        public function setLatitude(value:Number, immediate:Boolean = false):void
        {
            if (immediate)
            {
                _angleLatitude = value;
            }
            _lastLatitude = value;

        }

        /**
         * Camera???targetObject????????????
         * @param value    Camera???targetObject?????
         * @param immediate true?????????????
         */
        public function setDistance(value:Number, immediate:Boolean = false):void
        {
            if (immediate)
            {
                _length = value;
            }
            _lastLength = value;
        }
        
        public function getDistance():Number {
            return _length;
        }

        /**
         * ????????????????????????
         */
        public function dispose():void
        {
            // ???????
            if (_mouseDownEventSource)
                _mouseDownEventSource.removeEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);

            // ?????????
            if (_mouseDownEventSource)
                _mouseDownEventSource.removeEventListener(MouseEvent.MOUSE_WHEEL, mouseWheelHandler);

            // ???????
            if (_keyEventSource)
            {
                _keyEventSource.removeEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
                _keyEventSource.removeEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
            }

            // ?????????
            _easingSeparator = 0;
            maxDistance = 0;
            minDistance = 0;
            _mouseSensitivityX = 0;
            _mouseSensitivityY = 0;
            multiplyValue = 0;
            _needsRendering = false;
            _pitchSpeed = 0;
            useHandCursor = false;
            _yawSpeed = 0;
            _angleLatitude = 0;
            _angleLongitude = 0;
            _distanceSpeed = 0;
            _keyEventSource = null;
            _lastLatitude = 0;
            _lastLength = 0;
            _lastLongitude = 0;
            _lastLookAtX = 0;
            _lastLookAtY = 0;
            _lastLookAtZ = 0;
            _length = 0;
            _lookAtX = 0;
            _lookAtY = 0;
            _lookAtZ = 0;
            _mouseDownEventSource = null;
            _mouseMove = false;
            _mouseUpEventSource = null;
            _mouseX = 0;
            _mouseY = 0;
            _oldLatitude = 0;
            _oldLongitude = 0;
            _pitchDown = false;
            _pitchUp = false;
            _taregetDistanceValue = 0;
            _taregetPitchValue = 0;
            _taregetYawValue = 0;
            _target = null;
            _yawLeft = false;
            _yawRight = false;
            _zoomIn = false;
            _zoomOut = false;
        }

        protected function mouseDownHandler(event:Event):void
        {
            _oldLongitude = _lastLongitude;
            _oldLatitude = _lastLatitude;
            _mouseX = _mouseDownEventSource.mouseX;
            _mouseY = _mouseDownEventSource.mouseY;
            _mouseMove = true;

            if (useHandCursor)
                Mouse.cursor = MouseCursor.HAND;

        
        }

        protected function mouseUpHandler(event:Event):void
        {
            if (!_mouseMove) return;
            
            if (useHandCursor)
                Mouse.cursor = MouseCursor.AUTO;

            _mouseMove = false;
            
        }
        
        

        private function keyDownHandler(event:KeyboardEvent):void
        {
            for (var key:String in keyBindings)
            {
                if (String(event.keyCode) == key)
                {
                    keyBindings[key](true)
                }
            }
        }

        private function keyUpHandler(event:KeyboardEvent = null):void
        {
            for (var key:String in keyBindings)
            {
                keyBindings[key](false)
            }
        }

        private function mouseWheelHandler(event:MouseEvent):void
        {
            _lastLength -= event.delta * 20;
            if (_lastLength < minDistance)
            {
                _lastLength = minDistance
            }
            else if (_lastLength > maxDistance)
            {
                _lastLength = maxDistance
            }
        }

        /**
         * ????????????????
         * @param latitude    ??
         * @param longitude    ??
         * @param radius    ??
         * @return ????
         */
        private function translateGeoCoords(latitude:Number, longitude:Number, radius:Number):Vector3D
        {
            const latitudeDegreeOffset:Number = 90;
            const longitudeDegreeOffset:Number = -90;

            latitude = Math.PI * latitude / 180;
            longitude = Math.PI * longitude / 180;

            latitude -= (latitudeDegreeOffset * (Math.PI / 180));
            longitude -= (longitudeDegreeOffset * (Math.PI / 180));

            var x:Number = radius * Math.sin(latitude) * Math.cos(longitude);
            var y:Number = radius * Math.cos(latitude);
            var z:Number = radius * Math.sin(latitude) * Math.sin(longitude);

            return new Vector3D(x, z, y);
        }
    }
//}


class CrossHair extends Sprite {
    public function CrossHair() {
        graphics.lineStyle(1, 0xDDEE44, 1);
        graphics.moveTo(0, -2);
        graphics.lineTo(0, -6);
        
        graphics.moveTo(0, 2);
        graphics.lineTo(0, 6);
        
        graphics.moveTo(2, 0);
        graphics.lineTo(6, 0);
        
        graphics.moveTo(-2, 0);
        graphics.lineTo( -6, 0);
        
        filters = [new DropShadowFilter(1,45,0,1,0,0,1,1,false,false, false)];
    }
}
    
// Object3D node to hold multiple children to draw with either distance-sort/conflict
class ChildrenList extends Object3D {

    public static var collector:Object3D;
    
     // We assume this is called after numChildren was 1 and decremented to 0 after removeChild()
    public function destroy():void { 
        childrenList = null;
        next = collector;
        collector = this;
    }
    
    // We asume this is called when a child is created and forms up a list of his own...
    public static function create(child:Object3D):ChildrenList {
        var instance:ChildrenList = (collector as ChildrenList) || new ChildrenList();
        collector = instance.next;
        instance.next = null;
        instance.numChildren = 1;
        instance.childrenList = child;
        return instance;
    }
    
    public static var DS_SORT:DistanceSortContainer = new DistanceSortContainer();
    public static var CONFICT:ConflictContainer = new ConflictContainer();
    
    public var childrenList:Object3D;
    public var numChildren:int = 0;
    
    public function addChild(child:Object3D):void { //note: this doesnt validate if child is already added!
        numChildren++;
        child.next = childrenList;
        childrenList = child;
        
    }
    
    public function removeChild(child:Object3D):void {
        var nextChild:Object3D;
        for (var c:Object3D = childrenList; c != null; c = nextChild) {
            nextChild = c.next;
            if (c === child) {
                c.next = nextChild;
                numChildren--;
                return;
            }
        }
        throw new Error("FAILED to remove child from list!");
    }
    
}