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

Environment Reflect/Refract

http://davidejones.com/blog/1692-environment-reflect-refract/

...
@author David E Jones
/**
 * Copyright davidejones ( http://wonderfl.net/user/davidejones )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/zMCb
 */

// http://davidejones.com/blog/1692-environment-reflect-refract/
package 
{
    import alternativa.engine3d.controllers.SimpleObjectController;
    import alternativa.engine3d.core.Camera3D;
    import alternativa.engine3d.core.Debug;
    import alternativa.engine3d.core.Object3D;
    import alternativa.engine3d.loaders.ParserA3D;
    import alternativa.engine3d.core.Resource;
    import alternativa.engine3d.core.View;
    import alternativa.engine3d.materials.FillMaterial;
    import alternativa.engine3d.objects.Mesh;
    import alternativa.engine3d.objects.Surface;
    import alternativa.engine3d.resources.BitmapTextureResource;
    import alternativa.engine3d.resources.Geometry;
    import alternativa.engine3d.core.VertexAttributes;
    import flash.geom.Matrix;
    import flash.utils.Dictionary;
    import flash.display.BitmapData;
    import flash.display.StageQuality;
    import flash.display3D.Context3DRenderMode;
    import flash.events.ErrorEvent;
    import flash.events.MouseEvent;
    import flash.geom.Matrix3D;
    import flash.geom.Vector3D;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.display.Stage3D;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.net.URLRequest;
    import flash.system.LoaderContext;
    import flash.display.Loader;
    
    import com.bit101.components.*;
    import com.bit101.components.Style;
    
    import alternativa.engine3d.alternativa3d;
    use namespace alternativa3d;
    
    /**
     * ...
     * @author David E Jones
     */
    
    [SWF(backgroundColor = "#000000", width = "465", height = "465", frameRate = "60")]
    
    public class Main extends Sprite 
    {
        private var scene:Object3D = new Object3D();
        private var camera:Camera3D;
        private var stage3D:Stage3D;
        private var monkey:Mesh;
        private var controller:SimpleObjectController;
        private var refractmat3:RefractMaterial3;
        private var envobj:Object;
        private var EmbedBack:BitmapData;
        private var EmbedFront:BitmapData;
        private var EmbedLeft:BitmapData;
        private var EmbedRight:BitmapData;
        private var EmbedBottom:BitmapData;
        private var EmbedTop:BitmapData;
        private var assetsloadcount:Number = 0;
        private var contineSceneCalled:Boolean = false;
        
        public function Main():void 
        {
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            
            camera = new Camera3D(1, 10000);
            camera.view = new View(stage.stageWidth, stage.stageHeight, false, 0x000000, 0, 4);
            camera.view.antiAlias = 0;
            camera.view.mouseEnabled = false;
            camera.view.mouseChildren = false;
            addChild(camera.view);
            addChild(camera.diagram);
            camera.debug = false;
            
            camera.addToDebug(Debug.BOUNDS, Object3D);

            camera.x = 0;
            camera.y = -50;
            camera.z = 0;
            camera.lookAt(0, 0, 0);
            
            controller = new SimpleObjectController(stage, camera, 200);
            controller.lookAt(new Vector3D(0,0,0));
            
            scene.addChild(camera);

            stage3D = stage.stage3Ds[0];
            stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
            stage3D.requestContext3D(Context3DRenderMode.AUTO);
        }    
        
        private function onContextCreate(e:Event):void {
            stage3D.removeEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
            
            getAssets();
            
            stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
            stage.addEventListener(Event.RESIZE, onResize);
            onResize(); 
        }
        
        private function getAssets():void
        {
            var ldr1:Loader = new Loader;
            ldr1.load(
                new URLRequest("http://assets.wonderfl.net/images/related_images/f/ff/ffa1/ffa1e2c03b8b52248257a171c42374c779f692f9m"),
                new LoaderContext( true )
            );
            ldr1.contentLoaderInfo.addEventListener( Event.COMPLETE, function(e :Event) :void {     
                EmbedLeft = new BitmapData(ldr1.width,ldr1.height);
                EmbedLeft.draw( ldr1 );
                assetsloadcount++;
                if (assetsloadcount >= 6) {
                    continueScene();
                }
            });
            var ldr2:Loader = new Loader;
            ldr2.load(
                new URLRequest("http://assets.wonderfl.net/images/related_images/9/9f/9fd4/9fd49c688ff52caa3f50cdf9fb7256934462a250m"),
                new LoaderContext( true )
            );
            ldr2.contentLoaderInfo.addEventListener( Event.COMPLETE, function(e :Event) :void {     
                EmbedRight = new BitmapData(ldr2.width,ldr2.height);
                EmbedRight.draw( ldr2 );
                assetsloadcount++;
                if (assetsloadcount >= 6) {
                    continueScene();
                }
            });
            
            var ldr3:Loader = new Loader;
            ldr3.load(
                new URLRequest("http://assets.wonderfl.net/images/related_images/9/97/97be/97be5bd05f1cc63b63cf8c453e935b1571ddf7e1m"),
                new LoaderContext( true )
            );
            ldr3.contentLoaderInfo.addEventListener( Event.COMPLETE, function(e :Event) :void {     
                EmbedBack = new BitmapData(ldr3.width,ldr3.height);
                EmbedBack.draw( ldr3 );
                assetsloadcount++;
                if (assetsloadcount >= 6) {
                    continueScene();
                }
            });
            
            var ldr4:Loader = new Loader;
            ldr4.load(
                new URLRequest("http://assets.wonderfl.net/images/related_images/b/b2/b206/b206eb5ce6a87916b8f8ac0a6b31ec140e6aa0f1m"),
                new LoaderContext( true )
            );
            ldr4.contentLoaderInfo.addEventListener( Event.COMPLETE, function(e :Event) :void {     
                EmbedFront = new BitmapData(ldr4.width,ldr4.height);
                EmbedFront.draw( ldr4 );
                assetsloadcount++;
                if (assetsloadcount >= 6) {
                    continueScene();
                }
            });
            
            var ldr5:Loader = new Loader;
            ldr5.load(
                new URLRequest("http://assets.wonderfl.net/images/related_images/a/a3/a31a/a31aebfbf66d7f40461b61b71d89a0220337f405m"),
                new LoaderContext( true )
            );
            ldr5.contentLoaderInfo.addEventListener( Event.COMPLETE, function(e :Event) :void {     
                EmbedBottom = new BitmapData(ldr5.width,ldr5.height);
                EmbedBottom.draw( ldr5 );
                assetsloadcount++;
                if (assetsloadcount >= 6) {
                    continueScene();
                }
            });
            
            var ldr6:Loader = new Loader;
            ldr6.load(
                new URLRequest("http://assets.wonderfl.net/images/related_images/1/1c/1cea/1cea972cd3ec18ec96785b25d47dfac997200082m"),
                new LoaderContext( true )
            );
            ldr6.contentLoaderInfo.addEventListener( Event.COMPLETE, function(e :Event) :void {     
                EmbedTop = new BitmapData(ldr6.width,ldr6.height);
                EmbedTop.draw( ldr6 );
                assetsloadcount++;
                if (assetsloadcount >= 6) {
                    continueScene();
                }
            });
        }
        
        private function resizeForGPU(data:BitmapData):BitmapData
        {
            var source:BitmapData = data;
            var wLog2Num:Number = Math.log(data.width)/Math.LN2;
            var hLog2Num:Number = Math.log(data.height)/Math.LN2;
            var wLog2:int = Math.ceil(wLog2Num);
            var hLog2:int = Math.ceil(hLog2Num);
            var resizeMatrix:Matrix = new Matrix(1, 0, 0, 1);
            if (wLog2 != wLog2Num || hLog2 != hLog2Num || wLog2 > 11 || hLog2 > 11) {
                // Resize bitmap
                wLog2 = (wLog2 > 11) ? 11 : wLog2;
                hLog2 = (hLog2 > 11) ? 11 : hLog2;
                source = new BitmapData(1 << wLog2, 1 << hLog2, data.transparent, 0x0);
                resizeMatrix.a = (1 << wLog2)/data.width;
                resizeMatrix.d = (1 << hLog2)/data.height;
                source.draw(data, resizeMatrix, null, null, null, true);
            }
            return source;
        }
        
        private function continueScene():void
        {
            if (!contineSceneCalled)
            {
                envobj = new Object();
                envobj.EmbedLeft = resizeForGPU(EmbedLeft);
                envobj.EmbedRight = resizeForGPU(EmbedRight);
                envobj.EmbedBack = resizeForGPU(EmbedBack);
                envobj.EmbedFront = resizeForGPU(EmbedFront);
                envobj.EmbedBottom = resizeForGPU(EmbedBottom);
                envobj.EmbedTop = resizeForGPU(EmbedTop);
                setupMaterials();
                setupUI();
                setupSceneObjects();
                contineSceneCalled = true;
            }
        }
        
        private function setupMaterials():void
        {
            var envMap:EnvCubeResource = new EnvCubeResource(envobj);
            refractmat3 = new RefractMaterial3(envMap);
        }
        
        private function setupUI():void
        {
            //Style.setStyle(Style.DARK);

            var ui:Sprite = new Sprite();
            addChild(ui);
            
            //var win:Window = new Window(ui,10,10,"Settings");
            //win.setSize(170, 245);
            //win.hasMinimizeButton = true;
            //win.minimized = true;
            //win.draggable = false;
            //win.color = 0x666666;
            
            var vox2:VBox = new VBox(ui);
            vox2.x = 0;
            vox2.y = 10;
            vox2.spacing = 7;
            
            var info:Label = new Label(vox2, 10, 10, "http://davidejones.com");
            
            var hslid:HUISlider = new HUISlider(vox2, 10, 10, "Dispersion Red", updateSlider);
            hslid.setSliderParams(0.7, 1.1, refractmat3.chromaticDispersion.x); //0.7 - 1.1
            
            var hslid2:HUISlider = new HUISlider(vox2,10,10,"Dispersion Green",updateSlider);
            hslid2.setSliderParams(0.7, 1.1, refractmat3.chromaticDispersion.y); //0.7 - 1.1
            
            var hslid3:HUISlider = new HUISlider(vox2,10,10,"Dispersion Blue",updateSlider);
            hslid3.setSliderParams(0.7, 1.1, refractmat3.chromaticDispersion.z); //0.7 - 1.1
            
            var hslid4:HUISlider = new HUISlider(vox2,10,10,"FresnelBias",updateSlider);
            hslid4.setSliderParams(0, 1, refractmat3.fresnelBias); //0 - 1
            
            var hslid5:HUISlider = new HUISlider(vox2,10,10,"FresnelScale",updateSlider);
            hslid5.setSliderParams(-5, 5, refractmat3.fresnelScale); //-5 - 5
            
            var hslid6:HUISlider = new HUISlider(vox2,10,10,"FresnelPower",updateSlider);
            hslid6.setSliderParams(0, 20, refractmat3.fresnelPower); //0 - 20
            
        }
        
        private function updateSlider(e:Event):void
        {
            var hslid:HUISlider = e.target as HUISlider;
            switch(hslid.label)
            {
                case "Dispersion Red":
                    refractmat3.chromaticDispersion.x = hslid.value;
                    break;
                case "Dispersion Green":
                    refractmat3.chromaticDispersion.y = hslid.value;
                    break;
                case "Dispersion Blue":
                    refractmat3.chromaticDispersion.z = hslid.value;
                    break;
                case "FresnelBias":
                    refractmat3.fresnelBias = hslid.value;
                    break;
                case "FresnelScale":
                    refractmat3.fresnelScale = hslid.value;
                    break;
                case "FresnelPower":
                    refractmat3.fresnelPower = hslid.value;
                    break;
            }
        }
        
        private function setupSceneObjects():void
        {
            var skybox:MySkyBox = new MySkyBox(envobj);
            scene.addChild(skybox);
            
            var loaderA3D:URLLoader = new URLLoader();
            //loaderA3D.dataFormat = URLLoaderDataFormat.BINARY;
            //loaderA3D.load(new URLRequest("http://davidejones.com/labs/EnvReflectRefract/monkey.a3d"));
            //loaderA3D.addEventListener(Event.COMPLETE, onA3DLoad);
            onA3DLoad();
        }
        
        private function onA3DLoad(e:Event=null):void {
            var parser:ParserA3D = new ParserA3D();
            //parser.parse((e.target as URLLoader).data);
            parser.parse(new model().data);
            monkey = parser.getObjectByName("Monkey") as Mesh;
            monkey.calculateBoundBox();
            monkey.geometry.calculateNormals();
            monkey.geometry.calculateTangents(0);
            smoothShading(monkey);
            monkey.setMaterialToAllSurfaces(refractmat3);
            scene.addChild(monkey);
            
            uploadResources(scene.getResources(true));
        }
        
        public function smoothShading(mesh:Mesh, separateSurface:Boolean = false, threshold:Number = 0.000001):void 
        {
            var indices:Vector.<uint> = mesh.geometry.indices;
            var positions:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.POSITION);
            var normals:Vector.<Number> = mesh.geometry.getAttributeValues(VertexAttributes.NORMAL);
            
            var vartices:Vector.<Vector3D> = new Vector.<Vector3D>(positions.length / 3);
            var vNormals:Vector.<Vector3D> = new Vector.<Vector3D>(normals.length / 3);
            
            var vertexDictionary:Dictionary = new Dictionary()
            var exVertex:ExtraVertex;
            
            //?????????????

            for (var s:uint = 0; s < mesh.numSurfaces; s++ ) {
                var side:String = (separateSurface) ? s.toString() : '';
                for (var n:uint = 0; n < mesh.getSurface(s).numTriangles * 3; n++) {
                    var i:uint = indices[n+mesh.getSurface(s).indexBegin];
                    vartices[i] = new Vector3D(positions[i * 3], positions[i * 3 + 1], positions[i * 3 + 2]);
                    //??????
                    vartices[i].x = int(vartices[i].x / threshold) * threshold;
                    vartices[i].y = int(vartices[i].y / threshold) * threshold;
                    vartices[i].z = int(vartices[i].z / threshold) * threshold;                
                    vNormals[i] = new Vector3D(normals[i * 3], normals[i * 3 + 1], normals[i * 3 + 2]);
                    //??????
                    vNormals[i].x = int(vNormals[i].x / threshold) * threshold;
                    vNormals[i].y = int(vNormals[i].y / threshold) * threshold;
                    vNormals[i].z = int(vNormals[i].z / threshold) * threshold;
                    
                    //????????
                    //????????????????????????????
                    if (vertexDictionary[vartices[i].toString()+'_'+side]) {
                        exVertex = vertexDictionary[vartices[i].toString()+'_'+side]
                        if (exVertex.normals[vNormals[i].toString()+'_'+side] == null) {
                            exVertex.normals[vNormals[i].toString()+'_'+side] = vNormals[i];
                        }
                        exVertex.indices.push(i);

                    } else {
                        exVertex = new ExtraVertex(vNormals[i].x, vNormals[i].y, vNormals[i].z);
                        exVertex.normals[vNormals[i].toString()+'_'+side] = vNormals[i];
                        exVertex.indices.push(i)
                        vertexDictionary[vartices[i].toString()+'_'+side] = exVertex
                    }
                }                
                
            }

            //Normal????
            var count:uint = 0;
            for each (exVertex in vertexDictionary) {
                var normalX:Number = 0;
                var normalY:Number = 0;
                var normalZ:Number = 0;
                count = 0
                for each (var normal:Vector3D in exVertex.normals) {
                    normalX += normal.x;
                    normalY += normal.y;
                    normalZ += normal.z;
                    count++
                }
                normal = new Vector3D(normalX / count, normalY / count, normalZ / count);
                normal.normalize();
                count = exVertex.indices.length;
                for (i = 0; i < count; i++) {
                    vNormals[exVertex.indices[i]] = normal;
                }
            }
            count = vNormals.length;
            normals = new Vector.<Number>();
            for (i = 0; i < count; i++) {
                normals.push(vNormals[i].x, vNormals[i].y, vNormals[i].z);
            }
            
            mesh.geometry.setAttributeValues(VertexAttributes.NORMAL, normals);
        }
        
        private function uploadResources(resources:Vector.<Resource>):void {
            for each (var resource:Resource in resources) {
                resource.upload(stage3D.context3D);
            }
        }
        
        private function onEnterFrame(e:Event):void 
        {
            if (monkey) { monkey.rotationZ += 0.01; }
            camera.render(stage3D);
        }
        
        private function onResize(e:Event = null):void {
            camera.view.width = stage.stageWidth;
            camera.view.height = stage.stageHeight;
        }
        
    }
    
}

