mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 01:00:14 +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
61 lines
2.2 KiB
C++
61 lines
2.2 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
#pragma once
|
|
|
|
#include <cmath>
|
|
|
|
// Eye-tracked foveation ([vr] eye_tracked_foveation): where the player looks, as each eye's image
|
|
// measures it. Kept free of OpenXR types so it can be checked headlessly (tests/vr_eye_gaze_tests.cpp).
|
|
//
|
|
// Conventions are OpenXR's: right-handed, +Y up, and a pose looks down its -Z axis.
|
|
namespace mkw::vr::eye_gaze {
|
|
|
|
struct Quaternion {
|
|
float x = 0.0f;
|
|
float y = 0.0f;
|
|
float z = 0.0f;
|
|
float w = 1.0f;
|
|
};
|
|
|
|
// Tangents of an eye's view, x right and y up, as its frustum (XrFovf) measures them.
|
|
struct Tangents {
|
|
float x = 0.0f;
|
|
float y = 0.0f;
|
|
bool valid = false;
|
|
};
|
|
|
|
// Beyond this angle from an eye's forward direction a gaze is no point of its image (cos 80 deg).
|
|
inline constexpr float kMinForwardCosine = 0.17364818f;
|
|
|
|
// The gaze pose's look direction in one eye's view, from both orientations in the same space. The
|
|
// eyes' views can be canted outwards, so each eye gets its own tangents.
|
|
inline Tangents InEye(Quaternion gaze, Quaternion eye) noexcept {
|
|
const auto normalized = [](Quaternion q) {
|
|
const float length = std::sqrt(q.x * q.x + q.y * q.y + q.z * q.z + q.w * q.w);
|
|
if (!(length > 1.0e-6f)) {
|
|
return Quaternion{};
|
|
}
|
|
return Quaternion{q.x / length, q.y / length, q.z / length, q.w / length};
|
|
};
|
|
// q * v * conjugate(q).
|
|
const auto rotate = [](const Quaternion& q, float vx, float vy, float vz, float out[3]) {
|
|
const float tx = 2.0f * (q.y * vz - q.z * vy);
|
|
const float ty = 2.0f * (q.z * vx - q.x * vz);
|
|
const float tz = 2.0f * (q.x * vy - q.y * vx);
|
|
out[0] = vx + q.w * tx + (q.y * tz - q.z * ty);
|
|
out[1] = vy + q.w * ty + (q.z * tx - q.x * tz);
|
|
out[2] = vz + q.w * tz + (q.x * ty - q.y * tx);
|
|
};
|
|
gaze = normalized(gaze);
|
|
eye = normalized(eye);
|
|
float look[3];
|
|
rotate(gaze, 0.0f, 0.0f, -1.0f, look);
|
|
float seen[3];
|
|
rotate(Quaternion{-eye.x, -eye.y, -eye.z, eye.w}, look[0], look[1], look[2], seen);
|
|
if (!(-seen[2] > kMinForwardCosine)) {
|
|
return {};
|
|
}
|
|
return {seen[0] / -seen[2], seen[1] / -seen[2], true};
|
|
}
|
|
|
|
} // namespace mkw::vr::eye_gaze
|