Esempio n. 1
0
    def set_alpha(self, b_mat, ShaderProperty, n_alpha_prop):
        NifLog.debug("Alpha prop detected")
        b_mat.use_transparency = True
        if hasattr(ShaderProperty, 'alpha'):
            b_mat.alpha = (1 - ShaderProperty.alpha)
        else:
            b_mat.alpha = 0
        b_mat.transparency_method = 'Z_TRANSPARENCY'  # enable z-buffered transparency
        b_mat.offset_z = n_alpha_prop.threshold  # Transparency threshold
        b_mat.niftools_alpha.alphaflag = n_alpha_prop.flags

        return b_mat
    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)
Esempio n. 3
0
 def complete_bone_tree(self, bone, skelroot):
     """Make sure that the bones actually form a tree all the way
     down to the armature node. Call this function on all bones of
     a skin instance.
     """
     # we must already have marked this one as a bone
     assert skelroot in self.nif_import.dict_armatures  # debug
     assert bone in self.nif_import.dict_armatures[skelroot]  # debug
     # get the node parent, this should be marked as an armature or as a bone
     boneparent = bone._parent
     if boneparent != skelroot:
         # parent is not the skeleton root
         if boneparent not in self.nif_import.dict_armatures[skelroot]:
             # neither is it marked as a bone: so mark the parent as a bone
             self.nif_import.dict_armatures[skelroot].append(boneparent)
             # store the coordinates for realignement autodetection
             NifLog.debug("'{0}' is a bone of armature '{1}'".format(
                 boneparent.name, skelroot.name))
         # now the parent is marked as a bone
         # recursion: complete the bone tree,
         # this time starting from the parent bone
         self.complete_bone_tree(boneparent, skelroot)
Esempio n. 4
0
    def mark_armatures_bones(self, niBlock):
        """Mark armatures and bones by peeking into NiSkinInstance blocks."""
        # case where we import skeleton only,
        # or importing an Oblivion or Fallout 3 skeleton:
        # do all NiNode's as bones
        if NifOp.props.skeleton == "SKELETON_ONLY" or (
                self.nif_import.data.version in (0x14000005, 0x14020007) and
            (os.path.basename(NifOp.props.filepath).lower()
             in ('skeleton.nif', 'skeletonbeast.nif'))):

            if not isinstance(niBlock, NifFormat.NiNode):
                raise nif_utils.NifError(
                    "cannot import skeleton: root is not a NiNode")
            # for morrowind, take the Bip01 node to be the skeleton root
            if self.nif_import.data.version == 0x04000002:
                skelroot = niBlock.find(block_name='Bip01',
                                        block_type=NifFormat.NiNode)
                if not skelroot:
                    skelroot = niBlock
            else:
                skelroot = niBlock
            if skelroot not in self.nif_import.dict_armatures:
                self.nif_import.dict_armatures[skelroot] = []
            NifLog.info("Selecting node '%s' as skeleton root".format(
                skelroot.name))
            # add bones
            for bone in skelroot.tree():
                if bone is skelroot:
                    continue
                if not isinstance(bone, NifFormat.NiNode):
                    continue
                if self.nif_import.is_grouping_node(bone):
                    continue
                if bone not in self.nif_import.dict_armatures[skelroot]:
                    self.nif_import.dict_armatures[skelroot].append(bone)
            return  # done!

        # attaching to selected armature -> first identify armature and bones
        elif NifOp.props.skeleton == "GEOMETRY_ONLY" and not self.nif_import.dict_armatures:
            skelroot = niBlock.find(
                block_name=self.nif_import.selected_objects[0].name)
            if not skelroot:
                raise nif_utils.NifError(
                    "nif has no armature '%s'" %
                    self.nif_import.selected_objects[0].name)
            NifLog.debug("Identified '{0}' as armature".format(skelroot.name))
            self.nif_import.dict_armatures[skelroot] = []
            for bone_name in self.nif_import.selected_objects[
                    0].data.bones.keys():
                # blender bone naming -> nif bone naming
                nif_bone_name = armature.get_bone_name_for_nif(bone_name)
                # find a block with bone name
                bone_block = skelroot.find(block_name=nif_bone_name)
                # add it to the name list if there is a bone with that name
                if bone_block:
                    NifLog.info(
                        "Identified nif block '{0}' with bone '{1}' in selected armature"
                        .format(nif_bone_name, bone_name))
                    self.nif_import.dict_names[bone_block] = bone_name
                    self.nif_import.dict_armatures[skelroot].append(bone_block)
                    self.complete_bone_tree(bone_block, skelroot)

        # search for all NiTriShape or NiTriStrips blocks...
        if isinstance(niBlock, NifFormat.NiTriBasedGeom):
            # yes, we found one, get its skin instance
            if niBlock.is_skin():
                NifLog.debug("Skin found on block '{0}'".format(niBlock.name))
                # it has a skin instance, so get the skeleton root
                # which is an armature only if it's not a skinning influence
                # so mark the node to be imported as an armature
                skininst = niBlock.skin_instance
                skelroot = skininst.skeleton_root
                if NifOp.props.skeleton == "EVERYTHING":
                    if skelroot not in self.nif_import.dict_armatures:
                        self.nif_import.dict_armatures[skelroot] = []
                        NifLog.debug("'{0}' is an armature".format(
                            skelroot.name))
                elif NifOp.props.skeleton == "GEOMETRY_ONLY":
                    if skelroot not in self.nif_import.dict_armatures:
                        raise nif_utils.NifError(
                            "nif structure incompatible with '%s' as armature:"
                            " node '%s' has '%s' as armature" %
                            (self.nif_import.selected_objects[0].name,
                             niBlock.name, skelroot.name))

                for boneBlock in skininst.bones:
                    # boneBlock can be None; see pyffi issue #3114079
                    if not boneBlock:
                        continue
                    if boneBlock not in self.nif_import.dict_armatures[
                            skelroot]:
                        self.nif_import.dict_armatures[skelroot].append(
                            boneBlock)
                        NifLog.debug(
                            "'{0}' is a bone of armature '{1}'".format(
                                boneBlock.name, skelroot.name))
                    # now we "attach" the bone to the armature:
                    # we make sure all NiNodes from this bone all the way
                    # down to the armature NiNode are marked as bones
                    self.complete_bone_tree(boneBlock, skelroot)

                # mark all nodes as bones if asked
                if self.nif_import.IMPORT_EXTRANODES:
                    # add bones
                    for bone in skelroot.tree():
                        if bone is skelroot:
                            continue
                        if not isinstance(bone, NifFormat.NiNode):
                            continue
                        if isinstance(bone, NifFormat.NiLODNode):
                            # LOD nodes are never bones
                            continue
                        if self.nif_import.is_grouping_node(bone):
                            continue
                        if bone not in self.nif_import.dict_armatures[
                                skelroot]:
                            self.nif_import.dict_armatures[skelroot].append(
                                bone)
                            NifLog.debug(
                                "'{0}' marked as extra bone of armature '{1}'".
                                format(bone.name, skelroot.name))
                            # we make sure all NiNodes from this bone
                            # all the way down to the armature NiNode
                            # are marked as bones
                            self.complete_bone_tree(bone, skelroot)

        # continue down the tree
        for child in niBlock.get_refs():
            if not isinstance(child, NifFormat.NiAVObject):
                continue  # skip blocks that don't have transforms
            self.mark_armatures_bones(child)
