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

Stage3D VS Graphics

GPUおよびソフトウェアレンダリングのStage3D、Graphicsで同じ描画を行い、処理速度の比較を行っています。

[関連記事]
http://dev.yuichiroharai.com/post/stage3d-vs-graphics/

[追記]
ソフトウェアレンダリングのStage3Dにおいて、バッファサイズが偶数か奇数かで速度に違いが出てきます。詳細は以下のフォークしたコードを参照下さい。
http://wonderfl.net/c/4WlJ
Get Adobe Flash player
by yuichiroharai 13 Dec 2011
  • Related works: 1
  • Talk

    yonatan at 13 Dec 2011 23:20
    Interesting. Now we need to do a Stage3D vs copyPixels fork...
    makc3d at 14 Dec 2011 00:53
    very weird results here. software mode beats graphics on high polycount, but is slower on low polys
    yonatan at 14 Dec 2011 01:04
    same here.
    yonatan at 14 Dec 2011 01:25
    but not in http://wonderfl.net/c/bnJh/fullscreen (even if i shrink my browser window)
    keim_at_Si at 14 Dec 2011 07:07
    ソフトウェアレンダリングはバックバッファサイズを奇数ピクセル幅にするとすごく高負荷になるので、464x464 pixelで試すとどうなるかが気になります。 The software rendering is terribly slow with odd back buffer size. I wonder how about it with size of 464x464 pixels.
    katopz at 14 Dec 2011 08:46
    max tri via win7x64 GT220 intel core2 quad 2.66hz gpu : 61/60 software : 24/60 graphic : 15/60 maybe gpu fallback in some case?
    rect at 14 Dec 2011 09:27
    Graphics Card : NVIDIA GeForce GT 330M Processor : Intel Core i5 2.53 GHz OS : Mac OS X 10.6.8 Browser : firefox 8.0.1 Flash Player: 11.1.102.55 Back buffer size : 1386x1386 gpu : 61/60 software : 31/60 graphic : 12/60
    yuichiroharai at 14 Dec 2011 20:14
    すみません、元々はバッファサイズが偶数ピクセルになるように調整してたのですが(135-136行目)、ミスがあってステージサイズが465×465の時だけ奇数のままになってしまっています。 一応、57行目のCANVAS_MINIMUM_SIZEを464に設定するとすべて偶数になります。 上記ミスを修正しつつ、奇数/偶数を選べるようにしたものを週末にフォークする予定です。
    keim_at_Si at 15 Dec 2011 07:06
    あーなるほど。キャンバスサイズ最低値=465に固定してたんですね(line136)。で、フルスクリーンだと必ず偶数になるんだ(line135)。了解です。サイズ偶奇まで突っ込んだ解析期待してます。 OK, now I understand that the minimum canvas size is fixed as 465pix (line136). And the canvas size is always even with full-screen mode (line135). I hope you analyze more deeply with odd and even canvas size.
    yuichiroharai at 15 Dec 2011 18:01
    ご理解頂きありがとうございます!週末まで少々お待ちください。 後、間接的に翻訳して頂いた形になってて、とても助かってますw I will try it during this week. And thanks for your translation!
    makc3d at 17 Dec 2011 00:58
    "The issue was reproduce on our end, and we are investigating this" hopefully they can fix this. So many issues they investigate to no result.

    Tags

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

