mirror of
https://github.com/Cesarus85/ringfall-mjolnir-vr.git
synced 2026-10-29 04:01:10 +01:00
VR: recentre on entering play, limit the head's offset, log performance
- The view recentres (the head's position becomes the game camera's, its heading the player's facing) whenever play starts after menus, loading or a cinematic, and when the headset is recentred. - The head's offset from the body is limited (vr.lean_limit, vr.crouch_limit); vr.positional_tracking turns it off. - vr.log_performance logs the frame rate and the time spent drawing the eyes and waiting for the headset every five seconds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d331212729
commit
a7bc454563
2 files changed
+149
-3
No files matched your search
@@ -88,6 +88,18 @@ static const struct config_setting config_settings[] =
|
||||
{ "vr.refresh_rate", _config_real, "90.0", "HALO_VR_REFRESH_RATE", _environment_value, _platform_android,
|
||||
"The display refresh rate to ask the headset for, in Hz (72, 90, 120);\n"
|
||||
"it picks the nearest it has at or below. 0 for the highest." },
|
||||
{ "vr.positional_tracking", _config_boolean, "true", "HALO_VR_POSITIONAL", _environment_value, _platform_android,
|
||||
"Moving your head moves the view (leaning, ducking); false only turns it.\n"
|
||||
"The view recentres when play starts or resumes after a cinematic, and\n"
|
||||
"when you recentre the headset (hold the Meta button)." },
|
||||
{ "vr.lean_limit", _config_real, "0.35", "HALO_VR_LEAN_LIMIT", _environment_value, _platform_android,
|
||||
"How far, in metres, the view follows your head sideways or forwards\n"
|
||||
"away from the player's body (walls are not checked)." },
|
||||
{ "vr.crouch_limit", _config_real, "0.9", "HALO_VR_CROUCH_LIMIT", _environment_value, _platform_android,
|
||||
"How far, in metres, the view follows your head down." },
|
||||
{ "vr.log_performance", _config_boolean, "false", "HALO_VR_LOG_PERFORMANCE", _environment_value, _platform_android,
|
||||
"Log the frame rate and where the frame time goes every five seconds\n"
|
||||
"(adb logcat -s halo)." },
|
||||
{ "vr.smooth_turn", _config_boolean, "false", "HALO_VR_SMOOTH_TURN", _environment_value, _platform_android,
|
||||
"The right stick turns smoothly; false turns in steps (snap_turn_degrees)." },
|
||||
{ "vr.snap_turn_degrees", _config_real, "30.0", "HALO_VR_SNAP_TURN", _environment_value, _platform_android,
|
||||
|
||||
+137
-3
@@ -27,6 +27,7 @@ the game uses (halo_vr_aim) is the body's yaw plus the head's.
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#define METRES_PER_WORLD_UNIT 3.048f
|
||||
#define PI 3.14159265358979f
|
||||
@@ -44,6 +45,24 @@ static struct
|
||||
int overlay_mode; /* HALO_XR_OVERLAY_*, this frame */
|
||||
int overlay_blend;
|
||||
int last_overlay_mode;
|
||||
int last_frame_stereo; /* the last frame drew the eyes */
|
||||
|
||||
/* where in LOCAL space the game's camera is: the head's position when
|
||||
the view last recentred (vr_recentre) */
|
||||
float origin[3];
|
||||
int origin_valid;
|
||||
int recentre_frames; /* recentre on this many more frames */
|
||||
|
||||
/* vr.log_performance: sums over the frames since the last report */
|
||||
int log_performance;
|
||||
unsigned long long report_start_us;
|
||||
unsigned long long frame_start_us;
|
||||
unsigned long long eyes_start_us;
|
||||
unsigned long long eyes_us;
|
||||
unsigned long long work_us;
|
||||
unsigned long long wait_us;
|
||||
int report_frames;
|
||||
int report_stereo_frames;
|
||||
|
||||
float body_yaw;
|
||||
int aim_synced;
|
||||
@@ -60,6 +79,10 @@ static struct
|
||||
float hud_width;
|
||||
float screen_distance;
|
||||
float screen_width;
|
||||
int positional_tracking;
|
||||
float lean_limit;
|
||||
float crouch_limit;
|
||||
float rise_limit;
|
||||
} vr;
|
||||
|
||||
/* d3d8_gl.c */
|
||||
@@ -113,6 +136,49 @@ static void head_angles(float *yaw, float *pitch)
|
||||
*pitch = atan2f(direction[1], sqrtf(direction[0] * direction[0] + direction[2] * direction[2]));
|
||||
}
|
||||
|
||||
/* makes the head's position the game camera's, and the way it looks the
|
||||
game's facing (halo_vr_aim syncs the body's yaw again) */
|
||||
static void vr_recentre(void)
|
||||
{
|
||||
if (!(vr.frame.head_flags & HALO_XR_POSE_VALID))
|
||||
return;
|
||||
memcpy(vr.origin, vr.frame.head.position, sizeof(vr.origin));
|
||||
vr.origin_valid = 1;
|
||||
vr.aim_synced = 0;
|
||||
}
|
||||
|
||||
/* an eye's offset from the game's camera, in the game's axes (metres): the
|
||||
head's offset from the origin, kept within the lean and crouch limits so the
|
||||
view stays near the player's body, plus the eye's own offset from the head */
|
||||
static void head_offset(const struct halo_xr_view *view, float out[3])
|
||||
{
|
||||
float head[3] = { 0.0f, 0.0f, 0.0f }, local[3];
|
||||
int axis;
|
||||
|
||||
if (!vr.origin_valid)
|
||||
vr_recentre();
|
||||
if (vr.positional_tracking && vr.origin_valid)
|
||||
{
|
||||
float horizontal;
|
||||
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
head[axis] = vr.frame.head.position[axis] - vr.origin[axis];
|
||||
horizontal = sqrtf(head[0] * head[0] + head[2] * head[2]);
|
||||
if (horizontal > vr.lean_limit && horizontal > 0.0f)
|
||||
{
|
||||
head[0] *= vr.lean_limit / horizontal;
|
||||
head[2] *= vr.lean_limit / horizontal;
|
||||
}
|
||||
if (head[1] < -vr.crouch_limit)
|
||||
head[1] = -vr.crouch_limit;
|
||||
if (head[1] > vr.rise_limit)
|
||||
head[1] = vr.rise_limit;
|
||||
}
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
local[axis] = head[axis] + view->pose.position[axis] - vr.frame.head.position[axis];
|
||||
local_to_game(local, vr.body_yaw, out);
|
||||
}
|
||||
|
||||
/* ---------- platform side */
|
||||
|
||||
void halo_vr_initialize(void)
|
||||
@@ -127,6 +193,11 @@ void halo_vr_initialize(void)
|
||||
vr.hud_width = 2.0f * vr.hud_distance * tanf((float)config_real("vr.hud_field_of_view") * PI / 360.0f);
|
||||
vr.screen_distance = (float)config_real("vr.screen_distance");
|
||||
vr.screen_width = (float)config_real("vr.screen_width");
|
||||
vr.positional_tracking = config_boolean("vr.positional_tracking");
|
||||
vr.lean_limit = (float)config_real("vr.lean_limit");
|
||||
vr.crouch_limit = (float)config_real("vr.crouch_limit");
|
||||
vr.rise_limit = 0.25f;
|
||||
vr.log_performance = config_boolean("vr.log_performance");
|
||||
if (!host_xr_initialize(&vr.info, (float)config_real("vr.refresh_rate")))
|
||||
{
|
||||
platform_log("VR: the headset's session could not be created; see logcat");
|
||||
@@ -154,6 +225,41 @@ int halo_vr_eye_size(int *width, int *height)
|
||||
return *width > 0 && *height > 0;
|
||||
}
|
||||
|
||||
static unsigned long long now_us(void)
|
||||
{
|
||||
struct timespec now;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
return (unsigned long long)now.tv_sec * 1000000ULL + (unsigned long long)now.tv_nsec / 1000ULL;
|
||||
}
|
||||
|
||||
/* vr.log_performance: every five seconds, the frames the game gave the
|
||||
headset, and where their time went: waiting for the headset (xrWaitFrame),
|
||||
drawing the eyes, and the rest (the game's update, the overlay, the copies) */
|
||||
static void performance_report(int stereo)
|
||||
{
|
||||
unsigned long long now = now_us();
|
||||
|
||||
vr.work_us += now - vr.frame_start_us;
|
||||
vr.report_frames++;
|
||||
vr.report_stereo_frames += stereo != 0;
|
||||
if (!vr.report_start_us)
|
||||
vr.report_start_us = now;
|
||||
if (now - vr.report_start_us < 5000000)
|
||||
return;
|
||||
{
|
||||
float frames = (float)vr.report_frames;
|
||||
float seconds = (float)(now - vr.report_start_us) * 1e-6f;
|
||||
|
||||
platform_log("VR: %.1f fps (%d%% stereo); per frame %.1f ms working (eyes %.1f ms), %.1f ms waiting "
|
||||
"for the headset", frames / seconds, (int)(100.0f * vr.report_stereo_frames / frames),
|
||||
vr.work_us * 1e-3f / frames, vr.eyes_us * 1e-3f / frames, vr.wait_us * 1e-3f / frames);
|
||||
}
|
||||
vr.report_start_us = now;
|
||||
vr.report_frames = vr.report_stereo_frames = 0;
|
||||
vr.work_us = vr.eyes_us = vr.wait_us = 0;
|
||||
}
|
||||
|
||||
static void turn_update(void)
|
||||
{
|
||||
const struct halo_xr_hand *hand = &vr.frame.hands[HALO_XR_HAND_RIGHT];
|
||||
@@ -189,16 +295,30 @@ static void turn_update(void)
|
||||
|
||||
void halo_vr_frame_begin(void)
|
||||
{
|
||||
unsigned long long waited;
|
||||
|
||||
if (!vr.active || vr.frame_begun)
|
||||
return;
|
||||
waited = now_us();
|
||||
if (!host_xr_begin_frame(&vr.frame))
|
||||
memset(&vr.frame, 0, sizeof(vr.frame));
|
||||
vr.frame_start_us = now_us();
|
||||
vr.wait_us += vr.frame_start_us - waited;
|
||||
vr.frame_begun = 1;
|
||||
vr.eyes_captured = 0;
|
||||
vr.overlay_mode = HALO_XR_OVERLAY_SCREEN;
|
||||
vr.overlay_blend = 0;
|
||||
if (vr.frame.recentred)
|
||||
vr.aim_synced = 0;
|
||||
{
|
||||
/* the headset recentred (the Meta button held): its LOCAL space
|
||||
moves over the next few frames */
|
||||
vr.recentre_frames = 10;
|
||||
}
|
||||
if (vr.recentre_frames > 0)
|
||||
{
|
||||
vr.recentre_frames--;
|
||||
vr_recentre();
|
||||
}
|
||||
turn_update();
|
||||
}
|
||||
|
||||
@@ -234,7 +354,10 @@ void halo_vr_present(unsigned int back_buffer_texture)
|
||||
host_xr_copy_layer(HALO_XR_LAYER_OVERLAY, back_buffer_texture, !vr.overlay_blend);
|
||||
host_xr_end_frame(&submit);
|
||||
vr.last_overlay_mode = vr.overlay_mode;
|
||||
vr.last_frame_stereo = submit.eyes;
|
||||
vr.frame_begun = 0;
|
||||
if (vr.log_performance)
|
||||
performance_report(submit.eyes);
|
||||
}
|
||||
|
||||
/* ---------- the game's side */
|
||||
@@ -247,7 +370,13 @@ int halo_vr_stereo_frame(void)
|
||||
|
||||
int halo_vr_stereo_player(short local_player_index, int cinematic)
|
||||
{
|
||||
return halo_vr_stereo_frame() && local_player_index == 0 && (!cinematic || vr.cinematics_3d);
|
||||
int stereo = halo_vr_stereo_frame() && local_player_index == 0 && (!cinematic || vr.cinematics_3d);
|
||||
|
||||
/* from the menus, a loading screen or a cinematic into play: the game's
|
||||
camera is where the head is now, and the player faces where it looks */
|
||||
if (stereo && !vr.last_frame_stereo)
|
||||
vr_recentre();
|
||||
return stereo;
|
||||
}
|
||||
|
||||
void halo_vr_eye_camera(int eye, const float game_position[3],
|
||||
@@ -258,10 +387,13 @@ void halo_vr_eye_camera(int eye, const float game_position[3],
|
||||
static const float local_up[3] = { 0.0f, 1.0f, 0.0f };
|
||||
const struct halo_xr_view *view = &vr.frame.views[eye];
|
||||
float offset[3], direction[3];
|
||||
|
||||
if (eye == 0)
|
||||
vr.eyes_start_us = now_us();
|
||||
float half_tangent;
|
||||
int axis;
|
||||
|
||||
local_to_game(view->pose.position, vr.body_yaw, offset);
|
||||
head_offset(view, offset);
|
||||
for (axis = 0; axis < 3; axis++)
|
||||
position[axis] = game_position[axis] + offset[axis] / METRES_PER_WORLD_UNIT;
|
||||
quaternion_rotate(view->pose.orientation, local_forward, direction);
|
||||
@@ -287,6 +419,8 @@ void halo_vr_capture_eye(int eye)
|
||||
return;
|
||||
if (host_xr_copy_layer(HALO_XR_LAYER_EYE_LEFT + eye, d3d8_back_buffer_texture(), 1))
|
||||
vr.eyes_captured |= 1 << eye;
|
||||
if (eye == HALO_XR_EYE_COUNT - 1 && vr.eyes_start_us)
|
||||
vr.eyes_us += now_us() - vr.eyes_start_us;
|
||||
}
|
||||
|
||||
void halo_vr_begin_overlay(int menus_active)
|
||||
|
||||
Reference in new issue
Block a user