def test_bounding_box_bsbound(self):
        """Oblivion bounding box (BSBound) test."""
        def check_bsbound(root_blocks):
            bsbound = root_blocks[0].extra_data_list[0]
            assert (isinstance(bsbound, NifFormat.BSBound))
            assert (bsbound.name == "BBX")
            assert (bsbound.next_extra_data is None)
            # using assert_equal because we compare floats
            self.assert_equal(bsbound.center.x, 0.0)
            self.assert_equal(bsbound.center.y, 0.0)
            self.assert_equal(bsbound.center.z, 66.2201843262)
            self.assert_equal(bsbound.dimensions.x, 23.0976696014)
            self.assert_equal(bsbound.dimensions.y, 17.6446208954)
            self.assert_equal(bsbound.dimensions.z, 66.2201843262)

        # import
        with closing(open('test/nif/bounding_box_bsbound.nif')) as stream:
            self.info("Reading test/nif/bounding_box_bsbound.nif")
            nif = NifFormat.Data()
            nif.read(stream)
            check_bsbound(nif.roots)
        nif_import = self.test(filename='test/nif/bounding_box_bsbound.nif')
        b_bbox = Blender.Object.Get("BSBound")
        # test stuff
        assert (b_bbox.display_bounds_type == 'BOX')
        # export
        nif_export = self.test(filename='test/nif/_bounding_box_bsbound.nif',
                               config=dict(game='OBLIVION'),
                               selection=['BSBound'])
        # test stuff...
        with closing(open('test/nif/_bounding_box_bsbound.nif')) as stream:
            self.info("Reading test/nif/_bounding_box_bsbound.nif")
            nif = NifFormat.Data()
            nif.read(stream)
            check_bsbound(nif.roots)
    def test_mw_nifxnifkf(self):
        """Test the nif xnif kf export option."""
        def check_ctrl_flags(root):
            # test the kfctrl flags to be active + clamp
            for ctrl in root.get_global_iterator():
                if not isinstance(ctrl, NifFormat.NiTimeController):
                    continue
                if ctrl.flags != 12:
                    raise ValueError("bad value for controller flags")

        # import a nif with animation
        dance = self.test(filename='test/nif/mw/dance.nif')
        check_ctrl_flags(dance.root_blocks[0])
        # export as nif + xnif + kf
        self.test(filename='test/nif/mw/_testnifxnifkf.nif',
                  config=dict(game='MORROWIND', animation='ALL_NIF_XNIF_XKF'),
                  selection=['Dance'],
                  next_layer=True)
        # check that these files are present, and check some of their properties
        with closing(open('test/nif/mw/_testnifxnifkf.nif')) as stream:
            self.info("Reading test/nif/mw/_testnifxnifkf.nif")
            nif = NifFormat.Data()
            nif.read(stream)
        with closing(open('test/nif/mw/x_testnifxnifkf.nif')) as stream:
            self.info("Reading test/nif/mw/x_testnifxnifkf.nif")
            xnif = NifFormat.Data()
            xnif.read(stream)
        with closing(open('test/nif/mw/x_testnifxnifkf.kf')) as stream:
            self.info("Reading test/nif/mw/x_testnifxnifkf.kf")
            xkf = NifFormat.Data()
            xkf.read(stream)
        # check root blocks
        assert (len(nif.roots) == 1)
        assert (len(xnif.roots) == 1)
        assert (len(xkf.roots) == 1)
        assert (isinstance(nif.roots[0], NifFormat.NiNode))
        assert (isinstance(xnif.roots[0], NifFormat.NiNode))
        assert (isinstance(xkf.roots[0], NifFormat.NiSequenceStreamHelper))
        # compare text keys
        nif_textkeys = nif.roots[0].extra_data
        xkf_textkeys = xkf.roots[0].extra_data
        assert (isinstance(nif_textkeys, NifFormat.NiTextKeyExtraData))
        assert (isinstance(xkf_textkeys, NifFormat.NiTextKeyExtraData))
        #assert(nif_textkeys == xkf_textkeys) # ... up to extra data chain
        # check that xkf has no target set in keyframe controller
        ctrl = xkf.roots[0].controller
        while ctrl:
            if ctrl.target is not None:
                raise ValueError(
                    "NiKeyframeController target should be None in xkf")
            ctrl = ctrl.next_controller
        # check controller flags
        check_ctrl_flags(xkf.roots[0])
Esempio n. 3
0
    def __init__(self):
        """Initialize the test."""
        Base.__init__(self)

        self.n_data = NifFormat.Data()

        fp = INTEGRATION_ROOT
        root = os.path.join(fp, "gen")

        nif_path = os.path.join(root, "nif", self.g_path)
        nif_file_path = nif_path + os.path.sep + self.g_name
        self.n_filepath_0 = nif_file_path + "_py_code.nif"
        self.n_filepath_1 = nif_file_path + "_export_py_code.nif"
        self.n_filepath_2 = nif_file_path + "_export_user_ver.nif"

        blend_path = os.path.join(root, "autoblend", self.g_path)
        blend_file_path = blend_path + os.path.sep + self.g_name
        self.b_filepath_0 = blend_file_path + "_pycode_import.blend"
        self.b_filepath_1 = blend_file_path + "_userver.blend"
        self.b_filepath_2 = blend_file_path + "_userver_reimport.blend"
        self.b_filepath_except = blend_file_path + "_exception.blend"

        if not os.path.exists(nif_path):
            os.makedirs(nif_path)

        if not os.path.exists(blend_path):
            os.makedirs(blend_path)
Esempio n. 4
0
def n_create_data():
    n_data = NifFormat.Data()
    n_data.version = 0x14000005
    n_data.user_version = 11
    n_data.user_version_2 = 11
    n_create_blocks(n_data)
    return n_data
Esempio n. 5
0
def postprocessNif(filename):
    input_stream = open(filename, "rb")
    nifdata = NifFormat.Data()
    try:
        nifdata.read(input_stream)
    except Exception as e:
        # nif-read error, something wrong with NIF, delete...
        input_stream.close()
        os.remove(filename)
        s = "\n\npostprocessNif(): ERROR reading(" + filename + "); deleting file."
        #        print(s)
        error_list(s)
        return -1
    input_stream.close()
    try:
        nifdata = postprocess_nifdata(nifdata)
        output_stream = open(filename, "wb")
        nifdata.write(output_stream)
        output_stream.close()
        print "PostProcessing(" + filename + ") complete."
    except Exception as e:
        s = "\n\nERROR post-processing(" + filename + ")..."
        #        print(s)
        error_list(s)
    return 0
Esempio n. 6
0
def export(path) -> List[ShapeCollection]:
    data = NifFormat.Data()
    with open(path, 'rb') as f:
        data.inspect(f)
        data.read(f)

    objects = []
    for root in data.roots:
        name = root.name.decode('utf-8').lower()
        # For this project, only hair and head matters
        if 'hair' in name or 'head' in name:
            sys.stderr.write("> " + name + "\n")
        else:
            sys.stderr.write("  " + name + "\n")
            continue

        if isinstance(root, NifFormat.NiNode):
            objects.append(export_object(root))

        # Some NiTriShape's are a root object
        if isinstance(root, NifFormat.NiTriShape):
            coll = ShapeCollection(name)
            coll.shapes = [export_shape(root)]

            objects.append(coll)

    return objects
Esempio n. 7
0
 def run(self):
     # fo3 body path
     fo3_male = os.path.join(self.config.get("path", "fallout3"), "meshes",
                             "characters", "_male")
     nif_import = self.test(filename=os.path.join(fo3_male, "skeleton.nif"),
                            config=dict(IMPORT_ANIMATION=True))
     nif_export = self.test(filename='test/nif/fo3/_skeleton.nif',
                            config=dict(game='FALLOUT_3',
                                        EXPORT_SMOOTHOBJECTSEAMS=True,
                                        EXPORT_FLATTENSKIN=True),
                            selection=['Scene Root'])
     # open original nif to get rid of possible issues with scale
     # correction in nif_import
     data = NifFormat.Data()
     with open(os.path.join(fo3_male, "skeleton.nif")) as stream:
         data.read(stream)
     # compare NiNode transforms
     for n_imp_node in data.roots[0].tree():
         if not isinstance(n_imp_node, NifFormat.NiNode):
             continue
         for n_exp_node in nif_export.root_blocks[0].tree():
             if not isinstance(n_exp_node, NifFormat.NiNode):
                 continue
             if n_imp_node.name != n_exp_node.name:
                 continue
             # check that transforms are equal
             self.info("checking transform of %s" % n_imp_node.name)
             if (n_imp_node.get_transform() != n_exp_node.get_transform()):
                 raise ValueError("transforms are different\n%s\n%s\n" %
                                  (n_imp_node.get_transform(),
                                   n_exp_node.get_transform()))
