Table Of Contents

Previous topic

mqo Loader

Next topic

Skinning

This Page

Viewerの実装

RokuroView

メタセコ風味のカメラ

Rokuro.py

#!/usr/bin/python
# coding: utf-8

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

import baseview

class RokuroView(baseview.BaseView):
    def __init__(self, distance):
        super(RokuroView, self).__init__()
        self.w=1
        self.h=1
        self.head=0
        self.pitch=0
        self.distance=distance
        self.shiftX=0
        self.shiftY=0
        self.aspect=1
        self.n=1
        self.f=10000

    def onResize(self, w=None, h=None):
        super(RokuroView, self).onResize(w, h)
        self.aspect=float(self.w)/float(self.h)

    def dolly(self, d):
        if d>0:
            self.distance*=1.1
        elif d<0:
            self.distance*=0.9

    def shift(self, dx, dy):
        self.shiftX+=dx
        self.shiftY+=dy

    def rotate(self, head, pitch):
        self.head+=head
        self.pitch+=pitch

    def updateProjection(self):
        gluPerspective(30, self.aspect, self.n, self.f)

    def updateView(self):
        glTranslate(self.shiftX, self.shiftY, -self.distance)
        glRotate(self.head, 0, 1, 0)
        glRotate(self.pitch, 1, 0, 0)

    def onMotion(self, x, y):
        redraw=False
        if self.isLeftDown:
            self.dolly(y-self.y)
            redraw=True
        if self.isMiddelDown:
            self.shift(x-self.x, self.y-y)
            redraw=True
        if self.isRightDown:
            self.rotate(x-self.x, y-self.y)
            redraw=True
        self.x=x
        self.y=y
        return redraw

    def onWheel(self, d):
        if d!=0:
            self.dolly(d)
            return True


if __name__=="__main__":
    import glut_ui
    import Cube
    import glbase
    glut_ui.run(
            glbase.BaseController(
                RokuroView(80), 
                Cube.createCube(10)))

Geometry

頂点情報から頂点配列を組み立てる

builder1.py

#!/usr/bin/python
# coding: utf8

from OpenGL.GL import *

import glut_ui
import glbase
import Rokuro
import vertexarray
import mqo


def build(loader):
    vertices=[]
    indices=[]
    offset=0
    for o in loader.objects:
        if o.name.startswith("anchor"):
            continue
        if o.name.startswith("bone:"):
            continue
        if o.name.startswith("MCS:"):
            continue
        print o.name

        for v in o.vertices:
            vertices.append(v)
        for f in o.faces:
            if f.vertexCount==3:
                indices.append(offset+f.V[0])
                indices.append(offset+f.V[1])
                indices.append(offset+f.V[2])
            elif f.vertexCount==4:
                # triangle 1
                indices.append(offset+f.V[0])
                indices.append(offset+f.V[1])
                indices.append(offset+f.V[2])
                # triangle 2
                indices.append(offset+f.V[2])
                indices.append(offset+f.V[3])
                indices.append(offset+f.V[0])
        offset+=len(o.vertices)

    return vertexarray.IndexedVertexArray(vertices, indices)


if __name__=="__main__":
    import sys
    import time

    if len(sys.argv)<2:
        print "usage: %s {mqo file}" % sys.argv[0]
        sys.exit()

    t=time.time()
    l=mqo.Loader()
    if not l.load(sys.argv[1]):
        sys.exit()
    print time.time()-t, "sec"

    print l
    glut_ui.run(glbase.BaseController(
        Rokuro.RokuroView(400), build(l)))

Optimize

非常に遅くなるので、numpyの配列を使うことで高速化する

numpyのインストール

http://numpy.scipy.org/

  • Windowsでは、 インストーラを日本語パスから実行すると失敗するみたいなので注意
  • Ubuntuでは、easy_installするのににpython2.6-devパッケージのインストールが必要

実装

違うのは下記の部分だけ。

