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

Terrain tileset painter preview.

Textures from Starsiege:TRIBES. Preview...Not feature-complete atm.
Get Adobe Flash player
by Glidias 23 Oct 2012

    Talk

    makc3d at 21 Feb 2011 01:12
    tried twice, not a single texture was loaded (got all pink checkerboards)
    Glidias at 29 Mar 2011 21:04
    Paint tile texturing terrain/mipmapping, terrain lod , quad tree and dynamic bsp test. (TAB to view) glidias.freehostia.com/lodterrain_dynamicbsp.swf
    Embed
/**
 * Copyright Glidias ( http://wonderfl.net/user/Glidias )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/9dsO
 */

package {
    
    import alternativ7.engine3d.controllers.SimpleObjectController;
    import alternativ7.engine3d.core.Camera3D;
    import alternativ7.engine3d.core.Face;
    import alternativ7.engine3d.core.MouseEvent3D;
    import alternativ7.engine3d.core.Object3D;
    import alternativ7.engine3d.core.Object3DContainer;
    import alternativ7.engine3d.core.Vertex;
    import alternativ7.engine3d.core.View;
    import alternativ7.engine3d.loaders.Parser3DS;
    import alternativ7.engine3d.materials.FillMaterial;
    import alternativ7.engine3d.objects.Mesh;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.TimerEvent;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.net.URLRequest;

    /**
     * Terrain tileset painter
     */
    public class TerrainPainterPreview extends Sprite {
        
        private var rootContainer:Object3DContainer = new Object3DContainer();
        private var camera:Camera3D;
        private var controller:SimpleObjectController;

        public function TerrainPainterPreview() {
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            
            // Camera and view
            camera = new Camera3D();
            camera.view = new View(stage.stageWidth, stage.stageHeight);
            addChild(camera.view);
            addChild(camera.diagram);
            
            // Initial position
            camera.rotationX = -120*Math.PI/180;
            camera.y = -400;
            camera.z = 600;
            
            controller = new SimpleObjectController(stage, camera, 500);
            
            rootContainer.addChild(camera);
            
    

            // Listeners
            stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
            stage.addEventListener(Event.RESIZE, onResize);

            loadModel("http://glidias.freehostia.com/terrain.3ds");
        }
        
        private function loadModel(url:String):void 
        {
            var loader3ds:URLLoader = new URLLoader();
            loader3ds.dataFormat = URLLoaderDataFormat.BINARY;
            loader3ds.load(new URLRequest(url));
            loader3ds.addEventListener(Event.COMPLETE, on3dsLoad);
        }
        
        private function setupOrigin():void {
            var child:Object3D = rootContainer.addChild( new MarkerBox(0xFF0000, 100) );
            child.x = 100;
            child = rootContainer.addChild( new MarkerBox(0x00FF00, 10, 100) );
            child.y = 100;
            child = rootContainer.addChild( new MarkerBox(0x0000FF, 10, 10, 100) );
            child.z = 100;
        }
        
    
        private function on3dsLoad(e:Event):void 
        {
            e.currentTarget.removeEventListener(e.type, on3dsLoad);
            parseTerrain ( (e.target as URLLoader).data );
            
            prepareTileSet();
            setupOrigin();
        }
        
        private var testMaterial:FillMaterial = new FillMaterial( 0xFFDDCC);
        private var terrainGridPainter:TerrainGridPainter;
        private var tileSetViewer:TileSetViewer;
        
        
        
        private function parseTerrain(data:*):void {
            var parser:Parser3DS = new Parser3DS();
            parser.parse(data);
            var mesh:Mesh = (parser.objects[0] as Mesh);

            mesh.setMaterialToAllFaces( testMaterial);
            mesh.sorting = 1;

            
            terrainGridPainter = new TerrainGridPainter();
            terrainGridPainter.buildFromMesh(mesh);
            terrainGridPainter.addEventListener(MouseEvent3D.CLICK, paintOnTerrain);
            rootContainer.addChild(terrainGridPainter);
        }
        
        private function paintOnTerrain(e:MouseEvent3D):void 
        {
            if (tileSetViewer.model == null) return;
            terrainGridPainter.paintUsingRay(e.localOrigin, e.localDirection, tileSetViewer.model);
        }
        
        private function prepareTileSet():void {
            tileSetViewer = new TileSetViewer();
            //tileSetViewer.directoryPath = "http://localhost/tribes/textures/lushdml";
            tileSetViewer.directoryPath = "http://glidias.freehostia.com/tribes/textures/lushdml";
            tileSetViewer.loadForXMLFromURL(tileSetViewer.directoryPath + "/index.php");
            tileSetViewer.addEventListener(TileSetViewer.APPLY_ALL, onTileSetApplyAll, false, 0, true);
            addChild(tileSetViewer);
        }
        
        private function onTileSetApplyAll(e:Event):void 
        {
            terrainGridPainter.applyAll(tileSetViewer.model);
        }
        
        
        private function onEnterFrame(e:Event):void {
            controller.update();
            camera.render();
        }
        
        private function onResize(e:Event = null):void {
            // Width and height of view
            camera.view.width = stage.stageWidth;
            camera.view.height = stage.stageHeight;
        }
        
    }
}

//package utils.terrain 
//{
    /**
     * ...
     * @author Glenn Ko
     */
    //public 
    class PaintCommand
    {
        public var textureDef:TextureDefinition;
        public var patch:Patch;
        
        public function PaintCommand(patch:Patch, textureDef:TextureDefinition) {
            this.patch = patch;
            this.textureDef = textureDef;
        }
        
        public function execute():void {
            patch.textureDef = textureDef;
        }    
        
    }

//}

