ovr_sdk

diff LibOVR/Src/OVR_CAPI.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
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/LibOVR/Src/OVR_CAPI.cpp	Wed Jan 14 06:51:16 2015 +0200
     1.3 @@ -0,0 +1,1289 @@
     1.4 +/************************************************************************************
     1.5 +
     1.6 +Filename    :   OVR_CAPI.cpp
     1.7 +Content     :   Experimental simple C interface to the HMD - version 1.
     1.8 +Created     :   November 30, 2013
     1.9 +Authors     :   Michael Antonov
    1.10 +
    1.11 +Copyright   :   Copyright 2014 Oculus VR, LLC All Rights reserved.
    1.12 +
    1.13 +Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); 
    1.14 +you may not use the Oculus VR Rift SDK except in compliance with the License, 
    1.15 +which is provided at the time of installation or download, or which 
    1.16 +otherwise accompanies this software in either electronic or hard copy form.
    1.17 +
    1.18 +You may obtain a copy of the License at
    1.19 +
    1.20 +http://www.oculusvr.com/licenses/LICENSE-3.2 
    1.21 +
    1.22 +Unless required by applicable law or agreed to in writing, the Oculus VR SDK 
    1.23 +distributed under the License is distributed on an "AS IS" BASIS,
    1.24 +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    1.25 +See the License for the specific language governing permissions and
    1.26 +limitations under the License.
    1.27 +
    1.28 +************************************************************************************/
    1.29 +
    1.30 +#include "OVR_CAPI.h"
    1.31 +#include "Kernel/OVR_Timer.h"
    1.32 +#include "Kernel/OVR_Math.h"
    1.33 +#include "Kernel/OVR_System.h"
    1.34 +#include "OVR_Stereo.h"
    1.35 +#include "OVR_Profile.h"
    1.36 +#include "../Include/OVR_Version.h"
    1.37 +
    1.38 +#include "CAPI/CAPI_HMDState.h"
    1.39 +#include "CAPI/CAPI_FrameTimeManager.h"
    1.40 +
    1.41 +#include "Service/Service_NetClient.h"
    1.42 +#ifdef OVR_SINGLE_PROCESS
    1.43 +#include "Service/Service_NetServer.h"
    1.44 +#endif
    1.45 +
    1.46 +#ifdef OVR_OS_WIN32
    1.47 +#include "Displays/OVR_Win32_ShimFunctions.h"
    1.48 +#endif
    1.49 +
    1.50 +
    1.51 +using namespace OVR;
    1.52 +using namespace OVR::Util::Render;
    1.53 +using namespace OVR::Tracking;
    1.54 +
    1.55 +//-------------------------------------------------------------------------------------
    1.56 +// Math
    1.57 +namespace OVR {
    1.58 +
    1.59 +
    1.60 +// ***** FovPort
    1.61 +
    1.62 +// C-interop support: FovPort <-> ovrFovPort
    1.63 +FovPort::FovPort(const ovrFovPort &src)
    1.64 +    : UpTan(src.UpTan), DownTan(src.DownTan), LeftTan(src.LeftTan), RightTan(src.RightTan)
    1.65 +{ }    
    1.66 +
    1.67 +FovPort::operator ovrFovPort () const
    1.68 +{
    1.69 +    ovrFovPort result;
    1.70 +    result.LeftTan  = LeftTan;
    1.71 +    result.RightTan = RightTan;
    1.72 +    result.UpTan    = UpTan;
    1.73 +    result.DownTan  = DownTan;
    1.74 +    return result;
    1.75 +}
    1.76 +
    1.77 +// Converts Fov Tan angle units to [-1,1] render target NDC space
    1.78 +Vector2f FovPort::TanAngleToRendertargetNDC(Vector2f const &tanEyeAngle)
    1.79 +{  
    1.80 +    ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(*this);
    1.81 +    return tanEyeAngle * eyeToSourceNDC.Scale + eyeToSourceNDC.Offset;
    1.82 +}
    1.83 +
    1.84 +// ***** SensorDataType
    1.85 +
    1.86 +SensorDataType::SensorDataType(const ovrSensorData& s)
    1.87 +{
    1.88 +    Acceleration = s.Accelerometer;
    1.89 +    RotationRate = s.Gyro;
    1.90 +    MagneticField = s.Magnetometer;
    1.91 +    Temperature = s.Temperature;
    1.92 +    AbsoluteTimeSeconds = s.TimeInSeconds;
    1.93 +}
    1.94 +
    1.95 +SensorDataType::operator ovrSensorData () const
    1.96 +{
    1.97 +    ovrSensorData result;
    1.98 +    result.Accelerometer = Acceleration;
    1.99 +    result.Gyro = RotationRate;
   1.100 +    result.Magnetometer = MagneticField;
   1.101 +    result.Temperature = Temperature;
   1.102 +    result.TimeInSeconds = (float) AbsoluteTimeSeconds;
   1.103 +    return result;
   1.104 +}
   1.105 +
   1.106 +
   1.107 +// ***** SensorState
   1.108 +
   1.109 +TrackingState::TrackingState(const ovrTrackingState& s)
   1.110 +{
   1.111 +    HeadPose    = s.HeadPose;
   1.112 +    CameraPose  = s.CameraPose;
   1.113 +    LeveledCameraPose = s.LeveledCameraPose;
   1.114 +    RawSensorData = s.RawSensorData;
   1.115 +    StatusFlags = s.StatusFlags;
   1.116 +    LastVisionProcessingTime = s.LastVisionProcessingTime;
   1.117 +    LastVisionFrameLatency = s.LastVisionFrameLatency;
   1.118 +    LastCameraFrameCounter = s.LastCameraFrameCounter;
   1.119 +}
   1.120 +
   1.121 +TrackingState::operator ovrTrackingState() const
   1.122 +{
   1.123 +    ovrTrackingState result;
   1.124 +    result.HeadPose     = HeadPose;
   1.125 +    result.CameraPose   = CameraPose;
   1.126 +    result.LeveledCameraPose = LeveledCameraPose;
   1.127 +    result.RawSensorData  = RawSensorData;
   1.128 +    result.StatusFlags  = StatusFlags;
   1.129 +    result.LastVisionProcessingTime = LastVisionProcessingTime;
   1.130 +    result.LastVisionFrameLatency = LastVisionFrameLatency;
   1.131 +    result.LastCameraFrameCounter = LastCameraFrameCounter;
   1.132 +    return result;
   1.133 +}
   1.134 +
   1.135 +
   1.136 +} // namespace OVR
   1.137 +
   1.138 +//-------------------------------------------------------------------------------------
   1.139 +
   1.140 +using namespace OVR::CAPI;
   1.141 +
   1.142 +#ifdef __cplusplus 
   1.143 +extern "C" {
   1.144 +#endif
   1.145 +
   1.146 +
   1.147 +// Used to generate projection from ovrEyeDesc::Fov
   1.148 +OVR_EXPORT ovrMatrix4f ovrMatrix4f_Projection(ovrFovPort fov, float znear, float zfar, ovrBool rightHanded)
   1.149 +{
   1.150 +    return CreateProjection(rightHanded ? true : false, fov, znear, zfar);
   1.151 +}
   1.152 +
   1.153 +
   1.154 +OVR_EXPORT ovrMatrix4f ovrMatrix4f_OrthoSubProjection(ovrMatrix4f projection, ovrVector2f orthoScale,
   1.155 +                                                      float orthoDistance, float hmdToEyeViewOffsetX)
   1.156 +{
   1.157 +
   1.158 +    float orthoHorizontalOffset = hmdToEyeViewOffsetX / orthoDistance;
   1.159 +
   1.160 +    // Current projection maps real-world vector (x,y,1) to the RT.
   1.161 +    // We want to find the projection that maps the range [-FovPixels/2,FovPixels/2] to
   1.162 +    // the physical [-orthoHalfFov,orthoHalfFov]
   1.163 +    // Note moving the offset from M[0][2]+M[1][2] to M[0][3]+M[1][3] - this means
   1.164 +    // we don't have to feed in Z=1 all the time.
   1.165 +    // The horizontal offset math is a little hinky because the destination is
   1.166 +    // actually [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]
   1.167 +    // So we need to first map [-FovPixels/2,FovPixels/2] to
   1.168 +    //                         [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]:
   1.169 +    // x1 = x0 * orthoHalfFov/(FovPixels/2) + orthoHorizontalOffset;
   1.170 +    //    = x0 * 2*orthoHalfFov/FovPixels + orthoHorizontalOffset;
   1.171 +    // But then we need the same mapping as the existing projection matrix, i.e.
   1.172 +    // x2 = x1 * Projection.M[0][0] + Projection.M[0][2];
   1.173 +    //    = x0 * (2*orthoHalfFov/FovPixels + orthoHorizontalOffset) * Projection.M[0][0] + Projection.M[0][2];
   1.174 +    //    = x0 * Projection.M[0][0]*2*orthoHalfFov/FovPixels +
   1.175 +    //      orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2];
   1.176 +    // So in the new projection matrix we need to scale by Projection.M[0][0]*2*orthoHalfFov/FovPixels and
   1.177 +    // offset by orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2].
   1.178 +
   1.179 +    Matrix4f ortho;
   1.180 +    ortho.M[0][0] = projection.M[0][0] * orthoScale.x;
   1.181 +    ortho.M[0][1] = 0.0f;
   1.182 +    ortho.M[0][2] = 0.0f;
   1.183 +    ortho.M[0][3] = -projection.M[0][2] + ( orthoHorizontalOffset * projection.M[0][0] );
   1.184 +
   1.185 +    ortho.M[1][0] = 0.0f;
   1.186 +    ortho.M[1][1] = -projection.M[1][1] * orthoScale.y;       // Note sign flip (text rendering uses Y=down).
   1.187 +    ortho.M[1][2] = 0.0f;
   1.188 +    ortho.M[1][3] = -projection.M[1][2];
   1.189 +
   1.190 +    /*
   1.191 +    if ( fabsf ( zNear - zFar ) < 0.001f )
   1.192 +    {
   1.193 +        ortho.M[2][0] = 0.0f;
   1.194 +        ortho.M[2][1] = 0.0f;
   1.195 +        ortho.M[2][2] = 0.0f;
   1.196 +        ortho.M[2][3] = zFar;
   1.197 +    }
   1.198 +    else
   1.199 +    {
   1.200 +        ortho.M[2][0] = 0.0f;
   1.201 +        ortho.M[2][1] = 0.0f;
   1.202 +        ortho.M[2][2] = zFar / (zNear - zFar);
   1.203 +        ortho.M[2][3] = (zFar * zNear) / (zNear - zFar);
   1.204 +    }
   1.205 +    */
   1.206 +
   1.207 +    // MA: Undo effect of sign
   1.208 +    ortho.M[2][0] = 0.0f;
   1.209 +    ortho.M[2][1] = 0.0f;
   1.210 +    //ortho.M[2][2] = projection.M[2][2] * projection.M[3][2] * -1.0f; // reverse right-handedness
   1.211 +    ortho.M[2][2] = 0.0f;
   1.212 +    ortho.M[2][3] = 0.0f;
   1.213 +        //projection.M[2][3];
   1.214 +
   1.215 +    // No perspective correction for ortho.
   1.216 +    ortho.M[3][0] = 0.0f;
   1.217 +    ortho.M[3][1] = 0.0f;
   1.218 +    ortho.M[3][2] = 0.0f;
   1.219 +    ortho.M[3][3] = 1.0f;
   1.220 +
   1.221 +    return ortho;
   1.222 +}
   1.223 +
   1.224 +
   1.225 +OVR_EXPORT double ovr_GetTimeInSeconds()
   1.226 +{
   1.227 +    return Timer::GetSeconds();
   1.228 +}
   1.229 +
   1.230 +// Waits until the specified absolute time.
   1.231 +OVR_EXPORT double ovr_WaitTillTime(double absTime)
   1.232 +{
   1.233 +    double       initialTime = ovr_GetTimeInSeconds();
   1.234 +    double       newTime     = initialTime;
   1.235 +    
   1.236 +    while(newTime < absTime)
   1.237 +    {
   1.238 +        for (int j = 0; j < 5; j++)
   1.239 +            OVR_PROCESSOR_PAUSE();
   1.240 +
   1.241 +        newTime = ovr_GetTimeInSeconds();
   1.242 +    }
   1.243 +
   1.244 +    // How long we waited
   1.245 +    return newTime - initialTime;
   1.246 +}
   1.247 +
   1.248 +
   1.249 +//-------------------------------------------------------------------------------------
   1.250 +
   1.251 +// 1. Init/shutdown.
   1.252 +
   1.253 +static ovrBool CAPI_SystemInitCalled = 0;
   1.254 +static ovrBool CAPI_ovrInitializeCalled = 0;
   1.255 +
   1.256 +static OVR::Service::NetClient* CAPI_pNetClient = 0;
   1.257 +
   1.258 +OVR_EXPORT ovrBool ovr_InitializeRenderingShim()
   1.259 +{
   1.260 +    OVR::System::DirectDisplayInitialize();
   1.261 +    return OVR::System::DirectDisplayEnabled();
   1.262 +}
   1.263 +
   1.264 +OVR_EXPORT ovrBool ovr_Initialize()
   1.265 +{
   1.266 +    if (CAPI_ovrInitializeCalled)
   1.267 +        return 1;
   1.268 +
   1.269 +    // We must set up the system for the plugin to work
   1.270 +    if (!OVR::System::IsInitialized())
   1.271 +    {
   1.272 +        OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All));
   1.273 +        CAPI_SystemInitCalled = 1;
   1.274 +    }
   1.275 +
   1.276 +    if (!OVR::System::DirectDisplayEnabled() && !OVR::Display::InCompatibilityMode(false))
   1.277 +    {
   1.278 +        OVR_ASSERT(false);
   1.279 +        return 0;
   1.280 +    }
   1.281 +
   1.282 +    CAPI_pNetClient = NetClient::GetInstance();
   1.283 +
   1.284 +#ifdef OVR_SINGLE_PROCESS
   1.285 +
   1.286 +    // If the server could not start running,
   1.287 +    if (Service::NetServer::GetInstance()->IsInitialized())
   1.288 +    {
   1.289 +        CAPI_pNetClient->Connect(true);
   1.290 +    }
   1.291 +    else
   1.292 +    {
   1.293 +        // This normally will happen if the OVRService is running in the background,
   1.294 +        // or another SingleProcess-mode app is running in the background.
   1.295 +        // In this case, it's using the hardware and we should not also attempt to use
   1.296 +        // the hardware.
   1.297 +        LogError("{ERR-079} [LibOVR] Server is already running");
   1.298 +    }
   1.299 +#else
   1.300 +    CAPI_pNetClient->Connect(true);
   1.301 +#endif
   1.302 +
   1.303 +    CAPI_ovrInitializeCalled = 1;
   1.304 +
   1.305 +    return 1;
   1.306 +}
   1.307 +
   1.308 +OVR_EXPORT void ovr_Shutdown()
   1.309 +{  
   1.310 +    // We should clean up the system to be complete
   1.311 +    if (OVR::System::IsInitialized() && CAPI_SystemInitCalled)
   1.312 +    {
   1.313 +        OVR::System::Destroy();
   1.314 +    }
   1.315 +
   1.316 +    CAPI_SystemInitCalled = 0;
   1.317 +    CAPI_ovrInitializeCalled = 0;
   1.318 +}
   1.319 +
   1.320 +
   1.321 +// There is a thread safety issue with ovrHmd_Detect in that multiple calls from different
   1.322 +// threads can corrupt the global array state. This would lead to two problems:
   1.323 +//  a) Create(index) enumerator may miss or overshoot items. Probably not a big deal
   1.324 +//     as game logic can easily be written to only do Detect(s)/Creates in one place.
   1.325 +//     The alternative would be to return list handle.
   1.326 +//  b) TBD: Un-mutexed Detect access from two threads could lead to crash. We should
   1.327 +//         probably check this.
   1.328 +//
   1.329 +
   1.330 +OVR_EXPORT int ovrHmd_Detect()
   1.331 +{
   1.332 +    if (!CAPI_ovrInitializeCalled)
   1.333 +        return 0;
   1.334 +
   1.335 +    return CAPI_pNetClient->Hmd_Detect();
   1.336 +}
   1.337 +
   1.338 +
   1.339 +// ovrHmd_Create us explicitly separated from ConfigureTracking and ConfigureRendering to allow creation of 
   1.340 +// a relatively light-weight handle that would reference the device going forward and would 
   1.341 +// survive future ovrHmd_Detect calls. That is once ovrHMD is returned, index is no longer
   1.342 +// necessary and can be changed by a ovrHmd_Detect call.
   1.343 +OVR_EXPORT ovrHmd ovrHmd_Create(int index)
   1.344 +{
   1.345 +    if (!CAPI_ovrInitializeCalled)
   1.346 +        return 0;
   1.347 +
   1.348 +    double t0 = Timer::GetSeconds();
   1.349 +    HMDNetworkInfo netInfo;
   1.350 +
   1.351 +    // There may be some delay before the HMD is fully detected.
   1.352 +    // Since we are also trying to create the HMD immediately it may lose this race and
   1.353 +    // get "NO HMD DETECTED."  Wait a bit longer to avoid this.
   1.354 +    while (!CAPI_pNetClient->Hmd_Create(index, &netInfo) ||
   1.355 +           netInfo.NetId == InvalidVirtualHmdId)
   1.356 +    {
   1.357 +        // If two seconds elapse and still no HMD detected,
   1.358 +        if (Timer::GetSeconds() - t0 > 2.)
   1.359 +        {
   1.360 +            if (!NetClient::GetInstance()->IsConnected(false, false))
   1.361 +            {
   1.362 +                NetClient::GetInstance()->SetLastError("Not connected to service");
   1.363 +            }
   1.364 +            else
   1.365 +            {
   1.366 +                NetClient::GetInstance()->SetLastError("No HMD Detected");
   1.367 +            }
   1.368 +
   1.369 +            return 0;
   1.370 +        }
   1.371 +    }
   1.372 +
   1.373 +    // Create HMD State object
   1.374 +    HMDState* hmds = HMDState::CreateHMDState(CAPI_pNetClient, netInfo);
   1.375 +    if (!hmds)
   1.376 +    {
   1.377 +        CAPI_pNetClient->Hmd_Release(netInfo.NetId);
   1.378 +
   1.379 +        NetClient::GetInstance()->SetLastError("Unable to create HMD state");
   1.380 +        return 0;
   1.381 +    }
   1.382 +
   1.383 +    // Reset frame timing so that FrameTimeManager values are properly initialized in AppRendered mode.
   1.384 +    ovrHmd_ResetFrameTiming(hmds->pHmdDesc, 0);
   1.385 +
   1.386 +    return hmds->pHmdDesc;
   1.387 +}
   1.388 +
   1.389 +
   1.390 +OVR_EXPORT ovrBool ovrHmd_AttachToWindow( ovrHmd hmd, void* window,
   1.391 +                                         const ovrRecti* destMirrorRect,
   1.392 +                                         const ovrRecti* sourceRenderTargetRect )
   1.393 +{
   1.394 +    OVR_UNUSED( destMirrorRect );
   1.395 +    OVR_UNUSED( sourceRenderTargetRect );
   1.396 +
   1.397 +    if (!CAPI_ovrInitializeCalled)
   1.398 +        return false;
   1.399 +
   1.400 +    if (!hmd || !hmd->Handle)
   1.401 +        return false;
   1.402 +#ifndef OVR_OS_MAC
   1.403 +    HMDState* hmds = (HMDState*)hmd->Handle;
   1.404 +    CAPI_pNetClient->Hmd_AttachToWindow(hmds->GetNetId(), window);
   1.405 +    hmds->pWindow = window;
   1.406 +#endif
   1.407 +#ifdef OVR_OS_WIN32
   1.408 +    Win32::DisplayShim::GetInstance().hWindow = (HWND)window;
   1.409 +#endif
   1.410 +#ifdef OVR_OS_MAC
   1.411 +    OVR_UNUSED(window);
   1.412 +#endif
   1.413 +
   1.414 +    return true;
   1.415 +}
   1.416 +
   1.417 +OVR_EXPORT ovrHmd ovrHmd_CreateDebug(ovrHmdType type)
   1.418 +{
   1.419 +    if (!CAPI_ovrInitializeCalled)
   1.420 +        return 0;
   1.421 +
   1.422 +    HMDState* hmds = HMDState::CreateHMDState(type);
   1.423 +
   1.424 +    return hmds->pHmdDesc;
   1.425 +}
   1.426 +
   1.427 +OVR_EXPORT void ovrHmd_Destroy(ovrHmd hmddesc)
   1.428 +{
   1.429 +    if (!hmddesc || !hmddesc->Handle)
   1.430 +        return;
   1.431 +    
   1.432 +    // TBD: Any extra shutdown?
   1.433 +    HMDState* hmds = (HMDState*)hmddesc->Handle;
   1.434 +        
   1.435 +    {   // Thread checker in its own scope, to avoid access after 'delete'.
   1.436 +        // Essentially just checks that no other RenderAPI function is executing.
   1.437 +        ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_Destroy");
   1.438 +    }    
   1.439 +
   1.440 +#ifdef OVR_OS_WIN32
   1.441 +    if (hmds->pWindow)
   1.442 +    {
   1.443 +        // ? ok to call
   1.444 +        //CAPI_pNetClient->Hmd_AttachToWindow(hmds->GetNetId(), 0);
   1.445 +        hmds->pWindow = 0;
   1.446 +        Win32::DisplayShim::GetInstance().hWindow = (HWND)0;
   1.447 +    }    
   1.448 +#endif
   1.449 +
   1.450 +    delete (HMDState*)hmddesc->Handle;
   1.451 +}
   1.452 +
   1.453 +
   1.454 +OVR_EXPORT const char* ovrHmd_GetLastError(ovrHmd hmddesc)
   1.455 +{
   1.456 +    if (!CAPI_ovrInitializeCalled)
   1.457 +    {
   1.458 +        return "System initialize not called";
   1.459 +    }
   1.460 +
   1.461 +    VirtualHmdId netId = InvalidVirtualHmdId;
   1.462 +
   1.463 +    if (hmddesc && hmddesc->Handle)
   1.464 +    {
   1.465 +        HMDState* p = (HMDState*)hmddesc->Handle;
   1.466 +        netId = p->GetNetId();
   1.467 +    }
   1.468 +
   1.469 +    return CAPI_pNetClient->Hmd_GetLastError(netId);
   1.470 +}
   1.471 +
   1.472 +#define OVR_VERSION_LIBOVR_PFX "libOVR:"
   1.473 +
   1.474 +// Returns version string representing libOVR version. Static, so
   1.475 +// string remains valid for app lifespan
   1.476 +OVR_EXPORT const char* ovr_GetVersionString()
   1.477 +{
   1.478 +	static const char* version = OVR_VERSION_LIBOVR_PFX OVR_VERSION_STRING;
   1.479 +    return version + sizeof(OVR_VERSION_LIBOVR_PFX) - 1;
   1.480 +}
   1.481 +
   1.482 +
   1.483 +
   1.484 +//-------------------------------------------------------------------------------------
   1.485 +
   1.486 +// Returns capability bits that are enabled at this time; described by ovrHmdCapBits.
   1.487 +// Note that this value is different font ovrHmdDesc::Caps, which describes what
   1.488 +// capabilities are available.
   1.489 +OVR_EXPORT unsigned int ovrHmd_GetEnabledCaps(ovrHmd hmddesc)
   1.490 +{
   1.491 +    HMDState* p = (HMDState*)hmddesc->Handle;
   1.492 +    return p ? p->EnabledHmdCaps : 0;
   1.493 +}
   1.494 +
   1.495 +// Modifies capability bits described by ovrHmdCapBits that can be modified,
   1.496 +// such as ovrHmdCap_LowPersistance.
   1.497 +OVR_EXPORT void ovrHmd_SetEnabledCaps(ovrHmd hmddesc, unsigned int capsBits)
   1.498 +{
   1.499 +    HMDState* p = (HMDState*)hmddesc->Handle;
   1.500 +    if (p)
   1.501 +    {
   1.502 +        p->SetEnabledHmdCaps(capsBits);
   1.503 +    }
   1.504 +}
   1.505 +
   1.506 +
   1.507 +//-------------------------------------------------------------------------------------
   1.508 +// *** Sensor
   1.509 +
   1.510 +// Sensor APIs are separated from Create & Configure for several reasons:
   1.511 +//  - They need custom parameters that control allocation of heavy resources
   1.512 +//    such as Vision tracking, which you don't want to create unless necessary.
   1.513 +//  - A game may want to switch some sensor settings based on user input, 
   1.514 +//    or at lease enable/disable features such as Vision for debugging.
   1.515 +//  - The same or syntactically similar sensor interface is likely to be used if we 
   1.516 +//    introduce controllers.
   1.517 +//
   1.518 +//  - Sensor interface functions are all Thread-safe, unlike the frame/render API
   1.519 +//    functions that have different rules (all frame access functions
   1.520 +//    must be on render thread)
   1.521 +
   1.522 +OVR_EXPORT ovrBool ovrHmd_ConfigureTracking(ovrHmd hmddesc, unsigned int supportedCaps,
   1.523 +                                                            unsigned int requiredCaps)
   1.524 +{
   1.525 +    if (hmddesc)
   1.526 +    {
   1.527 +        HMDState* p = (HMDState*)hmddesc->Handle;
   1.528 +        return p->ConfigureTracking(supportedCaps, requiredCaps);
   1.529 +    }
   1.530 +
   1.531 +    return 0;
   1.532 +}
   1.533 +
   1.534 +OVR_EXPORT void ovrHmd_RecenterPose(ovrHmd hmddesc)
   1.535 +{
   1.536 +    if (hmddesc)
   1.537 +    {
   1.538 +        HMDState* p = (HMDState*)hmddesc->Handle;
   1.539 +        p->TheSensorStateReader.RecenterPose();
   1.540 +    }
   1.541 +}
   1.542 +
   1.543 +OVR_EXPORT ovrTrackingState ovrHmd_GetTrackingState(ovrHmd hmddesc, double absTime)
   1.544 +{
   1.545 +    ovrTrackingState result;
   1.546 +
   1.547 +    if (hmddesc)
   1.548 +    {
   1.549 +        HMDState* p = (HMDState*)hmddesc->Handle;
   1.550 +        result = p->PredictedTrackingState(absTime);
   1.551 +
   1.552 +        // Instrument data from eye pose
   1.553 +        p->LagStats.InstrumentEyePose(result);
   1.554 +    }
   1.555 +    else
   1.556 +        memset(&result, 0, sizeof(result));
   1.557 +
   1.558 +#ifdef OVR_OS_WIN32
   1.559 +        // Set up display code for Windows
   1.560 +        Win32::DisplayShim::GetInstance().Active = (result.StatusFlags & ovrStatus_HmdConnected) != 0;
   1.561 +#endif
   1.562 +
   1.563 +    return result;
   1.564 +}
   1.565 +
   1.566 +
   1.567 +//-------------------------------------------------------------------------------------
   1.568 +// *** General Setup
   1.569 +
   1.570 +// Per HMD -> calculateIdealPixelSize
   1.571 +OVR_EXPORT ovrSizei ovrHmd_GetFovTextureSize(ovrHmd hmddesc, ovrEyeType eye, ovrFovPort fov,
   1.572 +                                             float pixelsPerDisplayPixel)
   1.573 +{
   1.574 +    ovrHmdStruct *  hmd = hmddesc->Handle;
   1.575 +    if (!hmd) return Sizei(0);
   1.576 +    
   1.577 +    HMDState* hmds = (HMDState*)hmd;
   1.578 +    return hmds->RenderState.GetFOVTextureSize(eye, fov, pixelsPerDisplayPixel);
   1.579 +}
   1.580 +
   1.581 +
   1.582 +//-------------------------------------------------------------------------------------
   1.583 +
   1.584 +
   1.585 +OVR_EXPORT 
   1.586 +ovrBool ovrHmd_ConfigureRendering( ovrHmd hmddesc,
   1.587 +                                   const ovrRenderAPIConfig* apiConfig,
   1.588 +                                   unsigned int distortionCaps,
   1.589 +                                   const ovrFovPort eyeFovIn[2],
   1.590 +                                   ovrEyeRenderDesc eyeRenderDescOut[2] )
   1.591 +{
   1.592 +    ovrHmdStruct *  hmd = hmddesc->Handle;
   1.593 +    if (!hmd) return 0;
   1.594 +    return ((HMDState*)hmd)->ConfigureRendering(eyeRenderDescOut, eyeFovIn,
   1.595 +                                                apiConfig, distortionCaps);
   1.596 +}
   1.597 +
   1.598 +
   1.599 +
   1.600 +// TBD: MA - Deprecated, need alternative
   1.601 +void ovrHmd_SetVsync(ovrHmd hmddesc, ovrBool vsync)
   1.602 +{
   1.603 +    ovrHmdStruct *  hmd = hmddesc->Handle;
   1.604 +    if (!hmd) return;
   1.605 +
   1.606 +    return ((HMDState*)hmd)->TimeManager.SetVsync(vsync? true : false);
   1.607 +}
   1.608 +
   1.609 +
   1.610 +OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrame(ovrHmd hmddesc, unsigned int frameIndex)
   1.611 +{           
   1.612 +    HMDState* hmds = (HMDState*)hmddesc->Handle;
   1.613 +    if (!hmds)
   1.614 +    {
   1.615 +        ovrFrameTiming f;
   1.616 +        memset(&f, 0, sizeof(f));
   1.617 +        return f;
   1.618 +    }
   1.619 +
   1.620 +    // Check: Proper configure and threading state for the call.
   1.621 +    hmds->checkRenderingConfigured("ovrHmd_BeginFrame");
   1.622 +	OVR_DEBUG_LOG_COND(hmds->BeginFrameCalled, ("ovrHmd_BeginFrame called multiple times."));
   1.623 +    ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_BeginFrame");
   1.624 +    
   1.625 +    hmds->BeginFrameCalled   = true;
   1.626 +    hmds->BeginFrameThreadId = OVR::GetCurrentThreadId();
   1.627 +
   1.628 +    return ovrHmd_BeginFrameTiming(hmddesc, frameIndex);
   1.629 +}
   1.630 +
   1.631 +
   1.632 +// Renders textures to frame buffer
   1.633 +OVR_EXPORT void ovrHmd_EndFrame(ovrHmd hmddesc,
   1.634 +                                const ovrPosef renderPose[2],
   1.635 +                                const ovrTexture eyeTexture[2])
   1.636 +{
   1.637 +    HMDState* hmds = (HMDState*)hmddesc->Handle;
   1.638 +    if (!hmds) return;
   1.639 +
   1.640 +    // Instrument when the EndFrame() call started
   1.641 +    hmds->LagStats.InstrumentEndFrameStart(ovr_GetTimeInSeconds());
   1.642 +
   1.643 +    hmds->SubmitEyeTextures(renderPose, eyeTexture);
   1.644 +
   1.645 +    // Debug state checks: Must be in BeginFrame, on the same thread.
   1.646 +    hmds->checkBeginFrameScope("ovrHmd_EndFrame");
   1.647 +    ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_EndFrame");  
   1.648 +    
   1.649 +    hmds->pRenderer->SetLatencyTestColor(hmds->LatencyTestActive ? hmds->LatencyTestDrawColor : NULL);
   1.650 +
   1.651 +    ovrHmd_GetLatencyTest2DrawColor(hmddesc, NULL); // We don't actually need to draw color, so send NULL
   1.652 +    
   1.653 +    if (hmds->pRenderer)
   1.654 +    {
   1.655 +        hmds->pRenderer->SaveGraphicsState();
   1.656 +
   1.657 +        // See if we need to show the HSWDisplay.
   1.658 +        if (hmds->pHSWDisplay) // Until we know that these are valid, assume any of them can't be.
   1.659 +        {
   1.660 +            ovrHSWDisplayState hswDisplayState;
   1.661 +            hmds->pHSWDisplay->TickState(&hswDisplayState, true);  // This may internally call HASWarning::Display.
   1.662 +
   1.663 +            if (hswDisplayState.Displayed)
   1.664 +            {
   1.665 +                hmds->pHSWDisplay->Render(ovrEye_Left, &eyeTexture[ovrEye_Left]);
   1.666 +                hmds->pHSWDisplay->Render(ovrEye_Right, &eyeTexture[ovrEye_Right]);
   1.667 +            }
   1.668 +        }
   1.669 +
   1.670 +        hmds->pRenderer->EndFrame(true);
   1.671 +        hmds->pRenderer->RestoreGraphicsState();
   1.672 +    }
   1.673 +
   1.674 +    // Call after present
   1.675 +    ovrHmd_EndFrameTiming(hmddesc);
   1.676 +
   1.677 +    // Instrument latency tester
   1.678 +    hmds->LagStats.InstrumentLatencyTimings(hmds->TimeManager);
   1.679 +
   1.680 +    // Instrument when the EndFrame() call ended
   1.681 +    hmds->LagStats.InstrumentEndFrameEnd(ovr_GetTimeInSeconds());
   1.682 +
   1.683 +    // Out of BeginFrame
   1.684 +    hmds->BeginFrameThreadId = 0;
   1.685 +    hmds->BeginFrameCalled   = false;
   1.686 +}
   1.687 +
   1.688 +
   1.689 +// Not exposed as part of public API
   1.690 +OVR_EXPORT void ovrHmd_RegisterPostDistortionCallback(ovrHmd hmddesc, PostDistortionCallback callback)
   1.691 +{
   1.692 +    HMDState* hmds = (HMDState*)hmddesc->Handle;
   1.693 +    if (!hmds) return;
   1.694 +
   1.695 +    if (hmds->pRenderer)
   1.696 +    {
   1.697 +        hmds->pRenderer->RegisterPostDistortionCallback(callback);
   1.698 +    }
   1.699 +}
   1.700 +
   1.701 +
   1.702 +
   1.703 +//-------------------------------------------------------------------------------------
   1.704 +// ***** Frame Timing logic
   1.705 +
   1.706 +
   1.707 +OVR_EXPORT ovrFrameTiming ovrHmd_GetFrameTiming(ovrHmd hmddesc, unsigned int frameIndex)
   1.708 +{
   1.709 +    ovrHmdStruct *  hmd = hmddesc->Handle;
   1.710 +    ovrFrameTiming f;
   1.711 +    memset(&f, 0, sizeof(f));
   1.712 +
   1.713 +    HMDState* hmds = (HMDState*)hmd;
   1.714 +    if (hmds)
   1.715 +    {
   1.716 +        FrameTimeManager::Timing frameTiming = hmds->TimeManager.GetFrameTiming(frameIndex);
   1.717 +
   1.718 +        f.ThisFrameSeconds       = frameTiming.ThisFrameTime;
   1.719 +        f.NextFrameSeconds       = frameTiming.NextFrameTime;
   1.720 +        f.TimewarpPointSeconds   = frameTiming.TimewarpPointTime;
   1.721 +        f.ScanoutMidpointSeconds = frameTiming.MidpointTime;
   1.722 +        f.EyeScanoutSeconds[0]   = frameTiming.EyeRenderTimes[0];
   1.723 +        f.EyeScanoutSeconds[1]   = frameTiming.EyeRenderTimes[1];
   1.724 +
   1.725 +         // Compute DeltaSeconds.
   1.726 +        f.DeltaSeconds = (hmds->LastGetFrameTimeSeconds == 0.0f) ? 0.0f :
   1.727 +                         (float) (f.ThisFrameSeconds - hmds->LastFrameTimeSeconds);    
   1.728 +        hmds->LastGetFrameTimeSeconds = f.ThisFrameSeconds;
   1.729 +        if (f.DeltaSeconds > 1.0f)
   1.730 +            f.DeltaSeconds = 1.0f;
   1.731 +    }
   1.732 +        
   1.733 +    return f;
   1.734 +}
   1.735 +
   1.736 +OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrameTiming(ovrHmd hmddesc, unsigned int frameIndex)
   1.737 +{
   1.738 +    ovrHmdStruct *  hmd = hmddesc->Handle;
   1.739 +    ovrFrameTiming f;
   1.740 +    memset(&f, 0, sizeof(f));
   1.741 +
   1.742 +    HMDState* hmds = (HMDState*)hmd;
   1.743 +    if (!hmds) return f;
   1.744 +
   1.745 +    // Check: Proper state for the call.    
   1.746 +    OVR_DEBUG_LOG_COND(hmds->BeginFrameTimingCalled,
   1.747 +                      ("ovrHmd_BeginFrameTiming called multiple times."));    
   1.748 +    hmds->BeginFrameTimingCalled = true;
   1.749 +
   1.750 +    double thisFrameTime = hmds->TimeManager.BeginFrame(frameIndex);        
   1.751 +
   1.752 +    const FrameTimeManager::Timing &frameTiming = hmds->TimeManager.GetFrameTiming();
   1.753 +
   1.754 +    f.ThisFrameSeconds      = thisFrameTime;
   1.755 +    f.NextFrameSeconds      = frameTiming.NextFrameTime;
   1.756 +    f.TimewarpPointSeconds  = frameTiming.TimewarpPointTime;
   1.757 +    f.ScanoutMidpointSeconds= frameTiming.MidpointTime;
   1.758 +    f.EyeScanoutSeconds[0]  = frameTiming.EyeRenderTimes[0];
   1.759 +    f.EyeScanoutSeconds[1]  = frameTiming.EyeRenderTimes[1];
   1.760 +
   1.761 +    // Compute DeltaSeconds.
   1.762 +    f.DeltaSeconds = (hmds->LastFrameTimeSeconds == 0.0f) ? 0.0f :
   1.763 +                     (float) (thisFrameTime - hmds->LastFrameTimeSeconds);
   1.764 +    hmds->LastFrameTimeSeconds = thisFrameTime;
   1.765 +    if (f.DeltaSeconds > 1.0f)
   1.766 +        f.DeltaSeconds = 1.0f;
   1.767 +
   1.768 +    return f;
   1.769 +}
   1.770 +
   1.771 +
   1.772 +OVR_EXPORT void ovrHmd_EndFrameTiming(ovrHmd hmddesc)
   1.773 +{
   1.774 +    HMDState* hmds = (HMDState*)hmddesc->Handle;
   1.775 +    if (!hmds) return;
   1.776 +
   1.777 +    // Debug state checks: Must be in BeginFrameTiming, on the same thread.
   1.778 +    hmds->checkBeginFrameTimingScope("ovrHmd_EndTiming");
   1.779 +   // MA TBD: Correct check or not?
   1.780 +   // ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_EndFrame");
   1.781 +
   1.782 +    hmds->TimeManager.EndFrame();   
   1.783 +    hmds->BeginFrameTimingCalled = false;
   1.784 +
   1.785 +    bool dk2LatencyTest = (hmds->EnabledHmdCaps & ovrHmdCap_DynamicPrediction) != 0;
   1.786 +    if (dk2LatencyTest)
   1.787 +    {
   1.788 +        Util::FrameTimeRecordSet recordset;
   1.789 +        hmds->TheLatencyTestStateReader.GetRecordSet(recordset);
   1.790 +        hmds->TimeManager.UpdateFrameLatencyTrackingAfterEndFrame( hmds->LatencyTest2DrawColor,
   1.791 +            recordset);
   1.792 +    }
   1.793 +}
   1.794 +
   1.795 +
   1.796 +OVR_EXPORT void ovrHmd_ResetFrameTiming(ovrHmd hmddesc,  unsigned int frameIndex) 
   1.797 +{
   1.798 +    HMDState* hmds = (HMDState*)hmddesc->Handle;
   1.799 +    if (!hmds) return;
   1.800 +    
   1.801 +    hmds->TimeManager.ResetFrameTiming(frameIndex, 
   1.802 +                                       false,
   1.803 +                                       hmds->RenderingConfigured);
   1.804 +    hmds->LastFrameTimeSeconds    = 0.0;
   1.805 +    hmds->LastGetFrameTimeSeconds = 0.0;
   1.806 +}
   1.807 +
   1.808 +OVR_EXPORT void ovrHmd_GetEyePoses(ovrHmd hmd, unsigned int frameIndex, ovrVector3f hmdToEyeViewOffset[2],
   1.809 +                                   ovrPosef outEyePoses[2], ovrTrackingState* outHmdTrackingState)
   1.810 +{
   1.811 +    HMDState* hmds = (HMDState*)hmd->Handle;
   1.812 +    if (!hmds) return;
   1.813 +
   1.814 +    hmds->LatencyTestActive = hmds->ProcessLatencyTest(hmds->LatencyTestDrawColor);
   1.815 +    
   1.816 +    ovrTrackingState hmdTrackingState = hmds->TimeManager.GetEyePredictionTracking(hmd, ovrEye_Count, frameIndex);
   1.817 +    ovrPosef hmdPose = hmdTrackingState.HeadPose.ThePose;
   1.818 +
   1.819 +    // caller passed in a valid pointer, so copy to output
   1.820 +    if(outHmdTrackingState)
   1.821 +       *outHmdTrackingState = hmdTrackingState;
   1.822 +
   1.823 +    // Currently HmdToEyeViewOffset is only a 3D vector
   1.824 +    // (Negate HmdToEyeViewOffset because offset is a view matrix offset and not a camera offset)
   1.825 +    outEyePoses[0] = Posef(hmdPose.Orientation, ((Posef)hmdPose).Apply(-((Vector3f)hmdToEyeViewOffset[0])));
   1.826 +    outEyePoses[1] = Posef(hmdPose.Orientation, ((Posef)hmdPose).Apply(-((Vector3f)hmdToEyeViewOffset[1])));
   1.827 +
   1.828 + 	// Instrument data from eye pose
   1.829 +    hmds->LagStats.InstrumentEyePose(hmdTrackingState);
   1.830 +}
   1.831 +
   1.832 +ovrPosef ovrHmd_GetHmdPosePerEye(ovrHmd hmd, ovrEyeType eye)
   1.833 +{
   1.834 +    HMDState* hmds = (HMDState*)hmd->Handle;
   1.835 +    if (!hmds) return ovrPosef();    
   1.836 +
   1.837 +    // This isn't a great place, but since we removed ovrHmd_BeginEyeRender...
   1.838 +    // Only process latency tester for drawing the left eye (assumes left eye is drawn first)
   1.839 +    if (hmds->pRenderer && eye == 0)
   1.840 +    {
   1.841 +        hmds->LatencyTestActive = hmds->ProcessLatencyTest(hmds->LatencyTestDrawColor);
   1.842 +    }
   1.843 +
   1.844 +    hmds->checkBeginFrameTimingScope("ovrHmd_GetEyePose");
   1.845 +    return hmds->TimeManager.GetEyePredictionPose(hmd, eye);
   1.846 +}
   1.847 +
   1.848 +OVR_EXPORT void ovrHmd_AddDistortionTimeMeasurement(ovrHmd hmddesc, double distortionTimeSeconds)
   1.849 +{
   1.850 +    if (!hmddesc)
   1.851 +        return;
   1.852 +    HMDState* hmds = (HMDState*)hmddesc->Handle;
   1.853 +
   1.854 +    hmds->checkBeginFrameTimingScope("ovrHmd_GetTimewarpEyeMatrices");   
   1.855 +    hmds->TimeManager.AddDistortionTimeMeasurement(distortionTimeSeconds);
   1.856 +}
   1.857 +
   1.858 +
   1.859 +
   1.860 +OVR_EXPORT void ovrHmd_GetEyeTimewarpMatricesDebug(ovrHmd hmddesc, ovrEyeType eye,
   1.861 +                                              ovrPosef renderPose, ovrMatrix4f twmOut[2],double debugTimingOffsetInSeconds)
   1.862 +{
   1.863 +    if (!hmddesc)
   1.864 +        return;
   1.865 +    HMDState* hmds = (HMDState*)hmddesc->Handle;
   1.866 +
   1.867 +    // Debug checks: BeginFrame was called, on the same thread.
   1.868 +    hmds->checkBeginFrameTimingScope("ovrHmd_GetTimewarpEyeMatrices");   
   1.869 +
   1.870 +    hmds->TimeManager.GetTimewarpMatrices(hmddesc, eye, renderPose, twmOut, debugTimingOffsetInSeconds);
   1.871 +
   1.872 +    /*
   1.873 +    // MA: Took this out because new latency test approach just sames
   1.874 +    //     the sample times in FrameTimeManager.
   1.875 +    // TODO: if no timewarp, then test latency in begin eye render
   1.876 +    if (eye == 0)
   1.877 +    {        
   1.878 +        hmds->ProcessLatencyTest2(hmds->LatencyTest2DrawColor, -1.0f);
   1.879 +    }
   1.880 +    */
   1.881 +}
   1.882 +
   1.883 +
   1.884 +OVR_EXPORT void ovrHmd_GetEyeTimewarpMatrices(ovrHmd hmddesc, ovrEyeType eye,
   1.885 +                                              ovrPosef renderPose, ovrMatrix4f twmOut[2])
   1.886 +{
   1.887 +    return(ovrHmd_GetEyeTimewarpMatricesDebug(hmddesc, eye, renderPose, twmOut, 0.0));
   1.888 +}
   1.889 +
   1.890 +
   1.891 +
   1.892 +OVR_EXPORT ovrEyeRenderDesc ovrHmd_GetRenderDesc(ovrHmd hmddesc,
   1.893 +                                                 ovrEyeType eyeType, ovrFovPort fov)
   1.894 +{
   1.895 +    HMDState* hmds = (HMDState*)hmddesc->Handle;
   1.896 +    ovrEyeRenderDesc erd;
   1.897 +   
   1.898 +    if (!hmds)
   1.899 +    {
   1.900 +        memset(&erd, 0, sizeof(erd));
   1.901 +        return erd;
   1.902 +    }
   1.903 +
   1.904 +    return hmds->RenderState.CalcRenderDesc(eyeType, fov);
   1.905 +}
   1.906 +
   1.907 +
   1.908 +
   1.909 +#define OVR_OFFSET_OF(s, field) ((size_t)&((s*)0)->field)
   1.910 +
   1.911 +
   1.912 +
   1.913 +
   1.914 +OVR_EXPORT ovrBool ovrHmd_CreateDistortionMeshDebug( ovrHmd hmddesc,
   1.915 +                                                ovrEyeType eyeType, ovrFovPort fov,
   1.916 +                                                unsigned int distortionCaps,
   1.917 +                                                ovrDistortionMesh *meshData,
   1.918 +												float debugEyeReliefOverrideInMetres)
   1.919 +{
   1.920 +    // The 'internal' function below can be found in CAPI_HMDState.
   1.921 +    // Not ideal, but navigating the convolutions of what compiles
   1.922 +    // where, meant they are in the few places which actually lets these compile.
   1.923 +    // Please relocate (if you wish) to a more meaningful place if you can navigate the compiler gymnastics :)
   1.924 +    return(ovrHmd_CreateDistortionMeshInternal( hmddesc->Handle,
   1.925 +                                                eyeType, fov,
   1.926 +                                                distortionCaps,
   1.927 +                                                meshData,
   1.928 +                                                debugEyeReliefOverrideInMetres));
   1.929 +
   1.930 +}
   1.931 +OVR_EXPORT ovrBool ovrHmd_CreateDistortionMesh( ovrHmd hmddesc,
   1.932 +                                                ovrEyeType eyeType, ovrFovPort fov,
   1.933 +                                                unsigned int distortionCaps,
   1.934 +                                                ovrDistortionMesh *meshData)
   1.935 +{
   1.936 +    return(ovrHmd_CreateDistortionMeshDebug( hmddesc, eyeType, fov, distortionCaps,meshData, 0));
   1.937 +}
   1.938 +
   1.939 +
   1.940 +
   1.941 +// Frees distortion mesh allocated by ovrHmd_GenerateDistortionMesh. meshData elements
   1.942 +// are set to null and 0s after the call.
   1.943 +OVR_EXPORT void ovrHmd_DestroyDistortionMesh(ovrDistortionMesh* meshData)
   1.944 +{
   1.945 +    if (meshData->pVertexData)
   1.946 +        DistortionMeshDestroy((DistortionMeshVertexData*)meshData->pVertexData,
   1.947 +                              meshData->pIndexData);
   1.948 +    meshData->pVertexData = 0;
   1.949 +    meshData->pIndexData  = 0;
   1.950 +    meshData->VertexCount = 0;
   1.951 +    meshData->IndexCount  = 0;
   1.952 +}
   1.953 +
   1.954 +
   1.955 +
   1.956 +// Computes updated 'uvScaleOffsetOut' to be used with a distortion if render target size or
   1.957 +// viewport changes after the fact. This can be used to adjust render size every frame, if desired.
   1.958 +OVR_EXPORT void ovrHmd_GetRenderScaleAndOffset( ovrFovPort fov,
   1.959 +                                                ovrSizei textureSize, ovrRecti renderViewport,
   1.960 +                                                ovrVector2f uvScaleOffsetOut[2] )
   1.961 +{        
   1.962 +    // Find the mapping from TanAngle space to target NDC space.
   1.963 +    ScaleAndOffset2D  eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(fov);
   1.964 +    // Find the mapping from TanAngle space to textureUV space.
   1.965 +    ScaleAndOffset2D  eyeToSourceUV  = CreateUVScaleAndOffsetfromNDCScaleandOffset(
   1.966 +                                         eyeToSourceNDC,
   1.967 +                                         renderViewport, textureSize );
   1.968 +
   1.969 +    uvScaleOffsetOut[0] = eyeToSourceUV.Scale;
   1.970 +    uvScaleOffsetOut[1] = eyeToSourceUV.Offset;
   1.971 +}
   1.972 +
   1.973 +
   1.974 +//-------------------------------------------------------------------------------------
   1.975 +// ***** Latency Test interface
   1.976 +
   1.977 +OVR_EXPORT ovrBool ovrHmd_GetLatencyTestDrawColor(ovrHmd hmddesc, unsigned char rgbColorOut[3])
   1.978 +{
   1.979 +    HMDState* p = (HMDState*)hmddesc->Handle;
   1.980 +    rgbColorOut[0] = p->LatencyTestDrawColor[0];
   1.981 +    rgbColorOut[1] = p->LatencyTestDrawColor[1];
   1.982 +    rgbColorOut[2] = p->LatencyTestDrawColor[2];
   1.983 +    return p->LatencyTestActive;
   1.984 +}
   1.985 +
   1.986 +OVR_EXPORT ovrBool ovrHmd_ProcessLatencyTest(ovrHmd hmddesc, unsigned char rgbColorOut[3])
   1.987 +{
   1.988 +    OVR_UNUSED(hmddesc);
   1.989 +    return NetClient::GetInstance()->LatencyUtil_ProcessInputs(Timer::GetSeconds(), rgbColorOut);
   1.990 +}
   1.991 +
   1.992 +OVR_EXPORT const char*  ovrHmd_GetLatencyTestResult(ovrHmd hmddesc)
   1.993 +{
   1.994 +    OVR_UNUSED(hmddesc);
   1.995 +    return NetClient::GetInstance()->LatencyUtil_GetResultsString();
   1.996 +}
   1.997 +
   1.998 +OVR_EXPORT ovrBool ovrHmd_GetLatencyTest2DrawColor(ovrHmd hmddesc, unsigned char rgbColorOut[3])
   1.999 +{
  1.1000 +    HMDState* hmds = (HMDState*)hmddesc->Handle;
  1.1001 +    if (!hmds) return false;
  1.1002 +
  1.1003 +    // TBD: Move directly into renderer
  1.1004 +    bool dk2LatencyTest = (hmds->EnabledHmdCaps & ovrHmdCap_DynamicPrediction) != 0;
  1.1005 +    if (dk2LatencyTest)
  1.1006 +    {
  1.1007 +        hmds->TimeManager.GetFrameLatencyTestDrawColor(hmds->LatencyTest2DrawColor);
  1.1008 +        if(rgbColorOut != NULL)
  1.1009 +        {
  1.1010 +            rgbColorOut[0] = hmds->LatencyTest2DrawColor[0];
  1.1011 +            rgbColorOut[1] = hmds->LatencyTest2DrawColor[1];
  1.1012 +            rgbColorOut[2] = hmds->LatencyTest2DrawColor[2];
  1.1013 +        }
  1.1014 +
  1.1015 +        if(hmds->pRenderer != NULL)
  1.1016 +            hmds->pRenderer->SetLatencyTest2Color(hmds->LatencyTest2DrawColor);
  1.1017 +    }
  1.1018 +    else
  1.1019 +    {
  1.1020 +        if(hmds->pRenderer != NULL)
  1.1021 +            hmds->pRenderer->SetLatencyTest2Color(NULL);
  1.1022 +    }
  1.1023 +
  1.1024 +    return dk2LatencyTest;
  1.1025 +}
  1.1026 +
  1.1027 +
  1.1028 +OVR_EXPORT double ovrHmd_GetMeasuredLatencyTest2(ovrHmd hmddesc)
  1.1029 +{
  1.1030 +    HMDState* p = (HMDState*)hmddesc->Handle;
  1.1031 +
  1.1032 +    // MA Test
  1.1033 +    float latencyRender, latencyTimewarp, latencyPostPresent;
  1.1034 +
  1.1035 +    p->TimeManager.GetLatencyTimings(latencyRender, latencyTimewarp, latencyPostPresent);
  1.1036 +    return latencyPostPresent;
  1.1037 +}
  1.1038 +
  1.1039 +
  1.1040 +//-------------------------------------------------------------------------------------
  1.1041 +// ***** Health and Safety Warning Display interface
  1.1042 +//
  1.1043 +
  1.1044 +OVR_EXPORT void ovrHmd_GetHSWDisplayState(ovrHmd hmd, ovrHSWDisplayState *hswDisplayState)
  1.1045 +{
  1.1046 +    OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle;
  1.1047 +
  1.1048 +    if (pHMDState)
  1.1049 +    {
  1.1050 +        OVR::CAPI::HSWDisplay* pHSWDisplay = pHMDState->pHSWDisplay;
  1.1051 +
  1.1052 +        if(pHSWDisplay)
  1.1053 +            pHSWDisplay->TickState(hswDisplayState); // This may internally call HSWDisplay::Display.
  1.1054 +    }
  1.1055 +}
  1.1056 +
  1.1057 +OVR_EXPORT ovrBool ovrHmd_DismissHSWDisplay(ovrHmd hmd)
  1.1058 +{
  1.1059 +    OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle;
  1.1060 +
  1.1061 +    if (pHMDState)
  1.1062 +    {
  1.1063 +        OVR::CAPI::HSWDisplay* pHSWDisplay = pHMDState->pHSWDisplay;
  1.1064 +
  1.1065 +        if(pHSWDisplay)
  1.1066 +            return (pHSWDisplay->Dismiss() ? 1 : 0);
  1.1067 +    }
  1.1068 +
  1.1069 +    return false;
  1.1070 +}
  1.1071 +
  1.1072 +
  1.1073 +// -----------------------------------------------------------------------------------
  1.1074 +// ***** Property Access
  1.1075 +OVR_EXPORT ovrBool ovrHmd_GetBool(ovrHmd hmddesc,
  1.1076 +                                  const char* propertyName,
  1.1077 +                                  ovrBool defaultVal)
  1.1078 +{
  1.1079 +    OVR_ASSERT(propertyName);
  1.1080 +    if (hmddesc)
  1.1081 +    {
  1.1082 +        HMDState* hmds = (HMDState*)hmddesc->Handle;
  1.1083 +        OVR_ASSERT(hmds);
  1.1084 +        if (hmds)
  1.1085 +        {
  1.1086 +            return hmds->getBoolValue(propertyName, (defaultVal != 0));
  1.1087 +        }
  1.1088 +    }
  1.1089 +
  1.1090 +    return NetClient::GetInstance()->GetBoolValue(InvalidVirtualHmdId, propertyName, (defaultVal != 0)) ? 1 : 0;
  1.1091 +}
  1.1092 +
  1.1093 +OVR_EXPORT ovrBool ovrHmd_SetBool(ovrHmd hmddesc,
  1.1094 +                                  const char* propertyName,
  1.1095 +                                  ovrBool value)
  1.1096 +{
  1.1097 +    OVR_ASSERT(propertyName);
  1.1098 +    if (hmddesc)
  1.1099 +    {
  1.1100 +        HMDState* hmds = (HMDState*)hmddesc->Handle;
  1.1101 +        OVR_ASSERT(hmds);
  1.1102 +        if (hmds)
  1.1103 +        {
  1.1104 +            return hmds->setBoolValue(propertyName, value != 0) ? 1 : 0;
  1.1105 +        }
  1.1106 +    }
  1.1107 +
  1.1108 +    return NetClient::GetInstance()->SetBoolValue(InvalidVirtualHmdId, propertyName, (value != 0)) ? 1 : 0;
  1.1109 +}
  1.1110 +
  1.1111 +OVR_EXPORT int ovrHmd_GetInt(ovrHmd hmddesc,
  1.1112 +                             const char* propertyName,
  1.1113 +                             int defaultVal)
  1.1114 +{
  1.1115 +    OVR_ASSERT(propertyName);
  1.1116 +    if (hmddesc)
  1.1117 +    {
  1.1118 +        HMDState* hmds = (HMDState*)hmddesc->Handle;
  1.1119 +        OVR_ASSERT(hmds);
  1.1120 +        if (hmds)
  1.1121 +        {
  1.1122 +            return hmds->getIntValue(propertyName, defaultVal);
  1.1123 +        }
  1.1124 +    }
  1.1125 +
  1.1126 +    return NetClient::GetInstance()->GetIntValue(InvalidVirtualHmdId, propertyName, defaultVal);
  1.1127 +}
  1.1128 +
  1.1129 +OVR_EXPORT ovrBool ovrHmd_SetInt(ovrHmd hmddesc,
  1.1130 +                                 const char* propertyName,
  1.1131 +                                 int value)
  1.1132 +{
  1.1133 +    OVR_ASSERT(propertyName);
  1.1134 +    if (hmddesc)
  1.1135 +    {
  1.1136 +        HMDState* hmds = (HMDState*)hmddesc->Handle;
  1.1137 +        OVR_ASSERT(hmds);
  1.1138 +        if (hmds)
  1.1139 +        {
  1.1140 +            return hmds->setIntValue(propertyName, value) ? 1 : 0;
  1.1141 +        }
  1.1142 +    }
  1.1143 +
  1.1144 +    return NetClient::GetInstance()->SetIntValue(InvalidVirtualHmdId, propertyName, value) ? 1 : 0;
  1.1145 +}
  1.1146 +
  1.1147 +OVR_EXPORT float ovrHmd_GetFloat(ovrHmd hmddesc,
  1.1148 +                                 const char* propertyName,
  1.1149 +                                 float defaultVal)
  1.1150 +{
  1.1151 +    OVR_ASSERT(propertyName);
  1.1152 +    if (hmddesc)
  1.1153 +    {
  1.1154 +        HMDState* hmds = (HMDState*)hmddesc->Handle;
  1.1155 +        OVR_ASSERT(hmds);
  1.1156 +        if (hmds)
  1.1157 +        {
  1.1158 +            return hmds->getFloatValue(propertyName, defaultVal);
  1.1159 +        }
  1.1160 +    }
  1.1161 +
  1.1162 +    return (float)NetClient::GetInstance()->GetNumberValue(InvalidVirtualHmdId, propertyName, defaultVal);
  1.1163 +}
  1.1164 +
  1.1165 +OVR_EXPORT ovrBool ovrHmd_SetFloat(ovrHmd hmddesc,
  1.1166 +                                   const char* propertyName,
  1.1167 +                                   float value)
  1.1168 +{
  1.1169 +    OVR_ASSERT(propertyName);
  1.1170 +    if (hmddesc)
  1.1171 +    {
  1.1172 +        HMDState* hmds = (HMDState*)hmddesc->Handle;
  1.1173 +        OVR_ASSERT(hmds);
  1.1174 +        if (hmds)
  1.1175 +        {
  1.1176 +            return hmds->setFloatValue(propertyName, value) ? 1 : 0;
  1.1177 +        }
  1.1178 +    }
  1.1179 +
  1.1180 +    return NetClient::GetInstance()->SetNumberValue(InvalidVirtualHmdId, propertyName, value) ? 1 : 0;
  1.1181 +}
  1.1182 +
  1.1183 +OVR_EXPORT unsigned int ovrHmd_GetFloatArray(ovrHmd hmddesc,
  1.1184 +                                             const char* propertyName,
  1.1185 +                                             float values[],
  1.1186 +                                             unsigned int arraySize)
  1.1187 +{
  1.1188 +    OVR_ASSERT(propertyName);
  1.1189 +    OVR_ASSERT(hmddesc);
  1.1190 +    if (hmddesc)
  1.1191 +    {
  1.1192 +        HMDState* hmds = (HMDState*)hmddesc->Handle;
  1.1193 +        OVR_ASSERT(hmds);
  1.1194 +        if (hmds)
  1.1195 +        {
  1.1196 +            return hmds->getFloatArray(propertyName, values, arraySize);
  1.1197 +        }
  1.1198 +    }
  1.1199 +
  1.1200 +    // FIXME: Currently it takes a few lines of code to do this, so just not supported for now.
  1.1201 +    return 0;
  1.1202 +}
  1.1203 +
  1.1204 +// Modify float[] property; false if property doesn't exist or is readonly.
  1.1205 +OVR_EXPORT ovrBool ovrHmd_SetFloatArray(ovrHmd hmddesc,
  1.1206 +                                        const char* propertyName,
  1.1207 +                                        float values[],
  1.1208 +                                        unsigned int arraySize)
  1.1209 +{
  1.1210 +    OVR_ASSERT(propertyName);
  1.1211 +    OVR_ASSERT(hmddesc);
  1.1212 +    if (hmddesc)
  1.1213 +    {
  1.1214 +        HMDState* hmds = (HMDState*)hmddesc->Handle;
  1.1215 +        OVR_ASSERT(hmds);
  1.1216 +        if (hmds)
  1.1217 +        {
  1.1218 +            return hmds->setFloatArray(propertyName, values, arraySize) ? 1 : 0;
  1.1219 +        }
  1.1220 +    }
  1.1221 +
  1.1222 +    // FIXME: Currently it takes a few lines of code to do this, so just not supported for now.
  1.1223 +    return 0;
  1.1224 +}
  1.1225 +
  1.1226 +OVR_EXPORT const char* ovrHmd_GetString(ovrHmd hmddesc,
  1.1227 +                                        const char* propertyName,
  1.1228 +                                        const char* defaultVal)
  1.1229 +{
  1.1230 +    OVR_ASSERT(propertyName);
  1.1231 +    if (hmddesc)
  1.1232 +    {
  1.1233 +        HMDState* hmds = (HMDState*)hmddesc->Handle;
  1.1234 +        if (hmds)
  1.1235 +        {
  1.1236 +            return hmds->getString(propertyName, defaultVal);
  1.1237 +        }
  1.1238 +    }
  1.1239 +
  1.1240 +    return NetClient::GetInstance()->GetStringValue(InvalidVirtualHmdId, propertyName, defaultVal);
  1.1241 +}
  1.1242 + 
  1.1243 +OVR_EXPORT ovrBool ovrHmd_SetString(ovrHmd hmddesc,
  1.1244 +                                    const char* propertyName,
  1.1245 +                                    const char* value)
  1.1246 +{
  1.1247 +    OVR_ASSERT(propertyName);
  1.1248 +    if (hmddesc)
  1.1249 +    {
  1.1250 +        HMDState* hmds = (HMDState*)hmddesc->Handle;
  1.1251 +        if (hmds)
  1.1252 +        {
  1.1253 +            return hmds->setString(propertyName, value) ? 1 : 0;
  1.1254 +        }
  1.1255 +    }
  1.1256 +
  1.1257 +    return NetClient::GetInstance()->SetStringValue(InvalidVirtualHmdId, propertyName, value) ? 1 : 0;
  1.1258 +}
  1.1259 +
  1.1260 +// -----------------------------------------------------------------------------------
  1.1261 +// ***** Logging
  1.1262 +
  1.1263 +OVR_EXPORT ovrBool ovrHmd_StartPerfLog(ovrHmd hmd, const char* fileName, const char* userData1)
  1.1264 +{
  1.1265 +    OVR_ASSERT(fileName && fileName[0]);
  1.1266 +
  1.1267 +    OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle;
  1.1268 +
  1.1269 +    if (pHMDState)
  1.1270 +    {
  1.1271 +        ovrBool started = pHMDState->LagStatsCSV.Start(fileName, userData1) ? 1 : 0;
  1.1272 +        if (started)
  1.1273 +            pHMDState->LagStats.AddResultsObserver(pHMDState->LagStatsCSV.GetObserver());
  1.1274 +        return started;
  1.1275 +    }
  1.1276 +    return 0;
  1.1277 +}
  1.1278 +OVR_EXPORT ovrBool ovrHmd_StopPerfLog(ovrHmd hmd)
  1.1279 +{
  1.1280 +    OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle;
  1.1281 +
  1.1282 +    if (pHMDState)
  1.1283 +    {
  1.1284 +        return pHMDState->LagStatsCSV.Stop() ? 1 : 0;
  1.1285 +    }
  1.1286 +    return false;
  1.1287 +}
  1.1288 +
  1.1289 +
  1.1290 +#ifdef __cplusplus 
  1.1291 +} // extern "C"
  1.1292 +#endif