Esempio n. 8
0
 def test_nonaccum_export(self):
     """Test the nonaccum xy export option."""
     # import a nif with animation
     dance = self.test(
         filename = 'test/nif/mw/dance.nif')
     # export as nif with animation, default
     self.test(
         filename='test/nif/ob/_testnonaccum_default.nif',
         config=dict(game='OBLIVION',
                     EXPORT_NONACCUM=0),
         selection=['Dance'],
         next_layer=False)
     # export as nif with animation, accum xy
     self.test(
         filename='test/nif/ob/_testnonaccum_accumxy.nif',
         config=dict(game='OBLIVION',
                     EXPORT_NONACCUM=1),
         selection=['Dance'],
         next_layer=False)
     # export as nif with animation, no accum
     self.test(
         filename='test/nif/ob/_testnonaccum_accumnone.nif',
         config=dict(game='OBLIVION',
                     EXPORT_NONACCUM=2),
         selection=['Dance'],
         next_layer=False)
     # check that these files are present, and check some of their properties
     with closing(open('test/nif/ob/_testnonaccum_default.nif')) as stream:
         self.info("Reading test/nif/ob/_testnonaccum_default.nif")
         nif_default = NifFormat.Data()
         nif_default.read(stream)
     with closing(open('test/nif/ob/_testnonaccum_accumxy.nif')) as stream:
         self.info("Reading test/nif/ob/_testnonaccum_accumxy.nif")
         nif_xy = NifFormat.Data()
         nif_xy.read(stream)
     with closing(open('test/nif/ob/_testnonaccum_accumnone.nif')) as stream:
         self.info("Reading test/nif/ob/_testnonaccum_accumnone.nif")
         nif_none = NifFormat.Data()
         nif_none.read(stream)
     # check root blocks
     assert(len(nif_default.roots) == 1)
     assert(len(nif_xy.roots) == 1)
     assert(len(nif_none.roots) == 1)
     assert(isinstance(nif_default.roots[0], NifFormat.NiNode))
     assert(isinstance(nif_xy.roots[0], NifFormat.NiNode))
     assert(isinstance(nif_none.roots[0], NifFormat.NiNode))
 def setupClass(cls):
     #Uses script directory to read from/write from, override for different location
     cls.input_dir = os.path.dirname(__file__)
     cls.input_filename = 'test'
     cls.output_dir = cls.input_dir
     cls.output_filename = cls.input_filename + "_out"
     cls.ext = ".nif"
     cls.nif_file = NifFormat.Data()
     print("setup class")
Esempio n. 10
0
def remove_from_nif(filename):
    """Modify the file, remove blocks causing shiny texture from the nif file"""
    print("Operating on this file: " + filename)
    stream = open(filename, 'r+b')
    data = NifFormat.Data()
    data.read(stream)
    root_node = data.roots[0]
    remove_shiny_node(root_node)
    stream.close()
    os.remove(filename)
    print("Delete the old file")
    newstream = open(filename, 'wb')
    newdata = NifFormat.Data(version=0x04000002,
                             user_version=10)  #Morrowind version number
    newdata.roots = [root_node]
    newdata.write(newstream)
    print("Create a new file")
    newstream.close()
Esempio n. 11
0
 def check_priorities(self, filename, priorities):
     # helper function to check priorities
     data = NifFormat.Data()
     with open(filename, "rb") as stream:
         data.read(stream)
     nose.tools.assert_equal(len(data.roots), 1)
     seq = data.roots[0]
     nose.tools.assert_is_instance(seq, NifFormat.NiControllerSequence)
     nose.tools.assert_list_equal(
         [block.priority for block in seq.controlled_blocks], priorities)
 def setUpClass(cls):
     # Uses script directory to read from/write from, override for different location
     cls.root = os.path.dirname(__file__)
     cls.input_dir = os.path.join(cls.root, "input")
     cls.output_dir = os.path.join(cls.root, "output")
     cls.input_filename = ''  # Replace with name of file to be loaded
     cls.ext = ".nif"
     cls.output_filename = cls.input_filename + "_out"
     cls.nif_file = NifFormat.Data()
     print("setup class")
Esempio n. 13
0
def get_version_data():
    """ Returns NifFormat.Data of the correct version and user versions """
    b_scene = bpy.context.scene.niftools_scene
    game = b_scene.game
    version = b_scene.nif_version
    NifLog.info(f"Writing NIF version 0x{version:08X}")

    # get user version and user version 2 for export
    user_version = b_scene.user_version if b_scene.user_version else b_scene.USER_VERSION.get(game, 0)
    user_version_2 = b_scene.user_version_2 if b_scene.user_version_2 else b_scene.USER_VERSION_2.get(game, 0)

    return version, NifFormat.Data(version, user_version, user_version_2)
Esempio n. 14
0
def bsa_explore():
    """if os.path.exists(result):
        print("Found %s, cleaning directory!" % result)
        fileList = [ f for f in os.listdir(result)]
        for f in fileList:
            os.remove(result + "/%s" % f)
    
    elif not os.path.exists(result):
        print("Result path %s now found, creating it now!" % result);
        os.mkdir(result)
    """
    """Here we run over the nif files in the bsa and get their name, offset and size"""
    index = 0
    for folders in data.folders:
        for files in folders.files:
            if getExtension(files.name) != "nif":
                break
            offset = files.offset
            fSize = files.file_size.num_bytes
            n_files.append(files.name)
            n_offsets.append(offset)
            n_sizes.append(fSize)
            index += 1

    indexn = 0
    index = 0
    for f in n_files:
        stream.seek(n_offsets[index])
        nif = stream.read(n_sizes[index])
        tmpByte = io.BytesIO()
        tmpByte.write(nif)
        tmpByte.seek(0)
        ndata = NifFormat.Data()
        ndata.inspect(tmpByte)
        ndata.read(tmpByte)

        for block in ndata.roots:
            if isinstance(block, NifFormat.NiNode):
                if os.path.isfile(result + f):
                    f = renameFile(f, indexn)
                    indexn += 1
                streams = open(result + f, "wb")
                ndata.write(streams)
                print("written to %s" % (result + f))
                streams.close()
        index += 1
Esempio n. 15
0
    def process_nif_files(path, keywords, glossiness, specular_strength):
        success = False
        data = NifFormat.Data()

        try:
            with open(path, 'rb') as stream:
                data.read(stream)
        except Exception:
            log.exception("Error while reading stream from file : " + path)
            return success

        # First, let's get relevant NiTriShape block
        block = None
        index = 0
        try:
            root = data.roots[0]
            while not block and index < len(keywords):
                block = root.find(keywords[index])
                index += 1

            # Second, if found, change its parameters
            if block is not None:
                for subblock in block.tree():
                    if subblock.__class__.__name__ == "BSLightingShaderProperty":
                        old_gloss = subblock.glossiness
                        subblock.glossiness = glossiness
                        old_spec_strength = subblock.specular_strength
                        subblock.specular_strength = specular_strength
                        log.info("[" + path + "] ------ Glossiness " +
                                 str(old_gloss) + " -> " + str(glossiness) +
                                 " | Specular Strength " +
                                 str(old_spec_strength) + " -> " +
                                 str(specular_strength))
                        success = True
        except IndexError:
            pass

        if success:
            try:
                with open(path, 'wb') as stream:
                    data.write(stream)
            except Exception:
                log.exception("Error while writing to file : " + path)

        return success
Esempio n. 16
0
    def execute(self, context):
        filein = bpy.context.scene.vertexcolor.fileinput
        hexvalue = bpy.context.scene.vertexcolor.hexwidget

        # open file for binary reading
        print("Importing %s" % filein)
        niffile = open(filein, "rb")
        self.data = NifFormat.Data()

        # check if nif file is valid
        self.data.inspect(niffile)
        if self.data.version >= 0:
            # it is valid, so read the file
            print("NIF file version: 0x%08X" % self.data.version)
            print("Reading file")
            self.data.read(niffile)
        elif self.data.version == -1:
            print("Unsupported NIF version.")
        else:
            print("Not a NIF file.")

        for root in self.data.roots:
            for block in root.tree():
                if isinstance(block, NifFormat.NiTriShape):
                    if (block.data.has_vertex_colors):
                        vertexcol = block.data.vertex_colors[0]
                        bpy.context.scene.vertexcolor.hexwidget = [
                            vertexcol._items[0]._value,
                            vertexcol._items[1]._value,
                            vertexcol._items[2]._value
                        ]

        if (bpy.context.scene.vertexcolor.fileoutput == ""):
            print("check run ok")
            input = bpy.context.scene.vertexcolor.fileinput
            bpy.context.scene.vertexcolor.fileoutput = input[:-4] + "_copy.nif"

        niffile.close()
        print("Finished Reading file :D")

        nif = NifDataHolder()
        nif.setinstancedata(self.data)
        return {'FINISHED'}
Esempio n. 17
0
    def load_kf(file_path):
        """Loads a Kf file from the given path"""
        NifLog.info("Loading {0}".format(file_path))
        
        kf_file = NifFormat.Data()

        # open keyframe file for binary reading
        with open(file_path, "rb") as kf_stream:
            # check if nif file is valid
            kf_file.inspect_version_only(kf_stream)
            if kf_file.version >= 0:
                # it is valid, so read the file
                NifLog.info("KF file version: {0}".format(kf_file.version, "x"))
                NifLog.info("Reading keyframe file")
                kf_file.read(kf_stream)
            elif kf_file.version == -1:
                raise NifError("Unsupported KF version.")
            else:                    
                raise NifError("Not a KF file.")
            
        return kf_file    