//package utils.terrain 
//{
    import alternativ7.engine3d.materials.TextureMaterial;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.geom.Matrix;
    /**
     * A quad-based texture definition.
     * @author Glenn Ko
     */
    //public 
    class TextureDefinition 
    {
        public var material:TextureMaterial;
        public var stringId:String;
        public var identityTypeLayout:Vector.<int> = new Vector.<int>(4, true);
        public var oddType:int;  
        
        private static var HW:int = 64;

        // precompute all possible transforms
        private static const TRANSFORMS:Vector.<Matrix> = createStaticTransforms();
        private static const TRANSFORM_LAYOUTS:Vector.<Vector.<int>> = createStaticTransformLayouts();
        private static function createStaticTransforms():Vector.<Matrix> {
            var matrixList:Vector.<Matrix> = new Vector.<Matrix>(7, true);
            var mat:Matrix;
            // rotate 90 degrees clockwise successively
            matrixList[0] = mat = new Matrix(); mat.translate(-HW, -HW);  mat.rotate( .5*Math.PI);  mat.translate(HW, HW);
            matrixList[1] = mat = new Matrix(); mat.translate( -HW, -HW); mat.rotate( 1 * Math.PI); mat.translate(HW, HW);
            matrixList[2] = mat = new Matrix(); mat.translate( -HW, -HW); mat.rotate( 1.5 * Math.PI); mat.translate(HW, HW);
            // flip vertical and rotate 90 degrees anticlockwise successively
            matrixList[3] = mat = new Matrix(); mat.translate(-HW, -HW); mat.scale(1, -1);  mat.translate(HW, HW);
            matrixList[4] = mat = new Matrix(); mat.translate(-HW, -HW); mat.scale(1, -1); mat.rotate( -.5 * Math.PI); mat.translate(HW, HW);
            matrixList[5] = mat = new Matrix(); mat.translate(-HW, -HW); mat.scale(1, -1); mat.rotate( -1 * Math.PI); mat.translate(HW, HW);
            matrixList[6] = mat = new Matrix(); mat.translate(-HW, -HW); mat.scale(1, -1); mat.rotate( -1.5 * Math.PI); mat.translate(HW, HW);
            return matrixList;
        }
        private static function createStaticTransformLayouts():Vector.<Vector.<int>> {
            var vec:Vector.<Vector.<int>> = new Vector.<Vector.<int>>(7, true);
            var layout:Vector.<int>;
            vec[0] = layout =  new <int>[2,0,3,1]; layout.fixed = true;
            vec[1] = layout =  new <int>[3,2,1,0]; layout.fixed = true;
            vec[2] = layout =  new <int>[1,3,0,2]; layout.fixed = true;
            vec[3] = layout =  new <int>[2,3,0,1]; layout.fixed = true;
            vec[4] = layout =  new <int>[3,1,2,0]; layout.fixed = true;
            vec[5] = layout =  new <int>[1,0,3,2]; layout.fixed = true;
            vec[6] = layout =  new <int>[0,2,1,3]; layout.fixed = true;
            return vec;
        }
        
        public static function previewTransforms(texture:BitmapData):Sprite {
            var bmpData:BitmapData;
            var spr:Sprite = new Sprite();
            for (var i:int = 0; i < 7; i++) {
                bmpData = new BitmapData(texture.width, texture.height, texture.transparent, 0);
                bmpData.draw(texture, TRANSFORMS[i]);
                var child:DisplayObject = spr.addChild( new Bitmap(bmpData) );
                child.x = i * (bmpData.width + 2);
            }
            return spr;
        }
        
        private var _transformCache:Vector.<TextureDefinition>;
        
        public function TextureDefinition() 
        {
            
        }
        
        public function toString():String {
            return "[TextureDefinition "+identityTypeLayout+"]"
        }
        
        /**
         * The odd type is used to quickly determine within a common quad hash combination
         * (ie. their type combinations match) whether the quantities also match for each given type.
         * */
        public function calculateOddType():void {
            oddType = getOddType(identityTypeLayout);
        }
        
        public function createChildTransform(request:Vector.<int>):TextureDefinition {
            
            // find index based on request
            for (var i:int = 0; i < 7; i++) {
                var layout:Vector.<int> = TRANSFORM_LAYOUTS[i];
            
                if (  request[0] == identityTypeLayout[layout[0]] && request[1] == identityTypeLayout[layout[1]] && request[2] == identityTypeLayout[layout[2]] && request[3] == identityTypeLayout[layout[3]] ) {
                    break;
                }
            }

        
            if ( i < 7) {
                if (_transformCache == null) _transformCache = new Vector.<TextureDefinition>(7, true)
                else if (_transformCache[i] != null) return _transformCache[i];
                    //try {
                var texDef:TextureDefinition = new TextureDefinition();
                var bmpData:BitmapData = material.texture;
                bmpData = new BitmapData(bmpData.width, bmpData.height, bmpData.transparent, 0);
                bmpData.draw(material.texture, TRANSFORMS[i]);

                
                var textureMaterial:TextureMaterial = new TextureMaterial(bmpData, material.repeat, material.smooth, material.mipMapping, material.resolution);
                texDef.material = textureMaterial;
                texDef.identityTypeLayout[0] = identityTypeLayout[layout[0]];
                texDef.identityTypeLayout[1] = identityTypeLayout[layout[1]];
                texDef.identityTypeLayout[2] = identityTypeLayout[layout[2]];
                texDef.identityTypeLayout[3] = identityTypeLayout[layout[3]];
                texDef.oddType = oddType;
                _transformCache[i] = texDef;
                
                return texDef;
                //}
                // catch (e:Error) {
                //     throw new Error("CAUGHT!"+material);
                // }
            }
            
            //throw new Error("Could not resolve given transform!");
            return null;
        }
    
        public static function getOddType(request:Vector.<int>):int {
            var oddType:int = 0;
            var a:int = request[0];
            var b:int = request[1];
            var c:int = request[2];
            var d:int = request[3];
            var count:int = 0;  
            if (a == b || a == c || a == d) count++;
            if (b == c || b == d) count++;
            if (c == d) count++;
            count = 4 - count;  
        //    var sb:int =  count;    // for checking significant bits
            if (count == 2) { 
                // 2 significant bits, either 2/2 (no oddtype) or find odd type (1/3) which is 1 unique type against 3 similar types.
                count = 0;
                if (a != b) {
                    oddType = b;
                    count++;
                }
                if (a != c) {
                    oddType = c;
                    count++;
                }
                if (a != d) {
                    oddType = d;
                    count++;
                }
                // check for odd type of 1/3
                oddType = count != 2 ?  count != 1 ? a : oddType  // determine odd one out
                : 0;  // 2-2 case, no odd type
            }
            else if (count == 3) {  // for oddtype, find 2 of the same type among 2 which are different
                oddType = a == b || a == c || a == d  ? a  
                :  oddType = b == c || b == d ?  b : d;
            }
        //    throw new Error("Oddtype:" + oddType + ", " +sb);
            return oddType;
        }
        
        
        /**
         * Returns collision hash key.
         * @return
         */
        public function getTypeCombination():int {
            return identityTypeLayout[0] | identityTypeLayout[1] | identityTypeLayout[2] | identityTypeLayout[4];
        }
        
    
    }


