OSDN Git Service

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