diff --git a/Changelog.md b/Changelog.md index 647bbc9d..b4374d38 100644 --- a/Changelog.md +++ b/Changelog.md @@ -6,6 +6,10 @@ - Music: Add the ability to play a custom `main_menu` music file on the new game screen. +## FF7 + +- Rendering: Add uncrop option to remove black bars from top/bottom of the screen ( https://github.com/julianxhokaxhiu/FFNx/pull/767 ) + ## FF8 - SFX: Fix incorrect volume assignment when playing sfx effects using the external layer diff --git a/misc/FFNx.toml b/misc/FFNx.toml index 90c4ed18..4746cfc1 100644 --- a/misc/FFNx.toml +++ b/misc/FFNx.toml @@ -58,6 +58,11 @@ internal_resolution_scale = 0 #~~~~~~~~~~~~~~~~~~~~~~~~~~~ aspect_ratio = 0 +#[UNCROP] +# Uncrops the image to remove the black bars on the top and bottom of the screen. +#~~~~~~~~~~~~~~~~~~~~~~~~~~~ +enable_uncrop = false + #[ENABLE VSYNC] #~~~~~~~~~~~~~~~~~~~~~~~~~~~ enable_vsync = true diff --git a/src/cfg.cpp b/src/cfg.cpp index c16f1604..8ab02d9d 100644 --- a/src/cfg.cpp +++ b/src/cfg.cpp @@ -88,6 +88,7 @@ long window_size_x; long window_size_y; long internal_resolution_scale; long aspect_ratio; +bool enable_uncrop; bool fullscreen; bool borderless; long refresh_rate; @@ -249,6 +250,7 @@ void read_cfg() window_size_y = config["window_size_y"].value_or(0); internal_resolution_scale = config["internal_resolution_scale"].value_or(0); aspect_ratio = config["aspect_ratio"].value_or(0); + enable_uncrop = config["enable_uncrop"].value_or(false); fullscreen = config["fullscreen"].value_or(false); borderless = config["borderless"].value_or(false); refresh_rate = config["refresh_rate"].value_or(0); diff --git a/src/cfg.h b/src/cfg.h index 20c20a31..0410ad86 100644 --- a/src/cfg.h +++ b/src/cfg.h @@ -99,6 +99,7 @@ extern long window_size_x; extern long window_size_y; extern long internal_resolution_scale; extern long aspect_ratio; +extern bool enable_uncrop; extern bool fullscreen; extern bool borderless; extern long refresh_rate; diff --git a/src/ff7/field/background.cpp b/src/ff7/field/background.cpp index 1da6c3f2..ca366c0e 100644 --- a/src/ff7/field/background.cpp +++ b/src/ff7/field/background.cpp @@ -127,10 +127,10 @@ namespace ff7::field { const int left_offset = 352 + (is_fieldmap_wide() ? abs(wide_viewport_x) : 0); const int right_offset = is_fieldmap_wide() ? abs(wide_viewport_x) : 0; - const int top_offset = 256 + (widescreen_enabled ? 8 : 0); - const int bottom_offset = widescreen_enabled ? 8 : 0; + const int top_offset = 256 + (enable_uncrop ? 8 : 0); + const int bottom_offset = enable_uncrop ? 8 : 0; const int half_width = is_fieldmap_wide() ? ceil(wide_viewport_width / 4) : 160; - const int half_height = widescreen_enabled ? 120 : 112; + const int half_height = enable_uncrop ? 120 : 112; if(tile_position->x <= bg_position->x - left_offset || tile_position->x >= bg_position->x + right_offset) tile_position->x += (tile_position->x >= bg_position->x - half_width) ? -layer3_width : layer3_width; @@ -167,14 +167,14 @@ namespace ff7::field else z_value = 0.9998; - const bool do_increase_height = widescreen_enabled; + const bool do_increase_height = enable_uncrop; const bool do_increase_width = is_fieldmap_wide() && (*ff7_externals.field_triggers_header)->bg3_width < ceil(wide_viewport_width / 2); const int layer3_width = (*ff7_externals.field_triggers_header)->bg3_width * (do_increase_width ? 2 : 1); const int layer3_height = (*ff7_externals.field_triggers_header)->bg3_height * (do_increase_height ? 2 : 1); const int left_offset = 352 + (is_fieldmap_wide() ? abs(wide_viewport_x) : 0); const int right_offset = is_fieldmap_wide() ? abs(wide_viewport_x) : 0; - const int top_offset = 256 + (widescreen_enabled ? 8 : 0); - const int bottom_offset = widescreen_enabled ? 8 : 0; + const int top_offset = 256 + (enable_uncrop ? 8 : 0); + const int bottom_offset = enable_uncrop ? 8 : 0; for(int i = 0; i < *ff7_externals.field_layer3_tiles_num; i++) { @@ -202,7 +202,7 @@ namespace ff7::field layer3_tiles[tile_index].v, layer3_tiles[tile_index].palette_index, page); } - if(widescreen_enabled) + if(widescreen_enabled || enable_uncrop) { // Apply repeat x-y for background layer 4 tiles std::vector> tile_offsets; @@ -250,8 +250,8 @@ namespace ff7::field { const int left_offset = 352 + (is_fieldmap_wide() ? abs(wide_viewport_x) : 0); const int right_offset = is_fieldmap_wide() ? abs(wide_viewport_x) : 0; - const int top_offset = 256 + (widescreen_enabled ? 8 : 0); - const int bottom_offset = widescreen_enabled ? 8 : 0; + const int top_offset = 256 + (enable_uncrop ? 8 : 0); + const int bottom_offset = enable_uncrop ? 8 : 0; const int half_width = is_fieldmap_wide() ? ceil(wide_viewport_width / 4) : 160; if(tile_position->x <= bg_position->x - left_offset || tile_position->x >= bg_position->x + right_offset) @@ -289,14 +289,14 @@ namespace ff7::field initial_pos.y = ((ff7_field_center ? 232 : 224) - bg_position.y) * field_bg_multiplier; float z_value = ff7_externals.field_layer_sub_623C0F(ff7_externals.field_camera_rotation_matrix_CFF3D8, ff7_externals.modules_global_object->field_AE, 0, 0); - const bool do_increase_height = widescreen_enabled; + const bool do_increase_height = enable_uncrop; const bool do_increase_width = is_fieldmap_wide() && (*ff7_externals.field_triggers_header)->bg4_width < ceil(wide_viewport_width / 2); const int layer4_width = (*ff7_externals.field_triggers_header)->bg4_width * (do_increase_width ? 2 : 1); const int layer4_height = (*ff7_externals.field_triggers_header)->bg4_height * (do_increase_height ? 2 : 1); const int left_offset = 352 + (is_fieldmap_wide() ? abs(wide_viewport_x) : 0); const int right_offset = is_fieldmap_wide() ? abs(wide_viewport_x) : 0; - const int top_offset = 256 + (widescreen_enabled ? 8 : 0); - const int bottom_offset = widescreen_enabled ? 8 : 0; + const int top_offset = 256 + (enable_uncrop ? 8 : 0); + const int bottom_offset = enable_uncrop ? 8 : 0; for(int i = 0; i < *ff7_externals.field_layer4_tiles_num; i++) { @@ -326,7 +326,7 @@ namespace ff7::field } } - if(widescreen_enabled) + if(widescreen_enabled || enable_uncrop) { // Apply repeat x-y for background layer 4 tiles std::vector> tile_offsets; @@ -420,10 +420,13 @@ namespace ff7::field float half_width = 160; auto camera_range = field_triggers_header_ptr->camera_range; - if(widescreen_enabled && is_fieldmap_wide()) + if(widescreen_enabled || enable_uncrop) { camera_range = widescreen.getCameraRange(); + } + if(is_fieldmap_wide()) + { // Adjustment to prevent scrolling stopping one pixel too early camera_range.left += 1; camera_range.right -= 1; @@ -480,10 +483,13 @@ namespace ff7::field float half_width = 160; auto camera_range = trigger_header->camera_range; - if(widescreen_enabled && is_fieldmap_wide()) + if (enable_uncrop || widescreen_enabled) { camera_range = widescreen.getCameraRange(); + } + if(is_fieldmap_wide()) + { // This centers the background if necessary int cameraRangeSize = camera_range.right - camera_range.left; half_width = 160 + std::min(53, cameraRangeSize / 2 - 160); @@ -543,12 +549,23 @@ namespace ff7::field float half_width = 160 + std::min(53, cameraRangeSize / 2 - 160); point->x += widescreen.getHorizontalOffset(); - point->y += widescreen.getVerticalOffset(); if (point->x > camera_range.right - half_width) point->x = camera_range.right - half_width; if (point->x < camera_range.left + half_width) point->x = camera_range.left + half_width; + } + + void field_uncropped_height_clip_with_camera_range(vector2* point) + { + if(!widescreen.isScriptedClipEnabled()) + { + return; + } + + auto camera_range = widescreen.getCameraRange(); + + point->y += widescreen.getVerticalOffset(); if(widescreen.isScriptedVerticalClipEnabled()) { @@ -642,6 +659,9 @@ namespace ff7::field if(is_fieldmap_wide()) field_widescreen_width_clip_with_camera_range(&world_pos); + if(is_fieldmap_uncropped()) + field_uncropped_height_clip_with_camera_range(&world_pos); + *ff7_externals.scripted_world_initial_pos_x = -world_pos.x; *ff7_externals.scripted_world_initial_pos_y = -world_pos.y; @@ -653,6 +673,8 @@ namespace ff7::field world_pos = {-(ff7_externals.modules_global_object->field_A), -(ff7_externals.modules_global_object->field_C)}; if(is_fieldmap_wide()) field_widescreen_width_clip_with_camera_range(&world_pos); + if(is_fieldmap_uncropped()) + field_uncropped_height_clip_with_camera_range(&world_pos); *ff7_externals.field_curr_delta_world_pos_x = -world_pos.x; *ff7_externals.field_curr_delta_world_pos_y = -world_pos.y; @@ -667,6 +689,8 @@ namespace ff7::field world_pos = {(-*ff7_externals.field_curr_delta_world_pos_x), -(*ff7_externals.field_curr_delta_world_pos_y)}; if(is_fieldmap_wide()) field_widescreen_width_clip_with_camera_range(&world_pos); + if(is_fieldmap_uncropped()) + field_uncropped_height_clip_with_camera_range(&world_pos); *ff7_externals.scripted_world_initial_pos_x = -world_pos.x; *ff7_externals.scripted_world_initial_pos_y = -world_pos.y; @@ -674,6 +698,8 @@ namespace ff7::field world_pos = {-(ff7_externals.modules_global_object->field_A), -(ff7_externals.modules_global_object->field_C)}; if(is_fieldmap_wide()) field_widescreen_width_clip_with_camera_range(&world_pos); + if(is_fieldmap_uncropped()) + field_uncropped_height_clip_with_camera_range(&world_pos); *ff7_externals.scripted_world_final_pos_x = -world_pos.x; *ff7_externals.scripted_world_final_pos_y = -world_pos.y; @@ -776,6 +802,8 @@ namespace ff7::field field_widescreen_width_clip_with_camera_range(&world_pos); *ff7_externals.scripted_world_initial_pos_x = -world_pos.x; } + if(is_fieldmap_uncropped()) + field_uncropped_height_clip_with_camera_range(&world_pos); std::function field_get_interpolated_value = ff7_externals.modules_global_object->world_move_mode == 5 ? ff7_externals.field_get_linear_interpolated_value : ff7_externals.field_get_smooth_interpolated_value; @@ -831,6 +859,8 @@ namespace ff7::field field_widescreen_width_clip_with_camera_range(&world_pos); *ff7_externals.field_curr_delta_world_pos_x = -world_pos.x; } + if(is_fieldmap_uncropped()) + field_uncropped_height_clip_with_camera_range(&world_pos); } if(is_position_valid(field_curr_delta_world_pos)) diff --git a/src/ff7/field/enter.h b/src/ff7/field/enter.h index 5e32a4f8..ccc603bb 100644 --- a/src/ff7/field/enter.h +++ b/src/ff7/field/enter.h @@ -49,6 +49,6 @@ namespace ff7::field external_data.blinkFrameIndex = BLINKING_FRAMES; } - if(widescreen_enabled) widescreen.initParamsFromConfig(); + if(widescreen_enabled || enable_uncrop) widescreen.initParamsFromConfig(); } } diff --git a/src/ff7/field/field.cpp b/src/ff7/field/field.cpp index e644bc92..f0acf317 100644 --- a/src/ff7/field/field.cpp +++ b/src/ff7/field/field.cpp @@ -154,8 +154,11 @@ namespace ff7::field if(widescreen_enabled) { x -= abs(wide_viewport_x); - y -= ff7_field_center ? 16 : 0; width += (wide_viewport_width - game_width); + } + if(enable_uncrop) + { + y -= ff7_field_center ? 16 : 0; height += 32; } ff7_externals.field_sub_63AC3F(x, y, width, height); diff --git a/src/ff7/field/utils.h b/src/ff7/field/utils.h index 3e2f4cd4..2aa913d9 100644 --- a/src/ff7/field/utils.h +++ b/src/ff7/field/utils.h @@ -51,6 +51,11 @@ namespace ff7::field return widescreen_enabled && widescreen.getMode() != WM_DISABLED; } + inline bool is_fieldmap_uncropped() + { + return enable_uncrop && widescreen.getMode() != WM_DISABLED; + } + inline float field_get_linear_interpolated_value_float(float initial_value, float final_value, int n_steps, int step_idx) { return std::lerp(initial_value, final_value, step_idx / (float)n_steps); diff --git a/src/ff7_opengl.cpp b/src/ff7_opengl.cpp index 0b8c6dad..0a91b38d 100644 --- a/src/ff7_opengl.cpp +++ b/src/ff7_opengl.cpp @@ -204,9 +204,9 @@ void ff7_init_hooks(struct game_obj *_game_object) replace_call_function(ff7_externals.battle_set_do_render_menu_call, ff7::battle::battle_menu_enter); // ##################### - // widescreen + // widescreen / uncrop // ##################### - if(widescreen_enabled) + if(widescreen_enabled || enable_uncrop) ff7_widescreen_hook_init(); if (enable_time_cycle) diff --git a/src/renderer.cpp b/src/renderer.cpp index 84149d47..3a84f766 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -1722,6 +1722,22 @@ void Renderer::setScissor(uint16_t x, uint16_t y, uint16_t width, uint16_t heigh scissorWidth = getInternalCoordX(width); scissorHeight = getInternalCoordY(height); + // This removes the black bars on the top and bottom of the screen + if (enable_uncrop) + { + bool is_movie_playing = *ff7_externals.word_CC1638 && !ff7_externals.modules_global_object->BGMOVIE_flag; + if((is_movie_playing && widescreen.getMovieMode() == WM_DISABLED)) + { + return; + } + + if(y == 16 && height == 448) + { + scissorOffsetY = getInternalCoordY(0.0); + scissorHeight = getInternalCoordY(480); + } + } + if(!widescreen_enabled) return; struct game_mode* mode = getmode_cached(); @@ -1743,13 +1759,6 @@ void Renderer::setScissor(uint16_t x, uint16_t y, uint16_t width, uint16_t heigh return; } - // This removes the black bars on the top and bottom of the screen - if(y == 16 && height == 448) - { - scissorOffsetY = getInternalCoordY(0.0); - scissorHeight = getInternalCoordY(480); - } - // This changes the scissor width and makes it bigger to fit widescreen if(x == 0 && width == game_width) scissorWidth = getInternalCoordX(wide_viewport_width); @@ -1759,13 +1768,6 @@ void Renderer::setScissor(uint16_t x, uint16_t y, uint16_t width, uint16_t heigh break; case MODE_SWIRL: { - // This removes the black bars on the top and bottom of the screen - if(y == 16 && height == 448) - { - scissorOffsetY = getInternalCoordY(0.0); - scissorHeight = getInternalCoordY(480); - } - // This changes the scissor width and makes it bigger to fit widescreen if(x == 0 && width == game_width) scissorWidth = getInternalCoordX(wide_viewport_width);