"""\r
__author__=['ousttrue']\r
__url__ = ["http://gunload.web.fc2.com/blender/"]\r
-__version__= '0.6 2010/05/05'\r
+__version__= '2.0'\r
__bpydoc__= '''\\r
\r
MQO Importer\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
+0.8 20100619: fix multibyte object name.\r
+0.9 20100626: refactoring.\r
+2.0 20100724: update for Blender2.53.\r
+2.1 20100731: add full python module.\r
'''\r
+\r
+bl_addon_info = {\r
+ 'category': 'Import/Export',\r
+ 'name': 'Import: Metasequioa Model Format (.mqo)',\r
+ 'author': 'ousttrue',\r
+ 'version': '2.0',\r
+ 'blender': (2, 5, 3),\r
+ 'location': 'File > Import',\r
+ 'description': 'Import from the Metasequioa Model Format (.mqo)',\r
+ 'warning': '', # used for warning icon and text in addons panel\r
+ 'wiki_url': 'http://sourceforge.jp/projects/meshio/wiki/FrontPage',\r
+ }\r
+\r
import os\r
import sys\r
-import math\r
\r
-# C extension\r
-from meshio import mqo\r
+try:\r
+ # C extension\r
+ from meshio import mqo\r
+ print('use meshio C module')\r
+except ImportError:\r
+ # full python\r
+ from pymeshio 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
+ # 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.emit\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
\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
build_armature(armature, child, bone)\r
\r
\r
-def create_armature(scene, mqo):\r
+def create_armature(mqo):\r
"""\r
create armature\r
"""\r
\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
\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
# 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
+ filepath = bpy.props.StringProperty()\r
+ filename = bpy.props.StringProperty()\r
+ directory = bpy.props.StringProperty()\r
+\r
+ scale = bpy.props.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.filepath, \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
+ icon='PLUGIN'\r
+ )\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