summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bck_export.py133
-rw-r--r--bck_funcs.py227
-rw-r--r--collada_superbmd_import.py2
-rw-r--r--math_funcs.py7
-rw-r--r--smg_common.py8
-rw-r--r--test.py39
6 files changed, 284 insertions, 132 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")
diff --git a/bck_funcs.py b/bck_funcs.py
index d8c1162..8e2d6bb 100644
--- a/bck_funcs.py
+++ b/bck_funcs.py
@@ -199,7 +199,7 @@ class smg_bck_anim:
# create a global variable to hold temporal information
bck_raw_info = None
-bck_error_str = "bck-error: "
+bck_raw_error_str = "bck-raw-error: "
bck_anim_error_str = "bck-anim-error: "
pad_str = "hoot"
f = None
@@ -217,7 +217,7 @@ def read_bck_file(filepath):
# all good
bck_anim_info = None
- if (result_str == bck_error_str + "all good"):
+ if (result_str == bck_raw_error_str + "all good"):
# construct the data structure that is easier to deal with
print(bck_raw_info)
bck_anim_info = smg_bck_anim()
@@ -287,7 +287,7 @@ def pre_read_bck_file(filepath):
# check its size first
if (os.path.getsize(filepath) <= 32):
- return bck_error_str + "file size"
+ return bck_raw_error_str + "file size"
# make global variables editable
global f
@@ -309,7 +309,7 @@ def pre_read_bck_file(filepath):
elif (bck_raw_info.header.magic == "1D3J"):
bck_raw_info.endian = "LITTLE"
else:
- return bck_error_str + "magic"
+ return bck_raw_error_str + "magic"
bck_raw_info.header.magic = "J3D1"
# variable to set for struct.unpack byte order reading
@@ -321,24 +321,24 @@ def pre_read_bck_file(filepath):
bck_raw_info.header.ftype = f.read(4).decode("ascii")
if ((bck_raw_info.header.ftype == "bck1" and bck_raw_info.endian != "BIG")
and (bck_raw_info.header.ftype == "1kcb" and bck_raw_info.endian != "LITTLE")):
- return bck_error_str + "ftype"
+ return bck_raw_error_str + "ftype"
bck_raw_info.header.ftype = "bck1"
# file size
bck_raw_info.header.file_size = struct.unpack(endian_ch + "I", f.read(4))[0]
if (bck_raw_info.header.file_size != os.path.getsize(filepath)):
- return bck_error_str + "file size"
+ return bck_raw_error_str + "file size"
# section count
bck_raw_info.header.section_count = struct.unpack(endian_ch + "I", f.read(4))[0]
if (bck_raw_info.header.section_count != 1):
- return bck_error_str + "section count"
+ return bck_raw_error_str + "section count"
# unknown 1
bck_raw_info.header.unknown1 = list(f.read(16))
for i in range(16):
if (bck_raw_info.header.unknown1[i] != 0xFF):
- return bck_error_str + "unknown 1"
+ return bck_raw_error_str + "unknown 1"
##############
# ank1 section
@@ -347,18 +347,18 @@ def pre_read_bck_file(filepath):
bck_raw_info.ank1.magic = f.read(4).decode("ascii")
if ((bck_raw_info.ank1.magic == "ANK1" and bck_raw_info.endian != "BIG")
and (bck_raw_info.ank1.magic == "1KNA" and bck_raw_info.endian != "LITTLE")):
- return bck_error_str + "ank1 magic"
+ return bck_raw_error_str + "ank1 magic"
bck_raw_info.ank1.magic = "ANK1"
# size
bck_raw_info.ank1.size = struct.unpack(endian_ch + "I", f.read(4))[0]
if (bck_raw_info.ank1.size != bck_raw_info.header.file_size - 32):
- return bck_error_str + "ank1 size"
+ return bck_raw_error_str + "ank1 size"
# loop mode
bck_raw_info.ank1.loop_mode = struct.unpack(endian_ch + "B", f.read(1))[0]
if (bck_raw_info.ank1.loop_mode > 0x04):
- return bck_error_str + "ank1 loop mode"
+ return bck_raw_error_str + "ank1 loop mode"
# rotation left shift
bck_raw_info.ank1.rot_lshift = struct.unpack(endian_ch + "B", f.read(1))[0]
@@ -379,22 +379,22 @@ def pre_read_bck_file(filepath):
bck_raw_info.ank1.anim_data_offset = struct.unpack(endian_ch + "I", f.read(4))[0]
if (bck_raw_info.ank1.anim_data_offset
+ (bck_raw_info.ank1.bone_count * 9 * 6) > bck_raw_info.ank1.size):
- return bck_error_str + "ank1 bone animation data offset"
+ return bck_raw_error_str + "ank1 bone animation data offset"
# scale array offset
bck_raw_info.ank1.scale_arr_offset = struct.unpack(endian_ch + "I", f.read(4))[0]
if (bck_raw_info.ank1.scale_arr_offset
+ (bck_raw_info.ank1.scale_arr_length * 4) > bck_raw_info.ank1.size):
- return bck_error_str + "ank1 scale array offset"
+ return bck_raw_error_str + "ank1 scale array offset"
# rotation array offset
bck_raw_info.ank1.rot_arr_offset = struct.unpack(endian_ch + "I", f.read(4))[0]
if (bck_raw_info.ank1.rot_arr_offset
+ (bck_raw_info.ank1.rot_arr_length * 2) > bck_raw_info.ank1.size):
- return bck_error_str + "ank1 rotation array offset"
+ return bck_raw_error_str + "ank1 rotation array offset"
# translation array offset
bck_raw_info.ank1.transl_arr_offset = struct.unpack(endian_ch + "I", f.read(4))[0]
if (bck_raw_info.ank1.transl_arr_offset
+ (bck_raw_info.ank1.transl_arr_length * 4) > bck_raw_info.ank1.size):
- return bck_error_str + "ank1 translation array offset"
+ return bck_raw_error_str + "ank1 translation array offset"
########################################################################
# refer to the offsets to read the animation data always (SMG does this)
@@ -421,7 +421,7 @@ def pre_read_bck_file(filepath):
# check the interpolation mode and if nothing overflows
if (interp_mode > 1):
- return bck_error_str + "ank1 interpolation mode"
+ return bck_raw_error_str + "ank1 interpolation mode"
# variables to be used later
item_read_size = None
@@ -454,7 +454,7 @@ def pre_read_bck_file(filepath):
# check overflow
if (arr_offset + (item_read_size * data_index)
+ (item_read_size * kf_count * number_of_items_per_kf) > bck_raw_info.ank1.size):
- return bck_error_str + "ank1 anim data overflow"
+ return bck_raw_error_str + "ank1 anim data overflow"
# read the respective arrays to check time consistency
old_time = None
@@ -476,7 +476,7 @@ def pre_read_bck_file(filepath):
cur_time = struct.unpack(endian_ch + item_read_type, f.read(item_read_size))[0]
if (old_time != None):
if (old_time >= cur_time):
- return bck_error_str + "ank1 keyframe time"
+ return bck_raw_error_str + "ank1 keyframe time"
old_time = cur_time
# value
value = struct.unpack(endian_ch + item_read_type, f.read(item_read_size))[0]
@@ -500,7 +500,7 @@ def pre_read_bck_file(filepath):
bck_raw_info.ank1.transl_arr.append(struct.unpack(endian_ch + "f", f.read(4))[0])
# finally done bruh
- return bck_error_str + "all good"
+ return bck_raw_error_str + "all good"
# check if a smg_bck_anim structure is good
def check_smg_bck_anim(anim):
@@ -523,16 +523,17 @@ def check_smg_bck_anim(anim):
for comp in bone.comp:
# check object types and integer data
- if (type(comp) != smg_bck_anim.anim_data):
+ if (type(comp) != smg_bck_anim.anim_data.comp):
return bck_anim_error_str + "anim_data struct"
if (type(comp.kf_count) != int or comp.kf_count <= 0):
+ print(comp.kf_count)
return bck_anim_error_str + "keyframe count"
- if (type(comp.interp_mode) != int or (comp.kf_count != 0 and comp.kf_count != 1)):
+ if (type(comp.interp_mode) != int or (comp.interp_mode != 0 and comp.interp_mode != 1)):
return bck_anim_error_str + "interpolation mode"
if (type(comp.time) != list or len(comp.time) != comp.kf_count):
return bck_anim_error_str + "time list"
- if (type(comp.values) != list or len(comp.values) != comp.kf_count):
- return bck_anim_error_str + "values list"
+ if (type(comp.value) != list or len(comp.value) != comp.kf_count):
+ return bck_anim_error_str + "value list"
if (type(comp.in_slope) != list or len(comp.in_slope) != comp.kf_count):
return bck_anim_error_str + "in_slope list"
if (type(comp.out_slope) != list or len(comp.out_slope) != comp.kf_count):
@@ -549,6 +550,9 @@ def check_smg_bck_anim(anim):
return bck_anim_error_str + "all good"
# create smg_bck_raw from smg_bck_anim
+# assumes angles are in radians
+# and that the timings between the keyframes are t0 = 0 and tf = 1
+# (cubic hermite spline)
def create_smg_bck_raw(anim):
# calls check_smg_bck_anim()
@@ -563,7 +567,7 @@ def create_smg_bck_raw(anim):
# header
raw.header.magic = "J3D1"
- raw.header.ftype = "btp1"
+ raw.header.ftype = "bck1"
raw.header.file_size = 0 # update later
raw.header.section_count = 1
raw.header.unknown1 = [0xFF, 0xFF, 0xFF, 0xFF,
@@ -583,33 +587,38 @@ def create_smg_bck_raw(anim):
raw.ank1.anim_length = anim.anim_length
raw.ank1.bone_count = anim.bone_count
- # check all the rotation lists and get the average value
- avg_angle_mag = 0
- angle_count = 0
+ # check all the rotation lists and get the largest value/slope
+ max_angle_value_slope_mag = 0
for i in range(anim.bone_count):
for j in range(3):
- for k in range(len(anim.anim_data[i].comp[(j * 3) + 1].kf_count)):
- max_angle_mag += abs(anim.anim_data[i].comp[(j * 3) + 1].values[k])
- angle_count += 1
- # calculate rot_lshift so that this value can be represented
- # (shit can go crazy if this value is very large)
- avg_angle_mag = avg_angle_mag / angle_count
- raw.ank1.rot_lshift = int(math.ceil(math.log2(avg_angle_mag / math.pi)))
- # ceil of it because I am forcing the average to be represented
- # as 0x7FFF which cannot be done as rot_lshift needs to be an integer
+ for k in range(anim.anim_data[i].comp[(j * 3) + 1].kf_count):
+ if (abs(anim.anim_data[i].comp[(j * 3) + 1].value[k]) > max_angle_value_slope_mag):
+ max_angle_value_slope_mag = abs(anim.anim_data[i].comp[(j * 3) + 1].value[k])
+ # ~ if (k > 0):
+ # ~ if (abs(anim.anim_data[i].comp[(j * 3) + 1].in_slope[k]) > max_angle_value_slope_mag):
+ # ~ max_angle_value_slope_mag = abs(anim.anim_data[i].comp[(j * 3) + 1].in_slope[k])
+ # ~ if (k < anim.anim_data[i].comp[(j * 3) + 1].kf_count - 1):
+ # ~ if (abs(anim.anim_data[i].comp[(j * 3) + 1].out_slope[k]) > max_angle_value_slope_mag):
+ # ~ max_angle_value_slope_mag = abs(anim.anim_data[i].comp[(j * 3) + 1].out_slope[k])
+ # calculate rot_lshift so that the largest value can be represented
+ if (max_angle_value_slope_mag > math.pi):
+ raw.ank1.rot_lshift = int(math.ceil(math.log2(max_angle_value_slope_mag / math.pi)))
+ # ^ ceil of it because I am forcing the average to be represented
+ # as 0x7FFF which cannot be done as rot_lshift needs to be an integer
+ else: # angles can be represented between -180 and 180
+ raw.ank1.rot_lshift = 0
# start writing the animation data
# iterate over the bones
for i in range(anim.bone_count):
# add a section for a bone
- raw.ank1.anim_data.append(smg_bck_raw.anim_data())
+ raw.ank1.anim_data.append(smg_bck_raw.ank1.anim_data())
# iterate over the animation components
for j in range(9):
- # in case the same dataset can be found already
- # written in one of the animation data arrays
- match_found = True
+
+ # get the animation component array
arr = None
# scale
if (j == 0 or j == 3 or j == 6):
@@ -620,6 +629,7 @@ def create_smg_bck_raw(anim):
# translate
elif (j == 2 or j == 5 or j == 8):
arr = raw.ank1.transl_arr
+ # get the last index to write in the array
cur_index = len(arr)
# keyframe_count, interpolation_mode
@@ -633,64 +643,69 @@ def create_smg_bck_raw(anim):
elif (raw.ank1.anim_data[i].comp[j].interpolation_mode == 1):
number_of_items = 4
- # check if it is a single keyframe value
+ # variable to check if the animation track data
+ # can be found already in the component array
+ match_found = False
+
+ # rotation consideration
+ rot_mult = 1
+ if (j == 1 or j == 4 or j == 7):
+ rot_mult = (0x7FFF / (math.pi * math.pow(2, raw.ank1.rot_lshift)))
+
+ # check if it is a single keyframe value
if (number_of_items == 1):
# check if there is an equivalent value around the already written data
- match_found = False
- k = 0
- while (k < len(arr)):
- if (anim.anim_data[i].comp[j].values[0] == arr[k]):
- raw.ank1.anim_data[i].comp[j].anim_data_index = k
- match_found = True
- break
- k += 1
+ if (anim.anim_data[i].comp[j].value[0] in arr):
+ match_found = True
+ for k in range(len(arr)):
+ if (arr[k] == anim.anim_data[i].comp[j].value[0] * rot_mult):
+ raw.ank1.anim_data[i].comp[j].anim_data_index = k
+ break
# something was found, index was already assigned
if (match_found == True):
continue
- # otherwise, update the array
- arr.append(anim.anim_data[i].comp[j].values[0])
+ # otherwise, update the array and assign the new index
+ raw.ank1.anim_data[i].comp[j].anim_data_index = cur_index
+ arr.append(anim.anim_data[i].comp[j].value[0] * rot_mult)
# or if it is more
else:
# iterate over all the other values in the scale array to see if a match is found
- k = 0
- while ((k + (anim.anim_data[i].comp[j].kf_count * number_of_items)) < len(arr)):
- # check coincidence
- l = 0
+ for k in range(len(arr)):
+ # check if there is enough space left in the array
+ if (k + (anim.anim_data[i].comp[j].kf_count * number_of_items) > len(arr)):
+ break
+ # check if the section from the array is the same as the data to match
+ tmp = arr[k : k + (anim.anim_data[i].comp[j].kf_count * number_of_items)]
match_found = True
- while (l < anim.anim_data[i].comp[j].kf_count):
- # check value equality
- if ((anim.anim_data[i].comp[j].time[l] != arr[k + l + 0])
- or (anim.anim_data[i].comp[j].value[l] != arr[k + l + 1])
- or (anim.anim_data[i].comp[j].in_slope[l] != arr[k + l + 2])):
+ for l in range((anim.anim_data[i].comp[j].kf_count)):
+ # 3 items to check
+ if ((tmp[int((l * number_of_items) + 0)] != anim.anim_data[i].comp[j].time[l])
+ or (tmp[int((l * number_of_items) + 1)] != anim.anim_data[i].comp[j].value[l] * rot_mult)
+ or (tmp[int((l * number_of_items) + 2)] != anim.anim_data[i].comp[j].in_slope[l] * rot_mult)):
match_found = False
break
- # interpolation mode == 1
+ # 4 items to check
if ((anim.anim_data[i].comp[j].interp_mode == 1)
- and (anim.anim_data[i].comp[j].out_slope[l] != arr[k + l + 3])):
+ and (tmp[int((l * number_of_items) + 3)] != anim.anim_data[i].comp[j].out_slope[l] * rot_mult)):
match_found = False
break
- l += number_of_items
- # something was found
+ # something was found, assign the index and break out of the loop
if (match_found == True):
raw.ank1.anim_data[i].comp[j].anim_data_index = k
break
- # continue to next loop
- k += 1
# something was found, index was already assigned
if (match_found == True):
continue
- # else append the new data
+ # otherwise, update the array and assign the new index
raw.ank1.anim_data[i].comp[j].anim_data_index = cur_index
- # iterate over the frames and assign the scale values
- k = 0
- while (k < anim.anim_data[i].comp[j].kf_count):
+ # iterate over the frames and assign the values
+ for k in range(anim.anim_data[i].comp[j].kf_count):
arr.append(anim.anim_data[i].comp[j].time[k])
- arr.append(anim.anim_data[i].comp[j].value[k])
- arr.append(anim.anim_data[i].comp[j].in_slope[k])
+ arr.append(anim.anim_data[i].comp[j].value[k] * rot_mult)
+ arr.append(anim.anim_data[i].comp[j].in_slope[k] * rot_mult)
if (anim.anim_data[i].comp[j].interp_mode == 1):
- arr.append(anim.anim_data[i].comp[j].out_slope[k])
- k += 1
+ arr.append(anim.anim_data[i].comp[j].out_slope[k] * rot_mult)
# update the animation arrays
# scale
@@ -703,7 +718,8 @@ def create_smg_bck_raw(anim):
elif (j == 2 or j == 5 or j == 8):
raw.ank1.transl_arr = arr
- # assign these variables now
+ # assign these variables now
+ # dont be crazy with it an assign the data tables to the "standard offsets"
raw.ank1.scale_arr_length = len(raw.ank1.scale_arr)
raw.ank1.rot_arr_length = len(raw.ank1.rot_arr)
raw.ank1.transl_arr_length = len(raw.ank1.transl_arr)
@@ -717,7 +733,7 @@ def create_smg_bck_raw(anim):
raw.ank1.transl_arr_offset += len(pad_str.string_fill(32, raw.ank1.transl_arr_offset))
# section size and file size
raw.ank1.size = raw.ank1.transl_arr_offset + (raw.ank1.transl_arr_length * 4)
- raw.ank1.size += len(pad_str.string_fill(32, raw.ank1.size)) + 1
+ raw.ank1.size += len(pad_str.string_fill(32, raw.ank1.size))
raw.header.file_size = 32 + raw.ank1.size
# done!
@@ -726,11 +742,60 @@ def create_smg_bck_raw(anim):
# write smg_bck_raw
def write_smg_bck_raw(raw, filepath, endian_ch):
- # assumes smg_bck_raw struct is correct so don't even
- # attempt in making one yourself, use create_smg_bck_raw()
+ # assumes smg_bck_raw struct is correct so don't even attempt in making one yourself
+ # use create_smg_bck_raw() to get a raw struct from an anim struct
# use struct.pack() to write in different endian orders
- # dont be crazy with it an assign the data tables to the "standard offsets"
-
- global f
f = open(filepath, "wb")
+
+ # header
+ f.write(struct.pack(endian_ch + "I", struct.unpack(">I", raw.header.magic.encode("ascii"))[0]))
+ f.write(struct.pack(endian_ch + "I", struct.unpack(">I", raw.header.ftype.encode("ascii"))[0]))
+ f.write(struct.pack(endian_ch + "I", raw.header.file_size))
+ f.write(struct.pack(endian_ch + "I", raw.header.section_count))
+ f.write(bytes(raw.header.unknown1))
+
+ # ank1
+ f.write(struct.pack(endian_ch + "I", struct.unpack(">I", raw.ank1.magic.encode("ascii"))[0]))
+ f.write(struct.pack(endian_ch + "I", raw.ank1.size))
+ f.write(struct.pack(endian_ch + "B", raw.ank1.loop_mode))
+ f.write(struct.pack(endian_ch + "B", raw.ank1.rot_lshift))
+ f.write(struct.pack(endian_ch + "H", raw.ank1.anim_length))
+ f.write(struct.pack(endian_ch + "H", raw.ank1.bone_count))
+ f.write(struct.pack(endian_ch + "H", raw.ank1.scale_arr_length))
+ f.write(struct.pack(endian_ch + "H", raw.ank1.rot_arr_length))
+ f.write(struct.pack(endian_ch + "H", raw.ank1.transl_arr_length))
+ f.write(struct.pack(endian_ch + "I", raw.ank1.anim_data_offset))
+ f.write(struct.pack(endian_ch + "I", raw.ank1.scale_arr_offset))
+ f.write(struct.pack(endian_ch + "I", raw.ank1.rot_arr_offset))
+ f.write(struct.pack(endian_ch + "I", raw.ank1.transl_arr_offset))
+ pad = smg_common.padding()
+ f.write(pad.string_fill(32, 0x24))
+ # anim data
+ for i in range(raw.ank1.bone_count):
+ for j in range(9):
+ f.write(struct.pack(endian_ch + "H", raw.ank1.anim_data[i].comp[j].keyframe_count))
+ f.write(struct.pack(endian_ch + "H", raw.ank1.anim_data[i].comp[j].anim_data_index))
+ f.write(struct.pack(endian_ch + "H", raw.ank1.anim_data[i].comp[j].interpolation_mode))
+ f.write(pad.string_fill(32, raw.ank1.anim_data_offset + (raw.ank1.bone_count * 9 * 6)))
+ # scale array
+ for i in range(raw.ank1.scale_arr_length):
+ f.write(struct.pack(endian_ch + "f", raw.ank1.scale_arr[i]))
+ f.write(pad.string_fill(32, raw.ank1.scale_arr_offset + (raw.ank1.scale_arr_length * 4)))
+ # rotation array
+ for i in range(raw.ank1.rot_arr_length):
+ # check if the slope surpaces max representation container
+ # (truncate the value to the max representation)
+ value = int(raw.ank1.rot_arr[i])
+ if (value > 0x7FFF):
+ value = 0x7FFF
+ elif (value < -0x7FFF):
+ value = -0x7FFF
+ f.write(struct.pack(endian_ch + "h", value))
+ f.write(pad.string_fill(32, raw.ank1.rot_arr_offset + (raw.ank1.rot_arr_length * 2)))
+ # translation array
+ for i in range(raw.ank1.transl_arr_length):
+ f.write(struct.pack(endian_ch + "f", raw.ank1.transl_arr[i]))
+ f.write(pad.string_fill(32, raw.ank1.transl_arr_offset + (raw.ank1.transl_arr_length * 4)))
+
+ # done!
f.close()
diff --git a/collada_superbmd_import.py b/collada_superbmd_import.py
index 2fa7ef0..c19a91a 100644
--- a/collada_superbmd_import.py
+++ b/collada_superbmd_import.py
@@ -38,7 +38,7 @@ def import_collada_superbmd(context, filepath):
# get asset's unit element
root = xml.getroot()
-
+
# texture files path
if (os.name == "posix" and root.find("library_images") != None):
for image in root.find("library_images"):
diff --git a/math_funcs.py b/math_funcs.py
index ec7f7de..658797a 100644
--- a/math_funcs.py
+++ b/math_funcs.py
@@ -114,7 +114,7 @@ class best_chs_fits:
# for a given a set of points. The set of points is expected to be at
# each frame of the animation (start_frame indicates the start frame, integer)
# the function will return the above structure
-# it will be the generic cubic hermite spline form
+# it will be in the general cubic hermite spline form (t0 < tf)
def find_best_cubic_hermite_spline_fit(start_frame, values, angle_limit):
# check
@@ -157,10 +157,11 @@ def find_best_cubic_hermite_spline_fit(start_frame, values, angle_limit):
avg_value = 0
for i in range(len(values)):
if (abs(values[i]) < lowest_value):
- lowest_value = values[i]
+ lowest_value = abs(values[i])
if (abs(values[i]) > highest_value):
- highest_value = values[i]
+ highest_value = abs(values[i])
avg_value += abs(values[i])
+ print(highest_value)
scale_factor = (1 / highest_value) * (len(values) - start_frame)
avg_value /= len(values)
diff --git a/smg_common.py b/smg_common.py
index 3c7bb0f..4fa4017 100644
--- a/smg_common.py
+++ b/smg_common.py
@@ -1,7 +1,7 @@
# padding stuff
# padding string
-class padding():
+class padding:
# the fill string
def __init__(self):
@@ -14,16 +14,16 @@ class padding():
if ((type(start_index) != int)
or (type(byte_alignment) != int)
or (start_index < 0)
- or (byte_alignment != 4 and byte_alignment != 32):
+ or (byte_alignment != 4 and byte_alignment != 32)):
return None
# return the fill string
i = 0
rtn = ""
while (start_index % byte_alignment != 0):
- rtn += self.padding[i]
+ rtn += self.string[i]
i += 1
start_index += 1
- return rtn
+ return bytes(rtn.encode("ascii"))
# name tables
diff --git a/test.py b/test.py
index 1d74b42..db5e2a5 100644
--- a/test.py
+++ b/test.py
@@ -1,26 +1,29 @@
-import smg_name_table_funcs
+from . import bck_funcs
+
+print(bck_funcs.smg_bck_raw.anim_data)
+print(bck_funcs.smg_bck_anim.anim_data)
# I think I am making my own way to handle bytes
# ~ class owl_byte(int):
# ~ def __str__(self):
# ~ return "%0X" % (self)
-# I think I am making my own way to print bytes
-# the default printing is awful to look at and I need rapid inspection
-class owl_bytes(bytearray):
- def __str__(self):
- rtn = ""
- for i in range(len(self)):
- rtn += "%02X" % self[i]
- rtn += " "
- return rtn
+# ~ # I think I am making my own way to print bytes
+# ~ # the default printing is awful to look at and I need rapid inspection
+# ~ class owl_bytes(bytearray):
+ # ~ def __str__(self):
+ # ~ rtn = ""
+ # ~ for i in range(len(self)):
+ # ~ rtn += "%02X" % self[i]
+ # ~ rtn += " "
+ # ~ return rtn
-f = open("test.bin", "rb")
-byte_arr = owl_bytes(b"")
-byte = f.read(1)
-while (byte != b""):
- byte_arr += byte
- byte = f.read(1)
-f.close()
+# ~ f = open("test.bin", "rb")
+# ~ byte_arr = owl_bytes(b"")
+# ~ byte = f.read(1)
+# ~ while (byte != b""):
+ # ~ byte_arr += byte
+ # ~ byte = f.read(1)
+# ~ f.close()
-print(byte_arr)
+# ~ print(byte_arr)