座標変換

座標変換




仮想描画領域座標とスクリーン座標を変換するメソッドを作ります。

そもそも仮想描画領域座標とはなんぞや?
という話からしていきます。

描画する位置を指定するときにスクリーンの位置を指定して描画しますね。
例えば「(100,200)の位置に描画しろ」といった具合です。
一方、アンドロイドの端末やPCではディスプレイサイズがマシンによって違いますね。
もしも描画指定する座標の最大値がディスプレイよりも小さければ端っこの方が何も描画されません。
ディスプレイよりも大きな位置に描画しようとすると表示すらされません。
これでは困ります。。。

スクリーン座標を0~1で扱えば画面のサイズを気にせず座標を指定できます。
0~1に収めてしまえばとりあえず表示されるので。。。
ただ、細かい指定をするときに0.0001234とか指定したくないですね。

それなら座標を0~任意のサイズにしてしまえば扱いやすくなりそうです。
これが仮想描画領域です。

さらに、仮想描画領域で指定すれば画面比率も気にしなくて済みます!
よく640x480で作ったフリゲがワイドスクリーンでびろ~んと横長になっているのをみますが
仮想描画領域座標を使えばこうした問題を解決できます!

まずはスクリーンサイズを入れて仮想描画領域座標を設定するところを見てみましょう。

    //  スクリーン変換行列更新
    public void UpdateScreenMatrix( GL10 gl, int width, int height, int statusBarHeight )
    {
        final float drawWidth   = DrawDevice.DRAW_WIDTH;
        final float drawHeight  = DrawDevice.DRAW_HEIGHT;
        {
            //  拡大率更新
            final float widthScale  = width / drawWidth;
            final float heightScale = height / drawHeight;

            m_DrawScaleRate = Math.min( widthScale, heightScale );
        }

        final int       viewWidth   = ( int )( drawWidth * m_DrawScaleRate );
        final int       viewHeight  = ( int )( drawHeight * m_DrawScaleRate );
        {
            //  描画オフセット更新
            m_DrawOffset.x  = ( int )( width * 0.5f - viewWidth * 0.5f );
            m_DrawOffset.y  = ( int )( height * 0.5f - viewHeight * 0.5f );
        }

        gl.glViewport( m_DrawOffset.x, m_DrawOffset.y, viewWidth, viewHeight );
        //gl.glScissor( m_DrawOffset.x, m_DrawOffset.y, viewWidth, viewHeight );
        //gl.glEnable( GL10.GL_SCISSOR_TEST );

        gl.glMatrixMode( GL10.GL_PROJECTION );
        gl.glLoadIdentity();
        gl.glOrthof( 0.0f, drawWidth, drawHeight, 0.0f, 1.0f, 10.0f );

        m_StatusBarHeight   = statusBarHeight;
    }

ちょっと長いので分けて説明していきます。

        final float drawWidth   = DrawDevice.DRAW_WIDTH;
        final float drawHeight  = DrawDevice.DRAW_HEIGHT;
        {
            //  拡大率更新
            final float widthScale  = width / drawWidth;
            final float heightScale = height / drawHeight;

            m_DrawScaleRate = Math.min( widthScale, heightScale );
        }

この部分で拡大率を計算しています。
drawWidth、drawHeightが仮想描画領域のサイズになります。
ここではDrawDeviceクラスに定数を指定していますが、
外から渡してもらってその値を設定するようにしたほうがいいかもです。

拡大率は縦横を0~1に収めるための値です。
縦横比を一定にするために縦か横の小さい方の比率を使います。

        final int       viewWidth   = ( int )( drawWidth * m_DrawScaleRate );
        final int       viewHeight  = ( int )( drawHeight * m_DrawScaleRate );
        {
            //  描画オフセット更新
            m_DrawOffset.x  = ( int )( width * 0.5f - viewWidth * 0.5f );
            m_DrawOffset.y  = ( int )( height * 0.5f - viewHeight * 0.5f );
        }

ここでは実際に描画するときにX、Y位置の最小位置を計算します。
ここで出てきたスクリーン上の位置が描画領域の0の場所になります。

        gl.glViewport( m_DrawOffset.x, m_DrawOffset.y, viewWidth, viewHeight );
        //gl.glScissor( m_DrawOffset.x, m_DrawOffset.y, viewWidth, viewHeight );
        //gl.glEnable( GL10.GL_SCISSOR_TEST );

glViewportでビューポートの設定を行います。
この設定を行うことで画面外の表示を切り取ることができます。
なお、下のコメントアウトされているglScissorとglEnableは
画面外にピクセルを書き込まない設定らしいのですが
これを使うと実機上で全然関係ないピクセルが描画されるようなのでコメントアウトしています。

        gl.glMatrixMode( GL10.GL_PROJECTION );
        gl.glLoadIdentity();
        gl.glOrthof( 0.0f, drawWidth, drawHeight, 0.0f, 1.0f, 10.0f );

        m_StatusBarHeight   = statusBarHeight;

プロジェクション行列を設定します。
ここで指定するのは仮想描画領域座標の値です。

最後に、ステータスバーの高さを保存しておきます。

次にスクリーン座標から仮想描画領域座標に変換するメソッドを作りましょう。
これがあればタッチした位置を仮想描画領域座標に変換できます。

    //  スクリーン座標を描画座標に変換
    public PointF   ScreenPosToDrawPos( final PointF pos )
    {
        PointF  newPos  = new PointF( pos.x, pos.y );
        {
            newPos.x    -= m_DrawOffset.x;
            newPos.y    -= m_DrawOffset.y + m_StatusBarHeight;

            newPos.x    /= m_DrawScaleRate;
            newPos.y    /= m_DrawScaleRate;
        }
        return newPos;
    }

実に単純です。
スクリーンの基準位置を仮想描画領域座標の0位置にして拡大率で割るだけです。

これで描画される座標系を気にしなくてよくなりました。

次は各種設定、描画開始、終了メソッドを実装します。
描画はまだかいな。。。



<前のページ
次のページ>