d/rift: Fix distortion and render parameters

This appears to be fully correct on DK2 now. There are still some fixes
to this code required for CV1 (which is slightly off-center, and there's
a bug in this code making that not work correctly),
but those will be upstreamed at a later point, since it isn't relevant
to DK2 specifically, and would be hard to cherry-pick.

Part-of: <https://gitlab.freedesktop.org/monado/monado/-/merge_requests/2428>
This commit is contained in:
Beyley Thomas authored and Beyley Cardellio committed 2025-06-16 16:47:18 -07:00
1 parent 0714e012eb
commit 1250f8a9ff
5 files changed
+677 -173

No files matched your search

+1
View File
@@ -244,6 +244,7 @@ if(XRT_BUILD_DRIVER_RIFT)
add_library(
drv_rift STATIC
rift/rift_hmd.c
rift/rift_distortion.c
rift/rift_prober.c
)
target_include_directories(drv_rift SYSTEM PRIVATE)
+498
View File
@@ -0,0 +1,498 @@
// Copyright 2025, Beyley Cardellio
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Handles calculating the distortion profile for Oculus Rift devices.
* @author Beyley Cardellio <ep1cm1n10n123@gmail.com>
* @ingroup drv_rift
*/
#include "rift_distortion.h"
#include "math/m_vec2.h"
#define HMD_TRACE(hmd, ...) U_LOG_XDEV_IFL_T(&hmd->base, hmd->log_level, __VA_ARGS__)
#define HMD_DEBUG(hmd, ...) U_LOG_XDEV_IFL_D(&hmd->base, hmd->log_level, __VA_ARGS__)
#define HMD_INFO(hmd, ...) U_LOG_XDEV_IFL_I(&hmd->base, hmd->log_level, __VA_ARGS__)
#define HMD_WARN(hmd, ...) U_LOG_XDEV_IFL_W(&hmd->base, hmd->log_level, __VA_ARGS__)
#define HMD_ERROR(hmd, ...) U_LOG_XDEV_IFL_E(&hmd->base, hmd->log_level, __VA_ARGS__)
static float
rift_catmull_rom_spline(struct rift_catmull_rom_distortion_data *catmull, float scaled_value)
{
float scaled_value_floor = floorf(scaled_value);
scaled_value_floor = CLAMP(scaled_value_floor, 0, CATMULL_COEFFICIENTS - 1);
float t = scaled_value - scaled_value_floor;
int k = (int)scaled_value_floor;
float p0, p1, m0, m1;
switch (k) {
case 0:
p0 = 1.0f;
m0 = (catmull->k[1] - catmull->k[0]);
p1 = catmull->k[1];
m1 = 0.5f * (catmull->k[2] - catmull->k[0]);
break;
default:
p0 = catmull->k[k];
m0 = 0.5f * (catmull->k[k + 1] - catmull->k[k - 1]);
p1 = catmull->k[k + 1];
m1 = 0.5f * (catmull->k[k + 2] - catmull->k[k]);
break;
case CATMULL_COEFFICIENTS - 2:
p0 = catmull->k[CATMULL_COEFFICIENTS - 2];
m0 = 0.5f * (catmull->k[CATMULL_COEFFICIENTS - 1] - catmull->k[CATMULL_COEFFICIENTS - 2]);
p1 = catmull->k[CATMULL_COEFFICIENTS - 1];
m1 = catmull->k[CATMULL_COEFFICIENTS - 1] - catmull->k[CATMULL_COEFFICIENTS - 2];
break;
case CATMULL_COEFFICIENTS - 1:
p0 = catmull->k[CATMULL_COEFFICIENTS - 1];
m0 = catmull->k[CATMULL_COEFFICIENTS - 1] - catmull->k[CATMULL_COEFFICIENTS - 2];
p1 = p0 + m0;
m1 = m0;
break;
}
float omt = 1.0f - t;
float res = (p0 * (1.0f + 2.0f * t) + m0 * t) * omt * omt + (p1 * (1.0f + 2.0f * omt) - m1 * omt) * t * t;
return res;
}
static float
rift_distortion_distance_scale_squared(struct rift_lens_distortion *lens_distortion, float distance_squared)
{
float scale = 1.0f;
switch (lens_distortion->distortion_version) {
case RIFT_LENS_DISTORTION_LCSV_CATMULL_ROM_10_VERSION_1: {
struct rift_catmull_rom_distortion_data data = lens_distortion->data.lcsv_catmull_rom_10;
float scaled_distance_squared =
(float)(CATMULL_COEFFICIENTS - 1) * distance_squared / (data.max_r * data.max_r);
return rift_catmull_rom_spline(&data, scaled_distance_squared);
}
default: return scale;
}
}
struct xrt_vec3
rift_distortion_distance_scale_squared_split_chroma(struct rift_lens_distortion *lens_distortion,
float distance_squared)
{
float scale = rift_distortion_distance_scale_squared(lens_distortion, distance_squared);
struct xrt_vec3 scale_split;
scale_split.x = scale;
scale_split.y = scale;
scale_split.z = scale;
switch (lens_distortion->distortion_version) {
case RIFT_LENS_DISTORTION_LCSV_CATMULL_ROM_10_VERSION_1: {
struct rift_catmull_rom_distortion_data data = lens_distortion->data.lcsv_catmull_rom_10;
scale_split.x *= 1.0f + data.chromatic_abberation[0] + distance_squared * data.chromatic_abberation[1];
scale_split.z *= 1.0f + data.chromatic_abberation[2] + distance_squared * data.chromatic_abberation[3];
break;
}
}
return scale_split;
}
struct rift_distortion_render_info
rift_get_distortion_render_info(struct rift_hmd *hmd, uint32_t view)
{
struct rift_lens_distortion *distortion = &hmd->lens_distortions[hmd->distortion_in_use];
float display_width_meters = MICROMETERS_TO_METERS(hmd->display_info.display_width);
float display_height_meters = MICROMETERS_TO_METERS(hmd->display_info.display_height);
float lens_separation_meters = MICROMETERS_TO_METERS(hmd->display_info.lens_separation);
float center_from_top_meters = MICROMETERS_TO_METERS(hmd->display_info.center_v);
struct xrt_vec2 pixels_per_meter;
pixels_per_meter.x =
(float)hmd->display_info.resolution_x / (display_width_meters - hmd->extra_display_info.screen_gap_meters);
pixels_per_meter.y = (float)hmd->display_info.resolution_y / display_height_meters;
struct xrt_vec2 pixels_per_tan_angle_at_center;
pixels_per_tan_angle_at_center.x =
pixels_per_meter.x * distortion->data.lcsv_catmull_rom_10.meters_per_tan_angle_at_center;
pixels_per_tan_angle_at_center.y =
pixels_per_meter.y * distortion->data.lcsv_catmull_rom_10.meters_per_tan_angle_at_center;
struct xrt_vec2 tan_eye_angle_scale;
tan_eye_angle_scale.x =
0.25f * (display_width_meters / distortion->data.lcsv_catmull_rom_10.meters_per_tan_angle_at_center);
tan_eye_angle_scale.y =
0.5f * (display_height_meters / distortion->data.lcsv_catmull_rom_10.meters_per_tan_angle_at_center);
float visible_width_of_one_eye = 0.5f * (display_width_meters - hmd->extra_display_info.screen_gap_meters);
float center_from_left_meters = (display_width_meters - lens_separation_meters) * 0.5f;
struct xrt_vec2 lens_center;
lens_center.x = (center_from_left_meters / visible_width_of_one_eye) * 2.0f - 1.0f;
lens_center.y = (center_from_top_meters / display_height_meters) * 2.0f - 1.0f;
// if we're on the right eye, flip the lens center
if (view != 0) {
lens_center.x *= -1;
}
return (struct rift_distortion_render_info){
.distortion = distortion,
.lens_center = lens_center,
.pixels_per_tan_angle_at_center = pixels_per_tan_angle_at_center,
.tan_eye_angle_scale = tan_eye_angle_scale,
};
}
static struct rift_viewport_fov_tan
rift_calculate_fov_from_eye_position(
float eye_relief, float offset_to_right, float offset_downwards, float lens_diameter, float extra_eye_rotation)
{
float half_lens_diameter = lens_diameter * 0.5f;
struct rift_viewport_fov_tan fov_port;
fov_port.up_tan = (half_lens_diameter + offset_downwards) / eye_relief;
fov_port.down_tan = (half_lens_diameter - offset_downwards) / eye_relief;
fov_port.left_tan = (half_lens_diameter + offset_to_right) / eye_relief;
fov_port.right_tan = (half_lens_diameter - offset_to_right) / eye_relief;
if (extra_eye_rotation > 0.0f) {
extra_eye_rotation = CLAMP(extra_eye_rotation, 0, 30.0f);
float eyeball_center_to_pupil = 0.0135f;
float eyeball_lateral_pull = 0.001f * (extra_eye_rotation / DEG_TO_RAD(30.0f));
float extra_translation = eyeball_center_to_pupil * sinf(extra_eye_rotation) + eyeball_lateral_pull;
float extra_relief = eyeball_center_to_pupil * (1.0f - cosf(extra_eye_rotation));
fov_port.up_tan = fmaxf(fov_port.up_tan, (half_lens_diameter + offset_downwards + extra_translation) /
(eye_relief + extra_relief));
fov_port.down_tan =
fmaxf(fov_port.down_tan,
(half_lens_diameter - offset_downwards + extra_translation) / (eye_relief + extra_relief));
fov_port.left_tan =
fmaxf(fov_port.left_tan,
(half_lens_diameter + offset_to_right + extra_translation) / (eye_relief + extra_relief));
fov_port.right_tan =
fmaxf(fov_port.right_tan,
(half_lens_diameter - offset_to_right + extra_translation) / (eye_relief + extra_relief));
}
return fov_port;
}
static struct xrt_vec2
rift_transform_screen_ndc_to_tan_fov_space(struct rift_distortion_render_info *distortion, struct xrt_vec2 screen_ndc)
{
struct xrt_vec2 tan_eye_angle_distorted = {
(screen_ndc.x - distortion->lens_center.x) * distortion->tan_eye_angle_scale.x,
(screen_ndc.y - distortion->lens_center.y) * distortion->tan_eye_angle_scale.y};
float distance_squared = (tan_eye_angle_distorted.x * tan_eye_angle_distorted.x) +
(tan_eye_angle_distorted.y * tan_eye_angle_distorted.y);
float distortion_scale = rift_distortion_distance_scale_squared(distortion->distortion, distance_squared);
return m_vec2_mul_scalar(tan_eye_angle_distorted, distortion_scale);
}
static struct rift_viewport_fov_tan
rift_fov_find_range(struct xrt_vec2 from,
struct xrt_vec2 to,
int num_steps,
struct rift_distortion_render_info *distortion)
{
struct rift_viewport_fov_tan fov_port = {0.0f, 0.0f, 0.0f, 0.0f};
float step_scale = 1.0f / (num_steps - 1);
for (int step = 0; step < num_steps; step++) {
float lerp_factor = step_scale * (float)step;
struct xrt_vec2 sample = m_vec2_add(from, m_vec2_mul_scalar(m_vec2_sub(to, from), lerp_factor));
struct xrt_vec2 tan_eye_angle = rift_transform_screen_ndc_to_tan_fov_space(distortion, sample);
fov_port.left_tan = fmaxf(fov_port.left_tan, -tan_eye_angle.x);
fov_port.right_tan = fmaxf(fov_port.right_tan, tan_eye_angle.x);
fov_port.up_tan = fmaxf(fov_port.up_tan, -tan_eye_angle.y);
fov_port.down_tan = fmaxf(fov_port.down_tan, tan_eye_angle.y);
}
return fov_port;
}
static struct rift_viewport_fov_tan
rift_get_physical_screen_fov(struct rift_distortion_render_info *distortion)
{
struct xrt_vec2 lens_center = distortion->lens_center;
struct rift_viewport_fov_tan left_fov_port =
rift_fov_find_range(lens_center, (struct xrt_vec2){-1.0f, lens_center.y}, 10, distortion);
struct rift_viewport_fov_tan right_fov_port =
rift_fov_find_range(lens_center, (struct xrt_vec2){1.0f, lens_center.y}, 10, distortion);
struct rift_viewport_fov_tan up_fov_port =
rift_fov_find_range(lens_center, (struct xrt_vec2){lens_center.x, -1.0f}, 10, distortion);
struct rift_viewport_fov_tan down_fov_port =
rift_fov_find_range(lens_center, (struct xrt_vec2){lens_center.x, 1.0f}, 10, distortion);
return (struct rift_viewport_fov_tan){.left_tan = left_fov_port.left_tan,
.right_tan = right_fov_port.right_tan,
.up_tan = up_fov_port.up_tan,
.down_tan = down_fov_port.down_tan};
}
static struct rift_viewport_fov_tan
rift_clamp_fov_to_physical_screen_fov(struct rift_distortion_render_info *distortion,
struct rift_viewport_fov_tan fov_port)
{
struct rift_viewport_fov_tan result_fov_port;
struct rift_viewport_fov_tan physical_fov_port = rift_get_physical_screen_fov(distortion);
result_fov_port.left_tan = fminf(fov_port.left_tan, physical_fov_port.left_tan);
result_fov_port.right_tan = fminf(fov_port.right_tan, physical_fov_port.right_tan);
result_fov_port.up_tan = fminf(fov_port.up_tan, physical_fov_port.up_tan);
result_fov_port.down_tan = fminf(fov_port.down_tan, physical_fov_port.down_tan);
return result_fov_port;
}
struct rift_viewport_fov_tan
rift_calculate_fov_from_hmd(struct rift_hmd *hmd, struct rift_distortion_render_info *distortion, uint32_t view)
{
float eye_relief = distortion->distortion->eye_relief;
struct rift_viewport_fov_tan fov_port;
fov_port = rift_calculate_fov_from_eye_position(eye_relief, 0, 0, hmd->extra_display_info.lens_diameter_meters,
DEFAULT_EXTRA_EYE_ROTATION);
fov_port = rift_clamp_fov_to_physical_screen_fov(distortion, fov_port);
return fov_port;
}
struct rift_scale_and_offset
rift_calculate_ndc_scale_and_offset_from_fov(struct rift_viewport_fov_tan *fov)
{
struct xrt_vec2 proj_scale = {.x = 2.0f / (fov->left_tan + fov->right_tan),
.y = 2.0f / (fov->up_tan + fov->down_tan)};
struct xrt_vec2 proj_offset = {.x = (fov->left_tan - fov->right_tan) * proj_scale.x * 0.5,
.y = (fov->up_tan - fov->down_tan) * proj_scale.y * 0.5f};
return (struct rift_scale_and_offset){.scale = proj_scale, .offset = proj_offset};
}
struct rift_scale_and_offset
rift_calculate_uv_scale_and_offset_from_ndc_scale_and_offset(struct rift_scale_and_offset eye_to_source_ndc)
{
struct rift_scale_and_offset result = eye_to_source_ndc;
result.scale = m_vec2_mul_scalar(result.scale, 0.5f);
result.offset = m_vec2_add_scalar(m_vec2_mul_scalar(result.offset, 0.5f), 0.5f);
return result;
}
static struct xrt_uv_triplet
rift_transform_screen_ndc_to_tan_fov_space_chroma(struct rift_distortion_render_info *distortion,
struct xrt_vec2 screen_ndc)
{
struct xrt_vec2 tan_eye_angle_distorted = {
(screen_ndc.x - distortion->lens_center.x) * distortion->tan_eye_angle_scale.x,
(screen_ndc.y - distortion->lens_center.y) * distortion->tan_eye_angle_scale.y};
float distance_squared = (tan_eye_angle_distorted.x * tan_eye_angle_distorted.x) +
(tan_eye_angle_distorted.y * tan_eye_angle_distorted.y);
struct xrt_vec3 distortion_scales =
rift_distortion_distance_scale_squared_split_chroma(distortion->distortion, distance_squared);
return (struct xrt_uv_triplet){
.r = {tan_eye_angle_distorted.x * distortion_scales.x, tan_eye_angle_distorted.y * distortion_scales.x},
.g = {tan_eye_angle_distorted.x * distortion_scales.y, tan_eye_angle_distorted.y * distortion_scales.y},
.b = {tan_eye_angle_distorted.x * distortion_scales.z, tan_eye_angle_distorted.y * distortion_scales.z},
};
}
// unused math functions which may be useful in the future for stuff like calculating FOV.
// disabled to not give unused warnings
#if 0
static float
rift_distortion(struct rift_lens_distortion *lens_distortion, float distance)
{
return distance * rift_distortion_distance_scale_squared(lens_distortion, distance * distance);
}
static float
rift_distortion_distance_inverse(struct rift_lens_distortion *lens_distortion, float distance)
{
assert(distance <= 20.0f);
float delta = distance * 0.25f;
float s = distance * 0.25f;
float d = fabsf(distance - rift_distortion(lens_distortion, s));
for (int i = 0; i < 20; i++) {
float s_up = s + delta;
float s_down = s - delta;
float d_up = fabsf(distance - rift_distortion(lens_distortion, s_up));
float d_down = fabsf(distance - rift_distortion(lens_distortion, s_down));
if (d_up < d) {
s = s_up;
d = d_up;
} else if (d_down < d) {
s = s_down;
d = d_down;
} else {
delta *= 0.5f;
}
}
return s;
}
static struct xrt_vec2
rift_transform_tan_fov_space_to_render_target_tex_uv(struct rift_scale_and_offset *eye_to_source_uv,
struct xrt_vec2 tan_eye_angle)
{
return m_vec2_add(m_vec2_mul(tan_eye_angle, eye_to_source_uv->scale), eye_to_source_uv->offset);
}
static struct xrt_vec2
rift_transform_render_target_ndc_to_tan_fov_space(struct rift_scale_and_offset *eye_to_source_ndc, struct xrt_vec2 ndc)
{
return m_vec2_div(m_vec2_sub(ndc, eye_to_source_ndc->offset), eye_to_source_ndc->scale);
}
static struct xrt_vec2
rift_transform_tan_fov_space_to_screen_ndc(struct rift_distortion_render_info *distortion,
struct xrt_vec2 tan_eye_angle)
{
float tan_eye_angle_radius = m_vec2_len(tan_eye_angle);
float tan_eye_angle_distorted_radius =
rift_distortion_distance_inverse(distortion->distortion, tan_eye_angle_radius);
struct xrt_vec2 tan_eye_angle_distorted = tan_eye_angle;
if (tan_eye_angle_radius > 0) {
tan_eye_angle_distorted =
m_vec2_mul_scalar(tan_eye_angle, tan_eye_angle_distorted_radius / tan_eye_angle_radius);
}
return m_vec2_add(m_vec2_div(tan_eye_angle_distorted, distortion->tan_eye_angle_scale),
distortion->lens_center);
}
#endif
bool
rift_hmd_compute_distortion(struct xrt_device *dev, uint32_t view, float u, float v, struct xrt_uv_triplet *out_result)
{
#define TO_NDC(x) ((x * 2) - 1)
struct rift_hmd *hmd = rift_hmd(dev);
struct xrt_vec2 source_ndc = {TO_NDC(u), TO_NDC(v)};
struct rift_distortion_render_info distortion_render_info = rift_get_distortion_render_info(hmd, 0);
struct rift_scale_and_offset *eye_to_source_uv = &hmd->extra_display_info.eye_to_source_uv;
struct xrt_uv_triplet tan_fov_chroma =
rift_transform_screen_ndc_to_tan_fov_space_chroma(&distortion_render_info, source_ndc);
#if 0 // no distortion
struct xrt_uv_triplet sample_tex_coord = {
.r = m_vec2_add(m_vec2_mul(tan_fov_chroma.g, eye_to_source_uv->scale), eye_to_source_uv->offset),
.g = m_vec2_add(m_vec2_mul(tan_fov_chroma.g, eye_to_source_uv->scale), eye_to_source_uv->offset),
.b = m_vec2_add(m_vec2_mul(tan_fov_chroma.g, eye_to_source_uv->scale), eye_to_source_uv->offset)};
#else
struct xrt_uv_triplet sample_tex_coord = {
.r = m_vec2_add(m_vec2_mul(tan_fov_chroma.r, eye_to_source_uv->scale), eye_to_source_uv->offset),
.g = m_vec2_add(m_vec2_mul(tan_fov_chroma.g, eye_to_source_uv->scale), eye_to_source_uv->offset),
.b = m_vec2_add(m_vec2_mul(tan_fov_chroma.b, eye_to_source_uv->scale), eye_to_source_uv->offset)};
#endif
*out_result = sample_tex_coord;
return true;
#undef TO_NDC
}
void
rift_fill_in_default_distortions(struct rift_hmd *hmd)
{
hmd->num_lens_distortions = 2;
hmd->lens_distortions = calloc(hmd->num_lens_distortions, sizeof(struct rift_lens_distortion));
uint16_t i = 0;
struct rift_catmull_rom_distortion_data distortion_data;
// TODO: dump these from the latest oculus runtime
// TODO: select the right distorions based on rift variant, right now this is hard-coded to the DK2
hmd->lens_distortions[i].distortion_version = RIFT_LENS_DISTORTION_LCSV_CATMULL_ROM_10_VERSION_1;
hmd->lens_distortions[i].eye_relief = 0.008f;
distortion_data.meters_per_tan_angle_at_center = 0.036f;
distortion_data.max_r = 1.0f;
distortion_data.k[0] = 1.003f;
distortion_data.k[1] = 1.02f;
distortion_data.k[2] = 1.042f;
distortion_data.k[3] = 1.066f;
distortion_data.k[4] = 1.094f;
distortion_data.k[5] = 1.126f;
distortion_data.k[6] = 1.162f;
distortion_data.k[7] = 1.203f;
distortion_data.k[8] = 1.25f;
distortion_data.k[9] = 1.31f;
distortion_data.k[10] = 1.38f;
distortion_data.chromatic_abberation[0] = -0.0112f;
distortion_data.chromatic_abberation[1] = -0.015f;
distortion_data.chromatic_abberation[2] = 0.0187f;
distortion_data.chromatic_abberation[3] = 0.015f;
hmd->lens_distortions[i].data.lcsv_catmull_rom_10 = distortion_data;
i += 1;
hmd->lens_distortions[i].distortion_version = RIFT_LENS_DISTORTION_LCSV_CATMULL_ROM_10_VERSION_1;
hmd->lens_distortions[i].eye_relief = 0.018f;
distortion_data.meters_per_tan_angle_at_center = 0.036f;
distortion_data.max_r = 1.0f;
distortion_data.k[0] = 1.003f;
distortion_data.k[1] = 1.02f;
distortion_data.k[2] = 1.042f;
distortion_data.k[3] = 1.066f;
distortion_data.k[4] = 1.094f;
distortion_data.k[5] = 1.126f;
distortion_data.k[6] = 1.162f;
distortion_data.k[7] = 1.203f;
distortion_data.k[8] = 1.25f;
distortion_data.k[9] = 1.31f;
distortion_data.k[10] = 1.38f;
distortion_data.chromatic_abberation[0] = -0.015f;
distortion_data.chromatic_abberation[1] = -0.02f;
distortion_data.chromatic_abberation[2] = 0.025f;
distortion_data.chromatic_abberation[3] = 0.02f;
hmd->lens_distortions[i].data.lcsv_catmull_rom_10 = distortion_data;
i += 1;
// TODO: let the user specify which distortion is in use with an env var,
// and interpolate the distortions for the user's specific eye relief setting
hmd->distortion_in_use = 1;
}
+40
View File
@@ -0,0 +1,40 @@
// Copyright 2025, Beyley Cardellio
// SPDX-License-Identifier: BSL-1.0
/*!
* @file
* @brief Public functions for the Oculus Rift distortion correction.
* @author Beyley Cardellio <ep1cm1n10n123@gmail.com>
* @ingroup drv_rift
*/
#include "rift_interface.h"
struct rift_distortion_render_info
{
struct rift_lens_distortion *distortion;
struct xrt_vec2 lens_center;
struct xrt_vec2 tan_eye_angle_scale;
struct xrt_vec2 pixels_per_tan_angle_at_center;
};
struct rift_distortion_render_info
rift_get_distortion_render_info(struct rift_hmd *hmd, uint32_t view);
struct xrt_vec3
rift_distortion_distance_scale_squared_split_chroma(struct rift_lens_distortion *lens_distortion,
float distance_squared);
struct rift_viewport_fov_tan
rift_calculate_fov_from_hmd(struct rift_hmd *hmd, struct rift_distortion_render_info *distortion, uint32_t view);
struct rift_scale_and_offset
rift_calculate_ndc_scale_and_offset_from_fov(struct rift_viewport_fov_tan *fov);
struct rift_scale_and_offset
rift_calculate_uv_scale_and_offset_from_ndc_scale_and_offset(struct rift_scale_and_offset eye_to_source_ndc);
void
rift_fill_in_default_distortions(struct rift_hmd *hmd);
bool
rift_hmd_compute_distortion(struct xrt_device *dev, uint32_t view, float u, float v, struct xrt_uv_triplet *out_result);
+83 -169
View File
@@ -16,10 +16,13 @@
#include "os/os_time.h"
#include "xrt/xrt_defines.h"
#include "xrt/xrt_device.h"
#include "rift_interface.h"
#include "rift_distortion.h"
#include "math/m_relation_history.h"
#include "math/m_api.h"
#include "math/m_vec2.h"
#include "math/m_mathinclude.h" // IWYU pragma: keep
#include "util/u_debug.h"
@@ -42,13 +45,6 @@
*
*/
/// Casting helper function
static inline struct rift_hmd *
rift_hmd(struct xrt_device *xdev)
{
return (struct rift_hmd *)xdev;
}
DEBUG_GET_ONCE_LOG_OPTION(rift_log, "RIFT_LOG", U_LOGGING_WARN)
#define HMD_TRACE(hmd, ...) U_LOG_XDEV_IFL_T(&hmd->base, hmd->log_level, __VA_ARGS__)
@@ -96,8 +92,7 @@ rift_send_keepalive(struct rift_hmd *hmd)
struct dk2_report_keepalive_mux report = {0, IN_REPORT_DK2,
KEEPALIVE_INTERVAL_NS / 1000000}; // convert ns to ms
int result =
rift_send_report(hmd, FEATURE_REPORT_KEEPALIVE_MUX, &report, sizeof(report));
int result = rift_send_report(hmd, FEATURE_REPORT_KEEPALIVE_MUX, &report, sizeof(report));
if (result < 0) {
return result;
@@ -236,18 +231,15 @@ rift_hmd_get_view_poses(struct xrt_device *xdev,
struct xrt_fov *out_fovs,
struct xrt_pose *out_poses)
{
/*
* For HMDs you can call this function or directly set
* the `get_view_poses` function on the device to it.
*/
return u_device_get_view_poses( //
xdev, //
default_eye_relation, //
at_timestamp_ns, //
view_count, //
out_head_relation, //
out_fovs, //
out_poses); //
struct rift_hmd *hmd = rift_hmd(xdev);
return u_device_get_view_poses(xdev, //
&(struct xrt_vec3){hmd->extra_display_info.icd, 0.0f, 0.0f}, //
at_timestamp_ns, //
view_count, //
out_head_relation, //
out_fovs, //
out_poses);
}
static xrt_result_t
@@ -261,129 +253,6 @@ rift_hmd_get_visibility_mask(struct xrt_device *xdev,
return XRT_SUCCESS;
}
static float
rift_catmull_rom_spline(struct rift_catmull_rom_distortion_data *catmull, float scaled_value)
{
float scaled_value_floor = floorf(scaled_value);
scaled_value_floor = CLAMP(scaled_value_floor, 0, CATMULL_COEFFICIENTS - 1);
float t = scaled_value - scaled_value_floor;
int k = (int)scaled_value_floor;
float p0, p1, m0, m1;
switch (k) {
case 0:
p0 = 1.0f;
m0 = (catmull->k[1] - catmull->k[0]);
p1 = catmull->k[1];
m1 = 0.5f * (catmull->k[2] - catmull->k[0]);
break;
default:
p0 = catmull->k[k];
m0 = 0.5f * (catmull->k[k + 1] - catmull->k[k - 1]);
p1 = catmull->k[k + 1];
m1 = 0.5f * (catmull->k[k + 2] - catmull->k[k]);
break;
case CATMULL_COEFFICIENTS - 2:
p0 = catmull->k[CATMULL_COEFFICIENTS - 2];
m0 = 0.5f * (catmull->k[CATMULL_COEFFICIENTS - 1] - catmull->k[CATMULL_COEFFICIENTS - 2]);
p1 = catmull->k[CATMULL_COEFFICIENTS - 1];
m1 = catmull->k[CATMULL_COEFFICIENTS - 1] - catmull->k[CATMULL_COEFFICIENTS - 2];
break;
case CATMULL_COEFFICIENTS - 1:
p0 = catmull->k[CATMULL_COEFFICIENTS - 1];
m0 = catmull->k[CATMULL_COEFFICIENTS - 1] - catmull->k[CATMULL_COEFFICIENTS - 2];
p1 = p0 + m0;
m1 = m0;
break;
}
float omt = 1.0f - t;
float res = (p0 * (1.0f + 2.0f * t) + m0 * t) * omt * omt + (p1 * (1.0f + 2.0f * omt) - m1 * omt) * t * t;
return res;
}
static float
rift_distortion_distance_squared(struct rift_lens_distortion *lens_distortion, float distance_squared)
{
float scale = 1.0f;
switch (lens_distortion->distortion_version) {
case RIFT_LENS_DISTORTION_LCSV_CATMULL_ROM_10_VERSION_1: {
struct rift_catmull_rom_distortion_data data = lens_distortion->data.lcsv_catmull_rom_10;
float scaled_distance_squared =
(float)(CATMULL_COEFFICIENTS - 1) * distance_squared / (data.max_r * data.max_r);
return rift_catmull_rom_spline(&data, scaled_distance_squared);
}
default: return scale;
}
}
static struct xrt_vec3
rift_distortion_distance_squared_split_chroma(struct rift_lens_distortion *lens_distortion, float distance_squared)
{
float scale = rift_distortion_distance_squared(lens_distortion, distance_squared);
struct xrt_vec3 scale_split;
scale_split.x = scale;
scale_split.y = scale;
scale_split.z = scale;
switch (lens_distortion->distortion_version) {
case RIFT_LENS_DISTORTION_LCSV_CATMULL_ROM_10_VERSION_1: {
struct rift_catmull_rom_distortion_data data = lens_distortion->data.lcsv_catmull_rom_10;
scale_split.x *= 1.0f + data.chromatic_abberation[0] + distance_squared * data.chromatic_abberation[1];
scale_split.z *= 1.0f + data.chromatic_abberation[2] + distance_squared * data.chromatic_abberation[3];
break;
}
}
return scale_split;
}
static bool
rift_hmd_compute_distortion(struct xrt_device *dev, uint32_t view, float u, float v, struct xrt_uv_triplet *out_result)
{
#define ZERO_ONE_TO_N_ONE_ONE(x) ((x * 2) - 1)
#define N_ONE_ONE_TO_ZERO_ONE(x) ((x + 1) / 2)
struct rift_hmd *hmd = rift_hmd(dev);
struct rift_lens_distortion *distortion = &hmd->lens_distortions[0];
float display_width_meters = (float)hmd->display_info.display_width / 1000000.0f;
float display_height_meters = (float)hmd->display_info.display_height / 1000000.0f;
float tan_eye_angle_scale_x =
display_width_meters / distortion->data.lcsv_catmull_rom_10.meters_per_tan_angle_at_center * 0.25f;
float tan_eye_angle_scale_y =
display_height_meters / distortion->data.lcsv_catmull_rom_10.meters_per_tan_angle_at_center * 0.5f;
u = ZERO_ONE_TO_N_ONE_ONE(u) * tan_eye_angle_scale_x;
v = ZERO_ONE_TO_N_ONE_ONE(v) * tan_eye_angle_scale_y;
float distance_squared = fabsf(u * u + v * v);
struct xrt_vec3 chroma_distortions =
rift_distortion_distance_squared_split_chroma(distortion, distance_squared);
out_result->r = (struct xrt_vec2){N_ONE_ONE_TO_ZERO_ONE(u * chroma_distortions.x),
N_ONE_ONE_TO_ZERO_ONE(v * chroma_distortions.x)};
out_result->g = (struct xrt_vec2){N_ONE_ONE_TO_ZERO_ONE(u * chroma_distortions.y),
N_ONE_ONE_TO_ZERO_ONE(v * chroma_distortions.y)};
out_result->b = (struct xrt_vec2){N_ONE_ONE_TO_ZERO_ONE(u * chroma_distortions.z),
N_ONE_ONE_TO_ZERO_ONE(v * chroma_distortions.z)};
return true;
#undef ZERO_ONE_TO_N_ONE_ONE
#undef N_ONE_ONE_TO_ZERO_ONE
}
static float
rift_decode_fixed_point_uint16(uint16_t value, uint16_t zero_value, int fractional_bits)
{
@@ -403,6 +272,8 @@ rift_parse_distortion_report(struct rift_lens_distortion_report *report, struct
struct rift_catmull_rom_distortion_report_data report_data = report->data.lcsv_catmull_rom_10;
struct rift_catmull_rom_distortion_data data;
out->eye_relief = MICROMETERS_TO_METERS(report_data.eye_relief);
for (uint16_t i = 0; i < CATMULL_COEFFICIENTS; i += 1) {
data.k[i] = rift_decode_fixed_point_uint16(report_data.k[i], 0, 14);
}
@@ -614,30 +485,56 @@ rift_hmd_create(struct os_hid_device *dev, enum rift_variant variant, char *devi
}
HMD_INFO(hmd, "After writing, HMD has config flags: %X", hmd->config.config_flags);
// get the lens distortions
struct rift_lens_distortion_report lens_distortion;
result = rift_get_lens_distortion(hmd, &lens_distortion);
if (result < 0) {
HMD_ERROR(hmd, "Failed to get lens distortion, reason %d", result);
goto error;
}
hmd->num_lens_distortions = lens_distortion.num_distortions;
hmd->lens_distortions = calloc(lens_distortion.num_distortions, sizeof(struct rift_lens_distortion));
rift_parse_distortion_report(&lens_distortion, &hmd->lens_distortions[lens_distortion.distortion_idx]);
// TODO: actually verify we initialize all the distortions. if the headset is working correctly, this should
// have happened, but you never know.
for (uint16_t i = 1; i < hmd->num_lens_distortions; i++) {
if (getenv("RIFT_USE_FIRMWARE_DISTORTION") != NULL) {
// get the lens distortions
struct rift_lens_distortion_report lens_distortion;
result = rift_get_lens_distortion(hmd, &lens_distortion);
if (result < 0) {
HMD_ERROR(hmd, "Failed to get lens distortion idx %d, reason %d", i, result);
HMD_ERROR(hmd, "Failed to get lens distortion, reason %d", result);
goto error;
}
hmd->num_lens_distortions = lens_distortion.num_distortions;
hmd->lens_distortions = calloc(lens_distortion.num_distortions, sizeof(struct rift_lens_distortion));
rift_parse_distortion_report(&lens_distortion, &hmd->lens_distortions[lens_distortion.distortion_idx]);
// TODO: actually verify we initialize all the distortions. if the headset is working correctly, this
// should have happened, but you never know.
for (uint16_t i = 1; i < hmd->num_lens_distortions; i++) {
result = rift_get_lens_distortion(hmd, &lens_distortion);
if (result < 0) {
HMD_ERROR(hmd, "Failed to get lens distortion idx %d, reason %d", i, result);
goto error;
}
rift_parse_distortion_report(&lens_distortion,
&hmd->lens_distortions[lens_distortion.distortion_idx]);
}
// TODO: pick the correct distortion for the eye relief setting the user has picked
hmd->distortion_in_use = 0;
} else {
rift_fill_in_default_distortions(hmd);
}
// fill in extra display info about the headset
switch (hmd->variant) {
case RIFT_VARIANT_DK2:
hmd->extra_display_info.screen_gap_meters = 0.0f;
hmd->extra_display_info.lens_diameter_meters = 0.04f;
break;
default: break;
}
// hardcode left eye, probably not ideal, but sure, why not
struct rift_distortion_render_info distortion_render_info = rift_get_distortion_render_info(hmd, 0);
hmd->extra_display_info.fov = rift_calculate_fov_from_hmd(hmd, &distortion_render_info, 0);
hmd->extra_display_info.eye_to_source_ndc =
rift_calculate_ndc_scale_and_offset_from_fov(&hmd->extra_display_info.fov);
hmd->extra_display_info.eye_to_source_uv =
rift_calculate_uv_scale_and_offset_from_ndc_scale_and_offset(hmd->extra_display_info.eye_to_source_ndc);
// This list should be ordered, most preferred first.
size_t idx = 0;
hmd->base.hmd->blend_modes[idx++] = XRT_BLEND_MODE_OPAQUE;
@@ -674,24 +571,40 @@ rift_hmd_create(struct os_hid_device *dev, enum rift_variant variant, char *devi
struct u_device_simple_info info;
info.display.w_pixels = hmd->display_info.resolution_x;
info.display.h_pixels = hmd->display_info.resolution_y;
info.display.w_meters = (float)hmd->display_info.display_width / 1000000.0f; // micrometers -> meters
info.display.h_meters = (float)hmd->display_info.display_height / 1000000.0f;
info.display.w_meters = MICROMETERS_TO_METERS(hmd->display_info.display_width);
info.display.h_meters = MICROMETERS_TO_METERS(hmd->display_info.display_height);
info.lens_horizontal_separation_meters = (float)hmd->display_info.lens_separation / 1000000.0f;
info.lens_horizontal_separation_meters = MICROMETERS_TO_METERS(hmd->display_info.lens_separation);
info.lens_vertical_position_meters = MICROMETERS_TO_METERS(hmd->display_info.center_v);
// TODO: this is per eye on the headset, but we're just taking the left eye for this, we should be using both
// eyes, ideally
info.lens_vertical_position_meters = (float)hmd->display_info.lens_distance_l / 1000000.0f;
hmd->extra_display_info.icd = info.lens_horizontal_separation_meters;
// TODO: calculate this
info.fov[0] = (float)(93.0 * (M_PI / 180.0));
info.fov[1] = (float)(99.0 * (M_PI / 180.0));
for (int i = 0; i < 2; i++) {
info.fov[i] = 93;
}
if (!u_device_setup_split_side_by_side(&hmd->base, &info)) {
HMD_ERROR(hmd, "Failed to setup basic device info");
goto error;
}
switch (hmd->variant) {
case RIFT_VARIANT_DK2:
// TODO: figure out how to calculate this programatically, right now this is hardcoded with data dumped
// from oculus' OpenXR runtime
hmd->base.hmd->distortion.fov[0].angle_up = 0.92667186;
hmd->base.hmd->distortion.fov[0].angle_down = -0.92667186;
hmd->base.hmd->distortion.fov[0].angle_left = -0.8138836;
hmd->base.hmd->distortion.fov[0].angle_right = 0.82951474;
hmd->base.hmd->distortion.fov[1].angle_up = 0.92667186;
hmd->base.hmd->distortion.fov[1].angle_down = -0.92667186;
hmd->base.hmd->distortion.fov[1].angle_left = -0.82951474;
hmd->base.hmd->distortion.fov[1].angle_right = 0.8138836;
break;
default: break;
}
// Just put an initial identity value in the tracker
struct xrt_space_relation identity = XRT_SPACE_RELATION_ZERO;
identity.relation_flags = (enum xrt_space_relation_flags)(XRT_SPACE_RELATION_ORIENTATION_TRACKED_BIT |
@@ -718,6 +631,7 @@ rift_hmd_create(struct os_hid_device *dev, enum rift_variant variant, char *devi
// Setup variable tracker: Optional but useful for debugging
u_var_add_root(hmd, "Rift HMD", true);
u_var_add_log_level(hmd, &hmd->log_level, "log_level");
m_imu_3dof_add_vars(&hmd->fusion, hmd, "3dof_");
return hmd;
error:
+55 -4
View File
@@ -20,6 +20,8 @@
#include "util/u_logging.h"
#include "math/m_imu_3dof.h"
#include "math/m_api.h"
#include "math/m_mathinclude.h"
#include <stdlib.h>
#include <stdio.h>
@@ -28,11 +30,19 @@
extern "C" {
#endif
#define REPORT_MAX_SIZE 69 // max size of a feature report (FEATURE_REPORT_CALIBRATE)
#define REPORT_MAX_SIZE 69 // max size of a feature report (FEATURE_REPORT_CALIBRATE)
#define KEEPALIVE_INTERVAL_NS 10000000000 // 10 seconds
#define KEEPALIVE_SEND_RATE_NS (KEEPALIVE_INTERVAL_NS * 19) / 20 // give a 5% breathing room (at 10 seconds, this is 500 milliseconds of breathing room)
#define KEEPALIVE_SEND_RATE_NS \
(KEEPALIVE_INTERVAL_NS * 19) / \
20 // give a 5% breathing room (at 10 seconds, this is 500 milliseconds of breathing room)
#define IMU_SAMPLE_RATE 1000
#define DEG_TO_RAD(DEG) (DEG * M_PI / 180.0)
#define MICROMETERS_TO_METERS(microns) (float)microns / 1000000.0f
// value taken from LibOVR 0.4.4
#define DEFAULT_EXTRA_EYE_ROTATION DEG_TO_RAD(30.0f)
enum rift_feature_reports
{
// DK1
@@ -148,8 +158,7 @@ struct rift_display_info_report
uint32_t center_v;
// the separation between the two lenses, in micrometers
uint32_t lens_separation;
uint32_t lens_distance_l;
uint32_t lens_distance_r;
uint32_t lens_distance[2];
float distortion[6];
} RIFT_PACKED;
@@ -239,12 +248,44 @@ struct rift_lens_distortion
{
// the version of the lens distortion data
uint16_t distortion_version;
// eye relief setting, in meters from surface of lens
float eye_relief;
union {
struct rift_catmull_rom_distortion_data lcsv_catmull_rom_10;
} RIFT_PACKED data;
};
struct rift_scale_and_offset
{
struct xrt_vec2 scale;
struct xrt_vec2 offset;
};
struct rift_viewport_fov_tan
{
float up_tan;
float down_tan;
float left_tan;
float right_tan;
};
struct rift_extra_display_info
{
// gap left between the two eyes
float screen_gap_meters;
// the diameter of the lenses, may need to be extended to an array
float lens_diameter_meters;
// ipd of the headset
float icd;
// the fov of the headset
struct rift_viewport_fov_tan fov;
// mapping from tan-angle space to target NDC space
struct rift_scale_and_offset eye_to_source_ndc;
struct rift_scale_and_offset eye_to_source_uv;
};
enum rift_variant
{
RIFT_VARIANT_DK1,
@@ -296,8 +337,18 @@ struct rift_hmd
struct rift_lens_distortion *lens_distortions;
uint16_t num_lens_distortions;
uint16_t distortion_in_use;
struct rift_extra_display_info extra_display_info;
};
/// Casting helper function
static inline struct rift_hmd *
rift_hmd(struct xrt_device *xdev)
{
return (struct rift_hmd *)xdev;
}
struct rift_hmd *
rift_hmd_create(struct os_hid_device *dev, enum rift_variant variant, char *device_name, char *serial_number);