diff --git a/io_scene_niftools/__init__.py b/io_scene_niftools/__init__.py index 26f8138a1..54ef4aedc 100644 --- a/io_scene_niftools/__init__.py +++ b/io_scene_niftools/__init__.py @@ -46,10 +46,10 @@ # Blender addon info. bl_info = { "name": "NetImmerse/Gamebryo format support", - "description": "Import and export files in the NetImmerse/Gamebryo formats (.nif, .kf, .egm)", + "description": "Import and export files in the NetImmerse/Gamebryo formats (.nif, .kf, .kfa, .egm)", "author": "Niftools team", "blender": (2, 82, 0), - "version": (0, 0, 14), # can't read from VERSION, blender wants it hardcoded + "version": (0, 0, 15), # can't read from VERSION, blender wants it hardcoded "api": 39257, "location": "File > Import-Export", "warning": "Generally stable port of the Niftool's Blender NifScripts, many improvements, still work in progress", diff --git a/io_scene_niftools/kfa_export.py b/io_scene_niftools/kfa_export.py new file mode 100644 index 000000000..da00231c5 --- /dev/null +++ b/io_scene_niftools/kfa_export.py @@ -0,0 +1,118 @@ +"""This script imports Netimmerse/Gamebryo nif files to Blender.""" + +# ***** BEGIN LICENSE BLOCK ***** +# +# Copyright © 2019, NIF File Format Library and Tools contributors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# * Neither the name of the NIF File Format Library and Tools +# project nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# ***** END LICENSE BLOCK ***** + +import os +import bpy + +import pyffi.spells.nif.fix + +from io_scene_niftools.file_io.kf import KFFile +from io_scene_niftools.modules.nif_export import armature +from io_scene_niftools.modules.nif_export.animation.transform import TransformAnimation +from io_scene_niftools.nif_common import NifCommon +from io_scene_niftools.utils import math +from io_scene_niftools.utils.singleton import NifOp, NifData +from io_scene_niftools.utils.logging import NifLog, NifError +from io_scene_niftools.modules.nif_export import scene +from io_scene_niftools.modules.nif_export.block_registry import block_store + + +class KfaExport(NifCommon): + + def __init__(self, operator, context): + NifCommon.__init__(self, operator, context) + + # Helper systems + self.transform_anim = TransformAnimation() + + 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 = "" + self.version, data = scene.get_version_data() + 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") + kfa_root = self.transform_anim.export_kfa_root(b_armature) + + # write kfa + ext = ".kfa" + NifLog.info(f"Writing {prefix}{ext} file") + + data.roots = [] + # first NiNode + data.roots.append(kfa_root) + + # remaining NiNodes : corresponding to first bone position computed via NiKeyframeController + kfc = kfa_root.controller + + while kfc != None: + node_root = block_store.create_block("NiNode") + # TODO : rotation + # node_root.rotation = compute from kfc.data.quaternion_keys[0].value + node_root.translation = kfc.data.translations.keys[0].value*NifOp.props.scale_correction + # scale to improve + node_root.scale = 1.0 + data.roots.append(node_root) + kfc = kfc.next_controller + + # scale correction for the skeleton + self.apply_scale(data, round(1 / NifOp.props.scale_correction)) + + kfafile = os.path.join(directory, prefix + filebase + ext) + with open(kfafile, "wb") as stream: + data.write(stream) + + NifLog.info("Finished successfully") + return {'FINISHED'} + diff --git a/io_scene_niftools/kfa_import.py b/io_scene_niftools/kfa_import.py new file mode 100644 index 000000000..e0ab5e971 --- /dev/null +++ b/io_scene_niftools/kfa_import.py @@ -0,0 +1,91 @@ +"""This script imports Netimmerse/Gamebryo nif files to Blender.""" + +# ***** BEGIN LICENSE BLOCK ***** +# +# Copyright © 2019, NIF File Format Library and Tools contributors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# * Neither the name of the NIF File Format Library and Tools +# project nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# ***** END LICENSE BLOCK ***** + +import os + +import pyffi.spells.nif.fix + +from io_scene_niftools.file_io.kf import KFFile +from io_scene_niftools.modules.nif_export import armature +from io_scene_niftools.modules.nif_import.animation.transform import TransformAnimation +from io_scene_niftools.nif_common import NifCommon +from io_scene_niftools.utils import math +from io_scene_niftools.utils.singleton import NifOp +from io_scene_niftools.utils.logging import NifLog, NifError + + +class KfaImport(NifCommon): + + def __init__(self, operator, context): + NifCommon.__init__(self, operator, context) + + # Helper systems + self.transform_anim = TransformAnimation() + + def execute(self): + """Main import function.""" + + try: + dirname = os.path.dirname(NifOp.props.filepath) + kfa_files = [os.path.join(dirname, file.name) for file in NifOp.props.files if file.name.lower().endswith(".kfa")] + # 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 kfa_file in kfa_files: + kfadata = KFFile.load_kf(kfa_file) + + self.apply_scale(kfadata, NifOp.props.scale_correction) + + # calculate and set frames per second + self.transform_anim.set_frames_per_second(kfadata.roots) + # verify if NiNodes are present + if len(kfadata.roots)>0 : + kfa_root=kfadata.roots[0] + # no usage identified for others NiNode, so taking care only of the first one + self.transform_anim.import_kfa_root(kfa_root, b_armature) + + except NifError: + return {'CANCELLED'} + + NifLog.info("Finished successfully") + return {'FINISHED'} diff --git a/io_scene_niftools/modules/nif_export/animation/transform.py b/io_scene_niftools/modules/nif_export/animation/transform.py index 964660061..ea60de892 100644 --- a/io_scene_niftools/modules/nif_export/animation/transform.py +++ b/io_scene_niftools/modules/nif_export/animation/transform.py @@ -46,7 +46,9 @@ from io_scene_niftools.modules.nif_export.block_registry import block_store from io_scene_niftools.utils import math, consts from io_scene_niftools.utils.logging import NifError, NifLog +from io_scene_niftools.utils.bones_mapper import BonesMapper +from io_scene_niftools.modules.nif_export import block_registry class TransformAnimation(Animation): @@ -119,6 +121,75 @@ def export_kf_root(self, b_armature=None): kf_root.target_name = targetname return kf_root + + def export_kfa_root(self, b_armature=None): + """Creates and returns a KFA root block and exports controllers for objects and bones""" + scene = bpy.context.scene + game = scene.niftools_scene.game + kfa_root = block_store.create_block("NiNode") + + anim_textextra = self.create_text_keys(kfa_root) + targetname = "Scene Root" + + # mapping between bones name and id + bonename_dict = BonesMapper.bonename_dict + + # per-node animation + if b_armature: + b_action = self.get_active_action(b_armature) + # generate array of NiStringExtraData to indice the bones list used + extraDataList = [] + data_nb=0 + for b_bone in b_armature.data.bones: + # get bone name + bone_name = block_registry.ExportBlockRegistry.get_bone_name_for_nif(b_bone.name) + # retrieve the bone id from the bone name + bone_id = bonename_dict.get(bone_name) + + if bone_id != None: + if (b_bone and b_bone.name in b_action.groups) or (not b_bone) : + # NiStringExtraData corresponding to bone + extraData = block_store.create_block("NiStringExtraData") + extraData.name="NiStringED"+'%03d'%data_nb + extraData.string_data = str(bone_id) + extraDataList.append(extraData) + # Create and add the NiKeyframeController corresponding to the bone in the chained list + self.export_transforms(kfa_root, b_armature, b_action, b_bone) + data_nb = data_nb +1 + + kfa_root.set_extra_datas(extraDataList) + + # 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" + + # per-object animation + else: + for b_obj in bpy.data.objects: + b_action = self.get_active_action(b_obj) + self.export_transforms(kfa_root, b_obj, b_action) + + #self.export_text_keys(b_action, anim_textextra) + + kfa_root.name = b_action.name + kfa_root.unknown_int_1 = 1 + kfa_root.weight = 1.0 + kfa_root.cycle_type = NifFormat.CycleType.CYCLE_CLAMP + kfa_root.frequency = 1.0 + + if anim_textextra.num_text_keys > 0: + kfa_root.start_time = anim_textextra.text_keys[0].time + kfa_root.stop_time = anim_textextra.text_keys[anim_textextra.num_text_keys - 1].time + else: + kfa_root.start_time = scene.frame_start / self.fps + kfa_root.stop_time = scene.frame_end / self.fps + + kfa_root.target_name = targetname + return kfa_root + + def export_transforms(self, parent_block, b_obj, b_action, bone=None): """ If bone == None, object level animation is exported. @@ -174,6 +245,7 @@ def export_transforms(self, parent_block, b_obj, b_action, bone=None): # decompose the bind matrix bind_scale, bind_rot, bind_trans = math.decompose_srt(bind_matrix) + n_kfc, n_kfi = self.create_controller(parent_block, target_name, priority) # fill in the non-trivial values @@ -273,7 +345,8 @@ def export_transforms(self, parent_block, b_obj, b_action, bone=None): for key, (frame, scale) in zip(n_kfd.scales.keys, scale_curve): key.time = frame / self.fps key.value = scale - + + def create_text_keys(self, kf_root): """Create the text keys before filling in the data so that the extra data hierarchy is correct""" # add a NiTextKeyExtraData block diff --git a/io_scene_niftools/modules/nif_import/animation/transform.py b/io_scene_niftools/modules/nif_import/animation/transform.py index ce62370e0..d4468b5bb 100644 --- a/io_scene_niftools/modules/nif_import/animation/transform.py +++ b/io_scene_niftools/modules/nif_import/animation/transform.py @@ -49,7 +49,7 @@ from io_scene_niftools.utils import math from io_scene_niftools.utils.blocks import safe_decode from io_scene_niftools.utils.logging import NifLog - +from io_scene_niftools.utils.bones_mapper import BonesMapper def interpolate(x_out, x_in, y_in): """ @@ -174,6 +174,54 @@ def import_controller_sequence(self, kf_root, b_armature_obj): extend = self.get_extend_from_cycle_type(kf_root.cycle_type) self.set_extrapolation(extend, b_action.fcurves) + def import_kfa_root(self, kf_root, b_armature_obj): + + # extract from figures/animnode.dat + bone_names = BonesMapper.bone_names + + b_action_name = self.import_generic_kf_root(kf_root) + actions = set() + + curr_controller = kf_root.controller + + k = 1 + while curr_controller != None : + + # retrieve the corresponding bone + j = 1 + b_name = "" + + # first format : bone references are in an array + if len(kf_root.extra_data_list) > 0 : + bone_ref = kf_root.extra_data_list[k-1] + b_name = bone_names[int(bone_ref.string_data)] + + # second format : bone references are in chained list + else : + bone_ref = kf_root.extra_data + match_k = 1 + while ( bone_ref != None and match_k != k ) : + bone_ref = bone_ref.next_extra_data + match_k = match_k + 1 + b_name = bone_names[int(bone_ref.string_data)] + + data=curr_controller.data + + # retrieve bone from blender armature + b_target = self.get_target(b_armature_obj, b_name) + + # retrieve action root name + b_action_name = self.import_generic_kf_root(kf_root) + actions.add(self.import_keyframe_controller(curr_controller, b_armature_obj, b_target, b_action_name)) + + curr_controller = curr_controller.next_controller + k = k +1 + + for b_action in actions: + if b_action: + self.import_text_keys(kf_root, b_action) + + def import_keyframe_controller(self, n_kfc, b_armature, b_target, b_action_name): """ Imports a keyframe controller as fcurves in an action, which is created if necessary. diff --git a/io_scene_niftools/operators/__init__.py b/io_scene_niftools/operators/__init__.py index b5058d878..97dab04bd 100644 --- a/io_scene_niftools/operators/__init__.py +++ b/io_scene_niftools/operators/__init__.py @@ -40,13 +40,14 @@ import bpy from io_scene_niftools.utils.decorators import register_modules, unregister_modules -from io_scene_niftools.operators import object, geometry, nif_import_op, nif_export_op, kf_import_op, egm_import_op, kf_export_op +from io_scene_niftools.operators import object, geometry, nif_import_op, nif_export_op, kf_import_op, kfa_import_op, egm_import_op, kf_export_op, kfa_export_op # noinspection PyUnusedLocal def menu_func_import(self, context): self.layout.operator(nif_import_op.NifImportOperator.bl_idname, text="NetImmerse/Gamebryo (.nif)") self.layout.operator(kf_import_op.KfImportOperator.bl_idname, text="NetImmerse/Gamebryo (.kf)") + self.layout.operator(kfa_import_op.KfaImportOperator.bl_idname, text="NetImmerse/Gamebryo (.kfa)") self.layout.operator(egm_import_op.EgmImportOperator.bl_idname, text="NetImmerse/Gamebryo (.egm)") # TODO [general] get default path from config registry # default_path = bpy.data.filename.replace(".blend", ".nif") @@ -57,9 +58,10 @@ def menu_func_import(self, context): def menu_func_export(self, context): self.layout.operator(nif_export_op.NifExportOperator.bl_idname, text="NetImmerse/Gamebryo (.nif)") self.layout.operator(kf_export_op.KfExportOperator.bl_idname, text="NetImmerse/Gamebryo (.kf)") + self.layout.operator(kfa_export_op.KfaExportOperator.bl_idname, text="NetImmerse/Gamebryo (.kfa)") -MODS = [object, geometry, nif_import_op, nif_export_op, kf_import_op, kf_export_op, egm_import_op] +MODS = [object, geometry, nif_import_op, nif_export_op, kf_import_op, kfa_import_op, kf_export_op, kfa_export_op, egm_import_op] def register(): diff --git a/io_scene_niftools/operators/common_op.py b/io_scene_niftools/operators/common_op.py index bf67b7c47..5eb93b909 100644 --- a/io_scene_niftools/operators/common_op.py +++ b/io_scene_niftools/operators/common_op.py @@ -128,3 +128,12 @@ class CommonKf: filter_glob: bpy.props.StringProperty( default="*.kf", options={'HIDDEN'}) + +class CommonKfa: + # Default file name extension. + filename_ext = ".kfa" + + # File name filter for file select dialog. + filter_glob: bpy.props.StringProperty( + default="*.kfa", + options={'HIDDEN'}) diff --git a/io_scene_niftools/operators/kfa_export_op.py b/io_scene_niftools/operators/kfa_export_op.py new file mode 100644 index 000000000..ffc209492 --- /dev/null +++ b/io_scene_niftools/operators/kfa_export_op.py @@ -0,0 +1,77 @@ +"""Blender Niftools Addon Main Export operators, function called through Export Menu""" + +# ***** BEGIN LICENSE BLOCK ***** +# +# Copyright © 2019, NIF File Format Library and Tools contributors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# * Neither the name of the NIF File Format Library and Tools +# project nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# ***** END LICENSE BLOCK ***** + +import bpy +from bpy.types import Operator +from bpy_extras.io_utils import ExportHelper + +from io_scene_niftools.kfa_export import KfaExport +from io_scene_niftools.operators.common_op import CommonDevOperator, CommonScale, CommonKfa +from io_scene_niftools.utils.decorators import register_classes, unregister_classes + + +class KfaExportOperator(Operator, ExportHelper, CommonDevOperator, CommonScale, CommonKfa): + """Operator for saving a kfa file.""" + + # Name of function for calling the kfa export operators. + bl_idname = "export_scene.kfa" + + # How the kfa export operators is labelled in the user interface. + bl_label = "Export KFA" + + def execute(self, context): + """Execute the export operators: first constructs a + :class:`~io_scene_niftools.nif_export.NifExport` instance and then + calls its :meth:`~io_scene_niftools.nif_export.NifExport.execute` + method. + """ + return KfaExport(self, context).execute() + + +classes = [ + KfaExportOperator +] + + +def register(): + register_classes(classes, __name__) + + +def unregister(): + unregister_classes(classes, __name__) diff --git a/io_scene_niftools/operators/kfa_import_op.py b/io_scene_niftools/operators/kfa_import_op.py new file mode 100644 index 000000000..72a1d2de2 --- /dev/null +++ b/io_scene_niftools/operators/kfa_import_op.py @@ -0,0 +1,80 @@ +"""Blender Niftools Addon Main Import operators, function called through Import Menu""" + +# ***** BEGIN LICENSE BLOCK ***** +# +# Copyright © 2019, NIF File Format Library and Tools contributors. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# +# * Neither the name of the NIF File Format Library and Tools +# project nor the names of its contributors may be used to endorse +# or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# ***** END LICENSE BLOCK ***** + +import bpy +from bpy.types import Operator, PropertyGroup +from bpy_extras.io_utils import ImportHelper + +from io_scene_niftools.kfa_import import KfaImport +from io_scene_niftools.operators.common_op import CommonDevOperator, CommonScale, CommonKfa +from io_scene_niftools.utils.decorators import register_classes, unregister_classes + + +class KfaImportOperator(Operator, ImportHelper, CommonDevOperator, CommonScale, CommonKfa): + """Operator for loading a kfa file.""" + + # Name of function for calling the nif export operators. + bl_idname = "import_scene.kfa" + + # How the nif import operators is labelled in the user interface. + bl_label = "Import KFA" + + files: bpy.props.CollectionProperty(type=PropertyGroup) + + def execute(self, context): + """Execute the import operators: first constructs a + :class:`~io_scene_niftools.kfa_import.KfaImport` instance and then + calls its :meth:`~io_scene_niftools.kfa_import.KfImport.execute` + method. + """ + + return KfaImport(self, context).execute() + + +classes = [ + KfaImportOperator +] + + +def register(): + register_classes(classes, __name__) + + +def unregister(): + unregister_classes(classes, __name__) diff --git a/io_scene_niftools/utils/bones_mapper.py b/io_scene_niftools/utils/bones_mapper.py new file mode 100644 index 000000000..32c4fb134 --- /dev/null +++ b/io_scene_niftools/utils/bones_mapper.py @@ -0,0 +1,13 @@ +""" Nif Utilities, for bones mapping """ + +class BonesMapper: + """A simple class for bones mapping.""" + + # List of possible DAOC bones + bone_names = ["Bip01","Bip01 Pelvis","Bip01 Spine","Bip01 Spine1","Bip01 Spine2","Bip01 Spine3","Bip01 Neck","Bip01 Neck1","Bip01 Neck2","Bip01 Neck3","Bip01 Neck4","Bip01 Head","Bip01 Ponytail1","Bip01 Ponytail11","Bip01 Ponytail12","Bip01 Ponytail13","Bip01 Ponytail14","Bip01 Ponytail2","Bip01 Ponytail21","Bip01 Ponytail22","Bip01 Ponytail23","Bip01 Ponytail24","Bip01 L Clavicle","Bip01 L UpperArm","Bip01 L Forearm","Bip01 L Hand","Bip01 L Finger0","Bip01 L Finger01","Bip01 L Finger02","Bip01 L Finger1","Bip01 L Finger11","Bip01 L Finger12","Bip01 L Finger2","Bip01 L Finger21","Bip01 L Finger22","Bip01 R Clavicle","Bip01 R UpperArm","Bip01 R Forearm","Bip01 R Hand","Bip01 R Finger0","Bip01 R Finger01","Bip01 R Finger02","Bip01 R Finger1","Bip01 R Finger11","Bip01 R Finger12","Bip01 R Finger2","Bip01 R Finger21","Bip01 R Finger22","Bip01 L Thigh","Bip01 L Calf","Bip01 L HorseLink","Bip01 L Foot","Bip01 L Toe0","Bip01 L Toe01","Bip01 R Thigh","Bip01 R Calf","Bip01 R HorseLink","Bip01 R Foot","Bip01 R Toe0","Bip01 R Toe01","Bip01 Tail","Bip01 Tail1","Bip01 Tail2","Bip01 Tail3","Bip01 Tail4","Bip01 L Shield","Bip01 R Shield","Bip01 L Held","Bip01 R Held","Bip01 L Belt","Bip01 R Belt","Bip01 L Back","Bip01 R Back","Bip01 Helm","Bip01 Ext01","Bip01 Ext02","Bip01 Ext03","Bip01 Ext04","Bip01 Ext05","Bip01 Ext06","Bip01 Ext07","Bip01 Ext08","Bip01 Ext09","Bip01 Ext10","Bip01 Ext11","Bip01 Ext12","Bip01 Ext13","Bip01 Ext14","Bip01 Ext15","Bip01 Ext16","Bip01 Ext17","Bip01 Ext18","Bip01 Ext19","Bip01 Ext20","Bip01 L Finger3","Bip01 L Finger31","Bip01 L Finger32","Bip01 L Finger4","Bip01 L Finger41","Bip01 L Finger42","Bip01 R Finger3","Bip01 R Finger31","Bip01 R Finger32","Bip01 R Finger4","Bip01 R Finger41","Bip01 R Finger42","Bip01 L Toe02","Bip01 L Toe03","Bip01 L Toe1","Bip01 L Toe11","Bip01 L Toe12","Bip01 L Toe2","Bip01 L Toe21","Bip01 L Toe22","Bip01 R Toe02","Bip01 R Toe03","Bip01 R Toe1","Bip01 R Toe11","Bip01 R Toe12","Bip01 R Toe2","Bip01 R Toe21","Bip01 R Toe22","Bip01 L ForeTwist","Bip01 L ForeTwist1","Bip01 R ForeTwist","Bip01 R ForeTwist1","Bip01 EyeLids","Bip01 L BicepTwist","Bip01 R BicepTwist","Bip01 L Pauldron","Bip01 R Pauldron","Bip01 Beard1","Bip01 Beard2","Bip01 FrontRobe1","Bip01 FrontRobe2","Bip01 BackRobe1","Bip01 BackRobe2","Bip01 C Cloak01","Bip01 C Cloak02","Bip01 C Cloak03","Bip01 C Cloak04","Bip01 C Cloak05","Bip01 L Cloak01","Bip01 L Cloak02","Bip01 L Cloak03","Bip01 L Cloak04","Bip01 L Cloak05","Bip01 R Cloak01","Bip01 R Cloak02","Bip01 R Cloak03","Bip01 R Cloak04","Bip01 R Cloak05","Bip01 C CloakIKChain","Bip01 L CloakIKChain","Bip01 R CloakIKChain","Bip01 L ThighTwist","Bip01 R ThighTwist","R Cloak Control01","L Cloak Control01","C Cloak Control01","C Cloak Control02","C Cloak Control03","C Cloak Control04","C Cloak Control05","C Cloak Control06","L Cloak Control02","L Cloak Control03","L Cloak Control04","L Cloak Control05","L Cloak Control06","R Cloak Control02","R Cloak Control03","R Cloak Control04","R Cloak Control05","R Cloak Control06"] + + # Dictionnary to map bones names to id + bonename_dict = dict((j,i) for i,j in enumerate(bone_names)) + + +