Files
Madison 560233435f Draw both eyes in one world pass, each with its own camera (vr.multiview; headset-unverified)
The game side of single-pass stereo: with vr.multiview (on by default) a
stereo frame's two eye windows become one, drawn from between the eyes
with a frustum covering both (culling and visibility once for the pair),
and the windows after it move up. rasterizer_set_frustum_z then gives the
vertex programs each eye's camera: view 0's in c[-96..-89] as before,
view 1's through halo_vr_set_view1_vertex_constants, both with the call's
near and far. The pass draws into d3d8_gl.c's two-layer target
(halo_vr_multiview_begin/end, then halo_vr_resolve_multiview); if the
driver refuses it, that frame draws the left eye alone and the eyes are
drawn one at a time from then on. The hidden-area mask is left out of the
one pass for now. Non-VR builds keep rasterizer_set_frustum_z as it was.
2026-10-05 17:02:47 +09:00

742 lines
20 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"
#include "main/main.h"
#include "structures/structures.h"
#include "halo_vr.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 */
#ifdef HALO_VR
/* a screen flash that only darkens what is under it (the darken type, or a
lighten toward black, as a fade to black is); the types as
rasterizer_xbox_screen_effect.c numbers them: 1 lighten, 2 darken */
static boolean vr_screen_flash_darkens(
struct render_screen_flash const *flash)
{
switch (flash->type)
{
case 2:
return TRUE;
case 1:
return flash->color.red + flash->color.green + flash->color.blue < 0.3f;
default:
return FALSE;
}
}
#endif
/* ---------- 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);
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;
/* ---------- 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);
#ifdef HALO_VR
/* an eye's hidden area masked before anything is drawn (the window's
target, its depth too, is cleared by now) */
if (VR_RENDER_EYE() && !VR_RENDER_MULTIVIEW())
vr_draw_visibility_mask(vr_render_pass);
#endif
#ifdef HALO_VR
/* the headset's HUD pass: the HUD alone, on a transparent ground */
if (VR_RENDER_HUD())
{
halo_vr_clear_transparent();
interface_draw_screen();
/* a flash or fade is drawn over the whole of each eye; here only one
that darkens, so that a fade to black takes the HUD with it (its
alpha is its brightness): one that lightens would make the panel a
lit box */
if (vr_screen_flash_darkens(&parameters.screen_flash))
rasterizer_screen_flash();
halo_screen_ui_offset(TRUE);
render_ui_widgets(local_player_index, &rasterizer_camera->viewport_bounds);
halo_screen_ui_offset(FALSE);
rasterizer_window_end();
profile_render_window_end();
return;
}
#endif
if (!bink_playback_in_progress())
{
build_sprite_prepare_for_window();
render_sky();
#ifdef HALO_VR
/* posed once a frame, from the head between the eyes: per eye it
would sit at the same place in each and lose its depth */
if (!VR_RENDER_REPEAT())
{
struct render_camera eye_camera = render.camera;
vr_render_weapon_camera(&render.camera);
first_person_weapon_render_update();
render.camera = eye_camera;
}
#else
first_person_weapon_render_update();
#endif
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();
/* the fog's screen layers assume a symmetric frustum facing the
game's camera: not for the headset's eyes or scope */
if (!VR_RENDER_VIEW())
structure_render_fog_screen();
rasterizer_lens_flares_draw();
/* the headset shows the HUD on a layer of its own */
if (!VR_RENDER_VIEW())
{
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);
}
#ifdef HALO_VR
/* but a screen flash or fade (a white flash into a scene, a fade to
black) covers the whole view, as the game's own blend over each eye */
else if (VR_RENDER_EYE())
{
rasterizer_screen_flash();
}
#endif
}
bink_playback_render();
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);
#ifdef HALO_VR
/* the headset's HUD pass draws no world: getting the planar fog would
leave its animation offset waiting for a structure pass that never runs
(structure_render_set_fog_offset asserts it is taken once a window) */
if (!VR_RENDER_HUD())
#endif
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_VR
/* an eye's own, off-centre field; the scope's narrow one */
vr_render_frustum_bounds(&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);
/* (a mirror's camera assumes the whole screen, which the headset's
scope does not fill) */
if (main_get_window_count() == 1 && !VR_RENDER_SCOPE())
{
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;
}
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;
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;
}
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;
}
#ifdef HALO_VR
vr_render_window_begin(window_index);
#endif
render_player_frame(
window,
screenshot_index != NULL ? &screenshot_combined_index : NULL);
#ifdef HALO_VR
vr_render_window_end(window_index);
#endif
continue;
}
else
{
window_type = 1;
}
#ifdef HALO_VR
vr_render_window_begin(window_index);
#endif
render_nonplayer_frame(window, window_type);
#ifdef HALO_VR
vr_render_window_end(window_index);
#endif
}
halo_screen_ui_offset(TRUE);
progress_bar_eachframe();
halo_screen_ui_offset(FALSE);
rasterizer_windows_end();
rasterizer_frame_end();
return;
}
/* ---------- private code */