クラス DeprecatedApp の実装

本稿で述べるクラス DeprecatedApp は、あくまでも過去に制作したOpenGL 3.0 以前の機能で実装した PyOpenGL プログラムをリファクタリングするためのみに利用する。

もう少し具体的に説明すると、そのような古臭いソースコードが何十枚とある。そこでの処理で似通った部分、例えば

  • 描画処理開始直後のモデルビュー行列設定、

  • ウィンドウサイズ変更時におけるビューポートの更新処理

は deprecated features を用いた実装方法で共通化したい。言い換えると、OpenGL 3.0 以降の API と混在したコードを持ちたくないので、クラス DeprecatedApp とクラス ModernApp のようなものを設けることにした。

クラス DeprecatedApp

まずクラス全景を示す。

#!/usr/bin/env python
"""deprecatedapp.py: Do not use.

References:
  * rndblnch / opengl-programmable
    <http://bitbucket.org/rndblnch/opengl-programmable>
  * OpenGLBook.com
    <http://openglbook.com/chapter-4-entering-the-third-dimension.html>
  * Tutorials for modern OpenGL (3.3+)
    <http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/>
"""
# pylint: disable=unused-argument, no-self-use
import sys
import numpy as np
import OpenGL.GL as GL
import OpenGL.GLU as GLU
from appbase import AppBase

class DeprecatedApp(AppBase):
    """The base class for classes that use some deprecated features
    of OpenGL 3.0.
    """

    def __init__(self, **kwargs):
        """Initialize an instance of class DeprecatedApp."""

        kwargs.setdefault('context_version', (2, 1))
        super(DeprecatedApp, self).__init__(**kwargs)

        self.rotation_matrix = np.identity(4)

    def init_program(self):
        """Setup shaders."""
        self.program_manager = None

    def get_shader_sources(self):
        """Return shader sources."""
        return {}

    def update_projection(self, fovy, width, height):
        """Update the projection matrix."""

        GL.glMatrixMode(GL.GL_PROJECTION)
        GL.glLoadIdentity()
        GLU.gluPerspective(fovy, width / height, self.znear, self.zfar)
        self.fovy = fovy

    def update_rotation(self, quat=None):
        """Update the model transform."""

        if quat is None:
            # Initial update.
            self.rotation_matrix = np.identity(4)
        else:
            self.quat = quat
            self.rotation_matrix[:3, :3] = quat.transform.transpose()

    def set_modelview_matrix(self):
        """Set the modelview matrix with deprecated GL commands."""

        GL.glLoadIdentity()
        GLU.gluLookAt(
            self.camera_eye[0], self.camera_eye[1], self.camera_eye[2],
            self.camera_center[0], self.camera_center[1], self.camera_center[2],
            self.camera_up[0], self.camera_up[1], self.camera_up[2])
        GL.glMultMatrixf(self.rotation_matrix)

def main(args):
    """The main function."""

    app = DeprecatedApp()
    app.run(sys.argv)

if __name__ == "__main__":
    sys.exit(main(sys.argv))

各メソッドはほぼすべてベースクラス AppBase からのオーバーライドとなるが、その実装は OpenGL 3.0 以前の機能で実現する。

以下、特徴的なメソッドの説明をする。

メソッド __init__

        """Initialize an instance of class DeprecatedApp."""

        kwargs.setdefault('context_version', (2, 1))
        super(DeprecatedApp, self).__init__(**kwargs)

        self.rotation_matrix = np.identity(4)

  • コンテキストバージョンを未指定の場合 (2, 1) にしておく。この値は、私の環境で認められているバージョン値の最大値で、 3.0 を超えない値、という意味だ。

  • モデル回転用の行列として、メンバーデータ self.rotation_matrix を導入する。ベースクラスに同じ目的のメンバーデータ self.quat が既にあるので本来は要らないものだが、四元数オブジェクトから行列オブジェクトを生成する処理が高くつく可能性があるので、キャッシュ的に置く。

メソッド update_projection

        """Update the projection matrix."""

        GL.glMatrixMode(GL.GL_PROJECTION)
        GL.glLoadIdentity()
        GLU.gluPerspective(fovy, width / height, self.znear, self.zfar)
        self.fovy = fovy

ご覧のとおり、古典的な射影行列の設定処理である。

メソッド update_rotation

        """Update the model transform."""

        if quat is None:
            # Initial update.
            self.rotation_matrix = np.identity(4)
        else:
            self.quat = quat
            self.rotation_matrix[:3, :3] = quat.transform.transpose()

外部で計算した回転量を受け取って、メンバーデータ self.rotation_matrix に保存しておく。

クラス Quat のプロパティー transform で回転変換を表現する 3 次正規直交行列にアクセスする。これをマウスドラッグ時の優位な移動が発生する都度、OpenGL 形式の 4 次行列に作り変える。

最後の転置処理は、この self.rotation_matrixGL.glMultMatrixf にそのまま渡したいため行う。

メソッド set_modelview_matrix

        """Set the modelview matrix with deprecated GL commands."""

        GL.glLoadIdentity()
        GLU.gluLookAt(
            self.camera_eye[0], self.camera_eye[1], self.camera_eye[2],
            self.camera_center[0], self.camera_center[1], self.camera_center[2],
            self.camera_up[0], self.camera_up[1], self.camera_up[2])
        GL.glMultMatrixf(self.rotation_matrix)

メソッド set_modelview_matrix だけは、ベースクラスからのオーバーライドではない。処理は古典的なモデルビュー行列の適用である。

造りが悪いのだが、サブクラスのメソッド do_render からの呼び出しを想定している。その際には GL.glMatrixMode(GL.GL_MODELVIEW) を伴う。