def shift_all_colors_in_mat3(self, file_name, j3d_file, h_shift, v_shift): for i, color in enumerate(j3d_file.mat3.reg_colors): r, g, b, a = color if r < 0 or g < 0 or b < 0: # Negative color? Skip it to avoid errors. continue r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) j3d_file.mat3.reg_colors[i] = (r, g, b, a) for i, color in enumerate(j3d_file.mat3.konst_colors): r, g, b, a = color r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) j3d_file.mat3.konst_colors[i] = (r, g, b, a)
def shift_hardcoded_color_in_rel(rel, offset, h_shift, v_shift): r = rel.read_data(read_u8, offset + 0) g = rel.read_data(read_u8, offset + 1) b = rel.read_data(read_u8, offset + 2) r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) rel.write_data(write_u8, offset + 0, r) rel.write_data(write_u8, offset + 1, g) rel.write_data(write_u8, offset + 2, b)
def shift_all_colors_in_particle(self, particle, h_shift, v_shift): #print("%04X" % particle_id) #print(particle.tdb1.texture_filenames) # Changing value/saturation of particle colors can sometimes make them disappear or be bigger/smaller than in vanilla if the changes are too extreme, so limit to hue shifting only for particles. v_shift = 0 r, g, b, a = particle.bsp1.color_prm r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) particle.bsp1.color_prm = (r, g, b, a) r, g, b, a = particle.bsp1.color_env r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) particle.bsp1.color_env = (r, g, b, a) #print(particle.bsp1.color_prm_anm_data_count) for keyframe in particle.bsp1.color_prm_anm_table: r, g, b, a = keyframe.color r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) keyframe.color = (r, g, b, a) #print(particle.bsp1.color_env_anm_data_count) for keyframe in particle.bsp1.color_env_anm_table: r, g, b, a = keyframe.color r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) keyframe.color = (r, g, b, a) if hasattr(particle, "ssp1"): r, g, b, a = particle.ssp1.color_prm r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) particle.ssp1.color_prm = (r, g, b, a) r, g, b, a = particle.ssp1.color_env r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) particle.ssp1.color_env = (r, g, b, a)
def shift_hardcoded_puppet_ganon_colors(self, h_shift, v_shift): # Puppet ganon's strings rel = self.get_rel("files/rels/d_a_bgn.rel") offset = 0xF0A8 shift_hardcoded_color_in_rel(rel, offset, h_shift, v_shift) offset = 0xF0B0 shift_hardcoded_color_in_rel(rel, offset, h_shift, v_shift) rel = self.get_rel("files/rels/d_a_bgn3.rel") r = rel.read_data(read_u8, 0x2CF) g = rel.read_data(read_u8, 0x2D7) b = rel.read_data(read_u8, 0x2DF) r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) rel.write_data(write_u8, 0x2CF, r) rel.write_data(write_u8, 0x2D7, g) rel.write_data(write_u8, 0x2DF, b)
def shift_all_colors_in_mdl3(self, file_name, j3d_file, h_shift, v_shift): for entry in j3d_file.mdl3.entries: tev_color_commands = [ com for com in entry.bp_commands if com.register >= BPRegister.TEV_REGISTERL_0.value and com.register <= BPRegister.TEV_REGISTERH_3.value ] assert len(tev_color_commands ) % 2 == 0 # They should come in pairs of low and high last_hi_command = None last_hi_command_orig_value = None for i in range(0, len(tev_color_commands), 2): lo_command = tev_color_commands[i + 0] hi_command = tev_color_commands[i + 1] if hi_command.register != lo_command.register + 1: # The only time they're not properly paired is when the hi command gets duplicated an additional 2 times. assert last_hi_command is not None assert last_hi_command.register == hi_command.register == lo_command.register assert last_hi_command_orig_value == hi_command.value == lo_command.value # Update the color here too hi_command.value = last_hi_command.value lo_command.value = last_hi_command.value continue last_hi_command = hi_command last_hi_command_orig_value = hi_command.value r = (lo_command.value & 0x0007FF) g = (hi_command.value & 0x7FF000) >> 12 b = (hi_command.value & 0x0007FF) a = (lo_command.value & 0x7FF000) >> 12 r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) lo_command.value &= ~0x7FF7FF hi_command.value &= ~0x7FF7FF lo_command.value |= ((r << 0) & 0x0007FF) hi_command.value |= ((g << 12) & 0x7FF000) hi_command.value |= ((b << 0) & 0x0007FF) lo_command.value |= ((a << 12) & 0x7FF000)
def shift_hardcoded_chuchu_colors(self, h_shift, v_shift): # ChuChu particles rel = self.get_rel("files/rels/d_a_cc.rel") offset = 0x7F88 for i in range(5): shift_hardcoded_color_in_rel(rel, offset + i * 4, h_shift, v_shift) # The particles that come off of Dark ChuChus when attacked where they temporarily break apart and reform are tricky. # That RGB value is stored as three multiplier floats instead of three bytes, and the red multiplier in the float constant bank is coincidentally reused by other things in the ChuChu code unrelated to color so we can't change that. # So we change the asm code to read the red multiplier from elsewhere, and then modify that instead. r = int(rel.read_data(read_float, 0x7E9C)) g = int(rel.read_data(read_float, 0x7EBC)) b = int(rel.read_data(read_float, 0x7EC0)) assert r != 0 # Make sure the asm patch was applied r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) rel.write_data(write_float, 0x7E9C, r) rel.write_data(write_float, 0x7EBC, g) rel.write_data(write_float, 0x7EC0, b)
def shift_all_colors_in_trk1(self, file_name, j3d_file, h_shift, v_shift): animations = [] for mat_name, anims in j3d_file.trk1.mat_name_to_reg_anims.items(): animations += anims for mat_name, anims in j3d_file.trk1.mat_name_to_konst_anims.items(): animations += anims for anim_index, anim in enumerate(animations): if file_name == "cc.brk" and anim_index == 1: # ChuChu eyes material animation, doesn't look right recolored so we just recolor the texture instead continue assert len(anim.r.keyframes) > 0 and len(anim.g.keyframes) > 0 and len( anim.b.keyframes) > 0 # In some cases (specifically Gohma), there won't be an equal number of keyframes for R G and B, so we can't simply iterate over the list. # First make a list of what times are present on the timeline for this animation. unique_keyframe_times = [] for keyframe in (anim.r.keyframes + anim.g.keyframes + anim.b.keyframes): if keyframe.time not in unique_keyframe_times: unique_keyframe_times.append(keyframe.time) unique_keyframe_times.sort() def get_keyframe_by_closest_time(keyframes, keyframe_time): return min(keyframes, key=lambda kf: abs(kf.time - keyframe_time)) def get_keyframe_by_exact_time(keyframes, keyframe_time): return next((kf for kf in keyframes if kf.time == keyframe_time), None) # Then make a list of what the modified colors at each time will be, but don't actually modify them yet since we may need to re-read the values of previous times if the next time is missing a channel. modified_colors_by_time = {} for keyframe_time in unique_keyframe_times: #print(" %d" % keyframe_time) r = get_keyframe_by_closest_time(anim.r.keyframes, keyframe_time).value & 0xFF g = get_keyframe_by_closest_time(anim.g.keyframes, keyframe_time).value & 0xFF b = get_keyframe_by_closest_time(anim.b.keyframes, keyframe_time).value & 0xFF #print(" %d %d %d" % (r, g, b)) r, g, b = texture_utils.hsv_shift_color((r, g, b), h_shift, v_shift) modified_colors_by_time[keyframe_time] = (r, g, b) # Then actually modify the colors. for keyframe_time in unique_keyframe_times: r, g, b = modified_colors_by_time[keyframe_time] r_keyframe = get_keyframe_by_exact_time(anim.r.keyframes, keyframe_time) if r_keyframe: r_keyframe.value = r g_keyframe = get_keyframe_by_exact_time(anim.g.keyframes, keyframe_time) if g_keyframe: g_keyframe.value = g b_keyframe = get_keyframe_by_exact_time(anim.b.keyframes, keyframe_time) if b_keyframe: b_keyframe.value = b