Table Of Contents

Previous topic

mqo viewer

Next topic

Viewerの実装

This Page

mqo Loader

ここでMQOフォーマットの読み込み機能を実装する。

{}の対応を取る

mqoフォーマットは、{}で囲まれたチャンクの集合で構成される。 大雑把に下記のような構成となっている。

1行め
2行め

SceneChunk

MaterialChunk

ObjectChunk
ObjectChunk
ObjectChunk
:
:

SceneChunkとMaterialChunkは次の}が来るまでを行単位に処理すればいい。 ObjectChunkは中にVertexChunkとFaceChunkがネストしうるので、 {が来たときにひとつ深くなって}が来たときにひとつ浅くなる判定を追加して、 入ったときより浅くなったら脱出する。

mqo1.py

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

class Loader(object):
    def load(self, path):
        io=open(path, "rb")
        if not io:
            print "fail to open:", path
            return False
        # first line
        if io.readline()!="Metasequoia Document\r\n":
            print "is not mqo file"
            return False
        # second line
        if io.readline()!="Format Text Ver 1.0\r\n":
            print "invalid version"
            return False

        while True:
            line=io.readline()
            if line=="":
                print "invalid eof"
                return False
            elif line=="Eof\r\n":
                return True
            tokens=line.split(" ", 1)
            if len(tokens)<2:
                continue

            key, remain=tokens
            method_name="read"+key
            if hasattr(self, method_name):
                if not getattr(self, method_name)(io, remain):
                    return False
            else:
                if not self.readChunk(io, remain):
                    return False

    def readChunk(self, io, _=None):
        """
        {}の対応を取る
        """
        while True:
            line=io.readline()
            if line=="":
                print "readChunk: invalid eof"
                return False

            line=line.strip()
            if line=="":
                continue

            if line.find("{")!=-1:
                print line
                self.readChunk(io)

            if line=="}":
                print line
                return True


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=Loader()
    if not l.load(sys.argv[1]):
        sys.exit()
    print time.time()-t, "sec"

    print l

Material Chunk

1行Ⅰマテリアルとして解析する。

mqo2.py

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


class Material(object):
    def __init__(self, line):
        secondQuote=line.find('"', 2)
        self.name=line[1:secondQuote]
        self.shader=0
        self.col=[0, 0, 0, 0]
        self.dif=0
        self.amb=0
        self.emi=0
        self.spc=0
        self.power=0
        self.tex=None

        pos=secondQuote+2
        while True:
            leftParenthesis=line.find('(', pos)
            if leftParenthesis==-1:
                break
            rightParenthesis=line.find(')', leftParenthesis+1)
            key=line[pos:leftParenthesis]
            params=line[leftParenthesis+1:rightParenthesis]
            if key=="shader":
                self.shader=int(params)
            elif key=="col":
                self.col=[float(num) for num in params.split()]
            elif key=="dif":
                self.dif=float(params)
            elif key=="amb":
                self.amb=float(params)
            elif key=="emi":
                self.emi=float(params)
            elif key=="spc":
                self.spc=float(params)
            elif key=="power":
                self.power=float(params)
            elif key=="tex":
                self.tex=params[1:-1]
            else:
                print "Material: unknown key: ", key

            pos=rightParenthesis+2

    def __str__(self):
        return "<Material(%s) shader:%d, col:[%f,%f,%f,%f], dif:%f, amb:%f, emi:%f, spc:%f, power:%f, tex:%s>" % (
                self.name, self.shader, 
                self.col[0], self.col[1], self.col[2], self.col[3],
                self.dif, self.amb, self.emi, self.spc, self.power, self.tex
                )