import alternativa.engine3d.materials.A3DUtils;
import alternativa.engine3d.materials.Material;
import alternativa.engine3d.core.Camera3D;
import alternativa.engine3d.core.DebugMaterialsRenderer;
import alternativa.engine3d.core.DrawUnit;
import alternativa.engine3d.core.Light3D;
import alternativa.engine3d.core.Object3D;
import alternativa.engine3d.core.Transform3D;
import alternativa.engine3d.objects.Surface;
import alternativa.engine3d.resources.BitmapCubeTextureResource;
import alternativa.engine3d.resources.Geometry;
import alternativa.engine3d.core.Renderer;
import alternativa.engine3d.core.VertexAttributes;
import alternativa.engine3d.materials.compiler.Linker;
import alternativa.engine3d.materials.compiler.Procedure;
import alternativa.engine3d.materials.compiler.VariableType;
import alternativa.engine3d.resources.TextureResource;
import flash.display3D.textures.Texture;
import flash.geom.Vector3D;

import avmplus.getQualifiedClassName;

import flash.display3D.Context3D;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType;
import flash.display3D.VertexBuffer3D;
import flash.utils.Dictionary;
import flash.utils.getDefinitionByName;

import alternativa.engine3d.alternativa3d;
use namespace alternativa3d;
    
