def get_bind_data(self, b_armature):
     """Get the required bind data of an armature. Used by standalone KF import and export. """
     self.bind_data = {}
     if b_armature:
         for b_bone in b_armature.data.bones:
             n_bind_scale, n_bind_rot, n_bind_trans = math.decompose_srt(math.get_object_bind(b_bone))
             self.bind_data[b_bone.name] = (n_bind_rot.inverted(), n_bind_trans)
Пример #2
0
def get_bind_data(b_armature):
    """Get the required bind data of an armature. Used by standalone KF import and export. """
    if b_armature:
        bind_data = {}
        for b_bone in b_armature.data.bones:
            n_bone_bind_scale, n_bone_bind_rot, n_bone_bind_trans = math.decompose_srt(
                math.get_bind_matrix(b_bone))
            bind_data[b_bone.name] = (n_bone_bind_scale,
                                      n_bone_bind_rot.inverted(),
                                      n_bone_bind_trans)
        return bind_data
Пример #3
0
 def import_transforms(self, n_block, b_obj, bone_name=None):
     """Loads an animation attached to a nif block."""
     # find keyframe controller
     n_kfc = math.find_controller(n_block, NifFormat.NiKeyframeController)
     # try again
     if not n_kfc:
         n_kfc = math.find_controller(n_block,
                                      NifFormat.NiTransformController)
     if n_kfc:
         # skeletal animation
         if bone_name:
             bone_bm = math.import_matrix(n_block)  # base pose
             n_bone_bind_scale, n_bone_bind_rot, n_bone_bind_trans = math.decompose_srt(
                 bone_bm)
             self.import_keyframe_controller(n_kfc, b_obj, bone_name,
                                             n_bone_bind_scale,
                                             n_bone_bind_rot.inverted(),
                                             n_bone_bind_trans)
         # object-level animation
         else:
             self.create_action(b_obj, b_obj.name + "-Anim")
             self.import_keyframe_controller(n_kfc, b_obj)
