From 721ad3bd6f2bd1e5f73e97d1559a6263fe52be28 Mon Sep 17 00:00:00 2001 From: Candoran2 <45334438+Candoran2@users.noreply.github.com> Date: Tue, 2 Nov 2021 20:31:16 +0100 Subject: [PATCH] Added export of Blender pose. --- .../modules/nif_export/armature/__init__.py | 18 +++++++++---- .../nif_export/geometry/mesh/__init__.py | 27 ++++++++++++------- io_scene_niftools/utils/math.py | 4 +++ 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/io_scene_niftools/modules/nif_export/armature/__init__.py b/io_scene_niftools/modules/nif_export/armature/__init__.py index 7c8311015..c847ba8f9 100644 --- a/io_scene_niftools/modules/nif_export/armature/__init__.py +++ b/io_scene_niftools/modules/nif_export/armature/__init__.py @@ -43,6 +43,7 @@ from io_scene_niftools.utils import math from io_scene_niftools.utils.singleton import NifOp from io_scene_niftools.utils.logging import NifLog +from pyffi.formats.nif import NifFormat def get_bind_data(b_armature): @@ -68,11 +69,14 @@ def export_bones(self, b_obj, n_root_node): self.b_action = self.transform_anim.get_active_action(b_obj) # the armature b_obj was already exported as a NiNode ("Scene Root") n_root_node # export the bones as NiNodes, starting from root bones + old_position = b_obj.data.pose_position + b_obj.data.pose_position = 'POSE' for b_bone in b_obj.data.bones.values(): if not b_bone.parent: - self.export_bone(b_obj, b_bone, n_root_node) + self.export_bone(b_obj, b_bone, n_root_node, n_root_node) + b_obj.data.pose_position = old_position - def export_bone(self, b_obj, b_bone, n_parent_node): + def export_bone(self, b_obj, b_bone, n_parent_node, n_root_node): """Exports a bone and all of its children.""" # create a new nif block for this b_bone n_node = types.create_ninode(b_bone) @@ -81,14 +85,18 @@ def export_bone(self, b_obj, b_bone, n_parent_node): n_parent_node.add_child(n_node) self.export_bone_flags(b_bone, n_node) - # rest pose - math.set_object_matrix(b_bone, n_node) + # set the pose on the nodes + nif_matrix = NifFormat.Matrix44() + nif_matrix.set_rows(*math.blender_bind_to_nif_bind(b_obj.pose.bones[b_bone.name].matrix).transposed()) + # make the transform relative to the parent, rather than the armature + nif_matrix *= n_parent_node.get_transform(n_root_node).get_inverse(fast=False) + n_node.set_transform(nif_matrix) # per-bone animation self.transform_anim.export_transforms(n_node, b_obj, self.b_action, b_bone) # continue down the bone tree for b_child in b_bone.children: - self.export_bone(b_obj, b_child, n_node) + self.export_bone(b_obj, b_child, n_node, n_root_node) def export_bone_flags(self, b_bone, n_node): """Exports or sets the flags according to the custom data in b_bone or the game version if none was set""" diff --git a/io_scene_niftools/modules/nif_export/geometry/mesh/__init__.py b/io_scene_niftools/modules/nif_export/geometry/mesh/__init__.py index 606786bd2..6e8956ca6 100644 --- a/io_scene_niftools/modules/nif_export/geometry/mesh/__init__.py +++ b/io_scene_niftools/modules/nif_export/geometry/mesh/__init__.py @@ -453,7 +453,7 @@ def export_tri_shapes(self, b_obj, n_parent, n_root, trishape_name=None): # update bind position skinning data # trishape.update_bind_position() # override pyffi trishape.update_bind_position with custom one that is relative to the nif root - self.update_bind_position(trishape, n_root) + self.update_bind_position(trishape, n_root, b_obj_armature) # calculate center and radius for each skin bone data block trishape.update_skin_center_radius() @@ -501,12 +501,12 @@ def export_tri_shapes(self, b_obj, n_parent, n_root, trishape_name=None): self.morph_anim.export_morph(b_mesh, trishape, vertex_map) return trishape - def update_bind_position(self, n_geom, n_root): - """Make current position of the bones the bind position for this geometry. + def update_bind_position(self, n_geom, n_root, b_obj_armature): + """Transfer the Blender bind position to the nif bind position. Sets the NiSkinData overall transform to the inverse of the geometry transform relative to the skeleton root, and sets the NiSkinData of each bone to - the geometry transform relative to the skeleton root times the inverse of the bone - transform relative to the skeleton root.""" + the inverse of the transpose of the bone transform relative to the skeleton root, corrected + for the overall transform.""" if not n_geom.is_skin(): return @@ -516,23 +516,32 @@ def update_bind_position(self, n_geom, n_root): skindata = skininst.data skelroot = skininst.skeleton_root - # calculate overall offset - geomtransform = n_geom.get_transform(skelroot) - skindata.set_transform(geomtransform.get_inverse()) + # calculate overall offset (including the skeleton root transform) and use its inverse + geomtransform = (n_geom.get_transform(skelroot) * skelroot.get_transform()).get_inverse(fast=False) + skindata.set_transform(geomtransform) # for some nifs, somehow n_root is not set properly?! if not n_root: NifLog.warn(f"n_root was not set, bug") n_root = skelroot + old_position = b_obj_armature.data.pose_position + b_obj_armature.data.pose_position = 'POSE' + # calculate bone offsets for i, bone in enumerate(skininst.bones): + bone_name = block_store.block_to_obj[bone].name + pose_bone = b_obj_armature.pose.bones[bone_name] + n_bind = NifFormat.Matrix44() + n_bind.set_rows(*math.blender_bind_to_nif_bind(pose_bone.matrix).transposed()) # todo [armature] figure out the correct transform that works universally # inverse skin bind in nif armature space, relative to root / geom?? - skindata.bone_list[i].set_transform(geomtransform * bone.get_transform(n_root).get_inverse()) + skindata.bone_list[i].set_transform((n_bind * geomtransform).get_inverse(fast=False)) # this seems to be correct for skyrim heads, but breaks stuff like ZT2 elephant # skindata.bone_list[i].set_transform(bone.get_transform(n_root).get_inverse()) + b_obj_armature.data.pose_position = old_position + def get_bone_block(self, b_bone): """For a blender bone, return the corresponding nif node from the blocks that have already been exported""" for n_block, b_obj in block_store.block_to_obj.items(): diff --git a/io_scene_niftools/utils/math.py b/io_scene_niftools/utils/math.py index fa2d3344f..7194c106c 100644 --- a/io_scene_niftools/utils/math.py +++ b/io_scene_niftools/utils/math.py @@ -89,6 +89,10 @@ def get_bind_matrix(bone): return bind +def blender_bind_to_nif_bind(blender_armature_space_matrix): + return blender_armature_space_matrix @ correction + + def nif_bind_to_blender_bind(nif_armature_space_matrix): return nif_armature_space_matrix @ correction_inv