class RefractMaterial3 extends Material 
{
    private static var caches:Dictionary = new Dictionary(true);
    private var cachedContext3D:Context3D;
    private var programsCache:Dictionary;
    
    static alternativa3d const getDiffuseVProcedure:Procedure = new Procedure([
        "#a0=aPosition",
        "#a1=aNormal",
        "#c4=cLocalCamera",
        "#c5=cGlobalTransform",
        "#v0=vNormal",
        "#v1=vViewVec",
        //vertex normal
        "mov t0, a1",
        //normal from local to global
        "m33 t0.xyz, t0.xyz, c5",
        //viewVec
        "sub t1, c4, a0",
        //viewVec from local to global
        "m33 t1.xyz, t1.xyz, c5",    
        //normalize
        "nrm t0.xyz, t0",
        "nrm t1.xyz, t1",
        //pass to frag
        "mov v0, t0",
        "mov v1, t1",            
    ], "getDiffuseVProcedure");
    
    // Fragment procedure
    static alternativa3d const getDiffuseFProcedure:Procedure = new Procedure([
        "#v0=vNormal",
        "#v1=vViewVec",
        "#s0=sCubeMap",
        "#c0=cChromaticDispersion",
        "#c1=cChromaticDispersionSquared",
        "#c2=cFresnel",
        
        //reflect
        "dp3 t0, v1, v0",
        "add t0, t0, t0",
        "mul t0, v0, t0",
        "sub t0, v1, t0",
        "neg t0, t0",
        "nrm t0.xyz, t0",
        
        "tex t0, t0, s0 <cube,clamp,linear,nomip>",
        
        //refract R
        "dp3 t2, v1, v0",
        "mul t2, t2, v0",
        "sub t2, t2, v1",
        "mul t2, t2, c0.x",
        
        "dp3 t1, v1, v0",
        "mul t1, t1, t1",
        "sub t1, c0.w, t1",
        "mul t1, c1.x, t1", 
        "sub t1, c0.w, t1",
        "sqt t1, t1",
        "mul t1, t1, v0",
        "sub t1, t2, t1",
        //"nrm t1.xyz, t1",
        
        "tex t3, t1, s0 <cube,clamp,linear,nomip>",
        
        //refract G
        "dp3 t2, v1, v0",
        "mul t2, t2, v0",
        "sub t2, t2, v1",
        "mul t2, t2, c0.y",
        
        "dp3 t1, v1, v0",
        "mul t1, t1, t1",
        "sub t1, c0.w, t1",
        "mul t1, c1.y, t1", 
        "sub t1, c0.w, t1",
        "sqt t1, t1",
        "mul t1, t1, v0",
        "sub t1, t2, t1",
        //"nrm t1.xyz, t1",
        
        "tex t4, t1, s0 <cube,clamp,linear,nomip>",
        
        //refract B
        "dp3 t2, v1, v0",
        "mul t2, t2, v0",
        "sub t2, t2, v1",
        "mul t2, t2, c0.z",
        
        "dp3 t1, v1, v0",
        "mul t1, t1, t1",
        "sub t1, c0.w, t1",
        "mul t1, c1.z, t1", 
        "sub t1, c0.w, t1",
        "sqt t1, t1",
        "mul t1, t1, v0",
        "sub t1, t2, t1",
        //"nrm t1.xyz, t1",
        
        "tex t5, t1, s0 <cube,clamp,linear,nomip>",
        
        //combine rgb
        "mov t1.x, t3.x",
        "mov t1.y, t4.y",
        "mov t1.z, t5.z",
        "mov t1.w, c0.w",
        
        //rfac = bias + scale * pow(1.0 + dot(incident, vNormal), power);
        "dp3 t6, v1, v0", //dot(incident, vNormal)
        "add t6, t6, c2.w", //(1.0 + dot(incident, vNormal)
        "pow t6, t6, c2.z", //pow()
        "mul t6, t6, c2.y", //scale * pow()
        "add t6, t6, c2.x", // + bias        
        
        //gl_FragColor = ret * rfac + ref * (1.0 - rfac);
        "sub t4, c0.w, t6", //(1.0 - rfac)
        "mul t4, t0, t4", //ref * (1.0 - rfac)
        "mul t5, t1, t6", //ret * rfac
        "add t4, t4, t5", // add both together
        
        //lerp
        //"sub t2, t0, t1",
        //"mul t2, t2, v2",
        //"add t2, t2, t1",
        
        //output color
        "mov o0, t4"
    ], "getDiffuseFProcedure");
    
