def import_object_animation(self, n_block, b_obj):
     """
     Loads an animation attached to a nif block (non-skeletal).
     Becomes the object level animation of the blender object.
     """
     NifLog.debug('Importing animation for object %s'.format(b_obj.name))
     
     kfc = nif_utils.find_controller(n_block, NifFormat.NiKeyframeController)
     if kfc:
         self.nif_import.animationhelper.create_action(b_obj, b_obj.name+"-Anim" )
         self.import_keyframe_controller(kfc, b_obj)
Exemplo n.º 2
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):
     """
     Loads an animation attached to a nif block (skeletal).
     Becomes the pose bone level animation of the blender object.
     """
     NifLog.debug('Importing animation for bone %s'.format(bone_name))
     
     kfc = nif_utils.find_controller(n_block, NifFormat.NiKeyframeController)
     if kfc:
         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()
         self.import_keyframe_controller(kfc, b_armature_obj, bone_name, niBone_bind_scale, niBone_bind_rot_inv, niBone_bind_trans)
Exemplo n.º 4
0
 def populate_bone_tree(self, skelroot):
     """Add all of skelroot's bones to its dict_armatures list.
     """
     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.dict_armatures[skelroot]:
             self.dict_armatures[skelroot].append(bone)
             NifLog.debug(
                 "'{0}' marked as extra bone of armature '{1}'".format(
                     bone.name, skelroot.name))
Exemplo n.º 5
0
 def complete_bone_tree(self, bone, skelroot):
     """Make sure that the complete hierarchy from skelroot down to bone is marked in dict_armatures.
     """
     # we must already have marked this one as a bone
     assert skelroot in self.dict_armatures  # debug
     assert bone in self.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.dict_armatures[skelroot]:
             # neither is it marked as a bone: so mark the parent as a bone
             self.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)
    def import_keyframe_controller(self, kfc, b_obj, bone_name=None, niBone_bind_scale=None, niBone_bind_rot_inv=None, niBone_bind_trans=None):
        b_action = b_obj.animation_data.action
        
        if bone_name:
            b_obj = b_obj.pose.bones[bone_name]
            
        # 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 = self.nif_import.animationhelper.get_b_interp_from_n_interp(kfd.rotation_type)
            interp_loc = self.nif_import.animationhelper.get_b_interp_from_n_interp(kfd.translations.interpolation)
            interp_scale = self.nif_import.animationhelper.get_b_interp_from_n_interp(kfd.scales.interpolation)
            if kfd.rotation_type == 4:
                b_obj.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:
                b_obj.rotation_mode = "QUATERNION"
                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 = self.nif_import.animationhelper.create_fcurves(b_action, "rotation_euler", range(3), kfc.flags, bone_name)
            for t, val in eulers:
                key = mathutils.Euler( val )
                if bone_name:
                    key = armature.import_keymat(niBone_bind_rot_inv, key.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 = self.nif_import.animationhelper.create_fcurves(b_action, "rotation_quaternion", range(4), kfc.flags, bone_name)
            for t, val in rotations:
                key = mathutils.Quaternion([val.w, val.x, val.y, val.z])
                if bone_name:
                    key = armature.import_keymat(niBone_bind_rot_inv, key.to_matrix().to_4x4() ).to_quaternion()
                self.nif_import.animationhelper.add_key(fcurves, t, key, interp_rot)
        if translations:
            NifLog.debug('Translation keys...')
            fcurves = self.nif_import.animationhelper.create_fcurves(b_action, "location", range(3), kfc.flags, bone_name)
            for t, val in translations:
                key = mathutils.Vector([val.x, val.y, val.z])
                if bone_name:
                    key = armature.import_keymat(niBone_bind_rot_inv, mathutils.Matrix.Translation(key - niBone_bind_trans)).to_translation()
                self.nif_import.animationhelper.add_key(fcurves, t, key, interp_loc)
        if scales:
            NifLog.debug('Scale keys...')
            fcurves = self.nif_import.animationhelper.create_fcurves(b_action, "scale", range(3), kfc.flags, bone_name)
            for t, val in scales:
                key = (val, val, val)
                self.nif_import.animationhelper.add_key(fcurves, t, key, interp_scale)
Exemplo n.º 7
0
    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])
Exemplo n.º 8
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)

        # TODO: 3 - Implement full texture path finding.
        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')
        # 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,
                [[fn[:-4] + ext, fn[:-4].lower() + ext]
                 for ext in ('.DDS', '.dds', '.PNG', '.png', '.TGA', '.tga',
                             '.BMP', '.bmp', '.JPG', '.jpg')])
            texfns = [fn, 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]
Exemplo n.º 9
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.dict_armatures:
                self.dict_armatures[skelroot] = []
            NifLog.info("Selecting node '%s' as skeleton root".format(
                skelroot.name))
            # add bones
            self.populate_bone_tree(skelroot)
            return  # done!

        # attaching to selected armature -> first identify armature and bones
        elif NifOp.props.skeleton == "GEOMETRY_ONLY" and not self.dict_armatures:
            b_armature_obj = self.nif_import.selected_objects[0]
            skelroot = niBlock.find(block_name=b_armature_obj.name)
            if not skelroot:
                raise nif_utils.NifError("nif has no armature '%s'" %
                                         b_armature_obj.name)
            NifLog.debug("Identified '{0}' as armature".format(skelroot.name))
            self.dict_armatures[skelroot] = []
            for bone_name in b_armature_obj.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.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.dict_armatures:
                        self.dict_armatures[skelroot] = []
                        NifLog.debug("'{0}' is an armature".format(
                            skelroot.name))
                elif NifOp.props.skeleton == "GEOMETRY_ONLY":
                    if skelroot not in self.dict_armatures:
                        raise nif_utils.NifError(
                            "nif structure incompatible with '%s' as armature:"
                            " node '%s' has '%s' as armature" %
                            (b_armature_obj.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.dict_armatures[skelroot]:
                        self.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
                self.populate_bone_tree(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)