OSDN Git Service

fix blender plugin packaging
[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=b''
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=None):
248         indent=indent or []
249         if len(indent)>0:
250             prefix=''
251             for i, is_end in enumerate(indent):
252                 if i==len(indent)-1:
253                     break
254                 else:
255                     prefix+='  ' if is_end else ' |'
256             uni='%s +%s(%s)' % (prefix, unicode(self), self.english_name)
257             print(uni.encode(ENCODING))
258         else:
259             uni='%s(%s)' % (unicode(self), self.english_name)
260             print(uni.encode(ENCODING))
261
262         child_count=len(self.children)
263         for i in range(child_count):
264             child=self.children[i]
265             if i<child_count-1:
266                 child.display(indent+[False])
267             else:
268                 # last
269                 child.display(indent+[True])
270
271 # 0
272 class Bone_Rotate(Bone):
273     __slots__=[]
274     def __init__(self, name):
275         super(Bone_Rotate, self).__init__(name, 0)
276     def __str__(self):
277         return '<ROTATE %s>' % (self.name)
278 # 1
279 class Bone_RotateMove(Bone):
280     __slots__=[]
281     def __init__(self, name):
282         super(Bone_RotateMove, self).__init__(name, 1)
283     def __str__(self):
284         return '<ROTATE_MOVE %s>' % (self.name)
285 # 2
286 class Bone_IK(Bone):
287     __slots__=[]
288     def __init__(self, name):
289         super(Bone_IK, self).__init__(name, 2)
290     def __str__(self):
291         return '<IK %s>' % (self.name)
292 # 4
293 class Bone_IKRotateInfl(Bone):
294     __slots__=[]
295     def __init__(self, name):
296         super(Bone_IKRotateInfl, self).__init__(name, 4)
297     def __str__(self):
298         return '<IK_ROTATE_INFL %s>' % (self.name)
299 # 5
300 class Bone_RotateInfl(Bone):
301     __slots__=[]
302     def __init__(self, name):
303         super(Bone_RotateInfl, self).__init__(name, 5)
304     def __str__(self):
305         return '<ROTATE_INFL %s>' % (self.name)
306 # 6
307 class Bone_IKTarget(Bone):
308     __slots__=[]
309     def __init__(self, name):
310         super(Bone_IKTarget, self).__init__(name, 6)
311     def __str__(self):
312         return '<IK_TARGET %s>' % (self.name)
313 # 7
314 class Bone_Unvisible(Bone):
315     __slots__=[]
316     def __init__(self, name):
317         super(Bone_Unvisible, self).__init__(name, 7)
318     def __str__(self):
319         return '<UNVISIBLE %s>' % (self.name)
320 # 8
321 class Bone_Rolling(Bone):
322     __slots__=[]
323     def __init__(self, name):
324         super(Bone_Rolling, self).__init__(name, 8)
325     def __str__(self):
326         return '<ROLLING %s>' % (self.name)
327 # 9
328 class Bone_Tweak(Bone):
329     __slots__=[]
330     def __init__(self, name):
331         super(Bone_Tweak, self).__init__(name, 9)
332     def __str__(self):
333         return '<TWEAK %s>' % (self.name)
334
335
336 def createBone(name, type):
337     if type==0:
338         return Bone_Rotate(name)
339     elif type==1:
340         return Bone_RotateMove(name)
341     elif type==2:
342         return Bone_IK(name)
343     elif type==3:
344         raise Exception("no used bone type: 3(%s)" % name)
345     elif type==4:
346         return Bone_IKRotateInfl(name)
347     elif type==5:
348         return Bone_RotateInfl(name)
349     elif type==6:
350         return Bone_IKTarget(name)
351     elif type==7:
352         return Bone_Unvisible(name)
353     elif type==8:
354         return Bone_Rolling(name)
355     elif type==9:
356         return Bone_Tweak(name)
357     else:
358         raise Exception("unknown bone type: %d(%s)", type, name)
359
360
361 class IK(object):
362     __slots__=['index', 'target', 'iterations', 'weight', 'length', 'children']
363     def __init__(self, index=0, target=0):
364         self.index=index
365         self.target=target
366         self.iterations=None
367         self.weight=None
368         self.children=[]
369
370     def __str__(self):
371         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))
372
373     def __eq__(self, rhs):
374         return (
375                 self.index==rhs.index
376                 and self.target==rhs.target
377                 and self.iterations==rhs.iterations
378                 and self.weight==rhs.weight
379                 and self.children==rhs.children
380                 )
381
382
383 class Morph(object):
384     __slots__=['name', 'type', 'indices', 'pos_list', 'english_name',
385             'vertex_count']
386     def __init__(self, name):
387         self.name=name
388         self.type=None
389         self.indices=[]
390         self.pos_list=[]
391         self.english_name=b''
392         self.vertex_count=0
393
394     def append(self, index, x, y, z):
395         self.indices.append(index)
396         self.pos_list.append(common.Vector3(x, y, z))
397
398     def __str__(self):
399         return '<Skin name: "%s", type: %d, vertex: %d>' % (
400             self.name, self.type, len(self.indices))
401
402     def __eq__(self, rhs):
403         return (
404                 self.name==rhs.name
405                 and self.type==rhs.type
406                 and self.indices==rhs.indices
407                 and self.pos_list==rhs.pos_list
408                 and self.english_name==rhs.english_name
409                 and self.vertex_count==rhs.vertex_count
410                 )
411
412
413 class BoneGroup(object):
414     __slots__=['name', 'english_name']
415     def __init__(self, name=b'group', english_name=b'center'): 
416         self.name=name
417         self.english_name=english_name
418
419     def __eq__(self, rhs):
420         return self.name==rhs.name and self.english_name==rhs.english_name
421
422
423 SHAPE_SPHERE=0
424 SHAPE_BOX=1
425 SHAPE_CAPSULE=2
426
427 RIGIDBODY_KINEMATICS=0
428 RIGIDBODY_PHYSICS=1
429 RIGIDBODY_PHYSICS_WITH_BONE=2
430
431
432 class RigidBody(object):
433     __slots__=['name', 
434             'bone_index', 
435             'collision_group', 
436             'no_collision_group', 
437             'shape_type',
438             'shape_size',
439             'shape_position', 
440             'shape_rotation', 
441             'mass',
442             'linear_damping', 
443             'angular_damping', 
444             'restitution', 
445             'friction', 
446             'mode'
447             ]
448     def __init__(self, name,
449             bone_index, 
450             collision_group, 
451             no_collision_group, 
452             shape_type,
453             shape_size,
454             shape_position, 
455             shape_rotation,
456             mass,
457             linear_damping, 
458             angular_damping, 
459             restitution, 
460             friction, 
461             mode
462             ):
463         self.name=name
464         self.bone_index=bone_index
465         self.collision_group=collision_group 
466         self.no_collision_group=no_collision_group 
467         self.shape_type=shape_type
468         self.shape_size=shape_size
469         self.shape_position=shape_position
470         self.shape_rotation=shape_rotation
471         self.mass=mass
472         self.linear_damping=linear_damping
473         self.angular_damping=angular_damping
474         self.restitution=restitution
475         self.friction=friction
476         self.mode=mode
477
478     def __eq__(self, rhs):
479         return (
480                 self.name==rhs.name
481                 and self.bone_index==rhs.bone_index
482                 and self.collision_group==rhs.collision_group
483                 and self.no_collision_group==rhs.no_collision_group
484                 and self.shape_type==rhs.shape_type
485                 and self.shape_size==rhs.shape_size
486                 and self.shape_position==rhs.shape_position
487                 and self.shape_rotation==rhs.shape_rotation
488                 and self.mass==rhs.mass
489                 and self.linear_damping==rhs.linear_damping
490                 and self.angular_damping==rhs.angular_damping
491                 and self.restitution==rhs.restitution
492                 and self.friction==rhs.friction
493                 and self.mode==rhs.mode
494                 )
495
496
497 class Joint(object):
498     __slots__=[ 'name', 'rigidbody_index_a', 'rigidbody_index_b', 
499             'position', 'rotation',
500             'translation_limit_max', 'translation_limit_min',
501             'rotation_limit_max', 'rotation_limit_min',
502             'spring_constant_translation', 'spring_constant_rotation',
503             ]
504     def __init__(self, name,
505             rigidbody_index_a, rigidbody_index_b,
506             position, rotation,
507             translation_limit_max, translation_limit_min,
508             rotation_limit_max, rotation_limit_min,
509             spring_constant_translation, spring_constant_rotation
510             ):
511         self.name=name
512         self.rigidbody_index_a=rigidbody_index_a
513         self.rigidbody_index_b=rigidbody_index_b
514         self.position=position
515         self.rotation=rotation
516         self.translation_limit_max=translation_limit_max
517         self.translation_limit_min=translation_limit_min
518         self.rotation_limit_max=rotation_limit_max
519         self.rotation_limit_min=rotation_limit_min
520         self.spring_constant_translation=spring_constant_translation
521         self.spring_constant_rotation=spring_constant_rotation
522
523     def __eq__(self, rhs):
524         return (
525                 self.name==rhs.name
526                 and self.rigidbody_index_a==rhs.rigidbody_index_a
527                 and self.rigidbody_index_b==rhs.rigidbody_index_b
528                 and self.position==rhs.position
529                 and self.rotation==rhs.rotation
530                 and self.translation_limit_max==rhs.translation_limit_max
531                 and self.translation_limit_min==rhs.translation_limit_min
532                 and self.rotation_limit_max==rhs.rotation_limit_max
533                 and self.rotation_limit_min==rhs.rotation_limit_min
534                 and self.spring_constant_translation==rhs.spring_constant_translation
535                 and self.spring_constant_rotation==rhs.spring_constant_rotation
536                 )
537
538
539 class Model(object):
540     """pmd loader class.
541
542     Attributes:
543         io: internal use.
544         end: internal use.
545         pos: internal user.
546
547         version: pmd version number
548         _name: internal
549     """
550     __slots__=[
551             'path',
552             'version', 'name', 'comment',
553             'english_name', 'english_comment',
554             'vertices', 'indices', 'materials', 'bones', 
555             'ik_list', 'morphs',
556             'morph_indices', 'bone_group_list', 'bone_display_list',
557             'toon_textures',
558             'rigidbodies', 'joints',
559
560             'no_parent_bones',
561             ]
562     def __init__(self, version=1.0):
563         self.path=b''
564         self.version=version
565         self.name=b''
566         self.comment=b''
567         self.english_name=b''
568         self.english_comment=b''
569         self.vertices=[]
570         self.indices=[]
571         self.materials=[]
572         self.bones=[]
573         self.ik_list=[]
574         self.morphs=[]
575         self.morph_indices=[]
576         self.bone_group_list=[]
577         self.bone_display_list=[]
578         # extend
579         self.toon_textures=[b'']*10
580         self.rigidbodies=[]
581         self.joints=[]
582         # innner use
583         self.no_parent_bones=[]
584
585     def each_vertex(self): return self.vertices
586     def getUV(self, i): return self.vertices[i].uv
587
588     def __str__(self):
589         return '<pmd-%g, "%s" vertex: %d, face: %d, material: %d, bone: %d ik: %d, skin: %d>' % (
590             self.version, self.name, len(self.vertices), len(self.indices),
591             len(self.materials), len(self.bones), len(self.ik_list), len(self.morphs))
592
593     def __eq__(self, rhs):
594         return (
595                 self.name==rhs.name
596                 and self.comment==rhs.comment
597                 and self.english_name==rhs.english_name
598                 and self.english_comment==rhs.english_comment
599                 and self.vertices==rhs.vertices
600                 and self.indices==rhs.indices
601                 and self.materials==rhs.materials
602                 and self.bones==rhs.bones
603                 and self.ik_list==rhs.ik_list
604                 and self.morphs==rhs.morphs
605                 and self.morph_indices==rhs.morph_indices
606                 and self.bone_group_list==rhs.bone_group_list
607                 and self.bone_display_list==rhs.bone_display_list
608                 and self.toon_textures==rhs.toon_textures
609                 and self.rigidbodies==rhs.rigidbodies
610                 and self.joints==rhs.joints
611                 )
612
613     def diff(self, rhs):
614         if self.name!=rhs.name: 
615             print(self.name, rhs.name)
616             return
617         if self.comment!=rhs.comment: 
618             print(self.comment, rhs.comment)
619             return
620         if self.english_name!=rhs.english_name: 
621             print(self.english_name, rhs.english_name)
622             return
623         if self.english_comment!=rhs.english_comment: 
624             print(self.english_comment, rhs.english_comment)
625             return
626         if self.vertices!=rhs.vertices: 
627             print(self.vertices, rhs.vertices)
628             return
629         if self.indices!=rhs.indices: 
630             print(self.indices, rhs.indices)
631             return
632         if self.materials!=rhs.materials: 
633             print(self.materials, rhs.materials)
634             return
635         if self.bones!=rhs.bones: 
636             print(self.bones, rhs.bones)
637             return
638         if self.ik_list!=rhs.ik_list: 
639             print(self.ik_list, rhs.ik_list)
640             return
641         if self.morphs!=rhs.morphs: 
642             print(self.morphs, rhs.morphs)
643             return
644         if self.morph_indices!=rhs.morph_indices: 
645             print(self.morph_indices, rhs.morph_indices)
646             return
647         if self.bone_group_list!=rhs.bone_group_list: 
648             print(self.bone_group_list, rhs.bone_group_list)
649             return
650         if self.bone_display_list!=rhs.bone_display_list: 
651             print(self.bone_display_list, rhs.bone_display_list)
652             return
653         if self.toon_textures!=rhs.toon_textures: 
654             print(self.toon_textures, rhs.toon_textures)
655             return
656         if self.rigidbodies!=rhs.rigidbodies: 
657             print(self.rigidbodies, rhs.rigidbodies)
658             return
659         if self.joints!=rhs.joints: 
660             print(self.joints, rhs.joints)
661             return
662