mqo読み込み機能を作ってみた

http://www.metaseq.net/metaseq/format.html
を見ながらmqoの読み込み機能を作った(頂点と面のみ)。
python自体に不慣れだったのでちょっと時間がかかったけど、
iteratorとlistの内包表現はおもしろいですな。

しかし、正規表現ではまった。

^hoge(\s+\w+)*

というようなパターンのカッコ内の繰り返しを取る方法がわからなかった。
groupsじゃ取れなさそうだが。
やむを得ず

\s+(?=\w+)

でsplitしてからごにょごにょすることにした。
いずれやり方がわかるじゃろぅ。

参考にしたサイト
rubyからの移行だとこの辺の情報がありがたい。pop(0)は知らないと無理

# -*- coding: utf-8 -*-
# mqoファイルの読み込み

import re

class Object:
  def __init__(self):
    self.objects=[]
    pass

  def Load(self, io):
    print "mqo.load"
    self.io=io

    # ヘッダ
    if not io.next().startswith("Metasequoia Document"):
      return False

    m=re.match('^Format (\w+) Ver (\d+)\.(\d+)', io.next())
    if m.group(1)!="Text":
      return False
    if m.group(2)!="1":
      return False

    print "Format: %s, Version %s.%s" % (m.group(1), m.group(2), m.group(3))

    try:
      while True:
        chunk=self.Chunk()[0].lower()
        print "#%s" % (chunk)
        if chunk=="scene": # 1回
          self.scene=self.ChunkScene()
        elif chunk=="material": # 1回
          self.material=self.ChunkMaterial()
        elif chunk=="object": # 複数あり
          self.objects.append(self.ChunkObject())
    except StopIteration:
      # end
      return

  def Chunk(self):
    while True:
      line=self.io.next().strip()
      m=re.match('^(\w+)\s*(\d+)?\s*{', line)
      if m:
        return (m.group(1), m.group(2))
      m=re.match('^Object\s*"([^"]+)"\s+{', line)
      if m:
        return ('object', m.group(1))

  def ChunkScene(self):
    scene={}
    while True:
      line=self.io.next().strip()
      if line=='}': break
      #
      fields=line.split()
      scene[fields.pop(0)]=fields
    return scene

  def ChunkMaterial(self):
    materials=[]
# match()で取れない?
#    recomp=re.compile(r"""
#"([^)]*)" #1 材質名
#(?:\s+
#  (\w+) # パラメータ
#  \(
#    ([^)]*) # 値
#  \)
#)*
#    """, re.VERBOSE)
    recomp=re.compile(r"""
\s+
(?=
  \w+ # パラメータ
  \(
    [^)]* # 値
  \)
)
    """, re.VERBOSE)
    while True:
      material={}
      line=self.io.next().strip()
      if line=='}': break
      #
      fields=recomp.split(line)
      material['name']=fields.pop(0)[1:-1]
      for field in fields:
        m=re.match('^(\w+)\(([^)]*)\)', field)
        if m:
          values=m.group(2).split()
          if len(values)>1:
            material[m.group(1)]=values
          else:
            material[m.group(1)]=m.group(2)
      materials.append(material)
    return materials

  def ChunkObject(self):
    object={}
    while True:
      line=self.io.next().strip()
      if line=='}': break
      #
      m=re.match('^(\w+)\s*(\d+)?\s*{', line)
      if m:
        chunk=m.group(1).lower()
        if m.group(1)=="vertex":
          object["vertex"]=self.ChunkVertex()
        elif chunk=="bvertex":
          object["bvertex"]="not implemented"
        elif m.group(1)=="face":
          object["face"]=self.ChunkFace()
      else:
        fields=line.split()
        object[fields.pop(0)]=fields
    return object

  def ChunkVertex(self):
    vertex=[]
    while True:
      line=self.io.next().strip()
      if line=='}': break
      #
      vertex.append([float(i) for i in line.split()])
    return vertex

  def ChunkFace(self):
    face=[]
    recomp=re.compile(r"""
^(\w+)\s*  #1 頂点数
V\(([^)]*)\)\s* #2 頂点インデックス
M\(([^)]*)\)\s* #3  材質インデックス
UV\(([^)]*)\)\s* #4 UV値
(?:COL\(([^)]*)\))? #5 頂点カラー
    """, re.VERBOSE)
    while True:
      line=self.io.next().strip()
      if line=='}': break
      #
      m=recomp.match(line)
      if m:
        face.append({
          "index": [int(i) for i in m.group(2).split()],
          "material": int(m.group(3)),
          "uv": [float(i) for i in m.group(4).split()]
        })
        if m.group(5):
          face["color"]=m.group(5)
    return face


################################################################################
# test
################################################################################
if __name__=='__main__':
  def test():
    o=Object()
    #o.Load(open("cube.mqo"))
    o.Load(open("miry01.mqo"))

  import timeit
  t = timeit.Timer('test()', 'from __main__ import test')
  print "%.6f sec" % (t.timeit(1))

それで、さっそくネットからフリーの形状データを落として試してみたのだが、
表示が有り得ない遅さになってしまっていた。
OpenGLの方の最適化が必要の様子。