OSDN Git Service

fix export vertex map.
[meshio/meshio.git] / swig / blender / mqo_import.py
index cb05bc8..630501e 100644 (file)
@@ -21,10 +21,12 @@ This script imports a mqo into Blender for editing.
 0.5 20100311: create armature from mikoto bone.\r
 0.6 20100505: C extension.\r
 0.7 20100606: integrate 2.4 and 2.5.\r
+0.8 20100619: fix multibyte object name.\r
+0.9 20100626: refactoring.\r
 '''\r
+\r
 import os\r
 import sys\r
-import math\r
 \r
 # C extension\r
 from meshio import mqo\r
@@ -32,182 +34,186 @@ from meshio import mqo
 def isBlender24():\r
     return sys.version_info[0]<3\r
 \r
-\r
 if isBlender24():\r
     # for 2.4\r
     import Blender\r
     from Blender import Mathutils\r
     import bpy\r
 \r
-    # ファイルシステムの文字コード\r
-    # 改造版との共用のため\r
-    FS_ENCODING=sys.getfilesystemencoding()\r
-    if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"):\r
-        INTERNAL_ENCODING='utf-8'\r
-    else:\r
-        INTERNAL_ENCODING=FS_ENCODING\r
+    # wrapper\r
+    import bl24 as bl\r
+\r
+    def createMqoMaterial(m):\r
+        material = Blender.Material.New(\r
+                m.getName().encode(bl.INTERNAL_ENCODING))\r
+        #material.mode |= Blender.Material.Modes.SHADELESS\r
+        # diffuse\r
+        material.rgbCol = [m.color.r, m.color.g, m.color.b]\r
+        material.alpha = m.color.a\r
+        # other\r
+        material.amb=m.ambient\r
+        material.spec=m.specular\r
+        material.hard=int(255 * m.power)\r
+        material.emit=m.emmit\r
+        return material\r
+\r
 else:\r
     # for 2.5\r
     import bpy\r
     from bpy.props import *\r
 \r
+    # wrapper\r
+    import bl25 as bl\r
+\r
+    def createMqoMaterial(m):\r
+        material = bpy.data.materials.new(m.getName())\r
+        # shader\r
+        if m.shader==1:\r
+            material.diffuse_shader='FRESNEL'\r
+        else:\r
+            material.diffuse_shader='LAMBERT'\r
+        # diffuse\r
+        material.diffuse_color=[m.color.r, m.color.g, m.color.b]\r
+        material.diffuse_intensity=m.diffuse\r
+        material.alpha=m.color.a\r
+        # other\r
+        material.ambient = m.ambient\r
+        #material.specular = m.specular\r
+        material.emit=m.emit\r
+        return material\r
+\r
 \r
 def has_mikoto(mqo):\r
+    #for o in mqo.objects:\r
+    #    if o.getName().startswith('bone'):\r
+    #        return True\r
+    #    if o.getName().startswith('sdef'):\r
+    #        return True\r
+    #    if o.getName().startswith('anchor'):\r
+    #        return True\r
     return False\r
 \r
 \r
-def create_materials(scene, mqo, directory):\r
+def __createMaterials(mqo, directory):\r
     """\r
     create blender materials and renturn material list.\r
     """\r
     materials = []\r
-    images = []\r
-    for m in mqo.materials:\r
-        material = Blender.Material.New(m.getName().encode(INTERNAL_ENCODING))\r
-        materials.append(material)\r
-\r
-        material.mode |= Blender.Material.Modes.SHADELESS\r
-        material.rgbCol = [m.color.r, m.color.g, m.color.b]\r
-        material.alpha = m.color.a\r
-        material.amb = m.ambient\r
-        material.spec = m.specular\r
-        material.hard = int(255 * m.power)\r
-        if m.texture!="":\r
-            texture_path=m.getTexture()\r
-\r
-            # load texture image\r
-            if os.path.isabs(texture_path):\r
-                # absolute\r
-                path = texture_path\r
-            else:\r
-                # relative\r
-                path = os.path.join(directory, texture_path)\r
-\r
-            # backslash to slash\r
-            #path = path.replace('\\', '/')\r
-\r
+    textureMap={}\r
+    imageMap={}\r
+    if len(mqo.materials)>0:\r
+        for material_index, m in enumerate(mqo.materials):\r
+            # material\r
+            material=createMqoMaterial(m)\r
+            materials.append(material)\r
             # texture\r
-            if os.path.exists(path):\r
-                image = Blender.Image.Load(path.encode(INTERNAL_ENCODING))\r
-                images.append(image)\r
-                material.mode = material.mode | Blender.Material.Modes.TEXFACE\r
-                tex = Blender.Texture.New(path.encode(INTERNAL_ENCODING))\r
-                tex.type = Blender.Texture.Types.IMAGE\r
-                tex.image = image\r
-                material.setTexture(0, tex, Blender.Texture.TexCo.UV)\r
-            else:\r
-                print("%s not exits" % path)\r
-            \r
-    return materials\r
+            texture_name=m.getTexture()\r
+            if texture_name!='':\r
+                if texture_name in textureMap:\r
+                    texture=textureMap[texture_name]\r
+                else:\r
+                    # load texture image\r
+                    if os.path.isabs(texture_name):\r
+                        # absolute\r
+                        path = texture_name\r
+                    else:\r
+                        # relative\r
+                        path = os.path.join(directory, texture_name)\r
+                    # texture\r
+                    if os.path.exists(path):\r
+                        print("create texture:", path)\r
+                        texture, image=bl.texture.create(path)\r
+                        textureMap[texture_name]=texture\r
+                        imageMap[material_index]=image\r
+                    else:\r
+                        print("%s not exits" % path)\r
+                        continue\r
+                bl.material.addTexture(material, texture)\r
+    else:\r
+        # default material\r
+        pass\r
+    return materials, imageMap\r
 \r
 \r
-def create_objects(scene, root, mqo, materials):\r
+def __createObjects(mqo, root, materials, imageMap, scale):\r
     """\r
     create blender mesh objects.\r
     """\r
-    # store hierarchy\r
+    # tree stack\r
     stack=[root]    \r
-\r
     objects=[]\r
     for o in mqo.objects:\r
-        #print "%s:v(%d),f(%d)" % (o.name, len(o.vertices), len(o.faces))\r
-        # create mesh\r
-        mesh = Blender.Mesh.New()\r
-        mesh_object=scene.objects.new(mesh, o.name.encode('utf-8'))\r
+        mesh, mesh_object=bl.mesh.create(o.getName())\r
 \r
         # add hierarchy\r
         stack_depth=len(stack)-1\r
-        print(o.depth, stack_depth)\r
+        #print(o.depth, stack_depth)\r
         if o.depth<stack_depth:\r
             for i in range(stack_depth-o.depth):\r
                 stack.pop()\r
-        stack[-1].makeParent([mesh_object])\r
+        bl.object.makeParent(stack[-1], mesh_object)\r
         stack.append(mesh_object)\r
 \r
-        if o.name.startswith('sdef'):\r
-            # add sdef object\r
+        if o.getName().startswith('sdef'):\r
             objects.append(mesh_object)\r
-        elif o.name.startswith('anchor'):\r
-            #print("hide %s" % o.name)\r
-            #mesh_object.restrictDisplay=False\r
-            mesh_object.layers=[2]\r
-        elif o.name.startswith('bone'):\r
-            mesh_object.layers=[2]\r
-\r
-        # add vertices\r
-        mesh.verts.extend(Mathutils.Vector(0, 0, 0)) # dummy\r
-        mesh.verts.extend([(v.x, -v.z, v.y) for v in o.vertices])\r
-        # add faces\r
-        mesh_faces=[]\r
-        for face in o.faces:\r
+        elif o.getName().startswith('anchor'):\r
+            bl.object.setLayerMask(mesh_object, [0, 1])\r
+        elif o.getName().startswith('bone'):\r
+            bl.object.setLayerMask(mesh_object, [0, 1])\r
+\r
+        # geometry\r
+        vertices=[(v.x * scale, -v.z * scale, v.y * scale) for v in o.vertices]\r
+        faces=[]\r
+        materialMap={}\r
+        for f in o.faces:\r
             face_indices=[]\r
-            for i in xrange(face.index_count):\r
-                face_indices.append(face.getIndex(i)+1)\r
-            mesh_faces.append(face_indices)\r
-        #new_faces=mesh.faces.extend([face.indices for face in o.faces], \r
-        new_faces=mesh.faces.extend(mesh_faces,\r
-                #ignoreDups=True, \r
-                indexList=True)\r
-        mesh.update()\r
-        \r
-        # gather used materials\r
-        usedMaterials = {}\r
-        if new_faces:\r
-            for i in new_faces:\r
-                if type(i) is int:\r
-                    usedMaterials[o.faces[i].material_index]=True\r
+            # flip face\r
+            for i in reversed(range(f.index_count)):\r
+                face_indices.append(f.getIndex(i))\r
+            faces.append(face_indices)\r
+            materialMap[f.material_index]=True\r
+        bl.mesh.addGeometry(mesh, vertices, faces)\r
 \r
         # blender limits 16 materials per mesh\r
-        # separate mesh ?\r
-        for i, material_index in enumerate(usedMaterials.keys()):\r
+        for i, material_index in enumerate(materialMap.keys()):\r
             if i>=16:\r
+                # split a mesh ?\r
                 print("over 16 materials!")\r
                 break\r
-            mesh.materials+=[materials[material_index]]\r
-            usedMaterials[material_index]=i\r
-        \r
+            bl.mesh.addMaterial(mesh, materials[material_index])\r
+            materialMap[material_index]=i\r
\r
         # set face params\r
-        for i, f in enumerate(o.faces):       \r
-            if not type(new_faces[i]) is int:\r
-                continue\r
-\r
-            face=mesh.faces[new_faces[i]]\r
-\r
+        assert(len(o.faces)==len(mesh.faces))\r
+        bl.mesh.addUV(mesh)\r
+        for i, (f, face) in enumerate(zip(o.faces, mesh.faces)):\r
             uv_array=[]\r
-            for i in xrange(f.index_count):\r
-                uv_array.append(Blender.Mathutils.Vector(\r
-                    f.getUV(i).x, \r
-                    1.0-f.getUV(i).y)\r
-                    )\r
-            try:\r
-                face.uv=uv_array\r
-            except Exception, msg:\r
-                #print msg\r
-                #print face.index, uv_array\r
-                pass\r
-        \r
-            if f.material_index in usedMaterials:\r
-                face.mat = usedMaterials[f.material_index]\r
-\r
-            face.smooth = 1\r
-\r
-        # rmeove dummy 0 vertex\r
-        mesh.verts.delete(0)\r
-            \r
-        mesh.mode |= Blender.Mesh.Modes.AUTOSMOOTH\r
-        mesh.maxSmoothAngle = int(o.smoothing)\r
-        mesh.smooth()\r
-        mesh.calcNormals()\r
-        mesh.flipNormals()\r
-        mesh.update()\r
+            # ToDo FIX\r
+            # flip face\r
+            for j in reversed(range(f.index_count)):\r
+                uv_array.append((f.getUV(j).x, 1.0-f.getUV(j).y))\r
+            bl.mesh.setFaceUV(mesh, i, face, uv_array, \r
+                    imageMap.get(f.material_index, None))\r
+            if f.material_index in materialMap:\r
+                bl.face.setMaterial(face, materialMap[f.material_index])\r
+            bl.face.setSmooth(face, True)\r
 \r
         # mirror modifier\r
         if o.mirror:\r
-            mod=mesh_object.modifiers.append(Blender.Modifier.Types.MIRROR)\r
+            bl.modifier.addMirror(mesh_object)\r
+\r
+        # set smoothing\r
+        bl.mesh.setSmooth(mesh, o.smoothing)\r
+\r
+        # calc normal\r
+        bl.mesh.recalcNormals(mesh_object)\r
 \r
     return objects\r
 \r
 \r
+###############################################################################\r
+# for mqo mikoto bone.\r
+###############################################################################\r
 class MikotoBone(object):\r
     __slots__=[\r
             'name',\r
@@ -226,9 +232,9 @@ class MikotoBone(object):
 \r
         self.name=materials[face.material_index].name.encode('utf-8')\r
 \r
-        i0=face.indices[0]\r
-        i1=face.indices[1]\r
-        i2=face.indices[2]\r
+        i0=face.getIndex(0)\r
+        i1=face.getIndex(1)\r
+        i2=face.getIndex(2)\r
         v0=vertices[i0]\r
         v1=vertices[i1]\r
         v2=vertices[i2]\r
@@ -319,7 +325,7 @@ def build_armature(armature, mikotoBone, parent=None):
         build_armature(armature, child, bone)\r
 \r
 \r
-def create_armature(scene, mqo):\r
+def create_armature(mqo):\r
     """\r
     create armature\r
     """\r
@@ -409,8 +415,8 @@ def create_armature(scene, mqo):
 \r
 class TrianglePlane(object):\r
     """\r
-    mikoto方式ボーンのアンカーウェイト計算用。\r
-    (不完全)\r
+    mikoto\e$BJ}<0%\!<%s$N%"%s%+!<%&%'%$%H7W;;MQ!#\e(B\r
+    (\e$BIT40A4\e(B)\r
     """\r
     __slots__=['normal', \r
             'v0', 'v1', 'v2',\r
@@ -468,7 +474,7 @@ class TrianglePlane(object):
 \r
 class MikotoAnchor(object):\r
     """\r
-    mikoto方式スケルトンのアンカー。\r
+    mikoto\e$BJ}<0%9%1%k%H%s$N%"%s%+!<!#\e(B\r
     """\r
     __slots__=[\r
             "triangles", "bbox",\r
@@ -589,57 +595,110 @@ def create_bone_weight(scene, mqo, armature_object, objects):
                 # debug orphan vertex\r
                 print('orphan', mvert)\r
         mesh.update()\r
-    \r
 \r
-def execute_24(filename):\r
-    """\r
-    import a mqo file.\r
-    """\r
-    filename=filename.decode(INTERNAL_ENCODING)\r
-    print "##start mqo_import.py##"\r
-    print INTERNAL_ENCODING, FS_ENCODING\r
-    print "parse mqo file: %s" % (filename)\r
-\r
-    Blender.Window.WaitCursor(1) \r
-    t = Blender.sys.time() \r
 \r
+def __execute(filename, scene, scale=0.1):\r
     # parse file\r
     io=mqo.IO()\r
-    \r
     if not io.read(filename):\r
+        bl.message("fail to load %s" % filename)\r
         return\r
 \r
-    # get active scene\r
-    scene = Blender.Scene.GetCurrent()\r
-\r
     # create materials\r
-    materials=create_materials(scene, io, os.path.dirname(filename))\r
\r
+    materials, imageMap=__createMaterials(io, os.path.dirname(filename))\r
+    if len(materials)==0:\r
+        materials.append(bl.material.create('default'))\r
+\r
     # create objects\r
-    root=scene.objects.new("Empty")\r
-    root.setName(os.path.basename(filename))\r
-    objects=create_objects(scene, root, io, materials)\r
+    root=bl.object.createEmpty(os.path.basename(filename))\r
+    objects=__createObjects(io, root, materials, imageMap, scale)\r
 \r
     if has_mikoto(io):\r
         # create mikoto bone\r
-        armature_object=create_armature(scene, io)\r
+        armature_object=create_armature(io)\r
         if armature_object:\r
             root.makeParent([armature_object])\r
 \r
             # create bone weight\r
-            create_bone_weight(scene, io, armature_object, objects)\r
-\r
-\r
-    print('finished in %.2f seconds' % (Blender.sys.time()-t))\r
-    print('')\r
-    Blender.Redraw()\r
-    Blender.Window.WaitCursor(0) \r
-\r
+            create_bone_weight(io, armature_object, objects)\r
 \r
\r
+###############################################################################\r
+# register\r
+###############################################################################\r
 if isBlender24():\r
     # for 2.4\r
+    def execute_24(filename):\r
+        scene=Blender.Scene.GetCurrent()\r
+        bl.initialize('mqo_import', scene)\r
+        __execute(\r
+                filename.decode(bl.INTERNAL_ENCODING), \r
+                scene)\r
+        bl.finalize()\r
+\r
+    # execute\r
     Blender.Window.FileSelector(execute_24, 'Import MQO', '*.mqo')\r
+\r
 else:\r
     # for 2.5\r
-    pass\r
+    def execute_25(filename, scene, scale):\r
+        bl.initialize('mqo_import', scene)\r
+        __execute(filename, scene, scale)\r
+        bl.finalize()\r
+\r
+    # operator\r
+    class IMPORT_OT_mqo(bpy.types.Operator):\r
+        '''Import from Metasequoia file format (.mqo)'''\r
+        bl_idname = "import_scene.mqo"\r
+        bl_label = 'Import MQO'\r
+\r
+        # List of operator properties, the attributes will be assigned\r
+        # to the class instance from the operator settings before calling.\r
+\r
+        path = StringProperty(\r
+                name="File Path", \r
+                description="File path used for importing the MQO file", \r
+                maxlen= 1024, default= "")\r
+        filename = StringProperty(\r
+                name="File Name", \r
+                description="Name of the file.")\r
+        directory = StringProperty(\r
+                name="Directory", \r
+                description="Directory of the file.")\r
+\r
+        scale = FloatProperty(\r
+                name="Scale", \r
+                description="Scale the MQO by this value", \r
+                min=0.0001, max=1000000.0, \r
+                soft_min=0.001, soft_max=100.0, default=0.1)\r
+\r
+        def execute(self, context):\r
+            execute_25(\r
+                    self.properties.path, \r
+                    context.scene, \r
+                    self.properties.scale)\r
+            return 'FINISHED'\r
+\r
+        def invoke(self, context, event):\r
+            wm=context.manager\r
+            wm.add_fileselect(self)\r
+            return 'RUNNING_MODAL'\r
+\r
+\r
+    # register menu\r
+    def menu_func(self, context): \r
+        self.layout.operator(\r
+                IMPORT_OT_mqo.bl_idname, \r
+                text="Metasequoia (.mqo)")\r
+\r
+    def register():\r
+        bpy.types.register(IMPORT_OT_mqo)\r
+        bpy.types.INFO_MT_file_import.append(menu_func)\r
+\r
+    def unregister():\r
+        bpy.types.unregister(IMPORT_OT_mqo)\r
+        bpy.types.INFO_MT_file_import.remove(menu_func)\r
+\r
+    if __name__=="__main__":\r
+        register()\r
 \r