Files
Cesarus85--mjolnir-vr/source/render/render.c
T
Stefan MaierandClaude Opus 5.5 ce59138a2b Play in VR on OpenXR headsets (Quest 3/3S, Steam Frame)
The Android port gets an OpenXR session in the host (Khronos loader, core
OpenXR plus XR_KHR_opengl_es_enable): on a headset the game keeps its
640x480 layout drawn at the eye images' resolution, renders the player's
view once per eye from the tracked eye poses through asymmetric frusta,
and shows the HUD and menus as a head-locked or room-fixed quad layer.
The head aims the player; the right stick turns; the Touch controllers
map onto the Xbox pad; rumble goes to the controllers. Frames without a
player view (menus, loading, cinematics) go onto a screen in the room.

A marker file, xr_test, runs a self-test of the session, layers, colour
path and controllers without game data.

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

817 lines
22 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 "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);
#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(&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:
#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(&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);
#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(&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();
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();
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;
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_overlay(window);
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;
halo_vr_begin_overlay(ui_widgets_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;
rasterizer_window_begin(&parameters);
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(&parameters, 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(&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);
}
#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 */