Esempio n. 18
0
 def toastentry(cls, toaster):
     """Read reference nif file given as argument."""
     # if no argument given, do not apply spell
     if not toaster.options.get("arg"):
         return False
     # read reference nif
     toaster.refdata = NifFormat.Data()
     with closing(open(toaster.options["arg"], "rb")) as reffile:
         toaster.refdata.read(reffile)
     # find bone data in reference nif
     toaster.refbonedata = []
     for refgeom in toaster.refdata.get_global_iterator():
         if (isinstance(refgeom, NifFormat.NiGeometry)
             and refgeom.skin_instance and refgeom.skin_instance.data):
             toaster.refbonedata += list(zip(
                 repeat(refgeom.skin_instance.skeleton_root),
                 repeat(refgeom.skin_instance.data),
                 refgeom.skin_instance.bones,
                 refgeom.skin_instance.data.bone_list))
     # only apply spell if the reference nif has bone data
     return bool(toaster.refbonedata)
 def test_ob_animsequencename(self):
     """Test Oblivion animation sequence name export option."""
     # import a nif with animation
     dance = self.test(filename='test/nif/mw/dance.nif')
     # export as kf
     self.test(filename='test/nif/ob/_testanimseqname.kf',
               config=dict(game='OBLIVION',
                           animation='ANIM_KF',
                           EXPORT_ANIMSEQUENCENAME="TestAnimSeqName"),
               selection=['Dance'],
               next_layer=True)
     # check that these files are present, and check some of their properties
     with closing(open('test/nif/ob/_testanimseqname.kf')) as stream:
         self.info("Reading test/nif/ob/_testanimseqname.kf")
         kf = NifFormat.Data()
         kf.read(stream)
     # check root block
     assert (len(kf.roots) == 1)
     assert (isinstance(kf.roots[0], NifFormat.NiControllerSequence))
     # check animation sequence name
     assert (kf.roots[0].name == "TestAnimSeqName")
Esempio n. 20
0
    def load_nif(file_path):
        """Loads a nif from the given file path"""
        NifLog.info(f"Importing {file_path}")

        data = NifFormat.Data()

        # open file for binary reading
        with open(file_path, "rb") as nif_stream:
            # check if nif file is valid
            data.inspect_version_only(nif_stream)
            if data.version >= 0:
                # it is valid, so read the file
                NifLog.info(f"NIF file version: {data.version:x}")
                NifLog.info("Reading file")
                data.read(nif_stream)
            elif data.version == -1:
                raise NifError("Unsupported NIF version.")
            else:
                raise NifError("Not a NIF file.")

        return data
Esempio n. 21
0
    def __init__(self):
        """Initialize the test."""
        Base.__init__(self)

        self.n_data = NifFormat.Data()

        root = "integration/gen/"
        nif_path = root + "nif/" + self.n_name
        blend_path = root + "autoblend/" + self.n_name

        self.n_filepath_0 = nif_path + "_py_code.nif"
        self.n_filepath_1 = nif_path + "_export_py_code.nif"
        self.n_filepath_2 = nif_path + "_export_user_ver.nif"

        self.b_filepath_0 = blend_path + "_pycode_import.blend"
        self.b_filepath_1 = blend_path + "_userver.blend"
        self.b_filepath_2 = blend_path + "_userver_reimport.blend"
        self.b_filepath_except = blend_path + "_exception.blend"

        if not os.path.exists(nif_path):
            os.makedirs(nif_path)

        if not os.path.exists(blend_path):
            os.makedirs(blend_path)
Esempio n. 22
0
 def n_read(self, n_filepath):
     """Read nif file and return the data."""
     n_data = NifFormat.Data()
     with open(n_filepath, "rb") as stream:
         n_data.read(stream)
     return n_data
