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

3D procedural sphere

You can add or remove segments to vary simplisity of a sphere.
Get Adobe Flash player
by ke.kurono 25 Nov 2014
package 
{
    import com.adobe.utils.AGALMiniAssembler;
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.display.Stage3D;
    import flash.display.StageAlign;
    import flash.display.StageQuality;
    import flash.display.StageScaleMode;
    import flash.display3D.Context3D;
    import flash.display3D.Context3DCompareMode;
    import flash.display3D.Context3DProfile;
    import flash.display3D.Context3DProgramType;
    import flash.display3D.Context3DRenderMode;
    import flash.display3D.Context3DTextureFormat;
    import flash.display3D.Context3DTriangleFace;
    import flash.display3D.Context3DVertexBufferFormat;
    import flash.display3D.IndexBuffer3D;
    import flash.display3D.Program3D;
    import flash.display3D.textures.Texture;
    import flash.display3D.VertexBuffer3D;
    import flash.events.Event;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.events.TouchEvent;
    import flash.geom.Matrix;
    import flash.geom.Matrix3D;
    import flash.geom.Vector3D;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.ui.Keyboard;
    import flash.ui.Multitouch;
    import flash.ui.MultitouchInputMode;
    import flash.utils.getTimer;
    
    public class Main_lights_primitives extends Sprite 
    {
        private var _cameraRotation:Vector3D = new Vector3D(-340, -200, 0);
        private var _cameraRadius:Number = 100;
        private var _mousePos2d:Vector3D;
        private var _texture:BitmapData;
        private var _mx:Matrix3D = new Matrix3D();
        
        private var _verts:Vector.<Number>; //xyzuvnnn, xyzuvnnn, ... (stride=8)
        private var _vStride:uint = 8;
        private var _indices:Vector.<uint>;
        
        private var _rotTime:int;
        
        private var _HQ:Boolean = true;
        
        private var _ctx3d:Context3D;
        private var _shaders:Program3D;
        
        private var _iBuf:IndexBuffer3D;
        private var _vBuf:VertexBuffer3D;
        
        private var _viewMx:Matrix3D;
        private var _projMx:Matrix3D;
        
        private var _diffTex:Texture;
        
        private var _camPos:Vector3D;
        
        private var _lightPos:Vector3D = new Vector3D(-100, 100, -100);
        private var _ambient:Number = .5;
        
        private var _needsSnapshot:Boolean = false;
        private var _snapshot:Bitmap;
        
        public function Main_lights_primitives():void 
        {
            trace('initializing stage 2d ...');
            if (stage) init();
            else addEventListener(Event.ADDED_TO_STAGE, init);
        }
        
        private function init(e:Event = null):void 
        {
            removeEventListener(Event.ADDED_TO_STAGE, init);
            // entry point
            
            prepare2dStage();
            requestContext3DOf2dStage();
            
            initInteractivity();
        }
        
        private function prepare2dStage():void {
            stage.frameRate = 60;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            stage.align = StageAlign.TOP_LEFT;
            stage.quality = qualitySettings(StageQuality.LOW, StageQuality.HIGH);
            trace('stage 2d is ready!');
        }
        
        /// block 3d ///
        private function requestContext3DOf2dStage():void {
            trace('creating context 3d ...');
            stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContext3DCreate);
            stage.stage3Ds[0].requestContext3D(Context3DRenderMode.AUTO, Context3DProfile.BASELINE);
        }
        
        private function onContext3DCreate(e:Event):void {
            _ctx3d = (e.target as Stage3D).context3D;    
            if (!_ctx3d) {
                trace('Error: can\'t obtain context 3d!');
                return;
            } else {
                trace('context 3d created!');
            }
            
            initContet3D();
            initShaders();
            
            initMesh();
            initTexture();
            
            // add keyboard handlers
            stage.addEventListener(KeyboardEvent.KEY_UP, keyboardHandler);
            // start rendering
            stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
            
            initGUIControls();
        }
        
        private function initContet3D():void {
            // debug
            _ctx3d.enableErrorChecking = true;
            
            // handle resizing of a viewport
            stage.addEventListener(Event.RESIZE, onResize);
            onResize();
        }
        
        private function onResize(e:Event = null):void {
            _ctx3d.configureBackBuffer(stage.stageWidth, stage.stageHeight, qualitySettings(0, 4), true, false);
        }
        
        private function initShaders():void {
            // vertex shader which does a 3d transformation of a model
            var vs:String =
                "m44 op, va0, vc0" + "\n" + // OutputPosition(op) = xyz(va0) * transformMatrix(vc0)
                "mov v0, va0" + "\n" + // va0 = xyz, put it as v0
                "mov v1, va1" + "\n" + // va1 = uv, put it to FS as v1
                "mov v2, va2" + "\n" + // v2 = normal's xyz(va2)
                "sub v3, vc4, va0";    // v3 = "light's direction" = "light's position"(vc4) - "current vertex"(va0)
            var vsa:AGALMiniAssembler = new AGALMiniAssembler();
            vsa.assemble(Context3DProgramType.VERTEX, vs);            
            
            var texFlags:String = qualitySettings("<2d>", "<2d,linear,miplinear>");
            // fragment shader to render a model
            /*var fs:String = 
                "tex ft0, v1, fs0 " + texFlags + "\n" + // take the texture color from texture(fs0) in uv(v1), <2d,linear,miplinear>
                "mov oc, ft0" + "\n"; // color to OutputColor(oc)*/
            var fs:String = 
                "tex ft0, v1, fs0 " + texFlags + "\n" + // take the texture color from texture(fs0) in uv(v1), <2d,linear,miplinear>
                "nrm ft1.xyz, v2" + "\n" + // ft1 = norm(n), v2=n. make sure, that n is a unit vector
                "nrm ft2.xyz, v3" + "\n" + // ft2 = norm(lightDirection), lightDirection=v3
                "dp3 ft3.x, ft1.xyz, ft2.xyz" + "\n" +     // ft3.x = dot(n, lightDirection)
                "max ft3.x, ft3.x, fc1.x" + "\n" +     // ft3.x > 0 ! 
                "add ft3, ft3.x, fc0" + "\n" +     // ft3 += ambient
                "mul ft0, ft0, ft3" + "\n" +     // color *= ft3
                //"mov oc, v2"; // draw a normal
                "mov oc, ft0"; // color to OutputColor(oc)
            var fsa:AGALMiniAssembler = new AGALMiniAssembler();
            fsa.assemble(Context3DProgramType.FRAGMENT, fs);
            
            // combine shaders into a single program ready to be uploaded to GPU
            _shaders = _ctx3d.createProgram();
            _shaders.upload(vsa.agalcode, fsa.agalcode)
        }
        
        private function initMesh():void {
            // read a raw data
            readModel();
            
            // indices
            _iBuf = _ctx3d.createIndexBuffer(_indices.length); // total amount of vertices forming the triandles
            _iBuf.uploadFromVector(_indices, 0, _indices.length);

            // vertices
            _vBuf = _ctx3d.createVertexBuffer(_verts.length / _vStride, _vStride);
            _vBuf.uploadFromVector(_verts, 0, _verts.length / _vStride);
        }
        
        private function initTexture():void {
            readTexture();
            
            // textures
            _diffTex = createTexture(_texture);
        }
        
        private function createTexture(bd:BitmapData):Texture {
            var mipMap:BitmapData = bd;
            var tex:Texture = _ctx3d.createTexture(bd.width, bd.height, Context3DTextureFormat.BGRA, false);
            var mipLevel:int = 0;
            tex.uploadFromBitmapData(bd, mipLevel++);
            // create mipmaps
            while (bd.width > 1 || bd.height > 1) {                    
                mipMap = new BitmapData(Math.max(1, bd.width >> 1), Math.max(1, bd.height >> 1), true, 0);
                mipMap.draw(bd, new Matrix(0.5, 0, 0, 0.5, 0, 0), null, null, null, true);
                tex.uploadFromBitmapData(mipMap, mipLevel++);
                bd = mipMap;
            }
            return tex;
        }
        
        private function onEnterFrame(e:Event):void {
            autoRotate();
            updateCamera();
            render();
        }
        
        private function updateCamera():void {
            _mx = new Matrix3D();
            _viewMx = viewMatrix(_cameraRotation, _cameraRadius, 0);
            _projMx = projMatrix(45, stage.stageWidth / stage.stageHeight, 0.1, 1000);
            
            // multiply matrices
            _mx.append(_viewMx);            
            _mx.append(_projMx);
            
            // camera's position
            _viewMx.invert();
            _camPos = _viewMx.position;
        }
        
        private function autoRotate():void {
            // update camera                    
            if (!_mousePos2d) {
                var dr:Number = (getTimer() - _rotTime) * 0.01;
                //_cameraRotation.x += dr;
                _cameraRotation.y += dr;
                //_cameraRotation.z += dr;
                _rotTime = getTimer();
            }
        }
        
        private function render():void {
            if (!_ctx3d) return;
            
            _lightPos = _camPos;
            
            // init renderer for the new frame
            _ctx3d.setDepthTest(true, Context3DCompareMode.LESS_EQUAL);
            _ctx3d.setCulling(Context3DTriangleFace.FRONT);
            _ctx3d.clear(.5, .5, .5);
            
            // upload shaders
            _ctx3d.setProgram(_shaders);
            
            // upload constants
            // vertex
            _ctx3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, _mx, true); // vc0 = a 4x4 transform matrix -> 4 registers (vc0-vc3)
            _ctx3d.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 4, Vector.<Number>([_lightPos.x, _lightPos.y, _lightPos.z, 0.0])); // vc4 = light's position
            // fragment
            _ctx3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>([_ambient, _ambient, _ambient, 1.0])); // fc0 = ambient
            _ctx3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, Vector.<Number>([0.0, 0.0, 0.0, 0.0])); // fc1.x = 0
            
            // upload geometry
            _ctx3d.setVertexBufferAt(0, _vBuf,  0, Context3DVertexBufferFormat.FLOAT_3); // va0 = vertex xyz
            _ctx3d.setVertexBufferAt(1, _vBuf,  3, Context3DVertexBufferFormat.FLOAT_2); // va1 = texCoord uv
            _ctx3d.setVertexBufferAt(2, _vBuf,  5, Context3DVertexBufferFormat.FLOAT_3); // va2 = normal nnn    
            
            // upload texture
            _ctx3d.setTextureAt(0, _diffTex); // 0 means (fs0) in the fragment shader
            
            
            // draw!
            _ctx3d.drawTriangles(_iBuf, 0, _indices.length / 3);
            
            drawSnapshot();
            
            // backbuffer to the screen
            _ctx3d.present();
        }
        
        private function qualitySettings(low:*, high:*):* {
            if (_HQ) {
                return high;
            } else {
                return low;
            }
        }
        
        private function drawSnapshot():void {
            // render to texture if needed. should be called before _ctx3d.present();
            if (_needsSnapshot) {
                _needsSnapshot = false;
                _ctx3d.drawToBitmapData(_snapshot.bitmapData);
                _snapshot.visible = true;
                _needsSnapshot = false;
                
                // add text
                var tf:TextField = new TextField();
                tf.text = "This's a snapshot, press \"SPACE\" again to continue.";
                tf.autoSize = TextFieldAutoSize.LEFT;
                tf.textColor = 0xffffff;
                _snapshot.bitmapData.draw(tf);
            }
        }
        
        private function askForSnapshot():void {
            if (!_snapshot) {
                _snapshot = new Bitmap(new BitmapData(stage.stageWidth, stage.stageHeight, false));
                addChild(_snapshot);
            }
            if (_snapshot.visible) {
                _snapshot.visible = false;
            } else {
                _needsSnapshot = true;
            }
        }
        
        private function keyboardHandler(e:KeyboardEvent):void {
            switch (e.keyCode) {
                case Keyboard.SPACE:
                    askForSnapshot();
                    break;
                default:
                    break;
            }
        }
        /// block
        
        private function initInteractivity():void {
            // mouse listeners
            stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDwn);
            stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
            stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
            stage.addEventListener(MouseEvent.MOUSE_WHEEL, onMouseWheel);
            
            // touch listeners
            Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
            stage.addEventListener(TouchEvent.TOUCH_BEGIN, onTouchBegin);
            stage.addEventListener(TouchEvent.TOUCH_MOVE, onTouchMove);
            stage.addEventListener(TouchEvent.TOUCH_END, onTouchEnd);
        }
        
        private function onMouseDwn(e:MouseEvent):void {
            _mousePos2d = new Vector3D(mouseX, mouseY);
        }
        
        private function onMouseUp(e:MouseEvent):void {
            _mousePos2d = null;
            _rotTime = getTimer();
        }
        
        private function onMouseMove(e:MouseEvent):void {
            if (!_mousePos2d) return;
            _cameraRotation.x += (mouseY - _mousePos2d.y) * 0.5;
            _cameraRotation.y += (mouseX - _mousePos2d.x) * 0.5;                    
            _mousePos2d = new Vector3D(mouseX, mouseY);
        }
        
        private function onMouseWheel(e:MouseEvent):void {
            var factor:Number = (e.delta > 0) ? 1.1 : 0.9;
            _cameraRadius *= factor
            updateRadius(factor);
        }
        
        private function onTouchBegin(e:TouchEvent):void {
            _mousePos2d = new Vector3D(e.stageX, e.stageY);
        }
        
        private function onTouchMove(e:TouchEvent):void { 
            if (!_mousePos2d) return;
            _cameraRotation.x += (e.stageY - _mousePos2d.y) * 0.5;
            _cameraRotation.y += (e.stageX - _mousePos2d.x) * 0.5;                    
            _mousePos2d = new Vector3D(e.stageX, e.stageY);
        }
        
        private function onTouchEnd(e:TouchEvent):void { 
            _mousePos2d = null;
            _rotTime = getTimer();
        }
        
        private function projMatrix(FOV:Number, aspect:Number, zNear:Number, zFar:Number):Matrix3D {
            var sy:Number = 1.0 / Math.tan(FOV * Math.PI / 360.0),
                sx:Number = sy / aspect;
            return new Matrix3D(Vector.<Number>([
                    sx, 0.0, 0.0, 0.0,
                    0.0, sy, 0.0, 0.0,
                    0.0, 0.0, zFar / (zNear - zFar), -1.0,
                    0.0, 0.0, (zNear * zFar) / (zNear - zFar), 0.0]));
        }
        
        private function viewMatrix(rot:Vector3D, dist:Number, centerY:Number):Matrix3D {
            var m:Matrix3D = new Matrix3D();
            m.appendTranslation(0, -centerY, 0);
            m.appendRotation(rot.z, new Vector3D(0, 0, 1));
            m.appendRotation(rot.y, new Vector3D(0, 1, 0));            
            m.appendRotation(rot.x, new Vector3D(1, 0, 0));
            m.appendTranslation(0, 0, -dist);
            return m;
        }
        
        private function readModel():void {
            SphereMesh.buildGeometry();
            
            // geometry
            _verts = new Vector.<Number>();
            _indices = new Vector.<uint>();
            var i:uint;
            for (i = 0; i < SphereMesh.vertices.length / 3; i++) {
                // xyz
                _verts.push(SphereMesh.vertices[3*i]);
                _verts.push(SphereMesh.vertices[3*i+1]);
                _verts.push(SphereMesh.vertices[3*i+2]);
                // uv
                _verts.push(SphereMesh.uvs[2*i]);
                _verts.push(SphereMesh.uvs[2 * i + 1]);
                // nxnynz
                _verts.push(SphereMesh.normals[3*i]);
                _verts.push(SphereMesh.normals[3*i+1]);
                _verts.push(SphereMesh.normals[3*i+2]);
            }
            for (i = 0; i < SphereMesh.indices.length; i++) {
                _indices.push(SphereMesh.indices[i]);
            }
        }
        
        private function readTexture():void {
            SphereMesh.buildTexture();
            
            // texture
            _texture = SphereMesh.texture;
        }
        
        private function updateRadius(f:Number):void {
            var i:uint;
            for (i = 0; i < _verts.length; i++) {
                _verts[i] *= f;
            }
        }
        
        private function initGUIControls():void {
            new Button("+", this, 10, 10, onUpButtonClick);
            new Button("-", this, 10, 70, onDownButtonClick);
        }
        
        private function onDownButtonClick(e:MouseEvent):void {
            SphereMesh.stepsH = SphereMesh.stepsV *= .5;
            SphereMesh.stepsH = SphereMesh.stepsV = checkRange(SphereMesh.stepsH, 2, 245);
            trace(SphereMesh.stepsH);
            initMesh();
            initTexture();
        }
        
        private function onUpButtonClick(e:MouseEvent):void {
            SphereMesh.stepsH = SphereMesh.stepsV *= 1.5;
            SphereMesh.stepsH = SphereMesh.stepsV = checkRange(SphereMesh.stepsH, 2, 245);
            trace(SphereMesh.stepsH);
            initMesh();
            initTexture();
        }
        
        private function checkRange(v:Number, min:Number, max:Number):Number {
            if (v <= min) v = min;
            if (v >= max) v = max;
            return v;
        }
        
    }
    
}

