OSDN Git Service

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