//package utils.terrain 
//{
    import alternativ7.engine3d.materials.TextureMaterial;
    import flash.utils.Dictionary;
    /**
     * ...
     * @author Glenn Ko
     */
    //public
    class TileSetModel
    {
        private var _uniqueTextureCount:int = 0;
        private var _idTypeHash:Dictionary = new Dictionary(); // Vector.<TextureDefinition>
        private var _charIdMap:Dictionary = new Dictionary();
        private var _orderedUniqueTextureDefs:Vector.<TextureDefinition>
        private var _orderedUniqueTextures:Vector.<TextureMaterial>;

        public function get uniqueTextureCount():int { return _uniqueTextureCount; }
        public function get orderedUniqueTextureDefs():Vector.<TextureDefinition> { return _orderedUniqueTextureDefs; }
        public function get orderedUniqueTextures():Vector.<TextureMaterial> { return _orderedUniqueTextures; }
        
        public var selectedTexture:TextureDefinition;
        
        public function TileSetModel(vec:Vector.<TextureMaterial>=null) 
        {
            if (vec != null) setNewMaterials(vec);
        }
        
        
        private static const CANDIDATE_STACK:Vector.<TextureDefinition> = new Vector.<TextureDefinition>(4, true);
        /**
         * Finds a given texture definition given a combination request, or registers/creates
         * a new texture definition to match the request if not found yet. This method might
         * return null if it can't resolve a given combination.
         * @param    request    The combination request needs a filled integer vector of 4 values for each type,
         * reprensenting the 4 sub-quad corner terrain types of a given tile (NW, NE, SW, SE) accordingly.
         * @return    A matching texture definition, if available.
         */
        public function getDefinitionByCombination(request:Vector.<int>):TextureDefinition {
            var key:int = request[0] | request[1] | request[2] | request[3];
    
            var candidates:Vector.<TextureDefinition> = _idTypeHash[key];
            if (candidates == null) {
                
                return null;
            }
            var len:int = candidates.length;
            var oddType:int = TextureDefinition.getOddType(request);
            var count:int = 0;
            for (var i:int = 0 ; i < len; i++) {
                var def:TextureDefinition = candidates[i];
                
                if (oddType == def.oddType) { 
                    if (    // check for exact match
                    def.identityTypeLayout[0] == request[0] &&
                    def.identityTypeLayout[1] == request[1] &&
                    def.identityTypeLayout[2] == request[2] &&
                    def.identityTypeLayout[3] == request[3] ) {
                        //throw new Error(def.stringId);
                        return def;
                    }
                    else {  // push to potential candidate list to check for errors
                        CANDIDATE_STACK[count++] = def;
                    }
                }
                
                if (count > 0) {
                    if (count > 1) throw new Error("MORe than 1 candidate! Should not be!");
                
                    return CANDIDATE_STACK[0].createChildTransform(request);
                }
                
            }
            
            return null;
        }
        
        public function setNewMaterials(vec:Vector.<TextureMaterial>):void {
            _uniqueTextureCount = 0;
            var len:int = vec.length;
            var mat:TextureMaterial;
            var sortArr:Array = [];
            var ider:String;
            var c:String;
            
            var textureDefStack:Vector.<TextureDefinition> = new Vector.<TextureDefinition>(len, true);
            var textureDef:TextureDefinition;
            
            for (var i:int = 0 ; i < len; i++) {
                mat = vec[i];
                textureDef = new TextureDefinition();
                textureDef.material = mat;
                textureDefStack[i] = textureDef;
                var fileDirArr:Array = mat.diffuseMapURL.split("/");
                ider = fileDirArr[fileDirArr.length - 1];
                ider = ider.toLowerCase();
                ider =  ider.slice(0,ider.indexOf("."))
                ider = ider.slice(1);
                
                textureDef.stringId  = ider;
                
                c = ider.charAt(0);
                if (  ( c + c + c + c) === ider) {
                    _charIdMap[c] = 1 << ++_uniqueTextureCount;
                    sortArr.push(textureDef);
                }
            }
            
            sortArr.sortOn("stringId");
            
            _orderedUniqueTextures = new Vector.<TextureMaterial>(sortArr.length, true);
            _orderedUniqueTextureDefs = new Vector.<TextureDefinition>(sortArr.length, true);
            for (i = 0; i < sortArr.length; i++) {
                _orderedUniqueTextureDefs[i] = sortArr[i];
                _orderedUniqueTextures[i] = (sortArr[i] as TextureDefinition).material;
            }

            for (i = 0; i < len; i++) {
                textureDef = textureDefStack[i];
                
                var identityLayout:Vector.<int> = textureDef.identityTypeLayout;
                ider = textureDef.stringId;
                var key:int = 
                (identityLayout[0] = _charIdMap[ider.charAt(0)]) | 
                (identityLayout[1] = _charIdMap[ider.charAt(1)]) | 
                (identityLayout[2] = _charIdMap[ider.charAt(2)]) | 
                (identityLayout[3] = _charIdMap[ider.charAt(3)]);
                textureDef.calculateOddType();
                var textureDefList:Vector.<TextureDefinition> = _idTypeHash[key] || (_idTypeHash[key] =  new Vector.<TextureDefinition>());
                textureDefList.push(textureDef);
                
            }
        }
    
    }

