mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-23 08:00:24 +02:00
Seated, the view stays behind the player's own eyes (vr.vehicle_first_person) and turns with the vehicle, smoothly between ticks (its interpolated heading); the head looks around freely, and recentring faces the vehicle's forward. A driver who steers by facing (jeep, boat, plane, scout, fighter) steers with the right stick: aside up to vr.vehicle_steer_degrees off the heading, up and down for pitch (vr.vehicle_pitch_degrees). Gunners, the tank's driver and passengers aim with the right controller, and turn in the seat with the stick. The crosshair and its distance follow the vehicle's aim. While the vehicle turns the player, the eye images darken from their edges (vr.comfort_vignette, host_xr_set_vignette). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
945 lines
27 KiB
C
945 lines
27 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 "render.h"
|
|
#include "render_cameras_internal.h"
|
|
#include "render_particles.h"
|
|
#include "objects.h"
|
|
#include "scenario.h"
|
|
#include "structure_bsp_definitions.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 "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 "game/player_control.h"
|
|
#include "interface/hud.h"
|
|
#include "physics/collisions.h"
|
|
#include "units/units.h"
|
|
#include "halo_vr.h"
|
|
#endif
|
|
|
|
/* ---------- 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 */
|
|
|
|
#ifdef HALO_LINUX
|
|
#define RASTERIZER_TARGET_RENDER_PRIMARY_WIDTH halo_screen_width()
|
|
#else
|
|
#define RASTERIZER_TARGET_RENDER_PRIMARY_WIDTH 640
|
|
#endif
|
|
#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 structure_get_planar_fog(
|
|
short cluster_index,
|
|
struct render_fog *fog);
|
|
void player_effect_get_screen_flash(
|
|
short local_player_index,
|
|
struct render_screen_flash *screen_flash);
|
|
void build_sprite_prepare_for_window(
|
|
void);
|
|
void render_sky(
|
|
void);
|
|
void render_objects(
|
|
void);
|
|
void render_object_shadows(
|
|
void);
|
|
void rasterizer_transparent_geometry_stop(
|
|
void);
|
|
void interface_draw_fullscreen_overlays(
|
|
void);
|
|
void rasterizer_debug_draw(
|
|
void);
|
|
void render_debug(
|
|
void);
|
|
void progress_bar_eachframe(
|
|
void);
|
|
short main_get_window_count(
|
|
void);
|
|
|
|
/* ---------- globals */
|
|
|
|
struct render_globals render;
|
|
|
|
static boolean render_invalid_fog_warning_displayed;
|
|
|
|
extern short global_screenshot_count;
|
|
extern short global_screenshot_size;
|
|
|
|
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:
|
|
#ifdef HALO_LINUX
|
|
halo_screen_ui_offset(TRUE);
|
|
game_engine_nonplayer_post_rasterize();
|
|
halo_screen_ui_offset(FALSE);
|
|
#else
|
|
game_engine_nonplayer_post_rasterize();
|
|
#endif
|
|
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);
|
|
|
|
#ifdef HALO_LINUX
|
|
halo_screen_ui_offset(TRUE);
|
|
render_ui_widgets(0, &window->rasterizer_camera.viewport_bounds);
|
|
halo_screen_ui_offset(FALSE);
|
|
#else
|
|
render_ui_widgets(0, &window->rasterizer_camera.viewport_bounds);
|
|
#endif
|
|
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();
|
|
render_sky();
|
|
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) */
|
|
rasterizer_screen_flash();
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
interface_draw_screen();
|
|
rasterizer_screen_flash();
|
|
#ifdef HALO_LINUX
|
|
halo_screen_ui_offset(TRUE);
|
|
render_ui_widgets(local_player_index, &rasterizer_camera->viewport_bounds);
|
|
halo_screen_ui_offset(FALSE);
|
|
#else
|
|
render_ui_widgets(local_player_index, &rasterizer_camera->viewport_bounds);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
if (nodes)
|
|
halo_vr_set_seat_frame(window->local_player_index, (float)atan2(nodes[0].forward.j, nodes[0].forward.i));
|
|
}
|
|
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);
|
|
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;
|
|
|
|
if (!halo_vr_ready_laser(local_player_index, start, direction))
|
|
return;
|
|
player_index = local_player_get_player_index(local_player_index);
|
|
if (player_index != NONE)
|
|
unit_index = player_get(player_index)->unit_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_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);
|
|
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;
|
|
memset(¶meters, 0, sizeof(parameters));
|
|
#ifdef HALO_LINUX
|
|
/* continuous between ticks (render_interpolation.c) */
|
|
parameters.game_time_sec = render_interpolation_game_time_sec(game_time_get());
|
|
#else
|
|
parameters.game_time_sec = (real)game_time_get() * (1.0f / TICKS_PER_SECOND);
|
|
#endif
|
|
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);
|
|
}
|
|
|
|
#ifdef HALO_LINUX
|
|
halo_screen_ui_offset(TRUE);
|
|
progress_bar_eachframe();
|
|
halo_screen_ui_offset(FALSE);
|
|
#else
|
|
progress_bar_eachframe();
|
|
#endif
|
|
rasterizer_windows_end();
|
|
rasterizer_frame_end();
|
|
|
|
return;
|
|
}
|
|
|
|
/* ---------- private code */
|