4 Name: 'Metasequoia(.mqo)...'
7 Tooltip: 'Import from Metasequoia file format (.mqo)'
9 __author__=['ousttrue']
10 __url__ = ["http://gunload.web.fc2.com/blender/"]
15 This script imports a mqo into Blender for editing.
18 20091125: modify for linux.
20 20100311: create armature from mikoto bone.
21 20100505: C extension.
22 20100606: integrate 2.4 and 2.5.
23 20100619: fix multibyte object name.
24 20100626: refactoring.
25 20100724: update for Blender2.53.
26 20100731: add full python module.
27 20101005: update for Blender2.54.
28 20101228: update for Blender2.55.
29 20110429: update for Blender2.57b.
30 20110918: update for Blender2.59.
31 20111002: update for pymeshio-2.1.0
35 'category': 'Import/Export',
36 'name': 'Import: Metasequioa Model Format (.mqo)',
40 'location': 'File > Import',
41 'description': 'Import from the Metasequioa Model Format (.mqo)',
42 'warning': '', # used for warning icon and text in addons panel
43 'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',
44 'tracker_url': 'http://sourceforge.jp/ticket/newticket.php?group_id=5081',
49 from .pymeshio.mqo import reader
57 def createMqoMaterial(m):
58 material = bpy.data.materials.new(m.name.decode("cp932"))
61 material.diffuse_shader='FRESNEL'
63 material.diffuse_shader='LAMBERT'
65 material.diffuse_color=[m.color.r, m.color.g, m.color.b]
66 material.diffuse_intensity=m.diffuse
67 material.alpha=m.color.a
69 material.ambient = m.ambient
70 #material.specular = m.specular
72 material.use_shadeless=True
80 def __createMaterials(mqo, directory):
82 create blender materials and renturn material list.
87 if len(mqo.materials)>0:
88 for material_index, m in enumerate(mqo.materials):
90 material=createMqoMaterial(m)
91 materials.append(material)
93 texture_name=m.tex.decode("cp932")
95 if texture_name in textureMap:
96 texture=textureMap[texture_name]
99 if os.path.isabs(texture_name):
104 path = os.path.join(directory, texture_name)
106 path=path.replace("\\", "/")
107 if os.path.exists(path):
108 print("create texture:", path)
109 texture, image=bl.texture.create(path)
110 textureMap[texture_name]=texture
111 imageMap[material_index]=image
113 print("%s not exits" % path)
115 bl.material.addTexture(material, texture)
119 return materials, imageMap
122 def __createObjects(mqo, root, materials, imageMap, scale):
124 create blender mesh objects.
129 for o in mqo.objects:
130 mesh, mesh_object=bl.mesh.create(o.name.decode("cp932"))
133 stack_depth=len(stack)-1
134 #print(o.depth, stack_depth)
135 if o.depth<stack_depth:
136 for i in range(stack_depth-o.depth):
138 bl.object.makeParent(stack[-1], mesh_object)
139 stack.append(mesh_object)
141 obj_name=o.name.decode("cp932")
142 if obj_name.startswith('sdef'):
143 objects.append(mesh_object)
144 elif obj_name.startswith('anchor'):
145 bl.object.setLayerMask(mesh_object, [0, 1])
146 elif obj_name.startswith('bone'):
147 bl.object.setLayerMask(mesh_object, [0, 1])
150 vertices=[(v.x * scale, -v.z * scale, v.y * scale) for v in o.vertices]
156 for i in reversed(range(f.index_count)):
157 face_indices.append(f.getIndex(i))
158 faces.append(face_indices)
159 materialMap[f.material_index]=True
160 bl.mesh.addGeometry(mesh, vertices, faces)
162 # blender limits 16 materials per mesh
163 for i, material_index in enumerate(materialMap.keys()):
166 print("over 16 materials!")
168 bl.mesh.addMaterial(mesh, materials[material_index])
169 materialMap[material_index]=i
172 assert(len(o.faces)==len(mesh.faces))
174 for i, (f, face) in enumerate(zip(o.faces, mesh.faces)):
178 for j in reversed(range(f.index_count)):
179 uv_array.append((f.getUV(j).x, 1.0-f.getUV(j).y))
180 bl.mesh.setFaceUV(mesh, i, face, uv_array,
181 imageMap.get(f.material_index, None))
182 if f.material_index in materialMap:
183 bl.face.setMaterial(face, materialMap[f.material_index])
184 bl.face.setSmooth(face, True)
188 bl.modifier.addMirror(mesh_object)
191 bl.mesh.setSmooth(mesh, o.smoothing)
194 bl.mesh.recalcNormals(mesh_object)
199 ###############################################################################
200 # for mqo mikoto bone.
201 ###############################################################################
202 class MikotoBone(object):
205 'iHead', 'iTail', 'iUp',
206 'vHead', 'vTail', 'vUp',
207 'parent', 'isFloating',
210 def __init__(self, face=None, vertices=None, materials=None):
212 self.isFloating=False
218 self.name=materials[face.material_index].name.encode('utf-8')
229 sqNorm0=e01.getSqNorm()
230 sqNorm1=e12.getSqNorm()
231 sqNorm2=e20.getSqNorm()
267 self.vHead=vertices[self.iHead]
268 self.vTail=vertices[self.iTail]
269 self.vUp=vertices[self.iUp]
271 if self.name.endswith('[]'):
272 basename=self.name[0:-2]
275 self.name="%s_L" % basename
277 self.name="%s_R" % basename
280 def setParent(self, parent, floating=False):
284 parent.children.append(self)
286 def printTree(self, indent=''):
287 print("%s%s" % (indent, self.name))
288 for child in self.children:
289 child.printTree(indent+' ')
292 def build_armature(armature, mikotoBone, parent=None):
294 create a armature bone.
296 bone = Armature.Editbone()
297 bone.name = mikotoBone.name.encode('utf-8')
298 armature.bones[bone.name] = bone
300 bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a())
301 bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a())
304 if mikotoBone.isFloating:
307 bone.options=[Armature.CONNECTED]
309 for child in mikotoBone.children:
310 build_armature(armature, child, bone)
313 def create_armature(mqo):
318 for o in mqo.objects:
319 if o.name.startswith('bone'):
326 for f in boneObject.faces:
328 print("invalid index_count: %d" % f.index_count)
330 b=MikotoBone(f, boneObject.vertices, mqo.materials)
334 # build mikoto bone tree
336 mikotoRoot=MikotoBone()
338 for b in tailMap.values():
339 # each bone has unique parent or is root bone.
340 if b.iHead in tailMap:
341 b.setParent(tailMap[b.iHead])
344 for e in boneObject.edges:
345 if b.iHead==e.indices[0]:
347 if e.indices[1] in tailMap:
348 b.setParent(tailMap[e.indices[1]], True)
351 elif b.iHead==e.indices[1]:
353 if e.indices[0] in tailMap:
354 b.setParent(tailMap[e.indices[0]], True)
361 b.setParent(mikotoRoot, True)
363 if len(mikotoRoot.children)==0:
364 print("no root bone")
367 if len(mikotoRoot.children)==1:
369 mikotoRoot=mikotoRoot.children[0]
370 mikotoRoot.parent=None
372 mikotoRoot.vHead=Vector3(0, 10, 0)
373 mikotoRoot.vTail=Vector3(0, 0, 0)
378 armature = Armature.New()
380 armature_object = scene.objects.new(armature)
382 act = Armature.NLA.NewAction()
383 act.setActive(armature_object)
385 armature_object.drawMode |= Object.DrawModes.XRAY
387 armature.drawType = Armature.OCTAHEDRON
388 armature.envelopes = False
389 armature.vertexGroups = True
390 armature.mirrorEdit = True
391 armature.drawNames=True
394 armature.makeEditable()
395 build_armature(armature, mikotoRoot)
398 return armature_object
401 class TrianglePlane(object):
403 mikoto
\e$BJ}<0%\!<%s$N%"%s%+!<%&%'%$%H7W;;MQ!#
\e(B
409 def __init__(self, v0, v1, v2):
414 def isInsideXY(self, p):
415 v0=Vector2(self.v0.x, self.v0.y)
416 v1=Vector2(self.v1.x, self.v1.y)
417 v2=Vector2(self.v2.x, self.v2.y)
421 c0=Vector2.cross(e01, p-v0)
422 c1=Vector2.cross(e12, p-v1)
423 c2=Vector2.cross(e20, p-v2)
424 if c0>=0 and c1>=0 and c2>=0:
426 if c0<=0 and c1<=0 and c2<=0:
429 def isInsideYZ(self, p):
430 v0=Vector2(self.v0.y, self.v0.z)
431 v1=Vector2(self.v1.y, self.v1.z)
432 v2=Vector2(self.v2.y, self.v2.z)
436 c0=Vector2.cross(e01, p-v0)
437 c1=Vector2.cross(e12, p-v1)
438 c2=Vector2.cross(e20, p-v2)
439 if c0>=0 and c1>=0 and c2>=0:
441 if c0<=0 and c1<=0 and c2<=0:
444 def isInsideZX(self, p):
445 v0=Vector2(self.v0.z, self.v0.x)
446 v1=Vector2(self.v1.z, self.v1.x)
447 v2=Vector2(self.v2.z, self.v2.x)
451 c0=Vector2.cross(e01, p-v0)
452 c1=Vector2.cross(e12, p-v1)
453 c2=Vector2.cross(e20, p-v2)
454 if c0>=0 and c1>=0 and c2>=0:
456 if c0<=0 and c1<=0 and c2<=0:
460 class MikotoAnchor(object):
462 mikoto
\e$BJ}<0%9%1%k%H%s$N%"%s%+!<!#
\e(B
471 def push(self, face, vertices):
472 if face.index_count==3:
473 self.triangles.append(TrianglePlane(
474 vertices[face.indices[0]],
475 vertices[face.indices[1]],
476 vertices[face.indices[2]]
478 elif face.index_count==4:
479 self.triangles.append(TrianglePlane(
480 vertices[face.indices[0]],
481 vertices[face.indices[1]],
482 vertices[face.indices[2]]
484 self.triangles.append(TrianglePlane(
485 vertices[face.indices[2]],
486 vertices[face.indices[3]],
487 vertices[face.indices[0]]
491 self.bbox=BoundingBox(vertices[face.indices[0]])
492 for i in face.indices:
493 self.bbox.expand(vertices[i])
496 def calcWeight(self, v):
497 if not self.bbox.isInside(v):
500 if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x):
505 def anyXY(self, x, y):
506 for t in self.triangles:
507 if t.isInsideXY(Vector2(x, y)):
511 def anyYZ(self, y, z):
512 for t in self.triangles:
513 if t.isInsideYZ(Vector2(y, z)):
517 def anyZX(self, z, x):
518 for t in self.triangles:
519 if t.isInsideZX(Vector2(z, x)):
524 def create_bone_weight(scene, mqo, armature_object, objects):
526 create mikoto bone weight.
529 # setup mikoto anchors
530 for o in mqo.objects:
531 if o.name.startswith("anchor"):
533 name=mqo.materials[f.material_index].name
534 if name.endswith('[]'):
536 v=o.vertices[f.indices[0]]
540 if not name_L in anchorMap:
541 anchorMap[name_L]=MikotoAnchor()
542 anchorMap[name_L].push(f, o.vertices)
546 if not name_R in anchorMap:
547 anchorMap[name_R]=MikotoAnchor()
548 anchorMap[name_R].push(f, o.vertices)
552 if not name in anchorMap:
553 anchorMap[name]=MikotoAnchor()
554 anchorMap[name].push(f, o.vertices)
557 # add armature modifier
558 mod=o.modifiers.append(Modifier.Types.ARMATURE)
559 mod[Modifier.Settings.OBJECT] = armature_object
560 mod[Modifier.Settings.ENVELOPES] = False
562 # create vertex group
563 mesh=o.getData(mesh=True)
564 for name in anchorMap.keys():
565 mesh.addVertGroup(name)
568 # assing vertices to vertex group
570 mesh=o.getData(mesh=True)
571 for i, mvert in enumerate(mesh.verts):
573 for name, anchor in anchorMap.items():
574 weight=anchor.calcWeight(mvert.co)
576 mesh.assignVertsToGroup(
577 name, [i], weight, Mesh.AssignModes.ADD)
580 # debug orphan vertex
581 print('orphan', mvert)
585 def _execute(filepath='', scale=0.1):
587 model=reader.read_from_file(filepath)
589 bl.message("fail to load %s" % filepath)
593 materials, imageMap=__createMaterials(model, os.path.dirname(filepath))
594 if len(materials)==0:
595 materials.append(bl.material.create('default'))
598 root=bl.object.createEmpty(os.path.basename(filepath))
599 objects=__createObjects(model, root, materials, imageMap, scale)
601 if has_mikoto(model):
603 armature_object=create_armature(model)
605 root.makeParent([armature_object])
608 create_bone_weight(model, armature_object, objects)