Alternativa3D 7 - Loading from Base64

by Glidias
De-serialization test for entire online A3d7 scene.

To save/load any 3d scene to/from a ByteArray, use the A3D7SceneSerializer class. 

The accompanying package can be re-factored for general purposes (removing a3d7 dependencies). Currently, it helps to handle public read&write accessors or public variable from any objects, without requiring the objects to implement IExternalizable boiler-plate.

You just need the properties of serializable objects to be made public and it's non-primitive data types to be registered under custom read/write methods, if required, depending on your application needs. 
♥0 | Line 904 | Modified 2012-10-23 16:11:47 | MIT License
play

ActionScript3 source code

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

package  {
    
    import alternativ7.engine3d.containers.ConflictContainer;
    import alternativ7.engine3d.containers.DistanceSortContainer;
    import alternativ7.engine3d.controllers.SimpleObjectController;
    import alternativ7.engine3d.core.Camera3D;
    import alternativ7.engine3d.core.Debug;
    import alternativ7.engine3d.core.Object3D;
    import alternativ7.engine3d.core.Object3DContainer;
    import alternativ7.engine3d.core.Sorting;
    import alternativ7.engine3d.core.View;
    import alternativ7.engine3d.loaders.Parser3DS;
    import alternativ7.engine3d.materials.FillMaterial;
    import alternativ7.engine3d.materials.TextureMaterial;
    import alternativ7.engine3d.objects.Mesh;
    import flash.text.TextField;
    import flash.utils.ByteArray;
    
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.ui.Keyboard;
    
    /**
     * Base64 de-serialization test for entire online A3d7 scene.
     */
    public class A3d7Base64DeSerialization extends Sprite {

        private var rootContainer:Object3DContainer;
        private var camera:Camera3D;
        private var controller:SimpleObjectController;
        
        public function A3d7Base64DeSerialization() {
            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 = -130*Math.PI/180;
            camera.y = -250;
            camera.z = 250;
            controller = new SimpleObjectController(stage, camera, 200);
            
            
            setupScene();
            
            // Debug mode
            // Режим отладки
            camera.debug = true;
            camera.addToDebug(Debug.EDGES, Object3D);
            camera.addToDebug(Debug.BOUNDS, Object3D);
            
            // Listeners
            // Подписка на события

            stage.addEventListener(Event.RESIZE, onResize);
            
            // Let's begin!
            run();

        }
        
        private function setupScene():void 
        {
            // Register expected classes:
            
            // register PublicMesh and other serializables!
            RegisterAliases.check();    
            // what objects are we expecting?
            RegisterAliases.registerClass(Camera3D);
            RegisterAliases.registerClass(Object3D);
            RegisterAliases.registerClass(Object3DContainer);
            // what type of container classes are we expecting??
            RegisterAliases.registerClass(DistanceSortContainer);
            RegisterAliases.registerClass(ConflictContainer);
            // what type of materials could we be expecting?
            RegisterAliases.registerClass(FillMaterial);
            RegisterAliases.registerClass(TextureMaterial);
            
            var sceneLoader:A3D7SceneSerializer = new A3D7SceneSerializer();
            var bytes:ByteArray = Base64.decodeToByteArray(base64);
            bytes.uncompress();
            sceneLoader.readExternal(bytes);

            sceneLoader.scene.addChild(camera);
            
            sceneLoader.materials.loadTextureMaterials();
        }
        
    
        
        private function run():void 
        {
            stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
            stage.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
        }
        
        private function saveScene():ByteArray {
            var byteArray:ByteArray = new ByteArray();
            
            var sceneSerializer:A3D7SceneSerializer = new A3D7SceneSerializer(rootContainer);
            sceneSerializer.writeExternal(byteArray);
            
            byteArray.position = 0;
            byteArray.compress();
        
            
            return byteArray;
        }
        
        private function onEnterFrame(e:Event):void {
            
            //distanceSortContainer.rotationZ += 0.002;
            //conflictContainer.rotationZ += 0.002;
            
            controller.update();
            camera.render();
        }
        
        private function onResize(e:Event = null):void {
            camera.view.width = stage.stageWidth;
            camera.view.height = stage.stageHeight;
        }
        
        private function onKeyDown(e:KeyboardEvent):void {
            if (e.keyCode == Keyboard.TAB) {
                camera.debug = !camera.debug;
            }
            if (e.keyCode == 81) { // Q
                if (stage.quality == "HIGH") {
                    stage.quality = "LOW";
                } else {
                    stage.quality = "HIGH";
                }
            }
        }
        
private var base64:String  = "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"     
    }
    
}

    import alternativ7.engine3d.core.Object3D;
    import alternativ7.engine3d.core.Object3DContainer;
    import alternativ7.engine3d.objects.Mesh;
    import alternativ7.engine3d.objects.Sprite3D;
    import flash.utils.describeType;
    import flash.utils.getQualifiedClassName;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.utils.IExternalizable;
    import alternativ7.engine3d.alternativa3d;
    use namespace alternativa3d;
    /**
     * Binary scene serializer for A3D 7 onwards. Saves out entire scene in binary format which can be loaded again!
     * @author Glenn Ko
     */
    //public 
    class A3D7SceneSerializer implements IExternalizable
    {
        public var scene:Object3DContainer;
        public var materials:MaterialList;
        
        
        public function A3D7SceneSerializer(scene:Object3DContainer=null) 
        {
            this.scene = scene;
            RegisterAliases.check();
        }
        
        /* INTERFACE flash.utils.IExternalizable */
        
        public function writeExternal(output:IDataOutput):void 
        {
            // save out material list
            var matList:MaterialList = new MaterialList();
            MaterialList.current = matList;
            this.materials = matList;
            matList.collectMaterialsFromScene(scene);

            output.writeObject(matList);
            
            // save out class list of entire scene (reflection)
            var reflectMap:ReflectReadWrite = new ReflectReadWrite();
            ReflectReadWrite.current = reflectMap;
            describeObject3D(scene, reflectMap);
            describeContainer3D(scene, reflectMap);
            
            output.writeObject(reflectMap);
            
            // save out scene and it's descendant objects
            output.writeObject( new ExternalizableWrapper(scene) );
            writeContainer3D(scene, output);
        }
        
        public function readExternal(input:IDataInput):void 
        {
            MaterialList.current = this.materials = input.readObject();
            ReflectReadWrite.current = input.readObject();

            scene = (input.readObject() as ExternalizableWrapper).object;
            fillContainer(scene, input);
            
            
        }
        
        // -- Handy methods
        
        private function describeObject3D(obj:Object3D, reflectMap:ReflectReadWrite):void {
                //if (!reflectMap.cachedPropertyMaps[getQualifiedClassName(obj)]) {
                    var propMap:AbstractOrderedObject = reflectMap.propertyMap(obj);
                    
                    // this can be improved, (why not use "hasOwnProperty"??);
                    if (obj is Mesh) {  // only Mesh? may not be the case in the future...
                        propMap["vertexHeader"] = "vertexList";
                        propMap["faceHeader"] = "faceList";
                    }
                    else if (obj is Sprite3D) {
                        propMap["material"] = "material";
                    }
                //}
        
        }
        
        //recursive depth first traversal (consider: iterative..may be better in performance as it won't go so deep)
        private function describeContainer3D(cont:Object3DContainer, reflectMap:ReflectReadWrite):void {
            var lastObj:Object3D;
            for (var obj:Object3D = cont.childrenList; obj != null; obj = nextObj) {
                var nextObj:Object3D = obj.next;
                // Extended custom Mesh classes  must either extend from PublicMesh or implement IPublicMesh!
                if (obj is Mesh && !(obj is IPublicMesh) ) {  // do mesh replacement if required! 
                    obj._parent = null;
                    obj.next = null;
                    obj = PublicMesh.fromExistingMesh(obj as Mesh);
                    obj.next = nextObj;
                    obj._parent = cont;
                    if (lastObj != null) {
                        lastObj.next = obj;
                    }
                    else {
                        cont.childrenList = obj;
                    }
                }    
                describeObject3D(obj, reflectMap);
                
                if (obj is Object3DContainer) {
                    describeContainer3D(obj as Object3DContainer, reflectMap);
                }
                lastObj = obj;
            }
        }
        
        private function writeContainer3D(cont:Object3DContainer, output:IDataOutput):void {
            output.writeShort( cont.numChildren );
            for (var obj:Object3D = cont.childrenList; obj != null; obj = obj.next) {
                output.writeObject( new ExternalizableWrapper(obj) );
                if (obj is Object3DContainer) {
                    writeContainer3D(obj as Object3DContainer, output);
                }
            }
        }
        
        
        private function fillContainer(cont:Object3DContainer, input:IDataInput):void {
            var i:int = input.readShort();
            while (--i > -1) {
                var child:Object3D = cont.addChild( (input.readObject() as ExternalizableWrapper).object  );
                child.calculateBounds();
                var mesh:Mesh = child as Mesh;
                if (mesh != null) {
                    mesh.calculateFacesNormals();
                    mesh.calculateVerticesNormals();
                }
                if (child is Object3DContainer) {
                    fillContainer(child as Object3DContainer, input);
                }
            }
        }
        

        
    }

    /**
 * <p>Original Author:  jessefreeman</p>
 * <p>Class File: AbstractOrderedObject.as</p>
 *
 * <p>Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:</p>
 *
 * <p>The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.</p>
 *
 * <p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.</p>
 *
 * <p>Licensed under The MIT License</p>
 * <p>Redistributions of files must retain the above copyright notice.</p>
 *
 * <p>Revisions<br/>
 *        1.0.0  Initial version Feb 11, 2010</p>
 *
 */


    import flash.errors.IllegalOperationError;
    import flash.net.registerClassAlias;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.utils.IExternalizable;
    import flash.utils.Proxy;
    import flash.utils.flash_proxy;

    /**
     * @author jessefreeman
     */
    dynamic class AbstractOrderedObject extends Proxy //implements IExternalizable
    {

        protected var properties:Object = new Object();

        protected var propertiesIndex:Array = new Array();

        /**
         *
         * <p>AbstractOrderedObject is a special object that keeps an ordered
         * list of each property added to it to allow ordered looping through
         * it's values. This assures that the order of the values set will be
         * correctly returned in the order expected.</p>
         *
         * @param properties
         * @param propertiesIndex
         *
         */
        public function AbstractOrderedObject()
        {
           
        

        }

        /**
         *
         * <p>Merges the properties of one group of properties with the current
         * instances values. The supplied properties have a higher importance and
         * will override any properties with the same name.</p>
         *
         * <p>It is important to note that this is a 1 to 1 copy so Arrays, Object
         * and other "complex" property values will not be cleanly copied over.
         * This is meant to be used when you know a merge is ok to have references
         * to the actualy object getting merged in.</p>
         *
         * @param style
         *
         */
        public function merge(object:Object):void
        {
            for (var prop:String in object)
            {
                this[prop] = object[prop];
            }
        }

        /**
         * @private
         */
        public function toString():String
        {
            var styleString:String = "{";
            var i:int;
            var total:int = propertiesIndex.length;
            var prop:String;
            for (i = 0; i < total; i++)
            {
                prop = propertiesIndex[i];
                styleString = styleString.concat(prop, ":", properties[prop].toString(), ";");
            }

            styleString = styleString.concat("}");

            return styleString;
        }

        /**
         * @private
         */
        protected function $deleteProperty(name:*):Boolean
        {
            var wasDeleted:Boolean = delete properties[name];
            if (wasDeleted)
            {
                propertiesIndex.splice(propertiesIndex.indexOf(name), 1);
            }
            return wasDeleted;
        }

        /**
         * @private
         *
         * @return
         *
         */
        protected function $setProperty(name:*, value:*):void
        {
            if (!properties.hasOwnProperty(name))
            {
                propertiesIndex.push(name.toString());
            }

            properties[name] = value;

        }

       

        /**
         *
         * @private
         *
         * @param name
         * @return
         *
         */
        flash_proxy override function deleteProperty(name:*):Boolean
        {
            return $deleteProperty(name);
        }

        /**
         *
         * @private
         *
         * @param name
         * @return
         *
         */
        flash_proxy override function getProperty(name:*):*
        {
            return properties[name];
        }

        /**
         *
         * @private
         *
         * @param name
         * @return
         *
         */
        flash_proxy override function hasProperty(name:*):Boolean
        {
            return properties.hasOwnProperty(name);
        }

        /**
         *
         * @private
         *
         * @param index
         * @return
         *
         */
        flash_proxy override function nextName(index:int):String
        {
            return propertiesIndex[index - 1];
        }

        /**
         *
         * @private
         *
         * @param index
         * @return
         *
         */
        flash_proxy override function nextNameIndex(index:int):int
        {
            if (index < propertiesIndex.length)
                return index + 1;
            else
                return 0;
        }

        /**
         *
         * @private
         *
         * @param index
         * @return
         *
         */
        flash_proxy override function nextValue(index:int):*
        {
            return properties[propertiesIndex[index - 1]];
        }

        /**
         *
         * @private
         *
         * @param name
         * @param value
         *
         */
        flash_proxy override function setProperty(name:*, value:*):void
        {
            $setProperty(name, value);
        }
        

    }

    /*
Base64 - 1.1.0

Copyright (c) 2006 Steve Webster

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions: 

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/


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

        public static const version:String = "1.1.0";

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

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



    import flash.utils.ByteArray;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.utils.IExternalizable;
    
    import alternativ7.engine3d.core.Face;
    import alternativ7.engine3d.core.Vertex;
    import alternativ7.engine3d.objects.Mesh;
    import alternativ7.engine3d.core.Wrapper;
    import alternativ7.engine3d.materials.Material;
    import alternativ7.engine3d.alternativa3d;
    use namespace alternativa3d;
    /**
     * ...
     * @author Glenn Ko
     */
    class DataIOProxy
    {
        public var input:IDataInput;
        public var output:IDataOutput;
        
        public function DataIOProxy(input:IDataInput, output:IDataOutput) 
        {
            setInputOutput(input, output);
        }
        
        public function setInputOutput(input:IDataInput, output:IDataOutput):void 
        {
            this.input = input;
            this.output = output;
        }
        
        public function setInput(input:IDataInput):void {
            this.input = input;
        }
        public function setOutput(output:IDataOutput):void {
            this.output = output;
        }
        
        /* flash.utils.IDataInput */
        
        public function readBoolean():Boolean 
        {
            return input.readBoolean();
        }
        
        public function readByte():int 
        {
            return input.readByte();
        }
        
        public function readDouble():Number 
        {
            return input.readDouble();
        }
        
        public function readFloat():Number 
        {
            return input.readFloat();
        }
        
        public function readInt():int 
        {
            return input.readInt();
        }
        
        public function readObject():* 
        {
            return input.readObject();
        }
        
        public function readShort():int 
        {
            return input.readShort();
        }
        
        public function readUnsignedByte():uint 
        {
            return input.readUnsignedByte();
        }
        
        public function readUnsignedInt():uint 
        {
            return input.readUnsignedInt();
        }
        
        public function readUnsignedShort():uint 
        {
            return input.readUnsignedShort();
        }
        
        public function readUTF():String 
        {
            return input.readUTF();
        }
        
        
        
        /* flash.utils.IDataOutput */
        
        public function writeBoolean(value:Boolean):void 
        {
            output.writeBoolean(value);
        }
        
        public function writeByte(value:int):void 
        {
            output.writeByte(value);
        }
        
        public function writeShort(value:int):void 
        {
            output.writeShort(value);
        }
        
        public function writeInt(value:int):void 
        {
            output.writeInt(value);
        }
        
        public function writeUnsignedInt(value:uint):void 
        {
            output.writeUnsignedInt(value);
        }
        
        public function writeFloat(value:Number):void 
        {
            output.writeFloat(value);
        }
        
        public function writeDouble(value:Number):void 
        {
            output.writeDouble(value);
        }
        
        
        public function writeUTF(value:String):void 
        {
            output.writeUTF(value);
        }
        
        public function writeUTFBytes(value:String):void 
        {
            output.writeUTFBytes(value);
        }
        
        public function writeObject(object:*):void 
        {
            output.writeObject(object);
        }
        
        
        // A3D  7 specific stuff
        
        public function writeVertexList(vertexList:Vertex):void {    
            
            var len:int = 0;
            for (var v:Vertex = vertexList; v != null; v = v.next) {
                len++;
            }
            output.writeInt(len);
            
            len = 0;
            for (v = vertexList; v != null; v = v.next) {
                output.writeFloat(v.x);
                output.writeFloat(v.y);
                output.writeFloat(v.z);
                
                output.writeFloat(v.u);
                output.writeFloat(v.v);
                v.index = len++;
            }
        }
        
        private var lastVertexStream:Vector.<Vertex> = new Vector.<Vertex>();
        
        public function readVertexList():Vertex {
            var head:Vertex;
            var len:int = input.readInt();
            if (len == 0) return null;
            
            var v:Vertex = new Vertex();
            head = v;
            v.x = input.readFloat();
            v.y = input.readFloat();
            v.z = input.readFloat();
            v.u = input.readFloat();
            v.v = input.readFloat();
            lastVertexStream[0] = v;
            v.index = 0;
            var tail:Vertex = v;
            
            for (var i:int = 1; i < len; i++) {
                v = new Vertex();
                v.x = input.readFloat();
                v.y = input.readFloat();
                v.z = input.readFloat();
                v.u = input.readFloat();
                v.v = input.readFloat();
                tail.next = v;
                tail = v;
                v.index = i;
                lastVertexStream[i] = v;
            }
            
            DUMMY_MESH.vertexList = head;
            return head;
        }
        
        private static var DUMMY_MESH:Mesh = new Mesh();
        public function writeFaceList(faceList:Face):void {
            var f:Face;
            var count:int;
            var w:Wrapper;
            
            count = 0;
            for (f = faceList; f != null; f = f.next) {
                count++;
            }
            output.writeInt(count);
            
            for (f = faceList; f != null; f = f.next) {
                count = 0;
                for (w = f.wrapper; w != null; w = w.next) {
                    count++;
                }
                output.writeInt(count);
                
                for (w = f.wrapper; w != null; w = w.next) {
                    output.writeInt( w.vertex.index );
                }
                
                writeMaterial(f.material);
            }
            
        }
        
        private static const verticesToRefer:Vector.<Vertex> = new Vector.<Vertex>();
        
        public function readFaceList():Face {
            const DUMMY_MESH:Mesh = DUMMY_MESH;
            DUMMY_MESH.faceList = null;
            
            var uLen:int;
            var m:int;
            
            var len:int = readInt();
            for (var i:int = 0; i < len; i++) {
                verticesToRefer.length = uLen =  input.readInt();
                for (var u:int = 0 ; u < uLen; u++) {
                    verticesToRefer[u] = lastVertexStream[input.readInt()];
                }
                var f:Face = DUMMY_MESH.addFace(verticesToRefer);
                f.material  = readMaterial();
            }
            
            return DUMMY_MESH.faceList;
        }
        
        public function writeMaterial(mat:Material):void { 
            output.writeInt( mat!=null ? MaterialList.current.vec.indexOf(mat)  : -1 );
        }
        
        public function readMaterial():Material 
        {
            var m:int = input.readInt();
            return m >= 0 ? MaterialList.current.vec[m] : null;
        }
        
        
        
    }




    import flash.utils.getDefinitionByName;
    import flash.utils.getQualifiedClassName;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.utils.IExternalizable;
    /**
     * ...
     * @author Glenn Ko
     */
    class ExternalizableWrapper implements IExternalizable
    {
        private var _obj:Object;
        
        
        public function ExternalizableWrapper(obj:Object=null) 
        {
            _obj = obj;
            
        }
        
        /* INTERFACE flash.utils.IExternalizable */
        
        public function writeExternal(output:IDataOutput):void 
        {
            SerializableApplier.proxy.setOutput(output);
            const map:Object = SerializableApplier.MAP_WRITE;
            
            
            var index:int = ReflectReadWrite.current.classList.indexOf(getQualifiedClassName(_obj));
            if (index < 0) throw new Error("Valid class index not found in ReflectReadWrite class list!");
            output.writeInt( index );
            
            var orderedProps:AbstractOrderedObject = ReflectReadWrite.current.propertyMap(_obj);
            
            for (var prop:String in orderedProps) {
                
                var type:String = orderedProps[prop];
                if (map[type] != undefined) {
                
                    map[type]( _obj[prop] );
                }
                else {
                    output.writeObject( _obj[prop] );
                }
            }
        }
        
        public function readExternal(input:IDataInput):void 
        {
            SerializableApplier.proxy.setInput(input);
            const map:Object = SerializableApplier.MAP_READ;
            
            var index:int = input.readInt();

            // WonderFL hack to handle unpackaged classes (sob!)!!
            // I foresee if serializing KD/bsp tree , would need to resort to something like this!
            try {
                _obj = new (getDefinitionByName(ReflectReadWrite.current.classList[index]) as Class)();
            }
            catch (e:Error) {
                _obj =  new RegisterAliases.WONDER_FL[ReflectReadWrite.current.classList[index].split("::").pop()]();
            }
            
            var orderedProps:AbstractOrderedObject = ReflectReadWrite.current.propertyMaps[index];
            for (var prop:String in orderedProps) {
                var type:String = orderedProps[prop];
                _obj[prop] = map[type] != undefined ? map[type]() : input.readObject();
            }
        
        }
        
        public static function createList(arr:*):Vector.<ExternalizableWrapper> 
        {
            var len:int = arr.length;
            var vec:Vector.<ExternalizableWrapper> = new Vector.<ExternalizableWrapper>(len, true);
            for (var i:int = 0; i < len; i++) {
                vec[i] = new ExternalizableWrapper( arr[i] );
            }
            return vec;
        }
        
        public static function populateInto(vec:Vector.<ExternalizableWrapper>, arr:*):void {
            var len:int = vec.length;
            for (var i:int = 0; i < len; i++) {
                arr[i] = vec[i]._obj;
            }
        }
        
        public function get object():* 
        {
            return _obj;
        }
        
    }

    

    import alternativ7.engine3d.core.Face;
    import alternativ7.engine3d.core.Vertex;

    interface IPublicMesh 
    {
        function set vertexHeader(val:Vertex):void;
        function get vertexHeader():Vertex;
        function set faceHeader(val:Face):void;
        function get faceHeader():Face;
    }
    


    import alternativ7.engine3d.alternativa3d;
    import alternativ7.engine3d.core.EllipsoidCollider;
    import alternativ7.engine3d.core.Face;
    import alternativ7.engine3d.core.Object3DContainer;
    import alternativ7.engine3d.loaders.MaterialLoader;
    import alternativ7.engine3d.materials.Material;
    import alternativ7.engine3d.materials.TextureMaterial;
    import flash.geom.Vector3D;
    import flash.net.registerClassAlias;
    import flash.utils.Dictionary;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.utils.IExternalizable;
    use namespace alternativa3d;
    /**
     * A saved out material list for loading and indexed referencing.
     * @author Glenn Ko
     */
    class MaterialList implements IExternalizable
    {
        private var hash:Object;
        alternativa3d var vec:Vector.<Material>;
        alternativa3d static var current:MaterialList = new MaterialList();
        
        public var textureMaterials:Vector.<TextureMaterial>;
        public var materialLoader:MaterialLoader;

        
        public function MaterialList(list:Vector.<Material> = null) 
        {
            setList( list ||  new Vector.<Material>() );
            
            registerClassAlias("alternativa.engine3d.materials.Material", Material);
            
        }
        
        public function getMaterialByName(name:String):Material {
            return hash[name];
        }
        
        public function setList(list:Vector.<Material>):void {
            vec = list;
            hash = { };
            textureMaterials =  new Vector.<TextureMaterial>();
        
            var len:int = list.length;
            for (var i:int = 0; i < len; i++) {
                var mat:Material = list[i];
                if (mat is TextureMaterial) textureMaterials.push(mat);
                if ( hash[mat.name] == undefined ) {
                    hash[mat.name] = i;
                }
                //else {
                //    throw new Error("Material found in list with duplicate name!");
                //}
            }
        }
        
        public function loadTextureMaterials():void {
            if (textureMaterials.length == 0) return;
            materialLoader = new MaterialLoader();
        
            materialLoader.load(textureMaterials);
        }
        
        /* INTERFACE flash.utils.IExternalizable */
        
        public function writeExternal(output:IDataOutput):void 
        {
            
            var readWriteMap:ReflectReadWrite = new ReflectReadWrite();
            var i:int = vec.length;
            while (--i > -1) {
                readWriteMap.propertyMap(vec[i]);
            }
            
            // write
            output.writeObject( readWriteMap );
            ReflectReadWrite.current = readWriteMap;
            
            output.writeObject( ExternalizableWrapper.createList(vec) );
        }
        
        public function readExternal(input:IDataInput):void 
        {
            // read
            var readWriteMap:ReflectReadWrite = input.readObject();
            ReflectReadWrite.current = readWriteMap;
        
            ExternalizableWrapper.populateInto(input.readObject(), vec);
            setList(vec);
        }
        
        // Other useful methods
        
        private static const COLLIDER:EllipsoidCollider = new EllipsoidCollider(999999999, 999999999, 999999999);
        
        
        public function collectMaterialsFromScene(scene:Object3DContainer):void {
            var materialDict:Dictionary = new Dictionary();
            var dummyContainer:DummyContainer = new DummyContainer();
            dummyContainer.addChild(scene);
            
            
    
            COLLIDER.getCollision( new Vector3D(), new Vector3D(2, 2, 2), new Vector3D(), new Vector3D(), dummyContainer);
            var collector:Vector.<Face> = dummyContainer.collectedFaceList;
            
            for each(var f:Face in collector) {
                if (f.material != null) materialDict[f.material] = true;
            }
            
            // should give unique naming if required
            var list:Vector.<Material> = new Vector.<Material>();
            for (var m:* in materialDict) {
                list.push(m);
            }
            
            dummyContainer.collectedFaceList = null;
            dummyContainer.removeChild(scene);
            
            setList(list);
        }
        
    }