    public var alphaThreshold:Number = 0;
    public var alpha : Number = 1;
    
    public var chromaticDispersion:Vector3D = new Vector3D(0.9, 0.97, 1.04, 1);
    public var fresnelBias:Number = 0.9;
    public var fresnelScale:Number = 0.7;
    public var fresnelPower:Number = 1.1;
    
    static alternativa3d const _passUVProcedure:Procedure = new Procedure(["#v0=vUV", "#a0=aUV", "mov v0, a0"], "passUVProcedure");
    static alternativa3d const _passNormProcedure:Procedure = new Procedure(["#v0=vNormal", "#a1=aNormal", "mov v0, a1"], "passNormProcedure");
    
    private var localToGlobalTransform:Transform3D = new Transform3D();
    
    private var cubetexture:TextureResource;
    
    public function RefractMaterial3(cubetexture:TextureResource) 
    {
        this.cubetexture = cubetexture;
        super();
    }
    
    override alternativa3d function fillResources(resources:Dictionary, resourceType:Class):void {
        super.fillResources(resources, resourceType);
        //if (cubetexture != null && A3DUtils.checkParent(getDefinitionByName(getQualifiedClassName(cubetexture)) as Class, resourceType)) {
        //    resources[cubetexture] = true;
        //}
        if (cubetexture != null) {
            resources[cubetexture] = true;
        }
    }
    
