OSDN Git Service

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