//}

//package utils.terrain 
//{
    import alternativ7.engine3d.loaders.events.LoaderErrorEvent;
    import alternativ7.engine3d.loaders.events.LoaderEvent;
    import alternativ7.engine3d.loaders.events.LoaderProgressEvent;
    import alternativ7.engine3d.loaders.MaterialLoader;
    import alternativ7.engine3d.materials.TextureMaterial;
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    import flash.system.ApplicationDomain;
    import flash.system.LoaderContext;
    import flash.text.TextField;
    /**
     * ...
     * @author Glenn Ko
     */
    //public 
    class TileSetViewer extends Sprite
    {
        private var _materialLoader:MaterialLoader = new MaterialLoader();
        public var textureList:Vector.<TextureMaterial>;
        
        private var _partLoaded:int = 0;
        public var context:LoaderContext = null;
        public var directoryPath:String = "";
    
        public var model:TileSetModel;
        public static var APPLY_ALL:String = "tileSetApplyAll";

        
        public function TileSetViewer() 
        {
            
        }
        
        private function onApplyAllClick(e:MouseEvent):void 
        {
            dispatchEvent( new Event(APPLY_ALL) );
        }
        
        public function loadFromDefinitions(classDefs:Array, domain:ApplicationDomain):void {
            var len:int = classDefs.length;
            
            textureList = new Vector.<TextureMaterial>(len,true);
            for (var i:int = 0; i < len; i++) {
                var def:String = classDefs[i];
                textureList[i] = new TextureMaterial(new (domain.getDefinition(def) as Class)(0,0), true, true, 1, 2);
                addPart(i);
            }
            
            model = new TileSetModel(textureList);
            
        }
        
        public function loadForXMLFromURL(url:String):void {
            var urlLoader:URLLoader = new URLLoader();
            urlLoader.addEventListener(Event.COMPLETE, onURLLoadComplete);
            urlLoader.load( new URLRequest(url) );
        }
        
        private function onURLLoadComplete(e:Event):void 
        {
        //    throw new Error("LOAD DONE!");
            loadTexturesFromXML(XML(e.target.data));
        }
        
        public function loadTexturesFromXML(xml:XML):void {
            var xmlList:XMLList = xml.children();
            
            var len:int = xmlList.length();
            textureList = new Vector.<TextureMaterial>(len,true);
            for (var i:int = 0; i < len; i++) {
                var url:String = xmlList[i];
                textureList[i] = new TextureMaterial(null, true, true, 2, 2);
                textureList[i].diffuseMapURL = directoryPath + "/" + url;
            }
            
            _materialLoader.addEventListener(LoaderEvent.PART_COMPLETE, onPartLoadComplete, false, 0, true);
            _materialLoader.addEventListener(LoaderProgressEvent.LOADER_PROGRESS, onLoadProgress, false, 0, true);
            _materialLoader.addEventListener(    LoaderErrorEvent.LOADER_ERROR, onMaterialLoaderError, false, 0, true);
    
            _materialLoader.addEventListener(Event.COMPLETE, onAllLoaded, false, 0, true);
            _materialLoader.load(textureList, context);
            
            model = new TileSetModel(textureList);
            field = new TextField();
            field.x = 64;
            addChild(field);
            
            var wrapper:Sprite = _applyAllButton;
            var applyAllField:TextField = new TextField();
            applyAllField.autoSize = "left";
            applyAllField.selectable = false;
            applyAllField.text = "Apply All";
            _applyAllButton.visible = false;

            wrapper.addChild(applyAllField);
            wrapper.mouseChildren = false;
            wrapper.addEventListener(MouseEvent.CLICK, onApplyAllClick, false , 0, true);
            wrapper.buttonMode = true;
            wrapper.addChild(applyAllField);
            addChild(wrapper);
            wrapper.x = 32;
            wrapper.y = 16;
        }
        
        private var field:TextField;
        private function onLoadProgress(e:LoaderProgressEvent):void 
        {
            
            field.text = e.filesLoaded + "/" +e.filesTotal;
        }
        
        private function onMaterialLoaderError(e:LoaderErrorEvent):void 
        {
            
        }
        
        private function onAllLoaded(e:Event):void 
        {
            loadComplete();
        }
        
    
        private function onPartLoadComplete(e:LoaderEvent):void 
        {
            addPart(e.currentPart);
        }
        
        //private var _firstPart:Boolean = true;
        private function addPart(currentPart:int):void 
        {
            var textureMat:TextureMaterial = textureList[currentPart];
            var index:int;
            var textureDefs:Vector.<TextureDefinition> = model.orderedUniqueTextureDefs;
            if ( (index=model.orderedUniqueTextures.indexOf(textureMat)) != -1 ) {
                var child:DisplayObject = addChild( new Tile(textureMat.texture, textureDefs[index]) );
                child.addEventListener(MouseEvent.CLICK, onItemClick, false , 0, true);
                child.y = index * (child.height + 4);
            }
            /*
            else {
                if (_firstPart) {
                    addChild( TextureDefinition.previewTransforms(textureMat.texture) );
                    _firstPart = false;
                }
            }
            */    
        }
        
        private var _currentTile:Tile;
        private var _applyAllButton:Sprite = new Sprite();
        private function onItemClick(e:MouseEvent):void 
        {
            if (_currentTile) _currentTile.selected = false;
            _currentTile = (e.currentTarget as Tile);
            _currentTile.selected = true;
            model.selectedTexture = curTextureSelected;
            _applyAllButton.visible = true;
        }
    
        
        private function loadComplete():void {
        
            field.text = "Load done.";
            dispatchEvent( new Event(Event.COMPLETE) );
        }
        
        public function get curTextureSelected():TextureDefinition { return _currentTile._def; }
        
        
    }