Esempio n. 5
0
    def import_source(self, source):
        b_image = None
        fn = None

        # the texture uses an external image file
        if isinstance(source, NifFormat.NiSourceTexture):
            fn = source.file_name.decode()
        elif isinstance(source, str):
            fn = source
        else:
            raise TypeError("source must be NiSourceTexture or str")
        fn = fn.replace('\\', os.sep)
        fn = fn.replace('/', os.sep)
        # go searching for it
        importpath = os.path.dirname(NifOp.props.filepath)
        searchPathList = [importpath]
        if bpy.context.user_preferences.filepaths.texture_directory:
            searchPathList.append(
                bpy.context.user_preferences.filepaths.texture_directory)

        # guess the actual steam install path when steam is not installed at the default location and the texture path
        # is hardcoded for some reasons
        steamguessedpath = 'steamapps' + os.sep + 'common'
        importpathsplit = importpath.split(steamguessedpath)
        sourcesplit = source.split(steamguessedpath)

        if len(importpathsplit) == 2 and len(sourcesplit) == 2:
            searchPathList.append(
                os.path.dirname(importpathsplit[0] + steamguessedpath +
                                sourcesplit[1]))

        nif_dir = os.path.join(os.getcwd(), 'nif')
        searchPathList.append(nif_dir)

        # if it looks like a Morrowind style path, use common sense to
        # guess texture path
        meshes_index = importpath.lower().find("meshes")
        if meshes_index != -1:
            searchPathList.append(importpath[:meshes_index] + 'textures')
            # also assume that it could be a relative Skyrim style path
            searchPathList.append(
                os.path.dirname(importpath[:meshes_index] + source))
        # if it looks like a Civilization IV style path, use common sense
        # to guess texture path
        art_index = importpath.lower().find("art")
        if art_index != -1:
            searchPathList.append(importpath[:art_index] + 'shared')
        # go through all texture search paths
        for texdir in searchPathList:
            texdir = texdir.replace('\\', os.sep)
            texdir = texdir.replace('/', os.sep)
            # go through all possible file names, try alternate extensions
            # too; for linux, also try lower case versions of filenames
            texfns = reduce(operator.add, [[
                os.path.basename(fn)[:-4] + ext,
                os.path.basename(fn)[:-4].lower() + ext
            ] for ext in ('.DDS', '.dds', '.PNG', '.png', '.TGA', '.tga',
                          '.BMP', '.bmp', '.JPG', '.jpg')])
            texfns = [os.path.basename(fn),
                      os.path.basename(fn.lower())] + list(set(texfns))
            for texfn in texfns:
                # now a little trick, to satisfy many Morrowind mods
                if (texfn[:9].lower() == 'textures' + os.sep) \
                   and (texdir[-9:].lower() == os.sep + 'textures'):
                    # strip one of the two 'textures' from the path
                    tex = os.path.join(texdir[:-9], texfn)
                else:
                    tex = os.path.join(texdir, texfn)
                # "ignore case" on linux
                tex = bpy.path.resolve_ncase(tex)
                NifLog.debug("Searching {0}".format(tex))
                if os.path.exists(tex):
                    # tries to load the file
                    b_image = bpy.data.images.load(tex)
                    # Blender will return an image object even if the
                    # file format is not supported,
                    # so to check if the image is actually loaded an error
                    # is forced via "b_image.size"
                    try:
                        b_image.size
                    except:  # RuntimeError: couldn't load image data in Blender
                        b_image = None  # not supported, delete image object
                    else:
                        # file format is supported
                        NifLog.debug("Found '{0}' at {1}".format(fn, tex))
                        break
            if b_image:
                return [tex, b_image]
        else:
            tex = os.path.join(searchPathList[0], fn)

        return [tex, b_image]
    def import_keyframe_controller(self, kfc, b_armature_obj, bone_name,
                                   niBone_bind_scale, niBone_bind_rot_inv,
                                   niBone_bind_trans):
        if kfc:
            b_action = b_armature_obj.animation_data.action
            # old style: data directly on controller
            kfd = kfc.data
            # new style: data via interpolator
            kfi = kfc.interpolator

            translations = []
            scales = []
            rotations = []
            eulers = []

            #TODO: test interpolators
            # B-spline curve import
            if isinstance(kfi, NifFormat.NiBSplineInterpolator):
                times = list(kfi.get_times())

                translations = zip(times, list(kfi.get_translations()))
                scales = zip(times, list(kfi.get_scales()))
                rotations = zip(times, list(kfi.get_rotations()))

                #TODO: get these from interpolator?
                interp_rot = "LINEAR"
                interp_loc = "LINEAR"
                interp_scale = "LINEAR"
                return
            # next is a quick hack to make the new transform
            # interpolator work as if it is an old style keyframe data
            # block parented directly on the controller
            if isinstance(kfi, NifFormat.NiTransformInterpolator):
                kfd = kfi.data
                # for now, in this case, ignore interpolator
                kfi = None
            if isinstance(kfd, NifFormat.NiKeyframeData):
                interp_rot = get_interp_mode(kfd)
                interp_loc = get_interp_mode(kfd.translations)
                interp_scale = get_interp_mode(kfd.scales)
                if kfd.rotation_type == 4:
                    b_armature_obj.pose.bones[bone_name].rotation_mode = "XYZ"
                    # uses xyz rotation
                    if kfd.xyz_rotations[0].keys:

                        #euler keys need not be sampled at the same time in KFs
                        #but we need complete key sets to do the space conversion
                        #so perform linear interpolation to import all keys properly

                        #get all the keys' times
                        times_x = [
                            key.time for key in kfd.xyz_rotations[0].keys
                        ]
                        times_y = [
                            key.time for key in kfd.xyz_rotations[1].keys
                        ]
                        times_z = [
                            key.time for key in kfd.xyz_rotations[2].keys
                        ]
                        #the unique time stamps we have to sample all curves at
                        times_all = sorted(set(times_x + times_y + times_z))
                        #the actual resampling
                        x_r = interpolate(
                            times_all, times_x,
                            [key.value for key in kfd.xyz_rotations[0].keys])
                        y_r = interpolate(
                            times_all, times_y,
                            [key.value for key in kfd.xyz_rotations[1].keys])
                        z_r = interpolate(
                            times_all, times_z,
                            [key.value for key in kfd.xyz_rotations[2].keys])
                    eulers = zip(times_all, zip(x_r, y_r, z_r))
                else:
                    rotations = [(key.time, key.value)
                                 for key in kfd.quaternion_keys]

                if kfd.scales.keys:
                    scales = [(key.time, key.value) for key in kfd.scales.keys]

                if kfd.translations.keys:
                    translations = [(key.time, key.value)
                                    for key in kfd.translations.keys]

            if eulers:
                NifLog.debug('Rotation keys...(euler)')
                fcurves = create_fcurves(b_action, "rotation_euler", range(3),
                                         bone_name)
                for t, val in eulers:
                    euler = mathutils.Euler(val)
                    key = armature.import_keymat(
                        niBone_bind_rot_inv,
                        euler.to_matrix().to_4x4()).to_euler()
                    self.nif_import.animationhelper.add_key(
                        fcurves, t, key, interp_rot)
            elif rotations:
                NifLog.debug('Rotation keys...(quaternions)')
                fcurves = create_fcurves(b_action, "rotation_quaternion",
                                         range(4), bone_name)
                for t, val in rotations:
                    quat = mathutils.Quaternion([val.w, val.x, val.y, val.z])
                    key = armature.import_keymat(
                        niBone_bind_rot_inv,
                        quat.to_matrix().to_4x4()).to_quaternion()
                    self.nif_import.animationhelper.add_key(
                        fcurves, t, key, interp_rot)

            if scales:
                NifLog.debug('Scale keys...')
                fcurves = create_fcurves(b_action, "scale", range(3),
                                         bone_name)
                for t, val in scales:
                    key = (val, val, val)
                    self.nif_import.animationhelper.add_key(
                        fcurves, t, key, interp_scale)

            if translations:
                NifLog.debug('Translation keys...')
                fcurves = create_fcurves(b_action, "location", range(3),
                                         bone_name)
                for t, val in translations:
                    vec = mathutils.Vector([val.x, val.y, val.z])
                    key = armature.import_keymat(
                        niBone_bind_rot_inv,
                        mathutils.Matrix.Translation(
                            vec - niBone_bind_trans)).to_translation()
                    self.nif_import.animationhelper.add_key(
                        fcurves, t, key, interp_loc)
            #get extrapolation from kfc and set it to fcurves
            if any((eulers, rotations, scales, translations)):
                self.nif_import.animationhelper.set_extrapolation(
                    kfc.flags, b_action.groups[bone_name].channels)
    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)
    def export_bones(self, arm, parent_block):
        """Export the bones of an armature."""
        # the armature was already exported as a NiNode
        # now we must export the armature's bones
        assert (arm.type == 'ARMATURE')

        # find the root bones
        # list of all bones
        bones = arm.data.bones.values()

        # maps bone names to NiNode blocks
        bones_node = {}

        # here all the bones are added
        # first create all bones with their keyframes
        # and then fix the links in a second run

        # ok, let's create the bone NiNode blocks
        for bone in bones:
            # create a new block for this bone
            node = self.nif_export.objecthelper.create_ninode(bone)
            # doing bone map now makes linkage very easy in second run
            bones_node[bone.name] = node

            # add the node and the keyframe for this bone
            node.name = self.nif_export.objecthelper.get_full_name(bone.name)

            if (bone.niftools_bone.boneflags != 0):
                node.flags = bone.niftools_bone.boneflags
            else:
                if NifOp.props.game in ('OBLIVION', 'FALLOUT_3', 'SKYRIM'):
                    # default for Oblivion bones
                    # note: bodies have 0x000E, clothing has 0x000F
                    node.flags = 0x000E
                elif NifOp.props.game in ('CIVILIZATION_IV',
                                          'EMPIRE_EARTH_II'):
                    if bone.children:
                        # default for Civ IV/EE II bones with children
                        node.flags = 0x0006
                    else:
                        # default for Civ IV/EE II final bones
                        node.flags = 0x0016
                elif NifOp.props.game in ('DIVINITY_2', ):
                    if bone.children:
                        # default for Div 2 bones with children
                        node.flags = 0x0186
                    elif bone.name.lower()[-9:] == 'footsteps':
                        node.flags = 0x0116
                    else:
                        # default for Div 2 final bones
                        node.flags = 0x0196
                else:
                    node.flags = 0x0002  # default for Morrowind bones
            # rest pose
            self.nif_export.objecthelper.set_object_matrix(bone, node)

            # per-node animation
            self.nif_export.animationhelper.export_keyframes(node, arm, bone)

            # does bone have priority value in NULL constraint?
            for constr in arm.pose.bones[bone.name].constraints:
                # yes! store it for reference when creating the kf file
                if constr.name[:9].lower() == "priority:":
                    self.nif_export.dict_bone_priorities[
                        armature.get_bone_name_for_nif(bone.name)] = int(
                            constr.name[9:])

        # now fix the linkage between the blocks
        for bone in bones:
            # link the bone's children to the bone
            NifLog.debug("Linking children of bone {0}".format(bone.name))
            for child in bone.children:
                bones_node[bone.name].add_child(bones_node[child.name])
            # if it is a root bone, link it to the armature
            if not bone.parent:
                parent_block.add_child(bones_node[bone.name])