diff --git a/port/assets/menus/ce/main_menu.settings_select.player_setup.player_profile_edit.video_settings.fov_viewmodels.xml b/port/assets/menus/ce/main_menu.settings_select.player_setup.player_profile_edit.video_settings.fov_viewmodels.xml index 1cea9b97..faf580b8 100644 --- a/port/assets/menus/ce/main_menu.settings_select.player_setup.player_profile_edit.video_settings.fov_viewmodels.xml +++ b/port/assets/menus/ce/main_menu.settings_select.player_setup.player_profile_edit.video_settings.fov_viewmodels.xml @@ -67,8 +67,8 @@ - - + + diff --git a/port/assets/menus/ce/main_menu.settings_select.player_setup.player_profile_edit.video_settings.xml b/port/assets/menus/ce/main_menu.settings_select.player_setup.player_profile_edit.video_settings.xml index 7d4d8d36..0173ec76 100644 --- a/port/assets/menus/ce/main_menu.settings_select.player_setup.player_profile_edit.video_settings.xml +++ b/port/assets/menus/ce/main_menu.settings_select.player_setup.player_profile_edit.video_settings.xml @@ -155,7 +155,6 @@ - + - diff --git a/port/linux/README.md b/port/linux/README.md index 0c621fde..bbfcd2aa 100644 --- a/port/linux/README.md +++ b/port/linux/README.md @@ -316,21 +316,6 @@ from the next main menu. Each setting has an environment variable. The environment variable changes the setting for one start of the game. It has priority over the file. -Video Setup's **FOV AND VIEWMODELS** category offers optional world and -weapon field of view settings and first person visibility. **DEFAULT** keeps -the authored world projection. **DEFAULT** for the weapon keeps its original -view, and viewmodels start **ON**. A chosen weapon angle is that FOV. OK -saves and applies changes. Cancel discards them. The menu offers 80 to 150 -degrees in steps of five. The config file accepts 20 to 150 degrees, -including fractional values, or zero for Default. -Angles are horizontal at 16:9; other aspect ratios retain the same vertical -view. World FOV applies to on-foot first-person views, preserving authored -vehicle, death and cinematic cameras. Completed scopes keep their native -view with wider FOV; a narrower FOV also narrows the scope. Aiming reticles -scale with the world projection, while scope artwork keeps its layout. -Viewmodel FOV also applies to attached visuals. Hiding viewmodels changes -only their display; firing, animation, sound and world lights continue. - | Setting | Default | Environment variable | Function | | --- | --- | --- | --- | | `display.mode` | `""` | `HALO_DISPLAY_MODE` | `"fullscreen"`: the display, taken at the mode of `display.resolution` (the nearest the display has), or at its desktop mode. `"borderless"`: a window over the whole desktop, whose mode does not change. `"windowed"`: a window of `display.window_size`. Empty: `display.fullscreen` decides (`true`: borderless). F11 changes between the window and the fullscreen mode. Video Setup sets it. | @@ -345,9 +330,9 @@ only their display; firing, animation, sound and world lights continue. | `debug.gpu_flush_draws` | `-1` | `HALO_GPU_FLUSH_DRAWS` | Flush the GPU's pipeline every this many draws. `-1`: every 3 on Intel graphics with Mesa's driver, which can otherwise hang in the game's long runs of small draws and reset the desktop's graphics too. `0`: never. | | `display.interpolation` | `true` | `HALO_INTERPOLATION` | `true`: one frame for each refresh of the display. `false`: 30 frames each second, as on the Xbox. Refer to "Frame rate". | | `display.direct_camera` | `true` | `HALO_DIRECT_CAMERA` | `true`: in first person, on foot, the view points where the player aims in each frame, not where the last tick left it. Refer to "Frame rate". | -| `display.fov` | `0.0` | `HALO_FOV` | On-foot first-person horizontal FOV at 16:9, from 20 to 150 degrees. `0` keeps the authored view. Video Setup's FOV and Viewmodels screen sets it. | -| `display.viewmodel_fov` | `0.0` | `HALO_VIEWMODEL_FOV` | Weapon and hands horizontal FOV at 16:9, 20 to 150 degrees. `0` is Default and keeps the original weapon view. A chosen angle is that FOV. | -| `display.viewmodel_visible` | `true` | `HALO_VIEWMODEL_VIS` | Show first-person weapons, hands and attached visuals. This does not change gameplay or other players' models. | +| `display.fov` | `0.0` | `HALO_FOV` | The first-person view's field of view on foot, in degrees across at 16:9, from 20 to 150. `0`: the stock view. Refer to "Field of view". | +| `display.viewmodel_fov` | `0.0` | `HALO_VIEWMODEL_FOV` | The first-person weapon's and hands' field of view, in degrees across at 16:9, from 20 to 150. `0`: the weapon's stock view, also when `display.fov` widens the world. Refer to "Field of view". | +| `display.viewmodel_visible` | `true` | `HALO_VIEWMODEL_VISIBLE` | `true`: the first-person weapon, hands and what is attached to them are drawn. `false`: they are not; firing, animation, sound and lights go on, and other players' models are drawn. | | `display.high_res_hud` | `true` | `HALO_HIGH_RES_HUD` | `true`: the HUD (meters, counters, panels and their outlines, the motion sensor, reticles, waypoints, scopes) is drawn from the high-res assets in `port/assets/hud`, 8x the size of the maps' bitmaps. The bitmaps with English text keep the maps' own. `false`: the maps' own bitmaps. | | `display.high_res_text` | `true` | `HALO_HIGH_RES_TEXT` | `true`: the menus' and HUD's text is drawn with the fonts in `port/assets/fonts` (Overpass, in place of the maps' Interstate) at the resolution the game draws at, laid out as before, and the menus' titles are drawn from the high-res pictures in `port/assets/titles`. `false`: the maps' bitmap fonts and titles. | | `display.shadow_resolution` | `128` | `HALO_SHADOW_RESOLUTION` | The size of the maps that the shadows of the objects are drawn in, in pixels each way: `128`, `256`, `512` or `1024` (other values go down to one of these). The game draws the shadow of each object into a map of 128x128 pixels, blurs it and projects it onto the ground. On a large screen, the edges of these shadows show steps that move when the object moves. A larger map makes the edges smooth; the blur is made wider to match, so the shadows are as soft as on the Xbox. Each doubling adds two passes of the blur. `128`: as on the Xbox. | @@ -489,6 +474,29 @@ the world (such as `rasterizer_wireframe`). Refer to `NETCODE.md`. The frame rate shows at the bottom right of the screen. It is the mean over half a second. +## Field of view + +Video Setup's FOV AND VIEWMODELS screen sets the field of view of the +first-person view (`display.fov`) and of the weapon and hands +(`display.viewmodel_fov`), from 80 to 150 degrees in steps of 5, and can +hide the weapon (`display.viewmodel_visible`). DEFAULT, the setting's `0`, +keeps the stock view. `config.toml` takes any angle from 20 to 150. + +- An angle is across the screen at 16:9. Another shape keeps the same + angle up and down, as the stock view does. +- The field of view applies on foot in first person only. Vehicles, death, + cinematics and scripted cameras keep their own view. +- A scope's zoom levels keep their stock view: the extra width fades out as + the zoom comes in. A view narrower than the stock one narrows the zoom + too. +- The reticles scale with the view, so that they stay on the aim. The + scopes' pictures keep their place. +- By default the weapon keeps its stock view when the world is wider: + arms and a gun right against the camera stretch at a wide angle. + +Only this machine's view changes. Nothing the machines send each other +changes, so players with different settings play together. + ## System link The Xbox game lets 16 players on 4 machines play a system link game. This diff --git a/port/linux/game/render_fov.c b/port/linux/game/render_fov.c deleted file mode 100644 index cfb65b6e..00000000 --- a/port/linux/game/render_fov.c +++ /dev/null @@ -1,136 +0,0 @@ -/* FOV in horizontal degrees at 16:9. The observer, its transitions and the -network's camera values remain authored; only the local render view changes. -Default follows the original projection exactly. */ -#include "cseries.h" -#include "render_fov.h" -#include "camera/director.h" -#include "cutscene/cinematics.h" -#include "game/players.h" -#include "objects/objects.h" -#include "units/units.h" -#include "units/unit_definitions.h" -#include "items/weapons.h" -#include "render/render_cameras.h" - -#include - -double config_real(const char *name); -unsigned long config_changes(void); - -static real reticle_scales[MAXIMUM_LOCAL_PLAYERS]; -static real authored_vertical[MAXIMUM_LOCAL_PLAYERS]; - -static float render_fov_adjust(short local_player_index, float native_vertical_field_of_view) -{ - static boolean initialized; - static unsigned long read_at; - static real requested_tangent; - unsigned long changes = config_changes(); - long unit_index, weapon_index; - struct unit_datum *unit; - real unzoomed_angle, unzoomed_tangent, native_tangent, adjusted_tangent; - - if (!initialized || read_at != changes) - { - double degrees = config_real("display.fov"); - - initialized = TRUE; - read_at = changes; - requested_tangent = isfinite(degrees) && degrees >= 20.0 && degrees <= 150.0 ? - (real)(tan(degrees * 3.14159265358979323846 / 360.0) * (9.0 / 16.0)) : 0.0f; - } - if (!requested_tangent || local_player_index < 0 || local_player_index >= MAXIMUM_LOCAL_PLAYERS || - cinematic_in_progress() || (director_camera_scripted && *director_camera_scripted) || - director_get_perspective(local_player_index) != _director_perspective_first_person || - director_inhibited_facing(local_player_index)) - { - return native_vertical_field_of_view; - } - unit_index = player_control_get_unit_index(local_player_index); - if (unit_index == NONE) - return native_vertical_field_of_view; - unit = unit_try_and_get(unit_index); - if (!unit || unit->object.parent_object_index != NONE || TEST_FLAG(unit->object.damage_flags, _object_dead_bit)) - return native_vertical_field_of_view; - - unzoomed_angle = unit_definition_get(unit->definition_index)->unit.camera_field_of_view; - if (!(unzoomed_angle >= 0.001f && unzoomed_angle <= _pi / 2.0f) || - !(native_vertical_field_of_view > 0.0f && native_vertical_field_of_view < _pi)) - { - return native_vertical_field_of_view; - } - unzoomed_tangent = 0.75f * render_camera_get_adjusted_field_of_view_tangent(unzoomed_angle); - native_tangent = tanf(native_vertical_field_of_view * 0.5f); - if (requested_tangent < unzoomed_tangent) - { - /* A narrow base view must not become wider when zoom is pressed. */ - adjusted_tangent = native_tangent * (requested_tangent / unzoomed_tangent); - } - else - { - real blend = 1.0f; - - weapon_index = unit_inventory_get_weapon(unit_index, unit->unit.current_weapon_index); - if (weapon_index != NONE) - { - real scoped_angle = weapon_get_field_of_view(weapon_index, unzoomed_angle, 0); - - if (scoped_angle > 0.0f && scoped_angle < unzoomed_angle) - { - real scoped_tangent = 0.75f * render_camera_get_adjusted_field_of_view_tangent(scoped_angle); - /* The observer's atan/tan round trip can land just above - the endpoint. Keep a completed scope's original angle exactly. */ - if (native_tangent <= scoped_tangent * 1.000001f) - return native_vertical_field_of_view; - - /* Fade the extra width along the observer's existing scope - transition. Every completed scope level keeps its stock view. */ - blend = PIN((native_tangent - scoped_tangent) / - (unzoomed_tangent - scoped_tangent), 0.0f, 1.0f); - } - } - if (blend == 0.0f) - return native_vertical_field_of_view; - adjusted_tangent = native_tangent + (requested_tangent - unzoomed_tangent) * blend; - } - return 2.0f * atanf(adjusted_tangent); -} - -float render_fov_vertical(short local_player_index, float native_vertical_field_of_view) -{ - real adjusted = render_fov_adjust(local_player_index, native_vertical_field_of_view); - - if (local_player_index >= 0 && local_player_index < MAXIMUM_LOCAL_PLAYERS) - { - real scale = 1.0f; - - if (adjusted != native_vertical_field_of_view && - native_vertical_field_of_view > 0.0f && native_vertical_field_of_view < _pi && - adjusted > 0.0f && adjusted < _pi) - { - scale = tanf(native_vertical_field_of_view * 0.5f) / tanf(adjusted * 0.5f); - if (!isfinite(scale) || scale <= 0.0f) scale = 1.0f; - } - /* Record the projection used for this view, including its native zoom - * transition. A temporary viewmodel projection must not affect the HUD. */ - reticle_scales[local_player_index] = scale; - /* A widened world view. The weapon can return to this frame's own angle. */ - authored_vertical[local_player_index] = - adjusted != native_vertical_field_of_view ? native_vertical_field_of_view : 0.0f; - } - return adjusted; -} - -float render_fov_authored_vertical(short local_player_index) -{ - if (local_player_index < 0 || local_player_index >= MAXIMUM_LOCAL_PLAYERS) - return 0.0f; - return authored_vertical[local_player_index]; -} - -float render_fov_reticle_scale(short local_player_index) -{ - if (local_player_index < 0 || local_player_index >= MAXIMUM_LOCAL_PLAYERS || - reticle_scales[local_player_index] == 0.0f) return 1.0f; - return reticle_scales[local_player_index]; -} diff --git a/port/linux/game/render_fov.h b/port/linux/game/render_fov.h deleted file mode 100644 index 024a98b8..00000000 --- a/port/linux/game/render_fov.h +++ /dev/null @@ -1,12 +0,0 @@ -/* A local display adjustment, after the observer and before either render -camera's projection and visibility frustum are built. */ -#ifndef RENDER_FOV_H -#define RENDER_FOV_H - -float render_fov_vertical(short local_player_index, float native_vertical_field_of_view); -/* HUD pixel ratio relative to the native projection of the last local view. */ -float render_fov_reticle_scale(short local_player_index); -/* The view's own vertical angle when the world FOV replaced it, else 0. */ -float render_fov_authored_vertical(short local_player_index); - -#endif diff --git a/port/linux/game/view_fov.c b/port/linux/game/view_fov.c new file mode 100644 index 00000000..c6f3f6ac --- /dev/null +++ b/port/linux/game/view_fov.c @@ -0,0 +1,269 @@ +/* +VIEW_FOV.C + +The first-person view's field of view (display.fov, display.viewmodel_fov) +and whether the weapon is drawn (display.viewmodel_visible), the local +render view only: the observer, its transitions and what the network sends +keep the game's own camera. Each angle is horizontal at 16:9 (the vertical +angle is kept, so other shapes of window widen or narrow as the stock view +does), and 0 keeps the stock view. Called from the game's camera +(main.c's set_window_camera_values), its HUD (hud_draw.c) and the passes +that draw the first-person weapon (rasterizer and render sources). +*/ + +#include "cseries.h" +#include "view_fov.h" +#include "camera/director.h" +#include "cutscene/cinematics.h" +#include "game/players.h" +#include "items/weapons.h" +#include "objects/objects.h" +#include "rasterizer/rasterizer.h" +#include "rasterizer/xbox/rasterizer_xbox_internal.h" +#include "render/render.h" +#include "render/render_cameras.h" +#include "render/render_cameras_internal.h" +#include "units/unit_definitions.h" +#include "units/units.h" + +#include + +/* (the window's rasterizer target, as the rasterizer's sources number it) */ +enum +{ + _rasterizer_target_render_primary = 0 +}; + +double config_real(const char *name); +int config_boolean(const char *name); +unsigned long config_changes(void); + +/* ---------- the world's field of view */ + +static real reticle_scales[MAXIMUM_LOCAL_PLAYERS]; +static real authored_vertical[MAXIMUM_LOCAL_PLAYERS]; + +/* the view's vertical angle for display.fov: the game's own in a vehicle, +dead, in a cinematic, a scripted camera or a view the director holds, and +at a scope's full zoom; between, the zoom's own transition */ +static real render_fov_adjust(short local_player_index, real native_vertical_field_of_view) +{ + static boolean initialized; + static unsigned long read_at; + static real requested_tangent; + unsigned long changes = config_changes(); + long unit_index, weapon_index; + struct unit_datum *unit; + real unzoomed_angle, unzoomed_tangent, native_tangent, adjusted_tangent; + + if (!initialized || read_at != changes) + { + double degrees = config_real("display.fov"); + + initialized = TRUE; + read_at = changes; + requested_tangent = degrees >= 20.0 && degrees <= 150.0 ? + (real)(tan(degrees * _pi / 360.0) * (9.0 / 16.0)) : 0.0f; + } + if (!requested_tangent || local_player_index < 0 || local_player_index >= MAXIMUM_LOCAL_PLAYERS || + cinematic_in_progress() || (director_camera_scripted && *director_camera_scripted) || + director_get_perspective(local_player_index) != _director_perspective_first_person || + director_inhibited_facing(local_player_index)) + { + return native_vertical_field_of_view; + } + unit_index = player_control_get_unit_index(local_player_index); + if (unit_index == NONE) + return native_vertical_field_of_view; + unit = unit_try_and_get(unit_index); + if (!unit || unit->object.parent_object_index != NONE || TEST_FLAG(unit->object.damage_flags, _object_dead_bit)) + return native_vertical_field_of_view; + + unzoomed_angle = unit_definition_get(unit->definition_index)->unit.camera_field_of_view; + if (!(unzoomed_angle >= 0.001f && unzoomed_angle <= _pi / 2.0f) || + !(native_vertical_field_of_view > 0.0f && native_vertical_field_of_view < _pi)) + { + return native_vertical_field_of_view; + } + unzoomed_tangent = 0.75f * render_camera_get_adjusted_field_of_view_tangent(unzoomed_angle); + native_tangent = tanf(native_vertical_field_of_view * 0.5f); + if (requested_tangent < unzoomed_tangent) + { + /* (a view narrower than the stock one: the zoom narrows it further) */ + adjusted_tangent = native_tangent * (requested_tangent / unzoomed_tangent); + } + else + { + real blend = 1.0f; + + weapon_index = unit_inventory_get_weapon(unit_index, unit->unit.current_weapon_index); + if (weapon_index != NONE) + { + real scoped_angle = weapon_get_field_of_view(weapon_index, unzoomed_angle, 0); + + if (scoped_angle > 0.0f && scoped_angle < unzoomed_angle) + { + real scoped_tangent = 0.75f * render_camera_get_adjusted_field_of_view_tangent(scoped_angle); + /* (a scope's full zoom keeps its stock angle exactly: the + observer's arctangent and tangent can land just above it) */ + if (native_tangent <= scoped_tangent * 1.000001f) + return native_vertical_field_of_view; + + /* (the extra width fades out along the scope's own + transition, so every zoom level reached is the stock view) */ + blend = PIN((native_tangent - scoped_tangent) / + (unzoomed_tangent - scoped_tangent), 0.0f, 1.0f); + } + } + if (blend == 0.0f) + return native_vertical_field_of_view; + adjusted_tangent = native_tangent + (requested_tangent - unzoomed_tangent) * blend; + } + return 2.0f * atanf(adjusted_tangent); +} + +real render_fov_vertical(short local_player_index, real native_vertical_field_of_view) +{ + real adjusted = render_fov_adjust(local_player_index, native_vertical_field_of_view); + + if (local_player_index >= 0 && local_player_index < MAXIMUM_LOCAL_PLAYERS) + { + real scale = 1.0f; + + if (adjusted != native_vertical_field_of_view && + native_vertical_field_of_view > 0.0f && native_vertical_field_of_view < _pi && + adjusted > 0.0f && adjusted < _pi) + { + scale = tanf(native_vertical_field_of_view * 0.5f) / tanf(adjusted * 0.5f); + if (!(scale > 0.0f && scale < 1000.0f)) + scale = 1.0f; + } + /* (the view's projection, the zoom's transition too, for the HUD: + the weapon's own projection, viewmodel_projection_begin, is not it) */ + reticle_scales[local_player_index] = scale; + /* (a widened view's own angle, which the weapon keeps by default) */ + authored_vertical[local_player_index] = + adjusted != native_vertical_field_of_view ? native_vertical_field_of_view : 0.0f; + } + return adjusted; +} + +real render_fov_authored_vertical(short local_player_index) +{ + if (local_player_index < 0 || local_player_index >= MAXIMUM_LOCAL_PLAYERS) + return 0.0f; + return authored_vertical[local_player_index]; +} + +real render_fov_reticle_scale(short local_player_index) +{ + if (local_player_index < 0 || local_player_index >= MAXIMUM_LOCAL_PLAYERS || + reticle_scales[local_player_index] == 0.0f) + { + return 1.0f; + } + return reticle_scales[local_player_index]; +} + +/* ---------- the first-person weapon's */ + +/* how deep the weapon's passes nest, and whether the outermost changed the +view (the cameras and frustums it saved) */ +static unsigned long projection_depth; +static boolean projection_applied; +static struct render_camera saved_render_camera, saved_rasterizer_camera; +static struct render_frustum saved_render_frustum, saved_rasterizer_frustum; + +static real viewmodel_vertical(void) +{ + static boolean initialized; + static unsigned long read_at; + static real angle; + unsigned long changes = config_changes(); + + if (!initialized || read_at != changes) + { + double degrees = config_real("display.viewmodel_fov"); + + angle = degrees >= 20.0 && degrees <= 150.0 ? + (real)(2.0 * atan(tan(degrees * _pi / 360.0) * (9.0 / 16.0))) : 0.0f; + initialized = TRUE; + read_at = changes; + } + return angle; +} + +void viewmodel_projection_begin(void) +{ + real angle; + real_rectangle2d render_bounds, rasterizer_bounds; + + if (projection_depth++ != 0) + return; + projection_applied = FALSE; + /* (display.viewmodel_fov's angle; by default the view's own, from before + display.fov widened it: arms and a gun right against the camera stretch + at a wide angle) */ + angle = viewmodel_vertical(); + if (!angle) + angle = render_fov_authored_vertical(render.local_player_index); + if (!angle || render.local_player_index < 0 || render.local_player_index >= MAXIMUM_LOCAL_PLAYERS || + global_window_parameters.rasterizer_target != _rasterizer_target_render_primary || + cinematic_in_progress() || (director_camera_scripted && *director_camera_scripted) || + director_get_perspective(render.local_player_index) != _director_perspective_first_person || + director_inhibited_facing(render.local_player_index)) + { + return; + } + + saved_render_camera = render.camera; + saved_render_frustum = render.frustum; + saved_rasterizer_camera = global_window_parameters.camera; + saved_rasterizer_frustum = global_window_parameters.frustum; + /* (the frustums' own bounds, the view's crop of a split screen: the + visibility and the projection change together, the weapon's pose and + markers do not; get_projection_bounds' slopes are not bounds) */ + render_bounds = saved_render_frustum.frustum_bounds; + rasterizer_bounds = saved_rasterizer_frustum.frustum_bounds; + render.camera.vertical_field_of_view = angle; + global_window_parameters.camera.vertical_field_of_view = angle; + render_camera_build_frustum(&render.camera, &render_bounds, &render.frustum, TRUE); + render_camera_build_frustum(&global_window_parameters.camera, &rasterizer_bounds, + &global_window_parameters.frustum, TRUE); + projection_applied = TRUE; + rasterizer_set_frustum_z(0.0f, 0.0f); +} + +void viewmodel_projection_end(void) +{ + if (!projection_depth || --projection_depth || !projection_applied) + return; + render.camera = saved_render_camera; + render.frustum = saved_render_frustum; + global_window_parameters.camera = saved_rasterizer_camera; + global_window_parameters.frustum = saved_rasterizer_frustum; + projection_applied = FALSE; + rasterizer_set_frustum_z(0.0f, 0.0f); +} + +/* ---------- whether the first-person weapon is drawn */ + +boolean viewmodel_is_visible(void) +{ + static boolean initialized, visible; + static unsigned long read_at; + unsigned long changes = config_changes(); + + if (!initialized || read_at != changes) + { + visible = config_boolean("display.viewmodel_visible") != 0; + initialized = TRUE; + read_at = changes; + } + return visible; +} + +boolean viewmodel_draws_geometry(boolean first_person) +{ + return !first_person || viewmodel_is_visible(); +} diff --git a/port/linux/game/view_fov.h b/port/linux/game/view_fov.h new file mode 100644 index 00000000..6f410e0a --- /dev/null +++ b/port/linux/game/view_fov.h @@ -0,0 +1,31 @@ +/* +VIEW_FOV.H + +The first-person view's field of view and weapon (view_fov.c). +*/ + +#ifndef VIEW_FOV_H +#define VIEW_FOV_H + +/* the vertical angle the local player's world view is drawn at (display.fov, +after the observer and before the projection and the visibility frustum +are built), from the game's own */ +real render_fov_vertical(short local_player_index, real native_vertical_field_of_view); +/* how much the reticle shrinks or grows to stay on its aim at that view (the +last local view's own projection against the game's): 1 for the stock view */ +real render_fov_reticle_scale(short local_player_index); +/* the view's own vertical angle when display.fov widened it, else 0 */ +real render_fov_authored_vertical(short local_player_index); + +/* the first-person weapon's projection (display.viewmodel_fov) around its +passes: balanced and nestable, the current local render view only */ +void viewmodel_projection_begin(void); +void viewmodel_projection_end(void); + +/* whether the first-person weapon, hands and attached visuals are drawn +(display.viewmodel_visible); its firing, sounds and lights go on either way */ +boolean viewmodel_is_visible(void); +/* whether a pass draws: not one of the first-person weapon's that is hidden */ +boolean viewmodel_draws_geometry(boolean first_person); + +#endif diff --git a/port/linux/game/viewmodel_fov.c b/port/linux/game/viewmodel_fov.c deleted file mode 100644 index d049a836..00000000 --- a/port/linux/game/viewmodel_fov.c +++ /dev/null @@ -1,85 +0,0 @@ -#include "cseries.h" -#include "viewmodel_fov.h" -#include "render_fov.h" -#include "camera/director.h" -#include "cutscene/cinematics.h" -#include "game/players.h" -#include "render/render.h" -#include "render/render_cameras.h" -#include "render/render_cameras_internal.h" -#include "rasterizer/rasterizer.h" -#include "rasterizer/xbox/rasterizer_xbox_internal.h" -#include - -double config_real(const char *name); -unsigned long config_changes(void); - -static unsigned projection_depth; -static boolean projection_applied; -static struct render_camera saved_render_camera, saved_rasterizer_camera; -static struct render_frustum saved_render_frustum, saved_rasterizer_frustum; - -static real viewmodel_vertical(void) -{ - static boolean initialized; - static unsigned long read_at; - static real angle; - unsigned long changes = config_changes(); - if (!initialized || read_at != changes) - { - double degrees = config_real("display.viewmodel_fov"); - angle = isfinite(degrees) && degrees >= 20.0 && degrees <= 150.0 ? - (real)(2.0 * atan(tan(degrees * 3.14159265358979323846 / 360.0) * (9.0 / 16.0))) : 0.0f; - initialized = TRUE; - read_at = changes; - } - return angle; -} - -void viewmodel_projection_begin(void) -{ - real angle; - real_rectangle2d render_bounds, rasterizer_bounds; - if (projection_depth++ != 0) return; - projection_applied = FALSE; - angle = viewmodel_vertical(); - /* Default keeps the weapon at this frame's own angle. A chosen angle - is that FOV and is left as it is. A wide world view stretches arms - and a gun that sit against the camera, so Default does not follow it. */ - if (!angle) - angle = render_fov_authored_vertical(render.local_player_index); - if (!angle || render.local_player_index < 0 || render.local_player_index >= MAXIMUM_LOCAL_PLAYERS || - global_window_parameters.rasterizer_target != 0 /* primary view */ || - cinematic_in_progress() || (director_camera_scripted && *director_camera_scripted) || - director_get_perspective(render.local_player_index) != _director_perspective_first_person || - director_inhibited_facing(render.local_player_index)) return; - - saved_render_camera = render.camera; - saved_render_frustum = render.frustum; - saved_rasterizer_camera = global_window_parameters.camera; - saved_rasterizer_frustum = global_window_parameters.frustum; - /* Retain cropped/tiled viewport bounds. CPU visibility and GPU projection - change together; pose, weapon markers and gameplay values stay put. */ - /* The builder takes normalized crop bounds. get_projection_bounds instead - returns signed view-space ray slopes, which would invert/rescale the view. */ - render_bounds = saved_render_frustum.frustum_bounds; - rasterizer_bounds = saved_rasterizer_frustum.frustum_bounds; - render.camera.vertical_field_of_view = angle; - global_window_parameters.camera.vertical_field_of_view = angle; - render_camera_build_frustum(&render.camera, &render_bounds, &render.frustum, TRUE); - render_camera_build_frustum(&global_window_parameters.camera, &rasterizer_bounds, - &global_window_parameters.frustum, TRUE); - projection_applied = TRUE; - rasterizer_set_frustum_z(0.0f, 0.0f); -} - -void viewmodel_projection_end(void) -{ - if (!projection_depth || --projection_depth || !projection_applied) return; - render.camera = saved_render_camera; - render.frustum = saved_render_frustum; - global_window_parameters.camera = saved_rasterizer_camera; - global_window_parameters.frustum = saved_rasterizer_frustum; - projection_applied = FALSE; - rasterizer_set_frustum_z(0.0f, 0.0f); -} diff --git a/port/linux/game/viewmodel_fov.h b/port/linux/game/viewmodel_fov.h deleted file mode 100644 index f3fc9471..00000000 --- a/port/linux/game/viewmodel_fov.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef VIEWMODEL_FOV_H -#define VIEWMODEL_FOV_H -/* Balanced, nestable scopes change only the current local render view. */ -void viewmodel_projection_begin(void); -void viewmodel_projection_end(void); -#endif diff --git a/port/linux/game/viewmodel_visibility.c b/port/linux/game/viewmodel_visibility.c deleted file mode 100644 index 09f8d860..00000000 --- a/port/linux/game/viewmodel_visibility.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "viewmodel_visibility.h" - -int config_boolean(const char *name); -unsigned long config_changes(void); - -int viewmodel_is_visible(void) -{ - static int initialized, visible; - static unsigned long read_at; - unsigned long changes = config_changes(); - if (!initialized || read_at != changes) - { - visible = config_boolean("display.viewmodel_visible"); - initialized = 1; - read_at = changes; - } - return visible; -} - -int viewmodel_draws_geometry(int first_person) -{ - return !first_person || viewmodel_is_visible(); -} diff --git a/port/linux/game/viewmodel_visibility.h b/port/linux/game/viewmodel_visibility.h deleted file mode 100644 index 40e08b6b..00000000 --- a/port/linux/game/viewmodel_visibility.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef VIEWMODEL_VISIBILITY_H -#define VIEWMODEL_VISIBILITY_H -/* Display only: weapon updates, sounds, gameplay and world lights continue. */ -int viewmodel_is_visible(void); -int viewmodel_draws_geometry(int first_person); -#endif diff --git a/port/linux/include/math.h b/port/linux/include/math.h index 84d84dc3..63f92edc 100644 --- a/port/linux/include/math.h +++ b/port/linux/include/math.h @@ -11,12 +11,6 @@ strnlen() cannot collide with the game's own), which hides glibc's M_*. #include_next -/* glibc hides the C99 classification macros in the game's ISO C89 mode. -Use the compiler's classification, not a call to a nonexistent libm symbol. */ -#ifndef isfinite -#define isfinite(value) __builtin_isfinite(value) -#endif - #ifndef M_PI #define M_E 2.71828182845904523536 #define M_LOG2E 1.44269504088896340736 diff --git a/port/linux/src/port_config.c b/port/linux/src/port_config.c index d4457cc3..83254e68 100644 --- a/port/linux/src/port_config.c +++ b/port/linux/src/port_config.c @@ -113,15 +113,16 @@ static const struct config_setting config_settings[] = "where the last tick left it: the view turns the frame the mouse moves,\n" "not up to two ticks (66 ms) later." }, { "display.fov", _config_real, "0.0", "HALO_FOV", _environment_value, _platform_all, - "On-foot first-person horizontal FOV at 16:9, in degrees (20-150);\n" - "0 keeps the authored view. Other cameras keep their own FOV." }, + "The first-person view's field of view on foot, in degrees across at\n" + "16:9 (20 to 150); 0 keeps the stock view. Vehicles, cinematics and\n" + "scripted cameras keep their own." }, { "display.viewmodel_fov", _config_real, "0.0", "HALO_VIEWMODEL_FOV", _environment_value, _platform_all, - "Weapon and hands horizontal FOV at 16:9, in degrees (20 to 150);\n" - "0 is Default and keeps the original weapon view. A chosen angle is\n" - "that FOV. Attached visuals use the same projection." }, - { "display.viewmodel_visible", _config_boolean, "true", "HALO_VIEWMODEL_VIS", _environment_value, _platform_all, - "Draw the first-person weapon, hands and attached visuals. Turning\n" - "this off does not change firing, animation, sound or world lights." }, + "The first-person weapon's and hands' field of view, in degrees across\n" + "at 16:9 (20 to 150); 0 keeps the weapon's stock view, also when\n" + "display.fov widens the world." }, + { "display.viewmodel_visible", _config_boolean, "true", "HALO_VIEWMODEL_VISIBLE", _environment_value, _platform_all, + "Draw the first-person weapon, hands and what is attached to them.\n" + "Off, they are not drawn; firing, animation, sound and lights go on." }, { "display.high_res_hud", _config_boolean, "true", "HALO_HIGH_RES_HUD", _environment_value, _platform_all, "Draw the HUD (meters, counters, panels, motion sensor, reticles,\n" "waypoints, scopes) from the high-res assets (8x the maps' bitmaps);\n" diff --git a/source/interface/first_person_weapons.c b/source/interface/first_person_weapons.c index de7df2fa..733f71cf 100644 --- a/source/interface/first_person_weapons.c +++ b/source/interface/first_person_weapons.c @@ -100,7 +100,6 @@ symbols in this file: #define REAL_MATH_EXTERNAL_REAL_LOCAL_RANDOM #define REAL_MATH_EXTERNAL_REAL_LOCAL_RANDOM_RANGE #include "interface/first_person_weapons.h" -#include "viewmodel_visibility.h" #include "cache/predicted_resources.h" #include "camera/director.h" @@ -125,6 +124,7 @@ symbols in this file: #include "sound/game_sound.h" #include "sound/sound_manager.h" #include "units/units.h" +#include "view_fov.h" /* port: port/linux/game/view_fov.c */ /* ---------- constants */ @@ -470,6 +470,7 @@ long first_person_weapon_get_local_index( void first_person_weapon_draw( void) { + /* port: not drawn with display.viewmodel_visible off (view_fov.c) */ if (!viewmodel_is_visible()) return; if (render.local_player_index!=NONE) diff --git a/source/interface/hud_draw.c b/source/interface/hud_draw.c index ff62d705..4e6436f2 100644 --- a/source/interface/hud_draw.c +++ b/source/interface/hud_draw.c @@ -95,7 +95,6 @@ symbols in this file: #include "game/players.h" #include "interface/hud_definitions.h" #include "interface/hud_draw.h" -#include "render_fov.h" #include "interface/interface.h" #include "interface/unit_hud_interface_definition.h" #include "items/weapon_definitions.h" @@ -107,6 +106,7 @@ symbols in this file: #include "units/units.h" #include "cache_file_formats.h" /* port: port/linux/game/cache_file_formats.c */ #include "custom_edition_cache.h" +#include "view_fov.h" /* port: port/linux/game/view_fov.c */ /* ---------- constants */ @@ -397,7 +397,7 @@ static void hud_draw_bitmap_internal( real_vector2d const *xy_scale, real theta, pixel32 color, - real reticle_scale); + real reticle_scale); /* port: (display.fov, view_fov.c) */ static void hud_draw_bitmap_with_meter( void *meter_parameters, struct bitmap_data const *bitmap, @@ -1592,8 +1592,9 @@ static void hud_draw_bitmap_internal( real center_y = (render.camera.window_bounds.y1 + render.camera.window_bounds.y0) / 2 - render.camera.viewport_bounds.y0; - /* Transform the complete authored reticle about its aiming anchor: - * offsets and separate sprites must shrink along with their artwork. */ + /* port: the reticle scaled about the view's centre, where it aims, + with display.fov (view_fov.c): its offsets and separate pieces + shrink with its picture */ for (vertex_index = 0; vertex_index < 4; vertex_index++) { vertices[vertex_index].position.x = center_x + @@ -1687,6 +1688,7 @@ static void hud_draw_bitmap_with_meter( &xy_scale, theta, color, + /* port: a reticle at the centre keeps to its aim (view_fov.c) */ is_crosshair_bitmap && absolute_placement->corner == _hud_anchor_center ? render_fov_reticle_scale(render.local_player_index) : 1.0f); diff --git a/source/interface/hud_weapon.c b/source/interface/hud_weapon.c index 058a4bfc..4462270e 100644 --- a/source/interface/hud_weapon.c +++ b/source/interface/hud_weapon.c @@ -1292,6 +1292,7 @@ static void crosshairs_draw( color, in_multiplayer, interface_bitmap, + /* port: not scaled with display.fov (view_fov.c) */ FALSE); } else @@ -1308,7 +1309,9 @@ static void crosshairs_draw( color, in_multiplayer, bitmap_group->type == _bitmap_group_type_interface_bitmaps, - /* Scope artwork keeps its authored layout. */ + /* port: a reticle, scaled with display.fov + (view_fov.c); a scope's picture keeps its + place */ state_index != _crosshair_state_zoom); } } diff --git a/source/main/main.c b/source/main/main.c index 3ea113d5..db4a3210 100644 --- a/source/main/main.c +++ b/source/main/main.c @@ -393,7 +393,7 @@ symbols in this file: #include "text/font_group.h" #include "tag_files/files.h" #include "custom_edition_cache.h" /* port: custom_edition_level_name */ -#include "render_fov.h" /* port: local world FOV, before projection and culling */ +#include "view_fov.h" /* port: display.fov, before the projection and culling */ #if defined(HALO_WINDOWS) || defined(HALO_ANDROID) || defined(__linux__) #define HALO_NATIVE_BUILD_INFO 1 @@ -1152,6 +1152,7 @@ void set_window_camera_values( window->rasterizer_camera.position = observer->position; window->rasterizer_camera.forward = observer->forward; window->rasterizer_camera.up = observer->up; + /* port: display.fov's view, the local render view only (view_fov.c) */ window->rasterizer_camera.vertical_field_of_view = render_fov_vertical(window->local_player_index, 2.0f * arctangent( 0.75f * render_camera_get_adjusted_field_of_view_tangent( diff --git a/source/rasterizer/rasterizer_lights.c b/source/rasterizer/rasterizer_lights.c index ef91f729..61662307 100644 --- a/source/rasterizer/rasterizer_lights.c +++ b/source/rasterizer/rasterizer_lights.c @@ -81,8 +81,6 @@ symbols in this file: /* ---------- headers */ #include "cseries.h" -#include "viewmodel_fov.h" -#include "viewmodel_visibility.h" #include "errors.h" #include "bitmaps/bitmaps.h" #include "interface/hud_draw.h" @@ -104,6 +102,7 @@ symbols in this file: #include #include "rasterizer/xbox/rasterizer_xbox.h" #include "main/main.h" +#include "view_fov.h" /* port: port/linux/game/view_fov.c */ /* ---------- constants */ @@ -548,7 +547,8 @@ void rasterizer_lens_flare_submit( 268, (parameters->compressed_window_index&_lens_flare_window_index_mask)==global_window_parameters.window_index); - if (!viewmodel_draws_geometry(parameters->compressed_window_index & _lens_flare_first_person_weapon_flag)) + /* port: not the first-person weapon's with it hidden (view_fov.c) */ + if (!viewmodel_draws_geometry((parameters->compressed_window_index & _lens_flare_first_person_weapon_flag) != 0)) return; if (rasterizer_debug_options.draw_lens_flares && !screenshot_in_progress() && global_window_parameters.rasterizer_target==_rasterizer_target_render_primary) @@ -568,12 +568,15 @@ void rasterizer_lens_flare_submit( screen): its occlusion test would count no pixels, so it would not be drawn, and a level of many lights (Halo PC's maps) tested hundreds a frame, each a draw of its own (the sphere takes in the - test's offset point and size, rasterizer_lens_flares_submit_occlusion_tests) */ + test's offset point and size, rasterizer_lens_flares_submit_occlusion_tests). + Not the first-person weapon's: it is drawn at the weapon's own + projection (display.viewmodel_fov, view_fov.c), not this frustum's */ if ((parameters->definition->far_fade_distance==0.0f || camera_distancedefinition->far_fade_distance) && (parameters->compressed_light_color&LENS_FLARE_LIGHT_COLOR_ALPHA_MASK)>0 && + ((parameters->compressed_window_index&_lens_flare_first_person_weapon_flag) || render_frustum_sphere_visible(&global_window_parameters.frustum, ¶meters->position, - 3.0f * parameters->definition->occlusion_radius)) + 3.0f * parameters->definition->occlusion_radius))) { struct rasterizer_lens_flare_submit_parameters *lens_flare_parameters= lens_flare_parameters_get((short)local_lens_flare_count++); @@ -861,11 +864,15 @@ void rasterizer_lens_flares_submit_occlusion_tests( if ((lens_flare_parameters->compressed_window_index & _lens_flare_window_index_mask) == global_window_parameters.window_index) { - boolean first_person = (lens_flare_parameters->compressed_window_index & _lens_flare_first_person_weapon_flag) != 0; - if (first_person) viewmodel_projection_begin(); + boolean first_person = (lens_flare_parameters->compressed_window_index & + _lens_flare_first_person_weapon_flag) != 0; real occlusion_radius = definition->occlusion_radius; real_point3d occlusion_point; + /* port: the first-person weapon's projection (view_fov.c) */ + if (first_person) + viewmodel_projection_begin(); + switch (definition->occlusion_offset_direction) { case _lens_flare_occlusion_offset_direction_toward_viewer: @@ -900,7 +907,8 @@ void rasterizer_lens_flares_submit_occlusion_tests( occlusion_radius, lens_flare_index); - if (first_person) viewmodel_projection_end(); + if (first_person) + viewmodel_projection_end(); } } @@ -935,10 +943,14 @@ void rasterizer_lens_flares_draw( if ((lens_flare_parameters->compressed_window_index & _lens_flare_window_index_mask) == global_window_parameters.window_index) { - boolean first_person = (lens_flare_parameters->compressed_window_index & _lens_flare_first_person_weapon_flag) != 0; - if (first_person) viewmodel_projection_begin(); + boolean first_person = (lens_flare_parameters->compressed_window_index & + _lens_flare_first_person_weapon_flag) != 0; struct lens_flare_definition *definition = lens_flare_parameters->definition; + /* port: the first-person weapon's projection (view_fov.c) */ + if (first_person) + viewmodel_projection_begin(); + if (lens_flare_parameters->internal__occlusion_pixels > 0 && LENS_FLARE_LIGHT_COLOR_ALPHA(lens_flare_parameters->compressed_light_color) > 0 && definition->reflections.count > 0) @@ -1180,7 +1192,8 @@ void rasterizer_lens_flares_draw( } } - if (first_person) viewmodel_projection_end(); + if (first_person) + viewmodel_projection_end(); } } diff --git a/source/rasterizer/rasterizer_transparent_geometry.c b/source/rasterizer/rasterizer_transparent_geometry.c index f5534842..f5154ee3 100644 --- a/source/rasterizer/rasterizer_transparent_geometry.c +++ b/source/rasterizer/rasterizer_transparent_geometry.c @@ -83,7 +83,6 @@ symbols in this file: /* ---------- headers */ #include "cseries.h" -#include "viewmodel_fov.h" #include "cseries/errors.h" #include "rasterizer.h" #include "rasterizer_console_vars.h" @@ -93,6 +92,7 @@ symbols in this file: #include "rasterizer/xbox/rasterizer_xbox_state.h" #include "shaders/shader_definitions.h" #include "shaders/shaders.h" +#include "view_fov.h" /* port: port/linux/game/view_fov.c */ /* ---------- constants */ @@ -587,6 +587,7 @@ void rasterizer_transparent_geometry_draw( global_window_parameters.rasterizer_target==_rasterizer_target_render_primary); if (!first_person_flag) { + /* port: the first-person weapon's projection (view_fov.c) */ viewmodel_projection_begin(); rasterizer_set_stencil_mode(0); rasterizer_set_frustum_z( @@ -615,7 +616,7 @@ void rasterizer_transparent_geometry_draw( rasterizer_debug_options.transparent_pixel_counter_active = FALSE; if (first_person_flag) { - viewmodel_projection_end(); + viewmodel_projection_end(); /* port: (view_fov.c) */ rasterizer_set_frustum_z(0.0f, 0.0f); } } diff --git a/source/rasterizer/xbox/rasterizer_xbox_models.c b/source/rasterizer/xbox/rasterizer_xbox_models.c index e2135da0..f205c9f8 100644 --- a/source/rasterizer/xbox/rasterizer_xbox_models.c +++ b/source/rasterizer/xbox/rasterizer_xbox_models.c @@ -111,7 +111,6 @@ symbols in this file: #include "rasterizer/rasterizer_models.h" #include "rasterizer/rasterizer_transparent_geometry.h" #include "render/render.h" -#include "viewmodel_fov.h" #include "shaders/shader_definitions.h" #include "shaders/shaders.h" @@ -124,6 +123,7 @@ symbols in this file: #include "rasterizer/xbox/rasterizer_xbox_draw_primitives.h" #include "rasterizer/xbox/rasterizer_xbox_internal.h" #include "rasterizer/xbox/rasterizer_xbox_models.h" +#include "view_fov.h" /* port: port/linux/game/view_fov.c */ /* ---------- constants */ @@ -616,7 +616,7 @@ void _rasterizer_model_end( !local_do_not_change_z_stencil_states) { rasterizer_set_stencil_mode(RASTERIZER_STENCIL_MODE_REJECT); - viewmodel_projection_end(); + viewmodel_projection_end(); /* port: (view_fov.c) */ rasterizer_set_frustum_z(0.0f, 0.0f); } local_parameters = NULL; @@ -645,6 +645,7 @@ void _rasterizer_model_begin( !do_not_change_z_stencil_states) { rasterizer_set_stencil_mode(RASTERIZER_STENCIL_MODE_WRITE); + /* port: the first-person weapon's projection (view_fov.c) */ viewmodel_projection_begin(); rasterizer_set_frustum_z( rasterizer_globals.first_person_weapon_near_clip_distance, diff --git a/source/render/render_particles.c b/source/render/render_particles.c index 9d71f4e2..cfce7009 100644 --- a/source/render/render_particles.c +++ b/source/render/render_particles.c @@ -22,7 +22,6 @@ symbols in this file: #include "effects/particles.h" #include "game/players.h" #include "interface/first_person_weapons.h" -#include "viewmodel_visibility.h" #include "render.h" #include "render_cameras_internal.h" #include "render_particles.h" @@ -30,6 +29,7 @@ symbols in this file: #include "tag_files/tag_groups.h" #include "units/unit_definitions.h" #include "units/units.h" +#include "view_fov.h" /* port: port/linux/game/view_fov.c */ /* ---------- constants */ @@ -160,6 +160,7 @@ void render_particles( boolean owned_by_local_player = particle->local_player_index == local_player_index; + /* port: not the first-person weapon's with it hidden (view_fov.c) */ if (render_location_visible(&particle->location) && viewmodel_draws_geometry(owned_by_local_player && TEST_FLAG(particle->flags, _particle_datum_dont_draw_third_person_bit)) && diff --git a/source/render/render_sprite.c b/source/render/render_sprite.c index f3c31a95..a622cb63 100644 --- a/source/render/render_sprite.c +++ b/source/render/render_sprite.c @@ -76,7 +76,6 @@ symbols in this file: /* ---------- headers */ #include "cseries/cseries.h" -#include "viewmodel_fov.h" #include "bitmaps/bitmap_group.h" #include "cseries/errors.h" #include "interface/hud_draw.h" @@ -91,6 +90,7 @@ symbols in this file: #include "tag_files/tag_files.h" #include "tag_files/tag_groups.h" #include "custom_edition_cache.h" /* port: port/linux/game/custom_edition_cache.c */ +#include "view_fov.h" /* port: port/linux/game/view_fov.c */ /* ---------- constants */ @@ -215,6 +215,7 @@ void build_sprites_begin( !TEST_FLAG(flags, _build_sprites_valid_bit)); data->bitmap_group_index = bitmap_group_index; + /* port: the first-person weapon's projection (view_fov.c) */ if (TEST_FLAG(flags, _build_sprites_first_person_bit)) viewmodel_projection_begin(); data->flags = flags; @@ -286,6 +287,7 @@ void build_sprites_end( } SET_FLAG(data->flags, _build_sprites_valid_bit, FALSE); + /* port: (view_fov.c) */ if (TEST_FLAG(data->flags, _build_sprites_first_person_bit)) viewmodel_projection_end(); return; diff --git a/tools/port_settings.py b/tools/port_settings.py index 0fb941f9..944783f6 100644 --- a/tools/port_settings.py +++ b/tools/port_settings.py @@ -191,15 +191,15 @@ _video["rows"] = [row for row in _video["rows"] if row[1] not in _graphics] _video["categories"] = [ ("GRAPHICS:", "video_settings/graphics", "The HUD, text, anti-aliasing, shadows and\nlighting."), ("FOV AND VIEWMODELS:", "video_settings/fov_viewmodels", - "Optional field of view and first-person weapon\ndisplay settings. Defaults keep the original view."), + "The field of view and the first-person\nweapon. Their defaults keep the stock view."), ] SCREENS["video_settings/fov_viewmodels"] = { "screen": "fov_viewmodel_settings_screen", "header": _video["header"], "spacing": 30, "rows": [ ("FOV:", "display.fov", [("DEFAULT", "0")] + [(str(n), str(n)) for n in range(80, 151, 5)], - "On-foot horizontal FOV at 16:9, in degrees.\nDefault keeps the authored view and scopes.", None), + "On foot, horizontal at 16:9, in degrees.\nDefault keeps the stock view.", None), ("VIEWMODEL FOV:", "display.viewmodel_fov", [("DEFAULT", "0")] + [(str(n), str(n)) for n in range(80, 151, 5)], - "Weapon and hands horizontal FOV at 16:9, in degrees.\nA number sets that FOV. Default keeps the original weapon view.", None), + "The weapon and hands, horizontal at 16:9.\nDefault keeps the weapon's stock view.", None), ("VIEWMODELS:", "display.viewmodel_visible", ON_OFF, "Draw first-person weapons, hands and attached\nvisuals. Gameplay and sound continue when off.", None), ],