def setupProxySkinning(self): # Remove all meshes but the human and skeleton mesh from the animatedMesh object for mName in self.human.animated.getMeshes()[2:]: self.human.animated.removeMesh(mName) # TODO it's more optimized not to remove all proxy object meshes every time _, bodyWeights = self.human.animated.getMesh("base.obj") # Proxy mesh (always animate) if self.human.proxy and self.human.isProxied(): weights = skeleton.getProxyWeights(self.human.proxy, bodyWeights, self.human.getProxyMesh()) self.human.animated.addMesh(self.human.getProxyMesh(), weights) # Generate a vertex-to-bone mapping derived from that of the human for all proxy objects if self.skinProxiesTggl.selected: # Clothes for (name,obj) in self.human.clothesObjs.items(): proxy = self.human.clothesProxies[name] weights = skeleton.getProxyWeights(proxy, bodyWeights, obj.mesh) self.human.animated.addMesh(obj.mesh, weights) obj.show() # Hair if self.human.hairObj and self.human.hairProxy: weights = skeleton.getProxyWeights(self.human.hairProxy, bodyWeights, self.human.hairObj.mesh) self.human.animated.addMesh(self.human.hairObj.mesh, weights) self.human.hairObj.show() else: # Hide not animated proxies (clothes and hair) for (name,obj) in self.human.clothesObjs.items(): obj.hide() if self.human.hairObj: self.human.hairObj.hide()
def setupProxySkinning(self): # Remove all meshes but the human and skeleton mesh from the animatedMesh object for mName in self.human.animated.getMeshes()[2:]: self.human.animated.removeMesh(mName) # TODO it's more optimized not to remove all proxy object meshes every time _, bodyWeights = self.human.animated.getMesh("base.obj") # Proxy mesh (always animate) if self.human.proxy and self.human.isProxied(): weights = skeleton.getProxyWeights(self.human.proxy, bodyWeights, self.human.getProxyMesh()) self.human.animated.addMesh(self.human.getProxyMesh(), weights) # Generate a vertex-to-bone mapping derived from that of the human for all proxy objects if self.skinProxiesTggl.selected: # Clothes for (name, obj) in self.human.clothesObjs.items(): proxy = self.human.clothesProxies[name] weights = skeleton.getProxyWeights(proxy, bodyWeights, obj.mesh) self.human.animated.addMesh(obj.mesh, weights) obj.show() # Hair if self.human.hairObj and self.human.hairProxy: weights = skeleton.getProxyWeights(self.human.hairProxy, bodyWeights, self.human.hairObj.mesh) self.human.animated.addMesh(self.human.hairObj.mesh, weights) self.human.hairObj.show() else: # Hide not animated proxies (clothes and hair) for (name, obj) in self.human.clothesObjs.items(): obj.hide() if self.human.hairObj: self.human.hairObj.hide()
def setupProxySkinning(self): # Remove all meshes but the human and skeleton mesh from the animatedMesh object for mName in self.human.animated.getMeshes()[2:]: self.human.animated.removeMesh(mName) # TODO it's more optimized not to remove all proxy object meshes every time _, bodyWeights = self.human.animated.getMesh("base.obj") # Proxy mesh (always animate) if self.human.proxy and self.human.isProxied(): weights = skeleton.getProxyWeights(self.human.proxy, bodyWeights, self.human.getProxyMesh()) self.human.animated.addMesh(self.human.getProxyMesh(), weights) # Generate a vertex-to-bone mapping derived from that of the human for all proxy objects if self.skinProxiesTggl.selected: for proxy, obj in self.human.getProxiesAndObjects(): weights = skeleton.getProxyWeights(proxy, bodyWeights, obj.mesh) self.human.animated.addMesh(obj.mesh, weights) obj.show() else: for obj in self.human.getProxyObjects(): obj.hide()
def exportMd5(filepath, config): """ This function exports MakeHuman mesh and skeleton data to id Software's MD5 format. Parameters ---------- human: *Human*. The object whose information is to be used for the export. filepath: *string*. The filepath of the file to export the object to. config: *Config*. Export configuration. """ progress = Progress.begin(logging=True, timing=True) human = config.human obj = human.meshData config.zUp = True config.feetOnGround = True # TODO this only works when exporting MHX mesh (a design error in exportutils) config.scale = 1 config.setupTexFolder(filepath) filename = os.path.basename(filepath) name = config.goodName(os.path.splitext(filename)[0]) humanBBox = human.meshData.calcBBox() progress(0, 0.2, "Collecting Objects") rmeshes = exportutils.collect.setupMeshes( name, human, config=config, subdivide=config.subdivide) if human.getSkeleton(): numJoints = human.getSkeleton().getBoneCount() +1 # Amount of joints + the hardcoded origin below else: numJoints = 1 f = codecs.open(filepath, 'w', encoding="utf-8") f.write('MD5Version 10\n') f.write('commandline ""\n\n') f.write('numJoints %d\n' % numJoints) f.write('numMeshes %d\n\n' % (len(rmeshes))) f.write('joints {\n') # Hardcoded root joint f.write('\t"%s" %d ( %f %f %f ) ( %f %f %f )\n' % ('origin', -1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)) progress(0.2, 0.3, "Writing Bones") if human.getSkeleton(): bones = human.getSkeleton().getBones() boneprog = Progress(len(bones)) for bone in bones: writeBone(f, bone, human, config) boneprog.step() f.write('}\n\n') progress(0.3, 0.8, "Writing Objects") loopprog = Progress(len(rmeshes)) for rmeshIdx, rmesh in enumerate(rmeshes): # rmesh.type: None is human, "Proxymeshes" is human proxy, "Clothes" for clothing and "Hair" for hair objprog = Progress() objprog(0.0, 0.1, "Writing %s mesh." % rmesh.name) obj = rmesh.object obj.calcFaceNormals() obj.calcVertexNormals() obj.updateIndexBuffer() numVerts = len(obj.r_coord) if obj.vertsPerPrimitive == 4: # Quads numFaces = len(obj.r_faces) * 2 else: # Tris numFaces = len(obj.r_faces) f.write('mesh {\n') mat = rmesh.material if mat.diffuseTexture: tex = copyTexture(mat.diffuseTexture, human, config) f.write('\tshader "%s"\n' % tex) f.write('\n\tnumverts %d\n' % numVerts) # Collect vertex weights if human.getSkeleton(): objprog(0.1, 0.2, "Writing skeleton") bodyWeights = human.getVertexWeights() if rmesh.type: # Determine vertex weights for proxy weights = skeleton.getProxyWeights(rmesh.proxy, bodyWeights, obj) else: # Use vertex weights for human body weights = bodyWeights # Account for vertices that are filtered out if rmesh.vertexMapping != None: filteredVIdxMap = rmesh.vertexMapping weights2 = {} for (boneName, (verts,ws)) in weights.items(): verts2 = [] ws2 = [] for i, vIdx in enumerate(verts): if vIdx in filteredVIdxMap: verts2.append(filteredVIdxMap[vIdx]) ws2.append(ws[i]) weights2[boneName] = (verts2, ws2) weights = weights2 # Remap vertex weights to the unwelded vertices of the object (obj.coord to obj.r_coord) originalToUnweldedMap = obj.inverse_vmap # Build a weights list indexed per vertex jointIndexes = {} jointIndexes['origin'] = 0 joints = [None] + human.getSkeleton().getBones() # origin joint is None for idx,bone in enumerate(joints): if bone: jointIndexes[bone.name] = idx vertWeights = {} # = dict( vertIdx: [ (jointIdx1, weight1), ...]) for (jointName, (verts,ws)) in weights.items(): jointIdx = jointIndexes[jointName] for idx,v in enumerate(verts): try: for r_vIdx in originalToUnweldedMap[v]: if r_vIdx not in vertWeights: vertWeights[r_vIdx] = [] vertWeights[r_vIdx].append((jointIdx, ws[idx])) except: # unused coord pass for vert in xrange(numVerts): if vert not in vertWeights: # Weight vertex completely to origin joint vertWeights[vert] = [(0, 1.0)] else: vertWeights = None objprog(0.3, 0.7, "Writing vertices for %s." % rmesh.name) # Write vertices wCount = 0 for vert in xrange(numVerts): if obj.has_uv: u, v = obj.r_texco[vert] else: u, v = 0, 0 if vertWeights == None: numWeights = 1 else: numWeights = len(vertWeights[vert]) # vert [vertIndex] ( [texU] [texV] ) [weightIndex] [weightElem] f.write('\tvert %d ( %f %f ) %d %d\n' % (vert, u, 1.0-v, wCount, numWeights)) wCount = wCount + numWeights objprog(0.7, 0.8, "Writing faces for %s." % rmesh.name) # Write faces f.write('\n\tnumtris %d\n' % numFaces) fn = 0 for fv in obj.r_faces: # tri [triIndex] [vertIndex1] [vertIndex2] [vertIndex3] f.write('\ttri %d %d %d %d\n' % (fn, fv[2], fv[1], fv[0])) fn += 1 if fv[0] != fv[3]: f.write('\ttri %d %d %d %d\n' % (fn, fv[0], fv[3], fv[2])) fn += 1 objprog(0.8, 0.99, "Writing bone weights for %s." % rmesh.name) # Write bone weighting bwprog = Progress(len(obj.r_coord)).HighFrequency(200) if human.getSkeleton(): f.write('\n\tnumweights %d\n' % wCount) wCount = 0 for idx,co in enumerate(obj.r_coord): for (jointIdx, jointWght) in vertWeights[idx]: # Get vertex position in bone space if joints[jointIdx]: invbonematrix = joints[jointIdx].matRestGlobal.copy() invbonematrix[:3,3] = [0,0,0] invbonematrix = la.inv(invbonematrix) relPos = np.ones(4, dtype=np.float32) relPos[:3] = co[:3] relPos[:3] -= joints[jointIdx].getRestHeadPos() relPos[:3] *= scale #relPos = np.dot(relPos, invbonematrix) else: relPos = co[:3] * scale if config.zUp: relPos[:3] = relPos[[0,2,1]] * [1,-1,1] # weight [weightIndex] [jointIndex] [weightValue] ( [xPos] [yPos] [zPos] ) f.write('\tweight %d %d %f ( %f %f %f )\n' % (wCount, jointIdx, jointWght, relPos[0], relPos[1], relPos[2])) wCount = wCount +1 bwprog.step() else: # No skeleton selected: Attach all vertices to the root with weight 1.0 f.write('\n\tnumweights %d\n' % (numVerts)) for idx,co in enumerate(obj.r_coord): # weight [weightIndex] [jointIndex] [weightValue] ( [xPos] [yPos] [zPos] ) co = co.copy() * scale if config.feetOnGround: co[1] += (getFeetOnGroundOffset(human) * scale) if config.zUp: co = co[[0,2,1]] * [1,-1,1] f.write('\tweight %d %d %f ( %f %f %f )\n' % (idx, 0, 1.0, co[0], co[1], co[2])) # Note: MD5 has a z-up coordinate system bwprog.step() f.write('}\n\n') loopprog.step() f.close() progress(0.8, 0.99, "Writing Animations") if human.getSkeleton() and hasattr(human, 'animations'): animprog = Progress(len(human.animations)) for anim in human.animations: writeAnimation(filepath, human, humanBBox, config, anim.getAnimationTrack()) animprog.step() progress(1, None, "MD5 export finished. Exported file: %s", filepath)
def addGeometry(mhGeos, mesh, skel, rawWeights, mats, mname, cfg): mhGeo = OrderedDict() mhGeos.append(mhGeo) obj = mesh.object pxy = obj.proxy if pxy: if pxy.type == 'Proxymeshes': mhGeo["license"] = BaseMeshLicense else: addProxyLicense(mhGeo, pxy) else: mhGeo["license"] = BaseMeshLicense mhName = mhGeo["name"] = mname mhGeo["uuid"] = str(uuid4()) mhGeo["offset"] = cfg.offset mhGeo["scale"] = cfg.scale mhGeo["issubdivided"] = obj.isSubdivided() try: mhGeo["material"] = mats[mesh.name] except KeyError: pass mhMesh = mhGeo["mesh"] = OrderedDict() if pxy and pxy.type == 'Proxymeshes': addMesh(mhMesh, mesh.clone()) else: addMesh(mhMesh, mesh) mhSeed = mhGeo["seed_mesh"] = OrderedDict() obj = mesh.object addMesh(mhSeed, obj.getSeedMesh()) if pxy: if pxy.type == 'Proxymeshes': mhGeo["human"] = True mhProxySeed = mhGeo["proxy_seed_mesh"] = OrderedDict() addMesh(mhProxySeed, obj.getProxyMesh()) else: mhGeo["human"] = False mhProxySeed = None if skel: if hasattr(mesh, "getVertexWeights"): pxySeedWeights = pxy.getVertexWeights(rawWeights, skel) weights = mesh.getVertexWeights(pxySeedWeights) else: pxySeedWeights = skeleton.getProxyWeights(pxy, rawWeights) weights = mesh.getWeights(pxySeedWeights) addWeights(mhMesh, skel, weights) if mhProxySeed: addWeights(mhSeed, skel, rawWeights) addWeights(mhProxySeed, skel, pxySeedWeights) else: addWeights(mhSeed, skel, pxySeedWeights) mhProxy = mhGeo["proxy"] = OrderedDict() addProxyLicense(mhProxy, pxy) mhProxy["name"] = pxy.name.capitalize() mhProxy["type"] = pxy.type mhProxy["uuid"] = pxy.uuid mhProxy["basemesh"] = pxy.basemesh mhProxy["tags"] = list(pxy.tags) mhProxy["fitting"] = np.array([(vnums, pxy.weights[n], pxy.offsets[n]) for n, vnums in enumerate(pxy.ref_vIdxs) ]) #mhProxy["ref_wvIdxs"] = pxy.ref_wvIdxs mhProxy["delete_verts"] = pxy.deleteVerts if hasattr(pxy, "vertexBoneWeights") and pxy.vertexBoneWeights: mhProxy["vertex_bone_weights"] = pxy.vertexBoneWeights.data else: mhProxy["vertex_bone_weights"] = None else: mhGeo["human"] = True if skel: addWeights(mhSeed, skel, rawWeights) if hasattr(mesh, "getVertexWeights"): weights = mesh.getVertexWeights(rawWeights) else: weights = mesh.getWeights(rawWeights) addWeights(mhMesh, skel, weights)
def writeMeshFile(human, filepath, rmeshes, config, progressCallback=None): progress = Progress(len(rmeshes)) filename = os.path.basename(filepath) name = formatName(config.goodName(os.path.splitext(filename)[0])) f = codecs.open(filepath, 'w', encoding="utf-8") f.write('<?xml version="1.0" encoding="UTF-8"?>\n') f.write('<!-- Exported from MakeHuman (www.makehuman.org) -->\n') f.write('<mesh>\n') f.write(' <submeshes>\n') for rmeshIdx, rmesh in enumerate(rmeshes): loopprog = Progress()(0.0, 0.1, "Writing %s mesh." % rmesh.name) obj = rmesh.object # Make sure vertex normals are calculated obj.calcFaceNormals() obj.calcVertexNormals() # Calculate rendering data so we can use the unwelded vertices obj.updateIndexBuffer() numVerts = len(obj.r_coord) if obj.vertsPerPrimitive == 4: # Quads numFaces = len(obj.r_faces) * 2 else: # Tris numFaces = len(obj.r_faces) loopprog(0.1, 0.3, "Writing faces of %s." % rmesh.name) f.write( ' <submesh material="%s_%s_%s" usesharedvertices="false" use32bitindexes="false" operationtype="triangle_list">\n' % (formatName(name), rmeshIdx, formatName(rmesh.name) if formatName(rmesh.name) != name else "human")) # Faces f.write(' <faces count="%s">\n' % numFaces) for fv in obj.r_faces: f.write(' <face v1="%s" v2="%s" v3="%s" />\n' % (fv[0], fv[1], fv[2])) if obj.vertsPerPrimitive == 4: f.write(' <face v1="%s" v2="%s" v3="%s" />\n' % (fv[2], fv[3], fv[0])) f.write(' </faces>\n') loopprog(0.3, 0.7, "Writing vertices of %s." % rmesh.name) # Vertices f.write(' <geometry vertexcount="%s">\n' % numVerts) f.write( ' <vertexbuffer positions="true" normals="true">\n') #f.write(' <vertexbuffer positions="true">\n') for vIdx, co in enumerate(obj.r_coord): if config.feetOnGround: co = co.copy() co[1] += getFeetOnGroundOffset(human) # Note: Ogre3d uses a y-up coordinate system (just like MH) norm = obj.r_vnorm[vIdx] f.write(' <vertex>\n') f.write( ' <position x="%s" y="%s" z="%s" />\n' % (co[0], co[1], co[2])) f.write( ' <normal x="%s" y="%s" z="%s" />\n' % (norm[0], norm[1], norm[2])) f.write(' </vertex>\n') f.write(' </vertexbuffer>\n') loopprog(0.8 - 0.1 * bool(human.getSkeleton()), 0.9, "Writing UVs of %s." % rmesh.name) # UV Texture Coordinates f.write( ' <vertexbuffer texture_coord_dimensions_0="2" texture_coords="1">\n' ) for vIdx in xrange(numVerts): if obj.has_uv: u, v = obj.r_texco[vIdx] v = 1 - v else: u, v = 0, 0 f.write(' <vertex>\n') f.write(' <texcoord u="%s" v="%s" />\n' % (u, v)) f.write(' </vertex>\n') f.write(' </vertexbuffer>\n') f.write(' </geometry>\n') if human.getSkeleton(): loopprog(0.9, 0.99, "Writing bone assignments of %s." % rmesh.name) else: loopprog(0.99, None, "Written %s." % rmesh.name) # Skeleton bone assignments if human.getSkeleton(): bodyWeights = human.getVertexWeights() if rmesh.type: # Determine vertex weights for proxy weights = skeleton.getProxyWeights(rmesh.proxy, bodyWeights, obj) else: # Use vertex weights for human body weights = bodyWeights # Account for vertices that are filtered out if rmesh.vertexMapping != None: filteredVIdxMap = rmesh.vertexMapping weights2 = {} for (boneName, (verts, ws)) in weights.items(): verts2 = [] ws2 = [] for i, vIdx in enumerate(verts): if vIdx in filteredVIdxMap: verts2.append(filteredVIdxMap[vIdx]) ws2.append(ws[i]) weights2[boneName] = (verts2, ws2) weights = weights2 # Remap vertex weights to the unwelded vertices of the object (obj.coord to obj.r_coord) originalToUnweldedMap = {} for unweldedIdx, originalIdx in enumerate(obj.vmap): if originalIdx not in originalToUnweldedMap.keys(): originalToUnweldedMap[originalIdx] = [] originalToUnweldedMap[originalIdx].append(unweldedIdx) f.write(' <boneassignments>\n') boneNames = [bone.name for bone in human.getSkeleton().getBones()] for (boneName, (verts, ws)) in weights.items(): bIdx = boneNames.index(boneName) for i, vIdx in enumerate(verts): w = ws[i] try: for r_vIdx in originalToUnweldedMap[vIdx]: f.write( ' <vertexboneassignment vertexindex="%s" boneindex="%s" weight="%s" />\n' % (r_vIdx, bIdx, w)) except: # unused coord pass f.write(' </boneassignments>\n') progress.step() f.write(' </submesh>\n') f.write(' </submeshes>\n') f.write(' <submeshnames>\n') for rmeshIdx, rmesh in enumerate(rmeshes): f.write(' <submeshname name="%s" index="%s" />\n' % (formatName(rmesh.name) if formatName(rmesh.name) != name else "human", rmeshIdx)) f.write(' </submeshnames>\n') if human.getSkeleton(): f.write(' <skeletonlink name="%s.skeleton" />\n' % name) f.write('</mesh>') f.close()
def writeMeshFile(human, filepath, stuffs, config): filename = os.path.basename(filepath) name = formatName(config.goodName(os.path.splitext(filename)[0])) f = open(filepath, 'w') f.write('<?xml version="1.0" encoding="UTF-8"?>\n') f.write('<!-- Exported from MakeHuman (www.makehuman.org) -->\n') f.write('<mesh>\n') f.write(' <submeshes>\n') for stuffIdx, stuff in enumerate(stuffs): obj = stuff.meshInfo.object # Make sure vertex normals are calculated obj.calcFaceNormals() obj.calcVertexNormals() # Calculate rendering data so we can use the unwelded vertices obj.updateIndexBuffer() numVerts = len(obj.r_coord) if obj.vertsPerPrimitive == 4: # Quads numFaces = len(obj.r_faces) * 2 else: # Tris numFaces = len(obj.r_faces) f.write(' <submesh material="%s_%s_%s" usesharedvertices="false" use32bitindexes="false" operationtype="triangle_list">\n' % (formatName(name), stuffIdx, formatName(stuff.name) if formatName(stuff.name) != name else "human")) # Faces f.write(' <faces count="%s">\n' % numFaces) for fv in obj.r_faces: f.write(' <face v1="%s" v2="%s" v3="%s" />\n' % (fv[0], fv[1], fv[2])) if obj.vertsPerPrimitive == 4: f.write(' <face v1="%s" v2="%s" v3="%s" />\n' % (fv[2], fv[3], fv[0])) f.write(' </faces>\n') # Vertices f.write(' <geometry vertexcount="%s">\n' % numVerts) f.write(' <vertexbuffer positions="true" normals="true">\n') #f.write(' <vertexbuffer positions="true">\n') for vIdx, co in enumerate(obj.r_coord): if feetOnGround: co = co.copy() co[1] += getFeetOnGroundOffset(human) # Note: Ogre3d uses a y-up coordinate system (just like MH) norm = obj.r_vnorm[vIdx] f.write(' <vertex>\n') f.write(' <position x="%s" y="%s" z="%s" />\n' % (co[0], co[1], co[2])) f.write(' <normal x="%s" y="%s" z="%s" />\n' % (norm[0], norm[1], norm[2])) f.write(' </vertex>\n') f.write(' </vertexbuffer>\n') # UV Texture Coordinates f.write(' <vertexbuffer texture_coord_dimensions_0="2" texture_coords="1">\n') for vIdx in xrange(numVerts): if obj.has_uv: u, v = obj.r_texco[vIdx] v = 1-v else: u, v = 0, 0 f.write(' <vertex>\n') f.write(' <texcoord u="%s" v="%s" />\n' % (u, v)) f.write(' </vertex>\n') f.write(' </vertexbuffer>\n') f.write(' </geometry>\n') # Skeleton bone assignments if human.getSkeleton(): bodyWeights = human.getVertexWeights() if stuff.type: # Determine vertex weights for proxy weights = skeleton.getProxyWeights(stuff.proxy, bodyWeights, obj) else: # Use vertex weights for human body weights = bodyWeights # Account for vertices that are filtered out if stuff.meshInfo.vertexMapping != None: filteredVIdxMap = stuff.meshInfo.vertexMapping weights2 = {} for (boneName, (verts,ws)) in weights.items(): verts2 = [] ws2 = [] for i, vIdx in enumerate(verts): if vIdx in filteredVIdxMap: verts2.append(filteredVIdxMap[vIdx]) ws2.append(ws[i]) weights2[boneName] = (verts2, ws2) weights = weights2 # Remap vertex weights to the unwelded vertices of the object (obj.coord to obj.r_coord) originalToUnweldedMap = {} for unweldedIdx, originalIdx in enumerate(obj.vmap): if originalIdx not in originalToUnweldedMap.keys(): originalToUnweldedMap[originalIdx] = [] originalToUnweldedMap[originalIdx].append(unweldedIdx) f.write(' <boneassignments>\n') boneNames = [ bone.name for bone in human.getSkeleton().getBones() ] for (boneName, (verts,ws)) in weights.items(): bIdx = boneNames.index(boneName) for i, vIdx in enumerate(verts): w = ws[i] try: for r_vIdx in originalToUnweldedMap[vIdx]: f.write(' <vertexboneassignment vertexindex="%s" boneindex="%s" weight="%s" />\n' % (r_vIdx, bIdx, w)) except: # unused coord pass f.write(' </boneassignments>\n') f.write(' </submesh>\n') f.write(' </submeshes>\n') f.write(' <submeshnames>\n') for stuffIdx, stuff in enumerate(stuffs): f.write(' <submeshname name="%s" index="%s" />\n' % (formatName(stuff.name) if formatName(stuff.name) != name else "human", stuffIdx)) f.write(' </submeshnames>\n') if human.getSkeleton(): f.write(' <skeletonlink name="%s.skeleton" />\n' % name) f.write('</mesh>') f.close()
def exportMd5(human, filepath, config): """ This function exports MakeHuman mesh and skeleton data to id Software's MD5 format. Parameters ---------- human: *Human*. The object whose information is to be used for the export. filepath: *string*. The filepath of the file to export the object to. config: *Config*. Export configuration. """ progress = Progress() obj = human.meshData config.setHuman(human) config.zUp = True config.feetOnGround = True # TODO this only works when exporting MHX mesh (a design error in exportutils) config.scale = 1 config.setupTexFolder(filepath) filename = os.path.basename(filepath) name = config.goodName(os.path.splitext(filename)[0]) humanBBox = human.meshData.calcBBox() progress(0, 0.2, "Collecting Objects") rmeshes = exportutils.collect.setupMeshes( name, human, config=config, subdivide=config.subdivide) if human.getSkeleton(): numJoints = human.getSkeleton().getBoneCount() +1 # Amount of joints + the hardcoded origin below else: numJoints = 1 f = codecs.open(filepath, 'w', encoding="utf-8") f.write('MD5Version 10\n') f.write('commandline ""\n\n') f.write('numJoints %d\n' % numJoints) f.write('numMeshes %d\n\n' % (len(rmeshes))) f.write('joints {\n') # Hardcoded root joint f.write('\t"%s" %d ( %f %f %f ) ( %f %f %f )\n' % ('origin', -1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)) progress(0.2, 0.3, "Writing Bones") if human.getSkeleton(): bones = human.getSkeleton().getBones() boneprog = Progress(len(bones)) for bone in bones: writeBone(f, bone, human, config) boneprog.step() f.write('}\n\n') progress(0.3, 0.8, "Writing Objects") loopprog = Progress(len(rmeshes)) for rmeshIdx, rmesh in enumerate(rmeshes): # rmesh.type: None is human, "Proxymeshes" is human proxy, "Clothes" for clothing and "Hair" for hair objprog = Progress() obj = rmesh.object obj.calcFaceNormals() obj.calcVertexNormals() obj.updateIndexBuffer() numVerts = len(obj.r_coord) if obj.vertsPerPrimitive == 4: # Quads numFaces = len(obj.r_faces) * 2 else: # Tris numFaces = len(obj.r_faces) f.write('mesh {\n') mat = rmesh.material if mat.diffuseTexture: tex = copyTexture(mat.diffuseTexture, human, config) f.write('\tshader "%s"\n' % tex) f.write('\n\tnumverts %d\n' % numVerts) # Collect vertex weights if human.getSkeleton(): objprog(0, 0.2) bodyWeights = human.getVertexWeights() if rmesh.type: # Determine vertex weights for proxy weights = skeleton.getProxyWeights(rmesh.proxy, bodyWeights, obj) else: # Use vertex weights for human body weights = bodyWeights # Account for vertices that are filtered out if rmesh.vertexMapping != None: filteredVIdxMap = rmesh.vertexMapping weights2 = {} for (boneName, (verts,ws)) in weights.items(): verts2 = [] ws2 = [] for i, vIdx in enumerate(verts): if vIdx in filteredVIdxMap: verts2.append(filteredVIdxMap[vIdx]) ws2.append(ws[i]) weights2[boneName] = (verts2, ws2) weights = weights2 # Remap vertex weights to the unwelded vertices of the object (obj.coord to obj.r_coord) originalToUnweldedMap = {} for unweldedIdx, originalIdx in enumerate(obj.vmap): if originalIdx not in originalToUnweldedMap.keys(): originalToUnweldedMap[originalIdx] = [] originalToUnweldedMap[originalIdx].append(unweldedIdx) # Build a weights list indexed per vertex jointIndexes = {} jointIndexes['origin'] = 0 joints = [None] + human.getSkeleton().getBones() # origin joint is None for idx,bone in enumerate(joints): if bone: jointIndexes[bone.name] = idx vertWeights = {} # = dict( vertIdx: [ (jointIdx1, weight1), ...]) for (jointName, (verts,ws)) in weights.items(): jointIdx = jointIndexes[jointName] for idx,v in enumerate(verts): try: for r_vIdx in originalToUnweldedMap[v]: if r_vIdx not in vertWeights: vertWeights[r_vIdx] = [] vertWeights[r_vIdx].append((jointIdx, ws[idx])) except: # unused coord pass for vert in xrange(numVerts): if vert not in vertWeights: # Weight vertex completely to origin joint vertWeights[vert] = [(0, 1.0)] objprog(0.2, 0.3) else: vertWeights = None objprog(0, 0.3) # Write vertices wCount = 0 for vert in xrange(numVerts): if obj.has_uv: u, v = obj.r_texco[vert] else: u, v = 0, 0 if vertWeights == None: numWeights = 1 else: numWeights = len(vertWeights[vert]) # vert [vertIndex] ( [texU] [texV] ) [weightIndex] [weightElem] f.write('\tvert %d ( %f %f ) %d %d\n' % (vert, u, 1.0-v, wCount, numWeights)) wCount = wCount + numWeights objprog(0.3, 0.5) # Write faces f.write('\n\tnumtris %d\n' % numFaces) fn = 0 for fv in obj.r_faces: # tri [triIndex] [vertIndex1] [vertIndex2] [vertIndex3] f.write('\ttri %d %d %d %d\n' % (fn, fv[2], fv[1], fv[0])) fn += 1 if fv[0] != fv[3]: f.write('\ttri %d %d %d %d\n' % (fn, fv[0], fv[3], fv[2])) fn += 1 objprog(0.5, 0.99) # Write bone weighting bwprog = Progress(len(obj.r_coord)).HighFrequency(200) if human.getSkeleton(): f.write('\n\tnumweights %d\n' % wCount) wCount = 0 for idx,co in enumerate(obj.r_coord): for (jointIdx, jointWght) in vertWeights[idx]: # Get vertex position in bone space if joints[jointIdx]: invbonematrix = joints[jointIdx].matRestGlobal.copy() invbonematrix[:3,3] = [0,0,0] invbonematrix = la.inv(invbonematrix) relPos = np.ones(4, dtype=np.float32) relPos[:3] = co[:3] relPos[:3] -= joints[jointIdx].getRestHeadPos() relPos[:3] *= scale #relPos = np.dot(relPos, invbonematrix) else: relPos = co[:3] * scale if config.zUp: relPos[:3] = relPos[[0,2,1]] * [1,-1,1] # weight [weightIndex] [jointIndex] [weightValue] ( [xPos] [yPos] [zPos] ) f.write('\tweight %d %d %f ( %f %f %f )\n' % (wCount, jointIdx, jointWght, relPos[0], relPos[1], relPos[2])) wCount = wCount +1 bwprog.step() else: # No skeleton selected: Attach all vertices to the root with weight 1.0 f.write('\n\tnumweights %d\n' % (numVerts)) for idx,co in enumerate(obj.r_coord): # weight [weightIndex] [jointIndex] [weightValue] ( [xPos] [yPos] [zPos] ) co = co.copy() * scale if config.feetOnGround: co[1] += (getFeetOnGroundOffset(human) * scale) if config.zUp: co = co[[0,2,1]] * [1,-1,1] f.write('\tweight %d %d %f ( %f %f %f )\n' % (idx, 0, 1.0, co[0], co[1], co[2])) # Note: MD5 has a z-up coordinate system bwprog.step() f.write('}\n\n') loopprog.step() f.close() progress(0.8, 0.99, "Writing Animations") if human.getSkeleton() and hasattr(human, 'animations'): animprog = Progress(len(human.animations)) for anim in human.animations: writeAnimation(filepath, human, humanBBox, config, anim.getAnimationTrack()) animprog.step() progress(1, None, "MD5 export finished. Exported file: %s" % filepath)
def exportFbx(filepath, config): from armature.armature import setupArmature G.app.progress(0, text="Preparing") human = config.human config.setupTexFolder(filepath) log.message("Write FBX file %s" % filepath) filename = os.path.basename(filepath) name = config.goodName(os.path.splitext(filename)[0]) #rawTargets = exportutils.collect.readTargets(human, config) # TODO no idea what to do with this # Collect objects, scale meshes and filter out hidden faces/verts, scale rig objects = human.getObjects(excludeZeroFaceObjs=True) meshes = [obj.mesh.clone(config.scale, True) for obj in objects] skel = human.getSkeleton() if skel: skel = skel.scaled(config.scale) # Set mesh names for mesh in meshes: mesh.name = fbx_utils.getMeshName(mesh, skel) G.app.progress(0.5, text="Exporting %s" % filepath) fp = codecs.open(filepath, "w", encoding="utf-8") fbx_utils.resetId() # Reset global ID generator fbx_utils.setAbsolutePath(filepath) fbx_header.writeHeader(fp, filepath) # Generate bone weights for all meshes up front so they can be reused for all if skel: rawWeights = human.getVertexWeights() # Basemesh weights for mesh in meshes: if mesh.object.proxy: # Transfer weights to proxy parentWeights = skeleton.getProxyWeights( mesh.object.proxy, rawWeights) else: parentWeights = rawWeights # Transfer weights to face/vert masked and/or subdivided mesh weights = mesh.getWeights(parentWeights) # Attach these vertexWeights to the mesh to pass them around the # exporter easier, the cloned mesh is discarded afterwards, anyway mesh.vertexWeights = weights else: # Attach trivial weights to the meshes for mesh in meshes: mesh.vertexWeights = dict() # TODO if "shapes" need to be exported, attach them to meshes in a similar way nVertexGroups, nShapes = fbx_deformer.getObjectCounts(meshes) fbx_header.writeObjectDefs(fp, meshes, skel, config) fbx_skeleton.writeObjectDefs(fp, meshes, skel) fbx_mesh.writeObjectDefs(fp, meshes, nShapes) fbx_deformer.writeObjectDefs(fp, meshes, skel) if config.useMaterials: fbx_material.writeObjectDefs(fp, meshes) #fbx_anim.writeObjectDefs(fp) fp.write('}\n\n') fbx_header.writeObjectProps(fp) if skel: fbx_skeleton.writeObjectProps(fp, skel, config) fbx_mesh.writeObjectProps(fp, meshes, config) fbx_deformer.writeObjectProps(fp, meshes, skel, config) if config.useMaterials: fbx_material.writeObjectProps(fp, meshes, config) #fbx_anim.writeObjectProps(fp) fp.write('}\n\n') fbx_utils.startLinking() fbx_header.writeLinks(fp) if skel: fbx_skeleton.writeLinks(fp, skel) fbx_mesh.writeLinks(fp, meshes) fbx_deformer.writeLinks(fp, meshes, skel) if config.useMaterials: fbx_material.writeLinks(fp, meshes) #fbx_anim.writeLinks(fp) fp.write('}\n\n') fbx_header.writeTakes(fp) fp.close() G.app.progress(1) log.message("%s written" % filepath)
def writeMeshFile(human, filepath, objects, config): progress = Progress(len(objects)) filename = os.path.basename(filepath) name = formatName(os.path.splitext(filename)[0]) f = codecs.open(filepath, 'w', encoding="utf-8") lines = [] lines.append('<?xml version="1.0" encoding="UTF-8"?>') lines.append('<!-- Exported from MakeHuman (www.makehuman.org) -->') lines.append('<mesh>') lines.append(' <submeshes>') for objIdx, obj in enumerate(objects): loopprog = Progress() loopprog(0.0, 0.1, "Writing %s mesh.", obj.name) pxy = obj.proxy mesh = obj.mesh # Scale and filter out masked vertices/faces mesh = mesh.clone(scale=config.scale, filterMaskedVerts=True ) # here obj.parent is set to the original obj numVerts = len(mesh.r_coord) if mesh.vertsPerPrimitive == 4: # Quads numFaces = len(mesh.r_faces) * 2 else: # Tris numFaces = len(mesh.r_faces) loopprog(0.1, 0.3, "Writing faces of %s.", obj.name) # TODO add proxy type name in material name as well lines.append( ' <submesh material="%s_%s_%s" usesharedvertices="false" use32bitindexes="false" operationtype="triangle_list">' % (formatName(name), objIdx, formatName(obj.name) if formatName(obj.name) != name else "human")) # Faces lines.append(' <faces count="%s">' % numFaces) if mesh.vertsPerPrimitive == 4: lines.extend( ['''\ <face v1="%s" v2="%s" v3="%s" /> <face v1="%s" v2="%s" v3="%s" />''' % (fv[0], fv[1], fv[2], fv[2], fv[3], fv[0]) \ for fv in mesh.r_faces ] ) else: lines.extend([ ' <face v1="%s" v2="%s" v3="%s" />' % (fv[0], fv[1], fv[2]) for fv in mesh.r_faces ]) lines.append(' </faces>') loopprog(0.3, 0.7, "Writing vertices of %s.", obj.name) # Vertices lines.append(' <geometry vertexcount="%s">' % numVerts) lines.append( ' <vertexbuffer positions="true" normals="true">') coords = mesh.r_coord.copy() if config.feetOnGround: coords[:, 1] += getFeetOnGroundOffset(config) # Note: Ogre3d uses a y-up coordinate system (just like MH) lines.extend(['''\ <vertex> <position x="%s" y="%s" z="%s" /> <normal x="%s" y="%s" z="%s" /> </vertex>''' % (coords[vIdx,0], coords[vIdx,1], coords[vIdx,2], mesh.r_vnorm[vIdx,0], mesh.r_vnorm[vIdx,1], mesh.r_vnorm[vIdx,2]) \ for vIdx in xrange(coords.shape[0]) ]) lines.append(' </vertexbuffer>') loopprog(0.8 - 0.1 * bool(human.getSkeleton()), 0.9, "Writing UVs of %s.", obj.name) # UV Texture Coordinates lines.append( ' <vertexbuffer texture_coord_dimensions_0="2" texture_coords="1">' ) if mesh.has_uv: uvs = mesh.r_texco.copy() uvs[:, 1] = 1 - uvs[:, 1] # v = 1 - v else: uvs = np.zeros((numVerts, 2), dtype=np.float32) lines.extend([ '''\ <vertex> <texcoord u="%s" v="%s" /> </vertex>''' % (u, v) for u, v in uvs ]) lines.append(' </vertexbuffer>') lines.append(' </geometry>') if human.getSkeleton(): loopprog(0.9, 0.99, "Writing bone assignments of %s.", obj.name) else: loopprog(0.99, None, "Written %s.", obj.name) # Skeleton bone assignments if human.getSkeleton(): # TODO getVertexWeights is the best name for this, use it consistently in proxy/module3d bodyWeights = human.getVertexWeights() if pxy: # Determine vertex weights for proxy (map to unfiltered proxy mesh) weights = skeleton.getProxyWeights(pxy, bodyWeights) else: # Use vertex weights for human body weights = bodyWeights # Remap vertex weights to account for hidden vertices that are # filtered out, and remap to multiple vertices if mesh is subdivided weights = mesh.getWeights(weights) # Remap vertex weights to the unwelded vertices of the object (mesh.coord to mesh.r_coord) originalToUnweldedMap = mesh.inverse_vmap lines.append(' <boneassignments>') boneNames = [bone.name for bone in human.getSkeleton().getBones()] for (boneName, (verts, ws)) in weights.items(): bIdx = boneNames.index(boneName) for i, vIdx in enumerate(verts): w = ws[i] try: lines.extend([ ' <vertexboneassignment vertexindex="%s" boneindex="%s" weight="%s" />' % (r_vIdx, bIdx, w) for r_vIdx in originalToUnweldedMap[vIdx] ]) except: # unused coord pass lines.append(' </boneassignments>') progress.step() lines.append(' </submesh>') lines.append(' </submeshes>') lines.append(' <submeshnames>') for objIdx, obj in enumerate(objects): lines.append( ' <submeshname name="%s" index="%s" />' % (formatName(obj.name) if formatName(obj.name) != name else "human", objIdx)) lines.append(' </submeshnames>') if human.getSkeleton(): lines.append(' <skeletonlink name="%s.skeleton" />' % name) lines.append('</mesh>') f.write("\n".join(lines)) f.close()
def writeSkinController(fp, human, mesh, skel, config): """ Write controller for skinning or rigging, in other words: the controller that ties an animation skeleton to the mesh. """ progress = Progress() progress(0, 0.1) nVerts = len(mesh.coord) nBones = len(skel.getBones()) rawWeights = human.getVertexWeights() obj = mesh.object # Remap vertex weights to mesh if obj.proxy: import skeleton parentWeights = skeleton.getProxyWeights(obj.proxy, rawWeights) else: parentWeights = rawWeights weights = mesh.getWeights(parentWeights) vertexWeights = [list() for _ in xrange(nVerts)] skinWeights = [] wn = 0 boneNames = [bone.name for bone in skel.getBones()] for bIdx, boneName in enumerate(boneNames): try: (verts, ws) = weights[boneName] except: (verts, ws) = ([], []) wts = zip(verts, ws) skinWeights += wts for (vn, _w) in wts: vertexWeights[int(vn)].append((bIdx, wn)) wn += 1 nSkinWeights = len(skinWeights) # Write rig transform matrix progress(0.1, 0.2) fp.write('\n' + ' <controller id="%s-skin">\n' % mesh.name + ' <skin source="#%sMesh">\n' % mesh.name + ' <bind_shape_matrix>\n' + ' 1 0 0 0\n' + ' 0 1 0 0\n' + ' 0 0 1 0\n' + ' 0 0 0 1\n' + ' </bind_shape_matrix>\n' + ' <source id="%s-skin-joints">\n' % mesh.name + ' <IDREF_array count="%d" id="%s-skin-joints-array">\n' % (nBones, mesh.name) + ' ') # Write bones for bone in skel.getBones(): bname = goodBoneName(bone.name) fp.write(' %s' % bname) progress(0.2, 0.4) fp.write( '\n' + ' </IDREF_array>\n' + ' <technique_common>\n' + ' <accessor count="%d" source="#%s-skin-joints-array" stride="1">\n' % (nBones, mesh.name) + ' <param type="IDREF" name="JOINT"></param>\n' + ' </accessor>\n' + ' </technique_common>\n' + ' </source>\n' + ' <source id="%s-skin-weights">\n' % mesh.name + ' <float_array count="%d" id="%s-skin-weights-array">\n' % (nSkinWeights, mesh.name) + ' ') fp.write(' '.join('%s' % w[1] for w in skinWeights)) fp.write( '\n' + ' </float_array>\n' + ' <technique_common>\n' + ' <accessor count="%d" source="#%s-skin-weights-array" stride="1">\n' % (nSkinWeights, mesh.name) + ' <param type="float" name="WEIGHT"></param>\n' + ' </accessor>\n' + ' </technique_common>\n' + ' </source>\n' + ' <source id="%s-skin-poses">\n' % mesh.name + ' <float_array count="%d" id="%s-skin-poses-array">' % (16 * nBones, mesh.name)) progress(0.4, 0.6) for bone in skel.getBones(): #mat = la.inv(bone.getRestOrTPoseMatrix(config)) # TODO remove (this is a hack) mat = la.inv( bone.getRestMatrix(config.meshOrientation, config.localBoneAxis, config.offset)) for i in range(4): fp.write('\n ') for j in range(4): fp.write(' %.4f' % mat[i, j]) fp.write('\n') progress(0.6, 0.8) fp.write( '\n' + ' </float_array>\n' + ' <technique_common>\n' + ' <accessor count="%d" source="#%s-skin-poses-array" stride="16">\n' % (nBones, mesh.name) + ' <param type="float4x4"></param>\n' + ' </accessor>\n' + ' </technique_common>\n' + ' </source>\n' + ' <joints>\n' + ' <input semantic="JOINT" source="#%s-skin-joints"/>\n' % mesh.name + ' <input semantic="INV_BIND_MATRIX" source="#%s-skin-poses"/>\n' % mesh.name + ' </joints>\n' + ' <vertex_weights count="%d">\n' % nVerts + ' <input offset="0" semantic="JOINT" source="#%s-skin-joints"/>\n' % mesh.name + ' <input offset="1" semantic="WEIGHT" source="#%s-skin-weights"/>\n' % mesh.name + ' <vcount>\n' + ' ') # Write number of bones weighted per vertex fp.write(' '.join(['%d' % len(wts) for wts in vertexWeights])) progress(0.8, 0.99) fp.write('\n' + ' </vcount>\n' ' <v>\n' + ' ') for wts in vertexWeights: fp.write(''.join([' %d %d' % pair for pair in wts])) fp.write('\n' + ' </v>\n' + ' </vertex_weights>\n' + ' </skin>\n' + ' </controller>\n') progress(1)
def writeMeshFile(human, filepath, meshes, config): progress = Progress(len(meshes)) filename = os.path.basename(filepath) name = formatName(os.path.splitext(filename)[0]) f = codecs.open(filepath, 'w', encoding="utf-8") lines = [] lines.append('<?xml version="1.0" encoding="UTF-8"?>') lines.append('<!-- Exported from MakeHuman (www.makehuman.org) -->') lines.append('<mesh>') lines.append(' <submeshes>') for objIdx, mesh in enumerate(meshes): loopprog = Progress() loopprog(0.0, 0.1, "Writing %s mesh.", mesh.name) if isinstance(mesh, proxy.Proxy): # Object is proxy pxy = mesh obj = pxy.object.mesh else: # Object is human mesh pxy = mesh.proxy obj = mesh.mesh # Scale and filter out masked vertices/faces obj = obj.clone(scale=1, filterMaskedVerts=True) # here obj.parent is set to the original obj numVerts = len(obj.r_coord) if obj.vertsPerPrimitive == 4: # Quads numFaces = len(obj.r_faces) * 2 else: # Tris numFaces = len(obj.r_faces) loopprog(0.1, 0.3, "Writing faces of %s.", obj.name) # TODO add proxy type name in material name as well lines.append(' <submesh material="%s_%s_%s" usesharedvertices="false" use32bitindexes="false" operationtype="triangle_list">' % (formatName(name), objIdx, formatName(obj.name) if formatName(obj.name) != name else "human")) # Faces lines.append(' <faces count="%s">' % numFaces) if obj.vertsPerPrimitive == 4: lines.extend( ['''\ <face v1="%s" v2="%s" v3="%s" /> <face v1="%s" v2="%s" v3="%s" />''' % (fv[0], fv[1], fv[2], fv[2], fv[3], fv[0]) \ for fv in obj.r_faces ] ) else: lines.extend( [' <face v1="%s" v2="%s" v3="%s" />' % (fv[0], fv[1], fv[2]) for fv in obj.r_faces]) lines.append(' </faces>') loopprog(0.3, 0.7, "Writing vertices of %s.", obj.name) # Vertices lines.append(' <geometry vertexcount="%s">' % numVerts) lines.append(' <vertexbuffer positions="true" normals="true">') coords = obj.r_coord.copy() if config.feetOnGround: coords[:,1] += getFeetOnGroundOffset(human) # Note: Ogre3d uses a y-up coordinate system (just like MH) lines.extend(['''\ <vertex> <position x="%s" y="%s" z="%s" /> <normal x="%s" y="%s" z="%s" /> </vertex>''' % (coords[vIdx,0], coords[vIdx,1], coords[vIdx,2], obj.r_vnorm[vIdx,0], obj.r_vnorm[vIdx,1], obj.r_vnorm[vIdx,2]) \ for vIdx in xrange(coords.shape[0]) ]) lines.append(' </vertexbuffer>') loopprog(0.8 - 0.1*bool(human.getSkeleton()), 0.9, "Writing UVs of %s.", obj.name) # UV Texture Coordinates lines.append(' <vertexbuffer texture_coord_dimensions_0="2" texture_coords="1">') if obj.has_uv: uvs = obj.r_texco.copy() uvs[:,1] = 1-uvs[:,1] # v = 1 - v else: uvs = np.zeros((numVerts,2), dtype=np.float32) lines.extend( ['''\ <vertex> <texcoord u="%s" v="%s" /> </vertex>''' % (u, v) for u, v in uvs ] ) lines.append(' </vertexbuffer>') lines.append(' </geometry>') if human.getSkeleton(): loopprog(0.9, 0.99, "Writing bone assignments of %s.", obj.name) else: loopprog(0.99, None, "Written %s.", obj.name) # Skeleton bone assignments if human.getSkeleton(): bodyWeights = human.getVertexWeights() if pxy: # Determine vertex weights for proxy (map to unfiltered proxy mesh) weights = skeleton.getProxyWeights(pxy, bodyWeights, obj.parent) else: # Use vertex weights for human body weights = bodyWeights # Account for vertices that are filtered out filteredVIdxMap = obj.inverse_parent_map # Remap vertex weights to the unwelded vertices of the object (obj.coord to obj.r_coord) originalToUnweldedMap = obj.inverse_vmap lines.append(' <boneassignments>') boneNames = [ bone.name for bone in human.getSkeleton().getBones() ] for (boneName, (verts,ws)) in weights.items(): bIdx = boneNames.index(boneName) for i, vIdx in enumerate(verts): if filteredVIdxMap[vIdx] >= 0: # Vertex for weight is included in the filtered mesh vIdx = filteredVIdxMap[vIdx] w = ws[i] try: lines.extend( [' <vertexboneassignment vertexindex="%s" boneindex="%s" weight="%s" />' % (r_vIdx, bIdx, w) for r_vIdx in originalToUnweldedMap[vIdx]] ) except: # unused coord pass lines.append(' </boneassignments>') progress.step() lines.append(' </submesh>') lines.append(' </submeshes>') lines.append(' <submeshnames>') for objIdx, mesh in enumerate(meshes): # TODO this isinstance test everytime is a hassle, perhaps getMeshes() can return meshes in nicer form if isinstance(mesh, proxy.Proxy): # Object is proxy obj = mesh.object.mesh else: # Object is human mesh obj = mesh.mesh lines.append(' <submeshname name="%s" index="%s" />' % (formatName(obj.name) if formatName(obj.name) != name else "human", objIdx)) lines.append(' </submeshnames>') if human.getSkeleton(): lines.append(' <skeletonlink name="%s.skeleton" />' % name) lines.append('</mesh>') f.write("\n".join(lines)) f.close()