mirror of
https://github.com/mitch030504/AstroQuest-Frame.git
synced 2026-10-18 03:01:18 +02:00
115 lines
5.8 KiB
C++
115 lines
5.8 KiB
C++
#include <cmath>
|
|
#include <cstdio>
|
|
#include <limits>
|
|
|
|
#include "core/vr/spectator_view.h"
|
|
|
|
using namespace Core::Vr;
|
|
|
|
bool Check(bool value, const char* name) {
|
|
if (!value) {
|
|
std::fprintf(stderr, "FAIL: %s\n", name);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
bool Near(float a, float b) {
|
|
return std::abs(a - b) < 1e-5f;
|
|
}
|
|
|
|
bool CoversWidth(u32 width, const Fov& fov) {
|
|
const auto regions = CombinedEyeRegions(width, fov);
|
|
for (const auto& region : regions) {
|
|
if (region.x + region.width > width || region.clip_x + region.clip_width > width ||
|
|
(region.clip_width != 0 &&
|
|
(region.clip_x < region.x ||
|
|
region.clip_x + region.clip_width > region.x + region.width))) {
|
|
return false;
|
|
}
|
|
}
|
|
for (u32 pixel = 0; pixel < width; ++pixel) {
|
|
const bool primary =
|
|
pixel >= regions[0].clip_x && pixel < regions[0].clip_x + regions[0].clip_width;
|
|
const bool secondary =
|
|
pixel >= regions[1].clip_x && pixel < regions[1].clip_x + regions[1].clip_width;
|
|
if (primary == secondary) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
int main() {
|
|
bool ok = true;
|
|
ok &= Check(ParseDesktopView("") == DesktopView::Stereo &&
|
|
ParseDesktopView("unknown") == DesktopView::Stereo &&
|
|
ParseDesktopView("spectator") == DesktopView::Spectator &&
|
|
ParseDesktopView("combined") == DesktopView::Combined,
|
|
"desktop modes");
|
|
const Fov simple{2.0f, 1.0f, 1.5f, 1.0f};
|
|
const Fov reversed{1.0f, 2.0f, 1.5f, 1.0f};
|
|
const Fov symmetric{1.0f, 1.0f, 1.0f, 1.0f};
|
|
const Fov invalid{std::numeric_limits<float>::quiet_NaN(), 1.0f, 1.0f, 1.0f};
|
|
for (const float tangent :
|
|
{0.0f, -1.0f, 1e-30f, 101.0f, std::numeric_limits<float>::infinity()}) {
|
|
ok &= Check(!ValidSpectatorFov({1.0f, 1.0f, tangent, 1.0f}),
|
|
"reject unsafe projection bounds");
|
|
}
|
|
ok &= Check(Near(DesktopViewAspect(DesktopView::Stereo, simple, 0.9375f), 1.875f),
|
|
"stereo keeps pixel aspect");
|
|
ok &= Check(Near(DesktopViewAspect(DesktopView::Spectator, simple, 0.9375f), 0.9375f),
|
|
"default single eye stays unchanged");
|
|
ok &= Check(Near(DesktopViewAspect(DesktopView::Combined, simple, 0.9375f), 1.25f) &&
|
|
Near(DesktopViewAspect(DesktopView::Combined, reversed, 0.9375f), 1.25f),
|
|
"combined aspect preserves eye proportions with extra peripheral coverage");
|
|
ok &= Check(Near(DesktopViewAspect(DesktopView::Combined, symmetric, 0.9375f),
|
|
DesktopViewAspect(DesktopView::Spectator, symmetric, 0.9375f)),
|
|
"symmetric combined aspect equals single eye");
|
|
ok &= Check(Near(DesktopViewAspect(DesktopView::Combined, invalid, 0.9375f), 0.9375f),
|
|
"invalid FOV falls back to one eye");
|
|
for (const auto& fov : {simple, reversed, symmetric, invalid}) {
|
|
for (const u32 width : {0u, 1u, 2u, 3u, 101u, 1920u}) {
|
|
ok &= Check(CoversWidth(width, fov), "combined regions cover every pixel exactly once");
|
|
}
|
|
}
|
|
const auto layout = CombinedEyeRegions(100, simple);
|
|
ok &= Check(layout[0].x == 0 && layout[0].width == 75 && layout[1].x == 25 &&
|
|
layout[1].width == 75 && layout[1].clip_x == 75 && layout[1].clip_width == 25,
|
|
"combined keeps primary eye and right peripheral strip");
|
|
const auto mirror = CombinedEyeRegions(100, reversed);
|
|
ok &= Check(mirror[0].x == 25 && mirror[0].width == 75 && mirror[1].clip_x == 0 &&
|
|
mirror[1].clip_width == 25,
|
|
"reversed asymmetry keeps left peripheral strip");
|
|
const auto single_fit = SpectatorContentRect(1920, 1080, 0.9375f, false);
|
|
ok &= Check(single_fit.x == 454 && single_fit.y == 0 && single_fit.width == 1012 &&
|
|
single_fit.height == 1080,
|
|
"uncropped single eye keeps side bars");
|
|
const auto single_crop = SpectatorContentRect(1920, 1080, 0.9375f, true);
|
|
ok &= Check(single_crop.x == 0 && single_crop.y == -484 && single_crop.width == 1920 &&
|
|
single_crop.height == 2048,
|
|
"single eye fills width with centered vertical crop and no stretching");
|
|
for (const auto& fov : {simple, reversed, symmetric, invalid}) {
|
|
const float aspect = DesktopViewAspect(DesktopView::Combined, fov, 0.9375f);
|
|
const auto crop = SpectatorContentRect(1920, 1080, aspect, true);
|
|
ok &= Check(crop.x == 0 && crop.width == 1920 && crop.height >= 1080 && crop.y <= 0 &&
|
|
Near(static_cast<float>(crop.width) / crop.height, aspect) &&
|
|
CoversWidth(crop.width, fov),
|
|
"combined crop preserves proportions and peripheral coverage");
|
|
}
|
|
const auto combined_crop = SpectatorContentRect(1920, 1080, 1.25f, true);
|
|
const auto combined_layout = CombinedEyeRegions(combined_crop.width, simple);
|
|
ok &= Check(combined_crop.y == -228 && combined_crop.height == 1536 &&
|
|
Near(static_cast<float>(combined_layout[0].width) / combined_crop.height,
|
|
0.9375f),
|
|
"combined crop keeps each eye's original proportions");
|
|
const auto portrait = SpectatorContentRect(720, 1280, 1.25f, true);
|
|
ok &= Check(portrait.x == 0 && portrait.y == 352 && portrait.width == 720 &&
|
|
portrait.height == 576,
|
|
"portrait window preserves both sides");
|
|
const auto odd = SpectatorContentRect(1919, 1079, 0.9375f, true);
|
|
ok &= Check(odd.x == 0 && odd.width == 1919 && odd.height == 2046 && odd.y == -483,
|
|
"odd desktop size has centered crop within one pixel");
|
|
std::puts(ok ? "spectator view checks passed" : "spectator view checks failed");
|
|
return ok ? 0 : 1;
|
|
}
|