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