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
24 class Material(object):
\r
27 Attributes: see __init__
\r
41 'sphia_texture_index',
\r
43 'toon_sharing_flag',
\r
44 'toon_texture_index',
\r
51 diffuse_color: common.RGB,
\r
52 diffuse_alpha: float,
\r
53 specular_color: common.RGB,
\r
54 specular_factor: float,
\r
55 ambient_color: common.RGB,
\r
57 edge_color: common.RGBA,
\r
60 sphia_texture_index: int,
\r
62 toon_sharing_flag: int
\r
65 self.english_name=english_name
\r
66 self.diffuse_color=diffuse_color
\r
67 self.diffuse_alpha=diffuse_alpha
\r
68 self.specular_color=specular_color
\r
69 self.specular_factor=specular_factor
\r
70 self.ambient_color=ambient_color
\r
72 self.edge_color=edge_color
\r
73 self.edge_size=edge_size
\r
74 self.texture_index=texture_index
\r
75 self.sphia_texture_index=sphia_texture_index
\r
76 self.sphia_mode=sphia_mode
\r
77 self.toon_sharing_flag=toon_sharing_flag
\r
79 self.toon_texture_index=None
\r
84 class Deform(object):
\r
88 class Bdef1(object):
\r
89 """bone deform. use a weight
\r
91 Attributes: see __init__
\r
93 __slots__=[ 'bone_index']
\r
94 def __init__(self, bone_index: int):
\r
95 self.bone_index=bone_index
\r
98 class Bdef2(object):
\r
99 """bone deform. use two weights
\r
101 Attributes: see __init__
\r
103 __slots__=[ 'index0', 'index1', 'weight0']
\r
104 def __init__(self,
\r
110 self.weight0=weight0
\r
113 class Vertex(object):
\r
116 Attributes: see __init__
\r
118 __slots__=[ 'position', 'normal', 'uv', 'deform', 'edge_factor' ]
\r
119 def __init__(self,
\r
120 position: common.Vector3,
\r
121 normal: common.Vector3,
\r
122 uv: common.Vector2,
\r
124 edge_factor: float):
\r
125 self.position=position
\r
129 self.edge_factor=edge_factor
\r
132 class Model(object):
\r
133 """pmx data representation
\r
136 version: pmx version(expected 2.0)
\r
147 'version', # pmx version
\r
148 'name', # model name
\r
149 'english_name', # model name in english
\r
150 'comment', # model comment
\r
151 'english_comment', # model comment in english
\r
158 def __init__(self):
\r
161 self.english_name=''
\r
163 self.english_comment=''
\r
178 texture_index_size:
\r
179 material_index_size:
\r
182 rigidbody_index_size:
\r
184 def __init__(self):
\r
188 self.__model=Model()
\r
190 def read(self, path: 'filepath') -> Model:
\r
191 size=os.path.getsize(path)
\r
192 with open(path, "rb") as f:
\r
193 if self.__load(path, f, size):
\r
194 return self.__model
\r
196 def __load(self, path: 'filepath', io: io.IOBase, size: int) -> bool:
\r
201 ####################
\r
203 ####################
\r
204 signature=self.__unpack("4s", 4)
\r
205 if signature!=b"PMX ":
\r
206 print("invalid signature", self.signature)
\r
208 version=self.__unpack("f", 4)
\r
210 print("unknown version", version)
\r
211 self.__model.version=version
\r
213 flag_bytes=self.__unpack("B", 1)
\r
215 print("invalid flag length", self.flag_bytes)
\r
218 self.text_encoding=self.__unpack("B", 1)
\r
219 self.__read_text=self.__get_read_text()
\r
221 self.extended_uv=self.__unpack("B", 1)
\r
222 if self.extended_uv>0:
\r
223 print("extended uv is not supported", self.extended_uv)
\r
226 self.vertex_index_size=self.__unpack("B", 1)
\r
227 self.texture_index_size=self.__unpack("B", 1)
\r
228 self.material_index_size=self.__unpack("B", 1)
\r
229 self.bone_index_size=self.__unpack("B", 1)
\r
230 self.morph_index_size=self.__unpack("B", 1)
\r
231 self.rigidbody_index_size=self.__unpack("B", 1)
\r
233 ####################
\r
235 ####################
\r
236 self.__model.name = self.__read_text()
\r
237 self.__model.english_name = self.__read_text()
\r
238 self.__model.comment = self.__read_text()
\r
239 self.__model.english_comment = self.__read_text()
\r
241 ####################
\r
243 ####################
\r
244 vertex_count=self.__read_uint(4)
\r
245 self.__model.vertices=[self.__read_vertex()
\r
246 for i in range(vertex_count)]
\r
248 ####################
\r
250 ####################
\r
251 index_count=self.__read_uint(4)
\r
252 self.__model.indices=[self.__read_uint(self.vertex_index_size)
\r
253 for i in range(index_count)]
\r
255 ####################
\r
257 ####################
\r
258 texture_count=self.__read_uint(4)
\r
259 self.__model.textures=[self.__read_text()
\r
260 for i in range(texture_count)]
\r
262 ####################
\r
264 ####################
\r
265 material_count=self.__read_uint(4)
\r
266 self.__model.materials=[self.__read_material()
\r
267 for i in range(material_count)]
\r
269 ####################
\r
271 ####################
\r
272 bone_count=self.__read_uint(4)
\r
273 self.__model.bones=[self.__read_bone()
\r
274 for i in range(bone_count)]
\r
278 def __str__(self) -> str:
\r
281 def __check_position(self):
\r
282 self.__pos=self.__io.tell()
\r
284 def __unpack(self, fmt: str, size: int) -> "read value as format":
\r
285 result=struct.unpack(fmt, self.__io.read(size))
\r
286 self.__check_position()
\r
289 def __get_read_text(self) -> "text process function":
\r
290 if self.text_encoding==0:
\r
292 size=self.__unpack("I", 4)
\r
293 return self.__unpack("{0}s".format(size), size).decode("UTF16")
\r
295 elif self.text_encoding==1:
\r
297 size=self.__unpack("I", 4)
\r
298 return self.__unpack("{0}s".format(size), size).decode("UTF8")
\r
301 print("unknown text encoding", self.text_encoding)
\r
303 def __read_uint(self, size):
\r
305 return self.__unpack("B", size)
\r
307 return self.__unpack("H", size)
\r
309 return self.__unpack("I", size)
\r
310 print("not reach here")
\r
311 raise ParseException("invalid int size: "+size)
\r
313 def __read_vertex(self):
\r
315 self.__read_vector3(), # pos
\r
316 self.__read_vector3(), # normal
\r
317 self.__read_vector2(), # uv
\r
318 self.__read_deform(), # deform(bone weight)
\r
319 self.__unpack("f", 4) # edge factor
\r
322 def __read_vector2(self):
\r
323 return common.Vector2(
\r
324 self.__unpack("f", 4),
\r
325 self.__unpack("f", 4)
\r
328 def __read_vector3(self):
\r
329 return common.Vector3(
\r
330 self.__unpack("f", 4),
\r
331 self.__unpack("f", 4),
\r
332 self.__unpack("f", 4)
\r
335 def __read_rgba(self):
\r
336 return common.RGBA(
\r
337 self.__unpack("f", 4),
\r
338 self.__unpack("f", 4),
\r
339 self.__unpack("f", 4),
\r
340 self.__unpack("f", 4)
\r
343 def __read_rgb(self):
\r
345 self.__unpack("f", 4),
\r
346 self.__unpack("f", 4),
\r
347 self.__unpack("f", 4)
\r
350 def __read_deform(self):
\r
351 deform_type=self.__unpack("B", 1)
\r
353 return Bdef1(self.__read_uint(self.bone_index_size))
\r
356 self.__read_uint(self.bone_index_size),
\r
357 self.__read_uint(self.bone_index_size),
\r
358 self.__unpack("f", 4)
\r
363 self.__read_uint(self.bone_index_size),
\r
364 self.__read_uint(self.bone_index_size),
\r
365 self.__read_uint(self.bone_index_size),
\r
366 self.__read_uint(self.bone_index_size),
\r
367 self.__unpack("f", 4), self.__unpack("f", 4),
\r
368 self.__unpack("f", 4), self.__unpack("f", 4)
\r
371 raise ParseException("unknown deform type: {0}".format(deform_type))
\r
373 def __read_material(self):
\r
375 name=self.__read_text(),
\r
376 english_name=self.__read_text(),
\r
377 diffuse_color=self.__read_rgb(),
\r
378 diffuse_alpha=self.__unpack("f", 4),
\r
379 specular_color=self.__read_rgb(),
\r
380 specular_factor=self.__unpack("f", 4),
\r
381 ambient_color=self.__read_rgb(),
\r
382 flag=self.__read_uint(1),
\r
383 edge_color=self.__read_rgba(),
\r
384 edge_size=self.__unpack("f", 4),
\r
385 texture_index=self.__read_uint(self.texture_index_size),
\r
386 sphia_texture_index=self.__read_uint(self.texture_index_size),
\r
387 sphia_mode=self.__read_uint(1),
\r
388 toon_sharing_flag=self.__read_uint(1),
\r
390 if material.toon_sharing_flag==0:
\r
391 material.toon_texture_index=self.__read_uint(self.texture_index_size)
\r
392 elif material.toon_sharing_flag==1:
\r
393 material.toon_texture_index=self.__read_uint(1)
\r
395 raise ParseException("unknown toon_sharing_flag {0}".format(material.toon_sharing_flag))
\r
396 material.comment=self.__read_text()
\r
397 material.index_count=self.__read_uint(4)
\r
400 def __read_bone(self):
\r