OSDN Git Service

fix export_mqo
[meshio/pymeshio.git] / meshio / pymeshio / mmd.py
1 #!/usr/bin/python
2 # coding: utf-8
3 """
4 20091202: VPD読み込みを追加
5 20100318: PMD書き込みを追加
6 20100731: meshioと互換になるように改造
7
8 VMDの読み込み
9 http://yumin3123.at.webry.info/200810/article_4.html
10 http://atupdate.web.fc2.com/vmd_format.htm
11
12 PMDの読み込み
13 http://blog.goo.ne.jp/torisu_tetosuki/e/209ad341d3ece2b1b4df24abf619d6e4
14
15 VPDの読み込み
16
17 ToDo:
18     rigdid bodies
19     constraints
20 """
21 import sys
22 import codecs
23 import os.path
24 import struct
25 import math
26 import re
27 #import numpy
28 from decimal import *
29
30 ENCODING='cp932'
31
32 if sys.version_info[0]>=3:
33     xrange=range
34
35 ###############################################################################
36 # utility
37 ###############################################################################
38 def truncate_zero(src):
39     """
40     0x00以降を捨てる
41     """
42     pos = src.find(b"\x00")
43     assert(type(src)==bytes)
44     if pos >= 0:
45         return src[:pos]
46     else:
47         return src
48
49 def radian_to_degree(x):
50     return x/math.pi * 180.0
51
52
53 ###############################################################################
54 # geometry
55 ###############################################################################
56 class Vector2(object):
57     __slots__=['x', 'y']
58     def __init__(self, x=0, y=0):
59         self.x=x
60         self.y=y
61
62     def __str__(self):
63         return "<%f %f>" % (self.x, self.y)
64
65     def __getitem__(self, key):
66         if key==0:
67             return self.x
68         elif key==1:
69             return self.y
70         else:
71             assert(False)
72
73     def to_tuple(self):
74         return (self.x, self.y)
75
76
77 class Vector3(object):
78     __slots__=['x', 'y', 'z']
79     def __init__(self, x=0, y=0, z=0):
80         self.x=x
81         self.y=y
82         self.z=z
83
84     def __str__(self):
85         return "<%f %f %f>" % (self.x, self.y, self.z)
86
87     def __getitem__(self, key):
88         if key==0:
89             return self.x
90         elif key==1:
91             return self.y
92         elif key==2:
93             return self.z
94         else:
95             assert(False)
96
97     def to_tuple(self):
98         return (self.x, self.y, self.z)
99
100 class Quaternion(object):
101     __slots__=['x', 'y', 'z', 'w']
102     def __init__(self, x=0, y=0, z=0, w=1):
103         self.x=x
104         self.y=y
105         self.z=z
106         self.w=w
107
108     def __str__(self):
109         return "<%f %f %f %f>" % (self.x, self.y, self.z, self.w)
110
111     def __mul__(self, rhs):
112         u=numpy.array([self.x, self.y, self.z], 'f')
113         v=numpy.array([rhs.x, rhs.y, rhs.z], 'f')
114         xyz=self.w*v+rhs.w*u+numpy.cross(u, v)
115         q=Quaternion(xyz[0], xyz[1], xyz[2], self.w*rhs.w-numpy.dot(u, v))
116         return q
117
118     def dot(self, rhs):
119         return self.x*rhs.x+self.y*rhs.y+self.z*rhs.z+self.w*rhs.w
120
121     def getMatrix(self):
122         sqX=self.x*self.x
123         sqY=self.y*self.y
124         sqZ=self.z*self.z
125         xy=self.x*self.y
126         xz=self.x*self.z
127         yz=self.y*self.z
128         wx=self.w*self.x
129         wy=self.w*self.y
130         wz=self.w*self.z
131         return numpy.array([
132                 # 1
133                 [1-2*sqY-2*sqZ, 2*xy+2*wz, 2*xz-2*wy, 0],
134                 # 2
135                 [2*xy-2*wz, 1-2*sqX-2*sqZ, 2*yz+2*wx, 0],
136                 # 3
137                 [2*xz+2*wy, 2*yz-2*wx, 1-2*sqX-2*sqY, 0],
138                 # 4
139                 [0, 0, 0, 1]],
140                 'f')
141
142     def getRHMatrix(self):
143         x=-self.x
144         y=-self.y
145         z=self.z
146         w=self.w
147         sqX=x*x
148         sqY=y*y
149         sqZ=z*z
150         xy=x*y
151         xz=x*z
152         yz=y*z
153         wx=w*x
154         wy=w*y
155         wz=w*z
156         return numpy.array([
157                 # 1
158                 [1-2*sqY-2*sqZ, 2*xy+2*wz, 2*xz-2*wy, 0],
159                 # 2
160                 [2*xy-2*wz, 1-2*sqX-2*sqZ, 2*yz+2*wx, 0],
161                 # 3
162                 [2*xz+2*wy, 2*yz-2*wx, 1-2*sqX-2*sqY, 0],
163                 # 4
164                 [0, 0, 0, 1]],
165                 'f')
166
167     def getRollPitchYaw(self):
168         m=self.getMatrix()
169
170         roll = math.atan2(m[0, 1], m[1, 1])
171         pitch = math.asin(-m[2, 1])
172         yaw = math.atan2(m[2, 0], m[2, 2])
173
174         if math.fabs(math.cos(pitch)) < 1.0e-6:
175             roll += m[0, 1] > math.pi if 0.0 else -math.pi
176             yaw += m[2, 0] > math.pi if 0.0 else -math.pi
177
178         return roll, pitch, yaw
179
180     def getSqNorm(self):
181         return self.x*self.x+self.y*self.y+self.z*self.z+self.w*self.w
182
183     def getNormalized(self):
184         f=1.0/self.getSqNorm()
185         q=Quaternion(self.x*f, self.y*f, self.z*f, self.w*f)
186         return q
187
188     def getRightHanded(self):
189         "swap y and z axis"
190         return Quaternion(-self.x, -self.z, -self.y, self.w)
191
192     @staticmethod
193     def createFromAxisAngle(axis, rad):
194         q=Quaternion()
195         half_rad=rad/2.0
196         c=math.cos(half_rad)
197         s=math.sin(half_rad)
198         return Quaternion(axis[0]*s, axis[1]*s, axis[2]*s, c)
199
200
201 class RGBA(object):
202     __slots__=['r', 'g', 'b', 'a']
203     def __init__(self, r=0, g=0, b=0, a=1):
204         self.r=r
205         self.g=g
206         self.b=b
207         self.a=a
208
209     def __getitem__(self, key):
210         if key==0:
211             return self.r
212         elif key==1:
213             return self.g
214         elif key==2:
215             return self.b
216         elif key==3:
217             return self.a
218         else:
219             assert(False)
220
221
222 ###############################################################################
223 # VMD
224 ###############################################################################
225 class ShapeData(object):
226     __slots__=['name', 'frame', 'ratio']
227     def __init__(self, name):
228         self.name=name
229         self.frame=-1
230         self.ratio=0
231
232     def __cmp__(self, other):
233         return cmp(self.frame, other.frame)
234
235 class MotionData(object):
236     __slots__=['name', 'frame', 'pos', 'q', 'complement']
237     def __init__(self, name):
238         self.name=name
239         self.frame=-1
240         self.pos=Vector3()
241         self.q=Quaternion()
242
243     def __cmp__(self, other):
244         return cmp(self.frame, other.frame)
245
246     def __str__(self):
247         return '<MotionData "%s" %d %s%s>' % (self.name, self.frame, self.pos, self.q)
248
249 class VMDLoader(object):
250     __slots__=['io', 'end', 'signature',
251             'model_name', 'last_frame',
252             'motions', 'shapes', 'cameras', 'lights',
253             ]
254     def __init__(self):
255         self.model_name=''
256         self.motions=[]
257         self.shapes=[]
258         self.cameras=[]
259         self.lights=[]
260         self.last_frame=0
261
262     def __str__(self):
263         return '<VMDLoader model: "%s", motion: %d, shape: %d, camera: %d, light: %d>' % (
264             self.model_name, len(self.motions), len(self.shapes),
265             len(self.cameras), len(self.lights))
266
267     def load(self, path, io, end):
268         self.io=io
269         self.end=end
270
271         # signature
272         self.signature=truncate_zero(self.io.read(30))
273         version=self.validate_signature(self.signature)
274         if not version:
275             print("invalid signature", self.signature)
276             return False
277
278         if version==1:
279             if not self.load_verstion_1():
280                 return False
281         elif version==2:
282             if not  self.load_verstion_2():
283                 return False 
284         else:
285             raise Exception("unknown version") 
286
287         # post process
288         motions=self.motions
289         self.motions={}
290         for m in motions:
291             if not m.name in self.motions:
292                 self.motions[m.name]=[]
293             self.motions[m.name].append(m)
294         for name in self.motions.keys():
295             self.motions[name].sort()
296
297         shapes=self.shapes
298         self.shapes={}
299         for s in shapes:
300             if not s.name in self.shapes:
301                 self.shapes[s.name]=[]
302             self.shapes[s.name].append(s)
303         for name in self.shapes.keys():
304             self.shapes[name].sort()
305
306         return True
307
308     def getMotionCount(self):
309         count=0
310         for v in self.motions.values():
311             count+=len(v)
312         return count
313
314     def getShapeCount(self):
315         count=0
316         for v in self.shapes.values():
317             count+=len(v)
318         return count
319
320     def load_verstion_1(self):
321         # model name
322         self.model_name=truncate_zero(self.io.read(10))
323         if not self.loadMotion_1():
324             return False
325         return True
326
327     def loadMotion_1(self):
328         count=struct.unpack('H', self.io.read(2))[0]
329         self.io.read(2)
330         for i in xrange(0, count):
331             self.loadFrameData()
332         return True
333
334     ############################################################
335     def load_verstion_2(self):
336         # model name
337         self.model_name=truncate_zero(self.io.read(20))
338
339         if not self.loadMotion():
340             return False
341         if not self.loadShape():
342             return False
343         if not self.loadCamera():
344             return False
345         if not self.loadLight():
346             return False
347         #assert(self.io.tell()==self.end)
348         #self.motions.sort(lambda l, r: l.name<r.name)
349
350         return True
351
352     def validate_signature(self, signature):
353         if self.signature == "Vocaloid Motion Data 0002":
354             return 2
355         if self.signature == "Vocaloid Motion Data file":
356             return 1
357         else:
358             return None
359
360     def loadMotion(self):
361         count=struct.unpack('I', self.io.read(4))[0]
362         for i in xrange(0, count):
363             self.loadFrameData()
364         return True
365
366     def loadShape(self):
367         count=struct.unpack('I', self.io.read(4))[0]
368         for i in xrange(0, count):
369             self.loadShapeData()
370         return True
371
372     def loadCamera(self):
373         count=struct.unpack('I', self.io.read(4))[0]
374         for i in xrange(0, count):
375             # not implemented
376             assert(False)
377             pass
378         return True
379
380     def loadLight(self):
381         count=struct.unpack('I', self.io.read(4))[0]
382         for i in xrange(0, count):
383             # not implemented
384             assert(False)
385             pass
386         return True
387
388     def loadFrameData(self):
389         """
390         フレームひとつ分を読み込む
391         """
392         data=MotionData(truncate_zero(self.io.read(15)))
393         (data.frame, data.pos.x, data.pos.y, data.pos.z,
394         data.q.x, data.q.y, data.q.z, data.q.w) = struct.unpack(
395                 'I7f', self.io.read(32))
396         # complement data
397         data.complement=''.join(
398                 ['%x' % x for x in struct.unpack('64B', self.io.read(64))])
399         self.motions.append(data)
400         if data.frame>self.last_frame:
401             self.last_frame=data.frame
402
403     def loadShapeData(self):
404         """
405         モーフデータひとつ分を読み込む
406         """
407         data=ShapeData(truncate_zero(self.io.read(15)))
408         (data.frame, data.ratio)=struct.unpack('If', self.io.read(8))
409         self.shapes.append(data)
410         if data.frame>self.last_frame:
411             self.last_frame=data.frame
412
413     # vmd -> csv
414     ############################################################
415     def create_csv_line(m):
416         # quaternion -> euler angle
417         (roll, pitch, yaw)=m.q.getRollPitchYaw()
418         return '%s,%d,%g,%g,%g,%g,%g,%g,0x%s\n' % (
419                 m.name, m.frame, m.pos.x, m.pos.y, m.pos.z,
420                 to_degree(pitch), to_degree(yaw), to_degree(roll), m.complement
421                 )
422
423     def write_csv(l, path):
424         sys.setdefaultencoding('cp932')
425         csv=open(path, "w")
426         csv.write('%s,0\n' % l.signature)
427         csv.write('%s\n' % l.model_name)
428         # motion
429         csv.write('%d\n' % len(l.motions))
430         for m in l.motions:
431             csv.write(create_csv_line(m))
432         # shape
433         csv.write('%d\n' % len(l.shapes))
434         for s in l.shapes:
435             csv.write('%s,%d,%f\n' % ( s.name, s.frame, s.ratio))
436         # camera
437         csv.write('%d\n' % len(l.cameras))
438         for camera in l.cameras:
439             assert(False)
440         # light
441         csv.write('%d\n' % len(l.lights))
442         for light in l.lights:
443             assert(False)
444
445
446 ###############################################################################
447 # PMD
448 ###############################################################################
449 class Vertex(object):
450     __slots__=['pos', 'normal', 'uv', 'bone0', 'bone1', 'weight0', 'edge_flag']
451     def __init__(self, x=0, y=0, z=0, nx=0, ny=0, nz=0, u=0, v=0,
452             bone0=0, bone1=0, weight0=0, edge_flag=0):
453         self.pos=Vector3(x, y, z)
454         self.normal=Vector3(nx, ny, nz)
455         self.uv=Vector2(u, v)
456         self.bone0=bone0
457         self.bone1=bone1
458         self.weight0=weight0
459         self.edge_flag=edge_flag
460
461     def __str__(self):
462         return "<%s %s %s, (%d, %d, %d)>" % (str(self.pos), str(self.normal), str(self.uv), self.bone0, self.bone1, self.weight0)
463
464     def __getitem__(self, key):
465         if key==0:
466             return self.pos.x
467         elif key==1:
468             return self.pos.y
469         elif key==2:
470             return self.pos.z
471         else:
472             assert(False)
473
474 class Material(object):
475     __slots__=[
476             'diffuse', 'shinness', 'specular',
477             'ambient', 'vertex_count', 'texture', 'toon_index', 'flag',
478             ]
479
480     def __init__(self, dr=0, dg=0, db=0, alpha=1, 
481             specular=0, sr=0, sg=0, sb=0, ar=0, ag=0, ab=0):
482         self.diffuse=RGBA(dr, dg, db, alpha)
483         self.specular=RGBA(sr, sg, sb)
484         self.shinness=specular
485         self.ambient=RGBA(ar, ag, ab)
486         self.vertex_count=0
487         self.texture=''
488         self.toon_index=0
489         self.flag=0
490
491     def __str__(self):
492         return "<Material [%f, %f, %f, %f]>" % (
493                 self.diffuse[0], self.diffuse[1], 
494                 self.diffuse[2], self.diffuse[3],
495                 )
496
497     def getTexture(self): return self.texture.decode('cp932')
498     def setTexture(self, u): self.texture=u
499
500 # @return 各マテリアルについて、そのマテリアルが保持する面の回数だけ
501 # マテリアル自身を返す
502 def material_per_face(materials):
503     for m in materials:
504         for x in xrange(int(m.vertex_count/3)):
505             yield m
506
507 class Bone(object):
508     # kinds
509     ROTATE = 0
510     ROTATE_MOVE = 1
511     IK = 2
512     IK_ROTATE_INFL = 4
513     ROTATE_INFL = 5
514     IK_TARGET = 6
515     UNVISIBLE = 7
516     # since v4.0
517     ROLLING=8 # ?
518     TWEAK=9
519     __slots__=['name', 'index', 'type', 'parent', 'ik', 'pos',
520             'children', 'english_name', 'ik_index',
521             'parent_index', 'tail_index', 'tail',
522             ]
523     def __init__(self, name='bone', type=0):
524         self.name=name
525         self.index=0
526         self.type=type
527         self.parent_index=0xFFFF
528         self.tail_index=0
529         self.tail=Vector3(0, 0, 0)
530         self.parent=None
531         self.ik_index=0xFFFF
532         self.pos=Vector3(0, 0, 0)
533         self.children=[]
534         self.english_name=''
535
536     def getName(self): return self.name.decode('cp932')
537     def setName(self, u): self.name=u
538     def setEnglishName(self, u): self.english_name=u
539
540     def hasParent(self):
541         return self.parent_index!=0xFFFF
542
543     def hasChild(self):
544         return self.tail_index!=0
545
546     def display(self, indent=[]):
547         if len(indent)>0:
548             prefix=''
549             for i, is_end in enumerate(indent):
550                 if i==len(indent)-1:
551                     break
552                 else:
553                     prefix+='  ' if is_end else ' |'
554             uni='%s +%s(%s)' % (prefix, unicode(self), self.english_name)
555             print(uni.encode(ENCODING))
556         else:
557             uni='%s(%s)' % (unicode(self), self.english_name)
558             print(uni.encode(ENCODING))
559
560         child_count=len(self.children)
561         for i in xrange(child_count):
562             child=self.children[i]
563             if i<child_count-1:
564                 child.display(indent+[False])
565             else:
566                 # last
567                 child.display(indent+[True])
568
569 # 0
570 class Bone_Rotate(Bone):
571     __slots__=[]
572     def __init__(self, name):
573         super(Bone_Rotate, self).__init__(name, 0)
574     def __str__(self):
575         return '<ROTATE %s>' % (self.name)
576 # 1
577 class Bone_RotateMove(Bone):
578     __slots__=[]
579     def __init__(self, name):
580         super(Bone_RotateMove, self).__init__(name, 1)
581     def __str__(self):
582         return '<ROTATE_MOVE %s>' % (self.name)
583 # 2
584 class Bone_IK(Bone):
585     __slots__=[]
586     def __init__(self, name):
587         super(Bone_IK, self).__init__(name, 2)
588     def __str__(self):
589         return '<IK %s>' % (self.name)
590 # 4
591 class Bone_IKRotateInfl(Bone):
592     __slots__=[]
593     def __init__(self, name):
594         super(Bone_IKRotateInfl, self).__init__(name, 4)
595     def __str__(self):
596         return '<IK_ROTATE_INFL %s>' % (self.name)
597 # 5
598 class Bone_RotateInfl(Bone):
599     __slots__=[]
600     def __init__(self, name):
601         super(Bone_RotateInfl, self).__init__(name, 5)
602     def __str__(self):
603         return '<ROTATE_INFL %s>' % (self.name)
604 # 6
605 class Bone_IKTarget(Bone):
606     __slots__=[]
607     def __init__(self, name):
608         super(Bone_IKTarget, self).__init__(name, 6)
609     def __str__(self):
610         return '<IK_TARGET %s>' % (self.name)
611 # 7
612 class Bone_Unvisible(Bone):
613     __slots__=[]
614     def __init__(self, name):
615         super(Bone_Unvisible, self).__init__(name, 7)
616     def __str__(self):
617         return '<UNVISIBLE %s>' % (self.name)
618 # 8
619 class Bone_Rolling(Bone):
620     __slots__=[]
621     def __init__(self, name):
622         super(Bone_Rolling, self).__init__(name, 8)
623     def __str__(self):
624         return '<ROLLING %s>' % (self.name)
625 # 9
626 class Bone_Tweak(Bone):
627     __slots__=[]
628     def __init__(self, name):
629         super(Bone_Tweak, self).__init__(name, 9)
630     def __str__(self):
631         return '<TWEAK %s>' % (self.name)
632
633
634 def createBone(name, type):
635     if type==0:
636         return Bone_Rotate(name)
637     elif type==1:
638         return Bone_RotateMove(name)
639     elif type==2:
640         return Bone_IK(name)
641     elif type==3:
642         raise Exception("no used bone type: 3(%s)" % name)
643     elif type==4:
644         return Bone_IKRotateInfl(name)
645     elif type==5:
646         return Bone_RotateInfl(name)
647     elif type==6:
648         return Bone_IKTarget(name)
649     elif type==7:
650         return Bone_Unvisible(name)
651     elif type==8:
652         return Bone_Rolling(name)
653     elif type==9:
654         return Bone_Tweak(name)
655     else:
656         raise Exception("unknown bone type: %d(%s)", type, name)
657
658
659 class IK(object):
660     __slots__=['index', 'target', 'iterations', 'weight', 'length', 'children']
661     def __init__(self, index=0, target=0):
662         self.index=index
663         self.target=target
664         self.iterations=None
665         self.weight=None
666         self.children=[]
667
668     def __str__(self):
669         return "<IK index: %d, target: %d, iterations: %d, weight: %f, children: %s(%d)>" %(self.index, self.target, self.iterations, self.weight, '-'.join([str(i) for i in self.children]), len(self.children))
670
671 class Skin(object):
672     __slots__=['name', 'type', 'indices', 'pos_list', 'english_name',
673             'vertex_count']
674     def __init__(self, name='skin'):
675         self.name=name
676         self.type=None
677         self.indices=[]
678         self.pos_list=[]
679         self.english_name=''
680         self.vertex_count=0
681
682     def getName(self): return self.name.decode('cp932')
683     def setName(self, u): self.name=u
684     def setEnglishName(self, u): self.english_name=u
685
686     def append(self, index, x, y, z):
687         self.indices.append(index)
688         self.pos_list.append(Vector3(x, y, z))
689
690     def __str__(self):
691         return '<Skin name: "%s", type: %d, vertex: %d>' % (
692             self.name, self.type, len(self.indices))
693
694 class ToonTexture(object):
695     __slots__=['name']
696     def __init__(self, name): self.name=name
697     def getName(self): return self.name.decode('cp932')
698     def setName(self, u): self.name=u
699
700 class BoneGroup(object):
701     __slots__=['name', 'english_name']
702     def __init__(self, name='group'): self.name=name; self.english_name='center'
703     def getName(self): return self.name.decode('cp932')
704     def setName(self, u): self.name=u
705     def getEnglishName(self): return self.english_name.decode('cp932')
706     def setEnglishName(self, u): self.english_name=u
707
708 class PMDLoader(object):
709     __slots__=['io', 'end', 'pos',
710             'version', 'model_name', 'comment',
711             'english_model_name', 'english_comment',
712             'vertices', 'indices', 'materials', 'bones', 
713             'ik_list', 'morph_list',
714             'face_list', 'bone_group_list', 'bone_display_list',
715             'toon_textures',
716             'no_parent_bones',
717             'rigidbodies', 'constraints',
718             ]
719     def __init__(self):
720         self.version=1.0
721         self.model_name=b"default"
722         self.comment=b"default"
723         self.english_model_name=b'default'
724         self.english_comment=b'default'
725         self.vertices=[]
726         self.indices=[]
727         self.materials=[]
728         self.bones=[]
729         self.ik_list=[]
730         self.morph_list=[]
731
732         self.face_list=[]
733         self.bone_group_list=[]
734         self.bone_display_list=[]
735
736         self.toon_textures=[
737                 ToonTexture(b'toon'), ToonTexture(b'toon'),
738                 ToonTexture(b'toon'), ToonTexture(b'toon'),
739                 ToonTexture(b'toon'), ToonTexture(b'toon'),
740                 ToonTexture(b'toon'), ToonTexture(b'toon'),
741                 ToonTexture(b'toon'), ToonTexture(b'toon'),
742                 ]
743
744         self.no_parent_bones=[]
745
746         self.rigidbodies=[]
747         self.constraints=[]
748
749     def getName(self): return self.model_name.decode('cp932')
750     def setName(self, u): self.model_name=u
751     def getComment(self): return self.comment.decode('cp932')
752     def setComment(self, u): self.comment=u
753     def getEnglishName(self): return self.english_model_name.decode('cp932')
754     def setEnglishName(self, u): self.english_model_name=u
755     def getEnglishComment(self): return self.english_comment.decode('cp932')
756     def setEnglishComment(self, u): self.english_comment=u
757
758     def getToonTexture(self, i): return self.toon_textures[i]
759     def each_vertex(self): return self.vertices
760     def getUV(self, i): return self.vertices[i].uv
761     def addVertex(self): 
762         v=Vertex()
763         self.vertices.append(v)
764         return v
765     def addMaterial(self):
766         m=Material()
767         self.materials.append(m)
768         return m
769     def addBone(self):
770         b=Bone()
771         self.bones.append(b)
772         return b
773     def addIK(self):
774         ik=IK()
775         self.ik_list.append(ik)
776         return ik
777     def addMorph(self):
778         s=Skin()
779         self.morph_list.append(s)
780         return s
781     def addBoneGroup(self):
782         g=BoneGroup()
783         self.bone_group_list.append(g)
784         return g
785     def addBoneDisplay(self, b, g):
786         self.bone_display_list.append((b, g))
787
788     def __str__(self):
789         return '<PMDLoader version: %g, model: "%s", vertex: %d, face: %d, material: %d, bone: %d ik: %d, skin: %d>' % (
790             self.version, self.model_name, len(self.vertices), len(self.indices),
791             len(self.materials), len(self.bones), len(self.ik_list), len(self.morph_list))
792
793     def _check_position(self):
794         """
795         if self.pos:
796             print(self.pos, self.io.tell()-self.pos)
797         """
798         self.pos=self.io.tell()
799         pass
800
801     def read(self, path):
802         size=os.path.getsize(path)
803         f=open(path, "rb")
804         return self.load(path, f, size)
805
806     def load(self, path, io, end):
807         self.io=io
808         self.pos=self.io.tell()
809         self.end=end
810         self._check_position()
811
812         if not self._loadHeader():
813             return False
814         self._check_position()
815
816         if not self._loadVertex():
817             return False
818         self._check_position()
819
820         if not self._loadFace():
821             return False
822         self._check_position()
823
824         if not self._loadMaterial():
825             return False
826         self._check_position()
827
828         if not self._loadBone():
829             return False
830         self._check_position()
831
832         if not self._loadIK():
833             return False
834         self._check_position()
835
836         if not self._loadSkin():
837             return False
838         self._check_position()
839
840         if not self._loadSkinIndex():
841             return False
842         self._check_position()
843
844         if not self._loadBoneName():
845             return False
846         self._check_position()
847
848         if not self._loadBoneIndex():
849             return False
850         self._check_position()
851
852         if not self._loadExtend():
853             print('fail to loadExtend')
854             return False
855
856         # 終端
857         if self.io.tell()!=self.end:
858             print("can not reach eof.")
859             print("current: %d, end: %d, remain: %d" % (
860                     self.io.tell(), self.end, self.end-self.io.tell()))
861
862         # build bone tree
863         for i, child in enumerate(self.bones):
864             if child.parent_index==0xFFFF:
865                 # no parent
866                 self.no_parent_bones.append(child)
867                 child.parent=None
868             else:
869                 # has parent
870                 parent=self.bones[child.parent_index]
871                 child.parent=parent
872                 parent.children.append(child)
873             # 後位置
874             if child.hasChild():
875                 child.tail=self.bones[child.tail_index].pos
876
877         return True
878
879     def write(self, path):
880         io=open(path, 'wb')
881         if not io:
882             return False
883         # Header
884         io.write(b"Pmd")        
885         io.write(struct.pack("f", self.version))
886         io.write(struct.pack("20s", self.model_name))
887         io.write(struct.pack("256s", self.comment))
888
889         # Vertices
890         io.write(struct.pack("I", len(self.vertices)))
891         sVertex=struct.Struct("=8f2H2B") # 38byte
892         assert(sVertex.size==38)
893         for v in self.vertices:
894             data=sVertex.pack( 
895                 v.pos[0], v.pos[1], v.pos[2],
896                 v.normal[0], v.normal[1], v.normal[2],
897                 v.uv[0], v.uv[1],
898                 v.bone0, v.bone1, v.weight0, v.edge_flag)
899             io.write(data)
900
901         # Faces
902         io.write(struct.pack("I", len(self.indices)))
903         io.write(struct.pack("=%dH" % len(self.indices), *self.indices))
904
905         # material
906         io.write(struct.pack("I", len(self.materials)))
907         sMaterial=struct.Struct("=3fff3f3fBBI20s") # 70byte
908         assert(sMaterial.size==70)
909         for m in self.materials:
910             io.write(sMaterial.pack(
911                 m.diffuse[0], m.diffuse[1], m.diffuse[2], m.diffuse[3],
912                 m.shinness, 
913                 m.specular[0], m.specular[1], m.specular[2],
914                 m.ambient[0], m.ambient[1], m.ambient[2],
915                 m.toon_index, m.flag,
916                 m.vertex_count,
917                 m.texture
918                 ))
919
920         # bone
921         io.write(struct.pack("H", len(self.bones)))
922         sBone=struct.Struct("=20sHHBH3f")
923         assert(sBone.size==39)
924         for b in self.bones:
925             io.write(sBone.pack(
926                 b.name,
927                 b.parent_index, b.tail_index, b.type, b.ik_index,
928                 b.pos[0], b.pos[1], b.pos[2]))
929
930         # IK
931         io.write(struct.pack("H", len(self.ik_list)))
932         for ik in self.ik_list:
933             io.write(struct.pack("=2HBHf", 
934                 ik.index, ik.target, ik.length, ik.iterations, ik.weight
935                 ))
936             for c in ik.children:
937                 io.write(struct.pack("H", c))
938
939         # skin
940         io.write(struct.pack("H", len(self.morph_list)))
941         for s in self.morph_list:
942             io.write(struct.pack("20sIB", 
943                 s.name, len(s.indices), s.type))
944             for i, v in zip(s.indices, s.pos_list):
945                 io.write(struct.pack("I3f", i, v[0], v[1], v[2]))
946
947         # skin list
948         io.write(struct.pack("B", len(self.face_list)))
949         for i in self.face_list:
950             io.write(struct.pack("H", i))
951
952         # bone name
953         io.write(struct.pack("B", len(self.bone_group_list)))
954         for g in self.bone_group_list:
955             io.write(struct.pack("50s", g.name))
956
957         # bone list
958         io.write(struct.pack("I", len(self.bone_display_list)))
959         for l in self.bone_display_list:
960             io.write(struct.pack("=HB", *l))
961
962         # ToDo
963         # Extend Data
964
965         return True
966
967
968     def _loadExtend(self):
969         ############################################################
970         # extend1: english name
971         ############################################################
972         if self.io.tell()>=self.end:
973             return True
974         if struct.unpack("B", self.io.read(1))[0]==1:
975             if not self.loadEnglishName():
976                 return False
977         self._check_position()
978
979         ############################################################
980         # extend2: toon texture list
981         ############################################################
982         if self.io.tell()>=self.end:
983             return True
984         if not self.loadToonTexture():
985             return False
986         self._check_position()
987
988         ############################################################
989         # extend3: physics
990         ############################################################
991         if self.io.tell()>=self.end:
992             return True
993         #if not self.loadPhysics():
994         #    return False
995         self._check_position()
996
997         return True
998
999     def _loadHeader(self):
1000         signature=struct.unpack("3s", self.io.read(3))[0]
1001         print(signature)
1002         if signature!=b"Pmd":
1003             print("invalid signature", signature)
1004             return False
1005         self.version=struct.unpack("f", self.io.read(4))[0]
1006         self.model_name = truncate_zero(struct.unpack("20s", self.io.read(20))[0])
1007         self.comment = truncate_zero(
1008                 struct.unpack("256s", self.io.read(256))[0])
1009         return True
1010
1011     def _loadVertex(self):
1012         count = struct.unpack("I", self.io.read(4))[0]
1013         for i in xrange(count):
1014             self.vertices.append(Vertex(*struct.unpack("8f2H2B", self.io.read(38))))
1015         return True
1016
1017     def _loadFace(self):
1018         count = struct.unpack("I", self.io.read(4))[0]
1019         for i in xrange(0, count, 3):
1020             self.indices+=struct.unpack("HHH", self.io.read(6))
1021         return True
1022
1023     def _loadMaterial(self):
1024         count = struct.unpack("I", self.io.read(4))[0]
1025         for i in xrange(count):
1026             material=Material(*struct.unpack("4ff3f3f", self.io.read(44)))
1027             material.toon_index=struct.unpack("B", self.io.read(1))[0]
1028             material.flag=struct.unpack("B", self.io.read(1))[0]
1029             material.vertex_count=struct.unpack("I", self.io.read(4))[0]
1030             texture=truncate_zero(struct.unpack("20s", self.io.read(20))[0])
1031             # todo sphere map
1032             #material.texture=texture.split('*')[0]
1033             material.texture=texture
1034             self.materials.append(material)
1035         return True
1036
1037     def _loadBone(self):
1038         size = struct.unpack("H", self.io.read(2))[0]
1039         for i in xrange(size):
1040             name=truncate_zero(struct.unpack("20s", self.io.read(20))[0])
1041             parent_index, tail_index = struct.unpack("HH", self.io.read(4))
1042             type = struct.unpack("B", self.io.read(1))[0]
1043             bone=createBone(name, type)
1044             bone.parent_index=parent_index
1045             bone.tail_index=tail_index
1046             bone.ik_index = struct.unpack("H", self.io.read(2))[0]
1047             bone.pos = Vector3(*struct.unpack("3f", self.io.read(12)))
1048             bone.english_name="bone%03d" % len(self.bones)
1049             self.bones.append(bone)
1050         return True
1051
1052     def _loadIK(self):
1053         size = struct.unpack("H", self.io.read(2))[0]
1054         for i in xrange(size):
1055             ik=IK(*struct.unpack("2H", self.io.read(4)))
1056             ik.length = struct.unpack("B", self.io.read(1))[0]
1057             ik.iterations = struct.unpack("H", self.io.read(2))[0]
1058             ik.weight = struct.unpack("f", self.io.read(4))[0]
1059             for j in xrange(ik.length):
1060                 ik.children.append(struct.unpack("H", self.io.read(2))[0])
1061             self.ik_list.append(ik)
1062         return True
1063
1064     def _loadSkin(self):
1065         size = struct.unpack("H", self.io.read(2))[0]
1066         for i in xrange(size):
1067             skin=Skin(truncate_zero(struct.unpack("20s", self.io.read(20))[0]))
1068             skin_size = struct.unpack("I", self.io.read(4))[0]
1069             skin.type = struct.unpack("B", self.io.read(1))[0]
1070             for j in xrange(skin_size):
1071                 skin.indices.append(struct.unpack("I", self.io.read(4))[0])
1072                 skin.pos_list.append(
1073                         Vector3(*struct.unpack("3f", self.io.read(12))))
1074             skin.english_name="skin%03d" % len(self.morph_list)
1075             self.morph_list.append(skin)
1076         return True
1077
1078     def _loadSkinIndex(self):
1079         size = struct.unpack("B", self.io.read(1))[0]
1080         for i in xrange(size):
1081             self.face_list.append(struct.unpack("H", self.io.read(2))[0])
1082         return True
1083
1084     def _loadBoneName(self):
1085         size = struct.unpack("B", self.io.read(1))[0]
1086         for i in xrange(size):
1087             self.bone_group_list.append(BoneGroup(
1088                 truncate_zero(struct.unpack("50s", self.io.read(50))[0])))
1089         return True
1090
1091     def _loadBoneIndex(self):
1092         size = struct.unpack("I", self.io.read(4))[0]
1093         for i in xrange(size):
1094             self.bone_display_list.append(struct.unpack("HB", self.io.read(3)))
1095         return True
1096
1097     def loadToonTexture(self):
1098         """
1099         100bytex10
1100         """
1101         for i in xrange(10):
1102             self.toon_textures.append(ToonTexture(
1103                     truncate_zero(struct.unpack("100s", self.io.read(100))[0])))
1104         return True
1105
1106     def loadEnglishName(self):
1107         # english name
1108         self.english_model_name=truncate_zero(
1109                 struct.unpack("20s", self.io.read(20))[0])
1110         self.english_comment=truncate_zero(
1111                 struct.unpack("256s", self.io.read(256))[0])
1112         # english bone list
1113         for bone in self.bones:
1114             english_name=truncate_zero(
1115                     struct.unpack("20s", self.io.read(20))[0])
1116             if english_name!=bone.name:
1117                 bone.english_name=english_name
1118         # english skin list
1119         #for index in self.face_list:
1120         for skin in self.morph_list:
1121             if skin.name=='base':
1122                 continue
1123             english_name=truncate_zero(
1124                     struct.unpack("20s", self.io.read(20))[0])
1125             #skin=self.morph_list[index]
1126             if english_name!=skin.name:
1127                 skin.english_name=english_name
1128         # english bone list
1129         for i in xrange(0, len(self.bone_group_list)):
1130             self.bone_group_list[i].english_name=truncate_zero(
1131                     struct.unpack("50s", self.io.read(50))[0])
1132         return True
1133
1134     def loadPhysics(self):
1135         # 剛体リスト
1136         count = struct.unpack("I", self.io.read(4))[0]
1137         for i in xrange(count):
1138             struct.unpack("83s", self.io.read(83))[0]
1139         # ジョイントリスト
1140         count = struct.unpack("I", self.io.read(4))[0]
1141         for i in xrange(count):
1142             struct.unpack("124s", self.io.read(124))[0]
1143         return True
1144
1145
1146 ###############################################################################
1147 # VPD
1148 ###############################################################################
1149 class LineLoader(object):
1150     """
1151     行指向の汎用ローダ
1152     """
1153     __slots__=['path', 'io', 'end']
1154     def __str__(self):
1155         return "<%s current:%d, end:%d>" % (
1156                 self.__class__, self.getPos(), self.getEnd())
1157
1158     def getPos(self):
1159         return self.io.tell()
1160
1161     def getEnd(self):
1162         return self.end
1163
1164     def readline(self):
1165         return (self.io.readline()).strip()
1166
1167     def isEnd(self):
1168         return self.io.tell()>=self.end
1169
1170     def load(self, path, io, end):
1171         self.path=path
1172         self.io=io
1173         self.end=end
1174         return self.process()
1175
1176     def process(self):
1177         """
1178         dummy. read to end.
1179         """
1180         while not self.isEnd():
1181             self.io.readline()
1182         return True
1183
1184
1185 class VPDLoader(LineLoader):
1186     __slots__=['pose']
1187     def __init__(self):
1188         super(VPDLoader, self).__init__()
1189         self.pose=[]
1190
1191     def __str__(self):
1192         return "<VPD poses:%d>" % len(self.pose)
1193
1194     def process(self):
1195         if self.readline()!="Vocaloid Pose Data file":
1196             return
1197
1198         RE_OPEN=re.compile('^(\w+){(.*)')
1199         RE_OSM=re.compile('^\w+\.osm;')
1200         RE_COUNT=re.compile('^(\d+);')
1201
1202         bone_count=-1
1203         while not self.isEnd():
1204             line=self.readline()
1205             if line=='':
1206                 continue
1207             m=RE_OPEN.match(line)
1208             if m:
1209                 if not self.parseBone(m.group(2)):
1210                     raise Exception("invalid bone")
1211                 continue
1212
1213             m=RE_OSM.match(line)
1214             if m:
1215                 continue
1216
1217             m=RE_COUNT.match(line)
1218             if m:
1219                 bone_count=int(m.group(1))
1220                 continue
1221
1222         return len(self.pose)==bone_count
1223
1224     def parseBone(self, name):
1225         bone=MotionData(name)
1226         self.pose.append(bone)
1227         bone.pos=Vector3(*[float(token) for token in self.readline().split(';')[0].split(',')])
1228         bone.q=Quaternion(*[float(token) for token in self.readline().split(';')[0].split(',')])
1229         return self.readline()=="}"
1230
1231
1232 ###############################################################################
1233 # interface
1234 ###############################################################################
1235 def load_pmd(path):
1236     size=os.path.getsize(path)
1237     f=open(path, "rb")
1238     l=PMDLoader()
1239     if l.load(path, f, size):
1240         return l
1241
1242 def load_vmd(path):
1243     size=os.path.getsize(path)
1244     f=open(path, "rb")
1245     l=VMDLoader()
1246     if l.load(path, f, size):
1247         return l
1248
1249 def load_vpd(path):
1250     f=open(path, 'rb')
1251     if not f:
1252         return;
1253     size=os.path.getsize(path)
1254     l=VPDLoader()
1255     if l.load(path, f, size):
1256         return l
1257
1258
1259 ###############################################################################
1260 # debug
1261 ###############################################################################
1262 def debug_pmd(path):
1263     l=load_pmd(path)
1264     if not l:
1265         print("fail to load")
1266         sys.exit()
1267
1268     print(unicode(l).encode(ENCODING))
1269     print(l.comment.encode(ENCODING))
1270     print("<ボーン>".decode('utf-8').encode(ENCODING))
1271     for bone in l.no_parent_bones:
1272         print(bone.name.encode(ENCODING))
1273         bone.display()
1274     #for bone in l.bones:
1275     #    uni="%s:%s" % (bone.english_name, bone.name)
1276     #    print uni.encode(ENCODING)
1277     #for skin in l.morph_list:
1278     #    uni="%s:%s" % (skin.english_name, skin.name)
1279     #    print uni.encode(ENCODING)
1280     #for i, v in enumerate(l.vertices):
1281     #    print i, v
1282     #for i, f in enumerate(l.indices):
1283     #    print i, f
1284     for m in l.materials:
1285         print(m)
1286
1287 def debug_pmd_write(path, out):
1288     l=load_pmd(path)
1289     if not l:
1290         print("fail to load")
1291         sys.exit()
1292
1293     if not l.write(out):
1294         print("fail to write")
1295         sys.exit()
1296
1297 def debug_vmd(path):
1298     l=load_vmd(path)
1299     if not l:
1300         print("fail to load")
1301         sys.exit()
1302     print(unicode(l).encode(ENCODING))
1303
1304     #for m in l.motions[u'センター']:
1305     #    print m.frame, m.pos
1306     for n, m in l.shapes.items():
1307         print(unicode(n).encode(ENCODING), getEnglishSkinName(n))
1308
1309 def debug_vpd(path):
1310     l=load_vpd(path)
1311     if not l:
1312         print("fail to load")
1313         sys.exit()
1314     for bone in l.pose:
1315         print(unicode(bone).encode(ENCODING))
1316
1317 if __name__=="__main__":
1318     if len(sys.argv)<2:
1319         print("usage: %s {pmd file}" % sys.argv[0])
1320         print("usage: %s {vmd file}" % sys.argv[0])
1321         print("usage: %s {vpd file}" % sys.argv[0])
1322         print("usage: %s {pmd file} {export pmdfile}" % sys.argv[0])
1323         sys.exit()
1324
1325     path=sys.argv[1]
1326     if not os.path.exists(path):
1327         print("no such file: %s" % path)
1328
1329     if path.lower().endswith('.pmd'):
1330         if len(sys.argv)==2:
1331             debug_pmd(path)
1332         else:
1333             debug_pmd_write(path, sys.argv[2])
1334     elif path.lower().endswith('.vmd'):
1335         debug_vmd(path)
1336     elif path.lower().endswith('.vpd'):
1337         debug_vpd(path)
1338     else:
1339         print("unknown file type: %s" % path)
1340         sys.exit()
1341