OSDN Git Service

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