//}

import flash.display.BitmapData;
import flash.display.Shape;
import flash.display.Sprite;
import flash.geom.Matrix;

//internal
class Tile extends Sprite {
    private static const SCALE:Matrix = new Matrix();
    public var _def:TextureDefinition;
    private var outline:Shape = new Shape();
    private var _selected:Boolean;
    
    public function Tile(bmpData:BitmapData, def:TextureDefinition) {
        this._def = def;
        buttonMode = true;
        SCALE.identity();
        SCALE.scale(.25, .25);
        bmpData = bmpData.clone();
        
        graphics.beginBitmapFill(bmpData, SCALE, false, true);
        graphics.drawRect(0, 0, 32,32);
        addChild(outline);
        outline.graphics.lineStyle(1, 0xFFFFFF, 1);
        outline.graphics.drawRect(0, 0, 32, 32);
        
        mouseChildren = false;
    }
    
    public function get selected():Boolean { return _selected; }
    
    public function set selected(value:Boolean):void 
    {
        _selected = value;
        outline.graphics.clear();
        outline.graphics.lineStyle(1, value ? 0xFF0000 : 0xFFFFFF, 1);
        outline.graphics.drawRect(0, 0, 32, 32);
        mouseEnabled = !value;
    }
    
}



//package utils.terrain 
//{
    import alternativ7.engine3d.alternativa3d;
    import alternativ7.engine3d.core.Camera3D;
    import alternativ7.engine3d.core.Canvas;
    import alternativ7.engine3d.core.Debug;
    import alternativ7.engine3d.core.Face;
    import alternativ7.engine3d.core.RayIntersectionData;
    import alternativ7.engine3d.objects.Mesh;
    import flash.display.BitmapData;
    import flash.geom.Vector3D;
    import flash.utils.Dictionary;
    use namespace alternativa3d;
    /**
     * A terrain grid mesh for editing and tileset painting. 
     * @author Glenn Ko
     */