Esempio n. 23
0
    def execute(self):
        """Main export function."""
        if bpy.context.mode != 'OBJECT':
            bpy.ops.object.mode_set(mode='OBJECT', toggle=False)

        NifLog.info("Exporting {0}".format(NifOp.props.filepath))

        # TODO:
        '''
        if NifOp.props.animation == 'ALL_NIF_XNIF_XKF' and NifOp.props.game == 'MORROWIND':
            # if exporting in nif+xnif+kf mode, then first export
            # the nif with geometry + animation, which is done by:
            NifOp.props.animation = 'ALL_NIF'
        '''

        # extract directory, base name, extension
        directory = os.path.dirname(NifOp.props.filepath)
        filebase, fileext = os.path.splitext(
            os.path.basename(NifOp.props.filepath))

        self.dict_armatures = {}
        self.dict_bone_priorities = {}
        self.dict_havok_objects = {}
        self.dict_names = {}
        self.dict_blocks = {}
        self.dict_block_names = []
        self.dict_materials = {}
        self.dict_textures = {}
        self.dict_mesh_uvlayers = []

        # if an egm is exported, this will contain the data
        self.egm_data = None

        try:  # catch export errors

            for b_obj in bpy.data.objects:
                # armatures should not be in rest position
                if b_obj.type == 'ARMATURE':
                    # ensure we get the mesh vertices in animation mode,
                    # and not in rest position!
                    b_obj.data.pose_position = 'POSE'

                if b_obj.type == 'MESH':
                    # TODO - Need to implement correct armature checking
                    # b_armature_modifier = None
                    b_obj_name = b_obj.name
                    if b_obj.parent:
                        for b_mod in bpy.data.objects[b_obj_name].modifiers:
                            if b_mod.type == 'ARMATURE':
                                # b_armature_modifier = b_mod.name
                                if b_mod.use_bone_envelopes:
                                    raise nif_utils.NifError(
                                        "'%s': Cannot export envelope skinning. If you have vertex groups, turn off envelopes.\n"
                                        "If you don't have vertex groups, select the bones one by one press W to "
                                        "convert their envelopes to vertex weights, and turn off envelopes."
                                        % b_obj.name)
                            # if not b_armature_modifier:
                            #     raise nif_utils.NifError("'%s': is parented but does not have"
                            #                              " the armature modifier set. This will"
                            #                              " cause animations to fail."
                            #                              % b_obj.name)

                # check for non-uniform transforms
                # (lattices are not exported so ignore them as they often tend
                # to have non-uniform scaling)
                if b_obj.type != 'LATTICE':
                    scale = b_obj.matrix_local.to_scale()
                    if abs(scale.x - scale.y) > NifOp.props.epsilon or abs(
                            scale.y - scale.z) > NifOp.props.epsilon:
                        raise nif_utils.NifError(
                            "Non-uniform scaling not supported.\n "
                            "Workaround: apply size and rotation (CTRL-A) on '%s'."
                            % b_obj.name)

            root_name = filebase
            # get the root object from selected object
            # only export empties, meshes, and armatures
            if not bpy.context.selected_objects:
                raise nif_utils.NifError(
                    "Please select the object(s) to export, and run this script again."
                )

            root_objects = set()
            export_types = ('EMPTY', 'MESH', 'ARMATURE')
            exportable_objects = [
                b_obj for b_obj in bpy.context.selected_objects
                if b_obj.type in export_types
            ]
            for root_object in exportable_objects:
                while root_object.parent:
                    root_object = root_object.parent
                if NifOp.props.game in ('CIVILIZATION_IV', 'OBLIVION',
                                        'FALLOUT_3', 'ZOO_TYCOON_2'):
                    if (root_object.type
                            == 'ARMATURE') or (root_object.name.lower()
                                               == "bip01"):
                        root_name = 'Scene Root'
                # TODO remove as already filtered
                if root_object.type not in export_types:
                    raise nif_utils.NifError(
                        "Root object (%s) must be an 'EMPTY', 'MESH', or 'ARMATURE' object."
                        % root_object.name)
                root_objects.add(root_object)

            # smooth seams of objects
            if NifOp.props.smooth_object_seams:
                self.objecthelper.mesh_helper.smooth_mesh_seams(
                    bpy.context.scene.objects)

            # TODO: use Blender actions for animation groups
            # check for animation groups definition in a text buffer 'Anim'
            try:
                animtxt = None  # Changed for testing needs fix bpy.data.texts["Anim"]
            except NameError:
                animtxt = None

            # rebuild the full name dictionary from the 'FullNames' text buffer
            self.objecthelper.rebuild_full_names()

            # export nif:
            # -----------
            NifLog.info("Exporting")

            # find nif version to write
            # TODO Move fully to scene level
            self.version = NifOp.op.version[NifOp.props.game]
            self.user_version, self.user_version_2 = scene_export.get_version_info(
                NifOp.props)
            #the axes used for bone correction depend on the nif version
            armature.set_bone_correction_from_version(self.version)

            # create a nif object

            # export the root node (the name is fixed later to avoid confusing the
            # exporter with duplicate names)
            root_block = self.objecthelper.export_node(None, None, '')

            # TODO Move to object system and redo
            # export objects
            NifLog.info("Exporting objects")
            for root_object in root_objects:
                if NifOp.props.game in 'SKYRIM':
                    if root_object.niftools_bs_invmarker:
                        for extra_item in root_block.extra_data_list:
                            if isinstance(extra_item, NifFormat.BSInvMarker):
                                raise nif_utils.NifError(
                                    "Multiple Items have Inventory marker data only one item may contain this data"
                                )
                        else:
                            n_extra_list = NifFormat.BSInvMarker()
                            n_extra_list.name = root_object.niftools_bs_invmarker[
                                0].name.encode()
                            n_extra_list.rotation_x = root_object.niftools_bs_invmarker[
                                0].bs_inv_x
                            n_extra_list.rotation_y = root_object.niftools_bs_invmarker[
                                0].bs_inv_y
                            n_extra_list.rotation_z = root_object.niftools_bs_invmarker[
                                0].bs_inv_z
                            n_extra_list.zoom = root_object.niftools_bs_invmarker[
                                0].bs_inv_zoom

                            root_block.add_extra_data(n_extra_list)

                # export the root objects as a NiNodes; their children are
                # exported as well
                self.objecthelper.export_node(root_object, root_block,
                                              root_object.name)

            # post-processing:
            # ----------------

            # if we exported animations, but no animation groups are defined,
            # define a default animation group
            NifLog.info("Checking animation groups")
            if not animtxt:
                has_controllers = False
                for block in self.dict_blocks:
                    # has it a controller field?
                    if isinstance(block, NifFormat.NiObjectNET):
                        if block.controller:
                            has_controllers = True
                            break
                if has_controllers:
                    NifLog.info("Defining default animation group.")
                    # write the animation group text buffer
                    animtxt = bpy.data.texts.new("Anim")
                    animtxt.write(
                        "%i/Idle: Start/Idle: Loop Start\n%i/Idle: Loop Stop/Idle: Stop"
                        % (bpy.context.scene.frame_start,
                           bpy.context.scene.frame_end))

            # animations without keyframe animations crash the TESCS
            # if we are in that situation, add a trivial keyframe animation
            NifLog.info("Checking controllers")
            if animtxt and NifOp.props.game == 'MORROWIND':
                has_keyframecontrollers = False
                for block in self.dict_blocks:
                    if isinstance(block, NifFormat.NiKeyframeController):
                        has_keyframecontrollers = True
                        break
                if ((not has_keyframecontrollers)
                        and (not NifOp.props.bs_animation_node)):
                    NifLog.info("Defining dummy keyframe controller")
                    # add a trivial keyframe controller on the scene root
                    self.animationhelper.export_keyframes(root_block)

            if NifOp.props.bs_animation_node and NifOp.props.game == 'MORROWIND':
                for block in self.dict_blocks:
                    if isinstance(block, NifFormat.NiNode):
                        # if any of the shape children has a controller
                        # or if the ninode has a controller
                        # convert its type
                        if block.controller or any(
                                child.controller for child in block.children
                                if isinstance(child, NifFormat.NiGeometry)):
                            new_block = NifFormat.NiBSAnimationNode().deepcopy(
                                block)
                            # have to change flags to 42 to make it work
                            new_block.flags = 42
                            root_block.replace_global_node(block, new_block)
                            if root_block is block:
                                root_block = new_block

            # oblivion skeleton export: check that all bones have a
            # transform controller and transform interpolator
            if NifOp.props.game in ('OBLIVION', 'FALLOUT_3',
                                    'SKYRIM') and filebase.lower() in (
                                        'skeleton', 'skeletonbeast'):
                # here comes everything that is Oblivion skeleton export specific
                NifLog.info(
                    "Adding controllers and interpolators for skeleton")
                for block in list(self.dict_blocks.keys()):
                    if isinstance(block, NifFormat.NiNode
                                  ) and block.name.decode() == "Bip01":
                        for bone in block.tree(block_type=NifFormat.NiNode):
                            ctrl = self.objecthelper.create_block(
                                "NiTransformController")
                            interp = self.objecthelper.create_block(
                                "NiTransformInterpolator")

                            ctrl.interpolator = interp
                            bone.add_controller(ctrl)

                            ctrl.flags = 12
                            ctrl.frequency = 1.0
                            ctrl.phase = 0.0
                            ctrl.start_time = self.FLOAT_MAX
                            ctrl.stop_time = self.FLOAT_MIN
                            interp.translation.x = bone.translation.x
                            interp.translation.y = bone.translation.y
                            interp.translation.z = bone.translation.z
                            scale, quat = bone.rotation.get_scale_quat()
                            interp.rotation.x = quat.x
                            interp.rotation.y = quat.y
                            interp.rotation.z = quat.z
                            interp.rotation.w = quat.w
                            interp.scale = bone.scale
            else:
                # here comes everything that should be exported EXCEPT
                # for Oblivion skeleton exports
                # export animation groups (not for skeleton.nif export!)
                if animtxt:
                    # TODO: removed temorarily to process bseffectshader export
                    anim_textextra = None  # self.animationhelper.export_text_keys(root_block)
                else:
                    anim_textextra = None

            # oblivion and Fallout 3 furniture markers
            if NifOp.props.game in (
                    'OBLIVION', 'FALLOUT_3',
                    'SKYRIM') and filebase[:15].lower() == 'furnituremarker':
                # exporting a furniture marker for Oblivion/FO3
                try:
                    furniturenumber = int(filebase[15:])
                except ValueError:
                    raise nif_utils.NifError(
                        "Furniture marker has invalid number (%s).\n"
                        "Name your file 'furnituremarkerxx.nif' where xx is a number between 00 and 19."
                        % filebase[15:])
                # name scene root name the file base name
                root_name = filebase

                # create furniture marker block
                furnmark = self.objecthelper.create_block("BSFurnitureMarker")
                furnmark.name = "FRN"
                furnmark.num_positions = 1
                furnmark.positions.update_size()
                furnmark.positions[0].position_ref_1 = furniturenumber
                furnmark.positions[0].position_ref_2 = furniturenumber

                # create extra string data sgoKeep
                sgokeep = self.objecthelper.create_block("NiStringExtraData")
                sgokeep.name = "UPB"  # user property buffer
                sgokeep.string_data = "sgoKeep=1 ExportSel = Yes"  # Unyielding = 0, sgoKeep=1ExportSel = Yes

                # add extra blocks
                root_block.add_extra_data(furnmark)
                root_block.add_extra_data(sgokeep)

            # FIXME:
            NifLog.info("Checking collision")
            # activate oblivion/Fallout 3 collision and physics
            if NifOp.props.game in ('OBLIVION', 'FALLOUT_3', 'SKYRIM'):
                hascollision = False
                for b_obj in bpy.data.objects:
                    if b_obj.game.use_collision_bounds:
                        hascollision = True
                        break
                if hascollision:
                    # enable collision
                    bsx = self.objecthelper.create_block("BSXFlags")
                    bsx.name = 'BSX'
                    bsx.integer_data = b_obj.niftools.bsxflags
                    root_block.add_extra_data(bsx)

                    # many Oblivion nifs have a UPB, but export is disabled as
                    # they do not seem to affect anything in the game
                    if b_obj.niftools.upb:
                        upb = self.objecthelper.create_block(
                            "NiStringExtraData")
                        upb.name = 'UPB'
                        if b_obj.niftools.upb == '':
                            upb.string_data = 'Mass = 0.000000\r\nEllasticity = 0.300000\r\nFriction = 0.300000\r\nUnyielding = 0\r\nSimulation_Geometry = 2\r\nProxy_Geometry = <None>\r\nUse_Display_Proxy = 0\r\nDisplay_Children = 1\r\nDisable_Collisions = 0\r\nInactive = 0\r\nDisplay_Proxy = <None>\r\n'
                        else:
                            upb.string_data = b_obj.niftools.upb.encode()
                        root_block.add_extra_data(upb)

                # update rigid body center of gravity and mass
                if self.EXPORT_OB_COLLISION_DO_NOT_USE_BLENDER_PROPERTIES:
                    # we are not using blender properties to set the mass
                    # so calculate mass automatically first calculate distribution of mass
                    total_mass = 0
                    for block in self.dict_blocks:
                        if isinstance(block, NifFormat.bhkRigidBody):
                            block.update_mass_center_inertia(
                                solid=self.EXPORT_OB_SOLID)
                            total_mass += block.mass

                    if total_mass == 0:
                        # to avoid zero division error later (if mass is zero then this does not matter anyway)
                        total_mass = 1

                    # now update the mass ensuring that total mass is self.EXPORT_OB_MASS
                    for block in self.dict_blocks:
                        if isinstance(block, NifFormat.bhkRigidBody):
                            mass = self.EXPORT_OB_MASS * block.mass / total_mass
                            # lower bound on mass
                            if mass < 0.0001:
                                mass = 0.05
                            block.update_mass_center_inertia(
                                mass=mass, solid=self.EXPORT_OB_SOLID)
                else:
                    # using blender properties, so block.mass *should* have
                    # been set properly
                    for block in self.dict_blocks:
                        if isinstance(block, NifFormat.bhkRigidBody):
                            # lower bound on mass
                            if block.mass < 0.0001:
                                block.mass = 0.05
                            block.update_mass_center_inertia(
                                mass=block.mass, solid=self.EXPORT_OB_SOLID)

            # bhkConvexVerticesShape of children of bhkListShapes need an extra bhkConvexTransformShape (see issue #3308638, reported by Koniption)
            # note: self.dict_blocks changes during iteration, so need list copy
            for block in list(self.dict_blocks):
                if isinstance(block, NifFormat.bhkListShape):
                    for i, sub_shape in enumerate(block.sub_shapes):
                        if isinstance(sub_shape,
                                      NifFormat.bhkConvexVerticesShape):
                            coltf = self.objecthelper.create_block(
                                "bhkConvexTransformShape")
                            coltf.material = sub_shape.material
                            coltf.unknown_float_1 = 0.1
                            coltf.unknown_8_bytes[0] = 96
                            coltf.unknown_8_bytes[1] = 120
                            coltf.unknown_8_bytes[2] = 53
                            coltf.unknown_8_bytes[3] = 19
                            coltf.unknown_8_bytes[4] = 24
                            coltf.unknown_8_bytes[5] = 9
                            coltf.unknown_8_bytes[6] = 253
                            coltf.unknown_8_bytes[7] = 4
                            coltf.transform.set_identity()
                            coltf.shape = sub_shape
                            block.sub_shapes[i] = coltf

            # export constraints
            for b_obj in self.objecthelper.get_exported_objects():
                if isinstance(b_obj, bpy.types.Object) and b_obj.constraints:
                    self.constrainthelper.export_constraints(b_obj, root_block)

            # export weapon location
            if NifOp.props.game in ('OBLIVION', 'FALLOUT_3', 'SKYRIM'):
                if self.EXPORT_OB_PRN != "NONE":
                    # add string extra data
                    prn = self.objecthelper.create_block("NiStringExtraData")
                    prn.name = 'Prn'
                    prn.string_data = {
                        "BACK": "BackWeapon",
                        "SIDE": "SideWeapon",
                        "QUIVER": "Quiver",
                        "SHIELD": "Bip01 L ForearmTwist",
                        "HELM": "Bip01 Head",
                        "RING": "Bip01 R Finger1"
                    }[self.EXPORT_OB_PRN]
                    root_block.add_extra_data(prn)

            # add vertex color and zbuffer properties for civ4 and railroads
            if NifOp.props.game in ('CIVILIZATION_IV',
                                    'SID_MEIER_S_RAILROADS'):
                self.propertyhelper.object_property.export_vertex_color_property(
                    root_block)
                self.propertyhelper.object_property.export_z_buffer_property(
                    root_block)
            elif NifOp.props.game in ('EMPIRE_EARTH_II', ):
                self.propertyhelper.object_property.export_vertex_color_property(
                    root_block)
                self.propertyhelper.object_property.export_z_buffer_property(
                    root_block, flags=15, function=1)

            # FIXME:
            """
            if self.EXPORT_FLATTENSKIN:
                # (warning: trouble if armatures parent other armatures or
                # if bones parent geometries, or if object is animated)
                # flatten skins
                skelroots = set()
                affectedbones = []
                for block in self.dict_blocks:
                    if isinstance(block, NifFormat.NiGeometry) and block.is_skin():
                        NifLog.info("Flattening skin on geometry {0}".format(block.name))
                        affectedbones.extend(block.flatten_skin())
                        skelroots.add(block.skin_instance.skeleton_root)
                # remove NiNodes that do not affect skin
                for skelroot in skelroots:
                    NifLog.info("Removing unused NiNodes in '{0}'".format(skelroot.name))
                    skelrootchildren = [child for child in skelroot.children
                                        if ((not isinstance(child,
                                                            NifFormat.NiNode))
                                            or (child in affectedbones))]
                    skelroot.num_children = len(skelrootchildren)
                    skelroot.children.update_size()
                    for i, child in enumerate(skelrootchildren):
                        skelroot.children[i] = child
            """

            # apply scale
            if abs(NifOp.props.scale_correction_export) > NifOp.props.epsilon:
                NifLog.info("Applying scale correction {0}".format(
                    str(NifOp.props.scale_correction_export)))
                data = NifFormat.Data()
                data.roots = [root_block]
                toaster = pyffi.spells.nif.NifToaster()
                toaster.scale = NifOp.props.scale_correction_export
                pyffi.spells.nif.fix.SpellScale(data=data,
                                                toaster=toaster).recurse()
                # also scale egm
                if self.egm_data:
                    self.egm_data.apply_scale(
                        NifOp.props.scale_correction_export)

            # generate mopps (must be done after applying scale!)
            if NifOp.props.game in ('OBLIVION', 'FALLOUT_3', 'SKYRIM'):
                for block in self.dict_blocks:
                    if isinstance(block, NifFormat.bhkMoppBvTreeShape):
                        NifLog.info("Generating mopp...")
                        block.update_mopp()
                        # print "=== DEBUG: MOPP TREE ==="
                        # block.parse_mopp(verbose = True)
                        # print "=== END OF MOPP TREE ==="
                        # warn about mopps on non-static objects
                        if any(sub_shape.layer != 1
                               for sub_shape in block.shape.sub_shapes):
                            NifLog.warn(
                                "Mopps for non-static objects may not function correctly in-game. You may wish to use simple primitives for collision."
                            )

            # delete original scene root if a scene root object was already defined
            if root_block.num_children == 1 and (
                    root_block.children[0].name in ['Scene Root', 'Bip01']
                    or root_block.children[0].name[-3:] == 'nif'):
                if root_block.children[0].name[-3:] == 'nif':
                    root_block.children[0].name = filebase
                NifLog.info("Making '{0}' the root block".format(
                    root_block.children[0].name))
                # remove root_block from self.dict_blocks
                self.dict_blocks.pop(root_block)
                # set new root block
                old_root_block = root_block
                root_block = old_root_block.children[0]
                # copy extra data and properties
                for extra in old_root_block.get_extra_datas():
                    # delete links in extras to avoid parentship problems
                    extra.next_extra_data = None
                    # now add it
                    root_block.add_extra_data(extra)
                for b in old_root_block.get_controllers():
                    root_block.add_controller(b)
                for b in old_root_block.properties:
                    root_block.add_property(b)
                for b in old_root_block.effects:
                    root_block.add_effect(b)
            else:
                root_block.name = root_name

            self.root_ninode = None
            for root_obj in root_objects:
                if root_obj.niftools.rootnode == 'BSFadeNode':
                    self.root_ninode = 'BSFadeNode'
                elif self.root_ninode is None:
                    self.root_ninode = 'NiNode'

            # making root block a fade node
            if NifOp.props.game in ('FALLOUT_3', 'SKYRIM'
                                    ) and self.root_ninode == 'BSFadeNode':
                NifLog.info("Making root block a BSFadeNode")
                fade_root_block = NifFormat.BSFadeNode().deepcopy(root_block)
                fade_root_block.replace_global_node(root_block,
                                                    fade_root_block)
                root_block = fade_root_block

            export_animation = NifOp.props.animation
            if export_animation == 'ALL_NIF':
                NifLog.info("Exporting geometry and animation")
            elif export_animation == 'GEOM_NIF':
                # for morrowind: everything except keyframe controllers
                NifLog.info("Exporting geometry only")
            elif export_animation == 'ANIM_KF':
                # for morrowind: only keyframe controllers
                NifLog.info("Exporting animation only (as .kf file)")

            # export nif file:
            # ----------------

            NifLog.info("Writing NIF version 0x%08X" % self.version)

            if export_animation != 'ANIM_KF':
                if NifOp.props.game == 'EMPIRE_EARTH_II':
                    ext = ".nifcache"
                else:
                    ext = ".nif"
                NifLog.info("Writing {0} file".format(ext))

                # make sure we have the right file extension
                if fileext.lower() != ext:
                    NifLog.warn(
                        "Changing extension from {0} to {1} on output file".
                        format(fileext, ext))
                niffile = os.path.join(directory, filebase + ext)

                data = NifFormat.Data(version=self.version,
                                      user_version=self.user_version,
                                      user_version_2=self.user_version_2)
                data.roots = [root_block]
                if NifOp.props.game == 'NEOSTEAM':
                    data.modification = "neosteam"
                elif NifOp.props.game == 'ATLANTICA':
                    data.modification = "ndoors"
                elif NifOp.props.game == 'HOWLING_SWORD':
                    data.modification = "jmihs1"
                with open(niffile, "wb") as stream:
                    data.write(stream)

            # create and export keyframe file and xnif file:
            # ----------------------------------------------

            # convert root_block tree into a keyframe tree
            if export_animation == 'ANIM_KF' or export_animation == 'ALL_NIF_XNIF_XKF':
                NifLog.info("Creating keyframe tree")
                # find all nodes and relevant controllers
                node_kfctrls = {}
                for node in root_block.tree():
                    if not isinstance(node, NifFormat.NiAVObject):
                        continue
                    # get list of all controllers for this node
                    ctrls = node.get_controllers()
                    for ctrl in ctrls:
                        if NifOp.props.game == 'MORROWIND':
                            # morrowind: only keyframe controllers
                            if not isinstance(ctrl,
                                              NifFormat.NiKeyframeController):
                                continue
                        if node not in node_kfctrls:
                            node_kfctrls[node] = []
                        node_kfctrls[node].append(ctrl)
                # morrowind
                if NifOp.props.game in ('MORROWIND', 'FREEDOM_FORCE'):
                    # create kf root header
                    kf_root = self.objecthelper.create_block(
                        "NiSequenceStreamHelper")
                    kf_root.add_extra_data(anim_textextra)
                    # reparent controller tree
                    for node, ctrls in node_kfctrls.items():
                        for ctrl in ctrls:
                            # create node reference by name
                            nodename_extra = self.objecthelper.create_block(
                                "NiStringExtraData")
                            nodename_extra.bytes_remaining = len(node.name) + 4
                            nodename_extra.string_data = node.name

                            # break the controller chain
                            ctrl.next_controller = None

                            # add node reference and controller
                            kf_root.add_extra_data(nodename_extra)
                            kf_root.add_controller(ctrl)
                            # wipe controller target
                            ctrl.target = None
                # oblivion
                elif NifOp.props.game in ('OBLIVION', 'FALLOUT_3',
                                          'CIVILIZATION_IV', 'ZOO_TYCOON_2',
                                          'FREEDOM_FORCE_VS_THE_3RD_REICH'):
                    # TODO [animation] allow for object kf only
                    # create kf root header
                    kf_root = self.objecthelper.create_block(
                        "NiControllerSequence")
                    # if self.EXPORT_ANIMSEQUENCENAME:
                    # kf_root.name = self.EXPORT_ANIMSEQUENCENAME
                    # else:
                    kf_root.name = filebase
                    kf_root.unknown_int_1 = 1
                    kf_root.weight = 1.0
                    kf_root.text_keys = anim_textextra
                    kf_root.cycle_type = NifFormat.CycleType.CYCLE_CLAMP
                    kf_root.frequency = 1.0
                    kf_root.start_time = bpy.context.scene.frame_start * bpy.context.scene.render.fps
                    kf_root.stop_time = (bpy.context.scene.frame_end -
                                         bpy.context.scene.frame_start
                                         ) * bpy.context.scene.render.fps
                    # quick hack to set correct target name
                    if "Bip01" in b_armature.data.bones:
                        targetname = "Bip01"
                    elif "Bip02" in b_armature.data.bones:
                        targetname = "Bip02"
                    else:
                        targetname = root_block.name
                    kf_root.target_name = targetname
                    kf_root.string_palette = NifFormat.NiStringPalette()
                    b_armature = armature.get_armature()
                    # per-node animation
                    if b_armature:
                        for b_bone in b_armature.data.bones:
                            self.animationhelper.export_keyframes(
                                kf_root, b_armature, b_bone)
                    # per-object animation
                    else:
                        for b_obj in bpy.data.objects:
                            self.animationhelper.export_keyframes(
                                kf_root, b_obj)

                    # for node, ctrls in zip(iter(node_kfctrls.keys()), iter(node_kfctrls.values())):
                    # # export a block for every interpolator in every controller
                    # for ctrl in ctrls:
                    # # XXX add get_interpolators to pyffi interface
                    # if isinstance(ctrl, NifFormat.NiSingleInterpController):
                    # interpolators = [ctrl.interpolator]
                    # elif isinstance( ctrl, (NifFormat.NiGeomMorpherController, NifFormat.NiMorphWeightsController)):
                    # interpolators = ctrl.interpolators

                    # if isinstance(ctrl, NifFormat.NiGeomMorpherController):
                    # variable_2s = [morph.frame_name for morph in ctrl.data.morphs]
                    # else:
                    # variable_2s = [None for interpolator in interpolators]
                    # for interpolator, variable_2 in zip(interpolators, variable_2s):
                    # # create ControlledLink for each interpolator
                    # controlledblock = kf_root.add_controlled_block()
                    # if self.version < 0x0A020000:
                    # # older versions need the actual controller blocks
                    # controlledblock.target_name = node.name
                    # controlledblock.controller = ctrl
                    # # erase reference to target node
                    # ctrl.target = None
                    # else:
                    # # newer versions need the interpolator blocks
                    # controlledblock.interpolator = interpolator
                    # # get bone animation priority (previously fetched from the constraints during export_bones)
                    # if not node.name in self.dict_bone_priorities or self.EXPORT_ANIM_DO_NOT_USE_BLENDER_PROPERTIES:
                    # if self.EXPORT_ANIMPRIORITY != 0:
                    # priority = self.EXPORT_ANIMPRIORITY
                    # else:
                    # priority = 26
                    # NifLog.warn("No priority set for bone {0}, falling back on default value ({1})".format(node.name, str(priority)))
                    # else:
                    # priority = self.dict_bone_priorities[node.name]
                    # controlledblock.priority = priority
                    # # set palette, and node and controller type names, and variables
                    # controlledblock.string_palette = kf_root.string_palette
                    # controlledblock.set_node_name(node.name)
                    # controlledblock.set_controller_type(ctrl.__class__.__name__)
                    # if variable_2:
                    # controlledblock.set_variable_2(variable_2)
                else:
                    raise nif_utils.NifError(
                        "Keyframe export for '%s' is not supported.\nOnly Morrowind, Oblivion, Fallout 3, Civilization IV,"
                        " Zoo Tycoon 2, Freedom Force, and Freedom Force vs. the 3rd Reich keyframes are supported."
                        % NifOp.props.game)

                # write kf (and xnif if asked)
                prefix = "" if (
                    export_animation != 'ALL_NIF_XNIF_XKF') else "x"

                ext = ".kf"
                NifLog.info("Writing {0} file".format(prefix + ext))

                kffile = os.path.join(directory, prefix + filebase + ext)
                data = NifFormat.Data(version=self.version,
                                      user_version=self.user_version,
                                      user_version_2=self.user_version_2)
                data.roots = [kf_root]
                data.neosteam = (NifOp.props.game == 'NEOSTEAM')
                stream = open(kffile, "wb")
                try:
                    data.write(stream)
                finally:
                    stream.close()

            if export_animation == 'ALL_NIF_XNIF_XKF':
                NifLog.info("Detaching keyframe controllers from nif")
                # detach the keyframe controllers from the nif (for xnif)
                for node in root_block.tree():
                    if not isinstance(node, NifFormat.NiNode):
                        continue
                    # remove references to keyframe controllers from node
                    # (for xnif)
                    while isinstance(node.controller,
                                     NifFormat.NiKeyframeController):
                        node.controller = node.controller.next_controller
                    ctrl = node.controller
                    while ctrl:
                        if isinstance(ctrl.next_controller,
                                      NifFormat.NiKeyframeController):
                            ctrl.next_controller = ctrl.next_controller.next_controller
                        else:
                            ctrl = ctrl.next_controller

                NifLog.info("Detaching animation text keys from nif")
                # detach animation text keys
                if root_block.extra_data is not anim_textextra:
                    raise RuntimeError(
                        "Oops, you found a bug! Animation extra data"
                        " wasn't where expected...")
                root_block.extra_data = None

                prefix = "x"  # we are in morrowind 'nifxnifkf mode'
                ext = ".nif"
                NifLog.info("Writing {0} file".format(prefix + ext))

                xniffile = os.path.join(directory, prefix + filebase + ext)
                data = NifFormat.Data(version=self.version,
                                      user_version=self.user_version,
                                      user_version_2=self.user_version_2)
                data.roots = [root_block]
                data.neosteam = (NifOp.props.game == 'NEOSTEAM')
                stream = open(xniffile, "wb")
                try:
                    data.write(stream)
                finally:
                    stream.close()

            # export egm file:
            # -----------------
            if self.egm_data:
                ext = ".egm"
                NifLog.info("Writing {0} file".format(ext))

                egmfile = os.path.join(directory, filebase + ext)
                stream = open(egmfile, "wb")
                try:
                    self.egm_data.write(stream)
                finally:
                    stream.close()
        finally:
            # clear progress bar
            NifLog.info("Finished")

        # save exported file (this is used by the test suite)
        self.root_blocks = [root_block]

        return {'FINISHED'}
