collada_bmd_bdl_import.py 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068
  1. '''
  2. just a DAE importer for SuperBMD DAEs
  3. it loads the said DAE files with the default importer and the extension re-builds
  4. the armature and mesh skinning. Done because Blender's 2.79 Collada importer
  5. can miss armature stuff when importing (not all the time). It also lets
  6. you select the type of pose you want in the armature when imported in Blender
  7. For my mental sanity this importer is only for SuperBMD files
  8. Blender 2.79 deals with armature data on Collada files in an
  9. extremely poorly way. Hopefully in the future I can make my own
  10. Collada importer (epik project >:]).
  11. '''
  12. import bpy, math, re
  13. from mathutils import Matrix, Vector
  14. from numpy import array
  15. # ^ needed for converting an array to the correct type
  16. # Notes (AFAIK):
  17. # - bind shape matrix or BSM: matrix that tells the position of
  18. # a mesh with respect to the armature origin so it is in the
  19. # correct position for the bone it is assigned to.
  20. # - inverse bind matrix or IBM: matrix located at the skin section of
  21. # the collada file. The inverse of this matrix, the bind matrix,
  22. # describes a bone position with respect to the armature origin when the
  23. # meshes were assigned to the bones (shows on Blender in the
  24. # bind_mat custom property for the bones of an armature). Bind matrices
  25. # are the ones that show on Blender when loading a model. This matrix
  26. # is the reference matrix used to when vertex weighted agaisnt a bone
  27. # are moved by it in an animation
  28. # - rest matrix: matrix found on the armature section of the collada
  29. # file. It describes a bone position with respect to its parent bone
  30. # at the moment the armature was edited (after the skinning) to get
  31. # the model rest pose (t-pose stuff, for animation purposes). Shows
  32. # on Blender on the rest_mat custom property for the bones of an armature.
  33. # - All matrices read by Blender assume that the rotation system located in
  34. # each 4x4 matrix is in the extrinsic Euler XYZ system
  35. ################################################
  36. # get_bone_rest_matrix function
  37. # function to:
  38. # - calculate final rest matrix of a single bone
  39. ################################################
  40. def calculate_rest_matrix(rest_matrices, row_pos, bone_parenting_diagram):
  41. # acumulate transformation matrices while reading bone_parenting_diagram
  42. # reading is done from the child bone to the ultimate parent bone
  43. # final matrix will be initialized with current bone matrix from rest_matrices
  44. final_matrix = rest_matrices[row_pos]
  45. # bone position on bone_parenting_diagram will be found in the next for loop
  46. bone_parenting_pos = 0
  47. for number in range(len(bone_parenting_diagram[row_pos])):
  48. if (bone_parenting_diagram[row_pos][number] == 1):
  49. bone_parenting_pos = number
  50. break
  51. # with bone position on bone_parenting_diagram now it is time to read said
  52. # array from bottom to top searching for each consecutive parent of said bone
  53. # for a single bone, its parent is on the row the first "1" is found
  54. # that is 1 position left from said bone in its row
  55. # example:
  56. # |
  57. # v
  58. # bone 1 [1, 0, 0] row 1
  59. # bone 2 [0, 1, 0] row 2
  60. # bone 3 [0, 1 ,0] row 3
  61. # ^
  62. # |
  63. # bone 3's parent is bone 1
  64. # set 2 for loops to read the array from down to top and right to left >:]
  65. # first loop on each for loop is skipped
  66. # variable to be used on inner loop
  67. current_row = row_pos
  68. for parenting_pos in range(bone_parenting_pos, -1, -1):
  69. # first loop is skipped
  70. if (parenting_pos == bone_parenting_pos):
  71. continue
  72. for row_number in range(current_row, -1, -1):
  73. # first loop is skipped
  74. if (row_number == row_pos):
  75. continue
  76. # if element found is 0 go up one row
  77. # if element found is 1 grab transformation matrix from rest_matrices
  78. # perform multiplication, store row_number for the next inner loop
  79. # then break out of the inner loop
  80. if (bone_parenting_diagram[row_number][parenting_pos] == 0):
  81. continue
  82. # bone_parenting_diagram[row_number][parenting_pos] == 1 returns True
  83. else:
  84. # acumulate transformation matrices
  85. final_matrix = rest_matrices[row_number] * final_matrix
  86. # set new row for the next time the inner loop is executed
  87. current_row = row_number
  88. break
  89. #
  90. return final_matrix
  91. ###############################################
  92. ###############################################
  93. # read_bmd_bdl_collada function (MAIN FUNCTION)
  94. # read superbmd/blenxy collada file
  95. # used to import the collada file into Blender
  96. ###############################################
  97. ###############################################
  98. def read_bmd_bdl_collada(context, filepath, debug_messages, show_armature_pose):
  99. ###################
  100. # importing section
  101. ###################
  102. # scene variable is always needed for something
  103. scene = bpy.context.scene
  104. print("\nImporting Collada file used in BMD/BDL conversion...\n")
  105. # import collada in the standard way (blender importer, with no flags)
  106. bpy.ops.wm.collada_import(filepath=filepath)
  107. print("\nCollada file (DAE) imported.")
  108. # initialize armature variable
  109. armature = scene.objects.active
  110. # search the armature of the imported model
  111. # Collada importer selects a child of the armature
  112. # if only an armature is immported then the Collada
  113. # importer selection is already the armature object
  114. if (scene.objects.active.type != "ARMATURE"):
  115. armature = scene.objects.active.parent
  116. # deselect everything
  117. bpy.ops.object.select_all(action='DESELECT')
  118. scene.objects.active = None
  119. # select and make active the armature
  120. armature.select = True
  121. scene.objects.active = armature
  122. ###########################
  123. # get armature data section
  124. # from Collada file
  125. ###########################
  126. print("Getting Armature data...")
  127. # open DAE file
  128. f = open(filepath, 'r', encoding='utf-8')
  129. # I need the bind matrices and rest matrices from the armature bones
  130. # also the bind shape matrix for each mesh
  131. # bind matrices are in the library_controllers section of the Collada file
  132. # the inverse of them to be specific
  133. # each mesh that is assigned to a vertex group (and consequently to a bone)
  134. # has an inverse bind matrix for said bone. By inversing that matrix I get the
  135. # bind matrix for the bone. This matrix is the one that should be applied to
  136. # the bones on Blender.
  137. # rest matrices are in the library_visual_scenes section (matrices relative
  138. # to each bone) and they are used in the animation process.
  139. # If a bone does not have an inverse bind matrix on any mesh then
  140. # the matrix used to position the bone on blender is pulled from here
  141. # (the multiplication of the parent's rest matrices + the bone rest matrix).
  142. #########################################################
  143. # infinite loop to get to the skin section of the collada
  144. #########################################################
  145. while True:
  146. line = f.readline()
  147. # find() returns -1 if it does not find anything on a string
  148. # 0 is equivalent to False
  149. if (line.find("<library_controllers>") + 1):
  150. break
  151. #############################
  152. # library_visual_scenes start
  153. #############################
  154. # controller_data will be used to store the mesh id, its bind shape matrix
  155. # and bone names with their inverse bind pose matrix one mesh per row
  156. # as follows:
  157. # row 1: mesh 1 id, BSM, bone 1 name, IBM of bone 1, bone 2 name, IBM of bone 2, ...
  158. # row 2: mesh 2 id, BSM, bone a name, IBM of bone a, bone b name, IBM of bone b, ...
  159. # .
  160. # .
  161. # .
  162. controller_data = [[]]
  163. # variables to keep track of the crontroller data length
  164. i = -1 # for number of rows
  165. #######################################################
  166. # infinite loop to read the library_controllers section
  167. #######################################################
  168. while True:
  169. line = f.readline()
  170. #########################
  171. # controller start
  172. # the skin stuff for each
  173. # mesh is defined there
  174. #########################
  175. if (line.find("<controller") + 1):
  176. i = i + 1
  177. # for the first mesh there is already a row in controller_data
  178. if (i != 0):
  179. controller_data.append([])
  180. # get mesh id
  181. mesh_id = re.search('id="(.+?)"', line).group(1)
  182. controller_data[i].append(mesh_id)
  183. ##############################################
  184. # bind shape matrix from current mesh on row i
  185. ##############################################
  186. elif (line.find("<bind_shape_matrix>") + 1):
  187. k = 0
  188. temp_rest_matrices = []
  189. while (k < 4):
  190. temp_array = [0, 0, 0, 0]
  191. line = f.readline()
  192. temp_array = line.split()
  193. temp_array = [float(string) for string in temp_array]
  194. l = 0
  195. # fill temp_rest_matrices with a row
  196. while (l < 4):
  197. temp_rest_matrices.append(temp_array[l])
  198. l = l + 1
  199. k = k + 1
  200. # created "a" so the next matrix assignation is more readable
  201. a = temp_rest_matrices
  202. matrix = Matrix(( [a[0], a[1], a[2], a[3]],
  203. [a[4], a[5], a[6], a[7]],
  204. [a[8], a[9], a[10], a[11]],
  205. [a[12], a[13], a[14], a[15]]
  206. ))
  207. controller_data[i].append(matrix)
  208. ########################################################
  209. # Name_array start
  210. # the names of the bones to which the mesh was assigned
  211. # to are here. Next to this section a few lines down the
  212. # collada file there is the float_array section that has
  213. # the inverse bind matrices for each bone of Name_array
  214. ########################################################
  215. elif (line.find("<Name_array") + 1):
  216. # line were the bone names are
  217. line = f.readline()
  218. # store bone names in temp_bones
  219. temp_bones = []
  220. temp_bones = line.split()
  221. # skip the lines until float_array appears
  222. while True:
  223. line = f.readline()
  224. if(line.find("<float_array") + 1):
  225. break
  226. ###################
  227. # float_array start
  228. ###################
  229. # read a 4x4 matrix, store it on temp_array_matrices
  230. # then create a matrix and append the matrix with the
  231. # respetive bone on controller_data[i]
  232. # to store matrices
  233. temp_matrices = []
  234. while True:
  235. # to store a matrix in the form of an array
  236. temp_array_matrix = []
  237. # will store the numbers as strings first on temp_array_matrix
  238. k = 0
  239. while (k < 4):
  240. line = f.readline()
  241. for number in line.split():
  242. temp_array_matrix.append(float(number))
  243. k = k + 1
  244. # created "a" so the next matrix assignation is more readable
  245. a = temp_array_matrix
  246. # build matrix
  247. temp_matrix = Matrix(( [a[0], a[1], a[2], a[3]],
  248. [a[4], a[5], a[6], a[7]],
  249. [a[8], a[9], a[10],a[11]],
  250. [a[12], a[13],a[14],a[15]]
  251. ))
  252. # append matrix to temp_matrices
  253. temp_matrices.append(temp_matrix)
  254. # after reading a matrix there is either
  255. # - a blank line (another matrix starts next)
  256. # - the end of float_array
  257. line = f.readline()
  258. if (line.find("</float_array>") + 1):
  259. # assign all bone names and its matrices to controller_data[i]
  260. for k in range(len(temp_bones)):
  261. controller_data[i].append(temp_bones[k])
  262. controller_data[i].append(temp_matrices[k])
  263. # end of the Name_array and float_array section
  264. break
  265. ####################################
  266. # end of library_controllers section
  267. ####################################
  268. elif (line.find("</library_controllers>") + 1):
  269. break
  270. ######################
  271. # controller data view
  272. ######################
  273. if (debug_messages):
  274. for mesh in controller_data:
  275. print("\nrow start")
  276. for element in mesh:
  277. print(element)
  278. print()
  279. #############################
  280. # library_visual_scenes start
  281. #############################
  282. line = f.readline()
  283. if not (line.find("<library_visual_scenes>") + 1):
  284. print("DAE file contains an incompatible skin section structure for this importer. Check the file and try again.")
  285. # end importer
  286. return {'FINISHED'}
  287. line = f.readline() # contains visual_scene start
  288. # the next 2 lines have the armature name and tranformation matrix
  289. # respectively, so I will store those separately from the bone's data
  290. line = f.readline()
  291. armature_name = name = re.search('name="(.+?)"', line).group(1)
  292. line = f.readline()
  293. armature_matrix_string = re.search('<matrix.*>(.*)</matrix>', line).group(1).split()
  294. # created "a" so the next matrix assignation is more readable
  295. a = [float(string) for string in armature_matrix_string]
  296. armature_matrix = Matrix(( [a[0], a[4], a[8], a[12]],
  297. [a[1], a[5], a[9], a[13]],
  298. [a[2], a[6], a[10], a[14]],
  299. [a[3], a[7], a[11], a[15]]
  300. ))
  301. ###########
  302. # bone data
  303. ###########
  304. # infinite loop to read this section
  305. # all bone names will be stored in bones_array (as they show on the DAE file)
  306. # all the matrices will be stored in rest_matrices (as they show on the DAE file)
  307. # in this way the bones_array[i] bone rest matrix will be rest_matrices[i]
  308. # to check tag start/end bone_tag_check will be used
  309. # when <node> tag is reached the bone name related to that tag will be stored
  310. # in bone_tag_check, then, if </node> tag is reached whatever bone is at the
  311. # end of the bone_tag_check gets banished (like a stack)
  312. # in this way I can get parent bones more easily for complex bone structures
  313. bones_array = []
  314. rest_matrices = []
  315. bone_tag_check = []
  316. # the following array will be used to "draw" bone parenting
  317. # each row will represent a bone and it will be filled with zeros from left to right
  318. # until a position (position defined by the bone name position in bones_array) in
  319. # which there will be a 1. In this way I can get a visual diagram to get the bone
  320. # parenting for if I need to rebuild it later in the program
  321. bone_parenting_diagram = [[]]
  322. i = -1 # position of the last element on bones_array and rest_matrices
  323. j = -1 # position of the last non_empty element on bone_tag_check
  324. # ^ initialized to -1 so the while loop from below is more convinient
  325. #########################################################
  326. # infinite loop section
  327. # exit condition is when j = -1 (bone_tag_check is empty)
  328. #########################################################
  329. while True:
  330. line = f.readline()
  331. ############
  332. # node start
  333. ############
  334. if (line.find("<node ") + 1):
  335. # new bone, new matrix, new element in bone_tag_check
  336. i = i + 1
  337. j = j + 1
  338. # bone name
  339. bone_name = re.search('name="(.+?)"', line).group(1)
  340. bones_array.append(bone_name)
  341. # bone tag check
  342. if (j >= len(bone_tag_check)):
  343. bone_tag_check.append(bone_name)
  344. else:
  345. bone_tag_check[j] = bone_name
  346. # bone parenting diagram
  347. if (i == 0):
  348. bone_parenting_diagram[0] = [1]
  349. else:
  350. bone_parenting_diagram.append([])
  351. for k in range(j):
  352. bone_parenting_diagram[i].append(0)
  353. bone_parenting_diagram[i].append(1)
  354. ##################
  355. # matrix from node
  356. ##################
  357. elif (line.find("<matrix ") + 1):
  358. # store contents of matrix tag in an array
  359. # transform array to float type then to a 4x4 matrix
  360. bone_matrix_string_array = re.search('<matrix.*>(.*)</matrix>', line).group(1).split()
  361. # created "a" so the next matrix assignation is more readable
  362. a = [float(string) for string in bone_matrix_string_array]
  363. bone_matrix = Matrix(([a[0], a[1], a[2], a[3]],
  364. [a[4], a[5], a[6], a[7]],
  365. [a[8], a[9], a[10], a[11]],
  366. [a[12], a[13], a[14], a[15]]
  367. ))
  368. rest_matrices.append(bone_matrix)
  369. ##########
  370. # node end
  371. ##########
  372. elif (line.find("</node>") + 1):
  373. # empty element bone_tag_check[j]
  374. # decrease j by 1
  375. bone_tag_check[j] = ""
  376. j = j - 1
  377. #####################################
  378. # what kind of DAE file I am reading?
  379. #####################################
  380. else:
  381. # if the collada file contains a different structure than the usual
  382. # for the armature section just end the importer and print text error text
  383. print("DAE file contains an incompatible armature section structure for this importer. Check the file and try again.")
  384. return {'FINISHED'}
  385. ###########
  386. # exit loop
  387. ###########
  388. if (j == -1):
  389. break
  390. ##############
  391. # post process
  392. # finalize filling with zeros the rows of the bone_parenting_diagram
  393. final_row_length = len(bone_tag_check)
  394. for row in bone_parenting_diagram:
  395. for l in range(final_row_length - len(row)):
  396. row.append(0)
  397. ####################
  398. # armature data view
  399. ####################
  400. if (debug_messages):
  401. print("Bone Names")
  402. print()
  403. print(bones_array)
  404. print()
  405. print("Rest Matrices")
  406. print()
  407. for rest_mat in rest_matrices:
  408. print(rest_mat)
  409. print()
  410. print("Bone tag check (empty = good)")
  411. print("number of columns must match the bone\nparenting diagram number of columns")
  412. print()
  413. print(bone_tag_check)
  414. print()
  415. print("Bone Parenting Diagram")
  416. print()
  417. for bone in bone_parenting_diagram:
  418. print(bone)
  419. print()
  420. ##########################
  421. # re-ordering data section
  422. ##########################
  423. # this section is to fill 2 big and important arrays
  424. # with all the data read from above
  425. # - bind_matrices will hold all bone bind matrices for each bone (from the skin read)
  426. # the inverse of the ones read to be specific. Will use the rest matrix from the
  427. # armature section of the Collada if the bone does not have an IBM defined
  428. # - final_rest_matrices will hold all bone rest matrices (armature section of
  429. # the collada) matrices will be calculated with calculate_rest_matrix()
  430. # function for each bone
  431. # the matrices will be in the ordered as the bone names on bone_names
  432. bind_matrices = []
  433. final_rest_matrices = []
  434. # will be used to tell if a bone had defined a bind matrix and a rest matrix
  435. # checking if a bone has a bind matrix is enought
  436. has_bind_rest = []
  437. # rest_matrices will be filled first and bind_matrices after
  438. #####################
  439. # final_rest_matrices
  440. for i in range(len(bones_array)):
  441. final_rest_matrices.append(calculate_rest_matrix(rest_matrices, i, bone_parenting_diagram))
  442. ###############
  443. # bind_matrices
  444. for i in range(len(bones_array)):
  445. # loop through each element of the controller_data array
  446. # to find all the bone's inverse bind matrices available
  447. bone_found = False
  448. for row in range(len(controller_data)):
  449. for col in range(len(controller_data[row])):
  450. if (bones_array[i] == controller_data[row][col]):
  451. bind_matrices.append(controller_data[row][col + 1].inverted())
  452. has_bind_rest.append(True)
  453. bone_found = True
  454. break
  455. if (bone_found):
  456. break
  457. # if a bone has not been found
  458. # assign final rest matrix of said bone from final_rest_matrices
  459. if not (bone_found):
  460. bind_matrices.append(final_rest_matrices[i])
  461. has_bind_rest.append(False)
  462. ##############################################
  463. # bind, rest and final rest matrices data view
  464. ##############################################
  465. if (debug_messages):
  466. print("Bone Names")
  467. print()
  468. print(bones_array)
  469. print("\nBind Matrices\n")
  470. for bind_mat in bind_matrices:
  471. print(bind_mat)
  472. print("\nRest Matrices\n")
  473. for rest_mat in rest_matrices:
  474. print(rest_mat)
  475. print("\nFinal Rest Matrices\n")
  476. for final_rest_mat in final_rest_matrices:
  477. print(final_rest_mat)
  478. print("\nBone has both Matrices defined in Collada")
  479. print()
  480. print(bones_array)
  481. print(has_bind_rest)
  482. print()
  483. ##########################
  484. # Rebuild armature section
  485. ##########################
  486. # for this I will create a different armature object with all the bones
  487. # and then link all the meshes from the imported model into this armature
  488. # at the end the original imported armature will be deleted.
  489. print("Attempting re-build...")
  490. # change to object mode
  491. bpy.ops.object.mode_set(mode='OBJECT')
  492. # deselect everything
  493. bpy.ops.object.select_all(action='DESELECT')
  494. scene.objects.active = None
  495. # store old armature object
  496. armature_old = armature
  497. # create new armature
  498. bpy.ops.object.armature_add(location=(0.0, 0.0, 0.0))
  499. armature = scene.objects.active
  500. # change its name
  501. armature.name = armature_old.name + "_new"
  502. # apply transformation matrix of armature old into new armature
  503. armature.matrix_world = armature_matrix
  504. # enter edit mode
  505. bpy.ops.object.mode_set(mode='EDIT')
  506. # create, apply transformation matrix and parent all bones in a
  507. # column of the bone_parenting_diagram array (those at the "same parenting level")
  508. # elements in bone_parenting_diagram are bone_parenting_diagram[i][j]
  509. for j in range(len(bone_parenting_diagram[0])):
  510. for i in range(len(bone_parenting_diagram)):
  511. # first bone (position [0][0]) is already created
  512. # I assume there is one bone that is the main bone
  513. if (i == 0):
  514. armature.data.edit_bones[0].name = bones_array[0]
  515. armature.data.edit_bones[0].matrix = bind_matrices[0]
  516. continue
  517. ##################################
  518. # for the columns next to column 0
  519. ##################################
  520. # current position does not have a bone
  521. if (bone_parenting_diagram[i][j] == 0):
  522. continue
  523. # bone_parenting_diagram[i][j] == 1 returns True
  524. # bone found, create it and assign name
  525. bpy.ops.armature.bone_primitive_add(name = bones_array[i])
  526. # find parent bone
  527. # read the rows above the bone row to find its parent
  528. # it is located at the column next to it (one column left)
  529. # second argument in range() is -1 as I need a loop on k = 0
  530. for k in range(i, -1, -1):
  531. # make k 1 less than i (row adjustment)
  532. if (k >= i):
  533. continue
  534. # start reading the column next to bone column (left column)
  535. # Find the nearest 1 on said column (parent bone)
  536. if (bone_parenting_diagram[k][j - 1] == 0):
  537. continue
  538. # bone_parenting_diagram[k][j - 1] == 1 returns True --> parent bone found
  539. # assign bone parent
  540. armature.data.edit_bones[bones_array[i]].parent = armature.data.edit_bones[bones_array[k]]
  541. # and more length to the bone (visual stuff, non-important)
  542. armature.data.edit_bones[bones_array[i]].length = 10
  543. # apply transformation matrix (bind matrix)
  544. armature.data.edit_bones[bones_array[i]].matrix = bind_matrices[i]
  545. break
  546. # leave edit mode
  547. bpy.ops.object.mode_set(mode='OBJECT')
  548. # assign bone custom properties only to bones that have
  549. # both bind and rest matrices defined in the collada file
  550. for i in range(len(bones_array)):
  551. # only check bones that have a bind matrix
  552. if (has_bind_rest[i]):
  553. # Note: custom property matrices bind_mat and rest_mat are column written
  554. # create bind_mat custom property
  555. # Note: by using Blender's FloatVectorProperty I get
  556. # a custom property that can't be edited through GUI
  557. # (API Defined, but it works well on the bind/rest_mat export)
  558. # can be edited if exporting the model as DAE and then
  559. # re-importing after closing and re-openning Blender
  560. # create rest_mat custom property (Blender's FloatVectorProperty)
  561. # bpy.types.Bone.rest_mat = bpy.props.FloatVectorProperty(
  562. # name="rest_mat",
  563. # size=16,
  564. # subtype="MATRIX"
  565. # )
  566. # bind matrix for bone
  567. a = bind_matrices[i].transposed()
  568. temp_array = [a[0][0], a[0][1], a[0][2], a[0][3],
  569. a[1][0], a[1][1], a[1][2], a[1][3],
  570. a[2][0], a[2][1], a[2][2], a[2][3],
  571. a[3][0], a[3][1], a[3][2], a[3][3]]
  572. # convert temp_array to the right type with numpy
  573. temp_array = array(temp_array, dtype = 'f')
  574. armature.data.bones[bones_array[i]]["bind_mat"] = temp_array
  575. # rest matrix for bone
  576. a = rest_matrices[i].transposed()
  577. temp_array = (a[0][0], a[0][1], a[0][2], a[0][3],
  578. a[1][0], a[1][1], a[1][2], a[1][3],
  579. a[2][0], a[2][1], a[2][2], a[2][3],
  580. a[3][0], a[3][1], a[3][2], a[3][3])
  581. # convert temp_array to the right type with numpy
  582. temp_array = array(temp_array, dtype = 'f')
  583. armature.data.bones[bones_array[i]]["rest_mat"] = temp_array
  584. # Note: ^ if I don't convert temp_array to the right type and
  585. # just assign it to the custom property, the exported matrices
  586. # will contain weird data (no clue why that happens)
  587. # armature has finished being built
  588. print("Re-build done!")
  589. # assign meshes from old armature into new armature
  590. # also reconect mesh's armature modifiers to the new armature
  591. for child_mesh in armature_old.children:
  592. child_mesh.parent = armature
  593. child_mesh.modifiers["Armature"].object = armature
  594. # rename new armature with old armature's name
  595. armature.name = armature_old.name
  596. ###############
  597. # final section
  598. ###############
  599. # change back to object mode
  600. bpy.ops.object.mode_set(mode='OBJECT')
  601. # deselect everything
  602. bpy.ops.object.select_all(action='DESELECT')
  603. scene.objects.active = None
  604. # select old armature
  605. armature_old.select = True
  606. scene.objects.active = armature_old
  607. # delete old armature
  608. # it has been an honor :saluting_face:
  609. bpy.ops.object.delete(use_global = False)
  610. # select new armature again
  611. armature.select = True
  612. scene.objects.active = armature
  613. ########################
  614. # apply pose to armature
  615. ########################
  616. print("Generating Armature poses...")
  617. # already have done the bind pose from the steps above
  618. # so end the importer if the bind pose is the pose
  619. # is the one wanted to be applied to the bones of the armature
  620. if (show_armature_pose == "OPT_A"):
  621. print("Bind Pose done!")
  622. # ^ if that is not the case then I have to build again the
  623. # armature for the rest pose and the ignore rest pose
  624. # but this time I have to deform manually the meshes so they are
  625. # correct for the animation imports
  626. # Note: for some reason Mario's mesh has a deformation
  627. # in the Hip zone after these vertex transformation
  628. # rest pose/ignore rest pose build
  629. else:
  630. # change to edit mode
  631. bpy.ops.object.mode_set(mode='EDIT')
  632. # build the rest pose/ignore rest pose on the bones
  633. # of the armature (edit_bones, does not matter applying order)
  634. # use final_rest_matrices for rest pose
  635. # use rot_90_deg_mat for ignore rest pose
  636. for i in range(len(armature.data.edit_bones)):
  637. #
  638. # get edit_bone
  639. edit_bone = armature.data.edit_bones[i]
  640. if (show_armature_pose == "OPT_B"):
  641. # rotate matrix (use )
  642. matrix = final_rest_matrices[i]
  643. elif (show_armature_pose == "OPT_C"):
  644. # rotate matrix (all of the bones on ignore
  645. # rest pose are in the same place)
  646. matrix = Matrix(( [1, 0, 0, 0],
  647. [0, 1, 0, 0],
  648. [0, 0, 1, 0],
  649. [0, 0, 0, 1]
  650. ))
  651. # assign matrix
  652. edit_bone.matrix = matrix
  653. #
  654. # deform mesh according to the rest pose
  655. # of the armature a vertex at a time
  656. for mesh in armature.children:
  657. #
  658. # get all vertex groups on the mesh
  659. # its index and name
  660. mesh_vertex_groups = [[], []]
  661. for vertex_group in mesh.vertex_groups:
  662. mesh_vertex_groups[0].append(vertex_group.index)
  663. mesh_vertex_groups[1].append(vertex_group.name)
  664. for vertex in mesh.data.vertices:
  665. #
  666. # get vertex in armature's reference system
  667. # and as a vector to be able to be multiplied by matrices
  668. vertex_vector = Vector(([ vertex.co[0],
  669. vertex.co[1],
  670. vertex.co[2]]
  671. ))
  672. # final vertex position variable
  673. final_vertex_vector = Vector(([0, 0, 0]))
  674. # get the sum of the weights of this vertex on all
  675. # vertex groups, the individual weights on each group
  676. # and the vertex group index
  677. # leave extra row on vertex_vertex_groups to store
  678. # the vertex group names for later
  679. weight_sum = 0
  680. vertex_vertex_groups = [[], [], []]
  681. for vertex_group in vertex.groups:
  682. vertex_vertex_groups[0].append(vertex_group.group)
  683. vertex_vertex_groups[1].append(vertex_group.weight)
  684. weight_sum = weight_sum + vertex_group.weight
  685. # iterate through each bone this vertex is linked to
  686. # and apply the "skinning formula" AFAIK
  687. # this formula is as follows:
  688. # for a bone with a bind matrix (the matrix that describes the bone position
  689. # without deforming the mesh, respect to armature) and a pose matrix (the
  690. # position the bone has when the mesh deforms, with respect to armature)
  691. # that influences a vertex with armature coordinates v and relative
  692. # weight w for said bone the final position of the vertex for said
  693. # bone movement is
  694. # v_final_for_bone = inverse_bind_mat * bone_pose_matrix * v * w
  695. # were w is the fraction of the weight of the vertex on the vertex group
  696. # that is linked to the bone divided by the sum of all the weights the vertex
  697. # has on all vertex groups it is assigned to
  698. # v_final_for_all_bones (the final trasnformation of the vertex that is
  699. # got by all the bones that have influence on said vertex) is found by adding
  700. # all individual v_final_for_bone vectors (one per bone, all the bones that
  701. # influence the vertex)
  702. # find the bone names that affect this vertex
  703. # and add those to the 3rd row of vertex_vertex_groups
  704. # i will be used to go throught a mesh_vertex_groups row
  705. # j will ve used to go through a vertex_vertex_groups row
  706. i = 0
  707. j = 0
  708. while (j != len(vertex_vertex_groups[0])):
  709. #
  710. # when vertex group is located in mesh_vertex_groups
  711. # append vertex group name in vertex_vertex_groups[2] (bone name)
  712. # start reading mesh_vertex_groups again to search for the next bone
  713. if (mesh_vertex_groups[0][i] == vertex_vertex_groups[0][j]):
  714. vertex_vertex_groups[2].append(mesh_vertex_groups[1][i])
  715. i = 0
  716. j = j + 1
  717. i = i + 1
  718. #
  719. # iterate through each bone that affects this vertex
  720. for i in range(len(vertex_vertex_groups[0])):
  721. #
  722. # get bone and its index
  723. bone = armature.data.bones[vertex_vertex_groups[2][i]]
  724. bone_index = 0
  725. for j in range(len(armature.data.bones)):
  726. if (vertex_vertex_groups[2][i] == armature.data.bones[j].name):
  727. bone_index = j
  728. break
  729. # do the proper multiplication of the influence of
  730. # each bone on the vertex and sum the resulting vectors
  731. # inverse_bind_mat is got from bind_matrices
  732. # bone_pose_matrix will be the final_rest_matrices (for rest pose)
  733. # or the identity matrix (for ignore rest pose)
  734. bind_matrix = bind_matrices[bone_index]
  735. inv_bind_matrix = bind_matrix.inverted()
  736. if (show_armature_pose == "OPT_B"):
  737. bone_pose_matrix = final_rest_matrices[bone_index]
  738. elif (show_armature_pose == "OPT_C"):
  739. bone_pose_matrix = Matrix(( [1, 0, 0, 0],
  740. [0, 1, 0, 0],
  741. [0, 0, 1, 0],
  742. [0, 0, 0, 1]
  743. ))
  744. final_vertex_vector = final_vertex_vector + (inv_bind_matrix * bone_pose_matrix * vertex_vector * (vertex_vertex_groups[1][i] / weight_sum))
  745. #
  746. # apply final_vertex_vector to vertex
  747. # in armature reference system
  748. vertex.co = final_vertex_vector
  749. #
  750. #
  751. # scale down and rotate the armature
  752. armature.scale = (0.01, 0.01, 0.01)
  753. armature.rotation_euler[0] = math.radians(90)
  754. # apply scale to armature
  755. bpy.ops.object.mode_set(mode='OBJECT')
  756. bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
  757. # apply scale to all meshes on armature
  758. for mesh in armature.children:
  759. mesh.select = True
  760. scene.objects.active = mesh
  761. bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
  762. mesh.select = False
  763. scene.objects.active = None
  764. # select armature again
  765. armature.select = True
  766. scene.objects.active = armature
  767. if (show_armature_pose == "OPT_B"):
  768. print("Rest Pose done!")
  769. elif (show_armature_pose == "OPT_C"):
  770. print("Ignore Rest Pose done!")
  771. # end importer
  772. return {'FINISHED'}
  773. #################################################
  774. # Stuff down is for the menu appending
  775. # of the importer to work plus some setting stuff
  776. # comes from a Blender importer template
  777. #################################################
  778. # ExportHelper is a helper class, defines filename and
  779. # invoke() function which calls the file selector.
  780. from bpy_extras.io_utils import ExportHelper
  781. from bpy.props import StringProperty, BoolProperty, EnumProperty
  782. from bpy.types import Operator
  783. class Import_BMD_BDL_Collada(Operator, ExportHelper):
  784. """Import a Collada file from SuperBMD (SuperBMD only)"""
  785. bl_idname = "import_scene.collada_bmd_bdl"
  786. bl_label = "Import Collada (from SuperBMD)"
  787. # ExportHelper mixin class uses this
  788. filename_ext = ".dae"
  789. filter_glob = StringProperty(
  790. default="*.dae",
  791. options={'HIDDEN'},
  792. maxlen=255,
  793. )
  794. # option to show messages on terminal of the bone importing process
  795. debug_messages = BoolProperty( name = "Display debug messages",
  796. description = "Display armature data as it is read",
  797. default = False,
  798. )
  799. # option to allow users define the pose of the armature that is shown on Blender
  800. show_armature_pose = EnumProperty(
  801. name="Armature Pose",
  802. description="Pose to load on Blender for the imported armature",
  803. items=( ('OPT_A', "Bind Pose", "Show Armature on Bind Pose"),
  804. ('OPT_B', "Rest Pose", "Show Armature on Rest Pose"),
  805. ('OPT_C', "Ignore Rest Pose", "Show Armature as on SMG when an empty BCK animation is applied to it. WARNNING: for complex armature models bone deformations on mesh wont be accurate (it is unknown why)")),
  806. default='OPT_B',
  807. )
  808. def execute(self, context):
  809. return read_bmd_bdl_collada(context, self.filepath, self.debug_messages, self.show_armature_pose)
  810. # Only needed if you want to add into a dynamic menu
  811. def menu_import_bmd_bdl_collada(self, context):
  812. self.layout.operator(Import_BMD_BDL_Collada.bl_idname, text="Collada (from SuperBMD) (.dae)")
  813. bpy.utils.register_class(Import_BMD_BDL_Collada)
  814. bpy.types.INFO_MT_file_import.append(menu_import_bmd_bdl_collada)
  815. # test call
  816. bpy.ops.import_scene.collada_bmd_bdl('INVOKE_DEFAULT')