FOV and viewmodel settings: one unit, port marks, the lens flare cull (#234)

render_fov.c, viewmodel_fov.c and viewmodel_visibility.c are one unit,
port/linux/game/view_fov.c, with a header comment as the others have, real
for float, _pi for its own pi, the rasterizer's target by name, and no
isfinite shim in math.h (the angles' range test already refuses NaN). The
game code's hooks are marked "port:", their include at the end of each
block, and rasterizer_lights.c's loops declare before they act (C89).

The first-person weapon's lens flares are not culled by the world's
frustum (main's new off-screen cull): they are
drawn at the weapon's own projection, which display.viewmodel_fov can make
wider than the world's.

HALO_VIEWMODEL_VIS is HALO_VIEWMODEL_VISIBLE; the settings' descriptions and
help lines say "stock" view and fit their boxes; Video Setup's XML is
written again by ce_menus.py; the README's field of view text is a section
of its own.
This commit is contained in:
MrBruh committed 2026-10-10 16:24:04 +13:00
1 parent a8178af24a
commit b1a71709ef
23 files changed
+390 -331

No files matched your search

@@ -67,8 +67,8 @@
</strings>
<strings name="main_menu/settings_select/player_setup/player_profile_edit/video_settings/fov_viewmodels/help_strings">
<string text=""/>
<string text="On-foot horizontal FOV at 16:9, in degrees.\nDefault keeps the authored view and scopes."/>
<string text="Weapon and hands horizontal FOV at 16:9, in degrees.\nA number sets that FOV. Default keeps the original weapon view."/>
<string text="On foot, horizontal at 16:9, in degrees.\nDefault keeps the stock view."/>
<string text="The weapon and hands, horizontal at 16:9.\nDefault keeps the weapon's stock view."/>
<string text="Draw first-person weapons, hands and attached\nvisuals. Gameplay and sound continue when off."/>
</strings>
</menus>
@@ -155,7 +155,6 @@
<string text="Draw a frame for every display refresh, blending\nbetween the game's 30 ticks a second."/>
<string text="In first person, turn the view the moment the\nmouse moves, not up to two ticks later."/>
<string text="The HUD, text, anti-aliasing, shadows and\nlighting."/>
<string text="Optional field of view and first-person weapon\ndisplay settings. Defaults keep the original view."/>
<string text="The field of view and the first-person\nweapon. Their defaults keep the stock view."/>
</strings>
</menus>
+26 -18
View File
@@ -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
-136
View File
@@ -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 <math.h>
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];
}
-12
View File
@@ -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
+269
View File
@@ -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 <math.h>
/* (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();
}
+31
View File
@@ -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
-85
View File
@@ -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 <math.h>
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);
}
-6
View File
@@ -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
-23
View File
@@ -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();
}
-6
View File
@@ -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
-6
View File
@@ -11,12 +11,6 @@ strnlen() cannot collide with the game's own), which hides glibc's M_*.
#include_next <math.h>
/* 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
+9 -8
View File
@@ -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"
+2 -1
View File
@@ -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)
+6 -4
View File
@@ -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);
+4 -1
View File
@@ -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);
}
}
+2 -1
View File
@@ -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(
+24 -11
View File
@@ -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 <xtl.h>
#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_distance<parameters->definition->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, &parameters->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();
}
}
@@ -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);
}
}
@@ -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,
+2 -1
View File
@@ -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)) &&
+3 -1
View File
@@ -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;
+3 -3
View File
@@ -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),
],