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])
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)
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
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
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
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()))
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")
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()
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")
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)
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
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
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'}
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
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")
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
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)
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
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'}
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)
#!/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)
def n_create_data(): n_data = NifFormat.Data() n_data.version = 0x4000002 n_create_blocks(n_data) return n_data
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')
## 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()