OSDN Git Service

implement pmdbuilder
[meshio/pymeshio.git] / blender25-meshio / pymeshio / vmd.py
1 ###############################################################################
2 # VMD
3 ###############################################################################
4 class ShapeData(object):
5     __slots__=['name', 'frame', 'ratio']
6     def __init__(self, name):
7         self.name=name
8         self.frame=-1
9         self.ratio=0
10
11     def __cmp__(self, other):
12         return cmp(self.frame, other.frame)
13
14 class MotionData(object):
15     __slots__=['name', 'frame', 'pos', 'q', 'complement']
16     def __init__(self, name):
17         self.name=name
18         self.frame=-1
19         self.pos=Vector3()
20         self.q=Quaternion()
21
22     def __cmp__(self, other):
23         return cmp(self.frame, other.frame)
24
25     def __str__(self):
26         return '<MotionData "%s" %d %s%s>' % (self.name, self.frame, self.pos, self.q)
27
28 class VMDLoader(object):
29     __slots__=['io', 'end', 'signature',
30             'model_name', 'last_frame',
31             'motions', 'shapes', 'cameras', 'lights',
32             ]
33     def __init__(self):
34         self.model_name=''
35         self.motions=[]
36         self.shapes=[]
37         self.cameras=[]
38         self.lights=[]
39         self.last_frame=0
40
41     def __str__(self):
42         return '<VMDLoader model: "%s", motion: %d, shape: %d, camera: %d, light: %d>' % (
43             self.model_name, len(self.motions), len(self.shapes),
44             len(self.cameras), len(self.lights))
45
46     def load(self, path, io, end):
47         self.io=io
48         self.end=end
49
50         # signature
51         self.signature=truncate_zero(self.io.read(30))
52         version=self.validate_signature(self.signature)
53         if not version:
54             print("invalid signature", self.signature)
55             return False
56
57         if version==1:
58             if not self.load_verstion_1():
59                 return False
60         elif version==2:
61             if not  self.load_verstion_2():
62                 return False 
63         else:
64             raise Exception("unknown version") 
65
66         # post process
67         motions=self.motions
68         self.motions={}
69         for m in motions:
70             if not m.name in self.motions:
71                 self.motions[m.name]=[]
72             self.motions[m.name].append(m)
73         for name in self.motions.keys():
74             self.motions[name].sort()
75
76         shapes=self.shapes
77         self.shapes={}
78         for s in shapes:
79             if not s.name in self.shapes:
80                 self.shapes[s.name]=[]
81             self.shapes[s.name].append(s)
82         for name in self.shapes.keys():
83             self.shapes[name].sort()
84
85         return True
86
87     def getMotionCount(self):
88         count=0
89         for v in self.motions.values():
90             count+=len(v)
91         return count
92
93     def getShapeCount(self):
94         count=0
95         for v in self.shapes.values():
96             count+=len(v)
97         return count
98
99     def load_verstion_1(self):
100         # model name
101         self.model_name=truncate_zero(self.io.read(10))
102         if not self.loadMotion_1():
103             return False
104         return True
105
106     def loadMotion_1(self):
107         count=struct.unpack('H', self.io.read(2))[0]
108         self.io.read(2)
109         for i in xrange(0, count):
110             self.loadFrameData()
111         return True
112
113     ############################################################
114     def load_verstion_2(self):
115         # model name
116         self.model_name=truncate_zero(self.io.read(20))
117
118         if not self.loadMotion():
119             return False
120         if not self.loadShape():
121             return False
122         if not self.loadCamera():
123             return False
124         if not self.loadLight():
125             return False
126         #assert(self.io.tell()==self.end)
127         #self.motions.sort(lambda l, r: l.name<r.name)
128
129         return True
130
131     def validate_signature(self, signature):
132         if self.signature == "Vocaloid Motion Data 0002":
133             return 2
134         if self.signature == "Vocaloid Motion Data file":
135             return 1
136         else:
137             return None
138
139     def loadMotion(self):
140         count=struct.unpack('I', self.io.read(4))[0]
141         for i in xrange(0, count):
142             self.loadFrameData()
143         return True
144
145     def loadShape(self):
146         count=struct.unpack('I', self.io.read(4))[0]
147         for i in xrange(0, count):
148             self.loadShapeData()
149         return True
150
151     def loadCamera(self):
152         count=struct.unpack('I', self.io.read(4))[0]
153         for i in xrange(0, count):
154             # not implemented
155             assert(False)
156             pass
157         return True
158
159     def loadLight(self):
160         count=struct.unpack('I', self.io.read(4))[0]
161         for i in xrange(0, count):
162             # not implemented
163             assert(False)
164             pass
165         return True
166
167     def loadFrameData(self):
168         """
169         フレームひとつ分を読み込む
170         """
171         data=MotionData(truncate_zero(self.io.read(15)))
172         (data.frame, data.pos.x, data.pos.y, data.pos.z,
173         data.q.x, data.q.y, data.q.z, data.q.w) = struct.unpack(
174                 'I7f', self.io.read(32))
175         # complement data
176         data.complement=''.join(
177                 ['%x' % x for x in struct.unpack('64B', self.io.read(64))])
178         self.motions.append(data)
179         if data.frame>self.last_frame:
180             self.last_frame=data.frame
181
182     def loadShapeData(self):
183         """
184         モーフデータひとつ分を読み込む
185         """
186         data=ShapeData(truncate_zero(self.io.read(15)))
187         (data.frame, data.ratio)=struct.unpack('If', self.io.read(8))
188         self.shapes.append(data)
189         if data.frame>self.last_frame:
190             self.last_frame=data.frame
191
192     # vmd -> csv
193     ############################################################
194     def create_csv_line(m):
195         # quaternion -> euler angle
196         (roll, pitch, yaw)=m.q.getRollPitchYaw()
197         return '%s,%d,%g,%g,%g,%g,%g,%g,0x%s\n' % (
198                 m.name, m.frame, m.pos.x, m.pos.y, m.pos.z,
199                 to_degree(pitch), to_degree(yaw), to_degree(roll), m.complement
200                 )
201
202     def write_csv(l, path):
203         sys.setdefaultencoding('cp932')
204         csv=open(path, "w")
205         csv.write('%s,0\n' % l.signature)
206         csv.write('%s\n' % l.model_name)
207         # motion
208         csv.write('%d\n' % len(l.motions))
209         for m in l.motions:
210             csv.write(create_csv_line(m))
211         # shape
212         csv.write('%d\n' % len(l.shapes))
213         for s in l.shapes:
214             csv.write('%s,%d,%f\n' % ( s.name, s.frame, s.ratio))
215         # camera
216         csv.write('%d\n' % len(l.cameras))
217         for camera in l.cameras:
218             assert(False)
219         # light
220         csv.write('%d\n' % len(l.lights))
221         for light in l.lights:
222             assert(False)
223