diff options
author | Owl <isaclien9752@gmail.com> | 2025-08-23 00:29:45 -0400 |
---|---|---|
committer | Owl <isaclien9752@gmail.com> | 2025-08-23 00:29:45 -0400 |
commit | a5bdfdab2ec68ebfe99535a54b3b0cd8b95a87d6 (patch) | |
tree | 3cbc818cc7a9e221697fe0be05b9552fd6d33f09 /bck_export.py | |
parent | 51077c2fe8c160743a67303fb516126bb98afff7 (diff) | |
download | blenxy-a5bdfdab2ec68ebfe99535a54b3b0cd8b95a87d6.tar.gz blenxy-a5bdfdab2ec68ebfe99535a54b3b0cd8b95a87d6.zip |
bck exporter seems to be working
Diffstat (limited to 'bck_export.py')
-rw-r--r-- | bck_export.py | 133 |
1 files changed, 108 insertions, 25 deletions
diff --git a/bck_export.py b/bck_export.py index 9d8576c..9ba4427 100644 --- a/bck_export.py +++ b/bck_export.py @@ -62,20 +62,22 @@ def export_bck_func(options, context): bone_fcurves = [None, None, None, None, None, None, None, None, None] # ^ sx, rx, tx, sy (24!), ry, ty, sz, rz and tz in that order (bck order) bone_data_path_str = "pose.bones[\"%s\"]." % (data_bone.name) - for fcurve in armature.animation_data.action: + for fcurve in armature.animation_data.action.fcurves: if (fcurve.data_path == bone_data_path_str + "scale"): - bone_fcurves[0 + fcurve.array_index] = fcurve + bone_fcurves[int((3 * fcurve.array_index) + 0)] = fcurve elif (fcurve.data_path == bone_data_path_str + "rotation_euler"): - bone_fcurves[3 + fcurve.array_index] = fcurve + bone_fcurves[int((3 * fcurve.array_index) + 1)] = fcurve elif (fcurve.data_path == bone_data_path_str + "location"): - bone_fcurves[6 + fcurve.array_index] = fcurve + bone_fcurves[int((3 * fcurve.array_index) + 2)] = fcurve # generate all the animation points, interpolation stuff will be done later # get the rest pose matrix rest_mat = data_bone.matrix_local.copy() if (pose_bone.parent != None): - rest_mat = data_bone.parent.matrix_local.copy().inverted() * mat.copy() + rest_mat = data_bone.parent.matrix_local.copy().inverted() * rest_mat.copy() + else: + rest_mat = mathutils.Matrix.Identity(4) # get the points on all frames, only the points for j in range(bck_anim.anim_length): @@ -91,67 +93,142 @@ def export_bck_func(options, context): value = bone_fcurves[k].evaluate(options.first_frame + j) # check which is the component to get if (k == 0 or k == 3 or k == 6): - scale[k / 3] = value + scale[int((k - 0) / 3)] = value elif (k == 1 or k == 4 or k == 7): - rot[(k - 1) / 3] = value + rot[int((k - 1) / 3)] = value elif (k == 2 or k == 5 or k == 8): - trans[(k - 2) / 3] = value + transl[int((k - 2) / 3)] = value # convert the values to be respect to parent - new_mat = math_funcs.calc_transf_mat(scale, rot, transl).copy() * rest_mat.copy() + new_mat = rest_mat.copy() * math_funcs.calc_transf_mat(scale, rot, transl).copy() for k in range(9): value = None # check which is the component to get if (k == 0 or k == 3 or k == 6): - value = new_mat.to_scale()[k / 3] + value = new_mat.to_scale()[int((k - 0) / 3)] elif (k == 1 or k == 4 or k == 7): - value = new_mat.to_euler("XYZ")[(k - 1) / 3] + value = new_mat.to_euler("XYZ")[int((k - 1) / 3)] elif (k == 2 or k == 5 or k == 8): - value = new_mat.to_translation()[(k - 2) / 3] - bck_anim.anim_data[i].comp[k].values.append(value) + value = 100 * new_mat.to_translation()[int((k - 2) / 3)] # conversion from blenxy's coordinates + bck_anim.anim_data[i].comp[k].value.append(value) - # got all the animation points, now to decide what to do with them + # got all the animation points + # delete constant value animation tracks + for i in range(bck_anim.bone_count): + for j in range(9): + anim_track_constant = True + for k in range(bck_anim.anim_length): + if (k == 0): + continue + # check if the whole animation track is the same + comp_min_dif = options.min_dif # min difference (radians) + if (j == 0 or j == 3 or j == 6): # scale + comp_min_dif = options.min_dif / 3 + elif (j == 2 or j == 5 or j == 8): # translation + comp_min_dif = options.min_dif * 1000 + if (abs(bck_anim.anim_data[i].comp[j].value[k - 1] + - bck_anim.anim_data[i].comp[j].value[k]) > comp_min_dif): + anim_track_constant = False + break + if (anim_track_constant == True): + bck_anim.anim_data[i].comp[j].kf_count = 1 + bck_anim.anim_data[i].comp[j].interp_mode = 0 + bck_anim.anim_data[i].comp[j].time = [None] + bck_anim.anim_data[i].comp[j].value = [bck_anim.anim_data[i].comp[j].value[0]] + bck_anim.anim_data[i].comp[j].in_slope = [None] + bck_anim.anim_data[i].comp[j].out_slope = [None] + + print(bck_anim) # keep all the samples intact and calculate the slopes # using linear interpolation between consecutive frames if (options.export_type == "OPT_A"): + # assign the rest of the variables for i in range(bck_anim.bone_count): for j in range(9): - bck_anim.anim_data[i].comp[j].kf_count = len(bck_anim.anim_length) + # skip 1 keyframe animations + if (bck_anim.anim_data[i].comp[j].kf_count == 1): + continue + bck_anim.anim_data[i].comp[j].kf_count = bck_anim.anim_length bck_anim.anim_data[i].comp[j].interp_mode = 1 # has to be like this for k in range(bck_anim.anim_length): bck_anim.anim_data[i].comp[j].time.append(k) in_slope = 0 out_slope = 0 if (k > 0): - in_slope = bck_anim.anim_data[i].comp[j].values[k] - bck_anim.anim_data[i].comp[j].values[k - 1] + in_slope = bck_anim.anim_data[i].comp[j].value[k] - bck_anim.anim_data[i].comp[j].value[k - 1] if (k < bck_anim.anim_length - 1): - out_slope = bck_anim.anim_data[i].comp[j].values[k + 1] - bck_anim.anim_data[i].comp[j].values[k] + out_slope = bck_anim.anim_data[i].comp[j].value[k + 1] - bck_anim.anim_data[i].comp[j].value[k] bck_anim.anim_data[i].comp[j].in_slope.append(in_slope) bck_anim.anim_data[i].comp[j].out_slope.append(out_slope) # find "best" interpolator fits for the samples elif (options.export_type == "OPT_B"): - print() - + + # assign the rest of the variables + for i in range(bck_anim.bone_count): + # assign the best fit for each animation component + for j in range(9): + # skip 1 keyframe animations + if (bck_anim.anim_data[i].comp[j].kf_count == 1): + continue + # get the best fit interpolation result + interp_result = math_funcs.find_best_cubic_hermite_spline_fit(options.first_frame, + bck_anim.anim_data[i].comp[j].value, + options.angle_limit) + # check if the fit can be made in interpolation mode == 0 (in_slope = out_slope) + # assign the best fit for each animation component + can_use_smooth_interp = True + comp_min_dif = options.min_dif # min difference (radians) + if (j == 0 or j == 3 or j == 6): # scale + comp_min_dif = options.min_dif / 3 + elif (j == 2 or j == 5 or j == 8): # translation + comp_min_dif = options.min_dif * 1000 + for k in range(interp_result.kf_count): + if (k == 0 or k == interp_result.kf_count - 1): + continue + if (abs(interp_result.in_slope[k] - interp_result.out_slope[k]) > comp_min_dif): + can_use_smooth_interp = False + break + + # nice, adjust in_slope[0] and out_slope[-1] + if (can_use_smooth_interp == True): + interp_result.in_slope[0] = interp_result.out_slope[0] + interp_result.out_slope[-1] = interp_result.in_slope[-1] + else: + interp_result.in_slope[0] = 0 + interp_result.out_slope[-1] = 0 + + # overwrite the old animation track + bck_anim.anim_data[i].comp[j].kf_count = interp_result.kf_count + bck_anim.anim_data[i].comp[j].interp_mode = 1 + if (can_use_smooth_interp == True): + bck_anim.anim_data[i].comp[j].interp_mode = 0 + bck_anim.anim_data[i].comp[j].time = interp_result.time + bck_anim.anim_data[i].comp[j].value = interp_result.value + bck_anim.anim_data[i].comp[j].in_slope = interp_result.in_slope + bck_anim.anim_data[i].comp[j].out_slope = interp_result.out_slope + + # hopefully everything went okay + print(bck_anim) + # create a raw bck struct and write the BCK file raw = bck_funcs.create_smg_bck_raw(bck_anim) - print(raw) + print(raw) endian_ch = ">" # big endian character for struct.unpack() if (options.endian == "OPT_B"): # little character endian_ch = "<" - bck_funcs.write_smg_bck_raw(raw, filepath, endian_ch) + bck_funcs.write_smg_bck_raw(raw, options.filepath, endian_ch) # done! - blender_funcs.disp_msg("BCK animation \"%s\" written" % (file_ops.get_filename(filepath))) + blender_funcs.disp_msg("BCK animation \"%s\" written" % (file_ops.get_file_name(options.filepath))) return {"FINISHED"} # Stuff down is for the menu appending # of the importer to work plus some setting stuff # comes from a Blender importer template - from bpy_extras.io_utils import ExportHelper from bpy.props import StringProperty, BoolProperty, EnumProperty, FloatProperty, IntProperty from bpy.types import Operator @@ -177,7 +254,7 @@ class export_bck(Operator, ExportHelper): ) angle_limit = FloatProperty( name = "Derivative angle limit", - description = "Value used to specify a keyframe generation at curve points at which sudden slope changes occur. Useful to adjust several straight lines. The angle comes from scaling the vertical axis of the animation track so that the \"visual derivative changes\" become visible", + description = "Value used to specify a keyframe generation at curve points at which sudden slope changes occur. Useful to adjust several straight lines. The angle comes from scaling the vertical axis of the animation track so that the \"visual derivative changes\" become visible. For export option \"Find Best Interpolator\"", default = 45, min = 0, max = 180, @@ -213,6 +290,12 @@ class export_bck(Operator, ExportHelper): ("OPT_B", "Little", "Write data in the little endian byte ordering") ) ) + min_dif = FloatProperty( + name = "Minimum difference", + description = "Minimum difference at which 2 numbers can be considered the same. For reference, this value is linked to randians magnitude", + default = 0.001, + min = 1e-9, + ) # what the importer actually does def execute(self, context): return export_bck_func(self, context) @@ -225,6 +308,6 @@ bpy.utils.register_class(export_bck) bpy.types.INFO_MT_file_export.append(menu_export_bck) # test call -bpy.ops.export_scene.bck('INVOKE_DEFAULT') +bpy.ops.export_scene.bck("INVOKE_DEFAULT") |