The open source OpenXR runtime
at main 160 lines 4.7 kB view raw
1// Copyright 2019, Collabora, Ltd. 2// SPDX-License-Identifier: BSL-1.0 3/*! 4 * @file 5 * @brief Functions related to field-of-view. 6 * @author Rylie Pavlik <rylie.pavlik@collabora.com> 7 * @ingroup aux_math 8 */ 9 10#include "math/m_mathinclude.h" 11#include "math/m_api.h" 12#include "util/u_debug.h" 13 14#include <math.h> 15#include <stdio.h> 16#include <assert.h> 17 18 19DEBUG_GET_ONCE_BOOL_OPTION(views, "MATH_DEBUG_VIEWS", false) 20 21/*! 22 * Perform some of the computations from 23 * "Computing Half-Fields-Of-View from Simpler Display Models", 24 * to solve for the half-angles for a triangle where we know the center and 25 * total angle but not the "distance". 26 * 27 * In the diagram below, the top angle is theta_total, the length of the bottom 28 * is w_total, and the distance between the vertical line and the left corner is 29 * w_1. 30 * out_theta_1 is the angle at the top of the left-most right triangle, 31 * out_theta_2 is the angle at the top of the right-most right triangle, 32 * and out_d is the length of that center vertical line, a logical "distance". 33 * 34 * Any outparams that are NULL will simply not be set. 35 * 36 * The triangle need not be symmetrical, despite how the diagram looks. 37 * 38 * ``` 39 * theta_total 40 * * 41 * theta_1 -> / | \ <- theta_2 42 * / | \ 43 * / |d \ 44 * / | \ 45 * ------------- 46 * [ w_1 ][ w_2 ] 47 * 48 * [ --- w --- ] 49 * ``` 50 * 51 * Distances are in arbitrary but consistent units. Angles are in radians. 52 * 53 * @return true if successful. 54 */ 55static bool 56math_solve_triangle( 57 double w_total, double w_1, double theta_total, double *out_theta_1, double *out_theta_2, double *out_d) 58{ 59 /* should have at least one out-variable */ 60 assert(out_theta_1 || out_theta_2 || out_d); 61 const double w_2 = w_total - w_1; 62 63 const double u = w_2 / w_1; 64 const double v = tan(theta_total); 65 66 /* Parts of the quadratic formula solution */ 67 const double b = u + 1.0; 68 const double root = sqrt(b * b + 4 * u * v * v); 69 const double two_a = 2 * v; 70 71 /* The two possible solutions. */ 72 const double tan_theta_2_plus = (-b + root) / two_a; 73 const double tan_theta_2_minus = (-b - root) / two_a; 74 const double theta_2_plus = atan(tan_theta_2_plus); 75 const double theta_2_minus = atan(tan_theta_2_minus); 76 77 /* Pick the solution that is in the right range. */ 78 double tan_theta_2 = 0; 79 double theta_2 = 0; 80 if (theta_2_plus > 0.f && theta_2_plus < theta_total) { 81 // OH_DEBUG(ohd, "Using the + solution to the quadratic."); 82 tan_theta_2 = tan_theta_2_plus; 83 theta_2 = theta_2_plus; 84 } else if (theta_2_minus > 0.f && theta_2_minus < theta_total) { 85 // OH_DEBUG(ohd, "Using the - solution to the quadratic."); 86 tan_theta_2 = tan_theta_2_minus; 87 theta_2 = theta_2_minus; 88 } else { 89 // OH_ERROR(ohd, "NEITHER QUADRATIC SOLUTION APPLIES!"); 90 return false; 91 } 92#define METERS_FORMAT "%0.4fm" 93#define DEG_FORMAT "%0.1f deg" 94 if (debug_get_bool_option_views()) { 95 const double rad_to_deg = M_1_PI * 180.0; 96 // comments are to force wrapping 97 U_LOG_D("w=" METERS_FORMAT " theta=" DEG_FORMAT " w1=" METERS_FORMAT " theta1=" DEG_FORMAT 98 " w2=" METERS_FORMAT " theta2=" DEG_FORMAT " d=" METERS_FORMAT, 99 w_total, theta_total * rad_to_deg, // 100 w_1, (theta_total - theta_2) * rad_to_deg, // 101 w_2, theta_2 * rad_to_deg, // 102 w_2 / tan_theta_2); 103 } 104 if (out_theta_2) { 105 *out_theta_2 = theta_2; 106 } 107 108 if (out_theta_1) { 109 *out_theta_1 = theta_total - theta_2; 110 } 111 if (out_d) { 112 *out_d = w_2 / tan_theta_2; 113 } 114 return true; 115} 116 117bool 118math_compute_fovs(double w_total, 119 double w_1, 120 double horizfov_total, 121 double h_total, 122 double h_1, 123 double vertfov_total, 124 struct xrt_fov *fov) 125{ 126 double d = 0; 127 double theta_1 = 0; 128 double theta_2 = 0; 129 if (!math_solve_triangle(w_total, w_1, horizfov_total, &theta_1, &theta_2, &d)) { 130 /* failure is contagious */ 131 return false; 132 } 133 134 fov->angle_left = (float)-theta_1; 135 fov->angle_right = (float)theta_2; 136 137 double phi_1 = 0; 138 double phi_2 = 0; 139 if (vertfov_total == 0) { 140 phi_1 = atan(h_1 / d); 141 142 /* h_2 is "up". 143 * so the corresponding phi_2 is naturally positive. 144 */ 145 const double h_2 = h_total - h_1; 146 phi_2 = atan(h_2 / d); 147 } else { 148 /* Run the same algorithm again for vertical. */ 149 if (!math_solve_triangle(h_total, h_1, vertfov_total, &phi_1, &phi_2, NULL)) { 150 /* failure is contagious */ 151 return false; 152 } 153 } 154 155 /* phi_1 is "down" so we record this as negative. */ 156 fov->angle_down = (float)(-phi_1); 157 fov->angle_up = (float)phi_2; 158 159 return true; 160}