mesh.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import sys
  2. import os
  3. from struct import pack
  4. from copy import deepcopy
  5. from Blender import Mathutils
  6. from Blender.Mathutils import *
  7. #=======================================================================================================================
  8. # Initializer =
  9. #=======================================================================================================================
  10. class Initializer:
  11. def __init__(self):
  12. self.blMesh = None # Blender Mesh
  13. self.blSkeleton = None # Blender Armature
  14. self.saveDir = "" # the name of the saved file
  15. self.flipYZ = False #convert from bl to right handed coord system
  16. #=======================================================================================================================
  17. # Vert =
  18. #=======================================================================================================================
  19. class Vert:
  20. def __init__(self):
  21. self.x = 0.0
  22. self.y = 0.0
  23. self.z = 0.0
  24. self.s = 0.0
  25. self.t = 0.0
  26. self.bonesNum = 0
  27. self.boneIds = [-1, -1, -1, -1]
  28. self.weights = [-1.0, -1.0, -1.0, -1.0]
  29. self.nextId = -1 # shows the next vertId. Is != -1 if the vert is problematic
  30. #=======================================================================================================================
  31. # Tri =
  32. #=======================================================================================================================
  33. class Tri:
  34. def __init__(self):
  35. self.vertIds = [-1, -1, -1]
  36. #=======================================================================================================================
  37. # getBlMeshFromBlObj =
  38. #=======================================================================================================================
  39. def getBlMeshFromBlObj(obj):
  40. if(obj.__class__.__name__ != "Blender Object"):
  41. raise RuntimeError("The given func param is not a \"Blender Object\" class but a \"" + obj.__class__.__name__ + "\"")
  42. if obj.getType() != "Mesh":
  43. raise RuntimeError("The obj \"" + obj.getName() + "\" must link to a mesh and not to a(n) " + obj.getType())
  44. mesh = obj.getData(0, 1)
  45. return mesh
  46. #=======================================================================================================================
  47. # updateAnkiVertsWithBoneWeights =
  48. #=======================================================================================================================
  49. def updateAnkiVertsWithBoneWeights(mesh, skeleton, ankiVerts):
  50. boneNames = skeleton.bones.keys()
  51. boneNames.sort()
  52. # init text
  53. ftxt = ""
  54. # link the vert groups to the bone ids
  55. vgroup2boneId = {} # we give the vgroup name and we get the bone's id in the skeleton
  56. vgroupNames = mesh.getVertGroupNames()
  57. for vgroupName in vgroupNames:
  58. boneId = -1
  59. for boneName in boneNames:
  60. if boneName == vgroupName:
  61. boneId = boneNames.index(boneName)
  62. break
  63. if boneId == -1:
  64. print("Vert group \"" + vgroupName + "\" cant link to a bone")
  65. vgroup2boneId[vgroupName] = boneId
  66. # for every non problematic vert do some shit
  67. for i in range(len(mesh.verts)):
  68. vert = mesh.verts[i]
  69. influences = mesh.getVertexInfluences(vert.index)
  70. influencesNum = 0
  71. sumw = 0.0
  72. # calc the influences num and the total weight (NOTE:we may have a vert group that doesnt connect to a bone)
  73. for influence in influences:
  74. vgroup = influence[0]
  75. weight = influence[1]
  76. if vgroup2boneId[vgroup] != -1:
  77. influencesNum = influencesNum + 1
  78. sumw = sumw + weight
  79. # a check
  80. if influencesNum > 4:
  81. raise RuntimeError("Cannot have more than 4 bones per vert")
  82. # write influences num
  83. ankiVerts[i].bonesNum = influencesNum
  84. for j in range(len(influences)):
  85. influence = influences[j]
  86. vgroup = influence[0]
  87. weight = influence[1]
  88. if vgroup2boneId[vgroup] != -1:
  89. # write bone id
  90. ankiVerts[i].boneIds[j] = vgroup2boneId[vgroup]
  91. # write weight for that bone
  92. ankiVerts[i].weights[j] = weight/sumw
  93. # end for all non problematic verts
  94. # for every canonical ankiVert fill the problematics
  95. for i in range(len(mesh.verts)):
  96. ankiVert = ankiVerts[i]
  97. cid = i # current id
  98. nid = ankiVert.nextId # next id
  99. if nid == -1:
  100. continue
  101. while nid != -1:
  102. # copy vert weight data
  103. ankiVerts[nid].bonesNum = ankiVerts[cid].bonesNum
  104. ankiVerts[nid].boneIds = deepcopy(ankiVerts[cid].boneIds)
  105. ankiVerts[nid].weights = deepcopy(ankiVerts[cid].weights)
  106. cid = nid
  107. nid = ankiVerts[nid].nextId
  108. #=======================================================================================================================
  109. # getAnkiMeshScript =
  110. #=======================================================================================================================
  111. def getAnkiMeshScript(mesh, skeleton, meshName, flipYZ):
  112. # check verts number
  113. vertsNum = len(mesh.verts)
  114. if vertsNum < 3:
  115. raise RuntimeError("The mesh named \"" + mesh.name + "\" has insufficient vert num")
  116. # declare some vars
  117. ankiVerts = {}
  118. ankiTris = [Tri() for tri in range(len(mesh.faces))]
  119. hasUvs = 0
  120. # if it has UVs
  121. if mesh.faceUV:
  122. hasUvs = 1
  123. # for all faces
  124. for i in range(len(mesh.faces)):
  125. face = mesh.faces[i]
  126. if len(face.verts) != 3:
  127. raise RuntimeError("Only triangles are accepted")
  128. ankiTris[i] = Tri() # create new tri
  129. # for verts in the face
  130. for j in [0, 1, 2]:
  131. vertId = face.verts[j].index
  132. # vert does not exist
  133. if not ankiVerts.has_key(vertId):
  134. ankiVerts[vertId] = Vert()
  135. ankiVerts[vertId].x = mesh.verts[vertId].co.x
  136. ankiVerts[vertId].y = mesh.verts[vertId].co.y
  137. ankiVerts[vertId].z = mesh.verts[vertId].co.z
  138. ankiVerts[vertId].s = face.uv[j].x
  139. ankiVerts[vertId].t = face.uv[j].y
  140. ankiVerts[vertId].nextId = -1
  141. ankiTris[i].vertIds[j] = vertId
  142. else:
  143. prevId = -1
  144. while 1:
  145. # if in end of the list, create new vert and link list
  146. if vertId == -1:
  147. ankiVerts[vertsNum] = deepcopy(ankiVerts[prevId])
  148. ankiVerts[vertsNum].s = face.uv[j].x
  149. ankiVerts[vertsNum].t = face.uv[j].y
  150. ankiVerts[vertsNum].nextId = -1
  151. ankiVerts[prevId].nextId = vertsNum
  152. ankiTris[i].vertIds[j] = vertsNum
  153. vertsNum = vertsNum + 1
  154. break
  155. # a vert with the same UVs exists
  156. elif ankiVerts[vertId].s == face.uv[j].x and ankiVerts[vertId].t == face.uv[j].y:
  157. ankiTris[i].vertIds[j] = vertId
  158. break;
  159. # move to next node
  160. else:
  161. prevId = vertId
  162. vertId = ankiVerts[vertId].nextId
  163. # no UVs
  164. else:
  165. hasUvs = 0
  166. # set the verts
  167. for i in range(len(mesh.verts)):
  168. vert = mesh.verts[i]
  169. ankiVerts[i] = Vert()
  170. ankiVerts[i].x = vert.co.x
  171. ankiVerts[i].y = vert.co.y
  172. ankiVerts[i].z = vert.co.z
  173. # set the faces
  174. for i in range(len(mesh.faces)):
  175. face = mesh.faces[i]
  176. if len(face.verts) != 3:
  177. raise RuntimeError("Only triangles are accepted")
  178. for j in [0, 1, 2]:
  179. ankiTris[i].vertIds[j] = face.verts[j].index
  180. # write to ftxt
  181. # write mtl name
  182. """ftxt = "mesh\n{\n"
  183. ftxt += "\tmaterial \"" + mtlName + "\"\n"
  184. # write verts
  185. ftxt += "\tvertsNum " + str(len(ankiVerts)) + "\n\tverts\n\t{\n"
  186. for i in range(len(ankiVerts)):
  187. ankiVert = ankiVerts[i]
  188. if flipYZ == 0:
  189. ftxt += "\t\tvert {" + str(ankiVert.x) + " " + str(ankiVert.y) + " " + str(ankiVert.z) + "}\n"
  190. else:
  191. ftxt += "\t\tvert {" + str(ankiVert.x) + " " + str(ankiVert.z) + " " + str(-ankiVert.y) + "}\n"
  192. # write the tris
  193. ftxt += str(len(ankiTris)) + "\n"
  194. for ankiTri in ankiTris:
  195. ftxt += str(ankiTri.vertIds[0]) + " " + str(ankiTri.vertIds[1]) + " " + str(ankiTri.vertIds[2]) + "\n"
  196. # write the UVs
  197. if hasUvs:
  198. ftxt += str(len(ankiVerts)) + "\n"
  199. for i in range(len(ankiVerts)):
  200. ftxt += str(ankiVerts[i].s) + " " + str(ankiVerts[i].t) + "\n"
  201. else:
  202. ftxt += "0\n"
  203. # write the vert weights
  204. if skeleton != None:
  205. updateAnkiVertsWithBoneWeights(mesh, skeleton, ankiVerts)
  206. ftxt += str(len(ankiVerts)) + "\n"
  207. for i in range(len(ankiVerts)):
  208. ankiVert = ankiVerts[i]
  209. ftxt += str(ankiVert.bonesNum) + "\n"
  210. for j in range(ankiVert.bonesNum):
  211. ftxt += str(ankiVert.boneIds[j]) + " " + str(ankiVert.weights[j]) + "\n"
  212. else:
  213. ftxt += "0\n"
  214. return ftxt"""
  215. # Magic
  216. buff = pack("8s", "ANKIMESH")
  217. # Mesh name
  218. str_ = meshName
  219. buff += pack("I" + str(len(str_)) + "s", len(str_), str_)
  220. # Verts num
  221. buff += pack("I", len(ankiVerts))
  222. # Verts
  223. for i in range(len(ankiVerts)):
  224. ankiVert = ankiVerts[i]
  225. if not flipYZ:
  226. buff += pack("fff", ankiVert.x, ankiVert.y, ankiVert.z)
  227. else:
  228. buff += pack("fff", ankiVert.x, ankiVert.z, -ankiVert.y)
  229. # Tris num
  230. buff += pack("I", len(ankiTris))
  231. # Tris
  232. for ankiTri in ankiTris:
  233. buff += pack("III", int(ankiTri.vertIds[0]), int(ankiTri.vertIds[1]), int(ankiTri.vertIds[2]))
  234. # Tex coords
  235. if hasUvs:
  236. buff += pack("I", len(ankiVerts))
  237. for i in range(len(ankiVerts)):
  238. buff += pack("ff", ankiVerts[i].s, ankiVerts[i].t)
  239. else:
  240. buff += pack("I", 0)
  241. # Bone weights
  242. if skeleton != None:
  243. updateAnkiVertsWithBoneWeights(mesh, skeleton, ankiVerts)
  244. buff += pack("I", len(ankiVerts))
  245. for i in range(len(ankiVerts)):
  246. ankiVert = ankiVerts[i]
  247. buff += pack("I", ankiVert.bonesNum)
  248. for j in range(ankiVert.bonesNum):
  249. buff += pack("If", ankiVert.boneIds[j], ankiVert.weights[j])
  250. else:
  251. buff += pack("I", 0)
  252. return buff
  253. #=======================================================================================================================
  254. # export =
  255. #=======================================================================================================================
  256. def export(meshInit):
  257. mesh = meshInit.blMesh
  258. skeleton = meshInit.blSkeleton
  259. #check if mesh is the correct class
  260. if mesh.__class__.__name__ != "Blender Mesh":
  261. raise RuntimeError("The given func param is not a \"Blender Mesh\" class but a \"" + mesh.__class__.__name__ + "\"")
  262. if skeleton != None:
  263. #check if skeleton is the correct class
  264. if(skeleton.__class__.__name__ != "Armature"):
  265. raise RuntimeError("The given func param is not a \"Armature\" class but a \"" + skeleton.__class__.__name__ + "\"")
  266. print("Trying to export mesh \"" + mesh.name + "\"")
  267. filename = os.path.abspath(meshInit.saveDir + "/" + mesh.name + ".mesh")
  268. file = open(filename, "wb")
  269. file.write(getAnkiMeshScript(mesh, skeleton, mesh.name + ".mesh", meshInit.flipYZ))
  270. print("Mesh exported!! \"" + filename + "\"")