mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 07:00:30 +02:00
With [vr] eye_tracked_foveation (on by default on the Steam Frame, off elsewhere) the runtime asks for XR_EXT_eye_gaze_interaction. When the system reports an eye tracker, OpenXRInput binds the gaze pose and locates it for each packet's display time, in the space the eye views are located in; vr/eye_gaze.h turns it into tangents of each eye's own view, which AuroraStereoFrame now carries (appended, after the existing prefix). Aurora centres the eye's fragment density map on the gaze snapped to a cell of two map texels (about 3 degrees). Each eye keeps up to 32 maps, one per cell looked at, so a glance back reuses its map; a new map is bound once its upload completes, and until then the eye keeps the map it had. Without a tracked gaze (a blink, no tracker, the setting off) foveation centres on the forward direction exactly as before: the forward maps are byte-identical. Also logs every extension the OpenXR runtime offers at startup, so the first Steam Frame session shows what SteamVR's Android runtime has. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019HBRGKTE1GnN2ah8gcZKr3
76 lines
2.9 KiB
C++
76 lines
2.9 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
#include "vr/eye_gaze.h"
|
|
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
|
|
using mkw::vr::eye_gaze::InEye;
|
|
using mkw::vr::eye_gaze::Quaternion;
|
|
using mkw::vr::eye_gaze::Tangents;
|
|
|
|
namespace {
|
|
|
|
void Check(bool condition, const char* what) {
|
|
if (!condition) {
|
|
std::fprintf(stderr, "vr_eye_gaze_tests: %s\n", what);
|
|
std::exit(1);
|
|
}
|
|
}
|
|
|
|
void CheckNear(float value, float expected, const char* what) {
|
|
Check(std::fabs(value - expected) <= 1.0e-4f, what);
|
|
}
|
|
|
|
constexpr float kDegrees = 3.14159265358979f / 180.0f;
|
|
|
|
// A turn by `degrees` about the unit axis (x, y, z).
|
|
Quaternion Turn(float degrees, float x, float y, float z) {
|
|
const float half = 0.5f * degrees * kDegrees;
|
|
return {x * std::sin(half), y * std::sin(half), z * std::sin(half), std::cos(half)};
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
// Looking straight ahead through an eye looking straight ahead: its forward direction.
|
|
Tangents t = InEye({}, {});
|
|
Check(t.valid, "straight ahead is valid");
|
|
CheckNear(t.x, 0.0f, "straight ahead: x");
|
|
CheckNear(t.y, 0.0f, "straight ahead: y");
|
|
|
|
// A turn about +Y by a positive angle looks left (-X), about +X looks up (+Y).
|
|
t = InEye(Turn(20.0f, 0.0f, 1.0f, 0.0f), {});
|
|
Check(t.valid, "20 degrees left is valid");
|
|
CheckNear(t.x, -std::tan(20.0f * kDegrees), "20 degrees left: x");
|
|
CheckNear(t.y, 0.0f, "20 degrees left: y");
|
|
t = InEye(Turn(15.0f, 1.0f, 0.0f, 0.0f), {});
|
|
CheckNear(t.x, 0.0f, "15 degrees up: x");
|
|
CheckNear(t.y, std::tan(15.0f * kDegrees), "15 degrees up: y");
|
|
|
|
// An eye canted outwards sees the same gaze off its own centre.
|
|
t = InEye({}, Turn(10.0f, 0.0f, 1.0f, 0.0f));
|
|
CheckNear(t.x, std::tan(10.0f * kDegrees), "a left-canted eye sees straight ahead to its right");
|
|
t = InEye(Turn(10.0f, 0.0f, 1.0f, 0.0f), Turn(10.0f, 0.0f, 1.0f, 0.0f));
|
|
CheckNear(t.x, 0.0f, "gaze along the canted eye: x");
|
|
CheckNear(t.y, 0.0f, "gaze along the canted eye: y");
|
|
|
|
// The head's own turn cancels out: only the gaze relative to the eye counts.
|
|
const Quaternion head = Turn(70.0f, 0.0f, 1.0f, 0.0f);
|
|
const Quaternion look = Turn(70.0f + 12.0f, 0.0f, 1.0f, 0.0f);
|
|
t = InEye(look, head);
|
|
CheckNear(t.x, -std::tan(12.0f * kDegrees), "a turned head: x");
|
|
|
|
// Sideways or behind is no point of the image; an unnormalised quaternion still works.
|
|
Check(!InEye(Turn(85.0f, 0.0f, 1.0f, 0.0f), {}).valid, "85 degrees off is invalid");
|
|
Check(!InEye(Turn(180.0f, 0.0f, 1.0f, 0.0f), {}).valid, "behind is invalid");
|
|
Quaternion scaled = Turn(15.0f, 1.0f, 0.0f, 0.0f);
|
|
scaled = {scaled.x * 3.0f, scaled.y * 3.0f, scaled.z * 3.0f, scaled.w * 3.0f};
|
|
t = InEye(scaled, {});
|
|
CheckNear(t.y, std::tan(15.0f * kDegrees), "unnormalised quaternion");
|
|
Check(InEye({0.0f, 0.0f, 0.0f, 0.0f}, {}).valid, "a zero quaternion reads as identity");
|
|
|
|
std::printf("vr_eye_gaze_tests: all checks passed\n");
|
|
return 0;
|
|
}
|