diff --git a/autogen/convert_constants.py b/autogen/convert_constants.py index fd214b72a..9dcbf602e 100644 --- a/autogen/convert_constants.py +++ b/autogen/convert_constants.py @@ -118,7 +118,8 @@ include_constants = { "include/PR/gbi_extension.h": [ "^G_VTX_EXT$", "^G_PPARTTOCOLOR$", - "^G_SETENVRGB$" + "^G_SETENVRGB$", + "^G_STATE_EXT$", ], } diff --git a/autogen/lua_definitions/constants.lua b/autogen/lua_definitions/constants.lua index 41e73943c..885b5a19d 100644 --- a/autogen/lua_definitions/constants.lua +++ b/autogen/lua_definitions/constants.lua @@ -3031,6 +3031,9 @@ G_SETENVRGB = 0xd1 --- @type integer G_PPARTTOCOLOR = 0xd3 +--- @type integer +G_STATE_EXT = 0x10 + BACKGROUND_OCEAN_SKY = 0 --- @type SkyBackgroundParams BACKGROUND_FLAMING_SKY = 1 --- @type SkyBackgroundParams BACKGROUND_UNDERWATER_CITY = 2 --- @type SkyBackgroundParams diff --git a/data/dynos_bin_gfx.cpp b/data/dynos_bin_gfx.cpp index 506a51c35..35417c980 100644 --- a/data/dynos_bin_gfx.cpp +++ b/data/dynos_bin_gfx.cpp @@ -401,7 +401,7 @@ s64 DynOS_Gfx_ParseGfxConstants(const String& _Arg, bool* found) { gfx_constant(EMBLEM); gfx_constant(METAL); - // Extended + // Extended geometry modes gfx_constant(G_LIGHT_MAP_EXT); gfx_constant(G_LIGHTING_ENGINE_EXT); gfx_constant(G_PACKED_NORMALS_EXT); @@ -409,12 +409,30 @@ s64 DynOS_Gfx_ParseGfxConstants(const String& _Arg, bool* found) { gfx_constant(G_FRESNEL_COLOR_EXT); gfx_constant(G_FRESNEL_ALPHA_EXT); + // Player part to color gfx_constant(G_COL_PRIM); gfx_constant(G_COL_ENV); - gfx_constant(G_CP_LIGHT); gfx_constant(G_CP_AMBIENT); + // Gfx state + gfx_constant(G_STATE_GEOMETRY_MODE); + gfx_constant(G_STATE_COMBINE_MODE); + gfx_constant(G_STATE_OTHER_MODE_L); + gfx_constant(G_STATE_OTHER_MODE_H); + gfx_constant(G_STATE_OTHER_MODE); + gfx_constant(G_STATE_ENV_COLOR); + gfx_constant(G_STATE_PRIM_COLOR); + gfx_constant(G_STATE_FOG_COLOR); + gfx_constant(G_STATE_FILL_COLOR); + gfx_constant(G_STATE_FRESNEL); + gfx_constant(G_STATE_TEXTURES); + gfx_constant(G_STATE_LIGHTS); + gfx_constant(G_STATE_VIEWPORT); + gfx_constant(G_STATE_SCISSOR); + gfx_constant(G_STATE_Z_BUFFER); + gfx_constant(G_STATE_COLOR_IMAGE); + // Common values gfx_constant(CALC_DXT(4,G_IM_SIZ_4b_BYTES)); gfx_constant(CALC_DXT(8,G_IM_SIZ_4b_BYTES)); diff --git a/docs/lua/constants.md b/docs/lua/constants.md index 06d34d510..4456ef42d 100644 --- a/docs/lua/constants.md +++ b/docs/lua/constants.md @@ -1316,6 +1316,7 @@ - G_VTX_EXT - G_SETENVRGB - G_PPARTTOCOLOR +- G_STATE_EXT [:arrow_up_small:](#) diff --git a/include/PR/gbi_extension.h b/include/PR/gbi_extension.h index bba474dd5..9b2ecd042 100644 --- a/include/PR/gbi_extension.h +++ b/include/PR/gbi_extension.h @@ -9,7 +9,7 @@ // Please update the following table when implementing a new command. // // RSP -> 09 0a 0b 0c 0d 0e 0f -// 10 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f +// 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f // 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f // 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f // 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f @@ -222,3 +222,55 @@ #define gsSPFresnel(scale, offset) \ gsMoveWd(G_MW_FX, G_MWO_FRESNEL, \ (_SHIFTL((scale), 16, 16) | _SHIFTL((offset), 0, 16))) + +///////////////// +// G_STATE_EXT // +///////////////// + +#define G_STATE_EXT 0x10 + +#define G_STATE_LOAD 0 +#define G_STATE_SAVE 1 + +#define G_STATE_GEOMETRY_MODE (1 << 0) +#define G_STATE_COMBINE_MODE (1 << 1) +#define G_STATE_OTHER_MODE_L (1 << 2) +#define G_STATE_OTHER_MODE_H (1 << 3) +#define G_STATE_OTHER_MODE (G_STATE_OTHER_MODE_L | G_STATE_OTHER_MODE_H) +#define G_STATE_ENV_COLOR (1 << 4) +#define G_STATE_PRIM_COLOR (1 << 5) +#define G_STATE_FOG_COLOR (1 << 6) +#define G_STATE_FILL_COLOR (1 << 7) +#define G_STATE_FRESNEL (1 << 8) +#define G_STATE_TEXTURES (1 << 9) +#define G_STATE_LIGHTS (1 << 10) +#define G_STATE_VIEWPORT (1 << 11) +#define G_STATE_SCISSOR (1 << 12) +#define G_STATE_Z_BUFFER (1 << 13) +#define G_STATE_COLOR_IMAGE (1 << 14) + +#define gSPLoadState(pkt, state) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = _SHIFTL(G_STATE_EXT,24,8)|_SHIFTL(G_STATE_LOAD,16,8); \ + _g->words.w1 = (u32)(state); \ +} + +#define gsSPLoadState(state) \ +{{ \ + (_SHIFTL(G_STATE_EXT,24,8)|_SHIFTL(G_STATE_LOAD,16,8)), \ + (u32)(state) \ +}} + +#define gSPSaveState(pkt, state) \ +{ \ + Gfx *_g = (Gfx *)(pkt); \ + _g->words.w0 = _SHIFTL(G_STATE_EXT,24,8)|_SHIFTL(G_STATE_SAVE,16,8); \ + _g->words.w1 = (u32)(state); \ +} + +#define gsSPSaveState(state) \ +{{ \ + (_SHIFTL(G_STATE_EXT,24,8)|_SHIFTL(G_STATE_SAVE,16,8)), \ + (u32)(state) \ +}} diff --git a/include/gfx_symbols.h b/include/gfx_symbols.h index 3e1492bdc..4c6578ab9 100644 --- a/include/gfx_symbols.h +++ b/include/gfx_symbols.h @@ -61,6 +61,8 @@ define_gfx_symbol(gsSPFresnel, 2, false, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT) define_gfx_symbol(gsDPSetColorImage, 4, false, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT); define_gfx_symbol(gsSPNoOp, 0, false); define_gfx_symbol(gsSPMatrix, 2, false, GFX_PARAM_TYPE_PTR, GFX_PARAM_TYPE_INT); +define_gfx_symbol(gsSPLoadState, 1, false, GFX_PARAM_TYPE_INT); +define_gfx_symbol(gsSPSaveState, 1, false, GFX_PARAM_TYPE_INT); define_gfx_symbol_manual(gsSPTexture, 5, false, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT, GFX_PARAM_TYPE_INT); define_gfx_symbol_manual(gsSPSetGeometryMode, 1, false, GFX_PARAM_TYPE_INT); diff --git a/src/game/rendering_graph_node.c b/src/game/rendering_graph_node.c index f1042a21c..07089fea5 100644 --- a/src/game/rendering_graph_node.c +++ b/src/game/rendering_graph_node.c @@ -90,6 +90,34 @@ static Gfx obj_sanitize_gfx[] = { gsSPEndDisplayList(), }; +static Gfx obj_load_gfx_state[] = { + gsSPLoadState(G_STATE_GEOMETRY_MODE + | G_STATE_COMBINE_MODE + | G_STATE_OTHER_MODE + | G_STATE_ENV_COLOR + | G_STATE_PRIM_COLOR + | G_STATE_FOG_COLOR + | G_STATE_FILL_COLOR + | G_STATE_FRESNEL + | G_STATE_TEXTURES + | G_STATE_LIGHTS), + gsSPEndDisplayList(), +}; + +static Gfx obj_save_gfx_state[] = { + gsSPSaveState(G_STATE_GEOMETRY_MODE + | G_STATE_COMBINE_MODE + | G_STATE_OTHER_MODE + | G_STATE_ENV_COLOR + | G_STATE_PRIM_COLOR + | G_STATE_FOG_COLOR + | G_STATE_FILL_COLOR + | G_STATE_FRESNEL + | G_STATE_TEXTURES + | G_STATE_LIGHTS), + gsSPEndDisplayList(), +}; + /** * Animation nodes have state in global variables, so this struct captures * the animation state so a 'context switch' can be made when rendering the @@ -552,6 +580,16 @@ static void geo_append_display_list(void *displayList, s16 layer) { } } +static void geo_append_display_list_to_all_layers(void *displayList) { + geo_append_display_list(displayList, LAYER_OPAQUE); + geo_append_display_list(displayList, LAYER_OPAQUE_DECAL); + geo_append_display_list(displayList, LAYER_OPAQUE_INTER); + geo_append_display_list(displayList, LAYER_ALPHA); + geo_append_display_list(displayList, LAYER_TRANSPARENT); + geo_append_display_list(displayList, LAYER_TRANSPARENT_DECAL); + geo_append_display_list(displayList, LAYER_TRANSPARENT_INTER); +} + /** * Process the master list node. */ @@ -1436,13 +1474,15 @@ static s32 obj_is_in_view(struct GraphNodeObject *node, Mat4 matrix) { } static void geo_sanitize_object_gfx(void) { - geo_append_display_list(obj_sanitize_gfx, LAYER_OPAQUE); - geo_append_display_list(obj_sanitize_gfx, LAYER_OPAQUE_DECAL); - geo_append_display_list(obj_sanitize_gfx, LAYER_OPAQUE_INTER); - geo_append_display_list(obj_sanitize_gfx, LAYER_ALPHA); - geo_append_display_list(obj_sanitize_gfx, LAYER_TRANSPARENT); - geo_append_display_list(obj_sanitize_gfx, LAYER_TRANSPARENT_DECAL); - geo_append_display_list(obj_sanitize_gfx, LAYER_TRANSPARENT_INTER); + geo_append_display_list_to_all_layers(obj_sanitize_gfx); +} + +static void geo_load_object_gfx_state(void) { + geo_append_display_list_to_all_layers(obj_load_gfx_state); +} + +static void geo_save_object_gfx_state(void) { + geo_append_display_list_to_all_layers(obj_save_gfx_state); } static struct MarioBodyState *get_mario_body_state_from_mario_object(struct Object *marioObj) { @@ -1729,6 +1769,10 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { dynos_gfx_swap_animations(node->objNode); } + // The held object is going to change the gfx state before + // the holder finishes rendering, so let's save the state now + geo_save_object_gfx_state(); + geo_sanitize_object_gfx(); // While rendering the held object's geo tree, ensure "current object" globals // refer to the held object, otherwise Lua geo callbacks can accidentally @@ -1745,13 +1789,13 @@ void geo_process_held_object(struct GraphNodeHeldObject *node) { gCurrAnimAttribute = gGeoTempState.attribute; gCurAnim = gGeoTempState.anim; gPrevAnimFrame = gGeoTempState.prevFrame; + // Force-restore matrix stack index to avoid any imbalance caused by // held object geo trees (including Lua geo callbacks). gMatStackIndex = savedMatStackIndex; - // Reset any render-state changes performed by the held object's geo tree - // so the holder's remaining body parts render correctly. - geo_sanitize_object_gfx(); + // Restore the previously saved state before continuing + geo_load_object_gfx_state(); } if (node->fnNode.node.children != NULL) { diff --git a/src/pc/gfx/gfx.h b/src/pc/gfx/gfx.h index 75bf8bc88..34e735b01 100644 --- a/src/pc/gfx/gfx.h +++ b/src/pc/gfx/gfx.h @@ -26,6 +26,7 @@ #define MAX_TILES 8 #define MAX_TEXTURES 2 #define MAX_CACHED_TEXTURES 4096 // for preloading purposes +#define MAX_GFX_STATES 8 #define HASH_SHIFT 0 #define HASHMAP_LEN (MAX_CACHED_TEXTURES * 2) diff --git a/src/pc/gfx/gfx_pc.c b/src/pc/gfx/gfx_pc.c index 7135c7282..fd8045361 100644 --- a/src/pc/gfx/gfx_pc.c +++ b/src/pc/gfx/gfx_pc.c @@ -48,7 +48,7 @@ static struct ColorCombiner color_combiner_pool[CC_MAX_SHADERS] = { 0 }; static uint8_t color_combiner_pool_size = 0; static uint8_t color_combiner_pool_index = 0; -static struct RSP { +struct RSP { ALIGNED16 Mat4 MP_matrix; ALIGNED16 Mat4 P_matrix; ALIGNED16 Mat4 modelview_matrix_stack[MAX_MATRIX_STACK_SIZE]; @@ -70,9 +70,10 @@ static struct RSP { Light_t current_lights[MAX_LIGHTS + 1]; struct GfxVertex loaded_vertices[MAX_VERTICES + 4]; -} rsp; +}; +static struct RSP rsp; -static struct RDP { +struct RDP { const uint8_t *palette[2]; struct UnloadedTex texture_to_load; struct TextureTile texture_tile[MAX_TILES]; @@ -87,7 +88,15 @@ static struct RDP { bool viewport_or_scissor_changed; void *z_buf_address; void *color_image_address; -} rdp; +}; +static struct RDP rdp; + +struct GfxState { + struct RSP rsp; + struct RDP rdp; +}; +static struct GfxState gfx_states[MAX_GFX_STATES]; +static size_t num_gfx_states; static struct RenderingState { bool depth_test; @@ -1997,6 +2006,7 @@ static void gfx_sp_reset(void) { rsp.modelview_matrix_stack_size = 1; rsp.current_num_lights = 2; rsp.lights_changed = true; + num_gfx_states = 0; } void gfx_get_dimensions(uint32_t *width, uint32_t *height) { @@ -2245,6 +2255,150 @@ static void OPTIMIZE_O3 djui_gfx_sp_simple_tri1(uint8_t vtx1_idx, uint8_t vtx2_i return; } +static void gfx_sp_load_or_save_state(uint8_t cmd, uint32_t state) { + + // Load state + if (cmd == G_STATE_LOAD) { + if (num_gfx_states == 0) { + return; + } + + num_gfx_states--; + struct GfxState *gfx_state = &gfx_states[num_gfx_states]; + + if (state & G_STATE_GEOMETRY_MODE) { + rsp.geometry_mode = gfx_state->rsp.geometry_mode; + } + if (state & G_STATE_COMBINE_MODE) { + gfx_dp_set_combine_mode( + gfx_state->rdp.combine_mode.rgb1, + gfx_state->rdp.combine_mode.alpha1, + gfx_state->rdp.combine_mode.rgb2, + gfx_state->rdp.combine_mode.alpha2 + ); + } + if (state & G_STATE_OTHER_MODE_L) { + rdp.other_mode_l = gfx_state->rdp.other_mode_l; + } + if (state & G_STATE_OTHER_MODE_H) { + rdp.other_mode_h = gfx_state->rdp.other_mode_h; + } + if (state & G_STATE_ENV_COLOR) { + rdp.env_color = gfx_state->rdp.env_color; + } + if (state & G_STATE_PRIM_COLOR) { + rdp.prim_color = gfx_state->rdp.prim_color; + } + if (state & G_STATE_FOG_COLOR) { + rdp.fog_color = gfx_state->rdp.fog_color; + rsp.fog_mul = gfx_state->rsp.fog_mul; + rsp.fog_offset = gfx_state->rsp.fog_offset; + } + if (state & G_STATE_FILL_COLOR) { + rdp.fill_color = gfx_state->rdp.fill_color; + } + if (state & G_STATE_FRESNEL) { + rsp.fresnel_scale = gfx_state->rsp.fresnel_scale; + rsp.fresnel_offset = gfx_state->rsp.fresnel_offset; + } + if (state & G_STATE_TEXTURES) { + rsp.texture_scaling_factor = gfx_state->rsp.texture_scaling_factor; + rdp.texture_to_load = gfx_state->rdp.texture_to_load; + memcpy(rdp.palette, gfx_state->rdp.palette, sizeof(rdp.palette)); + memcpy(rdp.texture_tile, gfx_state->rdp.texture_tile, sizeof(rdp.texture_tile)); + memcpy(rdp.loaded_texture, gfx_state->rdp.loaded_texture, sizeof(rdp.loaded_texture)); + for (s32 i = 0; i != ARRAY_COUNT(rdp.textures_changed); ++i) { + rdp.textures_changed[i] = true; + } + } + if (state & G_STATE_LIGHTS) { + rsp.current_num_lights = gfx_state->rsp.current_num_lights; + memcpy(rsp.current_lights, gfx_state->rsp.current_lights, sizeof(rsp.current_lights)); + rsp.lights_changed = true; + } + if (state & G_STATE_VIEWPORT) { + rdp.viewport = gfx_state->rdp.viewport; + rdp.viewport_or_scissor_changed = true; + } + if (state & G_STATE_SCISSOR) { + rdp.scissor = gfx_state->rdp.scissor; + rdp.viewport_or_scissor_changed = true; + } + if (state & G_STATE_Z_BUFFER) { + rdp.z_buf_address = gfx_state->rdp.z_buf_address; + } + if (state & G_STATE_COLOR_IMAGE) { + rdp.color_image_address = gfx_state->rdp.color_image_address; + } + return; + } + + // Save state + if (cmd == G_STATE_SAVE) { + if (num_gfx_states == MAX_GFX_STATES) { + return; + } + + struct GfxState *gfx_state = &gfx_states[num_gfx_states]; + num_gfx_states++; + + if (state & G_STATE_GEOMETRY_MODE) { + gfx_state->rsp.geometry_mode = rsp.geometry_mode; + } + if (state & G_STATE_COMBINE_MODE) { + gfx_state->rdp.combine_mode = rdp.combine_mode; + } + if (state & G_STATE_OTHER_MODE_L) { + gfx_state->rdp.other_mode_l = rdp.other_mode_l; + } + if (state & G_STATE_OTHER_MODE_H) { + gfx_state->rdp.other_mode_h = rdp.other_mode_h; + } + if (state & G_STATE_ENV_COLOR) { + gfx_state->rdp.env_color = rdp.env_color; + } + if (state & G_STATE_PRIM_COLOR) { + gfx_state->rdp.prim_color = rdp.prim_color; + } + if (state & G_STATE_FOG_COLOR) { + gfx_state->rdp.fog_color = rdp.fog_color; + gfx_state->rsp.fog_mul = rsp.fog_mul; + gfx_state->rsp.fog_offset = rsp.fog_offset; + } + if (state & G_STATE_FILL_COLOR) { + gfx_state->rdp.fill_color = rdp.fill_color; + } + if (state & G_STATE_FRESNEL) { + gfx_state->rsp.fresnel_scale = rsp.fresnel_scale; + gfx_state->rsp.fresnel_offset = rsp.fresnel_offset; + } + if (state & G_STATE_TEXTURES) { + gfx_state->rsp.texture_scaling_factor = rsp.texture_scaling_factor; + gfx_state->rdp.texture_to_load = rdp.texture_to_load; + memcpy(gfx_state->rdp.palette, rdp.palette, sizeof(rdp.palette)); + memcpy(gfx_state->rdp.texture_tile, rdp.texture_tile, sizeof(rdp.texture_tile)); + memcpy(gfx_state->rdp.loaded_texture, rdp.loaded_texture, sizeof(rdp.loaded_texture)); + } + if (state & G_STATE_LIGHTS) { + gfx_state->rsp.current_num_lights = rsp.current_num_lights; + memcpy(gfx_state->rsp.current_lights, rsp.current_lights, sizeof(rsp.current_lights)); + } + if (state & G_STATE_VIEWPORT) { + gfx_state->rdp.viewport = rdp.viewport; + } + if (state & G_STATE_SCISSOR) { + gfx_state->rdp.scissor = rdp.scissor; + } + if (state & G_STATE_Z_BUFFER) { + gfx_state->rdp.z_buf_address = rdp.z_buf_address; + } + if (state & G_STATE_COLOR_IMAGE) { + gfx_state->rdp.color_image_address = rdp.color_image_address; + } + return; + } +} + void gfx_pc_precomp_shader(uint32_t rgb1, uint32_t alpha1, uint32_t rgb2, uint32_t alpha2, uint32_t flags) { gfx_dp_set_combine_mode(rgb1, alpha1, rgb2, alpha2); @@ -2285,5 +2439,8 @@ void OPTIMIZE_O3 ext_gfx_run_dl(Gfx* cmd) { case G_PPARTTOCOLOR: gfx_sp_copy_playerpart_to_color(C0(16, 8), cmd->words.w1); break; + case G_STATE_EXT: + gfx_sp_load_or_save_state(C0(16, 8), cmd->words.w1); + break; } } diff --git a/src/pc/lua/smlua_constants_autogen.c b/src/pc/lua/smlua_constants_autogen.c index 82ed10603..88096aebd 100644 --- a/src/pc/lua/smlua_constants_autogen.c +++ b/src/pc/lua/smlua_constants_autogen.c @@ -1584,6 +1584,7 @@ char gSmluaConstants[] = "" "G_VTX_EXT=0x11\n" "G_SETENVRGB=0xd1\n" "G_PPARTTOCOLOR=0xd3\n" +"G_STATE_EXT=0x10\n" "BACKGROUND_OCEAN_SKY=0\n" "BACKGROUND_FLAMING_SKY=1\n" "BACKGROUND_UNDERWATER_CITY=2\n"