Пример #4
0
    def export_transforms(self, parent_block, b_obj, b_action, bone=None):
        """
        If bone == None, object level animation is exported.
        If a bone is given, skeletal animation is exported.
        """

        # b_action may be None, then nothing is done.
        if not b_action:
            return

        # blender object must exist
        assert b_obj
        # if a bone is given, b_obj must be an armature
        if bone:
            assert type(b_obj.data) == bpy.types.Armature

        # just for more detailed error reporting later on
        bonestr = ""

        # skeletal animation - with bone correction & coordinate corrections
        if bone and bone.name in b_action.groups:
            # get bind matrix for bone or object
            bind_matrix = math.get_object_bind(bone)
            exp_fcurves = b_action.groups[bone.name].channels
            # just for more detailed error reporting later on
            bonestr = " in bone " + bone.name
            target_name = block_store.get_full_name(bone)
            priority = bone.niftools.priority

        # object level animation - no coordinate corrections
        elif not bone:

            # raise error on any objects parented to bones
            if b_obj.parent and b_obj.parent_type == "BONE":
                raise io_scene_niftools.utils.logging.NifError(
                    "{} is parented to a bone AND has animations. The nif format does not support this!"
                    .format(b_obj.name))

            target_name = block_store.get_full_name(b_obj)
            priority = 0

            # we have either a root object (Scene Root), in which case we take the coordinates without modification
            # or a generic object parented to an empty = node
            # objects may have an offset from their parent that is not apparent in the user input (ie. UI values and keyframes)
            # we want to export matrix_local, and the keyframes are in matrix_basis, so do:
            # matrix_local = matrix_parent_inverse * matrix_basis
            bind_matrix = b_obj.matrix_parent_inverse
            exp_fcurves = [
                fcu for fcu in b_action.fcurves
                if fcu.data_path in ("rotation_quaternion", "rotation_euler",
                                     "location", "scale")
            ]

        else:
            # bone isn't keyframed in this action, nothing to do here
            return

        # decompose the bind matrix
        bind_scale, bind_rot, bind_trans = math.decompose_srt(bind_matrix)
        n_kfc, n_kfi = self.create_controller(parent_block, target_name,
                                              priority)

        # fill in the non-trivial values
        start_frame, stop_frame = b_action.frame_range
        self.set_flags_and_timing(n_kfc, exp_fcurves, start_frame, stop_frame)

        # get the desired fcurves for each data type from exp_fcurves
        quaternions = [
            fcu for fcu in exp_fcurves if fcu.data_path.endswith("quaternion")
        ]
        translations = [
            fcu for fcu in exp_fcurves if fcu.data_path.endswith("location")
        ]
        eulers = [
            fcu for fcu in exp_fcurves if fcu.data_path.endswith("euler")
        ]
        scales = [
            fcu for fcu in exp_fcurves if fcu.data_path.endswith("scale")
        ]

        # ensure that those groups that are present have all their fcurves
        for fcus, num_fcus in ((quaternions, 4), (eulers, 3),
                               (translations, 3), (scales, 3)):
            if fcus and len(fcus) != num_fcus:
                raise io_scene_niftools.utils.logging.NifError(
                    "Incomplete key set {} for action {}. Ensure that if a bone is keyframed for a property, all channels are keyframed."
                    .format(bonestr, b_action.name))

        # go over all fcurves collected above and transform and store all their keys
        quat_curve = []
        euler_curve = []
        trans_curve = []
        scale_curve = []
        for frame, quat in self.iter_frame_key(quaternions,
                                               mathutils.Quaternion):
            quat = math.export_keymat(bind_rot,
                                      quat.to_matrix().to_4x4(),
                                      bone).to_quaternion()
            quat_curve.append((frame, quat))

        for frame, euler in self.iter_frame_key(eulers, mathutils.Euler):
            keymat = math.export_keymat(bind_rot,
                                        euler.to_matrix().to_4x4(), bone)
            euler = keymat.to_euler("XYZ", euler)
            euler_curve.append((frame, euler))

        for frame, trans in self.iter_frame_key(translations,
                                                mathutils.Vector):
            keymat = math.export_keymat(bind_rot,
                                        mathutils.Matrix.Translation(trans),
                                        bone)
            trans = keymat.to_translation() + bind_trans
            trans_curve.append((frame, trans))

        for frame, scale in self.iter_frame_key(scales, mathutils.Vector):
            # just use the first scale curve and assume even scale over all curves
            scale_curve.append((frame, scale[0]))

        if n_kfi:
            if max(
                    len(c) for c in (quat_curve, euler_curve, trans_curve,
                                     scale_curve)) > 1:
                # number of frames is > 1, so add transform data
                n_kfd = block_store.create_block("NiTransformData",
                                                 exp_fcurves)
                n_kfi.data = n_kfd
            else:
                # only add data if number of keys is > 1
                # (see importer comments with import_kf_root: a single frame
                # keyframe denotes an interpolator without further data)
                # insufficient keys, so set the data and we're done!
                if trans_curve:
                    trans = trans_curve[0][1]
                    n_kfi.translation.x, n_kfi.translation.y, n_kfi.translation.z = trans

                if quat_curve:
                    quat = quat_curve[0][1]
                elif euler_curve:
                    quat = euler_curve[0][1].to_quaternion()

                if quat_curve or euler_curve:
                    n_kfi.rotation.x = quat.x
                    n_kfi.rotation.y = quat.y
                    n_kfi.rotation.z = quat.z
                    n_kfi.rotation.w = quat.w
                # ignore scale for now...
                n_kfi.scale = 1.0
                # no need to add any keys, done
                return

        else:
            # add the keyframe data
            n_kfd = block_store.create_block("NiKeyframeData", exp_fcurves)
            n_kfc.data = n_kfd

        # TODO [animation] support other interpolation modes, get interpolation from blender?
        #                  probably requires additional data like tangents and stuff

        # finally we can export the data calculated above
        if euler_curve:
            n_kfd.rotation_type = NifFormat.KeyType.XYZ_ROTATION_KEY
            n_kfd.num_rotation_keys = 1  # *NOT* len(frames) this crashes the engine!
            for i, coord in enumerate(n_kfd.xyz_rotations):
                coord.num_keys = len(euler_curve)
                coord.interpolation = NifFormat.KeyType.LINEAR_KEY
                coord.keys.update_size()
                for key, (frame, euler) in zip(coord.keys, euler_curve):
                    key.time = frame / self.fps
                    key.value = euler[i]
        elif quat_curve:
            n_kfd.rotation_type = NifFormat.KeyType.QUADRATIC_KEY
            n_kfd.num_rotation_keys = len(quat_curve)
            n_kfd.quaternion_keys.update_size()
            for key, (frame, quat) in zip(n_kfd.quaternion_keys, quat_curve):
                key.time = frame / self.fps
                key.value.w = quat.w
                key.value.x = quat.x
                key.value.y = quat.y
                key.value.z = quat.z

        n_kfd.translations.interpolation = NifFormat.KeyType.LINEAR_KEY
        n_kfd.translations.num_keys = len(trans_curve)
        n_kfd.translations.keys.update_size()
        for key, (frame, trans) in zip(n_kfd.translations.keys, trans_curve):
            key.time = frame / self.fps
            key.value.x, key.value.y, key.value.z = trans

        n_kfd.scales.interpolation = NifFormat.KeyType.LINEAR_KEY
        n_kfd.scales.num_keys = len(scale_curve)
        n_kfd.scales.keys.update_size()
        for key, (frame, scale) in zip(n_kfd.scales.keys, scale_curve):
            key.time = frame / self.fps
            key.value = scale