//    public 
    class TerrainGridPainter extends Mesh
    {
        private var _aabb:Bounds3D = new Bounds3D();
        
        private var _patchMap:Vector.<Vector.<Patch>>;
        public var patchList:Vector.<Patch>;
        public var pi:int = 0;
        public var curPatch:Patch;
        
        public function TerrainGridPainter() 
        {
            
        }
        
        public function buildFromBitmapData(bmpData:BitmapData, tileWidth:int, tileHeight:int):void {
            
        }

        
        /**
         * Generates editable terrain patches from an already existing mesh. The mesh must
         * consist purely of ordered triangulated faces per square patch. (ie. triangulated along diagonal
         * of quad). It is assumed each patch is of the same grid size.
         * @param    mesh
         */
        public function buildFromMesh(mesh:Mesh):void {
            
            faceList = mesh.faceList;
            vertexList = mesh.vertexList;

            buildFromFaceList(mesh.faces);
        }

        public function buildFromFaceList(faceArray:Vector.<Face>):void {
            var i:int;

            // Generate patch list and entire terrain bounds
            var patch:Patch;
            var len:int = faceArray.length;
            patchList = new Vector.<Patch>(len>>1, true );
            for (i = 0 ; i < len; i += 2) {
                patch = new Patch( faceArray[i], faceArray[i + 1]);
                patchList[i >> 1] = patch;
                _aabb.union(patch.aabb);
            }
            _aabb.writeTo(this);
            
            // Create and fill up patch map from patch list
            patch = patchList[0];
            var w:Number = boundMaxX - boundMinX;
            var h:Number = boundMaxY - boundMinY;
            var tw:Number = patch.aabb.getWidth();
            var th:Number = patch.aabb.getHeight();
            var numXTiles:int = w / tw;
            var numYTiles:int = h / th;
            
            _patchMap = new Vector.<Vector.<Patch>>(numYTiles, true);
            for (var v:int = 0; v < numYTiles; v++) {
                _patchMap[v] = new Vector.<Patch>(numXTiles, true);
            }
            tw = 1 / tw;
            th = 1 / th;
            len  = patchList.length;
            for (i = 0; i < len; i++) {
                patch = patchList[i];
                patch.xi = (patch.aabb.boundMinX - boundMinX) * tw;
                patch.yi = (patch.aabb.boundMinY - boundMinY) * th;
                _patchMap[patch.yi][patch.xi] = patch;
            }
            
            // validate patch map
            ///*
            pi = 0;
            
            for (v = 0; v < numYTiles; v++) {
                for (i = 0 ; i < numXTiles; i++) {
                    if (_patchMap[v][i] == null) throw new Error("Patch map not filled completely!");
                    patchList[pi++] = _patchMap[v][i];
                }
            }
            pi = 0;
            //*/
            
            /*
            for (var ver:Vertex = vertexList; ver = ver.next; ver != null) {
                ver.u = (ver.x - boundMinX) * tw+0.0025;
                ver.v = -(ver.y - boundMaxY) * th+0.0025;
                
            }
            */
            
        }
        
        
        override alternativa3d function draw(camera:Camera3D, parentCanvas:Canvas):void {
            var canvas:Canvas  = parentCanvas.getChildCanvas(true, false, this, alpha, blendMode, colorTransform, filters);
                    
            super.draw(camera, parentCanvas);
    
            var patch:Patch = curPatch; // patchList[pi];
            if (patch == null) return;;
            var aabb:Bounds3D = patch.aabb;
            Debug.drawBounds(camera, canvas, this, aabb.boundMinX, aabb.boundMinY, aabb.boundMinZ, aabb.boundMaxX, aabb.boundMaxY, aabb.boundMaxZ, 0xFF0000, 1);
            
        }
        
        private var _appliedAllReady:Boolean = false;
        
        public function applyAll(model:TileSetModel):void {
            if (model.selectedTexture != null) {
                var targTexture:TextureDefinition = model.selectedTexture;
                var numYTiles:int = _patchMap.length;
                var numXTiles:int = _patchMap[0].length;
                var v:int; var i:int;
                for (v = 0; v < numYTiles; v++) {
                    for (i = 0 ; i < numXTiles; i++) {
                        _patchMap[v][i].textureDef = targTexture;
                    }
                }
                _appliedAllReady = true;
            }
        }
        
        public var excludedObjects:Dictionary = null;
        private static const requestPatchTypes:Vector.<int> = new Vector.<int>(4);
        private static const neighbourPaints:Vector.<PaintCommand> = new Vector.<PaintCommand>(8, true);
        
        public function paintUsingRay(origin:Vector3D, direction:Vector3D, model:TileSetModel):RayIntersectionData {
            if (!_appliedAllReady) return null;
            
            var data:RayIntersectionData = intersectRay(origin, direction, excludedObjects);
            
            if (data != null) {
                var request:Vector.<int>  = requestPatchTypes;
                var selDef:TextureDefinition = model.selectedTexture;
                if (selDef == null) return data;
                var patch:Patch = data.face.id as Patch;
                if (patch == null) return data;
                
                var type:int = selDef.identityTypeLayout[0];
                var xer:int = patch.xi;
                var yer:int = patch.yi;
                var xLen:int = _patchMap[0].length;
                var yLen:int = _patchMap.length;
                var tx:int;
                var ty:int;
                var applyDef:TextureDefinition;
                var layout:Vector.<int>;
                
                var count:int = 0;
            
                // South bound
                tx = xer - 1;
                ty = yer - 1;
                if (tx >= 0 && ty >= 0) {
                    patch = _patchMap[ty][tx]; 
                    layout = patch.textureDef.identityTypeLayout;
                    request[0] = layout[0];
                    request[1] = type;
                    request[2] = layout[2];
                    request[3] = layout[3];
                    applyDef = model.getDefinitionByCombination(request);
                    if (applyDef) {
                        neighbourPaints[count++] = new PaintCommand(patch, applyDef);
                        //patch.textureDef = applyDef;
                    }
                    
                }
                
                tx = xer;
                ty = yer - 1;
                if (ty >= 0) {
                    patch = _patchMap[ty][tx]; 
                    layout = patch.textureDef.identityTypeLayout;
                    request[0] = type;
                    request[1] = type;
                    request[2] = layout[2];
                    request[3] = layout[3];
                    applyDef = model.getDefinitionByCombination(request);
                    if (applyDef) {
                        neighbourPaints[count++] =  new PaintCommand(patch, applyDef);
                        //patch.textureDef = applyDef;
                    }
                }
                
                tx = xer + 1;
                ty = yer - 1;
                if (tx < yLen &&ty >=0) {
                    patch = _patchMap[ty][tx]; 
                    layout = patch.textureDef.identityTypeLayout;
                    request[0] = type;
                    request[1] = layout[1];
                    request[2] = layout[2];
                    request[3] = layout[3];
                    applyDef = model.getDefinitionByCombination(request);
                    if (applyDef) {
                        neighbourPaints[count++] =  new PaintCommand(patch, applyDef);
                        //patch.textureDef = applyDef;
                    }
                }
                
                // West to East
                tx = xer - 1;
                ty = yer;
                if (tx >= 0) {
                    patch = _patchMap[ty][tx]; 
                    layout = patch.textureDef.identityTypeLayout;        
                    request[0] = layout[0];
                    request[1] = type;
                    request[2] = layout[2];
                    request[3] = type;
                    applyDef = model.getDefinitionByCombination(request);
                    if (applyDef) {
                        neighbourPaints[count++] = new PaintCommand(patch, applyDef);
                        //patch.textureDef = applyDef;
                    }
                }
                
                tx =xer  + 1;
                ty = yer;
                if ( tx < xLen) {
                    patch = _patchMap[ty][tx]; 
                    layout = patch.textureDef.identityTypeLayout;        
                    request[0] = type;
                    request[1] = layout[1];
                    request[2] = type;
                    request[3] = layout[3];
                    applyDef = model.getDefinitionByCombination(request);
                    if (applyDef) {
                        neighbourPaints[count++] = new PaintCommand(patch, applyDef);
                        //patch.textureDef = applyDef;
                    }                
                }
                
                // North bound
                tx =xer - 1;
                ty =yer + 1;
                if (tx >= 0 && ty < yLen) {
                    patch = _patchMap[ty][tx]; 
                    layout = patch.textureDef.identityTypeLayout;        
                    request[0] = layout[0];
                    request[1] = layout[1];
                    request[2] = layout[2];
                    request[3] = type;
                    applyDef = model.getDefinitionByCombination(request);
                    if (applyDef) {
                        neighbourPaints[count++] = new PaintCommand(patch, applyDef);
                        //patch.textureDef = applyDef;
                    }                        
                }
                
                tx =xer
                ty =yer + 1;
                if (ty < yLen) {
                    patch = _patchMap[ty][tx]; 
                    layout = patch.textureDef.identityTypeLayout;        
                    request[0] = layout[0];
                    request[1] = layout[1];
                    request[2] = type;
                    request[3] = type;
                    applyDef = model.getDefinitionByCombination(request);
                    if (applyDef) {
                        neighbourPaints[count++] =new PaintCommand(patch, applyDef);
                        //patch.textureDef = applyDef;
                    }                
                }
                
                tx =xer + 1;
                ty =yer + 1;
                if (tx < xLen && ty < yLen) {
                    patch = _patchMap[ty][tx]; 
                    layout = patch.textureDef.identityTypeLayout;        
                    request[0] = layout[0];
                    request[1] = layout[1];
                    request[2] = type;
                    request[3] = layout[3];
                    applyDef = model.getDefinitionByCombination(request);
                    if (applyDef) {
                        neighbourPaints[count++] = new PaintCommand(patch, applyDef);
                        //patch.textureDef = applyDef;
                    }        
                }
                
                
                if (count == 8) {
                    patch = data.face.id as Patch;
                patch.textureDef = selDef;
                    for (var i:int = 0; i < 8; i++) {
                        neighbourPaints[i].execute();
                    }
                }
                
            }
            return data;
        }

    }

    
