diff --git a/plugin/import_bani.py b/plugin/import_bani.py index 62253072d..6fcc4fd3c 100644 --- a/plugin/import_bani.py +++ b/plugin/import_bani.py @@ -8,6 +8,7 @@ from generated.formats.bani import BaniFile from plugin.modules_export.armature import get_armature from plugin.modules_import.anim import create_anim, Animation +from plugin.utils.matrix_util import Corrector from plugin.utils.object import create_ob @@ -27,7 +28,7 @@ def load_bani(file_path): def load(files=[], filepath="", set_fps=False): - return load_old(files, filepath, set_fps) + # return load_old(files, filepath, set_fps) dirname, filename = os.path.split(filepath) bani = load_bani(filepath) bani.read_banis() @@ -46,114 +47,117 @@ def load(files=[], filepath="", set_fps=False): p_bones = sorted(b_armature_ob.pose.bones, key=lambda pbone: pbone["index"]) bones_table = [(bone["index"], bone.name) for bone in p_bones] bone_names = [tup[1] for tup in bones_table] - anim_sys = Animation() - # assert( len(bone_names) == len(data.bones_frames_eulers) == len(data.bones_frames_locs) ) - b_action = anim_sys.create_action(b_armature_ob, filename) - # go over list of euler keys - # for bone_i, bone_name in bones_table: - # empty = create_ob(scene, bone_name, None) - # empty.scale = (0.01, 0.01, 0.01) - # for frame_i in range(bani.data.num_frames): - # bpy.context.scene.frame_set(frame_i) - # euler = data.eulers[frame_i, bone_i] - # loc = data.locs[frame_i, bone_i] - # bpy.context.scene.frame_set(frame_i) - # empty.location = loc - # empty.keyframe_insert(data_path="location", frame=frame_i) - - # every bone is rotated without respect to the parent bone - # for the fcurves, we need to store it relative to the parent bone, which is keyframed - # to compensate, we have to accumulate the rotations for each frame in a tree-like structure - # use blender bone index and children to build the tree structure - # then multiply a bone's key with the inverse of its parent's key matrix - fcurves_rot = [anim_sys.create_fcurves(b_action, "rotation_quaternion", range(4), None, bone_name) for bone_name in bone_names] - child_indices_map = [[pchild["index"] for pchild in pbone.children_recursive] for pbone in p_bones] - rest_matrices_armature_space = [b_armature_ob.data.bones[bone_name].matrix_local for bone_name in bone_names] - - def get_p_index(pbone): - if pbone: - return pbone["index"] - else: - return None - - parent_index_map = [get_p_index(pbone.parent) for pbone in p_bones] - for frame_i in range(bani.data.num_frames): - logging.info(f"Frame {frame_i}") - frame_eulers = [mathutils.Euler([math.radians(k) for k in euler]) for euler in bani.eulers[frame_i]] - print(frame_eulers) - frame_keys = [euler.to_matrix().to_4x4() for euler in frame_eulers] - # frame_keys = [mathutils.Euler([0.0 for k in euler]).to_matrix().to_4x4() for euler in bani.eulers[frame_i]] - accum_mats = [mathutils.Matrix() for _ in bone_names] - for key_mat, accum_mat, child_indices, parent_index, fcurves, bind in zip( - frame_keys, accum_mats, child_indices_map, parent_index_map, fcurves_rot, rest_matrices_armature_space): - accum_mat @= key_mat - for i in child_indices: - accum_mats[i] @= key_mat - if parent_index is not None: - rel_mat = accum_mats[parent_index].inverted() @ accum_mat - else: - rel_mat = accum_mat - # the eulers are applied globally to the bone, equivalent to the user doing R+X, R+Y, R+Z for each axis. - # this expresses the rotation that should be done in blender coordinates about the center of the bone - space_corrected_rot = global_corr_mat @ rel_mat - # space_corrected_rot = rel_mat + animate_empties(anim_sys, bones_table, bani, scene, b_armature_ob, filename) - # rot_mat is the final armature space matrix of the posed bone - # rot_mat = space_corrected_rot @ armature_space_matrix - space_corrected_rot = bind @ space_corrected_rot @ bind.inverted() - # key = corrector.nif_bind_to_blender_bind(key.to_matrix().to_4x4())\ - key = space_corrected_rot.to_quaternion() - # key = (global_corr_mat @ key.to_matrix().to_4x4()).to_quaternion() - anim_sys.add_key(fcurves, frame_i, key, interp_loc) - # break - # for bone_i, bone_name in bones_table: - # # get pose pbone - # pbone = b_armature_ob.pose.bones[bone_name] - # # pbone.rotation_mode = "XYZ" - # # data_type = "rotation_euler" - # # get object mode bone - # obone = b_armature_ob.data.bones[bone_name] - # armature_space_matrix = obone.matrix_local - # - # # bpy.context.scene.frame_set(frame_i) - # euler = bani.eulers[frame_i, bone_i] - # loc = bani.locs[frame_i, bone_i] - # # convert to radians - # # euler = mathutils.Euler([math.radians(k) for k in euler]) - # - # # the eulers are applied globally to the bone, equivalent to the user doing R+X, R+Y, R+Z for each axis. - # # this expresses the rotation that should be done in blender coordinates about the center of the bone - # space_corrected_rot = global_corr_mat @ euler.to_matrix().to_4x4() - # - # # rot_mat is the final armature space matrix of the posed bone - # rot_mat = space_corrected_rot @ armature_space_matrix - # - # # for fcurve, k in zip(fcu, e): - # # fcurve.keyframe_points.insert(frame_i, k)#.interpolation = "Linear" - # - # # experiments - # # trans = (global_corr_mat @ mathutils.Vector(loc)) + armature_space_matrix.translation - # - # # mdl2 vectors: (-x,-z,y) - # # loc = mathutils.Vector((-loc[0], -loc[2], loc[1])) - # loc = mathutils.Vector(loc) - # # trans = (mathutils.Vector(loc)) + armature_space_matrix.translation - # # rot_mat.translation = (space_corrected_rot @ loc) + armature_space_matrix.translation - # # loc_key = (space_corrected_rot @ mathutils.Vector(loc)) - # loc_key = (euler.to_matrix().to_4x4() @ loc) - # # loc_key = ( loc @ space_corrected_rot) - # # loc_key = mathutils.Vector((-loc_key[0], -loc_key[2], loc_key[1])) - # # rot_mat.translation = loc_key + armature_space_matrix.translation - # # the ideal translation as calculated by blender - # rot_mat.translation = pbone.matrix.translation - # # print(rot_mat) - # pbone.matrix = rot_mat - # - # # pbone.keyframe_insert(data_path="rotation_euler", frame=frame_i, group=bone_name) - # # pbone.keyframe_insert(data_path="location", frame=frame_i, group=bone_name) return {'FINISHED'} + # # assert( len(bone_names) == len(data.bones_frames_eulers) == len(data.bones_frames_locs) ) + # b_action = anim_sys.create_action(b_armature_ob, filename) + # # go over list of euler keys + # # for bone_i, bone_name in bones_table: + # # b_empty_ob = create_ob(scene, bone_name, None) + # # b_empty_ob.scale = (0.01, 0.01, 0.01) + # # for frame_i in range(bani.data.num_frames): + # # bpy.context.scene.frame_set(frame_i) + # # euler = data.eulers[frame_i, bone_i] + # # loc = data.locs[frame_i, bone_i] + # # bpy.context.scene.frame_set(frame_i) + # # b_empty_ob.location = loc + # # b_empty_ob.keyframe_insert(data_path="location", frame=frame_i) + # + # # every bone is rotated without respect to the parent bone + # # for the fcurves, we need to store it relative to the parent bone, which is keyframed + # # to compensate, we have to accumulate the rotations for each frame in a tree-like structure + # # use blender bone index and children to build the tree structure + # # then multiply a bone's key with the inverse of its parent's key matrix + # fcurves_rot = [anim_sys.create_fcurves(b_action, "rotation_quaternion", range(4), None, bone_name) for bone_name in bone_names] + # child_indices_map = [[pchild["index"] for pchild in pbone.children_recursive] for pbone in p_bones] + # rest_matrices_armature_space = [b_armature_ob.data.bones[bone_name].matrix_local for bone_name in bone_names] + # + # def get_p_index(pbone): + # if pbone: + # return pbone["index"] + # else: + # return None + # + # parent_index_map = [get_p_index(pbone.parent) for pbone in p_bones] + # for frame_i in range(bani.data.num_frames): + # logging.info(f"Frame {frame_i}") + # frame_eulers = [mathutils.Euler([math.radians(k) for k in euler]) for euler in bani.eulers[frame_i]] + # print(frame_eulers) + # frame_keys = [euler.to_matrix().to_4x4() for euler in frame_eulers] + # # frame_keys = [mathutils.Euler([0.0 for k in euler]).to_matrix().to_4x4() for euler in bani.eulers[frame_i]] + # accum_mats = [mathutils.Matrix() for _ in bone_names] + # for key_mat, accum_mat, child_indices, parent_index, fcurves, bind in zip( + # frame_keys, accum_mats, child_indices_map, parent_index_map, fcurves_rot, rest_matrices_armature_space): + # accum_mat @= key_mat + # for i in child_indices: + # accum_mats[i] @= key_mat + # if parent_index is not None: + # rel_mat = accum_mats[parent_index].inverted() @ accum_mat + # else: + # rel_mat = accum_mat + # # the eulers are applied globally to the bone, equivalent to the user doing R+X, R+Y, R+Z for each axis. + # # this expresses the rotation that should be done in blender coordinates about the center of the bone + # space_corrected_rot = global_corr_mat @ rel_mat + # # space_corrected_rot = rel_mat + # + # # rot_mat is the final armature space matrix of the posed bone + # # rot_mat = space_corrected_rot @ armature_space_matrix + # space_corrected_rot = bind @ space_corrected_rot @ bind.inverted() + # # key = corrector.nif_bind_to_blender_bind(key.to_matrix().to_4x4())\ + # key = space_corrected_rot.to_quaternion() + # # key = (global_corr_mat @ key.to_matrix().to_4x4()).to_quaternion() + # anim_sys.add_key(fcurves, frame_i, key, interp_loc) + # # break + # # for bone_i, bone_name in bones_table: + # # # get pose pbone + # # pbone = b_armature_ob.pose.bones[bone_name] + # # # pbone.rotation_mode = "XYZ" + # # # data_type = "rotation_euler" + # # # get object mode bone + # # obone = b_armature_ob.data.bones[bone_name] + # # armature_space_matrix = obone.matrix_local + # # + # # # bpy.context.scene.frame_set(frame_i) + # # euler = bani.eulers[frame_i, bone_i] + # # loc = bani.locs[frame_i, bone_i] + # # # convert to radians + # # # euler = mathutils.Euler([math.radians(k) for k in euler]) + # # + # # # the eulers are applied globally to the bone, equivalent to the user doing R+X, R+Y, R+Z for each axis. + # # # this expresses the rotation that should be done in blender coordinates about the center of the bone + # # space_corrected_rot = global_corr_mat @ euler.to_matrix().to_4x4() + # # + # # # rot_mat is the final armature space matrix of the posed bone + # # rot_mat = space_corrected_rot @ armature_space_matrix + # # + # # # for fcurve, k in zip(fcu, e): + # # # fcurve.keyframe_points.insert(frame_i, k)#.interpolation = "Linear" + # # + # # # experiments + # # # trans = (global_corr_mat @ mathutils.Vector(loc)) + armature_space_matrix.translation + # # + # # # mdl2 vectors: (-x,-z,y) + # # # loc = mathutils.Vector((-loc[0], -loc[2], loc[1])) + # # loc = mathutils.Vector(loc) + # # # trans = (mathutils.Vector(loc)) + armature_space_matrix.translation + # # # rot_mat.translation = (space_corrected_rot @ loc) + armature_space_matrix.translation + # # # loc_key = (space_corrected_rot @ mathutils.Vector(loc)) + # # loc_key = (euler.to_matrix().to_4x4() @ loc) + # # # loc_key = ( loc @ space_corrected_rot) + # # # loc_key = mathutils.Vector((-loc_key[0], -loc_key[2], loc_key[1])) + # # # rot_mat.translation = loc_key + armature_space_matrix.translation + # # # the ideal translation as calculated by blender + # # rot_mat.translation = pbone.matrix.translation + # # # print(rot_mat) + # # pbone.matrix = rot_mat + # # + # # # pbone.keyframe_insert(data_path="rotation_euler", frame=frame_i, group=bone_name) + # # # pbone.keyframe_insert(data_path="location", frame=frame_i, group=bone_name) + # return {'FINISHED'} + def load_old(files=[], filepath="", set_fps=False): dirname, filename = os.path.split(filepath) @@ -176,7 +180,7 @@ def load_old(files=[], filepath="", set_fps=False): # assert( len(bone_names) == len(data.bones_frames_eulers) == len(data.bones_frames_locs) ) action = create_anim(ob, filename) - animate_empties(bones_table, data, scene, ob) + # animate_empties(anim_sys, bones_table, data, scene, ob) for i, bone_name in bones_table: print(i, bone_name) @@ -229,20 +233,28 @@ def load_old(files=[], filepath="", set_fps=False): return {'FINISHED'} -def animate_empties(bones_table, bani, scene, armature_ob): +def animate_empties(anim_sys, bones_table, bani, scene, armature_ob, filename): + corrector = Corrector(False) # go over list of euler keys for i, bone_name in bones_table: - empty = create_ob(scene, bone_name, None) + b_empty_ob = create_ob(scene, bone_name, None) + b_empty_ob.rotation_mode = "QUATERNION" + b_action = anim_sys.create_action(b_empty_ob, f"{filename}.{bone_name}") + bind = armature_ob.data.bones[bone_name].matrix_local + bind = corrector.blender_bind_to_nif_bind(bind) + inv_bind = bind.inverted() bind_loc = armature_ob.data.bones[bone_name].matrix_local.translation # bind_loc_inv = bind_loc.negate() - logging.info(f"Bone {bone_name} as empty, bind at {bind_loc}") + # fcurves_rot = anim_sys.create_fcurves(b_action, "rotation_quaternion", range(4), None, bone_name) + # fcurves_loc = anim_sys.create_fcurves(b_action, "location", range(3), None, bone_name) + # just object fcurves for now + fcurves_rot = anim_sys.create_fcurves(b_action, "rotation_quaternion", range(4)) + fcurves_loc = anim_sys.create_fcurves(b_action, "location", range(3)) + # logging.info(f"Bone {bone_name} as empty, bind at {bind_loc}") for frame_i in range(bani.data.num_frames): - bpy.context.scene.frame_set(frame_i) euler = bani.eulers[frame_i, i] euler = mathutils.Euler([math.radians(k) for k in euler]) rot = global_corr_mat @ euler.to_matrix().to_4x4() - # e_fixed = rot.to_euler() - # e_fixed = mathutils.Euler((-e_fixed[0], -e_fixed[1], -e_fixed[2])) loc = bani.locs[frame_i, i] # this seems to be absolutely correct for JWE2 tuna loc = mathutils.Vector((loc[0], loc[2], -loc[1])) @@ -253,17 +265,24 @@ def animate_empties(bones_table, bani, scene, armature_ob): corr.rotate(rot.inverted()) # corr.rotate(e_fixed) # go back to pose position - loc = corr - bind_loc + loc_final = corr - bind_loc - bpy.context.scene.frame_set(frame_i) - # empty.matrix_local = rot # euler y and z need to be negated e_fixed = rot.to_euler() - e_fixed = (e_fixed[0], -e_fixed[1], -e_fixed[2]) - empty.rotation_euler = e_fixed - empty.location = loc - empty.keyframe_insert(data_path="location", frame=frame_i) - # empty.keyframe_insert(data_path="rotation_quaternion", frame=frame_i) - empty.keyframe_insert(data_path="rotation_euler", frame=frame_i) - # break - empty.scale = (0.01, 0.01, 0.01) + e_fixed = mathutils.Euler((e_fixed[0], -e_fixed[1], -e_fixed[2])) + rot_final = e_fixed.to_quaternion() + + # assuming the transform is stored relative to the inverse skin bind transform + # some attempts, no success yet + # euler = bani.eulers[frame_i, i] + # loc = bani.locs[frame_i, i] + # euler = mathutils.Euler([math.radians(k) for k in euler]) + # key = euler.to_matrix().to_4x4() + # key.translation = loc + # # key = inv_bind @ key.inverted() @ bind + # key = bind @ key.inverted() @ inv_bind + # rot_final = key.to_quaternion() + # loc_final = key.translation + anim_sys.add_key(fcurves_rot, frame_i, rot_final, interp_loc) + anim_sys.add_key(fcurves_loc, frame_i, loc_final, interp_loc) + b_empty_ob.scale = (0.01, 0.01, 0.01) diff --git a/version.txt b/version.txt index 243f2d27c..0f914ad72 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -8433979b7 - Tue Sep 19 21:34:22 2023 +0200 \ No newline at end of file +afc0d5f10 - Wed Sep 20 18:44:05 2023 +0200 \ No newline at end of file