    private function getProgram(object:Object3D, programs:Vector.<DiffuseMaterialProgram>, camera:Camera3D, alphaTest:int):DiffuseMaterialProgram {
        var key:int = 0;
        var program:DiffuseMaterialProgram = programs[key];
        if (program == null) {
            
            var vertexLinker:Linker = new Linker(Context3DProgramType.VERTEX);
            var positionVar:String = "aPosition";
            var normalVar:String = "aNormal";
            vertexLinker.declareVariable(positionVar, VariableType.ATTRIBUTE);
            vertexLinker.declareVariable(normalVar, VariableType.ATTRIBUTE);
            
            if (object.transformProcedure != null) {
                positionVar = appendPositionTransformProcedure(object.transformProcedure, vertexLinker);
            }
            vertexLinker.addProcedure(_projectProcedure);
            vertexLinker.setInputParams(_projectProcedure, positionVar);
            vertexLinker.addProcedure(getDiffuseVProcedure);
            //vertexLinker.addProcedure(_passNormProcedure);
    
            // Pixel shader
            var fragmentLinker:Linker = new Linker(Context3DProgramType.FRAGMENT);
            var outProcedure:Procedure = getDiffuseFProcedure;
            fragmentLinker.addProcedure(outProcedure);
            
            /*
            if (alphaTest > 0) {
                fragmentLinker.declareVariable("tColor");
                fragmentLinker.setOutputParams(outProcedure, "tColor");
                if (alphaTest == 1) {
                    fragmentLinker.addProcedure(thresholdOpaqueAlphaProcedure, "tColor");
                } else {
                    fragmentLinker.addProcedure(thresholdTransparentAlphaProcedure, "tColor");
                }
            }*/
            
            fragmentLinker.varyings = vertexLinker.varyings;

            program = new DiffuseMaterialProgram(vertexLinker, fragmentLinker);

            program.upload(camera.context3D);
            programs[key] = program;
        }
        return program;
    }
    