import alternativ7.engine3d.alternativa3d;
import alternativ7.engine3d.core.Face;
import alternativ7.engine3d.core.Object3DContainer;
import flash.geom.Vector3D;
import flash.utils.Dictionary;
use namespace alternativa3d;

class DummyContainer extends Object3DContainer {
    
    public var collectedFaceList:Vector.<Face>;
    
    override alternativa3d function collectPlanes(center:Vector3D, a:Vector3D, b:Vector3D, c:Vector3D, d:Vector3D, collector:Vector.<Face> , excludedObjects:Dictionary = null):void {
        
        super.collectPlanes(center, a, b, c, d, collector, excludedObjects);
        
        collectedFaceList = collector.concat();
    }
}


    import alternativ7.engine3d.core.Face;
    import alternativ7.engine3d.core.Vertex;
    import alternativ7.engine3d.objects.Mesh;
    import alternativ7.engine3d.alternativa3d;
    use namespace alternativa3d;
    /**
     * This is necessary i guess to provide public namespace access to setup vertices/faces directly!
     * @author ...
     */
    class PublicMesh extends Mesh implements IPublicMesh
    {
        
        public function PublicMesh() 
        {
            
        }
        
        public function set vertexHeader(val:Vertex):void {
            vertexList = val;
        }
        public function get vertexHeader():Vertex {
            return vertexList;
        }
        public function set faceHeader(val:Face):void {
            faceList = val;
        }
        public function get faceHeader():Face {
            return faceList;
        }
        
        public static function fromExistingMesh(mesh:Mesh):PublicMesh {
            var pubMesh:PublicMesh = new PublicMesh();
            pubMesh.clonePropertiesFrom(mesh);
            return pubMesh;
        }
        
    }


    import flash.net.registerClassAlias;
    import flash.utils.Dictionary;
    import flash.utils.describeType;
    import flash.utils.getDefinitionByName;
    import flash.utils.getQualifiedClassName;
    import flash.utils.IDataInput;
    import flash.utils.IDataOutput;
    import flash.utils.IExternalizable;

    class ReflectReadWrite implements IExternalizable
    {
        public static var current:ReflectReadWrite;
        
        public var cachedPropertyMaps:Object = { };
        
        public var classList:Vector.<String> = new Vector.<String>();
        public var propertyMaps:Vector.<AbstractOrderedObject> = new Vector.<AbstractOrderedObject>;
        
        public function registerProperties(target:Object, map:Object):void {
            var className:String = getQualifiedClassName(target);
            var typeMap:Object = propertyMap(target);
            for (var i:String in map) {
                if (map[i] is String) continue;
                if (typeMap[i] != undefined) {
                    map[i] = typeMap[i];
                }
                else {
                    throw new Error("Serializable read/write property: " + i +" not found on:" + target);
                }
            }
            cachedPropertyMaps[className] =  map;
        }
        
        
        /**
         *
         * @param target
         * @return
         *
         */
        public function propertyMap(target:Object):AbstractOrderedObject
        {
            var propMap:AbstractOrderedObject;
            const MAP:Object = RegisterAliases.MAP;
           
            var className:String = getQualifiedClassName(target); // classXML.@name;

            if (!cachedPropertyMaps[className])
            {
                propMap = new AbstractOrderedObject();
                var classXML:XML = describeType(target);
                registerClassAlias(className, target as Class || target.constructor );
                var list:XMLList = classXML..*.((name() == "accessor") || (name() == "variable"));

                var item:XML;
                for each (item in list)
                {
                    var itemName:String = item.name().toString();
                    var prop:String = item.@name.toString();
                    // only serializable properties allowed!
                    var type:String =  item.@type.toString();
                    if (MAP[type] == undefined) continue;
                    
                    switch (itemName)
                    {
                        case "variable":
                            propMap[prop] =type;
                            
                            break;
                        case "accessor":
                            var access:String = item.@access;
                            if (access == "readwrite")  //|| (access == "writeonly")
                            {
                                propMap[prop] = type;
                            }
                            break;
                    }
                    cachedPropertyMaps[className] = propMap;
                }
                
                classList.push(className);
                propertyMaps.push( propMap);
            }
            else
            {
                propMap = cachedPropertyMaps[className];
            }

            return propMap;
        }
        
        /* INTERFACE flash.utils.IExternalizable */
        
        public function writeExternal(output:IDataOutput):void 
        {    
            output.writeObject(classList);
            output.writeObject(propertyMaps);
            
        }
        
        public function readExternal(input:IDataInput):void 
        {
            classList = input.readObject();
            propertyMaps = input.readObject();
    
            if (classList.length != propertyMaps.length) throw new Error("Class list / Property map vector length mismatch!" + classList.length + ", "+propertyMaps.length);

            cachedPropertyMaps = { };
            var count:int = classList.length;
            while (--count > -1) {
                cachedPropertyMaps[classList[count]] = propertyMaps[count];
            }
            
        }

       
        
    }



    import alternativ7.engine3d.core.Object3D;
    import flash.net.registerClassAlias;
    import flash.utils.getQualifiedClassName;
    /**
     * ...
     * @author Glenn Ko
     */
    class RegisterAliases 
    {
        // Primitive data-type map (will always spawn new instances/values when applied!)
        public static const MAP:Object = {
            "int": int,
            "Number": Number,
            "uint": uint,
            "Boolean": Boolean,
            "String": String
        };
        
        private static var CHECKED:Boolean = false;
        public static function check():void {
            if (CHECKED) return;
            CHECKED = true;
            
            registerClassAlias("String", String);    
            
            registerWonderFLAlias("portal.serialization.AbstractOrderedObject", AbstractOrderedObject);
            registerWonderFLAlias("portal.serialization.ExternalizableWrapper", ExternalizableWrapper);
            registerWonderFLAlias("portal.serialization.ReflectReadWrite", ReflectReadWrite);
            registerWonderFLAlias("portal.serialization.MaterialList", MaterialList);
            registerWonderFLAlias("portal.serialization.PublicMesh", PublicMesh);
            
            
            registerClassAlias("alternativa.engine3d.core.Object3D", Object3D);
        }
        
        public static function registerClass(classe:Class):void {
            registerClassAlias(getQualifiedClassName(classe), classe);
        }
        
        public static const WONDER_FL:Object = {};
        public static function registerWonderFLAlias(arg1:String, arg2:Class):void {
            WONDER_FL[arg1.split(".").pop()] = arg2;
            registerClassAlias(arg1, arg2);
        }
        
        public static function registerAsPrimitive(arg1:String, arg2:Class):void 
        {
            MAP[arg1] = arg2;
            registerClassAlias(arg1, arg2);
        }
        
        public static function unregisterPrimitive(arg1:String):void {
            delete MAP[arg1];
        }
        
        
        
    }
    
    

    import flash.utils.ByteArray;
    import flash.utils.Dictionary;
    /**
     * ...
     * @author Glenn Ko
     */
    class SerializableApplier
    {
        public static const proxy:DataIOProxy = new DataIOProxy(null, null);
        
        public static const MAP_READ:Object = {
            // primitive data types
            "int": proxy.readInt,
            "Number": proxy.readFloat,
            "uint": proxy.readUnsignedInt,
            "Boolean": proxy.readBoolean,
            "String": proxy.readObject,
            
            // a3d7 specific
            "faceList": proxy.readFaceList,
            "vertexList": proxy.readVertexList,
            "material": proxy.readMaterial
        };
        
        public static const MAP_WRITE:Object = {
            // primitive data types
            "int": proxy.writeInt,
            "Number": proxy.writeFloat, 
            "uint": proxy.writeUnsignedInt, 
            "Boolean": proxy.writeBoolean, 
            "String": proxy.writeObject,
            
            // a3d7 specific
            "faceList": proxy.writeFaceList,
            "vertexList": proxy.writeVertexList,
            "material": proxy.writeMaterial
        };
        
        
    }