package {

// ----------------------------------------------------------------------------------------------------
// インポート
//
    import com.adobe.utils.AGALMiniAssembler;
    import com.adobe.utils.PerspectiveMatrix3D;
    
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Graphics;
    import flash.display.Loader;
    import flash.display.LoaderInfo;
    import flash.display.Shape;
    import flash.display.SimpleButton;
    import flash.display.Sprite;
    import flash.display.Stage3D;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.display.TriangleCulling;
    import flash.display3D.Context3D;
    import flash.display3D.Context3DCompareMode;
    import flash.display3D.Context3DProgramType;
    import flash.display3D.Context3DRenderMode;
    import flash.display3D.Context3DTextureFormat;
    import flash.display3D.Context3DVertexBufferFormat;
    import flash.display3D.IndexBuffer3D;
    import flash.display3D.Program3D;
    import flash.display3D.VertexBuffer3D;
    import flash.display3D.textures.Texture;
    import flash.events.ErrorEvent;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.events.KeyboardEvent;
    import flash.events.MouseEvent;
    import flash.external.ExternalInterface;
    import flash.geom.ColorTransform;
    import flash.geom.Matrix3D;
    import flash.geom.Vector3D;
    import flash.net.URLRequest;
    import flash.system.LoaderContext;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.text.TextFormat;
    import flash.utils.getTimer;
    
    import net.hires.debug.Stats;

    [SWF(width="465", height="465", backgroundColor="0xffffff")]
    public class Stage3DGraphicsTest extends Sprite {

// ----------------------------------------------------------------------------------------------------
// メイン処理
//
        private const IMAGE_URL:String = "http://global.yuichiroharai.com/img/GoldenRectangle.png";
        private const FPS:uint = 60; // フレームレート
        private const CANVAS_MINIMUM_SIZE:uint = 465;
        private var _canvasSize:uint;
        private var _bmpd:BitmapData;
        private var _sphericalShader:SphericalShader;
        
        // Stage3D
        private var _stage3DGPU:Stage3D;
        private var _context3DGPU:Context3D;
        private var _stage3DSoftware:Stage3D;
        private var _context3DSoftware:Context3D;
        
        // Graphics
        private var _s:Sprite;
        private var _g:Graphics;
        
        //private var _screenShot:Bitmap;

        public function Stage3DGraphicsTest() {
            Wonderfl.disable_capture();
            //addChild(_screenShot = new Bitmap());
            
            stage.frameRate = FPS;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            _initStats();
            
            // 埋め込みフォントを取得
            FontLoader.loadFont(complete1);
            
            function complete1():void {
                var loader:Loader;
                loader = new Loader();
                loader.contentLoaderInfo.addEventListener(Event.COMPLETE, complete2);
                loader.load(new URLRequest(IMAGE_URL), new LoaderContext(true));
            }
            

            function complete2(e:Event):void {
                _bmpd = Bitmap(LoaderInfo(e.target).content).bitmapData;

                stage.addEventListener(Event.RESIZE, _onResize);
                stage.addEventListener(Event.FULLSCREEN, _onResize);
                _onResize();
                _initMessage();

                // GPU
                _stage3DGPU = stage.stage3Ds[0];
                _stage3DGPU.addEventListener(Event.CONTEXT3D_CREATE, context3dCreateGPU);
                _stage3DGPU.addEventListener(ErrorEvent.ERROR, error);
                _stage3DGPU.requestContext3D(Context3DRenderMode.AUTO);
            }
            function context3dCreateGPU():void {
                _context3DGPU = _stage3DGPU.context3D;

                // SOFTWARE
                _stage3DSoftware = stage.stage3Ds[1];
                _stage3DSoftware.addEventListener(Event.CONTEXT3D_CREATE, context3dCreateSoftware);
                _stage3DSoftware.addEventListener(ErrorEvent.ERROR, error);
                _stage3DSoftware.requestContext3D(Context3DRenderMode.SOFTWARE);
            }
            
            function context3dCreateSoftware():void {
                _context3DSoftware = _stage3DSoftware.context3D;
                
                _initDraw();
                _initOption();
            }
            
            function error():void {
                _changeMessage("STAGE3D IS NOT ENABLED ON THIS PC...", false);
                _textFieldMessage.visible = true;
            }
        }

        /**
         * ステージリサイズ時の処理
         */
        private function _onResize(e:Event=null):void {
            _canvasSize = (stage.stageWidth > stage.stageHeight) ? int(stage.stageHeight/2)*2 : int(stage.stageWidth/2)*2;
            if (_canvasSize < CANVAS_MINIMUM_SIZE) _canvasSize = CANVAS_MINIMUM_SIZE;
            _resizeStage3D();
            _resizeGraphics();
            _moveOption();
            _moveMessage();
            if (_textMethod != null) _textMethod.text = _canvasSize.toString();
        }

        
// ----------------------------------------------------------------------------------------------------
// 共通処理
//
        private static const DIV_LIST:Vector.<uint> = Vector.<uint>([2, 4, 8, 16, 32]);
        private var _3DRotationY:Number = 0;
        private var _3DRotationX:Number = 0;
        private var _time:uint;
        private var _drawMethod:String = "gpu"; // gpu=Stage3D(GPU), software=Stage3D, graphics=Graphics
        private var _matrix3D:Matrix3D;
        private var _currentDiv:uint = 2;
        
        private function _initDraw():void {
            // 透視投影変換
            _perspectiveMatrix3D = new PerspectiveMatrix3D();
            _perspectiveMatrix3D.perspectiveFieldOfViewLH(45*Math.PI/180, 1, 0.1, 1000);
            
            _changeDraw();
            initStage3D();
            initGraphics();
            addEventListener(Event.ENTER_FRAME, _onDraw);
            stage.addEventListener(KeyboardEvent.KEY_DOWN, _switchDraw);
            _textFieldMessage.visible = true;
        }
        
        private function _switchDraw(e:Event=null):void {
            if (_drawMethod == "gpu") {
                _drawMethod = "software";
                _stage3DGPU.visible = false;
                _stage3DSoftware.visible = true;
                _s.visible = false;
            } else if (_drawMethod == "software") {
                _drawMethod = "graphics";
                _stage3DGPU.visible = false;
                _stage3DSoftware.visible = false;
                _s.visible = true;
            } else {
                _drawMethod = "gpu";
                _stage3DGPU.visible = true;
                _stage3DSoftware.visible = false;
                _s.visible = false;
            }
            _changeOptionMethod();
        }
        
        private function _changeDraw(e:Event=null):void {
            // 頂点情報
            _sphericalShader = new SphericalShader(1, 60, 60, DIV_LIST[_currentDiv], DIV_LIST[_currentDiv]);
            _sphericalShader.setVertex(0, 0);
            _sphericalShader.setVertex(90, 0);
            _sphericalShader.setVertex(180, 0);
            _sphericalShader.setVertex(270, 0);
            _sphericalShader.setVertex(0, 90);
            _sphericalShader.setVertex(0, -90);
            
            _changeStage3D();
            _changeGraphics();
        }
        
        private function _onDraw(e:Event):void {
            var currentTime:uint;
            
            // 球面全体の回転
            _time = (currentTime = getTimer()) - _time;
            if ((_3DRotationY += 0.12*_time) > 360) _3DRotationY -= 360;
            if ((_3DRotationX += 0.09*_time) > 360) _3DRotationX -= 360;
            _time = currentTime;
            
            // matrix3Dを設定
            _matrix3D = new Matrix3D();
            _matrix3D.appendRotation(_3DRotationY, Vector3D.Y_AXIS);
            _matrix3D.appendRotation(_3DRotationX, Vector3D.X_AXIS);
            _matrix3D.appendTranslation(0, 0, 3);
            _matrix3D.append(_perspectiveMatrix3D);
            if (_drawMethod == "gpu") {
                _drawStage3D();
            } else if (_drawMethod == "software") {
                _drawStage3D();
            } else {
                _matrix3D.appendScale(_canvasSize/2, _canvasSize/2, _canvasSize/2);
                _drawGraphics();
            }        
        }
        
        
// ----------------------------------------------------------------------------------------------------
// Stage3D
//
        private static const AGAL_VERTEX:String = "m44 op, va0, vc0\n" + "mov v0, va1";
        private static const AGAL_FRAGMENT:String = "tex ft1, v0, fs0 <2d,nomip,linear,clamp>\n" + "mov oc, ft1";
        private static const ANTI_ALIAS:uint = 2;

        private var _perspectiveMatrix3D:PerspectiveMatrix3D;
        
        /**
         * Stage3D関連の初期化
         */
        private function initStage3D():void {
            var vertexAssembler:AGALMiniAssembler, fragmentAssembler:AGALMiniAssembler, program3D:Program3D,
                texture:Texture;

            //_context3DGPU.enableErrorChecking = true;
            //_context3DSoftware.enableErrorChecking = true;
            _resizeStage3D();
            _context3DGPU.setDepthTest(true, Context3DCompareMode.LESS);
            _context3DSoftware.setDepthTest(true, Context3DCompareMode.LESS);
            
            // AGAL
            vertexAssembler = new AGALMiniAssembler();
            vertexAssembler.assemble(Context3DProgramType.VERTEX, AGAL_VERTEX);
            fragmentAssembler = new AGALMiniAssembler();
            fragmentAssembler.assemble(Context3DProgramType.FRAGMENT, AGAL_FRAGMENT);
            program3D = _context3DGPU.createProgram();
            program3D.upload(vertexAssembler.agalcode, fragmentAssembler.agalcode);
            _context3DGPU.setProgram(program3D);
            vertexAssembler = new AGALMiniAssembler();
            vertexAssembler.assemble(Context3DProgramType.VERTEX, AGAL_VERTEX);
            fragmentAssembler = new AGALMiniAssembler();
            fragmentAssembler.assemble(Context3DProgramType.FRAGMENT, AGAL_FRAGMENT);
            program3D = _context3DSoftware.createProgram();
            program3D.upload(vertexAssembler.agalcode, fragmentAssembler.agalcode);
            _context3DSoftware.setProgram(program3D);
            
            // テクスチャ
            texture = _context3DGPU.createTexture(_bmpd.width, _bmpd.height, Context3DTextureFormat.BGRA, false);
            texture.uploadFromBitmapData(_bmpd, 0);
            _context3DGPU.setTextureAt(0, texture);
            texture = _context3DSoftware.createTexture(_bmpd.width, _bmpd.height, Context3DTextureFormat.BGRA, false);
            texture.uploadFromBitmapData(_bmpd, 0);
            _context3DSoftware.setTextureAt(0, texture);
            
            _changeStage3D();
        }
        
        private function _changeStage3D():void {
            var vertexBuffer:VertexBuffer3D;
            
            // 頂点リストのアップロード
            vertexBuffer = _context3DGPU.createVertexBuffer(_sphericalShader.vertexList.length/5, 5);
            vertexBuffer.uploadFromVector(_sphericalShader.vertexList, 0, _sphericalShader.vertexList.length/5);
            _context3DGPU.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
            _context3DGPU.setVertexBufferAt(1, vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
            
            vertexBuffer = _context3DSoftware.createVertexBuffer(_sphericalShader.vertexList.length/5, 5);
            vertexBuffer.uploadFromVector(_sphericalShader.vertexList, 0, _sphericalShader.vertexList.length/5);
            _context3DSoftware.setVertexBufferAt(0, vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
            _context3DSoftware.setVertexBufferAt(1, vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
        }

        private function _resizeStage3D():void {
            if (_context3DGPU == null || _context3DSoftware == null) return;
            _stage3DGPU.x = _stage3DSoftware.x = int((stage.stageWidth - _canvasSize)/2);
            _stage3DGPU.y = _stage3DSoftware.y = int((stage.stageHeight - _canvasSize)/2);
            _context3DGPU.configureBackBuffer(_canvasSize, _canvasSize, ANTI_ALIAS, true);
            _context3DSoftware.configureBackBuffer(_canvasSize, _canvasSize, ANTI_ALIAS, true);
            
            /*if (_screenShot.bitmapData != null) _screenShot.bitmapData.dispose(); 
            _screenShot.bitmapData = new BitmapData(_canvasSize, _canvasSize, false, 0);   
            _screenShot.x = _stage3DGPU.x;
            _screenShot.y = _stage3DGPU.y;*/
        }
        
        private function _drawStage3D():void {
            var indexBuffer:IndexBuffer3D;

            if (_drawMethod == "gpu") {
                // Matrix3Dの適用
                _context3DGPU.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, _matrix3D, true);
                // インデックスリストのアップロード
                indexBuffer = _context3DGPU.createIndexBuffer(_sphericalShader.indexList.length);
                indexBuffer.uploadFromVector(_sphericalShader.indexList, 0, _sphericalShader.indexList.length);
                // 描画
                _context3DGPU.clear(0.4, 0.6, 0.8);
                _context3DGPU.drawTriangles(indexBuffer, 0, _sphericalShader.indexList.length/3);
                //_context3DGPU.drawToBitmapData(_screenShot.bitmapData);
                _context3DGPU.present();
            } else {
                // Matrix3Dの適用
                _context3DSoftware.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, _matrix3D, true);
                // インデックスリストのアップロード
                indexBuffer = _context3DSoftware.createIndexBuffer(_sphericalShader.indexList.length);
                indexBuffer.uploadFromVector(_sphericalShader.indexList, 0, _sphericalShader.indexList.length);
                // 描画
                _context3DSoftware.clear(0.5, 0.7, 0.3);
                _context3DSoftware.drawTriangles(indexBuffer, 0, _sphericalShader.indexList.length/3);
                _context3DSoftware.present();
            }
        }


// ----------------------------------------------------------------------------------------------------
// Graphics
//
        private var _gVertexList:Vector.<Number>;
        private var _gUvList:Vector.<Number>;
        private var _gIndexList:Vector.<int>;
        private var _gBorder:Boolean = false;
        
        /**
         * Graphics関連の初期化
         */
        private function initGraphics():void {
            _s = new Sprite();
            _g = _s.graphics;
            addChildAt(_s, 0);
            _s.addEventListener(MouseEvent.CLICK, _switchGraphicsBorder);
            
            _changeGraphics();
            _resizeGraphics();
        }
        
        private function _changeGraphics():void {
            var i:uint, j:uint, len:uint;
            
            _gVertexList =  new Vector.<Number>();
            _gUvList = new Vector.<Number>();
            len = _sphericalShader.vertexList.length/5;
            for (i=0;i<len;i++) {
                j = i*5;
                _gVertexList.push(_sphericalShader.vertexList[j], _sphericalShader.vertexList[j+1], _sphericalShader.vertexList[j+2]);
                _gUvList.push(_sphericalShader.vertexList[j+3], _sphericalShader.vertexList[j+4]);
            }
            _gIndexList = Vector.<int>(_sphericalShader.indexList);
        }
        
        private function _switchGraphicsBorder(e:Event=null):void {
            _gBorder = !_gBorder;
        }
        
        private function _resizeGraphics():void {
            if (_s == null || _g == null) return;
            _s.x = int(stage.stageWidth/2);
            _s.y = int(stage.stageHeight/2);
        }
        
        private function _drawGraphics():void {
            var i:uint, j:uint, len:uint, v:Vector3D, vertex2DList:Vector.<Number>;

            vertex2DList = new Vector.<Number>();
            len = _gVertexList.length/3;
            for (i=0;i<len;i++) {
                j = i*3;
                v = _matrix3D.transformVector(new Vector3D(_gVertexList[j], _gVertexList[j+1], _gVertexList[j+2], 1));
                vertex2DList.push(v.x/v.w, -v.y/v.w);
            }
            _g.clear();
            _g.beginFill(0xB380B3, 1);
            _g.drawRect(-_canvasSize/2, -_canvasSize/2, _canvasSize, _canvasSize);
            if (_gBorder) _g.lineStyle(1, 0xffcc33, 1);
            _g.beginBitmapFill(_bmpd, null, false, true);
            _g.drawTriangles(vertex2DList, _gIndexList, _gUvList, TriangleCulling.POSITIVE); // 裏面
            _g.drawTriangles(vertex2DList, _gIndexList, _gUvList, TriangleCulling.NEGATIVE); // 表面
            _g.endFill();
        }

        
// ----------------------------------------------------------------------------------------------------
// オプション
//
        private const SLIDE_BAR_WIDTH:uint = 80;
        private var _spOption:Sprite;
        private var _textMethod:TextField;
        private var _slideBar:SlideBar;
        private var _textSlideBar:TextField;
        
        /**
         * オプションを初期化
         */
        private function _initOption():void {
            var text:TextField;
            
            _spOption = new Sprite();
            //addChildAt(_spOption, 2);
            addChildAt(_spOption, 1);
            
            // 描画タイプ
            text = new TextField();
            text.defaultTextFormat = new TextFormat(FontLoader.FONT_NAME_KROEGER_0555, 8, 0xcccccc);
            text.embedFonts = true;
            text.autoSize = TextFieldAutoSize.LEFT;
            text.multiline = false;
            text.selectable = false;
            text.text = "METHOD:";
            text.x = -2;
            text.y = 0;
            _spOption.addChild(text);
            _textMethod = new TextField();
            _textMethod.defaultTextFormat = new TextFormat(FontLoader.FONT_NAME_KROEGER_0655, 8, 0xffffff);
            _textMethod.embedFonts = true;
            _textMethod.autoSize = TextFieldAutoSize.LEFT;
            _textMethod.multiline = false;
            _textMethod.selectable = false;
            _textMethod.x = text.width;
            _textMethod.y = -2;
            _spOption.addChild(_textMethod);
            _changeOptionMethod();
            
            // 三角形の数
            text = new TextField();
            text.defaultTextFormat = new TextFormat(FontLoader.FONT_NAME_KROEGER_0555, 8, 0xcccccc);
            text.embedFonts = true;
            text.autoSize = TextFieldAutoSize.LEFT;
            text.multiline = false;
            text.selectable = false;
            text.text = "TRIANGLES:";
            text.x = -2;
            text.y = 15;
            _spOption.addChild(text);
            _textSlideBar = new TextField();
            _textSlideBar.defaultTextFormat = new TextFormat(FontLoader.FONT_NAME_KROEGER_0655, 8, 0xffffff);
            _textSlideBar.embedFonts = true;
            _textSlideBar.autoSize = TextFieldAutoSize.LEFT;
            _textSlideBar.multiline = false;
            _textSlideBar.selectable = false;
            _textSlideBar.text = ((DIV_LIST[_currentDiv])*(DIV_LIST[_currentDiv])*2*6).toString();
            _textSlideBar.x = text.width;
            _textSlideBar.y = 13;
            _spOption.addChild(_textSlideBar);
            _slideBar = new SlideBar(SLIDE_BAR_WIDTH, DIV_LIST.length-1, SlideBar.createBar(7, 15, 0xffffffff), SlideBar.createRail(SLIDE_BAR_WIDTH, 1, 0xa0ffffff));
            _slideBar.value = _currentDiv;
            _slideBar.onChange = onChange;
            _slideBar.y = 40;
            _spOption.addChild(_slideBar);

            _moveOption();
            
            function onChange(value:uint):void {
                _currentDiv = value;
                _changeDraw();
                _textSlideBar.text = ((DIV_LIST[_currentDiv])*(DIV_LIST[_currentDiv])*2*6).toString();
            }
        }
        
        /**
         * 描画方法の表示を更新
         */
        private function _changeOptionMethod():void {
            if (_spOption == null) return;
            if (_drawMethod == "gpu") {
                _textMethod.text = "GPU";
                _changeMessage("PRESS ANY KEY TO SWITCH BETWEEN STAGE3D(GPU), STAGE3D (SOTWARE) AND GRAPHICS.", true);
            } else if (_drawMethod == "software") {
                _textMethod.text = "SOFTWARE";
                _changeMessage("PRESS ANY KEY TO SWITCH BETWEEN STAGE3D(GPU), STAGE3D (SOTWARE) AND GRAPHICS.", true);
            } else {
                _textMethod.text = "GRAPHICS";
                _changeMessage("PRESS ANY KEY TO SWITCH BETWEEN STAGE3D(GPU), STAGE3D (SOTWARE) AND GRAPHICS.\nCLICK THE CANVAS TO SWITCH BORDER VISIBLITY.", true);
            }
        }
        
        /**
         * スライドバーの配置を更新
         */
        private function _moveOption():void {
            if (_spOption == null) return;
            _spOption.x = int(_stage3DGPU.x + _canvasSize - _spOption.width - 20);
            _spOption.y = int(_stage3DGPU.y + 5);
        }

        

// ----------------------------------------------------------------------------------------------------
// 画面にメッセージを表示
//
        private var _textFieldMessage:TextField;
        private var _messageTime:uint;
        private var _messageBottom:Boolean=false;

        /**
         * メッセージ表示を初期化
         */
        private function _initMessage():void {
            _textFieldMessage = new TextField;
            _textFieldMessage.defaultTextFormat = new TextFormat(FontLoader.FONT_NAME_KROEGER_0655, 8, 0xffffff);
            _textFieldMessage.embedFonts = true;
            _textFieldMessage.autoSize = TextFieldAutoSize.LEFT;
            _textFieldMessage.multiline = true;
            _textFieldMessage.selectable = false;
            _textFieldMessage.visible = false;
            addChild(_textFieldMessage);
            _moveMessage();
        }
        /**
         * メッセージ表示のテキストを変更
         * 
         * @param    text    表示メッセージ
         * @param    bottom    画面の下にメッセージを表示するかどうか
         */
        private function _changeMessage(text:String, bottom:Boolean=false):void {
            _messageBottom = bottom;
            _textFieldMessage.text = text;
            _moveMessage();
        }
        /**
         * メッセージ表示の配置を更新
         */
        private function _moveMessage():void {
            if (_textFieldMessage == null) return;
            _textFieldMessage.x = int((stage.stageWidth - _textFieldMessage.width)/2);
            _textFieldMessage.y = (_messageBottom) ? int(_stage3DGPU.y + _canvasSize - _textFieldMessage.height/2 - 15) : int((stage.stageHeight - _textFieldMessage.height)/2);
        }
        /**
         * メッセージ表示の点滅を開始
         */
        private function _showMessage():void {
            addEventListener(Event.ENTER_FRAME, _blinkMessage);
            _textFieldMessage.visible = true;
        }
        /**
         * メッセージ表示の点滅を終了
         */
        private function _hideMessage():void {
            removeEventListener(Event.ENTER_FRAME, _blinkMessage);
            _textFieldMessage.visible = false;
        }
        /**
         * メッセージ表示を一定間隔で点滅
         * 
         * @param    e    イベント
         */
        private function _blinkMessage(e:Event):void {
            var timer:uint;

            if ((timer = getTimer()) - _messageTime > 25) {
                _messageTime = timer;
                _textFieldMessage.visible = !_textFieldMessage.visible;
            }
        }


// ----------------------------------------------------------------------------------------------------
// スタッツ
//
        /**
         * スタッツを表示
         */
        public function _initStats():void {
            addChild(new Stats());
        }

    }
}


// ----------------------------------------------------------------------------------------------------
// 3Dの球面の頂点情報を管理するクラス
//    
import flash.geom.Matrix3D;
import flash.geom.Vector3D;

class SphericalShader {

    private static const PI2:Number = Math.PI*2;
    
    private var _radius:Number;
    private var _width:Number;
    private var _height:Number;
    private var _segmentX:uint = 2;
    private var _segmentY:uint = 2;
    public var segmentXP:uint;
    public var segmentYP:uint;
    private var _vertexLength:uint;
    private var _surfaceNum:uint=0;
    
    private var _textureDivX:uint = 1;
    private var _textureDivY:uint = 1;

    public var vertexList:Vector.<Number>;
    public var indexList:Vector.<uint>;

    /**
     * 球体の半径、球面のX軸とY軸方向の長さ、断面の数を指定してインスタンスを生成します。
     * 
     * @param    radius        球面の半径 - 球座標の半径
     * @param    width        球面のX軸方向(Y軸周り)の長さを表す回転角度
     * @param    height        球面のY軸方向(X軸周り)の長さを表す回転角度
     * @param    segmentX    球面上のX軸方向(Y軸周り)の断面(四角形)の数
     * @param    segmentY    球面上のY軸方向(X軸周り)の断面(四角形)の数
     */
    public function SphericalShader(radius:Number, width:Number, height:Number, segmentX:uint=2, segmentY:uint=2) {
        _radius = (radius > 0 && radius < 1) ? radius : 1;
        _width =  (width > 0) ? width : PI2;
        _height =  (height > 0) ? height : PI2;
        if (segmentX > 1) _segmentX = segmentX;
        if (segmentY > 1) _segmentY = segmentY;
        segmentXP = _segmentX + 1;
        segmentYP = _segmentY + 1;
        _vertexLength = segmentXP*segmentYP;        
        vertexList = new Vector.<Number>();
        indexList = new Vector.<uint>();
    }

    /**
     * 球面の中心点を指定して頂点の位置を計算し、内部データに頂点情報を追加します。
     * 球面の中心点は、真正面(x=0, y=0, z=-1)を基準点とし、画面の右側がX軸の正方向、上側がY軸の正方向です。
     * 
     * @param    rotationX        球面の中心点のX軸方向(Y軸周り)の回転角度
     * @param    rotationY        球面の中心点のY軸方向(X軸周り)の回転角度
     */
    public function setVertex(rotationX:Number, rotationY:Number):void {
        var i:uint, j:uint, angleX:Number, angleY:Number, offset:uint, m:Matrix3D, v:Vector3D;

        m = new Matrix3D();
        // 頂点リストにデータを追加
        for (j=0;j<segmentYP;j++) {
            angleY = _height*j/_segmentY - _height/2;
            
            for (i=0;i<segmentXP;i++) {
                angleX = _width*i/_segmentX - _width/2;
                
                // 真正面(x=0, y=0, z=-1)を基準点として球面の頂点を生成
                v = new Vector3D(0, 0, -_radius, 0);
                m.identity();
                m.appendRotation(-angleX, Vector3D.Y_AXIS);
                m.appendRotation(-angleY, Vector3D.X_AXIS);
                v = m.deltaTransformVector(v);
                
                // 球面の中心点を指定角度分ずらす
                v = new Vector3D(v.x, v.y, v.z, 0);
                m.identity();
                m.appendRotation(-rotationX, Vector3D.Y_AXIS);
                m.appendRotation(rotationY, Vector3D.X_AXIS);
                v = m.deltaTransformVector(v);
                
                vertexList.push(v.x, v.y, v.z, i/_segmentX, j/_segmentY);
            }
        }
        
        // インデックスリストにデータを追加
        offset = _surfaceNum*_vertexLength;
        for (j=0;j<_segmentY;j++) {
            for (i=0;i<_segmentX;i++) {            
                indexList.push(j*segmentXP + i + offset, (j+1)*segmentXP + i+1 + offset, (j+1)*segmentXP + i + offset);
                indexList.push(j*segmentXP + i + offset, j*segmentXP + i+1 + offset, (j+1)*segmentXP + i+1 + offset);
            }
        }
        
        // 面の数をインクリメント
        _surfaceNum++;
    }
}


// ----------------------------------------------------------------------------------------------------
// スライドバー
//
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.SimpleButton;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;

/**
 * スライドバー(X軸)を構成するクラスです。
 */
class SlideBar extends Sprite {

    private var _bar:SimpleButton;
    private var _rail:Sprite;    
    private var _width:uint;
    private var _range:uint;
    
    /**
     * バーの値です。
     */
    public function get value():uint {
        return _value;
    }
    public function set value(v:uint):void {
        _value = (v > _range) ? _range : v;
        _barMoveJust();
        if (onChange is Function) onChange.apply(null, [_value]);
    }
    private var _value:uint = 0;
    
    /**
     * バーの移動係数です。
     */
    public function get moveFactor():Number {
        return _moveFactor;
    }
    public function set moveFactor(v:Number):void {
        _moveFactor = (v >= 0 && v <= 1) ? v : 0.5;
    }
    private var _moveFactor:Number = 0.5;

    private var _x:Number=0;
    private var _moving:Boolean=false;
    
    /**
     * SlideBarの値が変更された時に呼び出されるコールバック関数です。
     */
    public var onChange:Function;
    
// ----------------------------------------------------------------------------------------------------
// コンストラクタ
//
    /**
     * スライドバーを作成します。
     *
     * @param    width    スライドバーの幅
     * @param    range    スライドバーの値の数
     * @param    bar        スライドバーのバーを表すSimpleButtonです。
     * @param    rail    スライドバーのレールを表すSpriteです。
     */
    public function SlideBar(width:uint, range:uint, bar:SimpleButton, rail:Sprite) {
        _width = (width < 2) ? 2 : width;
        _range = (range < 2 || range > width) ? width : range;
        addChildAt(_rail = rail, 0);
        addChild(_bar = bar);
        
        _bar.addEventListener(MouseEvent.MOUSE_DOWN, _barMouseDown, false, 0, true);
    }
    

// ----------------------------------------------------------------------------------------------------
// スライドバーの操作
//
    // バーのドラッグ開始
    private function _barMouseDown(e:Event):void {
        if (stage == null) return;
        stage.addEventListener(MouseEvent.MOUSE_MOVE, _mouseMove);
        stage.addEventListener(MouseEvent.MOUSE_UP, _mouseUp);
    }
    
    // バーのドラッグ終了
    private function _mouseUp(e:Event):void {
        if (stage == null) return;
        stage.removeEventListener(MouseEvent.MOUSE_MOVE, _mouseMove);
        stage.removeEventListener(MouseEvent.MOUSE_UP, _mouseUp);
    }
    
    // バーのドラッグ
    private function _mouseMove(e:Event):void {
        var temp:uint = _value;
        
        if (stage == null) return;
        
        if (!_moving) {
            stage.addEventListener(Event.ENTER_FRAME, _barMove);
            _moving = true;
        }
        
        if (mouseX < 0) {
            _value = 0;
            _x = 0;
        } else if (mouseX > _width) {
            _value = _range;
            _x = _width;
        } else {
            _value = uint(mouseX/_width*_range + 0.5);
            _x = uint(_width*_value/_range);
        }
        
        if (temp != _value && onChange is Function) onChange.apply(null, [_value]);
    }
    
    // バーの移動
    private function _barMove(e:Event):void {
        var diff:Number, abs:Number;
        
        diff = _x - _bar.x;
        abs = (diff < 0) ? -diff : diff;
        if (abs*_moveFactor < 0.05) {
            _bar.x = _x;
            if (_moving) {
                stage.removeEventListener(Event.ENTER_FRAME, _barMove);
                _moving = false;
            }
        } else {
            _bar.x += diff*_moveFactor;
        }
    }
    
    // バーの強制移動
    private function _barMoveJust():void {
        _bar.x = _x = uint(_width*_value/_range);
        if (_moving) {
            stage.removeEventListener(Event.ENTER_FRAME, _barMove);
            _moving = false;
        }
    }
    
    /**
     * バーを作成
     *
     * @param    width    バーの幅
     * @param    range    バーの高さ
     * @param    argb    バーのARGBカラー
     */
    public static function createBar(width:uint, height:uint, argb:uint):SimpleButton {
        var bmp:Bitmap;
        
        bmp = new Bitmap(new BitmapData(width, height, true, argb));
        bmp.x =  - uint(width/2);
        bmp.y =  - uint(height/2);
        return new SimpleButton(bmp, bmp, bmp, bmp);
    }
    
    /**
     * レールを作成
     *
     * @param    width    バーの幅
     * @param    range    バーの高さ
     * @param    argb    バーのARGBカラー
     */
    public static function createRail(width:uint, height:uint, argb:uint):Sprite {
        var bmp:Bitmap, sprite:Sprite;
        
        bmp = new Bitmap(new BitmapData(width, height, true, argb));
        bmp.x =  0;
        bmp.y =  - uint(height/2);
        sprite = new Sprite();
        sprite.addChild(bmp);
        return sprite;
    }
}


// ----------------------------------------------------------------------------------------------------
// フォントのロード用クラス
//    
import flash.display.Loader;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.system.SecurityDomain;
import flash.text.Font;

class FontLoader {

    public static const FONT_CLASS_KROEGER_0555:String = "Kroeger0555";
    public static const FONT_NAME_KROEGER_0555:String = "kroeger 05_55";
    public static const FONT_CLASS_KROEGER_0655:String = "Kroeger0655";
    public static const FONT_NAME_KROEGER_0655:String = "kroeger 06_55";
    //public static const FONT_CLASS_KROEGER_0665:String = "Kroeger0665";
    //public static const FONT_NAME_KROEGER_0665:String = "kroeger 06_65";
    private static const FONT_SWF_URL:String = "http://global.yuichiroharai.com/swf/FontKroeger.swf";
    
    /**
     * フォント内蔵のSWFをロード
     * 
     * @param    callback    ロード完了時のコールバック関数
     */
    public static function loadFont(callBack:Function):void {
        var loader:Loader;

        loader = new Loader();
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, complete);
        loader.load(new URLRequest(FONT_SWF_URL), new LoaderContext(true, ApplicationDomain.currentDomain, SecurityDomain.currentDomain));

        function complete(e:Event):void {
            try {
                Font.registerFont(loader.contentLoaderInfo.applicationDomain.getDefinition(FONT_CLASS_KROEGER_0555) as Class);
                Font.registerFont(loader.contentLoaderInfo.applicationDomain.getDefinition(FONT_CLASS_KROEGER_0655) as Class);
            } catch (e:Error) { return; }
            
            loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, complete);
            loader = null;
            if (callBack is Function) callBack.apply();
        }
    }
}