OSDN Git Service

implement converter
[meshio/pymeshio.git] / pymeshio / pmd / __init__.py
1 # coding: utf-8
2 """
3 ========================
4 MikuMikuDance PMD format
5 ========================
6
7 file format
8 ~~~~~~~~~~~
9 * http://blog.goo.ne.jp/torisu_tetosuki/e/209ad341d3ece2b1b4df24abf619d6e4 
10
11 specs
12 ~~~~~
13 * textencoding: bytes(cp932)
14 * coordinate: left handed y-up(DirectX)
15 * uv origin: 
16 * face: only triangle
17 * backculling: 
18
19 """
20 import os
21 import sys
22 import struct
23 import warnings
24 from .. import common
25
26
27 class Vertex(object):
28     """
29     ==========
30     pmd vertex
31     ==========
32     two bone weighted vertex with normal and uv.
33
34     format
35     ~~~~~~
36     * http://blog.goo.ne.jp/torisu_tetosuki/e/5a1b16e2f61067838dfc66d010389707
37
38     :IVariables:
39         pos
40             Vector3
41         normal 
42             Vector3
43         uv 
44             Vector2
45         bone0 
46             bone index
47         bone1 
48             bone index
49         weight0 
50             bone0 influence.  min: 0, max: 100
51         edge_flag 
52             int flag.  0: edge on, 1: edge off
53     """
54     __slots__=['pos', 'normal', 'uv', 'bone0', 'bone1', 'weight0', 'edge_flag']
55     def __init__(self, pos, normal, uv, 
56             bone0, bone1, weight0, edge_flag):
57         self.pos=pos
58         self.normal=normal
59         self.uv=uv
60         self.bone0=bone0
61         self.bone1=bone1
62         self.weight0=weight0
63         self.edge_flag=edge_flag
64
65     def __str__(self):
66         return "<%s %s %s, (%d, %d, %d)>" % (
67                 str(self.pos), 
68                 str(self.normal), 
69                 str(self.uv), 
70                 self.bone0, self.bone1, self.weight0)
71
72     def __eq__(self, rhs):
73         return (
74                 self.pos==rhs.pos
75                 and self.normal==rhs.normal
76                 and self.uv==rhs.uv
77                 and self.bone0==rhs.bone0
78                 and self.bone1==rhs.bone1
79                 and self.weight0==rhs.weight0
80                 and self.edge_flag==rhs.edge_flag
81                 )
82
83     def __getitem__(self, key):
84         if key==0:
85             return self.pos.x
86         elif key==1:
87             return self.pos.y
88         elif key==2:
89             return self.pos.z
90         else:
91             assert(False)
92
93
94 class Material(object):
95     """
96     ============
97     pmd material
98     ============
99
100     format
101     ~~~~~~
102     * http://blog.goo.ne.jp/torisu_tetosuki/e/ea0bb1b1d4c6ad98a93edbfe359dac32
103
104     :IVariables:
105         diffuse_color
106             RGB
107         alpha
108             float
109         specular_factor
110             float
111         specular_color
112             RGB
113         ambient_color
114             RGB
115         toon_index
116             int
117         edge_flag
118             int
119         vertex_count
120             indices length
121         texture_file
122             texture file path
123     """
124     __slots__=[
125             'diffuse_color', 'alpha', 
126             'specular_factor', 'specular_color', 'ambient_color', 
127             'toon_index', 'edge_flag',
128             'vertex_count', 'texture_file', 
129             ]
130     def __init__(self, diffuse_color, alpha,
131             specular_factor, specular_color, ambient_color,
132             toon_index, edge_flag, vertex_count, texture_file):
133         self.diffuse_color=diffuse_color
134         self.alpha=alpha
135         self.specular_factor=specular_factor
136         self.specular_color=specular_color
137         self.ambient_color=ambient_color
138         self.toon_index=toon_index
139         self.edge_flag=edge_flag
140         self.vertex_count=vertex_count
141         self.texture_file=texture_file
142
143     def __str__(self):
144         return "<Material [%f, %f, %f, %f]>" % (
145                 self.diffuse[0], self.diffuse[1], 
146                 self.diffuse[2], self.diffuse[3],
147                 )
148
149     def __eq__(self, rhs):
150         return (
151                 self.diffuse_color==rhs.diffuse_color
152                 and self.alpha==rhs.alpha
153                 and self.specular_factor==rhs.specular_factor
154                 and self.specular_color==rhs.specular_color
155                 and self.ambient_color==rhs.ambient_color
156                 and self.toon_index==rhs.toon_index
157                 and self.edge_flag==rhs.edge_flag
158                 and self.vertex_count==rhs.vertex_count
159                 and self.texture_file==rhs.texture_file
160                 )
161
162
163 class Bone(object):
164     """
165     ==========
166     pmd bone
167     ==========
168
169     format
170     ~~~~~~
171     * http://blog.goo.ne.jp/torisu_tetosuki/e/638463f52d0ad6ca1c46fd315a9b17d0
172
173     :IVariables:
174         name 
175             bone name
176         english_name
177             bone english_name
178         index
179             boen index(append for internal use)
180         type
181             bone type
182         ik
183             ik(append for internal use)
184         pos
185             bone head position
186         ik_index
187             ik target bone index
188         parent_index
189             parent bone index
190         tail_index
191             tail bone index
192         parent
193             parent bone(append for internal use)
194         tail
195             tail bone(append for internal use)
196         children
197             children bone(append for internal use)
198     """
199     # kinds
200     ROTATE = 0
201     ROTATE_MOVE = 1
202     IK = 2
203     IK_ROTATE_INFL = 4
204     ROTATE_INFL = 5
205     IK_TARGET = 6
206     UNVISIBLE = 7
207     # since v4.0
208     ROLLING=8 # ?
209     TWEAK=9
210     __slots__=['name', 'index', 'type', 'parent', 'ik', 'pos',
211             'children', 'english_name', 'ik_index',
212             'parent_index', 'tail_index', 'tail',
213             ]
214     def __init__(self, name=b'bone', type=0):
215         self.name=name
216         self.index=0
217         self.type=type
218         self.parent_index=0xFFFF
219         self.tail_index=0
220         self.tail=common.Vector3(0, 0, 0)
221         self.parent=None
222         self.ik_index=0xFFFF
223         self.pos=common.Vector3(0, 0, 0)
224         self.children=[]
225         self.english_name=''
226
227     def __eq__(self, rhs):
228         return (
229                 self.name==rhs.name
230                 and self.index==rhs.index
231                 and self.type==rhs.type
232                 and self.parent_index==rhs.parent_index
233                 and self.tail_index==rhs.tail_index
234                 and self.tail==rhs.tail
235                 and self.ik_index==rhs.ik_index
236                 and self.pos==rhs.pos
237                 and self.children==rhs.children
238                 and self.english_name==rhs.english_name
239                 )
240
241     def hasParent(self):
242         return self.parent_index!=0xFFFF
243
244     def hasChild(self):
245         return self.tail_index!=0
246
247     def display(self, indent=[]):
248         if len(indent)>0:
249             prefix=''
250             for i, is_end in enumerate(indent):
251                 if i==len(indent)-1:
252                     break
253                 else:
254                     prefix+='  ' if is_end else ' |'
255             uni='%s +%s(%s)' % (prefix, unicode(self), self.english_name)
256             print(uni.encode(ENCODING))
257         else:
258             uni='%s(%s)' % (unicode(self), self.english_name)
259             print(uni.encode(ENCODING))
260
261         child_count=len(self.children)
262         for i in range(child_count):
263             child=self.children[i]
264             if i<child_count-1:
265                 child.display(indent+[False])
266             else:
267                 # last
268                 child.display(indent+[True])
269
270 # 0
271 class Bone_Rotate(Bone):
272     __slots__=[]
273     def __init__(self, name):
274         super(Bone_Rotate, self).__init__(name, 0)
275     def __str__(self):
276         return '<ROTATE %s>' % (self.name)
277 # 1
278 class Bone_RotateMove(Bone):
279     __slots__=[]
280     def __init__(self, name):
281         super(Bone_RotateMove, self).__init__(name, 1)
282     def __str__(self):
283         return '<ROTATE_MOVE %s>' % (self.name)
284 # 2
285 class Bone_IK(Bone):
286     __slots__=[]
287     def __init__(self, name):
288         super(Bone_IK, self).__init__(name, 2)
289     def __str__(self):
290         return '<IK %s>' % (self.name)
291 # 4
292 class Bone_IKRotateInfl(Bone):
293     __slots__=[]
294     def __init__(self, name):
295         super(Bone_IKRotateInfl, self).__init__(name, 4)
296     def __str__(self):
297         return '<IK_ROTATE_INFL %s>' % (self.name)
298 # 5
299 class Bone_RotateInfl(Bone):
300     __slots__=[]
301     def __init__(self, name):
302         super(Bone_RotateInfl, self).__init__(name, 5)
303     def __str__(self):
304         return '<ROTATE_INFL %s>' % (self.name)
305 # 6
306 class Bone_IKTarget(Bone):
307     __slots__=[]
308     def __init__(self, name):
309         super(Bone_IKTarget, self).__init__(name, 6)
310     def __str__(self):
311         return '<IK_TARGET %s>' % (self.name)
312 # 7
313 class Bone_Unvisible(Bone):
314     __slots__=[]
315     def __init__(self, name):
316         super(Bone_Unvisible, self).__init__(name, 7)
317     def __str__(self):
318         return '<UNVISIBLE %s>' % (self.name)
319 # 8
320 class Bone_Rolling(Bone):
321     __slots__=[]
322     def __init__(self, name):
323         super(Bone_Rolling, self).__init__(name, 8)
324     def __str__(self):
325         return '<ROLLING %s>' % (self.name)
326 # 9
327 class Bone_Tweak(Bone):
328     __slots__=[]
329     def __init__(self, name):
330         super(Bone_Tweak, self).__init__(name, 9)
331     def __str__(self):
332         return '<TWEAK %s>' % (self.name)
333
334
335 def createBone(name, type):
336     if type==0:
337         return Bone_Rotate(name)
338     elif type==1:
339         return Bone_RotateMove(name)
340     elif type==2:
341         return Bone_IK(name)
342     elif type==3:
343         raise Exception("no used bone type: 3(%s)" % name)
344     elif type==4:
345         return Bone_IKRotateInfl(name)
346     elif type==5:
347         return Bone_RotateInfl(name)
348     elif type==6:
349         return Bone_IKTarget(name)
350     elif type==7:
351         return Bone_Unvisible(name)
352     elif type==8:
353         return Bone_Rolling(name)
354     elif type==9:
355         return Bone_Tweak(name)
356     else:
357         raise Exception("unknown bone type: %d(%s)", type, name)
358
359
360 class IK(object):
361     __slots__=['index', 'target', 'iterations', 'weight', 'length', 'children']
362     def __init__(self, index=0, target=0):
363         self.index=index
364         self.target=target
365         self.iterations=None
366         self.weight=None
367         self.children=[]
368
369     def __str__(self):
370         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))
371
372     def __eq__(self, rhs):
373         return (
374                 self.index==rhs.index
375                 and self.target==rhs.target
376                 and self.iterations==rhs.iterations
377                 and self.weight==rhs.weight
378                 and self.children==rhs.children
379                 )
380
381
382 class Morph(object):
383     __slots__=['name', 'type', 'indices', 'pos_list', 'english_name',
384             'vertex_count']
385     def __init__(self, name):
386         self.name=name
387         self.type=None
388         self.indices=[]
389         self.pos_list=[]
390         self.english_name=''
391         self.vertex_count=0
392
393     def append(self, index, x, y, z):
394         self.indices.append(index)
395         self.pos_list.append(common.Vector3(x, y, z))
396
397     def __str__(self):
398         return '<Skin name: "%s", type: %d, vertex: %d>' % (
399             self.name, self.type, len(self.indices))
400
401     def __eq__(self, rhs):
402         return (
403                 self.name==rhs.name
404                 and self.type==rhs.type
405                 and self.indices==rhs.indices
406                 and self.pos_list==rhs.pos_list
407                 and self.english_name==rhs.english_name
408                 and self.vertex_count==rhs.vertex_count
409                 )
410
411
412 class BoneGroup(object):
413     __slots__=['name', 'english_name']
414     def __init__(self, name=b'group', english_name=b'center'): 
415         self.name=name
416         self.english_name=english_name
417
418     def __eq__(self, rhs):
419         return self.name==rhs.name and self.english_name==rhs.english_name
420
421
422 SHAPE_SPHERE=0
423 SHAPE_BOX=1
424 SHAPE_CAPSULE=2
425
426 RIGIDBODY_KINEMATICS=0
427 RIGIDBODY_PHYSICS=1
428 RIGIDBODY_PHYSICS_WITH_BONE=2
429
430
431 class RigidBody(object):
432     __slots__=['name', 
433             'bone_index', 
434             'collision_group', 
435             'no_collision_group', 
436             'shape_type',
437             'shape_size',
438             'shape_position', 
439             'shape_rotation', 
440             'mass',
441             'linear_damping', 
442             'angular_damping', 
443             'restitution', 
444             'friction', 
445             'mode'
446             ]
447     def __init__(self, name,
448             bone_index, 
449             collision_group, 
450             no_collision_group, 
451             mass,
452             linear_damping, 
453             angular_damping, 
454             restitution, 
455             friction, 
456             mode,
457             shape_type=0,
458             shape_size=common.Vector3(),
459             shape_position=common.Vector3(), 
460             shape_rotation=common.Vector3() 
461             ):
462         self.name=name
463         self.bone_index=bone_index
464         self.collision_group=collision_group 
465         self.no_collision_group=no_collision_group 
466         self.shape_type=shape_type
467         self.shape_size=shape_size
468         self.shape_position=shape_position
469         self.shape_rotation=shape_rotation
470         self.mass=mass
471         self.linear_damping=linear_damping
472         self.angular_damping=angular_damping
473         self.restitution=restitution
474         self.friction=friction
475         self.mode=mode
476
477     def __eq__(self, rhs):
478         return (
479                 self.name==rhs.name
480                 and self.bone_index==rhs.bone_index
481                 and self.collision_group==rhs.collision_group
482                 and self.no_collision_group==rhs.no_collision_group
483                 and self.shape_type==rhs.shape_type
484                 and self.shape_size==rhs.shape_size
485                 and self.shape_position==rhs.shape_position
486                 and self.shape_rotation==rhs.shape_rotation
487                 and self.mass==rhs.mass
488                 and self.linear_damping==rhs.linear_damping
489                 and self.angular_damping==rhs.angular_damping
490                 and self.restitution==rhs.restitution
491                 and self.friction==rhs.friction
492                 and self.mode==rhs.mode
493                 )
494
495
496 class Joint(object):
497     __slots__=[ 'name', 'rigidbody_index_a', 'rigidbody_index_b', 
498             'position', 'rotation',
499             'translation_limit_max', 'translation_limit_min',
500             'rotation_limit_max', 'rotation_limit_min',
501             'spring_constant_translation', 'spring_constant_rotation',
502             ]
503     def __init__(self, name,
504             rigidbody_index_a, rigidbody_index_b,
505             position, rotation,
506             translation_limit_max, translation_limit_min,
507             rotation_limit_max, rotation_limit_min,
508             spring_constant_translation, spring_constant_rotation
509             ):
510         self.name=name
511         self.rigidbody_index_a=rigidbody_index_a
512         self.rigidbody_index_b=rigidbody_index_b
513         self.position=position
514         self.rotation=rotation
515         self.translation_limit_max=translation_limit_max
516         self.translation_limit_min=translation_limit_min
517         self.rotation_limit_max=rotation_limit_max
518         self.rotation_limit_min=rotation_limit_min
519         self.spring_constant_translation=spring_constant_translation
520         self.spring_constant_rotation=spring_constant_rotation
521
522     def __eq__(self, rhs):
523         return (
524                 self.name==rhs.name
525                 and self.rigidbody_index_a==rhs.rigidbody_index_a
526                 and self.rigidbody_index_b==rhs.rigidbody_index_b
527                 and self.position==rhs.position
528                 and self.rotation==rhs.rotation
529                 and self.translation_limit_max==rhs.translation_limit_max
530                 and self.translation_limit_min==rhs.translation_limit_min
531                 and self.rotation_limit_max==rhs.rotation_limit_max
532                 and self.rotation_limit_min==rhs.rotation_limit_min
533                 and self.spring_constant_translation==rhs.spring_constant_translation
534                 and self.spring_constant_rotation==rhs.spring_constant_rotation
535                 )
536
537
538 class Model(object):
539     """pmd loader class.
540
541     Attributes:
542         io: internal use.
543         end: internal use.
544         pos: internal user.
545
546         version: pmd version number
547         _name: internal
548     """
549     __slots__=[
550             'version', 'name', 'comment',
551             'english_name', 'english_comment',
552             'vertices', 'indices', 'materials', 'bones', 
553             'ik_list', 'morphs',
554             'morph_indices', 'bone_group_list', 'bone_display_list',
555             'toon_textures',
556             'rigidbodies', 'joints',
557
558             'no_parent_bones',
559             ]
560     def __init__(self, version):
561         self.version=version
562         self.name=b''
563         self.comment=b''
564         self.english_name=b''
565         self.english_comment=b''
566         self.vertices=[]
567         self.indices=[]
568         self.materials=[]
569         self.bones=[]
570         self.ik_list=[]
571         self.morphs=[]
572         self.morph_indices=[]
573         self.bone_group_list=[]
574         self.bone_display_list=[]
575         # extend
576         self.toon_textures=[b'']*10
577         self.rigidbodies=[]
578         self.joints=[]
579         # innner use
580         self.no_parent_bones=[]
581
582     def each_vertex(self): return self.vertices
583     def getUV(self, i): return self.vertices[i].uv
584
585     def __str__(self):
586         return '<pmd-%g, "%s" vertex: %d, face: %d, material: %d, bone: %d ik: %d, skin: %d>' % (
587             self.version, self.name, len(self.vertices), len(self.indices),
588             len(self.materials), len(self.bones), len(self.ik_list), len(self.morph_list))
589
590     def __eq__(self, rhs):
591         return (
592                 self.name==rhs.name
593                 and self.comment==rhs.comment
594                 and self.english_name==rhs.english_name
595                 and self.english_comment==rhs.english_comment
596                 and self.vertices==rhs.vertices
597                 and self.indices==rhs.indices
598                 and self.materials==rhs.materials
599                 and self.bones==rhs.bones
600                 and self.ik_list==rhs.ik_list
601                 and self.morphs==rhs.morphs
602                 and self.morph_indices==rhs.morph_indices
603                 and self.bone_group_list==rhs.bone_group_list
604                 and self.bone_display_list==rhs.bone_display_list
605                 and self.toon_textures==rhs.toon_textures
606                 and self.rigidbodies==rhs.rigidbodies
607                 and self.joints==rhs.joints
608                 )
609
610     def diff(self, rhs):
611         if self.name!=rhs.name: 
612             print(self.name, rhs.name)
613             return
614         if self.comment!=rhs.comment: 
615             print(self.comment, rhs.comment)
616             return
617         if self.english_name!=rhs.english_name: 
618             print(self.english_name, rhs.english_name)
619             return
620         if self.english_comment!=rhs.english_comment: 
621             print(self.english_comment, rhs.english_comment)
622             return
623         if self.vertices!=rhs.vertices: 
624             print(self.vertices, rhs.vertices)
625             return
626         if self.indices!=rhs.indices: 
627             print(self.indices, rhs.indices)
628             return
629         if self.materials!=rhs.materials: 
630             print(self.materials, rhs.materials)
631             return
632         if self.bones!=rhs.bones: 
633             print(self.bones, rhs.bones)
634             return
635         if self.ik_list!=rhs.ik_list: 
636             print(self.ik_list, rhs.ik_list)
637             return
638         if self.morphs!=rhs.morphs: 
639             print(self.morphs, rhs.morphs)
640             return
641         if self.morph_indices!=rhs.morph_indices: 
642             print(self.morph_indices, rhs.morph_indices)
643             return
644         if self.bone_group_list!=rhs.bone_group_list: 
645             print(self.bone_group_list, rhs.bone_group_list)
646             return
647         if self.bone_display_list!=rhs.bone_display_list: 
648             print(self.bone_display_list, rhs.bone_display_list)
649             return
650         if self.toon_textures!=rhs.toon_textures: 
651             print(self.toon_textures, rhs.toon_textures)
652             return
653         if self.rigidbodies!=rhs.rigidbodies: 
654             print(self.rigidbodies, rhs.rigidbodies)
655             return
656         if self.joints!=rhs.joints: 
657             print(self.joints, rhs.joints)
658             return
659