//}



//package utils.terrain 
//{
    import alternativ7.engine3d.core.Object3D;
    import flash.geom.Vector3D;
    /**
     * Basic app wrapper to hold bounds 3d information.
     * @author Glenn Ko
     */
    //public 
    class Bounds3D
    {
        public var boundMinX:Number =  1.79e+308;
        public var boundMaxX:Number =  -1.79e+308;
        
        public var boundMinY:Number =  1.79e+308;
        public var boundMaxY:Number = -1.79e+308;
        
        public var boundMinZ:Number =  1.79e+308;
        public var boundMaxZ:Number =  -1.79e+308;
        
        public function Bounds3D() {
            
        }
        
        public function writeTo(obj:Object3D):void {
            obj.boundMinX = boundMinX;
            obj.boundMinY = boundMinY;
            obj.boundMinZ = boundMinZ;
            
            obj.boundMaxX = boundMaxX;
            obj.boundMaxY = boundMaxY;
            obj.boundMaxZ = boundMaxZ;
        }
        
        public function isValid():Boolean {
            return !( isNaN(boundMaxX) || isNaN(boundMaxY) || isNaN(boundMaxZ) || isNaN(boundMinX) || isNaN(boundMinY) || isNaN(boundMinZ) );
        }
        
                
        public function getWidth():Number 
        {
            return boundMaxX - boundMinX;
        }
        public function getHeight():Number 
        {
            return boundMaxY - boundMinY;
        }
        
        public function calculateCenter(output:Vector3D):void {
            output.x = boundMinX + (boundMaxX - boundMinX) * .5;
            output.y = boundMinY + (boundMaxY - boundMinY) * .5;
            output.z = boundMinZ + (boundMaxZ - boundMinZ) * .5;
        }
        
        public function union(target:Bounds3D):void {
            if (target.boundMinX < boundMinX) boundMinX = target.boundMinX;
            if (target.boundMaxX > boundMaxX) boundMaxX = target.boundMaxX;
            if (target.boundMinY < boundMinY) boundMinY = target.boundMinY;
            if (target.boundMaxY > boundMaxY) boundMaxY = target.boundMaxY;
            if (target.boundMinZ < boundMinZ) boundMinZ = target.boundMinZ;
            if (target.boundMaxZ > boundMaxZ) boundMaxZ = target.boundMaxZ;
        }
        
        public function toString():String {
            return  "Bounds3D:: min/max: "+new Vector3D(boundMinX, boundMinY, boundMinZ) + ", "+ new Vector3D(boundMaxX, boundMaxY, boundMaxZ);
        }
    }

//}

