OSDN Git Service

refactoring pmd
[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 import pymeshio.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 __getitem__(self, key):
44         if key==0:
45             return self.pos.x
46         elif key==1:
47             return self.pos.y
48         elif key==2:
49             return self.pos.z
50         else:
51             assert(False)
52
53
54 class Material(object):
55     """pmd material struct.
56
57     Attributes:
58         diffuse_color: RGB
59         alpha: float
60         specular_factor: float
61         specular_color: RGB
62         ambient_color: RGB
63         toon_index: int
64         edge_flag: int
65         vertex_count: indices length
66         texture_file: texture file path
67     """
68     __slots__=[
69             'diffuse_color', 'alpha', 
70             'specular_factor', 'specular_color', 'ambient_color', 
71             'toon_index', 'edge_flag',
72             'vertex_count', 'texture_file', 
73             ]
74     def __init__(self, diffuse_color, alpha,
75             specular_factor, specular_color, ambient_color,
76             toon_index, edge_flag, vertex_count, texture_file):
77         self.diffuse_color=diffuse_color
78         self.alpha=alpha
79         self.specular_factor=specular_factor
80         self.specular_color=specular_color
81         self.ambient_color=ambient_color
82         self.toon_index=toon_index
83         self.edge_flag=edge_flag
84         self.vertex_count=vertex_count
85         self.texture_file=texture_file
86
87     def __str__(self):
88         return "<Material [%f, %f, %f, %f]>" % (
89                 self.diffuse[0], self.diffuse[1], 
90                 self.diffuse[2], self.diffuse[3],
91                 )
92
93
94 class Bone(object):
95     """pmd material struct.
96
97     Attributes:
98         _name: 
99         index:
100         type:
101         ik:
102         pos:
103         _english_name:
104         ik_index:
105         parent_index:
106         tail_index:
107
108         parent:
109         tail:
110         children:
111     """
112     # kinds
113     ROTATE = 0
114     ROTATE_MOVE = 1
115     IK = 2
116     IK_ROTATE_INFL = 4
117     ROTATE_INFL = 5
118     IK_TARGET = 6
119     UNVISIBLE = 7
120     # since v4.0
121     ROLLING=8 # ?
122     TWEAK=9
123     __slots__=['name', 'index', 'type', 'parent', 'ik', 'pos',
124             'children', 'english_name', 'ik_index',
125             'parent_index', 'tail_index', 'tail',
126             ]
127     def __init__(self, name=b'bone', type=0):
128         self.name=name
129         self.index=0
130         self.type=type
131         self.parent_index=0xFFFF
132         self.tail_index=0
133         self.tail=pymeshio.common.Vector3(0, 0, 0)
134         self.parent=None
135         self.ik_index=0xFFFF
136         self.pos=pymeshio.common.Vector3(0, 0, 0)
137         self.children=[]
138         self.english_name=''
139
140     def hasParent(self):
141         return self.parent_index!=0xFFFF
142
143     def hasChild(self):
144         return self.tail_index!=0
145
146     def display(self, indent=[]):
147         if len(indent)>0:
148             prefix=''
149             for i, is_end in enumerate(indent):
150                 if i==len(indent)-1:
151                     break
152                 else:
153                     prefix+='  ' if is_end else ' |'
154             uni='%s +%s(%s)' % (prefix, unicode(self), self.english_name)
155             print(uni.encode(ENCODING))
156         else:
157             uni='%s(%s)' % (unicode(self), self.english_name)
158             print(uni.encode(ENCODING))
159
160         child_count=len(self.children)
161         for i in range(child_count):
162             child=self.children[i]
163             if i<child_count-1:
164                 child.display(indent+[False])
165             else:
166                 # last
167                 child.display(indent+[True])
168
169 # 0
170 class Bone_Rotate(Bone):
171     __slots__=[]
172     def __init__(self, name):
173         super(Bone_Rotate, self).__init__(name, 0)
174     def __str__(self):
175         return '<ROTATE %s>' % (self.name)
176 # 1
177 class Bone_RotateMove(Bone):
178     __slots__=[]
179     def __init__(self, name):
180         super(Bone_RotateMove, self).__init__(name, 1)
181     def __str__(self):
182         return '<ROTATE_MOVE %s>' % (self.name)
183 # 2
184 class Bone_IK(Bone):
185     __slots__=[]
186     def __init__(self, name):
187         super(Bone_IK, self).__init__(name, 2)
188     def __str__(self):
189         return '<IK %s>' % (self.name)
190 # 4
191 class Bone_IKRotateInfl(Bone):
192     __slots__=[]
193     def __init__(self, name):
194         super(Bone_IKRotateInfl, self).__init__(name, 4)
195     def __str__(self):
196         return '<IK_ROTATE_INFL %s>' % (self.name)
197 # 5
198 class Bone_RotateInfl(Bone):
199     __slots__=[]
200     def __init__(self, name):
201         super(Bone_RotateInfl, self).__init__(name, 5)
202     def __str__(self):
203         return '<ROTATE_INFL %s>' % (self.name)
204 # 6
205 class Bone_IKTarget(Bone):
206     __slots__=[]
207     def __init__(self, name):
208         super(Bone_IKTarget, self).__init__(name, 6)
209     def __str__(self):
210         return '<IK_TARGET %s>' % (self.name)
211 # 7
212 class Bone_Unvisible(Bone):
213     __slots__=[]
214     def __init__(self, name):
215         super(Bone_Unvisible, self).__init__(name, 7)
216     def __str__(self):
217         return '<UNVISIBLE %s>' % (self.name)
218 # 8
219 class Bone_Rolling(Bone):
220     __slots__=[]
221     def __init__(self, name):
222         super(Bone_Rolling, self).__init__(name, 8)
223     def __str__(self):
224         return '<ROLLING %s>' % (self.name)
225 # 9
226 class Bone_Tweak(Bone):
227     __slots__=[]
228     def __init__(self, name):
229         super(Bone_Tweak, self).__init__(name, 9)
230     def __str__(self):
231         return '<TWEAK %s>' % (self.name)
232
233
234 def createBone(name, type):
235     if type==0:
236         return Bone_Rotate(name)
237     elif type==1:
238         return Bone_RotateMove(name)
239     elif type==2:
240         return Bone_IK(name)
241     elif type==3:
242         raise Exception("no used bone type: 3(%s)" % name)
243     elif type==4:
244         return Bone_IKRotateInfl(name)
245     elif type==5:
246         return Bone_RotateInfl(name)
247     elif type==6:
248         return Bone_IKTarget(name)
249     elif type==7:
250         return Bone_Unvisible(name)
251     elif type==8:
252         return Bone_Rolling(name)
253     elif type==9:
254         return Bone_Tweak(name)
255     else:
256         raise Exception("unknown bone type: %d(%s)", type, name)
257
258
259 class IK(object):
260     __slots__=['index', 'target', 'iterations', 'weight', 'length', 'children']
261     def __init__(self, index=0, target=0):
262         self.index=index
263         self.target=target
264         self.iterations=None
265         self.weight=None
266         self.children=[]
267
268     def __str__(self):
269         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))
270
271
272 class Skin(object):
273     __slots__=['name', 'type', 'indices', 'pos_list', 'english_name',
274             'vertex_count']
275     def __init__(self, name='skin'):
276         self.name=name
277         self.type=None
278         self.indices=[]
279         self.pos_list=[]
280         self.english_name=''
281         self.vertex_count=0
282
283     def append(self, index, x, y, z):
284         self.indices.append(index)
285         self.pos_list.append(Vector3(x, y, z))
286
287     def __str__(self):
288         return '<Skin name: "%s", type: %d, vertex: %d>' % (
289             self.name, self.type, len(self.indices))
290
291
292 class BoneGroup(object):
293     __slots__=['_name', '_english_name']
294     def __init__(self, name='group'): self._name=name; self._english_name='center'
295
296
297 SHAPE_SPHERE=0
298 SHAPE_BOX=1
299 SHAPE_CAPSULE=2
300
301 RIGIDBODY_KINEMATICS=0
302 RIGIDBODY_PHYSICS=1
303 RIGIDBODY_PHYSICS_WITH_BONE=2
304
305
306 class RigidBody(object):
307     __slots__=['name', 
308             'bone_index', 
309             'collision_group', 
310             'no_collision_group', 
311             'shape_type',
312             'shape_size',
313             'shape_position', 
314             'shape_rotation', 
315             'mass',
316             'linear_damping', 
317             'angular_damping', 
318             'restitution', 
319             'friction', 
320             'mode'
321             ]
322     def __init__(self, name,
323             bone_index, 
324             collision_group, 
325             no_collision_group, 
326             shape_type,
327             shape_size,
328             shape_position, 
329             shape_rotation, 
330             mass,
331             linear_damping, 
332             angular_damping, 
333             restitution, 
334             friction, 
335             mode
336             ):
337         self.name=name
338         self.bone_index=bone_index
339         self.collision_group=collision_group 
340         self.no_collision_group=no_collision_group 
341         self.shape_type=shape_type
342         self.shape_size=shape_size
343         self.shape_position=shape_position
344         self.shape_rotation=shape_rotation
345         self.mass=mass
346         self.linear_damping=linear_damping
347         self.angular_damping=angular_damping
348         self.restitution=restitution
349         self.friction=friction
350         self.mode=mode
351
352
353 class Joint(object):
354     __slots__=[ 'name', 'rigidbody_index_a', 'rigidbody_index_b', 
355             'position', 'rotation',
356             'translation_limit_max', 'translation_limit_min',
357             'rotation_limit_max', 'rotation_limit_min',
358             'spring_constant_translation', 'spring_constant_rotation',
359             ]
360     def __init__(self, name,
361             rigidbody_index_a, rigidbody_index_b,
362             position, rotation,
363             translation_limit_max, translation_limit_min,
364             rotation_limit_max, rotation_limit_min,
365             spring_constant_translation, spring_constant_rotation
366             ):
367         self.name=name
368         self.rigidbody_index_a=rigidbody_index_a
369         self.rigidbody_index_b=rigidbody_index_b
370         self.position=position
371         self.rotation=rotation
372         self.translation_limit_max=translation_limit_max
373         self.translation_limit_min=translation_limit_min
374         self.rotation_limit_max=rotation_limit_max
375         self.rotation_limit_min=rotation_limit_min
376         self.spring_constant_translation=spring_constant_translation
377         self.spring_constant_rotation=spring_constant_rotation
378
379
380 class ToonTextures(object):
381     __slots__=['_toon_textures']
382     def __init__(self):
383         self._toon_textures=[]
384         for i in range(10):
385             self._toon_textures.append('toon%02d.bmp' % (i+1))
386
387     def __getitem__(self, key):
388         return from_str(self._toon_textures[key])
389
390     def __setitem__(self, key, value):
391         self._toon_textures[key]=to_str(value)
392
393     def __iter__(self):
394         for toon_texture in self._toon_textures:
395             yield from_str(toon_texture)
396
397
398 class Model(object):
399     """pmd loader class.
400
401     Attributes:
402         io: internal use.
403         end: internal use.
404         pos: internal user.
405
406         version: pmd version number
407         _name: internal
408     """
409     __slots__=[
410             'version', 'name', 'comment',
411             'english_name', 'english_comment',
412             'vertices', 'indices', 'materials', 'bones', 
413             'ik_list', 'morphs',
414             'morph_indices', 'bone_group_list', 'bone_display_list',
415             'toon_textures',
416             'no_parent_bones',
417             'rigidbodies', 'joints',
418             ]
419     def __init__(self, version):
420         self.version=version
421         self.name=b''
422         self.comment=b''
423         self.english_name=b''
424         self.english_comment=b''
425         self.vertices=[]
426         self.indices=[]
427         self.materials=[]
428         self.bones=[]
429         self.ik_list=[]
430         self.morphs=[]
431         self.morph_indices=[]
432         self.bone_group_list=[]
433         self.bone_display_list=[]
434         # extend
435         self.toon_textures=ToonTextures()
436         self.rigidbodies=[]
437         self.joints=[]
438         # innner use
439         self.no_parent_bones=[]
440
441     def each_vertex(self): return self.vertices
442     def getUV(self, i): return self.vertices[i].uv
443
444     def __str__(self):
445         return '<PMDLoader version: %g, model: "%s", vertex: %d, face: %d, material: %d, bone: %d ik: %d, skin: %d>' % (
446             self.version, self.name, len(self.vertices), len(self.indices),
447             len(self.materials), len(self.bones), len(self.ik_list), len(self.morph_list))
448
449
450 class IO(object):
451     def __init__(self):
452         pass
453
454     def read(self, path):
455         warnings.warn("'pymeshio.mqo.IO.read' will be replaced by 'pymeshio.mqo.loader.load'")
456         model=pymeshio.pmd.loader.load(path)
457         if model:
458             return True
459