nuclear@0: /************************************************************************************ nuclear@0: nuclear@0: Filename : CAPI_HMDState.cpp nuclear@0: Content : State associated with a single HMD nuclear@0: Created : January 24, 2014 nuclear@0: Authors : Michael Antonov nuclear@0: nuclear@0: Copyright : Copyright 2014 Oculus VR, LLC All Rights reserved. nuclear@0: nuclear@0: Licensed under the Oculus VR Rift SDK License Version 3.2 (the "License"); nuclear@0: you may not use the Oculus VR Rift SDK except in compliance with the License, nuclear@0: which is provided at the time of installation or download, or which nuclear@0: otherwise accompanies this software in either electronic or hard copy form. nuclear@0: nuclear@0: You may obtain a copy of the License at nuclear@0: nuclear@0: http://www.oculusvr.com/licenses/LICENSE-3.2 nuclear@0: nuclear@0: Unless required by applicable law or agreed to in writing, the Oculus VR SDK nuclear@0: distributed under the License is distributed on an "AS IS" BASIS, nuclear@0: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. nuclear@0: See the License for the specific language governing permissions and nuclear@0: limitations under the License. nuclear@0: nuclear@0: ************************************************************************************/ nuclear@0: nuclear@0: #include "CAPI_HMDState.h" nuclear@0: #include "../OVR_Profile.h" nuclear@0: #include "../Service/Service_NetClient.h" nuclear@0: #ifdef OVR_OS_WIN32 nuclear@0: #include "../Displays/OVR_Win32_ShimFunctions.h" nuclear@0: #endif nuclear@0: nuclear@0: nuclear@0: namespace OVR { namespace CAPI { nuclear@0: nuclear@0: nuclear@0: // Accessed via HMDState::GetHMDStateList() nuclear@0: static OVR::List hmdStateList; // List of all created HMDStates. nuclear@0: nuclear@0: nuclear@0: //------------------------------------------------------------------------------------- nuclear@0: // ***** HMDState nuclear@0: nuclear@0: HMDState::HMDState(const OVR::Service::HMDNetworkInfo& netInfo, nuclear@0: const OVR::HMDInfo& hmdInfo, nuclear@0: Profile* profile, nuclear@0: Service::NetClient* client) : nuclear@0: pProfile(profile), nuclear@0: pHmdDesc(0), nuclear@0: pWindow(0), nuclear@0: pClient(client), nuclear@0: NetId(netInfo.NetId), nuclear@0: NetInfo(netInfo), nuclear@0: OurHMDInfo(hmdInfo), nuclear@0: pLastError(NULL), nuclear@0: EnabledHmdCaps(0), nuclear@0: EnabledServiceHmdCaps(0), nuclear@0: SharedStateReader(), nuclear@0: TheSensorStateReader(), nuclear@0: TheLatencyTestStateReader(), nuclear@0: LatencyTestActive(false), nuclear@0: //LatencyTestDrawColor(), nuclear@0: LatencyTest2Active(false), nuclear@0: //LatencyTest2DrawColor(), nuclear@0: TimeManager(true), nuclear@0: RenderState(), nuclear@0: pRenderer(), nuclear@0: pHSWDisplay(), nuclear@0: LastFrameTimeSeconds(0.), nuclear@0: LastGetFrameTimeSeconds(0.), nuclear@0: //LastGetStringValue(), nuclear@0: RenderingConfigured(false), nuclear@0: BeginFrameCalled(false), nuclear@0: BeginFrameThreadId(), nuclear@0: RenderAPIThreadChecker(), nuclear@0: BeginFrameTimingCalled(false) nuclear@0: { nuclear@0: sharedInit(profile); nuclear@0: hmdStateList.PushBack(this); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: HMDState::HMDState(const OVR::HMDInfo& hmdInfo, Profile* profile) : nuclear@0: pProfile(profile), nuclear@0: pHmdDesc(0), nuclear@0: pWindow(0), nuclear@0: pClient(0), nuclear@0: NetId(InvalidVirtualHmdId), nuclear@0: NetInfo(), nuclear@0: OurHMDInfo(hmdInfo), nuclear@0: pLastError(NULL), nuclear@0: EnabledHmdCaps(0), nuclear@0: EnabledServiceHmdCaps(0), nuclear@0: SharedStateReader(), nuclear@0: TheSensorStateReader(), nuclear@0: TheLatencyTestStateReader(), nuclear@0: LatencyTestActive(false), nuclear@0: //LatencyTestDrawColor(), nuclear@0: LatencyTest2Active(false), nuclear@0: //LatencyTest2DrawColor(), nuclear@0: TimeManager(true), nuclear@0: RenderState(), nuclear@0: pRenderer(), nuclear@0: pHSWDisplay(), nuclear@0: LastFrameTimeSeconds(0.), nuclear@0: LastGetFrameTimeSeconds(0.), nuclear@0: //LastGetStringValue(), nuclear@0: RenderingConfigured(false), nuclear@0: BeginFrameCalled(false), nuclear@0: BeginFrameThreadId(), nuclear@0: RenderAPIThreadChecker(), nuclear@0: BeginFrameTimingCalled(false) nuclear@0: { nuclear@0: sharedInit(profile); nuclear@0: hmdStateList.PushBack(this); nuclear@0: } nuclear@0: nuclear@0: HMDState::~HMDState() nuclear@0: { nuclear@0: hmdStateList.Remove(this); nuclear@0: nuclear@0: if (pClient) nuclear@0: { nuclear@0: pClient->Hmd_Release(NetId); nuclear@0: pClient = 0; nuclear@0: } nuclear@0: nuclear@0: ConfigureRendering(0,0,0,0); nuclear@0: nuclear@0: if (pHmdDesc) nuclear@0: { nuclear@0: OVR_FREE(pHmdDesc); nuclear@0: pHmdDesc = NULL; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: void HMDState::sharedInit(Profile* profile) nuclear@0: { nuclear@0: // TBD: We should probably be looking up the default profile for the given nuclear@0: // device type + user if profile == 0. nuclear@0: pLastError = 0; nuclear@0: nuclear@0: RenderState.OurHMDInfo = OurHMDInfo; nuclear@0: nuclear@0: UpdateRenderProfile(profile); nuclear@0: nuclear@0: OVR_ASSERT(!pHmdDesc); nuclear@0: pHmdDesc = (ovrHmdDesc*)OVR_ALLOC(sizeof(ovrHmdDesc)); nuclear@0: *pHmdDesc = RenderState.GetDesc(); nuclear@0: pHmdDesc->Handle = this; nuclear@0: nuclear@0: RenderState.ClearColor[0] = 0.0f; nuclear@0: RenderState.ClearColor[1] = 0.0f; nuclear@0: RenderState.ClearColor[2] = 0.0f; nuclear@0: RenderState.ClearColor[3] = 0.0f; nuclear@0: nuclear@0: RenderState.EnabledHmdCaps = 0; nuclear@0: nuclear@0: TimeManager.Init(RenderState.RenderInfo); nuclear@0: nuclear@0: /* nuclear@0: LatencyTestDrawColor[0] = 0; nuclear@0: LatencyTestDrawColor[1] = 0; nuclear@0: LatencyTestDrawColor[2] = 0; nuclear@0: */ nuclear@0: nuclear@0: RenderingConfigured = false; nuclear@0: BeginFrameCalled = false; nuclear@0: BeginFrameThreadId = 0; nuclear@0: BeginFrameTimingCalled = false; nuclear@0: nuclear@0: // Construct the HSWDisplay. We will later reconstruct it with a specific ovrRenderAPI type if the application starts using SDK-based rendering. nuclear@0: if(!pHSWDisplay) nuclear@0: { nuclear@0: pHSWDisplay = *OVR::CAPI::HSWDisplay::Factory(ovrRenderAPI_None, pHmdDesc, RenderState); nuclear@0: pHSWDisplay->Enable(pProfile->GetBoolValue("HSW", true)); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: static Vector3f GetNeckModelFromProfile(Profile* profile) nuclear@0: { nuclear@0: OVR_ASSERT(profile); nuclear@0: nuclear@0: float neckeye[2] = { OVR_DEFAULT_NECK_TO_EYE_HORIZONTAL, OVR_DEFAULT_NECK_TO_EYE_VERTICAL }; nuclear@0: profile->GetFloatValues(OVR_KEY_NECK_TO_EYE_DISTANCE, neckeye, 2); nuclear@0: nuclear@0: // Make sure these are vaguely sensible values. nuclear@0: //OVR_ASSERT((neckeye[0] > 0.05f) && (neckeye[0] < 0.5f)); nuclear@0: //OVR_ASSERT((neckeye[1] > 0.05f) && (neckeye[1] < 0.5f)); nuclear@0: nuclear@0: // Named for clarity nuclear@0: float NeckToEyeHorizontal = neckeye[0]; nuclear@0: float NeckToEyeVertical = neckeye[1]; nuclear@0: nuclear@0: // Store the neck model nuclear@0: return Vector3f(0.0, NeckToEyeVertical, -NeckToEyeHorizontal); nuclear@0: } nuclear@0: nuclear@0: static float GetCenterPupilDepthFromRenderInfo(HmdRenderInfo* hmdRenderInfo) nuclear@0: { nuclear@0: OVR_ASSERT(hmdRenderInfo); nuclear@0: nuclear@0: // Find the distance from the center of the screen to the "center eye" nuclear@0: // This center eye is used by systems like rendering & audio to represent the player, nuclear@0: // and they will handle the offsets needed from there to each actual eye. nuclear@0: nuclear@0: // HACK HACK HACK nuclear@0: // We know for DK1 the screen->lens surface distance is roughly 0.049f, and that the faceplate->lens is 0.02357f. nuclear@0: // We're going to assume(!!!!) that all HMDs have the same screen->faceplate distance. nuclear@0: // Crystal Cove was measured to be roughly 0.025 screen->faceplate which agrees with this assumption. nuclear@0: // TODO: do this properly! Update: Measured this at 0.02733 with a CC prototype, CES era (PT7), on 2/19/14 -Steve nuclear@0: float screenCenterToMidplate = 0.02733f; nuclear@0: float centerEyeRelief = hmdRenderInfo->GetEyeCenter().ReliefInMeters; nuclear@0: float CenterPupilDepth = screenCenterToMidplate + hmdRenderInfo->LensSurfaceToMidplateInMeters + centerEyeRelief; nuclear@0: nuclear@0: return CenterPupilDepth; nuclear@0: } nuclear@0: nuclear@0: void HMDState::UpdateRenderProfile(Profile* profile) nuclear@0: { nuclear@0: // Apply the given profile to generate a render context nuclear@0: RenderState.RenderInfo = GenerateHmdRenderInfoFromHmdInfo(RenderState.OurHMDInfo, profile); nuclear@0: RenderState.Distortion[0] = CalculateDistortionRenderDesc(StereoEye_Left, RenderState.RenderInfo, 0); nuclear@0: RenderState.Distortion[1] = CalculateDistortionRenderDesc(StereoEye_Right, RenderState.RenderInfo, 0); nuclear@0: nuclear@0: if (pClient) nuclear@0: { nuclear@0: // Center pupil depth nuclear@0: float centerPupilDepth = GetCenterPupilDepthFromRenderInfo(&RenderState.RenderInfo); nuclear@0: pClient->SetNumberValue(GetNetId(), "CenterPupilDepth", centerPupilDepth); nuclear@0: nuclear@0: // Neck model nuclear@0: Vector3f neckModel = GetNeckModelFromProfile(profile); nuclear@0: double neckModelArray[3] = { nuclear@0: neckModel.x, nuclear@0: neckModel.y, nuclear@0: neckModel.z nuclear@0: }; nuclear@0: pClient->SetNumberValues(GetNetId(), "NeckModelVector3f", neckModelArray, 3); nuclear@0: nuclear@0: double camerastate[7]; nuclear@0: if (profile->GetDoubleValues(OVR_KEY_CAMERA_POSITION, camerastate, 7) == 0) nuclear@0: { nuclear@0: //there is no value, so we load the default nuclear@0: for (int i = 0; i < 7; i++) camerastate[i] = 0; nuclear@0: camerastate[3] = 1;//no offset. by default, give the quaternion w component value 1 nuclear@0: } nuclear@0: else nuclear@0: nuclear@0: TheSensorStateReader.setCenteredFromWorld(OVR::Posed::FromArray(camerastate)); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: } nuclear@0: nuclear@0: HMDState* HMDState::CreateHMDState(NetClient* client, const HMDNetworkInfo& netInfo) nuclear@0: { nuclear@0: // HMDState works through a handle to service HMD.... nuclear@0: HMDInfo hinfo; nuclear@0: if (!client->Hmd_GetHmdInfo(netInfo.NetId, &hinfo)) nuclear@0: { nuclear@0: OVR_DEBUG_LOG(("[HMDState] Unable to get HMD info")); nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: #ifdef OVR_OS_WIN32 nuclear@0: OVR_DEBUG_LOG(("Setting up display shim")); nuclear@0: nuclear@0: // Initialize the display shim before reporting the display to the user code nuclear@0: // so that this will happen before the D3D display object is created. nuclear@0: Win32::DisplayShim::GetInstance().Update(&hinfo.ShimInfo); nuclear@0: #endif nuclear@0: nuclear@0: Ptr pDefaultProfile = *ProfileManager::GetInstance()->GetDefaultUserProfile(&hinfo); nuclear@0: OVR_DEBUG_LOG(("Using profile %s", pDefaultProfile->GetValue(OVR_KEY_USER))); nuclear@0: nuclear@0: HMDState* hmds = new HMDState(netInfo, hinfo, pDefaultProfile, client); nuclear@0: nuclear@0: if (!hmds->SharedStateReader.Open(netInfo.SharedMemoryName.ToCStr())) nuclear@0: { nuclear@0: delete hmds; nuclear@0: return NULL; nuclear@0: } nuclear@0: nuclear@0: hmds->TheSensorStateReader.SetUpdater(hmds->SharedStateReader.Get()); nuclear@0: hmds->TheLatencyTestStateReader.SetUpdater(hmds->SharedStateReader.Get()); nuclear@0: nuclear@0: return hmds; nuclear@0: } nuclear@0: nuclear@0: HMDState* HMDState::CreateHMDState(ovrHmdType hmdType) nuclear@0: { nuclear@0: HmdTypeEnum t = HmdType_None; nuclear@0: if (hmdType == ovrHmd_DK1) nuclear@0: t = HmdType_DK1; nuclear@0: else if (hmdType == ovrHmd_DK2) nuclear@0: t = HmdType_DK2; nuclear@0: nuclear@0: // FIXME: This does not actually grab the right user.. nuclear@0: Ptr pDefaultProfile = *ProfileManager::GetInstance()->GetDefaultProfile(t); nuclear@0: nuclear@0: return new HMDState(CreateDebugHMDInfo(t), pDefaultProfile); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: const OVR::List& HMDState::GetHMDStateList() nuclear@0: { nuclear@0: return hmdStateList; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //------------------------------------------------------------------------------------- nuclear@0: // *** Sensor nuclear@0: nuclear@0: bool HMDState::ConfigureTracking(unsigned supportedCaps, unsigned requiredCaps) nuclear@0: { nuclear@0: return pClient ? pClient->Hmd_ConfigureTracking(NetId, supportedCaps, requiredCaps) : true; nuclear@0: } nuclear@0: nuclear@0: void HMDState::ResetTracking() nuclear@0: { nuclear@0: if (pClient) pClient->Hmd_ResetTracking(NetId); nuclear@0: } nuclear@0: nuclear@0: // Re-center the orientation. nuclear@0: void HMDState::RecenterPose() nuclear@0: { nuclear@0: TheSensorStateReader.RecenterPose(); nuclear@0: } nuclear@0: nuclear@0: // Returns prediction for time. nuclear@0: ovrTrackingState HMDState::PredictedTrackingState(double absTime) nuclear@0: { nuclear@0: Tracking::TrackingState ss; nuclear@0: TheSensorStateReader.GetSensorStateAtTime(absTime, ss); nuclear@0: nuclear@0: // Zero out the status flags nuclear@0: if (!pClient || !pClient->IsConnected(false, false)) nuclear@0: { nuclear@0: ss.StatusFlags = 0; nuclear@0: } nuclear@0: nuclear@0: return ss; nuclear@0: } nuclear@0: nuclear@0: void HMDState::SetEnabledHmdCaps(unsigned hmdCaps) nuclear@0: { nuclear@0: if (OurHMDInfo.HmdType < HmdType_DK2) nuclear@0: { nuclear@0: // disable low persistence and pentile. nuclear@0: hmdCaps &= ~ovrHmdCap_LowPersistence; nuclear@0: hmdCaps &= ~ovrHmdCap_DirectPentile; nuclear@0: nuclear@0: // disable dynamic prediction using the internal latency tester nuclear@0: hmdCaps &= ~ovrHmdCap_DynamicPrediction; nuclear@0: } nuclear@0: nuclear@0: if (OurHMDInfo.HmdType >= HmdType_DK2) nuclear@0: { nuclear@0: if ((EnabledHmdCaps ^ hmdCaps) & ovrHmdCap_DynamicPrediction) nuclear@0: { nuclear@0: // DynamicPrediction change nuclear@0: TimeManager.ResetFrameTiming(TimeManager.GetFrameTiming().FrameIndex, nuclear@0: (hmdCaps & ovrHmdCap_DynamicPrediction) ? true : false, nuclear@0: RenderingConfigured); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: // Pentile unsupported on everything right now. nuclear@0: hmdCaps &= ~ovrHmdCap_DirectPentile; nuclear@0: nuclear@0: if ((EnabledHmdCaps ^ hmdCaps) & ovrHmdCap_NoVSync) nuclear@0: { nuclear@0: TimeManager.SetVsync((hmdCaps & ovrHmdCap_NoVSync) ? false : true); nuclear@0: } nuclear@0: nuclear@0: if ((EnabledHmdCaps ^ hmdCaps) & ovrHmdCap_NoMirrorToWindow) nuclear@0: { nuclear@0: #ifdef OVR_OS_WIN32 nuclear@0: Win32::DisplayShim::GetInstance().UseMirroring = (hmdCaps & ovrHmdCap_NoMirrorToWindow) ? nuclear@0: false : true; nuclear@0: if (pWindow) nuclear@0: { // Force window repaint so that stale mirrored image doesn't persist. nuclear@0: ::InvalidateRect((HWND)pWindow, 0, true); nuclear@0: } nuclear@0: #endif nuclear@0: } nuclear@0: nuclear@0: // TBD: Should this include be only the rendering flags? Otherwise, bits that failed nuclear@0: // modification in Hmd_SetEnabledCaps may mis-match... nuclear@0: EnabledHmdCaps = hmdCaps & ovrHmdCap_Writable_Mask; nuclear@0: RenderState.EnabledHmdCaps = EnabledHmdCaps; nuclear@0: nuclear@0: nuclear@0: // If any of the modifiable service caps changed, call on the service. nuclear@0: unsigned prevServiceCaps = EnabledServiceHmdCaps & ovrHmdCap_Writable_Mask; nuclear@0: unsigned newServiceCaps = hmdCaps & ovrHmdCap_Writable_Mask & ovrHmdCap_Service_Mask; nuclear@0: nuclear@0: if (prevServiceCaps ^ newServiceCaps) nuclear@0: { nuclear@0: EnabledServiceHmdCaps = pClient ? pClient->Hmd_SetEnabledCaps(NetId, newServiceCaps) nuclear@0: : newServiceCaps; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: unsigned HMDState::SetEnabledHmdCaps() nuclear@0: { nuclear@0: unsigned serviceCaps = pClient ? pClient->Hmd_GetEnabledCaps(NetId) : nuclear@0: EnabledServiceHmdCaps; nuclear@0: nuclear@0: return serviceCaps & ((~ovrHmdCap_Service_Mask) | EnabledHmdCaps); nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //------------------------------------------------------------------------------------- nuclear@0: // ***** Property Access nuclear@0: nuclear@0: // FIXME: Remove the EGetBoolValue stuff and do it with a "Server:" prefix, so we do not nuclear@0: // need to keep a white-list of keys. This is also way cool because it allows us to add nuclear@0: // new settings keys from outside CAPI that can modify internal server data. nuclear@0: nuclear@0: bool HMDState::getBoolValue(const char* propertyName, bool defaultVal) nuclear@0: { nuclear@0: if (NetSessionCommon::IsServiceProperty(NetSessionCommon::EGetBoolValue, propertyName)) nuclear@0: { nuclear@0: return NetClient::GetInstance()->GetBoolValue(GetNetId(), propertyName, defaultVal); nuclear@0: } nuclear@0: else if (pProfile) nuclear@0: { nuclear@0: return pProfile->GetBoolValue(propertyName, defaultVal); nuclear@0: } nuclear@0: return defaultVal; nuclear@0: } nuclear@0: nuclear@0: bool HMDState::setBoolValue(const char* propertyName, bool value) nuclear@0: { nuclear@0: if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetBoolValue, propertyName)) nuclear@0: { nuclear@0: return NetClient::GetInstance()->SetBoolValue(GetNetId(), propertyName, value); nuclear@0: } nuclear@0: nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: int HMDState::getIntValue(const char* propertyName, int defaultVal) nuclear@0: { nuclear@0: if (NetSessionCommon::IsServiceProperty(NetSessionCommon::EGetIntValue, propertyName)) nuclear@0: { nuclear@0: return NetClient::GetInstance()->GetIntValue(GetNetId(), propertyName, defaultVal); nuclear@0: } nuclear@0: else if (pProfile) nuclear@0: { nuclear@0: return pProfile->GetIntValue(propertyName, defaultVal); nuclear@0: } nuclear@0: return defaultVal; nuclear@0: } nuclear@0: nuclear@0: bool HMDState::setIntValue(const char* propertyName, int value) nuclear@0: { nuclear@0: if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetIntValue, propertyName)) nuclear@0: { nuclear@0: return NetClient::GetInstance()->SetIntValue(GetNetId(), propertyName, value); nuclear@0: } nuclear@0: nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: float HMDState::getFloatValue(const char* propertyName, float defaultVal) nuclear@0: { nuclear@0: if (OVR_strcmp(propertyName, "LensSeparation") == 0) nuclear@0: { nuclear@0: return OurHMDInfo.LensSeparationInMeters; nuclear@0: } nuclear@0: else if (OVR_strcmp(propertyName, "VsyncToNextVsync") == 0) nuclear@0: { nuclear@0: return OurHMDInfo.Shutter.VsyncToNextVsync; nuclear@0: } nuclear@0: else if (OVR_strcmp(propertyName, "PixelPersistence") == 0) nuclear@0: { nuclear@0: return OurHMDInfo.Shutter.PixelPersistence; nuclear@0: } nuclear@0: else if (NetSessionCommon::IsServiceProperty(NetSessionCommon::EGetNumberValue, propertyName)) nuclear@0: { nuclear@0: return (float)NetClient::GetInstance()->GetNumberValue(GetNetId(), propertyName, defaultVal); nuclear@0: } nuclear@0: else if (pProfile) nuclear@0: { nuclear@0: return pProfile->GetFloatValue(propertyName, defaultVal); nuclear@0: } nuclear@0: nuclear@0: return defaultVal; nuclear@0: } nuclear@0: nuclear@0: bool HMDState::setFloatValue(const char* propertyName, float value) nuclear@0: { nuclear@0: if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetNumberValue, propertyName)) nuclear@0: { nuclear@0: return NetClient::GetInstance()->SetNumberValue(GetNetId(), propertyName, value); nuclear@0: } nuclear@0: nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: static unsigned CopyFloatArrayWithLimit(float dest[], unsigned destSize, nuclear@0: float source[], unsigned sourceSize) nuclear@0: { nuclear@0: unsigned count = Alg::Min(destSize, sourceSize); nuclear@0: for (unsigned i = 0; i < count; i++) nuclear@0: dest[i] = source[i]; nuclear@0: return count; nuclear@0: } nuclear@0: nuclear@0: unsigned HMDState::getFloatArray(const char* propertyName, float values[], unsigned arraySize) nuclear@0: { nuclear@0: if (arraySize) nuclear@0: { nuclear@0: if (OVR_strcmp(propertyName, "ScreenSize") == 0) nuclear@0: { nuclear@0: float data[2] = { OurHMDInfo.ScreenSizeInMeters.w, OurHMDInfo.ScreenSizeInMeters.h }; nuclear@0: nuclear@0: return CopyFloatArrayWithLimit(values, arraySize, data, 2); nuclear@0: } nuclear@0: else if (OVR_strcmp(propertyName, "DistortionClearColor") == 0) nuclear@0: { nuclear@0: return CopyFloatArrayWithLimit(values, arraySize, RenderState.ClearColor, 4); nuclear@0: } nuclear@0: else if (OVR_strcmp(propertyName, "DK2Latency") == 0) nuclear@0: { nuclear@0: if (OurHMDInfo.HmdType != HmdType_DK2) nuclear@0: { nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: union { nuclear@0: struct X { nuclear@0: float latencyRender, latencyTimewarp, latencyPostPresent; nuclear@0: } x; nuclear@0: float data[3]; nuclear@0: } m; nuclear@0: nuclear@0: static_assert(sizeof(m.x)==sizeof(m.data), "sizeof(struct X) failure"); nuclear@0: nuclear@0: TimeManager.GetLatencyTimings(m.x.latencyRender, m.x.latencyTimewarp, m.x.latencyPostPresent); nuclear@0: nuclear@0: return CopyFloatArrayWithLimit(values, arraySize, m.data, 3); nuclear@0: } nuclear@0: else if (NetSessionCommon::IsServiceProperty(NetSessionCommon::EGetNumberValues, propertyName)) nuclear@0: { nuclear@0: // Convert floats to doubles nuclear@0: double* da = new double[arraySize]; nuclear@0: for (int i = 0; i < (int)arraySize; ++i) nuclear@0: { nuclear@0: da[i] = values[i]; nuclear@0: } nuclear@0: nuclear@0: int count = NetClient::GetInstance()->GetNumberValues(GetNetId(), propertyName, da, (int)arraySize); nuclear@0: nuclear@0: for (int i = 0; i < count; ++i) nuclear@0: { nuclear@0: values[i] = (float)da[i]; nuclear@0: } nuclear@0: nuclear@0: delete[] da; nuclear@0: nuclear@0: return count; nuclear@0: } nuclear@0: else if (pProfile) nuclear@0: { nuclear@0: // TBD: Not quite right. Should update profile interface, so that nuclear@0: // we can return 0 in all conditions if property doesn't exist. nuclear@0: nuclear@0: return pProfile->GetFloatValues(propertyName, values, arraySize); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: bool HMDState::setFloatArray(const char* propertyName, float values[], unsigned arraySize) nuclear@0: { nuclear@0: if (!arraySize) nuclear@0: { nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: if (OVR_strcmp(propertyName, "DistortionClearColor") == 0) nuclear@0: { nuclear@0: CopyFloatArrayWithLimit(RenderState.ClearColor, 4, values, arraySize); nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetNumberValues, propertyName)) nuclear@0: { nuclear@0: double* da = new double[arraySize]; nuclear@0: for (int i = 0; i < (int)arraySize; ++i) nuclear@0: { nuclear@0: da[i] = values[i]; nuclear@0: } nuclear@0: nuclear@0: bool result = NetClient::GetInstance()->SetNumberValues(GetNetId(), propertyName, da, arraySize); nuclear@0: nuclear@0: delete[] da; nuclear@0: nuclear@0: return result; nuclear@0: } nuclear@0: nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: const char* HMDState::getString(const char* propertyName, const char* defaultVal) nuclear@0: { nuclear@0: if (NetSessionCommon::IsServiceProperty(NetSessionCommon::EGetStringValue, propertyName)) nuclear@0: { nuclear@0: return NetClient::GetInstance()->GetStringValue(GetNetId(), propertyName, defaultVal); nuclear@0: } nuclear@0: nuclear@0: if (pProfile) nuclear@0: { nuclear@0: LastGetStringValue[0] = 0; nuclear@0: if (pProfile->GetValue(propertyName, LastGetStringValue, sizeof(LastGetStringValue))) nuclear@0: { nuclear@0: return LastGetStringValue; nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: return defaultVal; nuclear@0: } nuclear@0: nuclear@0: bool HMDState::setString(const char* propertyName, const char* value) nuclear@0: { nuclear@0: if (NetSessionCommon::IsServiceProperty(NetSessionCommon::ESetStringValue, propertyName)) nuclear@0: { nuclear@0: return NetClient::GetInstance()->SetStringValue(GetNetId(), propertyName, value); nuclear@0: } nuclear@0: nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: //------------------------------------------------------------------------------------- nuclear@0: // *** Latency Test nuclear@0: nuclear@0: bool HMDState::ProcessLatencyTest(unsigned char rgbColorOut[3]) nuclear@0: { nuclear@0: return NetClient::GetInstance()->LatencyUtil_ProcessInputs(Timer::GetSeconds(), rgbColorOut); nuclear@0: } nuclear@0: nuclear@0: //------------------------------------------------------------------------------------- nuclear@0: // *** Rendering nuclear@0: nuclear@0: bool HMDState::ConfigureRendering(ovrEyeRenderDesc eyeRenderDescOut[2], nuclear@0: const ovrFovPort eyeFovIn[2], nuclear@0: const ovrRenderAPIConfig* apiConfig, nuclear@0: unsigned distortionCaps) nuclear@0: { nuclear@0: ThreadChecker::Scope checkScope(&RenderAPIThreadChecker, "ovrHmd_ConfigureRendering"); nuclear@0: nuclear@0: // null -> shut down. nuclear@0: if (!apiConfig) nuclear@0: { nuclear@0: if (pHSWDisplay) nuclear@0: { nuclear@0: pHSWDisplay->Shutdown(); nuclear@0: pHSWDisplay.Clear(); nuclear@0: } nuclear@0: nuclear@0: if (pRenderer) nuclear@0: pRenderer.Clear(); nuclear@0: RenderingConfigured = false; nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: if (pRenderer && nuclear@0: (apiConfig->Header.API != pRenderer->GetRenderAPI())) nuclear@0: { nuclear@0: // Shutdown old renderer. nuclear@0: if (pHSWDisplay) nuclear@0: { nuclear@0: pHSWDisplay->Shutdown(); nuclear@0: pHSWDisplay.Clear(); nuclear@0: } nuclear@0: nuclear@0: if (pRenderer) nuclear@0: pRenderer.Clear(); nuclear@0: } nuclear@0: nuclear@0: distortionCaps = distortionCaps & pHmdDesc->DistortionCaps; nuclear@0: nuclear@0: // Step 1: do basic setup configuration nuclear@0: RenderState.EnabledHmdCaps = EnabledHmdCaps; // This is a copy... Any cleaner way? nuclear@0: RenderState.DistortionCaps = distortionCaps; nuclear@0: RenderState.EyeRenderDesc[0] = RenderState.CalcRenderDesc(ovrEye_Left, eyeFovIn[0]); nuclear@0: RenderState.EyeRenderDesc[1] = RenderState.CalcRenderDesc(ovrEye_Right, eyeFovIn[1]); nuclear@0: eyeRenderDescOut[0] = RenderState.EyeRenderDesc[0]; nuclear@0: eyeRenderDescOut[1] = RenderState.EyeRenderDesc[1]; nuclear@0: nuclear@0: TimeManager.ResetFrameTiming(0, nuclear@0: (EnabledHmdCaps & ovrHmdCap_DynamicPrediction) ? true : false, nuclear@0: true); nuclear@0: nuclear@0: LastFrameTimeSeconds = 0.0f; nuclear@0: nuclear@0: // Set RenderingConfigured early to avoid ASSERTs in renderer initialization. nuclear@0: RenderingConfigured = true; nuclear@0: nuclear@0: if (!pRenderer) nuclear@0: { nuclear@0: pRenderer = *DistortionRenderer::APICreateRegistry nuclear@0: [apiConfig->Header.API](pHmdDesc, TimeManager, RenderState); nuclear@0: } nuclear@0: nuclear@0: if (!pRenderer || nuclear@0: !pRenderer->Initialize(apiConfig)) nuclear@0: { nuclear@0: RenderingConfigured = false; nuclear@0: return false; nuclear@0: } nuclear@0: nuclear@0: // Setup the Health and Safety Warning display system. nuclear@0: if(pHSWDisplay && (pHSWDisplay->GetRenderAPIType() != apiConfig->Header.API)) // If we need to reconstruct the HSWDisplay for a different graphics API type, delete the existing display. nuclear@0: { nuclear@0: pHSWDisplay->Shutdown(); nuclear@0: pHSWDisplay.Clear(); nuclear@0: } nuclear@0: nuclear@0: if(!pHSWDisplay) // Use * below because that for of operator= causes it to inherit the refcount the factory gave the object. nuclear@0: { nuclear@0: pHSWDisplay = *OVR::CAPI::HSWDisplay::Factory(apiConfig->Header.API, pHmdDesc, RenderState); nuclear@0: pHSWDisplay->Enable(pProfile->GetBoolValue("HSW", true)); nuclear@0: } nuclear@0: nuclear@0: if (pHSWDisplay) nuclear@0: pHSWDisplay->Initialize(apiConfig); // This is potentially re-initializing it with a new config. nuclear@0: nuclear@0: return true; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: void HMDState::SubmitEyeTextures(const ovrPosef renderPose[2], nuclear@0: const ovrTexture eyeTexture[2]) nuclear@0: { nuclear@0: RenderState.EyeRenderPoses[0] = renderPose[0]; nuclear@0: RenderState.EyeRenderPoses[1] = renderPose[1]; nuclear@0: nuclear@0: if (pRenderer) nuclear@0: { nuclear@0: pRenderer->SubmitEye(0, &eyeTexture[0]); nuclear@0: pRenderer->SubmitEye(1, &eyeTexture[1]); nuclear@0: } nuclear@0: } nuclear@0: nuclear@0: nuclear@0: // I appreciate this is not an idea place for this function, but it didn't seem to be nuclear@0: // being linked properly when in OVR_CAPI.cpp. nuclear@0: // Please relocate if you know of a better place nuclear@0: ovrBool ovrHmd_CreateDistortionMeshInternal( ovrHmdStruct * hmd, nuclear@0: ovrEyeType eyeType, ovrFovPort fov, nuclear@0: unsigned int distortionCaps, nuclear@0: ovrDistortionMesh *meshData, nuclear@0: float overrideEyeReliefIfNonZero ) nuclear@0: { nuclear@0: if (!meshData) nuclear@0: return 0; nuclear@0: HMDState* hmds = (HMDState*)hmd; nuclear@0: nuclear@0: // Not used now, but Chromatic flag or others could possibly be checked for in the future. nuclear@0: OVR_UNUSED1(distortionCaps); nuclear@0: nuclear@0: #if defined (OVR_CC_MSVC) nuclear@0: static_assert(sizeof(DistortionMeshVertexData) == sizeof(ovrDistortionVertex), "DistortionMeshVertexData size mismatch"); nuclear@0: #endif nuclear@0: nuclear@0: // *** Calculate a part of "StereoParams" needed for mesh generation nuclear@0: nuclear@0: // Note that mesh distortion generation is invariant of RenderTarget UVs, allowing nuclear@0: // render target size and location to be changed after the fact dynamically. nuclear@0: // eyeToSourceUV is computed here for convenience, so that users don't need nuclear@0: // to call ovrHmd_GetRenderScaleAndOffset unless changing RT dynamically. nuclear@0: nuclear@0: const HmdRenderInfo& hmdri = hmds->RenderState.RenderInfo; nuclear@0: StereoEye stereoEye = (eyeType == ovrEye_Left) ? StereoEye_Left : StereoEye_Right; nuclear@0: nuclear@0: DistortionRenderDesc& distortion = hmds->RenderState.Distortion[eyeType]; nuclear@0: if (overrideEyeReliefIfNonZero) nuclear@0: { nuclear@0: distortion.Lens = GenerateLensConfigFromEyeRelief(overrideEyeReliefIfNonZero,hmdri); nuclear@0: } nuclear@0: nuclear@0: // Find the mapping from TanAngle space to target NDC space. nuclear@0: ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(fov); nuclear@0: nuclear@0: int triangleCount = 0; nuclear@0: int vertexCount = 0; nuclear@0: nuclear@0: DistortionMeshCreate((DistortionMeshVertexData**)&meshData->pVertexData, nuclear@0: (uint16_t**)&meshData->pIndexData, nuclear@0: &vertexCount, &triangleCount, nuclear@0: (stereoEye == StereoEye_Right), nuclear@0: hmdri, distortion, eyeToSourceNDC); nuclear@0: nuclear@0: if (meshData->pVertexData) nuclear@0: { nuclear@0: // Convert to index nuclear@0: meshData->IndexCount = triangleCount * 3; nuclear@0: meshData->VertexCount = vertexCount; nuclear@0: return 1; nuclear@0: } nuclear@0: nuclear@0: return 0; nuclear@0: } nuclear@0: nuclear@0: nuclear@0: nuclear@0: }} // namespace OVR::CAPI