def import_material_uv_controller(self, b_material, n_geom):
        """Import UV controller data."""
        # search for the block
        n_ctrl = nif_utils.find_controller(n_geom, NifFormat.NiUVController)
        if not (n_ctrl and n_ctrl.data):
            return
        NifLog.info("Importing UV controller")

        b_mat_action = self.nif_import.animationhelper.create_action(
            b_material, "MaterialAction")

        dtypes = ("offset", 0), ("offset", 1), ("scale", 0), ("scale", 1)
        for n_uvgroup, (data_path, array_ind) in zip(n_ctrl.data.uv_groups,
                                                     dtypes):
            if n_uvgroup.keys:
                interp = self.nif_import.animationhelper.get_b_interp_from_n_interp(
                    n_uvgroup.interpolation)
                #in blender, UV offset is stored per texture slot
                #so we have to repeat the import for each used tex slot
                for i, texture_slot in enumerate(b_material.texture_slots):
                    if texture_slot:
                        fcurves = create_fcurves(
                            b_mat_action,
                            "texture_slots[" + str(i) + "]." + data_path,
                            (array_ind, ))
                        for key in n_uvgroup.keys:
                            if "offset" in data_path:
                                self.nif_import.animationhelper.add_key(
                                    fcurves, key.time, (-key.value, ), interp)
                            else:
                                self.nif_import.animationhelper.add_key(
                                    fcurves, key.time, (key.value, ), interp)
                        self.nif_import.animationhelper.set_extrapolation(
                            n_ctrl.flags, fcurves)
    def import_bone_animation(self, n_block, b_armature_obj, bone_name):
        """
        Imports an animation contained in the NIF itself.
        """
        if NifOp.props.animation:
            NifLog.debug('Importing animation for bone %s'.format(bone_name))

            bone_bm = nif_utils.import_matrix(n_block)  # base pose
            niBone_bind_scale, niBone_bind_rot, niBone_bind_trans = nif_utils.decompose_srt(
                bone_bm)
            niBone_bind_rot_inv = niBone_bind_rot.to_4x4().inverted()
            kfc = nif_utils.find_controller(n_block,
                                            NifFormat.NiKeyframeController)

            self.import_keyframe_controller(kfc, b_armature_obj, bone_name,
                                            niBone_bind_scale,
                                            niBone_bind_rot_inv,
                                            niBone_bind_trans)
    def import_object_vis_controller(self, n_node, b_obj):
        """Import vis controller for blender object."""

        n_vis_ctrl = nif_utils.find_controller(n_node,
                                               NifFormat.NiVisController)
        if not (n_vis_ctrl and n_vis_ctrl.data):
            return
        NifLog.info("Importing vis controller")

        b_obj_action = self.nif_import.animationhelper.create_action(
            b_obj, b_obj.name + "-Anim")

        fcurves = create_fcurves(b_obj_action, "hide", (0, ))
        for key in n_vis_ctrl.data.keys:
            self.nif_import.animationhelper.add_key(fcurves, key.time,
                                                    (key.value, ), "CONSTANT")
        #get extrapolation from flags and set it to fcurves
        self.nif_import.animationhelper.set_extrapolation(
            n_vis_ctrl.flags, fcurves)
    def import_material_alpha_controller(self, b_material, n_geom):
        # find alpha controller
        n_matprop = nif_utils.find_property(n_geom,
                                            NifFormat.NiMaterialProperty)
        if not n_matprop:
            return
        n_alphactrl = nif_utils.find_controller(n_matprop,
                                                NifFormat.NiAlphaController)
        if not (n_alphactrl and n_alphactrl.data):
            return
        NifLog.info("Importing alpha controller")

        b_mat_action = self.nif_import.animationhelper.create_action(
            b_material, "MaterialAction")
        fcurves = create_fcurves(b_mat_action, "alpha", (0, ))
        interp = self.nif_import.animationhelper.get_b_interp_from_n_interp(
            n_alphactrl.data.data.interpolation)
        self.nif_import.animationhelper.set_extrapolation(
            n_alphactrl.flags, fcurves)
        for key in n_alphactrl.data.data.keys:
            self.nif_import.animationhelper.add_key(fcurves, key.time,
                                                    (key.value, ), interp)
    def import_mesh_controllers(self, n_node, b_obj):
        """Import mesh controller for blender object."""

        morphCtrl = nif_utils.find_controller(
            n_node, NifFormat.NiGeomMorpherController)
        if morphCtrl:
            b_mesh = b_obj.data
            morphData = morphCtrl.data
            if morphData.num_morphs:
                fps = bpy.context.scene.render.fps
                # insert base key at frame 1, using relative keys
                b_mesh.insertKey(1, 'relative')
                # get name for base key
                keyname = morphData.morphs[0].frame_name
                if not keyname:
                    keyname = 'Base'
                # set name for base key
                b_mesh.key.blocks[0].name = keyname
                # get base vectors and import all morphs
                baseverts = morphData.morphs[0].vectors
                b_ipo = Blender.Ipo.New('Key', 'KeyIpo')
                b_mesh.key.ipo = b_ipo
                for idxMorph in range(1, morphData.num_morphs):
                    # get name for key
                    keyname = morphData.morphs[idxMorph].frame_name
                    if not keyname:
                        keyname = 'Key %i' % idxMorph
                    NifLog.info("Inserting key '{0}'".format(keyname))
                    # get vectors
                    morphverts = morphData.morphs[idxMorph].vectors
                    # for each vertex calculate the key position from base
                    # pos + delta offset
                    assert (len(baseverts) == len(morphverts) == len(v_map))
                    for bv, mv, b_v_index in zip(baseverts, morphverts, v_map):
                        base = mathutils.Vector(bv.x, bv.y, bv.z)
                        delta = mathutils.Vector(mv.x, mv.y, mv.z)
                        v = base + delta
                        if applytransform:
                            v *= transform
                        b_mesh.vertices[b_v_index].co[0] = v.x
                        b_mesh.vertices[b_v_index].co[1] = v.y
                        b_mesh.vertices[b_v_index].co[2] = v.z
                    # update the mesh and insert key
                    b_mesh.insertKey(idxMorph, 'relative')
                    # set name for key
                    b_mesh.key.blocks[idxMorph].name = keyname
                    # set up the ipo key curve
                    try:
                        b_curve = b_ipo.addCurve(keyname)
                    except ValueError:
                        # this happens when two keys have the same name
                        # an instance of this is in fallout 3
                        # meshes/characters/_male/skeleton.nif HeadAnims:0
                        NifLog.warn(
                            "Skipped duplicate of key '{0}'".format(keyname))
                    # no idea how to set up the bezier triples -> switching
                    # to linear instead
                    b_curve.interpolation = Blender.IpoCurve.InterpTypes.LINEAR
                    # select extrapolation
                    b_curve.extend = self.get_extend_from_flags(
                        morphCtrl.flags)
                    # set up the curve's control points
                    # first find the keys
                    # older versions store keys in the morphData
                    morphkeys = morphData.morphs[idxMorph].keys
                    # newer versions store keys in the controller
                    if (not morphkeys) and morphCtrl.interpolators:
                        morphkeys = morphCtrl.interpolators[
                            idxMorph].data.data.keys
                    for key in morphkeys:
                        x = key.value
                        frame = 1 + int(key.time * fps + 0.5)
                        b_curve.addBezier((frame, x))
                    # finally: return to base position
                    for bv, b_v_index in zip(baseverts, v_map):
                        base = mathutils.Vector(bv.x, bv.y, bv.z)
                        if applytransform:
                            base *= transform
                        b_mesh.vertices[b_v_index].co[0] = base.x
                        b_mesh.vertices[b_v_index].co[1] = base.y
                        b_mesh.vertices[b_v_index].co[2] = base.z
    def import_object_animation(self, niBlock, b_obj):
        """
        Load animation attached to (Scene Root) object.
        Becomes the object level animation of the object.
        """

        # TODO: remove code duplication with import_keyframe_controller
        kfc = nif_utils.find_controller(niBlock,
                                        NifFormat.NiKeyframeController)
        if not kfc:
            # no animation data: do nothing
            return

        if kfc.interpolator:
            if isinstance(kfc.interpolator, NifFormat.NiBSplineInterpolator):
                kfd = None  # not supported yet so avoids fatal error - should be kfc.interpolator.spline_data when spline data is figured out.
            else:
                kfd = kfc.interpolator.data
        else:
            kfd = kfc.data

        if not kfd:
            # no animation data: do nothing
            return

        # denote progress
        NifLog.info("Animation")
        NifLog.info("Importing animation data for {0}".format(b_obj.name))
        assert (isinstance(kfd, NifFormat.NiKeyframeData))

        #get the interpolation modes
        interp_rot = get_interp_mode(kfd)
        interp_loc = get_interp_mode(kfd.translations)
        interp_scale = get_interp_mode(kfd.scales)

        b_obj_action = self.create_action(b_obj, b_obj.name + "-Anim")

        if kfd.scales.keys:
            NifLog.debug('Scale keys...')
            fcurves = create_fcurves(b_obj_action, "scale", range(3))
            for key in kfd.scales.keys:
                v = (key.value, key.value, key.value)
                self.add_key(fcurves, key.time, v, interp_scale)

        # detect the type of rotation keys
        if kfd.rotation_type == 4:
            NifLog.debug('Rotation keys...(eulers)')
            b_obj.rotation_mode = "XYZ"
            #Eulers are a bit different here, we can import them regardless of their timing
            #because they need no correction math in object space
            fcurves = create_fcurves(b_obj_action, "rotation_euler", range(3))
            keys = (kfd.xyz_rotations[0].keys, kfd.xyz_rotations[1].keys,
                    kfd.xyz_rotations[2].keys)
            for fcu, keys_dim in zip(fcurves, keys):
                for key in keys_dim:
                    self.add_key((fcu, ), key.time, (key.value, ), interp_rot)
        # uses quaternions
        elif kfd.quaternion_keys:
            NifLog.debug('Rotation keys...(quaternions)')
            b_obj.rotation_mode = "QUATERNION"
            fcurves = create_fcurves(b_obj_action, "rotation_quaternion",
                                     range(4))
            for key in kfd.quaternion_keys:
                v = (key.value.w, key.value.x, key.value.y, key.value.z)
                self.add_key(fcurves, key.time, v, interp_rot)

        if kfd.translations.keys:
            NifLog.debug('Translation keys...')
            fcurves = create_fcurves(b_obj_action, "location", range(3))
            for key in kfd.translations.keys:
                v = (key.value.x, key.value.y, key.value.z)
                self.add_key(fcurves, key.time, v, interp_rot)