OSDN Git Service

integrate 2.4 and 2.5.
authorousttrue <ousttrue@gmail.com>
Sun, 6 Jun 2010 03:15:33 +0000 (12:15 +0900)
committerousttrue <ousttrue@gmail.com>
Sun, 6 Jun 2010 03:15:33 +0000 (12:15 +0900)
swig/blender/Makefile [new file with mode: 0644]
swig/blender/cp.sh [new file with mode: 0755]
swig/blender/mqo_import.py [new file with mode: 0644]

diff --git a/swig/blender/Makefile b/swig/blender/Makefile
new file mode 100644 (file)
index 0000000..176a817
--- /dev/null
@@ -0,0 +1,5 @@
+all: copy
+
+copy: mqo_import.py
+       ./cp.sh
+
diff --git a/swig/blender/cp.sh b/swig/blender/cp.sh
new file mode 100755 (executable)
index 0000000..df4e175
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/sh
+DST="/cygdrive/T//Blender/bf-blender/build2.4git/bin/Release/.blender/scripts"
+cp *.py $DST
+
+DST="/cygdrive/T//Blender/bf-blender/build2.4git/bin/Debug/.blender/scripts"
+cp *.py $DST
+
+DST="T:/Blender/blender-2.49b-windows/.blender/scripts"
+cp *.py $DST
diff --git a/swig/blender/mqo_import.py b/swig/blender/mqo_import.py
new file mode 100644 (file)
index 0000000..cb05bc8
--- /dev/null
@@ -0,0 +1,645 @@
+#!BPY\r
+# coding: utf-8\r
+""" \r
+Name: 'Metasequoia(.mqo)...'\r
+Blender: 245\r
+Group: 'Import'\r
+Tooltip: 'Import from Metasequoia file format (.mqo)'\r
+"""\r
+__author__=['ousttrue']\r
+__url__ = ["http://gunload.web.fc2.com/blender/"]\r
+__version__= '0.6 2010/05/05'\r
+__bpydoc__= '''\\r
+\r
+MQO Importer\r
+\r
+This script imports a mqo into Blender for editing.\r
+\r
+0.2 20080123: update.\r
+0.3 20091125: modify for linux.\r
+0.4 20100310: rewrite.\r
+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
+'''\r
+import os\r
+import sys\r
+import math\r
+\r
+# C extension\r
+from meshio import mqo\r
+\r
+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
+else:\r
+    # for 2.5\r
+    import bpy\r
+    from bpy.props import *\r
+\r
+\r
+def has_mikoto(mqo):\r
+    return False\r
+\r
+\r
+def create_materials(scene, 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
+            # 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
+\r
+\r
+def create_objects(scene, root, mqo, materials):\r
+    """\r
+    create blender mesh objects.\r
+    """\r
+    # store hierarchy\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
+\r
+        # add hierarchy\r
+        stack_depth=len(stack)-1\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
+        stack.append(mesh_object)\r
+\r
+        if o.name.startswith('sdef'):\r
+            # add sdef object\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
+            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
+\r
+        # blender limits 16 materials per mesh\r
+        # separate mesh ?\r
+        for i, material_index in enumerate(usedMaterials.keys()):\r
+            if i>=16:\r
+                print("over 16 materials!")\r
+                break\r
+            mesh.materials+=[materials[material_index]]\r
+            usedMaterials[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
+            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
+\r
+        # mirror modifier\r
+        if o.mirror:\r
+            mod=mesh_object.modifiers.append(Blender.Modifier.Types.MIRROR)\r
+\r
+    return objects\r
+\r
+\r
+class MikotoBone(object):\r
+    __slots__=[\r
+            'name',\r
+            'iHead', 'iTail', 'iUp',\r
+            'vHead', 'vTail', 'vUp',\r
+            'parent', 'isFloating',\r
+            'children',\r
+            ]\r
+    def __init__(self, face=None, vertices=None, materials=None):\r
+        self.parent=None\r
+        self.isFloating=False\r
+        self.children=[]\r
+        if not face:\r
+            self.name='root'\r
+            return\r
+\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
+        v0=vertices[i0]\r
+        v1=vertices[i1]\r
+        v2=vertices[i2]\r
+        e01=v1-v0\r
+        e12=v2-v1\r
+        e20=v0-v2\r
+        sqNorm0=e01.getSqNorm()\r
+        sqNorm1=e12.getSqNorm()\r
+        sqNorm2=e20.getSqNorm()\r
+        if sqNorm0>sqNorm1:\r
+            if sqNorm1>sqNorm2:\r
+                # e01 > e12 > e20\r
+                self.iHead=i2\r
+                self.iTail=i1\r
+                self.iUp=i0\r
+            else:\r
+                if sqNorm0>sqNorm2:\r
+                    # e01 > e20 > e12\r
+                    self.iHead=i2\r
+                    self.iTail=i0\r
+                    self.iUp=i1\r
+                else:\r
+                    # e20 > e01 > e12\r
+                    self.iHead=i1\r
+                    self.iTail=i0\r
+                    self.iUp=i2\r
+        else:\r
+            # 0 < 1\r
+            if sqNorm1<sqNorm2:\r
+                # e20 > e12 > e01\r
+                self.iHead=i1\r
+                self.iTail=i2\r
+                self.iUp=i0\r
+            else:\r
+                if sqNorm0<sqNorm2:\r
+                    # e12 > e20 > e01\r
+                    self.iHead=i0\r
+                    self.iTail=i2\r
+                    self.iUp=i1\r
+                else:\r
+                    # e12 > e01 > e20\r
+                    self.iHead=i0\r
+                    self.iTail=i1\r
+                    self.iUp=i2\r
+        self.vHead=vertices[self.iHead]\r
+        self.vTail=vertices[self.iTail]\r
+        self.vUp=vertices[self.iUp]\r
+\r
+        if self.name.endswith('[]'):\r
+            basename=self.name[0:-2]\r
+            # expand LR name\r
+            if self.vTail.x>0:\r
+                self.name="%s_L" % basename\r
+            else:\r
+                self.name="%s_R" % basename\r
+\r
+\r
+    def setParent(self, parent, floating=False):\r
+        if floating:\r
+            self.isFloating=True\r
+        self.parent=parent\r
+        parent.children.append(self)\r
+\r
+    def printTree(self, indent=''):\r
+        print("%s%s" % (indent, self.name))\r
+        for child in self.children:\r
+            child.printTree(indent+'  ')\r
+\r
+\r
+def build_armature(armature, mikotoBone, parent=None):\r
+    """\r
+    create a armature bone.\r
+    """\r
+    bone = Armature.Editbone()\r
+    bone.name = mikotoBone.name.encode('utf-8')\r
+    armature.bones[bone.name] = bone\r
+\r
+    bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a())\r
+    bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a())\r
+    if parent:\r
+        bone.parent=parent\r
+        if mikotoBone.isFloating:\r
+            pass\r
+        else:\r
+            bone.options=[Armature.CONNECTED]\r
+\r
+    for child in mikotoBone.children:\r
+        build_armature(armature, child, bone)\r
+\r
+\r
+def create_armature(scene, mqo):\r
+    """\r
+    create armature\r
+    """\r
+    boneObject=None\r
+    for o in mqo.objects:\r
+        if o.name.startswith('bone'):\r
+            boneObject=o\r
+            break\r
+    if not boneObject:\r
+        return\r
+\r
+    tailMap={}\r
+    for f in boneObject.faces:\r
+        if f.index_count!=3:\r
+            print("invalid index_count: %d" % f.index_count)\r
+            continue\r
+        b=MikotoBone(f, boneObject.vertices, mqo.materials)\r
+        tailMap[b.iTail]=b\r
+\r
+    #################### \r
+    # build mikoto bone tree\r
+    #################### \r
+    mikotoRoot=MikotoBone()\r
+\r
+    for b in tailMap.values():\r
+        # each bone has unique parent or is root bone.\r
+        if b.iHead in tailMap:\r
+            b.setParent(tailMap[b.iHead])\r
+        else: \r
+            isFloating=False\r
+            for e in boneObject.edges:\r
+                if  b.iHead==e.indices[0]:\r
+                    # floating bone\r
+                    if e.indices[1] in tailMap:\r
+                        b.setParent(tailMap[e.indices[1]], True)\r
+                        isFloating=True\r
+                        break\r
+                elif b.iHead==e.indices[1]:\r
+                    # floating bone\r
+                    if e.indices[0] in tailMap:\r
+                        b.setParent(tailMap[e.indices[0]], True)\r
+                        isFloating=True\r
+                        break\r
+            if isFloating:\r
+                continue\r
+\r
+            # no parent bone\r
+            b.setParent(mikotoRoot, True)\r
+\r
+    if len(mikotoRoot.children)==0:\r
+        print("no root bone")\r
+        return\r
+\r
+    if len(mikotoRoot.children)==1:\r
+        # single root\r
+        mikotoRoot=mikotoRoot.children[0]\r
+        mikotoRoot.parent=None\r
+    else:\r
+        mikotoRoot.vHead=Vector3(0, 10, 0)\r
+        mikotoRoot.vTail=Vector3(0, 0, 0)\r
+\r
+    #################### \r
+    # create armature\r
+    #################### \r
+    armature = Armature.New()\r
+    # link to object\r
+    armature_object = scene.objects.new(armature)\r
+    # create action\r
+    act = Armature.NLA.NewAction()\r
+    act.setActive(armature_object)\r
+    # set XRAY\r
+    armature_object.drawMode |= Object.DrawModes.XRAY\r
+    # armature settings\r
+    armature.drawType = Armature.OCTAHEDRON\r
+    armature.envelopes = False\r
+    armature.vertexGroups = True\r
+    armature.mirrorEdit = True\r
+    armature.drawNames=True\r
+\r
+    # edit bones\r
+    armature.makeEditable()\r
+    build_armature(armature, mikotoRoot)\r
+    armature.update()\r
+\r
+    return armature_object\r
+        \r
+\r
+class TrianglePlane(object):\r
+    """\r
+    mikoto方式ボーンのアンカーウェイト計算用。\r
+    (不完全)\r
+    """\r
+    __slots__=['normal', \r
+            'v0', 'v1', 'v2',\r
+            ]\r
+    def __init__(self, v0, v1, v2):\r
+        self.v0=v0\r
+        self.v1=v1\r
+        self.v2=v2\r
+\r
+    def isInsideXY(self, p):\r
+        v0=Vector2(self.v0.x, self.v0.y)\r
+        v1=Vector2(self.v1.x, self.v1.y)\r
+        v2=Vector2(self.v2.x, self.v2.y)\r
+        e01=v1-v0\r
+        e12=v2-v1\r
+        e20=v0-v2\r
+        c0=Vector2.cross(e01, p-v0)\r
+        c1=Vector2.cross(e12, p-v1)\r
+        c2=Vector2.cross(e20, p-v2)\r
+        if c0>=0 and c1>=0 and c2>=0:\r
+            return True\r
+        if c0<=0 and c1<=0 and c2<=0:\r
+            return True\r
+\r
+    def isInsideYZ(self, p):\r
+        v0=Vector2(self.v0.y, self.v0.z)\r
+        v1=Vector2(self.v1.y, self.v1.z)\r
+        v2=Vector2(self.v2.y, self.v2.z)\r
+        e01=v1-v0\r
+        e12=v2-v1\r
+        e20=v0-v2\r
+        c0=Vector2.cross(e01, p-v0)\r
+        c1=Vector2.cross(e12, p-v1)\r
+        c2=Vector2.cross(e20, p-v2)\r
+        if c0>=0 and c1>=0 and c2>=0:\r
+            return True\r
+        if c0<=0 and c1<=0 and c2<=0:\r
+            return True\r
+\r
+    def isInsideZX(self, p):\r
+        v0=Vector2(self.v0.z, self.v0.x)\r
+        v1=Vector2(self.v1.z, self.v1.x)\r
+        v2=Vector2(self.v2.z, self.v2.x)\r
+        e01=v1-v0\r
+        e12=v2-v1\r
+        e20=v0-v2\r
+        c0=Vector2.cross(e01, p-v0)\r
+        c1=Vector2.cross(e12, p-v1)\r
+        c2=Vector2.cross(e20, p-v2)\r
+        if c0>=0 and c1>=0 and c2>=0:\r
+            return True\r
+        if c0<=0 and c1<=0 and c2<=0:\r
+            return True\r
+\r
+\r
+class MikotoAnchor(object):\r
+    """\r
+    mikoto方式スケルトンのアンカー。\r
+    """\r
+    __slots__=[\r
+            "triangles", "bbox",\r
+            ]\r
+    def __init__(self):\r
+        self.triangles=[]\r
+        self.bbox=None\r
+\r
+    def push(self, face, vertices):\r
+        if face.index_count==3:\r
+            self.triangles.append(TrianglePlane(\r
+                vertices[face.indices[0]],\r
+                vertices[face.indices[1]],\r
+                vertices[face.indices[2]]\r
+                ))\r
+        elif face.index_count==4:\r
+            self.triangles.append(TrianglePlane(\r
+                vertices[face.indices[0]],\r
+                vertices[face.indices[1]],\r
+                vertices[face.indices[2]]\r
+                ))\r
+            self.triangles.append(TrianglePlane(\r
+                vertices[face.indices[2]],\r
+                vertices[face.indices[3]],\r
+                vertices[face.indices[0]]\r
+                ))\r
+        # bounding box\r
+        if not self.bbox:\r
+            self.bbox=BoundingBox(vertices[face.indices[0]])\r
+        for i in face.indices:\r
+            self.bbox.expand(vertices[i])\r
+\r
+\r
+    def calcWeight(self, v):\r
+        if not self.bbox.isInside(v):\r
+            return 0\r
+\r
+        if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x):\r
+            return 1.0\r
+        else:\r
+            return 0\r
+        \r
+    def anyXY(self, x, y):\r
+        for t in self.triangles:\r
+            if t.isInsideXY(Vector2(x, y)):\r
+                return True\r
+        return False\r
+\r
+    def anyYZ(self, y, z):\r
+        for t in self.triangles:\r
+            if t.isInsideYZ(Vector2(y, z)):\r
+                return True\r
+        return False\r
+\r
+    def anyZX(self, z, x):\r
+        for t in self.triangles:\r
+            if t.isInsideZX(Vector2(z, x)):\r
+                return True\r
+        return False\r
+\r
+\r
+def create_bone_weight(scene, mqo, armature_object, objects):\r
+    """\r
+    create mikoto bone weight.\r
+    """\r
+    anchorMap={}\r
+    # setup mikoto anchors\r
+    for o in mqo.objects:\r
+        if o.name.startswith("anchor"):\r
+            for f in o.faces:\r
+                name=mqo.materials[f.material_index].name\r
+                if name.endswith('[]'):\r
+                    basename=name[0:-2]\r
+                    v=o.vertices[f.indices[0]]\r
+                    if(v.x>0):\r
+                        # L\r
+                        name_L=basename+'_L'\r
+                        if not name_L in anchorMap:\r
+                            anchorMap[name_L]=MikotoAnchor()\r
+                        anchorMap[name_L].push(f, o.vertices)\r
+                    elif(v.x<0):\r
+                        # R\r
+                        name_R=basename+'_R'\r
+                        if not name_R in anchorMap:\r
+                            anchorMap[name_R]=MikotoAnchor()\r
+                        anchorMap[name_R].push(f, o.vertices)\r
+                    else:\r
+                        print("no side", v)\r
+                else:\r
+                    if not name in anchorMap:\r
+                        anchorMap[name]=MikotoAnchor()\r
+                    anchorMap[name].push(f, o.vertices)\r
+\r
+    for o in objects:\r
+        # add armature modifier\r
+        mod=o.modifiers.append(Modifier.Types.ARMATURE)\r
+        mod[Modifier.Settings.OBJECT] = armature_object\r
+        mod[Modifier.Settings.ENVELOPES] = False\r
+        o.makeDisplayList()\r
+        # create vertex group\r
+        mesh=o.getData(mesh=True)\r
+        for name in anchorMap.keys():\r
+            mesh.addVertGroup(name)\r
+        mesh.update()\r
+                 \r
+    # assing vertices to vertex group\r
+    for o in objects:\r
+        mesh=o.getData(mesh=True)\r
+        for i, mvert in enumerate(mesh.verts):\r
+            hasWeight=False\r
+            for name, anchor in anchorMap.items():\r
+                weight=anchor.calcWeight(mvert.co)\r
+                if weight>0:\r
+                    mesh.assignVertsToGroup(\r
+                            name, [i], weight, Mesh.AssignModes.ADD)\r
+                    hasWeight=True\r
+            if not hasWeight:\r
+                # 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
+    # parse file\r
+    io=mqo.IO()\r
+    \r
+    if not io.read(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
+    # create objects\r
+    root=scene.objects.new("Empty")\r
+    root.setName(os.path.basename(filename))\r
+    objects=create_objects(scene, root, io, materials)\r
+\r
+    if has_mikoto(io):\r
+        # create mikoto bone\r
+        armature_object=create_armature(scene, 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
+\r
+if isBlender24():\r
+    # for 2.4\r
+    Blender.Window.FileSelector(execute_24, 'Import MQO', '*.mqo')\r
+else:\r
+    # for 2.5\r
+    pass\r
+\r