diff options
Diffstat (limited to 'LibOVR/Src/OVR_CAPI.cpp')
-rw-r--r-- | LibOVR/Src/OVR_CAPI.cpp | 925 |
1 files changed, 925 insertions, 0 deletions
diff --git a/LibOVR/Src/OVR_CAPI.cpp b/LibOVR/Src/OVR_CAPI.cpp new file mode 100644 index 0000000..a7f921f --- /dev/null +++ b/LibOVR/Src/OVR_CAPI.cpp @@ -0,0 +1,925 @@ +/************************************************************************************ + +Filename : OVR_CAPI.cpp +Content : Experimental simple C interface to the HMD - version 1. +Created : November 30, 2013 +Authors : Michael Antonov + +Copyright : Copyright 2014 Oculus VR, Inc. All Rights reserved. + +Licensed under the Oculus VR Rift SDK License Version 3.1 (the "License"); +you may not use the Oculus VR Rift SDK except in compliance with the License, +which is provided at the time of installation or download, or which +otherwise accompanies this software in either electronic or hard copy form. + +You may obtain a copy of the License at + +http://www.oculusvr.com/licenses/LICENSE-3.1 + +Unless required by applicable law or agreed to in writing, the Oculus VR SDK +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +************************************************************************************/ + +#include "OVR_CAPI.h" +#include "Kernel/OVR_Timer.h" +#include "Kernel/OVR_Math.h" +#include "Kernel/OVR_System.h" +#include "OVR_Stereo.h" +#include "OVR_Profile.h" + +#include "CAPI/CAPI_GlobalState.h" +#include "CAPI/CAPI_HMDState.h" +#include "CAPI/CAPI_FrameTimeManager.h" + + +using namespace OVR; +using namespace OVR::Util::Render; + +//------------------------------------------------------------------------------------- +// Math +namespace OVR { + + +// ***** FovPort + +// C-interop support: FovPort <-> ovrFovPort +FovPort::FovPort(const ovrFovPort &src) + : UpTan(src.UpTan), DownTan(src.DownTan), LeftTan(src.LeftTan), RightTan(src.RightTan) +{ } + +FovPort::operator ovrFovPort () const +{ + ovrFovPort result; + result.LeftTan = LeftTan; + result.RightTan = RightTan; + result.UpTan = UpTan; + result.DownTan = DownTan; + return result; +} + +// Converts Fov Tan angle units to [-1,1] render target NDC space +Vector2f FovPort::TanAngleToRendertargetNDC(Vector2f const &tanEyeAngle) +{ + ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(*this); + return tanEyeAngle * eyeToSourceNDC.Scale + eyeToSourceNDC.Offset; +} + + +// ***** SensorState + +SensorState::SensorState(const ovrSensorState& s) +{ + Predicted = s.Predicted; + Recorded = s.Recorded; + Temperature = s.Temperature; + StatusFlags = s.StatusFlags; +} + +SensorState::operator ovrSensorState() const +{ + ovrSensorState result; + result.Predicted = Predicted; + result.Recorded = Recorded; + result.Temperature = Temperature; + result.StatusFlags = StatusFlags; + return result; +} + + +} // namespace OVR + +//------------------------------------------------------------------------------------- + +using namespace OVR::CAPI; + +#ifdef __cplusplus +extern "C" { +#endif + + +// Used to generate projection from ovrEyeDesc::Fov +OVR_EXPORT ovrMatrix4f ovrMatrix4f_Projection(ovrFovPort fov, float znear, float zfar, ovrBool rightHanded) +{ + return CreateProjection(rightHanded ? true : false, fov, znear, zfar); +} + + +OVR_EXPORT ovrMatrix4f ovrMatrix4f_OrthoSubProjection(ovrMatrix4f projection, ovrVector2f orthoScale, + float orthoDistance, float eyeViewAdjustX) +{ + + float orthoHorizontalOffset = eyeViewAdjustX / orthoDistance; + + // Current projection maps real-world vector (x,y,1) to the RT. + // We want to find the projection that maps the range [-FovPixels/2,FovPixels/2] to + // the physical [-orthoHalfFov,orthoHalfFov] + // Note moving the offset from M[0][2]+M[1][2] to M[0][3]+M[1][3] - this means + // we don't have to feed in Z=1 all the time. + // The horizontal offset math is a little hinky because the destination is + // actually [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset] + // So we need to first map [-FovPixels/2,FovPixels/2] to + // [-orthoHalfFov+orthoHorizontalOffset,orthoHalfFov+orthoHorizontalOffset]: + // x1 = x0 * orthoHalfFov/(FovPixels/2) + orthoHorizontalOffset; + // = x0 * 2*orthoHalfFov/FovPixels + orthoHorizontalOffset; + // But then we need the sam mapping as the existing projection matrix, i.e. + // x2 = x1 * Projection.M[0][0] + Projection.M[0][2]; + // = x0 * (2*orthoHalfFov/FovPixels + orthoHorizontalOffset) * Projection.M[0][0] + Projection.M[0][2]; + // = x0 * Projection.M[0][0]*2*orthoHalfFov/FovPixels + + // orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2]; + // So in the new projection matrix we need to scale by Projection.M[0][0]*2*orthoHalfFov/FovPixels and + // offset by orthoHorizontalOffset*Projection.M[0][0] + Projection.M[0][2]. + + Matrix4f ortho; + ortho.M[0][0] = projection.M[0][0] * orthoScale.x; + ortho.M[0][1] = 0.0f; + ortho.M[0][2] = 0.0f; + ortho.M[0][3] = -projection.M[0][2] + ( orthoHorizontalOffset * projection.M[0][0] ); + + ortho.M[1][0] = 0.0f; + ortho.M[1][1] = -projection.M[1][1] * orthoScale.y; // Note sign flip (text rendering uses Y=down). + ortho.M[1][2] = 0.0f; + ortho.M[1][3] = -projection.M[1][2]; + + /* + if ( fabsf ( zNear - zFar ) < 0.001f ) + { + ortho.M[2][0] = 0.0f; + ortho.M[2][1] = 0.0f; + ortho.M[2][2] = 0.0f; + ortho.M[2][3] = zFar; + } + else + { + ortho.M[2][0] = 0.0f; + ortho.M[2][1] = 0.0f; + ortho.M[2][2] = zFar / (zNear - zFar); + ortho.M[2][3] = (zFar * zNear) / (zNear - zFar); + } + */ + + // MA: Undo effect of sign + ortho.M[2][0] = 0.0f; + ortho.M[2][1] = 0.0f; + //ortho.M[2][2] = projection.M[2][2] * projection.M[3][2] * -1.0f; // reverse right-handedness + ortho.M[2][2] = 0.0f; + ortho.M[2][3] = 0.0f; + //projection.M[2][3]; + + // No perspective correction for ortho. + ortho.M[3][0] = 0.0f; + ortho.M[3][1] = 0.0f; + ortho.M[3][2] = 0.0f; + ortho.M[3][3] = 1.0f; + + return ortho; +} + + +OVR_EXPORT double ovr_GetTimeInSeconds() +{ + return Timer::GetSeconds(); +} + +// Waits until the specified absolute time. +OVR_EXPORT double ovr_WaitTillTime(double absTime) +{ + volatile int i; + double initialTime = ovr_GetTimeInSeconds(); + double newTime = initialTime; + + while(newTime < absTime) + { + for (int j = 0; j < 50; j++) + i = 0; + newTime = ovr_GetTimeInSeconds(); + } + + // How long we waited + return newTime - initialTime; +} + +//------------------------------------------------------------------------------------- + +// 1. Init/shutdown. + +static ovrBool CAPI_SystemInitCalled = 0; + +OVR_EXPORT ovrBool ovr_Initialize() +{ + if (OVR::CAPI::GlobalState::pInstance) + return 1; + + // We must set up the system for the plugin to work + if (!OVR::System::IsInitialized()) + { + OVR::System::Init(OVR::Log::ConfigureDefaultLog(OVR::LogMask_All)); + CAPI_SystemInitCalled = 1; + } + + // Constructor detects devices + GlobalState::pInstance = new GlobalState; + return 1; +} + +OVR_EXPORT void ovr_Shutdown() +{ + if (!GlobalState::pInstance) + return; + + delete GlobalState::pInstance; + GlobalState::pInstance = 0; + + // We should clean up the system to be complete + if (CAPI_SystemInitCalled) + { + OVR::System::Destroy(); + CAPI_SystemInitCalled = 0; + } + return; +} + + +// There is a thread safety issue with ovrHmd_Detect in that multiple calls from different +// threads can corrupt the global array state. This would lead to two problems: +// a) Create(index) enumerator may miss or overshoot items. Probably not a big deal +// as game logic can easily be written to only do Detect(s)/Creates in one place. +// The alternative would be to return list handle. +// b) TBD: Un-mutexed Detect access from two threads could lead to crash. We should +// probably check this. +// + +OVR_EXPORT int ovrHmd_Detect() +{ + if (!GlobalState::pInstance) + return 0; + return GlobalState::pInstance->EnumerateDevices(); +} + + +// ovrHmd_Create us explicitly separated from StartSensor and Configure to allow creation of +// a relatively light-weight handle that would reference the device going forward and would +// survive future ovrHmd_Detect calls. That is once ovrHMD is returned, index is no longer +// necessary and can be changed by a ovrHmd_Detect call. + +OVR_EXPORT ovrHmd ovrHmd_Create(int index) +{ + if (!GlobalState::pInstance) + return 0; + Ptr<HMDDevice> device = *GlobalState::pInstance->CreateDevice(index); + if (!device) + return 0; + + HMDState* hmds = new HMDState(device); + if (!hmds) + return 0; + + return hmds; +} + +OVR_EXPORT ovrHmd ovrHmd_CreateDebug(ovrHmdType type) +{ + if (!GlobalState::pInstance) + return 0; + + HMDState* hmds = new HMDState(type); + return hmds; +} + +OVR_EXPORT void ovrHmd_Destroy(ovrHmd hmd) +{ + if (!hmd) + return; + // TBD: Any extra shutdown? + HMDState* hmds = (HMDState*)hmd; + + { // Thread checker in its own scope, to avoid access after 'delete'. + // Essentially just checks that no other RenderAPI function is executing. + ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_Destroy"); + } + + delete (HMDState*)hmd; +} + + +OVR_EXPORT const char* ovrHmd_GetLastError(ovrHmd hmd) +{ + using namespace OVR; + if (!hmd) + { + if (!GlobalState::pInstance) + return "LibOVR not initialized."; + return GlobalState::pInstance->GetLastError(); + } + HMDState* p = (HMDState*)hmd; + return p->GetLastError(); +} + + +//------------------------------------------------------------------------------------- + +// Returns capability bits that are enabled at this time; described by ovrHmdCapBits. +// Note that this value is different font ovrHmdDesc::Caps, which describes what +// capabilities are available. +OVR_EXPORT unsigned int ovrHmd_GetEnabledCaps(ovrHmd hmd) +{ + HMDState* p = (HMDState*)hmd; + return p ? p->EnabledHmdCaps : 0; +} + +// Modifies capability bits described by ovrHmdCapBits that can be modified, +// such as ovrHmd_LowPersistance. +OVR_EXPORT void ovrHmd_SetEnabledCaps(ovrHmd hmd, unsigned int capsBits) +{ + HMDState* p = (HMDState*)hmd; + if (p) + p->SetEnabledHmdCaps(capsBits); +} + + +//------------------------------------------------------------------------------------- +// *** Sensor + +// Sensor APIs are separated from Create & Configure for several reasons: +// - They need custom parameters that control allocation of heavy resources +// such as Vision tracking, which you don't want to create unless necessary. +// - A game may want to switch some sensor settings based on user input, +// or at lease enable/disable features such as Vision for debugging. +// - The same or syntactically similar sensor interface is likely to be used if we +// introduce controllers. +// +// - Sensor interface functions are all Thread-safe, unlike the frame/render API +// functions that have different rules (all frame access functions +// must be on render thread) + +OVR_EXPORT ovrBool ovrHmd_StartSensor(ovrHmd hmd, unsigned int supportedCaps, unsigned int requiredCaps) +{ + HMDState* p = (HMDState*)hmd; + // TBD: Decide if we null-check arguments. + return p->StartSensor(supportedCaps, requiredCaps); +} + +OVR_EXPORT void ovrHmd_StopSensor(ovrHmd hmd) +{ + HMDState* p = (HMDState*)hmd; + p->StopSensor(); +} + +OVR_EXPORT void ovrHmd_ResetSensor(ovrHmd hmd) +{ + HMDState* p = (HMDState*)hmd; + p->ResetSensor(); +} + +OVR_EXPORT ovrSensorState ovrHmd_GetSensorState(ovrHmd hmd, double absTime) +{ + HMDState* p = (HMDState*)hmd; + return p->PredictedSensorState(absTime); +} + +// Returns information about a sensor. Only valid after SensorStart. +OVR_EXPORT ovrBool ovrHmd_GetSensorDesc(ovrHmd hmd, ovrSensorDesc* descOut) +{ + HMDState* p = (HMDState*)hmd; + return p->GetSensorDesc(descOut) ? 1 : 0; +} + + + +//------------------------------------------------------------------------------------- +// *** General Setup + + +OVR_EXPORT void ovrHmd_GetDesc(ovrHmd hmd, ovrHmdDesc* desc) +{ + HMDState* hmds = (HMDState*)hmd; + *desc = hmds->RenderState.GetDesc(); + desc->Handle = hmd; +} + +// Per HMD -> calculateIdealPixelSize +OVR_EXPORT ovrSizei ovrHmd_GetFovTextureSize(ovrHmd hmd, ovrEyeType eye, ovrFovPort fov, + float pixelsPerDisplayPixel) +{ + if (!hmd) return Sizei(0); + + HMDState* hmds = (HMDState*)hmd; + return hmds->RenderState.GetFOVTextureSize(eye, fov, pixelsPerDisplayPixel); +} + + +//------------------------------------------------------------------------------------- + + +OVR_EXPORT +ovrBool ovrHmd_ConfigureRendering( ovrHmd hmd, + const ovrRenderAPIConfig* apiConfig, + unsigned int distortionCaps, + const ovrFovPort eyeFovIn[2], + ovrEyeRenderDesc eyeRenderDescOut[2] ) +{ + if (!hmd) return 0; + return ((HMDState*)hmd)->ConfigureRendering(eyeRenderDescOut, eyeFovIn, + apiConfig, distortionCaps); +} + + + +// TBD: MA - Deprecated, need alternative +void ovrHmd_SetVsync(ovrHmd hmd, ovrBool vsync) +{ + if (!hmd) return; + + return ((HMDState*)hmd)->TimeManager.SetVsync(vsync? true : false); +} + + +OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrame(ovrHmd hmd, unsigned int frameIndex) +{ + HMDState* hmds = (HMDState*)hmd; + if (!hmds) + { + ovrFrameTiming f; + memset(&f, 0, sizeof(f)); + return f; + } + + // Check: Proper configure and threading state for the call. + hmds->checkRenderingConfigured("ovrHmd_BeginFrame"); + OVR_ASSERT_LOG(hmds->BeginFrameCalled == false, ("ovrHmd_BeginFrame called multiple times.")); + ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_BeginFrame"); + + hmds->BeginFrameCalled = true; + hmds->BeginFrameThreadId = OVR::GetCurrentThreadId(); + + return ovrHmd_BeginFrameTiming(hmd, frameIndex); +} + + +// Renders textures to frame buffer +OVR_EXPORT void ovrHmd_EndFrame(ovrHmd hmd) +{ + HMDState* hmds = (HMDState*)hmd; + if (!hmds) return; + + // Debug state checks: Must be in BeginFrame, on the same thread. + hmds->checkBeginFrameScope("ovrHmd_EndFrame"); + ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_EndFrame"); + + // TBD: Move directly into renderer + bool dk2LatencyTest = (hmds->HMDInfo.HmdType == HmdType_DK2) && + (hmds->EnabledHmdCaps & ovrHmdCap_LatencyTest); + if (dk2LatencyTest) + { + hmds->LatencyTest2DrawColor[0] = hmds->TimeManager.GetFrameLatencyTestDrawColor(); + hmds->LatencyTest2DrawColor[1] = hmds->LatencyTest2DrawColor[0]; + hmds->LatencyTest2DrawColor[2] = hmds->LatencyTest2DrawColor[0]; + } + + if (hmds->pRenderer) + { + hmds->pRenderer->SaveGraphicsState(); + hmds->pRenderer->EndFrame(true, + hmds->LatencyTestActive ? hmds->LatencyTestDrawColor : NULL, + + // MA: Use this color since we are running DK2 test all the time. + dk2LatencyTest ? hmds->LatencyTest2DrawColor : 0 + //hmds->LatencyTest2Active ? hmds->LatencyTest2DrawColor : NULL + ); + hmds->pRenderer->RestoreGraphicsState(); + } + // Call after present + ovrHmd_EndFrameTiming(hmd); + + if (dk2LatencyTest) + { + hmds->TimeManager.UpdateFrameLatencyTrackingAfterEndFrame( + hmds->LatencyTest2DrawColor[0], hmds->LatencyUtil2.GetLocklessState()); + } + + // Out of BeginFrame + hmds->BeginFrameThreadId = 0; + hmds->BeginFrameCalled = false; +} + + +OVR_EXPORT ovrPosef ovrHmd_BeginEyeRender(ovrHmd hmd, ovrEyeType eye) +{ + HMDState* hmds = (HMDState*)hmd; + if (!hmds) return ovrPosef(); + return hmds->BeginEyeRender(eye); +} + +OVR_EXPORT void ovrHmd_EndEyeRender(ovrHmd hmd, ovrEyeType eye, + ovrPosef renderPose, ovrTexture* eyeTexture) +{ + HMDState* hmds = (HMDState*)hmd; + if (!hmds) return; + hmds->EndEyeRender(eye, renderPose, eyeTexture); +} + + +//------------------------------------------------------------------------------------- +// ***** Frame Timing logic + + +OVR_EXPORT ovrFrameTiming ovrHmd_GetFrameTiming(ovrHmd hmd, unsigned int frameIndex) +{ + ovrFrameTiming f; + memset(&f, 0, sizeof(f)); + + HMDState* hmds = (HMDState*)hmd; + if (hmds) + { + FrameTimeManager::Timing frameTiming = hmds->TimeManager.GetFrameTiming(frameIndex); + + f.ThisFrameSeconds = frameTiming.ThisFrameTime; + f.NextFrameSeconds = frameTiming.NextFrameTime; + f.TimewarpPointSeconds = frameTiming.TimewarpPointTime; + f.ScanoutMidpointSeconds= frameTiming.MidpointTime; + f.EyeScanoutSeconds[0] = frameTiming.EyeRenderTimes[0]; + f.EyeScanoutSeconds[1] = frameTiming.EyeRenderTimes[1]; + + // Compute DeltaSeconds. + f.DeltaSeconds = (hmds->LastGetFrameTimeSeconds == 0.0f) ? 0.0f : + (float) (f.ThisFrameSeconds - hmds->LastFrameTimeSeconds); + hmds->LastGetFrameTimeSeconds = f.ThisFrameSeconds; + if (f.DeltaSeconds > 1.0f) + f.DeltaSeconds = 1.0f; + } + + return f; +} + +OVR_EXPORT ovrFrameTiming ovrHmd_BeginFrameTiming(ovrHmd hmd, unsigned int frameIndex) +{ + ovrFrameTiming f; + memset(&f, 0, sizeof(f)); + + HMDState* hmds = (HMDState*)hmd; + if (!hmds) return f; + + // Check: Proper state for the call. + OVR_ASSERT_LOG(hmds->BeginFrameTimingCalled == false, + ("ovrHmd_BeginFrameTiming called multiple times.")); + hmds->BeginFrameTimingCalled = true; + + double thisFrameTime = hmds->TimeManager.BeginFrame(frameIndex); + + const FrameTimeManager::Timing &frameTiming = hmds->TimeManager.GetFrameTiming(); + + f.ThisFrameSeconds = thisFrameTime; + f.NextFrameSeconds = frameTiming.NextFrameTime; + f.TimewarpPointSeconds = frameTiming.TimewarpPointTime; + f.ScanoutMidpointSeconds= frameTiming.MidpointTime; + f.EyeScanoutSeconds[0] = frameTiming.EyeRenderTimes[0]; + f.EyeScanoutSeconds[1] = frameTiming.EyeRenderTimes[1]; + + // Compute DeltaSeconds. + f.DeltaSeconds = (hmds->LastFrameTimeSeconds == 0.0f) ? 0.0f : + (float) (thisFrameTime - hmds->LastFrameTimeSeconds); + hmds->LastFrameTimeSeconds = thisFrameTime; + if (f.DeltaSeconds > 1.0f) + f.DeltaSeconds = 1.0f; + + return f; +} + + +OVR_EXPORT void ovrHmd_EndFrameTiming(ovrHmd hmd) +{ + HMDState* hmds = (HMDState*)hmd; + if (!hmds) return; + + // Debug state checks: Must be in BeginFrameTiming, on the same thread. + hmds->checkBeginFrameTimingScope("ovrHmd_EndTiming"); + // MA TBD: Correct chek or not? + // ThreadChecker::Scope checkScope(&hmds->RenderAPIThreadChecker, "ovrHmd_EndFrame"); + + hmds->TimeManager.EndFrame(); + hmds->BeginFrameTimingCalled = false; +} + + +OVR_EXPORT void ovrHmd_ResetFrameTiming(ovrHmd hmd, unsigned int frameIndex) +{ + HMDState* hmds = (HMDState*)hmd; + if (!hmds) return; + + hmds->TimeManager.ResetFrameTiming(frameIndex, + false, + hmds->RenderingConfigured); + hmds->LastFrameTimeSeconds = 0.0; + hmds->LastGetFrameTimeSeconds = 0.0; +} + + + +OVR_EXPORT ovrPosef ovrHmd_GetEyePose(ovrHmd hmd, ovrEyeType eye) +{ + HMDState* hmds = (HMDState*)hmd; + if (!hmds) return ovrPosef(); + + hmds->checkBeginFrameTimingScope("ovrHmd_GetEyePose"); + return hmds->TimeManager.GetEyePredictionPose(hmd, eye); +} + + +OVR_EXPORT void ovrHmd_GetEyeTimewarpMatrices(ovrHmd hmd, ovrEyeType eye, + ovrPosef renderPose, ovrMatrix4f twmOut[2]) +{ + HMDState* hmds = (HMDState*)hmd; + if (!hmd) + return; + + // Debug checks: BeginFrame was called, on the same thread. + hmds->checkBeginFrameTimingScope("ovrHmd_GetTimewarpEyeMatrices"); + + hmds->TimeManager.GetTimewarpMatrices(hmd, eye, renderPose, twmOut); + + /* + // MA: Took this out because new latency test approach just sames + // the sample times in FrameTimeManager. + // TODO: if no timewarp, then test latency in begin eye render + if (eye == 0) + { + hmds->ProcessLatencyTest2(hmds->LatencyTest2DrawColor, -1.0f); + } + */ +} + + + +OVR_EXPORT ovrEyeRenderDesc ovrHmd_GetRenderDesc(ovrHmd hmd, + ovrEyeType eyeType, ovrFovPort fov) +{ + ovrEyeRenderDesc erd; + + HMDState* hmds = (HMDState*)hmd; + if (!hmds) + { + memset(&erd, 0, sizeof(erd)); + return erd; + } + + return hmds->RenderState.calcRenderDesc(eyeType, fov); +} + + + +#define OVR_OFFSET_OF(s, field) ((size_t)&((s*)0)->field) + + + +// Generate distortion mesh per eye. +// scaleAndOffsetOut - this will be needed for shader +OVR_EXPORT ovrBool ovrHmd_CreateDistortionMesh( ovrHmd hmd, + ovrEyeType eyeType, ovrFovPort fov, + unsigned int distortionCaps, + ovrDistortionMesh *meshData ) +{ + if (!meshData) + return 0; + HMDState* hmds = (HMDState*)hmd; + + // Not used now, but Chromatic flag or others could possibly be checked for in the future. + OVR_UNUSED1(distortionCaps); + +#if defined (OVR_OS_WIN32) + // TBD: We should probably be sharing some C API structures with C++ to avoid this mess... + OVR_COMPILER_ASSERT(sizeof(DistortionMeshVertexData) == sizeof(ovrDistortionVertex)); + OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, ScreenPosNDC) == OVR_OFFSET_OF(ovrDistortionVertex, Pos)); + OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, TimewarpLerp) == OVR_OFFSET_OF(ovrDistortionVertex, TimeWarpFactor)); + OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, Shade) == OVR_OFFSET_OF(ovrDistortionVertex, VignetteFactor)); + OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, TanEyeAnglesR) == OVR_OFFSET_OF(ovrDistortionVertex, TexR)); + OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, TanEyeAnglesG) == OVR_OFFSET_OF(ovrDistortionVertex, TexG)); + OVR_COMPILER_ASSERT(OVR_OFFSET_OF(DistortionMeshVertexData, TanEyeAnglesB) == OVR_OFFSET_OF(ovrDistortionVertex, TexB)); +#endif + + + // *** Calculate a part of "StereoParams" needed for mesh generation + + // Note that mesh distortion generation is invariant of RenderTarget UVs, allowing + // render target size and location to be changed after the fact dynamically. + // eyeToSourceUV is computed here for convenience, so that users don't need + // to call ovrHmd_GetRenderScaleAndOffset unless changing RT dynamically. + + const HmdRenderInfo& hmdri = hmds->RenderState.RenderInfo; + StereoEye stereoEye = (eyeType == ovrEye_Left) ? StereoEye_Left : StereoEye_Right; + + const DistortionRenderDesc& distortion = hmds->RenderState.Distortion[eyeType]; + + // Find the mapping from TanAngle space to target NDC space. + ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(fov); + + int triangleCount = 0; + int vertexCount = 0; + + DistortionMeshCreate((DistortionMeshVertexData**)&meshData->pVertexData, (UInt16**)&meshData->pIndexData, + &vertexCount, &triangleCount, + (stereoEye == StereoEye_Right), + hmdri, distortion, eyeToSourceNDC); + + if (meshData->pVertexData) + { + // Convert to index + meshData->IndexCount = triangleCount * 3; + meshData->VertexCount = vertexCount; + return 1; + } + + return 0; +} + + +// Frees distortion mesh allocated by ovrHmd_GenerateDistortionMesh. meshData elements +// are set to null and 0s after the call. +OVR_EXPORT void ovrHmd_DestroyDistortionMesh(ovrDistortionMesh* meshData) +{ + if (meshData->pVertexData) + DistortionMeshDestroy((DistortionMeshVertexData*)meshData->pVertexData, + meshData->pIndexData); + meshData->pVertexData = 0; + meshData->pIndexData = 0; + meshData->VertexCount = 0; + meshData->IndexCount = 0; +} + + + +// Computes updated 'uvScaleOffsetOut' to be used with a distortion if render target size or +// viewport changes after the fact. This can be used to adjust render size every frame, if desired. +OVR_EXPORT void ovrHmd_GetRenderScaleAndOffset( ovrFovPort fov, + ovrSizei textureSize, ovrRecti renderViewport, + ovrVector2f uvScaleOffsetOut[2] ) +{ + // Find the mapping from TanAngle space to target NDC space. + ScaleAndOffset2D eyeToSourceNDC = CreateNDCScaleAndOffsetFromFov(fov); + // Find the mapping from TanAngle space to textureUV space. + ScaleAndOffset2D eyeToSourceUV = CreateUVScaleAndOffsetfromNDCScaleandOffset( + eyeToSourceNDC, + renderViewport, textureSize ); + + uvScaleOffsetOut[0] = eyeToSourceUV.Scale; + uvScaleOffsetOut[1] = eyeToSourceUV.Offset; +} + + +//------------------------------------------------------------------------------------- +// ***** Latency Test interface + +OVR_EXPORT ovrBool ovrHmd_GetLatencyTestDrawColor(ovrHmd hmd, unsigned char rgbColorOut[3]) +{ + HMDState* p = (HMDState*)hmd; + rgbColorOut[0] = p->LatencyTestDrawColor[0]; + rgbColorOut[1] = p->LatencyTestDrawColor[1]; + rgbColorOut[2] = p->LatencyTestDrawColor[2]; + return p->LatencyTestActive; +} + +OVR_EXPORT const char* ovrHmd_GetLatencyTestResult(ovrHmd hmd) +{ + HMDState* p = (HMDState*)hmd; + return p->LatencyUtil.GetResultsString(); +} + +OVR_EXPORT double ovrHmd_GetMeasuredLatencyTest2(ovrHmd hmd) +{ + HMDState* p = (HMDState*)hmd; + + // MA Test + float latencies[3]; + p->TimeManager.GetLatencyTimings(latencies); + return latencies[2]; + // return p->LatencyUtil2.GetMeasuredLatency(); +} + + +// ----------------------------------------------------------------------------------- +// ***** Property Access + +OVR_EXPORT float ovrHmd_GetFloat(ovrHmd hmd, const char* propertyName, float defaultVal) +{ + HMDState* hmds = (HMDState*)hmd; + if (hmds) + { + return hmds->getFloatValue(propertyName, defaultVal); + } + + return defaultVal; +} + +OVR_EXPORT ovrBool ovrHmd_SetFloat(ovrHmd hmd, const char* propertyName, float value) +{ + HMDState* hmds = (HMDState*)hmd; + if (hmds) + { + return hmds->setFloatValue(propertyName, value); + } + return false; +} + + + +OVR_EXPORT unsigned int ovrHmd_GetFloatArray(ovrHmd hmd, const char* propertyName, + float values[], unsigned int arraySize) +{ + HMDState* hmds = (HMDState*)hmd; + if (hmds) + { + return hmds->getFloatArray(propertyName, values, arraySize); + } + + return 0; +} + + +// Modify float[] property; false if property doesn't exist or is readonly. +OVR_EXPORT ovrBool ovrHmd_SetFloatArray(ovrHmd hmd, const char* propertyName, + float values[], unsigned int arraySize) +{ + HMDState* hmds = (HMDState*)hmd; + if (hmds) + { + return hmds->setFloatArray(propertyName, values, arraySize); + } + + return 0; +} + +OVR_EXPORT const char* ovrHmd_GetString(ovrHmd hmd, const char* propertyName, + const char* defaultVal) +{ + HMDState* hmds = (HMDState*)hmd; + if (hmds) + { + return hmds->getString(propertyName, defaultVal); + } + + return defaultVal; +} + +/* Not needed yet. + +// Get array of strings, i.e. const char* [] property. +// Returns the number of elements filled in, 0 if property doesn't exist. +// Maximum of arraySize elements will be written. +// String memory is guaranteed to exist until next call to GetString or GetStringArray, or HMD is destroyed. +OVR_EXPORT +unsigned int ovrHmd_GetStringArray(ovrHmd hmd, const char* propertyName, + const char* values[], unsigned int arraySize) +{ + HMDState* hmds = (HMDState*)hmd; + if (hmds && hmds->pHMD && arraySize) + { + Profile* p = hmds->pHMD->GetProfile(); + + hmds->LastGetStringValue[0] = 0; + if (p && p->GetValue(propertyName, hmds->LastGetStringValue, sizeof(hmds->LastGetStringValue))) + { + values[0] = hmds->LastGetStringValue; + return 1; + } + } + + return 0; +} +*/ + +// Returns array size of a property, 0 if property doesn't exist. +// Can be used to check existence of a property. +OVR_EXPORT unsigned int ovrHmd_GetArraySize(ovrHmd hmd, const char* propertyName) +{ + HMDState* hmds = (HMDState*)hmd; + if (hmds && hmds->pHMD) + { + // For now, just access the profile. + Profile* p = hmds->pHMD->GetProfile(); + + if (p) + return p->GetNumValues(propertyName); + } + return 0; +} + + +#ifdef __cplusplus +} // extern "C" +#endif + + +//------------------------------------------------------------------------------------- +// ****** Special access for VRConfig + +// Return the sensor fusion object for the purposes of magnetometer calibration. The +// function is private and is only exposed through VRConfig header declarations +OVR::SensorFusion* ovrHmd_GetSensorFusion(ovrHmd hmd) +{ + HMDState* p = (HMDState*)hmd; + return &p->SFusion; +} + + |