/** * 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; import com.jogamp.math.VectorUtil; import com.jogamp.nativewindow.util.DimensionImmutable; import com.jogamp.opengl.util.stereo.generic.GenericStereoDeviceConfig; public class DistortionSpec { public DistortionSpec(final LensConfig lens) { this.lens = lens; this.pixelsPerTanAngleAtCenter = new float[2]; this.tanEyeAngleScale = new float[2]; this.lensCenter = new float[2]; } private static final float[] quaterHalf = new float[] { 0.25f, 0.5f }; final LensConfig lens; final float[] pixelsPerTanAngleAtCenter; final float[] tanEyeAngleScale; final float[] lensCenter; public static DistortionSpec[] CalculateDistortionSpec (final GenericStereoDeviceConfig deviceConfig, final float[] eyeReliefInMeters) { final DistortionSpec[] result = new DistortionSpec[2]; /// FIXME: Add 'pluggable' lense configuration final LensConfig[] lensConfig = LensConfig.GenerateLensConfigFromEyeRelief(eyeReliefInMeters, LensConfig.DistortionEquation.CatmullRom10); result[0] = CalculateDistortionSpec (deviceConfig, 0, eyeReliefInMeters[0], lensConfig[0]); result[1] = CalculateDistortionSpec (deviceConfig, 1, eyeReliefInMeters[1], lensConfig[1]); return result; } private static DistortionSpec CalculateDistortionSpec (final GenericStereoDeviceConfig deviceConfig, final int eyeName, final float eyeReliefInMeters, final LensConfig lensConfig) { // From eye relief, IPD and device characteristics, we get the distortion mapping. // This distortion does the following things: // 1. It undoes the distortion that happens at the edges of the lens. // 2. It maps the undistorted field into "retina" space. // So the input is a pixel coordinate - the physical pixel on the display itself. // The output is the real-world direction of the ray from this pixel as it comes out of the lens and hits the eye. // However we typically think of rays "coming from" the eye, so the direction (TanAngleX,TanAngleY,1) is the direction // that the pixel appears to be in real-world space, where AngleX and AngleY are relative to the straight-ahead vector. // If your renderer is a raytracer, you can use this vector directly (normalize as appropriate). // However in standard rasterisers, we have rendered a 2D image and are putting it in front of the eye, // so we then need a mapping from this space to the [-1,1] UV coordinate space, which depends on exactly // where "in space" the app wants to put that rendertarget. // Where in space, and how large this rendertarget is, is completely up to the app and/or user, // though of course we can provide some useful hints. // TODO: Use IPD and eye relief to modify distortion (i.e. non-radial component) // TODO: cope with lenses that don't produce collimated light. // This means that IPD relative to the lens separation changes the light vergence, // and so we actually need to change where the image is displayed. final DistortionSpec localDistortion = new DistortionSpec(lensConfig); final DimensionImmutable resolutionInPixels = deviceConfig.surfaceSizeInPixels; final float[] pixelsPerMeter = new float[] { resolutionInPixels.getWidth() / deviceConfig.screenSizeInMeters[0], resolutionInPixels.getHeight() / deviceConfig.screenSizeInMeters[1] }; VectorUtil.scaleVec2(localDistortion.pixelsPerTanAngleAtCenter, pixelsPerMeter, localDistortion.lens.MetersPerTanAngleAtCenter); // Same thing, scaled to [-1,1] for each eye, rather than pixels. VectorUtil.scaleVec2(localDistortion.tanEyeAngleScale, quaterHalf, VectorUtil.divVec2(new float[2], deviceConfig.screenSizeInMeters, localDistortion.lens.MetersPerTanAngleAtCenter) ); // <--------------left eye------------------><-ScreenGapSizeInMeters-><--------------right eye-----------------> // <------------------------------------------ScreenSizeInMeters.Width-----------------------------------------> // <------------interpupillaryDistanceInMeters--------------> // <--centerFromLeftInMeters-> // ^ // Center of lens // Find the lens centers in scale of [-1,+1] (NDC) in left eye. final float visibleWidthOfOneEye = 0.5f * ( deviceConfig.screenSizeInMeters[0] ); final float centerFromLeftInMeters = ( deviceConfig.screenSizeInMeters[0] - deviceConfig.interpupillaryDistanceInMeters ) * 0.5f; localDistortion.lensCenter[0] = ( centerFromLeftInMeters / visibleWidthOfOneEye ) * 2.0f - 1.0f; localDistortion.lensCenter[1] = ( deviceConfig.pupilCenterFromScreenTopInMeters / deviceConfig.screenSizeInMeters[1] ) * 2.0f - 1.0f; if ( 1 == eyeName ) { localDistortion.lensCenter[0] = -localDistortion.lensCenter[0]; } return localDistortion; } }