1 #!/usr/bin/env python
\r
7 PMDEditor's Lib/PMX仕様/PMX仕様.txt
\r
9 __author__="ousttrue"
\r
11 __versioon__="1.0.0"
\r
17 from . import common
\r
20 class ParseException(Exception):
\r
33 def __init__(self, target_index, loop, limit_radian):
\r
34 self.target_index=target_index
\r
36 self.limit_radian=limit_radian
\r
40 class IkLink(object):
\r
49 def __init__(self, bone_index, limit_angle):
\r
50 self.bone_index=bone_index
\r
51 self.limit_angle=limit_angle
\r
82 position: common.Vector3,
\r
88 self.english_name=english_name
\r
89 self.position=position
\r
90 self.parent_index=parent_index
\r
94 def getConnectionFlag(self) -> int:
\r
95 return self.flag & 0x0001
\r
97 def getIkFlag(self) -> int:
\r
98 return (self.flag & 0x0020) >> 5
\r
100 def getRotationFlag(self) -> int:
\r
101 return (self.flag & 0x0100) >> 8
\r
103 def getTranslationFlag(self) -> int:
\r
104 return (self.flag & 0x0200) >> 9
\r
106 def getFixedAxisFlag(self) -> int:
\r
107 return (self.flag & 0x0400) >> 10
\r
109 def getLocalCoordinateFlag(self) -> int:
\r
110 return (self.flag & 0x0800) >> 11
\r
112 def getExternalParentDeformFlag(self) -> int:
\r
113 return (self.flag & 0x2000) >> 13
\r
116 class Material(object):
\r
119 Attributes: see __init__
\r
133 'sphia_texture_index',
\r
135 'toon_sharing_flag',
\r
136 'toon_texture_index',
\r
143 diffuse_color: common.RGB,
\r
144 diffuse_alpha: float,
\r
145 specular_color: common.RGB,
\r
146 specular_factor: float,
\r
147 ambient_color: common.RGB,
\r
149 edge_color: common.RGBA,
\r
151 texture_index: int,
\r
152 sphia_texture_index: int,
\r
154 toon_sharing_flag: int
\r
157 self.english_name=english_name
\r
158 self.diffuse_color=diffuse_color
\r
159 self.diffuse_alpha=diffuse_alpha
\r
160 self.specular_color=specular_color
\r
161 self.specular_factor=specular_factor
\r
162 self.ambient_color=ambient_color
\r
164 self.edge_color=edge_color
\r
165 self.edge_size=edge_size
\r
166 self.texture_index=texture_index
\r
167 self.sphia_texture_index=sphia_texture_index
\r
168 self.sphia_mode=sphia_mode
\r
169 self.toon_sharing_flag=toon_sharing_flag
\r
171 self.toon_texture_index=None
\r
176 class Deform(object):
\r
180 class Bdef1(object):
\r
181 """bone deform. use a weight
\r
183 Attributes: see __init__
\r
185 __slots__=[ 'bone_index']
\r
186 def __init__(self, bone_index: int):
\r
187 self.bone_index=bone_index
\r
190 class Bdef2(object):
\r
191 """bone deform. use two weights
\r
193 Attributes: see __init__
\r
195 __slots__=[ 'index0', 'index1', 'weight0']
\r
196 def __init__(self,
\r
202 self.weight0=weight0
\r
205 class Vertex(object):
\r
208 Attributes: see __init__
\r
210 __slots__=[ 'position', 'normal', 'uv', 'deform', 'edge_factor' ]
\r
211 def __init__(self,
\r
212 position: common.Vector3,
\r
213 normal: common.Vector3,
\r
214 uv: common.Vector2,
\r
216 edge_factor: float):
\r
217 self.position=position
\r
221 self.edge_factor=edge_factor
\r
224 class Model(object):
\r
225 """pmx data representation
\r
228 version: pmx version(expected 2.0)
\r
239 'version', # pmx version
\r
240 'name', # model name
\r
241 'english_name', # model name in english
\r
242 'comment', # model comment
\r
243 'english_comment', # model comment in english
\r
250 def __init__(self):
\r
253 self.english_name=''
\r
255 self.english_comment=''
\r
270 texture_index_size:
\r
271 material_index_size:
\r
274 rigidbody_index_size:
\r
276 def __init__(self):
\r
280 self.__model=Model()
\r
282 def read(self, path: 'filepath') -> Model:
\r
283 size=os.path.getsize(path)
\r
284 with open(path, "rb") as f:
\r
285 if self.__load(path, f, size):
\r
286 return self.__model
\r
288 def __load(self, path: 'filepath', io: io.IOBase, size: int) -> bool:
\r
293 ####################
\r
295 ####################
\r
296 signature=self.__unpack("4s", 4)
\r
297 if signature!=b"PMX ":
\r
298 print("invalid signature", self.signature)
\r
300 version=self.__read_float()
\r
302 print("unknown version", version)
\r
303 self.__model.version=version
\r
305 flag_bytes=self.__read_uint(1)
\r
307 print("invalid flag length", self.flag_bytes)
\r
310 self.text_encoding=self.__read_uint(1)
\r
311 self.__read_text=self.__get_read_text()
\r
313 self.extended_uv=self.__read_uint(1)
\r
314 if self.extended_uv>0:
\r
315 print("extended uv is not supported", self.extended_uv)
\r
318 self.vertex_index_size=self.__read_uint(1)
\r
319 self.texture_index_size=self.__read_uint(1)
\r
320 self.material_index_size=self.__read_uint(1)
\r
321 self.bone_index_size=self.__read_uint(1)
\r
322 self.morph_index_size=self.__read_uint(1)
\r
323 self.rigidbody_index_size=self.__read_uint(1)
\r
325 ####################
\r
327 ####################
\r
328 self.__model.name = self.__read_text()
\r
329 self.__model.english_name = self.__read_text()
\r
330 self.__model.comment = self.__read_text()
\r
331 self.__model.english_comment = self.__read_text()
\r
333 ####################
\r
335 ####################
\r
336 vertex_count=self.__read_uint(4)
\r
337 self.__model.vertices=[self.__read_vertex()
\r
338 for i in range(vertex_count)]
\r
340 ####################
\r
342 ####################
\r
343 index_count=self.__read_uint(4)
\r
344 self.__model.indices=[self.__read_uint(self.vertex_index_size)
\r
345 for i in range(index_count)]
\r
347 ####################
\r
349 ####################
\r
350 texture_count=self.__read_uint(4)
\r
351 self.__model.textures=[self.__read_text()
\r
352 for i in range(texture_count)]
\r
354 ####################
\r
356 ####################
\r
357 material_count=self.__read_uint(4)
\r
358 self.__model.materials=[self.__read_material()
\r
359 for i in range(material_count)]
\r
361 ####################
\r
363 ####################
\r
364 bone_count=self.__read_uint(4)
\r
365 self.__model.bones=[self.__read_bone()
\r
366 for i in range(bone_count)]
\r
370 def __str__(self) -> str:
\r
373 def __check_position(self):
\r
374 self.__pos=self.__io.tell()
\r
376 def __unpack(self, fmt: str, size: int) -> "read value as format":
\r
377 result=struct.unpack(fmt, self.__io.read(size))
\r
378 self.__check_position()
\r
381 def __get_read_text(self) -> "text process function":
\r
382 if self.text_encoding==0:
\r
384 size=self.__read_uint(4)
\r
385 return self.__unpack("{0}s".format(size), size).decode("UTF16")
\r
387 elif self.text_encoding==1:
\r
389 size=self.__read_uint(4)
\r
390 return self.__unpack("{0}s".format(size), size).decode("UTF8")
\r
393 print("unknown text encoding", self.text_encoding)
\r
395 def __read_uint(self, size):
\r
397 return self.__unpack("B", size)
\r
399 return self.__unpack("H", size)
\r
401 return self.__unpack("I", size)
\r
402 print("not reach here")
\r
403 raise ParseException("invalid int size: "+size)
\r
405 def __read_float(self):
\r
406 return self.__unpack("f", 4)
\r
408 def __read_vector2(self):
\r
409 return common.Vector2(
\r
410 self.__read_float(),
\r
411 self.__read_float()
\r
414 def __read_vector3(self):
\r
415 return common.Vector3(
\r
416 self.__read_float(),
\r
417 self.__read_float(),
\r
418 self.__read_float()
\r
421 def __read_rgba(self):
\r
422 return common.RGBA(
\r
423 self.__read_float(),
\r
424 self.__read_float(),
\r
425 self.__read_float(),
\r
426 self.__read_float()
\r
429 def __read_rgb(self):
\r
431 self.__read_float(),
\r
432 self.__read_float(),
\r
433 self.__read_float()
\r
436 def __read_vertex(self):
\r
438 self.__read_vector3(), # pos
\r
439 self.__read_vector3(), # normal
\r
440 self.__read_vector2(), # uv
\r
441 self.__read_deform(), # deform(bone weight)
\r
442 self.__read_float() # edge factor
\r
445 def __read_deform(self):
\r
446 deform_type=self.__read_uint(1)
\r
448 return Bdef1(self.__read_uint(self.bone_index_size))
\r
451 self.__read_uint(self.bone_index_size),
\r
452 self.__read_uint(self.bone_index_size),
\r
453 self.__read_float()
\r
458 self.__read_uint(self.bone_index_size),
\r
459 self.__read_uint(self.bone_index_size),
\r
460 self.__read_uint(self.bone_index_size),
\r
461 self.__read_uint(self.bone_index_size),
\r
462 self.__read_float(), self.__read_float(),
\r
463 self.__read_float(), self.__read_float()
\r
466 raise ParseException("unknown deform type: {0}".format(deform_type))
\r
468 def __read_material(self):
\r
470 name=self.__read_text(),
\r
471 english_name=self.__read_text(),
\r
472 diffuse_color=self.__read_rgb(),
\r
473 diffuse_alpha=self.__read_float(),
\r
474 specular_color=self.__read_rgb(),
\r
475 specular_factor=self.__read_float(),
\r
476 ambient_color=self.__read_rgb(),
\r
477 flag=self.__read_uint(1),
\r
478 edge_color=self.__read_rgba(),
\r
479 edge_size=self.__read_float(),
\r
480 texture_index=self.__read_uint(self.texture_index_size),
\r
481 sphia_texture_index=self.__read_uint(self.texture_index_size),
\r
482 sphia_mode=self.__read_uint(1),
\r
483 toon_sharing_flag=self.__read_uint(1),
\r
485 if material.toon_sharing_flag==0:
\r
486 material.toon_texture_index=self.__read_uint(self.texture_index_size)
\r
487 elif material.toon_sharing_flag==1:
\r
488 material.toon_texture_index=self.__read_uint(1)
\r
490 raise ParseException("unknown toon_sharing_flag {0}".format(material.toon_sharing_flag))
\r
491 material.comment=self.__read_text()
\r
492 material.index_count=self.__read_uint(4)
\r
495 def __read_bone(self):
\r
497 name=self.__read_text(),
\r
498 english_name=self.__read_text(),
\r
499 position=self.__read_vector3(),
\r
500 parent_index=self.__read_uint(self.bone_index_size),
\r
501 layer=self.__read_uint(4),
\r
502 flag=self.__read_uint(2)
\r
504 if bone.getConnectionFlag()==0:
\r
505 bone.tail_positoin=self.__read_vector3()
\r
506 elif bone.getConnectionFlag()==1:
\r
507 bone.tail_index=self.__read_uint(self.bone_index_size)
\r
509 raise ParseException("unknown bone conenction flag: {0}".format(
\r
510 bone.getConnectionFlag()))
\r
512 if bone.getRotationFlag()==1 or bone.getTranslationFlag()==1:
\r
513 bone.effect_index=self.__read_uint(self.bone_index_size)
\r
514 bone.effect_factor=self.__read_float()
\r
516 if bone.getFixedAxisFlag()==1:
\r
517 bone.fixed_axis=self.__read_vector3()
\r
519 if bone.getLocalCoordinateFlag()==1:
\r
520 bone.local_x_vector=self.__read_vector3()
\r
521 bone.local_z_vector=self.__read_vector3()
\r
523 if bone.getExternalParentDeformFlag()==1:
\r
524 bone.external_key=self.__read_uint(4)
\r
526 if bone.getIkFlag()==1:
\r
527 bone.ik=self.__read_ik()
\r
531 def __read_ik(self):
\r
533 target_index=self.__read_uint(self.bone_index_size),
\r
534 loop=self.__read_uint(4),
\r
535 limit_radian=self.__read_float())
\r
536 link_size=self.__read_uint(4)
\r
537 ik.link=[self.__read_ik_link() for i in range(link_size)]
\r
539 def __read_ik_link(self):
\r
541 self.__read_uint(self.bone_index_size),
\r
542 self.__read_uint(1))
\r
543 if link.limit_angle==0:
\r
545 elif link.limit_angle==1:
\r
546 link.limit_min=self.__read_vector3()
\r
547 link.limit_max=self.__read_vector3()
\r
549 raise ParseException("invalid ik link limit_angle: {0}".format(
\r