Esempio n. 24
0
    def bsa_explore(self):
        oldtime = time.time()

        if self.bsa == None:
            print("Trying to read BSA when no bsa file is found!")
            return

        if not os.path.isfile(self.bsa):
            print("Couldn't find BSA file!")
            return
        else:
            stream = open(self.bsa, "rb")

        data = BsaFormat.Data()
        data.inspect(stream)
        data.read(stream)
        """if os.path.exists(result):
            print("Found %s, cleaning directory!" % result)
            fileList = [ f for f in os.listdir(result)]
            for f in fileList:
                os.remove(result + "/%s" % f)
        
        elif not os.path.exists(result):
            print("Result path %s now found, creating it now!" % result);
            os.mkdir(result)
        """
        """Here we run over the nif files in the bsa and get their name, offset and size"""
        self.index = 0
        for folders in data.folders:
            for files in folders.files:
                if self.getExtension(files.name) != "nif":
                    break
                offset = files.offset
                fSize = files.file_size.num_bytes
                self.bsa_files.append(files.name)
                self.bsa_offsets.append(offset)
                self.bsa_sizes.append(fSize)
                self.index += 1

        indexn = 0
        self.index = 0
        for f in self.bsa_files:
            stream.seek(self.bsa_offsets[self.index])
            nif = stream.read(self.bsa_sizes[self.index])
            tmpByte = io.BytesIO()
            tmpByte.write(nif)
            tmpByte.seek(0)
            ndata = NifFormat.Data()
            ndata.inspect(tmpByte)
            ndata.read(tmpByte)

            for block in ndata.roots:
                if isinstance(block, NifFormat.NiNode):
                    if os.path.isfile(self.result_path + f):
                        f = self.renameFile(f, indexn)
                        indexn += 1
                    if self.property != None:
                        if getattr(block, self.property):
                            print("Notice: %s found in %s" %
                                  (self.property, f.split("/")[-1]))
                        else:
                            print("Warning: No property %s found in %s" %
                                  (self.property, f.split("/")[-1]))
                            break
                    streams = open(self.result_path + "/" + f, "wb")
                    ndata.write(streams)
                    print("Notice: Written to %s" % (f))
                    streams.close()
            self.index += 1
        print("Counted %s objects in %ss" %
              (self.index, time.time() - oldtime))
        print("Output folder %s" % self.result_path)