//package utils.terrain 
//{
    import alternativ7.engine3d.core.Face;
    import alternativ7.engine3d.core.Vertex;

    use namespace alternativa3d;
    /**
     * A patch is a square tile definition consisting of 2 triangular faces
     * @author Glenn Ko
     */
    //public 
    class Patch
    {
        public var tri1:Face;
        public var tri2:Face;
        public var aabb:Bounds3D;
        // tile index
        public var yi:int;
        public var xi:int;
        
        private var _textureDef:TextureDefinition;
        
        public function Patch(tri1:Face, tri2:Face) 
        {
            this.tri1 = tri1;
            this.tri2 = tri2;
            tri1.id = this;
            tri2.id = this;
            aabb = new Bounds3D();
            calculateAABBForTri(tri1);
            calculateAABBForTri(tri2);    
            tri1.wrapper = FaceMacros.deepCloneWrapperAndVertices(tri1.wrapper);
            tri2.wrapper = FaceMacros.deepCloneWrapperAndVertices(tri2.wrapper);

        }
        
        public function calculateUVs():void {
            var v:Vertex;
            v = tri1.wrapper.vertex;
            v.u = int(v.x- aabb.boundMinX) >> 7; 
            v.v = int(v.y - aabb.boundMinY) >> 7;  v.v = 1 - v.v;
            
            v = tri1.wrapper.next.vertex;
            v.u = int(v.x- aabb.boundMinX) >> 7;
            v.v = int(v.y- aabb.boundMinY) >> 7; v.v = 1 - v.v;
            
            v = tri1.wrapper.next.next.vertex;
            v.u = int(v.x- aabb.boundMinX) >> 7;
            v.v = int(v.y - aabb.boundMinY) >> 7; v.v = 1 - v.v;
            
            
            v = tri2.wrapper.vertex;
            v.u = int(v.x- aabb.boundMinX) >> 7; 
            v.v = int(v.y - aabb.boundMinY) >> 7;  v.v = 1 - v.v;
            
            v = tri2.wrapper.next.vertex;
            v.u = int(v.x- aabb.boundMinX) >> 7;
            v.v = int(v.y- aabb.boundMinY) >> 7; v.v = 1 - v.v;
            
            v = tri2.wrapper.next.next.vertex;
            v.u = int(v.x- aabb.boundMinX) >> 7;
            v.v = int(v.y- aabb.boundMinY) >> 7; v.v = 1 - v.v;

        }

        
        private function calculateAABBForTri(tri:Face):void {
            var xer:Number;
            var v:Vertex;
            
            v = tri.wrapper.vertex;
            xer = v.x;
            if (xer < aabb.boundMinX)  aabb.boundMinX = xer;
            if (xer > aabb.boundMaxX)  aabb.boundMaxX = xer; 
            xer = v.y;
            if (xer < aabb.boundMinY)  aabb.boundMinY = xer;
            if (xer > aabb.boundMaxY)  aabb.boundMaxY = xer;
            xer = v.z;
            if (xer < aabb.boundMinZ)  aabb.boundMinZ = xer;
            if (xer > aabb.boundMaxZ)  aabb.boundMaxZ = xer;
            
            v = tri.wrapper.next.vertex;
            xer = v.x;
            if (xer < aabb.boundMinX)  aabb.boundMinX = xer;
            if (xer > aabb.boundMaxX)  aabb.boundMaxX = xer;
            xer = v.y;
            if (xer < aabb.boundMinY)  aabb.boundMinY = xer;
            if (xer > aabb.boundMaxY)  aabb.boundMaxY = xer;
            xer = v.z;
            if (xer < aabb.boundMinZ)  aabb.boundMinZ = xer;
            if (xer > aabb.boundMaxZ)  aabb.boundMaxZ = xer;
            
            v = tri.wrapper.next.next.vertex;
            xer = v.x;
            if (xer < aabb.boundMinX)  aabb.boundMinX = xer;
            if (xer > aabb.boundMaxX)  aabb.boundMaxX = xer;
            xer = v.y;
            if (xer < aabb.boundMinY)  aabb.boundMinY = xer;
            if (xer > aabb.boundMaxY)  aabb.boundMaxY = xer;
            xer = v.z;
            if (xer < aabb.boundMinZ)  aabb.boundMinZ = xer;
            if (xer > aabb.boundMaxZ)  aabb.boundMaxZ = xer;
            
            
            calculateUVs();
        }
        
        public function get textureDef():TextureDefinition { return _textureDef; }
        
        public function set textureDef(value:TextureDefinition):void 
        {
            _textureDef = value;
            tri1.material = _textureDef.material;
            tri2.material = _textureDef.material;
            _textureDef.material.repeat = false;
        }
        
        
        
    }

//}


//package portal 
//{
    import alternativ7.engine3d.core.Face;
    import alternativ7.engine3d.alternativa3d;
    import alternativ7.engine3d.core.Vertex;
    import alternativ7.engine3d.core.Wrapper;
    import alternativ7.engine3d.objects.Mesh;
    use namespace alternativa3d;
    /**
     * ...
     * @author Glenn Ko
     */
    //public 
    class FaceMacros
    {
        public static function deepCloneWrapperAndVertices(wrapper:Wrapper):Wrapper {
            var wrapperClone:Wrapper = wrapper.create();
            var v:Vertex = wrapper.vertex.create();
            v.x = wrapper.vertex.x;
            v.y = wrapper.vertex.y;
            v.z = wrapper.vertex.z;
            v.u = wrapper.vertex.u;
            v.v = wrapper.vertex.v;
            wrapperClone.vertex = v;

            var w:Wrapper = wrapper.next;
            var tailWrapper:Wrapper = wrapperClone;
            var wClone:Wrapper;
            while (w != null) {
                wClone = w.create();
                v =   w.vertex.create();
                v.x = w.vertex.x;
                v.y = w.vertex.y;
                v.z = w.vertex.z;
                v.u = w.vertex.u;
                v.v = w.vertex.v;
                wClone.vertex = v;
                tailWrapper.next = wClone;
                tailWrapper = wClone;
                w = w.next;
            }
            return wrapperClone;
        }

    }

//}

//package portal 
//{
    import alternativ7.engine3d.alternativa3d;
    import alternativ7.engine3d.materials.FillMaterial;
    import alternativ7.engine3d.materials.Material;
    import alternativ7.engine3d.primitives.Box;
    
    /**
     * ...
     * @author Glenn Ko
     */
    //public 
    class MarkerBox extends Box
    {
        
        public function MarkerBox(color:uint = 0x00FF00, width:Number = 10, length:Number = 10, height:Number = 10, widthSegments:uint = 1, lengthSegments:uint = 1, heightSegments:uint = 1, reverse:Boolean = false, triangulate:Boolean = false, left:Material = null, right:Material = null, back:Material = null, front:Material = null, bottom:Material = null, top:Material = null) 
        {
            var material:Material = new FillMaterial(color); 
            super(width, length, height, widthSegments, lengthSegments, heightSegments, reverse, triangulate, material, material, material, material, material, material);
            
        }
    
    }
//}