def execute(self): """Main import function.""" try: dirname = os.path.dirname(NifOp.props.filepath) kf_files = [os.path.join(dirname, file.name) for file in NifOp.props.files if file.name.lower().endswith(".kf")] # if an armature is present, prepare the bones for all actions b_armature = math.get_armature() if b_armature: # the axes used for bone correction depend on the armature in our scene math.set_bone_orientation(b_armature.data.niftools.axis_forward, b_armature.data.niftools.axis_up) # get nif space bind pose of armature here for all anims self.transform_anim.get_bind_data(b_armature) for kf_file in kf_files: kfdata = KFFile.load_kf(kf_file) self.apply_scale(kfdata, NifOp.props.scale_correction) # calculate and set frames per second self.transform_anim.set_frames_per_second(kfdata.roots) for kf_root in kfdata.roots: self.transform_anim.import_kf_root(kf_root, b_armature) except NifError: return {'CANCELLED'} NifLog.info("Finished successfully") return {'FINISHED'}
def execute(self): """Main export function.""" NifLog.info(f"Exporting {NifOp.props.filepath}") # extract directory, base name, extension directory = os.path.dirname(NifOp.props.filepath) filebase, fileext = os.path.splitext( os.path.basename(NifOp.props.filepath)) if bpy.context.scene.niftools_scene.game == 'NONE': raise NifError( "You have not selected a game. Please select a game in the scene tab." ) prefix = "x" if bpy.context.scene.niftools_scene.game in ( 'MORROWIND', ) else "" self.version, data = scene.get_version_data() # todo[anim] - change to KfData, but create_controller() [and maybe more] has to be updated first NifData.init(data) b_armature = math.get_armature() # some scenes may not have an armature, so nothing to do here if b_armature: math.set_bone_orientation(b_armature.data.niftools.axis_forward, b_armature.data.niftools.axis_up) NifLog.info("Creating keyframe tree") kf_root = self.transform_anim.export_kf_root(b_armature) # write kf (and xkf if asked) ext = ".kf" NifLog.info(f"Writing {prefix}{ext} file") data.roots = [kf_root] data.neosteam = (bpy.context.scene.niftools_scene.game == 'NEOSTEAM') # scale correction for the skeleton self.apply_scale(data, round(1 / NifOp.props.scale_correction)) kffile = os.path.join(directory, prefix + filebase + ext) with open(kffile, "wb") as stream: data.write(stream) NifLog.info("Finished successfully") return {'FINISHED'}
def execute(self): """Main import function.""" try: dirname = os.path.dirname(NifOp.props.filepath) kf_files = [ os.path.join(dirname, file.name) for file in NifOp.props.files if file.name.lower().endswith(".kf") ] b_armature = math.get_armature() if not b_armature: raise NifError( "No armature was found in scene, can not import KF animation!" ) # the axes used for bone correction depend on the armature in our scene math.set_bone_orientation(b_armature.data.niftools.axis_forward, b_armature.data.niftools.axis_up) # get nif space bind pose of armature here for all anims bind_data = armature.get_bind_data(b_armature) for kf_file in kf_files: kfdata = KFFile.load_kf(kf_file) # use pyffi toaster to scale the tree toaster = pyffi.spells.nif.NifToaster() toaster.scale = NifOp.props.scale_correction pyffi.spells.nif.fix.SpellScale(data=kfdata, toaster=toaster).recurse() # calculate and set frames per second self.tranform_anim.set_frames_per_second(kfdata.roots) for kf_root in kfdata.roots: self.tranform_anim.import_kf_root(kf_root, b_armature, bind_data) except NifError: return {'CANCELLED'} NifLog.info("Finished successfully") return {'FINISHED'}
def execute(self): """Main export function.""" if bpy.context.mode != 'OBJECT': bpy.ops.object.mode_set(mode='OBJECT', toggle=False) NifLog.info(f"Exporting {NifOp.props.filepath}") # extract directory, base name, extension directory = os.path.dirname(NifOp.props.filepath) filebase, fileext = os.path.splitext( os.path.basename(NifOp.props.filepath)) block_store.block_to_obj = {} # clear out previous iteration try: # catch export errors # protect against null nif versions if bpy.context.scene.niftools_scene.game == 'NONE': raise NifError( "You have not selected a game. Please select a game and" " nif version in the scene tab.") # find all objects that do not have a parent self.exportable_objects, self.root_objects = self.objecthelper.get_export_objects( ) if not self.exportable_objects: NifLog.warn("No objects can be exported!") return {'FINISHED'} for b_obj in self.exportable_objects: if b_obj.type == 'MESH': if b_obj.parent and b_obj.parent.type == 'ARMATURE': for b_mod in b_obj.modifiers: if b_mod.type == 'ARMATURE' and b_mod.use_bone_envelopes: raise NifError( f"'{b_obj.name}': Cannot export envelope skinning. If you have vertex groups, turn off envelopes.\n" f"If you don't have vertex groups, select the bones one by one press W to " f"convert their envelopes to vertex weights, and turn off envelopes." ) # check for non-uniform transforms scale = b_obj.scale if abs(scale.x - scale.y) > NifOp.props.epsilon or abs( scale.y - scale.z) > NifOp.props.epsilon: NifLog.warn( f"Non-uniform scaling not supported.\n" f"Workaround: apply size and rotation (CTRL-A) on '{b_obj.name}'." ) b_armature = math.get_armature() # some scenes may not have an armature, so nothing to do here if b_armature: math.set_bone_orientation( b_armature.data.niftools.axis_forward, b_armature.data.niftools.axis_up) prefix = "x" if bpy.context.scene.niftools_scene.game in ( 'MORROWIND', ) else "" NifLog.info("Exporting") if NifOp.props.animation == 'ALL_NIF': NifLog.info("Exporting geometry and animation") elif NifOp.props.animation == 'GEOM_NIF': # for morrowind: everything except keyframe controllers NifLog.info("Exporting geometry only") # find nif version to write self.version, data = scene.get_version_data() NifData.init(data) # export the actual root node (the name is fixed later to avoid confusing the exporter with duplicate names) root_block = self.objecthelper.export_root_node( self.root_objects, filebase) # post-processing: # ---------------- NifLog.info("Checking controllers") if bpy.context.scene.niftools_scene.game == 'MORROWIND': # animations without keyframe animations crash the TESCS # if we are in that situation, add a trivial keyframe animation has_keyframecontrollers = False for block in block_store.block_to_obj: 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.transform_anim.create_controller( root_block, root_block.name) if NifOp.props.bs_animation_node: for block in block_store.block_to_obj: 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 bpy.context.scene.niftools_scene.game in ( 'OBLIVION', 'FALLOUT_3', 'SKYRIM') and filebase.lower() in ('skeleton', 'skeletonbeast'): self.transform_anim.add_dummy_controllers() # bhkConvexVerticesShape of children of bhkListShapes need an extra bhkConvexTransformShape (see issue #3308638, reported by Koniption) # note: block_store.block_to_obj changes during iteration, so need list copy for block in list(block_store.block_to_obj.keys()): if isinstance(block, NifFormat.bhkListShape): for i, sub_shape in enumerate(block.sub_shapes): if isinstance(sub_shape, NifFormat.bhkConvexVerticesShape): coltf = block_store.create_block( "bhkConvexTransformShape") coltf.material = sub_shape.material coltf.unknown_float_1 = 0.1 unk_8 = coltf.unknown_8_bytes unk_8[0] = 96 unk_8[1] = 120 unk_8[2] = 53 unk_8[3] = 19 unk_8[4] = 24 unk_8[5] = 9 unk_8[6] = 253 unk_8[7] = 4 coltf.transform.set_identity() coltf.shape = sub_shape block.sub_shapes[i] = coltf # export constraints for b_obj in self.exportable_objects: if b_obj.constraints: self.constrainthelper.export_constraints(b_obj, root_block) object_prop = ObjectProperty() object_prop.export_root_node_properties(root_block) # 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 block_store.block_to_obj: 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 data.roots = [root_block] scale_correction = bpy.context.scene.niftools_scene.scale_correction if abs(scale_correction) > NifOp.props.epsilon: self.apply_scale(data, round(1 / NifOp.props.scale_correction)) # also scale egm if EGMData.data: EGMData.data.apply_scale(1 / scale_correction) # generate mopps (must be done after applying scale!) if bpy.context.scene.niftools_scene.game in ('OBLIVION', 'FALLOUT_3', 'SKYRIM'): for block in block_store.block_to_obj: 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." ) # export nif file: # ---------------- if bpy.context.scene.niftools_scene.game == 'EMPIRE_EARTH_II': ext = ".nifcache" else: ext = ".nif" NifLog.info(f"Writing {ext} file") # make sure we have the right file extension if fileext.lower() != ext: NifLog.warn( f"Changing extension from {fileext} to {ext} on output file" ) niffile = os.path.join(directory, prefix + filebase + ext) data.roots = [root_block] # todo [export] I believe this is redundant and setting modification only is the current way? data.neosteam = ( bpy.context.scene.niftools_scene.game == 'NEOSTEAM') if bpy.context.scene.niftools_scene.game == 'NEOSTEAM': data.modification = "neosteam" elif bpy.context.scene.niftools_scene.game == 'ATLANTICA': data.modification = "ndoors" elif bpy.context.scene.niftools_scene.game == 'HOWLING_SWORD': data.modification = "jmihs1" with open(niffile, "wb") as stream: data.write(stream) # export egm file: # ----------------- if EGMData.data: ext = ".egm" NifLog.info(f"Writing {ext} file") egmfile = os.path.join(directory, filebase + ext) with open(egmfile, "wb") as stream: EGMData.data.write(stream) # save exported file (this is used by the test suite) self.root_blocks = [root_block] except NifError: return {'CANCELLED'} NifLog.info("Finished") return {'FINISHED'}
def import_branch(self, n_block, b_armature=None, n_armature=None): """Read the content of the current NIF tree branch to Blender recursively. :param n_block: The nif block to import. :param b_armature: The blender armature for the current branch. :param n_armature: The corresponding nif block for the armature for the current branch. """ if not n_block: return None NifLog.info(f"Importing data for block '{n_block.name.decode()}'") if isinstance(n_block, NifFormat.NiTriBasedGeom ) and NifOp.props.process != "SKELETON_ONLY": return self.objecthelper.import_geometry_object( b_armature, n_block) elif isinstance(n_block, NifFormat.NiNode): # import object if self.armaturehelper.is_armature_root(n_block): # all bones in the tree are also imported by import_armature if NifOp.props.process != "GEOMETRY_ONLY": b_obj = self.armaturehelper.import_armature(n_block) else: n_name = block_store.import_name(n_block) b_obj = math.get_armature() NifLog.info( f"Merging nif tree '{n_name}' with armature '{b_obj.name}'" ) if n_name != b_obj.name: NifLog.warn( f"Using Nif block '{n_name}' as armature '{b_obj.name}' but names do not match" ) b_armature = b_obj n_armature = n_block elif self.armaturehelper.is_bone(n_block): # bones have already been imported during import_armature n_name = block_store.import_name(n_block) if n_name in b_armature.data.bones: b_obj = b_armature.data.bones[n_name] else: # this is a fallback for a weird bug, when a node is child of a NiLodNode in a skeletal nif b_obj = self.objecthelper.create_b_obj(n_block, None, name=n_name) b_obj.niftools.flags = n_block.flags else: # import as an empty b_obj = NiTypes.import_empty(n_block) # find children b_children = [] n_children = [child for child in n_block.children] for n_child in n_children: b_child = self.import_branch(n_child, b_armature=b_armature, n_armature=n_armature) if b_child and isinstance(b_child, bpy.types.Object): b_children.append(b_child) # import collision objects & bounding box if NifOp.props.process != "SKELETON_ONLY": b_children.extend(self.import_collision(n_block)) b_children.extend( self.boundhelper.import_bounding_box(n_block)) # set bind pose for children self.objecthelper.set_object_bind(b_obj, b_children, b_armature) # import extra node data, such as node type NiTypes.import_root_collision(n_block, b_obj) NiTypes.import_billboard(n_block, b_obj) NiTypes.import_range_lod_data(n_block, b_obj, b_children) # set object transform, this must be done after all children objects have been parented to b_obj if isinstance(b_obj, bpy.types.Object): # note: bones and this object's children already have their matrix set b_obj.matrix_local = math.import_matrix(n_block) # import object level animations (non-skeletal) if NifOp.props.animation: # self.animationhelper.import_text_keys(n_block) self.transform_anim.import_transforms(n_block, b_obj) self.object_anim.import_visibility(n_block, b_obj) return b_obj # all else is currently discarded return None