OSDN Git Service

033665a44835b4c83c586928ec250b8e45c4af2f
[meshio/pymeshio.git] / pymeshio / pmx / loader.py
1 # coding: utf-8\r
2 import io\r
3 import pymeshio.common\r
4 import pymeshio.pmx\r
5 \r
6 \r
7 class Loader(pymeshio.common.BinaryLoader):\r
8     """pmx loader\r
9     """\r
10     def __init__(self, io,\r
11             text_encoding,\r
12             extended_uv,\r
13             vertex_index_size,\r
14             texture_index_size,\r
15             material_index_size,\r
16             bone_index_size,\r
17             morph_index_size,\r
18             rigidbody_index_size\r
19             ):\r
20         super(Loader, self).__init__(io)\r
21         self.read_text=self.get_read_text(text_encoding)\r
22         if extended_uv>0:\r
23             raise pymeshio.common.ParseException(\r
24                     "extended uv is not supported", extended_uv)\r
25         self.read_vertex_index=lambda : self.read_uint(vertex_index_size)\r
26         self.read_texture_index=lambda : self.read_uint(texture_index_size)\r
27         self.read_material_index=lambda : self.read_uint(material_index_size)\r
28         self.read_bone_index=lambda : self.read_uint(bone_index_size)\r
29         self.read_morph_index=lambda : self.read_uint(morph_index_size)\r
30         self.read_rigidbody_index=lambda : self.read_uint(rigidbody_index_size)\r
31 \r
32     def __str__(self) -> str:\r
33         return '<pymeshio.pmx.Loader>'\r
34 \r
35     def get_read_text(self, text_encoding) -> "text process function":\r
36         if text_encoding==0:\r
37             def read_text():\r
38                 size=self.read_uint(4)\r
39                 return self.unpack("{0}s".format(size), size).decode("UTF16")\r
40             return read_text\r
41         elif text_encoding==1:\r
42             def read_text():\r
43                 size=self.read_uint(4)\r
44                 return self.unpack("{0}s".format(size), size).decode("UTF8")\r
45             return read_text\r
46         else:\r
47             print("unknown text encoding", text_encoding)\r
48 \r
49     def read_vertex(self):\r
50         return pymeshio.pmx.Vertex(\r
51                 self.read_vector3(), # pos\r
52                 self.read_vector3(), # normal\r
53                 self.read_vector2(), # uv\r
54                 self.read_deform(), # deform(bone weight)\r
55                 self.read_float() # edge factor\r
56                 )\r
57 \r
58     def read_deform(self):\r
59         deform_type=self.read_uint(1)\r
60         if deform_type==0:\r
61             return pymeshio.pmx.Bdef1(self.read_bone_index())\r
62         elif deform_type==1:\r
63             return pymeshio.pmx.Bdef2(\r
64                     self.read_bone_index(),\r
65                     self.read_bone_index(),\r
66                     self.read_float()\r
67                     )\r
68         elif deform_type==2:\r
69             # todo\r
70             raise pymeshio.common.ParseException(\r
71                     "not implemented Bdef4")\r
72         else:\r
73             raise pymeshio.common.ParseException(\r
74                     "unknown deform type: {0}".format(deform_type))\r
75 \r
76     def read_material(self):\r
77         material=pymeshio.pmx.Material(\r
78                 name=self.read_text(),\r
79                 english_name=self.read_text(),\r
80                 diffuse_color=self.read_rgb(),\r
81                 diffuse_alpha=self.read_float(),\r
82                 specular_color=self.read_rgb(),\r
83                 specular_factor=self.read_float(),\r
84                 ambient_color=self.read_rgb(),\r
85                 flag=self.read_uint(1),\r
86                 edge_color=self.read_rgba(),\r
87                 edge_size=self.read_float(),\r
88                 texture_index=self.read_texture_index(),\r
89                 sphia_texture_index=self.read_texture_index(),\r
90                 sphia_mode=self.read_uint(1),\r
91                 toon_sharing_flag=self.read_uint(1),\r
92                 )\r
93         if material.toon_sharing_flag==0:\r
94             material.toon_texture_index=self.read_texture_index()\r
95         elif material.toon_sharing_flag==1:\r
96             material.toon_texture_index=self.read_uint(1)\r
97         else:\r
98             raise pymeshio.common.ParseException(\r
99                     "unknown toon_sharing_flag {0}".format(\r
100                         material.toon_sharing_flag))\r
101         material.comment=self.read_text()\r
102         material.index_count=self.read_uint(4)\r
103         return material\r
104 \r
105     def read_bone(self):\r
106         bone=pymeshio.pmx.Bone(\r
107                 name=self.read_text(),\r
108                 english_name=self.read_text(),\r
109                 position=self.read_vector3(),\r
110                 parent_index=self.read_bone_index(),\r
111                 layer=self.read_uint(4),\r
112                 flag=self.read_uint(2)                \r
113                 )\r
114         if bone.getConnectionFlag()==0:\r
115             bone.tail_positoin=self.read_vector3()\r
116         elif bone.getConnectionFlag()==1:\r
117             bone.tail_index=self.read_bone_index()\r
118         else:\r
119             raise pymeshio.common.ParseException(\r
120                     "unknown bone conenction flag: {0}".format(\r
121                 bone.getConnectionFlag()))\r
122 \r
123         if bone.getRotationFlag()==1 or bone.getTranslationFlag()==1:\r
124             bone.effect_index=self.read_bone_index()\r
125             bone.effect_factor=self.read_float()\r
126 \r
127         if bone.getFixedAxisFlag()==1:\r
128             bone.fixed_axis=self.read_vector3()\r
129 \r
130         if bone.getLocalCoordinateFlag()==1:\r
131             bone.local_x_vector=self.read_vector3()\r
132             bone.local_z_vector=self.read_vector3()\r
133 \r
134         if bone.getExternalParentDeformFlag()==1:\r
135             bone.external_key=self.read_uint(4)\r
136 \r
137         if bone.getIkFlag()==1:\r
138             bone.ik=self.read_ik()\r
139 \r
140         return bone\r
141 \r
142     def read_ik(self):\r
143         ik=pymeshio.pmx.Ik(\r
144                 target_index=self.read_bone_index(),\r
145                 loop=self.read_uint(4),\r
146                 limit_radian=self.read_float())\r
147         link_size=self.read_uint(4)\r
148         ik.link=[self.read_ik_link() \r
149                 for _ in range(link_size)]\r
150 \r
151     def read_ik_link(self):\r
152         link=pymeshio.pmx.IkLink(\r
153                 self.read_bone_index(),\r
154                 self.read_uint(1))\r
155         if link.limit_angle==0:\r
156             pass\r
157         elif link.limit_angle==1:\r
158             link.limit_min=self.read_vector3()\r
159             link.limit_max=self.read_vector3()\r
160         else:\r
161             raise pymeshio.common.ParseException(\r
162                     "invalid ik link limit_angle: {0}".format(\r
163                 link.limit_angle))\r
164         return link\r
165 \r
166     def read_morgh(self):\r
167         name=self.read_text()\r
168         english_name=self.read_text()\r
169         panel=self.read_uint(1)\r
170         morph_type=self.read_uint(1)\r
171         offset_size=self.read_uint(4)\r
172         if morph_type==0:\r
173             # todo\r
174             raise pymeshio.common.ParseException(\r
175                     "not implemented GroupMorph")\r
176         elif morph_type==1:\r
177             morph=pymeshio.pmx.Morph(name, english_name, \r
178                     panel, morph_type)\r
179             morph.offsets=[self.read_vertex_morph_offset() \r
180                     for _ in range(offset_size)]\r
181             return morph\r
182         elif morph_type==2:\r
183             # todo\r
184             raise pymeshio.common.ParseException(\r
185                     "not implemented BoneMorph")\r
186         elif morph_type==3:\r
187             # todo\r
188             raise pymeshio.common.ParseException(\r
189                     "not implemented UvMorph")\r
190         elif morph_type==4:\r
191             # todo\r
192             raise pymeshio.common.ParseException(\r
193                     "not implemented extended UvMorph1")\r
194         elif morph_type==5:\r
195             # todo\r
196             raise pymeshio.common.ParseException(\r
197                     "not implemented extended UvMorph2")\r
198         elif morph_type==6:\r
199             # todo\r
200             raise pymeshio.common.ParseException(\r
201                     "not implemented extended UvMorph3")\r
202         elif morph_type==7:\r
203             # todo\r
204             raise pymeshio.common.ParseException(\r
205                     "not implemented extended UvMorph4")\r
206         elif morph_type==8:\r
207             # todo\r
208             raise pymeshio.common.ParseException(\r
209                     "not implemented extended MaterialMorph")\r
210         else:\r
211             raise pymeshio.common.ParseException(\r
212                     "unknown morph type: {0}".format(morph_type))\r
213 \r
214     def read_vertex_morph_offset(self):\r
215         return pymeshio.pmx.VerexMorphOffset(\r
216                 self.read_vertex_index(), self.read_vector3())\r
217 \r
218     def read_display_slot(self):\r
219         display_slot=pymeshio.pmx.DisplaySlot(self.read_text(), self.read_text(), \r
220                 self.read_uint(1))\r
221         display_count=self.read_uint(4)\r
222         for _ in range(display_count):\r
223             display_type=self.read_uint(1)\r
224             if display_type==0:\r
225                 display_slot.refrences.append(\r
226                         (display_type, self.read_bone_index()))\r
227             elif display_type==1:\r
228                 display_slot.refrences.append(\r
229                         (display_type, self.read_morph_index()))\r
230             else:\r
231                 raise pymeshio.common.ParseException(\r
232                         "unknown display_type: {0}".format(display_type))\r
233 \r
234     def read_rigidbody(self):\r
235         return pymeshio.pmx.RigidBody(\r
236                 name=self.read_text(), \r
237                 english_name=self.read_text(),\r
238                 bone_index=self.read_bone_index(),\r
239                 collision_group=self.read_uint(1),\r
240                 no_collision_flag=self.read_uint(2),\r
241                 shape_type=self.read_uint(1),\r
242                 shape_size=self.read_vector3(),\r
243                 shape_position=self.read_vector3(),\r
244                 shape_rotation=self.read_vector3(),\r
245                 mass=self.read_float(),\r
246                 linear_damping=self.read_float(),\r
247                 angular_damping=self.read_float(),\r
248                 restitution=self.read_float(),\r
249                 friction=self.read_float(),\r
250                 mode=self.read_uint(1)\r
251                 )\r
252 \r
253     def read_joint(self):\r
254         return pymeshio.pmx.Joint(\r
255                 name=self.read_text(),\r
256                 english_name=self.read_text(),\r
257                 joint_type=self.read_uint(1),\r
258                 rigidbody_index_a=self.read_rigidbody_index(),\r
259                 rigidbody_index_b=self.read_rigidbody_index(),\r
260                 position=self.read_vector3(),\r
261                 rotation=self.read_vector3(),\r
262                 translation_limit_min=self.read_vector3(),\r
263                 translation_limit_max=self.read_vector3(),\r
264                 rotation_limit_min=self.read_vector3(),\r
265                 rotation_limit_max=self.read_vector3(),\r
266                 spring_constant_translation=self.read_vector3(),\r
267                 spring_constant_rotation=self.read_vector3())\r
268 \r
269 \r
270 def load(path: str) -> pymeshio.pmx.Model:\r
271     # general binary loader\r
272     loader=pymeshio.common.BinaryLoader(\r
273             io.BytesIO(\r
274                 pymeshio.common.readall(path)))\r
275 \r
276     # header\r
277     signature=loader.unpack("4s", 4)\r
278     if signature!=b"PMX ":\r
279         raise pymeshio.common.ParseException(\r
280                 "invalid signature", loader.signature)\r
281 \r
282     version=loader.read_float()\r
283     if version!=2.0:\r
284         print("unknown version", version)\r
285     model=pymeshio.pmx.Model(version)\r
286 \r
287     # flags\r
288     flag_bytes=loader.read_uint(1)\r
289     if flag_bytes!=8:\r
290         raise pymeshio.common.ParseException(\r
291                 "invalid flag length", loader.flag_bytes)\r
292     text_encoding=loader.read_uint(1)\r
293     extended_uv=loader.read_uint(1)\r
294     vertex_index_size=loader.read_uint(1)\r
295     texture_index_size=loader.read_uint(1)\r
296     material_index_size=loader.read_uint(1)\r
297     bone_index_size=loader.read_uint(1)\r
298     morph_index_size=loader.read_uint(1)\r
299     rigidbody_index_size=loader.read_uint(1)\r
300     \r
301     # pmx custom loader\r
302     loader=Loader(loader.io,\r
303             text_encoding,\r
304             extended_uv,\r
305             vertex_index_size,\r
306             texture_index_size,\r
307             material_index_size,\r
308             bone_index_size,\r
309             morph_index_size,\r
310             rigidbody_index_size\r
311             )\r
312 \r
313     # model info\r
314     model.name = loader.read_text()\r
315     model.english_name = loader.read_text()\r
316     model.comment = loader.read_text()\r
317     model.english_comment = loader.read_text()\r
318 \r
319     # model data\r
320     model.vertices=[loader.read_vertex() \r
321             for _ in range(loader.read_uint(4))]\r
322     model.indices=[loader.read_vertex_index() \r
323             for _ in range(loader.read_uint(4))]\r
324     model.textures=[loader.read_text() \r
325             for _ in range(loader.read_uint(4))]\r
326     model.materials=[loader.read_material() \r
327             for _ in range(loader.read_uint(4))]\r
328     model.bones=[loader.read_bone() \r
329             for _ in range(loader.read_uint(4))]\r
330     model.morphs=[loader.read_morgh() \r
331             for _ in range(loader.read_uint(4))]\r
332     model.display_slots=[loader.read_display_slot() \r
333             for _ in range(loader.read_uint(4))]\r
334     model.rigidbodies=[loader.read_rigidbody()\r
335             for _ in range(loader.read_uint(4))]\r
336     model.joints=[loader.read_joint()\r
337             for _ in range(loader.read_uint(4))]\r
338 \r
339     return model\r
340 \r