3 Blenderのメッシュをワンスキンメッシュ化する
8 from .pymeshio import englishmap
10 class VertexAttribute(object):
12 'nx', 'ny', 'nz', # normal
15 def __init__(self, nx, ny, nz, u, v):
23 return "<vkey: %f, %f, %f, %f, %f>" % (
24 self.nx, self.ny, self.nz, self.u, self.v)
27 return int(100*(self.nx + self.ny + self.nz + self.u + self.v))
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
33 class VertexKey(object):
38 def __init__(self, obj_index, index):
39 self.obj_index=obj_index
43 return "<vkey: %d, %d>" % (self.obj_index, self.index)
46 return self.index*100+self.obj_index
48 def __eq__(self, rhs):
49 return self.obj_index==rhs.obj_index and self.index==rhs.index
52 class VertexArray(object):
59 'attributes', # normal and uv
65 # indexArrays split with each material
78 return "<VertexArray %d positions, %d indexArrays>" % (
79 len(self.positions), len(self.indexArrays))
83 self.positions, self.attributes,
84 self.b0, self.b1, self.weight)
87 keys=[key for key in self.indexArrays.keys()]
90 yield(key, self.indexArrays[key])
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],
97 if key in self.vertexMap:
98 if attribute in self.vertexMap[key]:
99 return self.vertexMap[key][attribute]
101 return self.__addVertex(self.vertexMap[key],
102 pos, attribute, b0, b1, weight0)
105 self.vertexMap[key]=vertexMapKey
106 return self.__addVertex(vertexMapKey,
107 pos, attribute, b0, b1, weight0)
109 def __addVertex(self, vertexMapKey, pos, attribute, b0, b1, weight0):
110 index=len(self.positions)
111 vertexMapKey[attribute]=index
113 self.positions.append((pos.x, pos.y, pos.z))
115 self.attributes.append(attribute)
119 self.weight.append(weight0)
123 def getMappedIndex(self, obj_name, base_index):
124 return self.vertexMap[VertexKey(self.objectMap[obj_name], base_index)]
126 def addTriangle(self,
127 object_name, material,
128 base_index0, base_index1, base_index2,
134 weight0, weight1, weight2
136 if object_name in self.objectMap:
137 obj_index=self.objectMap[object_name]
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)
145 if not material in self.indexArrays:
146 self.indexArrays[material]=[]
147 self.indexArrays[material]+=[index0, index1, index2]
151 __slots__=['name', 'type', 'offsets']
152 def __init__(self, name, type):
157 def add(self, index, offset):
158 self.offsets.append((index, offset))
161 self.offsets.sort(key=lambda e: e[0])
164 return "<Morph %s>" % self.name
166 class IKSolver(object):
167 __slots__=['target', 'effector', 'length', 'iterations', 'weight']
168 def __init__(self, target, effector, length, iterations, weight):
170 self.effector=effector
172 self.iterations=iterations
181 class DefaultMatrial(object):
185 self.diffuse_color=[1, 1, 1]
188 self.specular_toon_size=0
189 self.specular_hardness=5
190 self.specular_color=[1, 1, 1]
192 self.mirror_color=[1, 1, 1]
194 self.subsurface_scattering=SSS()
196 self.texture_slots=[]
199 class OneSkinMesh(object):
200 __slots__=['vertexArray', 'morphList', 'rigidbodies', 'constraints', ]
202 self.vertexArray=VertexArray()
208 return "<OneSkinMesh %s, morph:%d>" % (
212 def addMesh(self, obj):
213 if not bl.object.isVisible(obj):
217 self.__rigidbody(obj)
218 self.__constraint(obj)
220 def __getWeightMap(self, obj, mesh):
224 def setWeight(i, name, w):
227 if i in secondWeightMap:
229 if w<secondWeightMap[i][1]:
231 elif w<weightMap[i][1]:
233 secondWeightMap[i]=(name, w)
236 weightMap[i]=(name, w)
238 if w>weightMap[i][1]:
240 secondWeightMap[i]=weightMap[i]
241 weightMap[i]=(name, w)
243 secondWeightMap[i]=(name, w)
245 weightMap[i]=(name, w)
247 # ToDo bone weightと関係ないvertex groupを除外する
248 for i, v in enumerate(mesh.vertices):
251 setWeight(i, obj.vertex_groups[g.group].name, g.weight)
254 setWeight(i, obj.vertex_groups[0].name, 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])
264 weightMap[i]=(weightMap[i][0], 1.0)
265 secondWeightMap[i]=("", 0)
267 print("no weight vertex")
269 secondWeightMap[i]=("", 0)
271 return weightMap, secondWeightMap
273 def __processFaces(self, obj_name, mesh, weightMap, secondWeightMap):
274 default_material=DefaultMatrial()
276 for i, face in enumerate(mesh.faces):
277 faceVertexCount=bl.face.getVertexCount(face)
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))
286 if faceVertexCount==3:
288 self.vertexArray.addTriangle(
289 obj_name, material.name,
296 bl.vertex.getNormal(v[2]),
297 bl.vertex.getNormal(v[1]),
298 bl.vertex.getNormal(v[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]
312 elif faceVertexCount==4:
314 self.vertexArray.addTriangle(
315 obj_name, material.name,
322 bl.vertex.getNormal(v[2]),
323 bl.vertex.getNormal(v[1]),
324 bl.vertex.getNormal(v[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]
338 self.vertexArray.addTriangle(
339 obj_name, material.name,
346 bl.vertex.getNormal(v[0]),
347 bl.vertex.getNormal(v[3]),
348 bl.vertex.getNormal(v[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]
363 def __mesh(self, obj):
364 if bl.RIGID_SHAPE_TYPE in obj:
366 if bl.CONSTRAINT_A in obj:
369 bl.message("export: %s" % obj.name)
371 # メッシュのコピーを生成してオブジェクトの行列を適用する
372 copyMesh, copyObj=bl.object.duplicate(obj)
373 if len(copyMesh.vertices)>0:
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:
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()
393 copyMesh.transform(obj.matrix_world)
396 for m in [m for m in copyObj.modifiers]:
397 if m.type=='SOLIDFY':
399 elif m.type=='ARMATURE':
401 elif m.type=='MIRROR':
402 bpy.ops.object.modifier_apply(modifier=m.name)
406 weightMap, secondWeightMap=self.__getWeightMap(copyObj, copyMesh)
407 self.__processFaces(obj.name, copyMesh, weightMap, secondWeightMap)
408 bl.object.delete(copyObj)
410 def createEmptyBasicSkin(self):
411 self.__getOrCreateMorph('base', 0)
413 def __skin(self, obj):
414 if not bl.object.hasShapeKey(obj):
418 blenderMesh=bl.object.getData(obj)
422 vg=bl.object.getVertexGroup(obj, bl.MMD_SHAPE_GROUP_NAME)
426 for b in bl.object.getShapeKeys(obj):
427 if b.name==bl.BASE_SHAPE_NAME:
428 baseMorph=self.__getOrCreateMorph('base', 0)
433 v=bl.shapekey.getByIndex(b, index)
434 pos=[v[0], v[1], v[2]]
436 indices=self.vertexArray.getMappedIndex(obj.name, index)
437 for attribute, i in indices.items():
442 baseMorph.add(i, pos)
443 indexRelativeMap[i]=relativeIndex
448 #print(basis.name, len(baseMorph.offsets))
450 if len(baseMorph.offsets)==0:
454 for b in bl.object.getShapeKeys(obj):
455 if b.name==bl.BASE_SHAPE_NAME:
459 morph=self.__getOrCreateMorph(b.name, 4)
461 for index, src, dst in zip(
462 range(len(blenderMesh.vertices)),
463 bl.shapekey.get(basis),
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:
469 indices=self.vertexArray.getMappedIndex(obj.name, index)
470 for attribute, i in indices.items():
474 morph.add(indexRelativeMap[i], offset)
475 assert(len(morph.offsets)<len(baseMorph.offsets))
478 original=self.morphList[:]
480 for i, v in enumerate(englishmap.skinMap):
484 return len(englishmap.skinMap)
485 self.morphList.sort(key=getIndex)
487 def __rigidbody(self, obj):
488 if not bl.RIGID_SHAPE_TYPE in obj:
490 self.rigidbodies.append(obj)
492 def __constraint(self, obj):
493 if not bl.CONSTRAINT_A in obj:
495 self.constraints.append(obj)
497 def __getOrCreateMorph(self, name, type):
498 for m in self.morphList:
502 self.morphList.append(m)
505 def getVertexCount(self):
506 return len(self.vertexArray.positions)
510 __slots__=['index', 'name', 'ik_index',
511 'pos', 'tail', 'parent_index', 'tail_index', 'type', 'isConnect']
512 def __init__(self, name, pos, tail, isConnect):
517 self.parent_index=None
520 self.isConnect=isConnect
523 def __eq__(self, rhs):
524 return self.index==rhs.index
527 return "<Bone %s %d>" % (self.name, self.type)
529 class BoneBuilder(object):
530 __slots__=['bones', 'boneMap', 'ik_list', 'bone_groups',]
537 def getBoneGroup(self, bone):
538 for i, g in enumerate(self.bone_groups):
542 print('no gorup', bone)
545 def build(self, armatureObj):
549 bl.message("build skeleton")
550 armature=bl.object.getData(armatureObj)
555 for g in bl.object.boneGroups(armatureObj):
556 self.bone_groups.append((g.name, []))
561 for b in armature.bones.values():
565 bl.bone.getHeadLocal(b),
566 bl.bone.getTailLocal(b),
569 self.__getBone(bone, b)
571 for b in armature.bones.values():
573 self.__checkConnection(b, None)
578 pose = bl.object.getPose(armatureObj)
579 for b in pose.bones.values():
583 self.__assignBoneGroup(b, b.bone_group)
584 for c in b.constraints:
585 if bl.constraint.isIKSolver(c):
589 target=self.__boneByName(bl.constraint.ikTarget(c))
596 link=self.__boneByName(b.name)
601 chainLength=bl.constraint.ikChainLen(c)
602 for i in range(chainLength):
604 chainBone=self.__boneByName(e.name)
606 chainBone.ik_index=target.index
609 IKSolver(target, link, chainLength,
610 int(bl.constraint.ikItration(c) * 0.1),
611 bl.constraint.ikRotationWeight(c)
621 for i, v in enumerate(englishmap.boneMap):
622 if v[0]==ik.target.name:
624 return len(englishmap.boneMap)
625 self.ik_list.sort(key=getIndex)
627 def __assignBoneGroup(self, poseBone, boneGroup):
629 for g in self.bone_groups:
630 if g[0]==boneGroup.name:
631 g[1].append(poseBone.name)
633 def __checkConnection(self, b, p):
634 if bl.bone.isConnected(b):
635 parent=self.__boneByName(p.name)
636 parent.isConnect=True
639 self.__checkConnection(c, b)
645 boneMap=englishmap.boneMap
646 original=self.bones[:]
648 for i, k_v in enumerate(boneMap):
649 if k_v[0]==bone.name:
654 self.bones.sort(key=getIndex)
657 for i, b in enumerate(self.bones):
658 src=original.index(b)
661 b.index=sortMap[b.index]
663 b.parent_index=sortMap[b.parent_index]
665 b.tail_index=sortMap[b.tail_index]
667 b.ik_index=sortMap[b.ik_index]
675 if b.parent_index==None:
676 b.parent_index=0xFFFF
678 if b.type==6 or b.type==7:
680 parent=self.bones[b.parent_index]
681 #print('parnet', parent.name)
682 parent.tail_index=b.index
685 if b.tail_index==None:
690 def getIndex(self, bone):
691 for i, b in enumerate(self.bones):
696 def indexByName(self, name):
701 return self.getIndex(self.__boneByName(name))
705 def __boneByName(self, name):
706 return self.boneMap[name]
708 def __getBone(self, parent, b):
709 if len(b.children)==0:
713 for i, c in enumerate(b.children):
715 bl.bone.getHeadLocal(c),
716 bl.bone.getTailLocal(c),
717 bl.bone.isConnected(c))
720 bone.parent_index=parent.index
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)
726 def __addBone(self, bone):
727 bone.index=len(self.bones)
728 self.bones.append(bone)
729 self.boneMap[bone.name]=bone
733 __slots__=['o', 'children']
734 def __init__(self, o):
740 class Exporter(object):
752 self.armatureObj=None
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]
761 object_node_map[node.o.parent].children.append(node)
764 root=object_node_map[bl.object.getActive()]
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'
772 self.oneSkinMesh=OneSkinMesh()
773 self.__createOneSkinMesh(root)
774 bl.message(self.oneSkinMesh)
775 if len(self.oneSkinMesh.morphList)==0:
777 self.oneSkinMesh.createEmptyBasicSkin()
780 self.skeleton=BoneBuilder()
781 self.skeleton.build(self.armatureObj)
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.",
796 if node.o.type.upper()=='MESH':
797 self.oneSkinMesh.addMesh(node.o)
799 for child in node.children:
800 self.__createOneSkinMesh(child)