OSDN Git Service

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