import flash.display.BitmapData;
import flash.display.Graphics;
import flash.display.Shape;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.geom.*;
import flash.text.TextField;
    
class SphereMesh {
    public static const color:uint = 0x00ccff;
    public static const alpha:Number = 1;
    
    public static const R:Number = 10;
    public static var stepsH:uint = 10;
    public static var stepsV:uint = 10;
    // v0,v1,v2, v0,v1,v2, ...
    public static var indices:Array = [];
    // x,y,z, x,y,z, ...
    public static var vertices:Array = [];
    // u,v, u,v, ...
    public static var uvs:Array = [];
    // nx,ny,nz, nx,ny,nz, ...
    public static var normals:Array = [];
    public static var texture:BitmapData;
    
    public static function buildTexture():void {
        texture = new BitmapData(512, 512, false);
        var i:uint, j:uint;
        var size:uint = texture.height / stepsV;
        var checker:Shape = new Shape();
        checker.graphics.beginFill(color, alpha);
        for (i = 0; i < texture.width / size; i++) {
            for (j = 0; j < texture.height / size; j++) {
                with (checker.graphics) {
                    drawRect((2 * i + j % 2) * size, j * size, size, size);
                }
            }
        }
        checker.graphics.endFill();
        texture.draw(checker);
    }
    