    private function getDrawUnit(program:DiffuseMaterialProgram, camera:Camera3D, surface:Surface, geometry:Geometry):DrawUnit {
        var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
        var normalsBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.NORMAL);
        var uvBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.TEXCOORDS[0]);

        var object:Object3D = surface.object;

        // Draw call
        var drawUnit:DrawUnit = camera.renderer.createDrawUnit(object, program.program, geometry._indexBuffer, surface.indexBegin, surface.numTriangles, program);

        // Streams
        // a0, a1
        drawUnit.setVertexBufferAt(program.aPosition, positionBuffer, geometry._attributesOffsets[VertexAttributes.POSITION], VertexAttributes.FORMATS[VertexAttributes.POSITION]);
        drawUnit.setVertexBufferAt(program.aNormal, normalsBuffer, geometry._attributesOffsets[VertexAttributes.NORMAL], VertexAttributes.FORMATS[VertexAttributes.NORMAL]);
        //drawUnit.setVertexBufferAt(program.aNormal, uvBuffer, geometry._attributesOffsets[VertexAttributes.TEXCOORDS[0]], VertexAttributes.FORMATS[VertexAttributes.TEXCOORDS[0]]);
        
        //Constants
        object.setTransformConstants(drawUnit, surface, program.vertexShader, camera);
        drawUnit.setProjectionConstants(camera, program.cProjMatrix, object.localToCameraTransform); //cProjMatrix c0;
        
        //local-space camera position
        var cameraToLocalTransform : Transform3D = object.cameraToLocalTransform;
        drawUnit.setVertexConstantsFromNumbers(program.cLocalCamera, cameraToLocalTransform.d, cameraToLocalTransform.h, cameraToLocalTransform.l);
        
        //calculating local to global transform
        localToGlobalTransform.combine(camera.localToGlobalTransform, object.localToCameraTransform);
        drawUnit.setVertexConstantsFromTransform(program.cGlobalTransform, localToGlobalTransform);

        drawUnit.setTextureAt(program.sCubeMap, cubetexture._texture);
        
        drawUnit.setFragmentConstantsFromNumbers(program.cChromaticDispersion, chromaticDispersion.x, chromaticDispersion.y, chromaticDispersion.z, 1);
        drawUnit.setFragmentConstantsFromNumbers(program.cChromaticDispersionSquared, chromaticDispersion.x * chromaticDispersion.x, chromaticDispersion.y * chromaticDispersion.y, chromaticDispersion.z * chromaticDispersion.z, 1);
        drawUnit.setFragmentConstantsFromNumbers(program.cFresnel, fresnelBias, fresnelScale, fresnelPower, 1);
        
        drawUnit.blendSource = Context3DBlendFactor.SOURCE_ALPHA;
        drawUnit.blendDestination = Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA;


        return drawUnit;
    }
    
    override alternativa3d function collectDraws(camera:Camera3D, surface:Surface, geometry:Geometry, lights:Vector.<Light3D>, lightsLength:int, useShadow:Boolean, objectRenderPriority:int = -1):void {
        var object:Object3D = surface.object;
        var lightGroup:Vector.<Light3D> = new Vector.<Light3D>();
        var light:Light3D;

        // Buffers
        var positionBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.POSITION);
        var normalsBuffer:VertexBuffer3D = geometry.getVertexBuffer(VertexAttributes.NORMAL);

        // Check validity
        if (positionBuffer == null || normalsBuffer == null) return;

        // Refresh program cache for this context
        if (camera.context3D != cachedContext3D) {
            cachedContext3D = camera.context3D;
            programsCache = caches[cachedContext3D];
            if (programsCache == null) {
                programsCache = new Dictionary();
                caches[cachedContext3D] = programsCache;
            }
        }
        var optionsPrograms:Vector.<DiffuseMaterialProgram> = programsCache[object.transformProcedure];
        if(optionsPrograms == null) {
            optionsPrograms = new Vector.<DiffuseMaterialProgram>(5, true);
            programsCache[object.transformProcedure] = optionsPrograms;
        }
        
        var program:DiffuseMaterialProgram;
        var drawUnit:DrawUnit;
                    
        if (alphaThreshold > 0) {
            program = getProgram(object, optionsPrograms, camera, 1);
            drawUnit = getDrawUnit(program, camera, surface, geometry);
        } else {
            program = getProgram(object, optionsPrograms, camera, 0);
            drawUnit = getDrawUnit(program, camera, surface, geometry);
        }
        // Use z-buffer within DrawCall, draws without blending
        camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.OPAQUE);
        //camera.renderer.addDrawUnit(drawUnit, objectRenderPriority >= 0 ? objectRenderPriority : Renderer.TRANSPARENT_SORT);
        
    }
    
}

