OSDN Git Service

add englishmap.
[meshio/meshio.git] / swig / blender / mqo_import.py
1 #!BPY\r
2 # coding: utf-8\r
3 """ \r
4 Name: 'Metasequoia(.mqo)...'\r
5 Blender: 245\r
6 Group: 'Import'\r
7 Tooltip: 'Import from Metasequoia file format (.mqo)'\r
8 """\r
9 __author__=['ousttrue']\r
10 __url__ = ["http://gunload.web.fc2.com/blender/"]\r
11 __version__= '2.0'\r
12 __bpydoc__= '''\\r
13 \r
14 MQO Importer\r
15 \r
16 This script imports a mqo into Blender for editing.\r
17 \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 '''\r
29 \r
30 bl_addon_info = {\r
31         'category': 'Import/Export',\r
32         'name': 'Import: Metasequioa Model Format (.mqo)',\r
33         'author': 'ousttrue',\r
34         'version': '2.0',\r
35         'blender': (2, 5, 3),\r
36         'location': 'File > Import',\r
37         'description': 'Import from the Metasequioa Model Format (.mqo)',\r
38         'warning': '', # used for warning icon and text in addons panel\r
39         'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',\r
40         }\r
41 \r
42 import os\r
43 import sys\r
44 \r
45 try:\r
46     # C extension\r
47     from meshio import mqo\r
48     print('use meshio C module')\r
49 except ImportError:\r
50     # full python\r
51     from pymeshio import mqo\r
52 \r
53 def isBlender24():\r
54     return sys.version_info[0]<3\r
55 \r
56 if isBlender24():\r
57     # for 2.4\r
58     import Blender\r
59     from Blender import Mathutils\r
60     import bpy\r
61 \r
62     # wrapper\r
63     import bl24 as bl\r
64 \r
65     def createMqoMaterial(m):\r
66         material = Blender.Material.New(\r
67                 m.getName().encode(bl.INTERNAL_ENCODING))\r
68         #material.mode |= Blender.Material.Modes.SHADELESS\r
69         # diffuse\r
70         material.rgbCol = [m.color.r, m.color.g, m.color.b]\r
71         material.alpha = m.color.a\r
72         # other\r
73         material.amb=m.ambient\r
74         material.spec=m.specular\r
75         material.hard=int(255 * m.power)\r
76         material.emit=m.emit\r
77         return material\r
78 \r
79 else:\r
80     # for 2.5\r
81     import bpy\r
82 \r
83     # wrapper\r
84     import bl25 as bl\r
85 \r
86     def createMqoMaterial(m):\r
87         material = bpy.data.materials.new(m.getName())\r
88         # shader\r
89         if m.shader==1:\r
90             material.diffuse_shader='FRESNEL'\r
91         else:\r
92             material.diffuse_shader='LAMBERT'\r
93         # diffuse\r
94         material.diffuse_color=[m.color.r, m.color.g, m.color.b]\r
95         material.diffuse_intensity=m.diffuse\r
96         material.alpha=m.color.a\r
97         # other\r
98         material.ambient = m.ambient\r
99         #material.specular = m.specular\r
100         material.emit=m.emit\r
101         return material\r
102 \r
103 \r
104 def has_mikoto(mqo):\r
105     #for o in mqo.objects:\r
106     #    if o.getName().startswith('bone'):\r
107     #        return True\r
108     #    if o.getName().startswith('sdef'):\r
109     #        return True\r
110     #    if o.getName().startswith('anchor'):\r
111     #        return True\r
112     return False\r
113 \r
114 \r
115 def __createMaterials(mqo, directory):\r
116     """\r
117     create blender materials and renturn material list.\r
118     """\r
119     materials = []\r
120     textureMap={}\r
121     imageMap={}\r
122     if len(mqo.materials)>0:\r
123         for material_index, m in enumerate(mqo.materials):\r
124             # material\r
125             material=createMqoMaterial(m)\r
126             materials.append(material)\r
127             # texture\r
128             texture_name=m.getTexture()\r
129             if texture_name!='':\r
130                 if texture_name in textureMap:\r
131                     texture=textureMap[texture_name]\r
132                 else:\r
133                     # load texture image\r
134                     if os.path.isabs(texture_name):\r
135                         # absolute\r
136                         path = texture_name\r
137                     else:\r
138                         # relative\r
139                         path = os.path.join(directory, texture_name)\r
140                     # texture\r
141                     if os.path.exists(path):\r
142                         print("create texture:", path)\r
143                         texture, image=bl.texture.create(path)\r
144                         textureMap[texture_name]=texture\r
145                         imageMap[material_index]=image\r
146                     else:\r
147                         print("%s not exits" % path)\r
148                         continue\r
149                 bl.material.addTexture(material, texture)\r
150     else:\r
151         # default material\r
152         pass\r
153     return materials, imageMap\r
154 \r
155 \r
156 def __createObjects(mqo, root, materials, imageMap, scale):\r
157     """\r
158     create blender mesh objects.\r
159     """\r
160     # tree stack\r
161     stack=[root]    \r
162     objects=[]\r
163     for o in mqo.objects:\r
164         mesh, mesh_object=bl.mesh.create(o.getName())\r
165 \r
166         # add hierarchy\r
167         stack_depth=len(stack)-1\r
168         #print(o.depth, stack_depth)\r
169         if o.depth<stack_depth:\r
170             for i in range(stack_depth-o.depth):\r
171                 stack.pop()\r
172         bl.object.makeParent(stack[-1], mesh_object)\r
173         stack.append(mesh_object)\r
174 \r
175         if o.getName().startswith('sdef'):\r
176             objects.append(mesh_object)\r
177         elif o.getName().startswith('anchor'):\r
178             bl.object.setLayerMask(mesh_object, [0, 1])\r
179         elif o.getName().startswith('bone'):\r
180             bl.object.setLayerMask(mesh_object, [0, 1])\r
181 \r
182         # geometry\r
183         vertices=[(v.x * scale, -v.z * scale, v.y * scale) for v in o.vertices]\r
184         faces=[]\r
185         materialMap={}\r
186         for f in o.faces:\r
187             face_indices=[]\r
188             # flip face\r
189             for i in reversed(range(f.index_count)):\r
190                 face_indices.append(f.getIndex(i))\r
191             faces.append(face_indices)\r
192             materialMap[f.material_index]=True\r
193         bl.mesh.addGeometry(mesh, vertices, faces)\r
194 \r
195         # blender limits 16 materials per mesh\r
196         for i, material_index in enumerate(materialMap.keys()):\r
197             if i>=16:\r
198                 # split a mesh ?\r
199                 print("over 16 materials!")\r
200                 break\r
201             bl.mesh.addMaterial(mesh, materials[material_index])\r
202             materialMap[material_index]=i\r
203  \r
204         # set face params\r
205         assert(len(o.faces)==len(mesh.faces))\r
206         bl.mesh.addUV(mesh)\r
207         for i, (f, face) in enumerate(zip(o.faces, mesh.faces)):\r
208             uv_array=[]\r
209             # ToDo FIX\r
210             # flip face\r
211             for j in reversed(range(f.index_count)):\r
212                 uv_array.append((f.getUV(j).x, 1.0-f.getUV(j).y))\r
213             bl.mesh.setFaceUV(mesh, i, face, uv_array, \r
214                     imageMap.get(f.material_index, None))\r
215             if f.material_index in materialMap:\r
216                 bl.face.setMaterial(face, materialMap[f.material_index])\r
217             bl.face.setSmooth(face, True)\r
218 \r
219         # mirror modifier\r
220         if o.mirror:\r
221             bl.modifier.addMirror(mesh_object)\r
222 \r
223         # set smoothing\r
224         bl.mesh.setSmooth(mesh, o.smoothing)\r
225 \r
226         # calc normal\r
227         bl.mesh.recalcNormals(mesh_object)\r
228 \r
229     return objects\r
230 \r
231 \r
232 ###############################################################################\r
233 # for mqo mikoto bone.\r
234 ###############################################################################\r
235 class MikotoBone(object):\r
236     __slots__=[\r
237             'name',\r
238             'iHead', 'iTail', 'iUp',\r
239             'vHead', 'vTail', 'vUp',\r
240             'parent', 'isFloating',\r
241             'children',\r
242             ]\r
243     def __init__(self, face=None, vertices=None, materials=None):\r
244         self.parent=None\r
245         self.isFloating=False\r
246         self.children=[]\r
247         if not face:\r
248             self.name='root'\r
249             return\r
250 \r
251         self.name=materials[face.material_index].name.encode('utf-8')\r
252 \r
253         i0=face.getIndex(0)\r
254         i1=face.getIndex(1)\r
255         i2=face.getIndex(2)\r
256         v0=vertices[i0]\r
257         v1=vertices[i1]\r
258         v2=vertices[i2]\r
259         e01=v1-v0\r
260         e12=v2-v1\r
261         e20=v0-v2\r
262         sqNorm0=e01.getSqNorm()\r
263         sqNorm1=e12.getSqNorm()\r
264         sqNorm2=e20.getSqNorm()\r
265         if sqNorm0>sqNorm1:\r
266             if sqNorm1>sqNorm2:\r
267                 # e01 > e12 > e20\r
268                 self.iHead=i2\r
269                 self.iTail=i1\r
270                 self.iUp=i0\r
271             else:\r
272                 if sqNorm0>sqNorm2:\r
273                     # e01 > e20 > e12\r
274                     self.iHead=i2\r
275                     self.iTail=i0\r
276                     self.iUp=i1\r
277                 else:\r
278                     # e20 > e01 > e12\r
279                     self.iHead=i1\r
280                     self.iTail=i0\r
281                     self.iUp=i2\r
282         else:\r
283             # 0 < 1\r
284             if sqNorm1<sqNorm2:\r
285                 # e20 > e12 > e01\r
286                 self.iHead=i1\r
287                 self.iTail=i2\r
288                 self.iUp=i0\r
289             else:\r
290                 if sqNorm0<sqNorm2:\r
291                     # e12 > e20 > e01\r
292                     self.iHead=i0\r
293                     self.iTail=i2\r
294                     self.iUp=i1\r
295                 else:\r
296                     # e12 > e01 > e20\r
297                     self.iHead=i0\r
298                     self.iTail=i1\r
299                     self.iUp=i2\r
300         self.vHead=vertices[self.iHead]\r
301         self.vTail=vertices[self.iTail]\r
302         self.vUp=vertices[self.iUp]\r
303 \r
304         if self.name.endswith('[]'):\r
305             basename=self.name[0:-2]\r
306             # expand LR name\r
307             if self.vTail.x>0:\r
308                 self.name="%s_L" % basename\r
309             else:\r
310                 self.name="%s_R" % basename\r
311 \r
312 \r
313     def setParent(self, parent, floating=False):\r
314         if floating:\r
315             self.isFloating=True\r
316         self.parent=parent\r
317         parent.children.append(self)\r
318 \r
319     def printTree(self, indent=''):\r
320         print("%s%s" % (indent, self.name))\r
321         for child in self.children:\r
322             child.printTree(indent+'  ')\r
323 \r
324 \r
325 def build_armature(armature, mikotoBone, parent=None):\r
326     """\r
327     create a armature bone.\r
328     """\r
329     bone = Armature.Editbone()\r
330     bone.name = mikotoBone.name.encode('utf-8')\r
331     armature.bones[bone.name] = bone\r
332 \r
333     bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a())\r
334     bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a())\r
335     if parent:\r
336         bone.parent=parent\r
337         if mikotoBone.isFloating:\r
338             pass\r
339         else:\r
340             bone.options=[Armature.CONNECTED]\r
341 \r
342     for child in mikotoBone.children:\r
343         build_armature(armature, child, bone)\r
344 \r
345 \r
346 def create_armature(mqo):\r
347     """\r
348     create armature\r
349     """\r
350     boneObject=None\r
351     for o in mqo.objects:\r
352         if o.name.startswith('bone'):\r
353             boneObject=o\r
354             break\r
355     if not boneObject:\r
356         return\r
357 \r
358     tailMap={}\r
359     for f in boneObject.faces:\r
360         if f.index_count!=3:\r
361             print("invalid index_count: %d" % f.index_count)\r
362             continue\r
363         b=MikotoBone(f, boneObject.vertices, mqo.materials)\r
364         tailMap[b.iTail]=b\r
365 \r
366     #################### \r
367     # build mikoto bone tree\r
368     #################### \r
369     mikotoRoot=MikotoBone()\r
370 \r
371     for b in tailMap.values():\r
372         # each bone has unique parent or is root bone.\r
373         if b.iHead in tailMap:\r
374             b.setParent(tailMap[b.iHead])\r
375         else: \r
376             isFloating=False\r
377             for e in boneObject.edges:\r
378                 if  b.iHead==e.indices[0]:\r
379                     # floating bone\r
380                     if e.indices[1] in tailMap:\r
381                         b.setParent(tailMap[e.indices[1]], True)\r
382                         isFloating=True\r
383                         break\r
384                 elif b.iHead==e.indices[1]:\r
385                     # floating bone\r
386                     if e.indices[0] in tailMap:\r
387                         b.setParent(tailMap[e.indices[0]], True)\r
388                         isFloating=True\r
389                         break\r
390             if isFloating:\r
391                 continue\r
392 \r
393             # no parent bone\r
394             b.setParent(mikotoRoot, True)\r
395 \r
396     if len(mikotoRoot.children)==0:\r
397         print("no root bone")\r
398         return\r
399 \r
400     if len(mikotoRoot.children)==1:\r
401         # single root\r
402         mikotoRoot=mikotoRoot.children[0]\r
403         mikotoRoot.parent=None\r
404     else:\r
405         mikotoRoot.vHead=Vector3(0, 10, 0)\r
406         mikotoRoot.vTail=Vector3(0, 0, 0)\r
407 \r
408     #################### \r
409     # create armature\r
410     #################### \r
411     armature = Armature.New()\r
412     # link to object\r
413     armature_object = scene.objects.new(armature)\r
414     # create action\r
415     act = Armature.NLA.NewAction()\r
416     act.setActive(armature_object)\r
417     # set XRAY\r
418     armature_object.drawMode |= Object.DrawModes.XRAY\r
419     # armature settings\r
420     armature.drawType = Armature.OCTAHEDRON\r
421     armature.envelopes = False\r
422     armature.vertexGroups = True\r
423     armature.mirrorEdit = True\r
424     armature.drawNames=True\r
425 \r
426     # edit bones\r
427     armature.makeEditable()\r
428     build_armature(armature, mikotoRoot)\r
429     armature.update()\r
430 \r
431     return armature_object\r
432         \r
433 \r
434 class TrianglePlane(object):\r
435     """\r
436     mikoto\e$BJ}<0%\!<%s$N%"%s%+!<%&%'%$%H7W;;MQ!#\e(B\r
437     (\e$BIT40A4\e(B)\r
438     """\r
439     __slots__=['normal', \r
440             'v0', 'v1', 'v2',\r
441             ]\r
442     def __init__(self, v0, v1, v2):\r
443         self.v0=v0\r
444         self.v1=v1\r
445         self.v2=v2\r
446 \r
447     def isInsideXY(self, p):\r
448         v0=Vector2(self.v0.x, self.v0.y)\r
449         v1=Vector2(self.v1.x, self.v1.y)\r
450         v2=Vector2(self.v2.x, self.v2.y)\r
451         e01=v1-v0\r
452         e12=v2-v1\r
453         e20=v0-v2\r
454         c0=Vector2.cross(e01, p-v0)\r
455         c1=Vector2.cross(e12, p-v1)\r
456         c2=Vector2.cross(e20, p-v2)\r
457         if c0>=0 and c1>=0 and c2>=0:\r
458             return True\r
459         if c0<=0 and c1<=0 and c2<=0:\r
460             return True\r
461 \r
462     def isInsideYZ(self, p):\r
463         v0=Vector2(self.v0.y, self.v0.z)\r
464         v1=Vector2(self.v1.y, self.v1.z)\r
465         v2=Vector2(self.v2.y, self.v2.z)\r
466         e01=v1-v0\r
467         e12=v2-v1\r
468         e20=v0-v2\r
469         c0=Vector2.cross(e01, p-v0)\r
470         c1=Vector2.cross(e12, p-v1)\r
471         c2=Vector2.cross(e20, p-v2)\r
472         if c0>=0 and c1>=0 and c2>=0:\r
473             return True\r
474         if c0<=0 and c1<=0 and c2<=0:\r
475             return True\r
476 \r
477     def isInsideZX(self, p):\r
478         v0=Vector2(self.v0.z, self.v0.x)\r
479         v1=Vector2(self.v1.z, self.v1.x)\r
480         v2=Vector2(self.v2.z, self.v2.x)\r
481         e01=v1-v0\r
482         e12=v2-v1\r
483         e20=v0-v2\r
484         c0=Vector2.cross(e01, p-v0)\r
485         c1=Vector2.cross(e12, p-v1)\r
486         c2=Vector2.cross(e20, p-v2)\r
487         if c0>=0 and c1>=0 and c2>=0:\r
488             return True\r
489         if c0<=0 and c1<=0 and c2<=0:\r
490             return True\r
491 \r
492 \r
493 class MikotoAnchor(object):\r
494     """\r
495     mikoto\e$BJ}<0%9%1%k%H%s$N%"%s%+!<!#\e(B\r
496     """\r
497     __slots__=[\r
498             "triangles", "bbox",\r
499             ]\r
500     def __init__(self):\r
501         self.triangles=[]\r
502         self.bbox=None\r
503 \r
504     def push(self, face, vertices):\r
505         if face.index_count==3:\r
506             self.triangles.append(TrianglePlane(\r
507                 vertices[face.indices[0]],\r
508                 vertices[face.indices[1]],\r
509                 vertices[face.indices[2]]\r
510                 ))\r
511         elif face.index_count==4:\r
512             self.triangles.append(TrianglePlane(\r
513                 vertices[face.indices[0]],\r
514                 vertices[face.indices[1]],\r
515                 vertices[face.indices[2]]\r
516                 ))\r
517             self.triangles.append(TrianglePlane(\r
518                 vertices[face.indices[2]],\r
519                 vertices[face.indices[3]],\r
520                 vertices[face.indices[0]]\r
521                 ))\r
522         # bounding box\r
523         if not self.bbox:\r
524             self.bbox=BoundingBox(vertices[face.indices[0]])\r
525         for i in face.indices:\r
526             self.bbox.expand(vertices[i])\r
527 \r
528 \r
529     def calcWeight(self, v):\r
530         if not self.bbox.isInside(v):\r
531             return 0\r
532 \r
533         if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x):\r
534             return 1.0\r
535         else:\r
536             return 0\r
537         \r
538     def anyXY(self, x, y):\r
539         for t in self.triangles:\r
540             if t.isInsideXY(Vector2(x, y)):\r
541                 return True\r
542         return False\r
543 \r
544     def anyYZ(self, y, z):\r
545         for t in self.triangles:\r
546             if t.isInsideYZ(Vector2(y, z)):\r
547                 return True\r
548         return False\r
549 \r
550     def anyZX(self, z, x):\r
551         for t in self.triangles:\r
552             if t.isInsideZX(Vector2(z, x)):\r
553                 return True\r
554         return False\r
555 \r
556 \r
557 def create_bone_weight(scene, mqo, armature_object, objects):\r
558     """\r
559     create mikoto bone weight.\r
560     """\r
561     anchorMap={}\r
562     # setup mikoto anchors\r
563     for o in mqo.objects:\r
564         if o.name.startswith("anchor"):\r
565             for f in o.faces:\r
566                 name=mqo.materials[f.material_index].name\r
567                 if name.endswith('[]'):\r
568                     basename=name[0:-2]\r
569                     v=o.vertices[f.indices[0]]\r
570                     if(v.x>0):\r
571                         # L\r
572                         name_L=basename+'_L'\r
573                         if not name_L in anchorMap:\r
574                             anchorMap[name_L]=MikotoAnchor()\r
575                         anchorMap[name_L].push(f, o.vertices)\r
576                     elif(v.x<0):\r
577                         # R\r
578                         name_R=basename+'_R'\r
579                         if not name_R in anchorMap:\r
580                             anchorMap[name_R]=MikotoAnchor()\r
581                         anchorMap[name_R].push(f, o.vertices)\r
582                     else:\r
583                         print("no side", v)\r
584                 else:\r
585                     if not name in anchorMap:\r
586                         anchorMap[name]=MikotoAnchor()\r
587                     anchorMap[name].push(f, o.vertices)\r
588 \r
589     for o in objects:\r
590         # add armature modifier\r
591         mod=o.modifiers.append(Modifier.Types.ARMATURE)\r
592         mod[Modifier.Settings.OBJECT] = armature_object\r
593         mod[Modifier.Settings.ENVELOPES] = False\r
594         o.makeDisplayList()\r
595         # create vertex group\r
596         mesh=o.getData(mesh=True)\r
597         for name in anchorMap.keys():\r
598             mesh.addVertGroup(name)\r
599         mesh.update()\r
600                  \r
601     # assing vertices to vertex group\r
602     for o in objects:\r
603         mesh=o.getData(mesh=True)\r
604         for i, mvert in enumerate(mesh.verts):\r
605             hasWeight=False\r
606             for name, anchor in anchorMap.items():\r
607                 weight=anchor.calcWeight(mvert.co)\r
608                 if weight>0:\r
609                     mesh.assignVertsToGroup(\r
610                             name, [i], weight, Mesh.AssignModes.ADD)\r
611                     hasWeight=True\r
612             if not hasWeight:\r
613                 # debug orphan vertex\r
614                 print('orphan', mvert)\r
615         mesh.update()\r
616 \r
617 \r
618 def __execute(filename, scene, scale=0.1):\r
619     # parse file\r
620     io=mqo.IO()\r
621     if not io.read(filename):\r
622         bl.message("fail to load %s" % filename)\r
623         return\r
624 \r
625     # create materials\r
626     materials, imageMap=__createMaterials(io, os.path.dirname(filename))\r
627     if len(materials)==0:\r
628         materials.append(bl.material.create('default'))\r
629 \r
630     # create objects\r
631     root=bl.object.createEmpty(os.path.basename(filename))\r
632     objects=__createObjects(io, root, materials, imageMap, scale)\r
633 \r
634     if has_mikoto(io):\r
635         # create mikoto bone\r
636         armature_object=create_armature(io)\r
637         if armature_object:\r
638             root.makeParent([armature_object])\r
639 \r
640             # create bone weight\r
641             create_bone_weight(io, armature_object, objects)\r
642 \r
643  \r
644 ###############################################################################\r
645 # register\r
646 ###############################################################################\r
647 if isBlender24():\r
648     # for 2.4\r
649     def execute_24(filename):\r
650         scene=Blender.Scene.GetCurrent()\r
651         bl.initialize('mqo_import', scene)\r
652         __execute(\r
653                 filename.decode(bl.INTERNAL_ENCODING), \r
654                 scene)\r
655         bl.finalize()\r
656 \r
657     # execute\r
658     Blender.Window.FileSelector(execute_24, 'Import MQO', '*.mqo')\r
659 \r
660 else:\r
661     # for 2.5\r
662     def execute_25(filename, scene, scale):\r
663         bl.initialize('mqo_import', scene)\r
664         __execute(filename, scene, scale)\r
665         bl.finalize()\r
666 \r
667     # operator\r
668     class IMPORT_OT_mqo(bpy.types.Operator):\r
669         '''Import from Metasequoia file format (.mqo)'''\r
670         bl_idname = "import_scene.mqo"\r
671         bl_label = 'Import MQO'\r
672 \r
673         # List of operator properties, the attributes will be assigned\r
674         # to the class instance from the operator settings before calling.\r
675         filepath = bpy.props.StringProperty()\r
676         filename = bpy.props.StringProperty()\r
677         directory = bpy.props.StringProperty()\r
678 \r
679         scale = bpy.props.FloatProperty(\r
680                 name="Scale", \r
681                 description="Scale the MQO by this value", \r
682                 min=0.0001, max=1000000.0, \r
683                 soft_min=0.001, soft_max=100.0, default=0.1)\r
684 \r
685         def execute(self, context):\r
686             execute_25(\r
687                     self.properties.filepath, \r
688                     context.scene, \r
689                     self.properties.scale)\r
690             return 'FINISHED'\r
691 \r
692         def invoke(self, context, event):\r
693             wm=context.manager\r
694             wm.add_fileselect(self)\r
695             return 'RUNNING_MODAL'\r
696 \r
697 \r
698     # register menu\r
699     def menu_func(self, context): \r
700         self.layout.operator(\r
701                 IMPORT_OT_mqo.bl_idname, \r
702                 text="Metasequoia (.mqo)",\r
703                 icon='PLUGIN'\r
704                 )\r
705 \r
706     def register():\r
707         bpy.types.register(IMPORT_OT_mqo)\r
708         bpy.types.INFO_MT_file_import.append(menu_func)\r
709 \r
710     def unregister():\r
711         bpy.types.unregister(IMPORT_OT_mqo)\r
712         bpy.types.INFO_MT_file_import.remove(menu_func)\r
713 \r
714     if __name__=="__main__":\r
715         register()\r
716 \r