builder2.py

    return vertexarray.IndexedVertexArray(
            numpy.array(vertices, 'f'),
            numpy.array(indices, 'u4'))

Material

頂点配列をマテリアルで分割。分割された頂点配列ごとにマテリアルを適用する

material.py

class Material(object):
    def __init__(self, r, g, b, a):
        self.r=r
        self.g=g
        self.b=b
        self.a=a

    def begin(self):
        glColor4f(self.r, self.g, self.b, self.a)

    def end(self):
        pass

    def onInitialize(self):
        pass

    @staticmethod
    def create(src):
        m=material.Material(*src.col)
        return m

builder3.py

#!/usr/bin/python
# coding: utf8

from OpenGL.GL import *

import glut_ui
import glbase
import Rokuro
import mqo
import material
import vertexarraymap


def build(loader):
    vertexArrayMap=vertexarraymap.VertexArrayMap(
            [material.Material.create(m) for m in loader.materials])
    for o in loader.objects:
        if o.name.startswith("anchor"):
            continue
        if o.name.startswith("bone:"):
            continue
        if o.name.startswith("MCS:"):
            continue

        for f in o.faces:
            if f.vertexCount==3:
                vertexArrayMap.addTriangle(
                        f.M,
                        o.vertices[f.V[0]],
                        o.vertices[f.V[1]],
                        o.vertices[f.V[2]]
                        )
            elif f.vertexCount==4:
                # triangle 1
                vertexArrayMap.addTriangle(
                        f.M,
                        o.vertices[f.V[0]],
                        o.vertices[f.V[1]],
                        o.vertices[f.V[2]]
                        )
                # triangle 2
                vertexArrayMap.addTriangle(
                        f.M,
                        o.vertices[f.V[2]],
                        o.vertices[f.V[3]],
                        o.vertices[f.V[0]]
                        )

    vertexArrayMap.optimize()

    return vertexArrayMap


if __name__=="__main__":
    import sys
    import time

    if len(sys.argv)<2:
        print "usage: %s {mqo file}" % sys.argv[0]
        sys.exit()
    # load scenee
    t=time.time()
    l=mqo.Loader()
    if not l.load(sys.argv[1]):
        sys.exit()
    print time.time()-t, "sec"
    # build
    print l
    view=Rokuro.RokuroView(400)
    root=build(l)
    glut_ui.run(glbase.BaseController(view, root))

Texture

UV座標

頂点配列にUV属性を追加する。

vertexarray.py

class VertexArrayWithUV(object):
    def __init__(self, vertices, uvarray):
        self.vertices=vertices
        self.uvarray=uvarray

    def __str__(self):
        return "<VertexArrayWithUV %d>" % len(self.vertices)

    def draw(self):
        # 位置属性
        glEnableClientState(GL_VERTEX_ARRAY)
        glVertexPointer(3, GL_FLOAT, 0, self.vertices)
        # UV属性
        glEnableClientState(GL_TEXTURE_COORD_ARRAY)
        glTexCoordPointer(2, GL_FLOAT, 0, self.uvarray)
        # 描画
        glDrawArrays(GL_TRIANGLES, 0, len(self.vertices))
        # 後始末
        glDisableClientState(GL_TEXTURE_COORD_ARRAY)
        glDisableClientState(GL_VERTEX_ARRAY)

テクスチャ読み込みの組み込み

PILを使ったテクスチャ読み込み。

texture.py

#!/usr/bin/python
# coding: utf-8

import Image
from OpenGL.GL import *


