5 from Blender import Mathutils
10 FS_ENCODING=sys.getfilesystemencoding()
11 if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):
12 INTERNAL_ENCODING='utf-8'
14 INTERNAL_ENCODING=FS_ENCODING
17 ###############################################################################
19 ###############################################################################
21 def __init__(self, path, encoding):
22 self.io=open(path, "wb")
23 self.encoding=encoding
35 ###############################################################################
37 ###############################################################################
38 class ProgressBar(object):
39 def __init__(self, base):
40 print "#### %s ####" % base
42 self.start=Blender.sys.time()
43 self.set('<start>', 0)
45 def advance(self, message, progress):
46 self.progress+=float(progress)
49 def set(self, message, progress):
50 self.progress=float(progress)
53 def _print(self, message):
55 message="%s: %s" % (self.base, message)
56 if message.__class__ is unicode:
57 message=message.encode(FS_ENCODING)
58 Blender.Window.DrawProgressBar(self.progress, message)
62 message='finished in %.2f sec' % (Blender.sys.time()-self.start)
63 self.set(message, 1.0)
66 ###############################################################################
67 # for mqo mikoto bone.
68 ###############################################################################
69 class MikotoBone(object):
72 'iHead', 'iTail', 'iUp',
73 'vHead', 'vTail', 'vUp',
74 'parent', 'isFloating',
77 def __init__(self, face=None, vertices=None, materials=None):
85 self.name=materials[face.material_index].name.encode('utf-8')
96 sqNorm0=e01.getSqNorm()
97 sqNorm1=e12.getSqNorm()
98 sqNorm2=e20.getSqNorm()
134 self.vHead=vertices[self.iHead]
135 self.vTail=vertices[self.iTail]
136 self.vUp=vertices[self.iUp]
138 if self.name.endswith('[]'):
139 basename=self.name[0:-2]
142 self.name="%s_L" % basename
144 self.name="%s_R" % basename
147 def setParent(self, parent, floating=False):
151 parent.children.append(self)
153 def printTree(self, indent=''):
154 print("%s%s" % (indent, self.name))
155 for child in self.children:
156 child.printTree(indent+' ')
159 def build_armature(armature, mikotoBone, parent=None):
161 create a armature bone.
163 bone = Armature.Editbone()
164 bone.name = mikotoBone.name.encode('utf-8')
165 armature.bones[bone.name] = bone
167 bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a())
168 bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a())
171 if mikotoBone.isFloating:
174 bone.options=[Armature.CONNECTED]
176 for child in mikotoBone.children:
177 build_armature(armature, child, bone)
180 def create_armature(scene, mqo):
185 for o in mqo.objects:
186 if o.name.startswith('bone'):
193 for f in boneObject.faces:
195 print("invalid index_count: %d" % f.index_count)
197 b=MikotoBone(f, boneObject.vertices, mqo.materials)
201 # build mikoto bone tree
203 mikotoRoot=MikotoBone()
205 for b in tailMap.values():
206 # each bone has unique parent or is root bone.
207 if b.iHead in tailMap:
208 b.setParent(tailMap[b.iHead])
211 for e in boneObject.edges:
212 if b.iHead==e.indices[0]:
214 if e.indices[1] in tailMap:
215 b.setParent(tailMap[e.indices[1]], True)
218 elif b.iHead==e.indices[1]:
220 if e.indices[0] in tailMap:
221 b.setParent(tailMap[e.indices[0]], True)
228 b.setParent(mikotoRoot, True)
230 if len(mikotoRoot.children)==0:
231 print("no root bone")
234 if len(mikotoRoot.children)==1:
236 mikotoRoot=mikotoRoot.children[0]
237 mikotoRoot.parent=None
239 mikotoRoot.vHead=Vector3(0, 10, 0)
240 mikotoRoot.vTail=Vector3(0, 0, 0)
245 armature = Armature.New()
247 armature_object = scene.objects.new(armature)
249 act = Armature.NLA.NewAction()
250 act.setActive(armature_object)
252 armature_object.drawMode |= Object.DrawModes.XRAY
254 armature.drawType = Armature.OCTAHEDRON
255 armature.envelopes = False
256 armature.vertexGroups = True
257 armature.mirrorEdit = True
258 armature.drawNames=True
261 armature.makeEditable()
262 build_armature(armature, mikotoRoot)
265 return armature_object
268 class TrianglePlane(object):
270 mikoto方式ボーンのアンカーウェイト計算用。
276 def __init__(self, v0, v1, v2):
281 def isInsideXY(self, p):
282 v0=Vector2(self.v0.x, self.v0.y)
283 v1=Vector2(self.v1.x, self.v1.y)
284 v2=Vector2(self.v2.x, self.v2.y)
288 c0=Vector2.cross(e01, p-v0)
289 c1=Vector2.cross(e12, p-v1)
290 c2=Vector2.cross(e20, p-v2)
291 if c0>=0 and c1>=0 and c2>=0:
293 if c0<=0 and c1<=0 and c2<=0:
296 def isInsideYZ(self, p):
297 v0=Vector2(self.v0.y, self.v0.z)
298 v1=Vector2(self.v1.y, self.v1.z)
299 v2=Vector2(self.v2.y, self.v2.z)
303 c0=Vector2.cross(e01, p-v0)
304 c1=Vector2.cross(e12, p-v1)
305 c2=Vector2.cross(e20, p-v2)
306 if c0>=0 and c1>=0 and c2>=0:
308 if c0<=0 and c1<=0 and c2<=0:
311 def isInsideZX(self, p):
312 v0=Vector2(self.v0.z, self.v0.x)
313 v1=Vector2(self.v1.z, self.v1.x)
314 v2=Vector2(self.v2.z, self.v2.x)
318 c0=Vector2.cross(e01, p-v0)
319 c1=Vector2.cross(e12, p-v1)
320 c2=Vector2.cross(e20, p-v2)
321 if c0>=0 and c1>=0 and c2>=0:
323 if c0<=0 and c1<=0 and c2<=0:
327 class MikotoAnchor(object):
338 def push(self, face, vertices):
339 if face.index_count==3:
340 self.triangles.append(TrianglePlane(
341 vertices[face.indices[0]],
342 vertices[face.indices[1]],
343 vertices[face.indices[2]]
345 elif face.index_count==4:
346 self.triangles.append(TrianglePlane(
347 vertices[face.indices[0]],
348 vertices[face.indices[1]],
349 vertices[face.indices[2]]
351 self.triangles.append(TrianglePlane(
352 vertices[face.indices[2]],
353 vertices[face.indices[3]],
354 vertices[face.indices[0]]
358 self.bbox=BoundingBox(vertices[face.indices[0]])
359 for i in face.indices:
360 self.bbox.expand(vertices[i])
363 def calcWeight(self, v):
364 if not self.bbox.isInside(v):
367 if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x):
372 def anyXY(self, x, y):
373 for t in self.triangles:
374 if t.isInsideXY(Vector2(x, y)):
378 def anyYZ(self, y, z):
379 for t in self.triangles:
380 if t.isInsideYZ(Vector2(y, z)):
384 def anyZX(self, z, x):
385 for t in self.triangles:
386 if t.isInsideZX(Vector2(z, x)):
391 def create_bone_weight(scene, mqo, armature_object, objects):
393 create mikoto bone weight.
396 # setup mikoto anchors
397 for o in mqo.objects:
398 if o.name.startswith("anchor"):
400 name=mqo.materials[f.material_index].name
401 if name.endswith('[]'):
403 v=o.vertices[f.indices[0]]
407 if not name_L in anchorMap:
408 anchorMap[name_L]=MikotoAnchor()
409 anchorMap[name_L].push(f, o.vertices)
413 if not name_R in anchorMap:
414 anchorMap[name_R]=MikotoAnchor()
415 anchorMap[name_R].push(f, o.vertices)
419 if not name in anchorMap:
420 anchorMap[name]=MikotoAnchor()
421 anchorMap[name].push(f, o.vertices)
424 # add armature modifier
425 mod=o.modifiers.append(Modifier.Types.ARMATURE)
426 mod[Modifier.Settings.OBJECT] = armature_object
427 mod[Modifier.Settings.ENVELOPES] = False
429 # create vertex group
430 mesh=o.getData(mesh=True)
431 for name in anchorMap.keys():
432 mesh.addVertGroup(name)
435 # assing vertices to vertex group
437 mesh=o.getData(mesh=True)
438 for i, mvert in enumerate(mesh.verts):
440 for name, anchor in anchorMap.items():
441 weight=anchor.calcWeight(mvert.co)
443 mesh.assignVertsToGroup(
444 name, [i], weight, Mesh.AssignModes.ADD)
447 # debug orphan vertex
448 print('orphan', mvert)
451 ###############################################################################
452 def createEmptyObject(scene, name):
453 empty=scene.objects.new("Empty")
458 def createMqoMaterial(m):
459 material = Blender.Material.New(m.getName().encode(INTERNAL_ENCODING))
460 material.mode |= Blender.Material.Modes.SHADELESS
461 material.rgbCol = [m.color.r, m.color.g, m.color.b]
462 material.alpha = m.color.a
463 material.amb = m.ambient
464 material.spec = m.specular
465 material.hard = int(255 * m.power)
469 def createTexture(path):
470 image = Blender.Image.Load(path.encode(INTERNAL_ENCODING))
471 texture = Blender.Texture.New(path.encode(INTERNAL_ENCODING))
472 texture.type = Blender.Texture.Types.IMAGE
473 texture.image = image
474 return texture, image
477 def materialAddTexture(material, texture):
478 material.mode = material.mode | Blender.Material.Modes.TEXFACE
479 material.setTexture(0, texture, Blender.Texture.TexCo.UV)
482 def createMesh(scene, name):
483 mesh = Blender.Mesh.New()
484 mesh_object=scene.objects.new(mesh, name.encode(INTERNAL_ENCODING))
485 return mesh, mesh_object
488 def objectMakeParent(parent, child):
489 parent.makeParent([child])
492 def meshAddMqoGeometry(mesh, o, materials, imageMap, scale):
494 mesh.verts.extend(Mathutils.Vector(0, 0, 0)) # dummy
495 mesh.verts.extend([(v.x, -v.z, v.y) for v in o.vertices])
500 for i in xrange(face.index_count):
501 face_indices.append(face.getIndex(i)+1)
502 mesh_faces.append(face_indices)
503 #new_faces=mesh.faces.extend([face.indices for face in o.faces],
504 new_faces=mesh.faces.extend(mesh_faces,
509 # gather used materials
514 materialMap[o.faces[i].material_index]=True
516 # blender limits 16 materials per mesh
518 for i, material_index in enumerate(materialMap.keys()):
520 print("over 16 materials!")
522 mesh.materials+=[materials[material_index]]
523 materialMap[material_index]=i
526 for i, f in enumerate(o.faces):
527 if not type(new_faces[i]) is int:
530 face=mesh.faces[new_faces[i]]
533 for i in xrange(f.index_count):
534 uv_array.append(Blender.Mathutils.Vector(
540 except Exception as msg:
542 #print face.index, uv_array
545 if f.material_index in materialMap:
546 face.mat = materialMap[f.material_index]
550 # rmeove dummy 0 vertex
553 mesh.mode |= Blender.Mesh.Modes.AUTOSMOOTH
554 mesh.maxSmoothAngle = int(o.smoothing)
562 mod=mesh_object.modifiers.append(Blender.Modifier.Types.MIRROR)
564 def getTexture(m, dirname):
568 for texture in m.getTextures():
569 if texture and texture.tex and texture.tex.getImage():
570 image=texture.tex.getImage()
573 imagePath=Blender.sys.expandpath(image.getFilename())
574 if len(dirname)>0 and imagePath.startswith(dirname):
576 imagePath=imagePath[len(dirname)+1:len(imagePath)]
578 tex=" tex(\"%s\")" % imagePath
579 elif texture.mtAlpha>0:
580 aplane=" aplane(\"%s\")" % imagePath
583 def objectDuplicate(scene, obj):
584 mesh, dumy=createMesh(scene, obj.name.decode(INTERNAL_ENCODING))
585 # not apply modifiers
586 mesh.getFromObject(obj.name, 1)
588 dumy.setMatrix(obj.matrixWorld)
591 def faceVertexCount(face):
594 def faceVertices(face):
596 return [v.index for v in reversed(face.v)]
601 def faceHasUV(mesh, i, face):
602 return len(face.uv)>0
604 def faceGetUV(mesh, i, face, count):
606 return reversed(face.uv)
608 def materialToMqo(m):
609 return "\"%s\" shader(3) col(%f %f %f %f)" % (
610 m.name, m.rgbCol[0], m.rgbCol[1], m.rgbCol[2], m.alpha)
612 def faceMaterialIndex(face):
615 def objectGetData(o):
616 return o.getData(mesh=True)
618 def objectAddArmatureModifier(o, armature_object):
619 mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE)
620 mod[Blender.Modifier.Settings.OBJECT] = armature_object
621 mod[Blender.Modifier.Settings.ENVELOPES] = False
626 def objectGetPose(o):
629 def poseBoneLimit(n, b):
632 if n.startswith("knee_"):
637 b.limitMax=[180, 0, 0]
638 elif n.startswith("ankle_"):