※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
 
u"""
PyGame + OpenGLのテストサンプル
 
- Pキー : 平行投影/透視投影の切り替え
- Wキー : ワイヤーフレーム/ソリッド表示の切り替え
- Lキー : 照明のon/off
- Tキー : テクスチャ設定の切り替え(3種類)
- Sキー : ポイントスプライトの切り替え
- Fキー : ウインドウ/フルスクリーン切り替え
- カーソルキー : ティーポットの回転
- Bキー : Pause
- ESCキー,Qキー : 終了
 
- マウスカーソルの位置に合わせて図形の表示位置を変える。
 
参考ページ:
 
pygameに移行 — メモ庫 v1.0 documentation
http://gunload.web.fc2.com/opengl/tutorial/skinning/pygame/
 
PyOpenGL + PIL でテクスチャ貼り - 銀月の符号
http://d.hatena.ne.jp/fgshun/20080922/1222095288
 
Wiki - pygame - python game development
http://www.pygame.org/wiki/SimpleOpenGL2dClasses
 
"""
 
import pygame
from OpenGL.GL import *
from OpenGL.GLU import *
from OpenGL.GLUT import *
from OpenGL.extensions import alternate
from OpenGL.GL.ARB.point_parameters import *
from OpenGL.GL.EXT.point_parameters import *
import Image
import math
 
# 照明
light_ambient = [1.0, 1.0, 1.0, 1.0]  # 環境光(白色)
light_diffuse = [1.0, 1.0, 1.0, 1.0]  # 拡散光(白色)
light_specular = [1.0, 1.0, 1.0, 1.0]  # 鏡面光(白色)
light_position = [2.0, 2.0, 1.0, 1.0]  # 照明の位置
 
# マテリアル
no_mat = [0.0, 0.0, 0.0, 1.0]  # 反射しない
mat_ambient = [0.0, 0.0, 0.3, 1.0]  # 環境光の青成分だけ少し反射
mat_diffuse = [0.0, 1.0, 0.0, 1.0]  # 拡散光の緑成分を全反射
mat_specular = [1.0, 1.0, 1.0, 1.0]  # 鏡面光の全成分を全反射
mat_emission = [0.3, 0.3, 0.2, 0.0]  # 放射の色
no_shininess = [0.0]  # 鏡面反射しない
low_shininess = [5.0]  # 弱い鏡面反射
high_shininess = [100.0]  # 強い鏡面反射
 
glPointParameterf = alternate(
    glPointParameterf, glPointParameterfARB, glPointParameterfEXT
)
glPointParameterfv = alternate(
    glPointParameterfv, glPointParameterfvARB, glPointParameterfEXT
)
RESET_ATTENUATION = [1.0, 1.0, 1.0]
 
