From 15498bfdc4e7f3b7c7f2edc83bdf772cc3b805ff Mon Sep 17 00:00:00 2001 From: Cuyler36 Date: Thu, 13 Jul 2023 08:45:57 -0400 Subject: [PATCH] Implement & link evw_anime.c --- config/rel_slices.yml | 4 + include/evw_anime.h | 67 ++++++++- include/m_field_info.h | 2 + include/m_field_make.h | 39 ++++++ include/m_play.h | 4 +- rel/ac_train_window.c | 11 +- rel/evw_anime.c | 305 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 420 insertions(+), 12 deletions(-) create mode 100644 rel/evw_anime.c diff --git a/config/rel_slices.yml b/config/rel_slices.yml index fc44611e..220fa4ea 100644 --- a/config/rel_slices.yml +++ b/config/rel_slices.yml @@ -3,6 +3,10 @@ sys_vimgr.c: c_keyframe.c: .text: [0x80370418, 0x80372C8C] .rodata: [0x80641260, 0x806412A8] +evw_anime.c: + .text: [0x80372C8C, 0x803736D0] + .rodata: [0x806412A8, 0x806412C0] + .data: [0x8064D580, 0x8064D598] m_actor_dlftbls.c: .text: [0x8037619C, 0x803761BC] .data: [0x8064D628, 0x8064F4E8] diff --git a/include/evw_anime.h b/include/evw_anime.h index c1ba1c46..0fdd0686 100644 --- a/include/evw_anime.h +++ b/include/evw_anime.h @@ -2,19 +2,74 @@ #define EVW_ANIME_H #include "types.h" +#include "m_play_h.h" #ifdef __cplusplus extern "C"{ #endif +#define EVW_COLOR_BUF_MAX 50 -typedef struct evw_anime_s{ - s16 unk0; - s16 unk4; - void* data_p; -}EVW_ANIME_DATA; +enum { + EVW_ANIME_TYPE_SCROLL1, + EVW_ANIME_TYPE_SCROLL2, + EVW_ANIME_TYPE_COLREG_MANUAL, + EVW_ANIME_TYPE_COLREG_LINEAR, + EVW_ANIME_TYPE_COLREG_NONLINEAR, + EVW_ANIME_TYPE_TEXANIME, -void Evw_Anime_Set(GAME*, EVW_ANIME_DATA*); + EVW_ANIME_TYPE_NUM +}; + +typedef struct evw_anime_col_prim_s { + u8 r; + u8 g; + u8 b; + u8 a; + u8 l; +} EVW_ANIME_COL_PRIM; + +typedef struct evw_anime_col_env_s { + u8 r; + u8 g; + u8 b; + u8 a; +} EVW_ANIME_COL_ENV; + +typedef struct evw_anime_col_reg_s { + u16 frame_count; /* total frames in the animation */ + u16 key_count; /* number of animation keyframes */ + + EVW_ANIME_COL_PRIM* prim_colors; /* list of primitive colors per-keyframe */ + EVW_ANIME_COL_ENV* env_colors; /* list of environment colors per-keyframe */ + + u16* keyframes; /* list of keyframe frame indices */ +} EVW_ANIME_COLREG; + +typedef struct evw_anime_scroll_s { + s8 x; /* x (s) texture position */ + s8 y; /* y (t) texture position */ + u8 width; /* texture width */ + u8 height; /* texture height */ +} EVW_ANIME_SCROLL; + +typedef struct evw_anime_texanime_s { + u16 frame_count; /* total frames in the animation */ + u16 key_count; /* number of animation keyframes */ + + void** texture_tbl; /* list of each texture */ + u8* animation_pattern; /* list of which texture to use per keyframe (runs at 30 FPS) */ + + u16* keyframes; /* list of keyframe frame indices */ +} EVW_ANIME_TEXANIME; + +typedef struct evw_anime_s { + s8 segment; /* Negative segments signal the end of the animation data array */ + s16 type; + void* data_p; +} EVW_ANIME_DATA; + +extern void Evw_Anime_Set(GAME_PLAY* play, EVW_ANIME_DATA* evw_anime_data); #ifdef __cplusplus } diff --git a/include/m_field_info.h b/include/m_field_info.h index ccc46162..6cf8c931 100644 --- a/include/m_field_info.h +++ b/include/m_field_info.h @@ -112,6 +112,8 @@ typedef struct sound_source_info_s { xyz_t wpos; } mFI_sound_source_info_c; + + extern int mFI_CheckFieldData(); extern mActor_name_t mFI_GetFieldId(); extern int mFI_GetClimate(); diff --git a/include/m_field_make.h b/include/m_field_make.h index 08e96837..28997696 100644 --- a/include/m_field_make.h +++ b/include/m_field_make.h @@ -3,6 +3,7 @@ #include "types.h" #include "m_actor_type.h" +#include "libforest/gbi_extensions.h" #ifdef __cplusplus extern "C" { @@ -29,6 +30,8 @@ extern "C" { #define BLOCKXZ_2_BLOCKIDX(x, z) ((z) * BLOCK_X_NUM + (x)) +#define mFM_VISIBLE_BLOCK_NUM 4 /* number of visible blocks (nearest to the Player) */ + /* sizeof(mFM_combination_c) == 2 */ typedef struct block_combination_s { /* 0x00 */ u16 combination_type:14; /* acre type index */ @@ -47,6 +50,42 @@ typedef struct block_combo_s { /* 0x05 */ u8 type; } mFM_combo_info_c; +typedef struct field_display_list_info_s { + int block_x; + int block_z; + u8* display_list; +} mFM_field_draw_info_c; + +typedef struct field_pal_s { + u16* earth_pal; + u16* cliff_pal; + u16* bush_pal; + u16* flower0_pal; + u16* flower1_pal; + u16* flower2_pal; + u16* grass_pal; + u16* tree_pal; + u16* cedar_tree_pal; // probably? + u16* palm_tree_pal; + u16* golden_tree_pal; +} mFM_field_pal_c; + +typedef struct field_bg_info_s { + mFM_combination_c bg_id; + Gfx* oapque_gfx; + Gfx* translucent_gfx; + +} mFM_bg_info_c; + +typedef struct field_info_s { + mActor_name_t field_id; + u32 _04; // only set, never read? + mFM_field_draw_info_c bg_draw_info[mFM_VISIBLE_BLOCK_NUM]; + u8* bg_display_list_p[mFM_VISIBLE_BLOCK_NUM]; + mFM_field_pal_c field_palette; + +} mFM_fdinfo_c; + extern u8* g_block_type_p; extern int* g_block_kind_p; diff --git a/include/m_play.h b/include/m_play.h index ea4d5de3..d57e2f7f 100644 --- a/include/m_play.h +++ b/include/m_play.h @@ -39,7 +39,9 @@ struct game_play_s { /* 0x1FA4 */ u8 _1FA4[0x2008 - 0x1FA4]; /* 0x2008 */ int next_scene_no; /* 0x200C */ MtxF matrix; - /* 0x204C */ u8 _204C[0x20D0-0x204C]; + /* 0x204C */ u8 _204C[0x2090 - 0x204C]; + /* 0x2090 */ u32 game_frame; + /* 0x2094 */ u8 _2094[0x20D0 - 0x2094]; /* 0x20D0 */ u8 fb_fade_type; /* 0x20D1 */ u8 fb_wipe_type; /* 0x20D2 */ u8 fb_mode; diff --git a/rel/ac_train_window.c b/rel/ac_train_window.c index 3729abda..eafc637b 100644 --- a/rel/ac_train_window.c +++ b/rel/ac_train_window.c @@ -161,13 +161,13 @@ u16 aTrainWindow_tree_pal_table[15][16]= { } }; -u8 aTrainWindow_out_cloud[4] = { - 0,254, - 64,64 +EVW_ANIME_SCROLL aTrainWindow_out_cloud = { + 0, -2, + 64, 64 }; EVW_ANIME_DATA aTrainWindow_evw_anime_data = { - -512, 0, &aTrainWindow_out_cloud + -2, EVW_ANIME_TYPE_SCROLL1, &aTrainWindow_out_cloud }; static void Train_Window_Actor_ct(ACTOR*, GAME*); @@ -567,6 +567,7 @@ static void aTrainWindow_SetTreeTextureScroll(ACTOR* actor, GAME* game){ } static void Train_Window_Actor_draw(ACTOR* actor, GAME* game){ + GAME_PLAY* play = (GAME_PLAY*)game; TRAIN_WINDOW_ACTOR* window = (TRAIN_WINDOW_ACTOR* )actor; GRAPH* graph = game->graph; @@ -585,7 +586,7 @@ static void Train_Window_Actor_draw(ACTOR* actor, GAME* game){ if(window->draw_type(actor,game) != 0){ aTrainWindow_SetLightPrimColorDetail(game, 0,0,0,127,255,(u8)window->xlu_alpha); gSPSegment(NOW_POLY_XLU_DISP++,10, window->current_pallete); - Evw_Anime_Set(game, &aTrainWindow_evw_anime_data); + Evw_Anime_Set(play, &aTrainWindow_evw_anime_data); aTrainWindow_SetLightPrimColorDetail(game,0,0,0,43,255,window->xlu_alpha); gSPDisplayList(NOW_POLY_OPA_DISP++,rom_train_out_tunnel_model); aTrainWindow_SetLightPrimColorDetail(game,0,0,0,127,255,window->xlu_alpha); diff --git a/rel/evw_anime.c b/rel/evw_anime.c new file mode 100644 index 00000000..11c2059f --- /dev/null +++ b/rel/evw_anime.c @@ -0,0 +1,305 @@ +#include "evw_anime.h" + +#include "m_play.h" +#include "libforest/gbi_extensions.h" +#include "m_rcp.h" + +static Gfx* evw_tex_scroll_set(GAME_PLAY* play, EVW_ANIME_SCROLL* scroll) { + int scroll_x = scroll->x * play->game_frame; + int scroll_y = scroll->y * play->game_frame; + + return tex_scroll2_dolphin(play->game.graph, scroll_x, -scroll_y, scroll->width, scroll->height); +} + +static void evw_anime_scroll1(GAME_PLAY* play, int segment, void* evw_data) { + EVW_ANIME_SCROLL* scroll = (EVW_ANIME_SCROLL*)evw_data; + Gfx* scroll_gfx = evw_tex_scroll_set(play, scroll); + GRAPH* g = play->game.graph; + + OPEN_DISP(g); + + gSPSegment(NOW_BG_OPA_DISP++, segment, scroll_gfx); + gSPSegment(NOW_POLY_OPA_DISP++, segment, scroll_gfx); + gSPSegment(NOW_POLY_XLU_DISP++, segment, scroll_gfx); + + CLOSE_DISP(g); +} + +static Gfx* evw_two_tex_scroll_set(GAME_PLAY* play, EVW_ANIME_SCROLL* scrolls) { + u32 frame = play->game_frame; + + int x0 = scrolls[0].x * frame; + int y0 = scrolls[0].y * frame; + + int x1 = scrolls[1].x * frame; + int y1 = scrolls[1].y * frame; + + return two_tex_scroll_dolphin( + play->game.graph, + 0, + x0, -y0, scrolls[0].width, scrolls[0].height, + 1, + x1, -y1, scrolls[1].width, scrolls[1].height + ); +} + +static void evw_anime_scroll2(GAME_PLAY* play, int segment, void* evw_data) { + EVW_ANIME_SCROLL* scrolls = (EVW_ANIME_SCROLL*)evw_data; + Gfx* scroll_gfx = evw_two_tex_scroll_set(play, scrolls); + GRAPH* g = play->game.graph; + + OPEN_DISP(g); + + gSPSegment(NOW_BG_OPA_DISP++, segment, scroll_gfx); + gSPSegment(NOW_POLY_OPA_DISP++, segment, scroll_gfx); + gSPSegment(NOW_POLY_XLU_DISP++, segment, scroll_gfx); + + CLOSE_DISP(g); +} + +static void evw_color_set(GAME_PLAY* play, int segment, EVW_ANIME_COL_PRIM* prim, EVW_ANIME_COL_ENV* env) { + Gfx* col_gfx = GRAPH_ALLOC_TYPE(play->game.graph, Gfx, 3); + + /* Setup small display list for initializing color settings */ + gDPSetPrimColor(col_gfx + 0, 0, prim->l, prim->r, prim->g, prim->b, prim->a); + gDPSetEnvColor(col_gfx + 1, env->r, env->g, env->b, env->a); + gSPEndDisplayList(col_gfx + 2); + + OPEN_DISP(play->game.graph); + + /* Point the necessary Gfx buffers to our color display list */ + gSPSegment(NOW_BG_OPA_DISP++, segment, col_gfx); + gSPSegment(NOW_POLY_OPA_DISP++, segment, col_gfx); + gSPSegment(NOW_POLY_XLU_DISP++, segment, col_gfx); + + CLOSE_DISP(play->game.graph); +} + +static void evw_anime_colreg_manual(GAME_PLAY* play, int segment, void* evw_data) { + EVW_ANIME_COLREG* color_reg = (EVW_ANIME_COLREG*)evw_data; + + int frame_idx = play->game_frame % color_reg->frame_count; + EVW_ANIME_COL_PRIM* prim = color_reg->prim_colors; + EVW_ANIME_COL_ENV* env = color_reg->env_colors; + + prim += frame_idx; + env += frame_idx; + + evw_color_set(play, segment, prim, env); +} + +static int morf_calc(int now, int target, f32 rate) { + return now + (int)(rate * (f32)(target - now)); +} + +static void evw_anime_colreg_linear(GAME_PLAY* play, int segment, void* evw_data) { + EVW_ANIME_COLREG* color_reg = (EVW_ANIME_COLREG*)evw_data; + EVW_ANIME_COL_PRIM* src_prim = color_reg->prim_colors; + EVW_ANIME_COL_ENV* src_env = color_reg->env_colors; + + u16* keyframe = color_reg->keyframes; + u32 game_frame = play->game_frame; + int anime_frame = game_frame % color_reg->frame_count; + int now_keyframe; + int last_keyframe; + int frame_idx = 1; + int i; + + keyframe++; + for (i = 1; i < color_reg->key_count; i++) { + if (anime_frame < *keyframe) { + break; + } + + keyframe++; + frame_idx++; + } + + { + EVW_ANIME_COL_PRIM inter_prim; + EVW_ANIME_COL_ENV inter_env; + int a; + int b; + + now_keyframe = keyframe[0]; + last_keyframe = keyframe[-1]; + a = anime_frame - last_keyframe; + b = now_keyframe - last_keyframe; + + src_prim += frame_idx; + src_env += frame_idx; + + /* Interpolate primitive color */ + inter_prim.r = morf_calc(src_prim[-1].r, src_prim[0].r, (f32)a / (f32)b); + inter_prim.g = morf_calc(src_prim[-1].g, src_prim[0].g, (f32)a / (f32)b); + inter_prim.b = morf_calc(src_prim[-1].b, src_prim[0].b, (f32)a / (f32)b); + inter_prim.a = morf_calc(src_prim[-1].a, src_prim[0].a, (f32)a / (f32)b); + inter_prim.l = morf_calc(src_prim[-1].l, src_prim[0].l, (f32)a / (f32)b); + + /* Interpolate environment color */ + inter_env.r = morf_calc(src_env[-1].r, src_env[0].r, (f32)a / (f32)b); + inter_env.g = morf_calc(src_env[-1].g, src_env[0].g, (f32)a / (f32)b); + inter_env.b = morf_calc(src_env[-1].b, src_env[0].b, (f32)a / (f32)b); + inter_env.a = morf_calc(src_env[-1].a, src_env[0].a, (f32)a / (f32)b); + + evw_color_set(play, segment, &inter_prim, &inter_env); + } +} + +// Ragrange is probably Lagrange +static f32 RagrangeInt(int num_keyframes, f32* keyframes, f32* values, f32 now_frame) { + f32 stack[EVW_COLOR_BUF_MAX]; + f32 res; + f32 n; + f32 m; + int i; + + /* BUG: fixed stack buffer size but no input length validation. */ + for (i = 0; i < num_keyframes; i++) { + int j; + int k; + n = keyframes[i]; + m = 1.0f; + + /* Adjust keyframe by difference between all previous keyframes and the current keyframe */ + for (j = 0; j < i; j++) { + m *= n - keyframes[j]; + } + + for (k = j + 1; k < num_keyframes; k++) { + m *= n - keyframes[k]; + } + + stack[i] = values[i] / m; + } + + res = 0.0f; + for (i = 0; i < num_keyframes; i++) { + int j; + int k; + m = 1.0f; + + for (j = 0; j < i; j++) { + m *= now_frame - keyframes[j]; + } + + for (k = j + 1; k < num_keyframes; k++) { + m *= now_frame - keyframes[k]; + } + + res += m * stack[i]; + } + + return res; +} + +static u8 RagrangeInt_Color(int num_keyframes, f32* keyframes, f32* values, f32 now_frame) { + return (u8)(int)RagrangeInt(num_keyframes, keyframes, values, now_frame); +} + +typedef struct { + f32 keyframe_stack[EVW_COLOR_BUF_MAX]; + + f32 prim_r_stack[EVW_COLOR_BUF_MAX]; + f32 prim_g_stack[EVW_COLOR_BUF_MAX]; + f32 prim_b_stack[EVW_COLOR_BUF_MAX]; + f32 prim_a_stack[EVW_COLOR_BUF_MAX]; + f32 prim_l_stack[EVW_COLOR_BUF_MAX]; + + f32 env_r_stack[EVW_COLOR_BUF_MAX]; + f32 env_g_stack[EVW_COLOR_BUF_MAX]; + f32 env_b_stack[EVW_COLOR_BUF_MAX]; + f32 env_a_stack[EVW_COLOR_BUF_MAX]; +} EVW_ANIME_COLREG_F; + +static void evw_anime_colreg_nonlinear(GAME_PLAY* play, int segment, void* evw_data) { + EVW_ANIME_COLREG* colreg = (EVW_ANIME_COLREG*)evw_data; + + EVW_ANIME_COL_PRIM* prim_p; + EVW_ANIME_COL_ENV* env_p; + EVW_ANIME_COL_PRIM* prim_src = colreg->prim_colors; + EVW_ANIME_COL_ENV* env_src = colreg->env_colors; + u16* keyframes = colreg->keyframes; + f32 now_frame = play->game_frame % colreg->frame_count; + int i; + + EVW_ANIME_COLREG_F colreg_float; + + + /* BUG: fixed stack buffer size but no input length validation. */ + for (i = 0; i < colreg->key_count; i++) { + colreg_float.keyframe_stack[i] = (int)keyframes[i]; + + prim_p = &prim_src[i]; + env_p = &env_src[i]; + + colreg_float.prim_r_stack[i] = (int)prim_p->r; + colreg_float.prim_g_stack[i] = (int)prim_p->g; + colreg_float.prim_b_stack[i] = (int)prim_p->b; + colreg_float.prim_a_stack[i] = (int)prim_p->a; + colreg_float.prim_l_stack[i] = (int)prim_p->l; + + colreg_float.env_r_stack[i] = (int)env_p->r; + colreg_float.env_g_stack[i] = (int)env_p->g; + colreg_float.env_b_stack[i] = (int)env_p->b; + colreg_float.env_a_stack[i] = (int)env_p->a; + } + + { + EVW_ANIME_COL_PRIM nonlinear_prim; + EVW_ANIME_COL_ENV nonlinear_env; + + nonlinear_prim.r = RagrangeInt_Color(colreg->key_count, colreg_float.keyframe_stack, colreg_float.prim_r_stack, now_frame); + nonlinear_prim.g = RagrangeInt_Color(colreg->key_count, colreg_float.keyframe_stack, colreg_float.prim_g_stack, now_frame); + nonlinear_prim.b = RagrangeInt_Color(colreg->key_count, colreg_float.keyframe_stack, colreg_float.prim_b_stack, now_frame); + nonlinear_prim.a = RagrangeInt_Color(colreg->key_count, colreg_float.keyframe_stack, colreg_float.prim_a_stack, now_frame); + nonlinear_prim.l = RagrangeInt_Color(colreg->key_count, colreg_float.keyframe_stack, colreg_float.prim_l_stack, now_frame); + + nonlinear_env.r = RagrangeInt_Color(colreg->key_count, colreg_float.keyframe_stack, colreg_float.env_r_stack, now_frame); + nonlinear_env.g = RagrangeInt_Color(colreg->key_count, colreg_float.keyframe_stack, colreg_float.env_g_stack, now_frame); + nonlinear_env.b = RagrangeInt_Color(colreg->key_count, colreg_float.keyframe_stack, colreg_float.env_b_stack, now_frame); + nonlinear_env.a = RagrangeInt_Color(colreg->key_count, colreg_float.keyframe_stack, colreg_float.env_a_stack, now_frame); + + evw_color_set(play, segment, &nonlinear_prim, &nonlinear_env); + } +} + +static void evw_anime_texanime(GAME_PLAY* play, int segment, void* evw_data) { + EVW_ANIME_TEXANIME* texanime = (EVW_ANIME_TEXANIME*)evw_data; + GRAPH* g = play->game.graph; + int frame = play->game_frame % (texanime->frame_count * 2); // 30fps pattern -> 60fps pattern + void* tex_p = texanime->texture_tbl[texanime->animation_pattern[frame / 2]]; // 60fps pattern back to 30fps + + OPEN_DISP(g); + + gSPSegment(NOW_BG_OPA_DISP++, segment, tex_p); + gSPSegment(NOW_POLY_OPA_DISP++, segment, tex_p); + gSPSegment(NOW_POLY_XLU_DISP++, segment, tex_p); + + CLOSE_DISP(g); +} + +typedef void (*EVW_ANIME_PROC)(GAME_PLAY*, int, void*); + +extern void Evw_Anime_Set(GAME_PLAY* play, EVW_ANIME_DATA* evw_anime_data) { + static EVW_ANIME_PROC evw_anime_proc[EVW_ANIME_TYPE_NUM] = { + &evw_anime_scroll1, + &evw_anime_scroll2, + &evw_anime_colreg_manual, + &evw_anime_colreg_linear, + &evw_anime_colreg_nonlinear, + &evw_anime_texanime + }; + + if (evw_anime_data != NULL) { + int segment = evw_anime_data->segment; + + if (segment != 0) { + do { + segment = evw_anime_data->segment; + (*evw_anime_proc[evw_anime_data->type])(play, G_MWO_SEGMENT_7 + ABS(segment), evw_anime_data->data_p); + evw_anime_data++; + } while (segment >= 0); + } + } +}