class Texture(object):

    def __init__(self, path):
        self.path=path
        self.image=None

    def onInitialize(self):
        if not self.image:
            self.loadImage()

        assert(self.image)
        if self.createTexture():
            return True

    def loadImage(self):
        self.image=Image.open(self.path)
        if self.image:
            print "load image:", self.path
            return True
        else:
            print "failt to load image:", self.path
            return False

    def createTexture(self):
        self.texture=glGenTextures(1)
        if self.texture==0:
            print "fail to glGenTextures"
            return False

        channels=len(self.image.getbands())
        w, h=self.image.size
        glBindTexture(GL_TEXTURE_2D, self.texture)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
        if channels==4:
            print "RGBA"
            glPixelStorei(GL_UNPACK_ALIGNMENT, 4)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 
                    0, GL_RGBA, GL_UNSIGNED_BYTE, self.image.tostring())
        elif channels==3:
            print "RGB"
            glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 
                    0, GL_RGB, GL_UNSIGNED_BYTE, self.image.tostring())

    def begin(self):
        glEnable(GL_TEXTURE_2D)
        glBindTexture(GL_TEXTURE_2D, self.texture)

    def end(self):
        glDisable(GL_TEXTURE_2D)

バックフェイスカリングとアルファテストの有効化。

material.py

class MQOMaterial(object):
    def __init__(self, r, g, b, a):
        self.r=r
        self.g=g
        self.b=b
        self.a=a
        self.texture=None

    def begin(self):
        glColor4f(self.r, self.g, self.b, self.a)
        if self.texture:
            self.texture.begin()

        # backface culling
        glEnable(GL_CULL_FACE)
        glFrontFace(GL_CW)
        glCullFace(GL_BACK)
        # alpha test
        glEnable(GL_ALPHA_TEST);
        glAlphaFunc(GL_GREATER, 0.5);

    def end(self):
        if self.texture:
            self.texture.end()

    def onInitialize(self):
        if self.texture:
            self.texture.onInitialize()

    @staticmethod
    def create(src, basedir):
        m=MQOMaterial(*src.col)
        if src.tex:
            m.texture=texture.Texture((basedir+'/'+src.tex).replace('\\', '/'))
        return m

UV属性の組み込みと、テクスチャ対応マテリアルの構築。

builder4.py

#!/usr/bin/python
# coding: utf8

from OpenGL.GL import *
import os

import material
import vertexarraymap


def build(loader):
    basedir=os.path.dirname(loader.path)
    vertexArrayMap=vertexarraymap.VertexArrayMapWithUV(
            [material.MQOMaterial.create(m, basedir) 
                for m in loader.materials])
    for o in loader.objects:
        if o.name.startswith("anchor"):
            continue
        if o.name.startswith("bone:"):
            continue
        if o.name.startswith("MCS:"):
            continue

        for f in o.faces:
            if f.vertexCount==3:
                vertexArrayMap.addTriangle(
                        f.M,
                        o.vertices[f.V[0]],
                        o.vertices[f.V[1]],
                        o.vertices[f.V[2]],
                        f.UV[0:6]
                        )
            elif f.vertexCount==4:
                # triangle 1
                vertexArrayMap.addTriangle(
                        f.M,
                        o.vertices[f.V[0]],
                        o.vertices[f.V[1]],
                        o.vertices[f.V[2]],
                        f.UV[0:6]
                        )
                # triangle 2
                vertexArrayMap.addTriangle(
                        f.M,
                        o.vertices[f.V[2]],
                        o.vertices[f.V[3]],
                        o.vertices[f.V[0]],
                        f.UV[4:8]+f.UV[0:2]
                        )

    vertexArrayMap.optimize()

    return vertexArrayMap


if __name__=="__main__":
    import sys
    import time

    import glut_ui
    import glbase
    import Rokuro
    import mqo

    if len(sys.argv)<2:
        print "usage: %s {mqo file}" % sys.argv[0]
        sys.exit()
    # load scenee
    t=time.time()
    l=mqo.Loader()
    if not l.load(sys.argv[1]):
        sys.exit()
    print time.time()-t, "sec"
    # build
    print l
    view=Rokuro.RokuroView(400)
    root=build(l)
    glut_ui.run(glbase.BaseController(view, root))
inserted by FC2 system