Esempio n. 25
0
#!/usr/bin/env python3

import sys
import logging
import os

logging.basicConfig(level=logging.DEBUG)

from pyffi.formats.nif import NifFormat

for path in sys.argv[1:]:

    stream = open(path, 'rb')
    data = NifFormat.Data()
    data.inspect(stream)  # the file seems ok on inspection
    data.read(stream)  # doctest: +ELLIPSIS

    print("nif version 0x%08X, user version %d" %
          (data.version, data.user_version))

    for root in data.roots:
        for block in root.tree():
            if isinstance(block, NifFormat.NiNode):
                #print(block)
                print(block.name.decode("ascii"))
                for block in block.tree():
                    if isinstance(block, NifFormat.NiUDSFileObject):
                        name = block.name.decode("ascii")
                        print("Found file '%s'" % name)
                        #print(block)
Esempio n. 26
0
def n_create_data():
    n_data = NifFormat.Data()
    n_data.version = 0x4000002
    n_create_blocks(n_data)
    return n_data
Esempio n. 27
0
def generate_meshes(f_obj, fem, f_tex):

    #######################################################
    ## Remove second line of facegen generated obj because it has a weird char
    #######################################################
    basefile_obj, file_extension = os.path.splitext(f_obj)

    #basefile_obj = "input"
    basefile_obj_ = basefile_obj + ".obj"
    basefile_copy = basefile_obj + "_copy.obj"
    basefile_mtl = basefile_obj + ".mtl"
    basefile_mtl_copy = basefile_obj + "_copy.mtl"
    with open(basefile_obj_, "r") as infile:
        lines = infile.readlines()

    with open(basefile_copy, "w") as outfile:
        for pos, line in enumerate(lines):
            if pos != 1:
                outfile.write(line)

    copyfile(basefile_mtl, basefile_mtl_copy)

    #######################################################
    ## Load and extract info from obj
    #######################################################

    scene = pywavefront.Wavefront(basefile_copy)

    vertex_obj = scene.vertices
    num_vertex_obj = len(vertex_obj)
    print("#############################")
    print("num_vertex_obj")
    print(num_vertex_obj)
    print("#############################")

    #num_vertx_obj_eyes = 0
    #num_vertx_obj_browns = 0

    ###############################
    # OPEN AND EXTRACT NIF INFO
    ###############################

    #basefile = 'malehead.nif'
    female = fem
    if female:
        basefile = 'femalehead.nif'
    else:
        basefile = 'malehead.nif'

    stream = open(basefile, 'rb')
    data = NifFormat.Data()
    data.read(stream)
    stream.close()

    if female:
        num_vertx_obj_head = 3832
        num_vertx_obj_eyes = 176
        num_vertx_obj_browns = 318
    else:
        num_vertx_obj_head = 3598
        num_vertx_obj_eyes = 186
        num_vertx_obj_browns = 318

    for root in data.roots:
        for block in root.tree():
            if isinstance(block, NifFormat.NiNode):
                print(block.name.decode("ascii"))
                if block.name.decode("ascii") == 'MaleHeadKouLeifoh':
                    bloqueForma = block
                    print('entra if 1')
                if block.name.decode("ascii") == 'Scene Root':
                    bloqueForma = block
                    print('entra if 1')

    for elementos in bloqueForma.tree():
        if female:
            if isinstance(elementos, NifFormat.NiTriShape):
                print(elementos.name.decode("ascii"))
                if elementos.name.decode("ascii") == 'FemaleHead':
                    bloqueForma_sub = elementos
                    print('entra if 2')
                if elementos.name.decode("ascii") == 'HairFemaleRedguard03':
                    bloqueHair_sub = elementos
                    print('entra if 2.5')
                if elementos.name.decode(
                        "ascii") == 'FemaleEyesHumanHazelBrown':
                    bloqueEyes_sub = elementos
                    print('entra if 2.6')
                if elementos.name.decode("ascii") == 'FemaleBrowsHuman07':
                    bloqueBrowns_sub = elementos
                    print('entra if 2.7')
                if elementos.name.decode(
                        "ascii") == 'FemaleMouthHumanoidDefault':
                    bloqueMouth_sub = elementos
                    print('entra if 2.9')
        else:
            if isinstance(elementos, NifFormat.NiTriShape):
                print(elementos.name.decode("ascii"))
                if elementos.name.decode("ascii") == 'MaleHeadIMF':
                    bloqueForma_sub = elementos
                    print('entra if 2')
                if elementos.name.decode("ascii") == 'HairMaleImperial1':
                    bloqueHair_sub = elementos
                    print('entra if 2.5')
                if elementos.name.decode("ascii") == 'MaleEyesHumanHazelBrown':
                    bloqueEyes_sub = elementos
                    print('entra if 2.6')
                if elementos.name.decode(
                        "ascii") == '00KLH_BrowsMaleHumanoid07':
                    bloqueBrowns_sub = elementos
                    print('entra if 2.7')
                if elementos.name.decode(
                        "ascii") == 'MaleMouthHumanoidDefault':
                    bloqueMouth_sub = elementos
                    print('entra if 2.9')

    for elementos2 in bloqueForma_sub.tree():
        if isinstance(elementos2, NifFormat.NiTriShapeData):
            print('entra if 3')
            infoShape = elementos2

    verticesForma = infoShape.vertices

    triangles_nif = infoShape.triangles
    num_triangles_nif = infoShape.num_triangles

    ###############################
    # MAKE A COPY OF HEAD INFO
    ###############################

    stream_c = open(basefile, 'rb')
    data_c = NifFormat.Data()
    data_c.read(stream_c)
    stream_c.close()

    for root_c in data_c.roots:
        for block_c in root_c.tree():
            if isinstance(block_c, NifFormat.NiNode):
                #print(block.name.decode("ascii"))
                if block_c.name.decode("ascii") == 'MaleHeadKouLeifoh':
                    bloqueForma_c = block_c
                    print('entra if 1')
                if block_c.name.decode("ascii") == 'Scene Root':
                    bloqueForma_c = block_c
                    print('entra if 1')

    for elementos_c in bloqueForma_c.tree():
        if isinstance(elementos_c, NifFormat.NiTriShape):
            print(elementos_c.name.decode("ascii"))
            if (female):
                if elementos_c.name.decode("ascii") == 'FemaleHead':
                    bloqueForma_sub_c = elementos_c
                    print('entra if 2')
            else:
                if elementos_c.name.decode("ascii") == 'MaleHeadIMF':
                    bloqueForma_sub_c = elementos_c
                    print('entra if 2')

    for elementos2_c in bloqueForma_sub_c.tree():
        if isinstance(elementos2_c, NifFormat.NiTriShapeData):
            print('entra if 3')
            infoShape_c = elementos2_c

    verticesForma_copia = infoShape_c.vertices
    verticesForma_copia2 = infoShape_c.vertices

    ###############################
    # GET VERTEX NUMBER
    ###############################

    num_vertices = 0
    for vertx in verticesForma:
        #print(vertx)
        num_vertices = num_vertices + 1

    ######################################
    # CHECK OBJ ORIENTATION ##############
    ######################################

    #Check obj orientation
    x_max = 0
    x_min = 0
    y_max = 0
    y_min = 0
    z_max = 0
    z_min = 0

    for r in range(num_vertex_obj):
        x_d = vertex_obj[r][0]
        y_d = vertex_obj[r][1]
        z_d = vertex_obj[r][2]

        if x_d > x_max:
            x_max = x_d
        elif x_d < x_min:
            x_min = x_d
        if y_d > y_max:
            y_max = y_d
        elif y_d < y_min:
            y_min = y_d
        if z_d > z_max:
            z_max = z_d
        elif z_d < z_min:
            z_min = z_d

    if (z_max - z_min) > (x_max - x_min) and (z_max - z_min) > (y_max - y_min):
        print('correct')
    elif (x_max - x_min) > (z_max - z_min):
        print('incorrect rotation')
    elif (y_max - y_min) > (z_max - z_min):
        print('incorrect rotation')

    ######################################
    # NOSE TO NOSE OBJ MOVEMENT ##########
    ######################################

    max_y = 0
    for n in range(num_vertx_obj_head):
        if vertex_obj[n + num_vertx_obj_eyes][1] > max_y:
            v_ind_nose_obj = n + num_vertx_obj_eyes
            max_y = vertex_obj[n + num_vertx_obj_eyes][1]

    max_yn = 0
    for nn in range(num_vertices):
        if verticesForma[nn].y > max_yn:
            v_ind_nose_nif = nn
            max_yn = verticesForma[nn].y

    vertex_obj_ = [[0 for x in range(3)] for y in range(num_vertx_obj_head)]
    vertex_obj_eyes = [[0 for x in range(3)]
                       for y in range(num_vertx_obj_eyes)]
    vertex_obj_browns = [[0 for x in range(3)]
                         for y in range(num_vertx_obj_browns)]

    delta_nose_x = verticesForma[v_ind_nose_nif].x - vertex_obj[
        v_ind_nose_obj][0]
    delta_nose_y = verticesForma[v_ind_nose_nif].y - vertex_obj[
        v_ind_nose_obj][1]
    delta_nose_z = verticesForma[v_ind_nose_nif].z - vertex_obj[
        v_ind_nose_obj][2]

    for lm in range(num_vertx_obj_head):
        vertex_obj_[lm][0] = vertex_obj[lm +
                                        num_vertx_obj_eyes][0] + delta_nose_x
        vertex_obj_[lm][1] = vertex_obj[lm +
                                        num_vertx_obj_eyes][1] + delta_nose_y
        vertex_obj_[lm][2] = vertex_obj[lm +
                                        num_vertx_obj_eyes][2] + delta_nose_z

    ######################################
    # EYES MOVEMENT ##########
    ######################################

    if female:
        #d_h_x_ =  -0.000008
        #d_h_y_ =  -1.595064
        #d_h_z_ =  120.741463
        d_h_x_ = -0.000256
        d_h_y_ = -1.547514
        d_h_z_ = 120.343597
    else:
        #d_h_x_ =  -0.000256
        #d_h_y_ =  -1.547517
        #d_h_z_ =  120.343590
        d_h_x_ = -0.000256
        d_h_y_ = -1.547517
        d_h_z_ = 120.343590

    #################################################
    # project nif vertices to obj #############
    #################################################

    for i in range(num_vertx_obj_head):
        #print(verticesForma_ref[i])
        verticesForma[i].x = vertex_obj_[i][0]
        verticesForma[i].y = vertex_obj_[i][1]
        verticesForma[i].z = vertex_obj_[i][2]

    if female:
        path_h = 'FaceToSkyrimGenerated/Data/Meshes/RazaFacegen/Female/output_head.nif'
    else:
        #path_h = 'FaceToSkyrimGenerated/Data/Meshes/Male/output_head.nif'
        path_h = 'FaceToSkyrimGenerated/Data/Meshes/RazaFacegen/Male/output_head.nif'
    stream2 = open(path_h, 'wb')
    data.write(stream2)
    stream2.close()

    ###############################
    # OPEN AND EXTRACT EYES INFO
    ###############################
    #######################################################
    ## Remove second line of facegen generated obj because it has a weird char
    #######################################################
    """
    basefile_obj = "input_eyes"
    basefile_obj_ = basefile_obj + ".obj"
    basefile_copy = basefile_obj + "_copy.obj"
    basefile_mtl = basefile_obj+".mtl"
    basefile_mtl_copy = basefile_obj+"_copy.mtl"
    with open(basefile_obj_, "r") as infile:
        lines = infile.readlines()
    
    with open(basefile_copy, "w") as outfile:
        for pos, line in enumerate(lines):
            if pos != 1:
                outfile.write(line)
    
    
    #######################################################
    ## Load and extract info from obj
    #######################################################
    
    scene = pywavefront.Wavefront(basefile_copy)
    
    vertex_obj = scene.vertices
    num_vertex_obj = len(vertex_obj)
    print("#############################")
    print("num_vertex_obj")
    print(num_vertex_obj)
    print("#############################")
    
    """
    ###############################
    # OPEN AND EXTRACT NIF INFO
    ###############################

    if female:
        basefile = 'eyesfemale.nif'
    else:
        basefile = 'eyesmale.nif'

    stream = open(basefile, 'rb')
    dataE = NifFormat.Data()
    dataE.read(stream)
    stream.close()

    for root in dataE.roots:
        for block in root.tree():
            if isinstance(block, NifFormat.NiNode):
                print(block.name.decode("ascii"))
                if block.name.decode("ascii") == 'EyesMale.nif':
                    bloqueForma = block
                    print('entra if 1')
                if block.name.decode("ascii") == 'EyesFemale.nif':
                    bloqueForma = block
                    print('entra if 1')

    for elementos in bloqueForma.tree():
        if female:
            if isinstance(elementos, NifFormat.NiTriShape):
                if elementos.name.decode("ascii") == 'EyesFemaleV2':
                    bloqueEyes_sub = elementos
                    print('entra if 2.6')
        else:
            if isinstance(elementos, NifFormat.NiTriShape):
                print(elementos.name.decode("ascii"))
                if elementos.name.decode("ascii") == 'EyesMale':
                    bloqueEyes_sub = elementos
                    print('entra if 2.6')

    for elementosE in bloqueEyes_sub.tree():
        if isinstance(elementosE, NifFormat.NiTriShapeData):
            print('entra if 3.6')
            infoEyes = elementosE

    verticesEyes = infoEyes.vertices

    num_vertices_eyes = 0
    for vertx in verticesEyes:
        num_vertices_eyes = num_vertices_eyes + 1

    vertex_obj_eyes = [[0 for x in range(3)]
                       for y in range(num_vertx_obj_eyes)]

    delta_eyes_fix = 9.4 - 7.8

    for ey in range(num_vertx_obj_eyes):

        vertex_obj_eyes[ey][
            0] = vertex_obj[ey][0] + delta_nose_x - d_h_x_  #+ d_eyes_x
        vertex_obj_eyes[ey][
            1] = vertex_obj[ey][1] + delta_nose_y - d_h_y_  #+ d_eyes_y
        vertex_obj_eyes[ey][2] = vertex_obj[ey][
            2] + delta_nose_z - d_h_z_  #+ d_eyes_z #- delta_eyes_z

    #fix_y_factor = 0.35
    fix_y_factor = 0.1
    fix_z_factor = 0.1
    for e in range(len(vertex_obj_eyes)):
        #print(verticesForma_ref[i])
        verticesEyes[e].x = vertex_obj_eyes[e][0]
        verticesEyes[e].y = vertex_obj_eyes[e][1]  #+ fix_y_factor
        verticesEyes[e].z = vertex_obj_eyes[e][2]  #+ fix_z_factor

    if female:
        path_e = 'FaceToSkyrimGenerated/Data/Meshes/RazaFacegen/Female/output_eyes.nif'
    else:
        path_e = 'FaceToSkyrimGenerated/Data/Meshes/RazaFacegen/Male/output_eyes.nif'

    stream2 = open(path_e, 'wb')
    dataE.write(stream2)
    stream2.close()

    ###############################
    # GENERATE MOVED MOUTH
    ###############################
    ###############################
    # OPEN AND EXTRACT MOUTH INFO
    ###############################

    if female:
        basefileM = 'mouthhumanf.nif'
    else:
        basefileM = 'mouthhuman.nif'

    stream = open(basefileM, 'rb')
    dataM = NifFormat.Data()
    dataM.read(stream)
    stream.close()

    for root in dataM.roots:
        for block in root.tree():
            if isinstance(block, NifFormat.NiNode):
                print(block.name.decode("ascii"))
                if block.name.decode("ascii") == 'MouthHuman.nif':
                    bloqueForma = block
                    print('entra if 1')
                if block.name.decode("ascii") == 'MouthHumanF.nif':
                    bloqueForma = block
                    print('entra if 1')

    for elementos in bloqueForma.tree():
        if female:
            if isinstance(elementos, NifFormat.NiTriShape):
                if elementos.name.decode("ascii") == 'MouthHumanF':
                    bloqueMouth_sub = elementos
                    print('entra if 2.6')
        else:
            if isinstance(elementos, NifFormat.NiTriShape):
                print(elementos.name.decode("ascii"))
                if elementos.name.decode("ascii") == 'MouthHuman':
                    bloqueMouth_sub = elementos
                    print('entra if 2.6')

    for elementosE in bloqueMouth_sub.tree():
        if isinstance(elementosE, NifFormat.NiTriShapeData):
            print('entra if 3.6')
            infoMouth = elementosE

    verticesMouth = infoMouth.vertices

    num_vertices_mouth = 0
    for vertx in verticesMouth:
        num_vertices_mouth = num_vertices_mouth + 1

    num_vertx_obj_mouth = 141

    vertex_obj_mouth = [[0 for x in range(3)]
                        for y in range(num_vertx_obj_mouth)]

    for ey in range(num_vertx_obj_mouth):

        vertex_obj_mouth[ey][
            0] = verticesMouth[ey].x + delta_nose_x  #- d_h_x_ #+ d_eyes_x
        vertex_obj_mouth[ey][
            1] = verticesMouth[ey].y + delta_nose_y  #- d_h_y_ #+ d_eyes_y
        vertex_obj_mouth[ey][2] = verticesMouth[
            ey].z + delta_nose_z  #- d_h_z_ #+ d_eyes_z #- delta_eyes_z

    #fix_y_factor = 0.35
    fix_y_factor = 0.1
    fix_z_factor = 0.1

    for e in range(len(vertex_obj_mouth)):
        #print(verticesForma_ref[i])
        verticesMouth[e].x = vertex_obj_mouth[e][0]
        verticesMouth[e].y = vertex_obj_mouth[e][1]  #+ fix_y_factor
        verticesMouth[e].z = vertex_obj_mouth[e][2]  #+ fix_z_factor

    if female:
        path_m = 'FaceToSkyrimGenerated/Data/Meshes/RazaFacegen/Female/output_mouth.nif'
    else:
        path_m = 'FaceToSkyrimGenerated/Data/Meshes/RazaFacegen/Male/output_mouth.nif'

    stream2 = open(path_m, 'wb')
    dataM.write(stream2)
    stream2.close()

    tex_folder = Path(f_tex)
    if female:
        plus = 'FaceToSkyrimGenerated/Data/Textures/RazaFacegen/Female/output_texture.dds'
    else:
        plus = 'FaceToSkyrimGenerated/Data/Textures/RazaFacegen/Male/output_texture.dds'

    copyfile(tex_folder, plus)

    ###############################
    # CREATE ZIP FILE WITH FOLDER (RACE MOD)
    ###############################
    shutil.make_archive('FaceToSkyrimGenerated', 'zip',
                        'FaceToSkyrimGenerated')
Esempio n. 28
0
##        for r in result[1]:
##            print "New Triangle[%d]: (%f, %f, %f)" % (i, r.Position[0], r.Position[1], r.Position[2])
##            i = i + 1
##            if i > 5:
##                break
##        i = 0
##        for f in result[3]:
##            print "New Face[%d]: [%d, %d, %d]" % (i, f[0], f[1], f[2])
##            i = i + 1
##            if i > 5:
##                break

input_filename = 'meshes/floraUbcUtreeU01.nif'
#input_filename = 'meshes/cessna.nif'
fstream = open(input_filename, 'rb')
x = NifFormat.Data()
x.read(fstream)
fstream.close()

for root in x.roots:
    for block in root.tree():
        if isinstance(block, NifFormat.NiTriShapeData):
            if block.has_vertices and block.has_triangles:
                if block.num_vertices < 10000:
                    PMBlock(block)

output_filename = input_filename.lower().replace(".nif", "_reduced.nif")
ostream = open(output_filename, 'wb')
x.write(ostream)
ostream.close()