OSDN Git Service

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