OSDN Git Service

implement mqo_import.py
[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__= '0.6 2010/05/05'\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 '''\r
25 ###############################################################################\r
26 # import\r
27 ###############################################################################\r
28 import os\r
29 import sys\r
30 import math\r
31 \r
32 # C extension\r
33 from meshio import mqo\r
34 \r
35 def isBlender24():\r
36     return sys.version_info[0]<3\r
37 \r
38 if isBlender24():\r
39     # for 2.4\r
40     import Blender\r
41     from Blender import Mathutils\r
42     import bpy\r
43 \r
44     # ファイルシステムの文字コード\r
45     # 改造版との共用のため\r
46     FS_ENCODING=sys.getfilesystemencoding()\r
47     if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):\r
48         INTERNAL_ENCODING='utf-8'\r
49     else:\r
50         INTERNAL_ENCODING=FS_ENCODING\r
51 else:\r
52     # for 2.5\r
53     import bpy\r
54     from bpy.props import *\r
55 \r
56 \r
57 ###############################################################################\r
58 # implement\r
59 ###############################################################################\r
60 if isBlender24():\r
61     def has_mikoto(mqo):\r
62         return False\r
63 \r
64 \r
65     def create_materials(scene, mqo, directory):\r
66         """\r
67         create blender materials and renturn material list.\r
68         """\r
69         materials = []\r
70         images = []\r
71         for m in mqo.materials:\r
72             material = Blender.Material.New(m.getName().encode(INTERNAL_ENCODING))\r
73             materials.append(material)\r
74 \r
75             material.mode |= Blender.Material.Modes.SHADELESS\r
76             material.rgbCol = [m.color.r, m.color.g, m.color.b]\r
77             material.alpha = m.color.a\r
78             material.amb = m.ambient\r
79             material.spec = m.specular\r
80             material.hard = int(255 * m.power)\r
81             if m.texture!="":\r
82                 texture_path=m.getTexture()\r
83 \r
84                 # load texture image\r
85                 if os.path.isabs(texture_path):\r
86                     # absolute\r
87                     path = texture_path\r
88                 else:\r
89                     # relative\r
90                     path = os.path.join(directory, texture_path)\r
91 \r
92                 # backslash to slash\r
93                 #path = path.replace('\\', '/')\r
94 \r
95                 # texture\r
96                 if os.path.exists(path):\r
97                     image = Blender.Image.Load(path.encode(INTERNAL_ENCODING))\r
98                     images.append(image)\r
99                     material.mode = material.mode | Blender.Material.Modes.TEXFACE\r
100                     tex = Blender.Texture.New(path.encode(INTERNAL_ENCODING))\r
101                     tex.type = Blender.Texture.Types.IMAGE\r
102                     tex.image = image\r
103                     material.setTexture(0, tex, Blender.Texture.TexCo.UV)\r
104                 else:\r
105                     print("%s not exits" % path)\r
106                 \r
107         return materials\r
108 \r
109 \r
110     def create_objects(scene, root, mqo, materials):\r
111         """\r
112         create blender mesh objects.\r
113         """\r
114         # store hierarchy\r
115         stack=[root]    \r
116 \r
117         objects=[]\r
118         for o in mqo.objects:\r
119             #print "%s:v(%d),f(%d)" % (o.name, len(o.vertices), len(o.faces))\r
120             # create mesh\r
121             mesh = Blender.Mesh.New()\r
122             mesh_object=scene.objects.new(mesh, o.name.encode('utf-8'))\r
123 \r
124             # add hierarchy\r
125             stack_depth=len(stack)-1\r
126             print(o.depth, stack_depth)\r
127             if o.depth<stack_depth:\r
128                 for i in range(stack_depth-o.depth):\r
129                     stack.pop()\r
130             stack[-1].makeParent([mesh_object])\r
131             stack.append(mesh_object)\r
132 \r
133             if o.name.startswith('sdef'):\r
134                 # add sdef object\r
135                 objects.append(mesh_object)\r
136             elif o.name.startswith('anchor'):\r
137                 #print("hide %s" % o.name)\r
138                 #mesh_object.restrictDisplay=False\r
139                 mesh_object.layers=[2]\r
140             elif o.name.startswith('bone'):\r
141                 mesh_object.layers=[2]\r
142 \r
143             # add vertices\r
144             mesh.verts.extend(Mathutils.Vector(0, 0, 0)) # dummy\r
145             mesh.verts.extend([(v.x, -v.z, v.y) for v in o.vertices])\r
146             # add faces\r
147             mesh_faces=[]\r
148             for face in o.faces:\r
149                 face_indices=[]\r
150                 for i in xrange(face.index_count):\r
151                     face_indices.append(face.getIndex(i)+1)\r
152                 mesh_faces.append(face_indices)\r
153             #new_faces=mesh.faces.extend([face.indices for face in o.faces], \r
154             new_faces=mesh.faces.extend(mesh_faces,\r
155                     #ignoreDups=True, \r
156                     indexList=True)\r
157             mesh.update()\r
158             \r
159             # gather used materials\r
160             usedMaterials = {}\r
161             if new_faces:\r
162                 for i in new_faces:\r
163                     if type(i) is int:\r
164                         usedMaterials[o.faces[i].material_index]=True\r
165 \r
166             # blender limits 16 materials per mesh\r
167             # separate mesh ?\r
168             for i, material_index in enumerate(usedMaterials.keys()):\r
169                 if i>=16:\r
170                     print("over 16 materials!")\r
171                     break\r
172                 mesh.materials+=[materials[material_index]]\r
173                 usedMaterials[material_index]=i\r
174             \r
175             # set face params\r
176             for i, f in enumerate(o.faces):       \r
177                 if not type(new_faces[i]) is int:\r
178                     continue\r
179 \r
180                 face=mesh.faces[new_faces[i]]\r
181 \r
182                 uv_array=[]\r
183                 for i in xrange(f.index_count):\r
184                     uv_array.append(Blender.Mathutils.Vector(\r
185                         f.getUV(i).x, \r
186                         1.0-f.getUV(i).y)\r
187                         )\r
188                 try:\r
189                     face.uv=uv_array\r
190                 except Exception as msg:\r
191                     #print msg\r
192                     #print face.index, uv_array\r
193                     pass\r
194             \r
195                 if f.material_index in usedMaterials:\r
196                     face.mat = usedMaterials[f.material_index]\r
197 \r
198                 face.smooth = 1\r
199 \r
200             # rmeove dummy 0 vertex\r
201             mesh.verts.delete(0)\r
202                 \r
203             mesh.mode |= Blender.Mesh.Modes.AUTOSMOOTH\r
204             mesh.maxSmoothAngle = int(o.smoothing)\r
205             mesh.smooth()\r
206             mesh.calcNormals()\r
207             mesh.flipNormals()\r
208             mesh.update()\r
209 \r
210             # mirror modifier\r
211             if o.mirror:\r
212                 mod=mesh_object.modifiers.append(Blender.Modifier.Types.MIRROR)\r
213 \r
214         return objects\r
215 \r
216 \r
217     class MikotoBone(object):\r
218         __slots__=[\r
219                 'name',\r
220                 'iHead', 'iTail', 'iUp',\r
221                 'vHead', 'vTail', 'vUp',\r
222                 'parent', 'isFloating',\r
223                 'children',\r
224                 ]\r
225         def __init__(self, face=None, vertices=None, materials=None):\r
226             self.parent=None\r
227             self.isFloating=False\r
228             self.children=[]\r
229             if not face:\r
230                 self.name='root'\r
231                 return\r
232 \r
233             self.name=materials[face.material_index].name.encode('utf-8')\r
234 \r
235             i0=face.indices[0]\r
236             i1=face.indices[1]\r
237             i2=face.indices[2]\r
238             v0=vertices[i0]\r
239             v1=vertices[i1]\r
240             v2=vertices[i2]\r
241             e01=v1-v0\r
242             e12=v2-v1\r
243             e20=v0-v2\r
244             sqNorm0=e01.getSqNorm()\r
245             sqNorm1=e12.getSqNorm()\r
246             sqNorm2=e20.getSqNorm()\r
247             if sqNorm0>sqNorm1:\r
248                 if sqNorm1>sqNorm2:\r
249                     # e01 > e12 > e20\r
250                     self.iHead=i2\r
251                     self.iTail=i1\r
252                     self.iUp=i0\r
253                 else:\r
254                     if sqNorm0>sqNorm2:\r
255                         # e01 > e20 > e12\r
256                         self.iHead=i2\r
257                         self.iTail=i0\r
258                         self.iUp=i1\r
259                     else:\r
260                         # e20 > e01 > e12\r
261                         self.iHead=i1\r
262                         self.iTail=i0\r
263                         self.iUp=i2\r
264             else:\r
265                 # 0 < 1\r
266                 if sqNorm1<sqNorm2:\r
267                     # e20 > e12 > e01\r
268                     self.iHead=i1\r
269                     self.iTail=i2\r
270                     self.iUp=i0\r
271                 else:\r
272                     if sqNorm0<sqNorm2:\r
273                         # e12 > e20 > e01\r
274                         self.iHead=i0\r
275                         self.iTail=i2\r
276                         self.iUp=i1\r
277                     else:\r
278                         # e12 > e01 > e20\r
279                         self.iHead=i0\r
280                         self.iTail=i1\r
281                         self.iUp=i2\r
282             self.vHead=vertices[self.iHead]\r
283             self.vTail=vertices[self.iTail]\r
284             self.vUp=vertices[self.iUp]\r
285 \r
286             if self.name.endswith('[]'):\r
287                 basename=self.name[0:-2]\r
288                 # expand LR name\r
289                 if self.vTail.x>0:\r
290                     self.name="%s_L" % basename\r
291                 else:\r
292                     self.name="%s_R" % basename\r
293 \r
294 \r
295         def setParent(self, parent, floating=False):\r
296             if floating:\r
297                 self.isFloating=True\r
298             self.parent=parent\r
299             parent.children.append(self)\r
300 \r
301         def printTree(self, indent=''):\r
302             print("%s%s" % (indent, self.name))\r
303             for child in self.children:\r
304                 child.printTree(indent+'  ')\r
305 \r
306 \r
307     def build_armature(armature, mikotoBone, parent=None):\r
308         """\r
309         create a armature bone.\r
310         """\r
311         bone = Armature.Editbone()\r
312         bone.name = mikotoBone.name.encode('utf-8')\r
313         armature.bones[bone.name] = bone\r
314 \r
315         bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a())\r
316         bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a())\r
317         if parent:\r
318             bone.parent=parent\r
319             if mikotoBone.isFloating:\r
320                 pass\r
321             else:\r
322                 bone.options=[Armature.CONNECTED]\r
323 \r
324         for child in mikotoBone.children:\r
325             build_armature(armature, child, bone)\r
326 \r
327 \r
328     def create_armature(scene, mqo):\r
329         """\r
330         create armature\r
331         """\r
332         boneObject=None\r
333         for o in mqo.objects:\r
334             if o.name.startswith('bone'):\r
335                 boneObject=o\r
336                 break\r
337         if not boneObject:\r
338             return\r
339 \r
340         tailMap={}\r
341         for f in boneObject.faces:\r
342             if f.index_count!=3:\r
343                 print("invalid index_count: %d" % f.index_count)\r
344                 continue\r
345             b=MikotoBone(f, boneObject.vertices, mqo.materials)\r
346             tailMap[b.iTail]=b\r
347 \r
348         #################### \r
349         # build mikoto bone tree\r
350         #################### \r
351         mikotoRoot=MikotoBone()\r
352 \r
353         for b in tailMap.values():\r
354             # each bone has unique parent or is root bone.\r
355             if b.iHead in tailMap:\r
356                 b.setParent(tailMap[b.iHead])\r
357             else: \r
358                 isFloating=False\r
359                 for e in boneObject.edges:\r
360                     if  b.iHead==e.indices[0]:\r
361                         # floating bone\r
362                         if e.indices[1] in tailMap:\r
363                             b.setParent(tailMap[e.indices[1]], True)\r
364                             isFloating=True\r
365                             break\r
366                     elif b.iHead==e.indices[1]:\r
367                         # floating bone\r
368                         if e.indices[0] in tailMap:\r
369                             b.setParent(tailMap[e.indices[0]], True)\r
370                             isFloating=True\r
371                             break\r
372                 if isFloating:\r
373                     continue\r
374 \r
375                 # no parent bone\r
376                 b.setParent(mikotoRoot, True)\r
377 \r
378         if len(mikotoRoot.children)==0:\r
379             print("no root bone")\r
380             return\r
381 \r
382         if len(mikotoRoot.children)==1:\r
383             # single root\r
384             mikotoRoot=mikotoRoot.children[0]\r
385             mikotoRoot.parent=None\r
386         else:\r
387             mikotoRoot.vHead=Vector3(0, 10, 0)\r
388             mikotoRoot.vTail=Vector3(0, 0, 0)\r
389 \r
390         #################### \r
391         # create armature\r
392         #################### \r
393         armature = Armature.New()\r
394         # link to object\r
395         armature_object = scene.objects.new(armature)\r
396         # create action\r
397         act = Armature.NLA.NewAction()\r
398         act.setActive(armature_object)\r
399         # set XRAY\r
400         armature_object.drawMode |= Object.DrawModes.XRAY\r
401         # armature settings\r
402         armature.drawType = Armature.OCTAHEDRON\r
403         armature.envelopes = False\r
404         armature.vertexGroups = True\r
405         armature.mirrorEdit = True\r
406         armature.drawNames=True\r
407 \r
408         # edit bones\r
409         armature.makeEditable()\r
410         build_armature(armature, mikotoRoot)\r
411         armature.update()\r
412 \r
413         return armature_object\r
414             \r
415 \r
416     class TrianglePlane(object):\r
417         """\r
418         mikoto方式ボーンのアンカーウェイト計算用。\r
419         (不完全)\r
420         """\r
421         __slots__=['normal', \r
422                 'v0', 'v1', 'v2',\r
423                 ]\r
424         def __init__(self, v0, v1, v2):\r
425             self.v0=v0\r
426             self.v1=v1\r
427             self.v2=v2\r
428 \r
429         def isInsideXY(self, p):\r
430             v0=Vector2(self.v0.x, self.v0.y)\r
431             v1=Vector2(self.v1.x, self.v1.y)\r
432             v2=Vector2(self.v2.x, self.v2.y)\r
433             e01=v1-v0\r
434             e12=v2-v1\r
435             e20=v0-v2\r
436             c0=Vector2.cross(e01, p-v0)\r
437             c1=Vector2.cross(e12, p-v1)\r
438             c2=Vector2.cross(e20, p-v2)\r
439             if c0>=0 and c1>=0 and c2>=0:\r
440                 return True\r
441             if c0<=0 and c1<=0 and c2<=0:\r
442                 return True\r
443 \r
444         def isInsideYZ(self, p):\r
445             v0=Vector2(self.v0.y, self.v0.z)\r
446             v1=Vector2(self.v1.y, self.v1.z)\r
447             v2=Vector2(self.v2.y, self.v2.z)\r
448             e01=v1-v0\r
449             e12=v2-v1\r
450             e20=v0-v2\r
451             c0=Vector2.cross(e01, p-v0)\r
452             c1=Vector2.cross(e12, p-v1)\r
453             c2=Vector2.cross(e20, p-v2)\r
454             if c0>=0 and c1>=0 and c2>=0:\r
455                 return True\r
456             if c0<=0 and c1<=0 and c2<=0:\r
457                 return True\r
458 \r
459         def isInsideZX(self, p):\r
460             v0=Vector2(self.v0.z, self.v0.x)\r
461             v1=Vector2(self.v1.z, self.v1.x)\r
462             v2=Vector2(self.v2.z, self.v2.x)\r
463             e01=v1-v0\r
464             e12=v2-v1\r
465             e20=v0-v2\r
466             c0=Vector2.cross(e01, p-v0)\r
467             c1=Vector2.cross(e12, p-v1)\r
468             c2=Vector2.cross(e20, p-v2)\r
469             if c0>=0 and c1>=0 and c2>=0:\r
470                 return True\r
471             if c0<=0 and c1<=0 and c2<=0:\r
472                 return True\r
473 \r
474 \r
475     class MikotoAnchor(object):\r
476         """\r
477         mikoto方式スケルトンのアンカー。\r
478         """\r
479         __slots__=[\r
480                 "triangles", "bbox",\r
481                 ]\r
482         def __init__(self):\r
483             self.triangles=[]\r
484             self.bbox=None\r
485 \r
486         def push(self, face, vertices):\r
487             if face.index_count==3:\r
488                 self.triangles.append(TrianglePlane(\r
489                     vertices[face.indices[0]],\r
490                     vertices[face.indices[1]],\r
491                     vertices[face.indices[2]]\r
492                     ))\r
493             elif face.index_count==4:\r
494                 self.triangles.append(TrianglePlane(\r
495                     vertices[face.indices[0]],\r
496                     vertices[face.indices[1]],\r
497                     vertices[face.indices[2]]\r
498                     ))\r
499                 self.triangles.append(TrianglePlane(\r
500                     vertices[face.indices[2]],\r
501                     vertices[face.indices[3]],\r
502                     vertices[face.indices[0]]\r
503                     ))\r
504             # bounding box\r
505             if not self.bbox:\r
506                 self.bbox=BoundingBox(vertices[face.indices[0]])\r
507             for i in face.indices:\r
508                 self.bbox.expand(vertices[i])\r
509 \r
510 \r
511         def calcWeight(self, v):\r
512             if not self.bbox.isInside(v):\r
513                 return 0\r
514 \r
515             if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x):\r
516                 return 1.0\r
517             else:\r
518                 return 0\r
519             \r
520         def anyXY(self, x, y):\r
521             for t in self.triangles:\r
522                 if t.isInsideXY(Vector2(x, y)):\r
523                     return True\r
524             return False\r
525 \r
526         def anyYZ(self, y, z):\r
527             for t in self.triangles:\r
528                 if t.isInsideYZ(Vector2(y, z)):\r
529                     return True\r
530             return False\r
531 \r
532         def anyZX(self, z, x):\r
533             for t in self.triangles:\r
534                 if t.isInsideZX(Vector2(z, x)):\r
535                     return True\r
536             return False\r
537 \r
538 \r
539     def create_bone_weight(scene, mqo, armature_object, objects):\r
540         """\r
541         create mikoto bone weight.\r
542         """\r
543         anchorMap={}\r
544         # setup mikoto anchors\r
545         for o in mqo.objects:\r
546             if o.name.startswith("anchor"):\r
547                 for f in o.faces:\r
548                     name=mqo.materials[f.material_index].name\r
549                     if name.endswith('[]'):\r
550                         basename=name[0:-2]\r
551                         v=o.vertices[f.indices[0]]\r
552                         if(v.x>0):\r
553                             # L\r
554                             name_L=basename+'_L'\r
555                             if not name_L in anchorMap:\r
556                                 anchorMap[name_L]=MikotoAnchor()\r
557                             anchorMap[name_L].push(f, o.vertices)\r
558                         elif(v.x<0):\r
559                             # R\r
560                             name_R=basename+'_R'\r
561                             if not name_R in anchorMap:\r
562                                 anchorMap[name_R]=MikotoAnchor()\r
563                             anchorMap[name_R].push(f, o.vertices)\r
564                         else:\r
565                             print("no side", v)\r
566                     else:\r
567                         if not name in anchorMap:\r
568                             anchorMap[name]=MikotoAnchor()\r
569                         anchorMap[name].push(f, o.vertices)\r
570 \r
571         for o in objects:\r
572             # add armature modifier\r
573             mod=o.modifiers.append(Modifier.Types.ARMATURE)\r
574             mod[Modifier.Settings.OBJECT] = armature_object\r
575             mod[Modifier.Settings.ENVELOPES] = False\r
576             o.makeDisplayList()\r
577             # create vertex group\r
578             mesh=o.getData(mesh=True)\r
579             for name in anchorMap.keys():\r
580                 mesh.addVertGroup(name)\r
581             mesh.update()\r
582                      \r
583         # assing vertices to vertex group\r
584         for o in objects:\r
585             mesh=o.getData(mesh=True)\r
586             for i, mvert in enumerate(mesh.verts):\r
587                 hasWeight=False\r
588                 for name, anchor in anchorMap.items():\r
589                     weight=anchor.calcWeight(mvert.co)\r
590                     if weight>0:\r
591                         mesh.assignVertsToGroup(\r
592                                 name, [i], weight, Mesh.AssignModes.ADD)\r
593                         hasWeight=True\r
594                 if not hasWeight:\r
595                     # debug orphan vertex\r
596                     print('orphan', mvert)\r
597             mesh.update()\r
598         \r
599 \r
600     def execute_24(filename):\r
601         """\r
602         import a mqo file.\r
603         """\r
604         filename=filename.decode(INTERNAL_ENCODING)\r
605         print("##start mqo_import.py##")\r
606         print(INTERNAL_ENCODING, FS_ENCODING)\r
607         print("parse mqo file: %s" % (filename))\r
608 \r
609         Blender.Window.WaitCursor(1) \r
610         t = Blender.sys.time() \r
611 \r
612         # parse file\r
613         io=mqo.IO()\r
614         \r
615         if not io.read(filename):\r
616             return\r
617 \r
618         # get active scene\r
619         scene = Blender.Scene.GetCurrent()\r
620 \r
621         # create materials\r
622         materials=create_materials(scene, io, os.path.dirname(filename))\r
623      \r
624         # create objects\r
625         root=scene.objects.new("Empty")\r
626         root.setName(os.path.basename(filename))\r
627         objects=create_objects(scene, root, io, materials)\r
628 \r
629         if has_mikoto(io):\r
630             # create mikoto bone\r
631             armature_object=create_armature(scene, io)\r
632             if armature_object:\r
633                 root.makeParent([armature_object])\r
634 \r
635                 # create bone weight\r
636                 create_bone_weight(scene, io, armature_object, objects)\r
637 \r
638 \r
639         print('finished in %.2f seconds' % (Blender.sys.time()-t))\r
640         print('')\r
641         Blender.Redraw()\r
642         Blender.Window.WaitCursor(0) \r
643 \r
644 \r
645 else:\r
646     def create_texture(directory, texture_name):\r
647         texture=bpy.data.textures.new(texture_name)\r
648         texture.type='IMAGE'\r
649         texture=texture.recast_type()\r
650         #texturePath="%s/%s" % (directory, texture_name)\r
651         texturePath=os.path.join(directory, texture_name)\r
652         print('create_texture', texturePath)\r
653         image=bpy.data.images.load(texturePath)\r
654         texture.image=image\r
655         texture.mipmap = True\r
656         texture.interpolation = True\r
657         texture.use_alpha = True\r
658         return texture\r
659 \r
660     def create_materials(mqo, scene, directory):\r
661         materials = []\r
662         textureMap={}\r
663         imageMap={}\r
664         if len(mqo.materials)>0:\r
665             for material_index, m in enumerate(mqo.materials):\r
666                 material = bpy.data.materials.new(m.getName())\r
667                 materials.append(material)\r
668                 # mqo material\r
669                 material.diffuse_color=[m.color.r, m.color.g, m.color.b]\r
670                 material.alpha=m.color.a\r
671                 material.diffuse_intensity=m.diffuse\r
672                 texture_name=m.getTexture()\r
673                 if texture_name!='':\r
674                     if texture_name in textureMap:\r
675                         texture=textureMap[texture_name]\r
676                     else:\r
677                         texture=create_texture(directory, texture_name)\r
678                         textureMap[texture_name]=texture\r
679                         imageMap[material_index]=texture.image\r
680                     #material.add_texture(texture, "UV", {"COLOR", "ALPHA"})\r
681                     material.add_texture(texture, "UV", "COLOR")\r
682                     # temporary\r
683                     material.emit=1.0\r
684         else:\r
685             material = bpy.data.materials.new('Default')\r
686             materials.append(material)\r
687         return materials, imageMap\r
688 \r
689     def create_objects(mqo, scene, parent, materials, imageMap, scale):\r
690         for o in mqo.objects:\r
691 \r
692             # create mesh\r
693             mesh=bpy.data.meshes.new("Mesh")\r
694             meshObject= bpy.data.objects.new(o.getName(), mesh)\r
695             scene.objects.link(meshObject)\r
696             meshObject.parent=parent\r
697 \r
698             # count triangle and quadrangle\r
699             faceCount=0\r
700             for f in o.faces:\r
701                 if f.index_count==3 or f.index_count==4:\r
702                     faceCount+=1\r
703             mesh.add_geometry(len(o.vertices), 0, faceCount)\r
704 \r
705             # add vertex\r
706             unpackedVertices=[]\r
707             for v in o.vertices:\r
708                 # convert right-handed y-up to right-handed z-up\r
709                 unpackedVertices.extend(\r
710                         (scale*v.x, scale*-v.z, scale*v.y))\r
711             mesh.verts.foreach_set("co", unpackedVertices)\r
712 \r
713             # add face\r
714             unpackedFaces = []\r
715             usedMaterial=set()\r
716 \r
717             def getFace(f):\r
718                 face = []\r
719                 for i in range(f.index_count):\r
720                     face.append(f.getIndex(i))\r
721                 return face\r
722 \r
723             for f in o.faces:\r
724                 face=getFace(f)\r
725                 if len(face) != 3 and len(face) != 4:\r
726                     print("{0} vertices in face.".format(len(face)))\r
727                     continue\r
728 \r
729                 if len(face) == 4:\r
730                     if face[3] == 0:\r
731                         # rotate indices if the 4th is 0\r
732                         face = [face[3], face[0], face[1], face[2]]\r
733                 elif len(face) == 3:\r
734                     if face[2] == 0:\r
735                         # rotate indices if the 3rd is 0\r
736                         face = [face[2], face[0], face[1], 0]\r
737                     else:\r
738                         face.append(0)\r
739 \r
740                 unpackedFaces.extend(face)\r
741                 usedMaterial.add(f.material_index)\r
742             try:\r
743                 mesh.faces.foreach_set("verts_raw", unpackedFaces)\r
744             except:\r
745                 #print([getFace(f) for f in o.faces])\r
746                 print("fail to mesh.faces.foreach_set")\r
747                 return\r
748 \r
749             # add material\r
750             meshMaterialMap={}\r
751             materialIndex=0\r
752             for i in usedMaterial:\r
753                 mesh.add_material(materials[i])\r
754                 meshMaterialMap[i]=materialIndex\r
755                 materialIndex+=1\r
756 \r
757             # each face\r
758             mesh.add_uv_texture()\r
759             for mqo_face, blender_face, uv_face in zip(\r
760                     o.faces, mesh.faces, mesh.uv_textures[0].data):\r
761                 if mqo_face.index_count<3:\r
762                     continue\r
763                 blender_face.material_index=meshMaterialMap[mqo_face.material_index]\r
764                 if mqo_face.index_count>=3:\r
765                     uv_face.uv1=[mqo_face.getUV(0).x, 1.0-mqo_face.getUV(0).y]\r
766                     uv_face.uv2=[mqo_face.getUV(1).x, 1.0-mqo_face.getUV(1).y]\r
767                     uv_face.uv3=[mqo_face.getUV(2).x, 1.0-mqo_face.getUV(2).y]\r
768                     if mqo_face.index_count==4:\r
769                         uv_face.uv4=[\r
770                                 mqo_face.getUV(3).x, 1.0-mqo_face.getUV(3).y]\r
771                 if materials[mqo_face.material_index] in imageMap:\r
772                     uv_face.image=imageMap[mqo_face.material_index]\r
773                     uv_face.tex=True\r
774 \r
775             mesh.update()\r
776 \r
777     def load(filename, context, scale):\r
778         """\r
779         load mqo file to context.\r
780         """\r
781         io=mqo.IO()\r
782         if not io.read(filename):\r
783             print("fail to load",filename)\r
784             return\r
785 \r
786         scene=context.scene\r
787 \r
788         # create material\r
789         materials, imageMap=create_materials(\r
790                 io, scene, os.path.dirname(filename))\r
791 \r
792         # create group\r
793         empty=bpy.data.objects.new(os.path.basename(filename), None)\r
794         scene.objects.link(empty)\r
795 \r
796         # create mesh\r
797         create_objects(io, scene, empty, materials, imageMap, scale)\r
798  \r
799 \r
800 ###############################################################################\r
801 # register\r
802 ###############################################################################\r
803 if isBlender24():\r
804     # for 2.4\r
805     # execute\r
806     Blender.Window.FileSelector(execute_24, 'Import MQO', '*.mqo')\r
807 else:\r
808     # for 2.5\r
809     # import operator\r
810     class IMPORT_OT_mqo(bpy.types.Operator):\r
811         '''Import from Metasequoia file format (.mqo)'''\r
812         bl_idname = "import_scene.mqo"\r
813         bl_label = 'Import MQO'\r
814 \r
815         # List of operator properties, the attributes will be assigned\r
816         # to the class instance from the operator settings before calling.\r
817 \r
818         path = StringProperty(\r
819                 name="File Path", \r
820                 description="File path used for importing the MQO file", \r
821                 maxlen= 1024, default= "")\r
822         filename = StringProperty(\r
823                 name="File Name", \r
824                 description="Name of the file.")\r
825         directory = StringProperty(\r
826                 name="Directory", \r
827                 description="Directory of the file.")\r
828 \r
829         scale = FloatProperty(\r
830                 name="Scale", \r
831                 description="Scale the MQO by this value", \r
832                 min=0.0001, max=1000000.0, \r
833                 soft_min=0.001, soft_max=100.0, default=1.0)\r
834 \r
835         def execute(self, context):\r
836             load(self.properties.path, context, self.properties.scale)\r
837             return 'FINISHED'\r
838 \r
839         def invoke(self, context, event):\r
840             wm=context.manager\r
841             wm.add_fileselect(self)\r
842             return 'RUNNING_MODAL'\r
843 \r
844 \r
845     # register menu\r
846     def menu_func(self, context): \r
847         self.layout.operator(IMPORT_OT_mqo.bl_idname, \r
848                 text="Metasequoia (.mqo)")\r
849 \r
850     def register():\r
851         bpy.types.register(IMPORT_OT_mqo)\r
852         bpy.types.INFO_MT_file_import.append(menu_func)\r
853 \r
854     def unregister():\r
855         bpy.types.unregister(IMPORT_OT_mqo)\r
856         bpy.types.INFO_MT_file_import.remove(menu_func)\r
857 \r
858     if __name__=="__main__":\r
859         register()\r
860 \r