mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-17 18:00:25 +02:00
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>
1016 lines
30 KiB
C
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(¶meters, 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(¶meters.camera, NULL, ¶meters.frustum, TRUE);
|
|
|
|
parameters.rasterizer_target = _render_target_primary;
|
|
parameters.suppress_clear = window_type == 0;
|
|
parameters.window_index = NONE;
|
|
rasterizer_window_begin(¶meters);
|
|
|
|
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(¶meters);
|
|
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(¶meters, 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, ¶meters.screen_flash);
|
|
rasterizer_window_begin(¶meters);
|
|
|
|
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(¶meters, 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(¶meters.camera, NULL, ¶meters.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(¶meters);
|
|
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(¶meters);
|
|
hud_draw_crosshair();
|
|
rasterizer_window_end();
|
|
halo_vr_capture_crosshair();
|
|
}
|
|
rasterizer_window_begin(¶meters);
|
|
|
|
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(¶meters, 0, sizeof(parameters));
|
|
/* continuous between ticks (render_interpolation.c) */
|
|
parameters.game_time_sec = render_interpolation_game_time_sec(game_time_get());
|
|
rasterizer_frame_begin(¶meters);
|
|
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 */
|