import mqo1
class Loader(mqo1.Loader):
    def __init__(self):
        self.materials=[]

    def __str__(self):
        return "<MQO %d materials>" % len(self.materials)

    def readMaterial(self, io, _):
        """
        Materialの読み取り
        """
        while True:
            line=io.readline()
            if line=="":
                print "readMaterial: invalid eof"
                return False

            line=line.strip()
            if line=="":
                continue
            if line=="}":
                return True
            self.materials.append(Material(line))

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=Loader()
    if not l.load(sys.argv[1]):
        sys.exit()
    print time.time()-t, "sec"

    print l

Object ChunkとVertex Chunk

通常行を飛ばす。 VertexChunkから頂点配列を取得する。

mqo3.py

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


class Object(object):
    def __init__(self, name):
        self.name=name
        self.vertices=[]
        self.faces=[]

    def __str__(self):
        return "<Object(%s) %d vertices, %d faces>" % (
                self.name, len(self.vertices), len(self.faces))


import mqo2
class Loader(mqo2.Loader):
    def __init__(self):
        super(Loader, self).__init__()
        self.objects=[]

    def __str__(self):
        return "<MQO %d materials, %d objects>" % (
                len(self.materials), len(self.objects))

    def readObject(self, io, remain):
        """
        Objectの読み取り
        """
        secondQuote=remain.find('"', 1)
        name=remain[1:secondQuote]

        o=Object(name)
        self.objects.append(o)
        while True:
            line=io.readline()
            if line=="":
                print "readObject: invalid eof"
                return False

            line=line.strip()
            if line=="":
                continue

            if line=="}":
                return True

            tokens=line.split()
            key=tokens.pop(0)
            if tokens[-1]=="{":
                method_name="read"+key[0].upper()+key[1:]
                if hasattr(self, method_name):
                    getattr(self, method_name)(io, o)
                else:
                    if not self.readChunk(io):
                        return False

    def readVertex(self, io, o):
        while True:
            line=io.readline()
            if line=="":
                print "readObject: invalid eof"
                return False

            line=line.strip()
            if line=="":
                continue

            if line=="}":
                return True

            o.vertices.append([float(n) for n in line.split()])

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=Loader()
    if not l.load(sys.argv[1]):
        sys.exit()
    print time.time()-t, "sec"

    print l

Face Chunk

FaceChunkから面情報を取得する。

mqo4.py

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


def toRGBA(dword):
    mod=dword
    mod, r=divmod(mod, 256)
    mod, g=divmod(mod, 256)
    mod, b=divmod(mod, 256)
    mod, a=divmod(mod, 256)
    return (r/255.0, g/255.0, b/255.0, a/255.0)    


class Face(object):
    def __init__(self, vertexCount, line):
        self.vertexCount=vertexCount
        self.V=[]
        self.M=0
        self.UV=[0]*8
        self.COL=[(1, 1, 1, 1)]*4
        pos=0
        while True:
            leftParenthesis=line.find('(', pos)
            if leftParenthesis==-1:
                break
            rightParenthesis=line.find(')', leftParenthesis+1)
            key=line[pos:leftParenthesis]
            params=line[leftParenthesis+1:rightParenthesis]
            if key=="V":
                self.V=[int(i) for i in params.split()]
            elif key=="M":
                self.M=int(params)
            elif key=="UV":
                self.UV=[float(num) for num in params.split()]
            elif key=="COL":
                self.COL=[toRGBA(int(dword)) for dword in params.split()]
            else:
                print "Face: unknown key: ", key
            pos=rightParenthesis+2


import mqo3
class Loader(mqo3.Loader):
    def readFace(self, io, o):
        while True:
            line=io.readline()
            if line=="":
                print "readFace: invalid eof"
                return False

            line=line.strip()
            if line=="":
                continue

            if line=="}":
                return True

            key, line=line.split(' ', 1)
            o.faces.append(Face(int(key), line))


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=Loader()
    if not l.load(sys.argv[1]):
        sys.exit()
    print time.time()-t, "sec"

    print l

mqo loader

mqoの読み込み機能はできた。 mqo4 -> mqo3 -> mqo2 -> mqo1 を1つにまとめたソース。

mqo.py

inserted by FC2 system