X-Git-Url: http://git.osdn.jp/view?a=blobdiff_plain;f=swig%2Fblender%2Fbl24.py;h=a1a5133fbd18bf562e918f1ee8b50e887e498102;hb=e3663cdfa9eba5b3809390cbc46516f50cb244bc;hp=a9ee110f95528810461708de10297e36184e3b4c;hpb=0afdac244629258bebda6e65a25ba3e3879f4003;p=meshio%2Fmeshio.git diff --git a/swig/blender/bl24.py b/swig/blender/bl24.py index a9ee110..a1a5133 100644 --- a/swig/blender/bl24.py +++ b/swig/blender/bl24.py @@ -2,21 +2,461 @@ import sys import os import Blender +from Blender import Mathutils +import bpy -# $B%U%!%$%k%7%9%F%`$NJ8;z%3!<%I(B -# $B2~B$HG$H$N6&MQ$N$?$a(B + +# ファイルシステムの文字コード +# 改造版との共用のため FS_ENCODING=sys.getfilesystemencoding() if os.path.exists(os.path.dirname(sys.argv[0])+"/utf8"): INTERNAL_ENCODING='utf-8' else: INTERNAL_ENCODING=FS_ENCODING + +############################################################################### +# Writer +############################################################################### +class Writer(object): + def __init__(self, path, encoding): + self.io=open(path, "wb") + self.encoding=encoding + + def write(self, s): + self.io.write(s) + + def flush(self): + self.io.flush() + + def close(self): + self.io.close() + + +############################################################################### +# ProgressBar +############################################################################### +class ProgressBar(object): + def __init__(self, base): + print("#### %s ####" % base) + self.base=base + self.start=Blender.sys.time() + self.set('', 0) + + def advance(self, message, progress): + self.progress+=float(progress) + self._print(message) + + def set(self, message, progress): + self.progress=float(progress) + self._print(message) + + def _print(self, message): + print(message) + message="%s: %s" % (self.base, message) + if message.__class__ is unicode: + message=message.encode(FS_ENCODING) + Blender.Window.DrawProgressBar(self.progress, message) + + def finish(self): + self.progress=1.0 + message='finished in %.2f sec' % (Blender.sys.time()-self.start) + self.set(message, 1.0) + + +############################################################################### +# for mqo mikoto bone. +############################################################################### +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: + # 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: + 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 + + # no parent bone + b.setParent(mikotoRoot, True) + + if len(mikotoRoot.children)==0: + print("no root bone") + return + + 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 + + +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 + else: + 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() + +############################################################################### def createEmptyObject(scene, name): empty=scene.objects.new("Empty") empty.setName(name) return empty -def createMaterial(m): + +def createMqoMaterial(m): material = Blender.Material.New(m.getName().encode(INTERNAL_ENCODING)) material.mode |= Blender.Material.Modes.SHADELESS material.rgbCol = [m.color.r, m.color.g, m.color.b] @@ -26,6 +466,23 @@ def createMaterial(m): material.hard = int(255 * m.power) return material +def createPmdMaterial(m): + material=Blender.Material.New() + material.setDiffuseShader(Blender.Material.Shaders.DIFFUSE_TOON) + material.setRef(1) + material.diffuseSize = 3.14/2 + material.setDiffuseSmooth(0) + material.setSpecShader(Blender.Material.Shaders.SPEC_TOON) + material.setSpecSize(0) + material.setSpec(0) + material.setRGBCol([m.diffuse.r, m.diffuse.g, m.diffuse.b]) + material.setAlpha(m.diffuse.a) + material.setSpec(m.shinness*0.1) + material.setSpecCol([m.specular.r, m.specular.g, m.specular.b]) + material.setMirCol([m.ambient.r, m.ambient.g, m.ambient.b]) + material.enableSSS=True if m.flag==1 else False + return material + def createTexture(path): image = Blender.Image.Load(path.encode(INTERNAL_ENCODING)) texture = Blender.Texture.New(path.encode(INTERNAL_ENCODING)) @@ -37,3 +494,344 @@ def materialAddTexture(material, texture): material.mode = material.mode | Blender.Material.Modes.TEXFACE material.setTexture(0, texture, Blender.Texture.TexCo.UV) + +def createMesh(scene, name): + mesh = Blender.Mesh.New() + mesh_object=scene.objects.new(mesh, name.encode(INTERNAL_ENCODING)) + return mesh, mesh_object + + +def objectMakeParent(parent, child): + parent.makeParent([child]) + + +def meshAddMaterial(mesh, material): + mesh.materials+=[material] + + +def meshAddMqoGeometry(mesh, o, materials, imageMap, scale): + # add vertices + mesh.verts.extend(Mathutils.Vector(0, 0, 0)) # dummy + mesh.verts.extend([(v.x, -v.z, v.y) for v in o.vertices]) + # add faces + mesh_faces=[] + for face in o.faces: + face_indices=[] + for i in xrange(face.index_count): + face_indices.append(face.getIndex(i)+1) + mesh_faces.append(face_indices) + #new_faces=mesh.faces.extend([face.indices for face in o.faces], + new_faces=mesh.faces.extend(mesh_faces, + #ignoreDups=True, + indexList=True) + mesh.update() + + # gather used materials + materialMap = {} + if new_faces: + for i in new_faces: + if type(i) is int: + materialMap[o.faces[i].material_index]=True + + # blender limits 16 materials per mesh + # separate mesh ? + for i, material_index in enumerate(materialMap.keys()): + if i>=16: + print("over 16 materials!") + break + mesh.materials+=[materials[material_index]] + materialMap[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 materialMap: + face.mat = materialMap[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) + +def getTexture(m, dirname): + tex="" + aplane="" + # texture + for texture in m.getTextures(): + if texture and texture.tex and texture.tex.getImage(): + image=texture.tex.getImage() + if not image: + continue + imagePath=Blender.sys.expandpath(image.getFilename()) + if len(dirname)>0 and imagePath.startswith(dirname): + # 相対パスに変換する + imagePath=imagePath[len(dirname)+1:len(imagePath)] + if texture.mtCol>0: + tex=" tex(\"%s\")" % imagePath + elif texture.mtAlpha>0: + aplane=" aplane(\"%s\")" % imagePath + return tex, aplane + +def objectDuplicate(scene, obj): + mesh, dumy=createMesh(scene, obj.name.decode(INTERNAL_ENCODING)) + # not apply modifiers + mesh.getFromObject(obj.name, 1) + # apply matrix + dumy.setMatrix(obj.matrixWorld) + return mesh, dumy + +def objectDelete(scene, obj): + scene.objects.unlink(obj) + +def faceVertexCount(face): + return len(face.v) + +def faceVertices(face): + # flip + return [v.index for v in reversed(face.v)] + +def meshHasUV(mesh): + return mesh.faceUV + +def faceHasUV(mesh, i, face): + return len(face.uv)>0 + +def faceGetUV(mesh, i, face, count): + # flip + return reversed(face.uv) + +def materialToMqo(m): + return "\"%s\" shader(3) col(%f %f %f %f)" % ( + m.name, m.rgbCol[0], m.rgbCol[1], m.rgbCol[2], m.alpha) + +def faceMaterialIndex(face): + return face.mat + +def objectGetData(o): + return o.getData(mesh=True) + +def objectAddArmatureModifier(o, armature_object): + mod=o.modifiers.append(Blender.Modifier.Types.ARMATURE) + mod[Blender.Modifier.Settings.OBJECT] = armature_object + mod[Blender.Modifier.Settings.ENVELOPES] = False + +def objectSelect(o): + o.select(True) + +def objectGetPose(o): + return o.getPose() + +def poseBoneLimit(n, b): + if n.endswith("_t"): + return + if n.startswith("knee_"): + b.lockYRot=True + b.lockZRot=True + b.limitX=True + b.limitMin=[0, 0, 0] + b.limitMax=[180, 0, 0] + elif n.startswith("ankle_"): + b.lockYRot=True + +def enterEditMode(): + Blender.Window.EditMode(1) + +def exitEditMode(): + Blender.Window.EditMode(0) + +def objectDeselectAll(): + for o in bpy.data.scenes.active.objects: + o.select(False) + +def objectActivate(scene, o): + o.select(True ) + scene.objects.active=o + +def meshAddVertexGroup(meshObject, name): + meshObject.getData(mesh=True).addVertGroup(name) + +def meshUseVertexUv(mesh): + mesh.vertexUV = 1 + +def vertexSetNormal(mvert, normal): + mvert.no=Mathutils.Vector(*normal) + +def vertexSetUv(mvert, uv): + mvert.uvco=uv + +def meshAssignVertexGroup(meshObject, name, index, weight): + meshObject.getData(mesh=True).assignVertsToGroup(name, + [index], weight, Blender.Mesh.AssignModes.ADD) + +def meshCreateVerteicesAndFaces(mesh, vertices, faces): + mesh.verts.extend(vertices) + mesh.faces.extend(faces, ignoreDups=True) + +def meshAddUV(mesh): + mesh.addUVLayer('NewUV') + +def meshVertsDelete(mesh, remove_vertices): + mesh.verts.delete(remove_vertices) + +def createArmature(scene): + armature = Blender.Armature.New() + armature_object = scene.objects.new(armature) + + # set XRAY + armature_object.drawMode = ( + armature_object.drawMode | Blender.Object.DrawModes.XRAY) + # armature settings + armature.drawType = Blender.Armature.OCTAHEDRON + armature.drawNames=True + armature.envelopes = False + armature.vertexGroups = True + armature.mirrorEdit = True + + return armature, armature_object + +def armatureMakeEditable(scene, armature_object): + # create armature + armature_object.getData().makeEditable() + +def createIkConstraint(armature_object, p_bone, effector_name, ik): + cSetting = Blender.Constraint.Settings + # IK solver + constraint = p_bone.constraints.append(Blender.Constraint.Type.IKSOLVER) + constraint[cSetting.CHAINLEN]=len(ik.children) + constraint[cSetting.TARGET]=armature_object + constraint[cSetting.USETIP]=False + constraint[cSetting.BONE]=effector_name + # not used. place folder when export. + constraint[cSetting.ROTWEIGHT]=ik.weight + constraint[cSetting.ITERATIONS]=ik.iterations * 10 + return constraint + +def createArmatureBone(armature, name): + bone=Blender.Armature.Editbone() + bone.name=name.encode(INTERNAL_ENCODING) + armature.bones[name]=bone + return bone + +def boneSetConnected(bone): + bone.options+=[Blender.Armature.CONNECTED] + +def createVector(x, y, z): + return Mathutils.Vector(x, y, z) + +def armatureUpdate(armature): + armature.update() + +def boneLayerMask(bone, layers): + mask=0 + for i, enable in enumerate(layers): + if enable!=0: + mask+=(1<