''' file that contains some functions that I don't want to re-write on each individual .py file ''' import bpy, math, re from mathutils import Matrix ################################################ # show message on screen for errors or warnnings # copied this code from a page I saw it in :) ################################################ def show_message(message = "", title = "Message Box", icon = 'INFO'): # def draw(self, context): self.layout.label(text=message) bpy.context.window_manager.popup_menu(draw, title = title, icon = icon) # ####################################### # calc_scale_matrix function # function to build the scale matrix # # x_scale (float) --> scaling in X axis # y_scale (float) --> scaling in Y axis # z_scale (float) --> scaling in Z axis ####################################### def calc_scale_matrix(x_scale, y_scale, z_scale): # scale_matrix = Matrix(( [x_scale, 0, 0, 0], [0, y_scale, 0, 0], [0, 0, z_scale, 0], [0, 0, 0, 1] )) return scale_matrix # ################################################### # calc_rotation_matrix function # function to calculate the rotation matrix # for a Extrinsic Euler XYZ system # # x_angle (float) --> rot angle in X axis (radians) # y_angle (float) --> rot angle in Y axis (radians) # z_angle (float) --> rot angle in > axis (radians) ################################################### def calc_rotation_matrix(x_angle, y_angle, z_angle): # x_rot_mat = Matrix(([1, 0, 0, 0], [0, math.cos(x_angle), -math.sin(x_angle), 0], [0, math.sin(x_angle), math.cos(x_angle), 0], [0, 0, 0, 1])) y_rot_mat = Matrix(([math.cos(y_angle), 0, math.sin(y_angle), 0], [0, 1, 0, 0], [-math.sin(y_angle), 0, math.cos(y_angle), 0], [0, 0, 0, 1])) z_rot_mat = Matrix(([math.cos(z_angle), -math.sin(z_angle), 0, 0], [math.sin(z_angle), math.cos(z_angle), 0, 0], [0, 0, 1, 0], [0, 0, 0, 1])) return z_rot_mat * y_rot_mat * x_rot_mat # ################################################# # calc_translation_matrix function # function to build the translation matrix # # x_translation (float) --> translation in X axis # y_translation (float) --> translation in Y axis # z_translation (float) --> translation in Z axis ################################################# def calc_translation_matrix(x_translation, y_translation, z_translation): # translation_matrix = Matrix(( [1, 0, 0, x_translation], [0, 1, 0, y_translation], [0, 0, 1, z_translation], [0, 0, 0, 1] )) return translation_matrix # ############################################################## # interpolate function # used to find a value in an inte rval with the specified mode. # So that it is clear that the values are points that have 2 # coordinates I will treat the input as they are (x,y) points # the function will either return m_x or m_y depending on # which of the middle point value s is provided to the function # (set None to the variable going to be returned by the # function). Smooth will use the Cubic Hermite Spline # interpolation method and will be the one using the point's # derivatives/tangent. # # IMPORTANT (FOR SMOOTH INTERPOLA TION): # # As I don't really have information on how to define # tangent-out or tangent in/out interpolation (as JAE has # already done those calculations) I will use the same # interpolation method to both tangent-out or tangent in/out # CSV files. If someone knows about this better please open # an issue on blenxy's repository # # l_x (float) --> left point X axis component # l_y (float) --> left point Y axis component # r_x (float) --> right point X axis component # r_y (float) --> right point Y axis component # m_x (float) --> middle point X axis component # m_y (float) --> middle point Y axis component # interp_type (string) --> "linear" for linear interpolation # "smooth" for smooth interpolation ############################################################## def interpolate(l_x, l_y, r_x, r_y, m_x, m_y, interp_type): # # variable to be returned result = 0 ############################################## # if right point does not exist (special case) # return l_y as the interpolation result if (r_x == None or r_y == None): result = l_y return result ############################### # m_x is the one to be returned if (m_x == None): if (interp_type == "linear"): m_x = (((r_x - l_x) / (r_y - l_y)) * (m_y - r_y)) + r_x result = m_x else: # interpolation type is "smooth" ############################### # m_y is the one to be returned if (m_y == None): if (interp_type == "linear"): m_y = (((r_y - l_y) / (r_x - l_x)) * (m_x - r_x)) + r_y result = m_y return result # #################################################################### # find_left_right function # find the values and positions of the elements at the left and the # right of the element in position pos on the anim_array # elements will be used in the interpolate() function later # # anim_array (array of floats) --> anim property frame array its # length must the animation length # pos (int) --> position of the animation property value to be # later interpolated in the anim_array array #################################################################### def find_left_right(anim_array, pos): # ############################# # create left/right variables l_val = 0 l_val_pos = 0 r_val = 0 r_val_pos = 0 ##################################### # find near left value (has to exist) # read array from right to left for i in range(len(anim_array), -1, -1): # # skip elements at the right of # pos in anim_array if (i >= pos): continue # left value is found if (anim_array[i] != None): l_val = anim_array[i] l_val_pos = i break # ############## # special case ############## # if pos is the last element position on # the array r_val and r_val_pos do not exist if (pos == (len(anim_array) - 1)): return [l_val_pos, l_val, None, None] ######################################### # find near right value (might not exist) # read array from left to right for i in range(len(anim_array)): # # skip elements at the left of # pos in anim_array if (i <= pos): continue # right value is found if (anim_array[i] != None): r_val = anim_array[i] r_val_pos = i break # if no value is found at the end of # the anim_array r_val and r_val_pos do not exist # (value does not change between the left value # found and the end of the animation) if (i == (len(anim_array) - 1)): return [l_val_pos, l_val, None, None] # # if all values are found, return them return [l_val_pos, l_val, r_val_pos, r_val] # ####################################################### # convert_angle_to_180 function # function used by the convert_anim_rot_to_180 function # to convert a single angle in its representation on # the -180/+180 degree range (angles passed to the # function that are already in this range will be # returned without conversion) # # angle (float) --> angle to convert to the -180/+180 # degree range (angle is expected to # be in degrees) ####################################################### def convert_angle_to_180(angle): # # check if the angle really needs to be processed # i.e. is already inside the -180/+180 degree range if (angle >= -180 and angle <= 180): return angle # convert it otherwise # check if it is positive or negative # and set the opposite direction of the angle # if the angle is > 0 then its mesurement is clockwise (opposite is counter-clockwise) # if the angle is < 0 then its mesurement is counter-clockwise (opposite is clockwise) if (angle > 0): opposite_spin_dir = -1 else: # it is negative opposite_spin_dir = 1 # decrease the angle by 360 degrees until it # is in the -180/+180 degree interval while (abs(angle) > 180): angle = angle + (opposite_spin_dir * 360) return angle # ############################################################### # convert_anim_rot_to_180 function # used to re-calculate a rotation animation on an axis # so that angles used lay in between -180/180 degrees # done to avoid rotation animation data loss when extracting # said angles from a transformation matrix # this function calls the convert_angle_to_180() function # at the end of the function csv_keyframe_numbers is updated # with the new frames to be injected into the animation # # example: # # Original keyframes: # Frame 0 Frame 21 (2 keyframes) # 0º 360º # # Processed keyframes: # Frame 0 Frame 10 Frame 11 Frame 21 (4 keyframes) # 0º 171.4º -171.5º 0º # # rot_array (array of floats) --> bone rotation animation data # for a single axis # csv_keyframe_numbers (array of ints) --> original keyframes # of the animation # # Note: function will have problems interpreting keyframes with # high rotation diferences if the number of frames in # between said keyframes in lower than 2 times the spins # done in between those keyframe angles (thinking a fix) ############################################################### def convert_rot_anim_to_180(rot_array, csv_keyframe_numbers): # # temp rot array to store calculated values and keyframe position rot_array_cp = [[], []] # find the frames in which rot_array has values defined rot_array_kf = [] for i in range(len(rot_array)): if (rot_array[i] != None): rot_array_kf.append(i) ########################################################################### # loop through each consecutive pair of keyframes of the rot_array_kf array for i in range(len(rot_array_kf) - 1): # # get left/right keyframe values and positions l_kf_pos = rot_array_kf[i] l_kf_val = rot_array[l_kf_pos] r_kf_pos = rot_array_kf[i + 1] r_kf_val = rot_array[r_kf_pos] # append l_kf_val to rot_array_cp (converted) rot_array_cp[0].append(l_kf_pos) rot_array_cp[1].append(convert_angle_to_180(l_kf_val)) # get the rotation direction if (r_kf_val > l_kf_val): # clockwise rot_direction = 1 else: # counter-clockwise rot_direction = -1 ############################################################## # advance -180/+180 (depending on the rotation direction) # and generate the middle keyframe values # angle is fixed to the l_kf_val's closest 360 degree multiple angle_val = l_kf_val - convert_angle_to_180(l_kf_val) while (abs(r_kf_val - angle_val) > 180): # angle_val = angle_val + (rot_direction * 180) # find the frame (float) in which this value exists angle_pos = interpolate(l_kf_pos, l_kf_val, r_kf_pos, r_kf_val, None, angle_val, "linear") ############################################ # find the 2 frames (integer) that are lower # and upper limits of this angle_pos lower_frame = int(angle_pos) upper_frame = int(angle_pos + 1) ############################################################### # interpolate to find the values on lower_frame and upper_frame lower_frame_value = convert_angle_to_180(interpolate(l_kf_pos, l_kf_val, r_kf_pos, r_kf_val, lower_frame, None, "linear")) upper_frame_value = convert_angle_to_180(interpolate(l_kf_pos, l_kf_val, r_kf_pos, r_kf_val, upper_frame, None, "linear")) ################################ # append results to rot_array_cp # keyframes rot_array_cp[0].append(lower_frame) rot_array_cp[0].append(upper_frame) # values rot_array_cp[1].append(lower_frame_value) rot_array_cp[1].append(upper_frame_value) # # ########################################## # add the new keyframe values to rot_array # on their respective frame position for i in range(len(rot_array_cp[0])): rot_array[rot_array_cp[0][i]] = rot_array_cp[1][i] ####################################################### # update the keyframes on csv_keyframe_numbers to store # the calculated keyframes numbers from rot_array_cp[0] # append those at the end of csv_keyframe_numbers for i in range(len(rot_array_cp[0])): value_found = False for j in range(len(csv_keyframe_numbers)): if (rot_array_cp[0][i] == csv_keyframe_numbers[j]): value_found = True break if (value_found == False): csv_keyframe_numbers.append(rot_array_cp[0][i]) #