import csv import bpy from mathutils import * import sys argv = sys.argv argv = argv[argv.index("--") + 1:] # get all args after "--" D = bpy.data C = bpy.context def copy_armature_animation( source_armature_name, destination_armature_name, start_frame_source, end_frame_source, start_frame_target, ): # Get the source and destination armature objects source_armature = bpy.data.objects[source_armature_name] destination_armature = bpy.data.objects[destination_armature_name] # Copy the animation data from the source armature to the destination armature #print(f"copying animation {start_frame_source}-{end_frame_source} to {start_frame_target} ") for i, frame in enumerate(range(start_frame_source, end_frame_source + 1)): bpy.context.scene.frame_set(frame) # Set the location and rotation of the destination bone at the given frame for bone in source_armature.pose.bones: destination_bone = destination_armature.pose.bones.get(bone.name) destination_bone.location = bone.location destination_bone.rotation_quaternion = bone.rotation_quaternion destination_bone.keyframe_insert( data_path="location", frame=start_frame_target + i ) destination_bone.keyframe_insert( data_path="rotation_quaternion", frame=start_frame_target + i ) # Copy the bone's animation data for each frame in the range def adjust_armature_animation_timing( armature_name, old_start, old_end, new_start, new_end ): # Get the armature object by name armature = bpy.data.objects[armature_name] # Calculate the ratio between the old and new keyframe ranges old_range = old_end - old_start new_range = new_end - new_start ratio = new_range / old_range #print(f"Adjsuting timming from {old_start}-{old_end} to {new_start}-{new_end} with ratio: {ratio}") # Iterate through all the armature bones and modify their animation keyframes for fcurve in armature.animation_data.action.fcurves: if fcurve.array_index < 4: for keyframe in fcurve.keyframe_points: # Adjust the keyframe position based on the ratio if keyframe.co.x in range (old_start, old_end): #print(f"keyframe move from {keyframe.co.x}") keyframe.co.x = (keyframe.co.x - old_start) * ratio + new_start #print(f"to {keyframe.co.x}") else: continue def calculate_new_frame_range(frame_rate, timestamp, start_frame): adjusted_end_frame = int(timestamp / 1000 * frame_rate + start_frame) return adjusted_end_frame def change_material_property( material_name, node_name, new_value, start_frame, end_frame ): value = bpy.data.materials[material_name].node_tree.nodes[node_name].outputs[0] value.default_value = new_value value.keyframe_insert(data_path="default_value", frame=start_frame+1) value.keyframe_insert(data_path="default_value", frame=end_frame-1) def set_cues_state( global_state, global_color, c1_state, c1_color, c2_state, c2_color, c3_state, c3_color, c4_state, c4_color, start_frame, end_frame, ): # cue 1 noise change_material_property( "CUE_MAT_1", "Distortion", float(c1_state), start_frame, end_frame ) # cue 2 noise change_material_property( "CUE_MAT_2", "Distortion", float(c2_state), start_frame, end_frame ) # cue 3 noise change_material_property( "CUE_MAT_3", "Distortion", float(c3_state), start_frame, end_frame ) # cue 4 noise change_material_property( "CUE_MAT_4", "Distortion", float(c4_state), start_frame, end_frame ) # cue 1 color change_material_property( "CUE_MAT_1", "Color", float(c1_color), start_frame, end_frame ) # cue 2 color change_material_property( "CUE_MAT_2", "Color", float(c2_color), start_frame, end_frame ) # cue 3 color change_material_property( "CUE_MAT_3", "Color", float(c3_color), start_frame, end_frame ) # cue 4 color change_material_property( "CUE_MAT_4", "Color", float(c4_color), start_frame, end_frame ) # global cue on change_material_property( "GLOBAL_CUE_MAT", "MixFactor", float(global_state), start_frame, end_frame ) # global cue color change_material_property( "GLOBAL_CUE_MAT", "Color", float(global_color), start_frame, end_frame ) def animate_cycle(source_armature, target_armature, csv_file): # Get the current scene scene = bpy.context.scene # Get the frame rate of the current scene frame_rate = scene.render.fps / scene.render.fps_base gait_to_keyframe_start = {0: 10, 1: 80, 2: 60, 3: 250} gait_to_keyframe_end = {0: 49, 1: 99, 2: 74, 3: 310} # column_names = ["Gait", "TS", "State", "Condition", "Shape1", "Shape2", "Shape3", "Shape4", "Color1", "Color2", "Color3", "Color4", "Danger1", "Danger2", "Danger3", "Danger4"] previous_end_frame = 0 with open(csv_file, "r") as csvfile: datareader = csv.reader(csvfile) for i, row in enumerate(datareader): if i == 0 or row[0] is None: continue copy_armature_animation( source_armature, target_armature, gait_to_keyframe_start[int(row[0])], gait_to_keyframe_end[int(row[0])], previous_end_frame + 1, ) adjusted_end_frame = calculate_new_frame_range( frame_rate=frame_rate, timestamp=int(float(row[1])), start_frame=previous_end_frame + 1, ) adjust_armature_animation_timing( target_armature, previous_end_frame + 1, previous_end_frame + 1 + gait_to_keyframe_end[int(row[0])] - gait_to_keyframe_start[int(row[0])], previous_end_frame + 1, adjusted_end_frame, ) set_cues_state( row[3], row[4], row[8], row[5], row[9], row[6], row[10], row[7], row[11], row[2], previous_end_frame, adjusted_end_frame, ) #print(previous_end_frame, adjusted_end_frame) previous_end_frame = adjusted_end_frame print(f"Animated row {i}") bpy.context.scene.frame_end = previous_end_frame bpy.ops.render.render(animation=True) print("Cycle_device:") print(bpy.context.preferences.addons["cycles"].preferences.has_active_device()) animate_cycle("Deformation", "Deformation.001", str(argv[0]))