From ca1773dd2ea24d115322794f1f08c7ab08aa8b0c Mon Sep 17 00:00:00 2001 From: ousttrue Date: Sun, 6 Jun 2010 13:33:01 +0900 Subject: [PATCH] implement mqo_import.py --- swig/blender/Makefile | 2 +- swig/blender/cp.py | 40 ++ swig/blender/cp.sh | 9 - swig/blender/mqo_import.py | 1325 +++++++++++++++++++++++++------------------- 4 files changed, 811 insertions(+), 565 deletions(-) create mode 100644 swig/blender/cp.py delete mode 100755 swig/blender/cp.sh diff --git a/swig/blender/Makefile b/swig/blender/Makefile index 176a817..0bf01d0 100644 --- a/swig/blender/Makefile +++ b/swig/blender/Makefile @@ -1,5 +1,5 @@ all: copy copy: mqo_import.py - ./cp.sh + /cygdrive/C/Python26/python cp.py $^ diff --git a/swig/blender/cp.py b/swig/blender/cp.py new file mode 100644 index 0000000..f44832f --- /dev/null +++ b/swig/blender/cp.py @@ -0,0 +1,40 @@ +import shutil + +DST_24=[ + "T:/Blender/bf-blender/build2.4git/bin/Release/.blender/scripts", + "T:/Blender/bf-blender/build2.4git/bin/Debug/.blender/scripts", + "T:/Blender/blender-2.49b-windows/.blender/scripts", + ] + +MAP_25={ + "mqo_import.py": "import_scene_mqo.py", + } + +DST_25=[ + "T:/Blender/blender-2.5-alpha2-win32/.blender/scripts/io", + "T:/Blender/bf-blender/build2.5/bin/Debug/.blender/scripts/io", + "T:/Blender/bf-blender/build2.5git/bin/Release/.blender/scripts/io", + "T:/Blender/1340_Release/Release/.blender/scripts/io", + ] + +def copy24(src): + print("copy %s..." % src) + for dst in DST_24: + dst="%s/%s" % (dst, src) + print(dst) + shutil.copy(src, dst) + +def copy25(src): + print("copy %s..." % src) + for dst in DST_25: + dst="%s/%s" % (dst, MAP_25[src]) + print(dst) + shutil.copy(src, dst) + +if __name__=="__main__": + import sys + + for v in sys.argv[1:]: + copy24(v) + copy25(v) + diff --git a/swig/blender/cp.sh b/swig/blender/cp.sh deleted file mode 100755 index df4e175..0000000 --- a/swig/blender/cp.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/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 index cb05bc8..13d2aac 100644 --- a/swig/blender/mqo_import.py +++ b/swig/blender/mqo_import.py @@ -22,6 +22,9 @@ This script imports a mqo into Blender for editing. 0.6 20100505: C extension. 0.7 20100606: integrate 2.4 and 2.5. ''' +############################################################################### +# import +############################################################################### import os import sys import math @@ -32,7 +35,6 @@ from meshio import mqo def isBlender24(): return sys.version_info[0]<3 - if isBlender24(): # for 2.4 import Blender @@ -52,594 +54,807 @@ else: from bpy.props import * -def has_mikoto(mqo): - return False - - -def create_materials(scene, mqo, directory): - """ - create blender materials and renturn material list. - """ - materials = [] - images = [] - for m in mqo.materials: - material = Blender.Material.New(m.getName().encode(INTERNAL_ENCODING)) - materials.append(material) - - material.mode |= Blender.Material.Modes.SHADELESS - material.rgbCol = [m.color.r, m.color.g, m.color.b] - material.alpha = m.color.a - material.amb = m.ambient - material.spec = m.specular - material.hard = int(255 * m.power) - if m.texture!="": - texture_path=m.getTexture() - - # load texture image - if os.path.isabs(texture_path): - # absolute - path = texture_path +############################################################################### +# implement +############################################################################### +if isBlender24(): + def has_mikoto(mqo): + return False + + + def create_materials(scene, mqo, directory): + """ + create blender materials and renturn material list. + """ + materials = [] + images = [] + for m in mqo.materials: + material = Blender.Material.New(m.getName().encode(INTERNAL_ENCODING)) + materials.append(material) + + material.mode |= Blender.Material.Modes.SHADELESS + material.rgbCol = [m.color.r, m.color.g, m.color.b] + material.alpha = m.color.a + material.amb = m.ambient + material.spec = m.specular + material.hard = int(255 * m.power) + if m.texture!="": + texture_path=m.getTexture() + + # load texture image + if os.path.isabs(texture_path): + # absolute + path = texture_path + else: + # relative + path = os.path.join(directory, texture_path) + + # backslash to slash + #path = path.replace('\\', '/') + + # texture + if os.path.exists(path): + image = Blender.Image.Load(path.encode(INTERNAL_ENCODING)) + images.append(image) + material.mode = material.mode | Blender.Material.Modes.TEXFACE + tex = Blender.Texture.New(path.encode(INTERNAL_ENCODING)) + tex.type = Blender.Texture.Types.IMAGE + tex.image = image + material.setTexture(0, tex, Blender.Texture.TexCo.UV) + else: + print("%s not exits" % path) + + return materials + + + def create_objects(scene, root, mqo, materials): + """ + create blender mesh objects. + """ + # store hierarchy + stack=[root] + + objects=[] + for o in mqo.objects: + #print "%s:v(%d),f(%d)" % (o.name, len(o.vertices), len(o.faces)) + # create mesh + mesh = Blender.Mesh.New() + mesh_object=scene.objects.new(mesh, o.name.encode('utf-8')) + + # add hierarchy + stack_depth=len(stack)-1 + print(o.depth, stack_depth) + if o.depth=16: + print("over 16 materials!") + break + mesh.materials+=[materials[material_index]] + usedMaterials[material_index]=i + + # set face params + for i, f in enumerate(o.faces): + if not type(new_faces[i]) is int: + continue + + face=mesh.faces[new_faces[i]] + + uv_array=[] + for i in xrange(f.index_count): + uv_array.append(Blender.Mathutils.Vector( + f.getUV(i).x, + 1.0-f.getUV(i).y) + ) + try: + face.uv=uv_array + except Exception as msg: + #print msg + #print face.index, uv_array + pass + + if f.material_index in usedMaterials: + face.mat = usedMaterials[f.material_index] + + face.smooth = 1 + + # rmeove dummy 0 vertex + mesh.verts.delete(0) + + mesh.mode |= Blender.Mesh.Modes.AUTOSMOOTH + mesh.maxSmoothAngle = int(o.smoothing) + mesh.smooth() + mesh.calcNormals() + mesh.flipNormals() + mesh.update() + + # mirror modifier + if o.mirror: + mod=mesh_object.modifiers.append(Blender.Modifier.Types.MIRROR) + + return objects + + + class MikotoBone(object): + __slots__=[ + 'name', + 'iHead', 'iTail', 'iUp', + 'vHead', 'vTail', 'vUp', + 'parent', 'isFloating', + 'children', + ] + def __init__(self, face=None, vertices=None, materials=None): + self.parent=None + self.isFloating=False + self.children=[] + if not face: + self.name='root' + return + + self.name=materials[face.material_index].name.encode('utf-8') + + i0=face.indices[0] + i1=face.indices[1] + i2=face.indices[2] + v0=vertices[i0] + v1=vertices[i1] + v2=vertices[i2] + e01=v1-v0 + e12=v2-v1 + e20=v0-v2 + sqNorm0=e01.getSqNorm() + sqNorm1=e12.getSqNorm() + sqNorm2=e20.getSqNorm() + if sqNorm0>sqNorm1: + if sqNorm1>sqNorm2: + # e01 > e12 > e20 + self.iHead=i2 + self.iTail=i1 + self.iUp=i0 + else: + if sqNorm0>sqNorm2: + # e01 > e20 > e12 + self.iHead=i2 + self.iTail=i0 + self.iUp=i1 + else: + # e20 > e01 > e12 + self.iHead=i1 + self.iTail=i0 + self.iUp=i2 else: - # relative - path = os.path.join(directory, texture_path) - - # backslash to slash - #path = path.replace('\\', '/') - - # texture - if os.path.exists(path): - image = Blender.Image.Load(path.encode(INTERNAL_ENCODING)) - images.append(image) - material.mode = material.mode | Blender.Material.Modes.TEXFACE - tex = Blender.Texture.New(path.encode(INTERNAL_ENCODING)) - tex.type = Blender.Texture.Types.IMAGE - tex.image = image - material.setTexture(0, tex, Blender.Texture.TexCo.UV) + # 0 < 1 + if sqNorm1 e12 > e01 + self.iHead=i1 + self.iTail=i2 + self.iUp=i0 + else: + if sqNorm0 e20 > e01 + self.iHead=i0 + self.iTail=i2 + self.iUp=i1 + else: + # e12 > e01 > e20 + self.iHead=i0 + self.iTail=i1 + self.iUp=i2 + self.vHead=vertices[self.iHead] + self.vTail=vertices[self.iTail] + self.vUp=vertices[self.iUp] + + if self.name.endswith('[]'): + basename=self.name[0:-2] + # expand LR name + if self.vTail.x>0: + self.name="%s_L" % basename + else: + self.name="%s_R" % basename + + + def setParent(self, parent, floating=False): + if floating: + self.isFloating=True + self.parent=parent + parent.children.append(self) + + def printTree(self, indent=''): + print("%s%s" % (indent, self.name)) + for child in self.children: + child.printTree(indent+' ') + + + def build_armature(armature, mikotoBone, parent=None): + """ + create a armature bone. + """ + bone = Armature.Editbone() + bone.name = mikotoBone.name.encode('utf-8') + armature.bones[bone.name] = bone + + bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a()) + bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a()) + if parent: + bone.parent=parent + if mikotoBone.isFloating: + pass else: - print("%s not exits" % path) - - return materials + bone.options=[Armature.CONNECTED] + for child in mikotoBone.children: + build_armature(armature, child, bone) -def create_objects(scene, root, mqo, materials): - """ - create blender mesh objects. - """ - # store hierarchy - stack=[root] - objects=[] - for o in mqo.objects: - #print "%s:v(%d),f(%d)" % (o.name, len(o.vertices), len(o.faces)) - # create mesh - mesh = Blender.Mesh.New() - mesh_object=scene.objects.new(mesh, o.name.encode('utf-8')) - - # add hierarchy - stack_depth=len(stack)-1 - print(o.depth, stack_depth) - if o.depth=16: - print("over 16 materials!") + def create_armature(scene, mqo): + """ + create armature + """ + boneObject=None + for o in mqo.objects: + if o.name.startswith('bone'): + boneObject=o break - mesh.materials+=[materials[material_index]] - usedMaterials[material_index]=i - - # set face params - for i, f in enumerate(o.faces): - if not type(new_faces[i]) is int: + if not boneObject: + return + + tailMap={} + for f in boneObject.faces: + if f.index_count!=3: + print("invalid index_count: %d" % f.index_count) continue + b=MikotoBone(f, boneObject.vertices, mqo.materials) + tailMap[b.iTail]=b + + #################### + # build mikoto bone tree + #################### + mikotoRoot=MikotoBone() + + for b in tailMap.values(): + # each bone has unique parent or is root bone. + if b.iHead in tailMap: + b.setParent(tailMap[b.iHead]) + else: + isFloating=False + for e in boneObject.edges: + if b.iHead==e.indices[0]: + # floating bone + if e.indices[1] in tailMap: + b.setParent(tailMap[e.indices[1]], True) + isFloating=True + break + elif b.iHead==e.indices[1]: + # floating bone + if e.indices[0] in tailMap: + b.setParent(tailMap[e.indices[0]], True) + isFloating=True + break + if isFloating: + continue + + # no parent bone + b.setParent(mikotoRoot, True) + + if len(mikotoRoot.children)==0: + print("no root bone") + return - face=mesh.faces[new_faces[i]] + if len(mikotoRoot.children)==1: + # single root + mikotoRoot=mikotoRoot.children[0] + mikotoRoot.parent=None + else: + mikotoRoot.vHead=Vector3(0, 10, 0) + mikotoRoot.vTail=Vector3(0, 0, 0) + + #################### + # create armature + #################### + armature = Armature.New() + # link to object + armature_object = scene.objects.new(armature) + # create action + act = Armature.NLA.NewAction() + act.setActive(armature_object) + # set XRAY + armature_object.drawMode |= Object.DrawModes.XRAY + # armature settings + armature.drawType = Armature.OCTAHEDRON + armature.envelopes = False + armature.vertexGroups = True + armature.mirrorEdit = True + armature.drawNames=True + + # edit bones + armature.makeEditable() + build_armature(armature, mikotoRoot) + armature.update() + + return armature_object + - uv_array=[] - for i in xrange(f.index_count): - uv_array.append(Blender.Mathutils.Vector( - f.getUV(i).x, - 1.0-f.getUV(i).y) - ) - try: - face.uv=uv_array - except Exception, msg: - #print msg - #print face.index, uv_array - pass - - if f.material_index in usedMaterials: - face.mat = usedMaterials[f.material_index] + class TrianglePlane(object): + """ + mikoto方式ボーンのアンカーウェイト計算用。 + (不完全) + """ + __slots__=['normal', + 'v0', 'v1', 'v2', + ] + def __init__(self, v0, v1, v2): + self.v0=v0 + self.v1=v1 + self.v2=v2 + + def isInsideXY(self, p): + v0=Vector2(self.v0.x, self.v0.y) + v1=Vector2(self.v1.x, self.v1.y) + v2=Vector2(self.v2.x, self.v2.y) + e01=v1-v0 + e12=v2-v1 + e20=v0-v2 + c0=Vector2.cross(e01, p-v0) + c1=Vector2.cross(e12, p-v1) + c2=Vector2.cross(e20, p-v2) + if c0>=0 and c1>=0 and c2>=0: + return True + if c0<=0 and c1<=0 and c2<=0: + return True - face.smooth = 1 + def isInsideYZ(self, p): + v0=Vector2(self.v0.y, self.v0.z) + v1=Vector2(self.v1.y, self.v1.z) + v2=Vector2(self.v2.y, self.v2.z) + e01=v1-v0 + e12=v2-v1 + e20=v0-v2 + c0=Vector2.cross(e01, p-v0) + c1=Vector2.cross(e12, p-v1) + c2=Vector2.cross(e20, p-v2) + if c0>=0 and c1>=0 and c2>=0: + return True + if c0<=0 and c1<=0 and c2<=0: + return True - # rmeove dummy 0 vertex - mesh.verts.delete(0) - - mesh.mode |= Blender.Mesh.Modes.AUTOSMOOTH - mesh.maxSmoothAngle = int(o.smoothing) - mesh.smooth() - mesh.calcNormals() - mesh.flipNormals() - mesh.update() - - # mirror modifier - if o.mirror: - mod=mesh_object.modifiers.append(Blender.Modifier.Types.MIRROR) - - return objects - - -class MikotoBone(object): - __slots__=[ - 'name', - 'iHead', 'iTail', 'iUp', - 'vHead', 'vTail', 'vUp', - 'parent', 'isFloating', - 'children', - ] - def __init__(self, face=None, vertices=None, materials=None): - self.parent=None - self.isFloating=False - self.children=[] - if not face: - self.name='root' - return + def isInsideZX(self, p): + v0=Vector2(self.v0.z, self.v0.x) + v1=Vector2(self.v1.z, self.v1.x) + v2=Vector2(self.v2.z, self.v2.x) + e01=v1-v0 + e12=v2-v1 + e20=v0-v2 + c0=Vector2.cross(e01, p-v0) + c1=Vector2.cross(e12, p-v1) + c2=Vector2.cross(e20, p-v2) + if c0>=0 and c1>=0 and c2>=0: + return True + if c0<=0 and c1<=0 and c2<=0: + return True - self.name=materials[face.material_index].name.encode('utf-8') - - i0=face.indices[0] - i1=face.indices[1] - i2=face.indices[2] - v0=vertices[i0] - v1=vertices[i1] - v2=vertices[i2] - e01=v1-v0 - e12=v2-v1 - e20=v0-v2 - sqNorm0=e01.getSqNorm() - sqNorm1=e12.getSqNorm() - sqNorm2=e20.getSqNorm() - if sqNorm0>sqNorm1: - if sqNorm1>sqNorm2: - # e01 > e12 > e20 - self.iHead=i2 - self.iTail=i1 - self.iUp=i0 - else: - if sqNorm0>sqNorm2: - # e01 > e20 > e12 - self.iHead=i2 - self.iTail=i0 - self.iUp=i1 - else: - # e20 > e01 > e12 - self.iHead=i1 - self.iTail=i0 - self.iUp=i2 - else: - # 0 < 1 - if sqNorm1 e12 > e01 - self.iHead=i1 - self.iTail=i2 - self.iUp=i0 - else: - if sqNorm0 e20 > e01 - self.iHead=i0 - self.iTail=i2 - self.iUp=i1 - else: - # e12 > e01 > e20 - self.iHead=i0 - self.iTail=i1 - self.iUp=i2 - self.vHead=vertices[self.iHead] - self.vTail=vertices[self.iTail] - self.vUp=vertices[self.iUp] - - if self.name.endswith('[]'): - basename=self.name[0:-2] - # expand LR name - if self.vTail.x>0: - self.name="%s_L" % basename + + class MikotoAnchor(object): + """ + mikoto方式スケルトンのアンカー。 + """ + __slots__=[ + "triangles", "bbox", + ] + def __init__(self): + self.triangles=[] + self.bbox=None + + def push(self, face, vertices): + if face.index_count==3: + self.triangles.append(TrianglePlane( + vertices[face.indices[0]], + vertices[face.indices[1]], + vertices[face.indices[2]] + )) + elif face.index_count==4: + self.triangles.append(TrianglePlane( + vertices[face.indices[0]], + vertices[face.indices[1]], + vertices[face.indices[2]] + )) + self.triangles.append(TrianglePlane( + vertices[face.indices[2]], + vertices[face.indices[3]], + vertices[face.indices[0]] + )) + # bounding box + if not self.bbox: + self.bbox=BoundingBox(vertices[face.indices[0]]) + for i in face.indices: + self.bbox.expand(vertices[i]) + + + def calcWeight(self, v): + if not self.bbox.isInside(v): + return 0 + + if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x): + return 1.0 else: - self.name="%s_R" % basename - - - def setParent(self, parent, floating=False): - if floating: - self.isFloating=True - self.parent=parent - parent.children.append(self) - - def printTree(self, indent=''): - print("%s%s" % (indent, self.name)) - for child in self.children: - child.printTree(indent+' ') - - -def build_armature(armature, mikotoBone, parent=None): - """ - create a armature bone. - """ - bone = Armature.Editbone() - bone.name = mikotoBone.name.encode('utf-8') - armature.bones[bone.name] = bone - - bone.head = Mathutils.Vector(*mikotoBone.vHead.to_a()) - bone.tail = Mathutils.Vector(*mikotoBone.vTail.to_a()) - if parent: - bone.parent=parent - if mikotoBone.isFloating: - pass - else: - bone.options=[Armature.CONNECTED] - - for child in mikotoBone.children: - build_armature(armature, child, bone) - - -def create_armature(scene, mqo): - """ - create armature - """ - boneObject=None - for o in mqo.objects: - if o.name.startswith('bone'): - boneObject=o - break - if not boneObject: - return - - tailMap={} - for f in boneObject.faces: - if f.index_count!=3: - print("invalid index_count: %d" % f.index_count) - continue - b=MikotoBone(f, boneObject.vertices, mqo.materials) - tailMap[b.iTail]=b - - #################### - # build mikoto bone tree - #################### - mikotoRoot=MikotoBone() - - for b in tailMap.values(): - # each bone has unique parent or is root bone. - if b.iHead in tailMap: - b.setParent(tailMap[b.iHead]) - else: - isFloating=False - for e in boneObject.edges: - if b.iHead==e.indices[0]: - # floating bone - if e.indices[1] in tailMap: - b.setParent(tailMap[e.indices[1]], True) - isFloating=True - break - elif b.iHead==e.indices[1]: - # floating bone - if e.indices[0] in tailMap: - b.setParent(tailMap[e.indices[0]], True) - isFloating=True - break - if isFloating: - continue + return 0 + + def anyXY(self, x, y): + for t in self.triangles: + if t.isInsideXY(Vector2(x, y)): + return True + return False + + def anyYZ(self, y, z): + for t in self.triangles: + if t.isInsideYZ(Vector2(y, z)): + return True + return False + + def anyZX(self, z, x): + for t in self.triangles: + if t.isInsideZX(Vector2(z, x)): + return True + return False + + + def create_bone_weight(scene, mqo, armature_object, objects): + """ + create mikoto bone weight. + """ + anchorMap={} + # setup mikoto anchors + for o in mqo.objects: + if o.name.startswith("anchor"): + for f in o.faces: + name=mqo.materials[f.material_index].name + if name.endswith('[]'): + basename=name[0:-2] + v=o.vertices[f.indices[0]] + if(v.x>0): + # L + name_L=basename+'_L' + if not name_L in anchorMap: + anchorMap[name_L]=MikotoAnchor() + anchorMap[name_L].push(f, o.vertices) + elif(v.x<0): + # R + name_R=basename+'_R' + if not name_R in anchorMap: + anchorMap[name_R]=MikotoAnchor() + anchorMap[name_R].push(f, o.vertices) + else: + print("no side", v) + else: + if not name in anchorMap: + anchorMap[name]=MikotoAnchor() + anchorMap[name].push(f, o.vertices) + + for o in objects: + # add armature modifier + mod=o.modifiers.append(Modifier.Types.ARMATURE) + mod[Modifier.Settings.OBJECT] = armature_object + mod[Modifier.Settings.ENVELOPES] = False + o.makeDisplayList() + # create vertex group + mesh=o.getData(mesh=True) + for name in anchorMap.keys(): + mesh.addVertGroup(name) + mesh.update() + + # assing vertices to vertex group + for o in objects: + mesh=o.getData(mesh=True) + for i, mvert in enumerate(mesh.verts): + hasWeight=False + for name, anchor in anchorMap.items(): + weight=anchor.calcWeight(mvert.co) + if weight>0: + mesh.assignVertsToGroup( + name, [i], weight, Mesh.AssignModes.ADD) + hasWeight=True + if not hasWeight: + # debug orphan vertex + print('orphan', mvert) + mesh.update() + - # no parent bone - b.setParent(mikotoRoot, True) + def execute_24(filename): + """ + import a mqo file. + """ + filename=filename.decode(INTERNAL_ENCODING) + print("##start mqo_import.py##") + print(INTERNAL_ENCODING, FS_ENCODING) + print("parse mqo file: %s" % (filename)) - if len(mikotoRoot.children)==0: - print("no root bone") - return + Blender.Window.WaitCursor(1) + t = Blender.sys.time() - if len(mikotoRoot.children)==1: - # single root - mikotoRoot=mikotoRoot.children[0] - mikotoRoot.parent=None - else: - mikotoRoot.vHead=Vector3(0, 10, 0) - mikotoRoot.vTail=Vector3(0, 0, 0) - - #################### - # create armature - #################### - armature = Armature.New() - # link to object - armature_object = scene.objects.new(armature) - # create action - act = Armature.NLA.NewAction() - act.setActive(armature_object) - # set XRAY - armature_object.drawMode |= Object.DrawModes.XRAY - # armature settings - armature.drawType = Armature.OCTAHEDRON - armature.envelopes = False - armature.vertexGroups = True - armature.mirrorEdit = True - armature.drawNames=True - - # edit bones - armature.makeEditable() - build_armature(armature, mikotoRoot) - armature.update() - - return armature_object + # parse file + io=mqo.IO() + if not io.read(filename): + return + + # get active scene + scene = Blender.Scene.GetCurrent() + + # create materials + materials=create_materials(scene, io, os.path.dirname(filename)) + + # create objects + root=scene.objects.new("Empty") + root.setName(os.path.basename(filename)) + objects=create_objects(scene, root, io, materials) + + if has_mikoto(io): + # create mikoto bone + armature_object=create_armature(scene, io) + if armature_object: + root.makeParent([armature_object]) -class TrianglePlane(object): - """ - mikoto方式ボーンのアンカーウェイト計算用。 - (不完全) - """ - __slots__=['normal', - 'v0', 'v1', 'v2', - ] - def __init__(self, v0, v1, v2): - self.v0=v0 - self.v1=v1 - self.v2=v2 - - def isInsideXY(self, p): - v0=Vector2(self.v0.x, self.v0.y) - v1=Vector2(self.v1.x, self.v1.y) - v2=Vector2(self.v2.x, self.v2.y) - e01=v1-v0 - e12=v2-v1 - e20=v0-v2 - c0=Vector2.cross(e01, p-v0) - c1=Vector2.cross(e12, p-v1) - c2=Vector2.cross(e20, p-v2) - if c0>=0 and c1>=0 and c2>=0: - return True - if c0<=0 and c1<=0 and c2<=0: - return True - - def isInsideYZ(self, p): - v0=Vector2(self.v0.y, self.v0.z) - v1=Vector2(self.v1.y, self.v1.z) - v2=Vector2(self.v2.y, self.v2.z) - e01=v1-v0 - e12=v2-v1 - e20=v0-v2 - c0=Vector2.cross(e01, p-v0) - c1=Vector2.cross(e12, p-v1) - c2=Vector2.cross(e20, p-v2) - if c0>=0 and c1>=0 and c2>=0: - return True - if c0<=0 and c1<=0 and c2<=0: - return True - - def isInsideZX(self, p): - v0=Vector2(self.v0.z, self.v0.x) - v1=Vector2(self.v1.z, self.v1.x) - v2=Vector2(self.v2.z, self.v2.x) - e01=v1-v0 - e12=v2-v1 - e20=v0-v2 - c0=Vector2.cross(e01, p-v0) - c1=Vector2.cross(e12, p-v1) - c2=Vector2.cross(e20, p-v2) - if c0>=0 and c1>=0 and c2>=0: - return True - if c0<=0 and c1<=0 and c2<=0: - return True - - -class MikotoAnchor(object): - """ - mikoto方式スケルトンのアンカー。 - """ - __slots__=[ - "triangles", "bbox", - ] - def __init__(self): - self.triangles=[] - self.bbox=None - - def push(self, face, vertices): - if face.index_count==3: - self.triangles.append(TrianglePlane( - vertices[face.indices[0]], - vertices[face.indices[1]], - vertices[face.indices[2]] - )) - elif face.index_count==4: - self.triangles.append(TrianglePlane( - vertices[face.indices[0]], - vertices[face.indices[1]], - vertices[face.indices[2]] - )) - self.triangles.append(TrianglePlane( - vertices[face.indices[2]], - vertices[face.indices[3]], - vertices[face.indices[0]] - )) - # bounding box - if not self.bbox: - self.bbox=BoundingBox(vertices[face.indices[0]]) - for i in face.indices: - self.bbox.expand(vertices[i]) - - - def calcWeight(self, v): - if not self.bbox.isInside(v): - return 0 - - if self.anyXY(v.x, v.y) and self.anyYZ(v.y, v.z) and self.anyZX(v.z, v.x): - return 1.0 + # create bone weight + create_bone_weight(scene, io, armature_object, objects) + + + print('finished in %.2f seconds' % (Blender.sys.time()-t)) + print('') + Blender.Redraw() + Blender.Window.WaitCursor(0) + + +else: + def create_texture(directory, texture_name): + texture=bpy.data.textures.new(texture_name) + texture.type='IMAGE' + texture=texture.recast_type() + #texturePath="%s/%s" % (directory, texture_name) + texturePath=os.path.join(directory, texture_name) + print('create_texture', texturePath) + image=bpy.data.images.load(texturePath) + texture.image=image + texture.mipmap = True + texture.interpolation = True + texture.use_alpha = True + return texture + + def create_materials(mqo, scene, directory): + materials = [] + textureMap={} + imageMap={} + if len(mqo.materials)>0: + for material_index, m in enumerate(mqo.materials): + material = bpy.data.materials.new(m.getName()) + materials.append(material) + # mqo material + material.diffuse_color=[m.color.r, m.color.g, m.color.b] + material.alpha=m.color.a + material.diffuse_intensity=m.diffuse + texture_name=m.getTexture() + if texture_name!='': + if texture_name in textureMap: + texture=textureMap[texture_name] + else: + texture=create_texture(directory, texture_name) + textureMap[texture_name]=texture + imageMap[material_index]=texture.image + #material.add_texture(texture, "UV", {"COLOR", "ALPHA"}) + material.add_texture(texture, "UV", "COLOR") + # temporary + material.emit=1.0 else: - return 0 - - def anyXY(self, x, y): - for t in self.triangles: - if t.isInsideXY(Vector2(x, y)): - return True - return False + material = bpy.data.materials.new('Default') + materials.append(material) + return materials, imageMap - def anyYZ(self, y, z): - for t in self.triangles: - if t.isInsideYZ(Vector2(y, z)): - return True - return False + def create_objects(mqo, scene, parent, materials, imageMap, scale): + for o in mqo.objects: - def anyZX(self, z, x): - for t in self.triangles: - if t.isInsideZX(Vector2(z, x)): - return True - return False + # create mesh + mesh=bpy.data.meshes.new("Mesh") + meshObject= bpy.data.objects.new(o.getName(), mesh) + scene.objects.link(meshObject) + meshObject.parent=parent + # count triangle and quadrangle + faceCount=0 + for f in o.faces: + if f.index_count==3 or f.index_count==4: + faceCount+=1 + mesh.add_geometry(len(o.vertices), 0, faceCount) + + # add vertex + unpackedVertices=[] + for v in o.vertices: + # convert right-handed y-up to right-handed z-up + unpackedVertices.extend( + (scale*v.x, scale*-v.z, scale*v.y)) + mesh.verts.foreach_set("co", unpackedVertices) + + # add face + unpackedFaces = [] + usedMaterial=set() + + def getFace(f): + face = [] + for i in range(f.index_count): + face.append(f.getIndex(i)) + return face -def create_bone_weight(scene, mqo, armature_object, objects): - """ - create mikoto bone weight. - """ - anchorMap={} - # setup mikoto anchors - for o in mqo.objects: - if o.name.startswith("anchor"): for f in o.faces: - name=mqo.materials[f.material_index].name - if name.endswith('[]'): - basename=name[0:-2] - v=o.vertices[f.indices[0]] - if(v.x>0): - # L - name_L=basename+'_L' - if not name_L in anchorMap: - anchorMap[name_L]=MikotoAnchor() - anchorMap[name_L].push(f, o.vertices) - elif(v.x<0): - # R - name_R=basename+'_R' - if not name_R in anchorMap: - anchorMap[name_R]=MikotoAnchor() - anchorMap[name_R].push(f, o.vertices) + face=getFace(f) + if len(face) != 3 and len(face) != 4: + print("{0} vertices in face.".format(len(face))) + continue + + if len(face) == 4: + if face[3] == 0: + # rotate indices if the 4th is 0 + face = [face[3], face[0], face[1], face[2]] + elif len(face) == 3: + if face[2] == 0: + # rotate indices if the 3rd is 0 + face = [face[2], face[0], face[1], 0] else: - print("no side", v) - else: - if not name in anchorMap: - anchorMap[name]=MikotoAnchor() - anchorMap[name].push(f, o.vertices) - - for o in objects: - # add armature modifier - mod=o.modifiers.append(Modifier.Types.ARMATURE) - mod[Modifier.Settings.OBJECT] = armature_object - mod[Modifier.Settings.ENVELOPES] = False - o.makeDisplayList() - # create vertex group - mesh=o.getData(mesh=True) - for name in anchorMap.keys(): - mesh.addVertGroup(name) - mesh.update() - - # assing vertices to vertex group - for o in objects: - mesh=o.getData(mesh=True) - for i, mvert in enumerate(mesh.verts): - hasWeight=False - for name, anchor in anchorMap.items(): - weight=anchor.calcWeight(mvert.co) - if weight>0: - mesh.assignVertsToGroup( - name, [i], weight, Mesh.AssignModes.ADD) - hasWeight=True - if not hasWeight: - # debug orphan vertex - print('orphan', mvert) - mesh.update() - - -def execute_24(filename): - """ - import a mqo file. - """ - filename=filename.decode(INTERNAL_ENCODING) - print "##start mqo_import.py##" - print INTERNAL_ENCODING, FS_ENCODING - print "parse mqo file: %s" % (filename) - - Blender.Window.WaitCursor(1) - t = Blender.sys.time() - - # parse file - io=mqo.IO() - - if not io.read(filename): - return - - # get active scene - scene = Blender.Scene.GetCurrent() - - # create materials - materials=create_materials(scene, io, os.path.dirname(filename)) - - # create objects - root=scene.objects.new("Empty") - root.setName(os.path.basename(filename)) - objects=create_objects(scene, root, io, materials) + face.append(0) - if has_mikoto(io): - # create mikoto bone - armature_object=create_armature(scene, io) - if armature_object: - root.makeParent([armature_object]) + unpackedFaces.extend(face) + usedMaterial.add(f.material_index) + try: + mesh.faces.foreach_set("verts_raw", unpackedFaces) + except: + #print([getFace(f) for f in o.faces]) + print("fail to mesh.faces.foreach_set") + return + + # add material + meshMaterialMap={} + materialIndex=0 + for i in usedMaterial: + mesh.add_material(materials[i]) + meshMaterialMap[i]=materialIndex + materialIndex+=1 + + # each face + mesh.add_uv_texture() + for mqo_face, blender_face, uv_face in zip( + o.faces, mesh.faces, mesh.uv_textures[0].data): + if mqo_face.index_count<3: + continue + blender_face.material_index=meshMaterialMap[mqo_face.material_index] + if mqo_face.index_count>=3: + uv_face.uv1=[mqo_face.getUV(0).x, 1.0-mqo_face.getUV(0).y] + uv_face.uv2=[mqo_face.getUV(1).x, 1.0-mqo_face.getUV(1).y] + uv_face.uv3=[mqo_face.getUV(2).x, 1.0-mqo_face.getUV(2).y] + if mqo_face.index_count==4: + uv_face.uv4=[ + mqo_face.getUV(3).x, 1.0-mqo_face.getUV(3).y] + if materials[mqo_face.material_index] in imageMap: + uv_face.image=imageMap[mqo_face.material_index] + uv_face.tex=True + + mesh.update() + + def load(filename, context, scale): + """ + load mqo file to context. + """ + io=mqo.IO() + if not io.read(filename): + print("fail to load",filename) + return - # create bone weight - create_bone_weight(scene, io, armature_object, objects) + scene=context.scene + # create material + materials, imageMap=create_materials( + io, scene, os.path.dirname(filename)) - print('finished in %.2f seconds' % (Blender.sys.time()-t)) - print('') - Blender.Redraw() - Blender.Window.WaitCursor(0) + # create group + empty=bpy.data.objects.new(os.path.basename(filename), None) + scene.objects.link(empty) + # create mesh + create_objects(io, scene, empty, materials, imageMap, scale) + +############################################################################### +# register +############################################################################### if isBlender24(): # for 2.4 + # execute Blender.Window.FileSelector(execute_24, 'Import MQO', '*.mqo') else: # for 2.5 - pass + # import operator + class IMPORT_OT_mqo(bpy.types.Operator): + '''Import from Metasequoia file format (.mqo)''' + bl_idname = "import_scene.mqo" + bl_label = 'Import MQO' + + # List of operator properties, the attributes will be assigned + # to the class instance from the operator settings before calling. + + path = StringProperty( + name="File Path", + description="File path used for importing the MQO file", + maxlen= 1024, default= "") + filename = StringProperty( + name="File Name", + description="Name of the file.") + directory = StringProperty( + name="Directory", + description="Directory of the file.") + + scale = FloatProperty( + name="Scale", + description="Scale the MQO by this value", + min=0.0001, max=1000000.0, + soft_min=0.001, soft_max=100.0, default=1.0) + + def execute(self, context): + load(self.properties.path, context, self.properties.scale) + return 'FINISHED' + + def invoke(self, context, event): + wm=context.manager + wm.add_fileselect(self) + return 'RUNNING_MODAL' + + + # register menu + def menu_func(self, context): + self.layout.operator(IMPORT_OT_mqo.bl_idname, + text="Metasequoia (.mqo)") + + def register(): + bpy.types.register(IMPORT_OT_mqo) + bpy.types.INFO_MT_file_import.append(menu_func) + + def unregister(): + bpy.types.unregister(IMPORT_OT_mqo) + bpy.types.INFO_MT_file_import.remove(menu_func) + + if __name__=="__main__": + register() -- 2.11.0