pmdとmqoの入出力ライブラリと、それを使ったBlender2.5向けのaddon。
修訂 | 607dea923e3f6baa83031ae37e22bc2075ee71ff (tree) |
---|---|
時間 | 2011-10-15 16:26:32 |
作者 | ousttrue <ousttrue@gmai...> |
Commiter | ousttrue |
refactoring. separate exporter
@@ -35,7 +35,7 @@ This script exports a pmd model. | ||
35 | 35 | import io |
36 | 36 | |
37 | 37 | from . import bl |
38 | -from . import oneskinmesh | |
38 | +from . import exporter | |
39 | 39 | from .pymeshio import common |
40 | 40 | from .pymeshio import pmd |
41 | 41 | from .pymeshio import englishmap |
@@ -52,9 +52,18 @@ def toCP932(s): | ||
52 | 52 | |
53 | 53 | |
54 | 54 | def write(self, path): |
55 | - model=pmd.Model(1.0) | |
56 | - model.name=self.name.encode('cp932') | |
57 | - model.comment=self.comment.encode('cp932') | |
55 | + model=pmd.Model() | |
56 | + | |
57 | + o=self.root.o | |
58 | + englishName=o.name | |
59 | + name=o[bl.MMD_MB_NAME] if bl.MMD_MB_NAME in o else 'Blenderエクスポート' | |
60 | + comment=o[bl.MMD_MB_COMMENT] if bl.MMD_MB_COMMENT in o else 'Blnderエクスポート\n' | |
61 | + englishComment=o[bl.MMD_COMMENT] if bl.MMD_COMMENT in o else 'blender export\n' | |
62 | + | |
63 | + model.name=name.encode('cp932') | |
64 | + model.english_name=englishName.encode('cp932') | |
65 | + model.comment=comment.encode('cp932') | |
66 | + model.english_comment=englishComment.encode('cp932') | |
58 | 67 | |
59 | 68 | # 頂点 |
60 | 69 | model.vertices=[pmd.Vertex( |
@@ -221,10 +230,6 @@ def write(self, path): | ||
221 | 230 | continue |
222 | 231 | model.bone_display_list.append((i, self.skeleton.getBoneGroup(b))) |
223 | 232 | |
224 | - # English | |
225 | - model.english_name=self.englishName.encode('cp932') | |
226 | - model.english_comment=self.englishComment.encode('cp932') | |
227 | - | |
228 | 233 | # toon |
229 | 234 | toonMeshObject=None |
230 | 235 | for o in bl.object.each(): |
@@ -334,9 +339,9 @@ def write(self, path): | ||
334 | 339 | ) |
335 | 340 | for obj in self.oneSkinMesh.constraints] |
336 | 341 | |
337 | - # 書き込み | |
338 | 342 | bl.message('write: %s' % path) |
339 | - return writer.write(io.open(path, 'wb'), model) | |
343 | + with io.open(path, 'wb') as f: | |
344 | + return writer.write(f, model) | |
340 | 345 | |
341 | 346 | |
342 | 347 | def _execute(filepath=''): |
@@ -345,10 +350,11 @@ def _execute(filepath=''): | ||
345 | 350 | print("abort. no active object.") |
346 | 351 | return |
347 | 352 | |
348 | - exporter=oneskinmesh.Exporter() | |
349 | - exporter.setup() | |
350 | - print(exporter) | |
353 | + ex=exporter.Exporter() | |
354 | + ex.setup() | |
355 | + print(ex) | |
351 | 356 | |
352 | - write(exporter, filepath) | |
357 | + write(ex, filepath) | |
353 | 358 | bl.object.activate(active) |
359 | + return {'FINISHED'} | |
354 | 360 |
@@ -1,18 +1,314 @@ | ||
1 | 1 | # coding: utf-8 |
2 | 2 | |
3 | +import io | |
3 | 4 | from . import bl |
5 | +from . import oneskinmesh | |
6 | +from .pymeshio import pmx | |
7 | +from .pymeshio import common | |
8 | +from .pymeshio.pmx import writer | |
9 | + | |
10 | + | |
11 | +def create_pmx(ex): | |
12 | + model=pmx.Model() | |
13 | + model.name=ex.name | |
14 | + model.comment=ex.comment | |
15 | + | |
16 | + def get_deform(b0, b1, weight): | |
17 | + print(b0, b1, weight) | |
18 | + if b0==-1: | |
19 | + return pmx.Bdef1(b1, weight) | |
20 | + elif b1==-1: | |
21 | + return pmx.Bdef1(b0, weight) | |
22 | + else: | |
23 | + return pmx.Bdef2(b0, b1, weight) | |
24 | + | |
25 | + model.vertices=[pmx.Vertex( | |
26 | + # convert right-handed z-up to left-handed y-up | |
27 | + common.Vector3(pos[0], pos[2], pos[1]), | |
28 | + # convert right-handed z-up to left-handed y-up | |
29 | + common.Vector3(attribute.nx, attribute.nz, attribute.ny), | |
30 | + # reverse vertical | |
31 | + common.Vector2(attribute.u, 1.0-attribute.v), | |
32 | + get_deform(ex.skeleton.indexByName(b0), ex.skeleton.indexByName(b1), weight), | |
33 | + # edge flag, 0: enable edge, 1: not edge | |
34 | + 1.0 | |
35 | + ) | |
36 | + for pos, attribute, b0, b1, weight in ex.oneSkinMesh.vertexArray.zip()] | |
37 | + | |
38 | + # bones | |
39 | + boneNameMap={} | |
40 | + for i, b in enumerate(self.skeleton.bones): | |
41 | + | |
42 | + # name | |
43 | + boneNameMap[b.name]=i | |
44 | + v=englishmap.getUnicodeBoneName(b.name) | |
45 | + if not v: | |
46 | + v=[b.name, b.name] | |
47 | + assert(v) | |
48 | + bone=pmx.Bone(v[1]) | |
49 | + bone.english_name=b.name | |
50 | + | |
51 | + if len(v)>=3: | |
52 | + # has type | |
53 | + if v[2]==5: | |
54 | + b.ik_index=self.skeleton.indexByName('eyes') | |
55 | + bone.type=v[2] | |
56 | + else: | |
57 | + bone.type=b.type | |
58 | + | |
59 | + bone.parent_index=b.parent_index | |
60 | + bone.tail_index=b.tail_index | |
61 | + bone.ik_index=b.ik_index | |
62 | + | |
63 | + # convert right-handed z-up to left-handed y-up | |
64 | + bone.pos.x=b.pos[0] if not near(b.pos[0], 0) else 0 | |
65 | + bone.pos.y=b.pos[2] if not near(b.pos[2], 0) else 0 | |
66 | + bone.pos.z=b.pos[1] if not near(b.pos[1], 0) else 0 | |
67 | + | |
68 | + model.bones.append(bone) | |
69 | + return model | |
70 | + | |
71 | + # IK | |
72 | + for ik in self.skeleton.ik_list: | |
73 | + solver=pmd.IK() | |
74 | + solver.index=self.skeleton.getIndex(ik.target) | |
75 | + solver.target=self.skeleton.getIndex(ik.effector) | |
76 | + solver.length=ik.length | |
77 | + b=self.skeleton.bones[ik.effector.parent_index] | |
78 | + for i in range(solver.length): | |
79 | + solver.children.append(self.skeleton.getIndex(b)) | |
80 | + b=self.skeleton.bones[b.parent_index] | |
81 | + solver.iterations=ik.iterations | |
82 | + solver.weight=ik.weight | |
83 | + model.ik_list.append(solver) | |
84 | + | |
85 | + # 面とマテリアル | |
86 | + vertexCount=self.oneSkinMesh.getVertexCount() | |
87 | + for material_name, indices in self.oneSkinMesh.vertexArray.each(): | |
88 | + #print('material:', material_name) | |
89 | + try: | |
90 | + m=bl.material.get(material_name) | |
91 | + except KeyError as e: | |
92 | + m=DefaultMatrial() | |
93 | + def get_texture_name(texture): | |
94 | + pos=texture.replace("\\", "/").rfind("/") | |
95 | + if pos==-1: | |
96 | + return texture | |
97 | + else: | |
98 | + return texture[pos+1:] | |
99 | + textures=[get_texture_name(path) | |
100 | + for path in bl.material.eachEnalbeTexturePath(m)] | |
101 | + print(textures) | |
102 | + # マテリアル | |
103 | + model.materials.append(pmd.Material( | |
104 | + # diffuse_color | |
105 | + common.RGB(m.diffuse_color[0], m.diffuse_color[1], m.diffuse_color[2]), | |
106 | + m.alpha, | |
107 | + # specular_factor | |
108 | + 0 if m.specular_toon_size<1e-5 else m.specular_hardness*10, | |
109 | + # specular_color | |
110 | + common.RGB(m.specular_color[0], m.specular_color[1], m.specular_color[2]), | |
111 | + # ambient_color | |
112 | + common.RGB(m.mirror_color[0], m.mirror_color[1], m.mirror_color[2]), | |
113 | + # flag | |
114 | + 1 if m.subsurface_scattering.use else 0, | |
115 | + # toon | |
116 | + 0, | |
117 | + # vertex_count | |
118 | + len(indices), | |
119 | + # texture | |
120 | + ('*'.join(textures) if len(textures)>0 else "").encode('cp932') | |
121 | + )) | |
122 | + # 面 | |
123 | + for i in indices: | |
124 | + assert(i<vertexCount) | |
125 | + for i in range(0, len(indices), 3): | |
126 | + # reverse triangle | |
127 | + model.indices.append(indices[i]) | |
128 | + model.indices.append(indices[i+1]) | |
129 | + model.indices.append(indices[i+2]) | |
130 | + | |
131 | + # 表情 | |
132 | + for i, m in enumerate(self.oneSkinMesh.morphList): | |
133 | + v=englishmap.getUnicodeSkinName(m.name) | |
134 | + if not v: | |
135 | + v=[m.name, m.name, 0] | |
136 | + assert(v) | |
137 | + # morph | |
138 | + morph=pmd.Morph(v[1].encode("cp932")) | |
139 | + morph.english_name=m.name.encode("cp932") | |
140 | + m.type=v[2] | |
141 | + morph.type=v[2] | |
142 | + for index, offset in m.offsets: | |
143 | + # convert right-handed z-up to left-handed y-up | |
144 | + morph.append(index, offset[0], offset[2], offset[1]) | |
145 | + morph.vertex_count=len(m.offsets) | |
146 | + | |
147 | + # 表情枠 | |
148 | + # type==0はbase | |
149 | + for i, m in enumerate(self.oneSkinMesh.morphList): | |
150 | + if m.type==3: | |
151 | + model.morph_indices.append(i) | |
152 | + for i, m in enumerate(self.oneSkinMesh.morphList): | |
153 | + if m.type==2: | |
154 | + model.morph_indices.append(i) | |
155 | + for i, m in enumerate(self.oneSkinMesh.morphList): | |
156 | + if m.type==1: | |
157 | + model.morph_indices.append(i) | |
158 | + for i, m in enumerate(self.oneSkinMesh.morphList): | |
159 | + if m.type==4: | |
160 | + model.morph_indices.append(i) | |
161 | + | |
162 | + # ボーングループ | |
163 | + for g in self.skeleton.bone_groups: | |
164 | + name=englishmap.getUnicodeBoneGroupName(g[0]) | |
165 | + if not name: | |
166 | + name=g[0] | |
167 | + englishName=g[0] | |
168 | + | |
169 | + model.bone_group_list.append(pmd.BoneGroup( | |
170 | + (name+'\n').encode('cp932'), | |
171 | + (englishName+'\n').encode('cp932') | |
172 | + )) | |
173 | + | |
174 | + # ボーングループメンバー | |
175 | + for i, b in enumerate(self.skeleton.bones): | |
176 | + if i==0: | |
177 | + continue | |
178 | + if b.type in [6, 7]: | |
179 | + continue | |
180 | + model.bone_display_list.append((i, self.skeleton.getBoneGroup(b))) | |
181 | + | |
182 | + # English | |
183 | + model.english_name=self.englishName.encode('cp932') | |
184 | + model.english_comment=self.englishComment.encode('cp932') | |
185 | + | |
186 | + # toon | |
187 | + toonMeshObject=None | |
188 | + for o in bl.object.each(): | |
189 | + try: | |
190 | + if o.name.startswith(bl.TOON_TEXTURE_OBJECT): | |
191 | + toonMeshObject=o | |
192 | + except: | |
193 | + p(o.name) | |
194 | + break | |
195 | + if toonMeshObject: | |
196 | + toonMesh=bl.object.getData(toonMeshObject) | |
197 | + toonMaterial=bl.mesh.getMaterial(toonMesh, 0) | |
198 | + for i in range(10): | |
199 | + t=bl.material.getTexture(toonMaterial, i) | |
200 | + if t: | |
201 | + model.toon_textures[i]=("%s" % t.name).encode('cp932') | |
202 | + else: | |
203 | + model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932') | |
204 | + else: | |
205 | + for i in range(10): | |
206 | + model.toon_textures[i]=("toon%02d.bmp" % (i+1)).encode('cp932') | |
207 | + | |
208 | + # rigid body | |
209 | + rigidNameMap={} | |
210 | + for i, obj in enumerate(self.oneSkinMesh.rigidbodies): | |
211 | + name=obj[bl.RIGID_NAME] if bl.RIGID_NAME in obj else obj.name | |
212 | + print(name) | |
213 | + rigidNameMap[name]=i | |
214 | + boneIndex=boneNameMap[obj[bl.RIGID_BONE_NAME]] | |
215 | + if boneIndex==0: | |
216 | + boneIndex=-1 | |
217 | + bone=self.skeleton.bones[0] | |
218 | + else: | |
219 | + bone=self.skeleton.bones[boneIndex] | |
220 | + if obj[bl.RIGID_SHAPE_TYPE]==0: | |
221 | + shape_type=pmd.SHAPE_SPHERE | |
222 | + shape_size=common.Vector3(obj.scale[0], 0, 0) | |
223 | + elif obj[bl.RIGID_SHAPE_TYPE]==1: | |
224 | + shape_type=pmd.SHAPE_BOX | |
225 | + shape_size=common.Vector3(obj.scale[0], obj.scale[1], obj.scale[2]) | |
226 | + elif obj[bl.RIGID_SHAPE_TYPE]==2: | |
227 | + shape_type=pmd.SHAPE_CAPSULE | |
228 | + shape_size=common.Vector3(obj.scale[0], obj.scale[2], 0) | |
229 | + rigidBody=pmd.RigidBody( | |
230 | + name.encode('cp932'), | |
231 | + collision_group=obj[bl.RIGID_GROUP], | |
232 | + no_collision_group=obj[bl.RIGID_INTERSECTION_GROUP], | |
233 | + bone_index=boneIndex, | |
234 | + shape_position=common.Vector3( | |
235 | + obj.location.x-bone.pos[0], | |
236 | + obj.location.z-bone.pos[2], | |
237 | + obj.location.y-bone.pos[1]), | |
238 | + shape_rotation=common.Vector3( | |
239 | + -obj.rotation_euler[0], | |
240 | + -obj.rotation_euler[2], | |
241 | + -obj.rotation_euler[1]), | |
242 | + shape_type=shape_type, | |
243 | + shape_size=shape_size, | |
244 | + mass=obj[bl.RIGID_WEIGHT], | |
245 | + linear_damping=obj[bl.RIGID_LINEAR_DAMPING], | |
246 | + angular_damping=obj[bl.RIGID_ANGULAR_DAMPING], | |
247 | + restitution=obj[bl.RIGID_RESTITUTION], | |
248 | + friction=obj[bl.RIGID_FRICTION], | |
249 | + mode=obj[bl.RIGID_PROCESS_TYPE] | |
250 | + ) | |
251 | + model.rigidbodies.append(rigidBody) | |
252 | + | |
253 | + # constraint | |
254 | + model.joints=[pmd.Joint( | |
255 | + name=obj[bl.CONSTRAINT_NAME].encode('cp932'), | |
256 | + rigidbody_index_a=rigidNameMap[obj[bl.CONSTRAINT_A]], | |
257 | + rigidbody_index_b=rigidNameMap[obj[bl.CONSTRAINT_B]], | |
258 | + position=common.Vector3( | |
259 | + obj.location[0], | |
260 | + obj.location[2], | |
261 | + obj.location[1]), | |
262 | + rotation=common.Vector3( | |
263 | + -obj.rotation_euler[0], | |
264 | + -obj.rotation_euler[2], | |
265 | + -obj.rotation_euler[1]), | |
266 | + translation_limit_min=common.Vector3( | |
267 | + obj[bl.CONSTRAINT_POS_MIN][0], | |
268 | + obj[bl.CONSTRAINT_POS_MIN][1], | |
269 | + obj[bl.CONSTRAINT_POS_MIN][2] | |
270 | + ), | |
271 | + translation_limit_max=common.Vector3( | |
272 | + obj[bl.CONSTRAINT_POS_MAX][0], | |
273 | + obj[bl.CONSTRAINT_POS_MAX][1], | |
274 | + obj[bl.CONSTRAINT_POS_MAX][2] | |
275 | + ), | |
276 | + rotation_limit_min=common.Vector3( | |
277 | + obj[bl.CONSTRAINT_ROT_MIN][0], | |
278 | + obj[bl.CONSTRAINT_ROT_MIN][1], | |
279 | + obj[bl.CONSTRAINT_ROT_MIN][2]), | |
280 | + rotation_limit_max=common.Vector3( | |
281 | + obj[bl.CONSTRAINT_ROT_MAX][0], | |
282 | + obj[bl.CONSTRAINT_ROT_MAX][1], | |
283 | + obj[bl.CONSTRAINT_ROT_MAX][2]), | |
284 | + spring_constant_translation=common.Vector3( | |
285 | + obj[bl.CONSTRAINT_SPRING_POS][0], | |
286 | + obj[bl.CONSTRAINT_SPRING_POS][1], | |
287 | + obj[bl.CONSTRAINT_SPRING_POS][2]), | |
288 | + spring_constant_rotation=common.Vector3( | |
289 | + obj[bl.CONSTRAINT_SPRING_ROT][0], | |
290 | + obj[bl.CONSTRAINT_SPRING_ROT][1], | |
291 | + obj[bl.CONSTRAINT_SPRING_ROT][2]) | |
292 | + ) | |
293 | + for obj in self.oneSkinMesh.constraints] | |
294 | + | |
295 | + # 書き込み | |
296 | + bl.message('write: %s' % path) | |
297 | + return writer.write(io.open(path, 'wb'), model) | |
4 | 298 | |
5 | 299 | |
6 | 300 | def _execute(filepath): |
7 | - print(filepath) | |
8 | 301 | active=bl.object.getActive() |
9 | 302 | if not active: |
10 | 303 | print("abort. no active object.") |
11 | 304 | return |
12 | - exporter=PmdExporter() | |
305 | + | |
306 | + exporter=oneskinmesh.Exporter() | |
13 | 307 | exporter.setup() |
14 | - print(exporter) | |
15 | - exporter.write(filepath) | |
308 | + | |
309 | + model=create_pmx(exporter) | |
16 | 310 | bl.object.activate(active) |
311 | + with io.open(filepath, 'wb') as f: | |
312 | + writer.write(f, model) | |
17 | 313 | return {'FINISHED'} |
18 | 314 |
@@ -0,0 +1,51 @@ | ||
1 | +# coding: utf-8 | |
2 | +""" | |
3 | +Blenderのメッシュをワンスキンメッシュ化する | |
4 | +""" | |
5 | +from .. import bl | |
6 | +from . import oneskinmesh | |
7 | +from . import bonebuilder | |
8 | + | |
9 | + | |
10 | +class ObjectNode(object): | |
11 | + ''' | |
12 | + Objectの木構造構築 | |
13 | + ''' | |
14 | + __slots__=['o', 'children'] | |
15 | + def __init__(self, o): | |
16 | + self.o=o | |
17 | + self.children=[] | |
18 | + | |
19 | + | |
20 | +class Exporter(object): | |
21 | + ''' | |
22 | + Blenderから情報収集する | |
23 | + ''' | |
24 | + __slots__=[ | |
25 | + 'oneSkinMesh', | |
26 | + 'skeleton', | |
27 | + 'root', | |
28 | + ] | |
29 | + def setup(self): | |
30 | + # scene内のオブジェクトの木構造を構築する | |
31 | + object_node_map={} | |
32 | + for o in bl.object.each(): | |
33 | + object_node_map[o]=ObjectNode(o) | |
34 | + for o in bl.object.each(): | |
35 | + node=object_node_map[o] | |
36 | + if node.o.parent: | |
37 | + object_node_map[node.o.parent].children.append(node) | |
38 | + self.root=object_node_map[bl.object.getActive()] | |
39 | + | |
40 | + # ワンスキンメッシュを作る | |
41 | + self.oneSkinMesh=oneskinmesh.OneSkinMesh() | |
42 | + self.oneSkinMesh.build(self.root) | |
43 | + bl.message(self.oneSkinMesh) | |
44 | + if len(self.oneSkinMesh.morphList)==0: | |
45 | + # create emtpy skin | |
46 | + self.oneSkinMesh.createEmptyBasicSkin() | |
47 | + | |
48 | + # skeleton | |
49 | + self.skeleton=bonebuilder.BoneBuilder() | |
50 | + self.skeleton.build(self.oneSkinMesh.armatureObj) | |
51 | + |
@@ -0,0 +1,238 @@ | ||
1 | +# coding: utf-8 | |
2 | +from .. import bl | |
3 | +from ..pymeshio import englishmap | |
4 | + | |
5 | + | |
6 | +class IKSolver(object): | |
7 | + __slots__=['target', 'effector', 'length', 'iterations', 'weight'] | |
8 | + def __init__(self, target, effector, length, iterations, weight): | |
9 | + self.target=target | |
10 | + self.effector=effector | |
11 | + self.length=length | |
12 | + self.iterations=iterations | |
13 | + self.weight=weight | |
14 | + | |
15 | + | |
16 | +class Bone(object): | |
17 | + __slots__=['index', 'name', 'ik_index', | |
18 | + 'pos', 'tail', 'parent_index', 'tail_index', 'type', 'isConnect'] | |
19 | + def __init__(self, name, pos, tail, isConnect): | |
20 | + self.index=-1 | |
21 | + self.name=name | |
22 | + self.pos=pos | |
23 | + self.tail=tail | |
24 | + self.parent_index=None | |
25 | + self.tail_index=None | |
26 | + self.type=0 | |
27 | + self.isConnect=isConnect | |
28 | + self.ik_index=0 | |
29 | + | |
30 | + def __eq__(self, rhs): | |
31 | + return self.index==rhs.index | |
32 | + | |
33 | + def __str__(self): | |
34 | + return "<Bone %s %d>" % (self.name, self.type) | |
35 | + | |
36 | + | |
37 | +class BoneBuilder(object): | |
38 | + __slots__=['bones', 'boneMap', 'ik_list', 'bone_groups',] | |
39 | + def __init__(self): | |
40 | + self.bones=[] | |
41 | + self.boneMap={} | |
42 | + self.ik_list=[] | |
43 | + self.bone_groups=[] | |
44 | + | |
45 | + def getBoneGroup(self, bone): | |
46 | + for i, g in enumerate(self.bone_groups): | |
47 | + for b in g[1]: | |
48 | + if b==bone.name: | |
49 | + return i+1 | |
50 | + print('no gorup', bone) | |
51 | + return 0 | |
52 | + | |
53 | + def build(self, armatureObj): | |
54 | + if not armatureObj: | |
55 | + return | |
56 | + | |
57 | + bl.message("build skeleton") | |
58 | + armature=bl.object.getData(armatureObj) | |
59 | + | |
60 | + #################### | |
61 | + # bone group | |
62 | + #################### | |
63 | + for g in bl.object.boneGroups(armatureObj): | |
64 | + self.bone_groups.append((g.name, [])) | |
65 | + | |
66 | + #################### | |
67 | + # get bones | |
68 | + #################### | |
69 | + for b in armature.bones.values(): | |
70 | + if not b.parent: | |
71 | + # root bone | |
72 | + bone=Bone(b.name, | |
73 | + bl.bone.getHeadLocal(b), | |
74 | + bl.bone.getTailLocal(b), | |
75 | + False) | |
76 | + self.__addBone(bone) | |
77 | + self.__getBone(bone, b) | |
78 | + | |
79 | + for b in armature.bones.values(): | |
80 | + if not b.parent: | |
81 | + self.__checkConnection(b, None) | |
82 | + | |
83 | + #################### | |
84 | + # get IK | |
85 | + #################### | |
86 | + pose = bl.object.getPose(armatureObj) | |
87 | + for b in pose.bones.values(): | |
88 | + #################### | |
89 | + # assing bone group | |
90 | + #################### | |
91 | + self.__assignBoneGroup(b, b.bone_group) | |
92 | + for c in b.constraints: | |
93 | + if bl.constraint.isIKSolver(c): | |
94 | + #################### | |
95 | + # IK target | |
96 | + #################### | |
97 | + target=self.__boneByName(bl.constraint.ikTarget(c)) | |
98 | + target.type=2 | |
99 | + | |
100 | + #################### | |
101 | + # IK effector | |
102 | + #################### | |
103 | + # IK 接続先 | |
104 | + link=self.__boneByName(b.name) | |
105 | + link.type=6 | |
106 | + | |
107 | + # IK chain | |
108 | + e=b.parent | |
109 | + chainLength=bl.constraint.ikChainLen(c) | |
110 | + for i in range(chainLength): | |
111 | + # IK影響下 | |
112 | + chainBone=self.__boneByName(e.name) | |
113 | + chainBone.type=4 | |
114 | + chainBone.ik_index=target.index | |
115 | + e=e.parent | |
116 | + self.ik_list.append( | |
117 | + IKSolver(target, link, chainLength, | |
118 | + int(bl.constraint.ikItration(c) * 0.1), | |
119 | + bl.constraint.ikRotationWeight(c) | |
120 | + )) | |
121 | + | |
122 | + #################### | |
123 | + | |
124 | + # boneのsort | |
125 | + self._sortBy() | |
126 | + self._fix() | |
127 | + # IKのsort | |
128 | + def getIndex(ik): | |
129 | + for i, v in enumerate(englishmap.boneMap): | |
130 | + if v[0]==ik.target.name: | |
131 | + return i | |
132 | + return len(englishmap.boneMap) | |
133 | + self.ik_list.sort(key=getIndex) | |
134 | + | |
135 | + def __assignBoneGroup(self, poseBone, boneGroup): | |
136 | + if boneGroup: | |
137 | + for g in self.bone_groups: | |
138 | + if g[0]==boneGroup.name: | |
139 | + g[1].append(poseBone.name) | |
140 | + | |
141 | + def __checkConnection(self, b, p): | |
142 | + if bl.bone.isConnected(b): | |
143 | + parent=self.__boneByName(p.name) | |
144 | + parent.isConnect=True | |
145 | + | |
146 | + for c in b.children: | |
147 | + self.__checkConnection(c, b) | |
148 | + | |
149 | + def _sortBy(self): | |
150 | + """ | |
151 | + boneMap順に並べ替える | |
152 | + """ | |
153 | + boneMap=englishmap.boneMap | |
154 | + original=self.bones[:] | |
155 | + def getIndex(bone): | |
156 | + for i, k_v in enumerate(boneMap): | |
157 | + if k_v[0]==bone.name: | |
158 | + return i | |
159 | + print(bone) | |
160 | + return len(boneMap) | |
161 | + | |
162 | + self.bones.sort(key=getIndex) | |
163 | + | |
164 | + sortMap={} | |
165 | + for i, b in enumerate(self.bones): | |
166 | + src=original.index(b) | |
167 | + sortMap[src]=i | |
168 | + for b in self.bones: | |
169 | + b.index=sortMap[b.index] | |
170 | + if b.parent_index: | |
171 | + b.parent_index=sortMap[b.parent_index] | |
172 | + if b.tail_index: | |
173 | + b.tail_index=sortMap[b.tail_index] | |
174 | + if b.ik_index>0: | |
175 | + b.ik_index=sortMap[b.ik_index] | |
176 | + | |
177 | + def _fix(self): | |
178 | + """ | |
179 | + 調整 | |
180 | + """ | |
181 | + for b in self.bones: | |
182 | + # parent index | |
183 | + if b.parent_index==None: | |
184 | + b.parent_index=0xFFFF | |
185 | + else: | |
186 | + if b.type==6 or b.type==7: | |
187 | + # fix tail bone | |
188 | + parent=self.bones[b.parent_index] | |
189 | + #print('parnet', parent.name) | |
190 | + parent.tail_index=b.index | |
191 | + | |
192 | + for b in self.bones: | |
193 | + if b.tail_index==None: | |
194 | + b.tail_index=0 | |
195 | + elif b.type==9: | |
196 | + b.tail_index==0 | |
197 | + | |
198 | + def getIndex(self, bone): | |
199 | + for i, b in enumerate(self.bones): | |
200 | + if b==bone: | |
201 | + return i | |
202 | + assert(false) | |
203 | + | |
204 | + def indexByName(self, name): | |
205 | + if name=='': | |
206 | + return 0 | |
207 | + else: | |
208 | + try: | |
209 | + return self.getIndex(self.__boneByName(name)) | |
210 | + except: | |
211 | + return 0 | |
212 | + | |
213 | + def __boneByName(self, name): | |
214 | + return self.boneMap[name] | |
215 | + | |
216 | + def __getBone(self, parent, b): | |
217 | + if len(b.children)==0: | |
218 | + parent.type=7 | |
219 | + return | |
220 | + | |
221 | + for i, c in enumerate(b.children): | |
222 | + bone=Bone(c.name, | |
223 | + bl.bone.getHeadLocal(c), | |
224 | + bl.bone.getTailLocal(c), | |
225 | + bl.bone.isConnected(c)) | |
226 | + self.__addBone(bone) | |
227 | + if parent: | |
228 | + bone.parent_index=parent.index | |
229 | + #if i==0: | |
230 | + if bone.isConnect or (not parent.tail_index and parent.tail==bone.pos): | |
231 | + parent.tail_index=bone.index | |
232 | + self.__getBone(bone, c) | |
233 | + | |
234 | + def __addBone(self, bone): | |
235 | + bone.index=len(self.bones) | |
236 | + self.bones.append(bone) | |
237 | + self.boneMap[bone.name]=bone | |
238 | + |
@@ -0,0 +1,378 @@ | ||
1 | +# coding: utf-8 | |
2 | +import bpy | |
3 | +from . import vertexarray | |
4 | +from .. import bl | |
5 | +from ..pymeshio import englishmap | |
6 | + | |
7 | + | |
8 | +class Morph(object): | |
9 | + __slots__=['name', 'type', 'offsets'] | |
10 | + def __init__(self, name, type): | |
11 | + self.name=name | |
12 | + self.type=type | |
13 | + self.offsets=[] | |
14 | + | |
15 | + def add(self, index, offset): | |
16 | + self.offsets.append((index, offset)) | |
17 | + | |
18 | + def sort(self): | |
19 | + self.offsets.sort(key=lambda e: e[0]) | |
20 | + | |
21 | + def __str__(self): | |
22 | + return "<Morph %s>" % self.name | |
23 | + | |
24 | + | |
25 | +class SSS(object): | |
26 | + def __init__(self): | |
27 | + self.use=1 | |
28 | + | |
29 | + | |
30 | +class DefaultMatrial(object): | |
31 | + def __init__(self): | |
32 | + self.name='default' | |
33 | + # diffuse | |
34 | + self.diffuse_color=[1, 1, 1] | |
35 | + self.alpha=1 | |
36 | + # specular | |
37 | + self.specular_toon_size=0 | |
38 | + self.specular_hardness=5 | |
39 | + self.specular_color=[1, 1, 1] | |
40 | + # ambient | |
41 | + self.mirror_color=[1, 1, 1] | |
42 | + # flag | |
43 | + self.subsurface_scattering=SSS() | |
44 | + # texture | |
45 | + self.texture_slots=[] | |
46 | + | |
47 | + | |
48 | +class OneSkinMesh(object): | |
49 | + __slots__=['vertexArray', 'morphList', 'rigidbodies', 'constraints', 'armatureObj'] | |
50 | + def __init__(self): | |
51 | + self.vertexArray=vertexarray.VertexArray() | |
52 | + self.morphList=[] | |
53 | + self.rigidbodies=[] | |
54 | + self.constraints=[] | |
55 | + self.armatureObj=None | |
56 | + | |
57 | + def __str__(self): | |
58 | + return "<OneSkinMesh %s, morph:%d>" % ( | |
59 | + self.vertexArray, | |
60 | + len(self.morphList)) | |
61 | + | |
62 | + def build(self, node): | |
63 | + ############################################################ | |
64 | + # search armature modifier | |
65 | + ############################################################ | |
66 | + for m in node.o.modifiers: | |
67 | + if bl.modifier.isType(m, 'ARMATURE'): | |
68 | + armatureObj=bl.modifier.getArmatureObject(m) | |
69 | + if not self.armatureObj: | |
70 | + self.armatureObj=armatureObj | |
71 | + elif self.armatureObj!=armatureObj: | |
72 | + print("warning! found multiple armature. ignored.", | |
73 | + armatureObj.name) | |
74 | + | |
75 | + if node.o.type.upper()=='MESH': | |
76 | + self.addMesh(node.o) | |
77 | + | |
78 | + for child in node.children: | |
79 | + self.build(child) | |
80 | + | |
81 | + def addMesh(self, obj): | |
82 | + if not bl.object.isVisible(obj): | |
83 | + return | |
84 | + self.__mesh(obj) | |
85 | + self.__skin(obj) | |
86 | + self.__rigidbody(obj) | |
87 | + self.__constraint(obj) | |
88 | + | |
89 | + def __getWeightMap(self, obj, mesh): | |
90 | + # bone weight | |
91 | + weightMap={} | |
92 | + secondWeightMap={} | |
93 | + def setWeight(i, name, w): | |
94 | + if w>0: | |
95 | + if i in weightMap: | |
96 | + if i in secondWeightMap: | |
97 | + # 上位2つのweightを採用する | |
98 | + if w<secondWeightMap[i][1]: | |
99 | + pass | |
100 | + elif w<weightMap[i][1]: | |
101 | + # 2つ目を入れ替え | |
102 | + secondWeightMap[i]=(name, w) | |
103 | + else: | |
104 | + # 1つ目を入れ替え | |
105 | + weightMap[i]=(name, w) | |
106 | + else: | |
107 | + if w>weightMap[i][1]: | |
108 | + # 多い方をweightMapに | |
109 | + secondWeightMap[i]=weightMap[i] | |
110 | + weightMap[i]=(name, w) | |
111 | + else: | |
112 | + secondWeightMap[i]=(name, w) | |
113 | + else: | |
114 | + weightMap[i]=(name, w) | |
115 | + | |
116 | + # ToDo bone weightと関係ないvertex groupを除外する | |
117 | + for i, v in enumerate(mesh.vertices): | |
118 | + if len(v.groups)>0: | |
119 | + for g in v.groups: | |
120 | + setWeight(i, obj.vertex_groups[g.group].name, g.weight) | |
121 | + else: | |
122 | + try: | |
123 | + setWeight(i, obj.vertex_groups[0].name, 1) | |
124 | + except: | |
125 | + # no vertex_groups | |
126 | + pass | |
127 | + | |
128 | + # 合計値が1になるようにする | |
129 | + for i in range(len(mesh.vertices)): | |
130 | + if i in secondWeightMap: | |
131 | + secondWeightMap[i]=(secondWeightMap[i][0], 1.0-weightMap[i][1]) | |
132 | + elif i in weightMap: | |
133 | + weightMap[i]=(weightMap[i][0], 1.0) | |
134 | + secondWeightMap[i]=("", 0) | |
135 | + else: | |
136 | + print("no weight vertex") | |
137 | + weightMap[i]=("", 0) | |
138 | + secondWeightMap[i]=("", 0) | |
139 | + | |
140 | + return weightMap, secondWeightMap | |
141 | + | |
142 | + def __processFaces(self, obj_name, mesh, weightMap, secondWeightMap): | |
143 | + default_material=DefaultMatrial() | |
144 | + # 各面の処理 | |
145 | + for i, face in enumerate(mesh.faces): | |
146 | + faceVertexCount=bl.face.getVertexCount(face) | |
147 | + try: | |
148 | + material=mesh.materials[bl.face.getMaterialIndex(face)] | |
149 | + except IndexError as e: | |
150 | + material=default_material | |
151 | + v=[mesh.vertices[index] for index in bl.face.getVertices(face)] | |
152 | + uv=bl.mesh.getFaceUV( | |
153 | + mesh, i, face, bl.face.getVertexCount(face)) | |
154 | + # flip triangle | |
155 | + if faceVertexCount==3: | |
156 | + # triangle | |
157 | + self.vertexArray.addTriangle( | |
158 | + obj_name, material.name, | |
159 | + v[2].index, | |
160 | + v[1].index, | |
161 | + v[0].index, | |
162 | + v[2].co, | |
163 | + v[1].co, | |
164 | + v[0].co, | |
165 | + bl.vertex.getNormal(v[2]), | |
166 | + bl.vertex.getNormal(v[1]), | |
167 | + bl.vertex.getNormal(v[0]), | |
168 | + uv[2], | |
169 | + uv[1], | |
170 | + uv[0], | |
171 | + weightMap[v[2].index][0], | |
172 | + weightMap[v[1].index][0], | |
173 | + weightMap[v[0].index][0], | |
174 | + secondWeightMap[v[2].index][0], | |
175 | + secondWeightMap[v[1].index][0], | |
176 | + secondWeightMap[v[0].index][0], | |
177 | + weightMap[v[2].index][1], | |
178 | + weightMap[v[1].index][1], | |
179 | + weightMap[v[0].index][1] | |
180 | + ) | |
181 | + elif faceVertexCount==4: | |
182 | + # quadrangle | |
183 | + self.vertexArray.addTriangle( | |
184 | + obj_name, material.name, | |
185 | + v[2].index, | |
186 | + v[1].index, | |
187 | + v[0].index, | |
188 | + v[2].co, | |
189 | + v[1].co, | |
190 | + v[0].co, | |
191 | + bl.vertex.getNormal(v[2]), | |
192 | + bl.vertex.getNormal(v[1]), | |
193 | + bl.vertex.getNormal(v[0]), | |
194 | + uv[2], | |
195 | + uv[1], | |
196 | + uv[0], | |
197 | + weightMap[v[2].index][0], | |
198 | + weightMap[v[1].index][0], | |
199 | + weightMap[v[0].index][0], | |
200 | + secondWeightMap[v[2].index][0], | |
201 | + secondWeightMap[v[1].index][0], | |
202 | + secondWeightMap[v[0].index][0], | |
203 | + weightMap[v[2].index][1], | |
204 | + weightMap[v[1].index][1], | |
205 | + weightMap[v[0].index][1] | |
206 | + ) | |
207 | + self.vertexArray.addTriangle( | |
208 | + obj_name, material.name, | |
209 | + v[0].index, | |
210 | + v[3].index, | |
211 | + v[2].index, | |
212 | + v[0].co, | |
213 | + v[3].co, | |
214 | + v[2].co, | |
215 | + bl.vertex.getNormal(v[0]), | |
216 | + bl.vertex.getNormal(v[3]), | |
217 | + bl.vertex.getNormal(v[2]), | |
218 | + uv[0], | |
219 | + uv[3], | |
220 | + uv[2], | |
221 | + weightMap[v[0].index][0], | |
222 | + weightMap[v[3].index][0], | |
223 | + weightMap[v[2].index][0], | |
224 | + secondWeightMap[v[0].index][0], | |
225 | + secondWeightMap[v[3].index][0], | |
226 | + secondWeightMap[v[2].index][0], | |
227 | + weightMap[v[0].index][1], | |
228 | + weightMap[v[3].index][1], | |
229 | + weightMap[v[2].index][1] | |
230 | + ) | |
231 | + | |
232 | + def __mesh(self, obj): | |
233 | + if bl.RIGID_SHAPE_TYPE in obj: | |
234 | + return | |
235 | + if bl.CONSTRAINT_A in obj: | |
236 | + return | |
237 | + | |
238 | + bl.message("export: %s" % obj.name) | |
239 | + | |
240 | + # メッシュのコピーを生成してオブジェクトの行列を適用する | |
241 | + copyMesh, copyObj=bl.object.duplicate(obj) | |
242 | + if len(copyMesh.vertices)>0: | |
243 | + # apply transform | |
244 | + """ | |
245 | + try: | |
246 | + # svn 36722 | |
247 | + copyObj.scale=obj.scale | |
248 | + bpy.ops.object.transform_apply(scale=True) | |
249 | + copyObj.rotation_euler=obj.rotation_euler | |
250 | + bpy.ops.object.transform_apply(rotation=True) | |
251 | + copyObj.location=obj.location | |
252 | + bpy.ops.object.transform_apply(location=True) | |
253 | + except AttributeError as e: | |
254 | + # 2.57b | |
255 | + copyObj.scale=obj.scale | |
256 | + bpy.ops.object.scale_apply() | |
257 | + copyObj.rotation_euler=obj.rotation_euler | |
258 | + bpy.ops.object.rotation_apply() | |
259 | + copyObj.location=obj.location | |
260 | + bpy.ops.object.location_apply() | |
261 | + """ | |
262 | + copyMesh.transform(obj.matrix_world) | |
263 | + | |
264 | + # apply modifier | |
265 | + for m in [m for m in copyObj.modifiers]: | |
266 | + if m.type=='SOLIDFY': | |
267 | + continue | |
268 | + elif m.type=='ARMATURE': | |
269 | + continue | |
270 | + elif m.type=='MIRROR': | |
271 | + bpy.ops.object.modifier_apply(modifier=m.name) | |
272 | + else: | |
273 | + print(m.type) | |
274 | + | |
275 | + weightMap, secondWeightMap=self.__getWeightMap(copyObj, copyMesh) | |
276 | + self.__processFaces(obj.name, copyMesh, weightMap, secondWeightMap) | |
277 | + bl.object.delete(copyObj) | |
278 | + | |
279 | + def createEmptyBasicSkin(self): | |
280 | + self.__getOrCreateMorph('base', 0) | |
281 | + | |
282 | + def __skin(self, obj): | |
283 | + if not bl.object.hasShapeKey(obj): | |
284 | + return | |
285 | + | |
286 | + indexRelativeMap={} | |
287 | + blenderMesh=bl.object.getData(obj) | |
288 | + baseMorph=None | |
289 | + | |
290 | + # shape keys | |
291 | + vg=bl.object.getVertexGroup(obj, bl.MMD_SHAPE_GROUP_NAME) | |
292 | + | |
293 | + # base | |
294 | + used=set() | |
295 | + for b in bl.object.getShapeKeys(obj): | |
296 | + if b.name==bl.BASE_SHAPE_NAME: | |
297 | + baseMorph=self.__getOrCreateMorph('base', 0) | |
298 | + basis=b | |
299 | + | |
300 | + relativeIndex=0 | |
301 | + for index in vg: | |
302 | + v=bl.shapekey.getByIndex(b, index) | |
303 | + pos=[v[0], v[1], v[2]] | |
304 | + | |
305 | + indices=self.vertexArray.getMappedIndex(obj.name, index) | |
306 | + for attribute, i in indices.items(): | |
307 | + if i in used: | |
308 | + continue | |
309 | + used.add(i) | |
310 | + | |
311 | + baseMorph.add(i, pos) | |
312 | + indexRelativeMap[i]=relativeIndex | |
313 | + relativeIndex+=1 | |
314 | + | |
315 | + break | |
316 | + assert(basis) | |
317 | + #print(basis.name, len(baseMorph.offsets)) | |
318 | + | |
319 | + if len(baseMorph.offsets)==0: | |
320 | + return | |
321 | + | |
322 | + # shape keys | |
323 | + for b in bl.object.getShapeKeys(obj): | |
324 | + if b.name==bl.BASE_SHAPE_NAME: | |
325 | + continue | |
326 | + | |
327 | + #print(b.name) | |
328 | + morph=self.__getOrCreateMorph(b.name, 4) | |
329 | + used=set() | |
330 | + for index, src, dst in zip( | |
331 | + range(len(blenderMesh.vertices)), | |
332 | + bl.shapekey.get(basis), | |
333 | + bl.shapekey.get(b)): | |
334 | + offset=[dst[0]-src[0], dst[1]-src[1], dst[2]-src[2]] | |
335 | + if offset[0]==0 and offset[1]==0 and offset[2]==0: | |
336 | + continue | |
337 | + if index in vg: | |
338 | + indices=self.vertexArray.getMappedIndex(obj.name, index) | |
339 | + for attribute, i in indices.items(): | |
340 | + if i in used: | |
341 | + continue | |
342 | + used.add(i) | |
343 | + morph.add(indexRelativeMap[i], offset) | |
344 | + assert(len(morph.offsets)<len(baseMorph.offsets)) | |
345 | + | |
346 | + # sort skinmap | |
347 | + original=self.morphList[:] | |
348 | + def getIndex(morph): | |
349 | + for i, v in enumerate(englishmap.skinMap): | |
350 | + if v[0]==morph.name: | |
351 | + return i | |
352 | + #print(morph) | |
353 | + return len(englishmap.skinMap) | |
354 | + self.morphList.sort(key=getIndex) | |
355 | + | |
356 | + def __rigidbody(self, obj): | |
357 | + if not bl.RIGID_SHAPE_TYPE in obj: | |
358 | + return | |
359 | + self.rigidbodies.append(obj) | |
360 | + | |
361 | + def __constraint(self, obj): | |
362 | + if not bl.CONSTRAINT_A in obj: | |
363 | + return | |
364 | + self.constraints.append(obj) | |
365 | + | |
366 | + def __getOrCreateMorph(self, name, type): | |
367 | + for m in self.morphList: | |
368 | + if m.name==name: | |
369 | + return m | |
370 | + m=Morph(name, type) | |
371 | + self.morphList.append(m) | |
372 | + return m | |
373 | + | |
374 | + def getVertexCount(self): | |
375 | + return len(self.vertexArray.positions) | |
376 | + | |
377 | + | |
378 | + |
@@ -0,0 +1,142 @@ | ||
1 | +# coding: utf-8 | |
2 | + | |
3 | + | |
4 | +class VertexAttribute(object): | |
5 | + __slots__=[ | |
6 | + 'nx', 'ny', 'nz', # normal | |
7 | + 'u', 'v', # uv | |
8 | + ] | |
9 | + def __init__(self, nx, ny, nz, u, v): | |
10 | + self.nx=nx | |
11 | + self.ny=ny | |
12 | + self.nz=nz | |
13 | + self.u=u | |
14 | + self.v=v | |
15 | + | |
16 | + def __str__(self): | |
17 | + return "<vkey: %f, %f, %f, %f, %f>" % ( | |
18 | + self.nx, self.ny, self.nz, self.u, self.v) | |
19 | + | |
20 | + def __hash__(self): | |
21 | + return int(100*(self.nx + self.ny + self.nz + self.u + self.v)) | |
22 | + | |
23 | + def __eq__(self, rhs): | |
24 | + return self.nx==rhs.nx and self.ny==rhs.ny and self.nz==rhs.nz and self.u==rhs.u and self.v==rhs.v | |
25 | + | |
26 | + | |
27 | +class VertexKey(object): | |
28 | + __slots__=[ | |
29 | + 'obj_index', 'index', | |
30 | + ] | |
31 | + | |
32 | + def __init__(self, obj_index, index): | |
33 | + self.obj_index=obj_index | |
34 | + self.index=index | |
35 | + | |
36 | + def __str__(self): | |
37 | + return "<vkey: %d, %d>" % (self.obj_index, self.index) | |
38 | + | |
39 | + def __hash__(self): | |
40 | + return self.index*100+self.obj_index | |
41 | + | |
42 | + def __eq__(self, rhs): | |
43 | + return self.obj_index==rhs.obj_index and self.index==rhs.index | |
44 | + | |
45 | + | |
46 | +class VertexArray(object): | |
47 | + """ | |
48 | + 頂点配列 | |
49 | + """ | |
50 | + __slots__=[ | |
51 | + 'indexArrays', | |
52 | + 'positions', | |
53 | + 'attributes', # normal and uv | |
54 | + 'b0', 'b1', 'weight', | |
55 | + 'vertexMap', | |
56 | + 'objectMap', | |
57 | + ] | |
58 | + def __init__(self): | |
59 | + # indexArrays split with each material | |
60 | + self.indexArrays={} | |
61 | + | |
62 | + self.positions=[] | |
63 | + self.attributes=[] | |
64 | + self.b0=[] | |
65 | + self.b1=[] | |
66 | + self.weight=[] | |
67 | + | |
68 | + self.vertexMap={} | |
69 | + self.objectMap={} | |
70 | + | |
71 | + def __str__(self): | |
72 | + return "<VertexArray %d positions, %d indexArrays>" % ( | |
73 | + len(self.positions), len(self.indexArrays)) | |
74 | + | |
75 | + def zip(self): | |
76 | + return zip( | |
77 | + self.positions, self.attributes, | |
78 | + self.b0, self.b1, self.weight) | |
79 | + | |
80 | + def each(self): | |
81 | + keys=[key for key in self.indexArrays.keys()] | |
82 | + keys.sort() | |
83 | + for key in keys: | |
84 | + yield(key, self.indexArrays[key]) | |
85 | + | |
86 | + def __addOrGetIndex(self, obj_index, base_index, pos, normal, uv, b0, b1, weight0): | |
87 | + key=VertexKey(obj_index, base_index) | |
88 | + attribute=VertexAttribute( | |
89 | + normal[0], normal[1], normal[2], | |
90 | + uv[0], uv[1]) | |
91 | + if key in self.vertexMap: | |
92 | + if attribute in self.vertexMap[key]: | |
93 | + return self.vertexMap[key][attribute] | |
94 | + else: | |
95 | + return self.__addVertex(self.vertexMap[key], | |
96 | + pos, attribute, b0, b1, weight0) | |
97 | + else: | |
98 | + vertexMapKey={} | |
99 | + self.vertexMap[key]=vertexMapKey | |
100 | + return self.__addVertex(vertexMapKey, | |
101 | + pos, attribute, b0, b1, weight0) | |
102 | + | |
103 | + def __addVertex(self, vertexMapKey, pos, attribute, b0, b1, weight0): | |
104 | + index=len(self.positions) | |
105 | + vertexMapKey[attribute]=index | |
106 | + # position | |
107 | + self.positions.append((pos.x, pos.y, pos.z)) | |
108 | + # unique attribute | |
109 | + self.attributes.append(attribute) | |
110 | + # shared attribute | |
111 | + self.b0.append(b0) | |
112 | + self.b1.append(b1) | |
113 | + self.weight.append(weight0) | |
114 | + assert(index<=65535) | |
115 | + return index | |
116 | + | |
117 | + def getMappedIndex(self, obj_name, base_index): | |
118 | + return self.vertexMap[VertexKey(self.objectMap[obj_name], base_index)] | |
119 | + | |
120 | + def addTriangle(self, | |
121 | + object_name, material, | |
122 | + base_index0, base_index1, base_index2, | |
123 | + pos0, pos1, pos2, | |
124 | + n0, n1, n2, | |
125 | + uv0, uv1, uv2, | |
126 | + b0_0, b0_1, b0_2, | |
127 | + b1_0, b1_1, b1_2, | |
128 | + weight0, weight1, weight2 | |
129 | + ): | |
130 | + if object_name in self.objectMap: | |
131 | + obj_index=self.objectMap[object_name] | |
132 | + else: | |
133 | + obj_index=len(self.objectMap) | |
134 | + self.objectMap[object_name]=obj_index | |
135 | + index0=self.__addOrGetIndex(obj_index, base_index0, pos0, n0, uv0, b0_0, b1_0, weight0) | |
136 | + index1=self.__addOrGetIndex(obj_index, base_index1, pos1, n1, uv1, b0_1, b1_1, weight1) | |
137 | + index2=self.__addOrGetIndex(obj_index, base_index2, pos2, n2, uv2, b0_2, b1_2, weight2) | |
138 | + | |
139 | + if not material in self.indexArrays: | |
140 | + self.indexArrays[material]=[] | |
141 | + self.indexArrays[material]+=[index0, index1, index2] | |
142 | + |
@@ -1,801 +0,0 @@ | ||
1 | -# coding: utf-8 | |
2 | -""" | |
3 | -Blenderのメッシュをワンスキンメッシュ化する | |
4 | -""" | |
5 | -import bpy | |
6 | - | |
7 | -from . import bl | |
8 | -from .pymeshio import englishmap | |
9 | - | |
10 | -class VertexAttribute(object): | |
11 | - __slots__=[ | |
12 | - 'nx', 'ny', 'nz', # normal | |
13 | - 'u', 'v', # uv | |
14 | - ] | |
15 | - def __init__(self, nx, ny, nz, u, v): | |
16 | - self.nx=nx | |
17 | - self.ny=ny | |
18 | - self.nz=nz | |
19 | - self.u=u | |
20 | - self.v=v | |
21 | - | |
22 | - def __str__(self): | |
23 | - return "<vkey: %f, %f, %f, %f, %f>" % ( | |
24 | - self.nx, self.ny, self.nz, self.u, self.v) | |
25 | - | |
26 | - def __hash__(self): | |
27 | - return int(100*(self.nx + self.ny + self.nz + self.u + self.v)) | |
28 | - | |
29 | - def __eq__(self, rhs): | |
30 | - return self.nx==rhs.nx and self.ny==rhs.ny and self.nz==rhs.nz and self.u==rhs.u and self.v==rhs.v | |
31 | - | |
32 | - | |
33 | -class VertexKey(object): | |
34 | - __slots__=[ | |
35 | - 'obj_index', 'index', | |
36 | - ] | |
37 | - | |
38 | - def __init__(self, obj_index, index): | |
39 | - self.obj_index=obj_index | |
40 | - self.index=index | |
41 | - | |
42 | - def __str__(self): | |
43 | - return "<vkey: %d, %d>" % (self.obj_index, self.index) | |
44 | - | |
45 | - def __hash__(self): | |
46 | - return self.index*100+self.obj_index | |
47 | - | |
48 | - def __eq__(self, rhs): | |
49 | - return self.obj_index==rhs.obj_index and self.index==rhs.index | |
50 | - | |
51 | - | |
52 | -class VertexArray(object): | |
53 | - """ | |
54 | - 頂点配列 | |
55 | - """ | |
56 | - __slots__=[ | |
57 | - 'indexArrays', | |
58 | - 'positions', | |
59 | - 'attributes', # normal and uv | |
60 | - 'b0', 'b1', 'weight', | |
61 | - 'vertexMap', | |
62 | - 'objectMap', | |
63 | - ] | |
64 | - def __init__(self): | |
65 | - # indexArrays split with each material | |
66 | - self.indexArrays={} | |
67 | - | |
68 | - self.positions=[] | |
69 | - self.attributes=[] | |
70 | - self.b0=[] | |
71 | - self.b1=[] | |
72 | - self.weight=[] | |
73 | - | |
74 | - self.vertexMap={} | |
75 | - self.objectMap={} | |
76 | - | |
77 | - def __str__(self): | |
78 | - return "<VertexArray %d positions, %d indexArrays>" % ( | |
79 | - len(self.positions), len(self.indexArrays)) | |
80 | - | |
81 | - def zip(self): | |
82 | - return zip( | |
83 | - self.positions, self.attributes, | |
84 | - self.b0, self.b1, self.weight) | |
85 | - | |
86 | - def each(self): | |
87 | - keys=[key for key in self.indexArrays.keys()] | |
88 | - keys.sort() | |
89 | - for key in keys: | |
90 | - yield(key, self.indexArrays[key]) | |
91 | - | |
92 | - def __addOrGetIndex(self, obj_index, base_index, pos, normal, uv, b0, b1, weight0): | |
93 | - key=VertexKey(obj_index, base_index) | |
94 | - attribute=VertexAttribute( | |
95 | - normal[0], normal[1], normal[2], | |
96 | - uv[0], uv[1]) | |
97 | - if key in self.vertexMap: | |
98 | - if attribute in self.vertexMap[key]: | |
99 | - return self.vertexMap[key][attribute] | |
100 | - else: | |
101 | - return self.__addVertex(self.vertexMap[key], | |
102 | - pos, attribute, b0, b1, weight0) | |
103 | - else: | |
104 | - vertexMapKey={} | |
105 | - self.vertexMap[key]=vertexMapKey | |
106 | - return self.__addVertex(vertexMapKey, | |
107 | - pos, attribute, b0, b1, weight0) | |
108 | - | |
109 | - def __addVertex(self, vertexMapKey, pos, attribute, b0, b1, weight0): | |
110 | - index=len(self.positions) | |
111 | - vertexMapKey[attribute]=index | |
112 | - # position | |
113 | - self.positions.append((pos.x, pos.y, pos.z)) | |
114 | - # unique attribute | |
115 | - self.attributes.append(attribute) | |
116 | - # shared attribute | |
117 | - self.b0.append(b0) | |
118 | - self.b1.append(b1) | |
119 | - self.weight.append(weight0) | |
120 | - assert(index<=65535) | |
121 | - return index | |
122 | - | |
123 | - def getMappedIndex(self, obj_name, base_index): | |
124 | - return self.vertexMap[VertexKey(self.objectMap[obj_name], base_index)] | |
125 | - | |
126 | - def addTriangle(self, | |
127 | - object_name, material, | |
128 | - base_index0, base_index1, base_index2, | |
129 | - pos0, pos1, pos2, | |
130 | - n0, n1, n2, | |
131 | - uv0, uv1, uv2, | |
132 | - b0_0, b0_1, b0_2, | |
133 | - b1_0, b1_1, b1_2, | |
134 | - weight0, weight1, weight2 | |
135 | - ): | |
136 | - if object_name in self.objectMap: | |
137 | - obj_index=self.objectMap[object_name] | |
138 | - else: | |
139 | - obj_index=len(self.objectMap) | |
140 | - self.objectMap[object_name]=obj_index | |
141 | - index0=self.__addOrGetIndex(obj_index, base_index0, pos0, n0, uv0, b0_0, b1_0, weight0) | |
142 | - index1=self.__addOrGetIndex(obj_index, base_index1, pos1, n1, uv1, b0_1, b1_1, weight1) | |
143 | - index2=self.__addOrGetIndex(obj_index, base_index2, pos2, n2, uv2, b0_2, b1_2, weight2) | |
144 | - | |
145 | - if not material in self.indexArrays: | |
146 | - self.indexArrays[material]=[] | |
147 | - self.indexArrays[material]+=[index0, index1, index2] | |
148 | - | |
149 | - | |
150 | -class Morph(object): | |
151 | - __slots__=['name', 'type', 'offsets'] | |
152 | - def __init__(self, name, type): | |
153 | - self.name=name | |
154 | - self.type=type | |
155 | - self.offsets=[] | |
156 | - | |
157 | - def add(self, index, offset): | |
158 | - self.offsets.append((index, offset)) | |
159 | - | |
160 | - def sort(self): | |
161 | - self.offsets.sort(key=lambda e: e[0]) | |
162 | - | |
163 | - def __str__(self): | |
164 | - return "<Morph %s>" % self.name | |
165 | - | |
166 | -class IKSolver(object): | |
167 | - __slots__=['target', 'effector', 'length', 'iterations', 'weight'] | |
168 | - def __init__(self, target, effector, length, iterations, weight): | |
169 | - self.target=target | |
170 | - self.effector=effector | |
171 | - self.length=length | |
172 | - self.iterations=iterations | |
173 | - self.weight=weight | |
174 | - | |
175 | - | |
176 | -class SSS(object): | |
177 | - def __init__(self): | |
178 | - self.use=1 | |
179 | - | |
180 | - | |
181 | -class DefaultMatrial(object): | |
182 | - def __init__(self): | |
183 | - self.name='default' | |
184 | - # diffuse | |
185 | - self.diffuse_color=[1, 1, 1] | |
186 | - self.alpha=1 | |
187 | - # specular | |
188 | - self.specular_toon_size=0 | |
189 | - self.specular_hardness=5 | |
190 | - self.specular_color=[1, 1, 1] | |
191 | - # ambient | |
192 | - self.mirror_color=[1, 1, 1] | |
193 | - # flag | |
194 | - self.subsurface_scattering=SSS() | |
195 | - # texture | |
196 | - self.texture_slots=[] | |
197 | - | |
198 | - | |
199 | -class OneSkinMesh(object): | |
200 | - __slots__=['vertexArray', 'morphList', 'rigidbodies', 'constraints', ] | |
201 | - def __init__(self): | |
202 | - self.vertexArray=VertexArray() | |
203 | - self.morphList=[] | |
204 | - self.rigidbodies=[] | |
205 | - self.constraints=[] | |
206 | - | |
207 | - def __str__(self): | |
208 | - return "<OneSkinMesh %s, morph:%d>" % ( | |
209 | - self.vertexArray, | |
210 | - len(self.morphList)) | |
211 | - | |
212 | - def addMesh(self, obj): | |
213 | - if not bl.object.isVisible(obj): | |
214 | - return | |
215 | - self.__mesh(obj) | |
216 | - self.__skin(obj) | |
217 | - self.__rigidbody(obj) | |
218 | - self.__constraint(obj) | |
219 | - | |
220 | - def __getWeightMap(self, obj, mesh): | |
221 | - # bone weight | |
222 | - weightMap={} | |
223 | - secondWeightMap={} | |
224 | - def setWeight(i, name, w): | |
225 | - if w>0: | |
226 | - if i in weightMap: | |
227 | - if i in secondWeightMap: | |
228 | - # 上位2つのweightを採用する | |
229 | - if w<secondWeightMap[i][1]: | |
230 | - pass | |
231 | - elif w<weightMap[i][1]: | |
232 | - # 2つ目を入れ替え | |
233 | - secondWeightMap[i]=(name, w) | |
234 | - else: | |
235 | - # 1つ目を入れ替え | |
236 | - weightMap[i]=(name, w) | |
237 | - else: | |
238 | - if w>weightMap[i][1]: | |
239 | - # 多い方をweightMapに | |
240 | - secondWeightMap[i]=weightMap[i] | |
241 | - weightMap[i]=(name, w) | |
242 | - else: | |
243 | - secondWeightMap[i]=(name, w) | |
244 | - else: | |
245 | - weightMap[i]=(name, w) | |
246 | - | |
247 | - # ToDo bone weightと関係ないvertex groupを除外する | |
248 | - for i, v in enumerate(mesh.vertices): | |
249 | - if len(v.groups)>0: | |
250 | - for g in v.groups: | |
251 | - setWeight(i, obj.vertex_groups[g.group].name, g.weight) | |
252 | - else: | |
253 | - try: | |
254 | - setWeight(i, obj.vertex_groups[0].name, 1) | |
255 | - except: | |
256 | - # no vertex_groups | |
257 | - pass | |
258 | - | |
259 | - # 合計値が1になるようにする | |
260 | - for i in range(len(mesh.vertices)): | |
261 | - if i in secondWeightMap: | |
262 | - secondWeightMap[i]=(secondWeightMap[i][0], 1.0-weightMap[i][1]) | |
263 | - elif i in weightMap: | |
264 | - weightMap[i]=(weightMap[i][0], 1.0) | |
265 | - secondWeightMap[i]=("", 0) | |
266 | - else: | |
267 | - print("no weight vertex") | |
268 | - weightMap[i]=("", 0) | |
269 | - secondWeightMap[i]=("", 0) | |
270 | - | |
271 | - return weightMap, secondWeightMap | |
272 | - | |
273 | - def __processFaces(self, obj_name, mesh, weightMap, secondWeightMap): | |
274 | - default_material=DefaultMatrial() | |
275 | - # 各面の処理 | |
276 | - for i, face in enumerate(mesh.faces): | |
277 | - faceVertexCount=bl.face.getVertexCount(face) | |
278 | - try: | |
279 | - material=mesh.materials[bl.face.getMaterialIndex(face)] | |
280 | - except IndexError as e: | |
281 | - material=default_material | |
282 | - v=[mesh.vertices[index] for index in bl.face.getVertices(face)] | |
283 | - uv=bl.mesh.getFaceUV( | |
284 | - mesh, i, face, bl.face.getVertexCount(face)) | |
285 | - # flip triangle | |
286 | - if faceVertexCount==3: | |
287 | - # triangle | |
288 | - self.vertexArray.addTriangle( | |
289 | - obj_name, material.name, | |
290 | - v[2].index, | |
291 | - v[1].index, | |
292 | - v[0].index, | |
293 | - v[2].co, | |
294 | - v[1].co, | |
295 | - v[0].co, | |
296 | - bl.vertex.getNormal(v[2]), | |
297 | - bl.vertex.getNormal(v[1]), | |
298 | - bl.vertex.getNormal(v[0]), | |
299 | - uv[2], | |
300 | - uv[1], | |
301 | - uv[0], | |
302 | - weightMap[v[2].index][0], | |
303 | - weightMap[v[1].index][0], | |
304 | - weightMap[v[0].index][0], | |
305 | - secondWeightMap[v[2].index][0], | |
306 | - secondWeightMap[v[1].index][0], | |
307 | - secondWeightMap[v[0].index][0], | |
308 | - weightMap[v[2].index][1], | |
309 | - weightMap[v[1].index][1], | |
310 | - weightMap[v[0].index][1] | |
311 | - ) | |
312 | - elif faceVertexCount==4: | |
313 | - # quadrangle | |
314 | - self.vertexArray.addTriangle( | |
315 | - obj_name, material.name, | |
316 | - v[2].index, | |
317 | - v[1].index, | |
318 | - v[0].index, | |
319 | - v[2].co, | |
320 | - v[1].co, | |
321 | - v[0].co, | |
322 | - bl.vertex.getNormal(v[2]), | |
323 | - bl.vertex.getNormal(v[1]), | |
324 | - bl.vertex.getNormal(v[0]), | |
325 | - uv[2], | |
326 | - uv[1], | |
327 | - uv[0], | |
328 | - weightMap[v[2].index][0], | |
329 | - weightMap[v[1].index][0], | |
330 | - weightMap[v[0].index][0], | |
331 | - secondWeightMap[v[2].index][0], | |
332 | - secondWeightMap[v[1].index][0], | |
333 | - secondWeightMap[v[0].index][0], | |
334 | - weightMap[v[2].index][1], | |
335 | - weightMap[v[1].index][1], | |
336 | - weightMap[v[0].index][1] | |
337 | - ) | |
338 | - self.vertexArray.addTriangle( | |
339 | - obj_name, material.name, | |
340 | - v[0].index, | |
341 | - v[3].index, | |
342 | - v[2].index, | |
343 | - v[0].co, | |
344 | - v[3].co, | |
345 | - v[2].co, | |
346 | - bl.vertex.getNormal(v[0]), | |
347 | - bl.vertex.getNormal(v[3]), | |
348 | - bl.vertex.getNormal(v[2]), | |
349 | - uv[0], | |
350 | - uv[3], | |
351 | - uv[2], | |
352 | - weightMap[v[0].index][0], | |
353 | - weightMap[v[3].index][0], | |
354 | - weightMap[v[2].index][0], | |
355 | - secondWeightMap[v[0].index][0], | |
356 | - secondWeightMap[v[3].index][0], | |
357 | - secondWeightMap[v[2].index][0], | |
358 | - weightMap[v[0].index][1], | |
359 | - weightMap[v[3].index][1], | |
360 | - weightMap[v[2].index][1] | |
361 | - ) | |
362 | - | |
363 | - def __mesh(self, obj): | |
364 | - if bl.RIGID_SHAPE_TYPE in obj: | |
365 | - return | |
366 | - if bl.CONSTRAINT_A in obj: | |
367 | - return | |
368 | - | |
369 | - bl.message("export: %s" % obj.name) | |
370 | - | |
371 | - # メッシュのコピーを生成してオブジェクトの行列を適用する | |
372 | - copyMesh, copyObj=bl.object.duplicate(obj) | |
373 | - if len(copyMesh.vertices)>0: | |
374 | - # apply transform | |
375 | - """ | |
376 | - try: | |
377 | - # svn 36722 | |
378 | - copyObj.scale=obj.scale | |
379 | - bpy.ops.object.transform_apply(scale=True) | |
380 | - copyObj.rotation_euler=obj.rotation_euler | |
381 | - bpy.ops.object.transform_apply(rotation=True) | |
382 | - copyObj.location=obj.location | |
383 | - bpy.ops.object.transform_apply(location=True) | |
384 | - except AttributeError as e: | |
385 | - # 2.57b | |
386 | - copyObj.scale=obj.scale | |
387 | - bpy.ops.object.scale_apply() | |
388 | - copyObj.rotation_euler=obj.rotation_euler | |
389 | - bpy.ops.object.rotation_apply() | |
390 | - copyObj.location=obj.location | |
391 | - bpy.ops.object.location_apply() | |
392 | - """ | |
393 | - copyMesh.transform(obj.matrix_world) | |
394 | - | |
395 | - # apply modifier | |
396 | - for m in [m for m in copyObj.modifiers]: | |
397 | - if m.type=='SOLIDFY': | |
398 | - continue | |
399 | - elif m.type=='ARMATURE': | |
400 | - continue | |
401 | - elif m.type=='MIRROR': | |
402 | - bpy.ops.object.modifier_apply(modifier=m.name) | |
403 | - else: | |
404 | - print(m.type) | |
405 | - | |
406 | - weightMap, secondWeightMap=self.__getWeightMap(copyObj, copyMesh) | |
407 | - self.__processFaces(obj.name, copyMesh, weightMap, secondWeightMap) | |
408 | - bl.object.delete(copyObj) | |
409 | - | |
410 | - def createEmptyBasicSkin(self): | |
411 | - self.__getOrCreateMorph('base', 0) | |
412 | - | |
413 | - def __skin(self, obj): | |
414 | - if not bl.object.hasShapeKey(obj): | |
415 | - return | |
416 | - | |
417 | - indexRelativeMap={} | |
418 | - blenderMesh=bl.object.getData(obj) | |
419 | - baseMorph=None | |
420 | - | |
421 | - # shape keys | |
422 | - vg=bl.object.getVertexGroup(obj, bl.MMD_SHAPE_GROUP_NAME) | |
423 | - | |
424 | - # base | |
425 | - used=set() | |
426 | - for b in bl.object.getShapeKeys(obj): | |
427 | - if b.name==bl.BASE_SHAPE_NAME: | |
428 | - baseMorph=self.__getOrCreateMorph('base', 0) | |
429 | - basis=b | |
430 | - | |
431 | - relativeIndex=0 | |
432 | - for index in vg: | |
433 | - v=bl.shapekey.getByIndex(b, index) | |
434 | - pos=[v[0], v[1], v[2]] | |
435 | - | |
436 | - indices=self.vertexArray.getMappedIndex(obj.name, index) | |
437 | - for attribute, i in indices.items(): | |
438 | - if i in used: | |
439 | - continue | |
440 | - used.add(i) | |
441 | - | |
442 | - baseMorph.add(i, pos) | |
443 | - indexRelativeMap[i]=relativeIndex | |
444 | - relativeIndex+=1 | |
445 | - | |
446 | - break | |
447 | - assert(basis) | |
448 | - #print(basis.name, len(baseMorph.offsets)) | |
449 | - | |
450 | - if len(baseMorph.offsets)==0: | |
451 | - return | |
452 | - | |
453 | - # shape keys | |
454 | - for b in bl.object.getShapeKeys(obj): | |
455 | - if b.name==bl.BASE_SHAPE_NAME: | |
456 | - continue | |
457 | - | |
458 | - #print(b.name) | |
459 | - morph=self.__getOrCreateMorph(b.name, 4) | |
460 | - used=set() | |
461 | - for index, src, dst in zip( | |
462 | - range(len(blenderMesh.vertices)), | |
463 | - bl.shapekey.get(basis), | |
464 | - bl.shapekey.get(b)): | |
465 | - offset=[dst[0]-src[0], dst[1]-src[1], dst[2]-src[2]] | |
466 | - if offset[0]==0 and offset[1]==0 and offset[2]==0: | |
467 | - continue | |
468 | - if index in vg: | |
469 | - indices=self.vertexArray.getMappedIndex(obj.name, index) | |
470 | - for attribute, i in indices.items(): | |
471 | - if i in used: | |
472 | - continue | |
473 | - used.add(i) | |
474 | - morph.add(indexRelativeMap[i], offset) | |
475 | - assert(len(morph.offsets)<len(baseMorph.offsets)) | |
476 | - | |
477 | - # sort skinmap | |
478 | - original=self.morphList[:] | |
479 | - def getIndex(morph): | |
480 | - for i, v in enumerate(englishmap.skinMap): | |
481 | - if v[0]==morph.name: | |
482 | - return i | |
483 | - #print(morph) | |
484 | - return len(englishmap.skinMap) | |
485 | - self.morphList.sort(key=getIndex) | |
486 | - | |
487 | - def __rigidbody(self, obj): | |
488 | - if not bl.RIGID_SHAPE_TYPE in obj: | |
489 | - return | |
490 | - self.rigidbodies.append(obj) | |
491 | - | |
492 | - def __constraint(self, obj): | |
493 | - if not bl.CONSTRAINT_A in obj: | |
494 | - return | |
495 | - self.constraints.append(obj) | |
496 | - | |
497 | - def __getOrCreateMorph(self, name, type): | |
498 | - for m in self.morphList: | |
499 | - if m.name==name: | |
500 | - return m | |
501 | - m=Morph(name, type) | |
502 | - self.morphList.append(m) | |
503 | - return m | |
504 | - | |
505 | - def getVertexCount(self): | |
506 | - return len(self.vertexArray.positions) | |
507 | - | |
508 | - | |
509 | -class Bone(object): | |
510 | - __slots__=['index', 'name', 'ik_index', | |
511 | - 'pos', 'tail', 'parent_index', 'tail_index', 'type', 'isConnect'] | |
512 | - def __init__(self, name, pos, tail, isConnect): | |
513 | - self.index=-1 | |
514 | - self.name=name | |
515 | - self.pos=pos | |
516 | - self.tail=tail | |
517 | - self.parent_index=None | |
518 | - self.tail_index=None | |
519 | - self.type=0 | |
520 | - self.isConnect=isConnect | |
521 | - self.ik_index=0 | |
522 | - | |
523 | - def __eq__(self, rhs): | |
524 | - return self.index==rhs.index | |
525 | - | |
526 | - def __str__(self): | |
527 | - return "<Bone %s %d>" % (self.name, self.type) | |
528 | - | |
529 | -class BoneBuilder(object): | |
530 | - __slots__=['bones', 'boneMap', 'ik_list', 'bone_groups',] | |
531 | - def __init__(self): | |
532 | - self.bones=[] | |
533 | - self.boneMap={} | |
534 | - self.ik_list=[] | |
535 | - self.bone_groups=[] | |
536 | - | |
537 | - def getBoneGroup(self, bone): | |
538 | - for i, g in enumerate(self.bone_groups): | |
539 | - for b in g[1]: | |
540 | - if b==bone.name: | |
541 | - return i+1 | |
542 | - print('no gorup', bone) | |
543 | - return 0 | |
544 | - | |
545 | - def build(self, armatureObj): | |
546 | - if not armatureObj: | |
547 | - return | |
548 | - | |
549 | - bl.message("build skeleton") | |
550 | - armature=bl.object.getData(armatureObj) | |
551 | - | |
552 | - #################### | |
553 | - # bone group | |
554 | - #################### | |
555 | - for g in bl.object.boneGroups(armatureObj): | |
556 | - self.bone_groups.append((g.name, [])) | |
557 | - | |
558 | - #################### | |
559 | - # get bones | |
560 | - #################### | |
561 | - for b in armature.bones.values(): | |
562 | - if not b.parent: | |
563 | - # root bone | |
564 | - bone=Bone(b.name, | |
565 | - bl.bone.getHeadLocal(b), | |
566 | - bl.bone.getTailLocal(b), | |
567 | - False) | |
568 | - self.__addBone(bone) | |
569 | - self.__getBone(bone, b) | |
570 | - | |
571 | - for b in armature.bones.values(): | |
572 | - if not b.parent: | |
573 | - self.__checkConnection(b, None) | |
574 | - | |
575 | - #################### | |
576 | - # get IK | |
577 | - #################### | |
578 | - pose = bl.object.getPose(armatureObj) | |
579 | - for b in pose.bones.values(): | |
580 | - #################### | |
581 | - # assing bone group | |
582 | - #################### | |
583 | - self.__assignBoneGroup(b, b.bone_group) | |
584 | - for c in b.constraints: | |
585 | - if bl.constraint.isIKSolver(c): | |
586 | - #################### | |
587 | - # IK target | |
588 | - #################### | |
589 | - target=self.__boneByName(bl.constraint.ikTarget(c)) | |
590 | - target.type=2 | |
591 | - | |
592 | - #################### | |
593 | - # IK effector | |
594 | - #################### | |
595 | - # IK 接続先 | |
596 | - link=self.__boneByName(b.name) | |
597 | - link.type=6 | |
598 | - | |
599 | - # IK chain | |
600 | - e=b.parent | |
601 | - chainLength=bl.constraint.ikChainLen(c) | |
602 | - for i in range(chainLength): | |
603 | - # IK影響下 | |
604 | - chainBone=self.__boneByName(e.name) | |
605 | - chainBone.type=4 | |
606 | - chainBone.ik_index=target.index | |
607 | - e=e.parent | |
608 | - self.ik_list.append( | |
609 | - IKSolver(target, link, chainLength, | |
610 | - int(bl.constraint.ikItration(c) * 0.1), | |
611 | - bl.constraint.ikRotationWeight(c) | |
612 | - )) | |
613 | - | |
614 | - #################### | |
615 | - | |
616 | - # boneのsort | |
617 | - self._sortBy() | |
618 | - self._fix() | |
619 | - # IKのsort | |
620 | - def getIndex(ik): | |
621 | - for i, v in enumerate(englishmap.boneMap): | |
622 | - if v[0]==ik.target.name: | |
623 | - return i | |
624 | - return len(englishmap.boneMap) | |
625 | - self.ik_list.sort(key=getIndex) | |
626 | - | |
627 | - def __assignBoneGroup(self, poseBone, boneGroup): | |
628 | - if boneGroup: | |
629 | - for g in self.bone_groups: | |
630 | - if g[0]==boneGroup.name: | |
631 | - g[1].append(poseBone.name) | |
632 | - | |
633 | - def __checkConnection(self, b, p): | |
634 | - if bl.bone.isConnected(b): | |
635 | - parent=self.__boneByName(p.name) | |
636 | - parent.isConnect=True | |
637 | - | |
638 | - for c in b.children: | |
639 | - self.__checkConnection(c, b) | |
640 | - | |
641 | - def _sortBy(self): | |
642 | - """ | |
643 | - boneMap順に並べ替える | |
644 | - """ | |
645 | - boneMap=englishmap.boneMap | |
646 | - original=self.bones[:] | |
647 | - def getIndex(bone): | |
648 | - for i, k_v in enumerate(boneMap): | |
649 | - if k_v[0]==bone.name: | |
650 | - return i | |
651 | - print(bone) | |
652 | - return len(boneMap) | |
653 | - | |
654 | - self.bones.sort(key=getIndex) | |
655 | - | |
656 | - sortMap={} | |
657 | - for i, b in enumerate(self.bones): | |
658 | - src=original.index(b) | |
659 | - sortMap[src]=i | |
660 | - for b in self.bones: | |
661 | - b.index=sortMap[b.index] | |
662 | - if b.parent_index: | |
663 | - b.parent_index=sortMap[b.parent_index] | |
664 | - if b.tail_index: | |
665 | - b.tail_index=sortMap[b.tail_index] | |
666 | - if b.ik_index>0: | |
667 | - b.ik_index=sortMap[b.ik_index] | |
668 | - | |
669 | - def _fix(self): | |
670 | - """ | |
671 | - 調整 | |
672 | - """ | |
673 | - for b in self.bones: | |
674 | - # parent index | |
675 | - if b.parent_index==None: | |
676 | - b.parent_index=0xFFFF | |
677 | - else: | |
678 | - if b.type==6 or b.type==7: | |
679 | - # fix tail bone | |
680 | - parent=self.bones[b.parent_index] | |
681 | - #print('parnet', parent.name) | |
682 | - parent.tail_index=b.index | |
683 | - | |
684 | - for b in self.bones: | |
685 | - if b.tail_index==None: | |
686 | - b.tail_index=0 | |
687 | - elif b.type==9: | |
688 | - b.tail_index==0 | |
689 | - | |
690 | - def getIndex(self, bone): | |
691 | - for i, b in enumerate(self.bones): | |
692 | - if b==bone: | |
693 | - return i | |
694 | - assert(false) | |
695 | - | |
696 | - def indexByName(self, name): | |
697 | - if name=='': | |
698 | - return 0 | |
699 | - else: | |
700 | - try: | |
701 | - return self.getIndex(self.__boneByName(name)) | |
702 | - except: | |
703 | - return 0 | |
704 | - | |
705 | - def __boneByName(self, name): | |
706 | - return self.boneMap[name] | |
707 | - | |
708 | - def __getBone(self, parent, b): | |
709 | - if len(b.children)==0: | |
710 | - parent.type=7 | |
711 | - return | |
712 | - | |
713 | - for i, c in enumerate(b.children): | |
714 | - bone=Bone(c.name, | |
715 | - bl.bone.getHeadLocal(c), | |
716 | - bl.bone.getTailLocal(c), | |
717 | - bl.bone.isConnected(c)) | |
718 | - self.__addBone(bone) | |
719 | - if parent: | |
720 | - bone.parent_index=parent.index | |
721 | - #if i==0: | |
722 | - if bone.isConnect or (not parent.tail_index and parent.tail==bone.pos): | |
723 | - parent.tail_index=bone.index | |
724 | - self.__getBone(bone, c) | |
725 | - | |
726 | - def __addBone(self, bone): | |
727 | - bone.index=len(self.bones) | |
728 | - self.bones.append(bone) | |
729 | - self.boneMap[bone.name]=bone | |
730 | - | |
731 | - | |
732 | -class Node(object): | |
733 | - __slots__=['o', 'children'] | |
734 | - def __init__(self, o): | |
735 | - self.o=o | |
736 | - self.children=[] | |
737 | - | |
738 | - | |
739 | - | |
740 | -class Exporter(object): | |
741 | - | |
742 | - __slots__=[ | |
743 | - 'armatureObj', | |
744 | - 'oneSkinMesh', | |
745 | - 'englishName', | |
746 | - 'englishComment', | |
747 | - 'name', | |
748 | - 'comment', | |
749 | - 'skeleton', | |
750 | - ] | |
751 | - def setup(self): | |
752 | - self.armatureObj=None | |
753 | - | |
754 | - # 木構造を構築する | |
755 | - object_node_map={} | |
756 | - for o in bl.object.each(): | |
757 | - object_node_map[o]=Node(o) | |
758 | - for o in bl.object.each(): | |
759 | - node=object_node_map[o] | |
760 | - if node.o.parent: | |
761 | - object_node_map[node.o.parent].children.append(node) | |
762 | - | |
763 | - # ルートを得る | |
764 | - root=object_node_map[bl.object.getActive()] | |
765 | - o=root.o | |
766 | - self.englishName=o.name | |
767 | - self.englishComment=o[bl.MMD_COMMENT] if bl.MMD_COMMENT in o else 'blender export\n' | |
768 | - self.name=o[bl.MMD_MB_NAME] if bl.MMD_MB_NAME in o else 'Blenderエクスポート' | |
769 | - self.comment=o[bl.MMD_MB_COMMENT] if bl.MMD_MB_COMMENT in o else 'Blnderエクスポート\n' | |
770 | - | |
771 | - # ワンスキンメッシュを作る | |
772 | - self.oneSkinMesh=OneSkinMesh() | |
773 | - self.__createOneSkinMesh(root) | |
774 | - bl.message(self.oneSkinMesh) | |
775 | - if len(self.oneSkinMesh.morphList)==0: | |
776 | - # create emtpy skin | |
777 | - self.oneSkinMesh.createEmptyBasicSkin() | |
778 | - | |
779 | - # skeleton | |
780 | - self.skeleton=BoneBuilder() | |
781 | - self.skeleton.build(self.armatureObj) | |
782 | - | |
783 | - def __createOneSkinMesh(self, node): | |
784 | - ############################################################ | |
785 | - # search armature modifier | |
786 | - ############################################################ | |
787 | - for m in node.o.modifiers: | |
788 | - if bl.modifier.isType(m, 'ARMATURE'): | |
789 | - armatureObj=bl.modifier.getArmatureObject(m) | |
790 | - if not self.armatureObj: | |
791 | - self.armatureObj=armatureObj | |
792 | - elif self.armatureObj!=armatureObj: | |
793 | - print("warning! found multiple armature. ignored.", | |
794 | - armatureObj.name) | |
795 | - | |
796 | - if node.o.type.upper()=='MESH': | |
797 | - self.oneSkinMesh.addMesh(node.o) | |
798 | - | |
799 | - for child in node.children: | |
800 | - self.__createOneSkinMesh(child) | |
801 | - |