4 Name: 'Metasequoia(.mqo)...'
\r
7 Tooltip: 'Import from Metasequoia file format (.mqo)'
\r
9 __author__=['ousttrue']
\r
10 __url__ = ["http://gunload.web.fc2.com/blender/"]
\r
16 This script imports a mqo into Blender for editing.
\r
18 0.2 20080123: update.
\r
19 0.3 20091125: modify for linux.
\r
20 0.4 20100310: rewrite.
\r
21 0.5 20100311: create armature from mikoto bone.
\r
22 0.6 20100505: C extension.
\r
23 0.7 20100606: integrate 2.4 and 2.5.
\r
24 0.8 20100619: fix multibyte object name.
\r
25 0.9 20100626: refactoring.
\r
26 2.0 20100724: update for Blender2.53.
\r
27 2.1 20100731: add full python module.
\r
28 2.2 20101005: update for Blender2.54.
\r
29 2.3 20101228: update for Blender2.55.
\r
30 2.4 20110429: update for Blender2.57b.
\r
31 2.6 20110918: update for Blender2.59.
\r
35 'category': 'Import/Export',
\r
36 'name': 'Import: Metasequioa Model Format (.mqo)',
\r
37 'author': 'ousttrue',
\r
39 'blender': (2, 5, 3),
\r
40 'location': 'File > Import',
\r
41 'description': 'Import from the Metasequioa Model Format (.mqo)',
\r
42 'warning': '', # used for warning icon and text in addons panel
\r
43 'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',
\r
44 'tracker_url': 'http://sourceforge.jp/ticket/newticket.php?group_id=5081',
\r
52 from .meshio import mqo
\r
53 print('use meshio C module')
\r
56 from .pymeshio import mqo
\r
62 from . import bl25 as bl
\r
64 def createMqoMaterial(m):
\r
65 material = bpy.data.materials.new(m.getName())
\r
68 material.diffuse_shader='FRESNEL'
\r
70 material.diffuse_shader='LAMBERT'
\r
72 material.diffuse_color=[m.color.r, m.color.g, m.color.b]
\r
73 material.diffuse_intensity=m.diffuse
\r
74 material.alpha=m.color.a
\r
76 material.ambient = m.ambient
\r
77 #material.specular = m.specular
\r
78 material.emit=m.emit
\r
79 material.use_shadeless=True
\r
83 def has_mikoto(mqo):
\r
84 #for o in mqo.objects:
\r
85 # if o.getName().startswith('bone'):
\r
87 # if o.getName().startswith('sdef'):
\r
89 # if o.getName().startswith('anchor'):
\r
94 def __createMaterials(mqo, directory):
\r
96 create blender materials and renturn material list.
\r
101 if len(mqo.materials)>0:
\r
102 for material_index, m in enumerate(mqo.materials):
\r
104 material=createMqoMaterial(m)
\r
105 materials.append(material)
\r
107 texture_name=m.getTexture()
\r
108 if texture_name!='':
\r
109 if texture_name in textureMap:
\r
110 texture=textureMap[texture_name]
\r
112 # load texture image
\r
113 if os.path.isabs(texture_name):
\r
115 path = texture_name
\r
118 path = os.path.join(directory, texture_name)
\r
120 path=path.replace("\\", "/")
\r
121 if os.path.exists(path):
\r
122 print("create texture:", path)
\r
123 texture, image=bl.texture.create(path)
\r
124 textureMap[texture_name]=texture
\r
125 imageMap[material_index]=image
\r
127 print("%s not exits" % path)
\r
129 bl.material.addTexture(material, texture)
\r
133 return materials, imageMap
\r
136 def __createObjects(mqo, root, materials, imageMap, scale):
\r
138 create blender mesh objects.
\r
143 for o in mqo.objects:
\r
144 mesh, mesh_object=bl.mesh.create(o.getName())
\r
147 stack_depth=len(stack)-1
\r
148 #print(o.depth, stack_depth)
\r
149 if o.depth<stack_depth:
\r
150 for i in range(stack_depth-o.depth):
\r
152 bl.object.makeParent(stack[-1], mesh_object)
\r
153 stack.append(mesh_object)
\r
155 if o.getName().startswith('sdef'):
\r
156 objects.append(mesh_object)
\r
157 elif o.getName().startswith('anchor'):
\r
158 bl.object.setLayerMask(mesh_object, [0, 1])
\r
159 elif o.getName().startswith('bone'):
\r
160 bl.object.setLayerMask(mesh_object, [0, 1])
\r
163 vertices=[(v.x * scale, -v.z * scale, v.y * scale) for v in o.vertices]
\r
169 for i in reversed(range(f.index_count)):
\r
170 face_indices.append(f.getIndex(i))
\r
171 faces.append(face_indices)
\r
172 materialMap[f.material_index]=True
\r
173 bl.mesh.addGeometry(mesh, vertices, faces)
\r
175 # blender limits 16 materials per mesh
\r
176 for i, material_index in enumerate(materialMap.keys()):
\r
179 print("over 16 materials!")
\r
181 bl.mesh.addMaterial(mesh, materials[material_index])
\r
182 materialMap[material_index]=i
\r
185 assert(len(o.faces)==len(mesh.faces))
\r
186 bl.mesh.addUV(mesh)
\r
187 for i, (f, face) in enumerate(zip(o.faces, mesh.faces)):
\r
191 for j in reversed(range(f.index_count)):
\r
192 uv_array.append((f.getUV(j).x, 1.0-f.getUV(j).y))
\r
193 bl.mesh.setFaceUV(mesh, i, face, uv_array,
\r
194 imageMap.get(f.material_index, None))
\r
195 if f.material_index in materialMap:
\r
196 bl.face.setMaterial(face, materialMap[f.material_index])
\r
197 bl.face.setSmooth(face, True)
\r
201 bl.modifier.addMirror(mesh_object)
\r
204 bl.mesh.setSmooth(mesh, o.smoothing)
\r
207 bl.mesh.recalcNormals(mesh_object)
\r
212 ###############################################################################
\r
213 # for mqo mikoto bone.
\r
214 ###############################################################################
\r
215 class MikotoBone(object):
\r
218 'iHead', 'iTail', 'iUp',
\r
219 'vHead', 'vTail', 'vUp',
\r
220 'parent', 'isFloating',
\r
223 def __init__(self, face=None, vertices=None, materials=None):
\r
225 self.isFloating=False
\r
231 self.name=materials[face.material_index].name.encode('utf-8')
\r
233 i0=face.getIndex(0)
\r
234 i1=face.getIndex(1)
\r
235 i2=face.getIndex(2)
\r
242 sqNorm0=e01.getSqNorm()
\r
243 sqNorm1=e12.getSqNorm()
\r
244 sqNorm2=e20.getSqNorm()
\r
245 if sqNorm0>sqNorm1:
\r
246 if sqNorm1>sqNorm2:
\r
252 if sqNorm0>sqNorm2:
\r
264 if sqNorm1<sqNorm2:
\r
270 if sqNorm0<sqNorm2:
\r
280 self.vHead=vertices[self.iHead]
\r
281 self.vTail=vertices[self.iTail]
\r
282 self.vUp=vertices[self.iUp]
\r
284 if self.name.endswith('[]'):
\r
285 basename=self.name[0:-2]
\r
288 self.name="%s_L" % basename
\r
290 self.name="%s_R" % basename
\r
293 def setParent(self, parent, floating=False):
\r
295 self.isFloating=True
\r
297 parent.children.append(self)
\r
299 def printTree(self, indent=''):
\r
300 print("%s%s" % (indent, self.name))
\r
301 for child in self.children:
\r
302 child.printTree(indent+' ')
\r
305 def build_armature(armature, mikotoBone, parent=None):
\r
307 create a armature bone.
\r
309 bone = Armature.Editbone()
\r
310 bone.name = mikotoBone.name.encode('utf-8')
\r
311 armature.bones[bone.name] = bone
\r
313 bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a())
\r
314 bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a())
\r
317 if mikotoBone.isFloating:
\r
320 bone.options=[Armature.CONNECTED]
\r
322 for child in mikotoBone.children:
\r
323 build_armature(armature, child, bone)
\r
326 def create_armature(mqo):
\r
331 for o in mqo.objects:
\r
332 if o.name.startswith('bone'):
\r
339 for f in boneObject.faces:
\r
340 if f.index_count!=3:
\r
341 print("invalid index_count: %d" % f.index_count)
\r
343 b=MikotoBone(f, boneObject.vertices, mqo.materials)
\r
346 ####################
\r
347 # build mikoto bone tree
\r
348 ####################
\r
349 mikotoRoot=MikotoBone()
\r
351 for b in tailMap.values():
\r
352 # each bone has unique parent or is root bone.
\r
353 if b.iHead in tailMap:
\r
354 b.setParent(tailMap[b.iHead])
\r
357 for e in boneObject.edges:
\r
358 if b.iHead==e.indices[0]:
\r
360 if e.indices[1] in tailMap:
\r
361 b.setParent(tailMap[e.indices[1]], True)
\r
364 elif b.iHead==e.indices[1]:
\r
366 if e.indices[0] in tailMap:
\r
367 b.setParent(tailMap[e.indices[0]], True)
\r
374 b.setParent(mikotoRoot, True)
\r
376 if len(mikotoRoot.children)==0:
\r
377 print("no root bone")
\r
380 if len(mikotoRoot.children)==1:
\r
382 mikotoRoot=mikotoRoot.children[0]
\r
383 mikotoRoot.parent=None
\r
385 mikotoRoot.vHead=Vector3(0, 10, 0)
\r
386 mikotoRoot.vTail=Vector3(0, 0, 0)
\r
388 ####################
\r
390 ####################
\r
391 armature = Armature.New()
\r
393 armature_object = scene.objects.new(armature)
\r
395 act = Armature.NLA.NewAction()
\r
396 act.setActive(armature_object)
\r
398 armature_object.drawMode |= Object.DrawModes.XRAY
\r
399 # armature settings
\r
400 armature.drawType = Armature.OCTAHEDRON
\r
401 armature.envelopes = False
\r
402 armature.vertexGroups = True
\r
403 armature.mirrorEdit = True
\r
404 armature.drawNames=True
\r
407 armature.makeEditable()
\r
408 build_armature(armature, mikotoRoot)
\r
411 return armature_object
\r
414 class TrianglePlane(object):
\r
416 mikoto
\e$BJ}<0%\!<%s$N%"%s%+!<%&%'%$%H7W;;MQ!#
\e(B
\r
419 __slots__=['normal',
\r
422 def __init__(self, v0, v1, v2):
\r
427 def isInsideXY(self, p):
\r
428 v0=Vector2(self.v0.x, self.v0.y)
\r
429 v1=Vector2(self.v1.x, self.v1.y)
\r
430 v2=Vector2(self.v2.x, self.v2.y)
\r
434 c0=Vector2.cross(e01, p-v0)
\r
435 c1=Vector2.cross(e12, p-v1)
\r
436 c2=Vector2.cross(e20, p-v2)
\r
437 if c0>=0 and c1>=0 and c2>=0:
\r
439 if c0<=0 and c1<=0 and c2<=0:
\r
442 def isInsideYZ(self, p):
\r
443 v0=Vector2(self.v0.y, self.v0.z)
\r
444 v1=Vector2(self.v1.y, self.v1.z)
\r
445 v2=Vector2(self.v2.y, self.v2.z)
\r
449 c0=Vector2.cross(e01, p-v0)
\r
450 c1=Vector2.cross(e12, p-v1)
\r
451 c2=Vector2.cross(e20, p-v2)
\r
452 if c0>=0 and c1>=0 and c2>=0:
\r
454 if c0<=0 and c1<=0 and c2<=0:
\r
457 def isInsideZX(self, p):
\r
458 v0=Vector2(self.v0.z, self.v0.x)
\r
459 v1=Vector2(self.v1.z, self.v1.x)
\r
460 v2=Vector2(self.v2.z, self.v2.x)
\r
464 c0=Vector2.cross(e01, p-v0)
\r
465 c1=Vector2.cross(e12, p-v1)
\r
466 c2=Vector2.cross(e20, p-v2)
\r
467 if c0>=0 and c1>=0 and c2>=0:
\r
469 if c0<=0 and c1<=0 and c2<=0:
\r
473 class MikotoAnchor(object):
\r
475 mikoto
\e$BJ}<0%9%1%k%H%s$N%"%s%+!<!#
\e(B
\r
478 "triangles", "bbox",
\r
480 def __init__(self):
\r
484 def push(self, face, vertices):
\r
485 if face.index_count==3:
\r
486 self.triangles.append(TrianglePlane(
\r
487 vertices[face.indices[0]],
\r
488 vertices[face.indices[1]],
\r
489 vertices[face.indices[2]]
\r
491 elif face.index_count==4:
\r
492 self.triangles.append(TrianglePlane(
\r
493 vertices[face.indices[0]],
\r
494 vertices[face.indices[1]],
\r
495 vertices[face.indices[2]]
\r
497 self.triangles.append(TrianglePlane(
\r
498 vertices[face.indices[2]],
\r
499 vertices[face.indices[3]],
\r
500 vertices[face.indices[0]]
\r
504 self.bbox=BoundingBox(vertices[face.indices[0]])
\r
505 for i in face.indices:
\r
506 self.bbox.expand(vertices[i])
\r
509 def calcWeight(self, v):
\r
510 if not self.bbox.isInside(v):
\r
513 if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x):
\r
518 def anyXY(self, x, y):
\r
519 for t in self.triangles:
\r
520 if t.isInsideXY(Vector2(x, y)):
\r
524 def anyYZ(self, y, z):
\r
525 for t in self.triangles:
\r
526 if t.isInsideYZ(Vector2(y, z)):
\r
530 def anyZX(self, z, x):
\r
531 for t in self.triangles:
\r
532 if t.isInsideZX(Vector2(z, x)):
\r
537 def create_bone_weight(scene, mqo, armature_object, objects):
\r
539 create mikoto bone weight.
\r
542 # setup mikoto anchors
\r
543 for o in mqo.objects:
\r
544 if o.name.startswith("anchor"):
\r
546 name=mqo.materials[f.material_index].name
\r
547 if name.endswith('[]'):
\r
548 basename=name[0:-2]
\r
549 v=o.vertices[f.indices[0]]
\r
552 name_L=basename+'_L'
\r
553 if not name_L in anchorMap:
\r
554 anchorMap[name_L]=MikotoAnchor()
\r
555 anchorMap[name_L].push(f, o.vertices)
\r
558 name_R=basename+'_R'
\r
559 if not name_R in anchorMap:
\r
560 anchorMap[name_R]=MikotoAnchor()
\r
561 anchorMap[name_R].push(f, o.vertices)
\r
563 print("no side", v)
\r
565 if not name in anchorMap:
\r
566 anchorMap[name]=MikotoAnchor()
\r
567 anchorMap[name].push(f, o.vertices)
\r
570 # add armature modifier
\r
571 mod=o.modifiers.append(Modifier.Types.ARMATURE)
\r
572 mod[Modifier.Settings.OBJECT] = armature_object
\r
573 mod[Modifier.Settings.ENVELOPES] = False
\r
574 o.makeDisplayList()
\r
575 # create vertex group
\r
576 mesh=o.getData(mesh=True)
\r
577 for name in anchorMap.keys():
\r
578 mesh.addVertGroup(name)
\r
581 # assing vertices to vertex group
\r
583 mesh=o.getData(mesh=True)
\r
584 for i, mvert in enumerate(mesh.verts):
\r
586 for name, anchor in anchorMap.items():
\r
587 weight=anchor.calcWeight(mvert.co)
\r
589 mesh.assignVertsToGroup(
\r
590 name, [i], weight, Mesh.AssignModes.ADD)
\r
593 # debug orphan vertex
\r
594 print('orphan', mvert)
\r
598 def _execute(filepath='', scale=0.1):
\r
601 if not io.read(filepath):
\r
602 bl.message("fail to load %s" % filepath)
\r
606 materials, imageMap=__createMaterials(io, os.path.dirname(filepath))
\r
607 if len(materials)==0:
\r
608 materials.append(bl.material.create('default'))
\r
611 root=bl.object.createEmpty(os.path.basename(filepath))
\r
612 objects=__createObjects(io, root, materials, imageMap, scale)
\r
615 # create mikoto bone
\r
616 armature_object=create_armature(io)
\r
617 if armature_object:
\r
618 root.makeParent([armature_object])
\r
620 # create bone weight
\r
621 create_bone_weight(io, armature_object, objects)
\r