マウスでオブジェクトを選択する(1)

OpenGLで描画した物体をマウスで選択していろいろとやってみたい。セレクションという概念を使って実現するみたいだが、その練習をやってみる。

上記(1)のサイトのセレクションを使ったサンプル(アニメーション版)は、C言語で書かれたサンプルプログラムなのだが、これをPythonで書き直す。

from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *

SELECTIONS = 100
NOBJECTS = 5

color = [ [0.1, 0.1, 0.9],
          [0.1, 0.9, 0.1],
          [0.9, 0.1, 0.1],
          [0.1, 0.9, 0.9],
          [0.9, 0.1, 0.9],
          [0.9, 0.9, 0.1]
        ]

ARRANGEWIDTH = 4.0
ARRANGECENTER = ARRANGEWIDTH/2.0
ARRANGESTEP = ARRANGEWIDTH/(NOBJECTS-1)
ARRANGEDEPTH = 0.0
INITIALHEIGHT = 0.0
INITIALVELOCITY = 2.0
GRAVITY = -1.0
REFRESHRATE = 0.01

ESCAPE = 27
cframe = 0
light0pos = [4.0, 8.0, 6.0, 1.0]
objects = None
touchtime = list()

def init():
    global objects
    global touchtime

    glClearColor(1.0, 1.0, 1.0, 0.0)
    glEnable(GL_DEPTH_TEST)
    glEnable(GL_CULL_FACE)
    glEnable(GL_LIGHT0)
    glEnable(GL_LIGHTING)

    objects = glGenLists(NOBJECTS)
    touchtime = [0]*NOBJECTS

def display():
    global cframe
    cframe += 1

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    glLoadIdentity()
    gluLookAt(5.0, 4.0, 5.0, 0.4, 0.0, 0.0, 0.0, 1.0, 0.0)
    glLightfv(GL_LIGHT0, GL_POSITION, light0pos)

    for i in range(NOBJECTS):
        h = INITIALHEIGHT
        if touchtime[i] > 0:
            t = (cframe-touchtime[i]) * REFRESHRATE
            h += (GRAVITY*t*0.5 + INITIALVELOCITY)*t
            if h < 0.0:
                h = INITIALHEIGHT
                touchtime[i] = 0

        glMaterialfv(GL_FRONT, GL_DIFFUSE, color[i%6])
        glNewList(objects + i, GL_COMPILE_AND_EXECUTE)

        glPushMatrix()
        glTranslated(float(i)*ARRANGESTEP - ARRANGECENTER, h, ARRANGEDEPTH)
        glutSolidCube(0.8)
        glPopMatrix()

        glEndList()

    glutSwapBuffers()

def mouse(button, state, x, y):
    global touchtime
    if state == GLUT_DOWN:
        if button == GLUT_LEFT_BUTTON:
            sel = glSelectBuffer(SELECTIONS) #選択されたオブジェクトのデータを入れるバッファ
            glRenderMode(GL_SELECT)   #セレクションモードに入る
            glInitNames()        #名前スタックを初期化
            glPushName(-1)        #名前スタックの最初の名前を-1に
            glMatrixMode(GL_PROJECTION) #視点座標系に入る

            glPushMatrix()      #現在の透視変換マトリックスを保存
            glLoadIdentity()     #透視変換マトリックスを初期化
            vp = glGetIntegerv(GL_VIEWPORT) #現在のビューポート設定を得る
            gluPickMatrix(x, vp[3]-y-1, 1, 1, vp) #表示領域がマウスがクリックされた周囲だけになるように
            gluPerspective(30.0, vp[2]/vp[3], 1.0, 100.0) #透視変換マトリックスを設定
            glMatrixMode(GL_MODELVIEW) #モデルビューマトリックスに切り替え

            for i in range(NOBJECTS):
                glLoadName(i)      #オブジェクトの番号を設定
                glCallList(objects + i) #オブジェクトの描画(画面には表示されず)

            glMatrixMode(GL_PROJECTION)  #再び透視変換マトリックスに
            glPopMatrix()         #透視変換マトリックスを元に戻す
            glMatrixMode(GL_MODELVIEW)
            hits = glRenderMode(GL_RENDER) #レンダリングモードを元に戻す
            for n in hits:    #選択されたオブジェクトの処理
                h = n.names[0]  #選択されたオブジェクトの番号
                touchtime[h] = cframe

def resize(w, h):
    glViewport(0, 0, w, h)
    glMatrixMode(GL_PROJECTION)
    glLoadIdentity()
    gluPerspective(30.0, w/h, 1.0, 100.0)
    glMatrixMode(GL_MODELVIEW)

def keyboard(key, x, y):
    if key == 'q':
        sys.exit()

def idle():
    glutPostRedisplay()


glutInit(sys.argv)
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
glutInitWindowSize(320, 320)
glutCreateWindow("sample")
glutDisplayFunc(display)
glutReshapeFunc(resize)
glutKeyboardFunc(keyboard)
glutMouseFunc(mouse)
glutIdleFunc(idle)

init()

glutMainLoop()

ちゃんと動くようになったが、細かいところの理屈がまだ理解できていない。