import alternativa.engine3d.materials.ShaderProgram;
import alternativa.engine3d.materials.compiler.Linker;

import flash.display3D.Context3D;

class DiffuseMaterialProgram extends ShaderProgram {

    public var aPosition:int = -1;
    public var aNormal:int = -1;
    public var cProjMatrix:int = -1;    
    public var cLocalCamera:int = -1;    
    public var cGlobalTransform:int = -1;
    public var cTempV:int = -1;
    
    
    public var sCubeMap:int = -1;
    public var cChromaticDispersion:int = -1;
    public var cChromaticDispersionSquared:int = -1;
    public var cFresnel:int = -1;

    public function DiffuseMaterialProgram(vertex:Linker, fragment:Linker) {
        super(vertex, fragment);
    }

    override public function upload(context3D:Context3D):void {
        super.upload(context3D);

        aPosition = vertexShader.findVariable("aPosition");
        aNormal = vertexShader.findVariable("aNormal");
        cProjMatrix = vertexShader.findVariable("cProjMatrix");
        cLocalCamera = vertexShader.findVariable("cLocalCamera");
        cGlobalTransform = vertexShader.findVariable("cGlobalTransform");
        cTempV = vertexShader.findVariable("cTempV");
        
        
        sCubeMap = fragmentShader.findVariable("sCubeMap");
        cChromaticDispersion = fragmentShader.findVariable("cChromaticDispersion");
        cChromaticDispersionSquared = fragmentShader.findVariable("cChromaticDispersionSquared");
        cFresnel = fragmentShader.findVariable("cFresnel");
        
    }
}

