ovr_sdk
diff LibOVR/Src/CAPI/CAPI_HSWDisplay.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/CAPI/CAPI_HSWDisplay.cpp Wed Jan 14 06:51:16 2015 +0200 1.3 @@ -0,0 +1,482 @@ 1.4 +/************************************************************************************ 1.5 + 1.6 +Filename : CAPI_HSWDisplay.cpp 1.7 +Content : Implements Health and Safety Warning system. 1.8 +Created : July 3, 2014 1.9 +Authors : Paul Pedriana 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 "CAPI_HSWDisplay.h" 1.31 +#include "CAPI_HMDState.h" 1.32 +#include "../Kernel/OVR_Log.h" 1.33 +#include "../Kernel/OVR_String.h" 1.34 +#include "Textures/healthAndSafety.tga.h" // TGA file as a C array declaration. 1.35 +#include <stdlib.h> 1.36 + 1.37 +//------------------------------------------------------------------------------------- 1.38 +// ***** HSWDISPLAY_DEBUGGING 1.39 +// 1.40 +// Defined as 0 or 1. Enables debugging features of this module. 1.41 + 1.42 +#if !defined(HSWDISPLAY_DEBUGGING) 1.43 + #if defined(AUTHOR_PPEDRIANA) 1.44 + #define HSWDISPLAY_DEBUGGING 1 1.45 + #else 1.46 + #define HSWDISPLAY_DEBUGGING 0 1.47 + #endif 1.48 +#endif 1.49 + 1.50 +#if HSWDISPLAY_DEBUGGING 1.51 + OVR_DISABLE_ALL_MSVC_WARNINGS() 1.52 + #include <winsock2.h> 1.53 + #include <Windows.h> 1.54 + OVR_RESTORE_ALL_MSVC_WARNINGS() 1.55 +#endif 1.56 + 1.57 +OVR_DISABLE_MSVC_WARNING(4996) // "This function or variable may be unsafe..." 1.58 + 1.59 + 1.60 +//------------------------------------------------------------------------------------- 1.61 +// ***** HSWDISPLAY_DEFAULT_ENABLED 1.62 +// 1.63 +// Defined as 0 or 1. 1 is default. If 0 then by default HSWDisplay is disabled. 1.64 +// Developers can set it to 0 to disable HSW display. 1.65 +// 1.66 +#if !defined(HSWDISPLAY_DEFAULT_ENABLED) 1.67 + #define HSWDISPLAY_DEFAULT_ENABLED 1 1.68 +#endif 1.69 + 1.70 + 1.71 + 1.72 +//------------------------------------------------------------------------------------- 1.73 +// ***** Experimental C API functions 1.74 +// 1.75 + 1.76 +extern "C" 1.77 +{ 1.78 + OVR_EXPORT void ovrhmd_EnableHSWDisplaySDKRender(ovrHmd hmd, ovrBool enabled) 1.79 + { 1.80 + OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)hmd->Handle; 1.81 + 1.82 + if (pHMDState) 1.83 + { 1.84 + OVR::CAPI::HSWDisplay* pHSWDisplay = pHMDState->pHSWDisplay; 1.85 + 1.86 + if(pHSWDisplay) 1.87 + pHSWDisplay->EnableRender((enabled == 0) ? false : true); 1.88 + } 1.89 + } 1.90 +} 1.91 + 1.92 + 1.93 + 1.94 + 1.95 +//------------------------------------------------------------------------------------- 1.96 +// ***** HSWDisplay implementation 1.97 +// 1.98 + 1.99 +namespace OVR { namespace CAPI { 1.100 + 1.101 + 1.102 +static const time_t HSWDisplayTimeNever = (time_t)0; // Constant which denotes the time of "never", as in the display has never been shown yet. 1.103 + 1.104 +#define HSWDISPLAY_POLL_INTERVAL 0.400 // Seconds between polling for whether the display should be shown. 1.105 +#define OVR_KEY_HSWDISPLAYLASTDISPLAYEDTIME "HASWLastDisplayedTime" 1.106 + 1.107 + 1.108 +#if defined(OVR_BUILD_DEBUG) 1.109 + #define HSWDISPLAY_FIRST_DISMISSAL_TIME 4 // Earliest time in seconds until the user can dismiss the display. 1.110 + #define HSWDISPLAY_REGULAR_DISMISSAL_TIME 2 1.111 +#else 1.112 + #define HSWDISPLAY_FIRST_DISMISSAL_TIME 15 1.113 + #define HSWDISPLAY_REGULAR_DISMISSAL_TIME 6 1.114 +#endif 1.115 + 1.116 + 1.117 +HSWDisplay::HSWDisplay(ovrRenderAPIType renderAPIType, ovrHmd hmd, const HMDRenderState& hmdRenderState) 1.118 + : Enabled(HSWDISPLAY_DEFAULT_ENABLED ? true : false), 1.119 + Displayed(false), 1.120 + SDKRendered(false), 1.121 + DismissRequested(false), 1.122 + RenderEnabled(true), 1.123 + UnloadGraphicsRequested(false), 1.124 + StartTime(0.0), 1.125 + DismissibleTime(0.0), 1.126 + LastPollTime(0.0), 1.127 + HMD(hmd), 1.128 + HMDMounted(false), 1.129 + HMDNewlyMounted(false), 1.130 + RenderAPIType(renderAPIType), 1.131 + RenderState(hmdRenderState), 1.132 + LastProfileName(), 1.133 + LastHSWTime(0) 1.134 +{ 1.135 +} 1.136 + 1.137 + 1.138 +HSWDisplay::~HSWDisplay() 1.139 +{ 1.140 + // To consider: assert that we are already shut down. 1.141 + HSWDisplay::Shutdown(); 1.142 +} 1.143 + 1.144 + 1.145 +void HSWDisplay::Enable(bool enable) 1.146 +{ 1.147 + Enabled = enable; 1.148 + 1.149 + if(!enable && Displayed) // If it's visible but should not be... 1.150 + Dismiss(); 1.151 +} 1.152 + 1.153 + 1.154 +void HSWDisplay::EnableRender(bool enable) 1.155 +{ 1.156 + RenderEnabled = enable; 1.157 +} 1.158 + 1.159 + 1.160 +void HSWDisplay::Display() 1.161 +{ 1.162 + HSWDISPLAY_LOG(("[HSWDisplay] Display()")); 1.163 + 1.164 + DisplayInternal(); 1.165 + 1.166 + HMDNewlyMounted = false; 1.167 + Displayed = true; 1.168 + SDKRendered = RenderEnabled; 1.169 + StartTime = ovr_GetTimeInSeconds(); 1.170 + 1.171 + const time_t lastDisplayedTime = HSWDisplay::GetCurrentProfileLastHSWTime(); 1.172 + DismissibleTime = StartTime + ((lastDisplayedTime == HSWDisplayTimeNever) ? HSWDISPLAY_FIRST_DISMISSAL_TIME : HSWDISPLAY_REGULAR_DISMISSAL_TIME); 1.173 + 1.174 + SetCurrentProfileLastHSWTime(time(NULL)); 1.175 +} 1.176 + 1.177 + 1.178 +bool HSWDisplay::IsDisplayViewable() const 1.179 +{ 1.180 + // This function is called IsDisplayViewable, but currently it refers only to whether the 1.181 + // HMD is mounted on the user's head. 1.182 + 1.183 + return HMDMounted; 1.184 +} 1.185 + 1.186 + 1.187 +bool HSWDisplay::Dismiss() 1.188 +{ 1.189 + #if HSWDISPLAY_DEBUGGING && defined(OVR_OS_WIN32) 1.190 + if(GetKeyState(VK_SCROLL) & 0x0001) // If the scroll lock key is toggled on... 1.191 + return false; // Make it so that the display doesn't dismiss, so we can debug this. 1.192 + #endif 1.193 + 1.194 + // If dismissal is not requested yet, mark it as such. 1.195 + bool newlyRequested = false; 1.196 + 1.197 + if(!DismissRequested) 1.198 + { 1.199 + DismissRequested = true; 1.200 + newlyRequested = true; 1.201 + } 1.202 + 1.203 + // If displayed and time has elapsed, do the dismissal. 1.204 + OVR_ASSERT(DismissibleTime <= (ovr_GetTimeInSeconds() + HSWDISPLAY_FIRST_DISMISSAL_TIME)); // Make sure the dismissal time is sane. 1.205 + if (Displayed && (ovr_GetTimeInSeconds() >= DismissibleTime)) 1.206 + { 1.207 + DismissInternal(); 1.208 + Displayed = false; 1.209 + DismissRequested = false; 1.210 + SDKRendered = false; 1.211 + return true; 1.212 + } 1.213 + 1.214 + if(newlyRequested) 1.215 + { HSWDISPLAY_LOG(("[HSWDisplay] Dismiss(): Not permitted yet. Queued for timeout in %.1f seconds.", DismissibleTime - ovr_GetTimeInSeconds())); } 1.216 + 1.217 + return false; // Cannot dismiss yet. 1.218 +} 1.219 + 1.220 + 1.221 +bool HSWDisplay::TickState(ovrHSWDisplayState *hswDisplayState, bool graphicsContext) 1.222 +{ 1.223 + bool newlyDisplayed = false; 1.224 + const double currentTime = ovr_GetTimeInSeconds(); 1.225 + 1.226 + // See if we need to be currently displayed. By design we automatically display but don't automatically dismiss. 1.227 + if (Displayed) 1.228 + { 1.229 + if (DismissRequested) // If dismiss was previously requested, see if it can be executed. 1.230 + Dismiss(); 1.231 + 1.232 + if (Displayed) // If not already dismissed above... 1.233 + { 1.234 + // We currently have the debug behavior that we permit dismiss very soon after launch. 1.235 + #if defined(OVR_BUILD_DEBUG) 1.236 + if(currentTime >= (StartTime + 2)) 1.237 + { 1.238 + DismissibleTime = StartTime; 1.239 + //Dismiss(); 1.240 + } 1.241 + #endif 1.242 + } 1.243 + 1.244 + if (Displayed) // If not already dismissed above... 1.245 + { 1.246 + const ovrTrackingState ts = ((OVR::CAPI::HMDState*)HMD->Handle)->PredictedTrackingState(currentTime); 1.247 + 1.248 + if (ts.StatusFlags & ovrStatus_OrientationTracked) // If the Accelerometer data is valid... 1.249 + { 1.250 + const OVR::Vector3f v(ts.HeadPose.LinearAcceleration.x, ts.HeadPose.LinearAcceleration.y, ts.HeadPose.LinearAcceleration.z); 1.251 + 1.252 + const float minTapMagnitude = 350.0f; // Empirically determined by some testing. 1.253 + 1.254 + if (v.LengthSq() > minTapMagnitude) 1.255 + Dismiss(); // This will do nothing if the display is not present. 1.256 + } 1.257 + } 1.258 + } 1.259 + else if (Enabled && (currentTime >= (LastPollTime + HSWDISPLAY_POLL_INTERVAL))) 1.260 + { 1.261 + LastPollTime = currentTime; 1.262 + 1.263 + // We need to display if any of the following are true: 1.264 + // - The application is just started in Event Mode while the HMD is mounted (warning display would be viewable) and this app was not spawned from a launcher. 1.265 + // - The current user has never seen the display yet while the HMD is mounted (warning display would be viewable). 1.266 + // - The HMD is newly mounted (or the warning display is otherwise newly viewable). 1.267 + // - The warning display hasn't shown in 24 hours (need to verify this as a requirement). 1.268 + // Event Mode refers to when the app is being run in a public demo event such as a trade show. 1.269 + 1.270 + OVR::CAPI::HMDState* pHMDState = (OVR::CAPI::HMDState*)HMD->Handle; 1.271 + 1.272 + if(pHMDState) 1.273 + { 1.274 + const time_t lastDisplayedTime = HSWDisplay::GetCurrentProfileLastHSWTime(); 1.275 + 1.276 + // We currently unilaterally set HMDMounted to true because we don't yet have the ability to detect this. To do: Implement this when possible. 1.277 + const bool previouslyMounted = HMDMounted; 1.278 + HMDMounted = true; 1.279 + HMDNewlyMounted = (!previouslyMounted && HMDMounted); // We set this back to false in the Display function or if the HMD is unmounted before then. 1.280 + 1.281 + if((lastDisplayedTime == HSWDisplayTimeNever) || HMDNewlyMounted) 1.282 + { 1.283 + if(IsDisplayViewable()) // If the HMD is mounted and otherwise being viewed by the user... 1.284 + { 1.285 + Display(); 1.286 + newlyDisplayed = true; 1.287 + } 1.288 + } 1.289 + } 1.290 + } 1.291 + else if(graphicsContext && UnloadGraphicsRequested) 1.292 + { 1.293 + UnloadGraphics(); 1.294 + UnloadGraphicsRequested = false; 1.295 + } 1.296 + 1.297 + if(hswDisplayState) 1.298 + GetState(hswDisplayState); 1.299 + 1.300 + return newlyDisplayed; 1.301 +} 1.302 + 1.303 + 1.304 +void HSWDisplay::GetState(ovrHSWDisplayState *hswDisplayState) const 1.305 +{ 1.306 + // Return the state to the caller. 1.307 + OVR_ASSERT(hswDisplayState != NULL); 1.308 + if(hswDisplayState) 1.309 + { 1.310 + hswDisplayState->Displayed = Displayed; 1.311 + hswDisplayState->StartTime = StartTime; 1.312 + hswDisplayState->DismissibleTime = DismissibleTime; 1.313 + } 1.314 +} 1.315 + 1.316 + 1.317 +void HSWDisplay::Render(ovrEyeType eye, const ovrTexture* eyeTexture) 1.318 +{ 1.319 + SDKRendered = true; 1.320 + RenderInternal(eye, eyeTexture); 1.321 +} 1.322 + 1.323 +// Persist the HSW settings on the server, since it needs to be synchronized across all applications. 1.324 +// Note that the profile manager singleton cannot be used for this task because it overwrites the global 1.325 +// settings for which the rift config tool is supposed to be authoritative. That also would step on the 1.326 +// settings generated by other rift apps. The server settings, however, are synchronized for all apps 1.327 +// and so are appropriate for this task. 1.328 +static String getHSWTimeKey(const char* userName) 1.329 +{ 1.330 + String keyName = "server:"; 1.331 + keyName += OVR_KEY_HSWDISPLAYLASTDISPLAYEDTIME; 1.332 + keyName += ":"; 1.333 + if (userName) 1.334 + { 1.335 + keyName += userName; 1.336 + } 1.337 + return keyName; 1.338 +} 1.339 + 1.340 +// Returns HSWDisplayTimeNever (0) if there is no profile or this is the first time we are seeing this profile. 1.341 +time_t HSWDisplay::GetCurrentProfileLastHSWTime() const 1.342 +{ 1.343 + // We store the timeout value in HMDState's pProfile. 1.344 + HMDState* pHMDState = (HMDState*)HMD->Handle; 1.345 + 1.346 + if (pHMDState) 1.347 + { 1.348 + const char* profileName = pHMDState->pProfile ? pHMDState->pProfile->GetValue(OVR_KEY_USER) : NULL; 1.349 + 1.350 + if (profileName) 1.351 + { 1.352 + if (LastProfileName == profileName) 1.353 + { 1.354 + return LastHSWTime; 1.355 + } 1.356 + 1.357 + LastProfileName = profileName; 1.358 + String timeKey = getHSWTimeKey(profileName); 1.359 + int lastTime = pHMDState->getIntValue(timeKey.ToCStr(), (int)HSWDisplayTimeNever); 1.360 + 1.361 + LastHSWTime = lastTime; 1.362 + return lastTime; 1.363 + } 1.364 + } 1.365 + 1.366 + return HSWDisplayTimeNever; 1.367 +} 1.368 + 1.369 +void HSWDisplay::SetCurrentProfileLastHSWTime(time_t t) 1.370 +{ 1.371 + // The timeout value is stored in HMDState's pProfile. 1.372 + HMDState* pHMDState = (HMDState*)HMD->Handle; 1.373 + 1.374 + if (pHMDState) 1.375 + { 1.376 + const char* profileName = pHMDState->pProfile ? pHMDState->pProfile->GetValue(OVR_KEY_USER) : NULL; 1.377 + 1.378 + if (profileName) 1.379 + { 1.380 + LastProfileName = profileName; 1.381 + LastHSWTime = (int)t; 1.382 + 1.383 + String timeKey = getHSWTimeKey(profileName); 1.384 + pHMDState->setIntValue(timeKey.ToCStr(), (int)t); 1.385 + } 1.386 + } 1.387 +} 1.388 + 1.389 + 1.390 +// Generates an appropriate stereo ortho projection matrix. 1.391 +void HSWDisplay::GetOrthoProjection(const HMDRenderState& RenderState, Matrix4f OrthoProjection[2]) 1.392 +{ 1.393 + Matrix4f perspectiveProjection[2]; 1.394 + perspectiveProjection[0] = ovrMatrix4f_Projection(RenderState.EyeRenderDesc[0].Fov, 0.01f, 10000.f, true); 1.395 + perspectiveProjection[1] = ovrMatrix4f_Projection(RenderState.EyeRenderDesc[1].Fov, 0.01f, 10000.f, true); 1.396 + 1.397 + const float orthoDistance = HSWDISPLAY_DISTANCE; // This is meters from the camera (viewer) that we place the ortho plane. 1.398 + const Vector2f orthoScale0 = Vector2f(1.f) / Vector2f(RenderState.EyeRenderDesc[0].PixelsPerTanAngleAtCenter); 1.399 + const Vector2f orthoScale1 = Vector2f(1.f) / Vector2f(RenderState.EyeRenderDesc[1].PixelsPerTanAngleAtCenter); 1.400 + 1.401 + OrthoProjection[0] = ovrMatrix4f_OrthoSubProjection(perspectiveProjection[0], orthoScale0, orthoDistance, RenderState.EyeRenderDesc[0].HmdToEyeViewOffset.x); 1.402 + OrthoProjection[1] = ovrMatrix4f_OrthoSubProjection(perspectiveProjection[1], orthoScale1, orthoDistance, RenderState.EyeRenderDesc[1].HmdToEyeViewOffset.x); 1.403 +} 1.404 + 1.405 + 1.406 +const uint8_t* HSWDisplay::GetDefaultTexture(size_t& TextureSize) 1.407 +{ 1.408 + TextureSize = sizeof(healthAndSafety_tga); 1.409 + return healthAndSafety_tga; 1.410 +} 1.411 + 1.412 + 1.413 + 1.414 +}} // namespace OVR::CAPI 1.415 + 1.416 + 1.417 + 1.418 + 1.419 +//------------------------------------------------------------------------------------- 1.420 +// ***** HSWDisplay factory 1.421 +// 1.422 + 1.423 +#if defined (OVR_OS_WIN32) 1.424 + #define OVR_D3D_VERSION 9 1.425 + #include "D3D9/CAPI_D3D9_HSWDisplay.h" 1.426 + #undef OVR_D3D_VERSION 1.427 + 1.428 + #define OVR_D3D_VERSION 10 1.429 + #include "D3D1X/CAPI_D3D10_HSWDisplay.h" 1.430 + #undef OVR_D3D_VERSION 1.431 + 1.432 + #define OVR_D3D_VERSION 11 1.433 + #include "D3D1X/CAPI_D3D11_HSWDisplay.h" 1.434 + #undef OVR_D3D_VERSION 1.435 +#endif 1.436 + 1.437 +#include "GL/CAPI_GL_HSWDisplay.h" 1.438 + 1.439 + 1.440 +OVR::CAPI::HSWDisplay* OVR::CAPI::HSWDisplay::Factory(ovrRenderAPIType apiType, ovrHmd hmd, const OVR::CAPI::HMDRenderState& renderState) 1.441 +{ 1.442 + OVR::CAPI::HSWDisplay* pHSWDisplay = NULL; 1.443 + 1.444 + switch (apiType) 1.445 + { 1.446 + case ovrRenderAPI_None: 1.447 + pHSWDisplay = new OVR::CAPI::HSWDisplay(apiType, hmd, renderState); 1.448 + break; 1.449 + 1.450 + case ovrRenderAPI_OpenGL: 1.451 + pHSWDisplay = new OVR::CAPI::GL::HSWDisplay(apiType, hmd, renderState); 1.452 + break; 1.453 + 1.454 + #if defined(OVR_OS_WIN32) 1.455 + case ovrRenderAPI_D3D9: 1.456 + pHSWDisplay = new OVR::CAPI::D3D9::HSWDisplay(apiType, hmd, renderState); 1.457 + break; 1.458 + 1.459 + case ovrRenderAPI_D3D10: 1.460 + pHSWDisplay = new OVR::CAPI::D3D10::HSWDisplay(apiType, hmd, renderState); 1.461 + break; 1.462 + 1.463 + case ovrRenderAPI_D3D11: 1.464 + pHSWDisplay = new OVR::CAPI::D3D11::HSWDisplay(apiType, hmd, renderState); 1.465 + break; 1.466 + #else 1.467 + case ovrRenderAPI_D3D9: 1.468 + case ovrRenderAPI_D3D10: 1.469 + case ovrRenderAPI_D3D11: // Fall through 1.470 + #endif 1.471 + 1.472 + // Handle unsupported cases. 1.473 + case ovrRenderAPI_Android_GLES: 1.474 + case ovrRenderAPI_Count: // This is not actually a type. 1.475 + default: 1.476 + break; 1.477 + } 1.478 + 1.479 + return pHSWDisplay; 1.480 +} 1.481 + 1.482 + 1.483 + 1.484 + 1.485 +