Files
Cesarus85--ringfall-mjolnir-vr/source/render/render.c
T
Stefan MaierandClaude Opus 5.5 ca73ba7909 VR: graphics presets (vr.graphics)
port/linux/game/vr_graphics.c (after HaloCE-Quest-VR's, CC0) sets the
game's own effect switches each frame before drawing: "high" (the
default) draws everything as before; "medium" no object shadows,
environment reflections or lens flares; "low" nor specular light, bump
maps, detail objects (grass), the screen's fog layer, contrails, weather
or camouflage's second pass; "max" is "high" with per-pixel model
lighting and 512x512 shadow maps (d3d8_gl.c asks halo_vr_graphics). A
switch the game changes (scripts, console) is taken as its wish and
combined with the preset's. A client in another's game keeps grass, lens
flares and the fog layer as the host's rules have them; cheats.c leaves
the environment parts the preset may keep off there (shadows, specular,
reflections).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 19:28:48 +02:00

1016 lines
30 KiB
C

/*
RENDER.C
symbols in this file:
001743B0 0020:
_render_effects (0000)
001743D0 0010:
_render_initialize (0000)
001743E0 0010:
_render_initialize_for_new_map (0000)
001743F0 0010:
_render_dispose_from_old_map (0000)
00174400 0010:
_render_dispose (0000)
00174410 0110:
_render_nonplayer_frame (0000)
00174520 00f0:
_render_frame_pregame (0000)
00174610 0020:
_render_frame_present (0000)
00174630 0070:
_render_location_visible (0000)
001746A0 0050:
_rendered_cluster_get (0000)
001746F0 03f0:
_render_window (0000)
00174AE0 03f0:
_render_player_frame (0000)
00174ED0 00f0:
_render_frame (0000)
0029F44C 001f:
??_C@_0BP@JNDFGHKA@c?3?2halo?2SOURCE?2render?2render?4c?$AA@ (0000)
0029F470 0061:
??_C@_0GB@NHJDHKGO@location?9?$DOcluster_index?$DO?$DN0?5?$CG?$CG?5lo@ (0000)
0029F4D8 0052:
??_C@_0FC@DLFICDJP@rendered_cluster_index?$DO?$DN0?5?$CG?$CG?5ren@ (0000)
0029F530 0052:
??_C@_0FC@NBMIPGBN@window?9?$DOrender_camera?4viewport_b@ (0000)
0029F588 0051:
??_C@_0FB@IDPOBHAJ@window?9?$DOrender_camera?4viewport_b@ (0000)
0029F5DC 002c:
??_C@_0CM@LKHLGDKF@window?9?$DOrender_camera?4viewport_b@ (0000)
0029F608 002c:
??_C@_0CM@HBCHLAAA@window?9?$DOrender_camera?4viewport_b@ (0000)
0029F638 006d:
??_C@_0GN@PKELCNNH@?$CBmemcmp?$CI?$CGwindow?9?$DOrender_camera?4w@ (0000)
0029F6A8 0071:
??_C@_0HB@BCLIBPGM@?$CBmemcmp?$CI?$CGwindow?9?$DOrender_camera?4v@ (0000)
0029F720 0048:
??_C@_0EI@NGOLLMDP@?$CD?$CD?$CD?5ERROR?5something?5is?5wrong?5wit@ (0000)
0030D4D2 0004:
_render_contrails_enabled (0000)
_render_particles_enabled (0001)
_render_particle_systems_enabled (0002)
_render_weather_particle_systems_enabled (0003)
004B8B22 0001:
_render_invalid_fog_warning_displayed (0000)
*/
/* ---------- headers */
#include "cseries.h"
#include "cseries/errors.h"
#include "render.h"
#include "render_cameras_internal.h"
#include "render_particles.h"
#include "objects.h"
#include "render_sprite.h"
#include "scenario.h"
#include "structure_bsp_definitions.h"
#include "effects/player_effects.h"
#include "structures/structure_visibility.h"
#include "rasterizer.h"
#include "rasterizer/rasterizer_lights.h"
#include "profile.h"
#include "progress_bar.h"
#include "ui_widget.h"
#include "bink_playback.h"
#include "game.h"
#include "game_engine.h"
#include "interface/first_person_weapons.h"
#include "interface/interface.h"
#include "editor_stubs.h"
#include "render_debug.h"
#include "objects/object_lights_rendering.h"
#include "effects/particle_systems.h"
#include "effects/weather_particle_systems.h"
#ifdef HALO_ANDROID
#include "cutscene/cinematics.h"
#include "game/players.h"
#include "interface/hud.h"
#include "physics/collisions.h"
#include "units/units.h"
#include "halo_vr.h"
/* port/linux/game/vr_graphics.c */
void vr_graphics_update(void);
#endif
#include "main/main.h"
#include "structures/structures.h"
/* ---------- constants */
enum
{
_render_target_primary = 0,
_render_target_secondary,
};
enum
{
_decal_layer_primary = 0,
_decal_layer_secondary,
_decal_layer_light,
_decal_layer_alpha_tested,
_decal_layer_water,
};
enum
{
_render_planar_fog_mode_fully_fogged = 2,
};
/* ---------- macros */
#define RASTERIZER_TARGET_RENDER_PRIMARY_WIDTH halo_screen_width()
#define RASTERIZER_TARGET_RENDER_PRIMARY_HEIGHT 480
/* ---------- structures */
/* ---------- prototypes */
static void render_nonplayer_frame(
const struct render_window *window,
long window_type);
static void render_window(
short local_player_index,
const struct render_camera *camera,
const struct render_frustum *frustum,
const struct render_camera *rasterizer_camera,
const struct render_frustum *rasterizer_frustum,
short rasterizer_target,
boolean has_mirror);
static void render_player_frame(
struct render_window *window,
const point2d *screenshot_combined_index);
#ifdef HALO_ANDROID
static void render_vr_player_frame(
struct render_window *window);
static void render_vr_overlay(
const struct render_window *window);
static void render_vr_laser(
short local_player_index);
static void render_vr_scope(
struct render_window *window);
#endif
void render_sky(
void);
/* ---------- globals */
struct render_globals render;
static boolean render_invalid_fog_warning_displayed;
extern short global_screenshot_count;
boolean render_contrails_enabled = TRUE;
boolean render_particles_enabled = TRUE;
boolean render_particle_systems_enabled = TRUE;
boolean render_weather_particle_systems_enabled = TRUE;
#ifdef HALO_ANDROID
/* VR (render_vr_player_frame): drawing an eye, which leaves out the HUD and
the menus, and that eye's frustum bounds */
static boolean render_vr_eye_pass = FALSE;
static const real_rectangle2d *render_vr_frustum_bounds = NULL;
#endif
/* ---------- public code */
void render_effects(
boolean enable)
{
render_weather_particle_systems_enabled = enable;
render_particle_systems_enabled = enable;
render_particles_enabled = enable;
render_contrails_enabled = enable;
}
void render_initialize(
void)
{
render_objects_initialize();
}
void render_initialize_for_new_map(
void)
{
render_objects_initialize_for_new_map();
}
void render_dispose_from_old_map(
void)
{
render_objects_dispose_from_old_map();
}
void render_dispose(
void)
{
render_objects_dispose();
}
static void render_nonplayer_frame(
const struct render_window *window,
long window_type)
{
struct rasterizer_window_begin_parameters parameters;
profile_render_window_start(FALSE);
memset(&parameters, 0, sizeof(parameters));
render.camera = window->render_camera;
render_camera_build_frustum(&render.camera, NULL, &render.frustum, TRUE);
parameters.camera = window->rasterizer_camera;
render_camera_build_frustum(&parameters.camera, NULL, &parameters.frustum, TRUE);
parameters.rasterizer_target = _render_target_primary;
parameters.suppress_clear = window_type == 0;
parameters.window_index = NONE;
rasterizer_window_begin(&parameters);
switch (window_type)
{
case 0:
/* letterbox bars and split screen dividers: laid out from the
viewport, so not centered like the menus on a wide screen */
interface_draw_fullscreen_overlays();
rasterizer_debug_draw();
break;
case 1:
halo_screen_ui_offset(TRUE);
game_engine_nonplayer_post_rasterize();
halo_screen_ui_offset(FALSE);
break;
default:
match_assert("c:\\halo\\SOURCE\\render\\render.c", 287, !"unreachable");
break;
}
rasterizer_window_end();
profile_render_window_end();
return;
}
void render_frame_pregame(
struct render_window const *window,
struct bitmap_data *bitmap)
{
struct rasterizer_frame_begin_parameters parameters;
struct rasterizer_window_begin_parameters rasterizer_parameters;
render.frame_index++;
rasterizer_frame_begin(&parameters);
rasterizer_windows_begin();
profile_render_window_start(FALSE);
memset(&rasterizer_parameters, 0, sizeof(rasterizer_parameters));
render.camera = window->render_camera;
render_camera_build_frustum(&render.camera, NULL, &render.frustum, TRUE);
rasterizer_parameters.camera = window->rasterizer_camera;
render_camera_build_frustum(&rasterizer_parameters.camera, NULL, &rasterizer_parameters.frustum, TRUE);
rasterizer_parameters.rasterizer_target = 0;
rasterizer_window_begin(&rasterizer_parameters);
halo_screen_ui_offset(TRUE);
render_ui_widgets(0, &window->rasterizer_camera.viewport_bounds);
halo_screen_ui_offset(FALSE);
bink_playback_render();
{
real progress;
if (game_map_loading_in_progress(&progress))
{
progress_bar_display(progress);
}
}
rasterizer_window_end();
profile_render_window_end();
rasterizer_windows_end();
rasterizer_frame_end();
return;
}
void render_frame_present(
const point2d *screenshot_index,
struct bitmap_data *bitmap)
{
rasterizer_present(bitmap, screenshot_index);
return;
}
boolean render_location_visible(
struct location *location)
{
match_assert("c:\\halo\\SOURCE\\render\\render.c", 584, location->cluster_index>=0 && location->cluster_index<global_structure_bsp_get()->clusters.count);
return TEST_FLAG(render.visible_cluster_flags[location->cluster_index>>5], location->cluster_index&31);
}
struct rendered_cluster *rendered_cluster_get(
short rendered_cluster_index)
{
match_assert("c:\\halo\\SOURCE\\render\\render.c", 592, rendered_cluster_index>=0 && rendered_cluster_index<render.rendered_cluster_count);
return &render.rendered_clusters[rendered_cluster_index];
}
static void render_window(
short local_player_index,
const struct render_camera *camera,
const struct render_frustum *frustum,
const struct render_camera *rasterizer_camera,
const struct render_frustum *rasterizer_frustum,
short rasterizer_target,
boolean has_mirror)
{
struct rasterizer_window_begin_parameters parameters;
short rendered_cluster_index;
profile_render_window_start(TRUE);
render.scene_index++;
memset(&parameters, 0, sizeof(parameters));
render.local_player_index = local_player_index;
render.camera = *camera;
render.frustum = *frustum;
parameters.camera = *rasterizer_camera;
parameters.frustum = *rasterizer_frustum;
parameters.rasterizer_target = rasterizer_target;
parameters.has_mirror = has_mirror;
parameters.window_index = render.window_index;
parameters.fog = render.fog;
structure_visibility_compute();
player_effect_get_screen_flash(local_player_index, &parameters.screen_flash);
rasterizer_window_begin(&parameters);
if (!bink_playback_in_progress())
{
build_sprite_prepare_for_window();
#ifdef HALO_ANDROID
halo_vr_multiview_sky(TRUE);
#endif
render_sky();
#ifdef HALO_ANDROID
halo_vr_multiview_sky(FALSE);
#endif
first_person_weapon_render_update();
lights_preprocess_scene();
render_objects();
structure_render_preprocess();
structure_render_lightmaps();
rasterizer_lens_flares_submit_occlusion_tests();
render_object_shadows();
lights_render_diffuse();
rasterizer_decals_begin(_decal_layer_light);
for (rendered_cluster_index = 0;
rendered_cluster_index < render.rendered_cluster_count;
rendered_cluster_index++)
{
rasterizer_decals_draw(rendered_cluster_get(rendered_cluster_index)->cluster_index);
}
rasterizer_decals_end();
rasterizer_decals_begin(_decal_layer_alpha_tested);
for (rendered_cluster_index = 0;
rendered_cluster_index < render.rendered_cluster_count;
rendered_cluster_index++)
{
rasterizer_decals_draw(rendered_cluster_get(rendered_cluster_index)->cluster_index);
}
rasterizer_decals_end();
structure_render_diffuse_texture();
rasterizer_decals_begin(_decal_layer_primary);
for (rendered_cluster_index = 0;
rendered_cluster_index < render.rendered_cluster_count;
rendered_cluster_index++)
{
rasterizer_decals_draw(rendered_cluster_get(rendered_cluster_index)->cluster_index);
}
rasterizer_decals_end();
rasterizer_decals_begin(_decal_layer_secondary);
for (rendered_cluster_index = 0;
rendered_cluster_index < render.rendered_cluster_count;
rendered_cluster_index++)
{
rasterizer_decals_draw(rendered_cluster_get(rendered_cluster_index)->cluster_index);
}
rasterizer_decals_end();
lights_render_specular();
structure_render_specular_lightmaps();
structure_render_reflection_lightmap_masks();
structure_render_reflection_mirrors();
structure_render_reflections();
structure_render_transparent_geometry();
structure_render_fog();
game_engine_post_rasterize_objects();
weather_particle_systems_render();
render_particles();
particle_systems_render();
render_contrails_normal();
rasterizer_transparent_geometry_draw(TRUE);
rasterizer_decals_begin(_decal_layer_water);
for (rendered_cluster_index = 0;
rendered_cluster_index < render.rendered_cluster_count;
rendered_cluster_index++)
{
rasterizer_decals_draw(rendered_cluster_get(rendered_cluster_index)->cluster_index);
}
rasterizer_decals_end();
structure_render_detail_objects();
rasterizer_transparent_geometry_draw(FALSE);
rasterizer_transparent_geometry_stop();
structure_render_fog_screen();
rasterizer_lens_flares_draw();
#ifdef HALO_ANDROID
if (render_vr_eye_pass)
{
/* the HUD and the menus go on the overlay (render_vr_overlay);
no antialiasing pass on the eyes' pictures */
rasterizer_screen_flash();
}
else
#endif
{
/* port: the 3D view antialiased (display.anti_aliasing), before the
HUD and menus are drawn over it */
if (rasterizer_target == _render_target_primary)
{
halo_screen_anti_alias(
rasterizer_camera->viewport_bounds.x0,
rasterizer_camera->viewport_bounds.y0,
rasterizer_camera->viewport_bounds.x1,
rasterizer_camera->viewport_bounds.y1);
}
interface_draw_screen();
rasterizer_screen_flash();
halo_screen_ui_offset(TRUE);
render_ui_widgets(local_player_index, &rasterizer_camera->viewport_bounds);
halo_screen_ui_offset(FALSE);
}
}
bink_playback_render();
#ifdef HALO_ANDROID
if (render_vr_eye_pass)
render_vr_laser(local_player_index);
#endif
render_camera_debug_frustum(&render.camera, &render.frustum);
render_debug();
editor_render();
rasterizer_debug_draw();
rasterizer_window_end();
profile_render_window_end();
return;
}
static void render_player_frame(
struct render_window *window,
const point2d *screenshot_combined_index)
{
struct render_camera *camera;
boolean has_mirror;
real_rectangle2d frustum_bounds;
struct render_frustum frustum;
struct render_frustum rasterizer_frustum;
struct render_mirror mirror;
camera = &window->render_camera;
has_mirror = FALSE;
structure_visibility_find_camera(camera);
render.fog.runtime_flags = 0;
scenario_get_atmospheric_fog(
window->local_player_index,
(word)render.visible_sky_index,
&camera->position,
&render.fog);
structure_get_planar_fog((short)render.cluster_index, &render.fog);
if (render.fog.atmospheric_maximum_distance != 0.0f &&
render.visible_sky_index == NONE &&
render.fog.planar_maximum_distance > render.fog.atmospheric_maximum_distance)
{
render.fog.planar_maximum_distance = render.fog.atmospheric_maximum_distance;
}
if (render.fog.atmospheric_maximum_density == 1.0f &&
render.fog.atmospheric_maximum_distance != 0.0f)
{
window->render_camera.z_far = MIN(
window->render_camera.z_far,
render.fog.atmospheric_maximum_distance);
}
if (render.fog.planar_mode == _render_planar_fog_mode_fully_fogged &&
render.fog.planar_maximum_distance != 0.0f)
{
window->render_camera.z_far = MIN(
window->render_camera.z_far,
render.fog.planar_maximum_distance);
}
if (window->render_camera.z_far <= window->render_camera.z_near)
{
if (!render_invalid_fog_warning_displayed)
{
error(2, "### ERROR something is wrong with the fog in the sky tag or the fog tag");
render_invalid_fog_warning_displayed = TRUE;
}
window->render_camera.z_far = window->render_camera.z_near + 0.01f;
}
match_assert(
"c:\\halo\\SOURCE\\render\\render.c",
187,
!memcmp(&window->render_camera.viewport_bounds,
&window->rasterizer_camera.viewport_bounds,
sizeof(rectangle2d)));
match_assert(
"c:\\halo\\SOURCE\\render\\render.c",
188,
!memcmp(&window->render_camera.window_bounds,
&window->rasterizer_camera.window_bounds,
sizeof(rectangle2d)));
render_camera_build_frustum_bounds(camera, &frustum_bounds);
#ifdef HALO_ANDROID
if (render_vr_frustum_bounds)
frustum_bounds = *render_vr_frustum_bounds;
#endif
if (screenshot_combined_index != NULL)
{
long tile_count;
tile_count = global_screenshot_count * global_screenshot_size;
if (tile_count > 0)
{
real tile_width;
real tile_height;
real_rectangle2d adjusted_bounds;
tile_width = (frustum_bounds.x1 - frustum_bounds.x0) / (real)tile_count;
tile_height = (frustum_bounds.y1 - frustum_bounds.y0) / (real)tile_count;
adjusted_bounds.x0 =
(real)screenshot_combined_index->x * tile_width + frustum_bounds.x0;
adjusted_bounds.y0 =
(real)(tile_count - screenshot_combined_index->y - 1) * tile_height + frustum_bounds.y0;
adjusted_bounds.x1 = adjusted_bounds.x0 + tile_width;
adjusted_bounds.y1 = adjusted_bounds.y0 + tile_height;
frustum_bounds = adjusted_bounds;
}
}
render_camera_build_frustum(camera, &frustum_bounds, &frustum, TRUE);
render_camera_build_frustum(
&window->rasterizer_camera,
&frustum_bounds,
&rasterizer_frustum,
TRUE);
if (main_get_window_count() == 1)
{
if (structure_visibility_find_mirror(camera, &frustum, &mirror))
{
short saved_cluster_index;
struct render_camera mirror_camera;
struct render_frustum mirror_frustum;
saved_cluster_index = (short)render.cluster_index;
match_assert("c:\\halo\\SOURCE\\render\\render.c", 225, window->render_camera.viewport_bounds.x0==0);
match_assert("c:\\halo\\SOURCE\\render\\render.c", 226, window->render_camera.viewport_bounds.y0==0);
match_assert("c:\\halo\\SOURCE\\render\\render.c", 227, window->render_camera.viewport_bounds.x1==RASTERIZER_TARGET_RENDER_PRIMARY_WIDTH);
match_assert("c:\\halo\\SOURCE\\render\\render.c", 228, window->render_camera.viewport_bounds.y1==RASTERIZER_TARGET_RENDER_PRIMARY_HEIGHT);
render_camera_mirror(camera, &mirror, &mirror_camera);
render_camera_build_frustum(
&mirror_camera,
&frustum_bounds,
&mirror_frustum,
TRUE);
rasterizer_profile_enable(FALSE);
render.cluster_index = mirror.cluster_index;
render_window(
NONE,
&mirror_camera,
&mirror_frustum,
&mirror_camera,
&mirror_frustum,
_render_target_secondary,
FALSE);
render.cluster_index = saved_cluster_index;
rasterizer_profile_enable(TRUE);
has_mirror = TRUE;
}
}
render_window(
window->local_player_index,
camera,
&frustum,
&window->rasterizer_camera,
&rasterizer_frustum,
_render_target_primary,
has_mirror);
return;
}
#ifdef HALO_ANDROID
static void render_vr_camera_set(
struct render_camera *camera,
const float position[3],
const float forward[3],
const float up[3],
real vertical_field_of_view)
{
camera->position.x = position[0];
camera->position.y = position[1];
camera->position.z = position[2];
camera->forward.i = forward[0];
camera->forward.j = forward[1];
camera->forward.k = forward[2];
camera->up.i = up[0];
camera->up.j = up[1];
camera->up.k = up[2];
camera->vertical_field_of_view = vertical_field_of_view;
}
/* A player's view in VR (port/linux/include/halo_vr.h): drawn once per eye
from the headset's eye poses, placed at the game's camera, each copied to
its eye image; then the HUD and the menus, over nothing, for the overlay
the headset shows in front of the eyes. */
static void render_vr_player_frame(
struct render_window *window)
{
float game_position[3];
short eye;
game_position[0] = window->render_camera.position.x;
game_position[1] = window->render_camera.position.y;
game_position[2] = window->render_camera.position.z;
{
/* seated, the vehicle's heading as this frame draws it (between
ticks): the view turns with it smoothly */
long player_index = local_player_get_player_index(window->local_player_index);
long unit_index = player_index == NONE ? NONE : player_get(player_index)->unit_index;
long vehicle_index = unit_index == NONE ? NONE : object_get(unit_index)->object.parent_object_index;
real_matrix4x3 *nodes = vehicle_index == NONE ? NULL : render_interpolation_object_node_matrices(vehicle_index);
real_point3d left_hand;
real_point3d right_hand;
if (nodes)
halo_vr_set_seat_frame(window->local_player_index, (float)atan2(nodes[0].forward.j, nodes[0].forward.i));
if (nodes && render_object_vr_wheel_hands(unit_index, &left_hand, &right_hand))
{
/* (at a wheel: the hands that hold it) */
float left[3] = { left_hand.x - game_position[0], left_hand.y - game_position[1],
left_hand.z - game_position[2] };
float right[3] = { right_hand.x - game_position[0], right_hand.y - game_position[1],
right_hand.z - game_position[2] };
halo_vr_set_wheel(window->local_player_index, left, right);
}
else
{
halo_vr_set_wheel(window->local_player_index, NULL, NULL);
}
}
if (halo_vr_multiview())
{
/* both eyes at once, from between them */
struct render_window eye_window = *window;
float position[3], forward[3], up[3], bounds[4];
float vertical_field_of_view;
real_rectangle2d frustum_bounds;
halo_vr_center_camera(game_position, position, forward, up, &vertical_field_of_view, bounds);
render_vr_camera_set(&eye_window.render_camera, position, forward, up, vertical_field_of_view);
render_vr_camera_set(&eye_window.rasterizer_camera, position, forward, up, vertical_field_of_view);
frustum_bounds.x0 = bounds[0];
frustum_bounds.x1 = bounds[1];
frustum_bounds.y0 = bounds[2];
frustum_bounds.y1 = bounds[3];
{
/* the depth of what the game projects itself (its sprites) */
struct render_frustum frustum;
render_camera_build_frustum(&eye_window.rasterizer_camera, &frustum_bounds, &frustum, TRUE);
halo_vr_multiview_projection(frustum.projection_matrix[2][2], frustum.projection_matrix[3][2]);
}
render_vr_eye_pass = TRUE;
render_vr_frustum_bounds = &frustum_bounds;
render_player_frame(&eye_window, NULL);
render_vr_frustum_bounds = NULL;
render_vr_eye_pass = FALSE;
halo_vr_capture_multiview();
}
else for (eye = 0; eye < 2; eye++)
{
struct render_window eye_window = *window;
float position[3], forward[3], up[3], bounds[4];
float vertical_field_of_view;
real_rectangle2d frustum_bounds;
halo_vr_eye_camera(eye, game_position, position, forward, up, &vertical_field_of_view, bounds);
render_vr_camera_set(&eye_window.render_camera, position, forward, up, vertical_field_of_view);
render_vr_camera_set(&eye_window.rasterizer_camera, position, forward, up, vertical_field_of_view);
frustum_bounds.x0 = bounds[0];
frustum_bounds.x1 = bounds[1];
frustum_bounds.y0 = bounds[2];
frustum_bounds.y1 = bounds[3];
render_vr_eye_pass = TRUE;
render_vr_frustum_bounds = &frustum_bounds;
render_player_frame(&eye_window, NULL);
render_vr_frustum_bounds = NULL;
render_vr_eye_pass = FALSE;
halo_vr_capture_eye(eye);
}
render_vr_scope(window);
render_vr_overlay(window);
return;
}
/* The scope's view (halo_vr_scope): zoomed in, the player's view once more,
from the weapon along its aim through the zoom's field of view, into a
small square at the back buffer's top left (a sixteenth of it: the disc
is a few centimetres across), which becomes the disc over the weapon. */
#define RENDER_VR_SCOPE_SIZE 120
static void render_vr_scope(
struct render_window *window)
{
short zoom_level = player_control_get_zoom_level(window->local_player_index);
long player_index = local_player_get_player_index(window->local_player_index);
float position[3], forward[3], up[3];
float vertical_field_of_view;
real magnification;
struct render_window scope_window;
if (zoom_level == NONE || player_index == NONE || player_get(player_index)->unit_index == NONE)
return;
magnification = unit_get_zoom_magnification(player_get(player_index)->unit_index, zoom_level);
{
/* (a weapon whose body stands higher over the hand than a rifle's
has its scope's disc higher: the rocket launcher's tube) */
struct unit_datum *unit = unit_get(player_get(player_index)->unit_index);
long weapon_index = unit->unit.current_weapon_index >= 0 ?
unit->unit.weapon_object_indices[unit->unit.current_weapon_index] : NONE;
char const *name = weapon_index != NONE ? tag_get_name(object_get(weapon_index)->definition_index) : NULL;
halo_vr_set_scope_raise(name && strstr(name, "rocket launcher") ? 0.18f : 0.0f);
}
if (!halo_vr_scope(window->local_player_index, magnification, position, forward, up, &vertical_field_of_view))
return;
scope_window = *window;
render_vr_camera_set(&scope_window.render_camera, position, forward, up, vertical_field_of_view);
render_vr_camera_set(&scope_window.rasterizer_camera, position, forward, up, vertical_field_of_view);
scope_window.render_camera.viewport_bounds.x0 = scope_window.render_camera.viewport_bounds.y0 = 0;
scope_window.render_camera.viewport_bounds.x1 = scope_window.render_camera.viewport_bounds.y1 = RENDER_VR_SCOPE_SIZE;
scope_window.render_camera.window_bounds = scope_window.render_camera.viewport_bounds;
scope_window.rasterizer_camera.viewport_bounds = scope_window.render_camera.viewport_bounds;
scope_window.rasterizer_camera.window_bounds = scope_window.render_camera.viewport_bounds;
render_vr_eye_pass = TRUE;
render_player_frame(&scope_window, NULL);
render_vr_eye_pass = FALSE;
halo_vr_capture_scope(0.f, 0.f, (float)RENDER_VR_SCOPE_SIZE / RASTERIZER_TARGET_RENDER_PRIMARY_WIDTH,
(float)RENDER_VR_SCOPE_SIZE / RASTERIZER_TARGET_RENDER_PRIMARY_HEIGHT);
return;
}
/* where a ray from the weapon, as far as the longest-ranged weapons reach,
hits something, or its end */
static void render_vr_ray_end(
float const start[3],
float const direction[3],
long unit_index,
real_point3d *origin,
real_point3d *end)
{
real_vector3d ray;
struct collision_result collision;
origin->x = start[0];
origin->y = start[1];
origin->z = start[2];
ray.i = direction[0] * 100.f;
ray.j = direction[1] * 100.f;
ray.k = direction[2] * 100.f;
if (collision_test_vector(_collision_test_for_projectiles_flags, origin, &ray, unit_index, &collision))
*end = collision.point;
else
point_from_line3d(origin, &ray, 1.f, end);
}
/* how far the weapon in the right hand aims before it hits something (the
crosshair floats there, halo_vr_set_aim_distance), and its laser up to
there, along the barrel as a reload turns it (halo_vr_laser), if vr.laser
is on, but not over the menus */
static void render_vr_laser(
short local_player_index)
{
static real_argb_color const laser_color = { 1.f, 1.f, 0.15f, 0.1f };
float start[3], direction[3];
real_point3d origin, end;
long player_index;
long unit_index = NONE;
player_index = local_player_get_player_index(local_player_index);
if (player_index != NONE)
unit_index = player_get(player_index)->unit_index;
if (!halo_vr_ready_laser(local_player_index, start, direction))
return;
/* seated, the aim passes through the player's own vehicle (a gun's
barrel and shield are in front of the hand) */
if (unit_index != NONE && object_get(unit_index)->object.parent_object_index != NONE)
unit_index = object_get(unit_index)->object.parent_object_index;
render_vr_ray_end(start, direction, unit_index, &origin, &end);
halo_vr_set_aim_distance(distance3d(&origin, &end));
if (!halo_vr_laser(local_player_index, start, direction) || ui_widgets_menus_active())
return;
render_vr_ray_end(start, direction, unit_index, &origin, &end);
render_debug_line(TRUE, &origin, &end, &laser_color);
render_debug_point(TRUE, &end, 0.02f, &laser_color);
return;
}
/* the HUD and the menus of a player's view, on the cleared back buffer */
static void render_vr_overlay(
const struct render_window *window)
{
struct rasterizer_window_begin_parameters parameters;
boolean menus_active = ui_widgets_menus_active();
halo_vr_begin_overlay(menus_active);
profile_render_window_start(FALSE);
memset(&parameters, 0, sizeof(parameters));
render.local_player_index = window->local_player_index;
render.camera = window->render_camera;
render_camera_build_frustum(&render.camera, NULL, &render.frustum, TRUE);
parameters.camera = window->rasterizer_camera;
render_camera_build_frustum(&parameters.camera, NULL, &parameters.frustum, TRUE);
parameters.rasterizer_target = _render_target_primary;
parameters.suppress_clear = TRUE;
parameters.window_index = render.window_index;
if (!menus_active && halo_vr_names_apart())
{
/* the players' names, each to be shown over its head */
rasterizer_window_begin(&parameters);
hud_draw_player_names_vr();
rasterizer_window_end();
halo_vr_capture_names();
}
if (!menus_active && halo_vr_crosshair_apart())
{
/* the weapon's crosshair alone, taken to where the weapon aims */
rasterizer_window_begin(&parameters);
hud_draw_crosshair();
rasterizer_window_end();
halo_vr_capture_crosshair();
}
rasterizer_window_begin(&parameters);
interface_draw_screen();
halo_screen_ui_offset(TRUE);
render_ui_widgets(window->local_player_index, &window->rasterizer_camera.viewport_bounds);
if (menus_active)
{
/* the controller's pointer on the menus' screen: a dot, ringed */
int pointer_x, pointer_y;
if (halo_vr_menu_pointer_at(&pointer_x, &pointer_y))
{
rectangle2d dot;
pointer_x -= (int)(halo_screen_width() - 640) / 2;
dot.x0 = (short)(pointer_x - 5);
dot.x1 = (short)(pointer_x + 5);
dot.y0 = (short)(pointer_y - 5);
dot.y1 = (short)(pointer_y + 5);
draw_quad(&dot, 0xE0102030);
dot.x0 += 2;
dot.x1 -= 2;
dot.y0 += 2;
dot.y1 -= 2;
draw_quad(&dot, 0xFF8FD3FF);
}
}
halo_screen_ui_offset(FALSE);
rasterizer_window_end();
profile_render_window_end();
return;
}
#endif
void render_frame(
struct render_window *windoze,
short window_count,
const point2d *screenshot_page_index,
const point2d *screenshot_index,
struct bitmap_data *screenshot_bitmap,
real time_delta_since_tick_sec)
{
struct rasterizer_frame_begin_parameters parameters;
short window_index;
struct render_window *window;
point2d screenshot_combined_index;
render.frame_index++;
render.time_delta_since_tick_sec = time_delta_since_tick_sec;
#ifdef HALO_ANDROID
/* VR: the graphics preset's effects (port/linux/game/vr_graphics.c) */
if (halo_vr_active())
vr_graphics_update();
#endif
memset(&parameters, 0, sizeof(parameters));
/* continuous between ticks (render_interpolation.c) */
parameters.game_time_sec = render_interpolation_game_time_sec(game_time_get());
rasterizer_frame_begin(&parameters);
rasterizer_windows_begin();
for (window_index = 0; window_index < window_count; window_index++)
{
long window_type;
window = &windoze[window_index];
render.window_index = window_index;
if (window->console_window)
{
window_type = 0;
}
#ifdef HALO_ANDROID
else if (window->local_player_index != NONE && screenshot_index == NULL &&
halo_vr_stereo_player(window->local_player_index, cinematic_in_progress()))
{
render_vr_player_frame(window);
continue;
}
#endif
else if (window->local_player_index != NONE)
{
if (screenshot_index != NULL && screenshot_page_index != NULL)
{
screenshot_combined_index.x =
screenshot_page_index->x * global_screenshot_size + screenshot_index->x;
screenshot_combined_index.y =
screenshot_page_index->y * global_screenshot_size + screenshot_index->y;
}
render_player_frame(
window,
screenshot_index != NULL ? &screenshot_combined_index : NULL);
continue;
}
else
{
window_type = 1;
}
render_nonplayer_frame(window, window_type);
}
halo_screen_ui_offset(TRUE);
progress_bar_eachframe();
halo_screen_ui_offset(FALSE);
rasterizer_windows_end();
rasterizer_frame_end();
return;
}
/* ---------- private code */