ovr_sdk

annotate LibOVR/Src/OVR_Stereo.cpp @ 0:1b39a1b46319

initial 0.4.4
author John Tsiombikas <nuclear@member.fsf.org>
date Wed, 14 Jan 2015 06:51:16 +0200
parents
children
rev   line source
nuclear@0 1 /************************************************************************************
nuclear@0 2
nuclear@0 3 Filename : OVR_Stereo.cpp
nuclear@0 4 Content : Stereo rendering functions
nuclear@0 5 Created : November 30, 2013
nuclear@0 6 Authors : Tom Fosyth
nuclear@0 7
nuclear@0 8 Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved.
nuclear@0 9
nuclear@0 10 Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License");
nuclear@0 11 you may not use the Oculus VR Rift SDK except in compliance with the License,
nuclear@0 12 which is provided at the time of installation or download, or which
nuclear@0 13 otherwise accompanies this software in either electronic or hard copy form.
nuclear@0 14
nuclear@0 15 You may obtain a copy of the License at
nuclear@0 16
nuclear@0 17 http://www.oculusvr.com/licenses/LICENSE-3.2
nuclear@0 18
nuclear@0 19 Unless required by applicable law or agreed to in writing, the Oculus VR SDK
nuclear@0 20 distributed under the License is distributed on an "AS IS" BASIS,
nuclear@0 21 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
nuclear@0 22 See the License for the specific language governing permissions and
nuclear@0 23 limitations under the License.
nuclear@0 24
nuclear@0 25 *************************************************************************************/
nuclear@0 26
nuclear@0 27 #include "OVR_Stereo.h"
nuclear@0 28 #include "OVR_Profile.h"
nuclear@0 29 #include "Kernel/OVR_Log.h"
nuclear@0 30 #include "Kernel/OVR_Alg.h"
nuclear@0 31
nuclear@0 32 //To allow custom distortion to be introduced to CatMulSpline.
nuclear@0 33 float (*CustomDistortion)(float) = NULL;
nuclear@0 34 float (*CustomDistortionInv)(float) = NULL;
nuclear@0 35
nuclear@0 36
nuclear@0 37 namespace OVR {
nuclear@0 38
nuclear@0 39
nuclear@0 40 using namespace Alg;
nuclear@0 41
nuclear@0 42 //-----------------------------------------------------------------------------------
nuclear@0 43
nuclear@0 44 // Inputs are 4 points (pFitX[0],pFitY[0]) through (pFitX[3],pFitY[3])
nuclear@0 45 // Result is four coefficients in pResults[0] through pResults[3] such that
nuclear@0 46 // y = pResult[0] + x * ( pResult[1] + x * ( pResult[2] + x * ( pResult[3] ) ) );
nuclear@0 47 // passes through all four input points.
nuclear@0 48 // Return is true if it succeeded, false if it failed (because two control points
nuclear@0 49 // have the same pFitX value).
nuclear@0 50 bool FitCubicPolynomial ( float *pResult, const float *pFitX, const float *pFitY )
nuclear@0 51 {
nuclear@0 52 float d0 = ( ( pFitX[0]-pFitX[1] ) * ( pFitX[0]-pFitX[2] ) * ( pFitX[0]-pFitX[3] ) );
nuclear@0 53 float d1 = ( ( pFitX[1]-pFitX[2] ) * ( pFitX[1]-pFitX[3] ) * ( pFitX[1]-pFitX[0] ) );
nuclear@0 54 float d2 = ( ( pFitX[2]-pFitX[3] ) * ( pFitX[2]-pFitX[0] ) * ( pFitX[2]-pFitX[1] ) );
nuclear@0 55 float d3 = ( ( pFitX[3]-pFitX[0] ) * ( pFitX[3]-pFitX[1] ) * ( pFitX[3]-pFitX[2] ) );
nuclear@0 56
nuclear@0 57 if ( ( d0 == 0.0f ) || ( d1 == 0.0f ) || ( d2 == 0.0f ) || ( d3 == 0.0f ) )
nuclear@0 58 {
nuclear@0 59 return false;
nuclear@0 60 }
nuclear@0 61
nuclear@0 62 float f0 = pFitY[0] / d0;
nuclear@0 63 float f1 = pFitY[1] / d1;
nuclear@0 64 float f2 = pFitY[2] / d2;
nuclear@0 65 float f3 = pFitY[3] / d3;
nuclear@0 66
nuclear@0 67 pResult[0] = -( f0*pFitX[1]*pFitX[2]*pFitX[3]
nuclear@0 68 + f1*pFitX[0]*pFitX[2]*pFitX[3]
nuclear@0 69 + f2*pFitX[0]*pFitX[1]*pFitX[3]
nuclear@0 70 + f3*pFitX[0]*pFitX[1]*pFitX[2] );
nuclear@0 71 pResult[1] = f0*(pFitX[1]*pFitX[2] + pFitX[2]*pFitX[3] + pFitX[3]*pFitX[1])
nuclear@0 72 + f1*(pFitX[0]*pFitX[2] + pFitX[2]*pFitX[3] + pFitX[3]*pFitX[0])
nuclear@0 73 + f2*(pFitX[0]*pFitX[1] + pFitX[1]*pFitX[3] + pFitX[3]*pFitX[0])
nuclear@0 74 + f3*(pFitX[0]*pFitX[1] + pFitX[1]*pFitX[2] + pFitX[2]*pFitX[0]);
nuclear@0 75 pResult[2] = -( f0*(pFitX[1]+pFitX[2]+pFitX[3])
nuclear@0 76 + f1*(pFitX[0]+pFitX[2]+pFitX[3])
nuclear@0 77 + f2*(pFitX[0]+pFitX[1]+pFitX[3])
nuclear@0 78 + f3*(pFitX[0]+pFitX[1]+pFitX[2]) );
nuclear@0 79 pResult[3] = f0 + f1 + f2 + f3;
nuclear@0 80
nuclear@0 81 return true;
nuclear@0 82 }
nuclear@0 83
nuclear@0 84 #define TPH_SPLINE_STATISTICS 0
nuclear@0 85 #if TPH_SPLINE_STATISTICS
nuclear@0 86 static float max_scaledVal = 0;
nuclear@0 87 static float average_total_out_of_range = 0;
nuclear@0 88 static float average_out_of_range;
nuclear@0 89 static int num_total = 0;
nuclear@0 90 static int num_out_of_range = 0;
nuclear@0 91 static int num_out_of_range_over_1 = 0;
nuclear@0 92 static int num_out_of_range_over_2 = 0;
nuclear@0 93 static int num_out_of_range_over_3 = 0;
nuclear@0 94 static float percent_out_of_range;
nuclear@0 95 #endif
nuclear@0 96
nuclear@0 97 float EvalCatmullRom10Spline ( float const *K, float scaledVal )
nuclear@0 98 {
nuclear@0 99 int const NumSegments = LensConfig::NumCoefficients;
nuclear@0 100
nuclear@0 101 #if TPH_SPLINE_STATISTICS
nuclear@0 102 //Value should be in range of 0 to (NumSegments-1) (typically 10) if spline is valid. Right?
nuclear@0 103 if (scaledVal > (NumSegments-1))
nuclear@0 104 {
nuclear@0 105 num_out_of_range++;
nuclear@0 106 average_total_out_of_range+=scaledVal;
nuclear@0 107 average_out_of_range = average_total_out_of_range / ((float) num_out_of_range);
nuclear@0 108 percent_out_of_range = 100.0f*(num_out_of_range)/num_total;
nuclear@0 109 }
nuclear@0 110 if (scaledVal > (NumSegments-1+1)) num_out_of_range_over_1++;
nuclear@0 111 if (scaledVal > (NumSegments-1+2)) num_out_of_range_over_2++;
nuclear@0 112 if (scaledVal > (NumSegments-1+3)) num_out_of_range_over_3++;
nuclear@0 113 num_total++;
nuclear@0 114 if (scaledVal > max_scaledVal)
nuclear@0 115 {
nuclear@0 116 max_scaledVal = scaledVal;
nuclear@0 117 max_scaledVal = scaledVal;
nuclear@0 118 }
nuclear@0 119 #endif
nuclear@0 120
nuclear@0 121 float scaledValFloor = floorf ( scaledVal );
nuclear@0 122 scaledValFloor = Alg::Max ( 0.0f, Alg::Min ( (float)(NumSegments-1), scaledValFloor ) );
nuclear@0 123 float t = scaledVal - scaledValFloor;
nuclear@0 124 int k = (int)scaledValFloor;
nuclear@0 125
nuclear@0 126 float p0, p1;
nuclear@0 127 float m0, m1;
nuclear@0 128 switch ( k )
nuclear@0 129 {
nuclear@0 130 case 0:
nuclear@0 131 // Curve starts at 1.0 with gradient K[1]-K[0]
nuclear@0 132 p0 = 1.0f;
nuclear@0 133 m0 = ( K[1] - K[0] ); // general case would have been (K[1]-K[-1])/2
nuclear@0 134 p1 = K[1];
nuclear@0 135 m1 = 0.5f * ( K[2] - K[0] );
nuclear@0 136 break;
nuclear@0 137 default:
nuclear@0 138 // General case
nuclear@0 139 p0 = K[k ];
nuclear@0 140 m0 = 0.5f * ( K[k+1] - K[k-1] );
nuclear@0 141 p1 = K[k+1];
nuclear@0 142 m1 = 0.5f * ( K[k+2] - K[k ] );
nuclear@0 143 break;
nuclear@0 144 case NumSegments-2:
nuclear@0 145 // Last tangent is just the slope of the last two points.
nuclear@0 146 p0 = K[NumSegments-2];
nuclear@0 147 m0 = 0.5f * ( K[NumSegments-1] - K[NumSegments-2] );
nuclear@0 148 p1 = K[NumSegments-1];
nuclear@0 149 m1 = K[NumSegments-1] - K[NumSegments-2];
nuclear@0 150 break;
nuclear@0 151 case NumSegments-1:
nuclear@0 152 // Beyond the last segment it's just a straight line
nuclear@0 153 p0 = K[NumSegments-1];
nuclear@0 154 m0 = K[NumSegments-1] - K[NumSegments-2];
nuclear@0 155 p1 = p0 + m0;
nuclear@0 156 m1 = m0;
nuclear@0 157 break;
nuclear@0 158 }
nuclear@0 159
nuclear@0 160 float omt = 1.0f - t;
nuclear@0 161 float res = ( p0 * ( 1.0f + 2.0f * t ) + m0 * t ) * omt * omt
nuclear@0 162 + ( p1 * ( 1.0f + 2.0f * omt ) - m1 * omt ) * t * t;
nuclear@0 163
nuclear@0 164 return res;
nuclear@0 165 }
nuclear@0 166
nuclear@0 167
nuclear@0 168
nuclear@0 169
nuclear@0 170 // Converts a Profile eyecup string into an eyecup enumeration
nuclear@0 171 void SetEyeCup(HmdRenderInfo* renderInfo, const char* cup)
nuclear@0 172 {
nuclear@0 173 if (OVR_strcmp(cup, "A") == 0)
nuclear@0 174 renderInfo->EyeCups = EyeCup_DK1A;
nuclear@0 175 else if (OVR_strcmp(cup, "B") == 0)
nuclear@0 176 renderInfo->EyeCups = EyeCup_DK1B;
nuclear@0 177 else if (OVR_strcmp(cup, "C") == 0)
nuclear@0 178 renderInfo->EyeCups = EyeCup_DK1C;
nuclear@0 179 else if (OVR_strcmp(cup, "Orange A") == 0)
nuclear@0 180 renderInfo->EyeCups = EyeCup_OrangeA;
nuclear@0 181 else if (OVR_strcmp(cup, "Red A") == 0)
nuclear@0 182 renderInfo->EyeCups = EyeCup_RedA;
nuclear@0 183 else if (OVR_strcmp(cup, "Pink A") == 0)
nuclear@0 184 renderInfo->EyeCups = EyeCup_PinkA;
nuclear@0 185 else if (OVR_strcmp(cup, "Blue A") == 0)
nuclear@0 186 renderInfo->EyeCups = EyeCup_BlueA;
nuclear@0 187 else
nuclear@0 188 renderInfo->EyeCups = EyeCup_DK1A;
nuclear@0 189 }
nuclear@0 190
nuclear@0 191
nuclear@0 192
nuclear@0 193 //-----------------------------------------------------------------------------------
nuclear@0 194
nuclear@0 195
nuclear@0 196 // The result is a scaling applied to the distance.
nuclear@0 197 float LensConfig::DistortionFnScaleRadiusSquared (float rsq) const
nuclear@0 198 {
nuclear@0 199 float scale = 1.0f;
nuclear@0 200 switch ( Eqn )
nuclear@0 201 {
nuclear@0 202 case Distortion_Poly4:
nuclear@0 203 // This version is deprecated! Prefer one of the other two.
nuclear@0 204 scale = ( K[0] + rsq * ( K[1] + rsq * ( K[2] + rsq * K[3] ) ) );
nuclear@0 205 break;
nuclear@0 206 case Distortion_RecipPoly4:
nuclear@0 207 scale = 1.0f / ( K[0] + rsq * ( K[1] + rsq * ( K[2] + rsq * K[3] ) ) );
nuclear@0 208 break;
nuclear@0 209 case Distortion_CatmullRom10:{
nuclear@0 210 // A Catmull-Rom spline through the values 1.0, K[1], K[2] ... K[10]
nuclear@0 211 // evenly spaced in R^2 from 0.0 to MaxR^2
nuclear@0 212 // K[0] controls the slope at radius=0.0, rather than the actual value.
nuclear@0 213 const int NumSegments = LensConfig::NumCoefficients;
nuclear@0 214 OVR_ASSERT ( NumSegments <= NumCoefficients );
nuclear@0 215 float scaledRsq = (float)(NumSegments-1) * rsq / ( MaxR * MaxR );
nuclear@0 216 scale = EvalCatmullRom10Spline ( K, scaledRsq );
nuclear@0 217
nuclear@0 218
nuclear@0 219 //Intercept, and overrule if needed
nuclear@0 220 if (CustomDistortion)
nuclear@0 221 {
nuclear@0 222 scale = CustomDistortion(rsq);
nuclear@0 223 }
nuclear@0 224
nuclear@0 225 }break;
nuclear@0 226 default:
nuclear@0 227 OVR_ASSERT ( false );
nuclear@0 228 break;
nuclear@0 229 }
nuclear@0 230 return scale;
nuclear@0 231 }
nuclear@0 232
nuclear@0 233 // x,y,z components map to r,g,b
nuclear@0 234 Vector3f LensConfig::DistortionFnScaleRadiusSquaredChroma (float rsq) const
nuclear@0 235 {
nuclear@0 236 float scale = DistortionFnScaleRadiusSquared ( rsq );
nuclear@0 237 Vector3f scaleRGB;
nuclear@0 238 scaleRGB.x = scale * ( 1.0f + ChromaticAberration[0] + rsq * ChromaticAberration[1] ); // Red
nuclear@0 239 scaleRGB.y = scale; // Green
nuclear@0 240 scaleRGB.z = scale * ( 1.0f + ChromaticAberration[2] + rsq * ChromaticAberration[3] ); // Blue
nuclear@0 241 return scaleRGB;
nuclear@0 242 }
nuclear@0 243
nuclear@0 244 // DistortionFnInverse computes the inverse of the distortion function on an argument.
nuclear@0 245 float LensConfig::DistortionFnInverse(float r) const
nuclear@0 246 {
nuclear@0 247 OVR_ASSERT((r <= 20.0f));
nuclear@0 248
nuclear@0 249 float s, d;
nuclear@0 250 float delta = r * 0.25f;
nuclear@0 251
nuclear@0 252 // Better to start guessing too low & take longer to converge than too high
nuclear@0 253 // and hit singularities. Empirically, r * 0.5f is too high in some cases.
nuclear@0 254 s = r * 0.25f;
nuclear@0 255 d = fabs(r - DistortionFn(s));
nuclear@0 256
nuclear@0 257 for (int i = 0; i < 20; i++)
nuclear@0 258 {
nuclear@0 259 float sUp = s + delta;
nuclear@0 260 float sDown = s - delta;
nuclear@0 261 float dUp = fabs(r - DistortionFn(sUp));
nuclear@0 262 float dDown = fabs(r - DistortionFn(sDown));
nuclear@0 263
nuclear@0 264 if (dUp < d)
nuclear@0 265 {
nuclear@0 266 s = sUp;
nuclear@0 267 d = dUp;
nuclear@0 268 }
nuclear@0 269 else if (dDown < d)
nuclear@0 270 {
nuclear@0 271 s = sDown;
nuclear@0 272 d = dDown;
nuclear@0 273 }
nuclear@0 274 else
nuclear@0 275 {
nuclear@0 276 delta *= 0.5f;
nuclear@0 277 }
nuclear@0 278 }
nuclear@0 279
nuclear@0 280 return s;
nuclear@0 281 }
nuclear@0 282
nuclear@0 283
nuclear@0 284
nuclear@0 285 float LensConfig::DistortionFnInverseApprox(float r) const
nuclear@0 286 {
nuclear@0 287 float rsq = r * r;
nuclear@0 288 float scale = 1.0f;
nuclear@0 289 switch ( Eqn )
nuclear@0 290 {
nuclear@0 291 case Distortion_Poly4:
nuclear@0 292 // Deprecated
nuclear@0 293 OVR_ASSERT ( false );
nuclear@0 294 break;
nuclear@0 295 case Distortion_RecipPoly4:
nuclear@0 296 scale = 1.0f / ( InvK[0] + rsq * ( InvK[1] + rsq * ( InvK[2] + rsq * InvK[3] ) ) );
nuclear@0 297 break;
nuclear@0 298 case Distortion_CatmullRom10:{
nuclear@0 299 // A Catmull-Rom spline through the values 1.0, K[1], K[2] ... K[9]
nuclear@0 300 // evenly spaced in R^2 from 0.0 to MaxR^2
nuclear@0 301 // K[0] controls the slope at radius=0.0, rather than the actual value.
nuclear@0 302 const int NumSegments = LensConfig::NumCoefficients;
nuclear@0 303 OVR_ASSERT ( NumSegments <= NumCoefficients );
nuclear@0 304 float scaledRsq = (float)(NumSegments-1) * rsq / ( MaxInvR * MaxInvR );
nuclear@0 305 scale = EvalCatmullRom10Spline ( InvK, scaledRsq );
nuclear@0 306
nuclear@0 307 //Intercept, and overrule if needed
nuclear@0 308 if (CustomDistortionInv)
nuclear@0 309 {
nuclear@0 310 scale = CustomDistortionInv(rsq);
nuclear@0 311 }
nuclear@0 312
nuclear@0 313 }break;
nuclear@0 314 default:
nuclear@0 315 OVR_ASSERT ( false );
nuclear@0 316 break;
nuclear@0 317 }
nuclear@0 318 return r * scale;
nuclear@0 319 }
nuclear@0 320
nuclear@0 321 void LensConfig::SetUpInverseApprox()
nuclear@0 322 {
nuclear@0 323 float maxR = MaxInvR;
nuclear@0 324
nuclear@0 325 switch ( Eqn )
nuclear@0 326 {
nuclear@0 327 case Distortion_Poly4:
nuclear@0 328 // Deprecated
nuclear@0 329 OVR_ASSERT ( false );
nuclear@0 330 break;
nuclear@0 331 case Distortion_RecipPoly4:{
nuclear@0 332
nuclear@0 333 float sampleR[4];
nuclear@0 334 float sampleRSq[4];
nuclear@0 335 float sampleInv[4];
nuclear@0 336 float sampleFit[4];
nuclear@0 337
nuclear@0 338 // Found heuristically...
nuclear@0 339 sampleR[0] = 0.0f;
nuclear@0 340 sampleR[1] = maxR * 0.4f;
nuclear@0 341 sampleR[2] = maxR * 0.8f;
nuclear@0 342 sampleR[3] = maxR * 1.5f;
nuclear@0 343 for ( int i = 0; i < 4; i++ )
nuclear@0 344 {
nuclear@0 345 sampleRSq[i] = sampleR[i] * sampleR[i];
nuclear@0 346 sampleInv[i] = DistortionFnInverse ( sampleR[i] );
nuclear@0 347 sampleFit[i] = sampleR[i] / sampleInv[i];
nuclear@0 348 }
nuclear@0 349 sampleFit[0] = 1.0f;
nuclear@0 350 FitCubicPolynomial ( InvK, sampleRSq, sampleFit );
nuclear@0 351
nuclear@0 352 #if 0
nuclear@0 353 // Should be a nearly exact match on the chosen points.
nuclear@0 354 OVR_ASSERT ( fabs ( DistortionFnInverse ( sampleR[0] ) - DistortionFnInverseApprox ( sampleR[0] ) ) / maxR < 0.0001f );
nuclear@0 355 OVR_ASSERT ( fabs ( DistortionFnInverse ( sampleR[1] ) - DistortionFnInverseApprox ( sampleR[1] ) ) / maxR < 0.0001f );
nuclear@0 356 OVR_ASSERT ( fabs ( DistortionFnInverse ( sampleR[2] ) - DistortionFnInverseApprox ( sampleR[2] ) ) / maxR < 0.0001f );
nuclear@0 357 OVR_ASSERT ( fabs ( DistortionFnInverse ( sampleR[3] ) - DistortionFnInverseApprox ( sampleR[3] ) ) / maxR < 0.0001f );
nuclear@0 358 // Should be a decent match on the rest of the range.
nuclear@0 359 const int maxCheck = 20;
nuclear@0 360 for ( int i = 0; i < maxCheck; i++ )
nuclear@0 361 {
nuclear@0 362 float checkR = (float)i * maxR / (float)maxCheck;
nuclear@0 363 float realInv = DistortionFnInverse ( checkR );
nuclear@0 364 float testInv = DistortionFnInverseApprox ( checkR );
nuclear@0 365 float error = fabsf ( realInv - testInv ) / maxR;
nuclear@0 366 OVR_ASSERT ( error < 0.1f );
nuclear@0 367 }
nuclear@0 368 #endif
nuclear@0 369
nuclear@0 370 }break;
nuclear@0 371 case Distortion_CatmullRom10:{
nuclear@0 372
nuclear@0 373 const int NumSegments = LensConfig::NumCoefficients;
nuclear@0 374 OVR_ASSERT ( NumSegments <= NumCoefficients );
nuclear@0 375 for ( int i = 1; i < NumSegments; i++ )
nuclear@0 376 {
nuclear@0 377 float scaledRsq = (float)i;
nuclear@0 378 float rsq = scaledRsq * MaxInvR * MaxInvR / (float)( NumSegments - 1);
nuclear@0 379 float r = sqrtf ( rsq );
nuclear@0 380 float inv = DistortionFnInverse ( r );
nuclear@0 381 InvK[i] = inv / r;
nuclear@0 382 InvK[0] = 1.0f; // TODO: fix this.
nuclear@0 383 }
nuclear@0 384
nuclear@0 385 #if 0
nuclear@0 386 const int maxCheck = 20;
nuclear@0 387 for ( int i = 0; i <= maxCheck; i++ )
nuclear@0 388 {
nuclear@0 389 float checkR = (float)i * MaxInvR / (float)maxCheck;
nuclear@0 390 float realInv = DistortionFnInverse ( checkR );
nuclear@0 391 float testInv = DistortionFnInverseApprox ( checkR );
nuclear@0 392 float error = fabsf ( realInv - testInv ) / MaxR;
nuclear@0 393 OVR_ASSERT ( error < 0.01f );
nuclear@0 394 }
nuclear@0 395 #endif
nuclear@0 396
nuclear@0 397 }break;
nuclear@0 398
nuclear@0 399 default:
nuclear@0 400 break;
nuclear@0 401 }
nuclear@0 402 }
nuclear@0 403
nuclear@0 404
nuclear@0 405 void LensConfig::SetToIdentity()
nuclear@0 406 {
nuclear@0 407 for ( int i = 0; i < NumCoefficients; i++ )
nuclear@0 408 {
nuclear@0 409 K[i] = 0.0f;
nuclear@0 410 InvK[i] = 0.0f;
nuclear@0 411 }
nuclear@0 412 Eqn = Distortion_RecipPoly4;
nuclear@0 413 K[0] = 1.0f;
nuclear@0 414 InvK[0] = 1.0f;
nuclear@0 415 MaxR = 1.0f;
nuclear@0 416 MaxInvR = 1.0f;
nuclear@0 417 ChromaticAberration[0] = 0.0f;
nuclear@0 418 ChromaticAberration[1] = 0.0f;
nuclear@0 419 ChromaticAberration[2] = 0.0f;
nuclear@0 420 ChromaticAberration[3] = 0.0f;
nuclear@0 421 MetersPerTanAngleAtCenter = 0.05f;
nuclear@0 422 }
nuclear@0 423
nuclear@0 424
nuclear@0 425 enum LensConfigStoredVersion
nuclear@0 426 {
nuclear@0 427 LCSV_CatmullRom10Version1 = 1
nuclear@0 428 };
nuclear@0 429
nuclear@0 430 // DO NOT CHANGE THESE ONCE THEY HAVE BEEN BAKED INTO FIRMWARE.
nuclear@0 431 // If something needs to change, add a new one!
nuclear@0 432 struct LensConfigStored_CatmullRom10Version1
nuclear@0 433 {
nuclear@0 434 // All these items must be fixed-length integers - no "float", no "int", etc.
nuclear@0 435 uint16_t VersionNumber; // Must be LCSV_CatmullRom10Version1
nuclear@0 436
nuclear@0 437 uint16_t K[11];
nuclear@0 438 uint16_t MaxR;
nuclear@0 439 uint16_t MetersPerTanAngleAtCenter;
nuclear@0 440 uint16_t ChromaticAberration[4];
nuclear@0 441 // InvK and MaxInvR are calculated on load.
nuclear@0 442 };
nuclear@0 443
nuclear@0 444 uint16_t EncodeFixedPointUInt16 ( float val, uint16_t zeroVal, int fractionalBits )
nuclear@0 445 {
nuclear@0 446 OVR_ASSERT ( ( fractionalBits >= 0 ) && ( fractionalBits < 31 ) );
nuclear@0 447 float valWhole = val * (float)( 1 << fractionalBits );
nuclear@0 448 valWhole += (float)zeroVal + 0.5f;
nuclear@0 449 valWhole = floorf ( valWhole );
nuclear@0 450 OVR_ASSERT ( ( valWhole >= 0.0f ) && ( valWhole < (float)( 1 << 16 ) ) );
nuclear@0 451 return (uint16_t)valWhole;
nuclear@0 452 }
nuclear@0 453
nuclear@0 454 float DecodeFixedPointUInt16 ( uint16_t val, uint16_t zeroVal, int fractionalBits )
nuclear@0 455 {
nuclear@0 456 OVR_ASSERT ( ( fractionalBits >= 0 ) && ( fractionalBits < 31 ) );
nuclear@0 457 float valFloat = (float)val;
nuclear@0 458 valFloat -= (float)zeroVal;
nuclear@0 459 valFloat *= 1.0f / (float)( 1 << fractionalBits );
nuclear@0 460 return valFloat;
nuclear@0 461 }
nuclear@0 462
nuclear@0 463
nuclear@0 464 // Returns true on success.
nuclear@0 465 bool LoadLensConfig ( LensConfig *presult, uint8_t const *pbuffer, int bufferSizeInBytes )
nuclear@0 466 {
nuclear@0 467 if ( bufferSizeInBytes < 2 )
nuclear@0 468 {
nuclear@0 469 // Can't even tell the version number!
nuclear@0 470 return false;
nuclear@0 471 }
nuclear@0 472 uint16_t version = DecodeUInt16 ( pbuffer + 0 );
nuclear@0 473 switch ( version )
nuclear@0 474 {
nuclear@0 475 case LCSV_CatmullRom10Version1:
nuclear@0 476 {
nuclear@0 477 if ( bufferSizeInBytes < (int)sizeof(LensConfigStored_CatmullRom10Version1) )
nuclear@0 478 {
nuclear@0 479 return false;
nuclear@0 480 }
nuclear@0 481 LensConfigStored_CatmullRom10Version1 lcs;
nuclear@0 482 lcs.VersionNumber = DecodeUInt16 ( pbuffer + 0 );
nuclear@0 483 for ( int i = 0; i < 11; i++ )
nuclear@0 484 {
nuclear@0 485 lcs.K[i] = DecodeUInt16 ( pbuffer + 2 + 2*i );
nuclear@0 486 }
nuclear@0 487 lcs.MaxR = DecodeUInt16 ( pbuffer + 24 );
nuclear@0 488 lcs.MetersPerTanAngleAtCenter = DecodeUInt16 ( pbuffer + 26 );
nuclear@0 489 for ( int i = 0; i < 4; i++ )
nuclear@0 490 {
nuclear@0 491 lcs.ChromaticAberration[i] = DecodeUInt16 ( pbuffer + 28 + 2*i );
nuclear@0 492 }
nuclear@0 493 OVR_COMPILER_ASSERT ( sizeof(lcs) == 36 );
nuclear@0 494
nuclear@0 495 // Convert to the real thing.
nuclear@0 496 LensConfig result;
nuclear@0 497 result.Eqn = Distortion_CatmullRom10;
nuclear@0 498 for ( int i = 0; i < 11; i++ )
nuclear@0 499 {
nuclear@0 500 // K[] are mostly 1.something. They may get significantly bigger, but they never hit 0.0.
nuclear@0 501 result.K[i] = DecodeFixedPointUInt16 ( lcs.K[i], 0, 14 );
nuclear@0 502 }
nuclear@0 503 // MaxR is tan(angle), so always >0, typically just over 1.0 (45 degrees half-fov),
nuclear@0 504 // but may get arbitrarily high. tan(76)=4 is a very reasonable limit!
nuclear@0 505 result.MaxR = DecodeFixedPointUInt16 ( lcs.MaxR, 0, 14 );
nuclear@0 506 // MetersPerTanAngleAtCenter is also known as focal length!
nuclear@0 507 // Typically around 0.04 for our current screens, minimum of 0, sensible maximum of 0.125 (i.e. 3 "extra" bits of fraction)
nuclear@0 508 result.MetersPerTanAngleAtCenter = DecodeFixedPointUInt16 ( lcs.MetersPerTanAngleAtCenter, 0, 16+3 );
nuclear@0 509 for ( int i = 0; i < 4; i++ )
nuclear@0 510 {
nuclear@0 511 // ChromaticAberration[] are mostly 0.0something, centered on 0.0. Largest seen is 0.04, so set max to 0.125 (i.e. 3 "extra" bits of fraction)
nuclear@0 512 result.ChromaticAberration[i] = DecodeFixedPointUInt16 ( lcs.ChromaticAberration[i], 0x8000, 16+3 );
nuclear@0 513 }
nuclear@0 514 result.MaxInvR = result.DistortionFn ( result.MaxR );
nuclear@0 515 result.SetUpInverseApprox();
nuclear@0 516
nuclear@0 517 OVR_ASSERT ( version == lcs.VersionNumber );
nuclear@0 518
nuclear@0 519 *presult = result;
nuclear@0 520 }
nuclear@0 521 break;
nuclear@0 522 default:
nuclear@0 523 // Unknown format.
nuclear@0 524 return false;
nuclear@0 525 break;
nuclear@0 526 }
nuclear@0 527 return true;
nuclear@0 528 }
nuclear@0 529
nuclear@0 530 // Returns number of bytes needed.
nuclear@0 531 int SaveLensConfigSizeInBytes ( LensConfig const &config )
nuclear@0 532 {
nuclear@0 533 OVR_UNUSED ( config );
nuclear@0 534 return sizeof ( LensConfigStored_CatmullRom10Version1 );
nuclear@0 535 }
nuclear@0 536
nuclear@0 537 // Returns true on success.
nuclear@0 538 bool SaveLensConfig ( uint8_t *pbuffer, int bufferSizeInBytes, LensConfig const &config )
nuclear@0 539 {
nuclear@0 540 if ( bufferSizeInBytes < (int)sizeof ( LensConfigStored_CatmullRom10Version1 ) )
nuclear@0 541 {
nuclear@0 542 return false;
nuclear@0 543 }
nuclear@0 544
nuclear@0 545 // Construct the values.
nuclear@0 546 LensConfigStored_CatmullRom10Version1 lcs;
nuclear@0 547 lcs.VersionNumber = LCSV_CatmullRom10Version1;
nuclear@0 548 for ( int i = 0; i < 11; i++ )
nuclear@0 549 {
nuclear@0 550 // K[] are mostly 1.something. They may get significantly bigger, but they never hit 0.0.
nuclear@0 551 lcs.K[i] = EncodeFixedPointUInt16 ( config.K[i], 0, 14 );
nuclear@0 552 }
nuclear@0 553 // MaxR is tan(angle), so always >0, typically just over 1.0 (45 degrees half-fov),
nuclear@0 554 // but may get arbitrarily high. tan(76)=4 is a very reasonable limit!
nuclear@0 555 lcs.MaxR = EncodeFixedPointUInt16 ( config.MaxR, 0, 14 );
nuclear@0 556 // MetersPerTanAngleAtCenter is also known as focal length!
nuclear@0 557 // Typically around 0.04 for our current screens, minimum of 0, sensible maximum of 0.125 (i.e. 3 "extra" bits of fraction)
nuclear@0 558 lcs.MetersPerTanAngleAtCenter = EncodeFixedPointUInt16 ( config.MetersPerTanAngleAtCenter, 0, 16+3 );
nuclear@0 559 for ( int i = 0; i < 4; i++ )
nuclear@0 560 {
nuclear@0 561 // ChromaticAberration[] are mostly 0.0something, centered on 0.0. Largest seen is 0.04, so set max to 0.125 (i.e. 3 "extra" bits of fraction)
nuclear@0 562 lcs.ChromaticAberration[i] = EncodeFixedPointUInt16 ( config.ChromaticAberration[i], 0x8000, 16+3 );
nuclear@0 563 }
nuclear@0 564
nuclear@0 565
nuclear@0 566 // Now store them out, sensitive to endianness.
nuclear@0 567 EncodeUInt16 ( pbuffer + 0, lcs.VersionNumber );
nuclear@0 568 for ( int i = 0; i < 11; i++ )
nuclear@0 569 {
nuclear@0 570 EncodeUInt16 ( pbuffer + 2 + 2*i, lcs.K[i] );
nuclear@0 571 }
nuclear@0 572 EncodeUInt16 ( pbuffer + 24, lcs.MaxR );
nuclear@0 573 EncodeUInt16 ( pbuffer + 26, lcs.MetersPerTanAngleAtCenter );
nuclear@0 574 for ( int i = 0; i < 4; i++ )
nuclear@0 575 {
nuclear@0 576 EncodeUInt16 ( pbuffer + 28 + 2*i, lcs.ChromaticAberration[i] );
nuclear@0 577 }
nuclear@0 578 OVR_COMPILER_ASSERT ( 36 == sizeof(lcs) );
nuclear@0 579
nuclear@0 580 return true;
nuclear@0 581 }
nuclear@0 582
nuclear@0 583 #ifdef OVR_BUILD_DEBUG
nuclear@0 584 void TestSaveLoadLensConfig ( LensConfig const &config )
nuclear@0 585 {
nuclear@0 586 OVR_ASSERT ( config.Eqn == Distortion_CatmullRom10 );
nuclear@0 587 // As a test, make sure this can be encoded and decoded correctly.
nuclear@0 588 const int bufferSize = 256;
nuclear@0 589 uint8_t buffer[bufferSize];
nuclear@0 590 OVR_ASSERT ( SaveLensConfigSizeInBytes ( config ) < bufferSize );
nuclear@0 591 bool success;
nuclear@0 592 success = SaveLensConfig ( buffer, bufferSize, config );
nuclear@0 593 OVR_ASSERT ( success );
nuclear@0 594 LensConfig testConfig;
nuclear@0 595 success = LoadLensConfig ( &testConfig, buffer, bufferSize );
nuclear@0 596 OVR_ASSERT ( success );
nuclear@0 597 OVR_ASSERT ( testConfig.Eqn == config.Eqn );
nuclear@0 598 for ( int i = 0; i < 11; i++ )
nuclear@0 599 {
nuclear@0 600 OVR_ASSERT ( fabs ( testConfig.K[i] - config.K[i] ) < 0.0001f );
nuclear@0 601 }
nuclear@0 602 OVR_ASSERT ( fabsf ( testConfig.MaxR - config.MaxR ) < 0.0001f );
nuclear@0 603 OVR_ASSERT ( fabsf ( testConfig.MetersPerTanAngleAtCenter - config.MetersPerTanAngleAtCenter ) < 0.00001f );
nuclear@0 604 for ( int i = 0; i < 4; i++ )
nuclear@0 605 {
nuclear@0 606 OVR_ASSERT ( fabsf ( testConfig.ChromaticAberration[i] - config.ChromaticAberration[i] ) < 0.00001f );
nuclear@0 607 }
nuclear@0 608 }
nuclear@0 609 #endif
nuclear@0 610
nuclear@0 611
nuclear@0 612
nuclear@0 613 //-----------------------------------------------------------------------------------
nuclear@0 614
nuclear@0 615 // TBD: There is a question of whether this is the best file for CreateDebugHMDInfo. As long as there are many
nuclear@0 616 // constants for HmdRenderInfo here as well it is ok. The alternative would be OVR_Common_HMDDevice.cpp, but
nuclear@0 617 // that's specialized per platform... should probably move it there onces the code is in the common base class.
nuclear@0 618
nuclear@0 619 HMDInfo CreateDebugHMDInfo(HmdTypeEnum hmdType)
nuclear@0 620 {
nuclear@0 621 HMDInfo info;
nuclear@0 622
nuclear@0 623 if ((hmdType != HmdType_DK1) &&
nuclear@0 624 (hmdType != HmdType_CrystalCoveProto) &&
nuclear@0 625 (hmdType != HmdType_DK2))
nuclear@0 626 {
nuclear@0 627 LogText("Debug HMDInfo - HmdType not supported. Defaulting to DK1.\n");
nuclear@0 628 hmdType = HmdType_DK1;
nuclear@0 629 }
nuclear@0 630
nuclear@0 631 // The alternative would be to initialize info.HmdType to HmdType_None instead. If we did that,
nuclear@0 632 // code wouldn't be "maximally compatible" and devs wouldn't know what device we are
nuclear@0 633 // simulating... so if differentiation becomes necessary we better add Debug flag in the future.
nuclear@0 634 info.HmdType = hmdType;
nuclear@0 635 info.Manufacturer = "Oculus VR";
nuclear@0 636
nuclear@0 637 switch(hmdType)
nuclear@0 638 {
nuclear@0 639 case HmdType_DK1:
nuclear@0 640 info.ProductName = "Oculus Rift DK1";
nuclear@0 641 info.ResolutionInPixels = Sizei ( 1280, 800 );
nuclear@0 642 info.ScreenSizeInMeters = Sizef ( 0.1498f, 0.0936f );
nuclear@0 643 info.ScreenGapSizeInMeters = 0.0f;
nuclear@0 644 info.CenterFromTopInMeters = 0.0468f;
nuclear@0 645 info.LensSeparationInMeters = 0.0635f;
nuclear@0 646 info.PelOffsetR = Vector2f ( 0.0f, 0.0f );
nuclear@0 647 info.PelOffsetB = Vector2f ( 0.0f, 0.0f );
nuclear@0 648 info.Shutter.Type = HmdShutter_RollingTopToBottom;
nuclear@0 649 info.Shutter.VsyncToNextVsync = ( 1.0f / 60.0f );
nuclear@0 650 info.Shutter.VsyncToFirstScanline = 0.000052f;
nuclear@0 651 info.Shutter.FirstScanlineToLastScanline = 0.016580f;
nuclear@0 652 info.Shutter.PixelSettleTime = 0.015f;
nuclear@0 653 info.Shutter.PixelPersistence = ( 1.0f / 60.0f );
nuclear@0 654 break;
nuclear@0 655
nuclear@0 656 case HmdType_CrystalCoveProto:
nuclear@0 657 info.ProductName = "Oculus Rift Crystal Cove";
nuclear@0 658 info.ResolutionInPixels = Sizei ( 1920, 1080 );
nuclear@0 659 info.ScreenSizeInMeters = Sizef ( 0.12576f, 0.07074f );
nuclear@0 660 info.ScreenGapSizeInMeters = 0.0f;
nuclear@0 661 info.CenterFromTopInMeters = info.ScreenSizeInMeters.h * 0.5f;
nuclear@0 662 info.LensSeparationInMeters = 0.0635f;
nuclear@0 663 info.PelOffsetR = Vector2f ( 0.0f, 0.0f );
nuclear@0 664 info.PelOffsetB = Vector2f ( 0.0f, 0.0f );
nuclear@0 665 info.Shutter.Type = HmdShutter_RollingRightToLeft;
nuclear@0 666 info.Shutter.VsyncToNextVsync = ( 1.0f / 76.0f );
nuclear@0 667 info.Shutter.VsyncToFirstScanline = 0.0000273f;
nuclear@0 668 info.Shutter.FirstScanlineToLastScanline = 0.0131033f;
nuclear@0 669 info.Shutter.PixelSettleTime = 0.0f;
nuclear@0 670 info.Shutter.PixelPersistence = 0.18f * info.Shutter.VsyncToNextVsync;
nuclear@0 671 break;
nuclear@0 672
nuclear@0 673 case HmdType_DK2:
nuclear@0 674 info.ProductName = "Oculus Rift DK2";
nuclear@0 675 info.ResolutionInPixels = Sizei ( 1920, 1080 );
nuclear@0 676 info.ScreenSizeInMeters = Sizef ( 0.12576f, 0.07074f );
nuclear@0 677 info.ScreenGapSizeInMeters = 0.0f;
nuclear@0 678 info.CenterFromTopInMeters = info.ScreenSizeInMeters.h * 0.5f;
nuclear@0 679 info.LensSeparationInMeters = 0.0635f;
nuclear@0 680 info.PelOffsetR = Vector2f ( 0.5f, 0.5f );
nuclear@0 681 info.PelOffsetB = Vector2f ( 0.5f, 0.5f );
nuclear@0 682 info.Shutter.Type = HmdShutter_RollingRightToLeft;
nuclear@0 683 info.Shutter.VsyncToNextVsync = ( 1.0f / 76.0f );
nuclear@0 684 info.Shutter.VsyncToFirstScanline = 0.0000273f;
nuclear@0 685 info.Shutter.FirstScanlineToLastScanline = 0.0131033f;
nuclear@0 686 info.Shutter.PixelSettleTime = 0.0f;
nuclear@0 687 info.Shutter.PixelPersistence = 0.18f * info.Shutter.VsyncToNextVsync;
nuclear@0 688 break;
nuclear@0 689
nuclear@0 690 default:
nuclear@0 691 break;
nuclear@0 692 }
nuclear@0 693
nuclear@0 694 return info;
nuclear@0 695 }
nuclear@0 696
nuclear@0 697
nuclear@0 698 HmdRenderInfo GenerateHmdRenderInfoFromHmdInfo ( HMDInfo const &hmdInfo,
nuclear@0 699 Profile const *profile,
nuclear@0 700 DistortionEqnType distortionType /*= Distortion_CatmullRom10*/,
nuclear@0 701 EyeCupType eyeCupOverride /*= EyeCup_LAST*/ )
nuclear@0 702 {
nuclear@0 703 HmdRenderInfo renderInfo;
nuclear@0 704
nuclear@0 705 OVR_ASSERT(profile); // profiles are required
nuclear@0 706 if(!profile)
nuclear@0 707 return renderInfo;
nuclear@0 708
nuclear@0 709 renderInfo.HmdType = hmdInfo.HmdType;
nuclear@0 710 renderInfo.ResolutionInPixels = hmdInfo.ResolutionInPixels;
nuclear@0 711 renderInfo.ScreenSizeInMeters = hmdInfo.ScreenSizeInMeters;
nuclear@0 712 renderInfo.CenterFromTopInMeters = hmdInfo.CenterFromTopInMeters;
nuclear@0 713 renderInfo.ScreenGapSizeInMeters = hmdInfo.ScreenGapSizeInMeters;
nuclear@0 714 renderInfo.LensSeparationInMeters = hmdInfo.LensSeparationInMeters;
nuclear@0 715 renderInfo.PelOffsetR = hmdInfo.PelOffsetR;
nuclear@0 716 renderInfo.PelOffsetB = hmdInfo.PelOffsetB;
nuclear@0 717
nuclear@0 718 OVR_ASSERT ( sizeof(renderInfo.Shutter) == sizeof(hmdInfo.Shutter) ); // Try to keep the files in sync!
nuclear@0 719 renderInfo.Shutter.Type = hmdInfo.Shutter.Type;
nuclear@0 720 renderInfo.Shutter.VsyncToNextVsync = hmdInfo.Shutter.VsyncToNextVsync;
nuclear@0 721 renderInfo.Shutter.VsyncToFirstScanline = hmdInfo.Shutter.VsyncToFirstScanline;
nuclear@0 722 renderInfo.Shutter.FirstScanlineToLastScanline = hmdInfo.Shutter.FirstScanlineToLastScanline;
nuclear@0 723 renderInfo.Shutter.PixelSettleTime = hmdInfo.Shutter.PixelSettleTime;
nuclear@0 724 renderInfo.Shutter.PixelPersistence = hmdInfo.Shutter.PixelPersistence;
nuclear@0 725
nuclear@0 726 renderInfo.LensDiameterInMeters = 0.035f;
nuclear@0 727 renderInfo.LensSurfaceToMidplateInMeters = 0.025f;
nuclear@0 728 renderInfo.EyeCups = EyeCup_DK1A;
nuclear@0 729
nuclear@0 730 #if 0 // Device settings are out of date - don't use them.
nuclear@0 731 if (Contents & Contents_Distortion)
nuclear@0 732 {
nuclear@0 733 memcpy(renderInfo.DistortionK, DistortionK, sizeof(float)*4);
nuclear@0 734 renderInfo.DistortionEqn = Distortion_RecipPoly4;
nuclear@0 735 }
nuclear@0 736 #endif
nuclear@0 737
nuclear@0 738 // Defaults in case of no user profile.
nuclear@0 739 renderInfo.EyeLeft.NoseToPupilInMeters = 0.032f;
nuclear@0 740 renderInfo.EyeLeft.ReliefInMeters = 0.012f;
nuclear@0 741
nuclear@0 742 // 10mm eye-relief laser numbers for DK1 lenses.
nuclear@0 743 // These are a decent seed for finding eye-relief and IPD.
nuclear@0 744 // These are NOT used for rendering!
nuclear@0 745 // Rendering distortions are now in GenerateLensConfigFromEyeRelief()
nuclear@0 746 // So, if you're hacking in new distortions, don't do it here!
nuclear@0 747 renderInfo.EyeLeft.Distortion.SetToIdentity();
nuclear@0 748 renderInfo.EyeLeft.Distortion.MetersPerTanAngleAtCenter = 0.0449f;
nuclear@0 749 renderInfo.EyeLeft.Distortion.Eqn = Distortion_RecipPoly4;
nuclear@0 750 renderInfo.EyeLeft.Distortion.K[0] = 1.0f;
nuclear@0 751 renderInfo.EyeLeft.Distortion.K[1] = -0.494165344f;
nuclear@0 752 renderInfo.EyeLeft.Distortion.K[2] = 0.587046423f;
nuclear@0 753 renderInfo.EyeLeft.Distortion.K[3] = -0.841887126f;
nuclear@0 754 renderInfo.EyeLeft.Distortion.MaxR = 1.0f;
nuclear@0 755
nuclear@0 756 renderInfo.EyeLeft.Distortion.ChromaticAberration[0] = -0.006f;
nuclear@0 757 renderInfo.EyeLeft.Distortion.ChromaticAberration[1] = 0.0f;
nuclear@0 758 renderInfo.EyeLeft.Distortion.ChromaticAberration[2] = 0.014f;
nuclear@0 759 renderInfo.EyeLeft.Distortion.ChromaticAberration[3] = 0.0f;
nuclear@0 760
nuclear@0 761 renderInfo.EyeRight = renderInfo.EyeLeft;
nuclear@0 762
nuclear@0 763 // Obtain data from profile.
nuclear@0 764 char eyecup[16];
nuclear@0 765 if (profile->GetValue(OVR_KEY_EYE_CUP, eyecup, 16))
nuclear@0 766 {
nuclear@0 767 SetEyeCup(&renderInfo, eyecup);
nuclear@0 768 }
nuclear@0 769
nuclear@0 770 switch ( hmdInfo.HmdType )
nuclear@0 771 {
nuclear@0 772 case HmdType_None:
nuclear@0 773 case HmdType_DKProto:
nuclear@0 774 case HmdType_DK1:
nuclear@0 775 // Slight hack to improve usability.
nuclear@0 776 // If you have a DKHD-style lens profile enabled,
nuclear@0 777 // but you plug in DK1 and forget to change the profile,
nuclear@0 778 // obviously you don't want those lens numbers.
nuclear@0 779 if ( ( renderInfo.EyeCups != EyeCup_DK1A ) &&
nuclear@0 780 ( renderInfo.EyeCups != EyeCup_DK1B ) &&
nuclear@0 781 ( renderInfo.EyeCups != EyeCup_DK1C ) )
nuclear@0 782 {
nuclear@0 783 renderInfo.EyeCups = EyeCup_DK1A;
nuclear@0 784 }
nuclear@0 785 break;
nuclear@0 786
nuclear@0 787 case HmdType_DKHD2Proto:
nuclear@0 788 renderInfo.EyeCups = EyeCup_DKHD2A;
nuclear@0 789 break;
nuclear@0 790 case HmdType_CrystalCoveProto:
nuclear@0 791 renderInfo.EyeCups = EyeCup_PinkA;
nuclear@0 792 break;
nuclear@0 793 case HmdType_DK2:
nuclear@0 794 renderInfo.EyeCups = EyeCup_DK2A;
nuclear@0 795 break;
nuclear@0 796 default:
nuclear@0 797 break;
nuclear@0 798 }
nuclear@0 799
nuclear@0 800 if ( eyeCupOverride != EyeCup_LAST )
nuclear@0 801 {
nuclear@0 802 renderInfo.EyeCups = eyeCupOverride;
nuclear@0 803 }
nuclear@0 804
nuclear@0 805 switch ( renderInfo.EyeCups )
nuclear@0 806 {
nuclear@0 807 case EyeCup_DK1A:
nuclear@0 808 case EyeCup_DK1B:
nuclear@0 809 case EyeCup_DK1C:
nuclear@0 810 renderInfo.LensDiameterInMeters = 0.035f;
nuclear@0 811 renderInfo.LensSurfaceToMidplateInMeters = 0.02357f;
nuclear@0 812 // Not strictly lens-specific, but still wise to set a reasonable default for relief.
nuclear@0 813 renderInfo.EyeLeft.ReliefInMeters = 0.010f;
nuclear@0 814 renderInfo.EyeRight.ReliefInMeters = 0.010f;
nuclear@0 815 break;
nuclear@0 816 case EyeCup_DKHD2A:
nuclear@0 817 renderInfo.LensDiameterInMeters = 0.035f;
nuclear@0 818 renderInfo.LensSurfaceToMidplateInMeters = 0.02357f;
nuclear@0 819 // Not strictly lens-specific, but still wise to set a reasonable default for relief.
nuclear@0 820 renderInfo.EyeLeft.ReliefInMeters = 0.010f;
nuclear@0 821 renderInfo.EyeRight.ReliefInMeters = 0.010f;
nuclear@0 822 break;
nuclear@0 823 case EyeCup_PinkA:
nuclear@0 824 case EyeCup_DK2A:
nuclear@0 825 renderInfo.LensDiameterInMeters = 0.04f; // approximate
nuclear@0 826 renderInfo.LensSurfaceToMidplateInMeters = 0.01965f;
nuclear@0 827 // Not strictly lens-specific, but still wise to set a reasonable default for relief.
nuclear@0 828 renderInfo.EyeLeft.ReliefInMeters = 0.012f;
nuclear@0 829 renderInfo.EyeRight.ReliefInMeters = 0.012f;
nuclear@0 830 break;
nuclear@0 831 default: OVR_ASSERT ( false ); break;
nuclear@0 832 }
nuclear@0 833
nuclear@0 834 Profile* def = ProfileManager::GetInstance()->GetDefaultProfile(hmdInfo.HmdType);
nuclear@0 835
nuclear@0 836 // Set the eye position
nuclear@0 837 // Use the user profile value unless they have elected to use the defaults
nuclear@0 838 if (!profile->GetBoolValue(OVR_KEY_CUSTOM_EYE_RENDER, true))
nuclear@0 839 profile = def; // use the default
nuclear@0 840
nuclear@0 841 char user[32];
nuclear@0 842 profile->GetValue(OVR_KEY_USER, user, 32); // for debugging purposes
nuclear@0 843
nuclear@0 844 // TBD: Maybe we should separate custom camera positioning from custom distortion rendering ??
nuclear@0 845 float eye2nose[2] = { OVR_DEFAULT_IPD / 2, OVR_DEFAULT_IPD / 2 };
nuclear@0 846 if (profile->GetFloatValues(OVR_KEY_EYE_TO_NOSE_DISTANCE, eye2nose, 2) == 2)
nuclear@0 847 {
nuclear@0 848 renderInfo.EyeLeft.NoseToPupilInMeters = eye2nose[0];
nuclear@0 849 renderInfo.EyeRight.NoseToPupilInMeters = eye2nose[1];
nuclear@0 850 }
nuclear@0 851 else
nuclear@0 852 { // Legacy profiles may not include half-ipd, so use the regular IPD value instead
nuclear@0 853 float ipd = profile->GetFloatValue(OVR_KEY_IPD, OVR_DEFAULT_IPD);
nuclear@0 854 renderInfo.EyeLeft.NoseToPupilInMeters = 0.5f * ipd;
nuclear@0 855 renderInfo.EyeRight.NoseToPupilInMeters = 0.5f * ipd;
nuclear@0 856 }
nuclear@0 857
nuclear@0 858 float eye2plate[2];
nuclear@0 859 if ((profile->GetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, eye2plate, 2) == 2) ||
nuclear@0 860 (def->GetFloatValues(OVR_KEY_MAX_EYE_TO_PLATE_DISTANCE, eye2plate, 2) == 2))
nuclear@0 861 { // Subtract the eye-cup height from the plate distance to get the eye-to-lens distance
nuclear@0 862 // This measurement should be the the distance at maximum dial setting
nuclear@0 863 // We still need to adjust with the dial offset
nuclear@0 864 renderInfo.EyeLeft.ReliefInMeters = eye2plate[0] - renderInfo.LensSurfaceToMidplateInMeters;
nuclear@0 865 renderInfo.EyeRight.ReliefInMeters = eye2plate[1] - renderInfo.LensSurfaceToMidplateInMeters;
nuclear@0 866
nuclear@0 867 // Adjust the eye relief with the dial setting (from the assumed max eye relief)
nuclear@0 868 int dial = profile->GetIntValue(OVR_KEY_EYE_RELIEF_DIAL, OVR_DEFAULT_EYE_RELIEF_DIAL);
nuclear@0 869 renderInfo.EyeLeft.ReliefInMeters -= ((10 - dial) * 0.001f);
nuclear@0 870 renderInfo.EyeRight.ReliefInMeters -= ((10 - dial) * 0.001f);
nuclear@0 871 }
nuclear@0 872 else
nuclear@0 873 {
nuclear@0 874 // We shouldn't be here. The user or default profile should have the eye relief
nuclear@0 875 OVR_ASSERT(false);
nuclear@0 876
nuclear@0 877 // Set the eye relief with the user configured dial setting
nuclear@0 878 //int dial = profile->GetIntValue(OVR_KEY_EYE_RELIEF_DIAL, OVR_DEFAULT_EYE_RELIEF_DIAL);
nuclear@0 879
nuclear@0 880 // Assume a default of 7 to 17 mm eye relief based on the dial. This corresponds
nuclear@0 881 // to the sampled and tuned distortion range on the DK1.
nuclear@0 882 //renderInfo.EyeLeft.ReliefInMeters = 0.007f + (dial * 0.001f);
nuclear@0 883 //renderInfo.EyeRight.ReliefInMeters = 0.007f + (dial * 0.001f);
nuclear@0 884 }
nuclear@0 885
nuclear@0 886 def->Release();
nuclear@0 887
nuclear@0 888
nuclear@0 889 // Now we know where the eyes are relative to the lenses, we can compute a distortion for each.
nuclear@0 890 // TODO: incorporate lateral offset in distortion generation.
nuclear@0 891 // TODO: we used a distortion to calculate eye-relief, and now we're making a distortion from that eye-relief. Close the loop!
nuclear@0 892
nuclear@0 893 for ( int eyeNum = 0; eyeNum < 2; eyeNum++ )
nuclear@0 894 {
nuclear@0 895 HmdRenderInfo::EyeConfig *pHmdEyeConfig = ( eyeNum == 0 ) ? &(renderInfo.EyeLeft) : &(renderInfo.EyeRight);
nuclear@0 896
nuclear@0 897 float eye_relief = pHmdEyeConfig->ReliefInMeters;
nuclear@0 898 LensConfig distortionConfig = GenerateLensConfigFromEyeRelief ( eye_relief, renderInfo, distortionType );
nuclear@0 899 pHmdEyeConfig->Distortion = distortionConfig;
nuclear@0 900 }
nuclear@0 901
nuclear@0 902 return renderInfo;
nuclear@0 903 }
nuclear@0 904
nuclear@0 905
nuclear@0 906 LensConfig GenerateLensConfigFromEyeRelief ( float eyeReliefInMeters, HmdRenderInfo const &hmd, DistortionEqnType distortionType /*= Distortion_CatmullRom10*/ )
nuclear@0 907 {
nuclear@0 908 struct DistortionDescriptor
nuclear@0 909 {
nuclear@0 910 float EyeRelief;
nuclear@0 911 // The three places we're going to sample & lerp the curve at.
nuclear@0 912 // One sample is always at 0.0, and the distortion scale should be 1.0 or else!
nuclear@0 913 // Only use for poly4 numbers - CR has an implicit scale.
nuclear@0 914 float SampleRadius[3];
nuclear@0 915 // Where the distortion has actually been measured/calibrated out to.
nuclear@0 916 // Don't try to hallucinate data out beyond here.
nuclear@0 917 float MaxRadius;
nuclear@0 918 // The config itself.
nuclear@0 919 LensConfig Config;
nuclear@0 920 };
nuclear@0 921
nuclear@0 922 static const int MaxDistortions = 10;
nuclear@0 923 DistortionDescriptor distortions[MaxDistortions];
nuclear@0 924 for (int i = 0; i < MaxDistortions; i++)
nuclear@0 925 {
nuclear@0 926 distortions[i].EyeRelief = 0.0f;
nuclear@0 927 memset(distortions[i].SampleRadius, 0, sizeof(distortions[i].SampleRadius));
nuclear@0 928 distortions[i].MaxRadius = 1.0f;
nuclear@0 929 distortions[i].Config.SetToIdentity(); // Note: This line causes a false Microsoft static analysis error -cat
nuclear@0 930 }
nuclear@0 931 int numDistortions = 0;
nuclear@0 932 int defaultDistortion = 0; // index of the default distortion curve to use if zero eye relief supplied
nuclear@0 933
nuclear@0 934 if ( ( hmd.EyeCups == EyeCup_DK1A ) ||
nuclear@0 935 ( hmd.EyeCups == EyeCup_DK1B ) ||
nuclear@0 936 ( hmd.EyeCups == EyeCup_DK1C ) )
nuclear@0 937 {
nuclear@0 938
nuclear@0 939 numDistortions = 0;
nuclear@0 940
nuclear@0 941 // Tuned at minimum dial setting - extended to r^2 == 1.8
nuclear@0 942 distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10;
nuclear@0 943 distortions[numDistortions].EyeRelief = 0.012760465f - 0.005f;
nuclear@0 944 distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.0425f;
nuclear@0 945 distortions[numDistortions].Config.K[0] = 1.0000f;
nuclear@0 946 distortions[numDistortions].Config.K[1] = 1.06505f;
nuclear@0 947 distortions[numDistortions].Config.K[2] = 1.14725f;
nuclear@0 948 distortions[numDistortions].Config.K[3] = 1.2705f;
nuclear@0 949 distortions[numDistortions].Config.K[4] = 1.48f;
nuclear@0 950 distortions[numDistortions].Config.K[5] = 1.87f;
nuclear@0 951 distortions[numDistortions].Config.K[6] = 2.534f;
nuclear@0 952 distortions[numDistortions].Config.K[7] = 3.6f;
nuclear@0 953 distortions[numDistortions].Config.K[8] = 5.1f;
nuclear@0 954 distortions[numDistortions].Config.K[9] = 7.4f;
nuclear@0 955 distortions[numDistortions].Config.K[10] = 11.0f;
nuclear@0 956 distortions[numDistortions].MaxRadius = sqrt(1.8f);
nuclear@0 957 defaultDistortion = numDistortions; // this is the default
nuclear@0 958 numDistortions++;
nuclear@0 959
nuclear@0 960 // Tuned at middle dial setting
nuclear@0 961 distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10;
nuclear@0 962 distortions[numDistortions].EyeRelief = 0.012760465f; // my average eye-relief
nuclear@0 963 distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.0425f;
nuclear@0 964 distortions[numDistortions].Config.K[0] = 1.0f;
nuclear@0 965 distortions[numDistortions].Config.K[1] = 1.032407264f;
nuclear@0 966 distortions[numDistortions].Config.K[2] = 1.07160462f;
nuclear@0 967 distortions[numDistortions].Config.K[3] = 1.11998388f;
nuclear@0 968 distortions[numDistortions].Config.K[4] = 1.1808606f;
nuclear@0 969 distortions[numDistortions].Config.K[5] = 1.2590494f;
nuclear@0 970 distortions[numDistortions].Config.K[6] = 1.361915f;
nuclear@0 971 distortions[numDistortions].Config.K[7] = 1.5014339f;
nuclear@0 972 distortions[numDistortions].Config.K[8] = 1.6986004f;
nuclear@0 973 distortions[numDistortions].Config.K[9] = 1.9940577f;
nuclear@0 974 distortions[numDistortions].Config.K[10] = 2.4783147f;
nuclear@0 975 distortions[numDistortions].MaxRadius = 1.0f;
nuclear@0 976 numDistortions++;
nuclear@0 977
nuclear@0 978 // Tuned at maximum dial setting
nuclear@0 979 distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10;
nuclear@0 980 distortions[numDistortions].EyeRelief = 0.012760465f + 0.005f;
nuclear@0 981 distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.0425f;
nuclear@0 982 distortions[numDistortions].Config.K[0] = 1.0102f;
nuclear@0 983 distortions[numDistortions].Config.K[1] = 1.0371f;
nuclear@0 984 distortions[numDistortions].Config.K[2] = 1.0831f;
nuclear@0 985 distortions[numDistortions].Config.K[3] = 1.1353f;
nuclear@0 986 distortions[numDistortions].Config.K[4] = 1.2f;
nuclear@0 987 distortions[numDistortions].Config.K[5] = 1.2851f;
nuclear@0 988 distortions[numDistortions].Config.K[6] = 1.3979f;
nuclear@0 989 distortions[numDistortions].Config.K[7] = 1.56f;
nuclear@0 990 distortions[numDistortions].Config.K[8] = 1.8f;
nuclear@0 991 distortions[numDistortions].Config.K[9] = 2.25f;
nuclear@0 992 distortions[numDistortions].Config.K[10] = 3.0f;
nuclear@0 993 distortions[numDistortions].MaxRadius = 1.0f;
nuclear@0 994 numDistortions++;
nuclear@0 995
nuclear@0 996
nuclear@0 997
nuclear@0 998 // Chromatic aberration doesn't seem to change with eye relief.
nuclear@0 999 for ( int i = 0; i < numDistortions; i++ )
nuclear@0 1000 {
nuclear@0 1001 distortions[i].Config.ChromaticAberration[0] = -0.006f;
nuclear@0 1002 distortions[i].Config.ChromaticAberration[1] = 0.0f;
nuclear@0 1003 distortions[i].Config.ChromaticAberration[2] = 0.014f;
nuclear@0 1004 distortions[i].Config.ChromaticAberration[3] = 0.0f;
nuclear@0 1005 }
nuclear@0 1006 }
nuclear@0 1007 else if ( hmd.EyeCups == EyeCup_DKHD2A )
nuclear@0 1008 {
nuclear@0 1009 // Tuned DKHD2 lens
nuclear@0 1010 numDistortions = 0;
nuclear@0 1011
nuclear@0 1012 distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10;
nuclear@0 1013 distortions[numDistortions].EyeRelief = 0.010f;
nuclear@0 1014 distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.0425f;
nuclear@0 1015 distortions[numDistortions].Config.K[0] = 1.0f;
nuclear@0 1016 distortions[numDistortions].Config.K[1] = 1.0425f;
nuclear@0 1017 distortions[numDistortions].Config.K[2] = 1.0826f;
nuclear@0 1018 distortions[numDistortions].Config.K[3] = 1.130f;
nuclear@0 1019 distortions[numDistortions].Config.K[4] = 1.185f;
nuclear@0 1020 distortions[numDistortions].Config.K[5] = 1.250f;
nuclear@0 1021 distortions[numDistortions].Config.K[6] = 1.338f;
nuclear@0 1022 distortions[numDistortions].Config.K[7] = 1.455f;
nuclear@0 1023 distortions[numDistortions].Config.K[8] = 1.620f;
nuclear@0 1024 distortions[numDistortions].Config.K[9] = 1.840f;
nuclear@0 1025 distortions[numDistortions].Config.K[10] = 2.200f;
nuclear@0 1026 distortions[numDistortions].MaxRadius = 1.0f;
nuclear@0 1027
nuclear@0 1028 defaultDistortion = numDistortions; // this is the default
nuclear@0 1029 numDistortions++;
nuclear@0 1030
nuclear@0 1031 distortions[numDistortions] = distortions[0];
nuclear@0 1032 distortions[numDistortions].EyeRelief = 0.020f;
nuclear@0 1033 numDistortions++;
nuclear@0 1034
nuclear@0 1035 // Chromatic aberration doesn't seem to change with eye relief.
nuclear@0 1036 for ( int i = 0; i < numDistortions; i++ )
nuclear@0 1037 {
nuclear@0 1038 distortions[i].Config.ChromaticAberration[0] = -0.006f;
nuclear@0 1039 distortions[i].Config.ChromaticAberration[1] = 0.0f;
nuclear@0 1040 distortions[i].Config.ChromaticAberration[2] = 0.014f;
nuclear@0 1041 distortions[i].Config.ChromaticAberration[3] = 0.0f;
nuclear@0 1042 }
nuclear@0 1043 }
nuclear@0 1044 else if ( hmd.EyeCups == EyeCup_PinkA || hmd.EyeCups == EyeCup_DK2A )
nuclear@0 1045 {
nuclear@0 1046 // Tuned Crystal Cove & DK2 Lens (CES & GDC)
nuclear@0 1047 numDistortions = 0;
nuclear@0 1048
nuclear@0 1049
nuclear@0 1050 distortions[numDistortions].EyeRelief = 0.008f;
nuclear@0 1051 distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.036f;
nuclear@0 1052 // TODO: Need to retune this distortion for minimum eye relief
nuclear@0 1053 distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10;
nuclear@0 1054 distortions[numDistortions].Config.K[0] = 1.003f;
nuclear@0 1055 distortions[numDistortions].Config.K[1] = 1.02f;
nuclear@0 1056 distortions[numDistortions].Config.K[2] = 1.042f;
nuclear@0 1057 distortions[numDistortions].Config.K[3] = 1.066f;
nuclear@0 1058 distortions[numDistortions].Config.K[4] = 1.094f;
nuclear@0 1059 distortions[numDistortions].Config.K[5] = 1.126f;
nuclear@0 1060 distortions[numDistortions].Config.K[6] = 1.162f;
nuclear@0 1061 distortions[numDistortions].Config.K[7] = 1.203f;
nuclear@0 1062 distortions[numDistortions].Config.K[8] = 1.25f;
nuclear@0 1063 distortions[numDistortions].Config.K[9] = 1.31f;
nuclear@0 1064 distortions[numDistortions].Config.K[10] = 1.38f;
nuclear@0 1065 distortions[numDistortions].MaxRadius = 1.0f;
nuclear@0 1066
nuclear@0 1067 distortions[numDistortions].Config.ChromaticAberration[0] = -0.0112f;
nuclear@0 1068 distortions[numDistortions].Config.ChromaticAberration[1] = -0.015f;
nuclear@0 1069 distortions[numDistortions].Config.ChromaticAberration[2] = 0.0187f;
nuclear@0 1070 distortions[numDistortions].Config.ChromaticAberration[3] = 0.015f;
nuclear@0 1071
nuclear@0 1072 numDistortions++;
nuclear@0 1073
nuclear@0 1074
nuclear@0 1075
nuclear@0 1076
nuclear@0 1077
nuclear@0 1078 distortions[numDistortions].EyeRelief = 0.018f;
nuclear@0 1079 distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.036f;
nuclear@0 1080
nuclear@0 1081 distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10;
nuclear@0 1082 distortions[numDistortions].Config.K[0] = 1.003f;
nuclear@0 1083 distortions[numDistortions].Config.K[1] = 1.02f;
nuclear@0 1084 distortions[numDistortions].Config.K[2] = 1.042f;
nuclear@0 1085 distortions[numDistortions].Config.K[3] = 1.066f;
nuclear@0 1086 distortions[numDistortions].Config.K[4] = 1.094f;
nuclear@0 1087 distortions[numDistortions].Config.K[5] = 1.126f;
nuclear@0 1088 distortions[numDistortions].Config.K[6] = 1.162f;
nuclear@0 1089 distortions[numDistortions].Config.K[7] = 1.203f;
nuclear@0 1090 distortions[numDistortions].Config.K[8] = 1.25f;
nuclear@0 1091 distortions[numDistortions].Config.K[9] = 1.31f;
nuclear@0 1092 distortions[numDistortions].Config.K[10] = 1.38f;
nuclear@0 1093 distortions[numDistortions].MaxRadius = 1.0f;
nuclear@0 1094
nuclear@0 1095 distortions[numDistortions].Config.ChromaticAberration[0] = -0.015f;
nuclear@0 1096 distortions[numDistortions].Config.ChromaticAberration[1] = -0.02f;
nuclear@0 1097 distortions[numDistortions].Config.ChromaticAberration[2] = 0.025f;
nuclear@0 1098 distortions[numDistortions].Config.ChromaticAberration[3] = 0.02f;
nuclear@0 1099
nuclear@0 1100 defaultDistortion = numDistortions; // this is the default
nuclear@0 1101 numDistortions++;
nuclear@0 1102
nuclear@0 1103 /*
nuclear@0 1104 // Orange Lens on DK2
nuclear@0 1105 distortions[numDistortions].EyeRelief = 0.010f;
nuclear@0 1106 distortions[numDistortions].Config.MetersPerTanAngleAtCenter = 0.031f;
nuclear@0 1107
nuclear@0 1108 distortions[numDistortions].Config.Eqn = Distortion_CatmullRom10;
nuclear@0 1109 distortions[numDistortions].Config.K[0] = 1.00f;
nuclear@0 1110 distortions[numDistortions].Config.K[1] = 1.0169f;
nuclear@0 1111 distortions[numDistortions].Config.K[2] = 1.0378f;
nuclear@0 1112 distortions[numDistortions].Config.K[3] = 1.0648f;
nuclear@0 1113 distortions[numDistortions].Config.K[4] = 1.0990f;
nuclear@0 1114 distortions[numDistortions].Config.K[5] = 1.141f;
nuclear@0 1115 distortions[numDistortions].Config.K[6] = 1.192f;
nuclear@0 1116 distortions[numDistortions].Config.K[7] = 1.255f;
nuclear@0 1117 distortions[numDistortions].Config.K[8] = 1.335f;
nuclear@0 1118 distortions[numDistortions].Config.K[9] = 1.435f;
nuclear@0 1119 distortions[numDistortions].Config.K[10] = 1.56f;
nuclear@0 1120 distortions[numDistortions].MaxRadius = 1.0f;
nuclear@0 1121 */
nuclear@0 1122 }
nuclear@0 1123 else
nuclear@0 1124 {
nuclear@0 1125 // Unknown lens.
nuclear@0 1126 // Use DK1 black lens settings, just so we can continue to run with something.
nuclear@0 1127 distortions[0].EyeRelief = 0.005f;
nuclear@0 1128 distortions[0].Config.MetersPerTanAngleAtCenter = 0.043875f;
nuclear@0 1129 distortions[0].Config.Eqn = Distortion_RecipPoly4;
nuclear@0 1130 distortions[0].Config.K[0] = 1.0f;
nuclear@0 1131 distortions[0].Config.K[1] = -0.3999f;
nuclear@0 1132 distortions[0].Config.K[2] = 0.2408f;
nuclear@0 1133 distortions[0].Config.K[3] = -0.4589f;
nuclear@0 1134 distortions[0].SampleRadius[0] = 0.2f;
nuclear@0 1135 distortions[0].SampleRadius[1] = 0.4f;
nuclear@0 1136 distortions[0].SampleRadius[2] = 0.6f;
nuclear@0 1137
nuclear@0 1138 distortions[1] = distortions[0];
nuclear@0 1139 distortions[1].EyeRelief = 0.010f;
nuclear@0 1140 numDistortions = 2;
nuclear@0 1141
nuclear@0 1142 // Chromatic aberration doesn't seem to change with eye relief.
nuclear@0 1143 for ( int i = 0; i < numDistortions; i++ )
nuclear@0 1144 {
nuclear@0 1145 // These are placeholder, they have not been tuned!
nuclear@0 1146 distortions[i].Config.ChromaticAberration[0] = 0.0f;
nuclear@0 1147 distortions[i].Config.ChromaticAberration[1] = 0.0f;
nuclear@0 1148 distortions[i].Config.ChromaticAberration[2] = 0.0f;
nuclear@0 1149 distortions[i].Config.ChromaticAberration[3] = 0.0f;
nuclear@0 1150 }
nuclear@0 1151 }
nuclear@0 1152
nuclear@0 1153 OVR_ASSERT(numDistortions < MaxDistortions);
nuclear@0 1154
nuclear@0 1155 DistortionDescriptor *pUpper = NULL;
nuclear@0 1156 DistortionDescriptor *pLower = NULL;
nuclear@0 1157 float lerpVal = 0.0f;
nuclear@0 1158 if (eyeReliefInMeters == 0)
nuclear@0 1159 { // Use a constant default distortion if an invalid eye-relief is supplied
nuclear@0 1160 pLower = &(distortions[defaultDistortion]);
nuclear@0 1161 pUpper = &(distortions[defaultDistortion]);
nuclear@0 1162 lerpVal = 0.0f;
nuclear@0 1163 }
nuclear@0 1164 else
nuclear@0 1165 {
nuclear@0 1166 for ( int i = 0; i < numDistortions-1; i++ )
nuclear@0 1167 {
nuclear@0 1168 OVR_ASSERT ( distortions[i].EyeRelief < distortions[i+1].EyeRelief );
nuclear@0 1169 if ( ( distortions[i].EyeRelief <= eyeReliefInMeters ) && ( distortions[i+1].EyeRelief > eyeReliefInMeters ) )
nuclear@0 1170 {
nuclear@0 1171 pLower = &(distortions[i]);
nuclear@0 1172 pUpper = &(distortions[i+1]);
nuclear@0 1173 lerpVal = ( eyeReliefInMeters - pLower->EyeRelief ) / ( pUpper->EyeRelief - pLower->EyeRelief );
nuclear@0 1174 // No break here - I want the ASSERT to check everything every time!
nuclear@0 1175 }
nuclear@0 1176 }
nuclear@0 1177 }
nuclear@0 1178
nuclear@0 1179 if ( pUpper == NULL )
nuclear@0 1180 {
nuclear@0 1181 #if 0
nuclear@0 1182 // Outside the range, so extrapolate rather than interpolate.
nuclear@0 1183 if ( distortions[0].EyeRelief > eyeReliefInMeters )
nuclear@0 1184 {
nuclear@0 1185 pLower = &(distortions[0]);
nuclear@0 1186 pUpper = &(distortions[1]);
nuclear@0 1187 }
nuclear@0 1188 else
nuclear@0 1189 {
nuclear@0 1190 OVR_ASSERT ( distortions[numDistortions-1].EyeRelief <= eyeReliefInMeters );
nuclear@0 1191 pLower = &(distortions[numDistortions-2]);
nuclear@0 1192 pUpper = &(distortions[numDistortions-1]);
nuclear@0 1193 }
nuclear@0 1194 lerpVal = ( eyeReliefInMeters - pLower->EyeRelief ) / ( pUpper->EyeRelief - pLower->EyeRelief );
nuclear@0 1195 #else
nuclear@0 1196 // Do not extrapolate, just clamp - slightly worried about people putting in bogus settings.
nuclear@0 1197 if ( distortions[0].EyeRelief > eyeReliefInMeters )
nuclear@0 1198 {
nuclear@0 1199 pLower = &(distortions[0]);
nuclear@0 1200 pUpper = &(distortions[0]);
nuclear@0 1201 }
nuclear@0 1202 else
nuclear@0 1203 {
nuclear@0 1204 OVR_ASSERT ( distortions[numDistortions-1].EyeRelief <= eyeReliefInMeters );
nuclear@0 1205 pLower = &(distortions[numDistortions-1]);
nuclear@0 1206 pUpper = &(distortions[numDistortions-1]);
nuclear@0 1207 }
nuclear@0 1208 lerpVal = 0.0f;
nuclear@0 1209 #endif
nuclear@0 1210 }
nuclear@0 1211 float invLerpVal = 1.0f - lerpVal;
nuclear@0 1212
nuclear@0 1213 pLower->Config.MaxR = pLower->MaxRadius;
nuclear@0 1214 pUpper->Config.MaxR = pUpper->MaxRadius;
nuclear@0 1215
nuclear@0 1216 LensConfig result;
nuclear@0 1217 // Where is the edge of the lens - no point modelling further than this.
nuclear@0 1218 float maxValidRadius = invLerpVal * pLower->MaxRadius + lerpVal * pUpper->MaxRadius;
nuclear@0 1219 result.MaxR = maxValidRadius;
nuclear@0 1220
nuclear@0 1221 switch ( distortionType )
nuclear@0 1222 {
nuclear@0 1223 case Distortion_Poly4:
nuclear@0 1224 // Deprecated
nuclear@0 1225 OVR_ASSERT ( false );
nuclear@0 1226 break;
nuclear@0 1227 case Distortion_RecipPoly4:{
nuclear@0 1228 // Lerp control points and fit an equation to them.
nuclear@0 1229 float fitX[4];
nuclear@0 1230 float fitY[4];
nuclear@0 1231 fitX[0] = 0.0f;
nuclear@0 1232 fitY[0] = 1.0f;
nuclear@0 1233 for ( int ctrlPt = 1; ctrlPt < 4; ctrlPt ++ )
nuclear@0 1234 {
nuclear@0 1235 // SampleRadius is not valid for Distortion_RecipPoly4 types.
nuclear@0 1236 float radiusLerp = ( invLerpVal * pLower->MaxRadius + lerpVal * pUpper->MaxRadius ) * ( (float)ctrlPt / 4.0f );
nuclear@0 1237 float radiusLerpSq = radiusLerp * radiusLerp;
nuclear@0 1238 float fitYLower = pLower->Config.DistortionFnScaleRadiusSquared ( radiusLerpSq );
nuclear@0 1239 float fitYUpper = pUpper->Config.DistortionFnScaleRadiusSquared ( radiusLerpSq );
nuclear@0 1240 fitX[ctrlPt] = radiusLerpSq;
nuclear@0 1241 fitY[ctrlPt] = 1.0f / ( invLerpVal * fitYLower + lerpVal * fitYUpper );
nuclear@0 1242 }
nuclear@0 1243
nuclear@0 1244 result.Eqn = Distortion_RecipPoly4;
nuclear@0 1245 bool bSuccess = FitCubicPolynomial ( result.K, fitX, fitY );
nuclear@0 1246 OVR_ASSERT ( bSuccess );
nuclear@0 1247 OVR_UNUSED ( bSuccess );
nuclear@0 1248
nuclear@0 1249 // Set up the fast inverse.
nuclear@0 1250 float maxRDist = result.DistortionFn ( maxValidRadius );
nuclear@0 1251 result.MaxInvR = maxRDist;
nuclear@0 1252 result.SetUpInverseApprox();
nuclear@0 1253
nuclear@0 1254 }break;
nuclear@0 1255
nuclear@0 1256 case Distortion_CatmullRom10:{
nuclear@0 1257
nuclear@0 1258 // Evenly sample & lerp points on the curve.
nuclear@0 1259 const int NumSegments = LensConfig::NumCoefficients;
nuclear@0 1260 result.MaxR = maxValidRadius;
nuclear@0 1261 // Directly interpolate the K0 values
nuclear@0 1262 result.K[0] = invLerpVal * pLower->Config.K[0] + lerpVal * pUpper->Config.K[0];
nuclear@0 1263
nuclear@0 1264 // Sample and interpolate the distortion curves to derive K[1] ... K[n]
nuclear@0 1265 for ( int ctrlPt = 1; ctrlPt < NumSegments; ctrlPt++ )
nuclear@0 1266 {
nuclear@0 1267 float radiusSq = ( (float)ctrlPt / (float)(NumSegments-1) ) * maxValidRadius * maxValidRadius;
nuclear@0 1268 float fitYLower = pLower->Config.DistortionFnScaleRadiusSquared ( radiusSq );
nuclear@0 1269 float fitYUpper = pUpper->Config.DistortionFnScaleRadiusSquared ( radiusSq );
nuclear@0 1270 float fitLerp = invLerpVal * fitYLower + lerpVal * fitYUpper;
nuclear@0 1271 result.K[ctrlPt] = fitLerp;
nuclear@0 1272 }
nuclear@0 1273
nuclear@0 1274 result.Eqn = Distortion_CatmullRom10;
nuclear@0 1275
nuclear@0 1276 for ( int ctrlPt = 1; ctrlPt < NumSegments; ctrlPt++ )
nuclear@0 1277 {
nuclear@0 1278 float radiusSq = ( (float)ctrlPt / (float)(NumSegments-1) ) * maxValidRadius * maxValidRadius;
nuclear@0 1279 float val = result.DistortionFnScaleRadiusSquared ( radiusSq );
nuclear@0 1280 OVR_ASSERT ( Alg::Abs ( val - result.K[ctrlPt] ) < 0.0001f );
nuclear@0 1281 OVR_UNUSED1(val); // For release build.
nuclear@0 1282 }
nuclear@0 1283
nuclear@0 1284 // Set up the fast inverse.
nuclear@0 1285 float maxRDist = result.DistortionFn ( maxValidRadius );
nuclear@0 1286 result.MaxInvR = maxRDist;
nuclear@0 1287 result.SetUpInverseApprox();
nuclear@0 1288
nuclear@0 1289 }break;
nuclear@0 1290
nuclear@0 1291 default: OVR_ASSERT ( false ); break;
nuclear@0 1292 }
nuclear@0 1293
nuclear@0 1294
nuclear@0 1295 // Chromatic aberration.
nuclear@0 1296 result.ChromaticAberration[0] = invLerpVal * pLower->Config.ChromaticAberration[0] + lerpVal * pUpper->Config.ChromaticAberration[0];
nuclear@0 1297 result.ChromaticAberration[1] = invLerpVal * pLower->Config.ChromaticAberration[1] + lerpVal * pUpper->Config.ChromaticAberration[1];
nuclear@0 1298 result.ChromaticAberration[2] = invLerpVal * pLower->Config.ChromaticAberration[2] + lerpVal * pUpper->Config.ChromaticAberration[2];
nuclear@0 1299 result.ChromaticAberration[3] = invLerpVal * pLower->Config.ChromaticAberration[3] + lerpVal * pUpper->Config.ChromaticAberration[3];
nuclear@0 1300
nuclear@0 1301 // Scale.
nuclear@0 1302 result.MetersPerTanAngleAtCenter = pLower->Config.MetersPerTanAngleAtCenter * invLerpVal +
nuclear@0 1303 pUpper->Config.MetersPerTanAngleAtCenter * lerpVal;
nuclear@0 1304 /*
nuclear@0 1305 // Commented out - Causes ASSERT with no HMD plugged in
nuclear@0 1306 #ifdef OVR_BUILD_DEBUG
nuclear@0 1307 if ( distortionType == Distortion_CatmullRom10 )
nuclear@0 1308 {
nuclear@0 1309 TestSaveLoadLensConfig ( result );
nuclear@0 1310 }
nuclear@0 1311 #endif
nuclear@0 1312 */
nuclear@0 1313 return result;
nuclear@0 1314 }
nuclear@0 1315
nuclear@0 1316
nuclear@0 1317 DistortionRenderDesc CalculateDistortionRenderDesc ( StereoEye eyeType, HmdRenderInfo const &hmd,
nuclear@0 1318 const LensConfig *pLensOverride /*= NULL */ )
nuclear@0 1319 {
nuclear@0 1320 // From eye relief, IPD and device characteristics, we get the distortion mapping.
nuclear@0 1321 // This distortion does the following things:
nuclear@0 1322 // 1. It undoes the distortion that happens at the edges of the lens.
nuclear@0 1323 // 2. It maps the undistorted field into "retina" space.
nuclear@0 1324 // So the input is a pixel coordinate - the physical pixel on the display itself.
nuclear@0 1325 // The output is the real-world direction of the ray from this pixel as it comes out of the lens and hits the eye.
nuclear@0 1326 // However we typically think of rays "coming from" the eye, so the direction (TanAngleX,TanAngleY,1) is the direction
nuclear@0 1327 // that the pixel appears to be in real-world space, where AngleX and AngleY are relative to the straight-ahead vector.
nuclear@0 1328 // If your renderer is a raytracer, you can use this vector directly (normalize as appropriate).
nuclear@0 1329 // However in standard rasterisers, we have rendered a 2D image and are putting it in front of the eye,
nuclear@0 1330 // so we then need a mapping from this space to the [-1,1] UV coordinate space, which depends on exactly
nuclear@0 1331 // where "in space" the app wants to put that rendertarget.
nuclear@0 1332 // Where in space, and how large this rendertarget is, is completely up to the app and/or user,
nuclear@0 1333 // though of course we can provide some useful hints.
nuclear@0 1334
nuclear@0 1335 // TODO: Use IPD and eye relief to modify distortion (i.e. non-radial component)
nuclear@0 1336 // TODO: cope with lenses that don't produce collimated light.
nuclear@0 1337 // This means that IPD relative to the lens separation changes the light vergence,
nuclear@0 1338 // and so we actually need to change where the image is displayed.
nuclear@0 1339
nuclear@0 1340 const HmdRenderInfo::EyeConfig &hmdEyeConfig = ( eyeType == StereoEye_Left ) ? hmd.EyeLeft : hmd.EyeRight;
nuclear@0 1341
nuclear@0 1342 DistortionRenderDesc localDistortion;
nuclear@0 1343 localDistortion.Lens = hmdEyeConfig.Distortion;
nuclear@0 1344
nuclear@0 1345 if ( pLensOverride != NULL )
nuclear@0 1346 {
nuclear@0 1347 localDistortion.Lens = *pLensOverride;
nuclear@0 1348 }
nuclear@0 1349
nuclear@0 1350 Sizef pixelsPerMeter(hmd.ResolutionInPixels.w / ( hmd.ScreenSizeInMeters.w - hmd.ScreenGapSizeInMeters ),
nuclear@0 1351 hmd.ResolutionInPixels.h / hmd.ScreenSizeInMeters.h);
nuclear@0 1352
nuclear@0 1353 localDistortion.PixelsPerTanAngleAtCenter = (pixelsPerMeter * localDistortion.Lens.MetersPerTanAngleAtCenter).ToVector();
nuclear@0 1354 // Same thing, scaled to [-1,1] for each eye, rather than pixels.
nuclear@0 1355
nuclear@0 1356 localDistortion.TanEyeAngleScale = Vector2f(0.25f, 0.5f).EntrywiseMultiply(
nuclear@0 1357 (hmd.ScreenSizeInMeters / localDistortion.Lens.MetersPerTanAngleAtCenter).ToVector());
nuclear@0 1358
nuclear@0 1359 // <--------------left eye------------------><-ScreenGapSizeInMeters-><--------------right eye----------------->
nuclear@0 1360 // <------------------------------------------ScreenSizeInMeters.Width----------------------------------------->
nuclear@0 1361 // <----------------LensSeparationInMeters--------------->
nuclear@0 1362 // <--centerFromLeftInMeters->
nuclear@0 1363 // ^
nuclear@0 1364 // Center of lens
nuclear@0 1365
nuclear@0 1366 // Find the lens centers in scale of [-1,+1] (NDC) in left eye.
nuclear@0 1367 float visibleWidthOfOneEye = 0.5f * ( hmd.ScreenSizeInMeters.w - hmd.ScreenGapSizeInMeters );
nuclear@0 1368 float centerFromLeftInMeters = ( hmd.ScreenSizeInMeters.w - hmd.LensSeparationInMeters ) * 0.5f;
nuclear@0 1369 localDistortion.LensCenter.x = ( centerFromLeftInMeters / visibleWidthOfOneEye ) * 2.0f - 1.0f;
nuclear@0 1370 localDistortion.LensCenter.y = ( hmd.CenterFromTopInMeters / hmd.ScreenSizeInMeters.h ) * 2.0f - 1.0f;
nuclear@0 1371 if ( eyeType == StereoEye_Right )
nuclear@0 1372 {
nuclear@0 1373 localDistortion.LensCenter.x = -localDistortion.LensCenter.x;
nuclear@0 1374 }
nuclear@0 1375
nuclear@0 1376 return localDistortion;
nuclear@0 1377 }
nuclear@0 1378
nuclear@0 1379 FovPort CalculateFovFromEyePosition ( float eyeReliefInMeters,
nuclear@0 1380 float offsetToRightInMeters,
nuclear@0 1381 float offsetDownwardsInMeters,
nuclear@0 1382 float lensDiameterInMeters,
nuclear@0 1383 float extraEyeRotationInRadians /*= 0.0f*/ )
nuclear@0 1384 {
nuclear@0 1385 // 2D view of things:
nuclear@0 1386 // |-| <--- offsetToRightInMeters (in this case, it is negative)
nuclear@0 1387 // |=======C=======| <--- lens surface (C=center)
nuclear@0 1388 // \ | _/
nuclear@0 1389 // \ R _/
nuclear@0 1390 // \ | _/
nuclear@0 1391 // \ | _/
nuclear@0 1392 // \|/
nuclear@0 1393 // O <--- center of pupil
nuclear@0 1394
nuclear@0 1395 // (technically the lens is round rather than square, so it's not correct to
nuclear@0 1396 // separate vertical and horizontal like this, but it's close enough)
nuclear@0 1397 float halfLensDiameter = lensDiameterInMeters * 0.5f;
nuclear@0 1398 FovPort fovPort;
nuclear@0 1399 fovPort.UpTan = ( halfLensDiameter + offsetDownwardsInMeters ) / eyeReliefInMeters;
nuclear@0 1400 fovPort.DownTan = ( halfLensDiameter - offsetDownwardsInMeters ) / eyeReliefInMeters;
nuclear@0 1401 fovPort.LeftTan = ( halfLensDiameter + offsetToRightInMeters ) / eyeReliefInMeters;
nuclear@0 1402 fovPort.RightTan = ( halfLensDiameter - offsetToRightInMeters ) / eyeReliefInMeters;
nuclear@0 1403
nuclear@0 1404 if ( extraEyeRotationInRadians > 0.0f )
nuclear@0 1405 {
nuclear@0 1406 // That's the basic looking-straight-ahead eye position relative to the lens.
nuclear@0 1407 // But if you look left, the pupil moves left as the eyeball rotates, which
nuclear@0 1408 // means you can see more to the right than this geometry suggests.
nuclear@0 1409 // So add in the bounds for the extra movement of the pupil.
nuclear@0 1410
nuclear@0 1411 // Beyond 30 degrees does not increase FOV because the pupil starts moving backwards more than sideways.
nuclear@0 1412 extraEyeRotationInRadians = Alg::Min ( DegreeToRad ( 30.0f ), Alg::Max ( 0.0f, extraEyeRotationInRadians ) );
nuclear@0 1413
nuclear@0 1414 // The rotation of the eye is a bit more complex than a simple circle. The center of rotation
nuclear@0 1415 // at 13.5mm from cornea is slightly further back than the actual center of the eye.
nuclear@0 1416 // Additionally the rotation contains a small lateral component as the muscles pull the eye
nuclear@0 1417 const float eyeballCenterToPupil = 0.0135f; // center of eye rotation
nuclear@0 1418 const float eyeballLateralPull = 0.001f * (extraEyeRotationInRadians / DegreeToRad ( 30.0f)); // lateral motion as linear function
nuclear@0 1419 float extraTranslation = eyeballCenterToPupil * sinf ( extraEyeRotationInRadians ) + eyeballLateralPull;
nuclear@0 1420 float extraRelief = eyeballCenterToPupil * ( 1.0f - cosf ( extraEyeRotationInRadians ) );
nuclear@0 1421
nuclear@0 1422 fovPort.UpTan = Alg::Max ( fovPort.UpTan , ( halfLensDiameter + offsetDownwardsInMeters + extraTranslation ) / ( eyeReliefInMeters + extraRelief ) );
nuclear@0 1423 fovPort.DownTan = Alg::Max ( fovPort.DownTan , ( halfLensDiameter - offsetDownwardsInMeters + extraTranslation ) / ( eyeReliefInMeters + extraRelief ) );
nuclear@0 1424 fovPort.LeftTan = Alg::Max ( fovPort.LeftTan , ( halfLensDiameter + offsetToRightInMeters + extraTranslation ) / ( eyeReliefInMeters + extraRelief ) );
nuclear@0 1425 fovPort.RightTan = Alg::Max ( fovPort.RightTan, ( halfLensDiameter - offsetToRightInMeters + extraTranslation ) / ( eyeReliefInMeters + extraRelief ) );
nuclear@0 1426 }
nuclear@0 1427
nuclear@0 1428 return fovPort;
nuclear@0 1429 }
nuclear@0 1430
nuclear@0 1431
nuclear@0 1432
nuclear@0 1433 FovPort CalculateFovFromHmdInfo ( StereoEye eyeType,
nuclear@0 1434 DistortionRenderDesc const &distortion,
nuclear@0 1435 HmdRenderInfo const &hmd,
nuclear@0 1436 float extraEyeRotationInRadians /*= 0.0f*/ )
nuclear@0 1437 {
nuclear@0 1438 FovPort fovPort;
nuclear@0 1439 float eyeReliefInMeters;
nuclear@0 1440 float offsetToRightInMeters;
nuclear@0 1441 if ( eyeType == StereoEye_Right )
nuclear@0 1442 {
nuclear@0 1443 eyeReliefInMeters = hmd.EyeRight.ReliefInMeters;
nuclear@0 1444 offsetToRightInMeters = hmd.EyeRight.NoseToPupilInMeters - 0.5f * hmd.LensSeparationInMeters;
nuclear@0 1445 }
nuclear@0 1446 else
nuclear@0 1447 {
nuclear@0 1448 eyeReliefInMeters = hmd.EyeLeft.ReliefInMeters;
nuclear@0 1449 offsetToRightInMeters = -(hmd.EyeLeft.NoseToPupilInMeters - 0.5f * hmd.LensSeparationInMeters);
nuclear@0 1450 }
nuclear@0 1451
nuclear@0 1452 // Limit the eye-relief to 6 mm for FOV calculations since this just tends to spread off-screen
nuclear@0 1453 // and get clamped anyways on DK1 (but in Unity it continues to spreads and causes
nuclear@0 1454 // unnecessarily large render targets)
nuclear@0 1455 eyeReliefInMeters = Alg::Max(eyeReliefInMeters, 0.006f);
nuclear@0 1456
nuclear@0 1457 // Central view.
nuclear@0 1458 fovPort = CalculateFovFromEyePosition ( eyeReliefInMeters,
nuclear@0 1459 offsetToRightInMeters,
nuclear@0 1460 0.0f,
nuclear@0 1461 hmd.LensDiameterInMeters,
nuclear@0 1462 extraEyeRotationInRadians );
nuclear@0 1463
nuclear@0 1464 // clamp to the screen
nuclear@0 1465 fovPort = ClampToPhysicalScreenFov ( eyeType, distortion, fovPort );
nuclear@0 1466
nuclear@0 1467 return fovPort;
nuclear@0 1468 }
nuclear@0 1469
nuclear@0 1470
nuclear@0 1471
nuclear@0 1472 FovPort GetPhysicalScreenFov ( StereoEye eyeType, DistortionRenderDesc const &distortion )
nuclear@0 1473 {
nuclear@0 1474 OVR_UNUSED1 ( eyeType );
nuclear@0 1475
nuclear@0 1476 FovPort resultFovPort;
nuclear@0 1477
nuclear@0 1478 // Figure out the boundaries of the screen. We take the middle pixel of the screen,
nuclear@0 1479 // move to each of the four screen edges, and transform those back into TanAngle space.
nuclear@0 1480 Vector2f dmiddle = distortion.LensCenter;
nuclear@0 1481
nuclear@0 1482 // The gotcha is that for some distortion functions, the map will "wrap around"
nuclear@0 1483 // for screen pixels that are not actually visible to the user (especially on DK1,
nuclear@0 1484 // which has a lot of invisible pixels), and map to pixels that are close to the middle.
nuclear@0 1485 // This means the edges of the screen will actually be
nuclear@0 1486 // "closer" than the visible bounds, so we'll clip too aggressively.
nuclear@0 1487
nuclear@0 1488 // Solution - step gradually towards the boundary, noting the maximum distance.
nuclear@0 1489 struct FunctionHider
nuclear@0 1490 {
nuclear@0 1491 static FovPort FindRange ( Vector2f from, Vector2f to, int numSteps,
nuclear@0 1492 DistortionRenderDesc const &distortionL )
nuclear@0 1493 {
nuclear@0 1494 FovPort result;
nuclear@0 1495 result.UpTan = 0.0f;
nuclear@0 1496 result.DownTan = 0.0f;
nuclear@0 1497 result.LeftTan = 0.0f;
nuclear@0 1498 result.RightTan = 0.0f;
nuclear@0 1499
nuclear@0 1500 float stepScale = 1.0f / ( numSteps - 1 );
nuclear@0 1501 for ( int step = 0; step < numSteps; step++ )
nuclear@0 1502 {
nuclear@0 1503 float lerpFactor = stepScale * (float)step;
nuclear@0 1504 Vector2f sample = from + (to - from) * lerpFactor;
nuclear@0 1505 Vector2f tanEyeAngle = TransformScreenNDCToTanFovSpace ( distortionL, sample );
nuclear@0 1506
nuclear@0 1507 result.LeftTan = Alg::Max ( result.LeftTan, -tanEyeAngle.x );
nuclear@0 1508 result.RightTan = Alg::Max ( result.RightTan, tanEyeAngle.x );
nuclear@0 1509 result.UpTan = Alg::Max ( result.UpTan, -tanEyeAngle.y );
nuclear@0 1510 result.DownTan = Alg::Max ( result.DownTan, tanEyeAngle.y );
nuclear@0 1511 }
nuclear@0 1512 return result;
nuclear@0 1513 }
nuclear@0 1514 };
nuclear@0 1515
nuclear@0 1516 FovPort leftFovPort = FunctionHider::FindRange( dmiddle, Vector2f( -1.0f, dmiddle.y ), 10, distortion );
nuclear@0 1517 FovPort rightFovPort = FunctionHider::FindRange( dmiddle, Vector2f( 1.0f, dmiddle.y ), 10, distortion );
nuclear@0 1518 FovPort upFovPort = FunctionHider::FindRange( dmiddle, Vector2f( dmiddle.x, -1.0f ), 10, distortion );
nuclear@0 1519 FovPort downFovPort = FunctionHider::FindRange( dmiddle, Vector2f( dmiddle.x, 1.0f ), 10, distortion );
nuclear@0 1520
nuclear@0 1521 resultFovPort.LeftTan = leftFovPort.LeftTan;
nuclear@0 1522 resultFovPort.RightTan = rightFovPort.RightTan;
nuclear@0 1523 resultFovPort.UpTan = upFovPort.UpTan;
nuclear@0 1524 resultFovPort.DownTan = downFovPort.DownTan;
nuclear@0 1525
nuclear@0 1526 return resultFovPort;
nuclear@0 1527 }
nuclear@0 1528
nuclear@0 1529 FovPort ClampToPhysicalScreenFov( StereoEye eyeType, DistortionRenderDesc const &distortion,
nuclear@0 1530 FovPort inputFovPort )
nuclear@0 1531 {
nuclear@0 1532 FovPort resultFovPort;
nuclear@0 1533 FovPort phsyicalFovPort = GetPhysicalScreenFov ( eyeType, distortion );
nuclear@0 1534 resultFovPort.LeftTan = Alg::Min ( inputFovPort.LeftTan, phsyicalFovPort.LeftTan );
nuclear@0 1535 resultFovPort.RightTan = Alg::Min ( inputFovPort.RightTan, phsyicalFovPort.RightTan );
nuclear@0 1536 resultFovPort.UpTan = Alg::Min ( inputFovPort.UpTan, phsyicalFovPort.UpTan );
nuclear@0 1537 resultFovPort.DownTan = Alg::Min ( inputFovPort.DownTan, phsyicalFovPort.DownTan );
nuclear@0 1538
nuclear@0 1539 return resultFovPort;
nuclear@0 1540 }
nuclear@0 1541
nuclear@0 1542 Sizei CalculateIdealPixelSize ( StereoEye eyeType, DistortionRenderDesc const &distortion,
nuclear@0 1543 FovPort tanHalfFov, float pixelsPerDisplayPixel )
nuclear@0 1544 {
nuclear@0 1545 OVR_UNUSED(eyeType); // might be useful in the future if we do overlapping fovs
nuclear@0 1546
nuclear@0 1547 Sizei result;
nuclear@0 1548 // TODO: if the app passes in a FOV that doesn't cover the centre, use the distortion values for the nearest edge/corner to match pixel size.
nuclear@0 1549 result.w = (int)(0.5f + pixelsPerDisplayPixel * distortion.PixelsPerTanAngleAtCenter.x * ( tanHalfFov.LeftTan + tanHalfFov.RightTan ) );
nuclear@0 1550 result.h = (int)(0.5f + pixelsPerDisplayPixel * distortion.PixelsPerTanAngleAtCenter.y * ( tanHalfFov.UpTan + tanHalfFov.DownTan ) );
nuclear@0 1551 return result;
nuclear@0 1552 }
nuclear@0 1553
nuclear@0 1554 Recti GetFramebufferViewport ( StereoEye eyeType, HmdRenderInfo const &hmd )
nuclear@0 1555 {
nuclear@0 1556 Recti result;
nuclear@0 1557 result.w = hmd.ResolutionInPixels.w/2;
nuclear@0 1558 result.h = hmd.ResolutionInPixels.h;
nuclear@0 1559 result.x = 0;
nuclear@0 1560 result.y = 0;
nuclear@0 1561 if ( eyeType == StereoEye_Right )
nuclear@0 1562 {
nuclear@0 1563 result.x = (hmd.ResolutionInPixels.w+1)/2; // Round up, not down.
nuclear@0 1564 }
nuclear@0 1565 return result;
nuclear@0 1566 }
nuclear@0 1567
nuclear@0 1568
nuclear@0 1569 ScaleAndOffset2D CreateNDCScaleAndOffsetFromFov ( FovPort tanHalfFov )
nuclear@0 1570 {
nuclear@0 1571 float projXScale = 2.0f / ( tanHalfFov.LeftTan + tanHalfFov.RightTan );
nuclear@0 1572 float projXOffset = ( tanHalfFov.LeftTan - tanHalfFov.RightTan ) * projXScale * 0.5f;
nuclear@0 1573 float projYScale = 2.0f / ( tanHalfFov.UpTan + tanHalfFov.DownTan );
nuclear@0 1574 float projYOffset = ( tanHalfFov.UpTan - tanHalfFov.DownTan ) * projYScale * 0.5f;
nuclear@0 1575
nuclear@0 1576 ScaleAndOffset2D result;
nuclear@0 1577 result.Scale = Vector2f(projXScale, projYScale);
nuclear@0 1578 result.Offset = Vector2f(projXOffset, projYOffset);
nuclear@0 1579 // Hey - why is that Y.Offset negated?
nuclear@0 1580 // It's because a projection matrix transforms from world coords with Y=up,
nuclear@0 1581 // whereas this is from NDC which is Y=down.
nuclear@0 1582
nuclear@0 1583 return result;
nuclear@0 1584 }
nuclear@0 1585
nuclear@0 1586
nuclear@0 1587 ScaleAndOffset2D CreateUVScaleAndOffsetfromNDCScaleandOffset ( ScaleAndOffset2D scaleAndOffsetNDC,
nuclear@0 1588 Recti renderedViewport,
nuclear@0 1589 Sizei renderTargetSize )
nuclear@0 1590 {
nuclear@0 1591 // scaleAndOffsetNDC takes you to NDC space [-1,+1] within the given viewport on the rendertarget.
nuclear@0 1592 // We want a scale to instead go to actual UV coordinates you can sample with,
nuclear@0 1593 // which need [0,1] and ignore the viewport.
nuclear@0 1594 ScaleAndOffset2D result;
nuclear@0 1595 // Scale [-1,1] to [0,1]
nuclear@0 1596 result.Scale = scaleAndOffsetNDC.Scale * 0.5f;
nuclear@0 1597 result.Offset = scaleAndOffsetNDC.Offset * 0.5f + Vector2f(0.5f);
nuclear@0 1598
nuclear@0 1599 // ...but we will have rendered to a subsection of the RT, so scale for that.
nuclear@0 1600 Vector2f scale( (float)renderedViewport.w / (float)renderTargetSize.w,
nuclear@0 1601 (float)renderedViewport.h / (float)renderTargetSize.h );
nuclear@0 1602 Vector2f offset( (float)renderedViewport.x / (float)renderTargetSize.w,
nuclear@0 1603 (float)renderedViewport.y / (float)renderTargetSize.h );
nuclear@0 1604
nuclear@0 1605 result.Scale = result.Scale.EntrywiseMultiply(scale);
nuclear@0 1606 result.Offset = result.Offset.EntrywiseMultiply(scale) + offset;
nuclear@0 1607 return result;
nuclear@0 1608 }
nuclear@0 1609
nuclear@0 1610
nuclear@0 1611
nuclear@0 1612 Matrix4f CreateProjection( bool rightHanded, FovPort tanHalfFov,
nuclear@0 1613 float zNear /*= 0.01f*/, float zFar /*= 10000.0f*/ )
nuclear@0 1614 {
nuclear@0 1615 // A projection matrix is very like a scaling from NDC, so we can start with that.
nuclear@0 1616 ScaleAndOffset2D scaleAndOffset = CreateNDCScaleAndOffsetFromFov ( tanHalfFov );
nuclear@0 1617
nuclear@0 1618 float handednessScale = 1.0f;
nuclear@0 1619 if ( rightHanded )
nuclear@0 1620 {
nuclear@0 1621 handednessScale = -1.0f;
nuclear@0 1622 }
nuclear@0 1623
nuclear@0 1624 Matrix4f projection;
nuclear@0 1625 // Produces X result, mapping clip edges to [-w,+w]
nuclear@0 1626 projection.M[0][0] = scaleAndOffset.Scale.x;
nuclear@0 1627 projection.M[0][1] = 0.0f;
nuclear@0 1628 projection.M[0][2] = handednessScale * scaleAndOffset.Offset.x;
nuclear@0 1629 projection.M[0][3] = 0.0f;
nuclear@0 1630
nuclear@0 1631 // Produces Y result, mapping clip edges to [-w,+w]
nuclear@0 1632 // Hey - why is that YOffset negated?
nuclear@0 1633 // It's because a projection matrix transforms from world coords with Y=up,
nuclear@0 1634 // whereas this is derived from an NDC scaling, which is Y=down.
nuclear@0 1635 projection.M[1][0] = 0.0f;
nuclear@0 1636 projection.M[1][1] = scaleAndOffset.Scale.y;
nuclear@0 1637 projection.M[1][2] = handednessScale * -scaleAndOffset.Offset.y;
nuclear@0 1638 projection.M[1][3] = 0.0f;
nuclear@0 1639
nuclear@0 1640 // Produces Z-buffer result - app needs to fill this in with whatever Z range it wants.
nuclear@0 1641 // We'll just use some defaults for now.
nuclear@0 1642 projection.M[2][0] = 0.0f;
nuclear@0 1643 projection.M[2][1] = 0.0f;
nuclear@0 1644 projection.M[2][2] = -handednessScale * zFar / (zNear - zFar);
nuclear@0 1645 projection.M[2][3] = (zFar * zNear) / (zNear - zFar);
nuclear@0 1646
nuclear@0 1647 // Produces W result (= Z in)
nuclear@0 1648 projection.M[3][0] = 0.0f;
nuclear@0 1649 projection.M[3][1] = 0.0f;
nuclear@0 1650 projection.M[3][2] = handednessScale;
nuclear@0 1651 projection.M[3][3] = 0.0f;
nuclear@0 1652
nuclear@0 1653 return projection;
nuclear@0 1654 }
nuclear@0 1655
nuclear@0 1656
nuclear@0 1657 Matrix4f CreateOrthoSubProjection ( bool rightHanded, StereoEye eyeType,
nuclear@0 1658 float tanHalfFovX, float tanHalfFovY,
nuclear@0 1659 float unitsX, float unitsY,
nuclear@0 1660 float distanceFromCamera, float interpupillaryDistance,
nuclear@0 1661 Matrix4f const &projection,
nuclear@0 1662 float zNear /*= 0.0f*/, float zFar /*= 0.0f*/ )
nuclear@0 1663 {
nuclear@0 1664 OVR_UNUSED1 ( rightHanded );
nuclear@0 1665
nuclear@0 1666 float orthoHorizontalOffset = interpupillaryDistance * 0.5f / distanceFromCamera;
nuclear@0 1667 switch ( eyeType )
nuclear@0 1668 {
nuclear@0 1669 case StereoEye_Center:
nuclear@0 1670 orthoHorizontalOffset = 0.0f;
nuclear@0 1671 break;
nuclear@0 1672 case StereoEye_Left:
nuclear@0 1673 break;
nuclear@0 1674 case StereoEye_Right:
nuclear@0 1675 orthoHorizontalOffset = -orthoHorizontalOffset;
nuclear@0 1676 break;
nuclear@0 1677 default: OVR_ASSERT ( false ); break;
nuclear@0 1678 }
nuclear@0 1679
nuclear@0 1680 // Current projection maps real-world vector (x,y,1) to the RT.
nuclear@0 1681 // We want to find the projection that maps the range [-FovPixels/2,FovPixels/2] to
nuclear@0 1682 // the physical [-orthoHalfFov,orthoHalfFov]
nuclear@0 1683 // Note moving the offset from M[0][2]+M[1][2] to M[0][3]+M[1][3] - this means
nuclear@0 1684 // we don't have to feed in Z=1 all the time.
nuclear@0 1685 // The horizontal offset math is a little hinky because the destination is
nuclear@0 1686 // actually [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]
nuclear@0 1687 // So we need to first map [-FovPixels/2,FovPixels/2] to
nuclear@0 1688 // [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]:
nuclear@0 1689 // x1 = x0 * orthoHalfFov/(FovPixels/2) + orthoHorizontalOffset;
nuclear@0 1690 // = x0 * 2*orthoHalfFov/FovPixels + orthoHorizontalOffset;
nuclear@0 1691 // But then we need the sam mapping as the existing projection matrix, i.e.
nuclear@0 1692 // x2 = x1 * Projection.M[0][0] + Projection.M[0][2];
nuclear@0 1693 // = x0 * (2*orthoHalfFov/FovPixels + orthoHorizontalOffset) * Projection.M[0][0] + Projection.M[0][2];
nuclear@0 1694 // = x0 * Projection.M[0][0]*2*orthoHalfFov/FovPixels +
nuclear@0 1695 // orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2];
nuclear@0 1696 // So in the new projection matrix we need to scale by Projection.M[0][0]*2*orthoHalfFov/FovPixels and
nuclear@0 1697 // offset by orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2].
nuclear@0 1698
nuclear@0 1699 float orthoScaleX = 2.0f * tanHalfFovX / unitsX;
nuclear@0 1700 float orthoScaleY = 2.0f * tanHalfFovY / unitsY;
nuclear@0 1701 Matrix4f ortho;
nuclear@0 1702 ortho.M[0][0] = projection.M[0][0] * orthoScaleX;
nuclear@0 1703 ortho.M[0][1] = 0.0f;
nuclear@0 1704 ortho.M[0][2] = 0.0f;
nuclear@0 1705 ortho.M[0][3] = -projection.M[0][2] + ( orthoHorizontalOffset * projection.M[0][0] );
nuclear@0 1706
nuclear@0 1707 ortho.M[1][0] = 0.0f;
nuclear@0 1708 ortho.M[1][1] = -projection.M[1][1] * orthoScaleY; // Note sign flip (text rendering uses Y=down).
nuclear@0 1709 ortho.M[1][2] = 0.0f;
nuclear@0 1710 ortho.M[1][3] = -projection.M[1][2];
nuclear@0 1711
nuclear@0 1712 if ( fabsf ( zNear - zFar ) < 0.001f )
nuclear@0 1713 {
nuclear@0 1714 ortho.M[2][0] = 0.0f;
nuclear@0 1715 ortho.M[2][1] = 0.0f;
nuclear@0 1716 ortho.M[2][2] = 0.0f;
nuclear@0 1717 ortho.M[2][3] = zFar;
nuclear@0 1718 }
nuclear@0 1719 else
nuclear@0 1720 {
nuclear@0 1721 ortho.M[2][0] = 0.0f;
nuclear@0 1722 ortho.M[2][1] = 0.0f;
nuclear@0 1723 ortho.M[2][2] = zFar / (zNear - zFar);
nuclear@0 1724 ortho.M[2][3] = (zFar * zNear) / (zNear - zFar);
nuclear@0 1725 }
nuclear@0 1726
nuclear@0 1727 // No perspective correction for ortho.
nuclear@0 1728 ortho.M[3][0] = 0.0f;
nuclear@0 1729 ortho.M[3][1] = 0.0f;
nuclear@0 1730 ortho.M[3][2] = 0.0f;
nuclear@0 1731 ortho.M[3][3] = 1.0f;
nuclear@0 1732
nuclear@0 1733 return ortho;
nuclear@0 1734 }
nuclear@0 1735
nuclear@0 1736
nuclear@0 1737 //-----------------------------------------------------------------------------------
nuclear@0 1738 // A set of "forward-mapping" functions, mapping from framebuffer space to real-world and/or texture space.
nuclear@0 1739
nuclear@0 1740 // This mimics the first half of the distortion shader's function.
nuclear@0 1741 Vector2f TransformScreenNDCToTanFovSpace( DistortionRenderDesc const &distortion,
nuclear@0 1742 const Vector2f &framebufferNDC )
nuclear@0 1743 {
nuclear@0 1744 // Scale to TanHalfFov space, but still distorted.
nuclear@0 1745 Vector2f tanEyeAngleDistorted;
nuclear@0 1746 tanEyeAngleDistorted.x = ( framebufferNDC.x - distortion.LensCenter.x ) * distortion.TanEyeAngleScale.x;
nuclear@0 1747 tanEyeAngleDistorted.y = ( framebufferNDC.y - distortion.LensCenter.y ) * distortion.TanEyeAngleScale.y;
nuclear@0 1748 // Distort.
nuclear@0 1749 float radiusSquared = ( tanEyeAngleDistorted.x * tanEyeAngleDistorted.x )
nuclear@0 1750 + ( tanEyeAngleDistorted.y * tanEyeAngleDistorted.y );
nuclear@0 1751 float distortionScale = distortion.Lens.DistortionFnScaleRadiusSquared ( radiusSquared );
nuclear@0 1752 Vector2f tanEyeAngle;
nuclear@0 1753 tanEyeAngle.x = tanEyeAngleDistorted.x * distortionScale;
nuclear@0 1754 tanEyeAngle.y = tanEyeAngleDistorted.y * distortionScale;
nuclear@0 1755
nuclear@0 1756 return tanEyeAngle;
nuclear@0 1757 }
nuclear@0 1758
nuclear@0 1759 // Same, with chromatic aberration correction.
nuclear@0 1760 void TransformScreenNDCToTanFovSpaceChroma ( Vector2f *resultR, Vector2f *resultG, Vector2f *resultB,
nuclear@0 1761 DistortionRenderDesc const &distortion,
nuclear@0 1762 const Vector2f &framebufferNDC )
nuclear@0 1763 {
nuclear@0 1764 // Scale to TanHalfFov space, but still distorted.
nuclear@0 1765 Vector2f tanEyeAngleDistorted;
nuclear@0 1766 tanEyeAngleDistorted.x = ( framebufferNDC.x - distortion.LensCenter.x ) * distortion.TanEyeAngleScale.x;
nuclear@0 1767 tanEyeAngleDistorted.y = ( framebufferNDC.y - distortion.LensCenter.y ) * distortion.TanEyeAngleScale.y;
nuclear@0 1768 // Distort.
nuclear@0 1769 float radiusSquared = ( tanEyeAngleDistorted.x * tanEyeAngleDistorted.x )
nuclear@0 1770 + ( tanEyeAngleDistorted.y * tanEyeAngleDistorted.y );
nuclear@0 1771 Vector3f distortionScales = distortion.Lens.DistortionFnScaleRadiusSquaredChroma ( radiusSquared );
nuclear@0 1772 *resultR = tanEyeAngleDistorted * distortionScales.x;
nuclear@0 1773 *resultG = tanEyeAngleDistorted * distortionScales.y;
nuclear@0 1774 *resultB = tanEyeAngleDistorted * distortionScales.z;
nuclear@0 1775 }
nuclear@0 1776
nuclear@0 1777 // This mimics the second half of the distortion shader's function.
nuclear@0 1778 Vector2f TransformTanFovSpaceToRendertargetTexUV( ScaleAndOffset2D const &eyeToSourceUV,
nuclear@0 1779 Vector2f const &tanEyeAngle )
nuclear@0 1780 {
nuclear@0 1781 Vector2f textureUV;
nuclear@0 1782 textureUV.x = tanEyeAngle.x * eyeToSourceUV.Scale.x + eyeToSourceUV.Offset.x;
nuclear@0 1783 textureUV.y = tanEyeAngle.y * eyeToSourceUV.Scale.y + eyeToSourceUV.Offset.y;
nuclear@0 1784 return textureUV;
nuclear@0 1785 }
nuclear@0 1786
nuclear@0 1787 Vector2f TransformTanFovSpaceToRendertargetNDC( ScaleAndOffset2D const &eyeToSourceNDC,
nuclear@0 1788 Vector2f const &tanEyeAngle )
nuclear@0 1789 {
nuclear@0 1790 Vector2f textureNDC;
nuclear@0 1791 textureNDC.x = tanEyeAngle.x * eyeToSourceNDC.Scale.x + eyeToSourceNDC.Offset.x;
nuclear@0 1792 textureNDC.y = tanEyeAngle.y * eyeToSourceNDC.Scale.y + eyeToSourceNDC.Offset.y;
nuclear@0 1793 return textureNDC;
nuclear@0 1794 }
nuclear@0 1795
nuclear@0 1796 Vector2f TransformScreenPixelToScreenNDC( Recti const &distortionViewport,
nuclear@0 1797 Vector2f const &pixel )
nuclear@0 1798 {
nuclear@0 1799 // Move to [-1,1] NDC coords.
nuclear@0 1800 Vector2f framebufferNDC;
nuclear@0 1801 framebufferNDC.x = -1.0f + 2.0f * ( ( pixel.x - (float)distortionViewport.x ) / (float)distortionViewport.w );
nuclear@0 1802 framebufferNDC.y = -1.0f + 2.0f * ( ( pixel.y - (float)distortionViewport.y ) / (float)distortionViewport.h );
nuclear@0 1803 return framebufferNDC;
nuclear@0 1804 }
nuclear@0 1805
nuclear@0 1806 Vector2f TransformScreenPixelToTanFovSpace( Recti const &distortionViewport,
nuclear@0 1807 DistortionRenderDesc const &distortion,
nuclear@0 1808 Vector2f const &pixel )
nuclear@0 1809 {
nuclear@0 1810 return TransformScreenNDCToTanFovSpace( distortion,
nuclear@0 1811 TransformScreenPixelToScreenNDC( distortionViewport, pixel ) );
nuclear@0 1812 }
nuclear@0 1813
nuclear@0 1814 Vector2f TransformScreenNDCToRendertargetTexUV( DistortionRenderDesc const &distortion,
nuclear@0 1815 StereoEyeParams const &eyeParams,
nuclear@0 1816 Vector2f const &pixel )
nuclear@0 1817 {
nuclear@0 1818 return TransformTanFovSpaceToRendertargetTexUV ( eyeParams,
nuclear@0 1819 TransformScreenNDCToTanFovSpace ( distortion, pixel ) );
nuclear@0 1820 }
nuclear@0 1821
nuclear@0 1822 Vector2f TransformScreenPixelToRendertargetTexUV( Recti const &distortionViewport,
nuclear@0 1823 DistortionRenderDesc const &distortion,
nuclear@0 1824 StereoEyeParams const &eyeParams,
nuclear@0 1825 Vector2f const &pixel )
nuclear@0 1826 {
nuclear@0 1827 return TransformTanFovSpaceToRendertargetTexUV ( eyeParams,
nuclear@0 1828 TransformScreenPixelToTanFovSpace ( distortionViewport, distortion, pixel ) );
nuclear@0 1829 }
nuclear@0 1830
nuclear@0 1831
nuclear@0 1832 //-----------------------------------------------------------------------------------
nuclear@0 1833 // A set of "reverse-mapping" functions, mapping from real-world and/or texture space back to the framebuffer.
nuclear@0 1834
nuclear@0 1835 Vector2f TransformTanFovSpaceToScreenNDC( DistortionRenderDesc const &distortion,
nuclear@0 1836 const Vector2f &tanEyeAngle, bool usePolyApprox /*= false*/ )
nuclear@0 1837 {
nuclear@0 1838 float tanEyeAngleRadius = tanEyeAngle.Length();
nuclear@0 1839 float tanEyeAngleDistortedRadius = distortion.Lens.DistortionFnInverseApprox ( tanEyeAngleRadius );
nuclear@0 1840 if ( !usePolyApprox )
nuclear@0 1841 {
nuclear@0 1842 tanEyeAngleDistortedRadius = distortion.Lens.DistortionFnInverse ( tanEyeAngleRadius );
nuclear@0 1843 }
nuclear@0 1844 Vector2f tanEyeAngleDistorted = tanEyeAngle;
nuclear@0 1845 if ( tanEyeAngleRadius > 0.0f )
nuclear@0 1846 {
nuclear@0 1847 tanEyeAngleDistorted = tanEyeAngle * ( tanEyeAngleDistortedRadius / tanEyeAngleRadius );
nuclear@0 1848 }
nuclear@0 1849
nuclear@0 1850 Vector2f framebufferNDC;
nuclear@0 1851 framebufferNDC.x = ( tanEyeAngleDistorted.x / distortion.TanEyeAngleScale.x ) + distortion.LensCenter.x;
nuclear@0 1852 framebufferNDC.y = ( tanEyeAngleDistorted.y / distortion.TanEyeAngleScale.y ) + distortion.LensCenter.y;
nuclear@0 1853
nuclear@0 1854 return framebufferNDC;
nuclear@0 1855 }
nuclear@0 1856
nuclear@0 1857 Vector2f TransformRendertargetNDCToTanFovSpace( const ScaleAndOffset2D &eyeToSourceNDC,
nuclear@0 1858 const Vector2f &textureNDC )
nuclear@0 1859 {
nuclear@0 1860 Vector2f tanEyeAngle = (textureNDC - eyeToSourceNDC.Offset) / eyeToSourceNDC.Scale;
nuclear@0 1861 return tanEyeAngle;
nuclear@0 1862 }
nuclear@0 1863
nuclear@0 1864
nuclear@0 1865
nuclear@0 1866 } //namespace OVR
nuclear@0 1867
nuclear@0 1868 //Just want to make a copy disentangled from all these namespaces!
nuclear@0 1869 float ExtEvalCatmullRom10Spline ( float const *K, float scaledVal )
nuclear@0 1870 {
nuclear@0 1871 return(OVR::EvalCatmullRom10Spline ( K, scaledVal ));
nuclear@0 1872 }
nuclear@0 1873
nuclear@0 1874