OSDN Git Service

implement morph
[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("extended uv is not supported", extended_uv)\r
24         self.read_vertex_index=lambda : self.read_uint(vertex_index_size)\r
25         self.read_texture_index=lambda : self.read_uint(texture_index_size)\r
26         self.read_material_index=lambda : self.read_uint(material_index_size)\r
27         self.read_bone_index=lambda : self.read_uint(bone_index_size)\r
28         self.read_morph_index=lambda : self.read_uint(morph_index_size)\r
29         self.read_rigidbody_index=lambda : self.read_uint(rigidbody_index_size)\r
30 \r
31     def __str__(self) -> str:\r
32         return '<pymeshio.pmx.Loader>'\r
33 \r
34     def get_read_text(self, text_encoding) -> "text process function":\r
35         if text_encoding==0:\r
36             def read_text():\r
37                 size=self.read_uint(4)\r
38                 return self.unpack("{0}s".format(size), size).decode("UTF16")\r
39             return read_text\r
40         elif text_encoding==1:\r
41             def read_text():\r
42                 size=self.read_uint(4)\r
43                 return self.unpack("{0}s".format(size), size).decode("UTF8")\r
44             return read_text\r
45         else:\r
46             print("unknown text encoding", text_encoding)\r
47 \r
48     def read_vertex(self):\r
49         return pymeshio.pmx.Vertex(\r
50                 self.read_vector3(), # pos\r
51                 self.read_vector3(), # normal\r
52                 self.read_vector2(), # uv\r
53                 self.read_deform(), # deform(bone weight)\r
54                 self.read_float() # edge factor\r
55                 )\r
56 \r
57     def read_deform(self):\r
58         deform_type=self.read_uint(1)\r
59         if deform_type==0:\r
60             return pymeshio.pmx.Bdef1(self.read_bone_index())\r
61         elif deform_type==1:\r
62             return pymeshio.pmx.Bdef2(\r
63                     self.read_bone_index(),\r
64                     self.read_bone_index(),\r
65                     self.read_float()\r
66                     )\r
67         elif deform_type==2:\r
68             # todo\r
69             raise pymeshio.common.ParseException("not implemented Bdef4")\r
70         else:\r
71             raise pymeshio.common.ParseException("unknown deform type: {0}".format(deform_type))\r
72 \r
73     def read_material(self):\r
74         material=pymeshio.pmx.Material(\r
75                 name=self.read_text(),\r
76                 english_name=self.read_text(),\r
77                 diffuse_color=self.read_rgb(),\r
78                 diffuse_alpha=self.read_float(),\r
79                 specular_color=self.read_rgb(),\r
80                 specular_factor=self.read_float(),\r
81                 ambient_color=self.read_rgb(),\r
82                 flag=self.read_uint(1),\r
83                 edge_color=self.read_rgba(),\r
84                 edge_size=self.read_float(),\r
85                 texture_index=self.read_texture_index(),\r
86                 sphia_texture_index=self.read_texture_index(),\r
87                 sphia_mode=self.read_uint(1),\r
88                 toon_sharing_flag=self.read_uint(1),\r
89                 )\r
90         if material.toon_sharing_flag==0:\r
91             material.toon_texture_index=self.read_texture_index()\r
92         elif material.toon_sharing_flag==1:\r
93             material.toon_texture_index=self.read_uint(1)\r
94         else:\r
95             raise pymeshio.common.ParseException("unknown toon_sharing_flag {0}".format(material.toon_sharing_flag))\r
96         material.comment=self.read_text()\r
97         material.index_count=self.read_uint(4)\r
98         return material\r
99 \r
100     def read_bone(self):\r
101         bone=pymeshio.pmx.Bone(\r
102                 name=self.read_text(),\r
103                 english_name=self.read_text(),\r
104                 position=self.read_vector3(),\r
105                 parent_index=self.read_bone_index(),\r
106                 layer=self.read_uint(4),\r
107                 flag=self.read_uint(2)                \r
108                 )\r
109         if bone.getConnectionFlag()==0:\r
110             bone.tail_positoin=self.read_vector3()\r
111         elif bone.getConnectionFlag()==1:\r
112             bone.tail_index=self.read_bone_index()\r
113         else:\r
114             raise pymeshio.common.ParseException("unknown bone conenction flag: {0}".format(\r
115                 bone.getConnectionFlag()))\r
116 \r
117         if bone.getRotationFlag()==1 or bone.getTranslationFlag()==1:\r
118             bone.effect_index=self.read_bone_index()\r
119             bone.effect_factor=self.read_float()\r
120 \r
121         if bone.getFixedAxisFlag()==1:\r
122             bone.fixed_axis=self.read_vector3()\r
123 \r
124         if bone.getLocalCoordinateFlag()==1:\r
125             bone.local_x_vector=self.read_vector3()\r
126             bone.local_z_vector=self.read_vector3()\r
127 \r
128         if bone.getExternalParentDeformFlag()==1:\r
129             bone.external_key=self.read_uint(4)\r
130 \r
131         if bone.getIkFlag()==1:\r
132             bone.ik=self.read_ik()\r
133 \r
134         return bone\r
135 \r
136     def read_ik(self):\r
137         ik=pymeshio.pmx.Ik(\r
138                 target_index=self.read_bone_index(),\r
139                 loop=self.read_uint(4),\r
140                 limit_radian=self.read_float())\r
141         link_size=self.read_uint(4)\r
142         ik.link=[self.read_ik_link() for _ in range(link_size)]\r
143 \r
144     def read_ik_link(self):\r
145         link=pymeshio.pmx.IkLink(\r
146                 self.read_bone_index(),\r
147                 self.read_uint(1))\r
148         if link.limit_angle==0:\r
149             pass\r
150         elif link.limit_angle==1:\r
151             link.limit_min=self.read_vector3()\r
152             link.limit_max=self.read_vector3()\r
153         else:\r
154             raise pymeshio.common.ParseException("invalid ik link limit_angle: {0}".format(\r
155                 link.limit_angle))\r
156         return link\r
157 \r
158     def read_morgh(self):\r
159         name=self.read_text()\r
160         english_name=self.read_text()\r
161         panel=self.read_uint(1)\r
162         morph_type=self.read_uint(1)\r
163         offset_size=self.read_uint(4)\r
164         if morph_type==0:\r
165             # todo\r
166             raise pymeshio.common.ParseException("not implemented GroupMorph")\r
167         elif morph_type==1:\r
168             morph=pymeshio.pmx.Morph(name, english_name, panel, morph_type)\r
169             morph.offsets=[self.read_vertex_morph_offset() for _ in range(offset_size)]\r
170             return morph\r
171         elif morph_type==2:\r
172             # todo\r
173             raise pymeshio.common.ParseException("not implemented BoneMorph")\r
174         elif morph_type==3:\r
175             # todo\r
176             raise pymeshio.common.ParseException("not implemented UvMorph")\r
177         elif morph_type==4:\r
178             # todo\r
179             raise pymeshio.common.ParseException("not implemented extended UvMorph1")\r
180         elif morph_type==5:\r
181             # todo\r
182             raise pymeshio.common.ParseException("not implemented extended UvMorph2")\r
183         elif morph_type==6:\r
184             # todo\r
185             raise pymeshio.common.ParseException("not implemented extended UvMorph3")\r
186         elif morph_type==7:\r
187             # todo\r
188             raise pymeshio.common.ParseException("not implemented extended UvMorph4")\r
189         elif morph_type==8:\r
190             # todo\r
191             raise pymeshio.common.ParseException("not implemented extended MaterialMorph")\r
192         else:\r
193             raise pymeshio.common.ParseException("unknown morph type: {0}".format(morph_type))\r
194 \r
195     def read_vertex_morph_offset(self):\r
196         return pymeshio.pmx.VerexMorphOffset(self.read_vertex_index(), self.read_vector3())\r
197 \r
198     def read_display_slot(self):\r
199         pass\r
200 \r
201 \r
202 def load(path: str) -> pymeshio.pmx.Model:\r
203     # general binary loader\r
204     loader=pymeshio.common.BinaryLoader(\r
205             io.BytesIO(\r
206                 pymeshio.common.readall(path)))\r
207 \r
208     # header\r
209     signature=loader.unpack("4s", 4)\r
210     if signature!=b"PMX ":\r
211         raise pymeshio.common.ParseException("invalid signature", loader.signature)\r
212 \r
213     version=loader.read_float()\r
214     if version!=2.0:\r
215         print("unknown version", version)\r
216     model=pymeshio.pmx.Model(version)\r
217 \r
218     # flags\r
219     flag_bytes=loader.read_uint(1)\r
220     if flag_bytes!=8:\r
221         raise pymeshio.common.ParseException("invalid flag length", loader.flag_bytes)\r
222     text_encoding=loader.read_uint(1)\r
223     extended_uv=loader.read_uint(1)\r
224     vertex_index_size=loader.read_uint(1)\r
225     texture_index_size=loader.read_uint(1)\r
226     material_index_size=loader.read_uint(1)\r
227     bone_index_size=loader.read_uint(1)\r
228     morph_index_size=loader.read_uint(1)\r
229     rigidbody_index_size=loader.read_uint(1)\r
230     \r
231     # pmx custom loader\r
232     loader=Loader(loader.io,\r
233             text_encoding,\r
234             extended_uv,\r
235             vertex_index_size,\r
236             texture_index_size,\r
237             material_index_size,\r
238             bone_index_size,\r
239             morph_index_size,\r
240             rigidbody_index_size\r
241             )\r
242 \r
243     # model info\r
244     model.name = loader.read_text()\r
245     model.english_name = loader.read_text()\r
246     model.comment = loader.read_text()\r
247     model.english_comment = loader.read_text()\r
248 \r
249     # model data\r
250     vertex_count=loader.read_uint(4)\r
251     model.vertices=[loader.read_vertex() for _ in range(vertex_count)]\r
252 \r
253     index_count=loader.read_uint(4)\r
254     model.indices=[loader.read_vertex_index() for _ in range(index_count)]\r
255 \r
256     texture_count=loader.read_uint(4)\r
257     model.textures=[loader.read_text() for _ in range(texture_count)]\r
258 \r
259     material_count=loader.read_uint(4)\r
260     model.materials=[loader.read_material() for _ in range(material_count)]\r
261 \r
262     bone_count=loader.read_uint(4)\r
263     model.bones=[loader.read_bone() for _ in range(bone_count)]\r
264 \r
265     morph_count=loader.read_uint(4)\r
266     model.morphs=[loader.read_morgh() for _ in range(morph_count)]\r
267 \r
268     display_slot_count=loader.read_uint(4)\r
269     model.display_slots=[loader.read_display_slot() for _ in range(display_slot_count)]\r
270 \r
271     return model\r
272 \r