summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwl <isaclien9752@gmail.com>2025-08-23 12:37:51 -0400
committerOwl <isaclien9752@gmail.com>2025-08-23 12:37:51 -0400
commit0ab9e972cb4a4128c2041f6da580a72515b3db64 (patch)
tree1a79d85db13b9714af806b54358fda15aca2ddae
parenta5bdfdab2ec68ebfe99535a54b3b0cd8b95a87d6 (diff)
downloadblenxy-0ab9e972cb4a4128c2041f6da580a72515b3db64.tar.gz
blenxy-0ab9e972cb4a4128c2041f6da580a72515b3db64.zip
Some changes to the BCK exporter
-rw-r--r--README.md9
-rw-r--r--bck_export.py77
-rw-r--r--bck_funcs.py11
-rw-r--r--bck_import.py35
-rw-r--r--collada_superbmd_import.py5
-rw-r--r--csv_anim_bck_export.py473
-rw-r--r--csv_anim_bck_import.py644
-rw-r--r--test.binbin364 -> 0 bytes
-rw-r--r--test.py29
9 files changed, 67 insertions, 1216 deletions
diff --git a/README.md b/README.md
index 6727c20..c9d016e 100644
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ Blender 2.79 template for Super Mario Galaxy stuff.
- Be aware of future BCK importer/export bugs.
- Import "Inherit Parent Scale" data from BMD/BDL models into an already imported DAE model.
- Try to import directly a BMD/BDL model (one can dream).
-- Learn about other model related file formats
+- Learn about other model related file formats.
- Learn how to modify Blender's UI through its Python API to make "custom editors".
- ...
@@ -20,8 +20,8 @@ Blender 2.79 template for Super Mario Galaxy stuff.
- Epic Mario model like the default cube in Blender when starting the template.
- OBJ importer for NeoKCLCreate OBJs.
- OBJ exporter for KCL conversion.
-- BCK animation importer for SMG (with some importing options, read them).
-- BCK animation exporter for SMG (with some exporter options, read them).
+- BCK animation importer for SMG (with importing options, read them).
+- BCK animation exporter for SMG (with exporter options, read them).
- ...
# Installation
@@ -38,7 +38,8 @@ Blender 2.79 template for Super Mario Galaxy stuff.
- Tarsa for the J3D Animation Editor program (JAE).
- Gabbo for pointing me out the Blender Python API reference.
-- RenolY2 for J3DView and SuperBMD.
+- RenolY2 for J3DView and SuperBMD (and the people that contributed to those tools).
- The authors of the material I could read about BMD/BDL/BCK.
- The many stackoverflow threads (or similar forums/pages) in which Blender Python API issues/facts are discussed.
+- The Blender devs for Blender 2.79.
- The git repository hosting service I am using.
diff --git a/bck_export.py b/bck_export.py
index 9ba4427..8d838ce 100644
--- a/bck_export.py
+++ b/bck_export.py
@@ -105,14 +105,16 @@ def export_bck_func(options, context):
value = None
# check which is the component to get
if (k == 0 or k == 3 or k == 6):
- value = new_mat.to_scale()[int((k - 0) / 3)]
+ value = round(new_mat.to_scale()[int((k - 0) / 3)], options.rounding_vec[0])
elif (k == 1 or k == 4 or k == 7):
- value = new_mat.to_euler("XYZ")[int((k - 1) / 3)]
+ value = round(new_mat.to_euler("XYZ")[int((k - 1) / 3)], options.rounding_vec[1])
elif (k == 2 or k == 5 or k == 8):
- value = 100 * new_mat.to_translation()[int((k - 2) / 3)] # conversion from blenxy's coordinates
+ value = round(100 * new_mat.to_translation()[int((k - 2) / 3)], options.rounding_vec[2])
+ # 100 times because of blenxy's coordinates
bck_anim.anim_data[i].comp[k].value.append(value)
# got all the animation points
+
# delete constant value animation tracks
for i in range(bck_anim.bone_count):
for j in range(9):
@@ -121,13 +123,7 @@ def export_bck_func(options, context):
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):
+ if (bck_anim.anim_data[i].comp[j].value[k - 1] != bck_anim.anim_data[i].comp[j].value[k]):
anim_track_constant = False
break
if (anim_track_constant == True):
@@ -160,8 +156,14 @@ def export_bck_func(options, context):
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].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)
+ # set the rounding digit
+ rounding = options.rounding_vec[0] # scale
+ if (j == 1 or j == 4 or j == 7): # rotation
+ rounding = options.rounding_vec[1]
+ elif (j == 2 or j == 5 or j == 8): # translation
+ rounding = options.rounding_vec[2]
+ bck_anim.anim_data[i].comp[j].in_slope.append(round(in_slope, rounding))
+ bck_anim.anim_data[i].comp[j].out_slope.append(round(out_slope, rounding))
# find "best" interpolator fits for the samples
elif (options.export_type == "OPT_B"):
@@ -176,20 +178,28 @@ def export_bck_func(options, context):
# 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)
-
+ options.angle_limit)
+ # set the rounding digit
+ rounding = options.rounding_vec[0] # scale
+ if (j == 1 or j == 4 or j == 7): # rotation
+ rounding = options.rounding_vec[1]
+ elif (j == 2 or j == 5 or j == 8): # translation
+ rounding = options.rounding_vec[2]
+ # round all the values returned by the interpolation fit
+ for k in range(interp_result.kf_count):
+ interp_result.value[k] = round(interp_result.value[k], rounding)
+ if (k > 0):
+ interp_result.in_slope[k] = round(interp_result.in_slope[k], rounding)
+ if (k < interp_result.kf_count - 1):
+ interp_result.out_slope[k] = round(interp_result.out_slope[k], rounding)
+
# 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):
+ if (interp_result.in_slope[k] != interp_result.out_slope[k]):
can_use_smooth_interp = False
break
@@ -230,7 +240,7 @@ def export_bck_func(options, context):
# 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.props import StringProperty, BoolProperty, EnumProperty, FloatProperty, IntProperty, IntVectorProperty
from bpy.types import Operator
# export_bck class
@@ -261,12 +271,12 @@ class export_bck(Operator, ExportHelper):
)
first_frame = IntProperty(
name = "First frame",
- description = "Value used to specify the first frame of the animation.",
+ description = "Value used to specify the first frame of the animation",
default = 0,
)
anim_length = IntProperty(
name = "Animation length",
- description = "Value used to specify the number of frames of the BCK animation after the first frame specified.",
+ description = "Value used to specify the number of frames of the BCK animation after the first frame specified",
default = 30,
)
loop_mode = EnumProperty(
@@ -274,11 +284,11 @@ class export_bck(Operator, ExportHelper):
description = "Way in which the animation be played in-game",
default = "OPT_C",
items = (
- ("OPT_A", "Play once - Stop at last frame", "Animation will start playing forwards and, when the animation data finishes, the last frame will be kept loaded into the model."),
- ("OPT_B", "Play once - Stop at first frame", "Animation will start playing forwards and, when the animation data finishes, the first frame will be kept loaded into the model."),
- ("OPT_C", "Repeat - Play forwards always", "Animation will start playing forwards and, when the animation data finishes, will play again from the beginning forwards."),
- ("OPT_D", "Play once - First forwards then backwards", "Animation will start playing forwards and, when the animation data finishes, the animation will be played backwards. This occurs only once."),
- ("OPT_E", "Repeat - Play forwards then backwards always", "Animation will start playing forwards and, when the animation data finishes, the animation will be played backwards. This repeats infinitely.")
+ ("OPT_A", "Play once - Stop at last frame", "Animation will start playing forwards and, when the animation data finishes, the last frame will be kept loaded into the model"),
+ ("OPT_B", "Play once - Stop at first frame", "Animation will start playing forwards and, when the animation data finishes, the first frame will be kept loaded into the model"),
+ ("OPT_C", "Repeat - Play forwards always", "Animation will start playing forwards and, when the animation data finishes, will play again from the beginning forwards"),
+ ("OPT_D", "Play once - First forwards then backwards", "Animation will start playing forwards and, when the animation data finishes, the animation will be played backwards. This occurs only once"),
+ ("OPT_E", "Repeat - Play forwards then backwards always", "Animation will start playing forwards and, when the animation data finishes, the animation will be played backwards. This repeats infinitely")
)
)
endian = EnumProperty(
@@ -290,11 +300,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,
+ rounding_vec = IntVectorProperty(
+ name = "Round SRT to digit",
+ description = "Round the scale, rotation and translation values (in that order) to the specified decimal position",
+ default = (3, 5, 1),
+ min = 0,
+ max = 9
)
# what the importer actually does
def execute(self, context):
diff --git a/bck_funcs.py b/bck_funcs.py
index 8e2d6bb..e8482f8 100644
--- a/bck_funcs.py
+++ b/bck_funcs.py
@@ -7,9 +7,9 @@ from . import smg_common
# what this file will do is the following:
# pre_read_bck_file() takes the first look into the BCK and it checks if a BCK is correct.
-# On the way it assigns important variables to the final bck_raw_info (smg_bck class).
-# If the file is correct, then read_bck_file() will assign the remaining variables, if any
-# and return the respective structure (a copy of it, not the reference).
+# On the way it assigns all the variables to bck_raw_info (smg_bck_raw struct).
+# If the file is correct, then read_bck_file() will assign the actually useful variables
+# to a smg_bck_anim structure and return that structure
#
# in case of any error pre_read_bck_file() returns a string that can
# be read by a human to identify what it is wrong with the BCK file
@@ -299,7 +299,6 @@ def pre_read_bck_file(filepath):
# holder for variables
bck_raw_info = smg_bck_raw();
- ########
# header
# magic
@@ -340,7 +339,6 @@ def pre_read_bck_file(filepath):
if (bck_raw_info.header.unknown1[i] != 0xFF):
return bck_raw_error_str + "unknown 1"
- ##############
# ank1 section
# magic
@@ -396,7 +394,6 @@ def pre_read_bck_file(filepath):
+ (bck_raw_info.ank1.transl_arr_length * 4) > bck_raw_info.ank1.size):
return bck_raw_error_str + "ank1 translation array offset"
- ########################################################################
# refer to the offsets to read the animation data always (SMG does this)
# bone animation data
@@ -783,7 +780,7 @@ def write_smg_bck_raw(raw, filepath, endian_ch):
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
+ # check if the slope is larger in magnitude than the max representation container
# (truncate the value to the max representation)
value = int(raw.ank1.rot_arr[i])
if (value > 0x7FFF):
diff --git a/bck_import.py b/bck_import.py
index add03b1..eb1502c 100644
--- a/bck_import.py
+++ b/bck_import.py
@@ -324,18 +324,14 @@ def import_bck_func(context, filepath, import_type, angle_limit):
fcurves[8] = action.fcurves.new(data_path, 2, data_bone.name)
# for any other frame
- else:
-
+ else:
# check if the bone has more frames to animate in the 9 components
- for k in range(9):
-
+ for k in range(9):
# interpolation result
- result = 0
-
+ result = 0
# 1 keyframe animations are skipped
if (bone_anim.comp[k].kf_count == 1):
- continue
-
+ continue
# iterate over the keyframes on the component
for l in range(bone_anim.comp[k].kf_count):
# frame is exactly the last keyframe of the component
@@ -360,8 +356,7 @@ def import_bck_func(context, filepath, import_type, angle_limit):
result = None
break
- # ~ print("interpolation result: %s" % (result))
-
+ # ~ print("interpolation result: %s" % (result))
# assign to the respective component
if (result != None):
if (k == 0 or k == 3 or k == 6): # scale
@@ -420,7 +415,7 @@ def import_bck_func(context, filepath, import_type, angle_limit):
is_transl_anim = False
# remove the fcurve keyframe points that are constant (some cleanup by yours truly)
- # don't want to use blender's built-in action.clean() method cuz it resets the bezier keyframe handles
+ # don't want to use blender's built-in action.clean() method cuz it resets the bezier handles
for fcurve in action.fcurves:
if (len(fcurve.keyframe_points) == 1): # nothing to clean (are you sure about that?)
continue
@@ -446,11 +441,9 @@ def import_bck_func(context, filepath, import_type, angle_limit):
# now, if this has not been hard enough...
# find the "best" interpolator fits to be able to simplify the keyframe count
- if (import_type == "OPT_C"):
-
+ if (import_type == "OPT_C"):
# lets start
- for fcurve in action.fcurves:
-
+ for fcurve in action.fcurves:
# skip 1 frame animations
if (len(fcurve.keyframe_points) == 1):
continue
@@ -497,8 +490,7 @@ def import_bck_func(context, filepath, import_type, angle_limit):
kf_point.handle_right = right
# return to object mode
- bpy.ops.object.mode_set(mode = "OBJECT")
-
+ bpy.ops.object.mode_set(mode = "OBJECT")
# store the loop mode in a custom property of the armature object
if ("loop_mode" in armature.data):
# check if nothing touched the type of this property
@@ -508,8 +500,7 @@ def import_bck_func(context, filepath, import_type, angle_limit):
else:
armature.data["loop_mode"][file_ops.get_file_name(filepath)] = anim.loop_mode
else:
- armature.data["loop_mode"] = {file_ops.get_file_name(filepath) : anim.loop_mode}
-
+ armature.data["loop_mode"] = {file_ops.get_file_name(filepath) : anim.loop_mode}
# display some message
blender_funcs.disp_msg("Animation file \"%s\" imported." % (file_ops.get_file_name(filepath)))
# done!
@@ -532,11 +523,9 @@ class import_bck(Operator, ExportHelper):
filename_ext = ".bck"
filter_glob = StringProperty(default = "*.bck",
options = {"HIDDEN"},
- maxlen = 255)
+ maxlen = 255)
# importer options
-
- # import mode
import_type = EnumProperty(
name = "Import Mode",
description = "Way in which the animation will be imported",
@@ -570,6 +559,6 @@ bpy.utils.register_class(import_bck)
bpy.types.INFO_MT_file_import.append(menu_import_bck)
# test call
-bpy.ops.import_scene.bck('INVOKE_DEFAULT')
+bpy.ops.import_scene.bck("INVOKE_DEFAULT")
diff --git a/collada_superbmd_import.py b/collada_superbmd_import.py
index c19a91a..08d4677 100644
--- a/collada_superbmd_import.py
+++ b/collada_superbmd_import.py
@@ -190,7 +190,6 @@ def import_collada_superbmd(context, 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
@@ -219,10 +218,10 @@ class import_superbmd_collada(Operator, ExportHelper):
# Only needed if you want to add into a dynamic menu
def menu_import_superbmd_collada(self, context):
- self.layout.operator(import_superbmd_collada.bl_idname, text="SuperBMD Collada (.dae)")
+ self.layout.operator(import_superbmd_collada.bl_idname, text = "SuperBMD Collada (.dae)")
bpy.utils.register_class(import_superbmd_collada)
bpy.types.INFO_MT_file_import.append(menu_import_superbmd_collada)
# test call
-bpy.ops.import_scene.superbmd_collada('INVOKE_DEFAULT')
+bpy.ops.import_scene.superbmd_collada("INVOKE_DEFAULT")
diff --git a/csv_anim_bck_export.py b/csv_anim_bck_export.py
deleted file mode 100644
index 9451507..0000000
--- a/csv_anim_bck_export.py
+++ /dev/null
@@ -1,473 +0,0 @@
-'''
-CSV exporter for the BCK animation type
-CSV to be used with the j3d animation editor program
-'''
-
-import bpy, math, re
-from mathutils import Matrix
-from .my_functions import *
-
-# Notes (AFAIK):
-
-# - position/rotation/scaling values of a bone in an animation
-# must be the ones that are relative to its parent bone
-# - Extrinsic Euler XYZ system is the one being used for rotation values.
-# - all animations must start in Frame 0 (starting frame).
-
-######################################
-# write_csv_bck (MAIN FUNCTION)
-# function to write a CSV file with
-# data for the BCK animation type
-# to be used with J3D Animation Editor
-######################################
-def write_csv_bck(context, filepath, loop_mode, export_mode):
-
- # always needed
- scene = bpy.context.scene
-
- # loop mode variable declaration
- loop_number = 0
-
- if (loop_mode == "OPT_A"):
- loop_number = 0
- elif (loop_mode == "OPT_B"):
- loop_number = 1
- elif (loop_mode == "OPT_C"):
- loop_number = 2
- elif (loop_mode == "OPT_D"):
- loop_number = 3
- elif (loop_mode == "OPT_E"):
- loop_number = 4
-
- # if nothing is selected end the exporter
- if (scene.objects.active == None
- or
- scene.objects.active.type != 'ARMATURE'):
- error_string = "No Armature object selected. Select one and try again."
- print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n")
- show_message(error_string, "Error exporting collada file", 'ERROR')
- return {'FINISHED'}
-
- # get armature object
- armature = scene.objects.active
- print()
- print("###############")
- print("Armature found: %s" % armature.name)
- print()
-
- print("Creating CSV file (for BCK)...")
-
- # open file to write
- f = open(filepath, 'w', encoding='utf-8')
-
- print("Writing CSV header...")
-
- ###############################################
- # write the "header" of the CSV animation table
- ###############################################
-
- animation_length = scene.frame_end + 1
- f.write("%d,%d,%d,%s" % (loop_number, animation_length - 1, 0, ".bck"))
- f.write("\n")
-
- f.write("Bone Name,Tangent Interpolation,Component")
-
- for frame in range(animation_length):
- f.write(",Frame %d" % (frame))
- f.write("\n")
-
- ####################################################
- # get a detailed list of the keyframes used in the
- # animation and the bones related to those keyframes
- #
- # bone_kf_data will hold the bone and keyframe position as follows:
- #
- # bone name --> name of the animated bone
- # anim property --> Scale/Rotation/Translation
- # axis --> 0/1/2 --> X/Y/Z
- #
- # a bone name, anim property, axis, kf 1 pos, kf 2 pos, ...
- # another bone name, anim property, axis, kf 3 pos, kf 4 pos, ...
- # ...
- #
- # Note that each animation property can have different keyframe positions in time
-
- bone_kf_data = []
-
- for i in range(len(armature.animation_data.action.fcurves)):
- #
- # get curve
- curve = armature.animation_data.action.fcurves[i]
- # add new row for given curve in bone_kf_data
- bone_kf_data.append([])
-
- ################################################################
- # append bone name, animation property and axis related to curve
-
- # bone name
- bone_kf_data[i].append(re.search('\["(.+?)"\]', curve.data_path).group(1))
-
- # anim property
- if (curve.data_path.find("scale") + 1):
- bone_kf_data[i].append("scale")
- elif (curve.data_path.find("rotation_euler") + 1):
- bone_kf_data[i].append("rotation")
- elif (curve.data_path.find("location") + 1):
- bone_kf_data[i].append("translation")
- # axis
- if (curve.array_index == 0):
- bone_kf_data[i].append("x")
- elif (curve.array_index == 1):
- bone_kf_data[i].append("y")
- elif (curve.array_index == 2):
- bone_kf_data[i].append("z")
-
- # store keyframe data
- for j in range(len(curve.keyframe_points)):
- keyframe = curve.keyframe_points[j]
- bone_kf_data[i].append(int(keyframe.co[0])) # keyframe pos is an integer (frame)
- #
-
- # print bone_kf_data to terminal
- print()
- for row in bone_kf_data:
- print(row)
-
- ############################################################
- # get the armature bones that contain 2 or more keyframes
- # defined (read bone_kf_data) and store at the side of
- # each bone name the last keyframe position of its animation
- #
- # bone_last_kf_pos will contain data as follows:
- #
- # bone1 name, last bone1 keyframe position, bone2 name, last bone2 keyframe position, ...
- #
- bone_last_kf_pos = []
- for i in range(len(bone_kf_data)):
- #
- if (len(bone_last_kf_pos) != 0):
- # if the last bone name on bone_last_kf_pos is the same as the
- # one on bone_kf_data[i][0] go to the column element
- if (bone_last_kf_pos[len(bone_last_kf_pos) - 2] == bone_kf_data[i][0]):
- # check if the keyframe position of the bone is larger and store the larger value
- if (bone_last_kf_pos[len(bone_last_kf_pos) - 1] < bone_kf_data[i][len(bone_kf_data[i]) - 1]):
- bone_last_kf_pos[len(bone_last_kf_pos) - 1] = bone_kf_data[i][len(bone_kf_data[i]) - 1]
- continue
-
- # bone animation row has more than 1 keyframe on an anim property
- # append bone name and last keyframe position in time
- if (len(bone_kf_data[i]) > 4):
- bone_last_kf_pos.append(bone_kf_data[i][0])
- bone_last_kf_pos.append(bone_kf_data[i][len(bone_kf_data[i]) - 1])
- #
-
- # print bones_with_kf to terminal
- print()
- print(bone_last_kf_pos)
- print()
-
- ###################################################################
- # read animation data for one bone then dump the animation data for
- # said bone on the CSV file
- ###################################################################
-
- ########################
- # loop through each bone
- for i in range(len(armature.pose.bones)):
-
- # get bone
- bone = armature.pose.bones[i]
- # print bone going to be processed on terminal
- print("Processing animation for bone: %s" % (bone.name))
-
- # store the animation data scale/rotation/translation X/Y/Z on
- # bone_anim_data which will have 9 rows and each row will be the
- # length of the animation + 1 (including Frame 0 values)
-
- # row 1 --> Scale X
- # row 2 --> Scale Y
- # row 3 --> Scale Z
- # row 4 --> Rotation X
- # row 5 --> Rotation Y
- # row 6 --> Rotation Z
- # row 7 --> Translation X
- # row 8 --> Translation Y
- # row 9 --> Translation Z
-
- bone_anim_data = [[], [], [], [], [], [], [], [], []]
-
- ###############################################################
- # fill bone_anim_data so the rows have the animation length + 1
- for j in range(len(bone_anim_data)):
- for k in range(animation_length):
- bone_anim_data[j].append("")
-
- ###########################################################################
- # check if the bone has 2 or more keyframes (bone has an animation)
- # and store the animation length of that bone (read bone_last_kf_pos,
- # first frame counts on the animation length!, to use later)
- bone_has_anim = False
- anim_length_for_bone = 0 + 1
- for j in range(int(len(bone_last_kf_pos) / 2)):
- #
- if (bone_last_kf_pos[2 * j] == bone.name):
- bone_has_anim = True
- anim_length_for_bone = bone_last_kf_pos[(2 * j) + 1] + 1
- break
- #
-
- print("Bone has animation? ", end = "")
- print(bone_has_anim)
- print("Bone animation length: %d" % (anim_length_for_bone))
-
- ###################################################################
- # if export mode is "only keyframes" define current_bone_kf_data
- # and get from it all the bone keyframes in each animation property
- # keyframes on bone_kfs won't necessarily be on order
- # (do it on bones that have animation)
- current_bone_kf_data = []
- bone_kfs = []
- if (export_mode == "OPT_B" and bone_has_anim == True):
- #
- ##########################################################
- # store the rows of bone_kf_data in which the current bone
- # appears in current_bone_kf_data (to use later)
- current_bone_kf_data = []
- for j in range(len(bone_kf_data)):
- if (bone_kf_data[j][0] == bone.name):
- current_bone_kf_data.append(bone_kf_data[j])
-
- #################################################
- # read current_bone_kf_data to get all the unique
- # keyframe positions of the bone animation
- for j in range(len(current_bone_kf_data)):
- #
- # store the keyframes found on the first row
- # of current_bone_kf_data in bone_kfs
- if (j == 0):
- for k in range(len(current_bone_kf_data[0])):
- # make k equal to 3
- if (k < 3):
- continue
- bone_kfs.append(current_bone_kf_data[j][k])
-
- # other rows
- for k in range(len(current_bone_kf_data[j])):
- #
- if (k < 3): # make k equal to 3
- continue
-
- # loop through bone_kfs to check for new keyframe positions
- keyframe_exists = False
- for l in range(len(bone_kfs)):
- if (current_bone_kf_data[j][k] == bone_kfs[l]):
- keyframe_exists = True
- break
-
- if (keyframe_exists == False):
- bone_kfs.append(current_bone_kf_data[j][k])
- #
- #
- # ~ print(current_bone_kf_data)
- print("Bone's keyframes position:")
- print(bone_kfs)
- #
-
- print()
-
- # if bone_has_anim equals False only store its first frame animation values
- # if bone_has_anim equals True store (depending on the export mode) its
- # keyframes/all frame animation values (until the last keyframe)
-
- ########################################
- # loop through the animation of the bone
- # k is used to go through bone_kfs if
- # export mode is "only keyframes"
- k = 0
- for j in range(anim_length_for_bone):
- #
- ##########################################
- # set scene frame depending on export mode
- if (export_mode == "OPT_B" and bone_has_anim == True and j != 0):
- #
- # check k in case bone_kfs end is reached
- if (k == len(bone_kfs)):
- break
-
- frame = bone_kfs[k]
- scene.frame_set(frame)
- k = k + 1
- #
- else:
- #
- frame = j
- scene.frame_set(frame)
- #
-
- # first bone must be the outermost bone and therefore it
- # has no parent (batman moment, sorry batman u epik >:])
- if (i == 0):
- # -90 degree rotation matrix
- rot_mat = calc_rotation_matrix(math.radians(-90), 0, 0)
- # the first bone has to be rotated -90 degrees
- # on X to get correct animation values for it
- bone_rel_to_parent_mat = rot_mat * bone.matrix
- else: # for any other bone
- bone_rel_to_parent_mat = bone.parent.matrix.inverted() * bone.matrix
-
- ##########################################
- # extract bone_rel_to_parent_mat anim data
-
- # bone scaling
- bone_scale = bone_rel_to_parent_mat.to_scale()
- # bone rotation (stored in radians, extrinsic XYZ Euler)
- bone_rotation = bone_rel_to_parent_mat.to_euler("XYZ")
- # bone translation (multiplied by 100 because 1 GU is 100 meters)
- bone_translation = 100 * bone_rel_to_parent_mat.to_translation()
-
- ##########################################################
- # store frame animation values of bone into bone_anim_data
-
- # scaling data
- bone_anim_data[0][frame] = round(bone_scale[0], 2)
- bone_anim_data[1][frame] = round(bone_scale[1], 2)
- bone_anim_data[2][frame] = round(bone_scale[2], 2)
-
- # rotation data (must be in degrees!)
- bone_anim_data[3][frame] = round(math.degrees(bone_rotation[0]), 2)
- bone_anim_data[4][frame] = round(math.degrees(bone_rotation[1]), 2)
- bone_anim_data[5][frame] = round(math.degrees(bone_rotation[2]), 2)
-
- # position data
- bone_anim_data[6][frame] = round(bone_translation[0], 2)
- bone_anim_data[7][frame] = round(bone_translation[1], 2)
- bone_anim_data[8][frame] = round(bone_translation[2], 2)
-
- # ^ a lot of values can be repeated in each row.
- # When writing the animation data to the CSV is when
- # an optimization method will be done
- #
-
- # ~ for row in bone_anim_data:
- # ~ print(row)
- # ~ print()
-
- ####################################
- # write bone animation data into CSV
- # read bone_anim_data
- ####################################
- for j in range(9):
- #
- # first 3 rows are scaling data
- # the next 3 rows are rotation data
- # the final 3 rows are translation data
- # the start of the first row for a bone
- # animation data (Scale X) contains the bone name
- if (j == 0):
- f.write("%s,Linear,Scale X:" % (bone.name))
- elif(j == 1):
- f.write(",Linear,Scale Y:")
- elif(j == 2):
- f.write(",Linear,Scale Z:")
- elif(j == 3):
- f.write(",Linear,Rotation X:")
- elif(j == 4):
- f.write(",Linear,Rotation Y:")
- elif(j == 5):
- f.write(",Linear,Rotation Z:")
- elif(j == 6):
- f.write(",Linear,Translation X:")
- elif(j == 7):
- f.write(",Linear,Translation Y:")
- elif(j == 8):
- f.write(",Linear,Translation Z:")
-
- ###################################
- # write animation row data
- # will print values with 2 decimals
- ###################################
- for k in range(animation_length):
- #
- # get current animation value
- current_value = bone_anim_data[j][k]
-
- # write the first value from row
- if (k == 0):
- f.write(",%.2f" % (current_value))
- # compare old_value with current_value. if equal leave blank the
- # animation frame value otherwise write said value. This is done
- # to avoid repeating the same number each time (to save file size)
- elif (old_value == current_value or current_value == ""):
- f.write(",")
- else:
- f.write(",%.2f" % (current_value))
-
- # if the end of a row is reached write a newline char to the line
- if (k == (animation_length - 1)):
- f.write("\n")
-
- # store old animation value for the next loop
- if (current_value != ""):
- old_value = current_value
- #
- #
- #
-
- # exporter end
- return {'FINISHED'}
-
-
-#################################################
-# Stuff down is for the menu appending
-# of the exporter 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
-from bpy.types import Operator
-
-class Export_CSV_BCK(Operator, ExportHelper):
-#
- """Save a CSV file for BCK conversion (to use with J3D Animation Editor)"""
- bl_idname = "export_scene.csv_bck"
- bl_label = "Export CSV (for BCK)"
- filename_ext = ".csv"
-
- filter_glob = StringProperty(
- default="*.csv",
- options={'HIDDEN'},
- maxlen=255,
- )
-
- loop_mode = EnumProperty(
- name="Loop Mode",
- description="Choose the loop mode for the animation",
- items=( ('OPT_A', "Once", "Play the animation once and stop at the last frame"),
- ('OPT_B', "Once and Reset", "Play the animation once and stop at the first frame"),
- ('OPT_C', "Loop", "Loop the animation infinitely"),
- ('OPT_D', "Mirrored Once", "Play the animation forwards and then backwards once. Stops at the first frame"),
- ('OPT_E', "Mirrored Loop", "Play the animation forwards and then backwards infinitely")
- ), default='OPT_A'
- )
- export_mode = EnumProperty(
- name="Export Mode",
- description="Choose the method used to export the model animation data",
- items=( ('OPT_A', "All Frames", "Export animation values for each frame of the animation. Slow, higher CSV file size, more accurate animation"),
- ('OPT_B', "Only Keyframes", "Only export the animation keyframe values of each bone. Fast, lower CSV file size, least accurate animation"),
- ), default='OPT_A'
- )
-
- def execute(self, context):
- return write_csv_bck(context, self.filepath, self.loop_mode, self.export_mode)
-#
-
-# Only needed if you want to add into a dynamic menu
-def menu_export_csv_bck(self, context):
- self.layout.operator(Export_CSV_BCK.bl_idname, text="CSV Animation Table (for BCK) (.csv)")
-
-bpy.utils.register_class(Export_CSV_BCK)
-bpy.types.INFO_MT_file_export.append(menu_export_csv_bck)
-
-# test call
-bpy.ops.export_scene.csv_bck('INVOKE_DEFAULT')
diff --git a/csv_anim_bck_import.py b/csv_anim_bck_import.py
deleted file mode 100644
index 0c5741c..0000000
--- a/csv_anim_bck_import.py
+++ /dev/null
@@ -1,644 +0,0 @@
-'''
-CSV importer for the BCK animation type
-CSVs that come from the j3d animation editor program
-animations for now will be imported with linear interpolation
-seems to me that the majority of the animations just use linear
-as the interpolation method between keyframes of animation rows
-'''
-
-# Notes (AFAIK):
-#
-# - reference link --> https://wiki.cloudmodding.com/tww/BCK
-# - In all the Mario CSV BCKs I've seen from J3D Animation
-# Editor the "smooth" interpolation type isn't used at all
-# for animation rows that contain more than the first frame value
-# it could be that JAE just converts the animation to linear
-# timing for easier processing but I am not certain
-
-import bpy, math, re
-from mathutils import Matrix
-from .my_functions import *
-
-#################################################
-# read_csv_bck function (MAIN FUNCTION)
-# read CSV file of BCK from J3D Animation Editor
-# used to apply BCK animation to model on Blender
-#################################################
-def read_csv_bck(context, filepath, import_type):
-#
- # this thing is always needed for stuff
- scene = bpy.context.scene
-
- # if nothing is selected end the exporter
- if (scene.objects.active == None
- or
- scene.objects.active.type != 'ARMATURE'):
- error_string = "No Armature object selected. Select one and try again."
- print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n")
- show_message(error_string, "Error exporting collada file", 'ERROR')
- return {'FINISHED'}
-
- # get armature object
- armature = scene.objects.active
- print()
- print("###############")
- print("Armature found: %s" % armature.name)
- print()
-
- # deselect everything (to be sure nothing weird happens)
- bpy.ops.object.select_all(action='DESELECT')
- scene.objects.active = None
-
- # re-select the armature object only
- armature.select = True
- scene.objects.active = armature
-
- # open file to read
- f = open(filepath, 'r', encoding='utf-8')
-
- # check the file if it is from a BCK file and check the
- # number of bones, if the file is not from a BCK
- # or the bone count differs from the armature
- # bone count end the importer
-
- # first line has the animation type
- line = f.readline()
-
- if not (line.find("bck") + 1):
- error_string = "CSV is not for the BCK animation type. Check the file and try again."
- print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n")
- show_message(error_string, "Error importing CSV file data", 'ERROR')
- return {'FINISHED'}
-
- # count the bones of the animation on the CSV
- # store each time the "Scale X" is found in a line
- bone_count = 0
- while True:
-
- line = f.readline()
-
- # count the bones
- if (line.find("Scale X") + 1):
- bone_count = bone_count + 1
-
- # if an empty line or a line with a newline character
- # is reached end of file must've been reached
- if (line == "" or line == '\n'):
- break
-
- # check bone count
- if (bone_count != len(armature.data.bones)):
- error_string = "Number of bones on the Armature is different than the number of animated bones on the CSV. Check the CSV file and try again."
- print("\n### ERROR ###\n" + error_string + "\n### ERROR ###\n")
- show_message(error_string, "Error importing CSV file data", 'ERROR')
- return {'FINISHED'}
-
- ###############
- # file is valid
- ###############
-
- # start extracting animation data
-
- ###########################################################################
- ###########################################################################
- # import type "ignore rest pose"
- # in here I don't need to do any calculation of the data read from the CSV
- # the armature in which this animation is to be applied must've been
- # imported before in the "ignore rest pose" mode (i.e. the child bones are
- # in the origin of the reference system defined by the parent's bone head).
- # This is like this because BCK CSVs contain the data of how a child bone
- # moves with respect to its parent.
- ###########################################################################
- ###########################################################################
- if (import_type == True):
- #
- # read the CSV from the start again
- # to import the animation values
-
- f = open(filepath, 'r', encoding='utf-8')
-
- line = f.readline()
-
- # first line of the CSV
- csv_first_line = line.split(',')
- animation_length = int(csv_first_line[1])
-
- # get the animation last frame and set it in blender
- bpy.data.scenes["Scene"].frame_end = int(csv_first_line[1])
-
- # the next line comes with the notable keyframes used in the animation
- line = f.readline()
-
- # get all keyframe numbers
- csv_keyframe_numbers = re.findall('[0-9]+', line)
- csv_keyframe_numbers = [int(string) for string in csv_keyframe_numbers]
-
- ###############################################################
- # in the next loop starts the bone animation data table reading
- # loop through every bone
- ###############################################################
- for bone_number in range(bone_count):
-
- # get pose bone
- pose_bone = armature.pose.bones[bone_number]
- print("Reading/Writing animation for bone: %s" % (pose_bone.name))
-
- # set bone rotation mode to Extrinsic Euler XYZ
- pose_bone.rotation_mode = 'XYZ'
-
- # bone_anim_data will hold the bone keyframe animation
- # data for a single bone from the CSV file as follows
-
- # row 1 --> X scaling
- # row 2 --> Y scaling
- # row 3 --> Z scaling
- # row 4 --> X rotation
- # row 5 --> Y rotation
- # row 6 --> Z rotation
- # row 7 --> X translation
- # row 8 --> Y translation
- # row 9 --> Z translation
-
- # already initialized with 9 rows as I don't need more than that
- bone_anim_data = [[], [], [], [], [], [], [], [], []]
-
- ###########################
- # read bone animation table
- ###########################
- for i in range(9):
- #
- line = f.readline()
- row_data = line.split(',')
-
- # if row_data's length is not equal to the length of
- # csv_keyframe_numbers + 3 fill it with empty strings to match said length
- while (len(row_data) != (len(csv_keyframe_numbers) + 3)):
- row_data.append("")
-
- ############################################################
- # copy the keyframe values from row_data into bone_anim_data
- ############################################################
- for j in range(len(row_data)):
- #
- # first 3 elements of row_data are skipped
- # example: Joint 0, Scale X:, Linear, ...
- if (j < 3):
- continue
-
- # if there is no animation value for the bone on the
- # current keyframe data append None to said position
- # otherwise append the value
- if (row_data[j] == "" or row_data[j] == '\n'):
- bone_anim_data[i].append(None)
- else:
- bone_anim_data[i].append(float(row_data[j]))
- #
- #
-
- ####################################
- # write bone animation table to bone
- # only go through the keyframes
- # pointed to in csv_keyframe_numbers
- ####################################
- for k in range(len(csv_keyframe_numbers)):
- #
- # set frame on Blender
- scene.frame_set(csv_keyframe_numbers[k])
-
- # apply scale values X-Y-Z
- if (bone_anim_data[0][k] != None):
- pose_bone.scale[0] = bone_anim_data[0][k]
- pose_bone.keyframe_insert(data_path = 'scale', index = 0)
- if (bone_anim_data[1][k] != None):
- pose_bone.scale[1] = bone_anim_data[1][k]
- pose_bone.keyframe_insert(data_path = 'scale', index = 1)
- if (bone_anim_data[2][k] != None):
- pose_bone.scale[2] = bone_anim_data[2][k]
- pose_bone.keyframe_insert(data_path = 'scale', index = 2)
-
- # apply rotation values (converted to radians)
- if (bone_anim_data[3][k] != None):
- pose_bone.rotation_euler[0] = math.radians(bone_anim_data[3][k])
- pose_bone.keyframe_insert(data_path = 'rotation_euler', index = 0)
- if (bone_anim_data[4][k] != None):
- pose_bone.rotation_euler[1] = math.radians(bone_anim_data[4][k])
- pose_bone.keyframe_insert(data_path = 'rotation_euler', index = 1)
- if (bone_anim_data[5][k] != None):
- pose_bone.rotation_euler[2] = math.radians(bone_anim_data[5][k])
- pose_bone.keyframe_insert(data_path = 'rotation_euler', index = 2)
-
- # apply translation values (divided by 100 because 1 GU is 100 meters)
- if (bone_anim_data[6][k] != None):
- pose_bone.location[0] = bone_anim_data[6][k] / 100
- pose_bone.keyframe_insert(data_path = 'location', index = 0)
- if (bone_anim_data[7][k] != None):
- pose_bone.location[1] = bone_anim_data[7][k] / 100
- pose_bone.keyframe_insert(data_path = 'location', index = 1)
- if (bone_anim_data[8][k] != None):
- pose_bone.location[2] = bone_anim_data[8][k] / 100
- pose_bone.keyframe_insert(data_path = 'location', index = 2)
- #
- #
- #
-
- ###########################################################################
- ###########################################################################
- # import type "rest pose"
- # in here I need to calculate the animation values for the reference system
- # defined by the bone's rest pose as the BCK CSVs store animation data of a
- # bone with respect to its parent. Conversion from a reference system to
- # another can be done easily with matrix multiplication but the rotation
- # data must be preprocessed first (angles that are outside the -180/+180
- # degree range are lost in the conversion process)
- ###########################################################################
- ###########################################################################
- else:
- #
- # read the CSV from the start again
- # to import the animation values
-
- f = open(filepath, 'r', encoding='utf-8')
-
- line = f.readline()
-
- # first line of the CSV
- csv_first_line = line.split(',')
- animation_length = int(csv_first_line[1])
-
- # get the animation last frame and set it in blender
- bpy.data.scenes["Scene"].frame_end = int(csv_first_line[1])
-
- # the next line comes with the notable keyframes used in the animation
- line = f.readline()
-
- # get all keyframe numbers
- csv_keyframe_numbers = re.findall('[0-9]+', line)
- csv_keyframe_numbers = [int(string) for string in csv_keyframe_numbers]
-
- ###############################################################
- # in the next loop starts the bone animation data table reading
- # loop through every bone
- ###############################################################
- for bone_number in range(bone_count):
-
- # get pose.bone and data.bone
- pose_bone = armature.pose.bones[bone_number]
- data_bone = armature.data.bones[bone_number]
- print("Reading/Writing animation for bone: %s" % (pose_bone.name))
-
- # set bone rotation mode to Extrinsic Euler XYZ
- pose_bone.rotation_mode = 'XYZ'
-
- # bone_anim_data will hold the bone frame animation (all frames)
- # data for a single bone from the CSV file as follows
-
- # row 1 --> X scaling
- # row 2 --> Y scaling
- # row 3 --> Z scaling
- # row 4 --> X rotation
- # row 5 --> Y rotation
- # row 6 --> Z rotation
- # row 7 --> X translation
- # row 8 --> Y translation
- # row 9 --> Z translation
-
- # already initialized with 9 rows as I don't need more than that
- bone_anim_data = [[], [], [], [], [], [], [], [], []]
-
- ###########################
- # read bone animation table
- ###########################
- for i in range(9):
- #
- line = f.readline()
- row_data = line.split(',')
-
- # if row_data's length is not equal to the length of
- # csv_keyframe_numbers + 3 fill it with empty strings to match said length
- while (len(row_data) != (len(csv_keyframe_numbers) + 3)):
- row_data.append("")
-
- ############################################################
- # copy the keyframe values from row_data into bone_anim_data
- # and make bone_anim_data hold all frame values for the bone
- ############################################################
-
- # k will be used to go through csv_keyframe_numbers
- # and also through row_data as its length is
- # (len(csv_keyframe_numbers) + 3)
- j = 0
- for k in range(animation_length + 1):
- #
- # Note: first 3 elements of row_data are skipped
- # example: Joint 0, Scale X:, Linear, ...
-
- # if the animation frame isn't a keyframe append None to
- # bone_anim_data[i] and go to the next frame
- if (k != csv_keyframe_numbers[j]):
- bone_anim_data[i].append(None)
- continue
-
- # if it is a keyframe advance to the next keyframe
- # on csv_keyframe_numbers
-
- # if there is no animation value for the bone on the
- # current keyframe data append None to said position
- # otherwise append the value
- if (row_data[j + 3] == "" or row_data[j + 3] == '\n'):
- bone_anim_data[i].append(None)
- else:
- bone_anim_data[i].append(float(row_data[j + 3]))
-
- # advance in row_data
- j = j + 1
- #
- #
-
- ########################################################
- # modify bone_anim_data rotation animation values
- # so that the angles on it are between -180/+180
- # to avoid visual animation data loss
- # convert_rot_anim_to_180() will be an external function
- # the process of converting the animation rotation on all
- # axises into the -180/180 degree range can create new
- # keyframes to the aniamtion and they will be appended
- # at the end of csv_keyframe_numbers so that those
- # keyframes are processed after the main ones are done
- ########################################################
-
- convert_rot_anim_to_180(bone_anim_data[3], csv_keyframe_numbers)
- convert_rot_anim_to_180(bone_anim_data[4], csv_keyframe_numbers)
- convert_rot_anim_to_180(bone_anim_data[5], csv_keyframe_numbers)
-
- ####################################
- # write bone animation table to bone
- # only go through the keyframes
- # pointed to in csv_keyframe_numbers
- ####################################
-
- # k is used to go through csv_keyframe_numbers
- # kf_num is used to go through bone_anim_data
- for k in range(len(csv_keyframe_numbers)):
- #
- # get keyframe number
- kf_num = csv_keyframe_numbers[k]
- # set keyframe on Blender
- scene.frame_set(kf_num)
-
- # - get animation data
- # - convert animation to rest pose reference system
-
- # find all 9 animation properties with linear interpolation if needed
- # (for now) the final goal is to only keep the keyframes from the BCK without
- # having to assign a keyframe for each frame of the animation in Blender
-
- # anim_temp will be used to hold the 9 animation
- # property data as it is read/calculated
-
- anim_temp = []
-
- ########################################################
- # find scale values in ignore rest pose reference system
- ########################################################
-
- #########
- # Scale X
- if (bone_anim_data[0][kf_num] != None):
- anim_temp.append(bone_anim_data[0][kf_num])
- else:
- # find left and right values (store in temp array)
- a = find_left_right(bone_anim_data[0], kf_num)
- # interpolate ^ [l_val_pos, l_val, r_val_pos, r_val]
- anim_temp.append(interpolate(a[0], a[1], a[2], a[3], kf_num, None, "linear"))
-
- #########
- # Scale Y
- if (bone_anim_data[1][kf_num] != None):
- anim_temp.append(bone_anim_data[1][kf_num])
- else:
- # find left and right values (store in temp array)
- a = find_left_right(bone_anim_data[1], kf_num)
- # interpolate ^ [l_val_pos, l_val, r_val_pos, r_val]
- anim_temp.append(interpolate(a[0], a[1], a[2], a[3], kf_num, None, "linear"))
-
- #########
- # Scale Z
- if (bone_anim_data[2][kf_num] != None):
- anim_temp.append(bone_anim_data[2][kf_num])
- else:
- # find left and right values (store in temp array)
- a = find_left_right(bone_anim_data[2], kf_num)
- # interpolate ^ [l_val_pos, l_val, r_val_pos, r_val]
- anim_temp.append(interpolate(a[0], a[1], a[2], a[3], kf_num, None, "linear"))
-
- ########################################################################
- # find rotation values in ignore rest pose reference system (in radians)
- ########################################################################
-
- ############
- # Rotation X
- if (bone_anim_data[3][kf_num] != None):
- anim_temp.append(math.radians(bone_anim_data[3][kf_num]))
- else:
- # find left and right values (store in temp array)
- a = find_left_right(bone_anim_data[3], kf_num)
- # interpolate ^ [l_val_pos, l_val, r_val_pos, r_val]
- anim_temp.append(math.radians(interpolate(a[0], a[1], a[2], a[3], kf_num, None, "linear")))
-
- ############
- # Rotation Y
- if (bone_anim_data[4][kf_num] != None):
- anim_temp.append(math.radians(bone_anim_data[4][kf_num]))
- else:
- # find left and right values (store in temp array)
- a = find_left_right(bone_anim_data[4], kf_num)
- # interpolate ^ [l_val_pos, l_val, r_val_pos, r_val]
- anim_temp.append(math.radians(interpolate(a[0], a[1], a[2], a[3], kf_num, None, "linear")))
-
- ############
- # Rotation Z
- if (bone_anim_data[5][kf_num] != None):
- anim_temp.append(math.radians(bone_anim_data[5][kf_num]))
- else:
- # find left and right values (store in temp array)
- a = find_left_right(bone_anim_data[5], kf_num)
- # interpolate ^ [l_val_pos, l_val, r_val_pos, r_val]
- anim_temp.append(math.radians(interpolate(a[0], a[1], a[2], a[3], kf_num, None, "linear")))
-
- ##############################################################
- # find translation values in ignore rest pose reference system
- # divided by 100 because 1 GU is 100 meters
- ##############################################################
-
- ###############
- # Translation X
- if (bone_anim_data[6][kf_num] != None):
- anim_temp.append(bone_anim_data[6][kf_num] / 100)
- else:
- # find left and right values (store in temp array)
- a = find_left_right(bone_anim_data[6], kf_num)
- # interpolate ^ [l_val_pos, l_val, r_val_pos, r_val]
- anim_temp.append(interpolate(a[0], a[1], a[2], a[3], kf_num, None, "linear") / 100)
-
- ###############
- # Translation Y
- if (bone_anim_data[7][kf_num] != None):
- anim_temp.append(bone_anim_data[7][kf_num] / 100)
- else:
- # find left and right values (store in temp array)
- a = find_left_right(bone_anim_data[7], kf_num)
- # interpolate ^ [l_val_pos, l_val, r_val_pos, r_val]
- anim_temp.append(interpolate(a[0], a[1], a[2], a[3], kf_num, None, "linear") / 100)
-
- ###############
- # Translation Z
- if (bone_anim_data[8][kf_num] != None):
- anim_temp.append(bone_anim_data[8][kf_num] / 100)
- else:
- # find left and right values (store in temp array)
- a = find_left_right(bone_anim_data[8], kf_num)
- # interpolate ^ [l_val_pos, l_val, r_val_pos, r_val]
- anim_temp.append(interpolate(a[0], a[1], a[2], a[3], kf_num, None, "linear") / 100)
-
- ###################################################
- # create the animation matrix related to the ignore
- # rest pose (irp) transformation matrix (T * R * S)
- ###################################################
-
- anim_irp_mat = calc_translation_matrix(anim_temp[6], anim_temp[7], anim_temp[8]) * calc_rotation_matrix(anim_temp[3], anim_temp[4], anim_temp[5]) * calc_scale_matrix(anim_temp[0], anim_temp[1], anim_temp[2])
-
- #########################################################
- # calculate the animation matrix related to the rest pose
- # rest pose matrix of the bone is in the data_bone
- # the first bone has no parent
- #########################################################
-
- if (bone_number == 0):
- anim_rp_mat = anim_irp_mat
- else:
- anim_rp_mat = (data_bone.parent.matrix_local.inverted() * data_bone.matrix_local).inverted() * anim_irp_mat
-
- #####################################
- # extract calculated animation values
- #####################################
-
- # Scaling
- bone_scale = anim_rp_mat.to_scale()
- # Rotation (Extrinsic Euler XYZ)
- bone_rotation = anim_rp_mat.to_euler('XYZ')
- # Translation
- bone_translation = anim_rp_mat.to_translation()
-
- ####################################################
- # apply the respective keyframe values to the bone
- # if any of the values on scale/rotation/translation
- # is different than None create a keyframe for all
- # XYZ axises on said scale/rotation/translation
- # animation property. Will keep the individual
- # keyframe assignment just in case it is needed in
- # a future program logic update
- ####################################################
-
- ########################
- # apply scale values XYZ
- if (bone_anim_data[0][kf_num] != None
- or
- bone_anim_data[1][kf_num] != None
- or
- bone_anim_data[2][kf_num] != None):
- pose_bone.scale[0] = bone_scale[0]
- pose_bone.keyframe_insert(data_path = 'scale', index = 0)
- pose_bone.scale[1] = bone_scale[1]
- pose_bone.keyframe_insert(data_path = 'scale', index = 1)
- pose_bone.scale[2] = bone_scale[2]
- pose_bone.keyframe_insert(data_path = 'scale', index = 2)
-
- ###########################
- # apply rotation values XYZ
- if (bone_anim_data[3][kf_num] != None
- or
- bone_anim_data[4][kf_num] != None
- or
- bone_anim_data[5][kf_num] != None):
- pose_bone.rotation_euler[0] = bone_rotation[0]
- pose_bone.keyframe_insert(data_path = 'rotation_euler', index = 0)
- pose_bone.rotation_euler[1] = bone_rotation[1]
- pose_bone.keyframe_insert(data_path = 'rotation_euler', index = 1)
- pose_bone.rotation_euler[2] = bone_rotation[2]
- pose_bone.keyframe_insert(data_path = 'rotation_euler', index = 2)
-
- ##############################
- # apply translation values XYZ
- if (bone_anim_data[6][kf_num] != None
- or
- bone_anim_data[7][kf_num] != None
- or
- bone_anim_data[8][kf_num] != None):
- pose_bone.location[0] = bone_translation[0]
- pose_bone.keyframe_insert(data_path = 'location', index = 0)
- pose_bone.location[1] = bone_translation[1]
- pose_bone.keyframe_insert(data_path = 'location', index = 1)
- pose_bone.location[2] = bone_translation[2]
- pose_bone.keyframe_insert(data_path = 'location', index = 2)
- #
- #
- #
-
- ######################################
- # make all interpolation curves linear
- # loop through each curve and through
- # each of the curve's keyframe
- curves = bpy.context.active_object.animation_data.action.fcurves
- for curve in curves:
- for keyframe in curve.keyframe_points:
- keyframe.interpolation = 'LINEAR'
-
- # importer end
- 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
-from bpy.types import Operator
-
-
-class Import_CSV_BCK(Operator, ExportHelper):
-#
- """Import a CSV file from J3D Animation Editor of the BCK animation type. Armature to which the animation must be applied must be the only armature in scene and must be the correct one for the animation"""
- bl_idname = "import_scene.csv_bck"
- bl_label = "Import CSV of BCK (from J3D Anim Editor)"
-
- filename_ext = ".csv"
-
- filter_glob = StringProperty(
- default="*.csv",
- options={'HIDDEN'},
- maxlen=255,
- )
-
- import_type = BoolProperty( name = "Ignore Rest Pose",
- description = "Ignore all bone's rest poses and apply animations as SMG does. Modifies the bone's original rest pose.",
- default = False,
- )
-
- def execute(self, context):
- return read_csv_bck(context, self.filepath, self.import_type)
-#
-
-def menu_import_csv_bck(self, context):
- self.layout.operator(Import_CSV_BCK.bl_idname, text="CSV of BCK (from J3D Animation Editor) (.csv)")
-
-bpy.utils.register_class(Import_CSV_BCK)
-bpy.types.INFO_MT_file_import.append(menu_import_csv_bck)
-
-# test call
-bpy.ops.import_scene.csv_bck('INVOKE_DEFAULT')
-
-
diff --git a/test.bin b/test.bin
deleted file mode 100644
index 9f543a3..0000000
--- a/test.bin
+++ /dev/null
Binary files differ
diff --git a/test.py b/test.py
deleted file mode 100644
index db5e2a5..0000000
--- a/test.py
+++ /dev/null
@@ -1,29 +0,0 @@
-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
-
-# ~ 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)