ここでMQOフォーマットの読み込み機能を実装する。
mqoフォーマットは、{}で囲まれたチャンクの集合で構成される。 大雑把に下記のような構成となっている。
1行め
2行め
SceneChunk
MaterialChunk
ObjectChunk
ObjectChunk
ObjectChunk
:
:
SceneChunkとMaterialChunkは次の}が来るまでを行単位に処理すればいい。 ObjectChunkは中にVertexChunkとFaceChunkがネストしうるので、 {が来たときにひとつ深くなって}が来たときにひとつ浅くなる判定を追加して、 入ったときより浅くなったら脱出する。
#!/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
1行Ⅰマテリアルとして解析する。
#!/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
通常行を飛ばす。 VertexChunkから頂点配列を取得する。
#!/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
FaceChunkから面情報を取得する。
#!/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