    /**
     * Fill indices, vertices, normals  and uvs
     */
    public static function buildGeometry():void {
        // reset all
        indices = new Array();
        vertices = new Array();
        uvs = new Array();
        normals = new Array();
        
        var i:uint, j:uint;
        
        var p:Vector3D; // position of a vertex
        var n:Vector3D; // normal of a vertex
        var uv:Vector3D; // uv of a vertex
        var Phi:Number, Theta:Number;
        var dPhi:Number = Math.PI / stepsV;
        var dTheta:Number = 2 * Math.PI / stepsH;

        // build vertices
        
        for (i = 0; i <= stepsV; i++) {
            Phi = i * dPhi;
            for (j = 0; j <= stepsH; j++) {
                Theta = j * dTheta;
                // vertex
                p = new Vector3D(Math.sin(Phi)*Math.cos(Theta), Math.cos(Phi), Math.sin(Phi)*Math.sin(Theta));
                p.scaleBy(R);
                // normal (for sphere it equals its pos)
                n = new Vector3D(p.x, p.y, p.z);
                n.normalize();
                // uvs
                uv = new Vector3D(Theta / (2 * Math.PI), Phi / Math.PI);
                // save
                vertices.push(p.x);
                vertices.push(p.y);
                vertices.push(p.z);
                normals.push(n.x);
                normals.push(n.y);
                normals.push(n.z);
                uvs.push(uv.x);
                uvs.push(uv.y);
            }
        }

        // build indices (triangles)
        
        for (i = 0; i <= stepsH; i++) {
            indices.push(0);
            indices.push(i + 1);
            indices.push(i);
        }
        var baseIndex:uint = 0;
        var ringVertexCount:int = stepsH + 1;
        for (i = 0; i < stepsV; i++) {
            for (j = 0; j < stepsH; j++) {
                indices.push(baseIndex + i * ringVertexCount + j);
                indices.push(baseIndex + i * ringVertexCount + j + 1);
                indices.push(baseIndex + (i + 1) * ringVertexCount + j);

                indices.push(baseIndex + (i + 1) * ringVertexCount + j);
                indices.push(baseIndex + i * ringVertexCount + j + 1);
                indices.push(baseIndex + (i + 1) * ringVertexCount + j + 1);
            }
        }
    }
}


class Button extends Sprite {
    private var _body:Sprite;
    private var _text:TextField;
    private var _pressed:Boolean = false;
    
    public function get caption():String {
        return _text.text;
    }
    
    public function Button(name:String, parent:Sprite, x:Number, y:Number, clickHandler:Function) {
        super();
        _body = new Sprite();
        _text = new TextField();
        _text.text = name;
        _text.selectable = false;
        _text.x = 5;
        _text.y = 15;
        _text.width = 50;
        update();
        addChild(_text);
        addChild(_body);
        
        parent.addChild(this);
        this.x = x;
        this.y = y;
        this.buttonMode = true;
        
        addEventListener(MouseEvent.CLICK, clickHandler);
    }
    
    private function update():void {
        var g:Graphics = _body.graphics;
        g.clear();
        g.lineStyle(1, 0);
        g.beginFill(0xcccccc, .1);
        g.drawRect(0, 0, 50, 50);
        g.endFill();
    }
}