OSDN Git Service

refactoring pmd
[meshio/pymeshio.git] / pymeshio / pmd / loader.py
1 #coding: utf-8\r
2 import io\r
3 import pymeshio.common\r
4 import pymeshio.pmd\r
5 \r
6 \r
7 class Loader(pymeshio.common.BinaryLoader):\r
8     """pmx loader\r
9     """\r
10     def __init__(self, io, version):\r
11         super(Loader, self).__init__(io)\r
12         self.version=version\r
13 \r
14     def read_text(self, size):\r
15         """read cp932 text\r
16         """\r
17         src=self.unpack("%ds" % size, size)\r
18         assert(type(src)==bytes)\r
19         pos = src.find(b"\x00")\r
20         if pos==-1:\r
21             return src\r
22         else:\r
23             return src[:pos]\r
24 \r
25     def read_vertex(self):\r
26         return pymeshio.pmd.Vertex(\r
27                 self.read_vector3(),\r
28                 self.read_vector3(),\r
29                 self.read_vector2(),\r
30                 self.read_uint(2),\r
31                 self.read_uint(2),\r
32                 self.read_uint(1),\r
33                 self.read_uint(1))\r
34 \r
35     def read_material(self):\r
36         return pymeshio.pmd.Material(\r
37                 diffuse_color=self.read_rgb(),\r
38                 alpha=self.read_float(),\r
39                 specular_factor=self.read_float(),\r
40                 specular_color=self.read_rgb(),\r
41                 ambient_color=self.read_rgb(),\r
42                 toon_index=self.read_uint(1),\r
43                 edge_flag=self.read_uint(1),\r
44                 vertex_count=self.read_uint(4),\r
45                 texture_file=self.read_text(20)\r
46                 )\r
47 \r
48     def read_bone(self):\r
49         name=self.read_text(20)\r
50         parent_index=self.read_uint(2)\r
51         tail_index=self.read_uint(2)\r
52         bone=pymeshio.pmd.createBone(name, self.read_uint(1))\r
53         bone.parent_index=parent_index\r
54         bone.tail_index=tail_index\r
55         bone.ik_index = self.read_uint(2)\r
56         bone.pos = self.read_vector3()\r
57         return bone\r
58 \r
59     def read_ik(self):\r
60         ik=pymeshio.pmd.IK(self.read_uint(2), self.read_uint(2))\r
61         ik.length = self.read_uint(1)\r
62         ik.iterations = self.read_uint(2)\r
63         ik.weight = self.read_float()\r
64         ik.children=[self.read_uint(2) for _ in range(ik.length)]\r
65         return ik\r
66 \r
67     def read_morph(self):\r
68         skin=pymeshio.pmd.Skin(self.read_text(20))\r
69         skin_size = self.read_uint(4)\r
70         skin.type = self.read_uint(1)\r
71         for j in range(skin_size):\r
72             skin.indices.append(self.read_uint(4))\r
73             skin.pos_list.append(self.read_vector3())\r
74         return skin\r
75 \r
76     def read_rigidbody(self):\r
77         return pymeshio.pmd.RigidBody(\r
78                 name=self.read_text(20), \r
79                 bone_index=self.read_uint(2),\r
80                 collision_group=self.read_uint(1),\r
81                 no_collision_group=self.read_uint(2),\r
82                 shape_type=self.read_uint(1),\r
83                 shape_size=self.read_vector3(),\r
84                 shape_position=self.read_vector3(),\r
85                 shape_rotation=self.read_vector3(),\r
86                 mass=self.read_float(),\r
87                 linear_damping=self.read_float(),\r
88                 angular_damping=self.read_float(),\r
89                 restitution=self.read_float(),\r
90                 friction=self.read_float(),\r
91                 mode=self.read_uint(1)\r
92                 )\r
93 \r
94     def read_joint(self):\r
95         return pymeshio.pmd.Joint(\r
96                 name=self.read_text(20),\r
97                 rigidbody_index_a=self.read_uint(4),\r
98                 rigidbody_index_b=self.read_uint(4),\r
99                 position=self.read_vector3(),\r
100                 rotation=self.read_vector3(),\r
101                 translation_limit_min=self.read_vector3(),\r
102                 translation_limit_max=self.read_vector3(),\r
103                 rotation_limit_min=self.read_vector3(),\r
104                 rotation_limit_max=self.read_vector3(),\r
105                 spring_constant_translation=self.read_vector3(),\r
106                 spring_constant_rotation=self.read_vector3())\r
107 \r
108 \r
109 \r
110 def __load(loader, model):\r
111     # model info\r
112     model.name=loader.read_text(20)\r
113     model.comment=loader.read_text(256) \r
114 \r
115     # model data\r
116     model.vertices=[loader.read_vertex()\r
117             for _ in range(loader.read_uint(4))]\r
118     model.indices=[loader.read_uint(2)\r
119             for _ in range(loader.read_uint(4))]\r
120     model.materials=[loader.read_material()\r
121             for _ in range(loader.read_uint(4))]\r
122     model.bones=[loader.read_bone()\r
123             for _ in range(loader.read_uint(2))]\r
124     model.ik_list=[loader.read_ik()\r
125             for _ in range(loader.read_uint(2))]\r
126     model.morphs=[loader.read_morph()\r
127             for _ in range(loader.read_uint(2))]\r
128     model.morph_indices=[loader.read_uint(2)\r
129             for _ in range(loader.read_uint(1))]\r
130     model.bone_group_list=[loader.read_text(50)\r
131             for _ in range(loader.read_uint(1))]\r
132     model.bone_display_list=[(loader.read_uint(2), loader.read_uint(1))\r
133             for _i in range(loader.read_uint(4))]\r
134 \r
135     if loader.is_end():\r
136         # EOF\r
137         return True\r
138 \r
139     ############################################################\r
140     # extend1: english name\r
141     ############################################################\r
142     if loader.read_uint(1)==0:\r
143         print("no extend flag")\r
144         return True\r
145     model.english_name=loader.read_text(20)\r
146     model.english_comment=loader.read_text(256)\r
147     for bone in model.bones:\r
148         bone.english_name=loader.read_text(20)\r
149     for morph in model.morphs:\r
150         if morph.name==b'base':\r
151             continue\r
152         morph.english_name=loader.read_text(20)\r
153     for bone_group in model.bone_group_list:\r
154         bone_group=loader.read_text(50)\r
155 \r
156     ############################################################\r
157     # extend2: toon_textures\r
158     ############################################################\r
159     if loader.is_end():\r
160         # EOF\r
161         return True\r
162     model.toon_textures=[loader.read_text(100)\r
163             for _ in range(10)]\r
164 \r
165     ############################################################\r
166     # extend2: rigidbodies and joints\r
167     ############################################################\r
168     if loader.is_end():\r
169         # EOF\r
170         return True\r
171     model.rigidbodies=[loader.read_rigidbody()\r
172             for _ in range(loader.read_uint(4))]\r
173     model.joints=[loader.read_joint()\r
174             for _ in range(loader.read_uint(4))]\r
175 \r
176     return True\r
177 \r
178 \r
179 def load(path):\r
180     # general binary loader\r
181     #loader=pymeshio.common.BinaryLoader(open(path, 'rb'))\r
182     loader=pymeshio.common.BinaryLoader(io.BytesIO(pymeshio.common.readall(path)))\r
183 \r
184     # header\r
185     signature=loader.unpack("3s", 3)\r
186     if signature!=b"Pmd":\r
187         raise pymeshio.common.ParseException(\r
188                 "invalid signature: {0}".format(signature))\r
189     version=loader.read_float()\r
190 \r
191     model=pymeshio.pmd.Model(version)\r
192     loader=Loader(loader.io, version)\r
193     if(__load(loader, model)):\r
194         # check eof\r
195         if not loader.is_end():\r
196             print("can not reach eof.")\r
197 \r
198         # build bone tree\r
199         for i, child in enumerate(model.bones):\r
200             if child.parent_index==0xFFFF:\r
201                 # no parent\r
202                 model.no_parent_bones.append(child)\r
203                 child.parent=None\r
204             else:\r
205                 # has parent\r
206                 parent=model.bones[child.parent_index]\r
207                 child.parent=parent\r
208                 parent.children.append(child)\r
209             # \8cã\88Ê\92u\r
210             if child.hasChild():\r
211                 child.tail=model.bones[child.tail_index].pos\r
212 \r
213         return model\r
214 \r
215 \r
216 def save(self, path):\r
217     io=open(path, 'wb')\r
218     if not io:\r
219         return False\r
220     # Header\r
221     io.write(b"Pmd")\r
222     io.write(struct.pack("f", self.version))\r
223     io.write(struct.pack("20s", self.name))\r
224     io.write(struct.pack("256s", self.comment))\r
225 \r
226     # Vertices\r
227     io.write(struct.pack("I", len(self.vertices)))\r
228     sVertex=struct.Struct("=8f2H2B") # 38byte\r
229     assert(sVertex.size==38)\r
230     for v in self.vertices:\r
231         data=sVertex.pack( \r
232             v.pos[0], v.pos[1], v.pos[2],\r
233             v.normal[0], v.normal[1], v.normal[2],\r
234             v.uv[0], v.uv[1],\r
235             v.bone0, v.bone1, v.weight0, v.edge_flag)\r
236         io.write(data)\r
237 \r
238     # Faces\r
239     io.write(struct.pack("I", len(self.indices)))\r
240     io.write(struct.pack("=%dH" % len(self.indices), *self.indices))\r
241 \r
242     # material\r
243     io.write(struct.pack("I", len(self.materials)))\r
244     sMaterial=struct.Struct("=3fff3f3fBBI20s") # 70byte\r
245     assert(sMaterial.size==70)\r
246     for m in self.materials:\r
247         io.write(sMaterial.pack(\r
248             m.diffuse[0], m.diffuse[1], m.diffuse[2], m.diffuse[3],\r
249             m.shinness, \r
250             m.specular[0], m.specular[1], m.specular[2],\r
251             m.ambient[0], m.ambient[1], m.ambient[2],\r
252             m.toon_index, m.flag,\r
253             m.vertex_count,\r
254             m.texture\r
255             ))\r
256 \r
257     # bone\r
258     io.write(struct.pack("H", len(self.bones)))\r
259     sBone=struct.Struct("=20sHHBH3f")\r
260     assert(sBone.size==39)\r
261     for b in self.bones:\r
262         io.write(sBone.pack(\r
263             b.name,\r
264             b.parent_index, b.tail_index, b.type, b.ik_index,\r
265             b.pos[0], b.pos[1], b.pos[2]))\r
266 \r
267     # IK\r
268     io.write(struct.pack("H", len(self.ik_list)))\r
269     for ik in self.ik_list:\r
270         io.write(struct.pack("=2HBHf", \r
271             ik.index, ik.target, ik.length, ik.iterations, ik.weight\r
272             ))\r
273         for c in ik.children:\r
274             io.write(struct.pack("H", c))\r
275 \r
276     # skin\r
277     io.write(struct.pack("H", len(self.morph_list)))\r
278     for s in self.morph_list:\r
279         io.write(struct.pack("20sIB", \r
280             s.name, len(s.indices), s.type))\r
281         for i, v in zip(s.indices, s.pos_list):\r
282             io.write(struct.pack("I3f", i, v[0], v[1], v[2]))\r
283 \r
284     # skin disp list\r
285     io.write(struct.pack("B", len(self.face_list)))\r
286     for i in self.face_list:\r
287         io.write(struct.pack("H", i))\r
288 \r
289     # bone disp list\r
290     io.write(struct.pack("B", len(self.bone_group_list)))\r
291     for g in self.bone_group_list:\r
292         io.write(struct.pack("50s", g.name))\r
293 \r
294     io.write(struct.pack("I", len(self.bone_display_list)))\r
295     for l in self.bone_display_list:\r
296         io.write(struct.pack("=HB", *l))\r
297 \r
298     ############################################################\r
299     # extend data\r
300     ############################################################\r
301     io.write(struct.pack("B", 1))\r
302     # english name\r
303     io.write(struct.pack("=20s", self.english_name))\r
304     io.write(struct.pack("=256s", self.english_comment))\r
305     # english bone name\r
306     for bone in self.bones:\r
307         io.write(struct.pack("=20s", bone.english_name))\r
308     # english skin list\r
309     for skin in self.morph_list:\r
310         #print(skin.name)\r
311         if skin.name==b'base':\r
312             continue\r
313         io.write(struct.pack("=20s", skin.english_name))\r
314     # english bone list\r
315     for bone_group in self.bone_group_list:\r
316         io.write(struct.pack("50s", bone_group.english_name))\r
317     # toon texture\r
318     for toon_texture in self.toon_textures:\r
319         io.write(struct.pack("=100s", toon_texture))\r
320     # rigid\r
321     io.write(struct.pack("I", len(self.rigidbodies)))\r
322     for r in self.rigidbodies:\r
323         io.write(struct.pack("=20sHBHB14fB",\r
324             r.name, r.boneIndex, r.group, r.target, r.shapeType,\r
325             r.w, r.h, r.d, \r
326             r.position.x, r.position.y, r.position.z, \r
327             r.rotation.x, r.rotation.y, r.rotation.z, \r
328             r.weight,\r
329             r.linearDamping, r.angularDamping, r.restitution,\r
330             r.friction, r.processType))\r
331 \r
332     # constraint\r
333     io.write(struct.pack("I", len(self.constraints)))\r
334     for c in self.constraints:\r
335         io.write(struct.pack("=20sII24f",\r
336             c.name, c.rigidA, c.rigidB,\r
337             c.pos.x, c.pos.y, c.pos.z,\r
338             c.rot.x, c.rot.y, c.rot.z,\r
339             c.constraintPosMin.x, c.constraintPosMin.y, c.constraintPosMin.z,\r
340             c.constraintPosMax.x, c.constraintPosMax.y, c.constraintPosMax.z,\r
341             c.constraintRotMin.x, c.constraintRotMin.y, c.constraintRotMin.z,\r
342             c.constraintRotMax.x, c.constraintRotMax.y, c.constraintRotMax.z,\r
343             c.springPos.x, c.springPos.y, c.springPos.z,\r
344             c.springRot.x, c.springRot.y, c.springRot.z\r
345             ))\r
346 \r
347     return True\r
348 \r