OSDN Git Service

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