OSDN Git Service

14600e304ecf9f67c54ed7e19c6b73ea6564b8da
[meshio/pymeshio.git] / blender25-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 if sys.version_info[0]<3:
39     def truncate_zero(src):
40         """
41         0x00以降を捨てる
42         """
43         assert(type(src)==bytes)
44         pos = src.find(b"\x00")
45         if pos >= 0:
46             return src[:pos]
47         else:
48             return src
49 else:
50     def truncate_zero(src):
51         """
52         0x00以降を捨てる
53         """
54         assert(type(src)==bytes)
55         pos = src.find(b"\x00")
56         if pos >= 0:
57             return src[:pos].decode('cp932')
58         else:
59             return src.decode('cp932')
60
61
62 if sys.version_info[0]<3:
63     def to_str(src):
64         t=type(src)
65         if t==unicode:
66             return src.encode('cp932')
67         elif t==str:
68             return src
69         else:
70             raise "INVALID str: %s" % t
71
72 else:
73     def to_str(src):
74         t=type(src)
75         if t==str:
76             return src
77         elif t==bytes:
78             return src.decode('cp932')
79         else:
80             raise "INVALID str: %s" % t
81
82
83 def radian_to_degree(x):
84     return x/math.pi * 180.0
85
86
87 ###############################################################################
88 # geometry
89 ###############################################################################
90 class Vector2(object):
91     __slots__=['x', 'y']
92     def __init__(self, x=0, y=0):
93         self.x=x
94         self.y=y
95
96     def __str__(self):
97         return "<%f %f>" % (self.x, self.y)
98
99     def __getitem__(self, key):
100         if key==0:
101             return self.x
102         elif key==1:
103             return self.y
104         else:
105             assert(False)
106
107     def to_tuple(self):
108         return (self.x, self.y)
109
110
111 class Vector3(object):
112     __slots__=['x', 'y', 'z']
113     def __init__(self, x=0, y=0, z=0):
114         self.x=x
115         self.y=y
116         self.z=z
117
118     def __str__(self):
119         return "<%f %f %f>" % (self.x, self.y, self.z)
120
121     def __getitem__(self, key):
122         if key==0:
123             return self.x
124         elif key==1:
125             return self.y
126         elif key==2:
127             return self.z
128         else:
129             assert(False)
130
131     def to_tuple(self):
132         return (self.x, self.y, self.z)
133
134     def __add__(l, r):
135         return Vector3(l.x+r.x, l.y+r.y, l.z+r.z)
136
137
138 class Quaternion(object):
139     __slots__=['x', 'y', 'z', 'w']
140     def __init__(self, x=0, y=0, z=0, w=1):
141         self.x=x
142         self.y=y
143         self.z=z
144         self.w=w
145
146     def __str__(self):
147         return "<%f %f %f %f>" % (self.x, self.y, self.z, self.w)
148
149     def __mul__(self, rhs):
150         u=numpy.array([self.x, self.y, self.z], 'f')
151         v=numpy.array([rhs.x, rhs.y, rhs.z], 'f')
152         xyz=self.w*v+rhs.w*u+numpy.cross(u, v)
153         q=Quaternion(xyz[0], xyz[1], xyz[2], self.w*rhs.w-numpy.dot(u, v))
154         return q
155
156     def dot(self, rhs):
157         return self.x*rhs.x+self.y*rhs.y+self.z*rhs.z+self.w*rhs.w
158
159     def getMatrix(self):
160         sqX=self.x*self.x
161         sqY=self.y*self.y
162         sqZ=self.z*self.z
163         xy=self.x*self.y
164         xz=self.x*self.z
165         yz=self.y*self.z
166         wx=self.w*self.x
167         wy=self.w*self.y
168         wz=self.w*self.z
169         return numpy.array([
170                 # 1
171                 [1-2*sqY-2*sqZ, 2*xy+2*wz, 2*xz-2*wy, 0],
172                 # 2
173                 [2*xy-2*wz, 1-2*sqX-2*sqZ, 2*yz+2*wx, 0],
174                 # 3
175                 [2*xz+2*wy, 2*yz-2*wx, 1-2*sqX-2*sqY, 0],
176                 # 4
177                 [0, 0, 0, 1]],
178                 'f')
179
180     def getRHMatrix(self):
181         x=-self.x
182         y=-self.y
183         z=self.z
184         w=self.w
185         sqX=x*x
186         sqY=y*y
187         sqZ=z*z
188         xy=x*y
189         xz=x*z
190         yz=y*z
191         wx=w*x
192         wy=w*y
193         wz=w*z
194         return numpy.array([
195                 # 1
196                 [1-2*sqY-2*sqZ, 2*xy+2*wz, 2*xz-2*wy, 0],
197                 # 2
198                 [2*xy-2*wz, 1-2*sqX-2*sqZ, 2*yz+2*wx, 0],
199                 # 3
200                 [2*xz+2*wy, 2*yz-2*wx, 1-2*sqX-2*sqY, 0],
201                 # 4
202                 [0, 0, 0, 1]],
203                 'f')
204
205     def getRollPitchYaw(self):
206         m=self.getMatrix()
207
208         roll = math.atan2(m[0, 1], m[1, 1])
209         pitch = math.asin(-m[2, 1])
210         yaw = math.atan2(m[2, 0], m[2, 2])
211
212         if math.fabs(math.cos(pitch)) < 1.0e-6:
213             roll += m[0, 1] > math.pi if 0.0 else -math.pi
214             yaw += m[2, 0] > math.pi if 0.0 else -math.pi
215
216         return roll, pitch, yaw
217
218     def getSqNorm(self):
219         return self.x*self.x+self.y*self.y+self.z*self.z+self.w*self.w
220
221     def getNormalized(self):
222         f=1.0/self.getSqNorm()
223         q=Quaternion(self.x*f, self.y*f, self.z*f, self.w*f)
224         return q
225
226     def getRightHanded(self):
227         "swap y and z axis"
228         return Quaternion(-self.x, -self.z, -self.y, self.w)
229
230     @staticmethod
231     def createFromAxisAngle(axis, rad):
232         q=Quaternion()
233         half_rad=rad/2.0
234         c=math.cos(half_rad)
235         s=math.sin(half_rad)
236         return Quaternion(axis[0]*s, axis[1]*s, axis[2]*s, c)
237
238
239 class RGBA(object):
240     __slots__=['r', 'g', 'b', 'a']
241     def __init__(self, r=0, g=0, b=0, a=1):
242         self.r=r
243         self.g=g
244         self.b=b
245         self.a=a
246
247     def __getitem__(self, key):
248         if key==0:
249             return self.r
250         elif key==1:
251             return self.g
252         elif key==2:
253             return self.b
254         elif key==3:
255             return self.a
256         else:
257             assert(False)
258
259
260
261
262 ###############################################################################
263 # interface
264 ###############################################################################
265 def load_pmd(path):
266     size=os.path.getsize(path)
267     f=open(path, "rb")
268     l=PMDLoader()
269     if l.load(path, f, size):
270         return l
271
272 def load_vmd(path):
273     size=os.path.getsize(path)
274     f=open(path, "rb")
275     l=VMDLoader()
276     if l.load(path, f, size):
277         return l
278
279 def load_vpd(path):
280     f=open(path, 'rb')
281     if not f:
282         return;
283     size=os.path.getsize(path)
284     l=VPDLoader()
285     if l.load(path, f, size):
286         return l
287
288
289 ###############################################################################
290 # debug
291 ###############################################################################
292 def debug_pmd(path):
293     l=load_pmd(path)
294     if not l:
295         print("fail to load")
296         sys.exit()
297
298     print(unicode(l).encode(ENCODING))
299     print(l.comment.encode(ENCODING))
300     print("<ボーン>".decode('utf-8').encode(ENCODING))
301     for bone in l.no_parent_bones:
302         print(bone.name.encode(ENCODING))
303         bone.display()
304     #for bone in l.bones:
305     #    uni="%s:%s" % (bone.english_name, bone.name)
306     #    print uni.encode(ENCODING)
307     #for skin in l.morph_list:
308     #    uni="%s:%s" % (skin.english_name, skin.name)
309     #    print uni.encode(ENCODING)
310     #for i, v in enumerate(l.vertices):
311     #    print i, v
312     #for i, f in enumerate(l.indices):
313     #    print i, f
314     for m in l.materials:
315         print(m)
316
317 def debug_pmd_write(path, out):
318     l=load_pmd(path)
319     if not l:
320         print("fail to load")
321         sys.exit()
322
323     if not l.write(out):
324         print("fail to write")
325         sys.exit()
326
327 def debug_vmd(path):
328     l=load_vmd(path)
329     if not l:
330         print("fail to load")
331         sys.exit()
332     print(unicode(l).encode(ENCODING))
333
334     #for m in l.motions[u'センター']:
335     #    print m.frame, m.pos
336     for n, m in l.shapes.items():
337         print(unicode(n).encode(ENCODING), getEnglishSkinName(n))
338
339 def debug_vpd(path):
340     l=load_vpd(path)
341     if not l:
342         print("fail to load")
343         sys.exit()
344     for bone in l.pose:
345         print(unicode(bone).encode(ENCODING))
346
347 if __name__=="__main__":
348     if len(sys.argv)<2:
349         print("usage: %s {pmd file}" % sys.argv[0])
350         print("usage: %s {vmd file}" % sys.argv[0])
351         print("usage: %s {vpd file}" % sys.argv[0])
352         print("usage: %s {pmd file} {export pmdfile}" % sys.argv[0])
353         sys.exit()
354
355     path=sys.argv[1]
356     if not os.path.exists(path):
357         print("no such file: %s" % path)
358
359     if path.lower().endswith('.pmd'):
360         if len(sys.argv)==2:
361             debug_pmd(path)
362         else:
363             debug_pmd_write(path, sys.argv[2])
364     elif path.lower().endswith('.vmd'):
365         debug_vmd(path)
366     elif path.lower().endswith('.vpd'):
367         debug_vpd(path)
368     else:
369         print("unknown file type: %s" % path)
370         sys.exit()
371