開発環境 Apache Flex SDK 4.12.1
FlashDevelop 4.6.2.5
実行環境 Microsoft Windows 8.1 (64bit)
プロジェクトの種類 ActionScript 3/AS3 Project
プロジェクト名 Spheres


Project/Properties
Output
Flash Player 14.0

Main.as
package 
{
    import com.adobe.utils.*;
    import flash.display.*;
    import flash.display3D.*;
    import flash.events.*;
    import flash.geom.*;
    import flash.text.*;
 
    public class Main extends Sprite 
    {
        // 3D
        private var context3D:Context3D;
        private var vertexBuffer:VertexBuffer3D;
        private var indexBuffer:IndexBuffer3D;
 
        // model
        private var vertexData:Vector.<Number>;
        private var indexData:Vector.<uint>;
        private var numVertices:int;
 
        // matrix
        private var view:Matrix3D;
        private var projection:PerspectiveMatrix3D;
 
        // input
        private var mDown:Boolean = false;
        private var mx:Number;
        private var my:Number;
 
        // camera
        private var camDist:Number = 20;
        private var camPitch:int = 0;
        private var camYaw:int = 180;
 
        // button
        private var btnArray:Vector.<TextField>;
        private var btnSelect:int;
 
        private var firstIndex:Vector.<int> = new Vector.<int>;
        private var numTriangles:Vector.<int> = new Vector.<int>;
        private var prevIndex:int = 0;
        private var pos:Vector.<Vector3D>;
 
        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);
            // entry point
 
            stage.stage3Ds[0].addEventListener(Event.CONTEXT3D_CREATE, onContext3DCreate);
            stage.stage3Ds[0].requestContext3D();
        }
 
        private function onContext3DCreate(e:Event):void 
        {
            context3D = stage.stage3Ds[0].context3D;
            context3D.configureBackBuffer(stage.stageWidth, stage.stageHeight, 2);
            context3D.setCulling(Context3DTriangleFace.BACK);
            //context3D.setBlendFactors(
                //Context3DBlendFactor.SOURCE_COLOR, Context3DBlendFactor.DESTINATION_COLOR);
 
            // program
            var vertexProgram:AGALMiniAssembler = new AGALMiniAssembler;
            vertexProgram.assemble(Context3DProgramType.VERTEX,
                "m44 op va0 vc0\n" +
                "mov v0 va1"
            );
            var fragmentProgram:AGALMiniAssembler = new AGALMiniAssembler;
            fragmentProgram.assemble(Context3DProgramType.FRAGMENT,
                "mov oc v0"
            );
            var program:Program3D = context3D.createProgram();
            program.upload(vertexProgram.agalcode, fragmentProgram.agalcode);
            context3D.setProgram(program);
 
            //
            projection = new PerspectiveMatrix3D;
            projection.perspectiveFieldOfViewLH(
                radians(30), stage.stageWidth / stage.stageHeight, 1, 100);
 
            //
            generate();
 
            btnArray = new Vector.<TextField>;
            createButton(10, 10, 200, 50, "六方最密充填構造\nhexagonal close-packed");
            createButton(10, 70, 200, 50, "面心立方格子構造\nface-centered cubic");
            selectButton(0);
 
            stage.addEventListener(MouseEvent.MOUSE_DOWN, function(e:MouseEvent):void {
                mDown = true;
            });
            stage.addEventListener(MouseEvent.MOUSE_UP, function(e:MouseEvent):void {
                mDown = false;
            });
            stage.addEventListener(MouseEvent.MOUSE_WHEEL, function(e:MouseEvent):void {
                camDist -= e.delta;
            });
 
            stage.addEventListener(Event.ENTER_FRAME, render);
        }
 
        private function createButton(x:Number, y:Number, width:Number, height:Number, text:String):void
        {
            var tf:TextField = new TextField;
            tf.background = true;
            tf.border = true;
            tf.defaultTextFormat = new TextFormat(null, 20);
            tf.name = btnArray.length.toString();
            tf.selectable = false;
            tf.x = x;
            tf.y = y;
            tf.width = width;
            tf.height = height;
            tf.text = text;
            tf.addEventListener(MouseEvent.CLICK, onButtonClick);
            addChild(tf);
            btnArray.push(tf);
        }
 
        private function onButtonClick(e:MouseEvent):void 
        {
            var select:int = parseInt((e.target as TextField).name);
            selectButton(select);
        }
 
        private function selectButton(select:int):void 
        {
            for (var i:int = 0; i < btnArray.length; i++) 
            {
                var tf:TextField = btnArray[i];
                tf.backgroundColor = (i == select) ? 0xffff00 : 0x7f7f7f;
            }
            btnSelect = select;
 
            pos = new Vector.<Vector3D>;
            switch (btnSelect) 
            {
            case 0: hcp(); break;
            case 1: fcc(); break;
            }
        }
 
        private function hcp():void 
        {
            const h:Number = Math.sqrt(2 / 3) * 2;
            const r3:Number = Math.sqrt(3);
 
            // x, y, z, c(0:青 1:赤 2:緑 3:黄)
            var y:Number = -h;
            var z:Number = 0;
            var c:Number = 0;
            pos.push(new Vector3D( -1, y, z - r3, c));
            pos.push(new Vector3D(1, y, z - r3, c));
            pos.push(new Vector3D(3, y, z - r3, 3));
            pos.push(new Vector3D( -2, y, z, c));
            pos.push(new Vector3D(0, y, z, c));
            pos.push(new Vector3D(2, y, z, c));
            pos.push(new Vector3D( -1, y, z + r3, c));
            pos.push(new Vector3D(1, y, z + r3, c));
            pos.push(new Vector3D(3, y, z + r3, 3));
 
            y = 0;
            z = r3 * (2 / 3);
            c = 1;
            pos.push(new Vector3D( -1, y, z - r3, c));
            pos.push(new Vector3D(1, y, z - r3, c));
            pos.push(new Vector3D(3, y, z - r3, 3));
            pos.push(new Vector3D( -2, y, z, 3));
            pos.push(new Vector3D(0, y, z, c));
            pos.push(new Vector3D(2, y, z, 3));
            pos.push(new Vector3D( -1, y, z + r3, 3));
            pos.push(new Vector3D(1, y, z + r3, 3));
            pos.push(new Vector3D(3, y, z + r3, 3));
 
            y = h;
            z = 0;
            c = 0;
            pos.push(new Vector3D( -1, y, z - r3, c));
            pos.push(new Vector3D(1, y, z - r3, c));
            pos.push(new Vector3D(3, y, z - r3, 3));
            pos.push(new Vector3D( -2, y, z, c));
            pos.push(new Vector3D(0, y, z, c));
            pos.push(new Vector3D(2, y, z, c));
            pos.push(new Vector3D( -1, y, z + r3, c));
            pos.push(new Vector3D(1, y, z + r3, c));
            pos.push(new Vector3D(3, y, z + r3, 3));
        }
 
        private function fcc():void 
        {
            const h:Number = Math.sqrt(2 / 3) * 2;
            const r3:Number = Math.sqrt(3);
 
            // x, y, z, c(0:青 1:赤 2:緑 3:黄)
            var y:Number = -h;
            var z:Number = 0;
            var c:Number = 0;
            pos.push(new Vector3D( -1, y, z - r3, c));
            pos.push(new Vector3D(1, y, z - r3, 3));
            pos.push(new Vector3D(3, y, z - r3, c));
            pos.push(new Vector3D( -2, y, z, c));
            pos.push(new Vector3D(0, y, z, c));
            pos.push(new Vector3D(2, y, z, c));
            pos.push(new Vector3D( -1, y, z + r3, c));
            pos.push(new Vector3D(1, y, z + r3, c));
            pos.push(new Vector3D(3, y, z + r3, c));
 
            y = 0;
            z = r3 * (2 / 3);
            c = 1;
            pos.push(new Vector3D( -1, y, z - r3, 3));
            pos.push(new Vector3D(1, y, z - r3, 3));
            pos.push(new Vector3D(3, y, z - r3, 3));
            pos.push(new Vector3D( -2, y, z, c));
            pos.push(new Vector3D(0, y, z, c));
            pos.push(new Vector3D(2, y, z, c));
            pos.push(new Vector3D( -1, y, z + r3, c));
            pos.push(new Vector3D(1, y, z + r3, c));
            pos.push(new Vector3D(3, y, z + r3, c));
 
            y = h;
            z = r3 * (4 / 3);
            c = 2;
            pos.push(new Vector3D( -2, y, z - 2 * r3, c));
            pos.push(new Vector3D(0, y, z - 2 * r3, c));
            pos.push(new Vector3D(2, y, z - 2 * r3, c));
            pos.push(new Vector3D( -1, y, z - r3, c));
            pos.push(new Vector3D(1, y, z - r3, 3));
            pos.push(new Vector3D(3, y, z - r3, c));
            pos.push(new Vector3D( -2, y, z, c));
            pos.push(new Vector3D(0, y, z, c));
            pos.push(new Vector3D(2, y, z, c));
        }
 
        private function generate():void 
        {
            vertexData = new Vector.<Number>;
            indexData = new Vector.<uint>;
            numVertices = 0;
 
            genSphere(0.5, 0.5, 1);
            genSphere(1, 0.5, 0.5);
            genSphere(0.5, 1, 0.5);
            genSphere(1, 1, 0.5);
 
            vertexBuffer = context3D.createVertexBuffer(numVertices, 6);
            vertexBuffer.uploadFromVector(vertexData, 0, numVertices);
 
            indexBuffer = context3D.createIndexBuffer(indexData.length);
            indexBuffer.uploadFromVector(indexData, 0, indexData.length);
 
            //
            context3D.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
            context3D.setVertexBufferAt(1, vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_3);
        }
 
        private function genSphere(red:Number, green:Number, blue:Number):void 
        {
            const stackNum:int = 32; // 輪切りの数。2の倍数。最小2。
 
            // vertex
            var vertices:Vector.<Number> = new Vector.<Number>;
            vertices.push(0, 1, 0); // x, y, z
            for (var stack:int = 1; stack < stackNum; stack++) 
            {
                var lat:Number = Math.PI * (stack / stackNum);
                var y:Number = Math.cos(lat);
                var r:Number = Math.sin(lat);
                var sliceNum:int = 4 * Math.min(stack, stackNum - stack); // 放射頂点の数
                for (var slice:int = 0; slice <= sliceNum; slice++) 
                {
                    var lon:Number = 2 * Math.PI * (slice / sliceNum);
                    var z:Number = Math.cos(lon) * r;
                    var x:Number = Math.sin(lon) * r;
                    vertices.push(x, y, z);
                }
            }
            vertices.push(0, -1, 0);
 
            // index
            var indices:Vector.<uint> = new Vector.<uint>;
            var prevHead:int = 0; // 前の先頭頂点番号
            var prevVert:int = 1; // 前の頂点数
            for (stack = 0; stack < stackNum / 2; stack++)
            {
                var currHead:int = prevHead + prevVert; // 現在の先頭頂点番号
                var currVert:int = 4 * (stack + 1) + 1; // 現在の頂点数
                for (var quad:int = 0; quad < 4; quad++) // 4象限
                {
                    var prevQuad:int = quad * stack; // 前の象限オフセット
                    var currQuad:int = quad * (stack + 1); // 現在の象限オフセット
                    for (var i:int = 0; ; i++) 
                    {
                        indices.push(prevHead + prevQuad + i);
                        indices.push(currHead + currQuad + i);
                        indices.push(currHead + currQuad + i + 1);
                        if (stack <= i) break;
                        indices.push(prevHead + prevQuad + i);
                        indices.push(currHead + currQuad + i + 1);
                        indices.push(prevHead + prevQuad + i + 1);
                    }
                }
                prevHead = currHead;
                prevVert = currVert;
            }
            for (stack = stackNum / 2 - 1; 0 <= stack; stack--)
            {
                currHead = prevHead + prevVert;
                currVert = 4 * stack + 1;
                for (quad = 0; quad < 4; quad++) // 4象限
                {
                    prevQuad = quad * (stack + 1);
                    currQuad = quad * stack;
                    for (i = 0; ; i++) 
                    {
                        indices.push(currHead + currQuad + i);
                        indices.push(prevHead + prevQuad + i + 1);
                        indices.push(prevHead + prevQuad + i);
                        if (stack <= i) break;
                        indices.push(currHead + currQuad + i);
                        indices.push(currHead + currQuad + i + 1);
                        indices.push(prevHead + prevQuad + i + 1);
                    }
                }
                prevHead = currHead;
                prevVert = currVert;
            }
 
            //
            var num:int = vertices.length / 3;
            for (i = 0; i < num; i++) 
            {
                var base:int = i * 3;
                var c:Number = (1 + vertices[base + 1]) / 2;
                vertexData.push(vertices[base + 0]);
                vertexData.push(vertices[base + 1]);
                vertexData.push(vertices[base + 2]);
                vertexData.push(c * red, c * green, c * blue);
            }
            for (i = 0; i < indices.length; i++) 
            {
                indexData.push(numVertices + indices[i]);
            }
            numVertices += num;
 
            firstIndex.push(prevIndex);
            numTriangles.push((indexData.length - prevIndex) / 3);
            prevIndex = indexData.length;
        }
 
        private function render(e:Event):void 
        {
            updateView();
 
            context3D.clear(0.39, 0.58, 0.93);
 
            for each (var v:Vector3D in pos) 
            {
                var world:Matrix3D = new Matrix3D;
                world.appendTranslation(v.x, v.y, v.z);
 
                var m:Matrix3D = new Matrix3D;
                m.append(world);
                m.append(view);
                m.append(projection);
                context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, m, true);
                context3D.drawTriangles(indexBuffer, firstIndex[v.w], numTriangles[v.w]);
            }
 
            context3D.present();
        }
 
        private function updateView():void 
        {
            if (mDown)
            {
                camYaw += mouseX - mx;
                camPitch += mouseY - my;
            }
            mx = mouseX;
            my = mouseY;
 
            if (camPitch < -90) camPitch = -90;
            if (90 < camPitch) camPitch = 90;
            if (camYaw < 0) camYaw += 360;
            if (360 <= camYaw) camYaw -= 360;
            if (camDist < 1) camDist = 1;
 
            var pitch:Number = radians(camPitch);
            var y:Number = Math.sin(pitch) * camDist;
            var r:Number = Math.cos(pitch) * camDist;
            var yaw:Number = radians(camYaw);
            var z:Number = Math.cos(yaw) * r;
            var x:Number = Math.sin(yaw) * r;
 
            view = lookAt(new Vector3D(x, y, z), new Vector3D, Vector3D.Y_AXIS);
        }
 
        private function lookAt(
            cameraPosition:Vector3D, cameraTarget:Vector3D, cameraUpVector:Vector3D):Matrix3D
        {
            var zaxis:Vector3D = cameraTarget.subtract(cameraPosition);
            zaxis.normalize();
            var xaxis:Vector3D = cameraUpVector.crossProduct(zaxis);
            xaxis.normalize();
            var yaxis:Vector3D = zaxis.crossProduct(xaxis);
 
            return new Matrix3D(Vector.<Number>([
                xaxis.x, yaxis.x, zaxis.x, 0,
                xaxis.y, yaxis.y, zaxis.y, 0,
                xaxis.z, yaxis.z, zaxis.z, 0,
                -xaxis.dotProduct(cameraPosition), -yaxis.dotProduct(cameraPosition),
                -zaxis.dotProduct(cameraPosition), 1
            ]));
        }
 
        private function radians(x:Number):Number
        {
            return x * Math.PI / 180;
        }
 
    }
 
}
 
最終更新:2014年08月15日 22:16