class PyGame:
    def __init__(self):
        """コンストラクタ"""
        self._running = True
        self._screen = None
        self.frame = 0  # フレームカウンタ
        self.mx = 0
        self.my = 0
        self.tex = []
        self.cap = ""
        self.pausefg = False
 
        # キー操作で変更するフラグ群
        self.flag2d = True  # 平行/透視投影
        self.wireframe = False  # ワイヤーフレーム/ソリッド表示
        self.light_enable = True  # 照明 on/off
        self.tex_mode = 0  # テクスチャモード
        self.point_spr = False  # ポイントスプライト on/ff
        self.fullscr = False  # フルスクリーン/ウインドウ表示
 
        self.filter_type = 0  # テクスチャのフィルタ種類(0 or 1)
        self.tex_repeat = False  # テクスチャをリピートさせるか否か
        self.line_width = 1.0  # ワイヤーフレーム表示時の線の太さ
 
        # 画面の縦横幅記録用
        self.view_wh = 1.0
        self.view_hh = 1.0
 
        # テクスチャ付きポリゴンの色指定用
        self.texcol = 0.0
        self.texcol_spd = 0.01
 
        self.angle = 0.0  # 回転角度
        self.scale = 0.0  # 拡大縮小
        self.xrot = 0.0  # 回転角度
        self.yrot = 0.0
        self.xspeed = 0.0  # 回転速度
        self.yspeed = 0.0
        self.mvfrm = 0
 
        # eventの分岐をディクショナリに変更
        self._eventMap = {
                pygame.QUIT: self.onQuit,
                pygame.MOUSEBUTTONDOWN: self.onMouseDown,
                pygame.MOUSEBUTTONUP: self.onMouseUp,
                pygame.MOUSEMOTION: self.onMouseMotion,
                pygame.KEYDOWN: self.onKeyDown,
                pygame.KEYUP: self.onKeyUp,
                }
 
    def initialize(self, w, h):
        """初期化処理"""
        pygame.init()  # PyGame初期化
        self.scrw = float(w)
        self.scrh = float(h)
 
        return self.init_gl()
 
    def init_gl(self):
        """OpenGL関係の初期化"""
        w = int(self.scrw)
        h = int(self.scrh)
 
        # OPENGL向けに初期化する
        modev = pygame.OPENGL | pygame.DOUBLEBUF
        if self.fullscr:
            modev |= pygame.FULLSCREEN
        self._screen = pygame.display.set_mode((w, h), modev)
 
        if not self._screen:
            return False
 
        glViewport(0, 0, int(self.scrw), int(self.scrh))
        glClearColor(0.0, 0.0, 0.5, 1.0)  # クリア色の設定
 
        # 隠面消去、カリングを設定
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_CULL_FACE)
        glCullFace(GL_BACK)  # 裏面をカリング
 
        # 照明の設定
        glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient)
        glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse)
        glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular)
        glLightfv(GL_LIGHT0, GL_POSITION, light_position)
        glEnable(GL_LIGHTING)  # 照明を有効化
        glEnable(GL_LIGHT0)  # 0番目の照明を有効化
 
        # テクスチャの読み込み
        glEnable(GL_TEXTURE_2D)  # テクスチャ有効化
        t = self.loadImage('res/tex1.png')
        self.tex.append(t[0])
        t = self.loadImage('res/tex2.png')
        self.tex.append(t[0])
        t = self.loadImage('res/tex4.png')
        self.tex.append(t[0])
 
        # ウインドウ内の座標値を計算して記録
        # (-w ~ 0.0 ~ +w, +h ~ 0.0 ~ -h)
        s = 8.0
        self.view_wh = s * (self.scrw / self.scrh)
        self.view_hh = s
 
        if self.flag2d:
            self.set_view2d()
        else:
            self.set_view3d()
 
        return True
 
    def loadImage(self, image_fila_path):
        """テクスチャ画像をロード"""
 
        # PyGameを使って画像ロード
        textureSurface = pygame.image.load(image_fila_path)  # 画像読み込み
        width = textureSurface.get_width()  # 横幅取得
        height = textureSurface.get_height()  # 縦幅取得
 
        # OpenGLに渡すために文字列化
        textureData = pygame.image.tostring(textureSurface, "RGBA", False)
 
        texture = glGenTextures(1)  # テクスチャを1枚生成
        glBindTexture(GL_TEXTURE_2D, texture)  # テクスチャとして登録
 
        # _S は横方向、_T は縦方向
        # テクスチャをリピートさせるかしないか
        if self.tex_repeat:
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
        else:
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
 
        # _MAG_FILTER は拡大時のフィルタ種類, _MIN_ は縮小?
        if self.filter_type == 0:
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
        else:
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
 
        # glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
        # glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
 
        # ポイントスプライトにも使えるように設定
        glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE)
 
        # テクスチャを設定
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
                     width, height, 0, GL_RGBA,
                     GL_UNSIGNED_BYTE, textureData)
 
        # アルファテストの判別関数
        # コレを入れないとポイントスプライトを透過できない
        glAlphaFunc(GL_GREATER, 0.5)
 
        return texture, width, height
 
    def set_view2d(self):
        """2D描画用に設定"""
 
        # 座標系の設定
        glMatrixMode(GL_PROJECTION)  # 射影変換
        glLoadIdentity()  # 単位行列
 
        # 以後、平行投影で描画するよう指定
        glOrtho(-self.view_wh, self.view_wh,
                - self.view_hh, self.view_hh, 0.1, 100.0)
 
        # 以下のように書けば、2Dっぽい座標系になる
        # gluOrtho2D(0, 640, 480, 0)
 
    def set_view3d(self):
        """3D描画用に設定"""
 
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
 
        # 以後、透視投影で描画するよう指定
        gluPerspective(60.0, self.scrw / self.scrh, 0.1, 100.0)
 
    def set_tex_mode(self):
        """テクスチャモードの切り替え"""
        md = [
              GL_REPLACE,
              GL_MODULATE,
              GL_DECAL
              ]
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, md[self.tex_mode])
 
    def update(self):
        """毎フレーム呼ばれる処理"""
        if not self.pausefg:
            # マウス座標を表示系の座標値に変換して記録
            wh = self.scrw / 2.0
            hh = self.scrh / 2.0
            self.tx = (self.mx - wh) / wh
            self.ty = (self.scrh - self.my - hh) / hh
            self.tx *= self.view_wh
            self.ty *= self.view_hh
 
            self.angle += 1.5  # 角度を更新
            self.scale = 2.0 * math.cos(math.radians(self.mvfrm)) + 2.5
            self.xrot += self.xspeed
            self.yrot += self.yspeed
 
            # オブジェクトの色指定用変数を変更
            self.texcol += self.texcol_spd
            if self.texcol >= 1.0 or self.texcol <= 0.0:
                self.texcol_spd *= -1
 
            self.mvfrm += 1
 
    def draw(self):
        """描画処理"""
 
        # OpenGLバッファのクリア
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
 
        # 視野変換:カメラの位置と方向のセット
        # gluLookAt(0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)
 
        # glShadeModel(GL_FLAT) # フラットシェーディングを有効化
        glShadeModel(GL_SMOOTH)  # スムースシェーディングを有効化
        glEnable(GL_NORMALIZE)  # 法線の自動正規化を有効化
 
        # 平行投影/透視投影を設定
        if self.flag2d:
            self.set_view2d()
        else:
            self.set_view3d()
 
        # 照明の有効/無効化
        if self.light_enable:
            glEnable(GL_LIGHTING)
        else:
            glDisable(GL_LIGHTING)
 
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
 
        self.draw_teapot()  # ティーポットを描画
        self.draw_ball()  # 球を描画
        self.draw_cube()  # 箱を描画
 
        glDisable(GL_LIGHTING)  # 照明無効化
 
        self.draw_point(-self.angle / 4.0, self.view_hh * 0.5)  # 点を描画
 
        x = self.tx
        y = self.ty
        w = self.view_wh
        h = self.view_hh
 
        # テクスチャ付きポリゴンを描画(テクスチャ1)
        tx = 0.0
        ty = 0.0
        tz = -60.0
        texid = 0
        self.draw_polygon_tex(tx, ty, tz, 1.0, 1.0, h, h, texid)
 
        # テクスチャ付きポリゴンを描画(テクスチャ2)
        tx = self.view_wh / 3
        tz = -40.0
        texid = 1
        self.draw_polygon_tex(tx, ty, tz, 1.0, 1.0, h, h, texid)
 
        self.set_view2d()  # 平行投影にするよう設定
 
        glDisable(GL_DEPTH_TEST)  # 隠面消去を無効
        glDisable(GL_CULL_FACE)  # カリング無効化
        glClear(GL_DEPTH_BUFFER_BIT)  # デプスバッファクリア
 
        glMatrixMode(GL_MODELVIEW)
        glLoadIdentity()
 
        self.draw_box(x, y, w, h)  # 赤いグラデの四角形を描画
        self.draw_tri(x, y, w, h)  # 緑のグラデの三角形を描画
        self.draw_line(x, y, w, h)  # 線を描画
 
        # テキストを描画
        self.draw_info()
 
        # バッファ切り替え (ダブルバッファ時)
        # glutSwapBuffers()
 
        # OpenGL描画実行 (シングルバッファ時?)
        # glFlush()
 
        # pygameダブルバッファ交換
        pygame.display.flip()
 
    def draw_text(self, x, y, st):
        """文字列をビットマップフォントで描画"""
        glColor3f(1.0, 1.0, 1.0)
        glRasterPos3f(x, y, -3.0)
        for s in st:
            glutBitmapCharacter(GLUT_BITMAP_HELVETICA_10, ord(s))
 
    def draw_info(self):
        """現在の状態をテキスト描画する"""
        lyadd = -(self.view_hh / 12.0)
        lx = -(self.view_wh * 0.95)
        ly = self.view_hh + lyadd
        strlist = []
        strlist.append(self.cap)
        strlist.append("P: " + ("Pers" if not self.flag2d else "Ortho"))
        strlist.append("W: " + ("Wireframe" if self.wireframe else "Solid"))
        strlist.append("L: Light " + ("On" if self.light_enable else "Off"))
        strlist.append("T: " + (["GL_REPLACE",
                                 "GL_MODULATE",
                                 "GL_DECAL"][self.tex_mode]))
        strlist.append("S: PointSprite " + ("On" if self.point_spr else "Off"))
        strlist.append("F: " + ("Full" if self.fullscr else "Wdw"))
        strlist.append("B: Pause")
        strlist.append("ESC,Q : EXIT")
        for s in strlist:
            self.draw_text(lx, ly, s)
            ly += lyadd
 
    def draw_polygon_tex(self, x, y, z, tx, ty, vx, vy, texid):
        """テクスチャ付きポリゴンを描画"""
        col = self.texcol
 
        glFrontFace(GL_CCW)  # 頂点反時計回りを表として扱う
 
        # 頂点配列を作成
        vertices = [-1.0, -1.0, 0.0,  # 左下
                    1.0, -1.0, 0.0,  # 右下
                    1.0, 1.0, -25.0,  # 右上
                    - 1.0, 1.0, -25.0  # 左上
                    ]
 
        # テクスチャ座標配列を作成
        texcoords = [0.0, 1.0,
                     1.0, 1.0,
                     1.0, 0.0,
                     0.0, 0.0
                     ]
 
        # カラー配列を作成
        colors = [col, col, 0.5, 1.0,
                  col, col, 0.5, 1.0,
                  col, col, 0.0, 1.0,
                  col, col, 0.0, 1.0
                  ]
 
        # 使うテクスチャを選択
        glBindTexture(GL_TEXTURE_2D, self.tex[texid])
 
        # テクスチャを透過にする
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        glEnable(GL_BLEND);
 
        glLoadIdentity()
        glTranslatef(x, y, z)  # 平行移動
        glRotatef(self.angle / 2.0, 0.0, 0.0, 1.0)  # 回転
        glScale(vx, vy, 1.0)  # 拡大縮小
 
        # 頂点配列、テクスチャ座標配列、カラー配列有効化
        glEnableClientState(GL_VERTEX_ARRAY)
        glEnableClientState(GL_TEXTURE_COORD_ARRAY)
        glEnableClientState(GL_COLOR_ARRAY)
 
        # 頂点配列、テクスチャ座標配列、カラー配列を指定
        glVertexPointer(3, GL_FLOAT, 0, vertices)
        glTexCoordPointer(2, GL_FLOAT, 0, texcoords)
        glColorPointer(4, GL_FLOAT, 0, colors)
 
        glEnable(GL_TEXTURE_2D)  # テクスチャを有効に
 
        # ポリゴンとして描画
        glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, [0, 1, 2, 3])
        # glDrawArrays(GL_POLYGON, 0, 4)
 
        glDisable(GL_TEXTURE_2D)  # テクスチャを無効に
 
        # 頂点配列、テクスチャ座標配列、カラー配列無効化
        glDisableClientState(GL_VERTEX_ARRAY)
        glDisableClientState(GL_TEXTURE_COORD_ARRAY)
        glDisableClientState(GL_COLOR_ARRAY)
 
    def draw_box(self, x, y, w, h):
        """赤いグラデの四角形を描く"""
        glFrontFace(GL_CCW)  # 頂点反時計回りを表とする
        h /= 10.0
        w = h
        z = -40.0
 
        glLoadIdentity()
 
        glBegin(GL_QUADS)  # 描画開始
 
        glColor3f(0.5, 0.0, 0.0)  # 色を指定
        glVertex3f(x - w, y - h, z)  # 左下
 
        glColor3f(0.5, 0.0, 0.0)
        glVertex3f(x + w, y - h, z)  # 右下
 
        glColor3f(1.0, 0.0, 0.0)
        glVertex3f(x + w, y + h, z)  # 右上
 
        glColor3f(1.0, 0.0, 0.0)
        glVertex3f(x - w, y + h, z)  # 左上
 
        glEnd()  # 描画終了
 
    def draw_tri(self, x, y, w, h):
        """緑のグラデの三角形を描く"""
        glFrontFace(GL_CCW)  # 頂点反時計回りを表とする
        z = -50.0
 
        glBegin(GL_TRIANGLES)  # 三角形描画開始
 
        glColor4f(0.0, 1.0, 0.0, 1.0)  # 色を指定
        glVertex3f(-w, -h, z)  # 左下
        glVertex3f(w, -h, z)  # 右下
 
        glColor4f(0.0, 0.2, 0.0, 0.2)
        glVertex3f(x, y, z)  # 上
        glEnd()
 
    def draw_line(self, x, y, w, h):
        """線を描く"""
        z = -5.0
        glColor3f(0.0, 1.0, 1.0)  # 水色
        glLineWidth(self.line_width)  # 線のサイズ
        glBegin(GL_LINES)
 
        glVertex3f(-w, y, z)  # 始点
        glVertex3f(w, y, z)  # 終点
 
        glVertex3f(x, h, z)  # 始点
        glVertex3f(x, -h, z)  # 終点
 
        glEnd()
 
    def draw_point(self, startang, r):
        """点を描く"""
        glLoadIdentity()
 
        z_d = 10
        zadd = (float(self.mvfrm) / 5.0) % float(z_d)
 
        # アルファブレンディングを有効化
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glEnable(GL_BLEND)
 
        if self.point_spr:
            # ポイントスプライト有効時
 
            glEnable(GL_POINT_SPRITE)  # ポイントスプライトを有効化
            glDisable(GL_POINT_SMOOTH)  # 点のAAを無効化
 
            glEnable(GL_TEXTURE_2D)  # テクスチャを有効化
 
            # テクスチャに色指定を反映させるよう指定
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE)
 
            # 使うテクスチャを選択
            glBindTexture(GL_TEXTURE_2D, self.tex[2])
 
            glEnable(GL_ALPHA_TEST)  # アルファテストを有効化
 
            glPointSize(64.0)  # 点のサイズを指定
 
            # 距離に応じて点の大きさを変える
            glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION,
                               [0.0, 0.0, 0.02])
 
            glBegin(GL_POINTS)  # 点の描画開始を指定
 
            lang = math.radians(startang)
            langadd = math.radians(20)
            for z in range(-40, -5, z_d):
                # 奥行を変えて描画
                zz = z + zadd
                v = (30.0 + zz + 10.0) / 30.0
                if v > 1.0:
                    v = 1.0
                for i in range(0, 360, 20):
                    # 円を描くように描画
                    x = r * math.cos(lang)
                    y = r * math.sin(lang)
                    glColor3f(v, v, 0.5)  # 色を指定
                    glVertex3f(x, y, zz)  # 点座標を指定
                    lang += langadd
 
            glEnd()
 
            # 距離に応じて点のサイズを変えるソレに初期値を入れてリセット
            glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, [1.0, 0.0, 0.0])
 
            glDisable(GL_ALPHA_TEST)  # アルファテストを無効化
            glDisable(GL_POINT_SPRITE)  # ポイントスプライトを無効化
 
            self.set_tex_mode()
        else:
            # ポイントスプライト無効時
 
            glDisable(GL_TEXTURE_2D)  # テクスチャを無効化
            glDisable(GL_POINT_SPRITE)  # point_sprite を無効化
            glEnable(GL_POINT_SMOOTH)  # 点のAAを有効化
            glHint(GL_POINT_SMOOTH_HINT, GL_NICEST)  # AAの品質を参考に
 
            glPointSize(3.0)  # 点のサイズ
 
            # 距離に応じて点の大きさを変えない
            glPointParameterfv(GL_POINT_DISTANCE_ATTENUATION, [1.0, 0.0, 0.0])
 
            glBegin(GL_POINTS)
 
            lang = math.radians(startang)
            langadd = math.radians(10)
            for z in range(-40, -5, z_d):
                # 奥行を変えて描画
                zz = z + zadd
                v = (30.0 + zz + 10.0) / 30.0
                if v > 1.0:
                    v = 1.0
                for i in range(0, 360, 10):
                    # 円を描くように描画
                    x = r * math.cos(lang)
                    y = r * math.sin(lang)
                    glColor3f(v, v, 0.5)  # 色を指定
                    glVertex3f(x, y, zz)  # 点座標を指定
                    lang += langadd
 
            glEnd()
 
        glDisable(GL_TEXTURE_2D)  # テクスチャを無効化
 
    def set_material(self, kind):
        """マテリアルを設定"""
        matdata = [
                   [mat_ambient, mat_diffuse, no_mat, no_shininess, no_mat],
                   [no_mat, mat_diffuse, mat_specular, low_shininess, no_mat],
                   [no_mat, mat_diffuse, mat_specular, high_shininess, no_mat],
                   [no_mat, mat_diffuse, no_mat, no_shininess, mat_emission]
                   ]
 
        dt = matdata[kind]
        glMaterialfv(GL_FRONT, GL_AMBIENT, dt[0])
        glMaterialfv(GL_FRONT, GL_DIFFUSE, dt[1])
        glMaterialfv(GL_FRONT, GL_SPECULAR, dt[2])
        glMaterialfv(GL_FRONT, GL_SHININESS, dt[3])
        glMaterialfv(GL_FRONT, GL_EMISSION, dt[4])
 
    def draw_teapot(self):
        """ティーポットを描く"""
 
        glFrontFace(GL_CW)  # teapotは頂点時計回りが表。他と逆なので注意。
 
        glEnable(GL_DEPTH_TEST)  # 隠面消去を有効化
        glDisable(GL_CULL_FACE)  # カリング無効化
 
        # teapotだけはテクスチャを反映できるので、テクスチャを無効にしておく。
        # cubeやsphere等はテクスチャを反映できない。
        glDisable(GL_TEXTURE_2D)
 
        self.set_material(1)  # マテリアルの設定
 
        # 平行移動、回転、拡大縮小は、
        # 下に書いてある変換のほうから先に実行される
 
        glLoadIdentity()
        glTranslatef(-6.0, 0.0, -10.0)  # 平行移動
        glRotatef(self.xrot, 1.0, 0.0, 0.0)  # x軸に沿って回転
        glRotatef(self.yrot, 0.0, 1.0, 0.0)  # y軸に沿って回転
        glScale(1.0, 1.0, 1.0)  # 拡大縮小
 
        glColor3f(1.0, 0.0, 0.0)  # 色を赤に(照明利用時は無視される)
        if self.wireframe:
            # ワイヤーフレームで描画
            glLineWidth(self.line_width)  # 線のサイズ
            glutWireTeapot(2.0)
        else:
            # ソリッドで描画
            glutSolidTeapot(2.0)
 
    def draw_cube(self):
        """立方体を描画"""
        glFrontFace(GL_CCW)  # cubeは頂点反時計回りが表扱い
        glEnable(GL_CULL_FACE)  # カリング有効化
        glCullFace(GL_BACK)  # 裏面をカリングするように指定
        self.set_material(0)  # マテリアルの設定
        glLoadIdentity()
        glTranslatef(0.0, 0.0, -15.0)
        glRotatef(self.angle, 0.0, 1.0, 1.0)  # y軸、z軸に沿って回転
        glScale(1.0, 1.0, self.scale / 2)
        glColor3f(0.0, 1.0, 0.0)  # 色を緑に
        if self.wireframe:
            glLineWidth(self.line_width)
            glutWireCube(3.0)
        else:
            glutSolidCube(3.0)
 
    def draw_ball(self):
        """球を描画"""
        glFrontFace(GL_CCW)  # 球は頂点反時計回りが表扱い
        glEnable(GL_CULL_FACE)
        glCullFace(GL_BACK)
        self.set_material(2)  # マテリアルの設定
        glLoadIdentity()
        glTranslatef(6.0, 0.0, -20.0)
        glRotatef(self.angle, 1.0, 0.0, 0.0)  # x軸に沿って回転
        glScale(1.0, self.scale, 1.0)
        glColor3f(1.0, 0.0, 1.0)  # 色を紫に
        if self.wireframe:
            glLineWidth(self.line_width)
            glutWireSphere(1.0, 10, 10)
        else:
            glutSolidSphere(1.0, 10, 10)
 
    def on_event(self, event):
        """イベント発生時に呼ばれる処理"""
        if event.type in self._eventMap:
            self._eventMap[event.type](event)
 
    def onQuit(self, event):
        """メインループを終了させるよう設定"""
        self._running = False
 
    def onKeyDown(self, event):
        """キーが押された瞬間に呼ばれる処理"""
 
        if event.key == pygame.K_ESCAPE or event.unicode == u'q':
            # ESCキーかqキーが押されたので終了
            pygame.event.post(pygame.event.Event(pygame.QUIT))
        elif event.key == pygame.K_UP:
            # 上キーが押された
            self.xspeed -= 0.1
        elif event.key == pygame.K_DOWN:
            # 下キーが押された
            self.xspeed += 0.1
        elif event.key == pygame.K_LEFT:
            # 左キーが押された
            self.yspeed -= 0.1
        elif event.key == pygame.K_RIGHT:
            # 右キーが押された
            self.yspeed += 0.1
        elif event.key == pygame.K_l:
            # 照明のon/off
            self.light_enable = not self.light_enable
        elif event.key == pygame.K_p:
            # 平行投影/透視投影の切り替え
            self.flag2d = not self.flag2d
        elif event.key == pygame.K_w:
            # ワイヤーフレーム/ソリッド描画の切り替え
            self.wireframe = not self.wireframe
        elif event.key == pygame.K_t:
            # テクスチャモードの切り替え
            self.tex_mode = (self.tex_mode + 1) % 3
            md = [GL_REPLACE, GL_MODULATE, GL_DECAL]
            glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, md[self.tex_mode])
        elif event.key == pygame.K_f:
            # フルスクリーン/ウインドウの切り替え
            self.fullscr = not self.fullscr
            self.init_gl()
        elif event.key == pygame.K_s:
            # ポイントスプライトの切り替え
            self.point_spr = not self.point_spr
        elif event.key == pygame.K_b:
            # ポーズ
            self.pausefg = not self.pausefg
        else:
            print("KeyDown", event.unicode, event.key, event.mod)
 
    def onKeyUp(self, event):
        """キーが離された瞬間に呼ばれる処理"""
        # print("KeyUp", event.key, event.mod)
        pass
 
    def onMouseDown(self, event):
        """マウスボタンが押された瞬間に呼ばれる処理"""
        lis = [
               'unknown',  # 0
               'left',  # 1
               'middle',  # 2
               'right',  # 3
               'wheelup',  # 4
               'wheeldown'  # 5
               ]
        if event.button <= 5:
            print('onMouseDown', lis[event.button], event.pos)
 
    def onMouseUp(self, event):
        """マウスボタンが離された瞬間に呼ばれる処理"""
        lis = [
               'unknown',  # 0
               'left',  # 1
               'middle',  # 2
               'right',  # 3
               'wheelup',  # 4
               'wheeldown'  # 5
               ]
        if event.button <= 5:
            print('onMouseUp  ', lis[event.button], event.pos)
 
    def onMouseMotion(self, event):
        """マウスカーソルが動いた時に呼ばれる処理"""
        # print('onMouseMotion', event.pos, event.rel, event.buttons)
        self.mx = event.pos[0]
        self.my = event.pos[1]
 
    def execute(self, w, h):
        if not self.initialize(w, h):
            return
 
        timer = pygame.time.Clock()
 
        # メインループ
        while self._running:
            timer.tick(60)  # 60FPSを指定
 
            for event in pygame.event.get():
                self.on_event(event)
 
            self.update()
            self.draw()
 
            self.frame += 1
 
            self.cap = '%5.2f FPS' % (timer.get_fps())
 
            # ウインドウタイトル文字列を指定
            pygame.display.set_caption(self.cap)
 
        pygame.quit()
 
if __name__ == "__main__" :
    game = PyGame()
    game.execute(800, 600)