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             shape_type,
452             shape_size,
453             shape_position, 
454             shape_rotation,
455             mass,
456             linear_damping, 
457             angular_damping, 
458             restitution, 
459             friction, 
460             mode,
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             'path',
551             'version', 'name', 'comment',
552             'english_name', 'english_comment',
553             'vertices', 'indices', 'materials', 'bones', 
554             'ik_list', 'morphs',
555             'morph_indices', 'bone_group_list', 'bone_display_list',
556             'toon_textures',
557             'rigidbodies', 'joints',
558
559             'no_parent_bones',
560             ]
561     def __init__(self, version=1.0):
562         self.path=''
563         self.version=version
564         self.name=b''
565         self.comment=b''
566         self.english_name=b''
567         self.english_comment=b''
568         self.vertices=[]
569         self.indices=[]
570         self.materials=[]
571         self.bones=[]
572         self.ik_list=[]
573         self.morphs=[]
574         self.morph_indices=[]
575         self.bone_group_list=[]
576         self.bone_display_list=[]
577         # extend
578         self.toon_textures=[b'']*10
579         self.rigidbodies=[]
580         self.joints=[]
581         # innner use
582         self.no_parent_bones=[]
583
584     def each_vertex(self): return self.vertices
585     def getUV(self, i): return self.vertices[i].uv
586
587     def __str__(self):
588         return '<pmd-%g, "%s" vertex: %d, face: %d, material: %d, bone: %d ik: %d, skin: %d>' % (
589             self.version, self.name, len(self.vertices), len(self.indices),
590             len(self.materials), len(self.bones), len(self.ik_list), len(self.morphs))
591
592     def __eq__(self, rhs):
593         return (
594                 self.name==rhs.name
595                 and self.comment==rhs.comment
596                 and self.english_name==rhs.english_name
597                 and self.english_comment==rhs.english_comment
598                 and self.vertices==rhs.vertices
599                 and self.indices==rhs.indices
600                 and self.materials==rhs.materials
601                 and self.bones==rhs.bones
602                 and self.ik_list==rhs.ik_list
603                 and self.morphs==rhs.morphs
604                 and self.morph_indices==rhs.morph_indices
605                 and self.bone_group_list==rhs.bone_group_list
606                 and self.bone_display_list==rhs.bone_display_list
607                 and self.toon_textures==rhs.toon_textures
608                 and self.rigidbodies==rhs.rigidbodies
609                 and self.joints==rhs.joints
610                 )
611
612     def diff(self, rhs):
613         if self.name!=rhs.name: 
614             print(self.name, rhs.name)
615             return
616         if self.comment!=rhs.comment: 
617             print(self.comment, rhs.comment)
618             return
619         if self.english_name!=rhs.english_name: 
620             print(self.english_name, rhs.english_name)
621             return
622         if self.english_comment!=rhs.english_comment: 
623             print(self.english_comment, rhs.english_comment)
624             return
625         if self.vertices!=rhs.vertices: 
626             print(self.vertices, rhs.vertices)
627             return
628         if self.indices!=rhs.indices: 
629             print(self.indices, rhs.indices)
630             return
631         if self.materials!=rhs.materials: 
632             print(self.materials, rhs.materials)
633             return
634         if self.bones!=rhs.bones: 
635             print(self.bones, rhs.bones)
636             return
637         if self.ik_list!=rhs.ik_list: 
638             print(self.ik_list, rhs.ik_list)
639             return
640         if self.morphs!=rhs.morphs: 
641             print(self.morphs, rhs.morphs)
642             return
643         if self.morph_indices!=rhs.morph_indices: 
644             print(self.morph_indices, rhs.morph_indices)
645             return
646         if self.bone_group_list!=rhs.bone_group_list: 
647             print(self.bone_group_list, rhs.bone_group_list)
648             return
649         if self.bone_display_list!=rhs.bone_display_list: 
650             print(self.bone_display_list, rhs.bone_display_list)
651             return
652         if self.toon_textures!=rhs.toon_textures: 
653             print(self.toon_textures, rhs.toon_textures)
654             return
655         if self.rigidbodies!=rhs.rigidbodies: 
656             print(self.rigidbodies, rhs.rigidbodies)
657             return
658         if self.joints!=rhs.joints: 
659             print(self.joints, rhs.joints)
660             return
661