/* 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); /* port/linux/game/vr_body.c: debug.vr_body, flat */ boolean vr_body_debug_camera(short local_player_index, struct render_camera *render_camera, struct render_camera *rasterizer_camera); #endif #include "main/main.h" #include "structures/structures.h" /* port: static transparent part links for the newly loaded model tags. */ #include "models/models.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); static boolean render_vr_body_debug_frame( 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) { /* port: preserve retail's energy-then-two-sided-glass compositing. */ models_fix_transparent_part_links(); 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_indexclusters.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_indexcluster_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 /* debug.vr_body (flat, vr_body.c): the player's own body as VR draws it, the camera moved to look at it; FALSE when the view is the player's own */ static boolean render_vr_body_debug_frame( struct render_window *window) { struct render_window debug_window = *window; if (!vr_body_debug_camera(window->local_player_index, &debug_window.render_camera, &debug_window.rasterizer_camera)) { return FALSE; } render_player_frame(&debug_window, NULL); return TRUE; } 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 #ifdef HALO_ANDROID else if (window->local_player_index != NONE && screenshot_index == NULL && render_vr_body_debug_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 */