aboutsummaryrefslogtreecommitdiffstats
path: root/jogl/src/classes/jogamp/opengl/oculusvr/stereo/lense/LensConfig.java
diff options
context:
space:
mode:
Diffstat (limited to 'jogl/src/classes/jogamp/opengl/oculusvr/stereo/lense/LensConfig.java')
-rw-r--r--jogl/src/classes/jogamp/opengl/oculusvr/stereo/lense/LensConfig.java600
1 files changed, 600 insertions, 0 deletions
diff --git a/jogl/src/classes/jogamp/opengl/oculusvr/stereo/lense/LensConfig.java b/jogl/src/classes/jogamp/opengl/oculusvr/stereo/lense/LensConfig.java
new file mode 100644
index 0000000..f90470b
--- /dev/null
+++ b/jogl/src/classes/jogamp/opengl/oculusvr/stereo/lense/LensConfig.java
@@ -0,0 +1,600 @@
+/**
+ * Copyright 2014 JogAmp Community. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice, this list
+ * of conditions and the following disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 3. Compliance with Oculus VR RIFT SDK LICENSE (see below)
+ *
+ * THIS SOFTWARE IS PROVIDED BY JogAmp Community ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JogAmp Community OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation are those of the
+ * authors and should not be interpreted as representing official policies, either expressed
+ * or implied, of JogAmp Community.
+ *
+ * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ *
+ * This file contains mathematical equations, comments and algorithms
+ * used in the Oculus VR RIFT SDK 0.3.2.
+ *
+ * Due to unknown legal status, the 'requested' Copyright tag and disclaimer
+ * below has been added.
+ *
+ * XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+ *
+ * Copyright © 2014 Oculus VR, Inc. All rights reserved
+ *
+ * Oculus VR, Inc. Software Development Kit License Agreement
+ *
+ * Human-Readable Summary*:
+ *
+ * You are Free to:
+ *
+ * Use, modify, and distribute the Oculus VR Rift SDK in source and binary
+ * form with your applications/software.
+ *
+ * With the Following Restrictions:
+ *
+ * You can only distribute or re-distribute the source code to LibOVR in
+ * whole, not in part.
+ *
+ * Modifications to the Oculus VR Rift SDK in source or binary form must
+ * be shared with Oculus VR.
+ *
+ * If your applications cause health and safety issues, you may lose your
+ * right to use the Oculus VR Rift SDK, including LibOVR.
+ *
+ * The Oculus VR Rift SDK may not be used to interface with unapproved commercial
+ * virtual reality mobile or non-mobile products or hardware.
+
+ * * - This human-readable Summary is not a license. It is simply a convenient
+ * reference for understanding the full Oculus VR Rift SDK License Agreement.
+ * The Summary is written as a user-friendly interface to the full Oculus VR Rift
+ * SDK License below. This Summary itself has no legal value, and its contents do
+ * not appear in the actual license.
+ *
+ * Full-length Legal Copy may be found at:
+ * http://www.oculusvr.com/licenses/LICENSE-3.1
+ * http://jogamp.org/git/?p=oculusvr-sdk.git;a=blob;f=LICENSE.txt;hb=HEAD
+ * Or within this repository: oculusvr-sdk/LICENSE.txt
+ *
+ * THIS RIFT SDK AND ANY COMPONENT THEREOF IS PROVIDED BY OCULUS VR AND
+ * ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL OCULUS VR AS THE
+ * COPYRIGHT OWNER OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS RIFT
+ * SDK OR THE RIFT SDK DERIVATIVES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package jogamp.opengl.oculusvr.stereo.lense;
+
+public class LensConfig {
+ public static enum DistortionEquation { RecipPoly4, CatmullRom10 };
+
+ public static final int NumCoefficients = 11;
+
+ private DistortionEquation eqn;
+ /* pp */ float MetersPerTanAngleAtCenter;
+ private final float[] K;
+ private final float[] InvK;
+
+ private float MaxR; // The highest R you're going to query for - the curve is unpredictable beyond it.
+ private float MaxInvR;
+
+ // Additional per-channel scaling is applied after distortion:
+ // Index [0] - Red channel constant coefficient.
+ // Index [1] - Red channel r^2 coefficient.
+ // Index [2] - Blue channel constant coefficient.
+ // Index [3] - Blue channel r^2 coefficient.
+ private final float[] ChromaticAberration = new float[4];
+
+ public LensConfig() {
+ this.K = new float[NumCoefficients];
+ this.InvK = new float[NumCoefficients];
+ SetToIdentity();
+ }
+
+ public LensConfig(final DistortionEquation eqn, final float MetersPerTanAngleAtCenter, final float[] K) {
+ this.K = new float[NumCoefficients];
+ this.InvK = new float[NumCoefficients];
+ SetToIdentity();
+ this.eqn = eqn;
+ this.MetersPerTanAngleAtCenter = MetersPerTanAngleAtCenter;
+ System.arraycopy(K, 0, this.K, 0, K.length);
+
+ // Chromatic aberration doesn't seem to change with eye relief.
+ ChromaticAberration[0] = -0.006f;
+ ChromaticAberration[1] = 0.0f;
+ ChromaticAberration[2] = 0.014f;
+ ChromaticAberration[3] = 0.0f;
+ }
+
+ private void SetUpInverseApprox() {
+ switch ( eqn )
+ {
+ case RecipPoly4: {
+ final float[] sampleR = new float[4];
+ final float[] sampleRSq = new float[4];
+ final float[] sampleInv = new float[4];
+ final float[] sampleFit = new float[4];
+ final float maxR = MaxInvR;
+
+ // Found heuristically...
+ sampleR[0] = 0.0f;
+ sampleR[1] = maxR * 0.4f;
+ sampleR[2] = maxR * 0.8f;
+ sampleR[3] = maxR * 1.5f;
+ for ( int i = 0; i < 4; i++ ) {
+ sampleRSq[i] = sampleR[i] * sampleR[i];
+ sampleInv[i] = DistortionFnInverse ( sampleR[i] );
+ sampleFit[i] = sampleR[i] / sampleInv[i];
+ }
+ sampleFit[0] = 1.0f;
+ FitCubicPolynomial ( InvK, sampleRSq, sampleFit );
+ }
+ break;
+ case CatmullRom10: {
+ final int NumSegments = NumCoefficients;
+ for ( int i = 1; i < NumSegments; i++ ) {
+ final float scaledRsq = i;
+ final float rsq = scaledRsq * MaxInvR * MaxInvR / ( NumSegments - 1);
+ final float r = (float)Math.sqrt ( rsq );
+ final float inv = DistortionFnInverse ( r );
+ InvK[i] = inv / r;
+ InvK[0] = 1.0f; // TODO: fix this.
+ }
+ }
+ break;
+ default:
+ throw new InternalError("unsupported EQ "+eqn);
+ }
+
+ }
+
+ private void SetToIdentity() {
+ for ( int i = 0; i < NumCoefficients; i++ )
+ {
+ K[i] = 0.0f;
+ InvK[i] = 0.0f;
+ }
+ eqn = DistortionEquation.RecipPoly4;
+ K[0] = 1.0f;
+ InvK[0] = 1.0f;
+ MaxR = 1.0f;
+ MaxInvR = 1.0f;
+ ChromaticAberration[0] = 0.0f;
+ ChromaticAberration[1] = 0.0f;
+ ChromaticAberration[2] = 0.0f;
+ ChromaticAberration[3] = 0.0f;
+ MetersPerTanAngleAtCenter = 0.05f;
+ }
+
+ // DistortionFn applies distortion to the argument.
+ // Input: the distance in TanAngle/NIC space from the optical center to the input pixel.
+ // Output: the resulting distance after distortion.
+ public float DistortionFn(final float r) {
+ return r * DistortionFnScaleRadiusSquared ( r * r );
+ }
+
+ // The result is a scaling applied to the distance.
+ public float DistortionFnInverseApprox(final float r) {
+ final float rsq = r * r;
+ final float scale;
+
+ switch ( eqn )
+ {
+ case RecipPoly4: {
+ scale = 1.0f / ( InvK[0] + rsq * ( InvK[1] + rsq * ( InvK[2] + rsq * InvK[3] ) ) );
+ }
+ break;
+ case CatmullRom10: {
+ // A Catmull-Rom spline through the values 1.0, K[1], K[2] ... K[9]
+ // evenly spaced in R^2 from 0.0 to MaxR^2
+ // K[0] controls the slope at radius=0.0, rather than the actual value.
+ final int NumSegments = NumCoefficients;
+ final float scaledRsq = (NumSegments-1) * rsq / ( MaxInvR * MaxInvR );
+ scale = EvalCatmullRom10Spline ( InvK, scaledRsq );
+ }
+ break;
+ default:
+ throw new InternalError("unsupported EQ "+eqn);
+ }
+ return r * scale;
+ }
+
+ // DistortionFnInverse computes the inverse of the distortion function on an argument.
+ public float DistortionFnInverse(final float r) {
+ float s, d;
+ float delta = r * 0.25f;
+
+ // Better to start guessing too low & take longer to converge than too high
+ // and hit singularities. Empirically, r * 0.5f is too high in some cases.
+ s = r * 0.25f;
+ d = Math.abs(r - DistortionFn(s));
+
+ for (int i = 0; i < 20; i++)
+ {
+ final float sUp = s + delta;
+ final float sDown = s - delta;
+ final float dUp = Math.abs(r - DistortionFn(sUp));
+ final float dDown = Math.abs(r - DistortionFn(sDown));
+
+ if (dUp < d)
+ {
+ s = sUp;
+ d = dUp;
+ }
+ else if (dDown < d)
+ {
+ s = sDown;
+ d = dDown;
+ }
+ else
+ {
+ delta *= 0.5f;
+ }
+ }
+ return s;
+ }
+
+ // The result is a scaling applied to the distance from the center of the lens.
+ public float DistortionFnScaleRadiusSquared (final float rsq) {
+ final float scale;
+ switch ( eqn )
+ {
+ case RecipPoly4: {
+ scale = 1.0f / ( K[0] + rsq * ( K[1] + rsq * ( K[2] + rsq * K[3] ) ) );
+ }
+ break;
+ case CatmullRom10: {
+ // A Catmull-Rom spline through the values 1.0, K[1], K[2] ... K[10]
+ // evenly spaced in R^2 from 0.0 to MaxR^2
+ // K[0] controls the slope at radius=0.0, rather than the actual value.
+ final int NumSegments = LensConfig.NumCoefficients;
+ final float scaledRsq = (NumSegments-1) * rsq / ( MaxR * MaxR );
+ scale = EvalCatmullRom10Spline ( K, scaledRsq );
+ }
+ break;
+ default:
+ throw new InternalError("unsupported EQ "+eqn);
+ }
+ return scale;
+ }
+
+ // x,y,z components map to r,g,b
+ public float[] DistortionFnScaleRadiusSquaredChroma(final float radiusSquared) {
+ final float scale = DistortionFnScaleRadiusSquared ( radiusSquared );
+ final float[] scaleRGB = new float[3];
+ scaleRGB[0] = scale * ( 1.0f + ChromaticAberration[0] + radiusSquared * ChromaticAberration[1] ); // Red
+ scaleRGB[1] = scale; // Green
+ scaleRGB[2] = scale * ( 1.0f + ChromaticAberration[2] + radiusSquared * ChromaticAberration[3] ); // Blue
+ return scaleRGB;
+ }
+
+ /**
+ // Inputs are 4 points (pFitX[0],pFitY[0]) through (pFitX[3],pFitY[3])
+ // Result is four coefficients in pResults[0] through pResults[3] such that
+ // y = pResult[0] + x * ( pResult[1] + x * ( pResult[2] + x * ( pResult[3] ) ) );
+ // passes through all four input points.
+ // Return is true if it succeeded, false if it failed (because two control points
+ // have the same pFitX value).
+ *
+ * @param pResult
+ * @param pFitX
+ * @param pFitY
+ * @return
+ */
+ private static boolean FitCubicPolynomial ( final float[/*4*/] pResult, final float[/*4*/] pFitX, final float[/*4*/] pFitY ) {
+ final float d0 = ( ( pFitX[0]-pFitX[1] ) * ( pFitX[0]-pFitX[2] ) * ( pFitX[0]-pFitX[3] ) );
+ final float d1 = ( ( pFitX[1]-pFitX[2] ) * ( pFitX[1]-pFitX[3] ) * ( pFitX[1]-pFitX[0] ) );
+ final float d2 = ( ( pFitX[2]-pFitX[3] ) * ( pFitX[2]-pFitX[0] ) * ( pFitX[2]-pFitX[1] ) );
+ final float d3 = ( ( pFitX[3]-pFitX[0] ) * ( pFitX[3]-pFitX[1] ) * ( pFitX[3]-pFitX[2] ) );
+
+ if ( ( d0 == 0.0f ) || ( d1 == 0.0f ) || ( d2 == 0.0f ) || ( d3 == 0.0f ) )
+ {
+ return false;
+ }
+
+ final float f0 = pFitY[0] / d0;
+ final float f1 = pFitY[1] / d1;
+ final float f2 = pFitY[2] / d2;
+ final float f3 = pFitY[3] / d3;
+
+ pResult[0] = -( f0*pFitX[1]*pFitX[2]*pFitX[3]
+ + f1*pFitX[0]*pFitX[2]*pFitX[3]
+ + f2*pFitX[0]*pFitX[1]*pFitX[3]
+ + f3*pFitX[0]*pFitX[1]*pFitX[2] );
+ pResult[1] = f0*(pFitX[1]*pFitX[2] + pFitX[2]*pFitX[3] + pFitX[3]*pFitX[1])
+ + f1*(pFitX[0]*pFitX[2] + pFitX[2]*pFitX[3] + pFitX[3]*pFitX[0])
+ + f2*(pFitX[0]*pFitX[1] + pFitX[1]*pFitX[3] + pFitX[3]*pFitX[0])
+ + f3*(pFitX[0]*pFitX[1] + pFitX[1]*pFitX[2] + pFitX[2]*pFitX[0]);
+ pResult[2] = -( f0*(pFitX[1]+pFitX[2]+pFitX[3])
+ + f1*(pFitX[0]+pFitX[2]+pFitX[3])
+ + f2*(pFitX[0]+pFitX[1]+pFitX[3])
+ + f3*(pFitX[0]+pFitX[1]+pFitX[2]) );
+ pResult[3] = f0 + f1 + f2 + f3;
+
+ return true;
+ }
+
+ private static float EvalCatmullRom10Spline ( final float[] K, final float scaledVal ) {
+ final int NumSegments = NumCoefficients;
+
+ float scaledValFloor = (float)Math.floor( scaledVal );
+ scaledValFloor = Math.max( 0.0f, Math.min( NumSegments-1, scaledValFloor ) );
+ final float t = scaledVal - scaledValFloor;
+ final int k = (int)scaledValFloor;
+
+ float p0, p1;
+ float m0, m1;
+ switch ( k )
+ {
+ case 0:
+ // Curve starts at 1.0 with gradient K[1]-K[0]
+ p0 = 1.0f;
+ m0 = ( K[1] - K[0] ); // general case would have been (K[1]-K[-1])/2
+ p1 = K[1];
+ m1 = 0.5f * ( K[2] - K[0] );
+ break;
+ default:
+ // General case
+ p0 = K[k ];
+ m0 = 0.5f * ( K[k+1] - K[k-1] );
+ p1 = K[k+1];
+ m1 = 0.5f * ( K[k+2] - K[k ] );
+ break;
+ case NumSegments-2:
+ // Last tangent is just the slope of the last two points.
+ p0 = K[NumSegments-2];
+ m0 = 0.5f * ( K[NumSegments-1] - K[NumSegments-2] );
+ p1 = K[NumSegments-1];
+ m1 = K[NumSegments-1] - K[NumSegments-2];
+ break;
+ case NumSegments-1:
+ // Beyond the last segment it's just a straight line
+ p0 = K[NumSegments-1];
+ m0 = K[NumSegments-1] - K[NumSegments-2];
+ p1 = p0 + m0;
+ m1 = m0;
+ break;
+ }
+
+ final float omt = 1.0f - t;
+ final float res = ( p0 * ( 1.0f + 2.0f * t ) + m0 * t ) * omt * omt
+ + ( p1 * ( 1.0f + 2.0f * omt ) - m1 * omt ) * t * t;
+
+ return res;
+ }
+
+ /** FIXME: Add 'pluggable' lense configuration */
+ public static LensConfig[] GenerateLensConfigFromEyeRelief(final float[] eyeReliefInMeters, final DistortionEquation eqn) {
+ final LensConfig[] result = new LensConfig[2];
+ final DistortionDescriptor[] distortions = LensConfig.CreateDistortionDescriptorsforOVRDK1_CupsABC();
+ result[0] = GenerateLensConfigFromEyeRelief(eyeReliefInMeters[0], distortions, eqn);
+ result[1] = GenerateLensConfigFromEyeRelief(eyeReliefInMeters[1], distortions, eqn);
+ return result;
+ }
+
+ private static LensConfig GenerateLensConfigFromEyeRelief(final float eyeReliefInMeters, final DistortionDescriptor[] distortions, final DistortionEquation eqn) {
+ final int numDistortions = distortions.length;
+ final int defaultDistortion = 0; // index of the default distortion curve to use if zero eye relief supplied
+
+ DistortionDescriptor pUpper = null;
+ DistortionDescriptor pLower = null;
+ float lerpVal = 0.0f;
+ if (eyeReliefInMeters == 0)
+ { // Use a constant default distortion if an invalid eye-relief is supplied
+ pLower = distortions[defaultDistortion];
+ pUpper = distortions[defaultDistortion];
+ lerpVal = 0.0f;
+ } else {
+ for ( int i = 0; i < numDistortions-1; i++ )
+ {
+ assert( distortions[i].eyeRelief < distortions[i+1].eyeRelief );
+ if ( ( distortions[i].eyeRelief <= eyeReliefInMeters ) && ( distortions[i+1].eyeRelief > eyeReliefInMeters ) )
+ {
+ pLower = distortions[i];
+ pUpper = distortions[i+1];
+ lerpVal = ( eyeReliefInMeters - pLower.eyeRelief ) / ( pUpper.eyeRelief - pLower.eyeRelief );
+ // No break here - I want the ASSERT to check everything every time!
+ }
+ }
+ }
+
+ if ( pUpper == null )
+ {
+ // Do not extrapolate, just clamp - slightly worried about people putting in bogus settings.
+ if ( distortions[0].eyeRelief > eyeReliefInMeters )
+ {
+ pLower = distortions[0];
+ pUpper = distortions[0];
+ }
+ else
+ {
+ assert ( distortions[numDistortions-1].eyeRelief <= eyeReliefInMeters );
+ pLower = distortions[numDistortions-1];
+ pUpper = distortions[numDistortions-1];
+ }
+ lerpVal = 0.0f;
+ }
+ final float invLerpVal = 1.0f - lerpVal;
+
+ pLower.config.MaxR = pLower.maxRadius;
+ pUpper.config.MaxR = pUpper.maxRadius;
+
+ final LensConfig result = new LensConfig();
+ // Where is the edge of the lens - no point modelling further than this.
+ final float maxValidRadius = invLerpVal * pLower.maxRadius + lerpVal * pUpper.maxRadius;
+ result.MaxR = maxValidRadius;
+
+ switch ( eqn )
+ {
+ case RecipPoly4:{
+ // Lerp control points and fit an equation to them.
+ final float[] fitX = new float[4];
+ final float[] fitY = new float[4];
+ fitX[0] = 0.0f;
+ fitY[0] = 1.0f;
+ for ( int ctrlPt = 1; ctrlPt < 4; ctrlPt ++ )
+ {
+ final float radiusLerp = invLerpVal * pLower.sampleRadius[ctrlPt-1] + lerpVal * pUpper.sampleRadius[ctrlPt-1];
+ final float radiusLerpSq = radiusLerp * radiusLerp;
+ final float fitYLower = pLower.config.DistortionFnScaleRadiusSquared ( radiusLerpSq );
+ final float fitYUpper = pUpper.config.DistortionFnScaleRadiusSquared ( radiusLerpSq );
+ fitX[ctrlPt] = radiusLerpSq;
+ fitY[ctrlPt] = 1.0f / ( invLerpVal * fitYLower + lerpVal * fitYUpper );
+ }
+ result.eqn = DistortionEquation.RecipPoly4;
+ final boolean bSuccess = LensConfig.FitCubicPolynomial ( result.K, fitX, fitY );
+ assert ( bSuccess );
+
+ // Set up the fast inverse.
+ final float maxRDist = result.DistortionFn ( maxValidRadius );
+ result.MaxInvR = maxRDist;
+ result.SetUpInverseApprox();
+ }
+ break;
+ case CatmullRom10: {
+ // Evenly sample & lerp points on the curve.
+ final int NumSegments = LensConfig.NumCoefficients;
+ result.MaxR = maxValidRadius;
+ // Directly interpolate the K0 values
+ result.K[0] = invLerpVal * pLower.config.K[0] + lerpVal * pUpper.config.K[0];
+
+ // Sample and interpolate the distortion curves to derive K[1] ... K[n]
+ for ( int ctrlPt = 1; ctrlPt < NumSegments; ctrlPt++ )
+ {
+ final float radiusSq = ( (float)ctrlPt / (float)(NumSegments-1) ) * maxValidRadius * maxValidRadius;
+ final float fitYLower = pLower.config.DistortionFnScaleRadiusSquared ( radiusSq );
+ final float fitYUpper = pUpper.config.DistortionFnScaleRadiusSquared ( radiusSq );
+ final float fitLerp = invLerpVal * fitYLower + lerpVal * fitYUpper;
+ result.K[ctrlPt] = fitLerp;
+ }
+
+ result.eqn = DistortionEquation.CatmullRom10;
+
+ for ( int ctrlPt = 1; ctrlPt < NumSegments; ctrlPt++ )
+ {
+ final float radiusSq = ( (float)ctrlPt / (float)(NumSegments-1) ) * maxValidRadius * maxValidRadius;
+ final float val = result.DistortionFnScaleRadiusSquared ( radiusSq );
+ assert ( Math.abs( val - result.K[ctrlPt] ) < 0.0001f );
+ }
+
+ // Set up the fast inverse.
+ final float maxRDist = result.DistortionFn ( maxValidRadius );
+ result.MaxInvR = maxRDist;
+ result.SetUpInverseApprox();
+ }
+ break;
+ default:
+ throw new InternalError("unsupported EQ "+eqn);
+ }
+
+ // Chromatic aberration.
+ result.ChromaticAberration[0] = invLerpVal * pLower.config.ChromaticAberration[0] + lerpVal * pUpper.config.ChromaticAberration[0];
+ result.ChromaticAberration[1] = invLerpVal * pLower.config.ChromaticAberration[1] + lerpVal * pUpper.config.ChromaticAberration[1];
+ result.ChromaticAberration[2] = invLerpVal * pLower.config.ChromaticAberration[2] + lerpVal * pUpper.config.ChromaticAberration[2];
+ result.ChromaticAberration[3] = invLerpVal * pLower.config.ChromaticAberration[3] + lerpVal * pUpper.config.ChromaticAberration[3];
+
+ // Scale.
+ result.MetersPerTanAngleAtCenter = pLower.config.MetersPerTanAngleAtCenter * invLerpVal +
+ pUpper.config.MetersPerTanAngleAtCenter * lerpVal;
+
+ return result;
+ }
+
+ public static class DistortionDescriptor {
+ public DistortionDescriptor(final LensConfig lens, final float eyeRelief,
+ final float[] sampleRadius, final float maxRadius) {
+ this.config = lens;
+ this.eyeRelief = eyeRelief;
+ this.sampleRadius = sampleRadius;
+ this.maxRadius = maxRadius;
+ }
+
+ final LensConfig config;
+ final float eyeRelief;
+ final float[] sampleRadius;
+ final float maxRadius;
+ }
+
+ /*** Hardcoded OculusVR DK1 A, B, C eye cups (lenses) */
+ public static DistortionDescriptor[] CreateDistortionDescriptorsforOVRDK1_CupsABC() {
+ return new DistortionDescriptor[] {
+ // Tuned at minimum dial setting - extended to r^2 == 1.8
+ new DistortionDescriptor(
+ new LensConfig(DistortionEquation.CatmullRom10,
+ 0.0425f, // MetersPerTanAngleAtCenter
+ new float[] { 1.0000f, // K00
+ 1.06505f, // K01
+ 1.14725f, // K02
+ 1.2705f, // K03
+ 1.48f, // K04
+ 1.87f, // K05
+ 2.534f, // K06
+ 3.6f, // K07
+ 5.1f, // K08
+ 7.4f, // K09
+ 11.0f} ), // K10
+ 0.012760465f - 0.005f, // eyeRelief
+ new float[] { 0.222717149f, 0.512249443f, 0.712694878f }, // sampleRadius
+ (float)Math.sqrt(1.8f) ), // maxRadius
+ // Tuned at middle dial setting
+ new DistortionDescriptor(
+ new LensConfig(DistortionEquation.CatmullRom10,
+ 0.0425f, // MetersPerTanAngleAtCenter
+ new float[] { 1.0000f, // K00
+ 1.032407264f, // K01
+ 1.07160462f, // K02
+ 1.11998388f, // K03
+ 1.1808606f, // K04
+ 1.2590494f, // K05
+ 1.361915f, // K06
+ 1.5014339f, // K07
+ 1.6986004f, // K08
+ 1.9940577f, // K09
+ 2.4783147f} ), // K10
+ 0.012760465f, // eyeRelief
+ new float[] { 0.222717149f, 0.512249443f, 0.712694878f }, // sampleRadius
+ 1.0f ), // maxRadius
+ // Tuned at maximum dial setting
+ new DistortionDescriptor(
+ new LensConfig(DistortionEquation.CatmullRom10,
+ 0.0425f, // MetersPerTanAngleAtCenter
+ new float[] { 1.0102f, // K00
+ 1.0371f, // K01
+ 1.0831f, // K02
+ 1.1353f, // K03
+ 1.2f, // K04
+ 1.2851f, // K05
+ 1.3979f, // K06
+ 1.56f, // K07
+ 1.8f, // K08
+ 2.25f, // K09
+ 3.0f} ), // K10
+ 0.012760465f + 0.005f, // eyeRelief
+ new float[] { 0.222717149f, 0.512249443f, 0.712694878f }, // sampleRadius
+ 1.0f ), // maxRadius
+ };
+ }
+
+} \ No newline at end of file