import alternativa.engine3d.resources.BitmapCubeTextureResource;
import flash.display.BitmapData;
    
class EnvCubeResource extends BitmapCubeTextureResource 
{    
    public function EnvCubeResource(envobj:Object) 
    {
        super(
            envobj.EmbedBack,
            envobj.EmbedFront,
            envobj.EmbedLeft, 
            envobj.EmbedRight,
            envobj.EmbedBottom,
            envobj.EmbedTop,
            false
        );
    }
}

import alternativa.engine3d.materials.TextureMaterial;
import alternativa.engine3d.objects.SkyBox;
import alternativa.engine3d.resources.BitmapTextureResource;
import flash.display.BitmapData;

class MySkyBox extends SkyBox
{
    public function MySkyBox(envobj:Object) 
    {         
        var left_t:BitmapTextureResource = new BitmapTextureResource(envobj.EmbedLeft);
        var right_t:BitmapTextureResource = new BitmapTextureResource(envobj.EmbedRight);
        var top_t:BitmapTextureResource = new BitmapTextureResource(envobj.EmbedTop);
        var bottom_t:BitmapTextureResource = new BitmapTextureResource(envobj.EmbedBottom);
        var front_t:BitmapTextureResource = new BitmapTextureResource(envobj.EmbedFront);
        var back_t:BitmapTextureResource = new BitmapTextureResource(envobj.EmbedBack);
        
        super(
            100, 
            new TextureMaterial(back_t), 
            new TextureMaterial(front_t), 
            new TextureMaterial(left_t), 
            new TextureMaterial(right_t), 
            new TextureMaterial(bottom_t), 
            new TextureMaterial(top_t), 
            0.01
        );
    }
    
}

import flash.utils.ByteArray;

class model extends Object
{
    private var b:String = "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";
    public var data:ByteArray = new ByteArray();
    
    public function model():void
    {
        data = new Base64(b);
    }

}

import flash.utils.ByteArray; 
class Base64 extends ByteArray { 
    private static const BASE64:Array = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,0,0,0,0,0]; 
    public function Base64(str:String) { 
        var n:int, j:int; 
        for(var i:int = 0; i < str.length && str.charAt(i) != "="; i++) {
            if (str.charCodeAt (i) < 33) continue;
            j = (j << 6) | BASE64[str.charCodeAt(i)]; 
            n += 6; 
            while(n >= 8) { 
                writeByte((j >> (n -= 8)) & 0xFF); 
            } 
        } 
        position = 0; 
    } 
}

import flash.geom.Vector3D;
import flash.utils.Dictionary;
class ExtraVertex {
    public var vertex:Vector3D;
    public var normals:Dictionary;
    public var indices:Vector.<uint>;
    public function ExtraVertex(x:Number, y:Number, z:Number) {
        vertex = new Vector3D(x, y, z);
        normals = new Dictionary();
        indices = new Vector.<uint>();
    }
    
}